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