1 /*
2 * Copyright (C) 2011 Jiaju Zhang <jjzhang@suse.de>
3 * Copyright (C) 2013-2014 Philipp Marek <philipp.marek@linbit.com>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This software is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "b_config.h"
21
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <sched.h>
26 #include <errno.h>
27 #include <limits.h>
28 #include <sys/file.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <sys/mman.h>
32 #include <sys/time.h>
33 #include <sys/resource.h>
34 #include <sys/socket.h>
35 #include <sys/un.h>
36 #include <sys/poll.h>
37 #include <sys/wait.h>
38 #include <fcntl.h>
39 #include <string.h>
40 #include <ctype.h>
41 #include <assert.h>
42 #include <signal.h>
43 #include <netdb.h>
44 #include <arpa/inet.h>
45 #include <sys/types.h>
46
47 #include <crm/services.h>
48
49 #if HAVE_LIBGNUTLS
50 #include <gnutls/gnutls.h>
51 #endif
52 #if HAVE_LIBGCRYPT
53 #include <gcrypt.h>
54 #endif
55 #ifndef NAMETAG_LIBSYSTEMD
56 #include <clplumbing/setproctitle.h>
57 #else
58 #include "alt/nametag_libsystemd.h"
59 #endif
60 #ifdef COREDUMP_NURSING
61 #include <sys/prctl.h>
62 #include <clplumbing/coredumps.h>
63 #endif
64 #include "log.h"
65 #include "booth.h"
66 #include "config.h"
67 #include "transport.h"
68 #include "inline-fn.h"
69 #include "pacemaker.h"
70 #include "ticket.h"
71 #include "request.h"
72 #include "attr.h"
73 #include "handler.h"
74
75 #define RELEASE_STR VERSION
76
77 #define CLIENT_NALLOC 32
78
79 static int daemonize = 1;
80 int enable_stderr = 0;
81 timetype start_time;
82
83
84 /** Structure for "clients".
85 * Filehandles with incoming data get registered here (and in pollfds),
86 * along with their callbacks.
87 * Because these can be reallocated with every new fd, addressing
88 * happens _only_ by their numeric index. */
89 struct client *clients = NULL;
90 struct pollfd *pollfds = NULL;
91 static int client_maxi;
92 static int client_size = 0;
93
94
95 static const struct booth_site _no_leader = {
96 .addr_string = "none",
97 .site_id = NO_ONE,
98 .index = -1,
99 };
100 struct booth_site *const no_leader = (struct booth_site*) &_no_leader;
101
102 typedef enum
103 {
104 BOOTHD_STARTED=0,
105 BOOTHD_STARTING
106 } BOOTH_DAEMON_STATE;
107
108 int poll_timeout;
109
110
111
112 struct booth_config *booth_conf;
113 struct command_line cl;
114
115 /*
116 * Global signal handlers variables
117 */
118 static int sig_exit_handler_called = 0;
119 static int sig_exit_handler_sig = 0;
120 static int sig_usr1_handler_called = 0;
121 static int sig_chld_handler_called = 0;
122
123 static const char *state_string(BOOTH_DAEMON_STATE st)
124 {
125 if (st == BOOTHD_STARTED) {
126 return "started";
127 } else if (st == BOOTHD_STARTING) {
128 return "starting";
129 } else {
130 return "invalid";
131 }
132 }
133
134 static void client_alloc(void)
135 {
136 int i;
137
138 if (!(clients = realloc(
139 clients, (client_size + CLIENT_NALLOC) * sizeof(*clients))
140 ) || !(pollfds = realloc(
141 pollfds, (client_size + CLIENT_NALLOC) * sizeof(*pollfds))
142 )) {
143 log_error("can't alloc for client array");
144 exit(1);
145 }
146
147 for (i = client_size; i < client_size + CLIENT_NALLOC; i++) {
148 clients[i].workfn = NULL;
149 clients[i].deadfn = NULL;
150 clients[i].fd = -1;
151 pollfds[i].fd = -1;
152 pollfds[i].revents = 0;
153 }
154 client_size += CLIENT_NALLOC;
155 }
156
157 static void client_dead(int ci)
158 {
159 struct client *c = clients + ci;
160
161 if (c->fd != -1) {
162 log_debug("removing client %d", c->fd);
163 close(c->fd);
164 }
165
166 c->fd = -1;
167 c->workfn = NULL;
168
169 if (c->msg) {
170 free(c->msg);
171 c->msg = NULL;
172 c->offset = 0;
173 }
174
175 pollfds[ci].fd = -1;
176 }
177
178 int client_add(int fd, const struct booth_transport *tpt,
179 workfn_t workfn, void (*deadfn)(int ci))
180 {
181 int i;
182 struct client *c;
183
184
185 if (client_size - 1 <= client_maxi ) {
186 client_alloc();
187 }
188
189 for (i = 0; i < client_size; i++) {
190 c = clients + i;
191 if (c->fd != -1)
192 continue;
193
194 c->workfn = workfn;
195 if (deadfn)
196 c->deadfn = deadfn;
197 else
198 c->deadfn = client_dead;
199
200 c->transport = tpt;
201 c->fd = fd;
202 c->msg = NULL;
203 c->offset = 0;
204
205 pollfds[i].fd = fd;
206 pollfds[i].events = POLLIN;
207 if (i > client_maxi)
208 client_maxi = i;
209
210 return i;
211 }
212
213 assert(!"no client");
214 }
215
216 int find_client_by_fd(int fd)
217 {
218 int i;
219
220 if (fd < 0)
221 return -1;
222
223 for (i = 0; i <= client_maxi; i++) {
224 if (clients[i].fd == fd)
225 return i;
226 }
227 return -1;
228 }
229
230 static int format_peers(char **pdata, unsigned int *len)
231 {
232 struct booth_site *s;
233 char *data, *cp;
234 char time_str[64];
235 int i, alloc;
236
237 *pdata = NULL;
238 *len = 0;
239
240 alloc = booth_conf->site_count * (BOOTH_NAME_LEN + 256);
241 data = malloc(alloc);
242 if (!data)
243 return -ENOMEM;
244
245 cp = data;
246 _FOREACH_NODE(i, s) {
247 if (s == local)
248 continue;
249 strftime(time_str, sizeof(time_str), "%F %T",
250 localtime(&s->last_recv));
251 cp += snprintf(cp,
252 alloc - (cp - data),
253 "%-12s %s, last recv: %s\n",
254 type_to_string(s->type),
255 s->addr_string,
256 time_str);
257 cp += snprintf(cp,
258 alloc - (cp - data),
259 "\tSent pkts:%u error:%u resends:%u\n",
260 s->sent_cnt,
261 s->sent_err_cnt,
262 s->resend_cnt);
263 cp += snprintf(cp,
264 alloc - (cp - data),
265 "\tRecv pkts:%u error:%u authfail:%u invalid:%u\n\n",
266 s->recv_cnt,
267 s->recv_err_cnt,
268 s->sec_cnt,
269 s->invalid_cnt);
270 if (alloc - (cp - data) <= 0) {
271 free(data);
272 return -ENOMEM;
273 }
274 }
275
276 *pdata = data;
277 *len = cp - data;
278
279 return 0;
280 }
281
282
283 void list_peers(struct booth_config *conf, int fd)
284 {
285 char *data;
286 unsigned int olen;
287 struct boothc_hdr_msg hdr;
288
289 if (format_peers(&data, &olen) < 0) {
290 goto out;
291 }
292
293 init_header(&hdr.header, CL_LIST, 0, 0, RLT_SUCCESS, 0, sizeof(hdr) + olen);
294 send_header_plus(conf, fd, &hdr, data, olen);
295
296 out:
297 if (data) {
298 free(data);
299 }
300 }
301
302 /* trim trailing spaces if the key is ascii
303 */
304 static void trim_key()
305 {
306 char *p;
307 int i;
308
309 for (i=0, p=booth_conf->authkey; i < booth_conf->authkey_len; i++, p++)
310 if (!isascii(*p))
311 return;
312
313 p = booth_conf->authkey;
314 while (booth_conf->authkey_len > 0 && isspace(*p)) {
315 p++;
316 booth_conf->authkey_len--;
317 }
318 memmove(booth_conf->authkey, p, booth_conf->authkey_len);
319
320 p = booth_conf->authkey + booth_conf->authkey_len - 1;
321 while (booth_conf->authkey_len > 0 && isspace(*p)) {
322 booth_conf->authkey_len--;
323 p--;
324 }
325 }
326
327 static int read_authkey()
328 {
329 int fd;
330
331 booth_conf->authkey[0] = '\0';
332 fd = open(booth_conf->authfile, O_RDONLY);
333 if (fd < 0) {
334 log_error("cannot open %s: %s",
335 booth_conf->authfile, strerror(errno));
336 return -1;
337 }
338 if (fstat(fd, &booth_conf->authstat) < 0) {
339 log_error("cannot stat authentication file %s (%d): %s",
340 booth_conf->authfile, fd, strerror(errno));
341 close(fd);
342 return -1;
343 }
344 if (booth_conf->authstat.st_mode & (S_IRGRP | S_IROTH)) {
345 log_error("%s: file shall not be readable for anyone but the owner",
346 booth_conf->authfile);
347 close(fd);
348 return -1;
349 }
350 booth_conf->authkey_len = read(fd, booth_conf->authkey, BOOTH_MAX_KEY_LEN);
351 close(fd);
352 trim_key();
353 log_debug("read key of size %d in authfile %s",
354 booth_conf->authkey_len, booth_conf->authfile);
355 /* make sure that the key is of minimum length */
356 return (booth_conf->authkey_len >= BOOTH_MIN_KEY_LEN) ? 0 : -1;
357 }
358
359 int update_authkey()
360 {
361 struct stat statbuf;
362
363 if (stat(booth_conf->authfile, &statbuf) < 0) {
364 log_error("cannot stat authentication file %s: %s",
365 booth_conf->authfile, strerror(errno));
366 return -1;
367 }
368 if (statbuf.st_mtime > booth_conf->authstat.st_mtime) {
369 return read_authkey();
370 }
371 return 0;
372 }
373
374 static int setup_config(struct booth_config **conf, int type)
375 {
376 int rv;
377
378 assert(conf != NULL);
379
380 rv = read_config(conf, cl.configfile, type);
381 if (rv < 0) {
382 goto out;
383 }
384
385 if (booth_conf->authfile[0] != '\0') {
386 rv = read_authkey();
387 if (rv < 0)
388 goto out;
389 #if HAVE_LIBGCRYPT
390 if (!gcry_check_version(NULL)) {
391 log_error("gcry_check_version");
392 rv = -ENOENT;
393 goto out;
394 }
395 gcry_control(GCRYCTL_DISABLE_SECMEM, 0);
396 gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
397 #endif
398 #if HAVE_LIBGNUTLS
399 if (gnutls_global_init() != 0) {
400 log_error("Cannot initialize GnuTLS");
401 rv = -EINVAL;
402 goto out;
403 };
404 #endif
405 }
406
407 /* Set "local" pointer, ignoring errors. */
408 if (cl.type == DAEMON && cl.site[0]) {
409 if (!find_site_by_name(booth_conf, cl.site, &local, 1)) {
410 log_error("Cannot find \"%s\" in the configuration.",
411 cl.site);
412 return -EINVAL;
413 }
414 local->local = 1;
415 } else {
416 find_myself(booth_conf, NULL, type == CLIENT || type == GEOSTORE);
417 }
418
419 rv = check_config(booth_conf, type);
420 if (rv < 0)
421 goto out;
422
423
424 /* Per default the PID file name is derived from the
425 * configuration name. */
426 if (!cl.lockfile[0]) {
427 snprintf(cl.lockfile, sizeof(cl.lockfile) - 1,
428 "%s/%s.pid", BOOTH_RUN_DIR, (*conf)->name);
429 }
430
431 out:
432 return rv;
433 }
434
435 static int setup_transport(void)
436 {
437 int rv;
438
439 rv = transport()->init(message_recv);
440 if (rv < 0) {
441 log_error("failed to init booth_transport %s", transport()->name);
442 goto out;
443 }
444
445 rv = booth_transport[TCP].init(NULL);
446 if (rv < 0) {
447 log_error("failed to init booth_transport[TCP]");
448 goto out;
449 }
450
451 out:
452 return rv;
453 }
454
455
456 static int write_daemon_state(int fd, int state)
457 {
458 char *buffer;
459 int rv, size;
460
461 rv = asprintf(&buffer, "booth_pid=%d booth_state=%s booth_type=%s "
462 "booth_cfg_name='%s' booth_id=%d "
463 "booth_addr_string='%s' booth_port=%d\n",
464 getpid(), state_string(state), type_to_string(local->type),
465 booth_conf->name, get_local_id(), site_string(local),
466 site_port(local));
467
468 if (rv < 0) {
469 log_error("Buffer write failed in write_daemon_state().");
470 return -1;
471 }
472
473 size = rv;
474
475 rv = ftruncate(fd, 0);
476 if (rv < 0) {
477 log_error("lockfile %s truncate error %d: %s",
478 cl.lockfile, errno, strerror(errno));
479 free(buffer);
480 return rv;
481 }
482
483 rv = lseek(fd, 0, SEEK_SET);
484 if (rv < 0) {
485 log_error("lseek set fd(%d) offset to 0 error, return(%d), message(%s)",
486 fd, rv, strerror(errno));
487 free(buffer);
488 return -1;
489 }
490
491 rv = write(fd, buffer, size);
492
493 if (rv != size) {
494 log_error("write to fd(%d, %d) returned %d, errno %d, message(%s)",
495 fd, size, rv, errno, strerror(errno));
496 free(buffer);
497 return -1;
498 }
499
500 free(buffer);
501 return 0;
502 }
503
504 static int process_signals(void)
505 {
506 if (sig_exit_handler_called) {
507 log_info("caught signal %d", sig_exit_handler_sig);
508 return 1;
509 }
510 if (sig_usr1_handler_called) {
511 sig_usr1_handler_called = 0;
512 tickets_log_info(booth_conf);
513 }
514 if (sig_chld_handler_called) {
515 sig_chld_handler_called = 0;
516 wait_child(SIGCHLD);
517 }
518
519 return 0;
520 }
521
522 static int loop(int fd)
523 {
524 workfn_t workfn;
525 void (*deadfn) (int ci);
526 int rv, i;
527
528 rv = setup_transport();
529 if (rv < 0)
530 goto fail;
531
532 rv = setup_ticket(booth_conf);
533 if (rv < 0) {
534 goto fail;
535 }
536
537 rv = write_daemon_state(fd, BOOTHD_STARTED);
538 if (rv != 0) {
539 log_error("write daemon state %d to lockfile error %s: %s",
540 BOOTHD_STARTED, cl.lockfile, strerror(errno));
541 goto fail;
542 }
543
544 log_info("BOOTH %s daemon started, node id is 0x%08X (%d).",
545 type_to_string(local->type),
546 local->site_id, local->site_id);
547
548 while (1) {
549 rv = poll(pollfds, client_maxi + 1, poll_timeout);
550 if (rv == -1 && errno == EINTR)
551 continue;
552 if (rv < 0) {
553 log_error("poll failed: %s (%d)", strerror(errno), errno);
554 goto fail;
555 }
556
557 for (i = 0; i <= client_maxi; i++) {
558 if (clients[i].fd < 0)
559 continue;
560
561 if (pollfds[i].revents & POLLIN) {
562 workfn = clients[i].workfn;
563 if (workfn) {
564 workfn(booth_conf, i);
565 }
566 }
567 if (pollfds[i].revents &
568 (POLLERR | POLLHUP | POLLNVAL)) {
569 deadfn = clients[i].deadfn;
570 if (deadfn)
571 deadfn(i);
572 }
573 }
574
575 process_tickets(booth_conf);
576
577 if (process_signals() != 0) {
578 return 0;
579 }
580 }
581
582 return 0;
583
584 fail:
585 return -1;
586 }
587
588
589 static int test_reply(cmd_result_t reply_code, cmd_request_t cmd)
590 {
591 int rv = 0;
592 const char *op_str = NULL;
593
594 if (cmd == CMD_GRANT)
595 op_str = "grant";
596 else if (cmd == CMD_REVOKE)
597 op_str = "revoke";
598 else if (cmd == CMD_LIST)
599 op_str = "list";
600 else if (cmd == CMD_PEERS)
601 op_str = "peers";
602 else {
603 log_error("internal error reading reply result!");
604 return -1;
605 }
606
607 switch (reply_code) {
608 case RLT_OVERGRANT:
609 log_info("You're granting a granted ticket. "
610 "If you wanted to migrate a ticket, "
611 "use revoke first, then use grant.");
612 rv = -1;
613 break;
614
615 case RLT_TICKET_IDLE:
616 log_info("ticket is not owned");
617 rv = 0;
618 break;
619
620 case RLT_ASYNC:
621 log_info("%s command sent, result will be returned "
622 "asynchronously. Please use \"booth list\" to "
623 "see the outcome.", op_str);
624 rv = 0;
625 break;
626
627 case RLT_CIB_PENDING:
628 log_info("%s succeeded (CIB commit pending)", op_str);
629 /* wait for the CIB commit? */
630 rv = (cl.options & OPT_WAIT_COMMIT) ? 3 : 0;
631 break;
632
633 case RLT_MORE:
634 rv = 2;
635 break;
636
637 case RLT_SYNC_SUCC:
638 case RLT_SUCCESS:
639 if (cmd != CMD_LIST && cmd != CMD_PEERS)
640 log_info("%s succeeded!", op_str);
641 rv = 0;
642 break;
643
644 case RLT_SYNC_FAIL:
645 log_info("%s failed!", op_str);
646 rv = -1;
647 break;
648
649 case RLT_INVALID_ARG:
650 log_error("ticket \"%s\" does not exist",
651 cl.msg.ticket.id);
652 rv = -1;
653 break;
654
655 case RLT_AUTH:
656 log_error("authentication error");
657 rv = -1;
658 break;
659
660 case RLT_EXT_FAILED:
661 log_error("before-acquire-handler for ticket \"%s\" failed, grant denied",
662 cl.msg.ticket.id);
663 rv = -1;
664 break;
665
666 case RLT_ATTR_PREREQ:
667 log_error("attr-prereq for ticket \"%s\" failed, grant denied",
668 cl.msg.ticket.id);
669 rv = -1;
670 break;
671
672 case RLT_REDIRECT:
673 /* talk to another site */
674 rv = 1;
675 break;
676
677 default:
678 log_error("got an error code: %x", rv);
679 rv = -1;
680 }
681 return rv;
682 }
683
684 static int query_get_string_answer(cmd_request_t cmd)
685 {
686 struct booth_site *site;
687 struct boothc_hdr_msg reply;
688 struct boothc_header *header;
689 char *data;
690 int data_len;
691 int rv;
692 struct booth_transport const *tpt;
693 int (*test_reply_f) (cmd_result_t reply_code, cmd_request_t cmd);
694 size_t msg_size;
695 void *request;
696
697 if (cl.type == GEOSTORE) {
698 test_reply_f = test_attr_reply;
699 msg_size = sizeof(cl.attr_msg);
700 request = &cl.attr_msg;
701 } else {
702 test_reply_f = test_reply;
703 msg_size = sizeof(cl.msg);
704 request = &cl.msg;
705 }
706 header = (struct boothc_header *)request;
707 data = NULL;
708
709 init_header(header, cmd, 0, cl.options, 0, 0, msg_size);
710
711 if (!*cl.site)
712 site = local;
713 else if (!find_site_by_name(booth_conf, cl.site, &site, 1)) {
714 log_error("cannot find site \"%s\"", cl.site);
715 rv = ENOENT;
716 goto out;
717 }
718
719 tpt = booth_transport + TCP;
720 rv = tpt->open(site);
721 if (rv < 0)
722 goto out_close;
723
724 rv = tpt->send(booth_conf, site, request, msg_size);
725 if (rv < 0)
726 goto out_close;
727
728 rv = tpt->recv_auth(booth_conf, site, &reply, sizeof(reply));
729 if (rv < 0)
730 goto out_close;
731
732 data_len = ntohl(reply.header.length) - rv;
733
734 /* no attribute, or no ticket found */
735 if (!data_len) {
736 goto out_test_reply;
737 }
738
739 data = malloc(data_len+1);
740 if (!data) {
741 rv = -ENOMEM;
742 goto out_close;
743 }
744 rv = tpt->recv(site, data, data_len);
745 if (rv < 0)
746 goto out_close;
747
748 *(data + data_len) = '\0';
749 (void)fputs(data, stdout);
750 fflush(stdout);
751
752 out_test_reply:
753 rv = test_reply_f(ntohl(reply.header.result), cmd);
754 out_close:
755 tpt->close(site);
756 out:
757 if (data)
758 free(data);
759 return rv;
760 }
761
762
763 static int do_command(cmd_request_t cmd)
764 {
765 struct booth_site *site;
766 struct boothc_ticket_msg reply;
767 struct booth_transport const *tpt;
768 uint32_t leader_id;
769 int rv;
770 int reply_cnt = 0, msg_logged = 0;
771 const char *op_str = "";
772
773 if (cmd == CMD_GRANT)
774 op_str = "grant";
775 else if (cmd == CMD_REVOKE)
776 op_str = "revoke";
777
778 rv = -1;
779 site = NULL;
780
781 /* Always use TCP for client - at least for now. */
782 tpt = booth_transport + TCP;
783
784 if (!*cl.site)
785 site = local;
786 else {
787 if (!find_site_by_name(booth_conf, cl.site, &site, 1)) {
788 log_error("Site \"%s\" not configured.", cl.site);
789 goto out_close;
790 }
791 }
792
793 if (site->type == ARBITRATOR) {
794 if (site == local) {
795 log_error("We're just an arbitrator, cannot grant/revoke tickets here.");
796 } else {
797 log_error("%s is just an arbitrator, cannot grant/revoke tickets there.", cl.site);
798 }
799 goto out_close;
800 }
801
802 assert(site->type == SITE);
803
804 /* We don't check for existence of ticket, so that asking can be
805 * done without local configuration, too.
806 * Although, that means that the UDP port has to be specified, too. */
807 if (!cl.msg.ticket.id[0]) {
808 /* If the loaded configuration has only a single ticket defined, use that. */
809 if (booth_conf->ticket_count == 1) {
810 strncpy(cl.msg.ticket.id, booth_conf->ticket[0].name,
811 sizeof(cl.msg.ticket.id));
812 } else {
813 log_error("No ticket given.");
814 goto out_close;
815 }
816 }
817
818 redirect:
819 init_header(&cl.msg.header, cmd, 0, cl.options, 0, 0, sizeof(cl.msg));
820
821 rv = tpt->open(site);
822 if (rv < 0)
823 goto out_close;
824
825 rv = tpt->send(booth_conf, site, &cl.msg, sendmsglen(&cl.msg));
826 if (rv < 0) {
827 goto out_close;
828 }
829
830 read_more:
831 rv = tpt->recv_auth(booth_conf, site, &reply, sizeof(reply));
832 if (rv < 0) {
833 /* print any errors depending on the code sent by the
834 * server */
835 (void)test_reply(ntohl(reply.header.result), cmd);
836 goto out_close;
837 }
838
839 rv = test_reply(ntohl(reply.header.result), cmd);
840 if (rv == 1) {
841 tpt->close(site);
842 leader_id = ntohl(reply.ticket.leader);
843 if (!find_site_by_id(booth_conf, leader_id, &site)) {
844 log_error("Message with unknown redirect site %x received", leader_id);
845 rv = -1;
846 goto out_close;
847 }
848 goto redirect;
849 } else if (rv == 2 || rv == 3) {
850 /* the server has more to say */
851 /* don't wait too long */
852 if (reply_cnt > 1 && !(cl.options & OPT_WAIT)) {
853 rv = 0;
854 log_info("Giving up on waiting for the definite result. "
855 "Please use \"booth list\" later to "
856 "see the outcome.");
857 goto out_close;
858 }
859 if (reply_cnt == 0) {
860 log_info("%s request sent, "
861 "waiting for the result ...", op_str);
862 msg_logged++;
863 } else if (rv == 3 && msg_logged < 2) {
864 log_info("waiting for the CIB commit ...");
865 msg_logged++;
866 }
867 reply_cnt++;
868 goto read_more;
869 }
870
871 out_close:
872 if (site)
873 tpt->close(site);
874 return rv;
875 }
876
877
878
879 static int _lockfile(int mode, int *fdp, pid_t *locked_by)
880 {
881 struct flock lock;
882 int fd, rv;
883
884
885 /* After reboot the directory may not yet exist.
886 * Try to create it, but ignore errors. */
887 if (strncmp(cl.lockfile, BOOTH_RUN_DIR,
888 strlen(BOOTH_RUN_DIR)) == 0)
889 (void)mkdir(BOOTH_RUN_DIR, 0775);
890
891
892 if (locked_by)
893 *locked_by = 0;
894
895 *fdp = -1;
896 fd = open(cl.lockfile, mode, 0664);
897 if (fd < 0)
898 return errno;
899
900 *fdp = fd;
901
902 lock.l_type = F_WRLCK;
903 lock.l_start = 0;
904 lock.l_whence = SEEK_SET;
905 lock.l_len = 0;
906 lock.l_pid = 0;
907
908
909 if (fcntl(fd, F_SETLK, &lock) == 0)
910 return 0;
911
912 rv = errno;
913
914 if (locked_by)
915 if (fcntl(fd, F_GETLK, &lock) == 0)
916 *locked_by = lock.l_pid;
917
918 return rv;
919 }
920
921
922 static inline int is_root(void)
923 {
924 return geteuid() == 0;
925 }
926
927
928 static int create_lockfile(void)
929 {
930 int rv, fd;
931
932 fd = -1;
933 rv = _lockfile(O_CREAT | O_WRONLY, &fd, NULL);
934
935 if (fd == -1) {
936 log_error("lockfile %s open error %d: %s",
937 cl.lockfile, rv, strerror(rv));
938 return -1;
939 }
940
941 if (rv < 0) {
942 log_error("lockfile %s setlk error %d: %s",
943 cl.lockfile, rv, strerror(rv));
944 goto fail;
945 }
946
947 rv = write_daemon_state(fd, BOOTHD_STARTING);
948 if (rv != 0) {
949 log_error("write daemon state %d to lockfile error %s: %s",
950 BOOTHD_STARTING, cl.lockfile, strerror(errno));
951 goto fail;
952 }
953
954 if (is_root()) {
955 if (fchown(fd, booth_conf->uid, booth_conf->gid) < 0)
956 log_error("fchown() on lockfile said %d: %s",
957 errno, strerror(errno));
958 }
959
960 return fd;
961
962 fail:
963 close(fd);
964 return -1;
965 }
966
967 static void unlink_lockfile(int fd)
968 {
969 unlink(cl.lockfile);
970 close(fd);
971 }
972
973 static void print_usage(void)
974 {
975 printf(
976 "Usage:\n"
977 " booth list [options]\n"
978 " booth {grant|revoke} [options] <ticket>\n"
979 " booth status [options]\n"
980 "\n"
981 " list: List all tickets\n"
982 " grant: Grant ticket to site\n"
983 " revoke: Revoke ticket\n"
984 "\n"
985 "Options:\n"
986 " -c FILE Specify config file [default " BOOTH_DEFAULT_CONF "]\n"
987 " Can be a path or just a name without \".conf\" suffix\n"
988 " -s <site> Connect/grant to a different site\n"
989 " -F Try to grant the ticket immediately\n"
990 " even if not all sites are reachable\n"
991 " For manual tickets:\n"
992 " grant a manual ticket even if it has been already granted\n"
993 " -w Wait forever for the outcome of the request\n"
994 " -C Wait until the ticket is committed to the CIB (grant only)\n"
995 " -h Print this help\n"
996 "\n"
997 "Examples:\n"
998 "\n"
999 " # booth list (list tickets)\n"
1000 " # booth grant ticket-A (grant ticket here)\n"
1001 " # booth grant -s 10.121.8.183 ticket-A (grant ticket to site 10.121.8.183)\n"
1002 " # booth revoke ticket-A (revoke ticket)\n"
1003 "\n"
1004 "See the booth(8) man page for more details.\n"
1005 );
1006 }
1007
1008 #define OPTION_STRING "c:Dl:t:s:FhSwC"
1009 #define ATTR_OPTION_STRING "c:Dt:s:h"
1010
1011 void safe_copy(char *dest, char *value, size_t buflen, const char *description) {
1012 int content_len = buflen - 1;
1013
1014 if (strlen(value) >= content_len) {
1015 fprintf(stderr, "'%s' exceeds maximum %s length of %d\n",
1016 value, description, content_len);
1017 exit(EXIT_FAILURE);
1018 }
1019 strncpy(dest, value, content_len);
1020 dest[content_len] = 0;
1021 }
1022
1023 static int host_convert(char *hostname, char *ip_str, size_t ip_size)
1024 {
1025 struct addrinfo *result = NULL, hints = {0};
1026 int re = -1;
1027
1028 memset(&hints, 0, sizeof(hints));
1029 hints.ai_family = AF_INET;
1030 hints.ai_socktype = SOCK_DGRAM;
1031
1032 re = getaddrinfo(hostname, NULL, &hints, &result);
1033
1034 if (re == 0) {
1035 struct in_addr addr = ((struct sockaddr_in *)result->ai_addr)->sin_addr;
1036 const char *re_ntop = inet_ntop(AF_INET, &addr, ip_str, ip_size);
1037 if (re_ntop == NULL) {
1038 re = -1;
1039 }
1040 }
1041
1042 freeaddrinfo(result);
1043 return re;
1044 }
1045
1046 #define cparg(dest, descr) do { \
1047 if (optind >= argc) \
1048 goto missingarg; \
1049 safe_copy(dest, argv[optind], sizeof(dest), descr); \
1050 optind++; \
1051 } while(0)
1052
1053 static int read_arguments(int argc, char **argv)
1054 {
1055 int optchar;
1056 char *arg1 = argv[1];
1057 char *op = NULL;
1058 char *cp;
1059 const char *opt_string = OPTION_STRING;
1060 char site_arg[INET_ADDRSTRLEN] = {0};
1061 int left;
1062
1063 cl.type = 0;
1064 if ((cp = strstr(argv[0], ATTR_PROG)) &&
1065 !strcmp(cp, ATTR_PROG)) {
1066 cl.type = GEOSTORE;
1067 op = argv[1];
1068 optind = 2;
1069 opt_string = ATTR_OPTION_STRING;
1070 } else if (argc > 1 && (strcmp(arg1, "arbitrator") == 0 ||
1071 strcmp(arg1, "site") == 0 ||
1072 strcmp(arg1, "start") == 0 ||
1073 strcmp(arg1, "daemon") == 0)) {
1074 cl.type = DAEMON;
1075 optind = 2;
1076 } else if (argc > 1 && (strcmp(arg1, "status") == 0)) {
1077 cl.type = STATUS;
1078 optind = 2;
1079 } else if (argc > 1 && (strcmp(arg1, "client") == 0)) {
1080 cl.type = CLIENT;
1081 if (argc < 3) {
1082 print_usage();
1083 exit(EXIT_FAILURE);
1084 }
1085 op = argv[2];
1086 optind = 3;
1087 }
1088 if (!cl.type) {
1089 cl.type = CLIENT;
1090 op = argv[1];
1091 optind = 2;
1092 }
1093
1094 if (argc < 2 || !strcmp(arg1, "help") || !strcmp(arg1, "--help") ||
1095 !strcmp(arg1, "-h")) {
1096 if (cl.type == GEOSTORE)
1097 print_geostore_usage();
1098 else
1099 print_usage();
1100 exit(EXIT_SUCCESS);
1101 }
1102
1103 if (!strcmp(arg1, "version") || !strcmp(arg1, "--version") ||
1104 !strcmp(arg1, "-V")) {
1105 printf("%s %s\n", argv[0], RELEASE_STR);
1106 exit(EXIT_SUCCESS);
1107 }
1108
1109 if (cl.type == CLIENT) {
1110 if (!strcmp(op, "list"))
1111 cl.op = CMD_LIST;
1112 else if (!strcmp(op, "grant"))
1113 cl.op = CMD_GRANT;
1114 else if (!strcmp(op, "revoke"))
1115 cl.op = CMD_REVOKE;
1116 else if (!strcmp(op, "peers"))
1117 cl.op = CMD_PEERS;
1118 else {
1119 fprintf(stderr, "client operation \"%s\" is unknown\n",
1120 op);
1121 exit(EXIT_FAILURE);
1122 }
1123 } else if (cl.type == GEOSTORE) {
1124 if (!strcmp(op, "list"))
1125 cl.op = ATTR_LIST;
1126 else if (!strcmp(op, "set"))
1127 cl.op = ATTR_SET;
1128 else if (!strcmp(op, "get"))
1129 cl.op = ATTR_GET;
1130 else if (!strcmp(op, "delete"))
1131 cl.op = ATTR_DEL;
1132 else {
1133 fprintf(stderr, "attribute operation \"%s\" is unknown\n",
1134 op);
1135 exit(EXIT_FAILURE);
1136 }
1137 }
1138
1139 while (optind < argc) {
1140 optchar = getopt(argc, argv, opt_string);
1141
1142 switch (optchar) {
1143 case 'c':
1144 if (strchr(optarg, '/')) {
1145 safe_copy(cl.configfile, optarg,
1146 sizeof(cl.configfile), "config file");
1147 } else {
1148 /* If no "/" in there, use with default directory. */
1149 strcpy(cl.configfile, BOOTH_DEFAULT_CONF_DIR);
1150 cp = cl.configfile + strlen(BOOTH_DEFAULT_CONF_DIR);
1151 assert(cp > cl.configfile);
1152 assert(*(cp-1) == '/');
1153
1154 /* Write at the \0, ie. after the "/" */
1155 safe_copy(cp, optarg,
1156 (sizeof(cl.configfile) -
1157 (cp - cl.configfile) -
1158 strlen(BOOTH_DEFAULT_CONF_EXT)),
1159 "config name");
1160
1161 /* If no extension, append ".conf".
1162 * Space is available, see -strlen() above. */
1163 if (!strchr(cp, '.'))
1164 strcat(cp, BOOTH_DEFAULT_CONF_EXT);
1165 }
1166 break;
1167
1168 case 'D':
1169 debug_level++;
1170 break;
1171
1172 case 'S':
1173 daemonize = 0;
1174 enable_stderr = 1;
1175 break;
1176
1177 case 'l':
1178 safe_copy(cl.lockfile, optarg, sizeof(cl.lockfile), "lock file");
1179 break;
1180 case 't':
1181 if (cl.op == CMD_GRANT || cl.op == CMD_REVOKE) {
1182 safe_copy(cl.msg.ticket.id, optarg,
1183 sizeof(cl.msg.ticket.id), "ticket name");
1184 } else if (cl.type == GEOSTORE) {
1185 safe_copy(cl.attr_msg.attr.tkt_id, optarg,
1186 sizeof(cl.attr_msg.attr.tkt_id), "ticket name");
1187 } else {
1188 print_usage();
1189 exit(EXIT_FAILURE);
1190 }
1191 break;
1192
1193 case 's':
1194 /* For testing and debugging: allow "-s site" also for
1195 * daemon start, so that the address that should be used
1196 * can be set manually.
1197 * This makes it easier to start multiple processes
1198 * on one machine. */
1199 if (cl.type == CLIENT || cl.type == GEOSTORE ||
1200 (cl.type == DAEMON && debug_level)) {
1201 if (strcmp(optarg, OTHER_SITE) &&
1202 host_convert(optarg, site_arg, INET_ADDRSTRLEN) == 0) {
1203 safe_copy(cl.site, site_arg, sizeof(cl.site), "site name");
1204 } else {
1205 safe_copy(cl.site, optarg, sizeof(cl.site), "site name");
1206 }
1207 } else {
1208 log_error("\"-s\" not allowed in daemon mode.");
1209 exit(EXIT_FAILURE);
1210 }
1211 break;
1212
1213 case 'F':
1214 if (cl.type != CLIENT || cl.op != CMD_GRANT) {
1215 log_error("use \"-F\" only for client grant");
1216 exit(EXIT_FAILURE);
1217 }
1218 cl.options |= OPT_IMMEDIATE;
1219 break;
1220
1221 case 'w':
1222 if (cl.type != CLIENT ||
1223 (cl.op != CMD_GRANT && cl.op != CMD_REVOKE)) {
1224 log_error("use \"-w\" only for grant and revoke");
1225 exit(EXIT_FAILURE);
1226 }
1227 cl.options |= OPT_WAIT;
1228 break;
1229
1230 case 'C':
1231 if (cl.type != CLIENT || cl.op != CMD_GRANT) {
1232 log_error("use \"-C\" only for grant");
1233 exit(EXIT_FAILURE);
1234 }
1235 cl.options |= OPT_WAIT | OPT_WAIT_COMMIT;
1236 break;
1237
1238 case 'h':
1239 if (cl.type == GEOSTORE)
1240 print_geostore_usage();
1241 else
1242 print_usage();
1243 exit(EXIT_SUCCESS);
1244 break;
1245
1246 case ':':
1247 case '?':
1248 fprintf(stderr, "Please use '-h' for usage.\n");
1249 exit(EXIT_FAILURE);
1250 break;
1251
1252 case -1:
1253 /* No more parameters on cmdline, only arguments. */
1254 goto extra_args;
1255
1256 default:
1257 goto unknown;
1258 };
1259 }
1260
1261 return 0;
1262
1263 extra_args:
1264 if (cl.type == CLIENT && !cl.msg.ticket.id[0]) {
1265 cparg(cl.msg.ticket.id, "ticket name");
1266 } else if (cl.type == GEOSTORE) {
1267 if (cl.op != ATTR_LIST) {
1268 cparg(cl.attr_msg.attr.name, "attribute name");
1269 }
1270 if (cl.op == ATTR_SET) {
1271 cparg(cl.attr_msg.attr.val, "attribute value");
1272 }
1273 }
1274
1275 if (optind == argc)
1276 return 0;
1277
1278
1279 left = argc - optind;
1280 fprintf(stderr, "Superfluous argument%s: %s%s\n",
1281 left == 1 ? "" : "s",
1282 argv[optind],
1283 left == 1 ? "" : "...");
1284 exit(EXIT_FAILURE);
1285
1286 unknown:
1287 fprintf(stderr, "unknown option: %s\n", argv[optind]);
1288 exit(EXIT_FAILURE);
1289
1290 missingarg:
1291 fprintf(stderr, "not enough arguments\n");
1292 exit(EXIT_FAILURE);
1293 }
1294
1295
1296 static void set_scheduler(void)
1297 {
1298 struct sched_param sched_param;
1299 struct rlimit rlimit;
1300 int rv;
1301
1302 rlimit.rlim_cur = RLIM_INFINITY;
1303 rlimit.rlim_max = RLIM_INFINITY;
1304 rv = setrlimit(RLIMIT_MEMLOCK, &rlimit);
1305 if (rv < 0) {
1306 log_error("setrlimit failed");
1307 } else {
1308 rv = mlockall(MCL_CURRENT | MCL_FUTURE);
1309 if (rv < 0) {
1310 log_error("mlockall failed");
1311 }
1312 }
1313
1314 rv = sched_get_priority_max(SCHED_RR);
1315 if (rv != -1) {
1316 sched_param.sched_priority = rv;
1317 rv = sched_setscheduler(0, SCHED_RR, &sched_param);
1318 if (rv == -1)
1319 log_error("could not set SCHED_RR priority %d: %s (%d)",
1320 sched_param.sched_priority,
1321 strerror(errno), errno);
1322 } else {
1323 log_error("could not get maximum scheduler priority err %d",
1324 errno);
1325 }
1326 }
1327
1328 static int set_procfs_val(const char *path, const char *val)
1329 {
1330 int rc = -1;
1331 FILE *fp = fopen(path, "w");
1332
1333 if (fp) {
1334 if (fprintf(fp, "%s", val) > 0)
1335 rc = 0;
1336 fclose(fp);
1337 }
1338 return rc;
1339 }
1340
1341 static int do_status(struct booth_config **conf, int type)
1342 {
1343 pid_t pid;
1344 int rv, status_lock_fd, ret;
1345 const char *reason = NULL;
1346 char lockfile_data[1024], *cp;
1347
1348 assert(conf != NULL);
1349
1350 ret = PCMK_OCF_NOT_RUNNING;
1351
1352 rv = setup_config(conf, type);
1353 if (rv) {
1354 reason = "Error reading configuration.";
1355 ret = PCMK_OCF_UNKNOWN_ERROR;
1356 goto quit;
1357 }
1358
1359
1360 if (!local) {
1361 reason = "No Service IP active here.";
1362 goto quit;
1363 }
1364
1365
1366 rv = _lockfile(O_RDWR, &status_lock_fd, &pid);
1367 if (status_lock_fd == -1) {
1368 reason = "No PID file.";
1369 goto quit;
1370 }
1371 if (rv == 0) {
1372 close(status_lock_fd);
1373 reason = "PID file not locked.";
1374 goto quit;
1375 }
1376 if (pid) {
1377 fprintf(stdout, "booth_lockpid=%d ", pid);
1378 fflush(stdout);
1379 }
1380
1381 rv = read(status_lock_fd, lockfile_data, sizeof(lockfile_data) - 1);
1382 if (rv < 4) {
1383 close(status_lock_fd);
1384 reason = "Cannot read lockfile data.";
1385 ret = PCMK_LSB_UNKNOWN_ERROR;
1386 goto quit;
1387 }
1388 lockfile_data[rv] = 0;
1389
1390 close(status_lock_fd);
1391
1392
1393 /* Make sure it's only a single line */
1394 cp = strchr(lockfile_data, '\r');
1395 if (cp)
1396 *cp = 0;
1397 cp = strchr(lockfile_data, '\n');
1398 if (cp)
1399 *cp = 0;
1400
1401
1402
1403 rv = setup_tcp_listener(1);
1404 if (rv == 0) {
1405 reason = "TCP port not in use.";
1406 goto quit;
1407 }
1408
1409
1410 fprintf(stdout, "booth_lockfile='%s' %s\n",
1411 cl.lockfile, lockfile_data);
1412 if (!daemonize)
1413 fprintf(stderr, "Booth at %s port %d seems to be running.\n",
1414 local->addr_string, site_port(local));
1415 return 0;
1416
1417
1418 quit:
1419 log_debug("not running: %s", reason);
1420 /* Ie. "DEBUG" */
1421 if (!daemonize)
1422 fprintf(stderr, "not running: %s\n", reason);
1423 return ret;
1424 }
1425
1426
1427 static int limit_this_process(void)
1428 {
1429 int rv;
1430 if (!is_root())
1431 return 0;
1432
1433 if (setregid(booth_conf->gid, booth_conf->gid) < 0) {
1434 rv = errno;
1435 log_error("setregid() didn't work: %s", strerror(rv));
1436 return rv;
1437 }
1438
1439 if (setreuid(booth_conf->uid, booth_conf->uid) < 0) {
1440 rv = errno;
1441 log_error("setreuid() didn't work: %s", strerror(rv));
1442 return rv;
1443 }
1444
1445 return 0;
1446 }
1447
1448 static int lock_fd = -1;
1449
1450 static void server_exit(void)
1451 {
1452 int rv;
1453
1454 if (lock_fd >= 0) {
1455 /* We might not be able to delete it, but at least
1456 * make it empty. */
1457 rv = ftruncate(lock_fd, 0);
1458 (void)rv;
1459 unlink_lockfile(lock_fd);
1460 }
1461 log_info("exiting");
1462 }
1463
1464 static void sig_exit_handler(int sig)
1465 {
1466 sig_exit_handler_sig = sig;
1467 sig_exit_handler_called = 1;
1468 }
1469
1470 static void sig_usr1_handler(int sig)
1471 {
1472 sig_usr1_handler_called = 1;
1473 }
1474
1475 static void sig_chld_handler(int sig)
1476 {
1477 sig_chld_handler_called = 1;
1478 }
1479
1480 static int do_server(struct booth_config **conf, int type)
1481 {
1482 int rv = -1;
1483 static char log_ent[128] = DAEMON_NAME "-";
1484
1485 assert(conf != NULL);
1486
1487 rv = setup_config(conf, type);
1488 if (rv < 0) {
1489 return rv;
1490 }
1491
1492 if (!local) {
1493 log_error("Cannot find myself in the configuration.");
1494 exit(EXIT_FAILURE);
1495 }
1496
1497 if (daemonize) {
1498 if (daemon(0, 0) < 0) {
1499 perror("daemon error");
1500 exit(EXIT_FAILURE);
1501 }
1502 }
1503
1504 /*
1505 * Register signal and exit handler
1506 */
1507 signal(SIGUSR1, (__sighandler_t)sig_usr1_handler);
1508 signal(SIGTERM, (__sighandler_t)sig_exit_handler);
1509 signal(SIGINT, (__sighandler_t)sig_exit_handler);
1510 /* we'll handle errors there and then */
1511 signal(SIGPIPE, SIG_IGN);
1512
1513 atexit(server_exit);
1514
1515 /* The lockfile must be written to _after_ the call to daemon(), so
1516 * that the lockfile contains the pid of the daemon, not the parent. */
1517 lock_fd = create_lockfile();
1518 if (lock_fd < 0)
1519 return lock_fd;
1520
1521 strcat(log_ent, type_to_string(local->type));
1522 cl_log_set_entity(log_ent);
1523 cl_log_enable_stderr(enable_stderr ? TRUE : FALSE);
1524 cl_log_set_facility(HA_LOG_FACILITY);
1525 cl_inherit_logging_environment(0);
1526
1527 log_info("BOOTH %s %s daemon is starting",
1528 type_to_string(local->type), RELEASE_STR);
1529
1530
1531 set_scheduler();
1532 /* we don't want to be killed by the OOM-killer */
1533 if (set_procfs_val("/proc/self/oom_score_adj", "-999"))
1534 (void)set_procfs_val("/proc/self/oom_adj", "-16");
1535 set_proc_title("%s %s %s for [%s]:%d",
1536 DAEMON_NAME, cl.configfile, type_to_string(local->type),
1537 local->addr_string, site_port(local));
1538
1539 rv = limit_this_process();
1540 if (rv)
1541 return rv;
1542
1543 #ifdef COREDUMP_NURSING
1544 if (cl_enable_coredumps(TRUE) < 0){
1545 log_error("enabling core dump failed");
1546 }
1547 cl_cdtocoredir();
1548 prctl(PR_SET_DUMPABLE, (unsigned long)TRUE, 0UL, 0UL, 0UL);
1549 #else
1550 if (chdir(BOOTH_CORE_DIR) < 0) {
1551 log_error("cannot change working directory to %s", BOOTH_CORE_DIR);
1552 }
1553 #endif
1554
1555 signal(SIGCHLD, (__sighandler_t)sig_chld_handler);
1556 rv = loop(lock_fd);
1557
1558 return rv;
1559 }
1560
1561 static int do_client(struct booth_config **conf)
1562 {
1563 int rv;
1564
1565 rv = setup_config(conf, CLIENT);
1566 if (rv < 0) {
1567 log_error("cannot read config");
1568 goto out;
1569 }
1570
1571 switch (cl.op) {
1572 case CMD_LIST:
1573 case CMD_PEERS:
1574 rv = query_get_string_answer(cl.op);
1575 break;
1576
1577 case CMD_GRANT:
1578 case CMD_REVOKE:
1579 rv = do_command(cl.op);
1580 break;
1581 }
1582
1583 out:
1584 return rv;
1585 }
1586
1587 static int do_attr(struct booth_config **conf)
1588 {
1589 int rv = -1;
1590
|
(1) Event cond_true: |
Condition "conf != NULL", taking true branch. |
|
(2) Event if_fallthrough: |
Falling through to end of if statement. |
|
(3) Event if_end: |
End of if statement. |
1591 assert(conf != NULL);
1592
1593 rv = setup_config(conf, GEOSTORE);
|
(4) Event cond_false: |
Condition "rv < 0", taking false branch. |
1594 if (rv < 0) {
1595 log_error("cannot read config");
1596 goto out;
|
(5) Event if_end: |
End of if statement. |
1597 }
1598
1599 /* We don't check for existence of ticket, so that asking can be
1600 * done without local configuration, too.
1601 * Although, that means that the UDP port has to be specified, too. */
|
(6) Event cond_true: |
Condition "!cl.attr_msg.attr.tkt_id[0]", taking true branch. |
1602 if (!cl.attr_msg.attr.tkt_id[0]) {
1603 /* If the loaded configuration has only a single ticket defined, use that. */
|
(7) Event cond_true: |
Condition "(*conf)->ticket_count == 1", taking true branch. |
1604 if ((*conf)->ticket_count == 1) {
|
(8) Event buffer_size_warning: |
Calling "strncpy" with a maximum size argument of 64 bytes on destination array "cl.attr_msg.attr.tkt_id" of size 64 bytes might leave the destination string unterminated. |
1605 strncpy(cl.attr_msg.attr.tkt_id,
1606 (*conf)->ticket[0].name,
1607 sizeof(cl.attr_msg.attr.tkt_id));
|
(9) Event if_fallthrough: |
Falling through to end of if statement. |
1608 } else {
1609 rv = 1;
1610 log_error("No ticket given.");
1611 goto out;
|
(10) Event if_end: |
End of if statement. |
1612 }
1613 }
1614
|
(11) Event switch: |
Switch case value "ATTR_LIST". |
1615 switch (cl.op) {
|
(12) Event switch_case: |
Reached case "ATTR_LIST". |
1616 case ATTR_LIST:
1617 case ATTR_GET:
1618 rv = query_get_string_answer(cl.op);
|
(13) Event break: |
Breaking from switch. |
1619 break;
1620
1621 case ATTR_SET:
1622 case ATTR_DEL:
1623 rv = do_attr_command(booth_conf, cl.op);
1624 break;
|
(14) Event switch_end: |
Reached end of switch. |
1625 }
1626
1627 out:
1628 return rv;
1629 }
1630
1631 int main(int argc, char *argv[], char *envp[])
1632 {
1633 int rv;
1634 const char *cp;
1635 #ifdef LOGGING_LIBQB
1636 enum qb_log_target_slot i;
1637 #endif
1638
1639 init_set_proc_title(argc, argv, envp);
1640 get_time(&start_time);
1641
1642 memset(&cl, 0, sizeof(cl));
1643 strncpy(cl.configfile,
1644 BOOTH_DEFAULT_CONF, BOOTH_PATH_LEN - 1);
1645 cl.lockfile[0] = 0;
1646 debug_level = 0;
1647
1648
1649 cp = ((cp = strstr(argv[0], ATTR_PROG)) && !strcmp(cp, ATTR_PROG)
1650 ? ATTR_PROG
1651 : "booth");
1652 #ifndef LOGGING_LIBQB
1653 cl_log_set_entity(cp);
1654 #else
1655 qb_log_init(cp, LOG_USER, LOG_DEBUG); /* prio driven by debug_level */
1656 for (i = QB_LOG_TARGET_START; i < QB_LOG_TARGET_MAX; i++) {
1657 if (i == QB_LOG_SYSLOG || i == QB_LOG_BLACKBOX)
1658 continue;
1659 qb_log_format_set(i, "%t %H %N: [%P]: %p: %b");
1660 }
1661 (void) qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
1662 QB_LOG_FILTER_FILE, "*", LOG_DEBUG);
1663 #endif
1664 cl_log_enable_stderr(TRUE);
1665 cl_log_set_facility(0);
1666
1667 rv = read_arguments(argc, argv);
1668 if (rv < 0)
1669 goto out;
1670
1671
1672 switch (cl.type) {
1673 case STATUS:
1674 rv = do_status(&booth_conf, cl.type);
1675 break;
1676
1677 case ARBITRATOR:
1678 case DAEMON:
1679 case SITE:
1680 rv = do_server(&booth_conf, cl.type);
1681 break;
1682
1683 case CLIENT:
1684 rv = do_client(&booth_conf);
1685 break;
1686
1687 case GEOSTORE:
1688 rv = do_attr(&booth_conf);
1689 break;
1690 }
1691
1692 out:
1693 #if HAVE_LIBGNUTLS
1694 gnutls_global_deinit();
1695 #endif
1696 #ifdef LOGGING_LIBQB
1697 qb_log_fini();
1698 #endif
1699 /* Normalize values. 0x100 would be seen as "OK" by waitpid(). */
1700 return (rv >= 0 && rv < 0x70) ? rv : 1;
1701 }
1702