Code

parser: Let the TIMESERIES command accept optional data-source names.
[sysdb.git] / src / tools / sysdb / command.c
1 /*
2  * SysDB - src/tools/sysdb/command.c
3  * Copyright (C) 2014 Sebastian 'tokkee' Harl <sh@tokkee.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
17  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
28 #if HAVE_CONFIG_H
29 #       include "config.h"
30 #endif /* HAVE_CONFIG_H */
32 #include "sysdb.h"
34 #include "tools/sysdb/command.h"
35 #include "tools/sysdb/input.h"
36 #include "tools/sysdb/json.h"
38 #include "frontend/proto.h"
39 #include "utils/error.h"
40 #include "utils/proto.h"
41 #include "utils/strbuf.h"
43 #include <sys/types.h>
44 #include <sys/wait.h>
46 #include <errno.h>
48 #include <assert.h>
49 #include <ctype.h>
50 #include <signal.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <string.h>
54 #include <unistd.h>
56 static pid_t
57 exec_pager(int in)
58 {
59         pid_t pid;
60         long fd;
62         sigset_t sigs;
64         pid = fork();
65         if (pid) /* parent or error */
66                 return pid;
68         sigemptyset(&sigs);
69         sigprocmask(SIG_SETMASK, &sigs, NULL);
71         for (fd = 3; fd <= sysconf(_SC_OPEN_MAX); fd++)
72                 if (fd != in)
73                         close((int)fd);
75         if (dup2(in, STDIN_FILENO) >= 0) {
76                 /* TODO: make configurable */
77                 char *argv[] = { "less", "-FRX" };
79                 close(in);
80                 in = STDIN_FILENO;
82                 if (execvp(argv[0], argv)) {
83                         char cmdbuf[1024], errbuf[1024];
84                         sdb_data_format(&(sdb_data_t){
85                                                 SDB_TYPE_STRING | SDB_TYPE_ARRAY,
86                                                 { .array = { SDB_STATIC_ARRAY_LEN(argv), argv } },
87                                         }, cmdbuf, sizeof(cmdbuf), SDB_UNQUOTED);
88                         sdb_log(SDB_LOG_WARNING, "Failed to execute pager %s: %s",
89                                         cmdbuf, sdb_strerror(errno, errbuf, sizeof(errbuf)));
90                 }
91         }
93         /* else: something failed, simply print all input */
94         while (42) {
95                 char buf[1024 + 1];
96                 ssize_t n;
98                 n = read(in, buf, sizeof(buf) - 1);
99                 if (!n) /* EOF */
100                         break;
102                 if (n > 0) {
103                         buf[n] = '\0';
104                         printf("%s", buf);
105                         continue;
106                 }
108                 if ((errno == EAGAIN) || (errno == EWOULDBLOCK)
109                                 || (errno == EINTR))
110                         continue;
112                 sdb_log(SDB_LOG_ERR, "Failed to read query result: %s",
113                                 sdb_strerror(errno, buf, sizeof(buf)));
114                 exit(1);
115         }
116         exit(0);
117 } /* exec_pager */
119 static void
120 ok_printer(sdb_input_t __attribute__((unused)) *input, sdb_strbuf_t *buf)
122         const char *msg = sdb_strbuf_string(buf);
123         if (msg && *msg)
124                 printf("%s\n", msg);
125         else
126                 printf("OK\n");
127 } /* ok_printer */
129 static void
130 log_printer(sdb_input_t __attribute__((unused)) *input, sdb_strbuf_t *buf)
132         uint32_t prio = 0;
134         if (sdb_proto_unmarshal_int32(SDB_STRBUF_STR(buf), &prio) < 0) {
135                 sdb_log(SDB_LOG_WARNING, "Received a LOG message with invalid "
136                                 "or missing priority");
137                 prio = (uint32_t)SDB_LOG_ERR;
138         }
139         sdb_strbuf_skip(buf, 0, sizeof(prio));
141         sdb_log((int)prio, "%s", sdb_strbuf_string(buf));
142 } /* log_printer */
144 static void
145 data_printer(sdb_input_t *input, sdb_strbuf_t *buf)
147         size_t len = sdb_strbuf_len(buf);
148         uint32_t type = 0;
150         int pipefd[2] = { -1, -1 };
151         FILE *out = stdout;
152         pid_t pager = -1;
154         if ((! len) || (len == sizeof(uint32_t))) {
155                 /* empty command or empty reply */
156                 return;
157         }
158         else if (len < sizeof(uint32_t)) {
159                 sdb_log(SDB_LOG_ERR, "Received a DATA message with invalid "
160                                 "or missing data-type");
161                 return;
162         }
164         if (input->interactive) {
165                 if (pipe(pipefd)) {
166                         char errbuf[2014];
167                         sdb_log(SDB_LOG_WARNING, "Failed to open pipe: %s",
168                                         sdb_strerror(errno, errbuf, sizeof(errbuf)));
169                 }
170                 else {
171                         out = fdopen(pipefd[1], "w");
172                         pager = exec_pager(pipefd[0]);
173                         if (pager < 0) {
174                                 out = stdout;
175                                 close(pipefd[0]);
176                                 close(pipefd[1]);
177                         }
178                         else
179                                 close(pipefd[0]);
180                 }
181         }
183         sdb_proto_unmarshal_int32(SDB_STRBUF_STR(buf), &type);
184         sdb_strbuf_skip(buf, 0, sizeof(uint32_t));
185         if (sdb_json_print(out, input, (int)type, buf))
186                 sdb_log(SDB_LOG_ERR, "Failed to print result");
187         fprintf(out, "\n");
189         if (out != stdout)
190                 fclose(out); /* will close pipefd[1] */
191         if (pager > 0)
192                 waitpid(pager, NULL, 0);
193 } /* data_printer */
195 static struct {
196         int status;
197         void (*printer)(sdb_input_t *, sdb_strbuf_t *);
198 } response_printers[] = {
199         { SDB_CONNECTION_OK,   ok_printer },
200         { SDB_CONNECTION_LOG,  log_printer },
201         { SDB_CONNECTION_DATA, data_printer },
202 };
204 static void
205 clear_query(sdb_input_t *input)
207         sdb_strbuf_skip(input->input, 0, input->query_len);
208         input->tokenizer_pos -= input->query_len;
209         input->query_len = 0;
210         input->have_input = 0;
211 } /* clear_query */
213 /*
214  * public API
215  */
217 int
218 sdb_command_print_reply(sdb_input_t *input)
220         sdb_strbuf_t *recv_buf;
221         const char *result;
222         uint32_t rcode = 0;
224         int status = -1;
225         size_t i;
227         recv_buf = sdb_strbuf_create(1024);
228         if (! recv_buf)
229                 return -1;
231         if (sdb_client_recv(input->client, &rcode, recv_buf) < 0)
232                 rcode = UINT32_MAX;
234         if (sdb_client_eof(input->client)) {
235                 sdb_strbuf_destroy(recv_buf);
236                 return -1;
237         }
239         if (rcode != UINT32_MAX)
240                 status = (int)rcode;
242         for (i = 0; i < SDB_STATIC_ARRAY_LEN(response_printers); ++i) {
243                 if (status == response_printers[i].status) {
244                         response_printers[i].printer(input, recv_buf);
245                         sdb_strbuf_destroy(recv_buf);
246                         return status;
247                 }
248         }
250         result = sdb_strbuf_string(recv_buf);
251         if (result && *result)
252                 sdb_log(SDB_LOG_ERR, "%s", result);
253         else if (rcode == UINT32_MAX) {
254                 char errbuf[1024];
255                 sdb_log(SDB_LOG_ERR, "%s",
256                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
257         }
259         sdb_strbuf_destroy(recv_buf);
260         return status;
261 } /* sdb_command_print_reply */
263 char *
264 sdb_command_exec(sdb_input_t *input)
266         const char *query;
267         uint32_t query_len;
269         char *data = NULL;
271         query = sdb_strbuf_string(input->input);
272         query_len = (uint32_t)input->query_len;
274         assert(input->query_len <= input->tokenizer_pos);
276         /* removing leading and trailing newlines */
277         while (query_len && (*query == '\n')) {
278                 ++query;
279                 --query_len;
280         }
281         while (query_len && (query[query_len - 1]) == '\n')
282                 --query_len;
284         if (query_len) {
285                 data = strndup(query, query_len);
286                 /* ignore errors; we'll only hide the command from the caller */
287         }
289         if (sdb_client_eof(input->client)) {
290                 if (sdb_input_reconnect()) {
291                         clear_query(input);
292                         return data;
293                 }
294         }
295         else if (! query_len)
296                 return NULL;
298         sdb_client_send(input->client, SDB_CONNECTION_QUERY, query_len, query);
300         /* The server may send back log messages but will eventually reply to the
301          * query, which is either DATA or ERROR. */
302         while (42) {
303                 int status = sdb_command_print_reply(input);
304                 if (status < 0) {
305                         sdb_log(SDB_LOG_ERR, "Failed to read reply from server");
306                         break;
307                 }
309                 if ((status == SDB_CONNECTION_OK)
310                                 || (status == SDB_CONNECTION_DATA)
311                                 || (status == SDB_CONNECTION_ERROR))
312                         break;
313         }
314         clear_query(input);
315         return data;
316 } /* sdb_command_exec */
318 void
319 sdb_command_print_server_version(sdb_input_t *input)
321         sdb_strbuf_t *buf = sdb_strbuf_create(32);
322         uint32_t code = 0, version = 0;
323         const char *extra;
325         if ((sdb_client_rpc(input->client, SDB_CONNECTION_SERVER_VERSION,
326                                         0, NULL, &code, buf) < 0) || (code != SDB_CONNECTION_OK))
327                 return;
328         if (sdb_strbuf_len(buf) < sizeof(version))
329                 return;
331         sdb_proto_unmarshal_int32(SDB_STRBUF_STR(buf), &version);
332         extra = sdb_strbuf_string(buf) + sizeof(version);
333         sdb_log(SDB_LOG_INFO, "SysDB server %d.%d.%d%s",
334                         SDB_VERSION_DECODE((int)version), extra);
335         sdb_strbuf_destroy(buf);
336 } /* sdb_command_print_server_version */
338 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */