author | Sebastian Harl <sh@tokkee.org> | |
Fri, 12 Dec 2014 07:55:39 +0000 (08:55 +0100) | ||
committer | Sebastian Harl <sh@tokkee.org> | |
Fri, 12 Dec 2014 07:57:51 +0000 (08:57 +0100) |
index f2bfa10927425c87fc22a5d9aab0e0ce6993626b..a681916c3cbcc0cdf141de7664f1a5f5d8262e1e 100644 (file)
--- a/README
+++ b/README
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/>
* 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)
--- a/src/include/core/store.h
+++ b/src/include/core/store.h
SDB_HOST = 1,
SDB_SERVICE,
SDB_METRIC,
- SDB_ATTRIBUTE,
+
+ SDB_ATTRIBUTE = 1 << 4,
/*
* Queryable fields of a stored object.
index 143661868d49204c9436bdd2e370a1346881df71..9589875300d468c7b9e6b7268fef5705a9580773 100644 (file)
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
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;
}
{ 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
*/
const char *result;
uint32_t rcode = 0;
- int status = 0;
+ int status = -1;
size_t i;
recv_buf = sdb_strbuf_create(1024);
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) {
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);
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)
--- a/src/tools/sysdb/input.c
+++ b/src/tools/sysdb/input.c
*/
static struct termios orig_term_attrs;
+static bool have_orig_term_attrs;
/*
* private helper functions
{
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);
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 */
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)
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 */
int
sdb_input_mainloop(void)
{
- yylex();
+ while (! sysdb_input->eof)
+ yylex();
return 0;
} /* sdb_input_mainloop */
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)
--- a/src/tools/sysdb/input.h
+++ b/src/tools/sysdb/input.h
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:
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 : */
diff --git a/src/tools/sysdb/main.c b/src/tools/sysdb/main.c
index b3a73c698e1e2507f9741b60a6692cb11ed5dd6c..357ef34827672d0033030eeefe8d85d059f98836 100644 (file)
--- a/src/tools/sysdb/main.c
+++ b/src/tools/sysdb/main.c
main(int argc, char **argv)
{
const char *host = NULL;
- char *user = NULL;
const char *homedir;
char hist_file[1024] = "";
host = optarg;
break;
case 'U':
- user = optarg;
+ input.user = optarg;
break;
case 'c':
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);
}
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);
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';
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)
#define APPEND() \
do { \
+ if (! isspace((int)yytext[0])) \
+ sysdb_input->have_input = 1; \
sysdb_input->query_len += strlen(yytext); \
} while (0)
%x CSC
-whitespace ([ \t\n\r\f]+)
+newline (\n|\r\n)
simple_comment ("--"[^\n\r]*)
/*
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); }
<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(); }
%%
diff --git a/t/integration/query.sh b/t/integration/query.sh
index 5536c701c9c517eefd3882518e0e2eee1683a88a..eb4ccd8814771f058f6a9a357f1b669f5803b5e5 100755 (executable)
--- a/t/integration/query.sh
+++ b/t/integration/query.sh
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"
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'
| 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" \