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