~ubuntu-branches/ubuntu/maverick/dbus/maverick-proposed

« back to all changes in this revision

Viewing changes to dbus/dbus-spawn-win.c

  • Committer: Bazaar Package Importer
  • Author(s): Jonathan Riddell
  • Date: 2010-09-27 13:06:32 UTC
  • mfrom: (1.1.23 upstream)
  • Revision ID: james.westby@ubuntu.com-20100927130632-bqs145trvchd2lmf
Tags: 1.4.0-0ubuntu1
* New upstream release
 - Fixes https://bugs.freedesktop.org/show_bug.cgi?id=17754 Race condition in protected_change_timeout
 - Requested by various upstream KDE developers http://lists.kde.org/?t=128514970000004&r=1&w=2

Show diffs side-by-side

added added

removed removed

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