From: Lennart Poettering Date: Thu, 7 Jul 2011 00:34:35 +0000 (+0200) Subject: cgls: add pager support to systemd-cgls X-Git-Tag: v30~45 X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1968a360405e302d4d2c2abc03a3314f81375156;p=systemd cgls: add pager support to systemd-cgls --- diff --git a/Makefile.am b/Makefile.am index 9a675056..b4feb7a9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1118,7 +1118,8 @@ systemctl_SOURCES = \ src/cgroup-show.c \ src/cgroup-util.c \ src/exit-status.c \ - src/unit-name.c + src/unit-name.c \ + src/pager.c systemctl_CFLAGS = \ $(AM_CFLAGS) \ @@ -1177,7 +1178,8 @@ systemd_readahead_replay_LDADD = \ systemd_cgls_SOURCES = \ src/cgls.c \ src/cgroup-show.c \ - src/cgroup-util.c + src/cgroup-util.c \ + src/pager.c systemd_cgls_CFLAGS = \ $(AM_CFLAGS) diff --git a/src/cgls.c b/src/cgls.c index 2bde743a..aebf826c 100644 --- a/src/cgls.c +++ b/src/cgls.c @@ -30,20 +30,29 @@ #include "cgroup-util.h" #include "log.h" #include "util.h" +#include "pager.h" + +static bool arg_no_pager = false; static void help(void) { printf("%s [OPTIONS...] [CGROUP...]\n\n" "Recursively show control group contents.\n\n" - " -h --help Show this help\n", + " -h --help Show this help\n" + " --no-pager Do not pipe output into a pager.\n", program_invocation_short_name); } static int parse_argv(int argc, char *argv[]) { + enum { + ARG_NO_PAGER = 0x100 + }; + static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { NULL, 0, NULL, 0 } + { "help", no_argument, NULL, 'h' }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { NULL, 0, NULL, 0 } }; int c; @@ -59,6 +68,10 @@ static int parse_argv(int argc, char *argv[]) { help(); return 0; + case ARG_NO_PAGER: + arg_no_pager = true; + break; + case '?': return -EINVAL; @@ -84,6 +97,9 @@ int main(int argc, char *argv[]) { goto finish; } + if (!arg_no_pager) + pager_open(); + if (optind < argc) { unsigned i; @@ -132,6 +148,7 @@ int main(int argc, char *argv[]) { retval = EXIT_SUCCESS; finish: + pager_close(); return retval; } diff --git a/src/pager.c b/src/pager.c new file mode 100644 index 00000000..6ea25ada --- /dev/null +++ b/src/pager.c @@ -0,0 +1,122 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "pager.h" +#include "util.h" +#include "macro.h" + +static pid_t pager_pid = 0; + +void pager_open(void) { + int fd[2]; + const char *pager; + pid_t parent_pid; + + if (pager_pid > 0) + return; + + if (isatty(STDOUT_FILENO) <= 0) + return; + + if ((pager = getenv("SYSTEMD_PAGER")) || (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; + } + + parent_pid = getpid(); + + 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); + + /* Make sure the pager goes away when the parent dies */ + if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) + _exit(EXIT_FAILURE); + + /* Check whether our parent died before we were able + * to set the death signal */ + if (getppid() != parent_pid) + _exit(EXIT_SUCCESS); + + if (pager) { + execlp(pager, pager, NULL); + execl("/bin/sh", "sh", "-c", pager, NULL); + } + + /* 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); + execlp("cat", "cat", 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); +} + +void pager_close(void) { + + if (pager_pid <= 0) + return; + + /* Inform pager that we are done */ + fclose(stdout); + kill(pager_pid, SIGCONT); + wait_for_terminate(pager_pid, NULL); + pager_pid = 0; +} diff --git a/src/pager.h b/src/pager.h new file mode 100644 index 00000000..b5b49984 --- /dev/null +++ b/src/pager.h @@ -0,0 +1,28 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef foopagerhfoo +#define foopagerhfoo + +/*** + This file is part of systemd. + + Copyright 2010 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +void pager_open(void); +void pager_close(void); + +#endif diff --git a/src/systemctl.c b/src/systemctl.c index 8f904c16..005b45d4 100644 --- a/src/systemctl.c +++ b/src/systemctl.c @@ -56,6 +56,7 @@ #include "bus-errors.h" #include "build.h" #include "unit-name.h" +#include "pager.h" static const char *arg_type = NULL; static char **arg_property = NULL; @@ -118,11 +119,14 @@ static const char *arg_host = NULL; static bool private_bus = false; -static pid_t pager_pid = 0; static pid_t agent_pid = 0; static int daemon_reload(DBusConnection *bus, char **args, unsigned n); -static void pager_open(void); + +static void pager_open_if_enabled(void) { + if (!arg_no_pager) + pager_open(); +} static bool on_tty(void) { static int t = -1; @@ -475,7 +479,7 @@ static int list_units(DBusConnection *bus, char **args, unsigned n) { assert(bus); - pager_open(); + pager_open_if_enabled(); if (!(m = dbus_message_new_method_call( "org.freedesktop.systemd1", @@ -825,7 +829,7 @@ static int list_jobs(DBusConnection *bus, char **args, unsigned n) { assert(bus); - pager_open(); + pager_open_if_enabled(); if (!(m = dbus_message_new_method_call( "org.freedesktop.systemd1", @@ -2652,7 +2656,7 @@ static int show(DBusConnection *bus, char **args, unsigned n) { show_properties = !streq(args[0], "status"); if (show_properties) - pager_open(); + pager_open_if_enabled(); if (show_properties && n <= 1) { /* If not argument is specified inspect the manager @@ -3048,7 +3052,7 @@ static int dump(DBusConnection *bus, char **args, unsigned n) { dbus_error_init(&error); - pager_open(); + pager_open_if_enabled(); if (!(m = dbus_message_new_method_call( "org.freedesktop.systemd1", @@ -3419,7 +3423,7 @@ static int show_enviroment(DBusConnection *bus, char **args, unsigned n) { dbus_error_init(&error); - pager_open(); + pager_open_if_enabled(); if (!(m = dbus_message_new_method_call( "org.freedesktop.systemd1", @@ -5602,96 +5606,6 @@ static int runlevel_main(void) { return 0; } -static void pager_open(void) { - int fd[2]; - const char *pager; - pid_t parent_pid; - - if (pager_pid > 0) - return; - - if (!on_tty() || arg_no_pager) - return; - - if ((pager = getenv("SYSTEMD_PAGER")) || (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; - } - - parent_pid = getpid(); - - 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); - - /* Make sure the pager goes away when the parent dies */ - if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0) - _exit(EXIT_FAILURE); - - /* Check whether our parent died before we were able - * to set the death signal */ - if (getppid() != parent_pid) - _exit(EXIT_SUCCESS); - - 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); - kill(pager_pid, SIGCONT); - wait_for_terminate(pager_pid, &dummy); - pager_pid = 0; -} - static void agent_close(void) { siginfo_t dummy; diff --git a/src/util.c b/src/util.c index 356b4f9d..a0fbdc51 100644 --- a/src/util.c +++ b/src/util.c @@ -3866,8 +3866,12 @@ char *normalize_env_assignment(const char *s) { } int wait_for_terminate(pid_t pid, siginfo_t *status) { + siginfo_t dummy; + assert(pid >= 1); - assert(status); + + if (!status) + status = &dummy; for (;;) { zero(*status);