Code

Merged branch 'master' of git://git.tokkee.org/sysdb.
authorSebastian Harl <sh@tokkee.org>
Fri, 12 Dec 2014 07:55:39 +0000 (08:55 +0100)
committerSebastian Harl <sh@tokkee.org>
Fri, 12 Dec 2014 07:57:51 +0000 (08:57 +0100)
README
src/include/core/store.h
src/tools/sysdb/command.c
src/tools/sysdb/input.c
src/tools/sysdb/input.h
src/tools/sysdb/main.c
src/tools/sysdb/scanner.l
t/integration/query.sh

diff --git a/README b/README
index f2bfa10927425c87fc22a5d9aab0e0ce6993626b..a681916c3cbcc0cdf141de7664f1a5f5d8262e1e 100644 (file)
--- a/README
+++ b/README
@@ -51,7 +51,7 @@ Prerequisites
 
   To compile the SysDB package from source you need:
 
-  * A build environment: autotools, libtool, C compiler, ...
+  * A build environment: autotools, libtool, C and C++ compilers, ...
 
     <http://www.gnu.org/software/automake/>
     <http://www.gnu.org/software/autoconf/>
@@ -87,10 +87,22 @@ Prerequisites
 
   * libdbi:
     The database independent abstraction layer is used for database access by
-    the puppet::store-configs plugin.
+    the backend::puppet::store-configs plugin.
 
     <http://libdbi.sourceforge.net/>
 
+  * libfacter:
+    The cfacter library for gathering facts about a system is used by the
+    backend::facter plugin to retrieve attributes about the local system.
+
+    <https://github.com/puppetlabs/cfacter>
+
+  * librrd:
+    The RRDtool library is used by the timeseries::rrdtool plugin to retrieve
+    time-series from RRD files.
+
+    <https://oss.oetiker.ch/rrdtool/>
+
 Testing
 -------
 
index 6edbea4f173e89000f7869217b145a38ec3294be..2253d55a417ed65f75a422c244810bbdd02e6f8f 100644 (file)
@@ -49,7 +49,8 @@ enum {
        SDB_HOST = 1,
        SDB_SERVICE,
        SDB_METRIC,
-       SDB_ATTRIBUTE,
+
+       SDB_ATTRIBUTE = 1 << 4,
 
        /*
         * Queryable fields of a stored object.
index 143661868d49204c9436bdd2e370a1346881df71..9589875300d468c7b9e6b7268fef5705a9580773 100644 (file)
@@ -52,14 +52,13 @@ log_printer(sdb_strbuf_t *buf)
        uint32_t prio = sdb_proto_get_int(buf, 0);
 
        if (prio == UINT32_MAX) {
-               printf("ERROR: Received a LOG message with invalid "
-                               "or missing priority\n");
-               return;
+               sdb_log(SDB_LOG_WARNING, "Received a LOG message with invalid "
+                               "or missing priority");
+               prio = (uint32_t)SDB_LOG_ERR;
        }
        sdb_strbuf_skip(buf, 0, sizeof(prio));
 
-       printf("%s: %s\n", SDB_LOG_PRIO_TO_STRING((int)prio),
-                       sdb_strbuf_string(buf));
+       sdb_log((int)prio, "%s", sdb_strbuf_string(buf));
 } /* log_printer */
 
 static void
@@ -72,8 +71,8 @@ data_printer(sdb_strbuf_t *buf)
                return;
        }
        else if (len < sizeof(uint32_t)) {
-               printf("ERROR: Received a DATA message with invalid "
-                               "or missing data-type\n");
+               sdb_log(SDB_LOG_ERR, "Received a DATA message with invalid "
+                               "or missing data-type");
                return;
        }
 
@@ -91,6 +90,15 @@ static struct {
        { SDB_CONNECTION_DATA, data_printer },
 };
 
+static void
+clear_query(sdb_input_t *input)
+{
+       sdb_strbuf_skip(input->input, 0, input->query_len);
+       input->tokenizer_pos -= input->query_len;
+       input->query_len = 0;
+       input->have_input = 0;
+} /* clear_query */
+
 /*
  * public API
  */
