1 /*
2 * Copyright (C) 2015 Dejan Muhamedagic <dejan@hello-penguin.com>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #include <stdio.h>
20 #include <string.h>
21 #include "attr.h"
22 #include "booth.h"
23 #include "ticket.h"
24 #include "pacemaker.h"
25
26 void print_geostore_usage(void)
27 {
28 printf(
29 "Usage:\n"
30 " geostore {list|set|get|delete} [-t ticket] [options] attr [value]\n"
31 "\n"
32 " list: List all attributes\n"
33 " set: Set attribute to a value\n"
34 " get: Get attribute's value\n"
35 " delete: Delete attribute\n"
36 "\n"
37 " -t <ticket> Ticket where attribute resides\n"
38 " (required, if more than one ticket is configured)\n"
39 "\n"
40 "Options:\n"
41 " -c FILE Specify config file [default " BOOTH_DEFAULT_CONF "]\n"
42 " Can be a path or just a name without \".conf\" suffix\n"
43 " -s <site> Connect to a different site\n"
44 " -h Print this help\n"
45 "\n"
46 "Examples:\n"
47 "\n"
48 " # geostore list -t ticket-A -s 10.121.8.183\n"
49 " # geostore set -s 10.121.8.183 sr_status ACTIVE\n"
50 " # geostore get -t ticket-A -s 10.121.8.183 sr_status\n"
51 " # geostore delete -s 10.121.8.183 sr_status\n"
52 "\n"
53 "See the geostore(8) man page for more details.\n"
54 );
55 }
56
57 /*
58 * the client side
59 */
60
61 /* cl has all the input parameters:
62 * ticket, attr name, attr value
63 */
64
65 int test_attr_reply(cmd_result_t reply_code, cmd_request_t cmd)
66 {
67 int rv = 0;
68 const char *op_str = NULL;
69
70 switch (cmd) {
71 case ATTR_SET: op_str = "set"; break;
72 case ATTR_GET: op_str = "get"; break;
73 case ATTR_LIST: op_str = "list"; break;
74 case ATTR_DEL: op_str = "delete"; break;
75 default:
76 log_error("internal error reading reply result!");
77 return -1;
78 }
79
80 switch (reply_code) {
81 case RLT_ASYNC:
82 log_info("%s command sent, result will be returned "
83 "asynchronously.", op_str);
84 rv = 0;
85 break;
86
87 case RLT_SYNC_SUCC:
88 case RLT_SUCCESS:
89 if (cmd == ATTR_SET)
90 log_info("%s succeeded!", op_str);
91 rv = 0;
92 break;
93
94 case RLT_SYNC_FAIL:
95 log_info("%s failed!", op_str);
96 rv = -1;
97 break;
98
99 case RLT_INVALID_ARG:
100 log_error("ticket \"%s\" does not exist",
101 cl.attr_msg.attr.tkt_id);
102 rv = 1;
103 break;
104
105 case RLT_NO_SUCH_ATTR:
106 log_error("attribute \"%s\" not set",
107 cl.attr_msg.attr.name);
108 rv = 1;
109 break;
110
111 case RLT_AUTH:
112 log_error("authentication error");
113 rv = -1;
114 break;
115
116 default:
117 log_error("got an error code: %x", rv);
118 rv = -1;
119 }
120 return rv;
121 }
122
123 /* read the server's reply
124 * need to first get the header which contains the length of the
125 * reply
126 * return codes:
127 * -2: header not received
128 * -1: header received, but message too short
129 * >=0: success
130 */
131 static int read_server_reply(
132 struct booth_transport const *tpt, struct booth_site *site,
133 char *msg)
134 {
135 struct boothc_header *header;
136 int rv;
137 int len;
138
139 header = (struct boothc_header *)msg;
140 rv = tpt->recv(site, header, sizeof(*header));
141 if (rv < 0) {
142 return -2;
143 }
144 len = ntohl(header->length);
145 rv = tpt->recv(site, msg+sizeof(*header), len-sizeof(*header));
146 if (rv < 0) {
147 return -1;
148 }
149 return rv;
150 }
151
152 int do_attr_command(struct booth_config *conf, cmd_request_t cmd)
153 {
154 struct booth_site *site = NULL;
155 struct boothc_header *header;
156 struct booth_transport const *tpt = NULL;
157 int len, rv = -1;
158 char *msg = NULL;
159
160 if (!*cl.site)
161 site = local;
162 else {
163 if (!find_site_by_name(conf, cl.site, &site, 1)) {
164 log_error("Site \"%s\" not configured.", cl.site);
165 goto out_close;
166 }
167 }
168
169 if (site->type == ARBITRATOR) {
170 if (site == local) {
171 log_error("We're just an arbitrator, no attributes here.");
172 } else {
173 log_error("%s is just an arbitrator, no attributes there.", cl.site);
174 }
175 goto out_close;
176 }
177
178 tpt = booth_transport + TCP;
179
180 init_header(&cl.attr_msg.header, cmd, 0, cl.options, 0, 0,
181 sizeof(cl.attr_msg));
182
183 rv = tpt->open(site);
184 if (rv < 0)
185 goto out_close;
186
187 rv = tpt->send(conf, site, &cl.attr_msg, sendmsglen(&cl.attr_msg));
188 if (rv < 0) {
189 goto out_close;
190 }
191
192 msg = malloc(MAX_MSG_LEN);
193 if (!msg) {
194 log_error("out of memory");
195 rv = -1;
196 goto out_close;
197 }
198
199 rv = read_server_reply(tpt, site, msg);
200 header = (struct boothc_header *)msg;
201 if (rv < 0) {
202 if (rv == -1)
203 (void)test_attr_reply(ntohl(header->result), cmd);
204 goto out_close;
205 }
206 len = ntohl(header->length);
207
208 if (check_boothc_header(header, len) < 0) {
209 log_error("message from %s receive error", site_string(site));
210 rv = -1;
211 goto out_close;
212 }
213
214 if (check_auth(conf, site, msg, len)) {
215 log_error("%s failed to authenticate", site_string(site));
216 rv = -1;
217 goto out_close;
218 }
219 rv = test_attr_reply(ntohl(header->result), cmd);
220
221 out_close:
222 if (tpt && site)
223 tpt->close(site);
224 if (msg)
225 free(msg);
226 return rv;
227 }
228
229 /*
230 * the server side
231 */
232
233 /* need to invert gboolean, our success is 0
234 */
235 #define gbool2rlt(i) (i ? RLT_SUCCESS : RLT_SYNC_FAIL)
236
237 static void free_geo_attr(gpointer data)
238 {
239 struct geo_attr *a = (struct geo_attr *)data;
240
241 if (!a)
242 return;
243 g_free(a->val);
244 g_free(a);
245 }
246
247 int store_geo_attr(struct ticket_config *tk, const char *name,
248 const char *val, int notime)
249 {
250 struct geo_attr *a;
251 GDestroyNotify free_geo_attr_notify = free_geo_attr;
252
253 if (!tk)
254 return -1;
255 /*
256 * allocate new, if attr doesn't already exist
257 * copy the attribute value
258 * send status
259 */
260 if (!tk->attr)
261 tk->attr = g_hash_table_new_full(g_str_hash, g_str_equal,
262 g_free, free_geo_attr_notify);
263 if (!tk->attr) {
264 log_error("out of memory");
265 return -1;
266 }
267
268 if (strnlen(name, BOOTH_NAME_LEN) == BOOTH_NAME_LEN)
269 tk_log_warn("name of the attribute too long (%d+ bytes), skipped",
270 BOOTH_NAME_LEN);
271 else if (strnlen(val, BOOTH_ATTRVAL_LEN) == BOOTH_ATTRVAL_LEN)
272 tk_log_warn("value of the attribute too long (%d+ bytes), skipped",
273 BOOTH_ATTRVAL_LEN);
274 else {
275 a = (struct geo_attr *)calloc(1, sizeof(struct geo_attr));
276 if (!a) {
277 log_error("out of memory");
278 return -1;
279 }
280
281 a->val = g_strdup(val);
282 if (!notime)
283 get_time(&a->update_ts);
284
285 g_hash_table_insert(tk->attr,
286 g_strdup(name), a);
287 }
288
289 return 0;
290 }
291
292 static cmd_result_t attr_set(struct ticket_config *tk, struct boothc_attr_msg *msg)
293 {
294 int rc;
295
296 rc = store_geo_attr(tk, msg->attr.name, msg->attr.val, 0);
297 if (rc) {
298 return RLT_SYNC_FAIL;
299 }
300 (void)pcmk_handler.set_attr(tk, msg->attr.name, msg->attr.val);
301 return RLT_SUCCESS;
302 }
303
304 static cmd_result_t attr_del(struct ticket_config *tk, struct boothc_attr_msg *msg)
305 {
306 gboolean rv;
307 gpointer orig_key, value;
308
309 /*
310 * lookup attr
311 * deallocate, if found
312 * send status
313 */
314 if (!tk->attr)
315 return RLT_NO_SUCH_ATTR;
316
317 rv = g_hash_table_lookup_extended(tk->attr, msg->attr.name,
318 &orig_key, &value);
319 if (!rv)
320 return RLT_NO_SUCH_ATTR;
321
322 rv = g_hash_table_remove(tk->attr, msg->attr.name);
323
324 (void)pcmk_handler.del_attr(tk, msg->attr.name);
325
326 return gbool2rlt(rv);
327 }
328
329 static void
330 append_attr(gpointer key, gpointer value, gpointer user_data)
331 {
332 char *attr_name = (char *)key;
333 struct geo_attr *a = (struct geo_attr *)value;
334 GString *data = (GString *)user_data;
335 char time_str[64];
336 time_t ts;
337
338 if (is_time_set(&a->update_ts)) {
339 ts = wall_ts(&a->update_ts);
340 strftime(time_str, sizeof(time_str), "%F %T",
341 localtime(&ts));
342 } else {
343 time_str[0] = '\0';
344 }
345 g_string_append_printf(data, "%s %s %s\n",
346 attr_name, a->val, time_str);
347 }
348
349
350 static cmd_result_t attr_get(struct booth_config *conf, struct ticket_config *tk,
351 int fd, struct boothc_attr_msg *msg)
352 {
353 cmd_result_t rv = RLT_SUCCESS;
354 struct boothc_hdr_msg hdr;
355 struct geo_attr *a;
356 GString *attr_val;
357
358 /*
359 * lookup attr
360 * send value
361 */
362 if (!tk->attr) {
363 return RLT_NO_SUCH_ATTR;
364 }
365
366 a = (struct geo_attr *)g_hash_table_lookup(tk->attr, msg->attr.name);
367 if (!a) {
368 return RLT_NO_SUCH_ATTR;
369 }
370
371 attr_val = g_string_new(NULL);
372 if (!attr_val) {
373 log_error("out of memory");
374 return RLT_SYNC_FAIL;
375 }
376 g_string_printf(attr_val, "%s\n", a->val);
377 init_header(&hdr.header, ATTR_GET, 0, 0, RLT_SUCCESS, 0,
378 sizeof(hdr) + attr_val->len);
379
380 if (send_header_plus(conf, fd, &hdr, attr_val->str, attr_val->len)) {
381 rv = RLT_SYNC_FAIL;
382 }
383
384 if (attr_val) {
385 g_string_free(attr_val, TRUE);
386 }
387
388 return rv;
389 }
390
391 static cmd_result_t attr_list(struct booth_config *conf, struct ticket_config *tk,
392 int fd, struct boothc_attr_msg *msg)
393 {
394 GString *data;
395 cmd_result_t rv;
396 struct boothc_hdr_msg hdr;
397
398 /*
399 * list all attributes for the ticket
400 * send the list
401 */
402 data = g_string_sized_new(512);
|
(1) Event cond_false: |
Condition "!data", taking false branch. |
403 if (!data) {
404 log_error("out of memory");
405 return RLT_SYNC_FAIL;
|
(2) Event if_end: |
End of if statement. |
406 }
|
(3) Event cond_true: |
Condition "tk->attr", taking true branch. |
407 if (tk->attr) {
408 g_hash_table_foreach(tk->attr, append_attr, data);
409 }
410
411 init_header(&hdr.header, ATTR_LIST, 0, 0, RLT_SUCCESS, 0,
412 sizeof(hdr) + data->len);
|
(4) Event tainted_data_return: |
Called function "send_header_plus(conf, fd, &hdr, data->str, data->len)", and a possible return value may be less than zero. |
|
(5) Event cast_underflow: |
An assign of a possibly negative number to an unsigned type, which might trigger an underflow. |
| Also see events: |
[return_overflow] |
413 rv = send_header_plus(conf, fd, &hdr, data->str, data->len);
414
|
(6) Event cond_true: |
Condition "data", taking true branch. |
415 if (data) {
|
(7) Event cond_true: |
Condition "1", taking true branch. |
|
(8) Event cond_true: |
Condition "1 /* !0 */", taking true branch. |
416 g_string_free(data, TRUE);
417 }
418
419 return rv;
420 }
421
422 int process_attr_request(struct booth_config *conf, struct client *req_client,
423 void *buf)
424 {
425 cmd_result_t rv = RLT_SYNC_FAIL;
426 struct ticket_config *tk;
427 int cmd;
428 struct boothc_attr_msg *msg;
429 struct boothc_hdr_msg hdr;
430
431 msg = (struct boothc_attr_msg *)buf;
432 cmd = ntohl(msg->header.cmd);
433 if (!check_ticket(conf, msg->attr.tkt_id, &tk)) {
434 log_warn("client referenced unknown ticket %s",
435 msg->attr.tkt_id);
436 rv = RLT_INVALID_ARG;
437 goto reply_now;
438 }
439
440 switch (cmd) {
441 case ATTR_LIST:
442 rv = attr_list(conf, tk, req_client->fd, msg);
443 if (rv) {
444 goto reply_now;
445 }
446
447 return 1;
448 case ATTR_GET:
449 rv = attr_get(conf, tk, req_client->fd, msg);
450 if (rv) {
451 goto reply_now;
452 }
453
454 return 1;
455 case ATTR_SET:
456 rv = attr_set(tk, msg);
457 break;
458 case ATTR_DEL:
459 rv = attr_del(tk, msg);
460 break;
461 }
462
463 reply_now:
464 init_header(&hdr.header, CL_RESULT, 0, 0, rv, 0, sizeof(hdr));
465 send_header_plus(conf, req_client->fd, &hdr, NULL, 0);
466 return 1;
467 }
468
469 /* read attr message from another site */
470
471 /* this is a NOOP and it should never be invoked
472 * only clients retrieve/manage attributes and they connect
473 * directly to the target site
474 */
475 int attr_recv(struct booth_config *conf, void *buf, struct booth_site *source)
476 {
477 struct boothc_attr_msg *msg;
478 struct ticket_config *tk;
479
480 msg = (struct boothc_attr_msg *)buf;
481
482 log_warn("unexpected attribute message from %s",
483 site_string(source));
484
485 if (!check_ticket(conf, msg->attr.tkt_id, &tk)) {
486 log_warn("got invalid ticket name %s from %s",
487 msg->attr.tkt_id, site_string(source));
488 source->invalid_cnt++;
489 return -1;
490 }
491
492 return 0;
493 }
494