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