Code

Merged branch 'master' of git://git.tokkee.org/sysdb.
authorSebastian Harl <sh@tokkee.org>
Fri, 11 Apr 2014 06:44:10 +0000 (08:44 +0200)
committerSebastian Harl <sh@tokkee.org>
Fri, 11 Apr 2014 06:44:10 +0000 (08:44 +0200)
26 files changed:
.gitignore
.travis.yml
README
doc/Makefile.am
doc/sysdb.1.txt [new file with mode: 0644]
doc/sysdbd.1.txt
src/Makefile.am
src/core/object.c
src/core/store_lookup.c
src/frontend/connection.c
src/frontend/grammar.y
src/frontend/sock.c
src/include/client/sock.h
src/include/core/store.h
src/tools/sysdb/command.c
src/tools/sysdb/command.h
src/tools/sysdb/input.c
src/tools/sysdb/main.c
src/tools/sysdbd/main.c
t/Makefile.am
t/core/store_lookup_test.c
t/coverage.sh
t/frontend/parser_test.c
t/libsysdb_test.h
t/libsysdb_testutils.c [new file with mode: 0644]
t/libsysdb_testutils.h [new file with mode: 0644]

index f097f95edab772a39db674201301578f8e708b9a..9f1fd8ff4e3ca14646e8733b29d48b78738fa7d2 100644 (file)
@@ -29,6 +29,8 @@ libtool
 ltmain.sh
 
 # build output
+doc/*.1
+doc/*.5
 src/frontend/grammar.c
 src/frontend/grammar.h
 src/frontend/scanner.c
@@ -44,8 +46,6 @@ src/sysdbd
 *.la
 *.lo
 *.o
-sysdbd.1
-sysdbd.conf.5
 sysdb.h
 
 # unit testing output
index f3d8c0e693768fb170ea2aba8fb62a6d2165f739..87badb67b5e6da759f1a72b8eb031f8740e321f5 100644 (file)
@@ -11,10 +11,11 @@ before_install:
 install:
   - sudo apt-get install -qq check flex bison libtool libltdl-dev
   - sudo apt-get install -qq libdbi-dev libedit-dev libreadline-dev
-  - sudo pip install cpp-coveralls --use-mirrors
+  - sudo pip install cpp-coveralls
+  - pip show cpp-coveralls
 script: ./t/cibuild.sh
 after_success:
-  - coveralls --exclude t --verbose
+  - test "${CC}x" != "gccx" || coveralls --build-root src --exclude t
 branches:
   only:
     - master
diff --git a/README b/README
index 1db223979cb6daa1db6f3d450f90954b412d9481..e9578012ce00157679918eaa77b718f6a500b6c7 100644 (file)
--- a/README
+++ b/README
@@ -68,9 +68,15 @@ Testing
   library. It used used to mock I/O related functions. In case this function
   is not available, the respective tests will be disabled automatically.
 
+  For the latest build status, see:
+  <https://travis-ci.org/tokkee/sysdb>
+
   Code coverage testing using Gcov may be enabled when using the
   ‘--enable-gcov’ configure option.
 
+  For the latest coverage report, see:
+  <https://coveralls.io/r/tokkee/sysdb>
+
 Configuring / Compiling / Installing
 ------------------------------------
 
index a89ea25268609bb04aeca53c8e8e7f20603b5519..76d488fb455960035131a9539c70063c40d12a17 100644 (file)
@@ -1,29 +1,32 @@
 SUFFIXES = .1 .5 .1.txt .5.txt
 
 EXTRA_DIST = \
+               sysdb.1.txt \
                sysdbd.1.txt \
                sysdbd.conf.5.txt
 CLEANFILES = \
+               sysdb.1 \
                sysdbd.1 \
                sysdbd.conf.5
 
 man_MANS = \
+               sysdb.1 \
                sysdbd.1 \
                sysdbd.conf.5
 
+ADOC_ATTRS = -apackage_version=$(PACKAGE_VERSION) \
+               -abuild_date="$$( date --utc '+%F' )" \
+               -alocalstatedir=$(localstatedir)
+
+sysdb.1: sysdb.1.txt ../version
+
 sysdbd.1: sysdbd.1.txt ../version
 
 sysdbd.conf.5: sysdbd.conf.5.txt ../version
 
 .1.txt.1:
-       @A2X@ -d manpage -f manpage \
-               -apackage_version=$(PACKAGE_VERSION) \
-               -abuild_date="$$( date --utc '+%F' )" \
-               $<
+       @A2X@ -d manpage -f manpage $(ADOC_ATTRS) $<
 
 .5.txt.5:
-       @A2X@ -d manpage -f manpage \
-               -apackage_version=$(PACKAGE_VERSION) \
-               -abuild_date="$$( date --utc '+%F' )" \
-               $<
+       @A2X@ -d manpage -f manpage $(ADOC_ATTRS) $<
 
diff --git a/doc/sysdb.1.txt b/doc/sysdb.1.txt
new file mode 100644 (file)
index 0000000..b53a4f0
--- /dev/null
@@ -0,0 +1,76 @@
+sysdb(1)
+========
+Sebastian "tokkee" Harl <sh@tokkee.org>
+version {package_version}, {build_date}
+:doctype: manpage
+
+NAME
+----
+sysdb - interactive client for the system management and inventory collection
+service
+
+SYNOPSIS
+--------
+*sysdb* ['options']
+
+DESCRIPTION
+-----------
+*sysdb* is a terminal-based, interactive client program for SysDB, a
+multi-backend system management and inventory collection daemon. It connects
+to a running daemon and then accepts commands from the user, send them to the
+server, and display the result.
+
+OPTIONS
+-------
+*sysdb* accepts the following command-line options.
+
+*-H* '<host>'::
+       The SysDB host to connect to. May be specified as the path to a UNIX
+       socket opened by the daemon. Defaults to {localstatedir}/run/sysdbd.sock.
+
+*-U* '<username>'::
+       The username used to authenticate against the server. Defaults to the
+       current user.
+
+*-c* '<command>'::
+       Send the specified command to the server after authenticating. This option
+       may be used multiple times. Each command will be send to the server
+       separately. Exit the process after handling the reply from the last
+       command.
+
+*-h*::
+       Display a usage and help summary and exit.
+
+*-V*::
+       Display the version number and copyright information.
+
+EXIT CODES
+----------
+*0*::
+       Success.
+
+*1*::
+       Failure (syntax or usage error).
+
+BUGS
+----
+None known.
+
+SEE ALSO
+--------
+*sysdbd*(1)
+
+AUTHOR
+------
+sysdbd was written by Sebastian "tokkee" Harl <sh@tokkee.org>.
+
+COPYRIGHT
+---------
+Copyright (C) 2012-2014 Sebastian "tokkee" Harl <sh@tokkee.org>
+
+This is free software under the terms of the BSD license, see the source for
+copying conditions. There is NO WARRANTY; not even for MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.
+
+// vim: set tw=78 sw=4 ts=4 noexpandtab spell spelllang=en_us :
+
index 76d0944ecdac127435d5141998c00a2ef0a435e4..d3a7046f640266dfdf236c1a96d552a1907fc854 100644 (file)
@@ -57,7 +57,7 @@ None known.
 
 SEE ALSO
 --------
-*sysdb.conf*(5)
+*sysdbd.conf*(5)
 
 AUTHOR
 ------
index 8bd82149163643967aa3a5e3d833b769d41d609d..f6df34a25e7cdd16ac80ea4a06564ddf689cd785 100644 (file)
@@ -105,7 +105,9 @@ libsysdb_scanner_la_SOURCES = tools/sysdb/scanner.l
 libsysdb_scanner_la_CFLAGS = -DBUILD_DATE="\"$$( date --utc '+%F %T' ) (UTC)\""
 sysdb_SOURCES = tools/sysdb/main.c include/client/sysdb.h \
                tools/sysdb/command.c tools/sysdb/command.h \
-               tools/sysdb/input.c tools/sysdb/input.h
+               tools/sysdb/input.c tools/sysdb/input.h \
+               core/object.c include/core/object.h \
+               utils/llist.c include/utils/llist.h
 sysdb_CFLAGS = -DBUILD_DATE="\"$$( date --utc '+%F %T' ) (UTC)\"" \
                $(AM_CFLAGS) @READLINE_CFLAGS@
 sysdb_LDADD = libsysdb_scanner.la libsysdbclient.la @READLINE_LIBS@
index 177ac4c3ada9a76009805187d0a0cd4eda322b9e..9101918654379a1ff41491df64187d399468d186 100644 (file)
@@ -82,7 +82,7 @@ sdb_object_vcreate(const char *name, sdb_type_t type, va_list ap)
 {
        sdb_object_t *obj;
 
-       if (type.size <= sizeof(sdb_object_t))
+       if (type.size < sizeof(sdb_object_t))
                return NULL;
 
        obj = malloc(type.size);
index 7416800a2281dd398ee90af83b5942c3eaf3eb07..bb32157402d8c047a20326aab81e069a4073e5f1 100644 (file)
@@ -103,6 +103,106 @@ match_name(name_matcher_t *m, const char *name)
        return 1;
 } /* match_name */
 