@@ -102,7 +110,7 @@ sdb_command_print_reply(sdb_client_t *client)
        const char *result;
        uint32_t rcode = 0;
 
-       int status = 0;
+       int status = -1;
        size_t i;
 
        recv_buf = sdb_strbuf_create(1024);
@@ -117,11 +125,7 @@ sdb_command_print_reply(sdb_client_t *client)
                return -1;
        }
 
-       if (rcode == UINT32_MAX) {
-               printf("ERROR: ");
-               status = -1;
-       }
-       else
+       if (rcode != UINT32_MAX)
                status = (int)rcode;
 
        for (i = 0; i < SDB_STATIC_ARRAY_LEN(response_printers); ++i) {
@@ -134,10 +138,11 @@ sdb_command_print_reply(sdb_client_t *client)
 
        result = sdb_strbuf_string(recv_buf);
        if (result && *result)
-               printf("%s\n", result);
+               sdb_log(SDB_LOG_ERR, "%s", result);
        else if (rcode == UINT32_MAX) {
                char errbuf[1024];
-               printf("%s\n", sdb_strerror(errno, errbuf, sizeof(errbuf)));
+               sdb_log(SDB_LOG_ERR, "%s",
+                               sdb_strerror(errno, errbuf, sizeof(errbuf)));
        }
 
        sdb_strbuf_destroy(recv_buf);
@@ -168,24 +173,33 @@ sdb_command_exec(sdb_input_t *input)
        if (query_len) {
                data = strndup(query, query_len);
                /* ignore errors; we'll only hide the command from the caller */
+       }
 
-               sdb_client_send(input->client, SDB_CONNECTION_QUERY, query_len, query);
-
-               /* The server will send back *something*, either error/log messages
-                * and/or the reply to the query. Here, we don't care about what it
-                * sends back. We'll wait for the first reply and then return to the
-                * main loop which will handle any subsequent replies, including
-                * eventually the reply to the query (if it's not the first reply). */
-               if (sdb_command_print_reply(input->client) < 0) {
-                       if (data)
-                               free(data);
-                       return NULL;
+       if (sdb_client_eof(input->client)) {
+               if (sdb_input_reconnect()) {
+                       clear_query(input);
+                       return data;
                }
        }
+       else if (! query_len)
+               return NULL;
+
+       sdb_client_send(input->client, SDB_CONNECTION_QUERY, query_len, query);
+
+       /* The server may send back log messages but will eventually reply to the
+        * query, which is either DATA or ERROR. */
+       while (42) {
+               int status = sdb_command_print_reply(input->client);
+               if (status < 0) {
+                       sdb_log(SDB_LOG_ERR, "Failed to read reply from server");
+                       break;
+               }
 
-       sdb_strbuf_skip(input->input, 0, input->query_len);
-       input->tokenizer_pos -= input->query_len;
-       input->query_len = 0;
+               if ((status == SDB_CONNECTION_DATA)
+                               || (status == SDB_CONNECTION_ERROR))
+                       break;
+       }
+       clear_query(input);
        return data;
 } /* sdb_command_exec */
 
index 55a074cd3863f8a39fa947a7f96cba2d8822488f..b6603c08ed86aef9aa7aa5ac956634f4aafd888f 100644 (file)
@@ -92,6 +92,7 @@ sdb_input_t *sysdb_input = NULL;
  */
 
 static struct termios orig_term_attrs;
+static bool have_orig_term_attrs;
 
 /*
  * private helper functions
@@ -108,12 +109,15 @@ term_rawmode(void)
 {
        struct termios attrs;
 
+       if (! have_orig_term_attrs) {
+               memset(&orig_term_attrs, 0, sizeof(orig_term_attrs));
+               tcgetattr(STDIN_FILENO, &orig_term_attrs);
+               atexit(reset_term_attrs);
+               have_orig_term_attrs = 1;
+       }
+
        /* setup terminal to operate in non-canonical mode
         * and single character input */
-       memset(&orig_term_attrs, 0, sizeof(orig_term_attrs));
-       tcgetattr(STDIN_FILENO, &orig_term_attrs);
-       atexit(reset_term_attrs);
-
        memset(&attrs, 0, sizeof(attrs));
        tcgetattr(STDIN_FILENO, &attrs);
        attrs.c_lflag &= (tcflag_t)(~ICANON);
@@ -155,14 +159,17 @@ input_readline(void)
                return (ssize_t)(sdb_strbuf_len(sysdb_input->input) - len);
        }
 
