X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dbus%2Fdbus-spawn.c;h=f8d5fd1c7c6a3bcdabd54beb4f746f0419c58c1b;hb=2c59787feb14f3f6274b3c346630a3340c24969f;hp=eaa9ef156c1849936b4075dc809d943709f9082d;hpb=f55897af74ac072d3447e5cf513d0f4718b142c7;p=platform%2Fupstream%2Fdbus.git diff --git a/dbus/dbus-spawn.c b/dbus/dbus-spawn.c index eaa9ef1..f8d5fd1 100644 --- a/dbus/dbus-spawn.c +++ b/dbus/dbus-spawn.c @@ -18,9 +18,12 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ + +#include + #include "dbus-spawn.h" #include "dbus-sysdeps-unix.h" #include "dbus-internals.h" @@ -36,6 +39,8 @@ #include #endif +extern char **environ; + /** * @addtogroup DBusInternalsUtils * @{ @@ -72,7 +77,7 @@ read_ints (int fd, while (TRUE) { - size_t chunk; + ssize_t chunk; size_t to_read; to_read = sizeof (int) * n_ints_in_buf - bytes; @@ -127,9 +132,9 @@ read_pid (int fd, while (TRUE) { - size_t chunk; + ssize_t chunk; size_t to_read; - + to_read = sizeof (pid_t) - bytes; if (to_read == 0) @@ -200,6 +205,9 @@ struct DBusBabysitter DBusWatch *error_watch; /**< Error pipe watch */ DBusWatch *sitter_watch; /**< Sitter pipe watch */ + DBusBabysitterFinishedFunc finished_cb; + void *finished_data; + int errnum; /**< Error number */ int status; /**< Exit status code */ unsigned int have_child_status : 1; /**< True if child status has been reaped */ @@ -252,6 +260,9 @@ _dbus_babysitter_ref (DBusBabysitter *sitter) return sitter; } +static void close_socket_to_babysitter (DBusBabysitter *sitter); +static void close_error_pipe_from_child (DBusBabysitter *sitter); + /** * Decrement the reference count on the babysitter object. * When the reference count of the babysitter object reaches @@ -268,25 +279,17 @@ _dbus_babysitter_unref (DBusBabysitter *sitter) sitter->refcount -= 1; if (sitter->refcount == 0) - { - if (sitter->socket_to_babysitter >= 0) - { - /* If we haven't forked other babysitters - * since this babysitter and socket were - * created then this close will cause the - * babysitter to wake up from poll with - * a hangup and then the babysitter will - * quit itself. - */ - _dbus_close_socket (sitter->socket_to_babysitter, NULL); - sitter->socket_to_babysitter = -1; - } + { + /* If we haven't forked other babysitters + * since this babysitter and socket were + * created then this close will cause the + * babysitter to wake up from poll with + * a hangup and then the babysitter will + * quit itself. + */ + close_socket_to_babysitter (sitter); - if (sitter->error_pipe_from_child >= 0) - { - _dbus_close_socket (sitter->error_pipe_from_child, NULL); - sitter->error_pipe_from_child = -1; - } + close_error_pipe_from_child (sitter); if (sitter->sitter_pid > 0) { @@ -336,21 +339,7 @@ _dbus_babysitter_unref (DBusBabysitter *sitter) sitter->sitter_pid = -1; } - - if (sitter->error_watch) - { - _dbus_watch_invalidate (sitter->error_watch); - _dbus_watch_unref (sitter->error_watch); - sitter->error_watch = NULL; - } - if (sitter->sitter_watch) - { - _dbus_watch_invalidate (sitter->sitter_watch); - _dbus_watch_unref (sitter->sitter_watch); - sitter->sitter_watch = NULL; - } - if (sitter->watches) _dbus_watch_list_free (sitter->watches); @@ -471,16 +460,42 @@ static void close_socket_to_babysitter (DBusBabysitter *sitter) { _dbus_verbose ("Closing babysitter\n"); - _dbus_close_socket (sitter->socket_to_babysitter, NULL); - sitter->socket_to_babysitter = -1; + + if (sitter->sitter_watch != NULL) + { + _dbus_assert (sitter->watches != NULL); + _dbus_watch_list_remove_watch (sitter->watches, sitter->sitter_watch); + _dbus_watch_invalidate (sitter->sitter_watch); + _dbus_watch_unref (sitter->sitter_watch); + sitter->sitter_watch = NULL; + } + + if (sitter->socket_to_babysitter >= 0) + { + _dbus_close_socket (sitter->socket_to_babysitter, NULL); + sitter->socket_to_babysitter = -1; + } } static void close_error_pipe_from_child (DBusBabysitter *sitter) { _dbus_verbose ("Closing child error\n"); - _dbus_close_socket (sitter->error_pipe_from_child, NULL); - sitter->error_pipe_from_child = -1; + + if (sitter->error_watch != NULL) + { + _dbus_assert (sitter->watches != NULL); + _dbus_watch_list_remove_watch (sitter->watches, sitter->error_watch); + _dbus_watch_invalidate (sitter->error_watch); + _dbus_watch_unref (sitter->error_watch); + sitter->error_watch = NULL; + } + + if (sitter->error_pipe_from_child >= 0) + { + _dbus_close_socket (sitter->error_pipe_from_child, NULL); + sitter->error_pipe_from_child = -1; + } } static void @@ -552,10 +567,21 @@ babysitter_iteration (DBusBabysitter *sitter, { int ret; - ret = _dbus_poll (fds, i, 0); + do + { + ret = _dbus_poll (fds, i, 0); + } + while (ret < 0 && errno == EINTR); + if (ret == 0 && block) - ret = _dbus_poll (fds, i, -1); - + { + do + { + ret = _dbus_poll (fds, i, -1); + } + while (ret < 0 && errno == EINTR); + } + if (ret > 0) { descriptors_ready = TRUE; @@ -736,7 +762,7 @@ handle_watch (DBusWatch *watch, unsigned int condition, void *data) { - DBusBabysitter *sitter = data; + DBusBabysitter *sitter = _dbus_babysitter_ref (data); int revents; int fd; @@ -758,7 +784,20 @@ handle_watch (DBusWatch *watch, 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 != -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; } @@ -792,9 +831,25 @@ static dbus_bool_t make_pipe (int p[2], DBusError *error) { + int retval; + +#ifdef HAVE_PIPE2 + dbus_bool_t cloexec_done; + + retval = pipe2 (p, O_CLOEXEC); + cloexec_done = retval >= 0; + + /* Check if kernel seems to be too old to know pipe2(). We assume + that if pipe2 is available, O_CLOEXEC is too. */ + if (retval < 0 && errno == ENOSYS) +#endif + { + retval = pipe(p); + } + _DBUS_ASSERT_ERROR_IS_CLEAR (error); - - if (pipe (p) < 0) + + if (retval < 0) { dbus_set_error (error, DBUS_ERROR_SPAWN_FAILED, @@ -803,6 +858,14 @@ make_pipe (int p[2], return FALSE; } +#ifdef HAVE_PIPE2 + if (!cloexec_done) +#endif + { + _dbus_fd_set_close_on_exec (p[0]); + _dbus_fd_set_close_on_exec (p[1]); + } + return TRUE; } @@ -869,10 +932,11 @@ write_status_and_exit (int fd, int status) static void do_exec (int child_err_report_fd, char **argv, + char **envp, DBusSpawnChildSetupFunc child_setup, void *user_data) { -#ifdef DBUS_BUILD_TESTS +#ifdef DBUS_ENABLE_EMBEDDED_TESTS int i, max_open; #endif @@ -883,7 +947,7 @@ do_exec (int child_err_report_fd, if (child_setup) (* child_setup) (user_data); -#ifdef DBUS_BUILD_TESTS +#ifdef DBUS_ENABLE_EMBEDDED_TESTS max_open = sysconf (_SC_OPEN_MAX); for (i = 3; i < max_open; i++) @@ -899,8 +963,15 @@ do_exec (int child_err_report_fd, _dbus_warn ("Fd %d did not have the close-on-exec flag set!\n", i); } #endif + + if (envp == NULL) + { + _dbus_assert (environ != NULL); + + envp = environ; + } - execv (argv[0], argv); + execve (argv[0], argv, envp); /* Exec failed */ write_err_and_exit (child_err_report_fd, @@ -971,9 +1042,9 @@ babysit_signal_handler (int signo) { char b = '\0'; again: - write (babysit_sigchld_pipe, &b, 1); - if (errno == EINTR) - goto again; + if (write (babysit_sigchld_pipe, &b, 1) <= 0) + if (errno == EINTR) + goto again; } static void @@ -1018,7 +1089,11 @@ babysit (pid_t grandchild_pid, pfds[1].events = _DBUS_POLLIN; pfds[1].revents = 0; - _dbus_poll (pfds, _DBUS_N_ELEMENTS (pfds), -1); + if (_dbus_poll (pfds, _DBUS_N_ELEMENTS (pfds), -1) < 0 && errno != EINTR) + { + _dbus_warn ("_dbus_poll() error: %s\n", strerror (errno)); + exit (1); + } if (pfds[0].revents != 0) { @@ -1027,7 +1102,10 @@ babysit (pid_t grandchild_pid, else if (pfds[1].revents & _DBUS_POLLIN) { char b; - read (sigchld_pipe[READ_END], &b, 1); + if (read (sigchld_pipe[READ_END], &b, 1) == -1) + { + /* ignore */ + } /* do waitpid check */ check_babysit_events (grandchild_pid, parent_pipe, 0); } @@ -1070,7 +1148,9 @@ _dbus_spawn_async_with_babysitter (DBusBabysitter **sitter_p, _DBUS_ASSERT_ERROR_IS_CLEAR (error); - *sitter_p = NULL; + if (sitter_p != NULL) + *sitter_p = NULL; + sitter = NULL; sitter = _dbus_babysitter_new (); @@ -1090,15 +1170,9 @@ _dbus_spawn_async_with_babysitter (DBusBabysitter **sitter_p, 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 @@ -1115,6 +1189,12 @@ _dbus_spawn_async_with_babysitter (DBusBabysitter **sitter_p, 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; } @@ -1130,6 +1210,12 @@ _dbus_spawn_async_with_babysitter (DBusBabysitter **sitter_p, 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; } @@ -1170,9 +1256,14 @@ _dbus_spawn_async_with_babysitter (DBusBabysitter **sitter_p, _dbus_assert_not_reached ("Got to code after write_err_and_exit()"); } else if (grandchild_pid == 0) - { + { + /* Go back to ignoring SIGPIPE, since it's evil + */ + signal (SIGPIPE, SIG_IGN); + 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"); } @@ -1201,6 +1292,8 @@ _dbus_spawn_async_with_babysitter (DBusBabysitter **sitter_p, else _dbus_babysitter_unref (sitter); + dbus_free_string_array (env); + _DBUS_ASSERT_ERROR_IS_CLEAR (error); return TRUE; @@ -1221,9 +1314,42 @@ _dbus_spawn_async_with_babysitter (DBusBabysitter **sitter_p, return FALSE; } +void +_dbus_babysitter_set_result_function (DBusBabysitter *sitter, + DBusBabysitterFinishedFunc finished, + void *user_data) +{ + sitter->finished_cb = finished; + sitter->finished_data = user_data; +} + /** @} */ -#ifdef DBUS_BUILD_TESTS +#ifdef DBUS_ENABLE_EMBEDDED_TESTS + +static char * +get_test_exec (const char *exe, + DBusString *scratch_space) +{ + const char *dbus_test_exec; + + dbus_test_exec = _dbus_getenv ("DBUS_TEST_EXEC"); + + if (dbus_test_exec == NULL) + dbus_test_exec = DBUS_TEST_EXEC; + + if (!_dbus_string_init (scratch_space)) + return NULL; + + if (!_dbus_string_append_printf (scratch_space, "%s/%s%s", + dbus_test_exec, exe, DBUS_EXEEXT)) + { + _dbus_string_free (scratch_space); + return NULL; + } + + return _dbus_string_get_data (scratch_space); +} static void _dbus_babysitter_block_for_child_exit (DBusBabysitter *sitter) @@ -1279,10 +1405,18 @@ check_spawn_segfault (void *data) char *argv[4] = { NULL, NULL, NULL, NULL }; DBusBabysitter *sitter = NULL; DBusError error = DBUS_ERROR_INIT; + DBusString argv0; /*** Test launching segfault binary */ - - argv[0] = TEST_SEGFAULT_BINARY; + + argv[0] = get_test_exec ("test-segfault", &argv0); + + if (argv[0] == NULL) + { + /* OOM was simulated, never mind */ + return TRUE; + } + if (_dbus_spawn_async_with_babysitter (&sitter, argv, NULL, NULL, NULL, &error)) @@ -1291,6 +1425,8 @@ check_spawn_segfault (void *data) _dbus_babysitter_set_child_exit_error (sitter, &error); } + _dbus_string_free (&argv0); + if (sitter) _dbus_babysitter_unref (sitter); @@ -1320,10 +1456,18 @@ check_spawn_exit (void *data) char *argv[4] = { NULL, NULL, NULL, NULL }; DBusBabysitter *sitter = NULL; DBusError error = DBUS_ERROR_INIT; + DBusString argv0; /*** Test launching exit failure binary */ - - argv[0] = TEST_EXIT_BINARY; + + argv[0] = get_test_exec ("test-exit", &argv0); + + if (argv[0] == NULL) + { + /* OOM was simulated, never mind */ + return TRUE; + } + if (_dbus_spawn_async_with_babysitter (&sitter, argv, NULL, NULL, NULL, &error)) @@ -1332,6 +1476,8 @@ check_spawn_exit (void *data) _dbus_babysitter_set_child_exit_error (sitter, &error); } + _dbus_string_free (&argv0); + if (sitter) _dbus_babysitter_unref (sitter); @@ -1361,10 +1507,18 @@ 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] = TEST_SLEEP_FOREVER_BINARY; + 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, argv, NULL, NULL, NULL, &error)) @@ -1376,6 +1530,8 @@ check_spawn_and_kill (void *data) _dbus_babysitter_set_child_exit_error (sitter, &error); } + _dbus_string_free (&argv0); + if (sitter) _dbus_babysitter_unref (sitter);