+static char *
+name_tostring(name_matcher_t *m, char *buf, size_t buflen)
+{
+       snprintf(buf, buflen, "{ %s%s%s, %p }",
+                       m->name ? "'" : "", m->name ? m->name : "NULL", m->name ? "'" : "",
+                       m->name_re);
+       return buf;
+} /* name_tostring */
+
+static char *
+logical_tostring(sdb_store_matcher_t *m, char *buf, size_t buflen)
+{
+       char left[buflen + 1], right[buflen + 1];
+
+       if (! m) {
+               /* this should not happen */
+               snprintf(buf, buflen, "()");
+               return buf;
+       }
+
+       assert((m->type == MATCHER_OR) || (m->type == MATCHER_AND));
+       snprintf(buf, buflen, "(%s, %s, %s)",
+                       m->type == MATCHER_OR ? "OR" : "AND",
+                       sdb_store_matcher_tostring(OP_M(m)->left, left, sizeof(left)),
+                       sdb_store_matcher_tostring(OP_M(m)->right, right, sizeof(right)));
+       return buf;
+} /* logical_tostring */
+
+static char *
+unary_tostring(sdb_store_matcher_t *m, char *buf, size_t buflen)
+{
+       char op[buflen + 1];
+
+       if (! m) {
+               /* this should not happen */
+               snprintf(buf, buflen, "()");
+               return buf;
+       }
+
+       assert(m->type == MATCHER_NOT);
+       snprintf(buf, buflen, "(NOT, %s)",
+                       sdb_store_matcher_tostring(UOP_M(m)->op, op, sizeof(op)));
+       return buf;
+} /* unary_tostring */
+
+static char *
+attr_tostring(sdb_store_matcher_t *m, char *buf, size_t buflen)
+{
+       char name[buflen + 1], value[buflen + 1];
+
+       if (! m) {
+               snprintf(buf, buflen, "ATTR{}");
+               return buf;
+       }
+
+       assert(m->type == MATCHER_ATTR);
+       snprintf(buf, buflen, "ATTR{ NAME%s, VALUE%s }",
+                       name_tostring(&OBJ_M(m)->name, name, sizeof(name)),
+                       name_tostring(&ATTR_M(m)->value, value, sizeof(value)));
+       return buf;
+} /* attr_tostring */
+
+static char *
+service_tostring(sdb_store_matcher_t *m, char *buf, size_t buflen)
+{
+       char name[buflen + 1], attr[buflen + 1];
+
+       if (! m) {
+               snprintf(buf, buflen, "SERVICE{}");
+               return buf;
+       }
+
+       assert(m->type == MATCHER_SERVICE);
+       snprintf(buf, buflen, "SERVICE{ NAME%s, %s }",
+                       name_tostring(&OBJ_M(m)->name, name, sizeof(name)),
+                       attr_tostring(SDB_STORE_MATCHER(SERVICE_M(m)->attr),
+                               attr, sizeof(attr)));
+       return buf;
+} /* service_tostring */
+
+static char *
+host_tostring(sdb_store_matcher_t *m, char *buf, size_t buflen)
+{
+       char name[buflen + 1], service[buflen + 1], attr[buflen + 1];
+
+       if (! m) {
+               snprintf(buf, buflen, "HOST{}");
+               return buf;
+       }
+
+       assert(m->type == MATCHER_HOST);
+       snprintf(buf, buflen, "HOST{ NAME%s, %s, %s }",
+                       name_tostring(&OBJ_M(m)->name, name, sizeof(name)),
+                       service_tostring(SDB_STORE_MATCHER(HOST_M(m)->service),
+                               service, sizeof(service)),
+                       attr_tostring(SDB_STORE_MATCHER(HOST_M(m)->attr),
+                               attr, sizeof(attr)));
+       return buf;
+} /* host_tostring */
+
 /* match attribute specific values;
  * always call this function through match_obj() */
 static int