-       if (sysdb_input->query_len)
+       if (sysdb_input->have_input)
                prompt = "sysdb-> ";
+       if (sdb_client_eof(sysdb_input->client))
+               prompt = "!-> ";
 
        rl_callback_handler_install(prompt, handle_input);
        client_fd = sdb_client_sockfd(sysdb_input->client);
        while ((sdb_strbuf_len(sysdb_input->input) == len)
                        && (! sysdb_input->eof)) {
-               int n;
+               bool connected = !sdb_client_eof(sysdb_input->client);
+               int max_fd, n;
 
                /* XXX: some versions of libedit don't properly reset the terminal in
                 * rl_callback_read_char(); detect those versions */
@@ -170,9 +177,15 @@ input_readline(void)
 
                FD_ZERO(&fds);
                FD_SET(STDIN_FILENO, &fds);
-               FD_SET(client_fd, &fds);
+               max_fd = STDIN_FILENO;
 
-               n = select(client_fd + 1, &fds, NULL, NULL, /* timeout = */ NULL);
+               if (connected) {
+                       FD_SET(client_fd, &fds);
+                       if (client_fd > max_fd)
+                               max_fd = client_fd;
+               }
+
+               n = select(max_fd + 1, &fds, NULL, NULL, /* timeout = */ NULL);
                if (n < 0)
                        return (ssize_t)n;
                else if (! n)
@@ -184,22 +197,23 @@ input_readline(void)
                        continue;
                }
 
-               if (! FD_ISSET(client_fd, &fds))
+               if ((! connected) || (! FD_ISSET(client_fd, &fds)))
                        continue;
 
-               if (sdb_client_eof(sysdb_input->client)) {
-                       /* XXX: try to reconnect */
-                       printf("\n");
-                       sdb_log(SDB_LOG_ERR, "Remote side closed the connection.");
-                       /* return EOF */
-                       return 0;
-               }
-
                /* some response / error message from the server pending */
                /* XXX: clear current line */
                printf("\n");
                sdb_command_print_reply(sysdb_input->client);
-               rl_forced_update_display();
+
+               if (sdb_client_eof(sysdb_input->client)) {
+                       rl_callback_handler_remove();
+                       /* XXX */
+                       sdb_log(SDB_LOG_ERR, "Remote side closed the connection.");
+                       /* return EOF -> restart scanner */
+                       return 0;
+               }
+               else
+                       rl_forced_update_display();
        }
 
        /* new data available */
@@ -226,7 +240,8 @@ sdb_input_init(sdb_input_t *input)
 int
 sdb_input_mainloop(void)
 {
-       yylex();
+       while (! sysdb_input->eof)
+               yylex();
        return 0;
 } /* sdb_input_mainloop */
 
@@ -260,24 +275,44 @@ sdb_input_readline(char *buf, size_t *n_chars, size_t max_chars)
 int
 sdb_input_exec_query(void)
 {
-       char *query = sdb_command_exec(sysdb_input);
+       char *query = NULL;
 
-       HIST_ENTRY *current_hist;
-       const char *hist_line = NULL;
+       HIST_ENTRY *hist;
+       const char *prev = NULL;
 
+       if (! sysdb_input->have_input) {
+               /* empty line */
+               if (sdb_client_eof(sysdb_input->client))
+                       sdb_input_reconnect();
+               return 0;
+       }
+
+       query = sdb_command_exec(sysdb_input);
        if (! query)
                return -1;
 
-       current_hist = current_history();
-       if (current_hist)
-               hist_line = current_hist->line;
+       hist = history_get(history_length);
+       if (hist)
+               prev = hist->line;
 
        if (*query != ' ')
-               if ((! hist_line) || strcmp(hist_line, query))
+               if ((! prev) || strcmp(prev, query))
                        add_history(query);
        free(query);
        return 0;
 } /* sdb_input_exec_query */
 
