D-Bus 1.4.0

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 //#define STRICT
00046 //#include <windows.h>
00047 //#undef STRICT
00048 #include <winsock2.h>
00049 #undef interface
00050 
00051 #include <stdlib.h>
00052 
00053 #ifndef DBUS_WINCE
00054 #include <process.h>
00055 #endif
00056 
00060 struct DBusBabysitter
00061   {
00062     int refcount;
00063 
00064     HANDLE start_sync_event;
00065 #ifdef DBUS_BUILD_TESTS
00066 
00067     HANDLE end_sync_event;
00068 #endif
00069 
00070     char *executable;
00071     DBusSpawnChildSetupFunc child_setup;
00072     void *user_data;
00073 
00074     int argc;
00075     char **argv;
00076     char **envp;
00077 
00078     HANDLE child_handle;
00079     int socket_to_babysitter;   /* Connection to the babysitter thread */
00080     int socket_to_main;
00081 
00082     DBusWatchList *watches;
00083     DBusWatch *sitter_watch;
00084 
00085     dbus_bool_t have_spawn_errno;
00086     int spawn_errno;
00087     dbus_bool_t have_child_status;
00088     int child_status;
00089   };
00090 
00091 static DBusBabysitter*
00092 _dbus_babysitter_new (void)
00093 {
00094   DBusBabysitter *sitter;
00095 
00096   sitter = dbus_new0 (DBusBabysitter, 1);
00097   if (sitter == NULL)
00098     return NULL;
00099 
00100   sitter->refcount = 1;
00101 
00102   sitter->start_sync_event = CreateEvent (NULL, FALSE, FALSE, NULL);
00103   if (sitter->start_sync_event == NULL)
00104     {
00105       _dbus_babysitter_unref (sitter);
00106       return NULL;
00107     }
00108 
00109 #ifdef DBUS_BUILD_TESTS
00110   sitter->end_sync_event = CreateEvent (NULL, FALSE, FALSE, NULL);
00111   if (sitter->end_sync_event == NULL)
00112     {
00113       _dbus_babysitter_unref (sitter);
00114       return NULL;
00115     }
00116 #endif
00117 
00118   sitter->child_handle = NULL;
00119 
00120   sitter->socket_to_babysitter = sitter->socket_to_main = -1;
00121 
00122   sitter->argc = 0;
00123   sitter->argv = NULL;
00124   sitter->envp = NULL;
00125 
00126   sitter->watches = _dbus_watch_list_new ();
00127   if (sitter->watches == NULL)
00128     {
00129       _dbus_babysitter_unref (sitter);
00130       return NULL;
00131     }
00132 
00133   sitter->have_spawn_errno = FALSE;
00134   sitter->have_child_status = FALSE;
00135 
00136   return sitter;
00137 }
00138 
00145 DBusBabysitter *
00146 _dbus_babysitter_ref (DBusBabysitter *sitter)
00147 {
00148   PING();
00149   _dbus_assert (sitter != NULL);
00150   _dbus_assert (sitter->refcount > 0);
00151 
00152   sitter->refcount += 1;
00153 
00154   return sitter;
00155 }
00156 
00162 void
00163 _dbus_babysitter_unref (DBusBabysitter *sitter)
00164 {
00165   int i;
00166 
00167   PING();
00168   _dbus_assert (sitter != NULL);
00169   _dbus_assert (sitter->refcount > 0);
00170 
00171   sitter->refcount -= 1;
00172 
00173   if (sitter->refcount == 0)
00174     {
00175       if (sitter->socket_to_babysitter != -1)
00176         {
00177           _dbus_close_socket (sitter->socket_to_babysitter, NULL);
00178           sitter->socket_to_babysitter = -1;
00179         }
00180 
00181       if (sitter->socket_to_main != -1)
00182         {
00183           _dbus_close_socket (sitter->socket_to_main, NULL);
00184           sitter->socket_to_main = -1;
00185         }
00186 
00187       PING();
00188       if (sitter->argv != NULL)
00189         {
00190           for (i = 0; i < sitter->argc; i++)
00191             if (sitter->argv[i] != NULL)
00192               {
00193                 dbus_free (sitter->argv[i]);
00194                 sitter->argv[i] = NULL;
00195               }
00196           dbus_free (sitter->argv);
00197           sitter->argv = NULL;
00198         }
00199 
00200       if (sitter->envp != NULL)
00201         {
00202           char **e = sitter->envp;
00203 
00204           while (*e)
00205             dbus_free (*e++);
00206           dbus_free (sitter->envp);
00207           sitter->envp = NULL;
00208         }
00209 
00210       if (sitter->child_handle != NULL)
00211         {
00212           CloseHandle (sitter->child_handle);
00213           sitter->child_handle = NULL;
00214         }
00215 
00216       if (sitter->sitter_watch)
00217         {
00218           _dbus_watch_invalidate (sitter->sitter_watch);
00219           _dbus_watch_unref (sitter->sitter_watch);
00220           sitter->sitter_watch = NULL;
00221         }
00222 
00223       if (sitter->watches)
00224         _dbus_watch_list_free (sitter->watches);
00225 
00226       if (sitter->start_sync_event != NULL)
00227         {
00228           PING();
00229           CloseHandle (sitter->start_sync_event);
00230           sitter->start_sync_event = NULL;
00231         }
00232 
00233 #ifdef DBUS_BUILD_TESTS
00234       if (sitter->end_sync_event != NULL)
00235         {
00236           CloseHandle (sitter->end_sync_event);
00237           sitter->end_sync_event = NULL;
00238         }
00239 #endif
00240 
00241       dbus_free (sitter->executable);
00242 
00243       dbus_free (sitter);
00244     }
00245 }
00246 
00247 void
00248 _dbus_babysitter_kill_child (DBusBabysitter *sitter)
00249 {
00250   PING();
00251   if (sitter->child_handle == NULL)
00252     return; /* child is already dead, or we're so hosed we'll never recover */
00253 
00254   PING();
00255   TerminateProcess (sitter->child_handle, 12345);
00256 }
00257 
00263 dbus_bool_t
00264 _dbus_babysitter_get_child_exited (DBusBabysitter *sitter)
00265 {
00266   PING();
00267   return (sitter->child_handle == NULL);
00268 }
00269 
00282 dbus_bool_t
00283 _dbus_babysitter_get_child_exit_status (DBusBabysitter *sitter,
00284                                         int            *status)
00285 {
00286   if (!_dbus_babysitter_get_child_exited (sitter))
00287     _dbus_assert_not_reached ("Child has not exited");
00288 
00289   if (!sitter->have_child_status ||
00290       sitter->child_status == STILL_ACTIVE)
00291     return FALSE;
00292 
00293   *status = sitter->child_status;
00294   return TRUE;
00295 }
00296 
00306 void
00307 _dbus_babysitter_set_child_exit_error (DBusBabysitter *sitter,
00308                                        DBusError      *error)
00309 {
00310   PING();
00311   if (!_dbus_babysitter_get_child_exited (sitter))
00312     return;
00313 
00314   PING();
00315   if (sitter->have_spawn_errno)
00316     {
00317       char *emsg = _dbus_win_error_string (sitter->spawn_errno);
00318       dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED,
00319                       "Failed to execute program %s: %s",
00320                       sitter->executable, emsg);
00321       _dbus_win_free_error_string (emsg);
00322     }
00323   else if (sitter->have_child_status)
00324     {
00325       PING();
00326       dbus_set_error (error, DBUS_ERROR_SPAWN_CHILD_EXITED,
00327                       "Process %s exited with status %d",
00328                       sitter->executable, sitter->child_status);
00329     }
00330   else
00331     {
00332       PING();
00333       dbus_set_error (error, DBUS_ERROR_FAILED,
00334                       "Process %s exited, status unknown",
00335                       sitter->executable);
00336     }
00337   PING();
00338 }
00339 
00340 dbus_bool_t
00341 _dbus_babysitter_set_watch_functions (DBusBabysitter            *sitter,
00342                                       DBusAddWatchFunction       add_function,
00343                                       DBusRemoveWatchFunction    remove_function,
00344                                       DBusWatchToggledFunction   toggled_function,
00345                                       void                      *data,
00346                                       DBusFreeFunction           free_data_function)
00347 {
00348   PING();
00349   return _dbus_watch_list_set_functions (sitter->watches,
00350                                          add_function,
00351                                          remove_function,
00352                                          toggled_function,
00353                                          data,
00354                                          free_data_function);
00355 }
00356 
00357 static dbus_bool_t
00358 handle_watch (DBusWatch       *watch,
00359               unsigned int     condition,
00360               void            *data)
00361 {
00362   DBusBabysitter *sitter = data;
00363 
00364   /* On Unix dbus-spawn uses a babysitter *process*, thus it has to
00365    * actually send the exit statuses, error codes and whatnot through
00366    * sockets and/or pipes. On Win32, the babysitter is jus a thread,
00367    * so it can set the status fields directly in the babysitter struct
00368    * just fine. The socket pipe is used just so we can watch it with
00369    * select(), as soon as anything is written to it we know that the
00370    * babysitter thread has recorded the status in the babysitter
00371    * struct.
00372    */
00373 
00374   PING();
00375   _dbus_close_socket (sitter->socket_to_babysitter, NULL);
00376   PING();
00377   sitter->socket_to_babysitter = -1;
00378 
00379   return TRUE;
00380 }
00381 
00382 /* protect_argv lifted from GLib, relicensed by author, Tor Lillqvist */
00383 static int
00384 protect_argv (char  **argv,
00385               char ***new_argv)
00386 {
00387   int i;
00388   int argc = 0;
00389 
00390   while (argv[argc])
00391     ++argc;
00392   *new_argv = dbus_malloc ((argc + 1) * sizeof (char *));
00393   if (*new_argv == NULL)
00394     return -1;
00395 
00396   for (i = 0; i < argc; i++)
00397     (*new_argv)[i] = NULL;
00398 
00399   /* Quote each argv element if necessary, so that it will get
00400    * reconstructed correctly in the C runtime startup code.  Note that
00401    * the unquoting algorithm in the C runtime is really weird, and
00402    * rather different than what Unix shells do. See stdargv.c in the C
00403    * runtime sources (in the Platform SDK, in src/crt).
00404    *
00405    * Note that an new_argv[0] constructed by this function should
00406    * *not* be passed as the filename argument to a spawn* or exec*
00407    * family function. That argument should be the real file name
00408    * without any quoting.
00409    */
00410   for (i = 0; i < argc; i++)
00411     {
00412       char *p = argv[i];
00413       char *q;
00414       int len = 0;
00415       int need_dblquotes = FALSE;
00416       while (*p)
00417         {
00418           if (*p == ' ' || *p == '\t')
00419             need_dblquotes = TRUE;
00420           else if (*p == '"')
00421             len++;
00422           else if (*p == '\\')
00423             {
00424               char *pp = p;
00425               while (*pp && *pp == '\\')
00426                 pp++;
00427               if (*pp == '"')
00428                 len++;
00429             }
00430           len++;
00431           p++;
00432         }
00433 
00434       q = (*new_argv)[i] = dbus_malloc (len + need_dblquotes*2 + 1);
00435 
00436       if (q == NULL)
00437         return -1;
00438 
00439 
00440       p = argv[i];
00441 
00442       if (need_dblquotes)
00443         *q++ = '"';
00444 
00445       while (*p)
00446         {
00447           if (*p == '"')
00448             *q++ = '\\';
00449           else if (*p == '\\')
00450             {
00451               char *pp = p;
00452               while (*pp && *pp == '\\')
00453                 pp++;
00454               if (*pp == '"')
00455                 *q++ = '\\';
00456             }
00457           *q++ = *p;
00458           p++;
00459         }
00460 
00461       if (need_dblquotes)
00462         *q++ = '"';
00463       *q++ = '\0';
00464       /* printf ("argv[%d]:%s, need_dblquotes:%s len:%d => %s\n", i, argv[i], need_dblquotes?"TRUE":"FALSE", len, (*new_argv)[i]); */
00465     }
00466   (*new_argv)[argc] = NULL;
00467 
00468   return argc;
00469 }
00470 
00471 
00472 /* From GPGME, relicensed by g10 Code GmbH.  */
00473 static char *
00474 compose_string (char **strings, char separator)
00475 {
00476   int i;
00477   int n = 0;
00478   char *buf;
00479   char *p;
00480   const char *ptr;
00481   
00482   if (!strings || !strings[0])
00483     return 0;
00484   for (i = 0; strings[i]; i++)
00485     n += strlen (strings[i]) + 1;
00486   n++;
00487 
00488   buf = p = malloc (n);
00489   if (!buf)
00490     return NULL;
00491   for (i = 0; strings[i]; i++)
00492     {
00493       strcpy (p, strings[i]);
00494       p += strlen (strings[i]);
00495       *(p++) = separator;
00496     }
00497   p--;
00498   *(p++) = '\0';
00499   *p = '\0';
00500 
00501   return buf;
00502 }
00503 
00504 static char *
00505 build_commandline (char **argv)
00506 {
00507   return compose_string (argv, ' ');
00508 }
00509 
00510 static char *
00511 build_env_string (char** envp)
00512 {
00513   return compose_string (envp, '\0');
00514 }
00515 
00516 static HANDLE
00517 spawn_program (char* name, char** argv, char** envp)
00518 {
00519   PROCESS_INFORMATION pi = { NULL, 0, 0, 0 };
00520   STARTUPINFOA si;
00521   char *arg_string, *env_string;
00522   BOOL result;
00523 
00524 #ifdef DBUS_WINCE
00525   if (argv && argv[0])
00526     arg_string = build_commandline (argv + 1);
00527   else
00528     arg_string = NULL;
00529 #else
00530   arg_string = build_commandline (argv);
00531 #endif
00532   if (!arg_string)
00533     return INVALID_HANDLE_VALUE;
00534 
00535   env_string = build_env_string(envp);
00536 
00537   memset (&si, 0, sizeof (si));
00538   si.cb = sizeof (si);
00539 #ifdef DBUS_WINCE
00540   result = CreateProcessA (name, arg_string, NULL, NULL, FALSE, 0,
00541 #else
00542   result = CreateProcessA (NULL, arg_string, NULL, NULL, FALSE, 0,
00543 #endif
00544                            (LPVOID)env_string, NULL, &si, &pi);
00545   free (arg_string);
00546   if (env_string)
00547     free (env_string);
00548 
00549   if (!result)
00550     return INVALID_HANDLE_VALUE;
00551 
00552   CloseHandle (pi.hThread);
00553   return pi.hProcess;
00554 }
00555 
00556 
00557 static DWORD __stdcall
00558 babysitter (void *parameter)
00559 {
00560   DBusBabysitter *sitter = (DBusBabysitter *) parameter;
00561   int fd;
00562   PING();
00563   _dbus_babysitter_ref (sitter);
00564 
00565   if (sitter->child_setup)
00566     {
00567       PING();
00568       (*sitter->child_setup) (sitter->user_data);
00569     }
00570 
00571   _dbus_verbose ("babysitter: spawning %s\n", sitter->executable);
00572 
00573   PING();
00574   sitter->child_handle = spawn_program (sitter->executable,
00575                                         sitter->argv, sitter->envp);
00576 
00577   PING();
00578   if (sitter->child_handle == (HANDLE) -1)
00579     {
00580       sitter->child_handle = NULL;
00581       sitter->have_spawn_errno = TRUE;
00582       sitter->spawn_errno = GetLastError();
00583     }
00584   
00585   PING();
00586   SetEvent (sitter->start_sync_event);
00587 
00588   if (sitter->child_handle != NULL)
00589     {
00590       int ret;
00591       DWORD status;
00592 
00593       PING();
00594       WaitForSingleObject (sitter->child_handle, INFINITE);
00595 
00596       PING();
00597       ret = GetExitCodeProcess (sitter->child_handle, &status);
00598 
00599       sitter->child_status = status;
00600       sitter->have_child_status = TRUE;
00601 
00602       CloseHandle (sitter->child_handle);
00603       sitter->child_handle = NULL;
00604     }
00605 
00606 #ifdef DBUS_BUILD_TESTS
00607   SetEvent (sitter->end_sync_event);
00608 #endif
00609 
00610   PING();
00611   send (sitter->socket_to_main, " ", 1, 0);
00612 
00613   _dbus_babysitter_unref (sitter);
00614 
00615   return 0;
00616 }
00617 
00618 dbus_bool_t
00619 _dbus_spawn_async_with_babysitter (DBusBabysitter           **sitter_p,
00620                                    char                     **argv,
00621                                    char                     **envp,
00622                                    DBusSpawnChildSetupFunc    child_setup,
00623                                    void                      *user_data,
00624                                    DBusError                 *error)
00625 {
00626   DBusBabysitter *sitter;
00627   HANDLE sitter_thread;
00628   DWORD sitter_thread_id;
00629   
00630   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00631 
00632   *sitter_p = NULL;
00633 
00634   PING();
00635   sitter = _dbus_babysitter_new ();
00636   if (sitter == NULL)
00637     {
00638       _DBUS_SET_OOM (error);
00639       return FALSE;
00640     }
00641 
00642   sitter->child_setup = child_setup;
00643   sitter->user_data = user_data;
00644 
00645   sitter->executable = _dbus_strdup (argv[0]);
00646   if (sitter->executable == NULL)
00647     {
00648       _DBUS_SET_OOM (error);
00649       goto out0;
00650     }
00651 
00652   PING();
00653   if (!_dbus_full_duplex_pipe (&sitter->socket_to_babysitter,
00654                                &sitter->socket_to_main,
00655                                FALSE, error))
00656     goto out0;
00657 
00658   sitter->sitter_watch = _dbus_watch_new (sitter->socket_to_babysitter,
00659                                           DBUS_WATCH_READABLE,
00660                                           TRUE, handle_watch, sitter, NULL);
00661   PING();
00662   if (sitter->sitter_watch == NULL)
00663     {
00664       _DBUS_SET_OOM (error);
00665       goto out0;
00666     }
00667 
00668   PING();
00669   if (!_dbus_watch_list_add_watch (sitter->watches,  sitter->sitter_watch))
00670     {
00671       _DBUS_SET_OOM (error);
00672       goto out0;
00673     }
00674 
00675   sitter->argc = protect_argv (argv, &sitter->argv);
00676   if (sitter->argc == -1)
00677     {
00678       _DBUS_SET_OOM (error);
00679       goto out0;
00680     }
00681   sitter->envp = envp;
00682 
00683   PING();
00684   sitter_thread = (HANDLE) CreateThread (NULL, 0, babysitter,
00685                   sitter, 0, &sitter_thread_id);
00686 
00687   if (sitter_thread == 0)
00688     {
00689       PING();
00690       dbus_set_error_const (error, DBUS_ERROR_SPAWN_FORK_FAILED,
00691                             "Failed to create new thread");
00692       goto out0;
00693     }
00694   CloseHandle (sitter_thread);
00695 
00696   PING();
00697   WaitForSingleObject (sitter->start_sync_event, INFINITE);
00698 
00699   PING();
00700   if (sitter_p != NULL)
00701     *sitter_p = sitter;
00702   else
00703     _dbus_babysitter_unref (sitter);
00704 
00705   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
00706 
00707   PING();
00708   return TRUE;
00709 
00710 out0:
00711   _dbus_babysitter_unref (sitter);
00712 
00713   return FALSE;
00714 }
00715 
00716 #ifdef DBUS_BUILD_TESTS
00717 
00718 #define LIVE_CHILDREN(sitter) ((sitter)->child_handle != NULL)
00719 
00720 static void
00721 _dbus_babysitter_block_for_child_exit (DBusBabysitter *sitter)
00722 {
00723   if (sitter->child_handle == NULL)
00724     return;
00725 
00726   WaitForSingleObject (sitter->end_sync_event, INFINITE);
00727 }
00728 
00729 static dbus_bool_t
00730 check_spawn_nonexistent (void *data)
00731 {
00732   char *argv[4] = { NULL, NULL, NULL, NULL };
00733   DBusBabysitter *sitter;
00734   DBusError error;
00735 
00736   sitter = NULL;
00737 
00738   dbus_error_init (&error);
00739 
00740   /*** Test launching nonexistent binary */
00741 
00742   argv[0] = "/this/does/not/exist/32542sdgafgafdg";
00743   if (_dbus_spawn_async_with_babysitter (&sitter, argv, NULL,
00744                                          NULL, NULL,
00745                                          &error))
00746     {
00747       _dbus_babysitter_block_for_child_exit (sitter);
00748       _dbus_babysitter_set_child_exit_error (sitter, &error);
00749     }
00750 
00751   if (sitter)
00752     _dbus_babysitter_unref (sitter);
00753 
00754   if (!dbus_error_is_set (&error))
00755     {
00756       _dbus_warn ("Did not get an error launching nonexistent executable\n");
00757       return FALSE;
00758     }
00759 
00760   if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
00761         dbus_error_has_name (&error, DBUS_ERROR_SPAWN_EXEC_FAILED)))
00762     {
00763       _dbus_warn ("Not expecting error when launching nonexistent executable: %s: %s\n",
00764                   error.name, error.message);
00765       dbus_error_free (&error);
00766       return FALSE;
00767     }
00768 
00769   dbus_error_free (&error);
00770 
00771   return TRUE;
00772 }
00773 
00774 static dbus_bool_t
00775 check_spawn_segfault (void *data)
00776 {
00777   char *argv[4] = { NULL, NULL, NULL, NULL };
00778   DBusBabysitter *sitter;
00779   DBusError error;
00780 
00781   sitter = NULL;
00782 
00783   dbus_error_init (&error);
00784 
00785   /*** Test launching segfault binary */
00786 
00787   argv[0] = TEST_SEGFAULT_BINARY;
00788   if (_dbus_spawn_async_with_babysitter (&sitter, argv, NULL,
00789                                          NULL, NULL,
00790                                          &error))
00791     {
00792       _dbus_babysitter_block_for_child_exit (sitter);
00793       _dbus_babysitter_set_child_exit_error (sitter, &error);
00794     }
00795 
00796   if (sitter)
00797     _dbus_babysitter_unref (sitter);
00798 
00799   if (!dbus_error_is_set (&error))
00800     {
00801       _dbus_warn ("Did not get an error launching segfaulting binary\n");
00802       return FALSE;
00803     }
00804 
00805   if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
00806         dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED)))
00807     {
00808       _dbus_warn ("Not expecting error when launching segfaulting executable: %s: %s\n",
00809                   error.name, error.message);
00810       dbus_error_free (&error);
00811       return FALSE;
00812     }
00813 
00814   dbus_error_free (&error);
00815 
00816   return TRUE;
00817 }
00818 
00819 static dbus_bool_t
00820 check_spawn_exit (void *data)
00821 {
00822   char *argv[4] = { NULL, NULL, NULL, NULL };
00823   DBusBabysitter *sitter;
00824   DBusError error;
00825 
00826   sitter = NULL;
00827 
00828   dbus_error_init (&error);
00829 
00830   /*** Test launching exit failure binary */
00831 
00832   argv[0] = TEST_EXIT_BINARY;
00833   if (_dbus_spawn_async_with_babysitter (&sitter, argv, NULL,
00834                                          NULL, NULL,
00835                                          &error))
00836     {
00837       _dbus_babysitter_block_for_child_exit (sitter);
00838       _dbus_babysitter_set_child_exit_error (sitter, &error);
00839     }
00840 
00841   if (sitter)
00842     _dbus_babysitter_unref (sitter);
00843 
00844   if (!dbus_error_is_set (&error))
00845     {
00846       _dbus_warn ("Did not get an error launching binary that exited with failure code\n");
00847       return FALSE;
00848     }
00849 
00850   if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
00851         dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED)))
00852     {
00853       _dbus_warn ("Not expecting error when launching exiting executable: %s: %s\n",
00854                   error.name, error.message);
00855       dbus_error_free (&error);
00856       return FALSE;
00857     }
00858 
00859   dbus_error_free (&error);
00860 
00861   return TRUE;
00862 }
00863 
00864 static dbus_bool_t
00865 check_spawn_and_kill (void *data)
00866 {
00867   char *argv[4] = { NULL, NULL, NULL, NULL };
00868   DBusBabysitter *sitter;
00869   DBusError error;
00870 
00871   sitter = NULL;
00872 
00873   dbus_error_init (&error);
00874 
00875   /*** Test launching sleeping binary then killing it */
00876 
00877   argv[0] = TEST_SLEEP_FOREVER_BINARY;
00878   if (_dbus_spawn_async_with_babysitter (&sitter, argv, NULL,
00879                                          NULL, NULL,
00880                                          &error))
00881     {
00882       _dbus_babysitter_kill_child (sitter);
00883 
00884       _dbus_babysitter_block_for_child_exit (sitter);
00885 
00886       _dbus_babysitter_set_child_exit_error (sitter, &error);
00887     }
00888 
00889   if (sitter)
00890     _dbus_babysitter_unref (sitter);
00891 
00892   if (!dbus_error_is_set (&error))
00893     {
00894       _dbus_warn ("Did not get an error after killing spawned binary\n");
00895       return FALSE;
00896     }
00897 
00898   if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
00899         dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED)))
00900     {
00901       _dbus_warn ("Not expecting error when killing executable: %s: %s\n",
00902                   error.name, error.message);
00903       dbus_error_free (&error);
00904       return FALSE;
00905     }
00906 
00907   dbus_error_free (&error);
00908 
00909   return TRUE;
00910 }
00911 
00912 dbus_bool_t
00913 _dbus_spawn_test (const char *test_data_dir)
00914 {
00915   if (!_dbus_test_oom_handling ("spawn_nonexistent",
00916                                 check_spawn_nonexistent,
00917                                 NULL))
00918     return FALSE;
00919 
00920   /* Don't run the obnoxious segfault test by default,
00921    * it's a pain to have to click all those error boxes.
00922    */
00923   if (getenv ("DO_SEGFAULT_TEST"))
00924     if (!_dbus_test_oom_handling ("spawn_segfault",
00925                                   check_spawn_segfault,
00926                                   NULL))
00927       return FALSE;
00928 
00929   if (!_dbus_test_oom_handling ("spawn_exit",
00930                                 check_spawn_exit,
00931                                 NULL))
00932     return FALSE;
00933 
00934   if (!_dbus_test_oom_handling ("spawn_and_kill",
00935                                 check_spawn_and_kill,
00936                                 NULL))
00937     return FALSE;
00938 
00939   return TRUE;
00940 }
00941 #endif