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