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 <ctype.h>
24 #include <stdlib.h>
25 #include <assert.h>
26 #include <zlib.h>
27 #include <sys/types.h>
28 #include <pwd.h>
29 #include <grp.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <netdb.h>
33 #include "booth.h"
34 #include "config.h"
35 #include "raft.h"
36 #include "ticket.h"
37 #include "log.h"
38
39 static int ticket_size = 0;
40
41 static int
42 ticket_realloc(struct booth_config *conf)
43 {
44 const int added = 5;
45 int had, want;
46 void *p;
47
48 assert(conf != NULL);
49
50 had = conf->ticket_allocated;
51 want = had + added;
52
53 p = realloc(conf->ticket, sizeof(struct ticket_config) * want);
54 if (!p) {
55 log_error("can't alloc more tickets");
56 return -ENOMEM;
57 }
58
59 conf->ticket = p;
60 memset(conf->ticket + had, 0,
61 sizeof(struct ticket_config) * added);
62 conf->ticket_allocated = want;
63
64 return 0;
65 }
66
67 static void
68 hostname_to_ip(char *hostname)
69 {
70 struct addrinfo hints;
71 struct addrinfo *result, *rp;
72 int res;
73 int addr_found = 0;
74 const char *ntop_res;
75
76 memset(&hints, 0, sizeof(hints));
77 hints.ai_family = AF_UNSPEC;
78 hints.ai_socktype = SOCK_STREAM;
79
80 res = getaddrinfo(hostname, NULL, &hints, &result);
81
82 if (res != 0) {
83 log_error("can't find IP for the host \"%s\"", hostname);
84 return;
85 }
86
87 /* Return the first found AF_INET or AF_INET6 address */
88 for (rp = result; rp && !addr_found; rp = rp->ai_next) {
89 if (rp->ai_family != AF_INET && rp->ai_family != AF_INET6) {
90 continue ;
91 }
92
93 switch (rp->ai_family) {
94 case AF_INET:
95 ntop_res = inet_ntop(rp->ai_family,
96 &((struct sockaddr_in *)(rp->ai_addr))->sin_addr,
97 hostname, BOOTH_NAME_LEN - 1);
98 break;
99 case AF_INET6:
100 ntop_res = inet_ntop(rp->ai_family,
101 &((struct sockaddr_in6 *)(rp->ai_addr))->sin6_addr,
102 hostname, BOOTH_NAME_LEN - 1);
103 break;
104 }
105
106 if (ntop_res) {
107 /* buffer overflow will not happen (IPv6 notation < 63 chars),
108 but suppress the warnings */
109 hostname[BOOTH_NAME_LEN - 1] = '\0';
110 addr_found = 1;
111 }
112 }
113
114 if (!addr_found) {
115 log_error("no IP addresses found for the host \"%s\"", hostname);
116 }
117
118 freeaddrinfo(result);
119 }
120
121 static int
122 add_site(struct booth_config *conf, char *addr_string, int type)
123 {
124 int rv;
125 struct booth_site *site;
126 uLong nid;
127 uint32_t mask;
128 int i;
129
130 assert(conf != NULL);
131
132 rv = 1;
133 if (conf->site_count == MAX_NODES) {
134 log_error("too many nodes");
135 goto out;
136 }
137 if (strnlen(addr_string, sizeof(conf->site[0].addr_string))
138 >= sizeof(conf->site[0].addr_string)) {
139 log_error("site address \"%s\" too long", addr_string);
140 goto out;
141 }
142
143 site = conf->site + conf->site_count;
144
145 site->family = AF_INET;
146 site->type = type;
147
148 /* buffer overflow will not hapen (we've already checked that
149 addr_string will fit incl. terminating '\0' above), but
150 suppress the warnings with copying everything but the boundary
151 byte, which is valid as-is, since this last byte will be safely
152 pre-zeroed from the struct booth_config initialization */
153 strncpy(site->addr_string, addr_string, sizeof(site->addr_string) - 1);
154
155 if (!(inet_pton(AF_INET, site->addr_string, &site->sa4.sin_addr) > 0) &&
156 !(inet_pton(AF_INET6, site->addr_string, &site->sa6.sin6_addr) > 0)) {
157
158 /* Not a valid address, so let us try to convert it into an IP address */
159 hostname_to_ip(site->addr_string);
160 }
161
162 site->index = conf->site_count;
163 site->bitmask = 1 << conf->site_count;
164 /* Catch site overflow */
165 assert(site->bitmask);
166 conf->all_bits |= site->bitmask;
167 if (type == SITE) {
168 conf->sites_bits |= site->bitmask;
169 }
170
171 site->tcp_fd = -1;
172
173 conf->site_count++;
174
175 rv = 0;
176 memset(&site->sa6, 0, sizeof(site->sa6));
177
178 nid = crc32(0L, NULL, 0);
179 /* Using the ASCII representation in site->addr_string (both sizeof()
180 * and strlen()) gives quite a lot of collisions; a brute-force run
181 * from 0.0.0.0 to 24.0.0.0 gives ~4% collisions, and this tends to
182 * increase even more.
183 * Whether there'll be a collision in real-life, with 3 or 5 nodes, is
184 * another question ... but for now get the ID from the binary
185 * representation - that had *no* collisions up to 32.0.0.0.
186 * Note that POSIX mandates inet_pton to arange the address pointed
187 * to by "dst" in network byte order, assuring little/big-endianess
188 * mutual compatibility. */
189 if (inet_pton(AF_INET,
190 site->addr_string,
191 &site->sa4.sin_addr) > 0) {
192
193 site->family = AF_INET;
194 site->sa4.sin_family = site->family;
195 site->sa4.sin_port = htons(conf->port);
196 site->saddrlen = sizeof(site->sa4);
197 site->addrlen = sizeof(site->sa4.sin_addr);
198 site->site_id = crc32(nid, (void*)&site->sa4.sin_addr, site->addrlen);
199
200 } else if (inet_pton(AF_INET6,
201 site->addr_string,
202 &site->sa6.sin6_addr) > 0) {
203
204 site->family = AF_INET6;
205 site->sa6.sin6_family = site->family;
206 site->sa6.sin6_flowinfo = 0;
207 site->sa6.sin6_port = htons(conf->port);
208 site->saddrlen = sizeof(site->sa6);
209 site->addrlen = sizeof(site->sa6.sin6_addr);
210 site->site_id = crc32(nid, (void*)&site->sa6.sin6_addr, site->addrlen);
211
212 } else {
213 log_error("Address string \"%s\" is bad", site->addr_string);
214 rv = EINVAL;
215 }
216
217 /* Make sure we will never collide with NO_ONE,
218 * or be negative (to get "get_local_id() < 0" working). */
219 mask = 1 << (sizeof(site->site_id)*8 -1);
220 assert(NO_ONE & mask);
221 site->site_id &= ~mask;
222
223
224 /* Test for collisions with other sites */
225 for (i = 0; i < site->index; i++) {
226 if (conf->site[i].site_id == site->site_id) {
227 log_error("Got a site-ID collision. Please file a bug on https://github.com/ClusterLabs/booth/issues/new, attaching the configuration file.");
228 exit(1);
229 }
230 }
231
232 out:
233 return rv;
234 }
235
236 static inline char *
237 skip_while_in(const char *cp, int (*fn)(int), const char *allowed)
238 {
239 /* strchr() returns a pointer to the terminator if *cp == 0. */
240 while (*cp &&
241 (fn(*cp) ||
242 strchr(allowed, *cp)))
243 cp++;
244 /* discard "const" qualifier */
245 return (char*)cp;
246 }
247
248 static inline char *
249 skip_while(char *cp, int (*fn)(int))
250 {
251 while (fn(*cp))
252 cp++;
253 return cp;
254 }
255
256 static inline char *
257 skip_until(char *cp, char expected)
258 {
259 while (*cp && *cp != expected)
260 cp++;
261 return cp;
262 }
263
264 static inline int
265 is_end_of_line(char *cp)
266 {
267 char c = *cp;
268 return c == '\n' || c == 0 || c == '#';
269 }
270
271 static int
272 add_ticket(struct booth_config *conf, const char *name,
273 struct ticket_config **tkp, const struct ticket_config *def)
274 {
275 int rv;
276 struct ticket_config *tk;
277
|
(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. |
278 assert(conf != NULL);
279
|
(4) Event cond_true: |
Condition "conf->ticket_count == conf->ticket_allocated", taking true branch. |
280 if (conf->ticket_count == conf->ticket_allocated) {
281 rv = ticket_realloc(conf);
|
(5) Event cond_false: |
Condition "rv < 0", taking false branch. |
282 if (rv < 0) {
283 return rv;
|
(6) Event if_end: |
End of if statement. |
284 }
285 }
286
287
288 tk = conf->ticket + conf->ticket_count;
289 conf->ticket_count++;
290
|
(7) Event cond_false: |
Condition "!check_max_len_valid(name, 64 /* sizeof (tk->name) */)", taking false branch. |
291 if (!check_max_len_valid(name, sizeof(tk->name))) {
292 log_error("ticket name \"%s\" too long.", name);
293 return -EINVAL;
|
(8) Event if_end: |
End of if statement. |
294 }
295
|
(9) Event cond_false: |
Condition "find_ticket_by_name(conf, name, NULL)", taking false branch. |
296 if (find_ticket_by_name(conf, name, NULL)) {
297 log_error("ticket name \"%s\" used again.", name);
298 return -EINVAL;
|
(10) Event if_end: |
End of if statement. |
299 }
300
|
(11) Event cond_false: |
Condition "*skip_while_in(name, isalnum, "-/")", taking false branch. |
301 if (* skip_while_in(name, isalnum, "-/")) {
302 log_error("ticket name \"%s\" invalid; only alphanumeric names.", name);
303 return -EINVAL;
|
(12) Event if_end: |
End of if statement. |
304 }
305
|
(13) Event fixed_size_dest: |
You might overrun the 64-character fixed-size string "tk->name" by copying "name" without checking the length. |
|
(14) Event parameter_as_source: |
Note: This defect has an elevated risk because the source argument is a parameter of the current function. |
306 strcpy(tk->name, name);
307 tk->timeout = def->timeout;
308 tk->term_duration = def->term_duration;
309 tk->retries = def->retries;
310 memcpy(tk->weight, def->weight, sizeof(tk->weight));
311 tk->mode = def->mode;
312
313 if (tkp)
314 *tkp = tk;
315 return 0;
316 }
317
318 static int
319 postproc_ticket(struct ticket_config *tk)
320 {
321 if (!tk)
322 return 1;
323
324 if (!tk->renewal_freq) {
325 tk->renewal_freq = tk->term_duration/2;
326 }
327
328 if (tk->timeout*(tk->retries+1) >= tk->renewal_freq) {
329 log_error("%s: total amount of time to "
330 "retry sending packets cannot exceed "
331 "renewal frequency "
332 "(%d*(%d+1) >= %d)",
333 tk->name, tk->timeout, tk->retries, tk->renewal_freq);
334 return 0;
335 }
336 return 1;
337 }
338
339 /* returns number of weights, or -1 on bad input. */
340 static int
341 parse_weights(const char *input, int weights[MAX_NODES])
342 {
343 int i, v;
344 char *cp;
345
346 for(i=0; i<MAX_NODES; i++) {
347 /* End of input? */
348 if (*input == 0)
349 break;
350
351 v = strtol(input, &cp, 0);
352 if (input == cp) {
353 log_error("No integer weight value at \"%s\"", input);
354 return -1;
355 }
356
357 weights[i] = v;
358
359 while (*cp) {
360 /* Separator characters */
361 if (isspace(*cp) ||
362 strchr(",;:-+", *cp))
363 cp++;
364 /* Next weight */
365 else if (isdigit(*cp))
366 break;
367 /* Rest */
368 else {
369 log_error("Invalid character at \"%s\"", cp);
370 return -1;
371 }
372 }
373
374 input = cp;
375 }
376
377
378 /* Fill rest of vector. */
379 for(v=i; v<MAX_NODES; v++) {
380 weights[v] = 0;
381 }
382
383 return i;
384 }
385
386 /* returns TICKET_MODE_AUTO if failed to parse the ticket mode. */
387 static ticket_mode_e
388 retrieve_ticket_mode(const char *input)
389 {
390 if (strcasecmp(input, "manual") == 0) {
391 return TICKET_MODE_MANUAL;
392 }
393
394 return TICKET_MODE_AUTO;
395 }
396
397 /* scan val for time; time is [0-9]+(ms)?, i.e. either in seconds
398 * or milliseconds
399 * returns -1 on failure, otherwise time in ms
400 */
401 static long
402 read_time(char *val)
403 {
404 long t;
405 char *ep;
406
407 t = strtol(val, &ep, 10);
408 if (ep == val) { /* matched none */
409 t = -1L;
410 } else if (*ep == '\0') { /* matched all */
411 t = t*1000L; /* in seconds, convert to ms */
412 } else if (strcmp(ep, "ms")) { /* ms not exactly matched */
413 t = -1L;
414 } /* otherwise, time in ms */
415 /* if second fractions configured, send finer resolution
416 * times (i.e. term_valid_for) */
417 if (t % 1000L) {
418 TIME_MULT = 1000;
419 }
420 return t;
421 }
422
423 /* make arguments for execv(2)
424 * tk_test.path points to the path
425 * tk_test.argv is argument vector (starts with the prog)
426 * (strtok pokes holes in the configuration parameter value, i.e.
427 * we don't need to allocate memory for arguments)
428 */
429 static int
430 parse_extprog(char *val, struct ticket_config *tk)
431 {
432 char *p;
433 int i = 0;
434
435 if (tk_test.path) {
436 free(tk_test.path);
437 }
438 if (!(tk_test.path = strdup(val))) {
439 log_error("out of memory");
440 return -1;
441 }
442
443 p = strtok(tk_test.path, " \t");
444 tk_test.argv[i++] = p;
445 do {
446 p = strtok(NULL, " \t");
447 if (i >= MAX_ARGS) {
448 log_error("too many arguments for the acquire-handler");
449 free(tk_test.path);
450 return -1;
451 }
452 tk_test.argv[i++] = p;
453 } while (p);
454
455 return 0;
456 }
457
458 struct toktab grant_type[] = {
459 { "auto", GRANT_AUTO},
460 { "manual", GRANT_MANUAL},
461 { NULL, 0},
462 };
463
464 struct toktab attr_op[] = {
465 {"eq", ATTR_OP_EQ},
466 {"ne", ATTR_OP_NE},
467 {NULL, 0},
468 };
469
470 static int
471 lookup_tokval(char *key, struct toktab *tab)
472 {
473 struct toktab *tp;
474
475 for (tp = tab; tp->str; tp++) {
476 if (!strcmp(tp->str, key))
477 return tp->val;
478 }
479 return 0;
480 }
481
482 /* attribute prerequisite
483 */
484 static int
485 parse_attr_prereq(char *val, struct ticket_config *tk)
486 {
487 struct attr_prereq *ap = NULL;
488 char *p;
489
490 ap = (struct attr_prereq *)calloc(1, sizeof(struct attr_prereq));
491 if (!ap) {
492 log_error("out of memory");
493 return -1;
494 }
495
496 p = strtok(val, " \t");
497 if (!p) {
498 log_error("not enough arguments to attr-prereq");
499 goto err_out;
500 }
501 ap->grant_type = lookup_tokval(p, grant_type);
502 if (!ap->grant_type) {
503 log_error("%s is not a grant type", p);
504 goto err_out;
505 }
506
507 p = strtok(NULL, " \t");
508 if (!p) {
509 log_error("not enough arguments to attr-prereq");
510 goto err_out;
511 }
512 if (!(ap->attr_name = strdup(p))) {
513 log_error("out of memory");
514 goto err_out;
515 }
516
517 p = strtok(NULL, " \t");
518 if (!p) {
519 log_error("not enough arguments to attr-prereq");
520 goto err_out;
521 }
522 ap->op = lookup_tokval(p, attr_op);
523 if (!ap->op) {
524 log_error("%s is not an attribute operation", p);
525 goto err_out;
526 }
527
528 p = strtok(NULL, " \t");
529 if (!p) {
530 log_error("not enough arguments to attr-prereq");
531 goto err_out;
532 }
533 if (!(ap->attr_val = strdup(p))) {
534 log_error("out of memory");
535 goto err_out;
536 }
537
538 tk->attr_prereqs = g_list_append(tk->attr_prereqs, ap);
539 if (!tk->attr_prereqs) {
540 log_error("out of memory");
541 goto err_out;
542 }
543
544 return 0;
545
546 err_out:
547 if (ap) {
548 if (ap->attr_val)
549 free(ap->attr_val);
550 if (ap->attr_name)
551 free(ap->attr_name);
552 free(ap);
553 }
554 return -1;
555 }
556
557 extern int poll_timeout;
558
559 int
560 read_config(struct booth_config **conf, const char *path, int type)
561 {
562 char line[1024];
563 char error_str_buf[1024];
564 FILE *fp;
565 char *s, *key, *val, *end_of_key;
566 const char *error;
567 char *cp, *cp2;
568 int i;
569 int lineno = 0;
570 int got_transport = 0;
571 int min_timeout = 0;
572 struct ticket_config defaults = { { 0 } };
573 struct ticket_config *current_tk = NULL;
574
575 assert(conf != NULL);
576 free(*conf);
577
578 fp = fopen(path, "r");
579 if (!fp) {
580 log_error("failed to open %s: %s", path, strerror(errno));
581 *conf = NULL;
582 return -1;
583 }
584
585 *conf = malloc(sizeof(struct booth_config)
586 + TICKET_ALLOC * sizeof(struct ticket_config));
587 if (*conf == NULL) {
588 fclose(fp);
589 log_error("failed to alloc memory for booth config");
590 return -ENOMEM;
591 }
592 memset(*conf, 0, sizeof(struct booth_config)
593 + TICKET_ALLOC * sizeof(struct ticket_config));
594 ticket_size = TICKET_ALLOC;
595
596
597 (*conf)->proto = UDP;
598 (*conf)->port = BOOTH_DEFAULT_PORT;
599 (*conf)->maxtimeskew = BOOTH_DEFAULT_MAX_TIME_SKEW;
600 (*conf)->authkey[0] = '\0';
601
602
603 /* Provide safe defaults. -1 is reserved, though. */
604 (*conf)->uid = -2;
605 (*conf)->gid = -2;
606 strcpy((*conf)->site_user, "hacluster");
607 strcpy((*conf)->site_group, "haclient");
608 strcpy((*conf)->arb_user, "nobody");
609 strcpy((*conf)->arb_group, "nobody");
610
611 parse_weights("", defaults.weight);
612 defaults.clu_test.path = NULL;
613 defaults.clu_test.pid = 0;
614 defaults.clu_test.status = 0;
615 defaults.clu_test.progstate = EXTPROG_IDLE;
616 defaults.term_duration = DEFAULT_TICKET_EXPIRY;
617 defaults.timeout = DEFAULT_TICKET_TIMEOUT;
618 defaults.retries = DEFAULT_RETRIES;
619 defaults.acquire_after = 0;
620 defaults.mode = TICKET_MODE_AUTO;
621
622 error = "";
623
624 log_debug("reading config file %s", path);
625 while (fgets(line, sizeof(line), fp)) {
626 lineno++;
627
628 s = skip_while(line, isspace);
629 if (is_end_of_line(s) || *s == '#')
630 continue;
631 key = s;
632
633
634 /* Key */
635 end_of_key = skip_while_in(key, isalnum, "-_");
636 if (end_of_key == key) {
637 error = "No key";
638 goto err;
639 }
640
641 if (!*end_of_key)
642 goto exp_equal;
643
644
645 /* whitespace, and something else but nothing more? */
646 s = skip_while(end_of_key, isspace);
647
648
649 if (*s != '=') {
650 exp_equal:
651 error = "Expected '=' after key";
652 goto err;
653 }
654 s++;
655
656 /* It's my buffer, and I terminate if I want to. */
657 /* But not earlier than that, because we had to check for = */
658 *end_of_key = 0;
659
660
661 /* Value tokenizing */
662 s = skip_while(s, isspace);
663 switch (*s) {
664 case '"':
665 case '\'':
666 val = s+1;
667 s = skip_until(val, *s);
668 /* Terminate value */
669 if (!*s) {
670 error = "Unterminated quoted string";
671 goto err;
672 }
673
674 /* Remove and skip quote */
675 *s = 0;
676 s++;
677 if (*(s = skip_while(s, isspace)) && *s != '#') {
678 error = "Surplus data after value";
679 goto err;
680 }
681
682 *s = 0;
683
684 break;
685
686 case 0:
687 no_value:
688 error = "No value";
689 goto err;
690 break;
691
692 default:
693 val = s;
694 /* Rest of line. */
695 i = strlen(s);
696 /* i > 0 because of "case 0" above. */
697 while (i > 0 && isspace(s[i-1]))
698 i--;
699 s += i;
700 *s = 0;
701 }
702
703 if (val == s)
704 goto no_value;
705
706
707 if (strlen(key) > BOOTH_NAME_LEN
708 || strlen(val) > BOOTH_NAME_LEN) {
709 error = "key/value too long";
710 goto err;
711 }
712
713 if (strcmp(key, "transport") == 0) {
714 if (got_transport) {
715 error = "config file has multiple transport lines";
716 goto err;
717 }
718
719 if (strcasecmp(val, "UDP") == 0) {
720 (*conf)->proto = UDP;
721 } else if (strcasecmp(val, "SCTP") == 0) {
722 (*conf)->proto = SCTP;
723 } else {
724 (void)snprintf(error_str_buf, sizeof(error_str_buf),
725 "invalid transport protocol \"%s\"", val);
726 error = error_str_buf;
727 goto err;
728 }
729 got_transport = 1;
730 continue;
731 }
732
733 if (strcmp(key, "port") == 0) {
734 (*conf)->port = atoi(val);
735 continue;
736 }
737
738 if (strcmp(key, "name") == 0) {
739 safe_copy((*conf)->name,
740 val, BOOTH_NAME_LEN,
741 "name");
742 continue;
743 }
744
745 #if HAVE_LIBGNUTLS || HAVE_LIBGCRYPT || HAVE_LIBMHASH
746 if (strcmp(key, "authfile") == 0) {
747 safe_copy((*conf)->authfile,
748 val, BOOTH_PATH_LEN,
749 "authfile");
750 continue;
751 }
752
753 if (strcmp(key, "maxtimeskew") == 0) {
754 (*conf)->maxtimeskew = atoi(val);
755 continue;
756 }
757 #endif
758
759 if (strcmp(key, "site") == 0) {
760 if (add_site(*conf, val, SITE)) {
761 goto err;
762 }
763 continue;
764 }
765
766 if (strcmp(key, "arbitrator") == 0) {
767 if (add_site(*conf, val, ARBITRATOR)) {
768 goto err;
769 }
770 continue;
771 }
772
773 if (strcmp(key, "site-user") == 0) {
774 safe_copy((*conf)->site_user, optarg, BOOTH_NAME_LEN,
775 "site-user");
776 continue;
777 }
778 if (strcmp(key, "site-group") == 0) {
779 safe_copy((*conf)->site_group, optarg, BOOTH_NAME_LEN,
780 "site-group");
781 continue;
782 }
783 if (strcmp(key, "arbitrator-user") == 0) {
784 safe_copy((*conf)->arb_user, optarg, BOOTH_NAME_LEN,
785 "arbitrator-user");
786 continue;
787 }
788 if (strcmp(key, "arbitrator-group") == 0) {
789 safe_copy((*conf)->arb_group, optarg, BOOTH_NAME_LEN,
790 "arbitrator-group");
791 continue;
792 }
793
794 if (strcmp(key, "debug") == 0) {
795 if (type != CLIENT && type != GEOSTORE)
796 debug_level = max(debug_level, atoi(val));
797 continue;
798 }
799
800 if (strcmp(key, "ticket") == 0) {
801 if (current_tk && strcmp(current_tk->name, "__defaults__")) {
802 if (!postproc_ticket(current_tk)) {
803 goto err;
804 }
805 }
806 if (!strcmp(val, "__defaults__")) {
807 current_tk = &defaults;
808 } else if (add_ticket(*conf, val, ¤t_tk,
809 &defaults)) {
810 goto err;
811 }
812 continue;
813 }
814
815 /* current_tk must be allocated at this point, otherwise
816 * we don't know to which ticket the key refers
817 */
818 if (!current_tk) {
819 (void)snprintf(error_str_buf, sizeof(error_str_buf),
820 "Unexpected keyword \"%s\"", key);
821 error = error_str_buf;
822 goto err;
823 }
824
825 if (strcmp(key, "expire") == 0) {
826 current_tk->term_duration = read_time(val);
827 if (current_tk->term_duration <= 0) {
828 error = "Expected time >0 for expire";
829 goto err;
830 }
831 continue;
832 }
833
834 if (strcmp(key, "timeout") == 0) {
835 current_tk->timeout = read_time(val);
836 if (current_tk->timeout <= 0) {
837 error = "Expected time >0 for timeout";
838 goto err;
839 }
840 if (!min_timeout) {
841 min_timeout = current_tk->timeout;
842 } else {
843 min_timeout = min(min_timeout, current_tk->timeout);
844 }
845 continue;
846 }
847
848 if (strcmp(key, "retries") == 0) {
849 current_tk->retries = strtol(val, &s, 0);
850 if (*s || s == val ||
851 current_tk->retries<3 || current_tk->retries > 100) {
852 error = "Expected plain integer value in the range [3, 100] for retries";
853 goto err;
854 }
855 continue;
856 }
857
858 if (strcmp(key, "renewal-freq") == 0) {
859 current_tk->renewal_freq = read_time(val);
860 if (current_tk->renewal_freq <= 0) {
861 error = "Expected time >0 for renewal-freq";
862 goto err;
863 }
864 continue;
865 }
866
867 if (strcmp(key, "acquire-after") == 0) {
868 current_tk->acquire_after = read_time(val);
869 if (current_tk->acquire_after < 0) {
870 error = "Expected time >=0 for acquire-after";
871 goto err;
872 }
873 continue;
874 }
875
876 if (strcmp(key, "before-acquire-handler") == 0) {
877 if (parse_extprog(val, current_tk)) {
878 goto err;
879 }
880 continue;
881 }
882
883 if (strcmp(key, "attr-prereq") == 0) {
884 if (parse_attr_prereq(val, current_tk)) {
885 goto err;
886 }
887 continue;
888 }
889
890 if (strcmp(key, "mode") == 0) {
891 current_tk->mode = retrieve_ticket_mode(val);
892 continue;
893 }
894
895 if (strcmp(key, "weights") == 0) {
896 if (parse_weights(val, current_tk->weight) < 0)
897 goto err;
898 continue;
899 }
900
901 (void)snprintf(error_str_buf, sizeof(error_str_buf),
902 "Unknown keyword \"%s\"", key);
903 error = error_str_buf;
904 goto err;
905 }
906 fclose(fp);
907
908 if (((*conf)->site_count % 2) == 0) {
909 log_warn("Odd number of nodes is strongly recommended!");
910 }
911
912 /* Default: make config name match config filename. */
913 if (!(*conf)->name[0]) {
914 cp = strrchr(path, '/');
915 cp = cp ? cp+1 : (char *)path;
916 cp2 = strrchr(cp, '.');
917 if (!cp2)
918 cp2 = cp + strlen(cp);
919 if (cp2-cp >= BOOTH_NAME_LEN) {
920 log_error("booth config file name too long");
921 goto out;
922 }
923 strncpy((*conf)->name, cp, cp2-cp);
924 *((*conf)->name+(cp2-cp)) = '\0';
925 }
926
927 if (!postproc_ticket(current_tk)) {
928 goto out;
929 }
930
931 poll_timeout = min(POLL_TIMEOUT, min_timeout/10);
932 if (!poll_timeout)
933 poll_timeout = POLL_TIMEOUT;
934
935 return 0;
936
937
938 err:
939 fclose(fp);
940 out:
941 log_error("%s in config file line %d",
942 error, lineno);
943
944 free(*conf);
945 *conf = NULL;
946 return -1;
947 }
948
949 int
950 check_config(struct booth_config *conf, int type)
951 {
952 struct passwd *pw;
953 struct group *gr;
954 char *cp, *input;
955
956 if (conf == NULL) {
957 return -1;
958 }
959
960 input = (type == ARBITRATOR)
961 ? conf->arb_user
962 : conf->site_user;
963 if (!*input)
964 goto u_inval;
965 if (isdigit(input[0])) {
966 conf->uid = strtol(input, &cp, 0);
967 if (*cp != 0) {
968 u_inval:
969 log_error("User \"%s\" cannot be resolved into a UID.", input);
970 return ENOENT;
971 }
972 } else {
973 pw = getpwnam(input);
974 if (!pw)
975 goto u_inval;
976 conf->uid = pw->pw_uid;
977 }
978
979
980 input = (type == ARBITRATOR)
981 ? conf->arb_group
982 : conf->site_group;
983
984 if (!*input) {
985 goto g_inval;
986 }
987
988 if (isdigit(input[0])) {
989 conf->gid = strtol(input, &cp, 0);
990 if (*cp != 0) {
991 g_inval:
992 log_error("Group \"%s\" cannot be resolved into a UID.", input);
993 return ENOENT;
994 }
995 } else {
996 gr = getgrnam(input);
997 if (!gr) {
998 goto g_inval;
999 }
1000 conf->gid = gr->gr_gid;
1001 }
1002
1003 return 0;
1004 }
1005
1006
1007 static int
1008 get_other_site(struct booth_config *conf, struct booth_site **node)
1009 {
1010 struct booth_site *n;
1011 int i;
1012
1013 *node = NULL;
1014 if (conf == NULL) {
1015 return 0;
1016 }
1017
1018 FOREACH_NODE(conf, i, n) {
1019 if (n != local && n->type == SITE) {
1020 if (!*node) {
1021 *node = n;
1022 } else {
1023 return 0;
1024 }
1025 }
1026 }
1027
1028 return !*node ? 0 : 1;
1029 }
1030
1031
1032 int
1033 find_site_by_name(struct booth_config *conf, const char *site,
1034 struct booth_site **node, int any_type)
1035 {
1036 struct booth_site *n;
1037 int i;
1038
1039 if (conf == NULL) {
1040 return 0;
1041 }
1042
1043 if (!strcmp(site, OTHER_SITE)) {
1044 return get_other_site(conf, node);
1045 }
1046
1047 FOREACH_NODE(conf, i, n) {
1048 if ((n->type == SITE || any_type) &&
1049 strncmp(n->addr_string, site, sizeof(n->addr_string)) == 0) {
1050 *node = n;
1051 return 1;
1052 }
1053 }
1054
1055 return 0;
1056 }
1057
1058 int
1059 find_site_by_id(struct booth_config *conf, uint32_t site_id,
1060 struct booth_site **node)
1061 {
1062 struct booth_site *n;
1063 int i;
1064
1065 if (site_id == NO_ONE) {
1066 *node = no_leader;
1067 return 1;
1068 }
1069
1070 if (conf == NULL) {
1071 return 0;
1072 }
1073
1074 FOREACH_NODE(conf, i, n) {
1075 if (n->site_id == site_id) {
1076 *node = n;
1077 return 1;
1078 }
1079 }
1080
1081 return 0;
1082 }
1083
1084 const char *
1085 type_to_string(int type)
1086 {
1087 switch (type)
1088 {
1089 case ARBITRATOR: return "arbitrator";
1090 case SITE: return "site";
1091 case CLIENT: return "client";
1092 case GEOSTORE: return "attr";
1093 }
1094 return "??invalid-type??";
1095 }
1096