From 5cc17919bcf0cc5474811e376bdba8989f0b54d3 Mon Sep 17 00:00:00 2001 From: Sebastian Harl Date: Sun, 18 Dec 2016 16:36:38 +0100 Subject: [PATCH] sysdb: Use a pager for displaying query results in interactive mode. For now, hard code the pager to 'less -FRX'. --- src/tools/sysdb/command.c | 103 ++++++++++++++++++++++++++++++++++++-- src/tools/sysdb/json.c | 16 +++--- src/tools/sysdb/json.h | 4 +- src/tools/sysdb/main.c | 6 ++- 4 files changed, 116 insertions(+), 13 deletions(-) diff --git a/src/tools/sysdb/command.c b/src/tools/sysdb/command.c index 61960be..5888963 100644 --- a/src/tools/sysdb/command.c +++ b/src/tools/sysdb/command.c @@ -40,12 +40,81 @@ #include "utils/proto.h" #include "utils/strbuf.h" +#include +#include + #include #include #include +#include +#include #include #include +#include + +static pid_t +exec_pager(int in) +{ + pid_t pid; + long fd; + + sigset_t sigs; + + pid = fork(); + if (pid) /* parent or error */ + return pid; + + sigemptyset(&sigs); + sigprocmask(SIG_SETMASK, &sigs, NULL); + + for (fd = 3; fd <= sysconf(_SC_OPEN_MAX); fd++) + if (fd != in) + close((int)fd); + + if (dup2(in, STDIN_FILENO) >= 0) { + /* TODO: make configurable */ + char *argv[] = { "less", "-FRX" }; + + close(in); + in = STDIN_FILENO; + + if (execvp(argv[0], argv)) { + char cmdbuf[1024], errbuf[1024]; + sdb_data_format(&(sdb_data_t){ + SDB_TYPE_STRING | SDB_TYPE_ARRAY, + { .array = { SDB_STATIC_ARRAY_LEN(argv), argv } }, + }, cmdbuf, sizeof(cmdbuf), SDB_UNQUOTED); + sdb_log(SDB_LOG_WARNING, "Failed to execute pager %s: %s", + cmdbuf, sdb_strerror(errno, errbuf, sizeof(errbuf))); + } + } + + /* else: something failed, simply print all input */ + while (42) { + char buf[1024 + 1]; + ssize_t n; + + n = read(in, buf, sizeof(buf) - 1); + if (!n) /* EOF */ + break; + + if (n > 0) { + buf[n] = '\0'; + printf("%s", buf); + continue; + } + + if ((errno == EAGAIN) || (errno == EWOULDBLOCK) + || (errno == EINTR)) + continue; + + sdb_log(SDB_LOG_ERR, "Failed to read query result: %s", + sdb_strerror(errno, buf, sizeof(buf))); + exit(1); + } + exit(0); +} /* exec_pager */ static void ok_printer(sdb_input_t __attribute__((unused)) *input, sdb_strbuf_t *buf) @@ -73,11 +142,15 @@ log_printer(sdb_input_t __attribute__((unused)) *input, sdb_strbuf_t *buf) } /* log_printer */ static void -data_printer(sdb_input_t __attribute__((unused)) *input, sdb_strbuf_t *buf) +data_printer(sdb_input_t *input, sdb_strbuf_t *buf) { size_t len = sdb_strbuf_len(buf); uint32_t type = 0; + int pipefd[2] = { -1, -1 }; + FILE *out = stdout; + pid_t pager = -1; + if ((! len) || (len == sizeof(uint32_t))) { /* empty command or empty reply */ return; @@ -88,11 +161,35 @@ data_printer(sdb_input_t __attribute__((unused)) *input, sdb_strbuf_t *buf) return; } + if (input->interactive) { + if (pipe(pipefd)) { + char errbuf[2014]; + sdb_log(SDB_LOG_WARNING, "Failed to open pipe: %s", + sdb_strerror(errno, errbuf, sizeof(errbuf))); + } + else { + out = fdopen(pipefd[1], "w"); + pager = exec_pager(pipefd[0]); + if (pager < 0) { + out = stdout; + close(pipefd[0]); + close(pipefd[1]); + } + else + close(pipefd[0]); + } + } + sdb_proto_unmarshal_int32(SDB_STRBUF_STR(buf), &type); sdb_strbuf_skip(buf, 0, sizeof(uint32_t)); - if (sdb_json_print(input, (int)type, buf)) + if (sdb_json_print(out, input, (int)type, buf)) sdb_log(SDB_LOG_ERR, "Failed to print result"); - printf("\n"); + fprintf(out, "\n"); + + if (out != stdout) + fclose(out); /* will close pipefd[1] */ + if (pager > 0) + waitpid(pager, NULL, 0); } /* data_printer */ static struct { diff --git a/src/tools/sysdb/json.c b/src/tools/sysdb/json.c index 1bd8080..8e9b1e7 100644 --- a/src/tools/sysdb/json.c +++ b/src/tools/sysdb/json.c @@ -52,6 +52,8 @@ */ typedef struct { + FILE *out; + /* The context describes the state of the formatter along with the * respective parent contexts. */ int context[8]; @@ -62,7 +64,7 @@ typedef struct { bool have_output; } formatter_t; #define F(obj) ((formatter_t *)(obj)) -#define F_INIT { { 0 }, { -1, -1, -1, -1, -1, -1, -1, -1 }, 0, 0, false } +#define F_INIT(out) { (out), { 0 }, { -1, -1, -1, -1, -1, -1, -1, -1 }, 0, 0, false } static int push(formatter_t *f, int type) @@ -94,10 +96,10 @@ print(formatter_t *f, const char *s, ssize_t l) char buf[l + 1]; strncpy(buf, s, l); buf[l] = '\0'; - printf("%s", buf); + fprintf(f->out, "%s", buf); } else - printf("%s", s); + fprintf(f->out, "%s", s); f->have_output = true; } /* print */ @@ -252,7 +254,7 @@ static yajl_callbacks fmts = { */ int -sdb_json_print(sdb_input_t *input, int type, sdb_strbuf_t *buf) +sdb_json_print(FILE *out, sdb_input_t *input, int type, sdb_strbuf_t *buf) { #ifdef HAVE_LIBYAJL const unsigned char *json; @@ -260,13 +262,13 @@ sdb_json_print(sdb_input_t *input, int type, sdb_strbuf_t *buf) yajl_handle h; yajl_status status; - formatter_t f = F_INIT; + formatter_t f = F_INIT(out); int ret = 0; if (!input->interactive) { /* no formatting */ - printf("%s\n", sdb_strbuf_string(buf)); + fprintf(out, "%s\n", sdb_strbuf_string(buf)); return 0; } @@ -306,7 +308,7 @@ sdb_json_print(sdb_input_t *input, int type, sdb_strbuf_t *buf) #else /* HAVE_LIBYAJL */ (void)input; (void)type; - printf("%s\n", sdb_strbuf_string(buf)); + fprintf(out, "%s\n", sdb_strbuf_string(buf)); return 0; #endif /* HAVE_LIBYAJL */ } /* sdb_json_print */ diff --git a/src/tools/sysdb/json.h b/src/tools/sysdb/json.h index b276eff..ef7210d 100644 --- a/src/tools/sysdb/json.h +++ b/src/tools/sysdb/json.h @@ -29,6 +29,8 @@ #include "utils/strbuf.h" +#include + #ifndef SYSDB_JSON_H #define SYSDB_JSON_H 1 @@ -38,7 +40,7 @@ * specified buffer. The output is printed to the standard output channel. */ int -sdb_json_print(sdb_input_t *input, int type, sdb_strbuf_t *buf); +sdb_json_print(FILE *out, sdb_input_t *input, int type, sdb_strbuf_t *buf); #endif /* SYSDB_JSON_H */ diff --git a/src/tools/sysdb/main.c b/src/tools/sysdb/main.c index b45ef87..1f3975a 100644 --- a/src/tools/sysdb/main.c +++ b/src/tools/sysdb/main.c @@ -51,11 +51,10 @@ #include #include - +#include #include #include #include - #include #include @@ -358,6 +357,9 @@ main(int argc, char **argv) } } + signal(SIGPIPE, SIG_IGN); + signal(SIGCHLD, SIG_IGN); + sdb_input_mainloop(); sdb_client_shutdown(input.client, SHUT_WR); -- 2.30.2