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