1 /*-
2  * Copyright (c) 1999-2004 Andrey Simonenko
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include "config.h"
28 
29 #ifndef lint
30 static const char rcsid[] ATTR_UNUSED =
31   "@(#)$Id: ipa.c,v 1.4.2.1 2011/11/15 18:12:29 simon Exp $";
32 #endif /* !lint */
33 
34 #include <sys/types.h>
35 #include <sys/file.h>
36 #include <sys/stat.h>
37 #include <sys/time.h>
38 
39 #include <ctype.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <grp.h>
43 #include <locale.h>
44 #include <pwd.h>
45 #include <signal.h>
46 #include <stdlib.h>
47 #include <stdio.h>
48 #include <string.h>
49 #include <stdarg.h>
50 #include <time.h>
51 #include <unistd.h>
52 
53 #ifdef HAVE_PATHS_H
54 # include <paths.h>
55 #endif
56 #ifndef _PATH_DEVNULL
57 # ifdef __GNUC__
58 #  warning "defining _PATH_DEVNULL to \"/dev/null\""
59 # endif
60 # define _PATH_DEVNULL "/dev/null"
61 #endif
62 
63 #ifdef WITH_PTHREAD
64 # include <pthread.h>
65 #endif
66 
67 #include "ipa_mod.h"
68 
69 #include "queue.h"
70 
71 #include "dlapi.h"
72 #include "confcommon.h"
73 #include "memfunc.h"
74 #include "pathnames.h"
75 
76 #include "ipa_ac.h"
77 #include "ipa_db.h"
78 #include "ipa_ctl.h"
79 #include "ipa_cmd.h"
80 #include "ipa_time.h"
81 
82 #include "ipa_conf.h"
83 #include "ipa_ctl.h"
84 #include "ipa_log.h"
85 #include "ipa_main.h"
86 #include "ipa_rules.h"
87 #include "ipa_autorules.h"
88 
89 #if defined(HAVE_CLOSEFROM) && \
90     (STDIN_FILENO > 2 || STDOUT_FILENO > 2 || STDERR_FILENO > 2)
91 # undef HAVE_CLOSEFROM
92 #endif
93 
94 static const char *envprogname;
95 
96 static const char *pid_file_name = IPA_PID_FILE; /* -p pid_file */
97 static int	pid_file_fd;		/* File descriptor of PID-file. */
98 
99 static int	nofile_max;		/* Maximum number of opened files. */
100 
101 struct sig_descr {
102 	const char	*signame;	/* Command name. */
103 	int		signo;		/* Signal number. */
104 };
105 
106 static const struct sig_descr sig_descr_tbl[] = {
107 	{ "shutdown",	 SIGTERM },
108 	{ "reconfigure", SIGHUP	 },
109 	{ "kill",	 SIGKILL },
110 	{ "test",	 0	 },
111 	{ NULL,		 0	 }
112 };
113 
114 #define PID_FILE_PERM (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) /* 0644 */
115 
116 static void	exit_err(const char *, ...) ATTR_NORETURN
117 		    ATTR_FORMAT(printf, 1, 2);
118 static void	exit_errx(const char *, ...) ATTR_NORETURN
119 		    ATTR_FORMAT(printf, 1, 2);
120 
121 /*
122  * Close and unlink PID-file.
123  */
124 static void
unlink_pid_file(void)125 unlink_pid_file(void)
126 {
127 	if (close(pid_file_fd) < 0)
128 		logmsg(IPA_LOG_ERR, "unlink_pid_file: close(%s)",
129 		    pid_file_name);
130 	if (unlink(pid_file_name) < 0)
131 		logmsg(IPA_LOG_ERR, "unlink_pid_file: unlink(%s)",
132 		    pid_file_name);
133 }
134 
135 /*
136  * Output the program name, a message, an error message and exit.
137  */
138 static void
exit_err(const char * format,...)139 exit_err(const char *format, ...)
140 {
141 	va_list ap;
142 	int errno_save;
143 
144 	errno_save = errno;
145 	fflush(stdout);
146 	fprintf(stderr, "%s: ", envprogname);
147 	va_start(ap, format);
148 	vfprintf(stderr, format, ap);
149 	va_end(ap);
150 	if (errno_save != 0)
151 		fprintf(stderr, ": %s", strerror(errno_save));
152 	fprintf(stderr, "\n");
153 	exit(EXIT_FAILURE);
154 }
155 
156 /*
157  * Output the program name, a message and exit.
158  */
159 static void
exit_errx(const char * format,...)160 exit_errx(const char *format, ...)
161 {
162 	va_list ap;
163 
164 	fflush(stdout);
165 	fprintf(stderr, "%s: ", envprogname);
166 	va_start(ap, format);
167 	vfprintf(stderr, format, ap);
168 	va_end(ap);
169 	fprintf(stderr, "\n");
170 	exit(EXIT_FAILURE);
171 }
172 
173 /*
174  * Output version number and settings (-v and -h switches).
175  */
176 static void
show_version(void)177 show_version(void)
178 {
179 	printf(IPA_NAME ", version " PACKAGE_VERSION "\nRuntime settings: "
180 #ifdef SYM_PREFIX
181 	"symbol prefix \"" SYM_PREFIX "\", "
182 #else
183 	"symbol prefix is not used, "
184 #endif
185 #ifdef WITH_PTHREAD
186 	"thread-safe mode, "
187 #else
188 	"single-threaded mode, "
189 #endif
190 #ifdef USE_LIBLTDL
191 	"using libtool's ltdl library, "
192 #else
193 	"using dlopen interface, "
194 #endif
195 #ifdef WITH_MEMFUNC_DEBUG
196 	"memfunc debugging is enabled, "
197 #else
198 	"memfunc debugging is disabled, "
199 #endif
200 	"using shell %s, using null device %s.\nSupports:"
201 #ifdef WITH_AUTORULES
202 	" autorules"
203 #endif
204 #ifdef WITH_RULES
205 # ifdef WITH_AUTORULES
206 	","
207 # endif
208 	" rules"
209 #endif
210 #ifdef WITH_LIMITS
211 	", limits"
212 #endif
213 #ifdef WITH_SUBLIMITS
214 	", sublimits"
215 #endif
216 #ifdef WITH_THRESHOLDS
217 	", thresholds"
218 #endif
219 #ifdef CTL_CHECK_CREDS
220 	"; checking ipactl's messages credentials is enabled"
221 #else
222 	"; checking ipactl's messages credentials is disabled"
223 #endif
224 	".\n", shell_path_default, _PATH_DEVNULL);
225 }
226 
227 /*
228  * Output the help message (-h switch).
229  */
230 static void
usage(void)231 usage(void)
232 {
233 	show_version();
234 	printf("\
235 Usage: %s [options]\n\
236  Utility for accounting\n\
237  Options are:\n\
238   -c dir\t\tSet the directory " IPA_NAME " should chroot(2) into\n\
239 \t\t\timmediately, working directory is not changed\n\
240   -d\t\t\tDo not run in the background (debug mode)\n\
241   -f conf_file\t\tUse given config-file instead of using default\n\
242 \t\t\tconfiguration file %s\n", envprogname, IPA_CONF_FILE);
243 	printf("\
244   -i log_ident\t\tUse given log-ident (the default log-ident is \
245 \"" IPA_LOG_IDENT "\")\n\
246   -k signal\t\tSend signal to the running copy of ipa, signal\n\
247 \t\t\tcan be: reconfigure, shutdown, test or kill\n\
248   -o log_file\t\tUse given log-file instead of using syslog\n\
249   -p pid_file\t\tUse given PID-file instead of using default\n\
250 \t\t\tPID-file %s\n\
251   -t\t\t\tCheck the configuration file (two switches mimic\n\
252 \t\t\treal configuration regime) and exit\n", IPA_PID_FILE);
253 	printf("\
254   -u user\t\tChange UID of the running copy of " IPA_NAME " to user\n\
255   -g group\t\tChange GID of the running copy of " IPA_NAME " to group\n\
256   -x"
257 #ifdef WITH_RULES
258 " [-r rule"
259 # ifdef WITH_LIMITS
260 " [-l limit"
261 #  ifdef WITH_SUBLIMITS
262 " [-s sublimit]"
263 #  endif
264 "]"
265 # endif
266 # ifdef WITH_THRESHOLDS
267 " [-t threshold]"
268 # endif
269 "]\n     section"
270 # ifdef WITH_ANY_LIMITS
271 " [subsection]\n\t"
272 # endif
273 #else
274 " section"
275 #endif /* WITH_RULES */
276 "\t\tRun commands from the given section and exit\n\
277   -h\t\t\tOutput this help message and exit\n\
278   -v\t\t\tShow version number and exit\n");
279 }
280 
281 /*
282  * Ignore SIGTTOU, SIGTTIN, SIGTSTP and SIGPIPE and install sigmask
283  * to block signals which can be received.  By default SIGCHLD is ignored.
284  */
285 static int
sig_init_1(void)286 sig_init_1(void)
287 {
288 	struct sigaction sigact;
289 
290 	sigact.sa_handler = SIG_IGN;
291 	sigemptyset(&sigact.sa_mask);
292 	sigact.sa_flags = 0;
293 
294 	if (sigaction(SIGTTOU, &sigact, (struct sigaction *)NULL) < 0) {
295 		logmsg(IPA_LOG_ERR, "sig_init_1: sigaction(SIGTTOU)");
296 		return (-1);
297 	}
298 	if (sigaction(SIGTTIN, &sigact, (struct sigaction *)NULL) < 0) {
299 		logmsg(IPA_LOG_ERR, "sig_init_1: sigaction(SIGTTIN)");
300 		return (-1);
301 	}
302 	if (sigaction(SIGTSTP, &sigact, (struct sigaction *)NULL) < 0) {
303 		logmsg(IPA_LOG_ERR, "sig_init_1: sigaction(SIGTSTP)");
304 		return (-1);
305 	}
306 
307 	/*
308 	 * Need this for control socket and for asynchronous running
309 	 * commands (in case when ipa exits before asynchronous
310 	 * running commands).
311 	 */
312 	if (sigaction(SIGPIPE, &sigact, (struct sigaction *)NULL) < 0) {
313 		logmsg(IPA_LOG_ERR, "sig_init_1: sigaction(SIGPIPE)");
314 		return (-1);
315 	}
316 
317 	/* Set sigmask. */
318 	sigemptyset(&sigmask);
319 	if (debug)
320 		sigaddset(&sigmask, SIGINT);
321 	sigaddset(&sigmask, SIGHUP);
322 	sigaddset(&sigmask, SIGTERM);
323 
324 	/*
325 	 * Signals SIGTERM (and SIGINT) and SIGHUP
326 	 * before next function are not handled properly.
327 	 */
328 	if (Sigprocmask(SIG_SETMASK, &sigmask, (sigset_t *)NULL) < 0) {
329 		logmsg(IPA_LOG_ERR, "sig_init_1: "
330 		    SIGPROCMASK_NAME "(SIG_SETMASK)");
331 		return (-1);
332 	}
333 
334 	return (0);
335 }
336 
337 /*
338  * Add SIGCHLD to sigmask, initialize zeromask and set signal handlers.
339  */
340 static int
sig_init_2(void)341 sig_init_2(void)
342 {
343 	struct sigaction sigact;
344 
345 	/* Add SIGCHLD to sigmask. */
346 	sigaddset(&sigmask, SIGCHLD);
347 	if (Sigprocmask(SIG_SETMASK, &sigmask, (sigset_t *)NULL) < 0) {
348 		logmsg(IPA_LOG_ERR, "sig_init_2: "
349 		    SIGPROCMASK_NAME "(SIG_SETMASK)");
350 		return (-1);
351 	}
352 
353 	/* Empty zeromask. */
354 	sigemptyset(&zeromask);
355 
356 	/* Install signal handlers. */
357 	sigact.sa_flags = 0;
358 	sigact.sa_mask = sigmask;
359 
360 	/* Set SIGTERM (and SIGINT) handlers. */
361 	sigact.sa_handler = sig_term;
362 	if (debug)
363 		if (sigaction(SIGINT, &sigact, (struct sigaction *)NULL) < 0) {
364 			logmsg(IPA_LOG_ERR, "sig_init_2: sigaction(SIGINT)");
365 			return (-1);
366 		}
367 	if (sigaction(SIGTERM, &sigact, (struct sigaction *)NULL) < 0) {
368 		logmsg(IPA_LOG_ERR, "sig_init_2: sigaction(SIGTERM)");
369 		return (-1);
370 	}
371 
372 	/* Set SIGHUP handler. */
373 	sigact.sa_handler = sig_hup;
374 	if (sigaction(SIGHUP, &sigact, (struct sigaction *)NULL) < 0) {
375 		logmsg(IPA_LOG_ERR, "sig_init_2: sigaction(SIGHUP)");
376 		return (-1);
377 	}
378 
379 	/* Set SIGCHLD handler. */
380 	sigact.sa_handler = sig_chld;
381 	sigact.sa_flags = SA_NOCLDSTOP;
382 	if (sigaction(SIGCHLD, &sigact, (struct sigaction *)NULL) < 0) {
383 		logmsg(IPA_LOG_ERR, "sig_init_2: sigaction(SIGCHLD)");
384 		return (-1);
385 	}
386 
387 	return (0);
388 }
389 
390 /*
391  * Send a signal signo to the running copy of ipa(8).
392  * Check if the file pid_file_name is locked and if it is locked,
393  * then send a signal to the PID returned in l_pid field of
394  * struct flock{}.
395  */
396 static void
sendsig(int signo)397 sendsig(int signo)
398 {
399 	struct flock lock;
400 	int fd;
401 
402 	fd = open(pid_file_name, O_RDONLY);
403 	if (fd < 0)
404 		exit_err("sendsig: open(%s, O_RDONLY)", pid_file_name);
405 
406 	lock.l_start = 0;		/* Zero offset from... */
407 	lock.l_len = 0;			/* Entire file. */
408 	lock.l_type = F_WRLCK;		/* Should be valid value. */
409 	lock.l_whence = SEEK_SET;	/* ... the start of the file. */
410 
411 	if (fcntl(fd, F_GETLK, &lock) < 0) {
412 #ifdef __CYGWIN__
413 		if (errno == ENOSYS)
414 			exit_errx("on Cygwin the -k option cannot be "
415 			    "implemented in a safe way");
416 #endif
417 		exit_err("sendsig: fcntl(%s, F_GETLK)", pid_file_name);
418 	}
419 
420 	if (signo != 0) {
421 		switch (lock.l_type) {
422 		case F_UNLCK:
423 			exit_errx("sendsig: file %s is not locked, do not "
424 			    "send any signal", pid_file_name);
425 			/* NOTREACHED */
426 		case F_RDLCK:
427 			exit_errx("sendsig: file %s is lock, but lock is "
428 			    "shared, do not send any signal", pid_file_name);
429 		}
430 		printf("Sending signal %d to PID %ld...", signo,
431 		    (long)lock.l_pid);
432 		if (kill(lock.l_pid, signo) < 0) {
433 			printf("\n");
434 			exit_err("sendsig: kill(PID %ld, %d)",
435 			    (long)lock.l_pid, signo);
436 		}
437 		printf(" done\n");
438 	} else {
439 		if (lock.l_type == F_UNLCK)
440 			exit_err("file %s exists, but is not locked",
441 			    pid_file_name);
442 		printf("File %s is locked (%s) by PID %ld\n",
443 		    pid_file_name, lock.l_type == F_WRLCK ?
444 		    "exclusive" : "shared", (long)lock.l_pid);
445 	}
446 }
447 
448 /*
449  * Run in the background.
450  */
451 static int
bg_init(void)452 bg_init(void)
453 {
454 	pid_t pid;
455 
456 	pid = fork();
457 	if (pid == (pid_t)-1) {
458 		logmsg(IPA_LOG_ERR, "bg_init: fork");
459 		return (-1);
460 	}
461 
462 	if (pid != 0) {
463 		/* Parent exits. */
464 		exit(EXIT_SUCCESS);
465 	}
466 
467 	/* Child continues. */
468 	if (setsid() == (pid_t)-1)
469 		logmsg(IPA_LOG_ERR, "bg_init: setsid");
470 	return (0);
471 }
472 
473 /*
474  * Create pipe, if any of pipe's ends is stdin, stdout or stderr,
475  * then create another pipe and so on.  Then mark the read-end of
476  * a pipe as non-blocking, dup2() the write-end of a pipe to fd_write
477  * and mark fd_write as non-blocking, also mark the read-end of
478  * a pipe as non-blocking and return its value in *fd_read.  Read
479  * also comment for fd_init().
480  */
481 static int
pipe_stream(int fd_write,int * fd_read)482 pipe_stream(int fd_write, int *fd_read)
483 {
484 	unsigned int i, j;
485 	int fd0, fd1, fd[6];
486 
487 	/*
488 	 * Pipe's ends should not be equal to file descriptor of stdin,
489 	 * stdout or stderr.  SUSv3 claims that pipe(2) returns lowest
490 	 * available descriptors, so three (each with two descriptors)
491 	 * attempts are enough to get desired descriptor.
492 	 */
493 	for (i = j = 0;; j += 2, ++i) {
494 		if (pipe(fd + j) < 0) {
495 			logmsg(IPA_LOG_ERR, "pipe_stream: pipe");
496 			return (-1);
497 		}
498 		fd0 = fd[j];
499 		fd1 = fd[j + 1];
500 		if (fd0 != STDIN_FILENO && fd0 != STDOUT_FILENO &&
501 		    fd0 != STDERR_FILENO && fd1 != STDIN_FILENO &&
502 		    fd1 != STDOUT_FILENO && fd1 != STDERR_FILENO)
503 			break;
504 		if (i == 2) {
505 			logmsgx(IPA_LOG_ERR, "pipe_stream: something is wrong "
506 			    "in algorithm");
507 			return (-1);
508 		}
509 	}
510 
511 	/* Close "incorrectly" opened file descriptors. */
512 	while (j > 0) {
513 		--j;
514 		if (close(fd[j]) < 0) {
515 			logmsg(IPA_LOG_ERR, "pipe_stream: close(%d)", fd[j]);
516 			return (-1);
517 		}
518 	}
519 
520 	if (dup2(fd1, fd_write) < 0) {
521 		logmsg(IPA_LOG_ERR, "pipe_stream: dup2(%d, %d)", fd1, fd_write);
522 		return (-1);
523 	}
524 	if (close(fd1) < 0) {
525 		logmsg(IPA_LOG_ERR, "pipe_stream: close(%d)", fd1);
526 		return (-1);
527 	}
528 	if (fd_set_nonblock(fd0) < 0 || fd_set_nonblock(fd_write) < 0) {
529 		logbt("pipe_stream");
530 		return (-1);
531 	}
532 
533 	*fd_read = fd0;
534 	return (0);
535 }
536 
537 /*
538  * Close not used file descriptors and create pipes for standard
539  * streams stdout and stderr (if needed).  If this function returns -1,
540  * then function which called fd_init() should not call log_stdxxx()
541  * functions since syslog() or log file could use one of STDxxx_FILENO
542  * descriptors.  Also any log message should not be sent before this
543  * function invocation.
544  */
545 static int
fd_init(void)546 fd_init(void)
547 {
548 	long val;
549 	int fd;
550 
551 	errno = 0;
552 	val = sysconf(_SC_OPEN_MAX);
553 	if (val < 0) {
554 		if (errno != 0) {
555 			logmsg(IPA_LOG_ERR, "fd_init: sysconf(_SC_OPEN_MAX)");
556 			return (-1);
557 		}
558 #ifndef HAVE_CLOSEFROM
559 		logmsgx(IPA_LOG_ERR, "fd_init: infinite limit for "
560 		    "_SC_OPEN_MAX is not supported");
561 		return (-1);
562 #endif
563 	}
564 	nofile_max = (int)val;
565 
566 #ifdef HAVE_CLOSEFROM
567 	if (!debug) {
568 		(void)close(STDIN_FILENO);
569 		(void)close(STDERR_FILENO);
570 	}
571 	(void)close(STDOUT_FILENO);
572 	for (fd = 3; fd < pid_file_fd; ++fd)
573 		(void)close(fd);
574 	(void)closefrom(fd + 1);
575 #else
576 # if LONG_MAX > INT_MAX
577 	if (val > INT_MAX) {
578 		logmsgx(IPA_LOG_ERR, "fd_init: too big value %ld for 'int' "
579 		    "type from sysconf(_SC_OPEN_MAX)", val);
580 		return (-1);
581 	}
582 # endif
583 	for (fd = 0; fd < (int)val; ++fd)
584 		if (fd != pid_file_fd &&
585 		    !(debug && (fd == STDIN_FILENO || fd == STDERR_FILENO)))
586 			(void)close(fd);
587 #endif /* HAVE_CLOSEFROM */
588 
589 	if (!debug) {
590 		fd = open(_PATH_DEVNULL, O_RDWR);
591 		if (fd < 0) {
592 			logmsg(IPA_LOG_ERR, "fd_init: open(%s, O_RDWR)",
593 			    _PATH_DEVNULL);
594 			return (-1);
595 		}
596 		if (fd != STDIN_FILENO) {
597 			/* stdin -> /dev/null */
598 			if (dup2(fd, STDIN_FILENO) < 0) {
599 				logmsg(IPA_LOG_ERR, "fd_init: dup2(%d, %d)",
600 				    fd, STDIN_FILENO);
601 				return (-1);
602 			}
603 			if (close(fd) < 0) {
604 				logmsg(IPA_LOG_ERR, "fd_init: close(%d)", fd);
605 				return (-1);
606 			}
607 		}
608 		if (pipe_stream(STDERR_FILENO, &stderr_read) < 0) {
609 			logmsgx(IPA_LOG_ERR, "fd_init: pipe_stream for "
610 			    "STDERR_FILENO failed");
611 			return (-1);
612 		}
613 	}
614 
615 	if (pipe_stream(STDOUT_FILENO, &stdout_read) < 0) {
616 		logmsgx(IPA_LOG_ERR, "fd_init: pipe_stream for "
617 		    "STDOUT_FILENO failed");
618 		return (-1);
619 	}
620 
621 	return (0);
622 }
623 
624 /*
625  * Create/open PID-file and check if it is already locked, since it is
626  * better to create PID-file before writing something to the log, we need
627  * to choose file descriptor for PID-file not equal to stdxxx.
628  */
629 static void
create_pid_file(void)630 create_pid_file(void)
631 {
632 	struct flock lock;
633 	int fd[4];
634 	unsigned int i;
635 
636 	/*
637 	 * pid_file_fd should not be equal to file descriptor of stdin,
638 	 * stdout or stderr.  SUSv3 claims that open(2) returns lowest
639 	 * available descriptor, so four attempts are enough to get
640 	 * desired file descriptor.
641 	 */
642 	for (i = 0;; ++i) {
643 		fd[i] = open(pid_file_name, O_RDWR|O_CREAT, PID_FILE_PERM);
644 		if (fd[i] < 0)
645 			exit_err("create_pid_file: cannot create or open "
646 			    "PID-file: open(%s, O_RDWR|O_CREAT, 0%03o)",
647 			    pid_file_name, PID_FILE_PERM);
648 		if (fd[i] != STDIN_FILENO && fd[i] != STDOUT_FILENO &&
649 		    fd[i] != STDERR_FILENO) {
650 			pid_file_fd = fd[i];
651 			break;
652 		}
653 		if (i == 3)
654 			exit_err("create_pid_file: something is wrong "
655 			    "in algorithm");
656 	}
657 
658 	/* Close "incorrectly" opened file descriptors. */
659 	while (i > 0) {
660 		--i;
661 		if (close(fd[i]) < 0)
662 			exit_err("create_pid_file: close(%d)", fd[i]);
663 	}
664 
665 	lock.l_start = 0;		/* Zero offset from ... */
666 	lock.l_len = 0;			/* Entire file. */
667 	lock.l_type = F_WRLCK;		/* Should be a valid value. */
668 	lock.l_whence = SEEK_SET;	/* ... the start of the file. */
669 
670 	if (fcntl(pid_file_fd, F_GETLK, &lock) < 0) {
671 #ifdef __CYGWIN__
672 		if (errno == ENOSYS)
673 			return;
674 #endif
675 		exit_err("create_pid_file: fcntl(%s, F_GETLK)",
676 		    pid_file_name);
677 	}
678 
679 	switch (lock.l_type) {
680 	case F_UNLCK:
681 		break;
682 	case F_WRLCK:
683 	case F_RDLCK:
684 		exit_errx("create_pid_file: file %s is already locked (%s) by "
685 		    "PID %ld", pid_file_name, lock.l_type == F_WRLCK ?
686 		    "exclusive" : "shared", (long)lock.l_pid);
687 		/* NOTREACHED */
688 	default:
689 		exit_errx("create_pid_file: unknown lock type %d for file %s",
690 		    lock.l_type, pid_file_name);
691 	}
692 }
693 
694 /*
695  * Lock the file pid_file_name to prevent multiple copies of itself
696  * from running and store PID in this file.
697  */
698 static int
lock_pid_file(void)699 lock_pid_file(void)
700 {
701 	struct flock lock;
702 	ssize_t	n;
703 	int len;
704 	char num_str[22];
705 
706 	lock.l_start = 0;		/* Zero offset from... */
707 	lock.l_len = 0;			/* Entire file. */
708 	lock.l_type = F_WRLCK;		/* Write lock. */
709 	lock.l_whence = SEEK_SET;	/* ... the start of the file. */
710 
711 	if (fcntl(pid_file_fd, F_SETLK, &lock) < 0) {
712 		switch (errno) {
713 		case EACCES:
714 		case EAGAIN:
715 			logmsg(IPA_LOG_ERR, "lock_pid_file: cannot acquire "
716 			    "exclusive lock on file %s", pid_file_name);
717 			break;
718 		default:
719 			logmsg(IPA_LOG_ERR, "lock_pid_file: fcntl(%s, F_SETLK)",
720 			    pid_file_name);
721 		}
722 		return (-1);
723 	}
724 
725 	if (ftruncate(pid_file_fd, (off_t)0) < 0) {
726 		logmsg(IPA_LOG_ERR, "lock_pid_file: ftruncate(%s, 0)",
727 		    pid_file_name);
728 		goto failed;
729 	}
730 
731 	len = snprintf(num_str, sizeof(num_str), "%ld\n", (long)getpid());
732 	if (len < 0) {
733 		logmsg(IPA_LOG_ERR, "lock_pid_file: snprintf");
734 		goto failed;
735 	}
736 	if (len >= sizeof(num_str)) {
737 		logmsgx(IPA_LOG_ERR, "lock_pid_file: not enough space in "
738 		    "snprintf");
739 		goto failed;
740 	}
741 
742 	n = write(pid_file_fd, num_str, len);
743 	if (n < 0) {
744 		logmsg(IPA_LOG_ERR, "lock_pid_file: write(%s)", pid_file_name);
745 		goto failed;
746 	}
747 	if (n != len) {
748 		logmsgx(IPA_LOG_ERR, "lock_pid_file: write(%s): short write, "
749 		    "%ld of %lu bytes", pid_file_name, (long)n,
750 		    (unsigned long)len);
751 		goto failed;
752 	}
753 
754 	return (0);
755 
756 failed:
757 	unlink_pid_file();
758 	return (-1);
759 }
760 
761 #ifdef WITH_RULES
762 
763 # define X_OPTSTRING_RULE "r:"
764 
765 # ifdef WITH_LIMITS
766 #  define X_OPTSTRING_LIMIT "l:"
767 # else
768 #  define X_OPTSTRING_LIMIT ""
769 # endif
770 
771 # ifdef WITH_SUBLIMITS
772 #  define X_OPTSTRING_SUBLIMIT "s:"
773 # else
774 #  define X_OPTSTRING_SUBLIMIT ""
775 # endif
776 
777 # ifdef WITH_THRESHOLDS
778 #  define X_OPTSTRING_THRESHOLD "t:"
779 # else
780 #  define X_OPTSTRING_THRESHOLD ""
781 # endif
782 
783 # define X_OPTSTRING ":" \
784 	X_OPTSTRING_RULE X_OPTSTRING_LIMIT X_OPTSTRING_SUBLIMIT \
785 	X_OPTSTRING_THRESHOLD
786 
787 #else /* !WITH_RULES */
788 
789 # define X_OPTSTRING ""
790 
791 #endif /* WITH_RULES */
792 
793 /*
794  * Implement "-x ..." option.
795  */
796 static int
run_opt_cmds(int argc,char * argv[])797 run_opt_cmds(int argc, char *argv[])
798 {
799 	enum {
800 		SECT_STARTUP =		   0,
801 		SECT_SHUTDOWN,		/* 1 */
802 #if defined(WITH_RULES) && defined(WITH_LIMITS)
803 		SECT_REACH,		/* 2 */
804 		SECT_IF_REACHED,	/* 3 */
805 		SECT_IF_NOT_REACHED,	/* 4 */
806 #endif
807 		SECT_NOTSET
808 	};
809 	const char *section;
810 	const struct cmds *cmds;
811 #ifdef WITH_RULES
812 	const char *rule_name = NULL;
813 	const struct rule *rule;
814 	const struct cmds_rule *cmds_rule;
815 # ifdef WITH_LIMITS
816 	const char *subsection = NULL;
817 	const char *limit_name = NULL;
818 	const struct limit *limit;
819 	const struct cmds_limit *cmds_limit;
820 	unsigned int subsection_code;
821 # endif
822 # ifdef WITH_SUBLIMITS
823 	const char *sublimit_name = NULL;
824 	const struct sublimit *sublimit;
825 # endif
826 # ifdef WITH_THRESHOLDS
827 	const char *threshold_name = NULL;
828 	const struct threshold *threshold;
829 # endif
830 #endif /* WITH_RULES */
831 	unsigned int section_code;
832 	int opt;
833 
834 	xvlogmsgx = vlogmsgx_stderr;
835 
836 	while ((opt = getopt(argc, argv, X_OPTSTRING)) != -1)
837 		switch (opt) {
838 #ifdef WITH_RULES
839 		case ':':
840 			exit_errx("-x: option requires an argument -- %c",
841 			    optopt);
842 			/* NOTREACHED */
843 # ifdef WITH_LIMITS
844 		case 'l':
845 			if (limit_name != NULL)
846 				goto failed_duped;
847 			limit_name = optarg;
848 			if (validate_name(limit_name) < 0)
849 				exit_errx("-x: illegal %s name \"%s\"",
850 				    "limit", limit_name);
851 			break;
852 # endif
853 		case 'r':
854 			if (rule_name != NULL)
855 				goto failed_duped;
856 			rule_name = optarg;
857 			if (validate_name(rule_name) < 0)
858 				exit_errx("-x: illegal %s name \"%s\"",
859 				    "rule", rule_name);
860 			break;
861 # ifdef WITH_SUBLIMITS
862 		case 's':
863 			if (sublimit_name != NULL)
864 				goto failed_duped;
865 			sublimit_name = optarg;
866 			if (validate_name(sublimit_name) < 0)
867 				exit_errx("-x: illegal %s name \"%s\"",
868 				    "sublimit", sublimit_name);
869 			break;
870 # endif
871 # ifdef WITH_THRESHOLDS
872 		case 't':
873 			if (threshold_name != NULL)
874 				goto failed_duped;
875 			threshold_name = optarg;
876 			if (validate_name(threshold_name) < 0)
877 				exit_errx("-x: illegal %s name \"%s\"",
878 				    "threshold", threshold_name);
879 			break;
880 # endif
881 #endif /* WITH_RULES */
882 		case '?':
883 			exit_errx("-x: illegal option -- %c", optopt);
884 			/* NOTREACHED */
885 		default:
886 			exit_errx("-x: unexpected option -- %c", optopt);
887 		}
888 
889 	if (optind == argc)
890 		exit_errx("-x: section name is absent");
891 
892 	section = argv[optind++];
893 
894 #ifdef WITH_RULES
895 # if defined(WITH_LIMITS) && defined(WITH_THRESHOLDS)
896 	if (limit_name != NULL && threshold_name != NULL)
897 		exit_errx("-x: -l and -t options cannot be used together");
898 # endif
899 # if defined(WITH_SUBLIMITS) && defined(WITH_THRESHOLDS)
900 	if (sublimit_name != NULL && threshold_name != NULL)
901 		exit_errx("-x: -s and -t options cannot be used together");
902 # endif
903 	if (rule_name != NULL) {
904 		rule = rule_by_name(rule_name);
905 		if (rule == NULL)
906 			exit_errx("-x: cannot find rule %s in "
907 			    "the configuration file", rule_name);
908 	}
909 # ifdef WITH_LIMITS
910 	if (limit_name != NULL) {
911 		if (rule_name == NULL)
912 			exit_errx("-x: the -l option should be used "
913 			    "with the -r option");
914 		limit = limit_by_name(rule, limit_name);
915 		if (limit == NULL)
916 			exit_errx("-x: cannot find limit %s for rule %s in "
917 			    "the configuration file", limit_name, rule_name);
918 	}
919 # endif /* WITH_LIMITS */
920 # ifdef WITH_SUBLIMITS
921 	if (sublimit_name != NULL) {
922 		if (limit_name == NULL)
923 			exit_errx("-x: the -s option should be used "
924 			    "with the -l option");
925 		sublimit = sublimit_by_name(limit, sublimit_name);
926 		if (sublimit == NULL)
927 			exit_errx("-x: cannot find sublimit %s for "
928 			    "limit %s in rule %s in the configuration file",
929 			    sublimit_name, limit_name, rule_name);
930 	}
931 # endif /* WITH_SUBLIMITS */
932 # ifdef WITH_THRESHOLDS
933 	if (threshold_name != NULL) {
934 		if (rule_name == NULL)
935 			exit_errx("-x: the -t option should be used "
936 			    "with the -r option");
937 		threshold = threshold_by_name(rule, threshold_name);
938 		if (threshold == NULL)
939 			exit_errx("-x: cannot find threshold %s for "
940 			    "rule %s in the configuration file",
941 			    threshold_name, rule_name);
942 	}
943 # endif /* WITH_THRESHOLDS */
944 #endif /* WITH_RULES */
945 
946 	if (strcmp(section, "startup") == 0)
947 		section_code = SECT_STARTUP;
948 	else if (strcmp(section, "shutdown") == 0)
949 		section_code = SECT_SHUTDOWN;
950 	else
951 		section_code = SECT_NOTSET;
952 
953 #ifdef WITH_RULES
954 # ifdef WITH_LIMITS
955 	subsection_code = SECT_NOTSET;
956 # endif
957 	if (optind != argc) {
958 # ifdef WITH_LIMITS
959 		if (optind + 1 != argc)
960 			goto failed_many;
961 		subsection = argv[optind];
962 		if (limit_name != NULL) {
963 			if (strcmp(subsection, "if_reached") == 0)
964 				subsection_code = SECT_IF_REACHED;
965 			else if (strcmp(subsection, "if_not_reached") == 0)
966 				subsection_code = SECT_IF_NOT_REACHED;
967 			else
968 				goto failed_subs;
969 		}
970 #  ifdef WITH_THRESHOLDS
971 		else if (threshold_name != NULL)
972 			goto failed_many;
973 #  endif
974 # else
975 		goto failed_many;
976 # endif /* WITH_LIMITS */
977 	}
978 # ifdef WITH_LIMITS
979 	if (section_code == SECT_NOTSET)
980 		if (strcmp(section, "reach") == 0)
981 			section_code = SECT_REACH;
982 # endif
983 # ifdef WITH_SUBLIMITS
984 	if (sublimit_name != NULL) {
985 		switch (section_code) {
986 		case SECT_STARTUP:
987 			cmds_limit = &sublimit->rc[RC_STARTUP];
988 			break;
989 		case SECT_SHUTDOWN:
990 			cmds_limit = &sublimit->rc[RC_SHUTDOWN];
991 			break;
992 		case SECT_REACH:
993 			if (subsection != NULL)
994 				goto failed_many;
995 			return (run_cmds_cons(&sublimit->reach,
996 			    "rule %s { limit %s { sublimit %s { reach {}}}}",
997 			    rule_name, limit_name, sublimit_name));
998 		default: /* SECT_NOTSET */
999 			goto failed_sect;
1000 		}
1001 		switch (subsection_code) {
1002 		case SECT_IF_REACHED:
1003 			cmds = &cmds_limit->cmds_reached;
1004 			break;
1005 		case SECT_IF_NOT_REACHED:
1006 			cmds = &cmds_limit->cmds_not_reached;
1007 			break;
1008 		default: /* SECT_NOTSET */
1009 			return (run_cmds_cons(&cmds_limit->cmds,
1010 			    "rule %s { limit %s { sublimit %s { %s {}}}}",
1011 			    rule_name, limit_name, sublimit_name, section));
1012 		}
1013 		return (run_cmds_cons(cmds,
1014 		    "rule %s { limit %s { sublimit %s { %s { %s {}}}}}",
1015 		    rule_name, limit_name, sublimit_name, section, subsection));
1016 	}
1017 # endif /* WITH_SUBLIMITS */
1018 # ifdef WITH_LIMITS
1019 	if (limit_name != NULL) {
1020 		switch (section_code) {
1021 		case SECT_STARTUP:
1022 			cmds_limit = &limit->rc[RC_STARTUP];
1023 			break;
1024 		case SECT_SHUTDOWN:
1025 			cmds_limit = &limit->rc[RC_SHUTDOWN];
1026 			break;
1027 		default:
1028 			if (subsection != NULL)
1029 				goto failed_many;
1030 			if (section_code == SECT_REACH)
1031 				cmds = &limit->reach;
1032 			else if (strcmp(section, "expire") == 0)
1033 				cmds = &limit->expire.cmds;
1034 			else if (strcmp(section, "restart") == 0)
1035 				cmds = &limit->restart.cmds;
1036 			else
1037 				goto failed_sect;
1038 			return (run_cmds_cons(cmds,
1039 			    "rule %s { limit %s { %s {}}}",
1040 			    rule_name, limit_name, section));
1041 		}
1042 		switch (subsection_code) {
1043 		case SECT_IF_REACHED:
1044 			cmds = &cmds_limit->cmds_reached;
1045 			break;
1046 		case SECT_IF_NOT_REACHED:
1047 			cmds = &cmds_limit->cmds_not_reached;
1048 			break;
1049 		default: /* SECT_NOTSET */
1050 			return (run_cmds_cons(&cmds_limit->cmds,
1051 			    "rule %s { limit %s { %s {}}}",
1052 			    rule_name, limit_name, section));
1053 		}
1054 		return (run_cmds_cons(cmds,
1055 		    "rule %s { limit %s { %s { %s {}}}}",
1056 		    rule_name, limit_name, section, subsection));
1057 	}
1058 # endif /* WITH_LIMITS */
1059 # ifdef WITH_THRESHOLDS
1060 	if (threshold_name != NULL) {
1061 		switch (section_code) {
1062 		case SECT_STARTUP:
1063 			cmds = &threshold->rc[RC_STARTUP];
1064 			break;
1065 		case SECT_SHUTDOWN:
1066 			cmds = &threshold->rc[RC_SHUTDOWN];
1067 			break;
1068 		default:
1069 			if (strcmp(section, "below_threshold") == 0)
1070 				cmds = &threshold->below_thr;
1071 			else if (strcmp(section, "equal_threshold") == 0)
1072 				cmds = &threshold->equal_thr;
1073 			else if (strcmp(section, "above_threshold") == 0)
1074 				cmds = &threshold->above_thr;
1075 			else
1076 				goto failed_sect;
1077 		}
1078 		return (run_cmds_cons(cmds,
1079 		    "rule %s { threshold %s { %s {}}}",
1080 		    rule_name, threshold_name, section));
1081 	}
1082 # endif /* WITH_THRESHOLDS */
1083 	if (rule_name != NULL) {
1084 		switch (section_code) {
1085 		case SECT_STARTUP:
1086 			cmds_rule = &rule->rc[RC_STARTUP];
1087 			break;
1088 		case SECT_SHUTDOWN:
1089 			cmds_rule = &rule->rc[RC_SHUTDOWN];
1090 			break;
1091 		default:
1092 			goto failed_sect;
1093 		}
1094 # ifdef WITH_LIMITS
1095 		if (subsection != NULL) {
1096 			if (strcmp(subsection, "if_any_reached") == 0)
1097 				cmds = &cmds_rule->cmds_any_reached;
1098 			else if (strcmp(subsection, "if_any_not_reached") == 0)
1099 				cmds = &cmds_rule->cmds_any_not_reached;
1100 			else if (strcmp(subsection, "if_all_reached") == 0)
1101 				cmds = &cmds_rule->cmds_all_reached;
1102 			else if (strcmp(subsection, "if_all_not_reached") == 0)
1103 				cmds = &cmds_rule->cmds_all_not_reached;
1104 			else
1105 				goto failed_subs;
1106 			return (run_cmds_cons(cmds,
1107 			    "rule %s { %s { %s {}}}",
1108 			    rule_name, section, subsection));
1109 		}
1110 # endif /* WITH_LIMITS */
1111 		return (run_cmds_cons(&cmds_rule->cmds,
1112 		    "rule %s { %s {}}", rule_name, section));
1113 	}
1114 #endif /* WITH_RULES */
1115 
1116 	if (optind != argc)
1117 		goto failed_many;
1118 
1119 	switch (section_code) {
1120 	case SECT_STARTUP:
1121 		cmds = &cmds_global[RC_STARTUP];
1122 		break;
1123 	case SECT_SHUTDOWN:
1124 		cmds = &cmds_global[RC_SHUTDOWN];
1125 		break;
1126 	default:
1127 		goto failed_sect;
1128 	}
1129 	return (run_cmds_cons(cmds, "%s {}", section));
1130 
1131 #ifdef WITH_RULES
1132 failed_duped:
1133 	exit_errx("-x: duplicated option -- %c", optopt);
1134 #endif
1135 
1136 failed_many:
1137 	exit_errx("-x: too many sections for the given options");
1138 
1139 failed_sect:
1140 	exit_errx("-x: unknown section \"%s\" for the given options", section);
1141 
1142 #if defined(WITH_RULES) && defined(WITH_LIMITS)
1143 failed_subs:
1144 	exit_errx("-x: unknown subsection \"%s\" for the given options",
1145 	    subsection);
1146 #endif
1147 }
1148 
1149 /*
1150  * Implement "-u user" and "-g group" options.
1151  */
1152 static void
change_user(const char * u_name,const char * g_name)1153 change_user(const char *u_name, const char *g_name)
1154 {
1155 	const struct passwd *pwd;
1156 	const struct group *grp;
1157 	char *endptr;
1158 	uid_t uid;
1159 	gid_t gid;
1160 
1161 	if (u_name == NULL && g_name == NULL)
1162 		return;
1163 
1164 	xvlogmsgx = vlogmsgx_stderr;
1165 
1166 	if (g_name != NULL) {
1167 		/* -g group */
1168 		errno = 0;
1169 		grp = getgrnam(g_name);
1170 		if (grp == NULL) {
1171 			if (errno != 0)
1172 				exit_err("change_user: getgrnam(%s)", g_name);
1173 			if (!isdigit((unsigned char)*g_name))
1174 				exit_errx("cannot find group %s", g_name);
1175 			errno = 0;
1176 			gid = (gid_t)strtoul(g_name, &endptr, 10);
1177 			if (errno != 0)
1178 				exit_err("change_user: strtoul");
1179 			if (g_name == endptr || *endptr != '\0')
1180 				exit_errx("cannot find group %s", g_name);
1181 		} else
1182 			gid = grp->gr_gid;
1183 		if (setgroups(1, &gid) < 0)
1184 			exit_err("change_user: setgroups(1, [%lu])",
1185 			    (unsigned long)gid);
1186 	}
1187 
1188 	if (u_name != NULL) {
1189 		/* -u user */
1190 		errno = 0;
1191 		pwd = getpwnam(u_name);
1192 		if (pwd == NULL) {
1193 			if (errno != 0)
1194 				exit_err("change_user: getpwnam(%s)", u_name);
1195 			if (!isdigit((unsigned char)*u_name))
1196 				exit_errx("cannot find user %s", u_name);
1197 			if (g_name == NULL)
1198 				exit_errx("argument in the -u option is not "
1199 				    "a valid user name");
1200 			errno = 0;
1201 			uid = (uid_t)strtoul(u_name, &endptr, 10);
1202 			if (errno != 0)
1203 				exit_err("change_user: strtoul");
1204 			if (u_name == endptr || *endptr != '\0')
1205 				exit_errx("cannot find user %s", u_name);
1206 		} else {
1207 			uid = pwd->pw_uid;
1208 			if (g_name == NULL) {
1209 				gid = pwd->pw_gid;
1210 				if (setsuppgids(pwd->pw_name, gid) < 0)
1211 					exit_errx("change_user: cannot set "
1212 					    "all groups for user %s", u_name);
1213 			}
1214 		}
1215 	}
1216 
1217 	if (setgid(gid) < 0)
1218 		exit_err("change_user: setgid(%lu)", (unsigned long)gid);
1219 
1220 	if (u_name != NULL && setuid(uid) < 0)
1221 		exit_err("change_user: setuid(%lu)", (unsigned long)uid);
1222 
1223 	/*
1224 	 * To be sure, that descriptors used by getpw*() and getgr*()
1225 	 * functions are closed.
1226 	 */
1227 	endpwent();
1228 	endgrent();
1229 }
1230 
1231 int
main(int argc,char * argv[])1232 main(int argc, char *argv[])
1233 {
1234 	const char *k_optarg;		/* -k signal */
1235 	const char *u_name;		/* -u user */
1236 	const char *g_name;		/* -g group */
1237 	int opt;			/* Current option. */
1238 	int rv;				/* Return value. */
1239 	int action;			/* Return code from ipa_main(). */
1240 	char t_flag;			/* Set if -t. */
1241 	char x_flag;			/* Set if -x. */
1242 
1243 	/* Save the program name. */
1244 	envprogname = strrchr(argv[0], '/');
1245 	if (envprogname != NULL)
1246 		++envprogname;
1247 	else
1248 		envprogname = argv[0];
1249 
1250 	k_optarg = u_name = g_name = NULL;
1251 	t_flag = x_flag = 0;
1252 	opterr = 0;
1253 	while ((opt = getopt(argc, argv, ":c:df:g:hi:k:o:p:tu:vx")) != -1)
1254 		switch (opt) {
1255 		case ':':
1256 			exit_errx("option requires an argument -- %c", optopt);
1257 			/* NOTREACHED */
1258 		case '?':
1259 			exit_errx("illegal option -- %c", optopt);
1260 			/* NOTREACHED */
1261 		case 'c':
1262 			if (*optarg != '/')
1263 				exit_errx("path in the -%c option should be "
1264 				    "absolute", 'c');
1265 			if (chroot(optarg) < 0)
1266 				exit_err("main: chroot(%s)", optarg);
1267 			break;
1268 		case 'd':
1269 			debug = 1;
1270 			break;
1271 		case 'f':
1272 			ipa_conf_file = optarg;
1273 			break;
1274 		case 'g':
1275 			g_name = optarg;
1276 			break;
1277 		case 'h':
1278 			usage();
1279 			return (EXIT_SUCCESS);
1280 		case 'i':
1281 			log_ident = optarg;
1282 			break;
1283 		case 'k':
1284 			k_optarg = optarg;
1285 			break;
1286 		case 'o':
1287 			log_file = optarg;
1288 			if (*log_file != '/')
1289 				exit_errx("path in the -%c option should be "
1290 				    "absolute", 'o');
1291 			break;
1292 		case 'p':
1293 			pid_file_name = optarg;
1294 			if (*pid_file_name != '/')
1295 				exit_errx("path in the -%c option should be "
1296 				    "absolute", 'c');
1297 			break;
1298 		case 't':
1299 			if (t_flag)
1300 				mimic_real_config = 1;
1301 			else
1302 				t_flag = 1;
1303 			break;
1304 		case 'u':
1305 			u_name = optarg;
1306 			break;
1307 		case 'v':
1308 			show_version();
1309 			return (EXIT_SUCCESS);
1310 		case 'x':
1311 			x_flag = 1;
1312 			goto getopt_done;
1313 		default:
1314 			exit_errx("unexpected option -- %c", optopt);
1315 		}
1316 
1317 	if (optind < argc)
1318 		exit_errx("non-switch argument \"%s\"", argv[optind]);
1319 
1320 getopt_done:
1321 	change_user(u_name, g_name);
1322 
1323 	if (k_optarg != NULL) {
1324 		const struct sig_descr *sig;
1325 
1326 		for (sig = sig_descr_tbl; sig->signame != NULL; ++sig)
1327 			if (strcmp(k_optarg, sig->signame) == 0) {
1328 				sendsig(sig->signo);
1329 				return (EXIT_SUCCESS);
1330 			}
1331 		exit_errx("illegal argument \"%s\" for the -k option",
1332 		    k_optarg);
1333 	}
1334 
1335 #ifdef HAVE_DL_INIT
1336 	if (dl_init() != 0)
1337 		exit_errx("main: dl_init failed");
1338 #endif
1339 
1340 	if (setlocale(LC_ALL, "") == NULL)
1341 		exit_err("setlocale");
1342 
1343 	memfunc_pre_init();
1344 
1345 	myuid = getuid();
1346 	mygid = getgid();
1347 
1348 	if (t_flag) {
1349 		if (configure(TEST_PARSING) < 0)
1350 			return (EXIT_FAILURE);
1351 		show_config();
1352 		return (EXIT_SUCCESS);
1353 	}
1354 
1355 	if (x_flag) {
1356 		if (configure(CMD_PARSING) < 0)
1357 			return (EXIT_FAILURE);
1358 		return (run_opt_cmds(argc, argv) < 0 ?
1359 		    EXIT_FAILURE : EXIT_SUCCESS);
1360 	}
1361 
1362 	create_pid_file();
1363 
1364 	tzset();
1365 
1366 	init_log();
1367 	open_log();
1368 
1369 	if (fd_init() < 0) {
1370 		logbt("main");
1371 		return (EXIT_FAILURE);
1372 	}
1373 
1374 	logmsgx(IPA_LOG_INFO, IPA_NAME " (version " PACKAGE_VERSION ") "
1375 	    "started by UID %lu, GID %lu", (unsigned long)myuid,
1376 	    (unsigned long)mygid);
1377 #ifdef WITH_PTHREAD
1378 	logmsgx(IPA_LOG_INFO, "thread-safe mode");
1379 #endif
1380 #ifdef WITH_MEMFUNC_DEBUG
1381 	logmsgx(IPA_LOG_INFO, "memfunc debugging is enabled");
1382 #endif
1383 
1384 	rv = EXIT_FAILURE;
1385 
1386 	if (!debug) {
1387 		if (bg_init() < 0) {
1388 			logmsgx(IPA_LOG_ERR, "cannot run in the background");
1389 			goto out_exit;
1390 		}
1391 		logmsgx(IPA_LOG_INFO, "started in background mode");
1392 	} else
1393 		logmsgx(IPA_LOG_INFO, "started in foreground mode");
1394 
1395 	if (lock_pid_file() < 0) {
1396 		logmsgx(IPA_LOG_ERR, "cannot lock PID-file");
1397 		goto out_exit;
1398 	}
1399 
1400 	logmsgx(IPA_LOG_INFO, "maximum number of open descriptors is %d%s",
1401 	    nofile_max, nofile_max >= 0 ? "" : " (no limit)");
1402 
1403 	if (configure(CONFIG_PARSING) < 0) {
1404 		logmsgx(IPA_LOG_ERR, "main: configure failed");
1405 		goto out_exit_unlink;
1406 	}
1407 
1408 	/* Startup. */
1409 	if (sig_init_1() < 0)
1410 		goto out_deinit;
1411 
1412 	logmsgx(IPA_LOG_INFO, "startup... all signals are blocked");
1413 
1414 	if (init_all() < 0)
1415 		goto out_deinit;
1416 
1417 	if (run_all_cmds(RC_STARTUP) < 0)
1418 		goto out_deinit;
1419 
1420 	/*
1421 	 * If second stage of signal initialization succeeded,
1422 	 * then begin to work.
1423 	 */
1424 	if (sig_init_2() == 0) {
1425 		/* Working... */
1426 		for (;;) {
1427 			action = ipa_main();
1428 			if (action < 0) {
1429 				/* Some error occurred. */
1430 				break;
1431 			}
1432 			if (action == 0) {
1433 				/* Normal exit. */
1434 				rv = EXIT_SUCCESS;
1435 				break;
1436 			}
1437 
1438 			/* Reconfiguration. */
1439 			rv = EXIT_SUCCESS;
1440 			if (deinit_all() < 0)
1441 				rv = EXIT_FAILURE;
1442 			if (free_all() < 0)
1443 				rv = EXIT_FAILURE;
1444 			if (rv != EXIT_SUCCESS)
1445 				goto out_exit_unlink;
1446 			rv = EXIT_FAILURE;
1447 			if (configure(RECONFIG_PARSING) < 0)
1448 				goto out_exit_unlink;
1449 
1450 			/*
1451 			 * Now initialize everything and start to work
1452 			 * with new configuration.
1453 			 */
1454 			logmsgx(IPA_LOG_INFO, "using new configuration");
1455 			if (init_all() < 0)
1456 				goto out_deinit;
1457 		}
1458 	}
1459 
1460 	/* Shutdown. */
1461 	logmsgx(IPA_LOG_INFO, "shutdown... all signals are ignored");
1462 	if (run_all_cmds(RC_SHUTDOWN) < 0)
1463 		rv = EXIT_FAILURE;
1464 
1465 out_deinit:
1466 	/* Deiniting. */
1467 	if (deinit_all() < 0)
1468 		rv = EXIT_FAILURE;
1469 	if (free_all() < 0)
1470 		rv = EXIT_FAILURE;
1471 
1472 out_exit_unlink:
1473 	/* Unlink PID file, since it is locked by this process. */
1474 	unlink_pid_file();
1475 
1476 out_exit:
1477 	/* Exiting. */
1478 #ifdef HAVE_DL_EXIT
1479 	if (dl_exit() != 0) {
1480 		logmsgx(IPA_LOG_ERR, "main: dl_exit failed");
1481 		rv = EXIT_FAILURE;
1482 	}
1483 #endif
1484 	log_stdall();
1485 	logmsgx(IPA_LOG_INFO, IPA_NAME " (version " PACKAGE_VERSION ") "
1486 	    "exited, return code is %d", rv);
1487 
1488 	close_log();
1489 	return (rv);
1490 }
1491