From: Sebastian Harl Date: Fri, 11 Apr 2014 06:44:10 +0000 (+0200) Subject: Merged branch 'master' of git://git.tokkee.org/sysdb. X-Git-Tag: sysdb-0.1.0~132 X-Git-Url: https://git.tokkee.org/?p=sysdb.git;a=commitdiff_plain;h=72a63cfb0e65bb575889b8dbee6648cafd6a52f2;hp=28b9d363fbbd0936cbafbdf9d5de5ccb9f8412d3 Merged branch 'master' of git://git.tokkee.org/sysdb. --- diff --git a/.gitignore b/.gitignore index f097f95..9f1fd8f 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/.travis.yml b/.travis.yml index f3d8c0e..87badb6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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 1db2239..e957801 100644 --- 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: + + Code coverage testing using Gcov may be enabled when using the ‘--enable-gcov’ configure option. + For the latest coverage report, see: + + Configuring / Compiling / Installing ------------------------------------ diff --git a/doc/Makefile.am b/doc/Makefile.am index a89ea25..76d488f 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -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 index 0000000..b53a4f0 --- /dev/null +++ b/doc/sysdb.1.txt @@ -0,0 +1,76 @@ +sysdb(1) +======== +Sebastian "tokkee" Harl +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* '':: + 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* '':: + The username used to authenticate against the server. Defaults to the + current user. + +*-c* '':: + 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 . + +COPYRIGHT +--------- +Copyright (C) 2012-2014 Sebastian "tokkee" Harl + +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 : + diff --git a/doc/sysdbd.1.txt b/doc/sysdbd.1.txt index 76d0944..d3a7046 100644 --- a/doc/sysdbd.1.txt +++ b/doc/sysdbd.1.txt @@ -57,7 +57,7 @@ None known. SEE ALSO -------- -*sysdb.conf*(5) +*sysdbd.conf*(5) AUTHOR ------ diff --git a/src/Makefile.am b/src/Makefile.am index 8bd8214..f6df34a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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@ diff --git a/src/core/object.c b/src/core/object.c index 177ac4c..9101918 100644 --- a/src/core/object.c +++ b/src/core/object.c @@ -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); diff --git a/src/core/store_lookup.c b/src/core/store_lookup.c index 7416800..bb32157 100644 --- a/src/core/store_lookup.c +++ b/src/core/store_lookup.c @@ -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) diff --git a/src/frontend/connection.c b/src/frontend/connection.c index 12812c7..69c71ed 100644 --- a/src/frontend/connection.c +++ b/src/frontend/connection.c @@ -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))); diff --git a/src/frontend/grammar.y b/src/frontend/grammar.y index 1cf0685..a261c4c 100644 --- a/src/frontend/grammar.y +++ b/src/frontend/grammar.y @@ -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; } ; diff --git a/src/frontend/sock.c b/src/frontend/sock.c index 3b97994..7cfdb7c 100644 --- a/src/frontend/sock.c +++ b/src/frontend/sock.c @@ -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); diff --git a/src/include/client/sock.h b/src/include/client/sock.h index 4fd54ba..bce3da9 100644 --- a/src/include/client/sock.h +++ b/src/include/client/sock.h @@ -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 diff --git a/src/include/core/store.h b/src/include/core/store.h index cde6ad0..6106b3f 100644 --- a/src/include/core/store.h +++ b/src/include/core/store.h @@ -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 diff --git a/src/tools/sysdb/command.c b/src/tools/sysdb/command.c index 1128c38..c79259e 100644 --- a/src/tools/sysdb/command.c +++ b/src/tools/sysdb/command.c @@ -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; } diff --git a/src/tools/sysdb/command.h b/src/tools/sysdb/command.h index 726e27a..febf64f 100644 --- a/src/tools/sysdb/command.h +++ b/src/tools/sysdb/command.h @@ -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: diff --git a/src/tools/sysdb/input.c b/src/tools/sysdb/input.c index 3814f0c..b515c4e 100644 --- a/src/tools/sysdb/input.c +++ b/src/tools/sysdb/input.c @@ -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(); } diff --git a/src/tools/sysdb/main.c b/src/tools/sysdb/main.c index 33f96c4..991e1ff 100644 --- a/src/tools/sysdb/main.c +++ b/src/tools/sysdb/main.c @@ -29,11 +29,13 @@ # 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 @@ -78,37 +80,6 @@ # define DEFAULT_SOCKET "unix:"LOCALSTATEDIR"/run/sysdbd.sock" #endif -static void -exit_usage(char *name, int status) -{ - printf( -"Usage: %s \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 \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"); diff --git a/src/tools/sysdbd/main.c b/src/tools/sysdbd/main.c index ca1c50d..9828bf0 100644 --- a/src/tools/sysdbd/main.c +++ b/src/tools/sysdbd/main.c @@ -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)) { diff --git a/t/Makefile.am b/t/Makefile.am index c63a2bb..cc2607f 100644 --- a/t/Makefile.am +++ b/t/Makefile.am @@ -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 diff --git a/t/core/store_lookup_test.c b/t/core/store_lookup_test.c index a16d343..2b52a66 100644 --- a/t/core/store_lookup_test.c +++ b/t/core/store_lookup_test.c @@ -27,6 +27,7 @@ #include "core/store.h" #include "core/store-private.h" +#include "frontend/parser.h" #include "libsysdb_test.h" #include @@ -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: ", + 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 diff --git a/t/coverage.sh b/t/coverage.sh index 29f8c99..6acf8c9 100755 --- a/t/coverage.sh +++ b/t/coverage.sh @@ -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" diff --git a/t/frontend/parser_test.c b/t/frontend/parser_test.c index 74075b3..f64b9eb 100644 --- a/t/frontend/parser_test.c +++ b/t/frontend/parser_test.c @@ -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' ", 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 " diff --git a/t/libsysdb_test.h b/t/libsysdb_test.h index 3357260..5f0e1b4 100644 --- a/t/libsysdb_test.h +++ b/t/libsysdb_test.h @@ -31,6 +31,8 @@ #include "sysdb.h" #include "core/object.h" +#include "libsysdb_testutils.h" + #include #include diff --git a/t/libsysdb_testutils.c b/t/libsysdb_testutils.c new file mode 100644 index 0000000..964afdb --- /dev/null +++ b/t/libsysdb_testutils.c @@ -0,0 +1,54 @@ +/* + * SysDB - t/libsysdb_testutils.c + * Copyright (C) 2014 Sebastian 'tokkee' Harl + * 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 +#include +#include + +int +sdb_regmatches(const char *regex, const char *string) +{ + regex_t reg; + int status; + + status = regcomp(®, regex, REG_EXTENDED | REG_NOSUB); + if (status) + return status; + + status = regexec(®, string, /* matches = */ 0, NULL, /* flags = */ 0); + regfree(®); + 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 index 0000000..3f828a9 --- /dev/null +++ b/t/libsysdb_testutils.h @@ -0,0 +1,49 @@ +/* + * SysDB - t/libsysdb_testutils.h + * Copyright (C) 2014 Sebastian 'tokkee' Harl + * 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 : */ +