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, &current_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