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