+int
+sdb_input_reconnect(void)
+{
+       sdb_client_close(sysdb_input->client);
+       if (sdb_client_connect(sysdb_input->client, sysdb_input->user)) {
+               sdb_log(SDB_LOG_ERR, "Failed to reconnect to SysDBd");
+               return -1;
+       }
+       sdb_log(SDB_LOG_INFO, "Successfully reconnected to SysDBd");
+       return 0;
+} /* sdb_input_reconnect */
+
 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */
 
index 0ba5c0a4dd385169fc439c1fe133b33464f609a8..ba2b32d8d3553dad528bfda657d7f18f79b20234 100644 (file)
 
 typedef struct {
        sdb_client_t *client;
+       char *user;
 
        sdb_strbuf_t *input;
        size_t tokenizer_pos;
        size_t query_len;
 
+       /* indicates that we've had non-empty input */
+       bool have_input;
+
        bool interactive;
        bool eof;
 } sdb_input_t;
 
-#define SDB_INPUT_INIT { NULL, NULL, 0, 0, 1, 0 }
+#define SDB_INPUT_INIT { NULL, NULL, NULL, 0, 0, 0, 1, 0 }
 
 /*
  * sysdb_input:
@@ -91,6 +95,14 @@ sdb_input_readline(char *buf, size_t *n_chars, size_t max_chars);
 int
 sdb_input_exec_query(void);
 
+/*
+ * sdb_input_reconnect:
+ * Let the client reconnect to the server using the settings stored in
+ * sysdb_input.
+ */
+int
+sdb_input_reconnect(void);
+
 #endif /* SYSDB_INPUT_H */
 
 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */
index b3a73c698e1e2507f9741b60a6692cb11ed5dd6c..357ef34827672d0033030eeefe8d85d059f98836 100644 (file)
@@ -202,7 +202,6 @@ int
 main(int argc, char **argv)
 {
        const char *host = NULL;
-       char *user = NULL;
 
        const char *homedir;
        char hist_file[1024] = "";
@@ -221,7 +220,7 @@ main(int argc, char **argv)
                                host = optarg;
                                break;
                        case 'U':
-                               user = optarg;
+                               input.user = optarg;
                                break;
 
                        case 'c':
@@ -264,23 +263,23 @@ main(int argc, char **argv)
 
        if (! host)
                host = DEFAULT_SOCKET;
-       if (! user)
-               user = sdb_get_current_user();
+       if (! input.user)
+               input.user = sdb_get_current_user();
        else
-               user = strdup(user);
-       if (! user)
+               input.user = strdup(input.user);
+       if (! input.user)
                exit(1);
 
        input.client = sdb_client_create(host);
        if (! input.client) {
                sdb_log(SDB_LOG_ERR, "Failed to create client object");
-               free(user);
+               free(input.user);
                exit(1);
        }
-       if (sdb_client_connect(input.client, user)) {
+       if (sdb_client_connect(input.client, input.user)) {
                sdb_log(SDB_LOG_ERR, "Failed to connect to SysDBd");
                sdb_client_destroy(input.client);
-               free(user);
+               free(input.user);
                exit(1);
        }
 
@@ -288,7 +287,7 @@ main(int argc, char **argv)
                int status = execute_commands(input.client, commands);
                sdb_llist_destroy(commands);
                sdb_client_destroy(input.client);
-               free(user);
+               free(input.user);
                if ((status != SDB_CONNECTION_OK) && (status != SDB_CONNECTION_DATA))
                        exit(1);
                exit(0);
@@ -300,7 +299,7 @@ main(int argc, char **argv)
 
        using_history();
 
-       if ((homedir = get_homedir(user))) {
+       if ((homedir = get_homedir(input.user))) {
                snprintf(hist_file, sizeof(hist_file) - 1,
                                "%s/.sysdb_history", homedir);
                hist_file[sizeof(hist_file) - 1] = '\0';
@@ -312,7 +311,7 @@ main(int argc, char **argv)
                                        hist_file, sdb_strerror(errno, errbuf, sizeof(errbuf)));
                }
        }
-       free(user);
+       free(input.user);
 
        input.input = sdb_strbuf_create(2048);
        sdb_input_init(&input);
index cd465ce67f87be50c0456869053a7c88469e6484..16defa61859085dc20dad932c8f812986af494e3 100644 (file)
@@ -48,6 +48,8 @@
 
 #define APPEND() \
        do { \
+               if (! isspace((int)yytext[0])) \
+                       sysdb_input->have_input = 1; \
                sysdb_input->query_len += strlen(yytext); \
        } while (0)
 
@@ -63,7 +65,7 @@
 
 %x CSC
 
-whitespace             ([ \t\n\r\f]+)
+newline                        (\n|\r\n)
 simple_comment ("--"[^\n\r]*)
 
 /*
@@ -73,11 +75,19 @@ csc_start   \/\*
 csc_inside     ([^*/]+|[^*]\/|\*[^/])
 csc_end                \*\/
 
