diff options
author | Lubomir Rintel <lkundrak@v3.sk> | 2017-05-15 17:23:51 +0200 |
---|---|---|
committer | Lubomir Rintel <lkundrak@v3.sk> | 2017-05-15 17:23:51 +0200 |
commit | 24c079e4b2a06b4e5240a520650f6cfb183e2bf6 (patch) | |
tree | 5fa4fc54335cf85f89336de14f12ed5c950d23ea /clients | |
parent | 6fde475b2705381eb54350f116e978659f28b004 (diff) | |
download | NetworkManager-24c079e4b2a06b4e5240a520650f6cfb183e2bf6.tar.gz |
cli: spawn a pager when running on a terminal
This makes it a lot more convenient to deal with long outputs (such as
"nmcli c show id ...").
The implementation is essentially jacked from systemd. The bugs are
mine.
Diffstat (limited to 'clients')
-rw-r--r-- | clients/cli/general.c | 5 | ||||
-rw-r--r-- | clients/cli/nmcli.c | 16 | ||||
-rw-r--r-- | clients/cli/nmcli.h | 3 | ||||
-rw-r--r-- | clients/cli/utils.c | 112 | ||||
-rw-r--r-- | clients/cli/utils.h | 4 |
5 files changed, 132 insertions, 8 deletions
diff --git a/clients/cli/general.c b/clients/cli/general.c index 84c853aef0..7fa982fd2d 100644 --- a/clients/cli/general.c +++ b/clients/cli/general.c @@ -14,7 +14,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright 2010 - 2015 Red Hat, Inc. + * Copyright 2010 - 2017 Red Hat, Inc. */ #include "nm-default.h" @@ -1296,6 +1296,9 @@ do_overview (NmCli *nmc, int argc, char **argv) /* Register polkit agent */ nmc_start_polkit_agent_start_try (nmc); + /* Optionally start paging the output. */ + nmc_terminal_spawn_pager (&nmc->nmc_config); + /* The VPN connections don't have devices (yet?). */ p = nm_client_get_active_connections (nmc->client); for (i = 0; i < p->len; i++) { diff --git a/clients/cli/nmcli.c b/clients/cli/nmcli.c index c712fdec21..4403d53c6d 100644 --- a/clients/cli/nmcli.c +++ b/clients/cli/nmcli.c @@ -16,7 +16,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright 2010 - 2015 Red Hat, Inc. + * Copyright 2010 - 2017 Red Hat, Inc. */ #include "nm-default.h" @@ -148,7 +148,6 @@ complete_fields (const char *prefix) g_hash_table_destroy (h); } - static void usage (void) { @@ -547,7 +546,9 @@ nmc_init (NmCli *nmc) static void nmc_cleanup (NmCli *nmc) { - if (nmc->client) g_object_unref (nmc->client); + pid_t ret; + + g_clear_object (&nmc->client); g_string_free (nmc->return_text, TRUE); @@ -561,6 +562,15 @@ nmc_cleanup (NmCli *nmc) g_free (nmc->required_fields); + if (nmc->pager_pid > 0) { + fclose (stdout); + fclose (stderr); + do { + ret = waitpid (nmc->pager_pid, NULL, 0); + } while (ret == -1 && errno == EINTR); + nmc->pager_pid = 0; + } + nmc_polkit_agent_fini (nmc); } diff --git a/clients/cli/nmcli.h b/clients/cli/nmcli.h index 71ddb05596..874bca214b 100644 --- a/clients/cli/nmcli.h +++ b/clients/cli/nmcli.h @@ -14,7 +14,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright 2010 - 2015 Red Hat, Inc. + * Copyright 2010 - 2017 Red Hat, Inc. */ #ifndef NMC_NMCLI_H @@ -129,6 +129,7 @@ typedef struct _NmCli { NMCResultCode return_value; /* Return code of nmcli */ GString *return_text; /* Reason text */ + pid_t pager_pid; /* PID of a pager, if one was spawned */ int timeout; /* Operation timeout */ diff --git a/clients/cli/utils.c b/clients/cli/utils.c index f48d8639b9..117a33d62e 100644 --- a/clients/cli/utils.c +++ b/clients/cli/utils.c @@ -14,7 +14,8 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright 2010 - 2015 Red Hat, Inc. + * Copyright 2010 Lennart Poettering + * Copyright 2010 - 2017 Red Hat, Inc. */ #include "nm-default.h" @@ -28,6 +29,7 @@ #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> +#include <sys/prctl.h> #include "nm-client-utils.h" #include "nm-meta-setting-access.h" @@ -122,7 +124,7 @@ use_colors (NmcColorOption color_option) if (G_UNLIKELY (cached == NMC_USE_COLOR_AUTO)) { if ( g_strcmp0 (g_getenv ("TERM"), "dumb") == 0 - || !isatty (fileno (stdout))) + || !isatty (STDOUT_FILENO)) cached = NMC_USE_COLOR_NO; else cached = NMC_USE_COLOR_YES; @@ -1450,6 +1452,109 @@ nmc_print (const NmcConfig *nmc_config, /*****************************************************************************/ +static void +pager_fallback (void) +{ + char buf[64]; + int rb; + + do { + rb = read (STDIN_FILENO, buf, sizeof (buf)); + if (rb == -1) { + if (errno == EINTR) { + continue; + } else { + g_printerr (_("Error reading nmcli output: %s\n"), strerror (errno)); + _exit(EXIT_FAILURE); + } + } + if (write (STDOUT_FILENO, buf, rb) == -1) { + g_printerr (_("Error writing nmcli output: %s\n"), strerror (errno)); + _exit(EXIT_FAILURE); + } + } while (rb > 0); + + _exit(EXIT_SUCCESS); +} + +void +nmc_terminal_spawn_pager (const NmcConfig *nmc_config) +{ + const char *pager = getenv ("PAGER"); + pid_t parent_pid; + int fd[2]; + + if ( nm_cli.pager_pid > 0 + || nmc_config->print_output == NMC_PRINT_TERSE + || !use_colors (nmc_config->use_colors) + || g_strcmp0 (pager, "") == 0) + return; + + if (pipe (fd) == -1) { + g_printerr (_("Failed to create pager pipe: %s\n"), strerror (errno)); + return; + } + + parent_pid = getpid (); + + nm_cli.pager_pid = fork (); + if (nm_cli.pager_pid == -1) { + g_printerr (_("Failed to fork pager: %s\n"), strerror (errno)); + close (fd[0]); + close (fd[1]); + return; + } + + /* In the child start the pager */ + if (nm_cli.pager_pid == 0) { + dup2 (fd[0], STDIN_FILENO); + close (fd[0]); + close (fd[1]); + + setenv ("LESS", "FRSXMK", 1); + setenv ("LESSCHARSET", "utf-8", 1); + + /* 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); + + pager_fallback (); + /* not reached */ + } + + /* Return in the parent */ + if (dup2 (fd[1], STDOUT_FILENO) < 0) + g_printerr (_("Failed to duplicate pager pipe: %s\n"), strerror (errno)); + if (dup2 (fd[1], STDERR_FILENO) < 0) + g_printerr (_("Failed to duplicate pager pipe: %s\n"), strerror (errno)); + + close (fd[0]); + close (fd[1]); +} + +/*****************************************************************************/ + static const char * get_value_to_print (NmcColorOption color_option, const NmcOutputField *field, @@ -1514,6 +1619,9 @@ print_required_fields (const NmcConfig *nmc_config, gboolean field_names = of_flags & NMC_OF_FLAG_FIELD_NAMES; gboolean section_prefix = of_flags & NMC_OF_FLAG_SECTION_PREFIX; + /* Optionally start paging the output. */ + nmc_terminal_spawn_pager (nmc_config); + /* --- Main header --- */ if ((main_header_add || main_header_only) && pretty) { gs_free char *line = NULL; diff --git a/clients/cli/utils.h b/clients/cli/utils.h index 32a638af46..1724d4391e 100644 --- a/clients/cli/utils.h +++ b/clients/cli/utils.h @@ -14,7 +14,7 @@ * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * - * Copyright 2010 - 2015 Red Hat, Inc. + * Copyright 2010 - 2017 Red Hat, Inc. */ #ifndef NMC_UTILS_H @@ -40,6 +40,8 @@ gboolean nmc_parse_args (nmc_arg_t *arg_arr, gboolean last, int *argc, char ***a char *ssid_to_hex (const char *str, gsize len); void nmc_terminal_erase_line (void); void nmc_terminal_show_progress (const char *str); +void nmc_terminal_spawn_pager (const NmcConfig *nmc_config); +gboolean nmc_term_use_colors (NmcColorOption color_option); const char *nmc_term_color_sequence (NMMetaTermColor color); const char *nmc_term_format_sequence (NMMetaTermFormat format); NMMetaTermColor nmc_term_color_parse_string (const char *str, GError **error); |