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 <unistd.h>
24   	#include <stdlib.h>
25   	#include <sched.h>
26   	#include <errno.h>
27   	#include <limits.h>
28   	#include <sys/file.h>
29   	#include <sys/types.h>
30   	#include <sys/stat.h>
31   	#include <sys/mman.h>
32   	#include <sys/time.h>
33   	#include <sys/resource.h>
34   	#include <sys/socket.h>
35   	#include <sys/un.h>
36   	#include <sys/poll.h>
37   	#include <sys/wait.h>
38   	#include <fcntl.h>
39   	#include <string.h>
40   	#include <ctype.h>
41   	#include <assert.h>
42   	#include <signal.h>
43   	#include <netdb.h>
44   	#include <arpa/inet.h>
45   	#include <sys/types.h>
46   	
47   	#include <crm/services.h>
48   	
49   	#if HAVE_LIBGNUTLS
50   	#include <gnutls/gnutls.h>
51   	#endif
52   	#if HAVE_LIBGCRYPT
53   	#include <gcrypt.h>
54   	#endif
55   	#ifndef NAMETAG_LIBSYSTEMD
56   	#include <clplumbing/setproctitle.h>
57   	#else
58   	#include "alt/nametag_libsystemd.h"
59   	#endif
60   	#ifdef COREDUMP_NURSING
61   	#include <sys/prctl.h>
62   	#include <clplumbing/coredumps.h>
63   	#endif
64   	#include "log.h"
65   	#include "booth.h"
66   	#include "config.h"
67   	#include "transport.h"
68   	#include "inline-fn.h"
69   	#include "pacemaker.h"
70   	#include "ticket.h"
71   	#include "request.h"
72   	#include "attr.h"
73   	#include "handler.h"
74   	
75   	#define RELEASE_STR 	VERSION
76   	
77   	#define CLIENT_NALLOC		32
78   	
79   	static int daemonize = 1;
80   	int enable_stderr = 0;
81   	timetype start_time;
82   	
83   	
84   	/** Structure for "clients".
85   	 * Filehandles with incoming data get registered here (and in pollfds),
86   	 * along with their callbacks.
87   	 * Because these can be reallocated with every new fd, addressing
88   	 * happens _only_ by their numeric index. */
89   	struct client *clients = NULL;
90   	struct pollfd *pollfds = NULL;
91   	static int client_maxi;
92   	static int client_size = 0;
93   	
94   	
95   	static const struct booth_site _no_leader = {
96   		.addr_string = "none",
97   		.site_id = NO_ONE,
98   		.index = -1,
99   	};
100  	struct booth_site *const no_leader = (struct booth_site*) &_no_leader;
101  	
102  	typedef enum
103  	{
104  		BOOTHD_STARTED=0,
105  		BOOTHD_STARTING
106  	} BOOTH_DAEMON_STATE;
107  	
108  	int poll_timeout;
109  	
110  	struct command_line cl;
111  	
112  	/*
113  	 * Global signal handlers variables
114  	 */
115  	static int sig_exit_handler_called = 0;
116  	static int sig_exit_handler_sig = 0;
117  	static int sig_usr1_handler_called = 0;
118  	static int sig_chld_handler_called = 0;
119  	
120  	static const char *
121  	state_string(BOOTH_DAEMON_STATE st)
122  	{
123  		if (st == BOOTHD_STARTED) {
124  			return "started";
125  		} else if (st == BOOTHD_STARTING) {
126  			return "starting";
127  		} else {
128  			return "invalid";
129  		}
130  	}
131  	
132  	static void
133  	client_alloc(void)
134  	{
135  		int i;
136  	
137  		if (!(clients = realloc(
138  			clients, (client_size + CLIENT_NALLOC) * sizeof(*clients))
139  		) || !(pollfds = realloc(
140  			pollfds, (client_size + CLIENT_NALLOC) * sizeof(*pollfds))
141  		)) {
142  			log_error("can't alloc for client array");
143  			exit(1);
144  		}
145  	
146  		for (i = client_size; i < client_size + CLIENT_NALLOC; i++) {
147  			clients[i].workfn = NULL;
148  			clients[i].deadfn = NULL;
149  			clients[i].fd = -1;
150  			pollfds[i].fd = -1;
151  			pollfds[i].revents = 0;
152  		}
153  		client_size += CLIENT_NALLOC;
154  	}
155  	
156  	static void
157  	client_dead(int ci)
158  	{
159  		struct client *c = clients + ci;
160  	
161  		if (c->fd != -1) {
162  			log_debug("removing client %d", c->fd);
163  			close(c->fd);
164  		}
165  	
166  		c->fd = -1;
167  		c->workfn = NULL;
168  	
169  		if (c->msg) {
170  			free(c->msg);
171  			c->msg = NULL;
172  			c->offset = 0;
173  		}
174  	
175  		pollfds[ci].fd = -1;
176  	}
177  	
178  	int
179  	client_add(int fd, const struct booth_transport *tpt, workfn_t workfn,
180  	           void (*deadfn)(int ci))
181  	{
182  		int i;
183  		struct client *c;
184  	
185  	
186  		if (client_size - 1 <= client_maxi ) {
187  			client_alloc();
188  		}
189  	
190  		for (i = 0; i < client_size; i++) {
191  			c = clients + i;
192  			if (c->fd != -1)
193  				continue;
194  	
195  			c->workfn = workfn;
196  			if (deadfn)
197  				c->deadfn = deadfn;
198  			else
199  				c->deadfn = client_dead;
200  	
201  			c->transport = tpt;
202  			c->fd = fd;
203  			c->msg = NULL;
204  			c->offset = 0;
205  	
206  			pollfds[i].fd = fd;
207  			pollfds[i].events = POLLIN;
208  			if (i > client_maxi)
209  				client_maxi = i;
210  	
211  			return i;
212  		}
213  	
214  		assert(!"no client");
215  	}
216  	
217  	int
218  	find_client_by_fd(int fd)
219  	{
220  		int i;
221  	
222  		if (fd < 0)
223  			return -1;
224  	
225  		for (i = 0; i <= client_maxi; i++) {
226  			if (clients[i].fd == fd)
227  				return i;
228  		}
229  		return -1;
230  	}
231  	
232  	static int
233  	format_peers(const struct booth_config *conf, char **pdata, unsigned int *len)
234  	{
235  		const struct booth_site *s;
236  		char *data, *cp;
237  		char time_str[64];
238  		int i, alloc;
239  	
240  		*pdata = NULL;
241  		*len = 0;
242  	
243  		alloc = conf->site_count * (BOOTH_NAME_LEN + 256);
244  		data = malloc(alloc);
245  		if (!data)
246  			return -ENOMEM;
247  	
248  		cp = data;
249  		FOREACH_NODE(conf, i, s) {
250  			if (s == local)
251  				continue;
252  			strftime(time_str, sizeof(time_str), "%F %T",
253  				localtime(&s->last_recv));
254  			cp += snprintf(cp,
255  					alloc - (cp - data),
256  					"%-12s %s, last recv: %s\n",
257  					type_to_string(s->type),
258  					s->addr_string,
259  					time_str);
260  			cp += snprintf(cp,
261  					alloc - (cp - data),
262  					"\tSent pkts:%u error:%u resends:%u\n",
263  					s->sent_cnt,
264  					s->sent_err_cnt,
265  					s->resend_cnt);
266  			cp += snprintf(cp,
267  					alloc - (cp - data),
268  					"\tRecv pkts:%u error:%u authfail:%u invalid:%u\n\n",
269  					s->recv_cnt,
270  					s->recv_err_cnt,
271  					s->sec_cnt,
272  					s->invalid_cnt);
273  			if (alloc - (cp - data) <= 0) {
274  				free(data);
275  				return -ENOMEM;
276  			}
277  		}
278  	
279  		*pdata = data;
280  		*len = cp - data;
281  	
282  		return 0;
283  	}
284  	
285  	
286  	void
287  	list_peers(struct booth_config *conf, int fd)
288  	{
289  		char *data;
290  		unsigned int olen;
291  		struct boothc_hdr_msg hdr;
292  	
293  		if (format_peers(conf, &data, &olen) < 0) {
294  			goto out;
295  		}
296  	
297  		init_header(conf, &hdr.header, CL_LIST, 0, 0, RLT_SUCCESS, 0, sizeof(hdr) + olen);
298  		send_header_plus(conf, fd, &hdr, data, olen);
299  	
300  	out:
301  		free(data);
302  	}
303  	
304  	/* trim trailing spaces if the key is ascii
305  	 */
306  	static void
307  	trim_key(struct booth_config *conf)
308  	{
309  		char *p = conf->authkey;
310  	
311  		for (int i = 0; i < conf->authkey_len; i++, p++) {
312  			if (!isascii(*p)) {
313  				return;
314  			}
315  		}
316  	
317  		p = conf->authkey;
318  		while ((conf->authkey_len > 0) && isspace(*p)) {
319  			p++;
320  			conf->authkey_len--;
321  		}
322  		memmove(conf->authkey, p, conf->authkey_len);
323  	
324  		p = conf->authkey + conf->authkey_len - 1;
325  		while ((conf->authkey_len > 0) && isspace(*p)) {
326  			conf->authkey_len--;
327  			p--;
328  		}
329  	}
330  	
331  	static int
332  	read_authkey(struct booth_config *conf)
333  	{
334  		int fd;
335  		int rc = 0;
336  	
337  		conf->authkey[0] = '\0';
338  		fd = open(conf->authfile, O_RDONLY);
339  		if (fd < 0) {
340  			log_error("cannot open %s: %s",
341  				conf->authfile, strerror(errno));
342  			rc = -1;
343  			goto done;
344  		}
345  	
346  		if (fstat(fd, &conf->authstat) < 0) {
347  			log_error("cannot stat authentication file %s (%d): %s",
348  				conf->authfile, fd, strerror(errno));
349  			rc = -1;
350  			goto done;
351  		}
352  		if (conf->authstat.st_mode & (S_IRGRP | S_IROTH)) {
353  			log_error("%s: file shall not be readable for anyone but the owner",
354  				conf->authfile);
355  			rc = -1;
356  			goto done;
357  		}
358  	
359  		conf->authkey_len = read(fd, conf->authkey, BOOTH_MAX_KEY_LEN);
360  		if (conf->authkey_len < 0) {
361  			log_error("Failed to read authfile %s: %s",
362  				conf->authfile, strerror(errno));
363  			rc = -1;
364  			goto done;
365  		}
366  	
367  		trim_key(conf);
368  		log_debug("read key of size %d in authfile %s",
369  			conf->authkey_len, conf->authfile);
370  	
371  		// Make sure that the key is at least of minimum length
372  		if (conf->authkey_len < BOOTH_MIN_KEY_LEN) {
373  			log_error("Key in authfile %s is too short (%d < %d)",
374  				conf->authfile, conf->authkey_len,
375  				BOOTH_MIN_KEY_LEN);
376  			rc = -1;
377  		}
378  	
379  	done:
380  		if (fd >= 0) {
381  			close(fd);
382  		}
383  		return rc;
384  	}
385  	
386  	static int
387  	setup_config(struct booth_config **conf, int type)
388  	{
389  		int rv;
390  	
391  		assert(conf != NULL);
392  	
393  		rv = read_config(conf, cl.configfile, type);
394  		if (rv < 0) {
395  			return rv;
396  		}
397  	
398  		if ((*conf)->authfile[0] != '\0') {
399  			rv = read_authkey(*conf);
400  			if (rv < 0)
401  				return rv;
402  	#if HAVE_LIBGCRYPT
403  			if (!gcry_check_version(NULL)) {
404  				log_error("gcry_check_version");
405  				return -ENOENT;
406  			}
407  			gcry_control(GCRYCTL_DISABLE_SECMEM, 0);
408  			gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
409  	#endif
410  	#if HAVE_LIBGNUTLS
411  			if (gnutls_global_init() != 0) {
412  				log_error("Cannot initialize GnuTLS");
413  				return -EINVAL;
414  			};
415  	#endif
416  		}
417  	
418  		/* Set "local" pointer, ignoring errors. */
419  		if (cl.type == DAEMON && cl.site[0]) {
420  			if (!find_site_by_name(*conf, cl.site, &local, 1)) {
421  				log_error("Cannot find \"%s\" in the configuration.",
422  						cl.site);
423  				return -EINVAL;
424  			}
425  			local->local = 1;
426  		} else {
427  			find_myself(*conf, NULL, (type == CLIENT) || (type == GEOSTORE));
428  		}
429  	
430  		rv = check_config(*conf, type);
431  		if (rv < 0)
432  			return rv;
433  	
434  	
435  		/* Per default the PID file name is derived from the
436  		 * configuration name. */
437  		if (!cl.lockfile[0]) {
438  			snprintf(cl.lockfile, sizeof(cl.lockfile) - 1,
439  			         "%s/%s.pid", BOOTH_RUN_DIR, (*conf)->name);
440  		}
441  	
442  		return rv;
443  	}
444  	
445  	static int
446  	setup_transport(const struct booth_config *conf)
447  	{
448  		int rv;
449  	
450  		rv = transport(conf)->init(message_recv);
451  		if (rv < 0) {
452  			log_error("failed to init booth_transport %s", transport(conf)->name);
453  			return rv;
454  		}
455  	
456  		rv = booth_transport[TCP].init(NULL);
457  		if (rv < 0) {
458  			log_error("failed to init booth_transport[TCP]");
459  		}
460  	
461  		return rv;
462  	}
463  	
464  	static int
465  	write_daemon_state(const struct booth_config *conf, int fd, int state)
466  	{
467  		char *buffer;
468  		int rv, size;
469  	
470  		rv = asprintf(&buffer, "booth_pid=%d booth_state=%s booth_type=%s "
471  				       "booth_cfg_name='%s' booth_id=%d "
472  				       "booth_addr_string='%s' booth_port=%d\n",
473  			      getpid(), state_string(state), type_to_string(local->type),
474  			      conf->name, get_local_id(), site_string(local), site_port(local));
475  	
476  		if (rv < 0) {
477  			log_error("Buffer write failed in write_daemon_state().");
478  			return -1;
479  		}
480  	
481  		size = rv;
482  	
483  		rv = ftruncate(fd, 0);
484  		if (rv < 0) {
485  			log_error("lockfile %s truncate error %d: %s",
486  				  cl.lockfile, errno, strerror(errno));
487  			free(buffer);
488  			return rv;
489  		}
490  	
491  		rv = lseek(fd, 0, SEEK_SET);
492  		if (rv < 0) {
493  			log_error("lseek set fd(%d) offset to 0 error, return(%d), message(%s)",
494  				  fd, rv, strerror(errno));
495  			free(buffer);
496  			return -1;
497  		}
498  	
499  		rv = write(fd, buffer, size);
500  	
501  		if (rv != size) {
502  			log_error("write to fd(%d, %d) returned %d, errno %d, message(%s)",
503  				  fd, size, rv, errno, strerror(errno));
504  			free(buffer);
505  			return -1;
506  		}
507  	
508  		free(buffer);
509  		return 0;
510  	}
511  	
512  	static int
513  	process_signals(struct booth_config *conf)
514  	{
515  		if (sig_exit_handler_called) {
516  			log_info("caught signal %d", sig_exit_handler_sig);
517  			return 1;
518  		}
519  		if (sig_usr1_handler_called) {
520  			sig_usr1_handler_called = 0;
521  			tickets_log_info(conf);
522  		}
523  		if (sig_chld_handler_called) {
524  			sig_chld_handler_called = 0;
525  			wait_child(conf, SIGCHLD);
526  		}
527  	
528  		return 0;
529  	}
530  	
531  	static int
532  	loop(struct booth_config *conf, int fd)
533  	{
534  		workfn_t workfn;
535  		void (*deadfn) (int ci);
536  		int rv, i;
537  	
538  		rv = setup_transport(conf);
539  		if (rv < 0)
540  			goto fail;
541  	
542  		rv = setup_ticket(conf);
543  		if (rv < 0) {
544  			goto fail;
545  		}
546  	
547  		rv = write_daemon_state(conf, fd, BOOTHD_STARTED);
548  		if (rv != 0) {
549  			log_error("write daemon state %d to lockfile error %s: %s",
550  	                      BOOTHD_STARTED, cl.lockfile, strerror(errno));
551  			goto fail;
552  		}
553  	
554  		log_info("BOOTH %s daemon started, node id is 0x%08X (%d).",
555  			type_to_string(local->type),
556  				local->site_id, local->site_id);
557  	
558  		while (1) {
559  			rv = poll(pollfds, client_maxi + 1, poll_timeout);
560  			if (rv == -1 && errno == EINTR)
561  				continue;
562  			if (rv < 0) {
563  				log_error("poll failed: %s (%d)", strerror(errno), errno);
564  				goto fail;
565  			}
566  	
567  			for (i = 0; i <= client_maxi; i++) {
568  				if (clients[i].fd < 0)
569  					continue;
570  	
571  				if (pollfds[i].revents & POLLIN) {
572  					workfn = clients[i].workfn;
573  					if (workfn) {
574  						workfn(conf, i);
575  					}
576  				}
577  				if (pollfds[i].revents &
578  						(POLLERR | POLLHUP | POLLNVAL)) {
579  					deadfn = clients[i].deadfn;
580  					if (deadfn)
581  						deadfn(i);
582  				}
583  			}
584  	
585  			process_tickets(conf);
586  	
587  			if (process_signals(conf) != 0) {
588  				return 0;
589  			}
590  		}
591  	
592  		return 0;
593  	
594  	fail:
595  		return -1;
596  	}
597  	
598  	
599  	static int
600  	test_reply(cmd_result_t reply_code, cmd_request_t cmd)
601  	{
602  		int rv = 0;
603  		const char *op_str = NULL;
604  	
605  		if (cmd == CMD_GRANT)
606  			op_str = "grant";
607  		else if (cmd == CMD_REVOKE)
608  			op_str = "revoke";
609  		else if (cmd == CMD_LIST)
610  			op_str = "list";
611  		else if (cmd == CMD_PEERS)
612  			op_str = "peers";
613  		else {
614  			log_error("internal error reading reply result!");
615  			return -1;
616  		}
617  	
618  		switch (reply_code) {
619  		case RLT_OVERGRANT:
620  			log_info("You're granting a granted ticket. "
621  				 "If you wanted to migrate a ticket, "
622  				 "use revoke first, then use grant.");
623  			rv = -1;
624  			break;
625  	
626  		case RLT_TICKET_IDLE:
627  			log_info("ticket is not owned");
628  			rv = 0;
629  			break;
630  	
631  		case RLT_ASYNC:
632  			log_info("%s command sent, result will be returned "
633  				 "asynchronously. Please use \"booth list\" to "
634  				 "see the outcome.", op_str);
635  			rv = 0;
636  			break;
637  	
638  		case RLT_CIB_PENDING:
639  			log_info("%s succeeded (CIB commit pending)", op_str);
640  			/* wait for the CIB commit? */
641  			rv = (cl.options & OPT_WAIT_COMMIT) ? 3 : 0;
642  			break;
643  	
644  		case RLT_MORE:
645  			rv = 2;
646  			break;
647  	
648  		case RLT_SYNC_SUCC:
649  		case RLT_SUCCESS:
650  			if (cmd != CMD_LIST && cmd != CMD_PEERS)
651  				log_info("%s succeeded!", op_str);
652  			rv = 0;
653  			break;
654  	
655  		case RLT_SYNC_FAIL:
656  			log_info("%s failed!", op_str);
657  			rv = -1;
658  			break;
659  	
660  		case RLT_INVALID_ARG:
661  			log_error("ticket \"%s\" does not exist",
662  					cl.msg.ticket.id);
663  			rv = -1;
664  			break;
665  	
666  		case RLT_AUTH:
667  			log_error("authentication error");
668  			rv = -1;
669  			break;
670  	
671  		case RLT_EXT_FAILED:
672  			log_error("before-acquire-handler for ticket \"%s\" failed, grant denied",
673  					cl.msg.ticket.id);
674  			rv = -1;
675  			break;
676  	
677  		case RLT_ATTR_PREREQ:
678  			log_error("attr-prereq for ticket \"%s\" failed, grant denied",
679  					cl.msg.ticket.id);
680  			rv = -1;
681  			break;
682  	
683  		case RLT_REDIRECT:
684  			/* talk to another site */
685  			rv = 1;
686  			break;
687  	
688  		default:
689  			log_error("got an error code: %x", rv);
690  			rv = -1;
691  		}
692  		return rv;
693  	}
694  	
695  	static int
696  	query_get_string_answer(struct booth_config *conf, cmd_request_t cmd)
697  	{
698  		struct booth_site *site;
699  		struct boothc_hdr_msg reply;
700  		struct boothc_header *header;
701  		char *data;
702  		int data_len;
703  		int rv;
704  		struct booth_transport const *tpt;
705  		int (*test_reply_f) (cmd_result_t reply_code, cmd_request_t cmd);
706  		size_t msg_size;
707  		void *request;
708  	
709  		if (cl.type == GEOSTORE) {
710  			test_reply_f = test_attr_reply;
711  			msg_size = sizeof(cl.attr_msg);
712  			request = &cl.attr_msg;
713  		} else {
714  			test_reply_f = test_reply;
715  			msg_size = sizeof(cl.msg);
716  			request = &cl.msg;
717  		}
718  		header = (struct boothc_header *)request;
719  		data = NULL;
720  	
721  		init_header(conf, header, cmd, 0, cl.options, 0, 0, msg_size);
722  	
723  		if (!*cl.site)
724  			site = local;
725  		else if (!find_site_by_name(conf, cl.site, &site, 1)) {
726  			log_error("cannot find site \"%s\"", cl.site);
727  			rv = ENOENT;
728  			goto out;
729  		}
730  	
731  		tpt = booth_transport + TCP;
732  		rv = tpt->open(site);
733  		if (rv < 0)
734  			goto out_close;
735  	
736  		rv = tpt->send(conf, site, request, msg_size);
737  		if (rv < 0)
738  			goto out_close;
739  	
740  		rv = tpt->recv_auth(conf, site, &reply, sizeof(reply));
741  		if (rv < 0)
742  			goto out_close;
743  	
744  		data_len = ntohl(reply.header.length) - rv;
745  	
746  		/* no attribute, or no ticket found */
747  		if (!data_len) {
748  			goto out_test_reply;
749  		}
750  	
751  		data = malloc(data_len+1);
752  		if (!data) {
753  			rv = -ENOMEM;
754  			goto out_close;
755  		}
756  		rv = tpt->recv(site, data, data_len);
757  		if (rv < 0)
758  			goto out_close;
759  	
760  		*(data + data_len) = '\0';
761  		(void)fputs(data, stdout);
762  		fflush(stdout);
763  	
764  	out_test_reply:
765  		rv = test_reply_f(ntohl(reply.header.result), cmd);
766  	out_close:
767  		tpt->close(site);
768  	out:
769  		free(data);
770  		return rv;
771  	}
772  	
773  	
774  	static int
775  	do_command(struct booth_config *conf, cmd_request_t cmd)
776  	{
777  		struct booth_site *site;
778  		struct boothc_ticket_msg reply;
779  		struct booth_transport const *tpt;
780  		uint32_t leader_id;
781  		int rv;
782  		int reply_cnt = 0, msg_logged = 0;
783  		const char *op_str = "";
784  	
785  		if (cmd == CMD_GRANT)
786  			op_str = "grant";
787  		else if (cmd == CMD_REVOKE)
788  			op_str = "revoke";
789  	
790  		rv = -1;
791  		site = NULL;
792  	
793  		/* Always use TCP for client - at least for now. */
794  		tpt = booth_transport + TCP;
795  	
796  		if (!*cl.site)
797  			site = local;
798  		else {
799  			if (!find_site_by_name(conf, cl.site, &site, 1)) {
800  				log_error("Site \"%s\" not configured.", cl.site);
801  				goto out_close;
802  			}
803  		}
804  	
805  		if (site->type == ARBITRATOR) {
806  			if (site == local) {
807  				log_error("We're just an arbitrator, cannot grant/revoke tickets here.");
808  			} else {
809  				log_error("%s is just an arbitrator, cannot grant/revoke tickets there.", cl.site);
810  			}
811  			goto out_close;
812  		}
813  	
814  		assert(site->type == SITE);
815  	
816  		/* We don't check for existence of ticket, so that asking can be
817  		 * done without local configuration, too.
818  		 * Although, that means that the UDP port has to be specified, too. */
819  		if (!cl.msg.ticket.id[0]) {
820  			/* If the loaded configuration has only a single ticket defined, use that. */
821  			if (conf->ticket_count == 1) {
822  				strncpy(cl.msg.ticket.id, conf->ticket[0].name,
823  					sizeof(cl.msg.ticket.id));
824  			} else {
825  				log_error("No ticket given.");
826  				goto out_close;
827  			}
828  		}
829  	
830  	redirect:
831  		init_header(conf, &cl.msg.header, cmd, 0, cl.options, 0, 0, sizeof(cl.msg));
832  	
833  		rv = tpt->open(site);
834  		if (rv < 0)
835  			goto out_close;
836  	
837  		rv = tpt->send(conf, site, &cl.msg, sendmsglen(&cl.msg));
838  		if (rv < 0) {
839  			goto out_close;
840  		}
841  	
842  	read_more:
843  		rv = tpt->recv_auth(conf, site, &reply, sizeof(reply));
844  		if (rv < 0) {
845  			/* print any errors depending on the code sent by the
846  			 * server */
847  			(void)test_reply(ntohl(reply.header.result), cmd);
848  			goto out_close;
849  		}
850  	
851  		rv = test_reply(ntohl(reply.header.result), cmd);
852  		if (rv == 1) {
853  			tpt->close(site);
854  			leader_id = ntohl(reply.ticket.leader);
855  			if (!find_site_by_id(conf, leader_id, &site)) {
856  				log_error("Message with unknown redirect site %x received", leader_id);
857  				rv = -1;
858  				goto out_close;
859  			}
860  			goto redirect;
861  		} else if (rv == 2 || rv == 3) {
862  			/* the server has more to say */
863  			/* don't wait too long */
864  			if (reply_cnt > 1 && !(cl.options & OPT_WAIT)) {
865  				rv = 0;
866  				log_info("Giving up on waiting for the definite result. "
867  					 "Please use \"booth list\" later to "
868  					 "see the outcome.");
869  				goto out_close;
870  			}
871  			if (reply_cnt == 0) {
872  				log_info("%s request sent, "
873  					"waiting for the result ...", op_str);
874  				msg_logged++;
875  			} else if (rv == 3 && msg_logged < 2) {
876  				log_info("waiting for the CIB commit ...");
877  				msg_logged++;
878  			}
879  			reply_cnt++;
880  			goto read_more;
881  		}
882  	
883  	out_close:
884  		if (site)
885  			tpt->close(site);
886  		return rv;
887  	}
888  	
889  	
890  	
891  	static int
892  	_lockfile(int mode, int *fdp, pid_t *locked_by)
893  	{
894  		struct flock lock;
895  		int fd, rv;
896  	
897  	
898  		/* After reboot the directory may not yet exist.
899  		 * Try to create it, but ignore errors. */
900  		if (strncmp(cl.lockfile, BOOTH_RUN_DIR,
901  					strlen(BOOTH_RUN_DIR)) == 0)
902  			(void)mkdir(BOOTH_RUN_DIR, 0775);
903  	
904  	
905  		if (locked_by)
906  			*locked_by = 0;
907  	
908  		*fdp = -1;
909  		fd = open(cl.lockfile, mode, 0664);
910  		if (fd < 0)
911  			return errno;
912  	
913  		*fdp = fd;
914  	
915  		lock.l_type = F_WRLCK;
916  		lock.l_start = 0;
917  		lock.l_whence = SEEK_SET;
918  		lock.l_len = 0;
919  		lock.l_pid = 0;
920  	
921  	
922  		if (fcntl(fd, F_SETLK, &lock) == 0)
923  			return 0;
924  	
925  		rv = errno;
926  	
927  		if (locked_by)
928  			if (fcntl(fd, F_GETLK, &lock) == 0)
929  				*locked_by = lock.l_pid;
930  	
931  		return rv;
932  	}
933  	
934  	static inline int
935  	is_root(void)
936  	{
937  		return geteuid() == 0;
938  	}
939  	
940  	static int
941  	create_lockfile(const struct booth_config *conf)
942  	{
943  		int rv, fd;
944  	
945  		fd = -1;
946  		rv = _lockfile(O_CREAT | O_WRONLY, &fd, NULL);
947  	
948  		if (fd == -1) {
949  			log_error("lockfile %s open error %d: %s",
950  					cl.lockfile, rv, strerror(rv));
951  			return -1;
952  		}
953  	
954  		if (rv < 0) {
955  			log_error("lockfile %s setlk error %d: %s",
956  					cl.lockfile, rv, strerror(rv));
957  			goto fail;
958  		}
959  	
960  		rv = write_daemon_state(conf, fd, BOOTHD_STARTING);
961  		if (rv != 0) {
962  			log_error("write daemon state %d to lockfile error %s: %s",
963  					BOOTHD_STARTING, cl.lockfile, strerror(errno));
964  			goto fail;
965  		}
966  	
967  		if (is_root() && (fchown(fd, conf->uid, conf->gid) < 0)) {
968  			log_error("fchown() on lockfile said %d: %s",
969  					errno, strerror(errno));
970  		}
971  	
972  		return fd;
973  	
974  	fail:
975  		close(fd);
976  		return -1;
977  	}
978  	
979  	static void
980  	unlink_lockfile(int fd)
981  	{
982  		unlink(cl.lockfile);
983  		close(fd);
984  	}
985  	
986  	static void
987  	print_usage(void)
988  	{
989  		printf(
990  		"Usage:\n"
991  		"  booth list [options]\n"
992  		"  booth {grant|revoke} [options] <ticket>\n"
993  		"  booth status [options]\n"
994  		"\n"
995  		"  list:	     List all tickets\n"
996  		"  grant:        Grant ticket to site\n"
997  		"  revoke:       Revoke ticket\n"
998  		"\n"
999  		"Options:\n"
1000 		"  -c FILE       Specify config file [default " BOOTH_DEFAULT_CONF "]\n"
1001 		"                Can be a path or just a name without \".conf\" suffix\n"
1002 		"  -s <site>     Connect/grant to a different site\n"
1003 		"  -F            Try to grant the ticket immediately\n"
1004 		"                even if not all sites are reachable\n"
1005 		"                For manual tickets:\n"
1006 		"                grant a manual ticket even if it has been already granted\n"
1007 		"  -w            Wait forever for the outcome of the request\n"
1008 		"  -C            Wait until the ticket is committed to the CIB (grant only)\n"
1009 		"  -h            Print this help\n"
1010 		"\n"
1011 		"Examples:\n"
1012 		"\n"
1013 		"  # booth list (list tickets)\n"
1014 		"  # booth grant ticket-A (grant ticket here)\n"
1015 		"  # booth grant -s 10.121.8.183 ticket-A (grant ticket to site 10.121.8.183)\n"
1016 		"  # booth revoke ticket-A (revoke ticket)\n"
1017 		"\n"
1018 		"See the booth(8) man page for more details.\n"
1019 		);
1020 	}
1021 	
1022 	#define OPTION_STRING		"c:Dl:t:s:FhSwC"
1023 	#define ATTR_OPTION_STRING		"c:Dt:s:h"
1024 	
1025 	void
1026 	safe_copy(char *dest, char *value, size_t buflen, const char *description)
1027 	{
1028 		int content_len = buflen - 1;
1029 	
1030 		if (strlen(value) >= content_len) {
1031 			fprintf(stderr, "'%s' exceeds maximum %s length of %d\n",
1032 				value, description, content_len);
1033 			exit(EXIT_FAILURE);
1034 		}
1035 		strncpy(dest, value, content_len);
1036 		dest[content_len] = 0;
1037 	}
1038 	
1039 	static int
1040 	host_convert(char *hostname, char *ip_str, size_t ip_size)
1041 	{
1042 		struct addrinfo *result = NULL, hints = {0};
1043 		int re = -1;
1044 	
1045 		memset(&hints, 0, sizeof(hints));
1046 		hints.ai_family = AF_INET;
1047 		hints.ai_socktype = SOCK_DGRAM;
1048 	
1049 		re = getaddrinfo(hostname, NULL, &hints, &result);
1050 	
1051 		if (re == 0) {
1052 			struct in_addr addr = ((struct sockaddr_in *)result->ai_addr)->sin_addr;
1053 			const char *re_ntop = inet_ntop(AF_INET, &addr, ip_str, ip_size);
1054 			if (re_ntop == NULL) {
1055 				re = -1;
1056 			}
1057 		}
1058 	
1059 		freeaddrinfo(result);
1060 		return re;
1061 	}
1062 	
1063 	#define cparg(dest, descr) do { \
1064 		if (optind >= argc) \
1065 			goto missingarg; \
1066 		safe_copy(dest, argv[optind], sizeof(dest), descr); \
1067 		optind++; \
1068 	} while(0)
1069 	
1070 	static int
1071 	read_arguments(int argc, char **argv)
1072 	{
1073 		int optchar;
1074 		char *arg1 = argv[1];
1075 		char *op = NULL;
1076 		char *cp;
1077 		const char *opt_string = OPTION_STRING;
1078 		char site_arg[INET_ADDRSTRLEN] = {0};
1079 		int left;
1080 	
1081 		cl.type = 0;
1082 		if ((cp = strstr(argv[0], ATTR_PROG)) &&
1083 				!strcmp(cp, ATTR_PROG)) {
1084 			cl.type = GEOSTORE;
1085 			op = argv[1];
1086 			optind = 2;
1087 			opt_string = ATTR_OPTION_STRING;
1088 		} else if (argc > 1 && (strcmp(arg1, "arbitrator") == 0 ||
1089 				strcmp(arg1, "site") == 0 ||
1090 				strcmp(arg1, "start") == 0 ||
1091 				strcmp(arg1, "daemon") == 0)) {
1092 			cl.type = DAEMON;
1093 			optind = 2;
1094 		} else if (argc > 1 && (strcmp(arg1, "status") == 0)) {
1095 			cl.type = STATUS;
1096 			optind = 2;
1097 		} else if (argc > 1 && (strcmp(arg1, "client") == 0)) {
1098 			cl.type = CLIENT;
1099 			if (argc < 3) {
1100 				print_usage();
1101 				exit(EXIT_FAILURE);
1102 			}
1103 			op = argv[2];
1104 			optind = 3;
1105 		}
1106 		if (!cl.type) {
1107 			cl.type = CLIENT;
1108 			op = argv[1];
1109 			optind = 2;
1110 	    }
1111 	
1112 		if (argc < 2 || !strcmp(arg1, "help") || !strcmp(arg1, "--help") ||
1113 				!strcmp(arg1, "-h")) {
1114 			if (cl.type == GEOSTORE)
1115 				print_geostore_usage();
1116 			else
1117 				print_usage();
1118 			exit(EXIT_SUCCESS);
1119 		}
1120 	
1121 		if (!strcmp(arg1, "version") || !strcmp(arg1, "--version") ||
1122 				!strcmp(arg1, "-V")) {
1123 			printf("%s %s\n", argv[0], RELEASE_STR);
1124 			exit(EXIT_SUCCESS);
1125 		}
1126 	
1127 	    if (cl.type == CLIENT) {
1128 			if (!strcmp(op, "list"))
1129 				cl.op = CMD_LIST;
1130 			else if (!strcmp(op, "grant"))
1131 				cl.op = CMD_GRANT;
1132 			else if (!strcmp(op, "revoke"))
1133 				cl.op = CMD_REVOKE;
1134 			else if (!strcmp(op, "peers"))
1135 				cl.op = CMD_PEERS;
1136 			else {
1137 				fprintf(stderr, "client operation \"%s\" is unknown\n",
1138 						op);
1139 				exit(EXIT_FAILURE);
1140 			}
1141 		} else if (cl.type == GEOSTORE) {
1142 			if (!strcmp(op, "list"))
1143 				cl.op = ATTR_LIST;
1144 			else if (!strcmp(op, "set"))
1145 				cl.op = ATTR_SET;
1146 			else if (!strcmp(op, "get"))
1147 				cl.op = ATTR_GET;
1148 			else if (!strcmp(op, "delete"))
1149 				cl.op = ATTR_DEL;
1150 			else {
1151 				fprintf(stderr, "attribute operation \"%s\" is unknown\n",
1152 						op);
1153 				exit(EXIT_FAILURE);
1154 			}
1155 		}
1156 	
1157 		while (optind < argc) {
1158 			optchar = getopt(argc, argv, opt_string);
1159 	
1160 			switch (optchar) {
1161 			case 'c':
1162 				if (strchr(optarg, '/')) {
1163 					safe_copy(cl.configfile, optarg,
1164 							sizeof(cl.configfile), "config file");
1165 				} else {
1166 					/* If no "/" in there, use with default directory. */
1167 					strcpy(cl.configfile, BOOTH_DEFAULT_CONF_DIR);
1168 					cp = cl.configfile + strlen(BOOTH_DEFAULT_CONF_DIR);
1169 					assert(cp > cl.configfile);
1170 					assert(*(cp-1) == '/');
1171 	
1172 					/* Write at the \0, ie. after the "/" */
1173 					safe_copy(cp, optarg,
1174 							(sizeof(cl.configfile) -
1175 							 (cp -  cl.configfile) -
1176 							 strlen(BOOTH_DEFAULT_CONF_EXT)),
1177 							"config name");
1178 	
1179 					/* If no extension, append ".conf".
1180 					 * Space is available, see -strlen() above. */
1181 					if (!strchr(cp, '.'))
1182 						strcat(cp, BOOTH_DEFAULT_CONF_EXT);
1183 				}
1184 				break;
1185 	
1186 			case 'D':
1187 				debug_level++;
1188 				break;
1189 	
1190 			case 'S':
1191 				daemonize = 0;
1192 				enable_stderr = 1;
1193 				break;
1194 	
1195 			case 'l':
1196 				safe_copy(cl.lockfile, optarg, sizeof(cl.lockfile), "lock file");
1197 				break;
1198 			case 't':
1199 				if (cl.op == CMD_GRANT || cl.op == CMD_REVOKE) {
1200 					safe_copy(cl.msg.ticket.id, optarg,
1201 							sizeof(cl.msg.ticket.id), "ticket name");
1202 				} else if (cl.type == GEOSTORE) {
1203 					safe_copy(cl.attr_msg.attr.tkt_id, optarg,
1204 							sizeof(cl.attr_msg.attr.tkt_id), "ticket name");
1205 				} else {
1206 					print_usage();
1207 					exit(EXIT_FAILURE);
1208 				}
1209 				break;
1210 	
1211 			case 's':
1212 				/* For testing and debugging: allow "-s site" also for
1213 				 * daemon start, so that the address that should be used
1214 				 * can be set manually.
1215 				 * This makes it easier to start multiple processes
1216 				 * on one machine. */
1217 				if (cl.type == CLIENT || cl.type == GEOSTORE ||
1218 						(cl.type == DAEMON && debug_level)) {
1219 					if (strcmp(optarg, OTHER_SITE) &&
1220 							host_convert(optarg, site_arg, INET_ADDRSTRLEN) == 0) {
1221 						safe_copy(cl.site, site_arg, sizeof(cl.site), "site name");
1222 					} else {
1223 						safe_copy(cl.site, optarg, sizeof(cl.site), "site name");
1224 					}
1225 				} else {
1226 					log_error("\"-s\" not allowed in daemon mode.");
1227 					exit(EXIT_FAILURE);
1228 				}
1229 				break;
1230 	
1231 			case 'F':
1232 				if (cl.type != CLIENT || cl.op != CMD_GRANT) {
1233 					log_error("use \"-F\" only for client grant");
1234 					exit(EXIT_FAILURE);
1235 				}
1236 				cl.options |= OPT_IMMEDIATE;
1237 				break;
1238 	
1239 			case 'w':
1240 				if (cl.type != CLIENT ||
1241 						(cl.op != CMD_GRANT && cl.op != CMD_REVOKE)) {
1242 					log_error("use \"-w\" only for grant and revoke");
1243 					exit(EXIT_FAILURE);
1244 				}
1245 				cl.options |= OPT_WAIT;
1246 				break;
1247 	
1248 			case 'C':
1249 				if (cl.type != CLIENT || cl.op != CMD_GRANT) {
1250 					log_error("use \"-C\" only for grant");
1251 					exit(EXIT_FAILURE);
1252 				}
1253 				cl.options |= OPT_WAIT | OPT_WAIT_COMMIT;
1254 				break;
1255 	
1256 			case 'h':
1257 				if (cl.type == GEOSTORE)
1258 					print_geostore_usage();
1259 				else
1260 					print_usage();
1261 				exit(EXIT_SUCCESS);
1262 				break;
1263 	
1264 			case ':':
1265 			case '?':
1266 				fprintf(stderr, "Please use '-h' for usage.\n");
1267 				exit(EXIT_FAILURE);
1268 				break;
1269 	
1270 			case -1:
1271 				/* No more parameters on cmdline, only arguments. */
1272 				goto extra_args;
1273 	
1274 			default:
1275 				goto unknown;
1276 			};
1277 		}
1278 	
1279 		return 0;
1280 	
1281 	extra_args:
1282 		if (cl.type == CLIENT && !cl.msg.ticket.id[0]) {
1283 			cparg(cl.msg.ticket.id, "ticket name");
1284 		} else if (cl.type == GEOSTORE) {
1285 			if (cl.op != ATTR_LIST) {
1286 				cparg(cl.attr_msg.attr.name, "attribute name");
1287 			}
1288 			if (cl.op == ATTR_SET) {
1289 				cparg(cl.attr_msg.attr.val, "attribute value");
1290 			}
1291 		}
1292 	
1293 		if (optind == argc)
1294 			return 0;
1295 	
1296 	
1297 		left = argc - optind;
1298 		fprintf(stderr, "Superfluous argument%s: %s%s\n",
1299 				left == 1 ? "" : "s",
1300 				argv[optind],
1301 				left == 1 ? "" : "...");
1302 		exit(EXIT_FAILURE);
1303 	
1304 	unknown:
1305 		fprintf(stderr, "unknown option: %s\n", argv[optind]);
1306 		exit(EXIT_FAILURE);
1307 	
1308 	missingarg:
1309 		fprintf(stderr, "not enough arguments\n");
1310 		exit(EXIT_FAILURE);
1311 	}
1312 	
1313 	static void
1314 	set_scheduler(void)
1315 	{
1316 		struct sched_param sched_param;
1317 		struct rlimit rlimit;
1318 		int rv;
1319 	
1320 		rlimit.rlim_cur = RLIM_INFINITY;
1321 		rlimit.rlim_max = RLIM_INFINITY;
1322 		rv = setrlimit(RLIMIT_MEMLOCK, &rlimit);
1323 		if (rv < 0) {
1324 			log_error("setrlimit failed");
1325 		} else {
1326 	                rv = mlockall(MCL_CURRENT | MCL_FUTURE);
1327 	                if (rv < 0) {
1328 	                        log_error("mlockall failed");
1329 	                }
1330 	        }
1331 	
1332 		rv = sched_get_priority_max(SCHED_RR);
1333 		if (rv != -1) {
1334 			sched_param.sched_priority = rv;
1335 			rv = sched_setscheduler(0, SCHED_RR, &sched_param);
1336 			if (rv == -1)
1337 				log_error("could not set SCHED_RR priority %d: %s (%d)",
1338 						sched_param.sched_priority,
1339 						strerror(errno), errno);
1340 		} else {
1341 			log_error("could not get maximum scheduler priority err %d",
1342 					errno);
1343 		}
1344 	}
1345 	
1346 	static int
1347 	set_procfs_val(const char *path, const char *val)
1348 	{
1349 		int rc = -1;
1350 		FILE *fp = fopen(path, "w");
1351 	
1352 		if (fp) {
1353 			if (fprintf(fp, "%s", val) > 0)
1354 				rc = 0;
1355 			fclose(fp);
1356 		}
1357 		return rc;
1358 	}
1359 	
1360 	static int
1361 	do_status(struct booth_config **conf, int type)
1362 	{
1363 		pid_t pid;
1364 		int rv, status_lock_fd, ret;
1365 		const char *reason = NULL;
1366 		char lockfile_data[1024], *cp;
1367 	
1368 		assert(conf != NULL);
1369 	
1370 		ret = PCMK_OCF_NOT_RUNNING;
1371 	
1372 		rv = setup_config(conf, type);
1373 		if (rv) {
1374 			reason = "Error reading configuration.";
1375 			ret = PCMK_OCF_UNKNOWN_ERROR;
1376 			goto quit;
1377 		}
1378 	
1379 	
1380 		if (!local) {
1381 			reason = "No Service IP active here.";
1382 			goto quit;
1383 		}
1384 	
1385 	
1386 		rv = _lockfile(O_RDWR, &status_lock_fd, &pid);
1387 		if (status_lock_fd == -1) {
1388 			reason = "No PID file.";
1389 			goto quit;
1390 		}
1391 		if (rv == 0) {
1392 			close(status_lock_fd);
1393 			reason = "PID file not locked.";
1394 			goto quit;
1395 		}
1396 		if (pid) {
1397 			fprintf(stdout, "booth_lockpid=%d ", pid);
1398 			fflush(stdout);
1399 		}
1400 	
1401 		rv = read(status_lock_fd, lockfile_data, sizeof(lockfile_data) - 1);
1402 		if (rv < 4) {
1403 			close(status_lock_fd);
1404 			reason = "Cannot read lockfile data.";
1405 			ret = PCMK_LSB_UNKNOWN_ERROR;
1406 			goto quit;
1407 		}
1408 		lockfile_data[rv] = 0;
1409 	
1410 		close(status_lock_fd);
1411 	
1412 	
1413 		/* Make sure it's only a single line */
1414 		cp = strchr(lockfile_data, '\r');
1415 		if (cp)
1416 			*cp = 0;
1417 		cp = strchr(lockfile_data, '\n');
1418 		if (cp)
1419 			*cp = 0;
1420 	
1421 	
1422 	
1423 		rv = setup_tcp_listener(1);
1424 		if (rv == 0) {
1425 			reason = "TCP port not in use.";
1426 			goto quit;
1427 		}
1428 	
1429 	
1430 		fprintf(stdout, "booth_lockfile='%s' %s\n",
1431 				cl.lockfile, lockfile_data);
1432 		if (!daemonize)
1433 			fprintf(stderr, "Booth at %s port %d seems to be running.\n",
1434 			        local->addr_string, site_port(local));
1435 		return 0;
1436 	
1437 	
1438 	quit:
1439 		log_debug("not running: %s", reason);
1440 		/* Ie. "DEBUG" */
1441 		if (!daemonize)
1442 			fprintf(stderr, "not running: %s\n", reason);
1443 		return ret;
1444 	}
1445 	
1446 	static int
1447 	limit_this_process(const struct booth_config *conf)
1448 	{
1449 		int rv;
1450 		if (!is_root())
1451 			return 0;
1452 	
1453 		if (setregid(conf->gid, conf->gid) < 0) {
1454 			rv = errno;
1455 			log_error("setregid() didn't work: %s", strerror(rv));
1456 			return rv;
1457 		}
1458 	
1459 		if (setreuid(conf->uid, conf->uid) < 0) {
1460 			rv = errno;
1461 			log_error("setreuid() didn't work: %s", strerror(rv));
1462 			return rv;
1463 		}
1464 	
1465 		return 0;
1466 	}
1467 	
1468 	static int lock_fd = -1;
1469 	
1470 	static void
1471 	server_exit(void)
1472 	{
1473 		int rv;
1474 	
1475 		if (lock_fd >= 0) {
1476 			/* We might not be able to delete it, but at least
1477 			 * make it empty. */
1478 			rv = ftruncate(lock_fd, 0);
1479 			(void)rv;
1480 			unlink_lockfile(lock_fd);
1481 		}
1482 		log_info("exiting");
1483 	}
1484 	
1485 	static void
1486 	sig_exit_handler(int sig)
1487 	{
1488 		sig_exit_handler_sig = sig;
1489 		sig_exit_handler_called = 1;
1490 	}
1491 	
1492 	static void
1493 	sig_usr1_handler(int sig)
1494 	{
1495 		sig_usr1_handler_called = 1;
1496 	}
1497 	
1498 	static void
1499 	sig_chld_handler(int sig)
1500 	{
1501 		sig_chld_handler_called = 1;
1502 	}
1503 	
1504 	static int
1505 	do_server(struct booth_config **conf, int type)
1506 	{
1507 		int rv = -1;
1508 		static char log_ent[128] = DAEMON_NAME "-";
1509 	
1510 		assert(conf != NULL);
1511 	
1512 		rv = setup_config(conf, type);
1513 		if (rv < 0) {
1514 			return rv;
1515 		}
1516 	
1517 		if (!local) {
1518 			log_error("Cannot find myself in the configuration.");
1519 			exit(EXIT_FAILURE);
1520 		}
1521 	
1522 		if (daemonize) {
1523 			if (daemon(0, 0) < 0) {
1524 				perror("daemon error");
1525 				exit(EXIT_FAILURE);
1526 			}
1527 		}
1528 	
1529 		/*
1530 		 * Register signal and exit handler
1531 		 */
1532 		signal(SIGUSR1, (__sighandler_t)sig_usr1_handler);
1533 		signal(SIGTERM, (__sighandler_t)sig_exit_handler);
1534 		signal(SIGINT, (__sighandler_t)sig_exit_handler);
1535 		/* we'll handle errors there and then */
1536 		signal(SIGPIPE, SIG_IGN);
1537 	
1538 		atexit(server_exit);
1539 	
1540 		/* The lockfile must be written to _after_ the call to daemon(), so
1541 		 * that the lockfile contains the pid of the daemon, not the parent. */
1542 		lock_fd = create_lockfile(*conf);
1543 		if (lock_fd < 0)
1544 			return lock_fd;
1545 	
1546 		strcat(log_ent, type_to_string(local->type));
1547 		cl_log_set_entity(log_ent);
1548 		cl_log_enable_stderr(enable_stderr ? TRUE : FALSE);
1549 		cl_log_set_facility(HA_LOG_FACILITY);
1550 		cl_inherit_logging_environment(0);
1551 	
1552 		log_info("BOOTH %s %s daemon is starting",
1553 				type_to_string(local->type), RELEASE_STR);
1554 	
1555 	
1556 		set_scheduler();
1557 		/* we don't want to be killed by the OOM-killer */
1558 		if (set_procfs_val("/proc/self/oom_score_adj", "-999"))
1559 			(void)set_procfs_val("/proc/self/oom_adj", "-16");
1560 		set_proc_title("%s %s %s for [%s]:%d",
1561 		               DAEMON_NAME, cl.configfile, type_to_string(local->type),
1562 		               local->addr_string, site_port(local));
1563 	
1564 		rv = limit_this_process(*conf);
1565 		if (rv)
1566 			return rv;
1567 	
1568 	#ifdef COREDUMP_NURSING
1569 		if (cl_enable_coredumps(TRUE) < 0){
1570 			log_error("enabling core dump failed");
1571 		}
1572 		cl_cdtocoredir();
1573 		prctl(PR_SET_DUMPABLE, (unsigned long)TRUE, 0UL, 0UL, 0UL);
1574 	#else
1575 		if (chdir(BOOTH_CORE_DIR) < 0) {
1576 			log_error("cannot change working directory to %s", BOOTH_CORE_DIR);
1577 		}
1578 	#endif
1579 	
1580 		signal(SIGCHLD, (__sighandler_t)sig_chld_handler);
1581 		rv = loop(*conf, lock_fd);
1582 	
1583 		return rv;
1584 	}
1585 	
1586 	static int
1587 	do_client(struct booth_config **conf)
1588 	{
1589 		int rv;
1590 	
1591 		rv = setup_config(conf, CLIENT);
1592 		if (rv < 0) {
1593 			log_error("cannot read config");
1594 			return rv;
1595 		}
1596 	
1597 		switch (cl.op) {
1598 		case CMD_LIST:
1599 		case CMD_PEERS:
1600 			return query_get_string_answer(*conf, cl.op);
1601 	
1602 		case CMD_GRANT:
1603 		case CMD_REVOKE:
1604 			return do_command(*conf, cl.op);
1605 	
1606 		default:
1607 			return rv;
1608 		}
1609 	}
1610 	
1611 	static int
1612 	do_attr(struct booth_config **conf)
1613 	{
1614 		int rv = -1;
1615 	
(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.
1616 		assert(conf != NULL);
1617 	
1618 		rv = setup_config(conf, GEOSTORE);
(4) Event cond_false: Condition "rv < 0", taking false branch.
1619 		if (rv < 0) {
1620 			log_error("cannot read config");
1621 			return rv;
(5) Event if_end: End of if statement.
1622 		}
1623 	
1624 		/* We don't check for existence of ticket, so that asking can be
1625 		 * done without local configuration, too.
1626 		 * Although, that means that the UDP port has to be specified, too. */
(6) Event cond_true: Condition "!cl.attr_msg.attr.tkt_id[0]", taking true branch.
1627 		if (!cl.attr_msg.attr.tkt_id[0]) {
1628 			/* If the loaded configuration has only a single ticket defined, use that. */
(7) Event cond_true: Condition "(*conf)->ticket_count == 1", taking true branch.
1629 			if ((*conf)->ticket_count == 1) {
(8) Event buffer_size_warning: Calling "strncpy" with a maximum size argument of 64 bytes on destination array "cl.attr_msg.attr.tkt_id" of size 64 bytes might leave the destination string unterminated.
1630 				strncpy(cl.attr_msg.attr.tkt_id,
1631 				        (*conf)->ticket[0].name,
1632 				        sizeof(cl.attr_msg.attr.tkt_id));
(9) Event if_fallthrough: Falling through to end of if statement.
1633 			} else {
1634 				log_error("No ticket given.");
1635 				return 1;
(10) Event if_end: End of if statement.
1636 			}
1637 		}
1638 	
(11) Event switch: Switch case value "ATTR_LIST".
1639 		switch (cl.op) {
(12) Event switch_case: Reached case "ATTR_LIST".
1640 		case ATTR_LIST:
1641 		case ATTR_GET:
1642 			return query_get_string_answer(*conf, cl.op);
1643 	
1644 		case ATTR_SET:
1645 		case ATTR_DEL:
1646 			return do_attr_command(*conf, cl.op);
1647 	
1648 		default:
1649 			return rv;
1650 		}
1651 	}
1652 	
1653 	int
1654 	main(int argc, char *argv[], char *envp[])
1655 	{
1656 		int rv;
1657 		const char *cp;
1658 		struct booth_config *conf = NULL;
1659 	#ifdef LOGGING_LIBQB
1660 		enum qb_log_target_slot i;
1661 	#endif
1662 	
1663 		init_set_proc_title(argc, argv, envp);
1664 		get_time(&start_time);
1665 	
1666 		memset(&cl, 0, sizeof(cl));
1667 		strncpy(cl.configfile,
1668 				BOOTH_DEFAULT_CONF, BOOTH_PATH_LEN - 1);
1669 		cl.lockfile[0] = 0;
1670 		debug_level = 0;
1671 	
1672 	
1673 		cp = ((cp = strstr(argv[0], ATTR_PROG)) && !strcmp(cp, ATTR_PROG)
1674 			? ATTR_PROG
1675 			: "booth");
1676 	#ifndef LOGGING_LIBQB
1677 		cl_log_set_entity(cp);
1678 	#else
1679 		qb_log_init(cp, LOG_USER, LOG_DEBUG);  /* prio driven by debug_level */
1680 		for (i = QB_LOG_TARGET_START; i < QB_LOG_TARGET_MAX; i++) {
1681 			if (i == QB_LOG_SYSLOG || i == QB_LOG_BLACKBOX)
1682 				continue;
1683 			qb_log_format_set(i, "%t %H %N: [%P]: %p: %b");
1684 		}
1685 		(void) qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
1686 		                         QB_LOG_FILTER_FILE, "*", LOG_DEBUG);
1687 	#endif
1688 		cl_log_enable_stderr(TRUE);
1689 		cl_log_set_facility(0);
1690 	
1691 		rv = read_arguments(argc, argv);
1692 		if (rv < 0)
1693 			goto out;
1694 	
1695 	
1696 		switch (cl.type) {
1697 		case STATUS:
1698 			rv = do_status(&conf, cl.type);
1699 			break;
1700 	
1701 		case ARBITRATOR:
1702 		case DAEMON:
1703 		case SITE:
1704 			rv = do_server(&conf, cl.type);
1705 			break;
1706 	
1707 		case CLIENT:
1708 			rv = do_client(&conf);
1709 			break;
1710 	
1711 		case GEOSTORE:
1712 			rv = do_attr(&conf);
1713 			break;
1714 		}
1715 	
1716 	out:
1717 	    free_booth_config(conf);
1718 	
1719 	#if HAVE_LIBGNUTLS
1720 		gnutls_global_deinit();
1721 	#endif
1722 	#ifdef LOGGING_LIBQB
1723 		qb_log_fini();
1724 	#endif
1725 		/* Normalize values. 0x100 would be seen as "OK" by waitpid(). */
1726 		return (rv >= 0 && rv < 0x70) ? rv : 1;
1727 	}
1728