* dbus/dbus-sysdeps-win.c, dbus/dbus-sysdeps-spawn-win.c, dbus/dbus-sysdeps-win.h...
[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 #ifdef DBUS_WINCE
13 #include <process.h>
14 #endif
15
16 /* -*- mode: C; c-file-style: "gnu" -*- */
17 /* dbus-spawn-win32.c Wrapper around g_spawn
18  * 
19  * Copyright (C) 2002, 2003, 2004  Red Hat, Inc.
20  * Copyright (C) 2003 CodeFactory AB
21  * Copyright (C) 2005 Novell, Inc.
22  *
23  * Licensed under the Academic Free License version 2.1
24  * 
25  * This program is free software; you can redistribute it and/or modify
26  * it under the terms of the GNU General Public License as published by
27  * the Free Software Foundation; either version 2 of the License, or
28  * (at your option) any later version.
29  *
30  * This program is distributed in the hope that it will be useful,
31  * but WITHOUT ANY WARRANTY; without even the implied warranty of
32  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
33  * GNU General Public License for more details.
34  * 
35  * You should have received a copy of the GNU General Public License
36  * along with this program; if not, write to the Free Software
37  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
38  *
39  */
40 #include "dbus-spawn.h"
41 #include "dbus-sysdeps.h"
42 #include "dbus-sysdeps-win.h"
43 #include "dbus-internals.h"
44 #include "dbus-test.h"
45 #include "dbus-protocol.h"
46
47 #define WIN32_LEAN_AND_MEAN
48 //#define STRICT
49 //#include <windows.h>
50 //#undef STRICT
51 #include <winsock2.h>
52 #undef interface
53
54 #include <stdlib.h>
55
56 #include <process.h>
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
86     dbus_bool_t have_spawn_errno;
87     int spawn_errno;
88     dbus_bool_t have_child_status;
89     int child_status;
90   };
91
92 static DBusBabysitter*
93 _dbus_babysitter_new (void)
94 {
95   DBusBabysitter *sitter;
96
97   sitter = dbus_new0 (DBusBabysitter, 1);
98   if (sitter == NULL)
99     return NULL;
100
101   sitter->refcount = 1;
102
103   sitter->start_sync_event = CreateEvent (NULL, FALSE, FALSE, NULL);
104   if (sitter->start_sync_event == NULL)
105     {
106       _dbus_babysitter_unref (sitter);
107       return NULL;
108     }
109
110 #ifdef DBUS_BUILD_TESTS
111   sitter->end_sync_event = CreateEvent (NULL, FALSE, FALSE, NULL);
112   if (sitter->end_sync_event == NULL)
113     {
114       _dbus_babysitter_unref (sitter);
115       return NULL;
116     }
117 #endif
118
119   sitter->child_handle = NULL;
120
121   sitter->socket_to_babysitter = sitter->socket_to_main = -1;
122
123   sitter->argc = 0;
124   sitter->argv = NULL;
125   sitter->envp = NULL;
126
127   sitter->watches = _dbus_watch_list_new ();
128   if (sitter->watches == NULL)
129     {
130       _dbus_babysitter_unref (sitter);
131       return NULL;
132     }
133
134   sitter->have_spawn_errno = FALSE;
135   sitter->have_child_status = FALSE;
136
137   return sitter;
138 }
139
140 /**
141  * Increment the reference count on the babysitter object.
142  *
143  * @param sitter the babysitter
144  * @returns the babysitter
145  */
146 DBusBabysitter *
147 _dbus_babysitter_ref (DBusBabysitter *sitter)
148 {
149   PING();
150   _dbus_assert (sitter != NULL);
151   _dbus_assert (sitter->refcount > 0);
152
153   sitter->refcount += 1;
154
155   return sitter;
156 }
157
158 /**
159  * Decrement the reference count on the babysitter object.
160  *
161  * @param sitter the babysitter
162  */
163 void
164 _dbus_babysitter_unref (DBusBabysitter *sitter)
165 {
166   int i;
167
168   PING();
169   _dbus_assert (sitter != NULL);
170   _dbus_assert (sitter->refcount > 0);
171
172   sitter->refcount -= 1;
173
174   if (sitter->refcount == 0)
175     {
176       if (sitter->socket_to_babysitter != -1)
177         {
178           _dbus_close_socket (sitter->socket_to_babysitter, NULL);
179           sitter->socket_to_babysitter = -1;
180         }
181
182       if (sitter->socket_to_main != -1)
183         {
184           _dbus_close_socket (sitter->socket_to_main, NULL);
185           sitter->socket_to_main = -1;
186         }
187
188       PING();
189       if (sitter->argv != NULL)
190         {
191           for (i = 0; i < sitter->argc; i++)
192             if (sitter->argv[i] != NULL)
193               {
194                 dbus_free (sitter->argv[i]);
195                 sitter->argv[i] = NULL;
196               }
197           dbus_free (sitter->argv);
198           sitter->argv = NULL;
199         }
200
201       if (sitter->envp != NULL)
202         {
203           char **e = sitter->envp;
204
205           while (*e)
206             dbus_free (*e++);
207           dbus_free (sitter->envp);
208           sitter->envp = NULL;
209         }
210
211       if (sitter->child_handle != NULL)
212         {
213           CloseHandle (sitter->child_handle);
214           sitter->child_handle = NULL;
215         }
216
217       if (sitter->sitter_watch)
218         {
219           _dbus_watch_invalidate (sitter->sitter_watch);
220           _dbus_watch_unref (sitter->sitter_watch);
221           sitter->sitter_watch = NULL;
222         }
223
224       if (sitter->watches)
225         _dbus_watch_list_free (sitter->watches);
226
227       if (sitter->start_sync_event != NULL)
228         {
229           PING();
230           CloseHandle (sitter->start_sync_event);
231           sitter->end_sync_event = NULL;
232         }
233
234 #ifdef DBUS_BUILD_TESTS
235       if (sitter->end_sync_event != NULL)
236         {
237           CloseHandle (sitter->end_sync_event);
238           sitter->end_sync_event = NULL;
239         }
240 #endif
241
242       dbus_free (sitter->executable);
243
244       dbus_free (sitter);
245     }
246 }
247
248 void
249 _dbus_babysitter_kill_child (DBusBabysitter *sitter)
250 {
251   PING();
252   if (sitter->child_handle == NULL)
253     return; /* child is already dead, or we're so hosed we'll never recover */
254
255   PING();
256   TerminateProcess (sitter->child_handle, 12345);
257 }
258
259 /**
260  * Checks whether the child has exited, without blocking.
261  *
262  * @param sitter the babysitter
263  */
264 dbus_bool_t
265 _dbus_babysitter_get_child_exited (DBusBabysitter *sitter)
266 {
267   PING();
268   return (sitter->child_handle == NULL);
269 }
270
271 /**
272  * Sets the #DBusError with an explanation of why the spawned
273  * child process exited (on a signal, or whatever). If
274  * the child process has not exited, does nothing (error
275  * will remain unset).
276  *
277  * @param sitter the babysitter
278  * @param error an error to fill in
279  */
280 void
281 _dbus_babysitter_set_child_exit_error (DBusBabysitter *sitter,
282                                        DBusError      *error)
283 {
284   PING();
285   if (!_dbus_babysitter_get_child_exited (sitter))
286     return;
287
288   PING();
289   if (sitter->have_spawn_errno)
290     {
291       dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED,
292                       "Failed to execute program %s: %s",
293                       sitter->executable, _dbus_strerror (sitter->spawn_errno));
294     }
295   else if (sitter->have_child_status)
296     {
297       PING();
298       dbus_set_error (error, DBUS_ERROR_SPAWN_CHILD_EXITED,
299                       "Process %s exited with status %d",
300                       sitter->executable, sitter->child_status);
301     }
302   else
303     {
304       PING();
305       dbus_set_error (error, DBUS_ERROR_FAILED,
306                       "Process %s exited, status unknown",
307                       sitter->executable);
308     }
309   PING();
310 }
311
312 dbus_bool_t
313 _dbus_babysitter_set_watch_functions (DBusBabysitter            *sitter,
314                                       DBusAddWatchFunction       add_function,
315                                       DBusRemoveWatchFunction    remove_function,
316                                       DBusWatchToggledFunction   toggled_function,
317                                       void                      *data,
318                                       DBusFreeFunction           free_data_function)
319 {
320   PING();
321   return _dbus_watch_list_set_functions (sitter->watches,
322                                          add_function,
323                                          remove_function,
324                                          toggled_function,
325                                          data,
326                                          free_data_function);
327 }
328
329 static dbus_bool_t
330 handle_watch (DBusWatch       *watch,
331               unsigned int     condition,
332               void            *data)
333 {
334   DBusBabysitter *sitter = data;
335
336   /* On Unix dbus-spawn uses a babysitter *process*, thus it has to
337    * actually send the exit statuses, error codes and whatnot through
338    * sockets and/or pipes. On Win32, the babysitter is jus a thread,
339    * so it can set the status fields directly in the babysitter struct
340    * just fine. The socket pipe is used just so we can watch it with
341    * select(), as soon as anything is written to it we know that the
342    * babysitter thread has recorded the status in the babysitter
343    * struct.
344    */
345
346   PING();
347   _dbus_close_socket (sitter->socket_to_babysitter, NULL);
348   PING();
349   sitter->socket_to_babysitter = -1;
350
351   return TRUE;
352 }
353
354 /* protect_argv lifted from GLib, relicensed by author, Tor Lillqvist */
355 static int
356 protect_argv (char  **argv,
357               char ***new_argv)
358 {
359   int i;
360   int argc = 0;
361
362   while (argv[argc])
363     ++argc;
364   *new_argv = dbus_malloc ((argc + 1) * sizeof (char *));
365   if (*new_argv == NULL)
366     return -1;
367
368   for (i = 0; i < argc; i++)
369     (*new_argv)[i] = NULL;
370
371   /* Quote each argv element if necessary, so that it will get
372    * reconstructed correctly in the C runtime startup code.  Note that
373    * the unquoting algorithm in the C runtime is really weird, and
374    * rather different than what Unix shells do. See stdargv.c in the C
375    * runtime sources (in the Platform SDK, in src/crt).
376    *
377    * Note that an new_argv[0] constructed by this function should
378    * *not* be passed as the filename argument to a spawn* or exec*
379    * family function. That argument should be the real file name
380    * without any quoting.
381    */
382   for (i = 0; i < argc; i++)
383     {
384       char *p = argv[i];
385       char *q;
386       int len = 0;
387       int need_dblquotes = FALSE;
388       while (*p)
389         {
390           if (*p == ' ' || *p == '\t')
391             need_dblquotes = TRUE;
392           else if (*p == '"')
393             len++;
394           else if (*p == '\\')
395             {
396               char *pp = p;
397               while (*pp && *pp == '\\')
398                 pp++;
399               if (*pp == '"')
400                 len++;
401             }
402           len++;
403           p++;
404         }
405
406       q = (*new_argv)[i] = dbus_malloc (len + need_dblquotes*2 + 1);
407
408       if (q == NULL)
409         return -1;
410
411
412       p = argv[i];
413
414       if (need_dblquotes)
415         *q++ = '"';
416
417       while (*p)
418         {
419           if (*p == '"')
420             *q++ = '\\';
421           else if (*p == '\\')
422             {
423               char *pp = p;
424               while (*pp && *pp == '\\')
425                 pp++;
426               if (*pp == '"')
427                 *q++ = '\\';
428             }
429           *q++ = *p;
430           p++;
431         }
432
433       if (need_dblquotes)
434         *q++ = '"';
435       *q++ = '\0';
436       /* printf ("argv[%d]:%s, need_dblquotes:%s len:%d => %s\n", i, argv[i], need_dblquotes?"TRUE":"FALSE", len, (*new_argv)[i]); */
437     }
438   (*new_argv)[argc] = NULL;
439
440   return argc;
441 }
442
443 static unsigned __stdcall
444 babysitter (void *parameter)
445 {
446   DBusBabysitter *sitter = (DBusBabysitter *) parameter;
447 #ifdef ENABLE_DBUSSOCKET
448   DBusSocket *sock;
449 #else
450   int fd;
451 #endif
452   PING();
453   _dbus_babysitter_ref (sitter);
454
455   if (sitter->child_setup)
456     {
457       PING();
458       (*sitter->child_setup) (sitter->user_data);
459     }
460
461   _dbus_verbose ("babysitter: spawning %s\n", sitter->executable);
462
463   PING();
464   if (sitter->envp != NULL)
465     sitter->child_handle = (HANDLE) spawnve (P_NOWAIT, sitter->executable,
466                            (const char * const *) sitter->argv,
467                            (const char * const *) sitter->envp);
468   else
469     sitter->child_handle = (HANDLE) spawnv (P_NOWAIT, sitter->executable,
470                                             (const char * const *) sitter->argv);
471
472   PING();
473   if (sitter->child_handle == (HANDLE) -1)
474     {
475       sitter->child_handle = NULL;
476       sitter->have_spawn_errno = TRUE;
477       sitter->spawn_errno = errno;
478     }
479
480   PING();
481   SetEvent (sitter->start_sync_event);
482
483   if (sitter->child_handle != NULL)
484     {
485       int ret;
486       DWORD status;
487
488       PING();
489       WaitForSingleObject (sitter->child_handle, INFINITE);
490
491       PING();
492       ret = GetExitCodeProcess (sitter->child_handle, &status);
493
494       sitter->child_status = status;
495       sitter->have_child_status = TRUE;
496
497       CloseHandle (sitter->child_handle);
498       sitter->child_handle = NULL;
499     }
500
501 #ifdef DBUS_BUILD_TESTS
502   SetEvent (sitter->end_sync_event);
503 #endif
504
505   PING();
506 #ifdef ENABLE_DBUSSOCKET
507   _dbus_handle_to_socket (sitter->socket_to_main, &sock);
508   send (sock->fd, " ", 1, 0);
509 #else
510   send (sitter->socket_to_main, " ", 1, 0);
511 #endif
512
513   _dbus_babysitter_unref (sitter);
514
515   return 0;
516 }
517
518 dbus_bool_t
519 _dbus_spawn_async_with_babysitter (DBusBabysitter           **sitter_p,
520                                    char                     **argv,
521                                    char                     **envp,
522                                    DBusSpawnChildSetupFunc    child_setup,
523                                    void                      *user_data,
524                                    DBusError                 *error)
525 {
526   DBusBabysitter *sitter;
527   HANDLE sitter_thread;
528   int sitter_thread_id;
529
530   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
531
532   *sitter_p = NULL;
533
534   PING();
535   sitter = _dbus_babysitter_new ();
536   if (sitter == NULL)
537     {
538       _DBUS_SET_OOM (error);
539       return FALSE;
540     }
541
542   sitter->child_setup = child_setup;
543   sitter->user_data = user_data;
544
545   sitter->executable = _dbus_strdup (argv[0]);
546   if (sitter->executable == NULL)
547     {
548       _DBUS_SET_OOM (error);
549       goto out0;
550     }
551
552   PING();
553   if (!_dbus_full_duplex_pipe (&sitter->socket_to_babysitter,
554                                &sitter->socket_to_main,
555                                FALSE, error))
556     goto out0;
557
558   sitter->sitter_watch = _dbus_watch_new (sitter->socket_to_babysitter,
559                                           DBUS_WATCH_READABLE,
560                                           TRUE, handle_watch, sitter, NULL);
561   PING();
562   if (sitter->sitter_watch == NULL)
563     {
564       _DBUS_SET_OOM (error);
565       goto out0;
566     }
567
568   PING();
569   if (!_dbus_watch_list_add_watch (sitter->watches,  sitter->sitter_watch))
570     {
571       _DBUS_SET_OOM (error);
572       goto out0;
573     }
574
575   sitter->argc = protect_argv (argv, &sitter->argv);
576   if (sitter->argc == -1)
577     {
578       _DBUS_SET_OOM (error);
579       goto out0;
580     }
581   sitter->envp = envp;
582
583   PING();
584   sitter_thread = (HANDLE) _beginthreadex (NULL, 0, babysitter,
585                   sitter, 0, &sitter_thread_id);
586
587   if (sitter_thread == 0)
588     {
589       PING();
590       dbus_set_error_const (error, DBUS_ERROR_SPAWN_FORK_FAILED,
591                             "Failed to create new thread");
592       goto out0;
593     }
594   CloseHandle (sitter_thread);
595
596   PING();
597   WaitForSingleObject (sitter->start_sync_event, INFINITE);
598
599   PING();
600   if (sitter_p != NULL)
601     *sitter_p = sitter;
602   else
603     _dbus_babysitter_unref (sitter);
604
605   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
606
607   PING();
608   return TRUE;
609
610 out0:
611   _dbus_babysitter_unref (sitter);
612
613   return FALSE;
614 }
615
616 #ifdef DBUS_BUILD_TESTS
617
618 #define LIVE_CHILDREN(sitter) ((sitter)->child_handle != NULL)
619
620 static void
621 _dbus_babysitter_block_for_child_exit (DBusBabysitter *sitter)
622 {
623   if (sitter->child_handle == NULL)
624     return;
625
626   WaitForSingleObject (sitter->end_sync_event, INFINITE);
627 }
628
629 static dbus_bool_t
630 check_spawn_nonexistent (void *data)
631 {
632   char *argv[4] = { NULL, NULL, NULL, NULL };
633   DBusBabysitter *sitter;
634   DBusError error;
635
636   sitter = NULL;
637
638   dbus_error_init (&error);
639
640   /*** Test launching nonexistent binary */
641
642   argv[0] = "/this/does/not/exist/32542sdgafgafdg";
643   if (_dbus_spawn_async_with_babysitter (&sitter, argv, NULL,
644                                          NULL, NULL,
645                                          &error))
646     {
647       _dbus_babysitter_block_for_child_exit (sitter);
648       _dbus_babysitter_set_child_exit_error (sitter, &error);
649     }
650
651   if (sitter)
652     _dbus_babysitter_unref (sitter);
653
654   if (!dbus_error_is_set (&error))
655     {
656       _dbus_warn ("Did not get an error launching nonexistent executable\n");
657       return FALSE;
658     }
659
660   if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
661         dbus_error_has_name (&error, DBUS_ERROR_SPAWN_EXEC_FAILED)))
662     {
663       _dbus_warn ("Not expecting error when launching nonexistent executable: %s: %s\n",
664                   error.name, error.message);
665       dbus_error_free (&error);
666       return FALSE;
667     }
668
669   dbus_error_free (&error);
670
671   return TRUE;
672 }
673
674 static dbus_bool_t
675 check_spawn_segfault (void *data)
676 {
677   char *argv[4] = { NULL, NULL, NULL, NULL };
678   DBusBabysitter *sitter;
679   DBusError error;
680
681   sitter = NULL;
682
683   dbus_error_init (&error);
684
685   /*** Test launching segfault binary */
686
687   argv[0] = TEST_SEGFAULT_BINARY;
688   if (_dbus_spawn_async_with_babysitter (&sitter, argv, NULL,
689                                          NULL, NULL,
690                                          &error))
691     {
692       _dbus_babysitter_block_for_child_exit (sitter);
693       _dbus_babysitter_set_child_exit_error (sitter, &error);
694     }
695
696   if (sitter)
697     _dbus_babysitter_unref (sitter);
698
699   if (!dbus_error_is_set (&error))
700     {
701       _dbus_warn ("Did not get an error launching segfaulting binary\n");
702       return FALSE;
703     }
704
705   if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
706         dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED)))
707     {
708       _dbus_warn ("Not expecting error when launching segfaulting executable: %s: %s\n",
709                   error.name, error.message);
710       dbus_error_free (&error);
711       return FALSE;
712     }
713
714   dbus_error_free (&error);
715
716   return TRUE;
717 }
718
719 static dbus_bool_t
720 check_spawn_exit (void *data)
721 {
722   char *argv[4] = { NULL, NULL, NULL, NULL };
723   DBusBabysitter *sitter;
724   DBusError error;
725
726   sitter = NULL;
727
728   dbus_error_init (&error);
729
730   /*** Test launching exit failure binary */
731
732   argv[0] = TEST_EXIT_BINARY;
733   if (_dbus_spawn_async_with_babysitter (&sitter, argv, NULL,
734                                          NULL, NULL,
735                                          &error))
736     {
737       _dbus_babysitter_block_for_child_exit (sitter);
738       _dbus_babysitter_set_child_exit_error (sitter, &error);
739     }
740
741   if (sitter)
742     _dbus_babysitter_unref (sitter);
743
744   if (!dbus_error_is_set (&error))
745     {
746       _dbus_warn ("Did not get an error launching binary that exited with failure code\n");
747       return FALSE;
748     }
749
750   if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
751         dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED)))
752     {
753       _dbus_warn ("Not expecting error when launching exiting executable: %s: %s\n",
754                   error.name, error.message);
755       dbus_error_free (&error);
756       return FALSE;
757     }
758
759   dbus_error_free (&error);
760
761   return TRUE;
762 }
763
764 static dbus_bool_t
765 check_spawn_and_kill (void *data)
766 {
767   char *argv[4] = { NULL, NULL, NULL, NULL };
768   DBusBabysitter *sitter;
769   DBusError error;
770
771   sitter = NULL;
772
773   dbus_error_init (&error);
774
775   /*** Test launching sleeping binary then killing it */
776
777   argv[0] = TEST_SLEEP_FOREVER_BINARY;
778   if (_dbus_spawn_async_with_babysitter (&sitter, argv, NULL,
779                                          NULL, NULL,
780                                          &error))
781     {
782       _dbus_babysitter_kill_child (sitter);
783
784       _dbus_babysitter_block_for_child_exit (sitter);
785
786       _dbus_babysitter_set_child_exit_error (sitter, &error);
787     }
788
789   if (sitter)
790     _dbus_babysitter_unref (sitter);
791
792   if (!dbus_error_is_set (&error))
793     {
794       _dbus_warn ("Did not get an error after killing spawned binary\n");
795       return FALSE;
796     }
797
798   if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
799         dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED)))
800     {
801       _dbus_warn ("Not expecting error when killing executable: %s: %s\n",
802                   error.name, error.message);
803       dbus_error_free (&error);
804       return FALSE;
805     }
806
807   dbus_error_free (&error);
808
809   return TRUE;
810 }
811
812 dbus_bool_t
813 _dbus_spawn_test (const char *test_data_dir)
814 {
815   if (!_dbus_test_oom_handling ("spawn_nonexistent",
816                                 check_spawn_nonexistent,
817                                 NULL))
818     return FALSE;
819
820   /* Don't run the obnoxious segfault test by default,
821    * it's a pain to have to click all those error boxes.
822    */
823   if (getenv ("DO_SEGFAULT_TEST"))
824     if (!_dbus_test_oom_handling ("spawn_segfault",
825                                   check_spawn_segfault,
826                                   NULL))
827       return FALSE;
828
829   if (!_dbus_test_oom_handling ("spawn_exit",
830                                 check_spawn_exit,
831                                 NULL))
832     return FALSE;
833
834   if (!_dbus_test_oom_handling ("spawn_and_kill",
835                                 check_spawn_and_kill,
836                                 NULL))
837     return FALSE;
838
839   return TRUE;
840 }
841 #endif