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