summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLubomir Rintel <lkundrak@v3.sk>2017-05-15 17:23:51 +0200
committerLubomir Rintel <lkundrak@v3.sk>2017-05-15 17:23:51 +0200
commit24c079e4b2a06b4e5240a520650f6cfb183e2bf6 (patch)
tree5fa4fc54335cf85f89336de14f12ed5c950d23ea
parent6fde475b2705381eb54350f116e978659f28b004 (diff)
downloadNetworkManager-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.
-rw-r--r--clients/cli/general.c5
-rw-r--r--clients/cli/nmcli.c16
-rw-r--r--clients/cli/nmcli.h3
-rw-r--r--clients/cli/utils.c112
-rw-r--r--clients/cli/utils.h4
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);