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