X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fsystemctl.c;h=66903aba28a5a1c276e081fa65a9a713be0e93ea;hb=7f6d613516020bf390d8de25bbbb2551ea8dade0;hp=585f1dc5fee1d56670a9c0089187a78ead227013;hpb=0736af98c6fae9c7d31e3dd17589421b7e883ef5;p=elogind.git diff --git a/src/systemctl.c b/src/systemctl.c index 585f1dc5f..66903aba2 100644 --- a/src/systemctl.c +++ b/src/systemctl.c @@ -110,11 +110,20 @@ static enum dot { static bool private_bus = false; +static pid_t pager_pid = 0; + static int daemon_reload(DBusConnection *bus, char **args, unsigned n); +static void pager_open(void); static bool on_tty(void) { static int t = -1; + /* Note that this is invoked relatively early, before we start + * the pager. That means the value we return reflects whether + * we originally were started on a tty, not if we currently + * are. But this is intended, since we want color, and so on + * when run in our own pager. */ + if (_unlikely_(t < 0)) t = isatty(STDOUT_FILENO) > 0; @@ -142,13 +151,15 @@ static void spawn_ask_password_agent(void) { if (child == 0) { /* In the child */ - const char * const args[] = { SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, "--watch", NULL }; + int fd; + + /* Make sure the agent goes away when the parent dies */ if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) _exit(EXIT_FAILURE); @@ -157,6 +168,31 @@ static void spawn_ask_password_agent(void) { if (getppid() != parent) _exit(EXIT_SUCCESS); + /* Don't leak fds to the agent */ + close_all_fds(NULL, 0); + + /* 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 + * because we keep an unused copy of stdin around. */ + if ((fd = open("/dev/tty", O_RDWR|O_CLOEXEC|O_NONBLOCK)) < 0) { + log_error("Failed to open /dev/tty: %m"); + _exit(EXIT_FAILURE); + } + + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + + if (fd > 2) + close(fd); + execv(args[0], (char **) args); _exit(EXIT_FAILURE); } @@ -419,6 +455,8 @@ static int list_units(DBusConnection *bus, char **args, unsigned n) { assert(bus); + pager_open(); + if (!(m = dbus_message_new_method_call( "org.freedesktop.systemd1", "/org/freedesktop/systemd1", @@ -491,8 +529,10 @@ static int list_units(DBusConnection *bus, char **args, unsigned n) { c++; } - qsort(unit_infos, c, sizeof(struct unit_info), compare_unit_info); - output_units_list(unit_infos, c); + if (c > 0) { + qsort(unit_infos, c, sizeof(struct unit_info), compare_unit_info); + output_units_list(unit_infos, c); + } r = 0; @@ -765,6 +805,8 @@ static int list_jobs(DBusConnection *bus, char **args, unsigned n) { assert(bus); + pager_open(); + if (!(m = dbus_message_new_method_call( "org.freedesktop.systemd1", "/org/freedesktop/systemd1", @@ -1602,6 +1644,8 @@ finish: } typedef struct ExecStatusInfo { + char *name; + char *path; char **argv; @@ -1619,6 +1663,7 @@ typedef struct ExecStatusInfo { static void exec_status_info_free(ExecStatusInfo *i) { assert(i); + free(i->name); free(i->path); strv_free(i->argv); free(i); @@ -1831,15 +1876,31 @@ static void print_status_info(UnitStatusInfo *i) { LIST_FOREACH(exec, p, i->exec) { char *t; + bool good; /* Only show exited processes here */ if (p->code == 0) continue; t = strv_join(p->argv, " "); - printf("\t Process: %u (%s, code=%s, ", p->pid, strna(t), sigchld_code_to_string(p->code)); + printf("\t Process: %u %s=%s ", p->pid, p->name, strna(t)); free(t); +#ifdef HAVE_SYSV_COMPAT + if (i->is_sysv) + good = is_clean_exit_lsb(p->code, p->status); + else +#endif + good = is_clean_exit(p->code, p->status); + + if (!good) { + on = ansi_highlight(true); + off = ansi_highlight(false); + } else + on = off = ""; + + printf("%s(code=%s, ", on, sigchld_code_to_string(p->code)); + if (p->code == CLD_EXITED) { const char *c; @@ -1854,7 +1915,10 @@ static void print_status_info(UnitStatusInfo *i) { } else printf("signal=%s", signal_to_string(p->status)); - printf(")\n"); + + printf(")%s\n", off); + + on = off = NULL; if (i->main_pid == p->pid && i->start_timestamp == p->start_timestamp && @@ -2070,6 +2134,11 @@ static int status_property(const char *name, DBusMessageIter *iter, UnitStatusIn if (!(info = new0(ExecStatusInfo, 1))) return -ENOMEM; + if (!(info->name = strdup(name))) { + free(info); + return -ENOMEM; + } + if ((r = exec_status_info_deserialize(&sub, info)) < 0) { free(info); return r; @@ -2475,6 +2544,9 @@ static int show(DBusConnection *bus, char **args, unsigned n) { show_properties = !streq(args[0], "status"); + if (show_properties) + pager_open(); + if (show_properties && n <= 1) { /* If not argument is specified inspect the manager * itself */ @@ -2858,6 +2930,8 @@ static int dump(DBusConnection *bus, char **args, unsigned n) { dbus_error_init(&error); + pager_open(); + if (!(m = dbus_message_new_method_call( "org.freedesktop.systemd1", "/org/freedesktop/systemd1", @@ -3220,6 +3294,8 @@ static int show_enviroment(DBusConnection *bus, char **args, unsigned n) { dbus_error_init(&error); + pager_open(); + if (!(m = dbus_message_new_method_call( "org.freedesktop.systemd1", "/org/freedesktop/systemd1", @@ -4251,7 +4327,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { ARG_SYSTEM, ARG_GLOBAL, ARG_NO_BLOCK, - ARG_NO_PAGER, + ARG_NO_PAGER, ARG_NO_WALL, ARG_ORDER, ARG_REQUIRE, @@ -4352,9 +4428,9 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_no_block = true; break; - case ARG_NO_PAGER: - arg_no_pager = true; - break; + case ARG_NO_PAGER: + arg_no_pager = true; + break; case ARG_NO_WALL: arg_no_wall = true; @@ -5283,45 +5359,82 @@ static int runlevel_main(void) { } static void pager_open(void) { - pid_t pid; - int fd[2]; - const char *pager = getenv("PAGER"); - - if (!on_tty() || arg_no_pager) - return; - if (!pager) - pager = "less"; - else if (!*pager || !strcmp(pager, "cat")) - return; - - if (pipe(fd) < 0) - return; - pid = fork(); - if (pid < 0) { - close(fd[0]); - close(fd[1]); - return; - } - - /* Return in the child */ - if (!pid) { - dup2(fd[1], 1); - close(fd[0]); - close(fd[1]); - return; - } - - /* The original process turns into the PAGER */ - dup2(fd[0], 0); - close(fd[0]); - close(fd[1]); - - setenv("LESS", "FRSX", 0); - execlp(pager, pager, NULL); - execl("/bin/sh", "sh", "-c", pager, NULL); - - log_error("unable to execute pager '%s'", pager); - _exit(EXIT_FAILURE); + int fd[2]; + const char *pager; + + if (pager_pid > 0) + return; + + if (!on_tty() || arg_no_pager) + return; + + if ((pager = getenv("PAGER"))) + if (!*pager || streq(pager, "cat")) + return; + + /* Determine and cache number of columns before we spawn the + * pager so that we get the value from the actual tty */ + columns(); + + if (pipe(fd) < 0) { + log_error("Failed to create pager pipe: %m"); + return; + } + + pager_pid = fork(); + if (pager_pid < 0) { + log_error("Failed to fork pager: %m"); + close_pipe(fd); + return; + } + + /* In the child start the pager */ + if (pager_pid == 0) { + + dup2(fd[0], STDIN_FILENO); + close_pipe(fd); + + setenv("LESS", "FRSX", 0); + + prctl(PR_SET_PDEATHSIG, SIGTERM); + + if (pager) { + execlp(pager, pager, NULL); + execl("/bin/sh", "sh", "-c", pager, NULL); + } else { + /* Debian's alternatives command for pagers is + * called 'pager'. Note that we do not call + * sensible-pagers here, since that is just a + * shell script that implements a logic that + * is similar to this one anyway, but is + * Debian-specific. */ + execlp("pager", "pager", NULL); + + execlp("less", "less", NULL); + execlp("more", "more", NULL); + } + + log_error("Unable to execute pager: %m"); + _exit(EXIT_FAILURE); + } + + /* Return in the parent */ + if (dup2(fd[1], STDOUT_FILENO) < 0) + log_error("Failed to duplicate pager pipe: %m"); + + close_pipe(fd); +} + +static void pager_close(void) { + siginfo_t dummy; + + if (pager_pid <= 0) + return; + + /* Inform pager that we are done */ + fclose(stdout); + wait_for_terminate(pager_pid, &dummy); + pager_pid = 0; } int main(int argc, char*argv[]) { @@ -5341,8 +5454,6 @@ int main(int argc, char*argv[]) { goto finish; } - pager_open(); - /* /sbin/runlevel doesn't need to communicate via D-Bus, so * let's shortcut this */ if (arg_action == ACTION_RUNLEVEL) { @@ -5406,5 +5517,7 @@ finish: strv_free(arg_property); + pager_close(); + return retval; }