Code

frontend: Split up command_handle() into several sdb_fe_<cmd> functions.
[sysdb.git] / src / frontend / query.c
1 /*
2  * SysDB - src/frontend/query.c
3  * Copyright (C) 2013 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 #include "sysdb.h"
30 #include "core/store.h"
31 #include "frontend/connection-private.h"
32 #include "frontend/parser.h"
33 #include "utils/error.h"
34 #include "utils/strbuf.h"
36 #include <errno.h>
37 #include <string.h>
39 /*
40  * private helper functions
41  */
43 typedef struct {
44         sdb_strbuf_t *buf;
45         sdb_store_matcher_t *filter;
47         size_t last_len;
48 } tojson_data_t;
50 static int
51 lookup_tojson(sdb_store_obj_t *obj, void *user_data)
52 {
53         tojson_data_t *data = user_data;
54         int status;
56         if (data->filter && (! sdb_store_matcher_matches(data->filter, obj, NULL)))
57                 return 0;
59         if (sdb_strbuf_len(data->buf) > data->last_len)
60                 sdb_strbuf_append(data->buf, ",");
61         data->last_len = sdb_strbuf_len(data->buf);
62         status = sdb_store_host_tojson(obj, data->buf,
63                         data->filter, /* flags = */ 0);
64         return status;
65 } /* lookup_tojson */
67 /*
68  * public API
69  */
71 int
72 sdb_fe_query(sdb_conn_t *conn)
73 {
74         sdb_llist_t *parsetree;
75         sdb_conn_node_t *node = NULL;
76         int status = 0;
78         parsetree = sdb_fe_parse(sdb_strbuf_string(conn->buf),
79                         (int)conn->cmd_len);
80         if (! parsetree) {
81                 char query[conn->cmd_len + 1];
82                 strncpy(query, sdb_strbuf_string(conn->buf), conn->cmd_len);
83                 query[sizeof(query) - 1] = '\0';
84                 sdb_log(SDB_LOG_ERR, "frontend: Failed to parse query '%s'",
85                                 query);
86                 return -1;
87         }
89         switch (sdb_llist_len(parsetree)) {
90                 case 0:
91                         /* skipping empty command */
92                         break;
93                 case 1:
94                         node = SDB_CONN_NODE(sdb_llist_get(parsetree, 0));
95                         break;
97                 default:
98                         {
99                                 char query[conn->cmd_len + 1];
100                                 strncpy(query, sdb_strbuf_string(conn->buf), conn->cmd_len);
101                                 query[sizeof(query) - 1] = '\0';
102                                 sdb_log(SDB_LOG_WARNING, "frontend: Ignoring %d command%s "
103                                                 "in multi-statement query '%s'",
104                                                 sdb_llist_len(parsetree) - 1,
105                                                 sdb_llist_len(parsetree) == 2 ? "" : "s",
106                                                 query);
107                                 node = SDB_CONN_NODE(sdb_llist_get(parsetree, 0));
108                         }
109         }
111         if (node) {
112                 status = sdb_fe_exec(conn, node);
113                 sdb_object_deref(SDB_OBJ(node));
114         }
116         sdb_llist_destroy(parsetree);
117         return status;
118 } /* sdb_fe_query */
120 int
121 sdb_fe_fetch(sdb_conn_t *conn)
123         char hostname[conn->cmd_len + 1];
124         strncpy(hostname, sdb_strbuf_string(conn->buf), conn->cmd_len);
125         hostname[sizeof(hostname) - 1] = '\0';
126         return sdb_fe_exec_fetch(conn, hostname, /* filter = */ NULL);
127 } /* sdb_fe_fetch */
129 int
130 sdb_fe_list(sdb_conn_t *conn)
132         return sdb_fe_exec_list(conn, /* filter = */ NULL);
133 } /* sdb_fe_list */
135 int
136 sdb_fe_lookup(sdb_conn_t *conn)
138         sdb_store_matcher_t *m;
139         int status;
141         m = sdb_fe_parse_matcher(sdb_strbuf_string(conn->buf),
142                         (int)conn->cmd_len);
143         if (! m) {
144                 char expr[conn->cmd_len + 1];
145                 strncpy(expr, sdb_strbuf_string(conn->buf), conn->cmd_len);
146                 expr[sizeof(expr) - 1] = '\0';
147                 sdb_log(SDB_LOG_ERR, "frontend: Failed to parse "
148                                 "lookup condition '%s'", expr);
149                 return -1;
150         }
152         status = sdb_fe_exec_lookup(conn, m, /* filter = */ NULL);
153         sdb_object_deref(SDB_OBJ(m));
154         return status;
155 } /* sdb_fe_lookup */
157 int
158 sdb_fe_exec(sdb_conn_t *conn, sdb_conn_node_t *node)
160         if (! node)
161                 return -1;
163         switch (node->cmd) {
164                 case CONNECTION_FETCH:
165                         return sdb_fe_exec_fetch(conn, CONN_FETCH(node)->name,
166                                         /* filter = */ NULL);
167                 case CONNECTION_LIST:
168                         return sdb_fe_exec_list(conn, /* filter = */ NULL);
169                 case CONNECTION_LOOKUP:
170                 {
171                         sdb_store_matcher_t *m = NULL, *filter = NULL;
172                         if (CONN_LOOKUP(node)->matcher)
173                                 m = CONN_LOOKUP(node)->matcher->matcher;
174                         if (CONN_LOOKUP(node)->filter)
175                                 filter = CONN_LOOKUP(node)->filter->matcher;
176                         return sdb_fe_exec_lookup(conn, m, filter);
177                 }
179                 default:
180                         sdb_log(SDB_LOG_ERR, "frontend: Unknown command %i", node->cmd);
181                         return -1;
182         }
183         return -1;
184 } /* sdb_fe_exec */
186 int
187 sdb_fe_exec_fetch(sdb_conn_t *conn, const char *name,
188                 sdb_store_matcher_t *filter)
190         sdb_strbuf_t *buf;
191         sdb_store_obj_t *host;
193         host = sdb_store_get_host(name);
194         if (! host) {
195                 sdb_log(SDB_LOG_DEBUG, "frontend: Failed to fetch host '%s': "
196                                 "not found", name);
198                 sdb_strbuf_sprintf(conn->errbuf, "Host %s not found", name);
199                 return -1;
200         }
202         buf = sdb_strbuf_create(1024);
203         if (! buf) {
204                 char errbuf[1024];
205                 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
206                                 "buffer to handle FETCH command: %s",
207                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
209                 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
210                 sdb_strbuf_destroy(buf);
211                 sdb_object_deref(SDB_OBJ(host));
212                 return -1;
213         }
215         if (sdb_store_host_tojson(host, buf, filter, /* flags = */ 0)) {
216                 sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize "
217                                 "host '%s' to JSON", name);
218                 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
219                 sdb_strbuf_destroy(buf);
220                 sdb_object_deref(SDB_OBJ(host));
221                 return -1;
222         }
224         sdb_connection_send(conn, CONNECTION_OK,
225                         (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
226         sdb_strbuf_destroy(buf);
227         sdb_object_deref(SDB_OBJ(host));
228         return 0;
229 } /* sdb_fe_exec_fetch */
231 int
232 sdb_fe_exec_list(sdb_conn_t *conn, sdb_store_matcher_t *filter)
234         sdb_strbuf_t *buf;
236         buf = sdb_strbuf_create(1024);
237         if (! buf) {
238                 char errbuf[1024];
239                 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
240                                 "buffer to handle LIST command: %s",
241                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
243                 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
244                 sdb_strbuf_destroy(buf);
245                 return -1;
246         }
248         if (sdb_store_tojson(buf, filter, /* flags = */ SDB_SKIP_ALL)) {
249                 sdb_log(SDB_LOG_ERR, "frontend: Failed to serialize "
250                                 "store to JSON");
251                 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
252                 sdb_strbuf_destroy(buf);
253                 return -1;
254         }
256         sdb_connection_send(conn, CONNECTION_OK,
257                         (uint32_t)sdb_strbuf_len(buf), sdb_strbuf_string(buf));
258         sdb_strbuf_destroy(buf);
259         return 0;
260 } /* sdb_fe_exec_list */
262 int
263 sdb_fe_exec_lookup(sdb_conn_t *conn, sdb_store_matcher_t *m,
264                 sdb_store_matcher_t *filter)
266         tojson_data_t data = { NULL, filter, 0 };
268         data.buf = sdb_strbuf_create(1024);
269         if (! data.buf) {
270                 char errbuf[1024];
271                 sdb_log(SDB_LOG_ERR, "frontend: Failed to create "
272                                 "buffer to handle LOOKUP command: %s",
273                                 sdb_strerror(errno, errbuf, sizeof(errbuf)));
275                 sdb_strbuf_sprintf(conn->errbuf, "Out of memory");
276                 sdb_strbuf_destroy(data.buf);
277                 return -1;
278         }
280         sdb_strbuf_append(data.buf, "[");
282         /* Let the JSON serializer handle the filter instead of the scanner. Else,
283          * we'd have to filter twice -- once in the scanner and then again in the
284          * serializer. */
285         data.last_len = sdb_strbuf_len(data.buf);
286         if (sdb_store_scan(m, /* filter */ NULL, lookup_tojson, &data)) {
287                 sdb_log(SDB_LOG_ERR, "frontend: Failed to lookup hosts");
288                 sdb_strbuf_sprintf(conn->errbuf, "Failed to lookup hosts");
289                 sdb_strbuf_destroy(data.buf);
290                 return -1;
291         }
293         sdb_strbuf_append(data.buf, "]");
295         sdb_connection_send(conn, CONNECTION_OK,
296                         (uint32_t)sdb_strbuf_len(data.buf), sdb_strbuf_string(data.buf));
297         sdb_strbuf_destroy(data.buf);
298         return 0;
299 } /* sdb_fe_exec_lookup */
301 /* vim: set tw=78 sw=4 ts=4 noexpandtab : */