Code

sysdb: Use a pager for displaying query results in interactive mode.
authorSebastian Harl <sh@tokkee.org>
Sun, 18 Dec 2016 15:36:38 +0000 (16:36 +0100)
committerSebastian Harl <sh@tokkee.org>
Sun, 18 Dec 2016 15:36:38 +0000 (16:36 +0100)
For now, hard code the pager to 'less -FRX'.

src/tools/sysdb/command.c
src/tools/sysdb/json.c
src/tools/sysdb/json.h
src/tools/sysdb/main.c

index 61960be..5888963 100644 (file)
 #include "utils/proto.h"
 #include "utils/strbuf.h"
 
+#include <sys/types.h>
+#include <sys/wait.h>
+
 #include <errno.h>
 
 #include <assert.h>
 #include <ctype.h>
+#include <signal.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
+
+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 {
index 1bd8080..8e9b1e7 100644 (file)
@@ -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 */
index b276eff..ef7210d 100644 (file)
@@ -29,6 +29,8 @@
 
 #include "utils/strbuf.h"
 
+#include <stdio.h>
+
 #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 */
 
index b45ef87..1f3975a 100644 (file)
 
 #include <sys/stat.h>
 #include <fcntl.h>
-
+#include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-
 #include <unistd.h>
 
 #include <sys/types.h>
@@ -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);