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