D-Bus  1.8.20
dbus-spawn-win.c
00001 #include <config.h>
00002 
00003 //#define SPAWN_DEBUG
00004 
00005 #if !defined(SPAWN_DEBUG) || defined(_MSC_VER)
00006 #define PING()
00007 #else
00008 #define PING() fprintf (stderr, "%s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__); fflush (stderr)
00009 #endif
00010 
00011 #include <stdio.h>
00012 
00013 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
00014 /* dbus-spawn-win32.c Wrapper around g_spawn
00015  * 
00016  * Copyright (C) 2002, 2003, 2004  Red Hat, Inc.
00017  * Copyright (C) 2003 CodeFactory AB
00018  * Copyright (C) 2005 Novell, Inc.
00019  *
00020  * Licensed under the Academic Free License version 2.1
00021  * 
00022  * This program is free software; you can redistribute it and/or modify
00023  * it under the terms of the GNU General Public License as published by
00024  * the Free Software Foundation; either version 2 of the License, or
00025  * (at your option) any later version.
00026  *
00027  * This program is distributed in the hope that it will be useful,
00028  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00029  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00030  * GNU General Public License for more details.
00031  * 
00032  * You should have received a copy of the GNU General Public License
00033  * along with this program; if not, write to the Free Software
00034  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00035  *
00036  */
00037 #include "dbus-spawn.h"
00038 #include "dbus-sysdeps.h"
00039 #include "dbus-sysdeps-win.h"
00040 #include "dbus-internals.h"
00041 #include "dbus-test.h"
00042 #include "dbus-protocol.h"
00043 
00044 #define WIN32_LEAN_AND_MEAN
00045 #include <windows.h>
00046 //#define STRICT
00047 //#include <windows.h>
00048 //#undef STRICT
00049 #include <winsock2.h>
00050 #undef interface
00051 
00052 #include <stdlib.h>
00053 
00054 #ifndef DBUS_WINCE
00055 #include <process.h>
00056 #endif
00057 
00061 struct DBusBabysitter
00062   {
00063     int refcount;
00064 
00065     HANDLE start_sync_event;
00066 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
00067 
00068     HANDLE end_sync_event;
00069 #endif
00070 
00071     char *log_name;
00072     DBusSpawnChildSetupFunc child_setup;
00073     void *user_data;
00074 
00075     int argc;
00076     char **argv;
00077     char **envp;
00078 
00079     HANDLE child_handle;
00080     int socket_to_babysitter;   /* Connection to the babysitter thread */
00081     int socket_to_main;
00082 
00083     DBusWatchList *watches;
00084     DBusWatch *sitter_watch;
00085     DBusBabysitterFinishedFunc finished_cb;
00086     void *finished_data;
00087 
00088     dbus_bool_t have_spawn_errno;
00089     int spawn_errno;
00090     dbus_bool_t have_child_status;
00091     int child_status;
00092   };
00093 
00094 static DBusBabysitter*
00095 _dbus_babysitter_new (void)
00096 {
00097   DBusBabysitter *sitter;
00098 
00099   sitter = dbus_new0 (DBusBabysitter, 1);
00100   if (sitter == NULL)
00101     return NULL;
00102 
00103   sitter->refcount = 1;
00104 
00105   sitter->start_sync_event = CreateEvent (NULL, FALSE, FALSE, NULL);
00106   if (sitter->start_sync_event == NULL)
00107     {
00108       _dbus_babysitter_unref (sitter);
00109       return NULL;
00110     }
00111 
00112 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
00113   sitter->end_sync_event = CreateEvent (NULL, FALSE, FALSE, NULL);
00114   if (sitter->end_sync_event == NULL)
00115     {
00116       _dbus_babysitter_unref (sitter);
00117       return NULL;
00118     }
00119 #endif
00120 
00121   sitter->child_handle = NULL;
00122 
00123   sitter->socket_to_babysitter = sitter->socket_to_main = -1;
00124 
00125   sitter->argc = 0;
00126   sitter->argv = NULL;
00127   sitter->envp = NULL;
00128 
00129   sitter->watches = _dbus_watch_list_new ();
00130   if (sitter->watches == NULL)
00131     {
00132       _dbus_babysitter_unref (sitter);
00133       return NULL;
00134     }
00135 
00136   sitter->have_spawn_errno = FALSE;
00137   sitter->have_child_status = FALSE;
00138 
00139   return sitter;
00140 }
00141 
00148 DBusBabysitter *
00149 _dbus_babysitter_ref (DBusBabysitter *sitter)
00150 {
00151   PING();
00152   _dbus_assert (sitter != NULL);
00153   _dbus_assert (sitter->refcount > 0);
00154 
00155   sitter->refcount += 1;
00156 
00157   return sitter;
00158 }
00159 
00160 static void
00161 close_socket_to_babysitter (DBusBabysitter *sitter)
00162 {
00163   _dbus_verbose ("Closing babysitter\n");
00164 
00165   if (sitter->sitter_watch != NULL)
00166     {
00167       _dbus_assert (sitter->watches != NULL);
00168       _dbus_watch_list_remove_watch (sitter->watches,  sitter->sitter_watch);
00169       _dbus_watch_invalidate (sitter->sitter_watch);
00170       _dbus_watch_unref (sitter->sitter_watch);
00171       sitter->sitter_watch = NULL;
00172     }
00173 
00174   if (sitter->socket_to_babysitter != -1)
00175     {
00176       _dbus_close_socket (sitter->socket_to_babysitter, NULL);
00177       sitter->socket_to_babysitter = -1;
00178     }
00179 }
00180 
00186 void
00187 _dbus_babysitter_unref (DBusBabysitter *sitter)
00188 {
00189   int i;
00190 
00191   PING();
00192   _dbus_assert (sitter != NULL);
00193   _dbus_assert (sitter->refcount > 0);
00194 
00195   sitter->refcount -= 1;
00196 
00197   if (sitter->refcount == 0)
00198     {
00199       close_socket_to_babysitter (sitter);
00200 
00201       if (sitter->socket_to_main != -1)
00202         {
00203           _dbus_close_socket (sitter->socket_to_main, NULL);
00204           sitter->socket_to_main = -1;
00205         }
00206 
00207       PING();
00208       if (sitter->argv != NULL)
00209         {
00210           for (i = 0; i < sitter->argc; i++)
00211             if (sitter->argv[i] != NULL)
00212               {
00213                 dbus_free (sitter->argv[i]);
00214                 sitter->argv[i] = NULL;
00215               }
00216           dbus_free (sitter->argv);
00217           sitter->argv = NULL;
00218         }
00219 
00220       if (sitter->envp != NULL)
00221         {
00222           char **e = sitter->envp;
00223 
00224           while (*e)
00225             dbus_free (*e++);
00226           dbus_free (sitter->envp);
00227           sitter->envp = NULL;
00228         }
00229 
00230       if (sitter->child_handle != NULL)
00231         {
00232           CloseHandle (sitter->child_handle);
00233           sitter->child_handle = NULL;
00234         }
00235 
00236       if (sitter->sitter_watch)
00237         {
00238           _dbus_watch_invalidate (sitter->sitter_watch);
00239           _dbus_watch_unref (sitter->sitter_watch);
00240           sitter->sitter_watch = NULL;
00241         }
00242 
00243       if (sitter->watches)
00244         _dbus_watch_list_free (sitter->watches);
00245 
00246       if (sitter->start_sync_event != NULL)
00247         {
00248           PING();
00249           CloseHandle (sitter->start_sync_event);
00250           sitter->start_sync_event = NULL;
00251         }
00252 
00253 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
00254       if (sitter->end_sync_event != NULL)
00255         {
00256           CloseHandle (sitter->end_sync_event);
00257           sitter->end_sync_event = NULL;
00258         }
00259 #endif
00260 
00261       dbus_free (sitter->log_name);
00262 
00263       dbus_free (sitter);
00264     }
00265 }
00266 
00267 void
00268 _dbus_babysitter_kill_child (DBusBabysitter *sitter)
00269 {
00270   PING();
00271   if (sitter->child_handle == NULL)
00272     return; /* child is already dead, or we're so hosed we'll never recover */
00273 
00274   PING();
00275   TerminateProcess (sitter->child_handle, 12345);
00276 }
00277 
00283 dbus_bool_t
00284 _dbus_babysitter_get_child_exited (DBusBabysitter *sitter)
00285 {
00286   PING();
00287   return (sitter->child_handle == NULL);
00288 }
00289 
00302 dbus_bool_t
00303 _dbus_babysitter_get_child_exit_status (DBusBabysitter *sitter,
00304                                         int            *status)
00305 {
00306   if (!_dbus_babysitter_get_child_exited (sitter))
00307     _dbus_assert_not_reached ("Child has not exited");
00308 
00309   if (!sitter->have_child_status ||
00310       sitter->child_status == STILL_ACTIVE)
00311     return FALSE;
00312 
00313   *status = sitter->child_status;
00314   return TRUE;
00315 }
00316 
00326 void
00327 _dbus_babysitter_set_child_exit_error (DBusBabysitter *sitter,
00328                                        DBusError      *error)
00329 {
00330   PING();
00331   if (!_dbus_babysitter_get_child_exited (sitter))
00332     return;
00333 
00334   PING();
00335   if (sitter->have_spawn_errno)
00336     {
00337       char *emsg = _dbus_win_error_string (sitter->spawn_errno);
00338       dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED,
00339                       "Failed to execute program %s: %s",
00340                       sitter->log_name, emsg);
00341       _dbus_win_free_error_string (emsg);
00342     }
00343   else if (sitter->have_child_status)
00344     {
00345       PING();
00346       dbus_set_error (error, DBUS_ERROR_SPAWN_CHILD_EXITED,
00347                       "Process %s exited with status %d",
00348                       sitter->log_name, sitter->child_status);
00349     }
00350   else
00351     {
00352       PING();
00353       dbus_set_error (error, DBUS_ERROR_FAILED,
00354                       "Process %s exited, status unknown",
00355                       sitter->log_name);
00356     }
00357   PING();
00358 }
00359 
00360 dbus_bool_t
00361 _dbus_babysitter_set_watch_functions (DBusBabysitter            *sitter,
00362                                       DBusAddWatchFunction       add_function,
00363                                       DBusRemoveWatchFunction    remove_function,
00364                                       DBusWatchToggledFunction   toggled_function,
00365                                       void                      *data,
00366                                       DBusFreeFunction           free_data_function)
00367 {
00368   PING();
00369   return _dbus_watch_list_set_functions (sitter->watches,
00370                                          add_function,
00371                                          remove_function,
00372                                          toggled_function,
00373                                          data,
00374                                          free_data_function);
00375 }
00376 
00377 static dbus_bool_t
00378 handle_watch (DBusWatch       *watch,
00379               unsigned int     condition,
00380               void            *data)
00381 {
00382   DBusBabysitter *sitter = data;
00383 
00384   /* On Unix dbus-spawn uses a babysitter *process*, thus it has to
00385    * actually send the exit statuses, error codes and whatnot through
00386    * sockets and/or pipes. On Win32, the babysitter is jus a thread,
00387    * so it can set the status fields directly in the babysitter struct
00388    * just fine. The socket pipe is used just so we can watch it with
00389    * select(), as soon as anything is written to it we know that the
00390    * babysitter thread has recorded the status in the babysitter
00391    * struct.
00392    */
00393 
00394   PING();
00395   close_socket_to_babysitter (sitter);
00396   PING();
00397 
00398   if (_dbus_babysitter_get_child_exited (sitter) &&
00399       sitter->finished_cb != NULL)
00400     {
00401       sitter->finished_cb (sitter, sitter->finished_data);
00402       sitter->finished_cb = NULL;
00403     }
00404 
00405   return TRUE;
00406 }
00407 
00408 /* protect_argv lifted from GLib, relicensed by author, Tor Lillqvist */
00409 static int
00410 protect_argv (char  **argv,
00411               char ***new_argv)
00412 {
00413   int i;
00414   int argc = 0;
00415 
00416   while (argv[argc])
00417     ++argc;
00418   *new_argv = dbus_malloc ((argc + 1) * sizeof (char *));
00419   if (*new_argv == NULL)
00420     return -1;
00421 
00422   for (i = 0; i < argc; i++)
00423     (*new_argv)[i] = NULL;
00424 
00425   /* Quote each argv element if necessary, so that it will get
00426    * reconstructed correctly in the C runtime startup code.  Note that
00427    * the unquoting algorithm in the C runtime is really weird, and
00428    * rather different than what Unix shells do. See stdargv.c in the C
00429    * runtime sources (in the Platform SDK, in src/crt).
00430    *
00431    * Note that an new_argv[0] constructed by this function should
00432    * *not* be passed as the filename argument to a spawn* or exec*
00433    * family function. That argument should be the real file name
00434    * without any quoting.
00435    */
00436   for (i = 0; i < argc; i++)
00437     {
00438       char *p = argv[i];
00439       char *q;
00440       int len = 0;
00441       int need_dblquotes = FALSE;
00442       while (*p)
00443         {
00444           if (*p == ' ' || *p == '\t')
00445             need_dblquotes = TRUE;
00446           else if (*p == '"')
00447             len++;
00448           else if (*p == '\\')
00449             {
00450               char *pp = p;
00451               while (*pp && *pp == '\\')
00452                 pp++;
00453               if (*pp == '"')
00454                 len++;
00455             }
00456           len++;
00457           p++;
00458         }
00459 
00460       q = (*new_argv)[i] = dbus_malloc (len + need_dblquotes*2 + 1);
00461 
00462       if (q == NULL)
00463         return -1;
00464 
00465 
00466       p = argv[i];
00467 
00468       if (need_dblquotes)
00469         *q++ = '"';
00470 
00471       while (*p)
00472         {
00473           if (*p == '"')
00474             *q++ = '\\';
00475           else if (*p == '\\')
00476             {
00477               char *pp = p;
00478               while (*pp && *pp == '\\')
00479                 pp++;
00480               if (*pp == '"')
00481                 *q++ = '\\';
00482             }
00483           *q++ = *p;
00484           p++;
00485         }
00486 
00487       if (need_dblquotes)
00488         *q++ = '"';
00489       *q++ = '\0';
00490       /* printf ("argv[%d]:%s, need_dblquotes:%s len:%d => %s\n", i, argv[i], need_dblquotes?"TRUE":"FALSE", len, (*new_argv)[i]); */
00491     }
00492   (*new_argv)[argc] = NULL;
00493 
00494   return argc;
00495 }
00496 
00497 
00498 /* From GPGME, relicensed by g10 Code GmbH.  */
00499 static char *
00500 compose_string (char **strings, char separator)
00501 {
00502   int i;
00503   int n = 0;
00504   char *buf;
00505   char *p;
00506 
00507   if (!strings || !strings[0])
00508     return 0;
00509   for (i = 0; strings[i]; i++)
00510     n += strlen (strings[i]) + 1;
00511   n++;
00512 
00513   buf = p = malloc (n);
00514   if (!buf)
00515     return NULL;
00516   for (i = 0; strings[i]; i++)
00517     {
00518       strcpy (p, strings[i]);
00519       p += strlen (strings[i]);
00520       *(p++) = separator;
00521     }
00522   p--;
00523   *(p++) = '\0';
00524   *p = '\0';
00525 
00526   return buf;
00527 }
00528 
00529 static char *
00530 build_commandline (char **argv)
00531 {
00532   return compose_string (argv, ' ');
00533 }
00534 
00535 static char *
00536 build_env_string (char** envp)
00537 {
00538   return compose_string (envp, '\0');
00539 }
00540 
00541 static HANDLE
00542 spawn_program (char* name, char** argv, char** envp)
00543 {
00544   PROCESS_INFORMATION pi = { NULL, 0, 0, 0 };
00545   STARTUPINFOA si;
00546   char *arg_string, *env_string;
00547   BOOL result;
00548 
00549 #ifdef DBUS_WINCE
00550   if (argv && argv[0])
00551     arg_string = build_commandline (argv + 1);
00552   else
00553     arg_string = NULL;
00554 #else
00555   arg_string = build_commandline (argv);
00556 #endif
00557   if (!arg_string)
00558     return INVALID_HANDLE_VALUE;
00559 
00560   env_string = build_env_string(envp);
00561 
00562   memset (&si, 0, sizeof (si));
00563   si.cb = sizeof (si);
00564 #ifdef DBUS_WINCE
00565   result = CreateProcessA (name, arg_string, NULL, NULL, FALSE, 0,
00566 #else
00567   result = CreateProcessA (NULL, arg_string, NULL, NULL, FALSE, 0,
00568 #endif
00569                            (LPVOID)env_string, NULL, &si, &pi);
00570   free (arg_string);
00571   if (env_string)
00572     free (env_string);
00573 
00574   if (!result)
00575     return INVALID_HANDLE_VALUE;
00576 
00577   CloseHandle (pi.hThread);
00578   return pi.hProcess;
00579 }
00580 
00581 
00582 static DWORD __stdcall
00583 babysitter (void *parameter)
00584 {
00585   DBusBabysitter *sitter = (DBusBabysitter *) parameter;
00586 
00587   PING();
00588   _dbus_babysitter_ref (sitter);
00589 
00590   if (sitter->child_setup)
00591     {
00592       PING();
00593       (*sitter->child_setup) (sitter->user_data);
00594     }
00595 
00596   _dbus_verbose ("babysitter: spawning %s\n", sitter->log_name);
00597 
00598   PING();
00599   sitter->child_handle = spawn_program (sitter->log_name,
00600                                         sitter->argv, sitter->envp);
00601 
00602   PING();
00603   if (sitter->child_handle == (HANDLE) -1)
00604     {
00605       sitter->child_handle = NULL;
00606       sitter->have_spawn_errno = TRUE;
00607       sitter->spawn_errno = GetLastError();
00608     }
00609   
00610   PING();
00611   SetEvent (sitter->start_sync_event);
00612 
00613   if (sitter->child_handle != NULL)
00614     {
00615       int ret;
00616       DWORD status;
00617 
00618       PING();
00619       WaitForSingleObject (sitter->child_handle, INFINITE);
00620 
00621       PING();
00622       ret = GetExitCodeProcess (sitter->child_handle, &status);
00623 
00624       sitter->child_status = status;
00625       sitter->have_child_status = TRUE;
00626 
00627       CloseHandle (sitter->child_handle);
00628       sitter->child_handle = NULL;
00629     }
00630 
00631 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
00632   SetEvent (sitter->end_sync_event);
00633 #endif
00634 
00635   PING();
00636   send (sitter->socket_to_main, " ", 1, 0);
00637 
00638   _dbus_babysitter_unref (sitter);
00639 
00640   return 0;
00641 }
00642 
00643 dbus_bool_t
00644 _dbus_spawn_async_with_babysitter (DBusBabysitter           **sitter_p,
00645                                    const char                *log_name,
00646                                    char                     **argv,
00647                                    char                     **envp,
00648                                    DBusSpawnChildSetupFunc    child_setup,
00649                                    void                      *user_data,
00650                                    DBusError                 *error)
00651 {
00652   DBusBabysitter *sitter;
00653   HANDLE sitter_thread;
00654   DWORD sitter_thread_id;
00655   
00656   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00657   _dbus_assert (argv[0] != NULL);
00658 
00659   *sitter_p = NULL;
00660 
00661   PING();
00662   sitter = _dbus_babysitter_new ();
00663   if (sitter == NULL)
00664     {
00665       _DBUS_SET_OOM (error);
00666       return FALSE;
00667     }
00668 
00669   sitter->child_setup = child_setup;
00670   sitter->user_data = user_data;
00671 
00672   sitter->log_name = _dbus_strdup (log_name);
00673   if (sitter->log_name == NULL && log_name != NULL)
00674     {
00675       _DBUS_SET_OOM (error);
00676       goto out0;
00677     }
00678 
00679   if (sitter->log_name == NULL)
00680     sitter->log_name = _dbus_strdup (argv[0]);
00681 
00682   if (sitter->log_name == NULL)
00683     {
00684       _DBUS_SET_OOM (error);
00685       goto out0;
00686     }
00687 
00688   PING();
00689   if (!_dbus_full_duplex_pipe (&sitter->socket_to_babysitter,
00690                                &sitter->socket_to_main,
00691                                FALSE, error))
00692     goto out0;
00693 
00694   sitter->sitter_watch = _dbus_watch_new (sitter->socket_to_babysitter,
00695                                           DBUS_WATCH_READABLE,
00696                                           TRUE, handle_watch, sitter, NULL);
00697   PING();
00698   if (sitter->sitter_watch == NULL)
00699     {
00700       _DBUS_SET_OOM (error);
00701       goto out0;
00702     }
00703 
00704   PING();
00705   if (!_dbus_watch_list_add_watch (sitter->watches,  sitter->sitter_watch))
00706     {
00707       /* we need to free it early so the destructor won't try to remove it
00708        * without it having been added, which DBusLoop doesn't allow */
00709       _dbus_watch_invalidate (sitter->sitter_watch);
00710       _dbus_watch_unref (sitter->sitter_watch);
00711       sitter->sitter_watch = NULL;
00712 
00713       _DBUS_SET_OOM (error);
00714       goto out0;
00715     }
00716 
00717   sitter->argc = protect_argv (argv, &sitter->argv);
00718   if (sitter->argc == -1)
00719     {
00720       _DBUS_SET_OOM (error);
00721       goto out0;
00722     }
00723   sitter->envp = envp;
00724 
00725   PING();
00726   sitter_thread = (HANDLE) CreateThread (NULL, 0, babysitter,
00727                   sitter, 0, &sitter_thread_id);
00728 
00729   if (sitter_thread == 0)
00730     {
00731       PING();
00732       dbus_set_error_const (error, DBUS_ERROR_SPAWN_FORK_FAILED,
00733                             "Failed to create new thread");
00734       goto out0;
00735     }
00736   CloseHandle (sitter_thread);
00737 
00738   PING();
00739   WaitForSingleObject (sitter->start_sync_event, INFINITE);
00740 
00741   PING();
00742   if (sitter_p != NULL)
00743     *sitter_p = sitter;
00744   else
00745     _dbus_babysitter_unref (sitter);
00746 
00747   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00748 
00749   PING();
00750   return TRUE;
00751 
00752 out0:
00753   _dbus_babysitter_unref (sitter);
00754 
00755   return FALSE;
00756 }
00757 
00758 void
00759 _dbus_babysitter_set_result_function  (DBusBabysitter             *sitter,
00760                                        DBusBabysitterFinishedFunc  finished,
00761                                        void                       *user_data)
00762 {
00763   sitter->finished_cb = finished;
00764   sitter->finished_data = user_data;
00765 }
00766 
00767 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
00768 
00769 static char *
00770 get_test_exec (const char *exe,
00771                DBusString *scratch_space)
00772 {
00773   const char *dbus_test_exec;
00774 
00775   dbus_test_exec = _dbus_getenv ("DBUS_TEST_EXEC");
00776 
00777   if (dbus_test_exec == NULL)
00778     dbus_test_exec = DBUS_TEST_EXEC;
00779 
00780   if (!_dbus_string_init (scratch_space))
00781     return NULL;
00782 
00783   if (!_dbus_string_append_printf (scratch_space, "%s/%s%s",
00784                                    dbus_test_exec, exe, DBUS_EXEEXT))
00785     {
00786       _dbus_string_free (scratch_space);
00787       return NULL;
00788     }
00789 
00790   return _dbus_string_get_data (scratch_space);
00791 }
00792 
00793 #define LIVE_CHILDREN(sitter) ((sitter)->child_handle != NULL)
00794 
00795 static void
00796 _dbus_babysitter_block_for_child_exit (DBusBabysitter *sitter)
00797 {
00798   if (sitter->child_handle == NULL)
00799     return;
00800 
00801   WaitForSingleObject (sitter->end_sync_event, INFINITE);
00802 }
00803 
00804 static dbus_bool_t
00805 check_spawn_nonexistent (void *data)
00806 {
00807   char *argv[4] = { NULL, NULL, NULL, NULL };
00808   DBusBabysitter *sitter;
00809   DBusError error;
00810 
00811   sitter = NULL;
00812 
00813   dbus_error_init (&error);
00814 
00815   /*** Test launching nonexistent binary */
00816 
00817   argv[0] = "/this/does/not/exist/32542sdgafgafdg";
00818   if (_dbus_spawn_async_with_babysitter (&sitter, "spawn_nonexistent", argv, NULL,
00819                                          NULL, NULL,
00820                                          &error))
00821     {
00822       _dbus_babysitter_block_for_child_exit (sitter);
00823       _dbus_babysitter_set_child_exit_error (sitter, &error);
00824     }
00825 
00826   if (sitter)
00827     _dbus_babysitter_unref (sitter);
00828 
00829   if (!dbus_error_is_set (&error))
00830     {
00831       _dbus_warn ("Did not get an error launching nonexistent executable\n");
00832       return FALSE;
00833     }
00834 
00835   if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
00836         dbus_error_has_name (&error, DBUS_ERROR_SPAWN_EXEC_FAILED)))
00837     {
00838       _dbus_warn ("Not expecting error when launching nonexistent executable: %s: %s\n",
00839                   error.name, error.message);
00840       dbus_error_free (&error);
00841       return FALSE;
00842     }
00843 
00844   dbus_error_free (&error);
00845 
00846   return TRUE;
00847 }
00848 
00849 static dbus_bool_t
00850 check_spawn_segfault (void *data)
00851 {
00852   char *argv[4] = { NULL, NULL, NULL, NULL };
00853   DBusBabysitter *sitter;
00854   DBusError error;
00855   DBusString argv0;
00856 
00857   sitter = NULL;
00858 
00859   dbus_error_init (&error);
00860 
00861   /*** Test launching segfault binary */
00862 
00863   argv[0] = get_test_exec ("test-segfault", &argv0);
00864 
00865   if (argv[0] == NULL)
00866     {
00867       /* OOM was simulated, never mind */
00868       return TRUE;
00869     }
00870 
00871   if (_dbus_spawn_async_with_babysitter (&sitter, "spawn_segfault", argv, NULL,
00872                                          NULL, NULL,
00873                                          &error))
00874     {
00875       _dbus_babysitter_block_for_child_exit (sitter);
00876       _dbus_babysitter_set_child_exit_error (sitter, &error);
00877     }
00878 
00879   _dbus_string_free (&argv0);
00880 
00881   if (sitter)
00882     _dbus_babysitter_unref (sitter);
00883 
00884   if (!dbus_error_is_set (&error))
00885     {
00886       _dbus_warn ("Did not get an error launching segfaulting binary\n");
00887       return FALSE;
00888     }
00889 
00890   if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
00891         dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED)))
00892     {
00893       _dbus_warn ("Not expecting error when launching segfaulting executable: %s: %s\n",
00894                   error.name, error.message);
00895       dbus_error_free (&error);
00896       return FALSE;
00897     }
00898 
00899   dbus_error_free (&error);
00900 
00901   return TRUE;
00902 }
00903 
00904 static dbus_bool_t
00905 check_spawn_exit (void *data)
00906 {
00907   char *argv[4] = { NULL, NULL, NULL, NULL };
00908   DBusBabysitter *sitter;
00909   DBusError error;
00910   DBusString argv0;
00911 
00912   sitter = NULL;
00913 
00914   dbus_error_init (&error);
00915 
00916   /*** Test launching exit failure binary */
00917 
00918   argv[0] = get_test_exec ("test-exit", &argv0);
00919 
00920   if (argv[0] == NULL)
00921     {
00922       /* OOM was simulated, never mind */
00923       return TRUE;
00924     }
00925 
00926   if (_dbus_spawn_async_with_babysitter (&sitter, "spawn_exit", argv, NULL,
00927                                          NULL, NULL,
00928                                          &error))
00929     {
00930       _dbus_babysitter_block_for_child_exit (sitter);
00931       _dbus_babysitter_set_child_exit_error (sitter, &error);
00932     }
00933 
00934   _dbus_string_free (&argv0);
00935 
00936   if (sitter)
00937     _dbus_babysitter_unref (sitter);
00938 
00939   if (!dbus_error_is_set (&error))
00940     {
00941       _dbus_warn ("Did not get an error launching binary that exited with failure code\n");
00942       return FALSE;
00943     }
00944 
00945   if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
00946         dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED)))
00947     {
00948       _dbus_warn ("Not expecting error when launching exiting executable: %s: %s\n",
00949                   error.name, error.message);
00950       dbus_error_free (&error);
00951       return FALSE;
00952     }
00953 
00954   dbus_error_free (&error);
00955 
00956   return TRUE;
00957 }
00958 
00959 static dbus_bool_t
00960 check_spawn_and_kill (void *data)
00961 {
00962   char *argv[4] = { NULL, NULL, NULL, NULL };
00963   DBusBabysitter *sitter;
00964   DBusError error;
00965   DBusString argv0;
00966 
00967   sitter = NULL;
00968 
00969   dbus_error_init (&error);
00970 
00971   /*** Test launching sleeping binary then killing it */
00972 
00973   argv[0] = get_test_exec ("test-sleep-forever", &argv0);
00974 
00975   if (argv[0] == NULL)
00976     {
00977       /* OOM was simulated, never mind */
00978       return TRUE;
00979     }
00980 
00981   if (_dbus_spawn_async_with_babysitter (&sitter, "spawn_and_kill", argv, NULL,
00982                                          NULL, NULL,
00983                                          &error))
00984     {
00985       _dbus_babysitter_kill_child (sitter);
00986 
00987       _dbus_babysitter_block_for_child_exit (sitter);
00988 
00989       _dbus_babysitter_set_child_exit_error (sitter, &error);
00990     }
00991 
00992   _dbus_string_free (&argv0);
00993 
00994   if (sitter)
00995     _dbus_babysitter_unref (sitter);
00996 
00997   if (!dbus_error_is_set (&error))
00998     {
00999       _dbus_warn ("Did not get an error after killing spawned binary\n");
01000       return FALSE;
01001     }
01002 
01003   if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
01004         dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED)))
01005     {
01006       _dbus_warn ("Not expecting error when killing executable: %s: %s\n",
01007                   error.name, error.message);
01008       dbus_error_free (&error);
01009       return FALSE;
01010     }
01011 
01012   dbus_error_free (&error);
01013 
01014   return TRUE;
01015 }
01016 
01017 dbus_bool_t
01018 _dbus_spawn_test (const char *test_data_dir)
01019 {
01020   if (!_dbus_test_oom_handling ("spawn_nonexistent",
01021                                 check_spawn_nonexistent,
01022                                 NULL))
01023     return FALSE;
01024 
01025   /* Don't run the obnoxious segfault test by default,
01026    * it's a pain to have to click all those error boxes.
01027    */
01028   if (getenv ("DO_SEGFAULT_TEST"))
01029     if (!_dbus_test_oom_handling ("spawn_segfault",
01030                                   check_spawn_segfault,
01031                                   NULL))
01032       return FALSE;
01033 
01034   if (!_dbus_test_oom_handling ("spawn_exit",
01035                                 check_spawn_exit,
01036                                 NULL))
01037     return FALSE;
01038 
01039   if (!_dbus_test_oom_handling ("spawn_and_kill",
01040                                 check_spawn_and_kill,
01041                                 NULL))
01042     return FALSE;
01043 
01044   return TRUE;
01045 }
01046 #endif