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