program-client-local.c revision 2558976f0b03852ba673d75d0af6850a61e733ce
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi/* Copyright (c) 2002-2016 Dovecot authors, see the included COPYING file
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomivoid program_client_local_waitchild(const struct child_wait_status *,
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomivoid program_client_local_disconnect(struct program_client *pclient, bool force);
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomivoid program_client_local_exited(struct program_client_local *slclient);
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomivoid exec_child(const char *bin_path, const char *const *args,
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomi const char *const *envs,
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi int in_fd, int out_fd, int *extra_fds, bool drop_stderr)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (in_fd != STDIN_FILENO && dup2(in_fd, STDIN_FILENO) < 0)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (out_fd != STDOUT_FILENO && dup2(out_fd, STDOUT_FILENO) < 0)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (out_fd != STDOUT_FILENO && (out_fd != in_fd) && close(out_fd) < 0)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* Drop stderr if requested */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* Setup extra fds */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* Compose argv */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* Setup environment */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* Execute */
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomivoid program_client_local_waitchild(const struct child_wait_status *status,
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomiint program_client_local_connect(struct program_client *pclient)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi int *parent_extra_fds = NULL, *child_extra_fds = NULL;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi unsigned int xfd_count = 0, i;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* create normal I/O fds */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (pclient->output != NULL || pclient->output_seekable) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* create pipes for additional output through side-channel fds */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi efds = array_get_modifiable(&pclient->extra_fds, &xfd_count);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi parent_extra_fds = t_malloc0(sizeof(int) * xfd_count);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi for(i = 0; i < xfd_count; i++) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* fork child */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* clean up */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi for(i = 0; i < xfd_count; i++) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi unsigned int count;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi for(i = 0; i < xfd_count; i++) {
2558976f0b03852ba673d75d0af6850a61e733ceAki Tuomi /* if we want to allow root, then we will not drop
2558976f0b03852ba673d75d0af6850a61e733ceAki Tuomi root privileges */
1acc8d6538864577b8f40cc4e1ca922a62f52327Aki Tuomi restrict_access(&pclient->set.restrict_set, pclient->set.home,
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* parent */
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi if (fd_out[0] >= 0) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi for(i = 0; i < xfd_count; i++) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomiint program_client_local_close_output(struct program_client *pclient)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* Shutdown output; program stdin will get EOF */
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomivoid program_client_local_exited(struct program_client_local *slclient)
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi struct program_client *pclient = &slclient->client;
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi /* Evaluate child exit status */
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi /* Exited */
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi i_info("program `%s' terminated with non-zero exit code %d",
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi /* Killed with a signal */
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi i_error("program `%s' was forcibly terminated with signal %d",
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi i_error("program `%s' terminated abnormally, signal %d",
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi /* Stopped */
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi /* Something else */
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi i_error("program `%s' terminated abnormally, return status %d",
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomivoid program_client_local_kill(struct program_client_local *slclient)
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi /* time to die */
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi if (slclient->client.error == PROGRAM_CLIENT_ERROR_NONE)
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi slclient->client.error = PROGRAM_CLIENT_ERROR_RUN_TIMEOUT;
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi /* no need for this anymore */
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi /* Timed out again */
17541ea25593c656060199715051db2c1eef221dAki Tuomi i_debug("program `%s' (%d) did not die after %d milliseconds: "
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi "sending KILL signal",
17541ea25593c656060199715051db2c1eef221dAki Tuomi slclient->client.path, slclient->pid, KILL_TIMEOUT);
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi /* Kill it brutally now, it should die right away */
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi i_error("failed to send SIGKILL signal to program `%s'",
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi } else if (waitpid(slclient->pid, &slclient->status, 0) < 0) {
17541ea25593c656060199715051db2c1eef221dAki Tuomi i_debug("program `%s'(%d) execution timed out after %u milliseconds: "
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi "sending TERM signal", slclient->client.path, slclient->pid,
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi /* send sigterm, keep on waiting */
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi /* Kill child gently first */
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi i_error("failed to send SIGTERM signal to program `%s'",
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi slclient->to_kill = timeout_add_short(KILL_TIMEOUT,
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomivoid program_client_local_disconnect(struct program_client *pclient, bool force)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* program never started */
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi /* make sure it hasn't already been reaped */
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi if (waitpid(slclient->pid, &slclient->status, WNOHANG) > 0) {
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* Calculate timeout */
17541ea25593c656060199715051db2c1eef221dAki Tuomi runtime = timeval_diff_msecs(&ioloop_timeval, &pclient->start_time);
17541ea25593c656060199715051db2c1eef221dAki Tuomi if (!force && pclient->set.input_idle_timeout_msecs > 0 &&
17541ea25593c656060199715051db2c1eef221dAki Tuomi timeout = pclient->set.input_idle_timeout_msecs - runtime;
17541ea25593c656060199715051db2c1eef221dAki Tuomi i_debug("waiting for program `%s' to finish after %lu msecs",
17541ea25593c656060199715051db2c1eef221dAki Tuomi (timeout == 0 && pclient->set.input_idle_timeout_msecs > 0);
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomivoid program_client_local_destroy(struct program_client *pclient ATTR_UNUSED)
204afc1f4f37a4f1cb53ff44b993a661cc45bf5dAki Tuomivoid program_client_local_switch_ioloop(struct program_client *pclient)
204afc1f4f37a4f1cb53ff44b993a661cc45bf5dAki Tuomi slclient->to_kill = io_loop_move_timeout(&slclient->to_kill);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi const char *const *args,
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi pool = pool_alloconly_create("program client local", 1024);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi pclient = p_new(pool, struct program_client_local, 1);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi program_client_init(&pclient->client, pool, bin_path, args, set);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi pclient->client.connect = program_client_local_connect;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi pclient->client.close_output = program_client_local_close_output;
204afc1f4f37a4f1cb53ff44b993a661cc45bf5dAki Tuomi pclient->client.switch_ioloop = program_client_local_switch_ioloop;
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi pclient->client.disconnect = program_client_local_disconnect;