@@ -218,6 +318,17 @@ static matcher_cb matchers[] = {
        match_obj,
 };
 
+typedef char *(*matcher_tostring_cb)(sdb_store_matcher_t *, char *, size_t);
+
+static matcher_tostring_cb matchers_tostring[] = {
+       logical_tostring,
+       logical_tostring,
+       unary_tostring,
+       attr_tostring,
+       service_tostring,
+       host_tostring,
+};
+
 static int
 match_logical(sdb_store_matcher_t *m, sdb_store_base_t *obj)
 {
@@ -550,7 +661,9 @@ sdb_store_matcher_parse_cmp(const char *obj_type, const char *attr,
                /* accept */
        }
        else if (typ == SDB_ATTRIBUTE)
-               m = sdb_store_attr_matcher(attr, NULL, matcher, matcher_re);
+               m = sdb_store_host_matcher(/* name = */ NULL, NULL,
+                               /* service = */ NULL,
+                               sdb_store_attr_matcher(attr, NULL, matcher, matcher_re));
        else
                return NULL;
 
@@ -560,9 +673,13 @@ sdb_store_matcher_parse_cmp(const char *obj_type, const char *attr,
        else if (typ == SDB_HOST)
                m = sdb_store_host_matcher(matcher, matcher_re, NULL, NULL);
        else if (typ == SDB_SERVICE)
-               m = sdb_store_service_matcher(matcher, matcher_re, NULL);
+               m = sdb_store_host_matcher(/* name = */ NULL, NULL,
+                               sdb_store_service_matcher(matcher, matcher_re, NULL),
+                               /* attr = */ NULL);
        else if (typ == SDB_ATTRIBUTE)
-               m = sdb_store_attr_matcher(matcher, matcher_re, NULL, NULL);
+               m = sdb_store_host_matcher(/* name = */ NULL, NULL,
+                               /* service = */ NULL,
+                               sdb_store_attr_matcher(matcher, matcher_re, NULL, NULL));
 
        if (m && inv) {
                sdb_store_matcher_t *tmp;
@@ -607,6 +724,18 @@ sdb_store_matcher_matches(sdb_store_matcher_t *m, sdb_store_base_t *obj)
        return matchers[m->type](m, obj);
 } /* sdb_store_matcher_matches */
 
+char *
+sdb_store_matcher_tostring(sdb_store_matcher_t *m, char *buf, size_t buflen)
+{
+       if (! m)
+               return NULL;
+
+       if ((m->type < 0)
+                       || (((size_t)m->type >= SDB_STATIC_ARRAY_LEN(matchers_tostring))))
+               return NULL;
+       return matchers_tostring[m->type](m, buf, buflen);
+} /* sdb_store_matcher_tostring */
+
 int
 sdb_store_lookup(sdb_store_matcher_t *m, sdb_store_lookup_cb cb,
                void *user_data)
index 12812c7132b20e35da6954757ddbcb5f6f2e7a7d..69c71edbd3e6f7ac9e5876841d9237f738267ab5 100644 (file)
@@ -142,7 +142,8 @@ connection_destroy(sdb_object_t *obj)
 
        sdb_log(SDB_LOG_DEBUG, "frontend: Closing connection on fd=%i",
                        conn->fd);
-       close(conn->fd);
+       if (conn->fd >= 0)
+               close(conn->fd);
        conn->fd = -1;
 
        sdb_strbuf_destroy(conn->buf);
@@ -382,6 +383,9 @@ connection_read(sdb_conn_t *conn)
 {
        ssize_t n = 0;
 
+       if ((! conn) || (conn->fd < 0))
+               return -1;
+
        while (42) {
                ssize_t status;
 
@@ -390,6 +394,9 @@ connection_read(sdb_conn_t *conn)
                if (status < 0) {
                        if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
                                break;
+
+                       close(conn->fd);
+                       conn->fd = -1;
                        return (int)status;
                }
                else if (! status) /* EOF */
@@ -470,6 +477,11 @@ sdb_connection_send(sdb_conn_t *conn, uint32_t code,
        if (status < 0) {
                char errbuf[1024];
 
+               /* tell other code that there was a problem and, more importantly,
+                * make sure we don't try to send further logs to the connection */
+               close(conn->fd);
+               conn->fd = -1;
+
                sdb_log(SDB_LOG_ERR, "frontend: Failed to send msg "
                                "(code: %u, len: %u) to client: %s", code, msg_len,
                                sdb_strerror(errno, errbuf, sizeof(errbuf)));
index 1cf068538bc13f7686fc12df2d0ba22247f9d553..a261c4c81d3253320b9d8cf960378918f84d0249 100644 (file)
@@ -232,8 +232,7 @@ lookup_statement:
 expression:
        matcher
                {
-                       sdb_store_matcher_t *m = $1;
-                       if (! m) {
+                       if (! $1) {
                                /* TODO: improve error reporting */
                                sdb_fe_yyerror(&yylloc, scanner,
                                                YY_("syntax error, invalid expression"));
@@ -243,29 +242,7 @@ expression:
                        $$ = SDB_CONN_NODE(sdb_object_create_dT(/* name = */ NULL,
                                                conn_node_matcher_t, conn_matcher_destroy));
                        $$->cmd = CONNECTION_EXPR;
-
-                       if ((M(m)->type == MATCHER_HOST)
-                                       || (M(m)->type == MATCHER_AND)
-                                       || (M(m)->type == MATCHER_OR)
-                                       || (M(m)->type == MATCHER_NOT))
-                               CONN_MATCHER($$)->matcher = m;
-                       else if (M(m)->type == MATCHER_SERVICE)
-                               CONN_MATCHER($$)->matcher = sdb_store_host_matcher(NULL,
-                                               /* name_re = */ NULL, /* service = */ m,
-                                               /* attr = */ NULL);
-                       else if (M(m)->type == MATCHER_ATTR)
-                               CONN_MATCHER($$)->matcher = sdb_store_host_matcher(NULL,
-                                               /* name_re = */ NULL, /* service = */ NULL,
-                                               /* attr = */ m);
-                       else {
-                               char errbuf[1024];
-                               snprintf(errbuf, sizeof(errbuf),
-                                               YY_("syntax error, unexpected matcher type %d"),
-                                               M(m)->type);
-                               sdb_object_deref(SDB_OBJ($$));
-                               sdb_fe_yyerror(&yylloc, scanner, errbuf);
-                               YYABORT;
-                       }
+                       CONN_MATCHER($$)->matcher = $1;
                }
        ;
 
index 3b9799496df8bf1e70d501393e36b91de0ebd86e..7cfdb7cac4b84d21c2f476e38b08855ea80c970b 100644 (file)
@@ -381,9 +381,14 @@ socket_handle_incoming(sdb_fe_socket_t *sock,
        while (sdb_llist_iter_has_next(iter)) {
                sdb_object_t *obj = sdb_llist_iter_get_next(iter);
 
-               if (FD_ISSET(CONN(obj)->fd, exceptions))
+               if (FD_ISSET(CONN(obj)->fd, exceptions)) {
                        sdb_log(SDB_LOG_INFO, "Exception on fd %d",
                                        CONN(obj)->fd);
+                       /* close the connection */
+                       sdb_llist_iter_remove_current(iter);
+                       sdb_object_deref(obj);
+                       continue;
+               }
 
                if (FD_ISSET(CONN(obj)->fd, ready)) {
                        sdb_llist_iter_remove_current(iter);
@@ -529,6 +534,13 @@ sdb_fe_sock_listen_and_serve(sdb_fe_socket_t *sock, sdb_fe_loop_t *loop)
 
                while (sdb_llist_iter_has_next(iter)) {
                        sdb_object_t *obj = sdb_llist_iter_get_next(iter);
+
+                       if (CONN(obj)->fd < 0) {
+                               sdb_llist_iter_remove_current(iter);
+                               sdb_object_deref(obj);
+                               continue;
+                       }
+
                        FD_SET(CONN(obj)->fd, &ready);
                        FD_SET(CONN(obj)->fd, &exceptions);
 
index 4fd54baa1c5fa7ac99c3c0f46bbf2e2d8f8f2cd6..bce3da91898a670a00eb532e630e2b0663462321 100644 (file)
@@ -96,7 +96,7 @@ sdb_client_close(sdb_client_t *client);
  * connection.
  *
  * Returns:
- *  - 0 on success
+ *  - the number of bytes send
  *  - a negative value else.
  */
 ssize_t
index cde6ad0b8c2763dd15b4759b27f005b87441db5a..6106b3fdbe2490629a6b1e42d02ee85184d134dc 100644 (file)
@@ -213,6 +213,14 @@ sdb_store_inv_matcher(sdb_store_matcher_t *m);
 int
 sdb_store_matcher_matches(sdb_store_matcher_t *m, sdb_store_base_t *obj);
 
+/*
+ * sdb_store_matcher_tostring:
+ * Format a matcher object as string. This is meant for logging or debugging
+ * purposes.
+ */
+char *
+sdb_store_matcher_tostring(sdb_store_matcher_t *m, char *buf, size_t buflen);
+
 /*
  * sdb_store_lookup_cb:
  * Lookup callback. It is called for each matching object when looking up data
index 1128c388e8bbd827f63e63cb2cd2628137e146fd..c79259e401c0786bb80336601208b058180e23e9 100644 (file)
@@ -47,7 +47,7 @@
  */
 
 int
-sdb_command_print_reply(sdb_input_t *input)
+sdb_command_print_reply(sdb_client_t *client)
 {
        sdb_strbuf_t *recv_buf;
        const char *result;
@@ -57,10 +57,10 @@ sdb_command_print_reply(sdb_input_t *input)
        if (! recv_buf)
                return -1;
 
-       if (sdb_client_recv(input->client, &rcode, recv_buf) < 0)
+       if (sdb_client_recv(client, &rcode, recv_buf) < 0)
                rcode = UINT32_MAX;
 
-       if (sdb_client_eof(input->client))
+       if (sdb_client_eof(client))
                return -1;
 
        if (rcode == UINT32_MAX)
@@ -103,7 +103,7 @@ sdb_command_exec(sdb_input_t *input)
                /* ignore errors; we'll only hide the command from the caller */
 
                sdb_client_send(input->client, CONNECTION_QUERY, query_len, query);
-               if (sdb_command_print_reply(input))
+               if (sdb_command_print_reply(input->client))
                        return NULL;
        }
 
index 726e27a59f05f1229ecf79ac8874df625203b6a5..febf64f144a3c6f54ecee86d077531d14b414f76 100644 (file)
@@ -39,7 +39,7 @@
  *  - a negative value else
  */
 int
-sdb_command_print_reply(sdb_input_t *input);
+sdb_command_print_reply(sdb_client_t *client);
 
 /*
  * sdb_command_exec:
index 3814f0c2def7a239d8397d9cad4558cbd52112ae..b515c4e10931db0c8e599d9820b991d974a6fb6b 100644 (file)
@@ -190,7 +190,7 @@ input_readline(void)
                /* some response / error message from the server pending */
                /* XXX: clear current line */
                printf("\n");
-               sdb_command_print_reply(sysdb_input);
+               sdb_command_print_reply(sysdb_input->client);
                rl_forced_update_display();
        }
 
index 33f96c4461c24cbea9dd17b5b8c0c611fc428fa9..991e1ffb6d8e744252309646f4741e67abfe901d 100644 (file)
 #      include "config.h"
 #endif /* HAVE_CONFIG_H */
 
+#include "tools/sysdb/command.h"
 #include "tools/sysdb/input.h"
 
 #include "client/sysdb.h"
 #include "client/sock.h"
 #include "utils/error.h"
+#include "utils/llist.h"
 #include "utils/strbuf.h"
 
 #include <errno.h>
 #      define DEFAULT_SOCKET "unix:"LOCALSTATEDIR"/run/sysdbd.sock"
 #endif
 
-static void
-exit_usage(char *name, int status)
-{
-       printf(
-"Usage: %s <options>\n"
-
-"\nOptions:\n"
-"  -h        display this help and exit\n"
-"  -V        display the version number and copyright\n"
-
-"\nSysDB client "SDB_CLIENT_VERSION_STRING SDB_CLIENT_VERSION_EXTRA", "
-PACKAGE_URL"\n", basename(name));
-       exit(status);
-} /* exit_usage */
-
-static void
-exit_version(void)
-{
-       printf("SysDB version "SDB_CLIENT_VERSION_STRING
-                       SDB_CLIENT_VERSION_EXTRA", built "BUILD_DATE"\n"
-                       "using libsysdbclient version %s%s\n"
-                       "Copyright (C) 2012-2014 "PACKAGE_MAINTAINER"\n"
-
-                       "\nThis is free software under the terms of the BSD license, see "
-                       "the source for\ncopying conditions. There is NO WARRANTY; not "
-                       "even for MERCHANTABILITY or\nFITNESS FOR A PARTICULAR "
-                       "PURPOSE.\n", sdb_client_version_string(),
-                       sdb_client_version_extra());
-       exit(0);
-} /* exit_version */
-
 static const char *
 get_current_user(void)
 {
@@ -162,6 +133,75 @@ get_homedir(const char *username)
        return result->pw_dir;
 } /* get_homedir */
 
+static void
+exit_usage(char *name, int status)
+{
+       printf(
+"Usage: %s <options>\n"
+
+"\nOptions:\n"
+"  -H HOST   the host to connect to\n"
+"            default: "DEFAULT_SOCKET"\n"
+"  -U USER   the username to connect as\n"
+"            default: %s\n"
+"  -c CMD    execute the specified command and then exit\n"
+"\n"
+"  -h        display this help and exit\n"
+"  -V        display the version number and copyright\n"
+
+"\nSysDB client "SDB_CLIENT_VERSION_STRING SDB_CLIENT_VERSION_EXTRA", "
+PACKAGE_URL"\n", basename(name), get_current_user());
+       exit(status);
+} /* exit_usage */
+
+static void
+exit_version(void)
+{
+       printf("SysDB version "SDB_CLIENT_VERSION_STRING
+                       SDB_CLIENT_VERSION_EXTRA", built "BUILD_DATE"\n"
+                       "using libsysdbclient version %s%s\n"
+                       "Copyright (C) 2012-2014 "PACKAGE_MAINTAINER"\n"
+
+                       "\nThis is free software under the terms of the BSD license, see "
+                       "the source for\ncopying conditions. There is NO WARRANTY; not "
+                       "even for MERCHANTABILITY or\nFITNESS FOR A PARTICULAR "
+                       "PURPOSE.\n", sdb_client_version_string(),
+                       sdb_client_version_extra());
+       exit(0);
+} /* exit_version */
+
+static int
+execute_commands(sdb_client_t *client, sdb_llist_t *commands)
+{
+       sdb_llist_iter_t *iter;
+       int status = 0;
+
+       iter = sdb_llist_get_iter(commands);
+       if (! iter) {
+               sdb_log(SDB_LOG_ERR, "Failed to iterate commands");
+               return 1;
+       }
+
+       while (sdb_llist_iter_has_next(iter)) {
+               sdb_object_t *obj = sdb_llist_iter_get_next(iter);
+               if (sdb_client_send(client, CONNECTION_QUERY,
+                                       (uint32_t)strlen(obj->name), obj->name) <= 0) {
+                       sdb_log(SDB_LOG_ERR, "Failed to send command '%s' to server",
+                                       obj->name);
+                       status = 1;
+                       break;
+               }
+               if (sdb_command_print_reply(client)) {
+                       sdb_log(SDB_LOG_ERR, "Failed to read reply from server");
+                       status = 1;
+                       break;
+               }
+       }
+
+       sdb_llist_iter_destroy(iter);
+       return status;
+} /* execute_commands */
+
 int
 main(int argc, char **argv)
 {
@@ -172,9 +212,10 @@ main(int argc, char **argv)
        char hist_file[1024] = "";
 
        sdb_input_t input = SDB_INPUT_INIT;
+       sdb_llist_t *commands = NULL;
 
        while (42) {
-               int opt = getopt(argc, argv, "H:U:hV");
+               int opt = getopt(argc, argv, "H:U:c:hV");
 
                if (-1 == opt)
                        break;
@@ -187,6 +228,30 @@ main(int argc, char **argv)
                                user = optarg;
                                break;
 
+                       case 'c':
+                               {
+                                       sdb_object_t *obj;
+
+                                       if (! commands)
+                                               commands = sdb_llist_create();
+                                       if (! commands) {
+                                               sdb_log(SDB_LOG_ERR, "Failed to create list object");
+                                               exit(1);
+                                       }
+
+                                       if (! (obj = sdb_object_create_T(optarg, sdb_object_t))) {
+                                               sdb_log(SDB_LOG_ERR, "Failed to create object");
+                                               exit(1);
+                                       }
+                                       if (sdb_llist_append(commands, obj)) {
+                                               sdb_log(SDB_LOG_ERR, "Failed to append command to list");
+                                               sdb_object_deref(obj);
+                                               exit(1);
+                                       }
+                                       sdb_object_deref(obj);
+                               }
+                               break;
+
                        case 'h':
                                exit_usage(argv[0], 0);
                                break;
@@ -220,6 +285,13 @@ main(int argc, char **argv)
                exit(1);
        }
 
+       if (commands) {
+               int status = execute_commands(input.client, commands);
+               sdb_llist_destroy(commands);
+               sdb_client_destroy(input.client);
+               exit(status);
+       }
+
        sdb_log(SDB_LOG_INFO, "SysDB client "SDB_CLIENT_VERSION_STRING
                        SDB_CLIENT_VERSION_EXTRA"\n");
 
index ca1c50de3866318efefe06f399539d65a762f35d..9828bf0881bd1c6510b8c3bef22ef567ad776fa1 100644 (file)
@@ -262,6 +262,10 @@ main(int argc, char **argv)
        sdb_plugin_init_all();
        plugin_main_loop.default_interval = SECS_TO_SDB_TIME(60);
 
+       /* ignore, we see this, for example, if a client disconnects without
+        * closing the connection cleanly */
+       signal(SIGPIPE, SIG_IGN);
+
        memset(&backend_thread, 0, sizeof(backend_thread));
        if (pthread_create(&backend_thread, /* attr = */ NULL,
                                backend_handler, /* arg = */ NULL)) {
index c63a2bb21d0c8f9a8e1ee25836723266f70fbebe..cc2607f75e2b09d9129cc9ff77cfe08630649932 100644 (file)
@@ -10,6 +10,7 @@ check_PROGRAMS = libsysdb_test libsysdb_net_test
 
 libsysdb_test_SOURCES = \
                libsysdb_test.c libsysdb_test.h \
+               libsysdb_testutils.c libsysdb_testutils.h \
                core/data_test.c \
                core/object_test.c \
                core/store_test.c \
@@ -25,7 +26,8 @@ libsysdb_test_CFLAGS = $(AM_CFLAGS) @CHECK_CFLAGS@
 libsysdb_test_LDADD = $(top_builddir)/src/libsysdb.la @CHECK_LIBS@
 
 libsysdb_net_test_SOURCES = \
-               libsysdb_net_test.c libsysdb_test.h
+               libsysdb_net_test.c libsysdb_test.h \
+               libsysdb_testutils.c libsysdb_testutils.h
 if BUILD_WITH_FOPENCOOKIE
 libsysdb_net_test_SOURCES += utils/unixsock_test.c
 endif
index a16d343d37d0a9991e244d419c6226ecc64c23d5..2b52a66de1d32e05d7a79a371c6d095d3eed6fc8 100644 (file)
@@ -27,6 +27,7 @@
 
 #include "core/store.h"
 #include "core/store-private.h"
+#include "frontend/parser.h"
 #include "libsysdb_test.h"
 
 #include <check.h>
@@ -354,20 +355,20 @@ START_TEST(test_parse_cmp)
                { "host",      "attr", "=",  "hostname", -1 },
                { "host",      "attr", "!=", "hostname", -1 },
                { "host",      "name", "&^", "hostname", -1 },
-               { "service",   "name", "=",  "srvname",  MATCHER_SERVICE },
+               { "service",   "name", "=",  "srvname",  MATCHER_HOST },
                { "service",   "name", "!=", "srvname",  MATCHER_NOT },
-               { "service",   "name", "=~", "srvname",  MATCHER_SERVICE },
+               { "service",   "name", "=~", "srvname",  MATCHER_HOST },
                { "service",   "name", "!~", "srvname",  MATCHER_NOT },
                { "service",   "attr", "=",  "srvname",  -1 },
                { "service",   "attr", "!=", "srvname",  -1 },
                { "service",   "name", "&^", "srvname",  -1 },
-               { "attribute", "name", "=",  "attrname", MATCHER_ATTR },
+               { "attribute", "name", "=",  "attrname", MATCHER_HOST },
                { "attribute", "name", "!=", "attrname", MATCHER_NOT },
-               { "attribute", "name", "=~", "attrname", MATCHER_ATTR },
+               { "attribute", "name", "=~", "attrname", MATCHER_HOST },
                { "attribute", "name", "!~", "attrname", MATCHER_NOT },
-               { "attribute", "attr", "=",  "attrname", MATCHER_ATTR },
+               { "attribute", "attr", "=",  "attrname", MATCHER_HOST },
                { "attribute", "attr", "!=", "attrname", MATCHER_NOT },
-               { "attribute", "attr", "=~", "attrname", MATCHER_ATTR },
+               { "attribute", "attr", "=~", "attrname", MATCHER_HOST },
                { "attribute", "attr", "!~", "attrname", MATCHER_NOT },
                { "attribute", "attr", "&^", "attrname", -1 },
        };
@@ -404,7 +405,7 @@ END_TEST
 static int
 lookup_cb(sdb_store_base_t *obj, void *user_data)
 {
-       intptr_t *i = user_data;
+       int *i = user_data;
 
        fail_unless(obj != NULL,
                        "sdb_store_lookup callback received NULL obj; expected: "
@@ -419,14 +420,93 @@ lookup_cb(sdb_store_base_t *obj, void *user_data)
 
 START_TEST(test_lookup)
 {
-       intptr_t i = 0;
-       int check;
+#define PTR_RE "0x[0-9a-f]+"
+       struct {
+               const char *query;
+               int expected;
+               const char *tostring_re;
+       } golden_data[] = {
+               { "host.name = 'a'",       1,
+                       "HOST\\{ NAME\\{ 'a', \\(nil\\) \\}, SERVICE\\{\\}, ATTR\\{\\} \\}" },
+               { "host.name =~ 'a|b'",    2,
+                       "HOST\\{ NAME\\{ NULL, "PTR_RE" \\}, SERVICE\\{\\}, ATTR\\{\\} \\}" },
+               { "host.name =~ 'host'",   0,
+                       "HOST\\{ NAME\\{ NULL, "PTR_RE" \\}, SERVICE\\{\\}, ATTR\\{\\} \\}" },
+               { "host.name =~ '.'",      3,
+                       "HOST\\{ NAME\\{ NULL, "PTR_RE" \\}, SERVICE\\{\\}, ATTR\\{\\} \\}" },
+               { "service.name = 's1'",   2,
+                       "HOST\\{ NAME\\{ NULL, \\(nil\\) \\}, SERVICE\\{ "
+                                       "NAME\\{ 's1', \\(nil\\) }, ATTR\\{\\} "
+                               "\\}, ATTR\\{\\} \\}" },
+               { "service.name =~ 's'",   2,
+                       "HOST\\{ NAME\\{ NULL, \\(nil\\) \\}, SERVICE\\{ "
+                                       "NAME\\{ NULL, "PTR_RE" }, ATTR\\{\\} "
+                               "\\}, ATTR\\{\\} \\}" },
+               { "service.name !~ 's'",   1,
+                       "(NOT, HOST\\{ NAME\\{ NULL, \\(nil\\) \\}, SERVICE\\{ "
+                                       "NAME\\{ NULL, "PTR_RE" }, ATTR\\{\\} "
+                               "\\}, ATTR\\{\\} \\})" },
+               { "attribute.name = 'k1'", 1,
+                       "HOST\\{ NAME\\{ NULL, \\(nil\\) \\}, SERVICE\\{\\}, ATTR\\{ "
+                                       "NAME\\{ 'k1', \\(nil\\) }, VALUE\\{ NULL, \\(nil\\) \\} "
+                               "\\} \\}" },
+               { "attribute.name = 'x'",  0,
+                       "HOST\\{ NAME\\{ NULL, \\(nil\\) \\}, SERVICE\\{\\}, ATTR\\{ "
+                                       "NAME\\{ 'x', \\(nil\\) }, VALUE\\{ NULL, \\(nil\\) \\} "
+                               "\\} \\}" },
+               { "attribute.k1 = 'v1'",   1,
+                       "HOST\\{ NAME\\{ NULL, \\(nil\\) \\}, SERVICE\\{\\}, ATTR\\{ "
+                                       "NAME\\{ 'k1', \\(nil\\) }, VALUE\\{ 'v1', \\(nil\\) \\} "
+                               "\\} \\}" },
+               { "attribute.k1 != 'v1'",  2,
+                       "(NOT, HOST\\{ NAME\\{ NULL, \\(nil\\) \\}, SERVICE\\{\\}, ATTR\\{ "
+                                       "NAME\\{ 'k1', \\(nil\\) }, VALUE\\{ 'v1', \\(nil\\) \\} "
+                               "\\} \\})" },
+               { "attribute.k1 != 'v2'",  3,
+                       "(NOT, HOST\\{ NAME\\{ NULL, \\(nil\\) \\}, SERVICE\\{\\}, ATTR\\{ "
+                                       "NAME\\{ 'k1', \\(nil\\) }, VALUE\\{ 'v2', \\(nil\\) \\} "
+                               "\\} \\})" },
+               { "attribute.name != 'x' "
+                 "AND attribute.y !~ 'x'", 3,
+                       "\\(AND, \\(NOT, HOST\\{ NAME\\{ NULL, \\(nil\\) \\}, SERVICE\\{\\}, ATTR\\{ "
+                                       "NAME\\{ 'x', \\(nil\\) }, VALUE\\{ NULL, \\(nil\\) \\} "
+                               "\\} \\}\\), \\(NOT, HOST\\{ NAME\\{ NULL, \\(nil\\) \\}, SERVICE\\{\\}, ATTR\\{ "
+                                               "NAME\\{ 'y', \\(nil\\) }, VALUE\\{ NULL, "PTR_RE" \\} "
+                                       "\\} \\}\\)\\)" },
+       };
+
+       int check, n;
+       size_t i;
 
-       check = sdb_store_lookup(NULL, lookup_cb, &i);
+       n = 0;
+       check = sdb_store_lookup(NULL, lookup_cb, &n);
        fail_unless(check == 0,
                        "sdb_store_lookup() = %d; expected: 0", check);
-       fail_unless(i == 3,
-                       "sdb_store_lookup called callback %d times; expected: 3", (int)i);
+       fail_unless(n == 3,
+                       "sdb_store_lookup called callback %d times; expected: 3", (int)n);
+
+       for (i = 0; i < SDB_STATIC_ARRAY_LEN(golden_data); ++i) {
+               sdb_store_matcher_t *m;
+               char buf[4096];
+
+               m = sdb_fe_parse_matcher(golden_data[i].query, -1);
+               fail_unless(m != NULL,
+                               "sdb_fe_parse_matcher(%s, -1) = NULL; expected: <matcher>",
+                               golden_data[i].query);
+               fail_unless(sdb_regmatches(golden_data[i].tostring_re,
+                                       sdb_store_matcher_tostring(m, buf, sizeof(buf))) == 0,
+                               "sdb_fe_parse_matcher(%s, -1) = %s; expected: %s",
+                               golden_data[i].query,
+                               sdb_store_matcher_tostring(m, buf, sizeof(buf)),
+                               golden_data[i].tostring_re);
+
+               n = 0;
+               sdb_store_lookup(m, lookup_cb, &n);
+               fail_unless(n == golden_data[i].expected,
+                               "sdb_store_lookup(matcher{%s}) found %d hosts; expected: %d",
+                               golden_data[i].query, n, golden_data[i].expected);
+               sdb_object_deref(SDB_OBJ(m));
+       }
 }
 END_TEST
 
index 29f8c99a7ad7b7f914dea601aa93ccfde413cda4..6acf8c96faa2ec520fb6ba0c64c1ae24db6f3423 100755 (executable)
@@ -38,7 +38,7 @@ if test -d "$srcdir"/.git/; then
        cp -a "$srcdir"/.git .
 
        # reset all files which are not part of the tarball
-       git checkout HEAD .gitignore .travis.yml
+       git checkout HEAD .gitignore .travis.yml t/cibuild.sh
 fi
 
 ./configure --enable-gcov --disable-shared CFLAGS="-O0 -g"
index 74075b3a6c8de866840891e52d975fdc0fdc35ba..f64b9ebca95b05e66bb568a9c6535f72cc7f7462 100644 (file)
@@ -142,8 +142,22 @@ START_TEST(test_parse_matcher)
 
                /* valid expressions */
                { "host.name = 'localhost'",        -1,  MATCHER_HOST },
+               { "host.name != 'localhost'",       -1,  MATCHER_NOT },
+               { "host.name =~ 'host'",            -1,  MATCHER_HOST },
+               { "host.name !~ 'host'",            -1,  MATCHER_NOT },
                { "host.name = 'localhost' -- foo", -1,  MATCHER_HOST },
                { "host.name = 'host' <garbage>",   18,  MATCHER_HOST },
+               /* match hosts by service */
+               { "service.name = 'name'",          -1,  MATCHER_HOST },
+               { "service.name != 'name'",         -1,  MATCHER_NOT },
+               { "service.name =~ 'pattern'",      -1,  MATCHER_HOST },
+               { "service.name !~ 'pattern'",      -1,  MATCHER_NOT },
+               /* match hosts by attribute */
+               { "attribute.name = 'name'",        -1,  MATCHER_HOST },
+               { "attribute.name != 'name'",       -1,  MATCHER_NOT },
+               { "attribute.name =~ 'pattern'",    -1,  MATCHER_HOST },
+               { "attribute.name !~ 'pattern'",    -1,  MATCHER_NOT },
+               /* composite expressions */
                { "host.name =~ 'pattern' AND "
                  "service.name =~ 'pattern'",      -1,  MATCHER_AND },
                { "host.name =~ 'pattern' OR "
index 3357260f6a52559ac0d70906aff2e5a927cd08cf..5f0e1b486eda09647e1fdd9cbd72d38c8f7af0b2 100644 (file)
@@ -31,6 +31,8 @@
 #include "sysdb.h"
 #include "core/object.h"
 
+#include "libsysdb_testutils.h"
+
 #include <check.h>
 #include <string.h>
 
diff --git a/t/libsysdb_testutils.c b/t/libsysdb_testutils.c
new file mode 100644 (file)
index 0000000..964afdb
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * SysDB - t/libsysdb_testutils.c
+ * Copyright (C) 2014 Sebastian 'tokkee' Harl <sh@tokkee.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if HAVE_CONFIG_H
+#      include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include "libsysdb_testutils.h"
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <regex.h>
+
+int
+sdb_regmatches(const char *regex, const char *string)
+{
+       regex_t reg;
+       int status;
+
+       status = regcomp(&reg, regex, REG_EXTENDED | REG_NOSUB);
+       if (status)
+               return status;
+
+       status = regexec(&reg, string, /* matches = */ 0, NULL, /* flags = */ 0);
+       regfree(&reg);
+       return status;
+} /* sdb_regmatches */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/t/libsysdb_testutils.h b/t/libsysdb_testutils.h
new file mode 100644 (file)
index 0000000..3f828a9
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * SysDB - t/libsysdb_testutils.h
+ * Copyright (C) 2014 Sebastian 'tokkee' Harl <sh@tokkee.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Utility functions for test suites.
+ */
+
+#ifndef T_LIBSYSDB_UTILS_H
+#define T_LIBSYSDB_UTILS_H 1
+
+/*
+ * sdb_regmatches:
+ * Check if a regex matches a string.
+ *
+ * Returns:
+ *  - 0 if the regex matches
+ *  - a non-zero error value else (see regcomp(3) for details)
+ */
+int
+sdb_regmatches(const char *regex, const char *string);
+
+#endif /* T_LIBSYSDB_UTILS_H */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+