bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2002-2018 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);
6aecc29b894bf0eb6784d212eec13cb37c72951cStephan Boschvoid program_client_local_exited(struct program_client_local *plclient);
1be27c35ea17fccd83c54e2acc66eb8c44d1a8feAki Tuomivoid exec_child(const char *bin_path, const char *const *args,
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)
41ee23907b084da5baed459e35bccd5a33430419Timo Sirainen if (in_fd != STDIN_FILENO && in_fd != dev_null_fd && close(in_fd) < 0)
41ee23907b084da5baed459e35bccd5a33430419Timo Sirainen if (out_fd != STDOUT_FILENO && out_fd != dev_null_fd &&
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,
6aecc29b894bf0eb6784d212eec13cb37c72951cStephan Bosch program_client_program_input(&plclient->client);
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomiint program_client_local_connect(struct program_client *pclient)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi int *parent_extra_fds = NULL, *child_extra_fds = NULL;
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);
e7d0bea63a08b08c47c4b5c187d2cb7127859657Timo Sirainen child_extra_fds = t_new(int, xfd_count * 2 + 1);
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 for(i = 0; i < xfd_count; i++) {
2558976f0b03852ba673d75d0af6850a61e733ceAki Tuomi /* if we want to allow root, then we will not drop
2558976f0b03852ba673d75d0af6850a61e733ceAki Tuomi root privileges */
816d20be0cf95fc4eb1a8aa716639e73b8ba525eMartti Rannanjärvi restrict_access(&pclient->set.restrict_set,
816d20be0cf95fc4eb1a8aa716639e73b8ba525eMartti Rannanjärvi pclient->set.allow_root ? RESTRICT_ACCESS_FLAG_ALLOW_ROOT : 0,
fc2b1489550d5b20f369ceaf92f590cf06b002fdStephan Bosch exec_child(pclient->path, pclient->args, &pclient->envs,
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 */
6aecc29b894bf0eb6784d212eec13cb37c72951cStephan Boschvoid program_client_local_exited(struct program_client_local *plclient)
6aecc29b894bf0eb6784d212eec13cb37c72951cStephan Bosch struct program_client *pclient = &plclient->client;
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi /* Evaluate child exit status */
d9683a6493b2298ecd309522df315f8e7188e869Stephan Bosch pclient->exit_code = PROGRAM_CLIENT_EXIT_INTERNAL_FAILURE;
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi /* Exited */
6aecc29b894bf0eb6784d212eec13cb37c72951cStephan Bosch int exit_code = WEXITSTATUS(plclient->status);
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi i_info("program `%s' terminated with non-zero exit code %d",
d9683a6493b2298ecd309522df315f8e7188e869Stephan Bosch pclient->exit_code = PROGRAM_CLIENT_EXIT_FAILURE;
d9683a6493b2298ecd309522df315f8e7188e869Stephan Bosch pclient->exit_code = PROGRAM_CLIENT_EXIT_SUCCESS;
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",
6aecc29b894bf0eb6784d212eec13cb37c72951cStephan Boschvoid program_client_local_kill(struct program_client_local *plclient)
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi /* time to die */
6aecc29b894bf0eb6784d212eec13cb37c72951cStephan Bosch if (plclient->client.error == PROGRAM_CLIENT_ERROR_NONE)
6aecc29b894bf0eb6784d212eec13cb37c72951cStephan Bosch plclient->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",
6aecc29b894bf0eb6784d212eec13cb37c72951cStephan Bosch plclient->client.path, plclient->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'",
6aecc29b894bf0eb6784d212eec13cb37c72951cStephan Bosch } else if (waitpid(plclient->pid, &plclient->status, 0) < 0) {
17541ea25593c656060199715051db2c1eef221dAki Tuomi i_debug("program `%s'(%d) execution timed out after %u milliseconds: "
6aecc29b894bf0eb6784d212eec13cb37c72951cStephan Bosch "sending TERM signal", plclient->client.path, plclient->pid,
6aecc29b894bf0eb6784d212eec13cb37c72951cStephan Bosch plclient->client.set.input_idle_timeout_msecs);
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'",
6aecc29b894bf0eb6784d212eec13cb37c72951cStephan Bosch plclient->to_kill = timeout_add_short(KILL_TIMEOUT,
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomivoid program_client_local_disconnect(struct program_client *pclient, bool force)
4036c1ca99d2c517f68a5b67a419ae7fdfd45300Aki Tuomi /* program never started */
d9683a6493b2298ecd309522df315f8e7188e869Stephan Bosch pclient->exit_code = PROGRAM_CLIENT_EXIT_FAILURE;
4fbe0d10901a80b27aacc9d9e6848e30e5fe727dAki Tuomi /* make sure it hasn't already been reaped */
6aecc29b894bf0eb6784d212eec13cb37c72951cStephan Bosch if (waitpid(plclient->pid, &plclient->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)
6aecc29b894bf0eb6784d212eec13cb37c72951cStephan Bosch plclient->to_kill = io_loop_move_timeout(&plclient->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;