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     DBusAtomic refcount;
64 
65     HANDLE start_sync_event;
66 
67     char *log_name;
68 
69     int argc;
70     char **argv;
71     char **envp;
72 
73     HANDLE thread_handle;
74     HANDLE child_handle;
75     DBusSocket socket_to_babysitter;	/* Connection to the babysitter thread */
76     DBusSocket socket_to_main;
77 
78     DBusWatchList *watches;
79     DBusWatch *sitter_watch;
80     DBusBabysitterFinishedFunc finished_cb;
81     void *finished_data;
82 
83     dbus_bool_t have_spawn_errno;
84     int spawn_errno;
85     dbus_bool_t have_child_status;
86     int child_status;
87   };
88 
89 static void
_dbus_babysitter_trace_ref(DBusBabysitter * sitter,int old_refcount,int new_refcount,const char * why)90 _dbus_babysitter_trace_ref (DBusBabysitter *sitter,
91     int old_refcount,
92     int new_refcount,
93     const char *why)
94 {
95 #ifdef DBUS_ENABLE_VERBOSE_MODE
96   static int enabled = -1;
97 
98   _dbus_trace_ref ("DBusBabysitter", sitter, old_refcount, new_refcount, why,
99       "DBUS_BABYSITTER_TRACE", &enabled);
100 #endif
101 }
102 
103 static DBusBabysitter*
_dbus_babysitter_new(void)104 _dbus_babysitter_new (void)
105 {
106   DBusBabysitter *sitter;
107   dbus_int32_t old_refcount;
108 
109   sitter = dbus_new0 (DBusBabysitter, 1);
110   if (sitter == NULL)
111     return NULL;
112 
113   old_refcount = _dbus_atomic_inc (&sitter->refcount);
114 
115   _dbus_babysitter_trace_ref (sitter, old_refcount, old_refcount+1, __FUNCTION__);
116 
117   sitter->start_sync_event = CreateEvent (NULL, FALSE, FALSE, NULL);
118   if (sitter->start_sync_event == NULL)
119     {
120       _dbus_babysitter_unref (sitter);
121       return NULL;
122     }
123 
124   sitter->child_handle = NULL;
125 
126   sitter->socket_to_babysitter = sitter->socket_to_main = _dbus_socket_get_invalid ();
127 
128   sitter->argc = 0;
129   sitter->argv = NULL;
130   sitter->envp = NULL;
131 
132   sitter->watches = _dbus_watch_list_new ();
133   if (sitter->watches == NULL)
134     {
135       _dbus_babysitter_unref (sitter);
136       return NULL;
137     }
138 
139   sitter->have_spawn_errno = FALSE;
140   sitter->have_child_status = FALSE;
141 
142   return sitter;
143 }
144 
145 /**
146  * Increment the reference count on the babysitter object.
147  *
148  * @param sitter the babysitter
149  * @returns the babysitter
150  */
151 DBusBabysitter *
_dbus_babysitter_ref(DBusBabysitter * sitter)152 _dbus_babysitter_ref (DBusBabysitter *sitter)
153 {
154   dbus_int32_t old_refcount;
155   PING();
156   _dbus_assert (sitter != NULL);
157 
158   old_refcount = _dbus_atomic_inc (&sitter->refcount);
159   _dbus_assert (old_refcount > 0);
160   _dbus_babysitter_trace_ref (sitter, old_refcount, old_refcount+1, __FUNCTION__);
161 
162   return sitter;
163 }
164 
165 static void
close_socket_to_babysitter(DBusBabysitter * sitter)166 close_socket_to_babysitter (DBusBabysitter *sitter)
167 {
168   _dbus_verbose ("Closing babysitter\n");
169 
170   if (sitter->sitter_watch != NULL)
171     {
172       _dbus_assert (sitter->watches != NULL);
173       _dbus_watch_list_remove_watch (sitter->watches,  sitter->sitter_watch);
174       _dbus_watch_invalidate (sitter->sitter_watch);
175       _dbus_watch_unref (sitter->sitter_watch);
176       sitter->sitter_watch = NULL;
177     }
178 
179   if (sitter->socket_to_babysitter.sock != INVALID_SOCKET)
180     {
181       _dbus_close_socket (sitter->socket_to_babysitter, NULL);
182       sitter->socket_to_babysitter.sock = INVALID_SOCKET;
183     }
184 }
185 
186 /**
187  * Decrement the reference count on the babysitter object.
188  *
189  * @param sitter the babysitter
190  */
191 void
_dbus_babysitter_unref(DBusBabysitter * sitter)192 _dbus_babysitter_unref (DBusBabysitter *sitter)
193 {
194   int i;
195   dbus_int32_t old_refcount;
196 
197   PING();
198   _dbus_assert (sitter != NULL);
199 
200   old_refcount = _dbus_atomic_dec (&sitter->refcount);
201   _dbus_assert (old_refcount > 0);
202   _dbus_babysitter_trace_ref (sitter, old_refcount, old_refcount-1, __FUNCTION__);
203 
204   if (old_refcount == 1)
205     {
206       close_socket_to_babysitter (sitter);
207 
208       if (sitter->socket_to_main.sock != INVALID_SOCKET)
209         {
210           _dbus_close_socket (sitter->socket_to_main, NULL);
211           sitter->socket_to_main.sock = INVALID_SOCKET;
212         }
213 
214       PING();
215       if (sitter->argv != NULL)
216         {
217           for (i = 0; i < sitter->argc; i++)
218             if (sitter->argv[i] != NULL)
219               {
220                 dbus_free (sitter->argv[i]);
221                 sitter->argv[i] = NULL;
222               }
223           dbus_free (sitter->argv);
224           sitter->argv = NULL;
225         }
226 
227       if (sitter->envp != NULL)
228         {
229           char **e = sitter->envp;
230 
231           while (*e)
232             dbus_free (*e++);
233           dbus_free (sitter->envp);
234           sitter->envp = NULL;
235         }
236 
237       if (sitter->child_handle != NULL)
238         {
239           CloseHandle (sitter->child_handle);
240           sitter->child_handle = NULL;
241         }
242 
243       if (sitter->sitter_watch)
244         {
245           _dbus_watch_invalidate (sitter->sitter_watch);
246           _dbus_watch_unref (sitter->sitter_watch);
247           sitter->sitter_watch = NULL;
248         }
249 
250       if (sitter->watches)
251         _dbus_watch_list_free (sitter->watches);
252 
253       if (sitter->start_sync_event != NULL)
254         {
255           PING();
256           CloseHandle (sitter->start_sync_event);
257           sitter->start_sync_event = NULL;
258         }
259 
260       if (sitter->thread_handle)
261         {
262           CloseHandle (sitter->thread_handle);
263           sitter->thread_handle = NULL;
264         }
265 
266       dbus_free (sitter->log_name);
267 
268       dbus_free (sitter);
269     }
270 }
271 
272 void
_dbus_babysitter_kill_child(DBusBabysitter * sitter)273 _dbus_babysitter_kill_child (DBusBabysitter *sitter)
274 {
275   PING();
276   if (sitter->child_handle == NULL)
277     return; /* child is already dead, or we're so hosed we'll never recover */
278 
279   PING();
280   TerminateProcess (sitter->child_handle, 12345);
281 }
282 
283 /**
284  * Checks whether the child has exited, without blocking.
285  *
286  * @param sitter the babysitter
287  */
288 dbus_bool_t
_dbus_babysitter_get_child_exited(DBusBabysitter * sitter)289 _dbus_babysitter_get_child_exited (DBusBabysitter *sitter)
290 {
291   PING();
292   return (sitter->child_handle == NULL);
293 }
294 
295 /**
296  * Gets the exit status of the child. We do this so implementation specific
297  * detail is not cluttering up dbus, for example the system launcher code.
298  * This can only be called if the child has exited, i.e. call
299  * _dbus_babysitter_get_child_exited(). It returns FALSE if the child
300  * did not return a status code, e.g. because the child was signaled
301  * or we failed to ever launch the child in the first place.
302  *
303  * @param sitter the babysitter
304  * @param status the returned status code
305  * @returns #FALSE on failure
306  */
307 dbus_bool_t
_dbus_babysitter_get_child_exit_status(DBusBabysitter * sitter,int * status)308 _dbus_babysitter_get_child_exit_status (DBusBabysitter *sitter,
309                                         int            *status)
310 {
311   if (!_dbus_babysitter_get_child_exited (sitter))
312     _dbus_assert_not_reached ("Child has not exited");
313 
314   if (!sitter->have_child_status ||
315       sitter->child_status == STILL_ACTIVE)
316     return FALSE;
317 
318   *status = sitter->child_status;
319   return TRUE;
320 }
321 
322 /**
323  * Sets the #DBusError with an explanation of why the spawned
324  * child process exited (on a signal, or whatever). If
325  * the child process has not exited, does nothing (error
326  * will remain unset).
327  *
328  * @param sitter the babysitter
329  * @param error an error to fill in
330  */
331 void
_dbus_babysitter_set_child_exit_error(DBusBabysitter * sitter,DBusError * error)332 _dbus_babysitter_set_child_exit_error (DBusBabysitter *sitter,
333                                        DBusError      *error)
334 {
335   PING();
336   if (!_dbus_babysitter_get_child_exited (sitter))
337     return;
338 
339   PING();
340   if (sitter->have_spawn_errno)
341     {
342       char *emsg = _dbus_win_error_string (sitter->spawn_errno);
343       dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED,
344                       "Failed to execute program %s: %s",
345                       sitter->log_name, emsg);
346       _dbus_win_free_error_string (emsg);
347     }
348   else if (sitter->have_child_status)
349     {
350       PING();
351       dbus_set_error (error, DBUS_ERROR_SPAWN_CHILD_EXITED,
352                       "Process %s exited with status %d",
353                       sitter->log_name, sitter->child_status);
354     }
355   else
356     {
357       PING();
358       dbus_set_error (error, DBUS_ERROR_FAILED,
359                       "Process %s exited, status unknown",
360                       sitter->log_name);
361     }
362   PING();
363 }
364 
365 dbus_bool_t
_dbus_babysitter_set_watch_functions(DBusBabysitter * sitter,DBusAddWatchFunction add_function,DBusRemoveWatchFunction remove_function,DBusWatchToggledFunction toggled_function,void * data,DBusFreeFunction free_data_function)366 _dbus_babysitter_set_watch_functions (DBusBabysitter            *sitter,
367                                       DBusAddWatchFunction       add_function,
368                                       DBusRemoveWatchFunction    remove_function,
369                                       DBusWatchToggledFunction   toggled_function,
370                                       void                      *data,
371                                       DBusFreeFunction           free_data_function)
372 {
373   PING();
374   return _dbus_watch_list_set_functions (sitter->watches,
375                                          add_function,
376                                          remove_function,
377                                          toggled_function,
378                                          data,
379                                          free_data_function);
380 }
381 
382 static dbus_bool_t
handle_watch(DBusWatch * watch,unsigned int condition,void * data)383 handle_watch (DBusWatch       *watch,
384               unsigned int     condition,
385               void            *data)
386 {
387   DBusBabysitter *sitter = data;
388 
389   /* On Unix dbus-spawn uses a babysitter *process*, thus it has to
390    * actually send the exit statuses, error codes and whatnot through
391    * sockets and/or pipes. On Win32, the babysitter is jus a thread,
392    * so it can set the status fields directly in the babysitter struct
393    * just fine. The socket pipe is used just so we can watch it with
394    * select(), as soon as anything is written to it we know that the
395    * babysitter thread has recorded the status in the babysitter
396    * struct.
397    */
398 
399   PING();
400   close_socket_to_babysitter (sitter);
401   PING();
402 
403   if (_dbus_babysitter_get_child_exited (sitter) &&
404       sitter->finished_cb != NULL)
405     {
406       sitter->finished_cb (sitter, sitter->finished_data);
407       sitter->finished_cb = NULL;
408     }
409 
410   return TRUE;
411 }
412 
413 /* protect_argv lifted from GLib, relicensed by author, Tor Lillqvist */
414 static int
protect_argv(char * const * argv,char *** new_argv)415 protect_argv (char  * const *argv,
416               char        ***new_argv)
417 {
418   int i;
419   int argc = 0;
420 
421   while (argv[argc])
422     ++argc;
423   *new_argv = dbus_malloc ((argc + 1) * sizeof (char *));
424   if (*new_argv == NULL)
425     return -1;
426 
427   for (i = 0; i < argc; i++)
428     (*new_argv)[i] = NULL;
429 
430   /* Quote each argv element if necessary, so that it will get
431    * reconstructed correctly in the C runtime startup code.  Note that
432    * the unquoting algorithm in the C runtime is really weird, and
433    * rather different than what Unix shells do. See stdargv.c in the C
434    * runtime sources (in the Platform SDK, in src/crt).
435    *
436    * Note that an new_argv[0] constructed by this function should
437    * *not* be passed as the filename argument to a spawn* or exec*
438    * family function. That argument should be the real file name
439    * without any quoting.
440    */
441   for (i = 0; i < argc; i++)
442     {
443       const char *p = argv[i];
444       char *q;
445       int len = 0;
446       int need_dblquotes = FALSE;
447       while (*p)
448         {
449           if (*p == ' ' || *p == '\t')
450             need_dblquotes = TRUE;
451           else if (*p == '"')
452             len++;
453           else if (*p == '\\')
454             {
455               const char *pp = p;
456               while (*pp && *pp == '\\')
457                 pp++;
458               if (*pp == '"')
459                 len++;
460             }
461           len++;
462           p++;
463         }
464 
465       q = (*new_argv)[i] = dbus_malloc (len + need_dblquotes*2 + 1);
466 
467       if (q == NULL)
468         return -1;
469 
470 
471       p = argv[i];
472 
473       if (need_dblquotes)
474         *q++ = '"';
475 
476       while (*p)
477         {
478           if (*p == '"')
479             *q++ = '\\';
480           else if (*p == '\\')
481             {
482               const char *pp = p;
483               while (*pp && *pp == '\\')
484                 pp++;
485               if (*pp == '"')
486                 *q++ = '\\';
487             }
488           *q++ = *p;
489           p++;
490         }
491 
492       if (need_dblquotes)
493         *q++ = '"';
494       *q++ = '\0';
495       /* printf ("argv[%d]:%s, need_dblquotes:%s len:%d => %s\n", i, argv[i], need_dblquotes?"TRUE":"FALSE", len, (*new_argv)[i]); */
496     }
497   (*new_argv)[argc] = NULL;
498 
499   return argc;
500 }
501 
502 
503 /* From GPGME, relicensed by g10 Code GmbH.  */
504 static char *
compose_string(char ** strings,char separator)505 compose_string (char **strings, char separator)
506 {
507   int i;
508   int n = 0;
509   char *buf;
510   char *p;
511 
512   if (!strings || !strings[0])
513     return 0;
514   for (i = 0; strings[i]; i++)
515     n += strlen (strings[i]) + 1;
516   n++;
517 
518   buf = p = malloc (n);
519   if (!buf)
520     return NULL;
521   for (i = 0; strings[i]; i++)
522     {
523       strcpy (p, strings[i]);
524       p += strlen (strings[i]);
525       *(p++) = separator;
526     }
527   p--;
528   *(p++) = '\0';
529   *p = '\0';
530 
531   return buf;
532 }
533 
534 static char *
build_commandline(char ** argv)535 build_commandline (char **argv)
536 {
537   return compose_string (argv, ' ');
538 }
539 
540 static char *
build_env_string(char ** envp)541 build_env_string (char** envp)
542 {
543   return compose_string (envp, '\0');
544 }
545 
546 static HANDLE
spawn_program(char * name,char ** argv,char ** envp)547 spawn_program (char* name, char** argv, char** envp)
548 {
549   PROCESS_INFORMATION pi = { NULL, 0, 0, 0 };
550   STARTUPINFOA si;
551   char *arg_string, *env_string;
552   BOOL result;
553 
554 #ifdef DBUS_WINCE
555   if (argv && argv[0])
556     arg_string = build_commandline (argv + 1);
557   else
558     arg_string = NULL;
559 #else
560   arg_string = build_commandline (argv);
561 #endif
562   if (!arg_string)
563     return INVALID_HANDLE_VALUE;
564 
565   env_string = build_env_string(envp);
566 
567   memset (&si, 0, sizeof (si));
568   si.cb = sizeof (si);
569 #ifdef DBUS_WINCE
570   result = CreateProcessA (name, arg_string, NULL, NULL, FALSE, 0,
571 #else
572   result = CreateProcessA (NULL, arg_string, NULL, NULL, FALSE, 0,
573 #endif
574 			   (LPVOID)env_string, NULL, &si, &pi);
575   free (arg_string);
576   if (env_string)
577     free (env_string);
578 
579   if (!result)
580     return INVALID_HANDLE_VALUE;
581 
582   CloseHandle (pi.hThread);
583   return pi.hProcess;
584 }
585 
586 
587 static DWORD __stdcall
babysitter(void * parameter)588 babysitter (void *parameter)
589 {
590   int ret = 0;
591   DBusBabysitter *sitter = (DBusBabysitter *) parameter;
592   HANDLE handle;
593 
594   PING();
595   _dbus_verbose ("babysitter: spawning %s\n", sitter->log_name);
596 
597   PING();
598   handle = spawn_program (sitter->log_name, sitter->argv, sitter->envp);
599 
600   PING();
601   if (handle != INVALID_HANDLE_VALUE)
602     {
603       sitter->child_handle = handle;
604     }
605   else
606     {
607       sitter->child_handle = NULL;
608       sitter->have_spawn_errno = TRUE;
609       sitter->spawn_errno = GetLastError();
610     }
611 
612   PING();
613   SetEvent (sitter->start_sync_event);
614 
615   if (sitter->child_handle != NULL)
616     {
617       DWORD status;
618 
619       PING();
620       // wait until process finished
621       WaitForSingleObject (sitter->child_handle, INFINITE);
622 
623       PING();
624       ret = GetExitCodeProcess (sitter->child_handle, &status);
625       if (ret)
626         {
627           sitter->child_status = status;
628           sitter->have_child_status = TRUE;
629         }
630 
631       CloseHandle (sitter->child_handle);
632       sitter->child_handle = NULL;
633     }
634 
635   PING();
636   send (sitter->socket_to_main.sock, " ", 1, 0);
637 
638   _dbus_babysitter_unref (sitter);
639 
640   return ret ? 0 : 1;
641 }
642 
643 dbus_bool_t
_dbus_spawn_async_with_babysitter(DBusBabysitter ** sitter_p,const char * log_name,char * const * argv,char ** envp,DBusSpawnFlags flags _DBUS_GNUC_UNUSED,DBusSpawnChildSetupFunc child_setup _DBUS_GNUC_UNUSED,void * user_data _DBUS_GNUC_UNUSED,DBusError * error)644 _dbus_spawn_async_with_babysitter (DBusBabysitter           **sitter_p,
645                                    const char                *log_name,
646                                    char              * const *argv,
647                                    char                     **envp,
648                                    DBusSpawnFlags             flags _DBUS_GNUC_UNUSED,
649                                    DBusSpawnChildSetupFunc    child_setup _DBUS_GNUC_UNUSED,
650                                    void                      *user_data _DBUS_GNUC_UNUSED,
651                                    DBusError                 *error)
652 {
653   DBusBabysitter *sitter;
654   DWORD sitter_thread_id;
655 
656   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
657   _dbus_assert (argv[0] != NULL);
658 
659   if (sitter_p != NULL)
660     *sitter_p = NULL;
661 
662   PING();
663   sitter = _dbus_babysitter_new ();
664   if (sitter == NULL)
665     {
666       _DBUS_SET_OOM (error);
667       return FALSE;
668     }
669 
670   sitter->log_name = _dbus_strdup (log_name);
671   if (sitter->log_name == NULL && log_name != NULL)
672     {
673       _DBUS_SET_OOM (error);
674       goto out0;
675     }
676 
677   if (sitter->log_name == NULL)
678     sitter->log_name = _dbus_strdup (argv[0]);
679 
680   if (sitter->log_name == NULL)
681     {
682       _DBUS_SET_OOM (error);
683       goto out0;
684     }
685 
686   PING();
687   if (!_dbus_socketpair (&sitter->socket_to_babysitter,
688                          &sitter->socket_to_main,
689                          FALSE, error))
690     goto out0;
691 
692   sitter->sitter_watch = _dbus_watch_new (sitter->socket_to_babysitter,
693                                           DBUS_WATCH_READABLE,
694                                           TRUE, handle_watch, sitter, NULL);
695   PING();
696   if (sitter->sitter_watch == NULL)
697     {
698       _DBUS_SET_OOM (error);
699       goto out0;
700     }
701 
702   PING();
703   if (!_dbus_watch_list_add_watch (sitter->watches,  sitter->sitter_watch))
704     {
705       /* we need to free it early so the destructor won't try to remove it
706        * without it having been added, which DBusLoop doesn't allow */
707       _dbus_watch_invalidate (sitter->sitter_watch);
708       _dbus_watch_unref (sitter->sitter_watch);
709       sitter->sitter_watch = NULL;
710 
711       _DBUS_SET_OOM (error);
712       goto out0;
713     }
714 
715   sitter->argc = protect_argv (argv, &sitter->argv);
716   if (sitter->argc == -1)
717     {
718       _DBUS_SET_OOM (error);
719       goto out0;
720     }
721   sitter->envp = envp;
722 
723   PING();
724   sitter->thread_handle = (HANDLE) CreateThread (NULL, 0, babysitter,
725                   _dbus_babysitter_ref (sitter), 0, &sitter_thread_id);
726 
727   if (sitter->thread_handle == NULL)
728     {
729       PING();
730       dbus_set_error_const (error, DBUS_ERROR_SPAWN_FORK_FAILED,
731                             "Failed to create new thread");
732       goto out0;
733     }
734 
735   PING();
736   WaitForSingleObject (sitter->start_sync_event, INFINITE);
737 
738   PING();
739   if (sitter_p != NULL)
740     *sitter_p = sitter;
741   else
742     _dbus_babysitter_unref (sitter);
743 
744   _DBUS_ASSERT_ERROR_IS_CLEAR (error);
745 
746   PING();
747   return TRUE;
748 
749 out0:
750   _dbus_babysitter_unref (sitter);
751 
752   return FALSE;
753 }
754 
755 void
_dbus_babysitter_set_result_function(DBusBabysitter * sitter,DBusBabysitterFinishedFunc finished,void * user_data)756 _dbus_babysitter_set_result_function  (DBusBabysitter             *sitter,
757                                        DBusBabysitterFinishedFunc  finished,
758                                        void                       *user_data)
759 {
760   sitter->finished_cb = finished;
761   sitter->finished_data = user_data;
762 }
763 
764 #define LIVE_CHILDREN(sitter) ((sitter)->child_handle != NULL)
765 
766 void
_dbus_babysitter_block_for_child_exit(DBusBabysitter * sitter)767 _dbus_babysitter_block_for_child_exit (DBusBabysitter *sitter)
768 {
769   /* The thread terminates after the child does. We want to wait for the thread,
770    * not just the child, to avoid data races and ensure that it has freed all
771    * its memory. */
772   WaitForSingleObject (sitter->thread_handle, INFINITE);
773 }
774