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