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