-identifier     ([A-Za-z_][A-Za-z_0-9$]*)
+/*
+ * Strings.
+ */
+/* TODO: fully support SQL strings */
+string         ('([^']|'')*')
 
 %%
 
-{whitespace}           { APPEND(); }
+       /*
+        * Here, we only care about syntax elements that may include semicolons
+        * and escape their meaning as a query terminator.
+        */
+
 {simple_comment}       { APPEND(); }
 
 {csc_start}                    { APPEND(); BEGIN(CSC); }
@@ -85,15 +95,23 @@ identifier  ([A-Za-z_][A-Za-z_0-9$]*)
 <CSC>{csc_end}         { APPEND(); BEGIN(INITIAL); }
 <CSC><<EOF>>           { return -1; }
 
-{identifier}           { APPEND(); }
+{string}                       { APPEND(); }
 
        /*
         * The following rules are specific to the command line tool.
         */
 
-";"    { APPEND(); sdb_input_exec_query(); }
+";\n"          { APPEND(); sdb_input_exec_query(); }
+";"                    { APPEND(); sdb_input_exec_query(); }
+
+{newline}      {
+                               APPEND();
+                               if (! sysdb_input->have_input)
+                                       /* give the input module a chance to do stuff on empty lines */
+                                       sdb_input_exec_query();
+                       }
 
-.      { APPEND(); }
+.                      { APPEND(); }
 
 %%
 
index 5536c701c9c517eefd3882518e0e2eee1683a88a..eb4ccd8814771f058f6a9a357f1b669f5803b5e5 100755 (executable)
@@ -54,7 +54,7 @@ output="$( run_sysdb_nouser -H "$SOCKET_FILE" \
 echo "$output" | grep -F 'Access denied'
 
 # On parse errors, expect a non-zero exit code.
-output="$( run_sysdb -H "$SOCKET_FILE" -c INVALID )" && exit 1
+output="$( run_sysdb -H "$SOCKET_FILE" -c INVALID 2>&1 )" && exit 1
 echo "$output" | grep "Failed to parse query 'INVALID'"
 echo "$output" | grep "parse error: syntax error"
 
@@ -99,7 +99,7 @@ echo "$output" | grep -F 'other.host.name' && exit 1
 echo "$output" | grep -F 'some.host.name' && exit 1
 
 output="$( run_sysdb -H "$SOCKET_FILE" \
-  -c "FETCH host 'host1.example.com' FILTER last_update < 0" )" \
+  -c "FETCH host 'host1.example.com' FILTER last_update < 0" 2>&1 )" \
   && exit 1
 echo "$output" | grep -F 'not found'
 
@@ -107,8 +107,8 @@ echo "$output" | grep -F 'not found'
        | run_sysdb -H "$SOCKET_FILE"
 
 # When requesting information for unknown hosts, expect a non-zero exit code.
-output="$( run_sysdb -H "$SOCKET_FILE" -c "FETCH host 'does.not.exist'" )" \
-       && exit 1
+output="$( run_sysdb -H "$SOCKET_FILE" \
+       -c "FETCH host 'does.not.exist'" 2>&1 )" && exit 1
 echo "$output" | grep -F 'not found'
 
 run_sysdb -H "$SOCKET_FILE" \