X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=dbus%2Fdbus-spawn.c;h=55a7e1e620e8e79fc359188e1d429c7add604d9d;hb=757b80b9711d9733798c927495d74c7323e95400;hp=ba6e6ddfd0602ef8b93bc39874621efff8f0ac2e;hpb=7ba3dc8bf980857e03e0f19713adb918dcab4d15;p=platform%2Fupstream%2Fdbus.git diff --git a/dbus/dbus-spawn.c b/dbus/dbus-spawn.c index ba6e6dd..55a7e1e 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,8 +77,8 @@ read_ints (int fd, while (TRUE) { - size_t chunk; - ssize_t to_read; + 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 to_read; - + 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 @@ -747,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; @@ -769,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; } @@ -803,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, @@ -814,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; } @@ -880,6 +932,7 @@ 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) { @@ -910,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, @@ -982,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 @@ -1042,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); } @@ -1107,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 @@ -1132,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; } @@ -1147,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; } @@ -1187,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"); } @@ -1218,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; @@ -1238,10 +1314,43 @@ _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 +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) { @@ -1296,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)) @@ -1308,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); @@ -1337,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)) @@ -1349,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); @@ -1378,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)) @@ -1393,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);