D-Bus  1.6.8
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_BUILD_TESTS
00067 
00068     HANDLE end_sync_event;
00069 #endif
00070 
00071     char *executable;
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_BUILD_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_BUILD_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->executable);
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->executable, 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->executable, 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->executable);
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->executable);
00597 
00598   PING();
00599   sitter->child_handle = spawn_program (sitter->executable,
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_BUILD_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                                    char                     **argv,
00646                                    char                     **envp,
00647                                    DBusSpawnChildSetupFunc    child_setup,
00648                                    void                      *user_data,
00649                                    DBusError                 *error)
00650 {
00651   DBusBabysitter *sitter;
00652   HANDLE sitter_thread;
00653   DWORD sitter_thread_id;
00654   
00655   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00656 
00657   *sitter_p = NULL;
00658 
00659   PING();
00660   sitter = _dbus_babysitter_new ();
00661   if (sitter == NULL)
00662     {
00663       _DBUS_SET_OOM (error);
00664       return FALSE;
00665     }
00666 
00667   sitter->child_setup = child_setup;
00668   sitter->user_data = user_data;
00669 
00670   sitter->executable = _dbus_strdup (argv[0]);
00671   if (sitter->executable == NULL)
00672     {
00673       _DBUS_SET_OOM (error);
00674       goto out0;
00675     }
00676 
00677   PING();
00678   if (!_dbus_full_duplex_pipe (&sitter->socket_to_babysitter,
00679                                &sitter->socket_to_main,
00680                                FALSE, error))
00681     goto out0;
00682 
00683   sitter->sitter_watch = _dbus_watch_new (sitter->socket_to_babysitter,
00684                                           DBUS_WATCH_READABLE,
00685                                           TRUE, handle_watch, sitter, NULL);
00686   PING();
00687   if (sitter->sitter_watch == NULL)
00688     {
00689       _DBUS_SET_OOM (error);
00690       goto out0;
00691     }
00692 
00693   PING();
00694   if (!_dbus_watch_list_add_watch (sitter->watches,  sitter->sitter_watch))
00695     {
00696       /* we need to free it early so the destructor won't try to remove it
00697        * without it having been added, which DBusLoop doesn't allow */
00698       _dbus_watch_invalidate (sitter->sitter_watch);
00699       _dbus_watch_unref (sitter->sitter_watch);
00700       sitter->sitter_watch = NULL;
00701 
00702       _DBUS_SET_OOM (error);
00703       goto out0;
00704     }
00705 
00706   sitter->argc = protect_argv (argv, &sitter->argv);
00707   if (sitter->argc == -1)
00708     {
00709       _DBUS_SET_OOM (error);
00710       goto out0;
00711     }
00712   sitter->envp = envp;
00713 
00714   PING();
00715   sitter_thread = (HANDLE) CreateThread (NULL, 0, babysitter,
00716                   sitter, 0, &sitter_thread_id);
00717 
00718   if (sitter_thread == 0)
00719     {
00720       PING();
00721       dbus_set_error_const (error, DBUS_ERROR_SPAWN_FORK_FAILED,
00722                             "Failed to create new thread");
00723       goto out0;
00724     }
00725   CloseHandle (sitter_thread);
00726 
00727   PING();
00728   WaitForSingleObject (sitter->start_sync_event, INFINITE);
00729 
00730   PING();
00731   if (sitter_p != NULL)
00732     *sitter_p = sitter;
00733   else
00734     _dbus_babysitter_unref (sitter);
00735 
00736   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00737 
00738   PING();
00739   return TRUE;
00740 
00741 out0:
00742   _dbus_babysitter_unref (sitter);
00743 
00744   return FALSE;
00745 }
00746 
00747 void
00748 _dbus_babysitter_set_result_function  (DBusBabysitter             *sitter,
00749                                        DBusBabysitterFinishedFunc  finished,
00750                                        void                       *user_data)
00751 {
00752   sitter->finished_cb = finished;
00753   sitter->finished_data = user_data;
00754 }
00755 
00756 #ifdef DBUS_BUILD_TESTS
00757 
00758 static char *
00759 get_test_exec (const char *exe,
00760                DBusString *scratch_space)
00761 {
00762   const char *dbus_test_exec;
00763 
00764   dbus_test_exec = _dbus_getenv ("DBUS_TEST_EXEC");
00765 
00766   if (dbus_test_exec == NULL)
00767     dbus_test_exec = DBUS_TEST_EXEC;
00768 
00769   if (!_dbus_string_init (scratch_space))
00770     return NULL;
00771 
00772   if (!_dbus_string_append_printf (scratch_space, "%s/%s%s",
00773                                    dbus_test_exec, exe, DBUS_EXEEXT))
00774     {
00775       _dbus_string_free (scratch_space);
00776       return NULL;
00777     }
00778 
00779   return _dbus_string_get_data (scratch_space);
00780 }
00781 
00782 #define LIVE_CHILDREN(sitter) ((sitter)->child_handle != NULL)
00783 
00784 static void
00785 _dbus_babysitter_block_for_child_exit (DBusBabysitter *sitter)
00786 {
00787   if (sitter->child_handle == NULL)
00788     return;
00789 
00790   WaitForSingleObject (sitter->end_sync_event, INFINITE);
00791 }
00792 
00793 static dbus_bool_t
00794 check_spawn_nonexistent (void *data)
00795 {
00796   char *argv[4] = { NULL, NULL, NULL, NULL };
00797   DBusBabysitter *sitter;
00798   DBusError error;
00799 
00800   sitter = NULL;
00801 
00802   dbus_error_init (&error);
00803 
00804   /*** Test launching nonexistent binary */
00805 
00806   argv[0] = "/this/does/not/exist/32542sdgafgafdg";
00807   if (_dbus_spawn_async_with_babysitter (&sitter, argv, NULL,
00808                                          NULL, NULL,
00809                                          &error))
00810     {
00811       _dbus_babysitter_block_for_child_exit (sitter);
00812       _dbus_babysitter_set_child_exit_error (sitter, &error);
00813     }
00814 
00815   if (sitter)
00816     _dbus_babysitter_unref (sitter);
00817 
00818   if (!dbus_error_is_set (&error))
00819     {
00820       _dbus_warn ("Did not get an error launching nonexistent executable\n");
00821       return FALSE;
00822     }
00823 
00824   if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
00825         dbus_error_has_name (&error, DBUS_ERROR_SPAWN_EXEC_FAILED)))
00826     {
00827       _dbus_warn ("Not expecting error when launching nonexistent executable: %s: %s\n",
00828                   error.name, error.message);
00829       dbus_error_free (&error);
00830       return FALSE;
00831     }
00832 
00833   dbus_error_free (&error);
00834 
00835   return TRUE;
00836 }
00837 
00838 static dbus_bool_t
00839 check_spawn_segfault (void *data)
00840 {
00841   char *argv[4] = { NULL, NULL, NULL, NULL };
00842   DBusBabysitter *sitter;
00843   DBusError error;
00844   DBusString argv0;
00845 
00846   sitter = NULL;
00847 
00848   dbus_error_init (&error);
00849 
00850   /*** Test launching segfault binary */
00851 
00852   argv[0] = get_test_exec ("test-segfault", &argv0);
00853 
00854   if (argv[0] == NULL)
00855     {
00856       /* OOM was simulated, never mind */
00857       return TRUE;
00858     }
00859 
00860   if (_dbus_spawn_async_with_babysitter (&sitter, argv, NULL,
00861                                          NULL, NULL,
00862                                          &error))
00863     {
00864       _dbus_babysitter_block_for_child_exit (sitter);
00865       _dbus_babysitter_set_child_exit_error (sitter, &error);
00866     }
00867 
00868   _dbus_string_free (&argv0);
00869 
00870   if (sitter)
00871     _dbus_babysitter_unref (sitter);
00872 
00873   if (!dbus_error_is_set (&error))
00874     {
00875       _dbus_warn ("Did not get an error launching segfaulting binary\n");
00876       return FALSE;
00877     }
00878 
00879   if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
00880         dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED)))
00881     {
00882       _dbus_warn ("Not expecting error when launching segfaulting executable: %s: %s\n",
00883                   error.name, error.message);
00884       dbus_error_free (&error);
00885       return FALSE;
00886     }
00887 
00888   dbus_error_free (&error);
00889 
00890   return TRUE;
00891 }
00892 
00893 static dbus_bool_t
00894 check_spawn_exit (void *data)
00895 {
00896   char *argv[4] = { NULL, NULL, NULL, NULL };
00897   DBusBabysitter *sitter;
00898   DBusError error;
00899   DBusString argv0;
00900 
00901   sitter = NULL;
00902 
00903   dbus_error_init (&error);
00904 
00905   /*** Test launching exit failure binary */
00906 
00907   argv[0] = get_test_exec ("test-exit", &argv0);
00908 
00909   if (argv[0] == NULL)
00910     {
00911       /* OOM was simulated, never mind */
00912       return TRUE;
00913     }
00914 
00915   if (_dbus_spawn_async_with_babysitter (&sitter, argv, NULL,
00916                                          NULL, NULL,
00917                                          &error))
00918     {
00919       _dbus_babysitter_block_for_child_exit (sitter);
00920       _dbus_babysitter_set_child_exit_error (sitter, &error);
00921     }
00922 
00923   _dbus_string_free (&argv0);
00924 
00925   if (sitter)
00926     _dbus_babysitter_unref (sitter);
00927 
00928   if (!dbus_error_is_set (&error))
00929     {
00930       _dbus_warn ("Did not get an error launching binary that exited with failure code\n");
00931       return FALSE;
00932     }
00933 
00934   if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
00935         dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED)))
00936     {
00937       _dbus_warn ("Not expecting error when launching exiting executable: %s: %s\n",
00938                   error.name, error.message);
00939       dbus_error_free (&error);
00940       return FALSE;
00941     }
00942 
00943   dbus_error_free (&error);
00944 
00945   return TRUE;
00946 }
00947 
00948 static dbus_bool_t
00949 check_spawn_and_kill (void *data)
00950 {
00951   char *argv[4] = { NULL, NULL, NULL, NULL };
00952   DBusBabysitter *sitter;
00953   DBusError error;
00954   DBusString argv0;
00955 
00956   sitter = NULL;
00957 
00958   dbus_error_init (&error);
00959 
00960   /*** Test launching sleeping binary then killing it */
00961 
00962   argv[0] = get_test_exec ("test-sleep-forever", &argv0);
00963 
00964   if (argv[0] == NULL)
00965     {
00966       /* OOM was simulated, never mind */
00967       return TRUE;
00968     }
00969 
00970   if (_dbus_spawn_async_with_babysitter (&sitter, argv, NULL,
00971                                          NULL, NULL,
00972                                          &error))
00973     {
00974       _dbus_babysitter_kill_child (sitter);
00975 
00976       _dbus_babysitter_block_for_child_exit (sitter);
00977 
00978       _dbus_babysitter_set_child_exit_error (sitter, &error);
00979     }
00980 
00981   _dbus_string_free (&argv0);
00982 
00983   if (sitter)
00984     _dbus_babysitter_unref (sitter);
00985 
00986   if (!dbus_error_is_set (&error))
00987     {
00988       _dbus_warn ("Did not get an error after killing spawned binary\n");
00989       return FALSE;
00990     }
00991 
00992   if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
00993         dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED)))
00994     {
00995       _dbus_warn ("Not expecting error when killing executable: %s: %s\n",
00996                   error.name, error.message);
00997       dbus_error_free (&error);
00998       return FALSE;
00999     }
01000 
01001   dbus_error_free (&error);
01002 
01003   return TRUE;
01004 }
01005 
01006 dbus_bool_t
01007 _dbus_spawn_test (const char *test_data_dir)
01008 {
01009   if (!_dbus_test_oom_handling ("spawn_nonexistent",
01010                                 check_spawn_nonexistent,
01011                                 NULL))
01012     return FALSE;
01013 
01014   /* Don't run the obnoxious segfault test by default,
01015    * it's a pain to have to click all those error boxes.
01016    */
01017   if (getenv ("DO_SEGFAULT_TEST"))
01018     if (!_dbus_test_oom_handling ("spawn_segfault",
01019                                   check_spawn_segfault,
01020                                   NULL))
01021       return FALSE;
01022 
01023   if (!_dbus_test_oom_handling ("spawn_exit",
01024                                 check_spawn_exit,
01025                                 NULL))
01026     return FALSE;
01027 
01028   if (!_dbus_test_oom_handling ("spawn_and_kill",
01029                                 check_spawn_and_kill,
01030                                 NULL))
01031     return FALSE;
01032 
01033   return TRUE;
01034 }
01035 #endif