diff --git a/src/shared/ask-password-agent.c b/src/shared/ask-password-agent.c index 62b73503ca..c5898ca564 100644 --- a/src/shared/ask-password-agent.c +++ b/src/shared/ask-password-agent.c @@ -8,7 +8,6 @@ #include "exec-util.h" #include "log.h" #include "process-util.h" -#include "terminal-util.h" static pid_t agent_pid = 0; @@ -18,26 +17,14 @@ int ask_password_agent_open(void) { if (agent_pid > 0) return 0; - /* We check STDIN here, not STDOUT, since this is about input, not output */ - if (!isatty_safe(STDIN_FILENO)) - return 0; - - /* Also check if we have a controlling terminal. If not (ENXIO here), we aren't actually invoked - * interactively on a terminal, hence fail */ - r = get_ctty_devnr(0, NULL); - if (r == -ENXIO) - return 0; - if (r < 0) + r = shall_fork_agent(); + if (r <= 0) return r; - if (!is_main_thread()) - return -EPERM; - r = fork_agent("(sd-askpwagent)", NULL, 0, &agent_pid, SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, - SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, "--watch"); if (r < 0) return log_error_errno(r, "Failed to fork TTY ask password agent: %m"); diff --git a/src/shared/exec-util.c b/src/shared/exec-util.c index 8435c4f118..35ceab980b 100644 --- a/src/shared/exec-util.c +++ b/src/shared/exec-util.c @@ -543,11 +543,24 @@ int fexecve_or_execve(int executable_fd, const char *executable, char *const arg return -errno; } +int shall_fork_agent(void) { + int r; + + /* Check if we have a controlling terminal. If not (ENXIO here), we aren't actually invoked + * interactively on a terminal, hence fail. */ + r = get_ctty_devnr(0, NULL); + if (r == -ENXIO) + return false; + if (r < 0) + return r; + + if (!is_main_thread()) + return -EPERM; + + return true; +} + int _fork_agent(const char *name, const int except[], size_t n_except, pid_t *ret_pid, const char *path, ...) { - bool stdout_is_tty, stderr_is_tty; - size_t n, i; - va_list ap; - char **l; int r; assert(path); @@ -567,56 +580,43 @@ int _fork_agent(const char *name, const int except[], size_t n_except, pid_t *re /* In the child: */ - stdout_is_tty = isatty_safe(STDOUT_FILENO); - stderr_is_tty = isatty_safe(STDERR_FILENO); + bool stdin_is_tty = isatty_safe(STDIN_FILENO), + stdout_is_tty = isatty_safe(STDOUT_FILENO), + stderr_is_tty = isatty_safe(STDERR_FILENO); - if (!stdout_is_tty || !stderr_is_tty) { + if (!stdin_is_tty || !stdout_is_tty || !stderr_is_tty) { int fd; - /* Detach from stdout/stderr and reopen /dev/tty for them. This is important to ensure that - * when systemctl is started via popen() or a similar call that expects to read EOF we + /* Detach from stdin/stdout/stderr and reopen /dev/tty for them. This is important to ensure + * that when systemctl is started via popen() or a similar call that expects to read EOF we * actually do generate EOF and not delay this indefinitely by keeping an unused copy of * stdin around. */ - fd = open("/dev/tty", O_WRONLY); + fd = open_terminal("/dev/tty", stdin_is_tty ? O_WRONLY : (stdout_is_tty && stderr_is_tty) ? O_RDONLY : O_RDWR); if (fd < 0) { - if (errno != ENXIO) { - log_error_errno(errno, "Failed to open /dev/tty: %m"); - _exit(EXIT_FAILURE); - } - - /* If we get ENXIO here we have no controlling TTY even though stdout/stderr are - * connected to a TTY. That's a weird setup, but let's handle it gracefully: let's - * skip the forking of the agents, given the TTY setup is not in order. */ - } else { - if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) { - log_error_errno(errno, "Failed to dup2 /dev/tty: %m"); - _exit(EXIT_FAILURE); - } - - if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) { - log_error_errno(errno, "Failed to dup2 /dev/tty: %m"); - _exit(EXIT_FAILURE); - } - - fd = safe_close_above_stdio(fd); + log_error_errno(fd, "Failed to open /dev/tty: %m"); + _exit(EXIT_FAILURE); } + + if (!stdin_is_tty && dup2(fd, STDIN_FILENO) < 0) { + log_error_errno(errno, "Failed to dup2 /dev/tty to STDIN: %m"); + _exit(EXIT_FAILURE); + } + + if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) { + log_error_errno(errno, "Failed to dup2 /dev/tty to STDOUT: %m"); + _exit(EXIT_FAILURE); + } + + if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) { + log_error_errno(errno, "Failed to dup2 /dev/tty to STDERR: %m"); + _exit(EXIT_FAILURE); + } + + fd = safe_close_above_stdio(fd); } /* Count arguments */ - va_start(ap, path); - for (n = 0; va_arg(ap, char*); n++) - ; - va_end(ap); - - /* Allocate strv */ - l = newa(char*, n + 1); - - /* Fill in arguments */ - va_start(ap, path); - for (i = 0; i <= n; i++) - l[i] = va_arg(ap, char*); - va_end(ap); - + char **l = strv_from_stdarg_alloca(path); execv(path, l); log_error_errno(errno, "Failed to execute %s: %m", path); _exit(EXIT_FAILURE); diff --git a/src/shared/exec-util.h b/src/shared/exec-util.h index ca7d30d45e..4f8eae26e7 100644 --- a/src/shared/exec-util.h +++ b/src/shared/exec-util.h @@ -61,5 +61,6 @@ ExecCommandFlags exec_command_flags_from_string(const char *s); int fexecve_or_execve(int executable_fd, const char *executable, char *const argv[], char *const envp[]); +int shall_fork_agent(void); int _fork_agent(const char *name, const int except[], size_t n_except, pid_t *ret_pid, const char *path, ...) _sentinel_; #define fork_agent(name, ...) _fork_agent(name, __VA_ARGS__, NULL) diff --git a/src/shared/polkit-agent.c b/src/shared/polkit-agent.c index 842e41e8db..a652b465b9 100644 --- a/src/shared/polkit-agent.c +++ b/src/shared/polkit-agent.c @@ -14,15 +14,15 @@ #include "polkit-agent.h" #include "process-util.h" #include "stdio-util.h" -#include "terminal-util.h" #include "time-util.h" #if ENABLE_POLKIT static pid_t agent_pid = 0; int polkit_agent_open(void) { + _cleanup_close_pair_ int pipe_fd[2] = EBADF_PAIR; char notify_fd[DECIMAL_STR_MAX(int) + 1]; - int pipe_fd[2], r; + int r; if (agent_pid > 0) return 0; @@ -31,21 +31,10 @@ int polkit_agent_open(void) { if (geteuid() == 0) return 0; - /* We check STDIN here, not STDOUT, since this is about input, not output */ - if (!isatty_safe(STDIN_FILENO)) - return 0; - - /* Also check if we have a controlling terminal. If not (ENXIO here), we aren't actually invoked - * interactively on a terminal, hence fail */ - r = get_ctty_devnr(0, NULL); - if (r == -ENXIO) - return 0; - if (r < 0) + r = shall_fork_agent(); + if (r <= 0) return r; - if (!is_main_thread()) - return -EPERM; - if (pipe2(pipe_fd, 0) < 0) return -errno; @@ -56,22 +45,18 @@ int polkit_agent_open(void) { 1, &agent_pid, POLKIT_AGENT_BINARY_PATH, - POLKIT_AGENT_BINARY_PATH, "--notify-fd", notify_fd, "--fallback"); + if (r < 0) + return log_error_errno(r, "Failed to fork polkit agent: %m"); /* Close the writing side, because that's the one for the agent */ - safe_close(pipe_fd[1]); + pipe_fd[1] = safe_close(pipe_fd[1]); - if (r < 0) - log_error_errno(r, "Failed to fork polkit agent: %m"); - else - /* Wait until the agent closes the fd */ - (void) fd_wait_for_event(pipe_fd[0], POLLHUP, USEC_INFINITY); + /* Wait until the agent closes the fd */ + (void) fd_wait_for_event(pipe_fd[0], POLLHUP, USEC_INFINITY); - safe_close(pipe_fd[0]); - - return r; + return 1; } void polkit_agent_close(void) {