D-Bus: dbus-spawn-win.c Source File

D-Bus 1.10.22

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
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
61 struct DBusBabysitter
62 {
63 DBusAtomic refcount;
64
65 HANDLE start_sync_event;
66 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
67
68 HANDLE end_sync_event;
69 #endif
70
71 char *log_name;
72 DBusSpawnChildSetupFunc child_setup;
73 void *user_data;
74
75 int argc;
76 char **argv;
77 char **envp;
78
79 HANDLE child_handle;
80 DBusSocket socket_to_babysitter; /* Connection to the babysitter thread */
81 DBusSocket socket_to_main;
82
83 DBusWatchList *watches;
84 DBusWatch *sitter_watch;
85 DBusBabysitterFinishedFunc finished_cb;
86 void *finished_data;
87
88 dbus_bool_t have_spawn_errno;
89 int spawn_errno;
90 dbus_bool_t have_child_status;
91 int child_status;
92 };
93
94 static void
95 _dbus_babysitter_trace_ref (DBusBabysitter *sitter,
96 int old_refcount,
97 int new_refcount,
98 const char *why)
99 {
100 #ifdef DBUS_ENABLE_VERBOSE_MODE
101 static int enabled = -1;
102
103 _dbus_trace_ref ("DBusBabysitter", sitter, old_refcount, new_refcount, why,
104 "DBUS_BABYSITTER_TRACE", &enabled);
105 #endif
106 }
107
108 static DBusBabysitter*
109 _dbus_babysitter_new (void)
110 {
111 DBusBabysitter *sitter;
112 dbus_int32_t old_refcount;
113
114 sitter = dbus_new0 (DBusBabysitter, 1);
115 if (sitter == NULL)
116 return NULL;
117
118 old_refcount = _dbus_atomic_inc (&sitter->refcount);
119
120 _dbus_babysitter_trace_ref (sitter, old_refcount, old_refcount+1, __FUNCTION__);
121
122 sitter->start_sync_event = CreateEvent (NULL, FALSE, FALSE, NULL);
123 if (sitter->start_sync_event == NULL)
124 {
125 _dbus_babysitter_unref (sitter);
126 return NULL;
127 }
128
129 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
130 sitter->end_sync_event = CreateEvent (NULL, FALSE, FALSE, NULL);
131 if (sitter->end_sync_event == NULL)
132 {
133 _dbus_babysitter_unref (sitter);
134 return NULL;
135 }
136 #endif
137
138 sitter->child_handle = NULL;
139
140 sitter->socket_to_babysitter = sitter->socket_to_main = _dbus_socket_get_invalid ();
141
142 sitter->argc = 0;
143 sitter->argv = NULL;
144 sitter->envp = NULL;
145
146 sitter->watches = _dbus_watch_list_new ();
147 if (sitter->watches == NULL)
148 {
149 _dbus_babysitter_unref (sitter);
150 return NULL;
151 }
152
153 sitter->have_spawn_errno = FALSE;
154 sitter->have_child_status = FALSE;
155
156 return sitter;
157 }
158
165 DBusBabysitter *
166 _dbus_babysitter_ref (DBusBabysitter *sitter)
167 {
168 dbus_int32_t old_refcount;
169 PING();
170 _dbus_assert (sitter != NULL);
171
172 old_refcount = _dbus_atomic_inc (&sitter->refcount);
173 _dbus_assert (old_refcount > 0);
174 _dbus_babysitter_trace_ref (sitter, old_refcount, old_refcount+1, __FUNCTION__);
175
176 return sitter;
177 }
178
179 static void
180 close_socket_to_babysitter (DBusBabysitter *sitter)
181 {
182 _dbus_verbose ("Closing babysitter\n");
183
184 if (sitter->sitter_watch != NULL)
185 {
186 _dbus_assert (sitter->watches != NULL);
187 _dbus_watch_list_remove_watch (sitter->watches, sitter->sitter_watch);
188 _dbus_watch_invalidate (sitter->sitter_watch);
189 _dbus_watch_unref (sitter->sitter_watch);
190 sitter->sitter_watch = NULL;
191 }
192
193 if (sitter->socket_to_babysitter.sock != INVALID_SOCKET)
194 {
195 _dbus_close_socket (sitter->socket_to_babysitter, NULL);
196 sitter->socket_to_babysitter.sock = INVALID_SOCKET;
197 }
198 }
199
205 void
206 _dbus_babysitter_unref (DBusBabysitter *sitter)
207 {
208 int i;
209 dbus_int32_t old_refcount;
210
211 PING();
212 _dbus_assert (sitter != NULL);
213
214 old_refcount = _dbus_atomic_dec (&sitter->refcount);
215 _dbus_assert (old_refcount > 0);
216 _dbus_babysitter_trace_ref (sitter, old_refcount, old_refcount-1, __FUNCTION__);
217
218 if (old_refcount == 1)
219 {
220 close_socket_to_babysitter (sitter);
221
222 if (sitter->socket_to_main.sock != INVALID_SOCKET)
223 {
224 _dbus_close_socket (sitter->socket_to_main, NULL);
225 sitter->socket_to_main.sock = INVALID_SOCKET;
226 }
227
228 PING();
229 if (sitter->argv != NULL)
230 {
231 for (i = 0; i < sitter->argc; i++)
232 if (sitter->argv[i] != NULL)
233 {
234 dbus_free (sitter->argv[i]);
235 sitter->argv[i] = NULL;
236 }
237 dbus_free (sitter->argv);
238 sitter->argv = NULL;
239 }
240
241 if (sitter->envp != NULL)
242 {
243 char **e = sitter->envp;
244
245 while (*e)
246 dbus_free (*e++);
247 dbus_free (sitter->envp);
248 sitter->envp = NULL;
249 }
250
251 if (sitter->child_handle != NULL)
252 {
253 CloseHandle (sitter->child_handle);
254 sitter->child_handle = NULL;
255 }
256
257 if (sitter->sitter_watch)
258 {
259 _dbus_watch_invalidate (sitter->sitter_watch);
260 _dbus_watch_unref (sitter->sitter_watch);
261 sitter->sitter_watch = NULL;
262 }
263
264 if (sitter->watches)
265 _dbus_watch_list_free (sitter->watches);
266
267 if (sitter->start_sync_event != NULL)
268 {
269 PING();
270 CloseHandle (sitter->start_sync_event);
271 sitter->start_sync_event = NULL;
272 }
273
274 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
275 if (sitter->end_sync_event != NULL)
276 {
277 CloseHandle (sitter->end_sync_event);
278 sitter->end_sync_event = NULL;
279 }
280 #endif
281
282 dbus_free (sitter->log_name);
283
284 dbus_free (sitter);
285 }
286 }
287
288 void
289 _dbus_babysitter_kill_child (DBusBabysitter *sitter)
290 {
291 PING();
292 if (sitter->child_handle == NULL)
293 return; /* child is already dead, or we're so hosed we'll never recover */
294
295 PING();
296 TerminateProcess (sitter->child_handle, 12345);
297 }
298
304 dbus_bool_t
305 _dbus_babysitter_get_child_exited (DBusBabysitter *sitter)
306 {
307 PING();
308 return (sitter->child_handle == NULL);
309 }
310
323 dbus_bool_t
324 _dbus_babysitter_get_child_exit_status (DBusBabysitter *sitter,
325 int *status)
326 {
327 if (!_dbus_babysitter_get_child_exited (sitter))
328 _dbus_assert_not_reached ("Child has not exited");
329
330 if (!sitter->have_child_status ||
331 sitter->child_status == STILL_ACTIVE)
332 return FALSE;
333
334 *status = sitter->child_status;
335 return TRUE;
336 }
337
347 void
348 _dbus_babysitter_set_child_exit_error (DBusBabysitter *sitter,
349 DBusError *error)
350 {
351 PING();
352 if (!_dbus_babysitter_get_child_exited (sitter))
353 return;
354
355 PING();
356 if (sitter->have_spawn_errno)
357 {
358 char *emsg = _dbus_win_error_string (sitter->spawn_errno);
359 dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED,
360 "Failed to execute program %s: %s",
361 sitter->log_name, emsg);
362 _dbus_win_free_error_string (emsg);
363 }
364 else if (sitter->have_child_status)
365 {
366 PING();
367 dbus_set_error (error, DBUS_ERROR_SPAWN_CHILD_EXITED,
368 "Process %s exited with status %d",
369 sitter->log_name, sitter->child_status);
370 }
371 else
372 {
373 PING();
374 dbus_set_error (error, DBUS_ERROR_FAILED,
375 "Process %s exited, status unknown",
376 sitter->log_name);
377 }
378 PING();
379 }
380
381 dbus_bool_t
382 _dbus_babysitter_set_watch_functions (DBusBabysitter *sitter,
383 DBusAddWatchFunction add_function,
384 DBusRemoveWatchFunction remove_function,
385 DBusWatchToggledFunction toggled_function,
386 void *data,
387 DBusFreeFunction free_data_function)
388 {
389 PING();
390 return _dbus_watch_list_set_functions (sitter->watches,
391 add_function,
392 remove_function,
393 toggled_function,
394 data,
395 free_data_function);
396 }
397
398 static dbus_bool_t
399 handle_watch (DBusWatch *watch,
400 unsigned int condition,
401 void *data)
402 {
403 DBusBabysitter *sitter = data;
404
405 /* On Unix dbus-spawn uses a babysitter *process*, thus it has to
406 * actually send the exit statuses, error codes and whatnot through
407 * sockets and/or pipes. On Win32, the babysitter is jus a thread,
408 * so it can set the status fields directly in the babysitter struct
409 * just fine. The socket pipe is used just so we can watch it with
410 * select(), as soon as anything is written to it we know that the
411 * babysitter thread has recorded the status in the babysitter
412 * struct.
413 */
414
415 PING();
416 close_socket_to_babysitter (sitter);
417 PING();
418
419 if (_dbus_babysitter_get_child_exited (sitter) &&
420 sitter->finished_cb != NULL)
421 {
422 sitter->finished_cb (sitter, sitter->finished_data);
423 sitter->finished_cb = NULL;
424 }
425
426 return TRUE;
427 }
428
429 /* protect_argv lifted from GLib, relicensed by author, Tor Lillqvist */
430 static int
431 protect_argv (char **argv,
432 char ***new_argv)
433 {
434 int i;
435 int argc = 0;
436
437 while (argv[argc])
438 ++argc;
439 *new_argv = dbus_malloc ((argc + 1) * sizeof (char *));
440 if (*new_argv == NULL)
441 return -1;
442
443 for (i = 0; i < argc; i++)
444 (*new_argv)[i] = NULL;
445
446 /* Quote each argv element if necessary, so that it will get
447 * reconstructed correctly in the C runtime startup code. Note that
448 * the unquoting algorithm in the C runtime is really weird, and
449 * rather different than what Unix shells do. See stdargv.c in the C
450 * runtime sources (in the Platform SDK, in src/crt).
451 *
452 * Note that an new_argv[0] constructed by this function should
453 * *not* be passed as the filename argument to a spawn* or exec*
454 * family function. That argument should be the real file name
455 * without any quoting.
456 */
457 for (i = 0; i < argc; i++)
458 {
459 char *p = argv[i];
460 char *q;
461 int len = 0;
462 int need_dblquotes = FALSE;
463 while (*p)
464 {
465 if (*p == ' ' || *p == '\t')
466 need_dblquotes = TRUE;
467 else if (*p == '"')
468 len++;
469 else if (*p == '\\')
470 {
471 char *pp = p;
472 while (*pp && *pp == '\\')
473 pp++;
474 if (*pp == '"')
475 len++;
476 }
477 len++;
478 p++;
479 }
480
481 q = (*new_argv)[i] = dbus_malloc (len + need_dblquotes*2 + 1);
482
483 if (q == NULL)
484 return -1;
485
486
487 p = argv[i];
488
489 if (need_dblquotes)
490 *q++ = '"';
491
492 while (*p)
493 {
494 if (*p == '"')
495 *q++ = '\\';
496 else if (*p == '\\')
497 {
498 char *pp = p;
499 while (*pp && *pp == '\\')
500 pp++;
501 if (*pp == '"')
502 *q++ = '\\';
503 }
504 *q++ = *p;
505 p++;
506 }
507
508 if (need_dblquotes)
509 *q++ = '"';
510 *q++ = '\0';
511 /* printf ("argv[%d]:%s, need_dblquotes:%s len:%d => %s\n", i, argv[i], need_dblquotes?"TRUE":"FALSE", len, (*new_argv)[i]); */
512 }
513 (*new_argv)[argc] = NULL;
514
515 return argc;
516 }
517
518
519 /* From GPGME, relicensed by g10 Code GmbH. */
520 static char *
521 compose_string (char **strings, char separator)
522 {
523 int i;
524 int n = 0;
525 char *buf;
526 char *p;
527
528 if (!strings || !strings[0])
529 return 0;
530 for (i = 0; strings[i]; i++)
531 n += strlen (strings[i]) + 1;
532 n++;
533
534 buf = p = malloc (n);
535 if (!buf)
536 return NULL;
537 for (i = 0; strings[i]; i++)
538 {
539 strcpy (p, strings[i]);
540 p += strlen (strings[i]);
541 *(p++) = separator;
542 }
543 p--;
544 *(p++) = '\0';
545 *p = '\0';
546
547 return buf;
548 }
549
550 static char *
551 build_commandline (char **argv)
552 {
553 return compose_string (argv, ' ');
554 }
555
556 static char *
557 build_env_string (char** envp)
558 {
559 return compose_string (envp, '\0');
560 }
561
562 static HANDLE
563 spawn_program (char* name, char** argv, char** envp)
564 {
565 PROCESS_INFORMATION pi = { NULL, 0, 0, 0 };
566 STARTUPINFOA si;
567 char *arg_string, *env_string;
568 BOOL result;
569
570 #ifdef DBUS_WINCE
571 if (argv && argv[0])
572 arg_string = build_commandline (argv + 1);
573 else
574 arg_string = NULL;
575 #else
576 arg_string = build_commandline (argv);
577 #endif
578 if (!arg_string)
579 return INVALID_HANDLE_VALUE;
580
581 env_string = build_env_string(envp);
582
583 memset (&si, 0, sizeof (si));
584 si.cb = sizeof (si);
585 #ifdef DBUS_WINCE
586 result = CreateProcessA (name, arg_string, NULL, NULL, FALSE, 0,
587 #else
588 result = CreateProcessA (NULL, arg_string, NULL, NULL, FALSE, 0,
589 #endif
590 (LPVOID)env_string, NULL, &si, &pi);
591 free (arg_string);
592 if (env_string)
593 free (env_string);
594
595 if (!result)
596 return INVALID_HANDLE_VALUE;
597
598 CloseHandle (pi.hThread);
599 return pi.hProcess;
600 }
601
602
603 static DWORD __stdcall
604 babysitter (void *parameter)
605 {
606 int ret = 0;
607 DBusBabysitter *sitter = (DBusBabysitter *) parameter;
608 HANDLE handle;
609
610 PING();
611 if (sitter->child_setup)
612 {
613 PING();
614 (*sitter->child_setup) (sitter->user_data);
615 }
616
617 _dbus_verbose ("babysitter: spawning %s\n", sitter->log_name);
618
619 PING();
620 handle = spawn_program (sitter->log_name, sitter->argv, sitter->envp);
621
622 PING();
623 if (handle != INVALID_HANDLE_VALUE)
624 {
625 sitter->child_handle = handle;
626 }
627 else
628 {
629 sitter->child_handle = NULL;
630 sitter->have_spawn_errno = TRUE;
631 sitter->spawn_errno = GetLastError();
632 }
633
634 PING();
635 SetEvent (sitter->start_sync_event);
636
637 if (sitter->child_handle != NULL)
638 {
639 DWORD status;
640
641 PING();
642 // wait until process finished
643 WaitForSingleObject (sitter->child_handle, INFINITE);
644
645 PING();
646 ret = GetExitCodeProcess (sitter->child_handle, &status);
647 if (ret)
648 {
649 sitter->child_status = status;
650 sitter->have_child_status = TRUE;
651 }
652
653 CloseHandle (sitter->child_handle);
654 sitter->child_handle = NULL;
655 }
656
657 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
658 SetEvent (sitter->end_sync_event);
659 #endif
660
661 PING();
662 send (sitter->socket_to_main.sock, " ", 1, 0);
663
664 _dbus_babysitter_unref (sitter);
665
666 return ret ? 0 : 1;
667 }
668
669 dbus_bool_t
670 _dbus_spawn_async_with_babysitter (DBusBabysitter **sitter_p,
671 const char *log_name,
672 char **argv,
673 char **envp,
674 DBusSpawnChildSetupFunc child_setup,
675 void *user_data,
676 DBusError *error)
677 {
678 DBusBabysitter *sitter;
679 HANDLE sitter_thread;
680 DWORD sitter_thread_id;
681
682 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
683 _dbus_assert (argv[0] != NULL);
684
685 if (sitter_p != NULL)
686 *sitter_p = NULL;
687
688 PING();
689 sitter = _dbus_babysitter_new ();
690 if (sitter == NULL)
691 {
692 _DBUS_SET_OOM (error);
693 return FALSE;
694 }
695
696 sitter->child_setup = child_setup;
697 sitter->user_data = user_data;
698
699 sitter->log_name = _dbus_strdup (log_name);
700 if (sitter->log_name == NULL && log_name != NULL)
701 {
702 _DBUS_SET_OOM (error);
703 goto out0;
704 }
705
706 if (sitter->log_name == NULL)
707 sitter->log_name = _dbus_strdup (argv[0]);
708
709 if (sitter->log_name == NULL)
710 {
711 _DBUS_SET_OOM (error);
712 goto out0;
713 }
714
715 PING();
716 if (!_dbus_socketpair (&sitter->socket_to_babysitter,
717 &sitter->socket_to_main,
718 FALSE, error))
719 goto out0;
720
721 sitter->sitter_watch = _dbus_watch_new (sitter->socket_to_babysitter,
722 DBUS_WATCH_READABLE,
723 TRUE, handle_watch, sitter, NULL);
724 PING();
725 if (sitter->sitter_watch == NULL)
726 {
727 _DBUS_SET_OOM (error);
728 goto out0;
729 }
730
731 PING();
732 if (!_dbus_watch_list_add_watch (sitter->watches, sitter->sitter_watch))
733 {
734 /* we need to free it early so the destructor won't try to remove it
735 * without it having been added, which DBusLoop doesn't allow */
736 _dbus_watch_invalidate (sitter->sitter_watch);
737 _dbus_watch_unref (sitter->sitter_watch);
738 sitter->sitter_watch = NULL;
739
740 _DBUS_SET_OOM (error);
741 goto out0;
742 }
743
744 sitter->argc = protect_argv (argv, &sitter->argv);
745 if (sitter->argc == -1)
746 {
747 _DBUS_SET_OOM (error);
748 goto out0;
749 }
750 sitter->envp = envp;
751
752 PING();
753 sitter_thread = (HANDLE) CreateThread (NULL, 0, babysitter,
754 _dbus_babysitter_ref (sitter), 0, &sitter_thread_id);
755
756 if (sitter_thread == 0)
757 {
758 PING();
759 dbus_set_error_const (error, DBUS_ERROR_SPAWN_FORK_FAILED,
760 "Failed to create new thread");
761 goto out0;
762 }
763 CloseHandle (sitter_thread);
764
765 PING();
766 WaitForSingleObject (sitter->start_sync_event, INFINITE);
767
768 PING();
769 if (sitter_p != NULL)
770 *sitter_p = sitter;
771 else
772 _dbus_babysitter_unref (sitter);
773
774 _DBUS_ASSERT_ERROR_IS_CLEAR (error);
775
776 PING();
777 return TRUE;
778
779 out0:
780 _dbus_babysitter_unref (sitter);
781
782 return FALSE;
783 }
784
785 void
786 _dbus_babysitter_set_result_function (DBusBabysitter *sitter,
787 DBusBabysitterFinishedFunc finished,
788 void *user_data)
789 {
790 sitter->finished_cb = finished;
791 sitter->finished_data = user_data;
792 }
793
794 #ifdef DBUS_ENABLE_EMBEDDED_TESTS
795
796 static char *
797 get_test_exec (const char *exe,
798 DBusString *scratch_space)
799 {
800 const char *dbus_test_exec;
801
802 dbus_test_exec = _dbus_getenv ("DBUS_TEST_EXEC");
803
804 if (dbus_test_exec == NULL)
805 dbus_test_exec = DBUS_TEST_EXEC;
806
807 if (!_dbus_string_init (scratch_space))
808 return NULL;
809
810 if (!_dbus_string_append_printf (scratch_space, "%s/%s%s",
811 dbus_test_exec, exe, DBUS_EXEEXT))
812 {
813 _dbus_string_free (scratch_space);
814 return NULL;
815 }
816
817 return _dbus_string_get_data (scratch_space);
818 }
819
820 #define LIVE_CHILDREN(sitter) ((sitter)->child_handle != NULL)
821
822 static void
823 _dbus_babysitter_block_for_child_exit (DBusBabysitter *sitter)
824 {
825 if (sitter->child_handle == NULL)
826 return;
827
828 WaitForSingleObject (sitter->end_sync_event, INFINITE);
829 }
830
831 static dbus_bool_t
832 check_spawn_nonexistent (void *data)
833 {
834 char *argv[4] = { NULL, NULL, NULL, NULL };
835 DBusBabysitter *sitter;
836 DBusError error;
837
838 sitter = NULL;
839
840 dbus_error_init (&error);
841
842 /*** Test launching nonexistent binary */
843
844 argv[0] = "/this/does/not/exist/32542sdgafgafdg";
845 if (_dbus_spawn_async_with_babysitter (&sitter, "spawn_nonexistent", argv, NULL,
846 NULL, NULL,
847 &error))
848 {
849 _dbus_babysitter_block_for_child_exit (sitter);
850 _dbus_babysitter_set_child_exit_error (sitter, &error);
851 }
852
853 if (sitter)
854 _dbus_babysitter_unref (sitter);
855
856 if (!dbus_error_is_set (&error))
857 {
858 _dbus_warn ("Did not get an error launching nonexistent executable\n");
859 return FALSE;
860 }
861
862 if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
863 dbus_error_has_name (&error, DBUS_ERROR_SPAWN_EXEC_FAILED)))
864 {
865 _dbus_warn ("Not expecting error when launching nonexistent executable: %s: %s\n",
866 error.name, error.message);
867 dbus_error_free (&error);
868 return FALSE;
869 }
870
871 dbus_error_free (&error);
872
873 return TRUE;
874 }
875
876 static dbus_bool_t
877 check_spawn_segfault (void *data)
878 {
879 char *argv[4] = { NULL, NULL, NULL, NULL };
880 DBusBabysitter *sitter;
881 DBusError error;
882 DBusString argv0;
883
884 sitter = NULL;
885
886 dbus_error_init (&error);
887
888 /*** Test launching segfault binary */
889
890 argv[0] = get_test_exec ("test-segfault", &argv0);
891
892 if (argv[0] == NULL)
893 {
894 /* OOM was simulated, never mind */
895 return TRUE;
896 }
897
898 if (_dbus_spawn_async_with_babysitter (&sitter, "spawn_segfault", argv, NULL,
899 NULL, NULL,
900 &error))
901 {
902 _dbus_babysitter_block_for_child_exit (sitter);
903 _dbus_babysitter_set_child_exit_error (sitter, &error);
904 }
905
906 _dbus_string_free (&argv0);
907
908 if (sitter)
909 _dbus_babysitter_unref (sitter);
910
911 if (!dbus_error_is_set (&error))
912 {
913 _dbus_warn ("Did not get an error launching segfaulting binary\n");
914 return FALSE;
915 }
916
917 if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
918 dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED)))
919 {
920 _dbus_warn ("Not expecting error when launching segfaulting executable: %s: %s\n",
921 error.name, error.message);
922 dbus_error_free (&error);
923 return FALSE;
924 }
925
926 dbus_error_free (&error);
927
928 return TRUE;
929 }
930
931 static dbus_bool_t
932 check_spawn_exit (void *data)
933 {
934 char *argv[4] = { NULL, NULL, NULL, NULL };
935 DBusBabysitter *sitter;
936 DBusError error;
937 DBusString argv0;
938
939 sitter = NULL;
940
941 dbus_error_init (&error);
942
943 /*** Test launching exit failure binary */
944
945 argv[0] = get_test_exec ("test-exit", &argv0);
946
947 if (argv[0] == NULL)
948 {
949 /* OOM was simulated, never mind */
950 return TRUE;
951 }
952
953 if (_dbus_spawn_async_with_babysitter (&sitter, "spawn_exit", argv, NULL,
954 NULL, NULL,
955 &error))
956 {
957 _dbus_babysitter_block_for_child_exit (sitter);
958 _dbus_babysitter_set_child_exit_error (sitter, &error);
959 }
960
961 _dbus_string_free (&argv0);
962
963 if (sitter)
964 _dbus_babysitter_unref (sitter);
965
966 if (!dbus_error_is_set (&error))
967 {
968 _dbus_warn ("Did not get an error launching binary that exited with failure code\n");
969 return FALSE;
970 }
971
972 if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
973 dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED)))
974 {
975 _dbus_warn ("Not expecting error when launching exiting executable: %s: %s\n",
976 error.name, error.message);
977 dbus_error_free (&error);
978 return FALSE;
979 }
980
981 dbus_error_free (&error);
982
983 return TRUE;
984 }
985
986 static dbus_bool_t
987 check_spawn_and_kill (void *data)
988 {
989 char *argv[4] = { NULL, NULL, NULL, NULL };
990 DBusBabysitter *sitter;
991 DBusError error;
992 DBusString argv0;
993
994 sitter = NULL;
995
996 dbus_error_init (&error);
997
998 /*** Test launching sleeping binary then killing it */
999
1000 argv[0] = get_test_exec ("test-sleep-forever", &argv0);
1001
1002 if (argv[0] == NULL)
1003 {
1004 /* OOM was simulated, never mind */
1005 return TRUE;
1006 }
1007
1008 if (_dbus_spawn_async_with_babysitter (&sitter, "spawn_and_kill", argv, NULL,
1009 NULL, NULL,
1010 &error))
1011 {
1012 _dbus_babysitter_kill_child (sitter);
1013
1014 _dbus_babysitter_block_for_child_exit (sitter);
1015
1016 _dbus_babysitter_set_child_exit_error (sitter, &error);
1017 }
1018
1019 _dbus_string_free (&argv0);
1020
1021 if (sitter)
1022 _dbus_babysitter_unref (sitter);
1023
1024 if (!dbus_error_is_set (&error))
1025 {
1026 _dbus_warn ("Did not get an error after killing spawned binary\n");
1027 return FALSE;
1028 }
1029
1030 if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) ||
1031 dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED)))
1032 {
1033 _dbus_warn ("Not expecting error when killing executable: %s: %s\n",
1034 error.name, error.message);
1035 dbus_error_free (&error);
1036 return FALSE;
1037 }
1038
1039 dbus_error_free (&error);
1040
1041 return TRUE;
1042 }
1043
1044 dbus_bool_t
1045 _dbus_spawn_test (const char *test_data_dir)
1046 {
1047 if (!_dbus_test_oom_handling ("spawn_nonexistent",
1048 check_spawn_nonexistent,
1049 NULL))
1050 return FALSE;
1051
1052 /* Don't run the obnoxious segfault test by default,
1053 * it's a pain to have to click all those error boxes.
1054 */
1055 if (getenv ("DO_SEGFAULT_TEST"))
1056 if (!_dbus_test_oom_handling ("spawn_segfault",
1057 check_spawn_segfault,
1058 NULL))
1059 return FALSE;
1060
1061 if (!_dbus_test_oom_handling ("spawn_exit",
1062 check_spawn_exit,
1063 NULL))
1064 return FALSE;
1065
1066 if (!_dbus_test_oom_handling ("spawn_and_kill",
1067 check_spawn_and_kill,
1068 NULL))
1069 return FALSE;
1070
1071 return TRUE;
1072 }
1073 #endif
dbus_error_has_name
dbus_bool_t dbus_error_has_name(const DBusError *error, const char *name)
Checks whether the error is set and has the given name.
Definition: dbus-errors.c:302
DBusAtomic
An atomic integer safe to increment or decrement from multiple threads.
Definition: dbus-sysdeps.h:279
DBusError::message
const char * message
public error message field
Definition: dbus-errors.h:51
_dbus_watch_new
DBusWatch * _dbus_watch_new(DBusPollable fd, unsigned int flags, dbus_bool_t enabled, DBusWatchHandler handler, void *data, DBusFreeFunction free_data_function)
Creates a new DBusWatch.
Definition: dbus-watch.c:88
DBusWatch
Implementation of DBusWatch.
Definition: dbus-watch.c:40
NULL
#define NULL
A null pointer, defined appropriately for C or C++.
DBUS_ERROR_SPAWN_EXEC_FAILED
#define DBUS_ERROR_SPAWN_EXEC_FAILED
While starting a new process, the exec() call failed.
Definition: dbus-protocol.h:416
DBusFreeFunction
void(* DBusFreeFunction)(void *memory)
The type of a function which frees a block of memory.
Definition: dbus-memory.h:64
dbus_free
void dbus_free(void *memory)
Frees a block of memory previously allocated by dbus_malloc() or dbus_malloc0().
Definition: dbus-memory.c:701
_dbus_socketpair
dbus_bool_t _dbus_socketpair(DBusSocket *fd1, DBusSocket *fd2, dbus_bool_t blocking, DBusError *error)
Creates pair of connect sockets (as in socketpair()).
Definition: dbus-sysdeps-unix.c:3370
DBusWatchToggledFunction
void(* DBusWatchToggledFunction)(DBusWatch *watch, void *data)
Called when dbus_watch_get_enabled() may return a different value than it did before.
Definition: dbus-connection.h:97
DBUS_ERROR_SPAWN_CHILD_EXITED
#define DBUS_ERROR_SPAWN_CHILD_EXITED
While starting a new process, the child exited with a status code.
Definition: dbus-protocol.h:420
DBusBabysitter::status
int status
Exit status code.
Definition: dbus-spawn.c:261
_dbus_assert
#define _dbus_assert(condition)
Aborts with an error message if the condition is false.
Definition: dbus-internals.h:148
dbus_error_free
void dbus_error_free(DBusError *error)
Frees an error that&#39;s been set (or just initialized), then reinitializes the error as in dbus_error_i...
Definition: dbus-errors.c:211
_dbus_watch_list_free
void _dbus_watch_list_free(DBusWatchList *watch_list)
Frees a DBusWatchList.
Definition: dbus-watch.c:249
_dbus_watch_list_new
DBusWatchList * _dbus_watch_list_new(void)
Creates a new watch list.
Definition: dbus-watch.c:232
_dbus_string_init
dbus_bool_t _dbus_string_init(DBusString *str)
Initializes a string.
Definition: dbus-string.c:175
_dbus_close_socket
dbus_bool_t _dbus_close_socket(DBusSocket fd, DBusError *error)
Closes a socket.
Definition: dbus-sysdeps-unix.c:283
_dbus_babysitter_get_child_exited
dbus_bool_t _dbus_babysitter_get_child_exited(DBusBabysitter *sitter)
Checks whether the child has exited, without blocking.
Definition: dbus-spawn.c:706
_dbus_watch_list_set_functions
dbus_bool_t _dbus_watch_list_set_functions(DBusWatchList *watch_list, DBusAddWatchFunction add_function, DBusRemoveWatchFunction remove_function, DBusWatchToggledFunction toggled_function, void *data, DBusFreeFunction free_data_function)
Sets the watch functions.
Definition: dbus-watch.c:296
DBusSocket
Socket interface.
Definition: dbus-sysdeps.h:148
DBusBabysitter::watches
DBusWatchList * watches
Watches.
Definition: dbus-spawn-win.c:83
dbus_malloc
void * dbus_malloc(size_t bytes)
Allocates the given number of bytes, as with standard malloc().
Definition: dbus-memory.c:461
dbus_new0
#define dbus_new0(type, count)
Safe macro for using dbus_malloc0().
Definition: dbus-memory.h:59
DBusString
Definition: dbus-string.h:42
dbus_bool_t
dbus_uint32_t dbus_bool_t
A boolean, valid values are TRUE and FALSE.
Definition: dbus-types.h:35
DBusBabysitter::sitter_watch
DBusWatch * sitter_watch
Sitter pipe watch.
Definition: dbus-spawn-win.c:84
_dbus_babysitter_get_child_exit_status
dbus_bool_t _dbus_babysitter_get_child_exit_status(DBusBabysitter *sitter, int *status)
Gets the exit status of the child.
Definition: dbus-spawn.c:731
_dbus_babysitter_kill_child
void _dbus_babysitter_kill_child(DBusBabysitter *sitter)
Blocks until the babysitter process gives us the PID of the spawned grandchild, then kills the spawne...
Definition: dbus-spawn.c:684
DBusBabysitter
Babysitter implementation details.
Definition: dbus-spawn-win.c:61
_dbus_spawn_async_with_babysitter
dbus_bool_t _dbus_spawn_async_with_babysitter(DBusBabysitter **sitter_p, const char *log_name, char **argv, char **env, DBusSpawnChildSetupFunc child_setup, void *user_data, DBusError *error)
Spawns a new process.
Definition: dbus-spawn.c:1210
_dbus_warn
void _dbus_warn(const char *format,...)
Prints a warning message to stderr.
Definition: dbus-internals.c:230
_dbus_atomic_inc
dbus_int32_t _dbus_atomic_inc(DBusAtomic *atomic)
Atomically increments an integer.
Definition: dbus-sysdeps-unix.c:2753
_dbus_string_append_printf
dbus_bool_t _dbus_string_append_printf(DBusString *str, const char *format,...)
Appends a printf-style formatted string to the DBusString.
Definition: dbus-string.c:1114
_dbus_watch_invalidate
void _dbus_watch_invalidate(DBusWatch *watch)
Clears the file descriptor from a now-invalid watch object so that no one tries to use it...
Definition: dbus-watch.c:169
_dbus_babysitter_set_watch_functions
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)
Sets watch functions to notify us when the babysitter object needs to read/write file descriptors...
Definition: dbus-spawn.c:813
DBusError
Object representing an exception.
Definition: dbus-errors.h:48
dbus_set_error
void dbus_set_error(DBusError *error, const char *name, const char *format,...)
Assigns an error name and message to a DBusError.
Definition: dbus-errors.c:354
_dbus_string_free
void _dbus_string_free(DBusString *str)
Frees a string created by _dbus_string_init().
Definition: dbus-string.c:259
TRUE
#define TRUE
Expands to "1".
_dbus_assert_not_reached
#define _dbus_assert_not_reached(explanation)
Aborts with an error message if called.
Definition: dbus-internals.h:159
DBUS_ERROR_FAILED
#define DBUS_ERROR_FAILED
A generic error; "something went wrong" - see the error message for more.
Definition: dbus-protocol.h:355
_dbus_watch_list_add_watch
dbus_bool_t _dbus_watch_list_add_watch(DBusWatchList *watch_list, DBusWatch *watch)
Adds a new watch to the watch list, invoking the application DBusAddWatchFunction if appropriate...
Definition: dbus-watch.c:382
DBusError::name
const char * name
public error name field
Definition: dbus-errors.h:50
DBusAddWatchFunction
dbus_bool_t(* DBusAddWatchFunction)(DBusWatch *watch, void *data)
Called when libdbus needs a new watch to be monitored by the main loop.
Definition: dbus-connection.h:91
DBusRemoveWatchFunction
void(* DBusRemoveWatchFunction)(DBusWatch *watch, void *data)
Called when libdbus no longer needs a watch to be monitored by the main loop.
Definition: dbus-connection.h:103
DBusWatchList
DBusWatchList implementation details.
Definition: dbus-watch.c:214
_dbus_watch_list_remove_watch
void _dbus_watch_list_remove_watch(DBusWatchList *watch_list, DBusWatch *watch)
Removes a watch from the watch list, invoking the application&#39;s DBusRemoveWatchFunction if appropriat...
Definition: dbus-watch.c:415
_dbus_babysitter_unref
void _dbus_babysitter_unref(DBusBabysitter *sitter)
Decrement the reference count on the babysitter object.
Definition: dbus-spawn.c:324
dbus_error_init
void dbus_error_init(DBusError *error)
Initializes a DBusError structure.
Definition: dbus-errors.c:188
_dbus_atomic_dec
dbus_int32_t _dbus_atomic_dec(DBusAtomic *atomic)
Atomically decrement an integer.
Definition: dbus-sysdeps-unix.c:2776
DBUS_ERROR_SPAWN_FORK_FAILED
#define DBUS_ERROR_SPAWN_FORK_FAILED
While starting a new process, the fork() call failed.
Definition: dbus-protocol.h:418
DBusBabysitter::log_name
char * log_name
the name under which to log messages about this process being spawned
Definition: dbus-spawn-win.c:71
_dbus_watch_unref
void _dbus_watch_unref(DBusWatch *watch)
Decrements the reference count of a DBusWatch object and finalizes the object if the count reaches ze...
Definition: dbus-watch.c:138
DBUS_ERROR_NO_MEMORY
#define DBUS_ERROR_NO_MEMORY
There was not enough memory to complete an operation.
Definition: dbus-protocol.h:357
FALSE
#define FALSE
Expands to "0".
dbus_set_error_const
void dbus_set_error_const(DBusError *error, const char *name, const char *message)
Assigns an error name and message to a DBusError.
Definition: dbus-errors.c:243
DBUS_WATCH_READABLE
As in POLLIN.
Definition: dbus-connection.h:60
DBusBabysitter::socket_to_babysitter
DBusSocket socket_to_babysitter
Connection to the babysitter process.
Definition: dbus-spawn-win.c:80
_dbus_strdup
char * _dbus_strdup(const char *str)
Duplicates a string.
Definition: dbus-internals.c:537
_dbus_babysitter_set_child_exit_error
void _dbus_babysitter_set_child_exit_error(DBusBabysitter *sitter, DBusError *error)
Sets the DBusError with an explanation of why the spawned child process exited (on a signal...
Definition: dbus-spawn.c:755
_dbus_getenv
const char * _dbus_getenv(const char *varname)
Wrapper for getenv().
Definition: dbus-sysdeps.c:185
dbus_error_is_set
dbus_bool_t dbus_error_is_set(const DBusError *error)
Checks whether an error occurred (the error is set).
Definition: dbus-errors.c:329
_dbus_babysitter_ref
DBusBabysitter * _dbus_babysitter_ref(DBusBabysitter *sitter)
Increment the reference count on the babysitter object.
Definition: dbus-spawn.c:302