static DBusBabysitter* _dbus_babysitter_new (void) { DBusBabysitter *sitter; sitter = dbus_new0 (DBusBabysitter, 1); if (sitter == NULL) return NULL; sitter->refcount = 1; sitter->socket_to_babysitter.fd = -1; sitter->error_pipe_from_child = -1; sitter->sitter_pid = -1; sitter->grandchild_pid = -1; sitter->watches = _dbus_watch_list_new (); if (sitter->watches == NULL) goto failed; return sitter; failed: _dbus_babysitter_unref (sitter); return NULL; }
static DBusBabysitter* _dbus_babysitter_new (void) { DBusBabysitter *sitter; sitter = dbus_new0 (DBusBabysitter, 1); if (sitter == NULL) return NULL; sitter->refcount = 1; sitter->start_sync_event = CreateEvent (NULL, FALSE, FALSE, NULL); if (sitter->start_sync_event == NULL) { _dbus_babysitter_unref (sitter); return NULL; } #ifdef DBUS_BUILD_TESTS sitter->end_sync_event = CreateEvent (NULL, FALSE, FALSE, NULL); if (sitter->end_sync_event == NULL) { _dbus_babysitter_unref (sitter); return NULL; } #endif sitter->child_handle = NULL; sitter->socket_to_babysitter = sitter->socket_to_main = -1; sitter->argc = 0; sitter->argv = NULL; sitter->envp = NULL; sitter->watches = _dbus_watch_list_new (); if (sitter->watches == NULL) { _dbus_babysitter_unref (sitter); return NULL; } sitter->have_spawn_errno = FALSE; sitter->have_child_status = FALSE; return sitter; }
static dbus_bool_t check_spawn_and_kill (void *data) { char *argv[4] = { NULL, NULL, NULL, NULL }; DBusBabysitter *sitter = NULL; DBusError error = DBUS_ERROR_INIT; DBusString argv0; /*** Test launching sleeping binary then killing it */ argv[0] = get_test_exec ("test-sleep-forever", &argv0); if (argv[0] == NULL) { /* OOM was simulated, never mind */ return TRUE; } if (_dbus_spawn_async_with_babysitter (&sitter, "spawn_and_kill", argv, NULL, NULL, NULL, &error)) { _dbus_babysitter_kill_child (sitter); _dbus_babysitter_block_for_child_exit (sitter); _dbus_babysitter_set_child_exit_error (sitter, &error); } _dbus_string_free (&argv0); if (sitter) _dbus_babysitter_unref (sitter); if (!dbus_error_is_set (&error)) { _dbus_warn ("Did not get an error after killing spawned binary\n"); return FALSE; } if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) || dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_SIGNALED))) { _dbus_warn ("Not expecting error when killing executable: %s: %s\n", error.name, error.message); dbus_error_free (&error); return FALSE; } dbus_error_free (&error); return TRUE; }
static dbus_bool_t check_spawn_exit (void *data) { char *argv[4] = { NULL, NULL, NULL, NULL }; DBusBabysitter *sitter; DBusError error; sitter = NULL; dbus_error_init (&error); /*** Test launching exit failure binary */ argv[0] = TEST_EXIT_BINARY; if (_dbus_spawn_async_with_babysitter (&sitter, argv, NULL, NULL, &error)) { #ifdef __SYMBIAN32__ if(sitter) { #endif _dbus_babysitter_block_for_child_exit (sitter); _dbus_babysitter_set_child_exit_error (sitter, &error); #ifdef __SYMBIAN32__ } #endif /* no baby sitter support in Symbian */ } if (sitter) _dbus_babysitter_unref (sitter); if (!dbus_error_is_set (&error)) { _dbus_warn ("Did not get an error launching binary that exited with failure code\n"); return FALSE; } if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) || dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED))) { _dbus_warn ("Not expecting error when launching exiting executable: %s: %s\n", error.name, error.message); dbus_error_free (&error); return FALSE; } dbus_error_free (&error); return TRUE; }
static dbus_bool_t check_spawn_and_kill (void *data) { char *argv[4] = { NULL, NULL, NULL, NULL }; DBusBabysitter *sitter; DBusError error; sitter = NULL; dbus_error_init (&error); /*** Test launching sleeping binary then killing it */ argv[0] = TEST_SLEEP_FOREVER_BINARY; if (_dbus_spawn_async_with_babysitter (&sitter, argv, NULL, NULL, NULL, &error)) { _dbus_babysitter_kill_child (sitter); _dbus_babysitter_block_for_child_exit (sitter); _dbus_babysitter_set_child_exit_error (sitter, &error); } if (sitter) _dbus_babysitter_unref (sitter); if (!dbus_error_is_set (&error)) { _dbus_warn ("Did not get an error after killing spawned binary\n"); return FALSE; } if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) || dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_EXITED))) { _dbus_warn ("Not expecting error when killing executable: %s: %s\n", error.name, error.message); dbus_error_free (&error); return FALSE; } dbus_error_free (&error); return TRUE; }
static dbus_bool_t check_spawn_nonexistent (void *data) { char *argv[4] = { NULL, NULL, NULL, NULL }; DBusBabysitter *sitter; DBusError error; sitter = NULL; dbus_error_init (&error); /*** Test launching nonexistent binary */ argv[0] = "/this/does/not/exist/32542sdgafgafdg"; if (_dbus_spawn_async_with_babysitter (&sitter, argv, NULL, NULL, NULL, &error)) { _dbus_babysitter_block_for_child_exit (sitter); _dbus_babysitter_set_child_exit_error (sitter, &error); } if (sitter) _dbus_babysitter_unref (sitter); if (!dbus_error_is_set (&error)) { _dbus_warn ("Did not get an error launching nonexistent executable\n"); return FALSE; } if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) || dbus_error_has_name (&error, DBUS_ERROR_SPAWN_EXEC_FAILED))) { _dbus_warn ("Not expecting error when launching nonexistent executable: %s: %s\n", error.name, error.message); dbus_error_free (&error); return FALSE; } dbus_error_free (&error); return TRUE; }
static dbus_bool_t handle_watch (DBusWatch *watch, unsigned int condition, void *data) { DBusBabysitter *sitter = _dbus_babysitter_ref (data); int revents; int fd; revents = 0; if (condition & DBUS_WATCH_READABLE) revents |= _DBUS_POLLIN; if (condition & DBUS_WATCH_ERROR) revents |= _DBUS_POLLERR; if (condition & DBUS_WATCH_HANGUP) revents |= _DBUS_POLLHUP; fd = dbus_watch_get_socket (watch); if (fd == sitter->error_pipe_from_child) handle_error_pipe (sitter, revents); else if (fd == sitter->socket_to_babysitter.fd) handle_babysitter_socket (sitter, revents); while (LIVE_CHILDREN (sitter) && babysitter_iteration (sitter, FALSE)) ; /* fd.o #32992: if the handle_* methods closed their sockets, they previously * didn't always remove the watches. Check that we don't regress. */ _dbus_assert (sitter->socket_to_babysitter.fd != -1 || sitter->sitter_watch == NULL); _dbus_assert (sitter->error_pipe_from_child != -1 || sitter->error_watch == NULL); if (_dbus_babysitter_get_child_exited (sitter) && sitter->finished_cb != NULL) { sitter->finished_cb (sitter, sitter->finished_data); sitter->finished_cb = NULL; } _dbus_babysitter_unref (sitter); return TRUE; }
static dbus_bool_t check_spawn_segfault (void *data) { char *argv[4] = { NULL, NULL, NULL, NULL }; DBusBabysitter *sitter = NULL; DBusError error = DBUS_ERROR_INIT; /*** Test launching segfault binary */ argv[0] = TEST_SEGFAULT_BINARY; if (_dbus_spawn_async_with_babysitter (&sitter, argv, NULL, NULL, NULL, &error)) { _dbus_babysitter_block_for_child_exit (sitter); _dbus_babysitter_set_child_exit_error (sitter, &error); } if (sitter) _dbus_babysitter_unref (sitter); if (!dbus_error_is_set (&error)) { _dbus_warn ("Did not get an error launching segfaulting binary\n"); return FALSE; } if (!(dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY) || dbus_error_has_name (&error, DBUS_ERROR_SPAWN_CHILD_SIGNALED))) { _dbus_warn ("Not expecting error when launching segfaulting executable: %s: %s\n", error.name, error.message); dbus_error_free (&error); return FALSE; } dbus_error_free (&error); return TRUE; }
/** * Spawns a new process. * * On Unix platforms, the child_setup function is passed the given * user_data and is run in the child after fork() but before calling exec(). * This can be used to change uid, resource limits and so on. * On Windows, this functionality does not fit the multi-processing model * (Windows does the equivalent of fork() and exec() in a single API call), * and the child_setup function and its user_data are ignored. * * Also creates a "babysitter" which tracks the status of the * child process, advising the parent if the child exits. * If the spawn fails, no babysitter is created. * If sitter_p is #NULL, no babysitter is kept. * * @param sitter_p return location for babysitter or #NULL * @param log_name the name under which to log messages about this process being spawned * @param argv the executable and arguments * @param env the environment, or #NULL to copy the parent's * @param child_setup function to call in child pre-exec() * @param user_data user data for setup function * @param error error object to be filled in if function fails * @returns #TRUE on success, #FALSE if error is filled in */ dbus_bool_t _dbus_spawn_async_with_babysitter (DBusBabysitter **sitter_p, const char *log_name, char * const *argv, char **env, DBusSpawnFlags flags, DBusSpawnChildSetupFunc child_setup, void *user_data, DBusError *error) { DBusBabysitter *sitter; int child_err_report_pipe[2] = { -1, -1 }; DBusSocket babysitter_pipe[2] = { DBUS_SOCKET_INIT, DBUS_SOCKET_INIT }; pid_t pid; #ifdef HAVE_SYSTEMD int fd_out = -1; int fd_err = -1; #endif _DBUS_ASSERT_ERROR_IS_CLEAR (error); _dbus_assert (argv[0] != NULL); if (sitter_p != NULL) *sitter_p = NULL; sitter = NULL; sitter = _dbus_babysitter_new (); if (sitter == NULL) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); return FALSE; } sitter->log_name = _dbus_strdup (log_name); if (sitter->log_name == NULL && log_name != NULL) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); goto cleanup_and_fail; } if (sitter->log_name == NULL) sitter->log_name = _dbus_strdup (argv[0]); if (sitter->log_name == NULL) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); goto cleanup_and_fail; } if (!make_pipe (child_err_report_pipe, error)) goto cleanup_and_fail; if (!_dbus_socketpair (&babysitter_pipe[0], &babysitter_pipe[1], TRUE, error)) goto cleanup_and_fail; /* Setting up the babysitter is only useful in the parent, * but we don't want to run out of memory and fail * after we've already forked, since then we'd leak * child processes everywhere. */ sitter->error_watch = _dbus_watch_new (child_err_report_pipe[READ_END], DBUS_WATCH_READABLE, TRUE, handle_watch, sitter, NULL); if (sitter->error_watch == NULL) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); goto cleanup_and_fail; } if (!_dbus_watch_list_add_watch (sitter->watches, sitter->error_watch)) { /* we need to free it early so the destructor won't try to remove it * without it having been added, which DBusLoop doesn't allow */ _dbus_watch_invalidate (sitter->error_watch); _dbus_watch_unref (sitter->error_watch); sitter->error_watch = NULL; dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); goto cleanup_and_fail; } sitter->sitter_watch = _dbus_watch_new (babysitter_pipe[0].fd, DBUS_WATCH_READABLE, TRUE, handle_watch, sitter, NULL); if (sitter->sitter_watch == NULL) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); goto cleanup_and_fail; } if (!_dbus_watch_list_add_watch (sitter->watches, sitter->sitter_watch)) { /* we need to free it early so the destructor won't try to remove it * without it having been added, which DBusLoop doesn't allow */ _dbus_watch_invalidate (sitter->sitter_watch); _dbus_watch_unref (sitter->sitter_watch); sitter->sitter_watch = NULL; dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); goto cleanup_and_fail; } _DBUS_ASSERT_ERROR_IS_CLEAR (error); #ifdef HAVE_SYSTEMD if (flags & DBUS_SPAWN_REDIRECT_OUTPUT) { /* This may fail, but it's not critical. * In particular, if we were compiled with journald support but are now * running on a non-systemd system, this is going to fail, so we * have to cope gracefully. */ fd_out = sd_journal_stream_fd (sitter->log_name, LOG_INFO, FALSE); fd_err = sd_journal_stream_fd (sitter->log_name, LOG_WARNING, FALSE); } #endif pid = fork (); if (pid < 0) { dbus_set_error (error, DBUS_ERROR_SPAWN_FORK_FAILED, "Failed to fork (%s)", _dbus_strerror (errno)); goto cleanup_and_fail; } else if (pid == 0) { /* Immediate child, this is the babysitter process. */ int grandchild_pid; /* Be sure we crash if the parent exits * and we write to the err_report_pipe */ signal (SIGPIPE, SIG_DFL); /* Close the parent's end of the pipes. */ close_and_invalidate (&child_err_report_pipe[READ_END]); close_and_invalidate (&babysitter_pipe[0].fd); /* Create the child that will exec () */ grandchild_pid = fork (); if (grandchild_pid < 0) { write_err_and_exit (babysitter_pipe[1].fd, CHILD_FORK_FAILED); _dbus_assert_not_reached ("Got to code after write_err_and_exit()"); } else if (grandchild_pid == 0) { #ifdef __linux__ int fd = -1; #ifdef O_CLOEXEC fd = open ("/proc/self/oom_score_adj", O_WRONLY | O_CLOEXEC); #endif if (fd < 0) { fd = open ("/proc/self/oom_score_adj", O_WRONLY); _dbus_fd_set_close_on_exec (fd); } if (fd >= 0) { if (write (fd, "0", sizeof (char)) < 0) _dbus_warn ("writing oom_score_adj error: %s", strerror (errno)); _dbus_close (fd, NULL); } #endif /* Go back to ignoring SIGPIPE, since it's evil */ signal (SIGPIPE, SIG_IGN); close_and_invalidate (&babysitter_pipe[1].fd); #ifdef HAVE_SYSTEMD /* log to systemd journal if possible */ if (fd_out >= 0) dup2 (fd_out, STDOUT_FILENO); if (fd_err >= 0) dup2 (fd_err, STDERR_FILENO); close_and_invalidate (&fd_out); close_and_invalidate (&fd_err); #endif do_exec (child_err_report_pipe[WRITE_END], argv, env, child_setup, user_data); _dbus_assert_not_reached ("Got to code after exec() - should have exited on error"); } else { close_and_invalidate (&child_err_report_pipe[WRITE_END]); #ifdef HAVE_SYSTEMD close_and_invalidate (&fd_out); close_and_invalidate (&fd_err); #endif babysit (grandchild_pid, babysitter_pipe[1].fd); _dbus_assert_not_reached ("Got to code after babysit()"); } } else { /* Close the uncared-about ends of the pipes */ close_and_invalidate (&child_err_report_pipe[WRITE_END]); close_and_invalidate (&babysitter_pipe[1].fd); #ifdef HAVE_SYSTEMD close_and_invalidate (&fd_out); close_and_invalidate (&fd_err); #endif sitter->socket_to_babysitter = babysitter_pipe[0]; babysitter_pipe[0].fd = -1; sitter->error_pipe_from_child = child_err_report_pipe[READ_END]; child_err_report_pipe[READ_END] = -1; sitter->sitter_pid = pid; if (sitter_p != NULL) *sitter_p = sitter; else _dbus_babysitter_unref (sitter); dbus_free_string_array (env); _DBUS_ASSERT_ERROR_IS_CLEAR (error); return TRUE; } cleanup_and_fail: _DBUS_ASSERT_ERROR_IS_SET (error); close_and_invalidate (&child_err_report_pipe[READ_END]); close_and_invalidate (&child_err_report_pipe[WRITE_END]); close_and_invalidate (&babysitter_pipe[0].fd); close_and_invalidate (&babysitter_pipe[1].fd); #ifdef HAVE_SYSTEMD close_and_invalidate (&fd_out); close_and_invalidate (&fd_err); #endif if (sitter != NULL) _dbus_babysitter_unref (sitter); return FALSE; }
dbus_bool_t _dbus_spawn_async_with_babysitter (DBusBabysitter **sitter_p, char **argv, char **envp, DBusSpawnChildSetupFunc child_setup, void *user_data, DBusError *error) { DBusBabysitter *sitter; HANDLE sitter_thread; int sitter_thread_id; _DBUS_ASSERT_ERROR_IS_CLEAR (error); *sitter_p = NULL; PING(); sitter = _dbus_babysitter_new (); if (sitter == NULL) { _DBUS_SET_OOM (error); return FALSE; } sitter->child_setup = child_setup; sitter->user_data = user_data; sitter->executable = _dbus_strdup (argv[0]); if (sitter->executable == NULL) { _DBUS_SET_OOM (error); goto out0; } PING(); if (!_dbus_full_duplex_pipe (&sitter->socket_to_babysitter, &sitter->socket_to_main, FALSE, error)) goto out0; sitter->sitter_watch = _dbus_watch_new (sitter->socket_to_babysitter, DBUS_WATCH_READABLE, TRUE, handle_watch, sitter, NULL); PING(); if (sitter->sitter_watch == NULL) { _DBUS_SET_OOM (error); goto out0; } PING(); if (!_dbus_watch_list_add_watch (sitter->watches, sitter->sitter_watch)) { _DBUS_SET_OOM (error); goto out0; } sitter->argc = protect_argv (argv, &sitter->argv); if (sitter->argc == -1) { _DBUS_SET_OOM (error); goto out0; } sitter->envp = envp; PING(); sitter_thread = (HANDLE) _beginthreadex (NULL, 0, babysitter, sitter, 0, &sitter_thread_id); if (sitter_thread == 0) { PING(); dbus_set_error_const (error, DBUS_ERROR_SPAWN_FORK_FAILED, "Failed to create new thread"); goto out0; } CloseHandle (sitter_thread); PING(); WaitForSingleObject (sitter->start_sync_event, INFINITE); PING(); if (sitter_p != NULL) *sitter_p = sitter; else _dbus_babysitter_unref (sitter); _DBUS_ASSERT_ERROR_IS_CLEAR (error); PING(); return TRUE; out0: _dbus_babysitter_unref (sitter); return FALSE; }
static unsigned __stdcall babysitter (void *parameter) { DBusBabysitter *sitter = (DBusBabysitter *) parameter; int fd; PING(); _dbus_babysitter_ref (sitter); if (sitter->child_setup) { PING(); (*sitter->child_setup) (sitter->user_data); } _dbus_verbose ("babysitter: spawning %s\n", sitter->executable); PING(); if (sitter->envp != NULL) sitter->child_handle = (HANDLE) spawnve (P_NOWAIT, sitter->executable, (const char * const *) sitter->argv, (const char * const *) sitter->envp); else sitter->child_handle = (HANDLE) spawnv (P_NOWAIT, sitter->executable, (const char * const *) sitter->argv); PING(); if (sitter->child_handle == (HANDLE) -1) { sitter->child_handle = NULL; sitter->have_spawn_errno = TRUE; sitter->spawn_errno = errno; } PING(); SetEvent (sitter->start_sync_event); if (sitter->child_handle != NULL) { int ret; DWORD status; PING(); WaitForSingleObject (sitter->child_handle, INFINITE); PING(); ret = GetExitCodeProcess (sitter->child_handle, &status); sitter->child_status = status; sitter->have_child_status = TRUE; CloseHandle (sitter->child_handle); sitter->child_handle = NULL; } #ifdef DBUS_BUILD_TESTS SetEvent (sitter->end_sync_event); #endif PING(); send (sitter->socket_to_main, " ", 1, 0); _dbus_babysitter_unref (sitter); return 0; }
/** * Spawns a new process. The executable name and argv[0] * are the same, both are provided in argv[0]. The child_setup * function is passed the given user_data and is run in the child * just before calling exec(). * * Also creates a "babysitter" which tracks the status of the * child process, advising the parent if the child exits. * If the spawn fails, no babysitter is created. * If sitter_p is #NULL, no babysitter is kept. * * @param sitter_p return location for babysitter or #NULL * @param argv the executable and arguments * @param env the environment (not used on unix yet) * @param child_setup function to call in child pre-exec() * @param user_data user data for setup function * @param error error object to be filled in if function fails * @returns #TRUE on success, #FALSE if error is filled in */ dbus_bool_t _dbus_spawn_async_with_babysitter (DBusBabysitter **sitter_p, char **argv, char **env, DBusSpawnChildSetupFunc child_setup, void *user_data, DBusError *error) { DBusBabysitter *sitter; int child_err_report_pipe[2] = { -1, -1 }; int babysitter_pipe[2] = { -1, -1 }; pid_t pid; _DBUS_ASSERT_ERROR_IS_CLEAR (error); if (sitter_p != NULL) *sitter_p = NULL; sitter = NULL; sitter = _dbus_babysitter_new (); if (sitter == NULL) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); return FALSE; } sitter->executable = _dbus_strdup (argv[0]); if (sitter->executable == NULL) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); goto cleanup_and_fail; } if (!make_pipe (child_err_report_pipe, error)) goto cleanup_and_fail; if (!_dbus_full_duplex_pipe (&babysitter_pipe[0], &babysitter_pipe[1], TRUE, error)) goto cleanup_and_fail; /* Setting up the babysitter is only useful in the parent, * but we don't want to run out of memory and fail * after we've already forked, since then we'd leak * child processes everywhere. */ sitter->error_watch = _dbus_watch_new (child_err_report_pipe[READ_END], DBUS_WATCH_READABLE, TRUE, handle_watch, sitter, NULL); if (sitter->error_watch == NULL) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); goto cleanup_and_fail; } if (!_dbus_watch_list_add_watch (sitter->watches, sitter->error_watch)) { /* we need to free it early so the destructor won't try to remove it * without it having been added, which DBusLoop doesn't allow */ _dbus_watch_invalidate (sitter->error_watch); _dbus_watch_unref (sitter->error_watch); sitter->error_watch = NULL; dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); goto cleanup_and_fail; } sitter->sitter_watch = _dbus_watch_new (babysitter_pipe[0], DBUS_WATCH_READABLE, TRUE, handle_watch, sitter, NULL); if (sitter->sitter_watch == NULL) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); goto cleanup_and_fail; } if (!_dbus_watch_list_add_watch (sitter->watches, sitter->sitter_watch)) { /* we need to free it early so the destructor won't try to remove it * without it having been added, which DBusLoop doesn't allow */ _dbus_watch_invalidate (sitter->sitter_watch); _dbus_watch_unref (sitter->sitter_watch); sitter->sitter_watch = NULL; dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); goto cleanup_and_fail; } _DBUS_ASSERT_ERROR_IS_CLEAR (error); pid = fork (); if (pid < 0) { dbus_set_error (error, DBUS_ERROR_SPAWN_FORK_FAILED, "Failed to fork (%s)", _dbus_strerror (errno)); goto cleanup_and_fail; } else if (pid == 0) { /* Immediate child, this is the babysitter process. */ int grandchild_pid; /* Be sure we crash if the parent exits * and we write to the err_report_pipe */ signal (SIGPIPE, SIG_DFL); /* Close the parent's end of the pipes. */ close_and_invalidate (&child_err_report_pipe[READ_END]); close_and_invalidate (&babysitter_pipe[0]); /* Create the child that will exec () */ grandchild_pid = fork (); if (grandchild_pid < 0) { write_err_and_exit (babysitter_pipe[1], CHILD_FORK_FAILED); _dbus_assert_not_reached ("Got to code after write_err_and_exit()"); } else if (grandchild_pid == 0) { do_exec (child_err_report_pipe[WRITE_END], argv, env, child_setup, user_data); _dbus_assert_not_reached ("Got to code after exec() - should have exited on error"); } else { babysit (grandchild_pid, babysitter_pipe[1]); _dbus_assert_not_reached ("Got to code after babysit()"); } } else { /* Close the uncared-about ends of the pipes */ close_and_invalidate (&child_err_report_pipe[WRITE_END]); close_and_invalidate (&babysitter_pipe[1]); sitter->socket_to_babysitter = babysitter_pipe[0]; babysitter_pipe[0] = -1; sitter->error_pipe_from_child = child_err_report_pipe[READ_END]; child_err_report_pipe[READ_END] = -1; sitter->sitter_pid = pid; if (sitter_p != NULL) *sitter_p = sitter; else _dbus_babysitter_unref (sitter); dbus_free_string_array (env); _DBUS_ASSERT_ERROR_IS_CLEAR (error); return TRUE; } cleanup_and_fail: _DBUS_ASSERT_ERROR_IS_SET (error); close_and_invalidate (&child_err_report_pipe[READ_END]); close_and_invalidate (&child_err_report_pipe[WRITE_END]); close_and_invalidate (&babysitter_pipe[0]); close_and_invalidate (&babysitter_pipe[1]); if (sitter != NULL) _dbus_babysitter_unref (sitter); return FALSE; }
EXPORT_C #endif dbus_bool_t _dbus_spawn_async_with_babysitter (DBusBabysitter **sitter_p, char **argv, DBusSpawnChildSetupFunc child_setup, void *user_data, DBusError *error) { #ifndef __SYMBIAN32__ DBusBabysitter *sitter; int child_err_report_pipe[2] = { -1, -1 }; int babysitter_pipe[2] = { -1, -1 }; pid_t pid; #ifndef __SYMBIAN32__ _DBUS_ASSERT_ERROR_IS_CLEAR (error); *sitter_p = NULL; sitter = NULL; sitter = _dbus_babysitter_new (); if (sitter == NULL) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); return FALSE; } sitter->executable = _dbus_strdup (argv[0]); if (sitter->executable == NULL) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); goto cleanup_and_fail; } if (!make_pipe (child_err_report_pipe, error)) goto cleanup_and_fail; _dbus_fd_set_close_on_exec (child_err_report_pipe[READ_END]); _dbus_fd_set_close_on_exec (child_err_report_pipe[WRITE_END]); if (!_dbus_full_duplex_pipe (&babysitter_pipe[0], &babysitter_pipe[1], TRUE, error)) goto cleanup_and_fail; _dbus_fd_set_close_on_exec (babysitter_pipe[0]); _dbus_fd_set_close_on_exec (babysitter_pipe[1]); /* Setting up the babysitter is only useful in the parent, * but we don't want to run out of memory and fail * after we've already forked, since then we'd leak * child processes everywhere. */ sitter->error_watch = _dbus_watch_new (child_err_report_pipe[READ_END], DBUS_WATCH_READABLE, TRUE, handle_watch, sitter, NULL); if (sitter->error_watch == NULL) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); goto cleanup_and_fail; } if (!_dbus_watch_list_add_watch (sitter->watches, sitter->error_watch)) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); goto cleanup_and_fail; } sitter->sitter_watch = _dbus_watch_new (babysitter_pipe[0], DBUS_WATCH_READABLE, TRUE, handle_watch, sitter, NULL); if (sitter->sitter_watch == NULL) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); goto cleanup_and_fail; } if (!_dbus_watch_list_add_watch (sitter->watches, sitter->sitter_watch)) { dbus_set_error (error, DBUS_ERROR_NO_MEMORY, NULL); goto cleanup_and_fail; } _DBUS_ASSERT_ERROR_IS_CLEAR (error); pid = fork (); if (pid < 0) { dbus_set_error (error, DBUS_ERROR_SPAWN_FORK_FAILED, "Failed to fork (%s)", _dbus_strerror (errno)); goto cleanup_and_fail; } else if (pid == 0) { /* Immediate child, this is the babysitter process. */ int grandchild_pid; /* Be sure we crash if the parent exits * and we write to the err_report_pipe */ #ifndef __SYMBIAN32__ signal (SIGPIPE, SIG_DFL); #endif /* Close the parent's end of the pipes. */ close_and_invalidate (&child_err_report_pipe[READ_END]); close_and_invalidate (&babysitter_pipe[0]); /* Create the child that will exec () */ grandchild_pid = fork (); if (grandchild_pid < 0) { write_err_and_exit (babysitter_pipe[1], CHILD_FORK_FAILED); _dbus_assert_not_reached ("Got to code after write_err_and_exit()"); } else if (grandchild_pid == 0) { do_exec (child_err_report_pipe[WRITE_END], argv, child_setup, user_data); _dbus_assert_not_reached ("Got to code after exec() - should have exited on error"); } else { babysit (grandchild_pid, babysitter_pipe[1]); _dbus_assert_not_reached ("Got to code after babysit()"); } } else { /* Close the uncared-about ends of the pipes */ close_and_invalidate (&child_err_report_pipe[WRITE_END]); close_and_invalidate (&babysitter_pipe[1]); sitter->socket_to_babysitter = babysitter_pipe[0]; babysitter_pipe[0] = -1; sitter->error_pipe_from_child = child_err_report_pipe[READ_END]; child_err_report_pipe[READ_END] = -1; sitter->sitter_pid = pid; if (sitter_p != NULL) *sitter_p = sitter; else _dbus_babysitter_unref (sitter); _DBUS_ASSERT_ERROR_IS_CLEAR (error); return TRUE; } cleanup_and_fail: _DBUS_ASSERT_ERROR_IS_SET (error); close_and_invalidate (&child_err_report_pipe[READ_END]); close_and_invalidate (&child_err_report_pipe[WRITE_END]); close_and_invalidate (&babysitter_pipe[0]); close_and_invalidate (&babysitter_pipe[1]); if (sitter != NULL) _dbus_babysitter_unref (sitter); #endif return FALSE; #else /* FILE* ChildProcessStream; ChildProcessStream =popen(argv[0], "r"); if (ChildProcessStream == NULL) { dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED, "Failed to execute service process"); return FALSE; } */ //static pid_t Childpid; //have to make it static for thread fuction to access it pid_t childPid; int retVal; //pthread_t thread; retVal = posix_spawn(&childPid, argv[0], NULL,NULL, NULL, NULL); if (retVal!=0) { dbus_set_error (error, DBUS_ERROR_SPAWN_EXEC_FAILED, "Failed to execute service process"); return FALSE; } /* pthread_create(&thread, NULL, (void*)&thread_fun, &Childpid); //(void) waitpid(Childpid, NULL, 0); //printf("\r\n*** Child process finished ***\r\n"); */ if(!retVal) return TRUE; else return FALSE; #endif }