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