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 */
60struct 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
91static 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 */
145DBusBabysitter *
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 */
162void
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
247void
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 */
263dbus_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 */
282dbus_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 */
306void
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
340dbus_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
357static dbus_bool_t
358handle_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 */
383static int
384protect_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.  */
473static char *
474compose_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
504static char *
505build_commandline (char **argv)
506{
507  return compose_string (argv, ' ');
508}
509
510static char *
511build_env_string (char** envp)
512{
513  return compose_string (envp, '\0');
514}
515
516static HANDLE
517spawn_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
557static DWORD __stdcall
558babysitter (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
618dbus_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
710out0:
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
720static 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
729static dbus_bool_t
730check_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
774static dbus_bool_t
775check_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
819static dbus_bool_t
820check_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
864static dbus_bool_t
865check_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
912dbus_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
942