windows fix: use install root as base path for relative pathes in dbus service file
[platform/upstream/dbus.git] / dbus / dbus-spawn-win.c
1 #include <config.h>
2
3 //#define SPAWN_DEBUG
4
5 #if !defined(SPAWN_DEBUG) || defined(_MSC_VER)
6 #define PING()
7 #else
8 #define PING() fprintf (stderr, "%s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__); fflush (stderr)
9 #endif
10
11 #include <stdio.h>
12
13 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
14 /* dbus-spawn-win32.c Wrapper around g_spawn
15  * 
16  * Copyright (C) 2002, 2003, 2004  Red Hat, Inc.
17  * Copyright (C) 2003 CodeFactory AB
18  * Copyright (C) 2005 Novell, Inc.
19  *
20  * Licensed under the Academic Free License version 2.1
21  * 
22  * This program is free software; you can redistribute it and/or modify
23  * it under the terms of the GNU General Public License as published by
24  * the Free Software Foundation; either version 2 of the License, or
25  * (at your option) any later version.
26  *
27  * This program is distributed in the hope that it will be useful,
28  * but WITHOUT ANY WARRANTY; without even the implied warranty of
29  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
30  * GNU General Public License for more details.
31  * 
32  * You should have received a copy of the GNU General Public License
33  * along with this program; if not, write to the Free Software
34  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
35  *
36  */
37 #include "dbus-spawn.h"
38 #include "dbus-sysdeps.h"
39 #include "dbus-sysdeps-win.h"
40 #include "dbus-internals.h"
41 #include "dbus-test.h"
42 #include "dbus-protocol.h"
43
44 #define WIN32_LEAN_AND_MEAN
45 //#define STRICT
46 //#include <windows.h>
47 //#undef STRICT
48 #include <winsock2.h>
49 #undef interface
50
51 #include <stdlib.h>
52
53 #ifndef DBUS_WINCE
54 #include <process.h>
55 #endif
56
57 /**
58  * Babysitter implementation details
59  */
60 struct DBusBabysitter
61   {
62     int refcount;
63
64     HANDLE start_sync_event;
65 #ifdef DBUS_BUILD_TESTS
66
67     HANDLE end_sync_event;
68 #endif
69
70     char *executable;
71     DBusSpawnChildSetupFunc child_setup;
72     void *user_data;
73
74     int argc;
75     char **argv;
76     char **envp;
77
78     HANDLE child_handle;
79     int socket_to_babysitter;   /* Connection to the babysitter thread */
80     int socket_to_main;
81
82     DBusWatchList *watches;
83     DBusWatch *sitter_watch;
84     DBusBabysitterFinishedFunc finished_cb;
85     void *finished_data;
86
87     dbus_bool_t have_spawn_errno;
88     int spawn_errno;
89     dbus_bool_t have_child_status;
90     int child_status;
91   };
92
93 static DBusBabysitter*
94 _dbus_babysitter_new (void)
95 {
96   DBusBabysitter *sitter;
97
98   sitter = dbus_new0 (DBusBabysitter, 1);
99   if (sitter == NULL)
100     return NULL;
101
102   sitter->refcount = 1;
103
104   sitter->start_sync_event = CreateEvent (NULL, FALSE, FALSE, NULL);
105   if (sitter->start_sync_event == NULL)
106     {
107       _dbus_babysitter_unref (sitter);
108       return NULL;
109     }
110
111 #ifdef DBUS_BUILD_TESTS
112   sitter->end_sync_event = CreateEvent (NULL, FALSE, FALSE, NULL);
113   if (sitter->end_sync_event == NULL)
114     {
115       _dbus_babysitter_unref (sitter);
116       return NULL;
117     }
118 #endif
119
120   sitter->child_handle = NULL;
121
122   sitter->socket_to_babysitter = sitter->socket_to_main = -1;
123
124   sitter->argc = 0;
125   sitter->argv = NULL;
126   sitter->envp = NULL;
127
128   sitter->watches = _dbus_watch_list_new ();
129   if (sitter->watches == NULL)
130     {
131       _dbus_babysitter_unref (sitter);
132       return NULL;
133     }
134
135   sitter->have_spawn_errno = FALSE;
136   sitter->have_child_status = FALSE;
137
138   return sitter;
139 }
140
141 /**
142  * Increment the reference count on the babysitter object.
143  *
144  * @param sitter the babysitter
145  * @returns the babysitter
146  */
147 DBusBabysitter *
148 _dbus_babysitter_ref (DBusBabysitter *sitter)
149 {
150   PING();
151   _dbus_assert (sitter != NULL);
152   _dbus_assert (sitter->refcount > 0);
153
154   sitter->refcount += 1;
155
156   return sitter;
157 }
158
159 static void
160 close_socket_to_babysitter (DBusBabysitter *sitter)
161 {
162   _dbus_verbose ("Closing babysitter\n");
163
164   if (sitter->sitter_watch != NULL)
165     {
166       _dbus_assert (sitter->watches != NULL);
167       _dbus_watch_list_remove_watch (sitter->watches,  sitter->sitter_watch);
168       _dbus_watch_invalidate (sitter->sitter_watch);
169       _dbus_watch_unref (sitter->sitter_watch);
170       sitter->sitter_watch = NULL;
171     }
172
173   if (sitter->socket_to_babysitter != -1)
174     {
175       _dbus_close_socket (sitter->socket_to_babysitter, NULL);
176       sitter->socket_to_babysitter = -1;
177     }
178 }
179
180 /**
181  * Decrement the reference count on the babysitter object.
182  *
183  * @param sitter the babysitter
184  */
185 void
186 _dbus_babysitter_unref (DBusBabysitter *sitter)
187 {
188   int i;
189
190   PING();
191   _dbus_assert (sitter != NULL);
192   _dbus_assert (sitter->refcount > 0);
193
194   sitter->refcount -= 1;
195
196   if (sitter->refcount == 0)
197     {
198       close_socket_to_babysitter (sitter);
199
200       if (sitter->socket_to_main != -1)
201         {
202           _dbus_close_socket (sitter->socket_to_main, NULL);
203           sitter->socket_to_main = -1;
204         }
205
206       PING();
207       if (sitter->argv != NULL)
208         {
209           for (i = 0; i < sitter->argc; i++)
210             if (sitter->argv[i] != NULL)
211               {
212                 dbus_free (sitter->argv[i]);
213                 sitter->argv[i] = NULL;
214               }
215           dbus_free (sitter->argv);
216           sitter->argv = NULL;
217         }
218
219       if (sitter->envp != NULL)
220         {
221           char **e = sitter->envp;
222
223           while (*e)
224             dbus_free (*e++);
225           dbus_free (sitter->envp);
226           sitter->envp = NULL;
227         }
228
229       if (sitter->child_handle != NULL)
230         {
231           CloseHandle (sitter->child_handle);
232           sitter->child_handle = NULL;
233         }
234
235       if (sitter->sitter_watch)
236         {
237           _dbus_watch_invalidate (sitter->sitter_watch);
238           _dbus_watch_unref (sitter->sitter_watch);
239           sitter->sitter_watch = NULL;
240         }
241
242       if (sitter->watches)
243         _dbus_watch_list_free (sitter->watches);
244
245       if (sitter->start_sync_event != NULL)
246         {
247           PING();
248           CloseHandle (sitter->start_sync_event);
249           sitter->start_sync_event = NULL;
250         }
251
252 #ifdef DBUS_BUILD_TESTS
253       if (sitter->end_sync_event != NULL)
254         {
255           CloseHandle (sitter->end_sync_event);
256           sitter->end_sync_event = NULL;
257         }
258 #endif
259
260       dbus_free (sitter->executable);
261
262       dbus_free (sitter);
263     }
264 }
265
266 void
267 _dbus_babysitter_kill_child (DBusBabysitter *sitter)
268 {
269   PING();
270   if (sitter->child_handle == NULL)
271     return; /* child is already dead, or we're so hosed we'll never recover */
272
273   PING();
274   TerminateProcess (sitter->child_handle, 12345);
275 }
276
277 /**
278  * Checks whether the child has exited, without blocking.
279  *
280  * @param sitter the babysitter
281  */
282 dbus_bool_t
283 _dbus_babysitter_get_child_exited (DBusBabysitter *sitter)
284 {
285   PING();
286   return (sitter->child_handle == NULL);
287 }
288
289 /**
290  * Gets the exit status of the child. We do this so implementation specific
291  * detail is not cluttering up dbus, for example the system launcher code.
292  * This can only be called if the child has exited, i.e. call
293  * _dbus_babysitter_get_child_exited(). It returns FALSE if the child
294  * did not return a status code, e.g. because the child was signaled
295  * or we failed to ever launch the child in the first place.
296  *
297  * @param sitter the babysitter
298  * @param status the returned status code
299  * @returns #FALSE on failure
300  */
301 dbus_bool_t
302 _dbus_babysitter_get_child_exit_status (DBusBabysitter *sitter,
303                                         int            *status)
304 {
305   if (!_dbus_babysitter_get_child_exited (sitter))
306     _dbus_assert_not_reached ("Child has not exited");
307
308   if (!sitter->have_child_status ||
309       sitter->child_status == STILL_ACTIVE)
310     return FALSE;
311
312   *status = sitter->child_status;
313   return TRUE;
314 }
315
316 /**
317  * Sets the #DBusError with an explanation of why the spawned
318  * child process exited (on a signal, or whatever). If
319  * the child process has not exited, does nothing (error
320  * will remain unset).
321  *
322  * @param sitter the babysitter
323  * @param error an error to fill in
324  */
325 void
326 _dbus_babysitter_set_child_exit_error (DBusBabysitter *sitter,
327                                        DBusError      *error)
328 {
329   PING();
330   if (!_dbus_babysitter_get_child_exited (sitter))
331     return;
332
333   PING();
334   if (sitter->have_spawn_errno)
335     {
336       char *emsg = _dbus_win_error_string (sitter->spawn_errno);
337       dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED,
338                       "Failed to execute program %s: %s",
339                       sitter->executable, emsg);
340       _dbus_win_free_error_string (emsg);
341     }
342   else if (sitter->have_child_status)
343     {
344       PING();
345       dbus_set_error (error, DBUS_ERROR_SPAWN_CHILD_EXITED,
346                       "Process %s exited with status %d",
347                       sitter->executable, sitter->child_status);
348     }
349   else
350     {
351       PING();
352       dbus_set_error (error, DBUS_ERROR_FAILED,
353                       "Process %s exited, status unknown",
354                       sitter->executable);
355     }
356   PING();
357 }
358
359 dbus_bool_t
360 _dbus_babysitter_set_watch_functions (DBusBabysitter            *sitter,
361                                       DBusAddWatchFunction       add_function,
362                                       DBusRemoveWatchFunction    remove_function,
363                                       DBusWatchToggledFunction   toggled_function,
364                                       void                      *data,
365                                       DBusFreeFunction           free_data_function)
366 {
367   PING();
368   return _dbus_watch_list_set_functions (sitter->watches,
369                                          add_function,
370                                          remove_function,
371                                          toggled_function,
372                                          data,
373                                          free_data_function);
374 }
375
376 static dbus_bool_t
377 handle_watch (DBusWatch       *watch,
378               unsigned int     condition,
379               void            *data)
380 {
381   DBusBabysitter *sitter = data;
382
383   /* On Unix dbus-spawn uses a babysitter *process*, thus it has to
384    * actually send the exit statuses, error codes and whatnot through
385    * sockets and/or pipes. On Win32, the babysitter is jus a thread,
386    * so it can set the status fields directly in the babysitter struct
387    * just fine. The socket pipe is used just so we can watch it with
388    * select(), as soon as anything is written to it we know that the
389    * babysitter thread has recorded the status in the babysitter
390    * struct.
391    */
392
393   PING();
394   close_socket_to_babysitter (sitter);
395   PING();
396
397   if (_dbus_babysitter_get_child_exited (sitter) &&
398       sitter->finished_cb != NULL)
399     {
400       sitter->finished_cb (sitter, sitter->finished_data);
401       sitter->finished_cb = NULL;
402     }
403
404   return TRUE;
405 }
406
407 /* protect_argv lifted from GLib, relicensed by author, Tor Lillqvist */
408 static int
409 protect_argv (char  **argv,
410               char ***new_argv)
411 {
412   int i;
413   int argc = 0;
414
415   while (argv[argc])
416     ++argc;
417   *new_argv = dbus_malloc ((argc + 1) * sizeof (char *));
418   if (*new_argv == NULL)
419     return -1;
420
421   for (i = 0; i < argc; i++)
422     (*new_argv)[i] = NULL;
423
424   /* Quote each argv element if necessary, so that it will get
425    * reconstructed correctly in the C runtime startup code.  Note that
426    * the unquoting algorithm in the C runtime is really weird, and
427    * rather different than what Unix shells do. See stdargv.c in the C
428    * runtime sources (in the Platform SDK, in src/crt).
429    *
430    * Note that an new_argv[0] constructed by this function should
431    * *not* be passed as the filename argument to a spawn* or exec*
432    * family function. That argument should be the real file name
433    * without any quoting.
434    */
435   for (i = 0; i < argc; i++)
436     {
437       char *p = argv[i];
438       char *q;
439       int len = 0;
440       int need_dblquotes = FALSE;
441       while (*p)
442         {
443           if (*p == ' ' || *p == '\t')
444             need_dblquotes = TRUE;
445           else if (*p == '"')
446             len++;
447           else if (*p == '\\')
448             {
449               char *pp = p;
450               while (*pp && *pp == '\\')
451                 pp++;
452               if (*pp == '"')
453                 len++;
454             }
455           len++;
456           p++;
457         }
458
459       q = (*new_argv)[i] = dbus_malloc (len + need_dblquotes*2 + 1);
460
461       if (q == NULL)
462         return -1;
463
464
465       p = argv[i];
466
467       if (need_dblquotes)
468         *q++ = '"';
469
470       while (*p)
471         {
472           if (*p == '"')
473             *q++ = '\\';
474           else if (*p == '\\')
475             {
476               char *pp = p;
477               while (*pp && *pp == '\\')
478                 pp++;
479               if (*pp == '"')
480                 *q++ = '\\';
481             }
482           *q++ = *p;
483           p++;
484         }
485
486       if (need_dblquotes)
487         *q++ = '"';
488       *q++ = '\0';
489       /* printf ("argv[%d]:%s, need_dblquotes:%s len:%d => %s\n", i, argv[i], need_dblquotes?"TRUE":"FALSE", len, (*new_argv)[i]); */
490     }
491   (*new_argv)[argc] = NULL;
492
493   return argc;
494 }
495
496
497 /* From GPGME, relicensed by g10 Code GmbH.  */
498 static char *
499 compose_string (char **strings, char separator)
500 {
501   int i;
502   int n = 0;
503   char *buf;
504   char *p;
505
506   if (!strings || !strings[0])
507     return 0;
508   for (i = 0; strings[i]; i++)
509     n += strlen (strings[i]) + 1;
510   n++;
511
512   buf = p = malloc (n);
513   if (!buf)
514     return NULL;
515   for (i = 0; strings[i]; i++)
516     {
517       strcpy (p, strings[i]);
518       p += strlen (strings[i]);
519       *(p++) = separator;
520     }
521   p--;
522   *(p++) = '\0';
523   *p = '\0';
524
525   return buf;
526 }
527
528 static char *
529 build_commandline (char **argv)
530 {
531   return compose_string (argv, ' ');
532 }
533
534 static char *
535 build_env_string (char** envp)
536 {
537   return compose_string (envp, '\0');
538 }
539
540 static HANDLE
541 spawn_program (char* name, char** argv, char** envp)
542 {
543   PROCESS_INFORMATION pi = { NULL, 0, 0, 0 };
544   STARTUPINFOA si;
545   char *arg_string, *env_string;
546   BOOL result;
547   char exe_path[MAX_PATH];
548
549 #ifdef DBUS_WINCE
550   if (argv && argv[0])
551     arg_string = build_commandline (argv + 1);
552   else
553     arg_string = NULL;
554 #else
555   arg_string = build_commandline (argv);
556 #endif
557   if (!arg_string)
558     return INVALID_HANDLE_VALUE;
559
560   env_string = build_env_string(envp);
561
562 #ifndef DBUS_WINCE
563   // handle relative pathes
564   if (strlen(name) > 2 && name[0] != '\\' && name[0] != '/' && name[1] != ':')
565     {
566       _dbus_verbose ("babysitter: spawning %s", name);
567       char install_root[2*MAX_PATH];
568       LPSTR lpFile;
569       char *p;
570       if (!_dbus_get_install_root (install_root, sizeof(install_root)))
571         return INVALID_HANDLE_VALUE;
572
573       strcat(install_root,name);
574
575       // add exe extension, if not present
576       p = strrchr(name,'.');
577       if (!p)
578         strcat(install_root,".exe");
579
580       // convert '/' into '\\'
581       while((p = strchr(install_root,'/')) != 0)
582         *p = '\\';
583
584       // separate path from filename
585       p = strrchr(install_root,'\\');
586       // no complete path: error condition
587       if (!p)
588         return INVALID_HANDLE_VALUE;
589       *p = 0;
590       if (!SearchPathA(install_root, p+1, NULL, sizeof(exe_path), exe_path, &lpFile))
591         return INVALID_HANDLE_VALUE;
592     }
593   else
594 #endif
595     strncpy(exe_path,name,MAX_PATH);
596
597   memset (&si, 0, sizeof (si));
598   si.cb = sizeof (si);
599   result = CreateProcessA (exe_path, arg_string, NULL, NULL, FALSE, 0,
600                (LPVOID)env_string, NULL, &si, &pi);
601   free (arg_string);
602   if (env_string)
603     free (env_string);
604
605   if (!result)
606     return INVALID_HANDLE_VALUE;
607
608   CloseHandle (pi.hThread);
609   return pi.hProcess;
610 }
611
612
613 static DWORD __stdcall
614 babysitter (void *parameter)
615 {
616   DBusBabysitter *sitter = (DBusBabysitter *) parameter;
617
618   PING();
619   _dbus_babysitter_ref (sitter);
620
621   if (sitter->child_setup)
622     {
623       PING();
624       (*sitter->child_setup) (sitter->user_data);
625     }
626
627   _dbus_verbose ("babysitter: spawning %s\n", sitter->executable);
628
629   PING();
630   sitter->child_handle = spawn_program (sitter->executable,
631                                         sitter->argv, sitter->envp);
632
633   PING();
634   if (sitter->child_handle == (HANDLE) -1)
635     {
636       sitter->child_handle = NULL;
637       sitter->have_spawn_errno = TRUE;
638       sitter->spawn_errno = GetLastError();
639     }
640   
641   PING();
642   SetEvent (sitter->start_sync_event);
643
644   if (sitter->child_handle != NULL)
645     {
646       int ret;
647       DWORD status;
648
649       PING();
650       WaitForSingleObject (sitter->child_handle, INFINITE);
651
652       PING();
653       ret = GetExitCodeProcess (sitter->child_handle, &status);
654
655       sitter->child_status = status;
656       sitter->have_child_status = TRUE;
657
658       CloseHandle (sitter->child_handle);
659       sitter->child_handle = NULL;
660     }
661
662 #ifdef DBUS_BUILD_TESTS
663   SetEvent (sitter->end_sync_event);
664 #endif
665
666   PING();
667   send (sitter->socket_to_main, " ", 1, 0);
668
669   _dbus_babysitter_unref (sitter);
670
671   return 0;
672 }
673
674 dbus_bool_t
675 _dbus_spawn_async_with_babysitter (DBusBabysitter           **sitter_p,
676                                    char                     **argv,
677                                    char                     **envp,
678                                    DBusSpawnChildSetupFunc    child_setup,
679                                    void                      *user_data,
680                                    DBusError                 *error)
681 {
682   DBusBabysitter *sitter;
683   HANDLE sitter_thread;
684   DWORD sitter_thread_id;
685   
686   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
687
688   *sitter_p = NULL;
689
690   PING();
691   sitter = _dbus_babysitter_new ();
692   if (sitter == NULL)
693     {
694       _DBUS_SET_OOM (error);
695       return FALSE;
696     }
697
698   sitter->child_setup = child_setup;
699   sitter->user_data = user_data;
700
701   sitter->executable = _dbus_strdup (argv[0]);
702   if (sitter->executable == NULL)
703     {
704       _DBUS_SET_OOM (error);
705       goto out0;
706     }
707
708   PING();
709   if (!_dbus_full_duplex_pipe (&sitter->socket_to_babysitter,
710                                &sitter->socket_to_main,
711                                FALSE, error))
712     goto out0;
713
714   sitter->sitter_watch = _dbus_watch_new (sitter->socket_to_babysitter,
715                                           DBUS_WATCH_READABLE,
716                                           TRUE, handle_watch, sitter, NULL);
717   PING();
718   if (sitter->sitter_watch == NULL)
719     {
720       _DBUS_SET_OOM (error);
721       goto out0;
722     }
723
724   PING();
725   if (!_dbus_watch_list_add_watch (sitter->watches,  sitter->sitter_watch))
726     {
727       /* we need to free it early so the destructor won't try to remove it
728        * without it having been added, which DBusLoop doesn't allow */
729       _dbus_watch_invalidate (sitter->sitter_watch);
730       _dbus_watch_unref (sitter->sitter_watch);
731       sitter->sitter_watch = NULL;
732
733       _DBUS_SET_OOM (error);
734       goto out0;
735     }
736
737   sitter->argc = protect_argv (argv, &sitter->argv);
738   if (sitter->argc == -1)
739     {
740       _DBUS_SET_OOM (error);
741       goto out0;
742     }
743   sitter->envp = envp;
744
745   PING();
746   sitter_thread = (HANDLE) CreateThread (NULL, 0, babysitter,
747                   sitter, 0, &sitter_thread_id);
748
749   if (sitter_thread == 0)
750     {
751       PING();
752       dbus_set_error_const (error, DBUS_ERROR_SPAWN_FORK_FAILED,
753                             "Failed to create new thread");
754       goto out0;
755     }
756   CloseHandle (sitter_thread);
757
758   PING();
759   WaitForSingleObject (sitter->start_sync_event, INFINITE);
760
761   PING();
762   if (sitter_p != NULL)
763     *sitter_p = sitter;
764   else
765     _dbus_babysitter_unref (sitter);
766
767   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
768
769   PING();
770   return TRUE;
771
772 out0:
773   _dbus_babysitter_unref (sitter);
774
775   return FALSE;
776 }
777
778 void
779 _dbus_babysitter_set_result_function  (DBusBabysitter             *sitter,
780                                        DBusBabysitterFinishedFunc  finished,
781                                        void                       *user_data)
782 {
783   sitter->finished_cb = finished;
784   sitter->finished_data = user_data;
785 }
786
787 #ifdef DBUS_BUILD_TESTS
788
789 static char *
790 get_test_exec (const char *exe,
791                DBusString *scratch_space)
792 {
793   const char *dbus_test_exec;
794
795   dbus_test_exec = _dbus_getenv ("DBUS_TEST_EXEC");
796
797   if (dbus_test_exec == NULL)
798     dbus_test_exec = DBUS_TEST_EXEC;
799
800   if (!_dbus_string_init (scratch_space))
801     return NULL;
802
803   if (!_dbus_string_append_printf (scratch_space, "%s/%s%s",
804                                    dbus_test_exec, exe, DBUS_EXEEXT))
805     {
806       _dbus_string_free (scratch_space);
807       return NULL;
808     }
809
810   return _dbus_string_get_data (scratch_space);
811 }
812
813 #define LIVE_CHILDREN(sitter) ((sitter)->child_handle != NULL)
814
815 static void
816 _dbus_babysitter_block_for_child_exit (DBusBabysitter *sitter)
817 {
818   if (sitter->child_handle == NULL)
819     return;
820
821   WaitForSingleObject (sitter->end_sync_event, INFINITE);
822 }
823
824 static dbus_bool_t
825 check_spawn_nonexistent (void *data)
826 {
827   char *argv[4] = { NULL, NULL, NULL, NULL };
828   DBusBabysitter *sitter;
829   DBusError error;
830
831   sitter = NULL;
832
833   dbus_error_init (&error);
834
835   /*** Test launching nonexistent binary */
836
837   argv[0] = "/this/does/not/exist/32542sdgafgafdg";
838   if (_dbus_spawn_async_with_babysitter (&sitter, argv, NULL,
839                                          NULL, NULL,
840                                          &error))
841     {
842       _dbus_babysitter_block_for_child_exit (sitter);
843       _dbus_babysitter_set_child_exit_error (sitter, &error);
844     }
845
846   if (sitter)
847     _dbus_babysitter_unref (sitter);
848
849   if (!dbus_error_is_set (&error))
850     {
851       _dbus_warn ("Did not get an error launching nonexistent executable\n");
852       return FALSE;
853     }
854
855   if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
856         dbus_error_has_name (&error, DBUS_ERROR_SPAWN_EXEC_FAILED)))
857     {
858       _dbus_warn ("Not expecting error when launching nonexistent executable: %s: %s\n",
859                   error.name, error.message);
860       dbus_error_free (&error);
861       return FALSE;
862     }
863
864   dbus_error_free (&error);
865
866   return TRUE;
867 }
868
869 static dbus_bool_t
870 check_spawn_segfault (void *data)
871 {
872   char *argv[4] = { NULL, NULL, NULL, NULL };
873   DBusBabysitter *sitter;
874   DBusError error;
875   DBusString argv0;
876
877   sitter = NULL;
878
879   dbus_error_init (&error);
880
881   /*** Test launching segfault binary */
882
883   argv[0] = get_test_exec ("test-segfault", &argv0);
884
885   if (argv[0] == NULL)
886     {
887       /* OOM was simulated, never mind */
888       return TRUE;
889     }
890
891   if (_dbus_spawn_async_with_babysitter (&sitter, argv, NULL,
892                                          NULL, NULL,
893                                          &error))
894     {
895       _dbus_babysitter_block_for_child_exit (sitter);
896       _dbus_babysitter_set_child_exit_error (sitter, &error);
897     }
898
899   _dbus_string_free (&argv0);
900
901   if (sitter)
902     _dbus_babysitter_unref (sitter);
903
904   if (!dbus_error_is_set (&error))
905     {
906       _dbus_warn ("Did not get an error launching segfaulting binary\n");
907       return FALSE;
908     }
909
910   if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
911         dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED)))
912     {
913       _dbus_warn ("Not expecting error when launching segfaulting executable: %s: %s\n",
914                   error.name, error.message);
915       dbus_error_free (&error);
916       return FALSE;
917     }
918
919   dbus_error_free (&error);
920
921   return TRUE;
922 }
923
924 static dbus_bool_t
925 check_spawn_exit (void *data)
926 {
927   char *argv[4] = { NULL, NULL, NULL, NULL };
928   DBusBabysitter *sitter;
929   DBusError error;
930   DBusString argv0;
931
932   sitter = NULL;
933
934   dbus_error_init (&error);
935
936   /*** Test launching exit failure binary */
937
938   argv[0] = get_test_exec ("test-exit", &argv0);
939
940   if (argv[0] == NULL)
941     {
942       /* OOM was simulated, never mind */
943       return TRUE;
944     }
945
946   if (_dbus_spawn_async_with_babysitter (&sitter, argv, NULL,
947                                          NULL, NULL,
948                                          &error))
949     {
950       _dbus_babysitter_block_for_child_exit (sitter);
951       _dbus_babysitter_set_child_exit_error (sitter, &error);
952     }
953
954   _dbus_string_free (&argv0);
955
956   if (sitter)
957     _dbus_babysitter_unref (sitter);
958
959   if (!dbus_error_is_set (&error))
960     {
961       _dbus_warn ("Did not get an error launching binary that exited with failure code\n");
962       return FALSE;
963     }
964
965   if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
966         dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED)))
967     {
968       _dbus_warn ("Not expecting error when launching exiting executable: %s: %s\n",
969                   error.name, error.message);
970       dbus_error_free (&error);
971       return FALSE;
972     }
973
974   dbus_error_free (&error);
975
976   return TRUE;
977 }
978
979 static dbus_bool_t
980 check_spawn_and_kill (void *data)
981 {
982   char *argv[4] = { NULL, NULL, NULL, NULL };
983   DBusBabysitter *sitter;
984   DBusError error;
985   DBusString argv0;
986
987   sitter = NULL;
988
989   dbus_error_init (&error);
990
991   /*** Test launching sleeping binary then killing it */
992
993   argv[0] = get_test_exec ("test-sleep-forever", &argv0);
994
995   if (argv[0] == NULL)
996     {
997       /* OOM was simulated, never mind */
998       return TRUE;
999     }
1000
1001   if (_dbus_spawn_async_with_babysitter (&sitter, argv, NULL,
1002                                          NULL, NULL,
1003                                          &error))
1004     {
1005       _dbus_babysitter_kill_child (sitter);
1006
1007       _dbus_babysitter_block_for_child_exit (sitter);
1008
1009       _dbus_babysitter_set_child_exit_error (sitter, &error);
1010     }
1011
1012   _dbus_string_free (&argv0);
1013
1014   if (sitter)
1015     _dbus_babysitter_unref (sitter);
1016
1017   if (!dbus_error_is_set (&error))
1018     {
1019       _dbus_warn ("Did not get an error after killing spawned binary\n");
1020       return FALSE;
1021     }
1022
1023   if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
1024         dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED)))
1025     {
1026       _dbus_warn ("Not expecting error when killing executable: %s: %s\n",
1027                   error.name, error.message);
1028       dbus_error_free (&error);
1029       return FALSE;
1030     }
1031
1032   dbus_error_free (&error);
1033
1034   return TRUE;
1035 }
1036
1037 dbus_bool_t
1038 _dbus_spawn_test (const char *test_data_dir)
1039 {
1040   if (!_dbus_test_oom_handling ("spawn_nonexistent",
1041                                 check_spawn_nonexistent,
1042                                 NULL))
1043     return FALSE;
1044
1045   /* Don't run the obnoxious segfault test by default,
1046    * it's a pain to have to click all those error boxes.
1047    */
1048   if (getenv ("DO_SEGFAULT_TEST"))
1049     if (!_dbus_test_oom_handling ("spawn_segfault",
1050                                   check_spawn_segfault,
1051                                   NULL))
1052       return FALSE;
1053
1054   if (!_dbus_test_oom_handling ("spawn_exit",
1055                                 check_spawn_exit,
1056                                 NULL))
1057     return FALSE;
1058
1059   if (!_dbus_test_oom_handling ("spawn_and_kill",
1060                                 check_spawn_and_kill,
1061                                 NULL))
1062     return FALSE;
1063
1064   return TRUE;
1065 }
1066 #endif