Enable launchd.
[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
85     dbus_bool_t have_spawn_errno;
86     int spawn_errno;
87     dbus_bool_t have_child_status;
88     int child_status;
89   };
90
91 static DBusBabysitter*
92 _dbus_babysitter_new (void)
93 {
94   DBusBabysitter *sitter;
95
96   sitter = dbus_new0 (DBusBabysitter, 1);
97   if (sitter == NULL)
98     return NULL;
99
100   sitter->refcount = 1;
101
102   sitter->start_sync_event = CreateEvent (NULL, FALSE, FALSE, NULL);
103   if (sitter->start_sync_event == NULL)
104     {
105       _dbus_babysitter_unref (sitter);
106       return NULL;
107     }
108
109 #ifdef DBUS_BUILD_TESTS
110   sitter->end_sync_event = CreateEvent (NULL, FALSE, FALSE, NULL);
111   if (sitter->end_sync_event == NULL)
112     {
113       _dbus_babysitter_unref (sitter);
114       return NULL;
115     }
116 #endif
117
118   sitter->child_handle = NULL;
119
120   sitter->socket_to_babysitter = sitter->socket_to_main = -1;
121
122   sitter->argc = 0;
123   sitter->argv = NULL;
124   sitter->envp = NULL;
125
126   sitter->watches = _dbus_watch_list_new ();
127   if (sitter->watches == NULL)
128     {
129       _dbus_babysitter_unref (sitter);
130       return NULL;
131     }
132
133   sitter->have_spawn_errno = FALSE;
134   sitter->have_child_status = FALSE;
135
136   return sitter;
137 }
138
139 /**
140  * Increment the reference count on the babysitter object.
141  *
142  * @param sitter the babysitter
143  * @returns the babysitter
144  */
145 DBusBabysitter *
146 _dbus_babysitter_ref (DBusBabysitter *sitter)
147 {
148   PING();
149   _dbus_assert (sitter != NULL);
150   _dbus_assert (sitter->refcount > 0);
151
152   sitter->refcount += 1;
153
154   return sitter;
155 }
156
157 /**
158  * Decrement the reference count on the babysitter object.
159  *
160  * @param sitter the babysitter
161  */
162 void
163 _dbus_babysitter_unref (DBusBabysitter *sitter)
164 {
165   int i;
166
167   PING();
168   _dbus_assert (sitter != NULL);
169   _dbus_assert (sitter->refcount > 0);
170
171   sitter->refcount -= 1;
172
173   if (sitter->refcount == 0)
174     {
175       if (sitter->socket_to_babysitter != -1)
176         {
177           _dbus_close_socket (sitter->socket_to_babysitter, NULL);
178           sitter->socket_to_babysitter = -1;
179         }
180
181       if (sitter->socket_to_main != -1)
182         {
183           _dbus_close_socket (sitter->socket_to_main, NULL);
184           sitter->socket_to_main = -1;
185         }
186
187       PING();
188       if (sitter->argv != NULL)
189         {
190           for (i = 0; i < sitter->argc; i++)
191             if (sitter->argv[i] != NULL)
192               {
193                 dbus_free (sitter->argv[i]);
194                 sitter->argv[i] = NULL;
195               }
196           dbus_free (sitter->argv);
197           sitter->argv = NULL;
198         }
199
200       if (sitter->envp != NULL)
201         {
202           char **e = sitter->envp;
203
204           while (*e)
205             dbus_free (*e++);
206           dbus_free (sitter->envp);
207           sitter->envp = NULL;
208         }
209
210       if (sitter->child_handle != NULL)
211         {
212           CloseHandle (sitter->child_handle);
213           sitter->child_handle = NULL;
214         }
215
216       if (sitter->sitter_watch)
217         {
218           _dbus_watch_invalidate (sitter->sitter_watch);
219           _dbus_watch_unref (sitter->sitter_watch);
220           sitter->sitter_watch = NULL;
221         }
222
223       if (sitter->watches)
224         _dbus_watch_list_free (sitter->watches);
225
226       if (sitter->start_sync_event != NULL)
227         {
228           PING();
229           CloseHandle (sitter->start_sync_event);
230           sitter->start_sync_event = NULL;
231         }
232
233 #ifdef DBUS_BUILD_TESTS
234       if (sitter->end_sync_event != NULL)
235         {
236           CloseHandle (sitter->end_sync_event);
237           sitter->end_sync_event = NULL;
238         }
239 #endif
240
241       dbus_free (sitter->executable);
242
243       dbus_free (sitter);
244     }
245 }
246
247 void
248 _dbus_babysitter_kill_child (DBusBabysitter *sitter)
249 {
250   PING();
251   if (sitter->child_handle == NULL)
252     return; /* child is already dead, or we're so hosed we'll never recover */
253
254   PING();
255   TerminateProcess (sitter->child_handle, 12345);
256 }
257
258 /**
259  * Checks whether the child has exited, without blocking.
260  *
261  * @param sitter the babysitter
262  */
263 dbus_bool_t
264 _dbus_babysitter_get_child_exited (DBusBabysitter *sitter)
265 {
266   PING();
267   return (sitter->child_handle == NULL);
268 }
269
270 /**
271  * Gets the exit status of the child. We do this so implementation specific
272  * detail is not cluttering up dbus, for example the system launcher code.
273  * This can only be called if the child has exited, i.e. call
274  * _dbus_babysitter_get_child_exited(). It returns FALSE if the child
275  * did not return a status code, e.g. because the child was signaled
276  * or we failed to ever launch the child in the first place.
277  *
278  * @param sitter the babysitter
279  * @param status the returned status code
280  * @returns #FALSE on failure
281  */
282 dbus_bool_t
283 _dbus_babysitter_get_child_exit_status (DBusBabysitter *sitter,
284                                         int            *status)
285 {
286   if (!_dbus_babysitter_get_child_exited (sitter))
287     _dbus_assert_not_reached ("Child has not exited");
288
289   if (!sitter->have_child_status ||
290       sitter->child_status == STILL_ACTIVE)
291     return FALSE;
292
293   *status = sitter->child_status;
294   return TRUE;
295 }
296
297 /**
298  * Sets the #DBusError with an explanation of why the spawned
299  * child process exited (on a signal, or whatever). If
300  * the child process has not exited, does nothing (error
301  * will remain unset).
302  *
303  * @param sitter the babysitter
304  * @param error an error to fill in
305  */
306 void
307 _dbus_babysitter_set_child_exit_error (DBusBabysitter *sitter,
308                                        DBusError      *error)
309 {
310   PING();
311   if (!_dbus_babysitter_get_child_exited (sitter))
312     return;
313
314   PING();
315   if (sitter->have_spawn_errno)
316     {
317       char *emsg = _dbus_win_error_string (sitter->spawn_errno);
318       dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED,
319                       "Failed to execute program %s: %s",
320                       sitter->executable, emsg);
321       _dbus_win_free_error_string (emsg);
322     }
323   else if (sitter->have_child_status)
324     {
325       PING();
326       dbus_set_error (error, DBUS_ERROR_SPAWN_CHILD_EXITED,
327                       "Process %s exited with status %d",
328                       sitter->executable, sitter->child_status);
329     }
330   else
331     {
332       PING();
333       dbus_set_error (error, DBUS_ERROR_FAILED,
334                       "Process %s exited, status unknown",
335                       sitter->executable);
336     }
337   PING();
338 }
339
340 dbus_bool_t
341 _dbus_babysitter_set_watch_functions (DBusBabysitter            *sitter,
342                                       DBusAddWatchFunction       add_function,
343                                       DBusRemoveWatchFunction    remove_function,
344                                       DBusWatchToggledFunction   toggled_function,
345                                       void                      *data,
346                                       DBusFreeFunction           free_data_function)
347 {
348   PING();
349   return _dbus_watch_list_set_functions (sitter->watches,
350                                          add_function,
351                                          remove_function,
352                                          toggled_function,
353                                          data,
354                                          free_data_function);
355 }
356
357 static dbus_bool_t
358 handle_watch (DBusWatch       *watch,
359               unsigned int     condition,
360               void            *data)
361 {
362   DBusBabysitter *sitter = data;
363
364   /* On Unix dbus-spawn uses a babysitter *process*, thus it has to
365    * actually send the exit statuses, error codes and whatnot through
366    * sockets and/or pipes. On Win32, the babysitter is jus a thread,
367    * so it can set the status fields directly in the babysitter struct
368    * just fine. The socket pipe is used just so we can watch it with
369    * select(), as soon as anything is written to it we know that the
370    * babysitter thread has recorded the status in the babysitter
371    * struct.
372    */
373
374   PING();
375   _dbus_close_socket (sitter->socket_to_babysitter, NULL);
376   PING();
377   sitter->socket_to_babysitter = -1;
378
379   return TRUE;
380 }
381
382 /* protect_argv lifted from GLib, relicensed by author, Tor Lillqvist */
383 static int
384 protect_argv (char  **argv,
385               char ***new_argv)
386 {
387   int i;
388   int argc = 0;
389
390   while (argv[argc])
391     ++argc;
392   *new_argv = dbus_malloc ((argc + 1) * sizeof (char *));
393   if (*new_argv == NULL)
394     return -1;
395
396   for (i = 0; i < argc; i++)
397     (*new_argv)[i] = NULL;
398
399   /* Quote each argv element if necessary, so that it will get
400    * reconstructed correctly in the C runtime startup code.  Note that
401    * the unquoting algorithm in the C runtime is really weird, and
402    * rather different than what Unix shells do. See stdargv.c in the C
403    * runtime sources (in the Platform SDK, in src/crt).
404    *
405    * Note that an new_argv[0] constructed by this function should
406    * *not* be passed as the filename argument to a spawn* or exec*
407    * family function. That argument should be the real file name
408    * without any quoting.
409    */
410   for (i = 0; i < argc; i++)
411     {
412       char *p = argv[i];
413       char *q;
414       int len = 0;
415       int need_dblquotes = FALSE;
416       while (*p)
417         {
418           if (*p == ' ' || *p == '\t')
419             need_dblquotes = TRUE;
420           else if (*p == '"')
421             len++;
422           else if (*p == '\\')
423             {
424               char *pp = p;
425               while (*pp && *pp == '\\')
426                 pp++;
427               if (*pp == '"')
428                 len++;
429             }
430           len++;
431           p++;
432         }
433
434       q = (*new_argv)[i] = dbus_malloc (len + need_dblquotes*2 + 1);
435
436       if (q == NULL)
437         return -1;
438
439
440       p = argv[i];
441
442       if (need_dblquotes)
443         *q++ = '"';
444
445       while (*p)
446         {
447           if (*p == '"')
448             *q++ = '\\';
449           else if (*p == '\\')
450             {
451               char *pp = p;
452               while (*pp && *pp == '\\')
453                 pp++;
454               if (*pp == '"')
455                 *q++ = '\\';
456             }
457           *q++ = *p;
458           p++;
459         }
460
461       if (need_dblquotes)
462         *q++ = '"';
463       *q++ = '\0';
464       /* printf ("argv[%d]:%s, need_dblquotes:%s len:%d => %s\n", i, argv[i], need_dblquotes?"TRUE":"FALSE", len, (*new_argv)[i]); */
465     }
466   (*new_argv)[argc] = NULL;
467
468   return argc;
469 }
470
471
472 /* From GPGME, relicensed by g10 Code GmbH.  */
473 static char *
474 compose_string (char **strings, char separator)
475 {
476   int i;
477   int n = 0;
478   char *buf;
479   char *p;
480   const char *ptr;
481   
482   if (!strings || !strings[0])
483     return 0;
484   for (i = 0; strings[i]; i++)
485     n += strlen (strings[i]) + 1;
486   n++;
487
488   buf = p = malloc (n);
489   if (!buf)
490     return NULL;
491   for (i = 0; strings[i]; i++)
492     {
493       strcpy (p, strings[i]);
494       p += strlen (strings[i]);
495       *(p++) = separator;
496     }
497   p--;
498   *(p++) = '\0';
499   *p = '\0';
500
501   return buf;
502 }
503
504 static char *
505 build_commandline (char **argv)
506 {
507   return compose_string (argv, ' ');
508 }
509
510 static char *
511 build_env_string (char** envp)
512 {
513   return compose_string (envp, '\0');
514 }
515
516 static HANDLE
517 spawn_program (char* name, char** argv, char** envp)
518 {
519   PROCESS_INFORMATION pi = { NULL, 0, 0, 0 };
520   STARTUPINFOA si;
521   char *arg_string, *env_string;
522   BOOL result;
523
524 #ifdef DBUS_WINCE
525   if (argv && argv[0])
526     arg_string = build_commandline (argv + 1);
527   else
528     arg_string = NULL;
529 #else
530   arg_string = build_commandline (argv);
531 #endif
532   if (!arg_string)
533     return INVALID_HANDLE_VALUE;
534
535   env_string = build_env_string(envp);
536
537   memset (&si, 0, sizeof (si));
538   si.cb = sizeof (si);
539 #ifdef DBUS_WINCE
540   result = CreateProcessA (name, arg_string, NULL, NULL, FALSE, 0,
541 #else
542   result = CreateProcessA (NULL, arg_string, NULL, NULL, FALSE, 0,
543 #endif
544                            (LPVOID)env_string, NULL, &si, &pi);
545   free (arg_string);
546   if (env_string)
547     free (env_string);
548
549   if (!result)
550     return INVALID_HANDLE_VALUE;
551
552   CloseHandle (pi.hThread);
553   return pi.hProcess;
554 }
555
556
557 static DWORD __stdcall
558 babysitter (void *parameter)
559 {
560   DBusBabysitter *sitter = (DBusBabysitter *) parameter;
561   int fd;
562   PING();
563   _dbus_babysitter_ref (sitter);
564
565   if (sitter->child_setup)
566     {
567       PING();
568       (*sitter->child_setup) (sitter->user_data);
569     }
570
571   _dbus_verbose ("babysitter: spawning %s\n", sitter->executable);
572
573   PING();
574   sitter->child_handle = spawn_program (sitter->executable,
575                                         sitter->argv, sitter->envp);
576
577   PING();
578   if (sitter->child_handle == (HANDLE) -1)
579     {
580       sitter->child_handle = NULL;
581       sitter->have_spawn_errno = TRUE;
582       sitter->spawn_errno = GetLastError();
583     }
584   
585   PING();
586   SetEvent (sitter->start_sync_event);
587
588   if (sitter->child_handle != NULL)
589     {
590       int ret;
591       DWORD status;
592
593       PING();
594       WaitForSingleObject (sitter->child_handle, INFINITE);
595
596       PING();
597       ret = GetExitCodeProcess (sitter->child_handle, &status);
598
599       sitter->child_status = status;
600       sitter->have_child_status = TRUE;
601
602       CloseHandle (sitter->child_handle);
603       sitter->child_handle = NULL;
604     }
605
606 #ifdef DBUS_BUILD_TESTS
607   SetEvent (sitter->end_sync_event);
608 #endif
609
610   PING();
611   send (sitter->socket_to_main, " ", 1, 0);
612
613   _dbus_babysitter_unref (sitter);
614
615   return 0;
616 }
617
618 dbus_bool_t
619 _dbus_spawn_async_with_babysitter (DBusBabysitter           **sitter_p,
620                                    char                     **argv,
621                                    char                     **envp,
622                                    DBusSpawnChildSetupFunc    child_setup,
623                                    void                      *user_data,
624                                    DBusError                 *error)
625 {
626   DBusBabysitter *sitter;
627   HANDLE sitter_thread;
628   DWORD sitter_thread_id;
629   
630   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
631
632   *sitter_p = NULL;
633
634   PING();
635   sitter = _dbus_babysitter_new ();
636   if (sitter == NULL)
637     {
638       _DBUS_SET_OOM (error);
639       return FALSE;
640     }
641
642   sitter->child_setup = child_setup;
643   sitter->user_data = user_data;
644
645   sitter->executable = _dbus_strdup (argv[0]);
646   if (sitter->executable == NULL)
647     {
648       _DBUS_SET_OOM (error);
649       goto out0;
650     }
651
652   PING();
653   if (!_dbus_full_duplex_pipe (&sitter->socket_to_babysitter,
654                                &sitter->socket_to_main,
655                                FALSE, error))
656     goto out0;
657
658   sitter->sitter_watch = _dbus_watch_new (sitter->socket_to_babysitter,
659                                           DBUS_WATCH_READABLE,
660                                           TRUE, handle_watch, sitter, NULL);
661   PING();
662   if (sitter->sitter_watch == NULL)
663     {
664       _DBUS_SET_OOM (error);
665       goto out0;
666     }
667
668   PING();
669   if (!_dbus_watch_list_add_watch (sitter->watches,  sitter->sitter_watch))
670     {
671       _DBUS_SET_OOM (error);
672       goto out0;
673     }
674
675   sitter->argc = protect_argv (argv, &sitter->argv);
676   if (sitter->argc == -1)
677     {
678       _DBUS_SET_OOM (error);
679       goto out0;
680     }
681   sitter->envp = envp;
682
683   PING();
684   sitter_thread = (HANDLE) CreateThread (NULL, 0, babysitter,
685                   sitter, 0, &sitter_thread_id);
686
687   if (sitter_thread == 0)
688     {
689       PING();
690       dbus_set_error_const (error, DBUS_ERROR_SPAWN_FORK_FAILED,
691                             "Failed to create new thread");
692       goto out0;
693     }
694   CloseHandle (sitter_thread);
695
696   PING();
697   WaitForSingleObject (sitter->start_sync_event, INFINITE);
698
699   PING();
700   if (sitter_p != NULL)
701     *sitter_p = sitter;
702   else
703     _dbus_babysitter_unref (sitter);
704
705   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
706
707   PING();
708   return TRUE;
709
710 out0:
711   _dbus_babysitter_unref (sitter);
712
713   return FALSE;
714 }
715
716 #ifdef DBUS_BUILD_TESTS
717
718 #define LIVE_CHILDREN(sitter) ((sitter)->child_handle != NULL)
719
720 static void
721 _dbus_babysitter_block_for_child_exit (DBusBabysitter *sitter)
722 {
723   if (sitter->child_handle == NULL)
724     return;
725
726   WaitForSingleObject (sitter->end_sync_event, INFINITE);
727 }
728
729 static dbus_bool_t
730 check_spawn_nonexistent (void *data)
731 {
732   char *argv[4] = { NULL, NULL, NULL, NULL };
733   DBusBabysitter *sitter;
734   DBusError error;
735
736   sitter = NULL;
737
738   dbus_error_init (&error);
739
740   /*** Test launching nonexistent binary */
741
742   argv[0] = "/this/does/not/exist/32542sdgafgafdg";
743   if (_dbus_spawn_async_with_babysitter (&sitter, argv, NULL,
744                                          NULL, NULL,
745                                          &error))
746     {
747       _dbus_babysitter_block_for_child_exit (sitter);
748       _dbus_babysitter_set_child_exit_error (sitter, &error);
749     }
750
751   if (sitter)
752     _dbus_babysitter_unref (sitter);
753
754   if (!dbus_error_is_set (&error))
755     {
756       _dbus_warn ("Did not get an error launching nonexistent executable\n");
757       return FALSE;
758     }
759
760   if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
761         dbus_error_has_name (&error, DBUS_ERROR_SPAWN_EXEC_FAILED)))
762     {
763       _dbus_warn ("Not expecting error when launching nonexistent executable: %s: %s\n",
764                   error.name, error.message);
765       dbus_error_free (&error);
766       return FALSE;
767     }
768
769   dbus_error_free (&error);
770
771   return TRUE;
772 }
773
774 static dbus_bool_t
775 check_spawn_segfault (void *data)
776 {
777   char *argv[4] = { NULL, NULL, NULL, NULL };
778   DBusBabysitter *sitter;
779   DBusError error;
780
781   sitter = NULL;
782
783   dbus_error_init (&error);
784
785   /*** Test launching segfault binary */
786
787   argv[0] = TEST_SEGFAULT_BINARY;
788   if (_dbus_spawn_async_with_babysitter (&sitter, argv, NULL,
789                                          NULL, NULL,
790                                          &error))
791     {
792       _dbus_babysitter_block_for_child_exit (sitter);
793       _dbus_babysitter_set_child_exit_error (sitter, &error);
794     }
795
796   if (sitter)
797     _dbus_babysitter_unref (sitter);
798
799   if (!dbus_error_is_set (&error))
800     {
801       _dbus_warn ("Did not get an error launching segfaulting binary\n");
802       return FALSE;
803     }
804
805   if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
806         dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED)))
807     {
808       _dbus_warn ("Not expecting error when launching segfaulting executable: %s: %s\n",
809                   error.name, error.message);
810       dbus_error_free (&error);
811       return FALSE;
812     }
813
814   dbus_error_free (&error);
815
816   return TRUE;
817 }
818
819 static dbus_bool_t
820 check_spawn_exit (void *data)
821 {
822   char *argv[4] = { NULL, NULL, NULL, NULL };
823   DBusBabysitter *sitter;
824   DBusError error;
825
826   sitter = NULL;
827
828   dbus_error_init (&error);
829
830   /*** Test launching exit failure binary */
831
832   argv[0] = TEST_EXIT_BINARY;
833   if (_dbus_spawn_async_with_babysitter (&sitter, argv, NULL,
834                                          NULL, NULL,
835                                          &error))
836     {
837       _dbus_babysitter_block_for_child_exit (sitter);
838       _dbus_babysitter_set_child_exit_error (sitter, &error);
839     }
840
841   if (sitter)
842     _dbus_babysitter_unref (sitter);
843
844   if (!dbus_error_is_set (&error))
845     {
846       _dbus_warn ("Did not get an error launching binary that exited with failure code\n");
847       return FALSE;
848     }
849
850   if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
851         dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED)))
852     {
853       _dbus_warn ("Not expecting error when launching exiting executable: %s: %s\n",
854                   error.name, error.message);
855       dbus_error_free (&error);
856       return FALSE;
857     }
858
859   dbus_error_free (&error);
860
861   return TRUE;
862 }
863
864 static dbus_bool_t
865 check_spawn_and_kill (void *data)
866 {
867   char *argv[4] = { NULL, NULL, NULL, NULL };
868   DBusBabysitter *sitter;
869   DBusError error;
870
871   sitter = NULL;
872
873   dbus_error_init (&error);
874
875   /*** Test launching sleeping binary then killing it */
876
877   argv[0] = TEST_SLEEP_FOREVER_BINARY;
878   if (_dbus_spawn_async_with_babysitter (&sitter, argv, NULL,
879                                          NULL, NULL,
880                                          &error))
881     {
882       _dbus_babysitter_kill_child (sitter);
883
884       _dbus_babysitter_block_for_child_exit (sitter);
885
886       _dbus_babysitter_set_child_exit_error (sitter, &error);
887     }
888
889   if (sitter)
890     _dbus_babysitter_unref (sitter);
891
892   if (!dbus_error_is_set (&error))
893     {
894       _dbus_warn ("Did not get an error after killing spawned binary\n");
895       return FALSE;
896     }
897
898   if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
899         dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED)))
900     {
901       _dbus_warn ("Not expecting error when killing executable: %s: %s\n",
902                   error.name, error.message);
903       dbus_error_free (&error);
904       return FALSE;
905     }
906
907   dbus_error_free (&error);
908
909   return TRUE;
910 }
911
912 dbus_bool_t
913 _dbus_spawn_test (const char *test_data_dir)
914 {
915   if (!_dbus_test_oom_handling ("spawn_nonexistent",
916                                 check_spawn_nonexistent,
917                                 NULL))
918     return FALSE;
919
920   /* Don't run the obnoxious segfault test by default,
921    * it's a pain to have to click all those error boxes.
922    */
923   if (getenv ("DO_SEGFAULT_TEST"))
924     if (!_dbus_test_oom_handling ("spawn_segfault",
925                                   check_spawn_segfault,
926                                   NULL))
927       return FALSE;
928
929   if (!_dbus_test_oom_handling ("spawn_exit",
930                                 check_spawn_exit,
931                                 NULL))
932     return FALSE;
933
934   if (!_dbus_test_oom_handling ("spawn_and_kill",
935                                 check_spawn_and_kill,
936                                 NULL))
937     return FALSE;
938
939   return TRUE;
940 }
941 #endif