1 /*-
2  * Copyright (c) 1999-2004, 2009 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_main.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/time.h>
36 #include <sys/wait.h>
37 
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <signal.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <time.h>
45 #include <unistd.h>
46 
47 #ifdef WITH_PTHREAD
48 # include <pthread.h>
49 #endif
50 
51 #include "ipa_mod.h"
52 
53 #include "queue.h"
54 
55 #include "dlapi.h"
56 #include "confcommon.h"
57 #include "memfunc.h"
58 #include "parser.h"
59 
60 #include "ipa_ac.h"
61 #include "ipa_db.h"
62 #include "ipa_ctl.h"
63 #include "ipa_log.h"
64 #include "ipa_time.h"
65 
66 #include "ipa_cmd.h"
67 #include "ipa_conf.h"
68 #include "ipa_main.h"
69 #include "ipa_rules.h"
70 #include "ipa_autorules.h"
71 
72 #ifndef WCOREDUMP
73 # define WCOREDUMP(x) (0)
74 #endif
75 
76 char		debug = 0;		/* -d */
77 
78 uid_t		myuid;			/* Current process UID. */
79 gid_t		mygid;			/* Current process GID. */
80 
81 const uint64_t	uint64_zero = 0;
82 const uint64_t	uint64_max = UINT64_MAX;
83 
84 ipa_mem_type	*m_anon;		/* Anonymous memory allocations. */
85 ipa_mem_type	*m_tmp;			/* Temporary memory allocations. */
86 
87 sigset_t	sigmask;		/* Signal mask. */
88 sigset_t	zeromask;		/* Zero mask for unblocking signals. */
89 
90 int		stdout_read;		/* Pipe's read-end for stdout. */
91 int		stderr_read;		/* Pipe's read-end for stderr. */
92 
93 char		dump_flag = 0;		/* Set if ctl command "dump". */
94 char		freeze_flag = 0;	/* Set if ctl command "freeze". */
95 
96 static time_t	newdaytime;		/* Time of a new day. */
97 
98 unsigned int	main_check_sec;		/* Time in seconds since midnight
99 					   when to wakeup. */
100 
101 static fd_set	readfdset;		/* Read descriptors set. */
102 static int	maxfd;			/* Maximum #descr + 1 in readfdset. */
103 
104 /*
105  * All sig_*() functions write one character to sig_pipe, since its
106  * write-end is marked as non-blocking, they cannot block in write().
107  * According to SUSv3 a signal handler can assign value only to
108  * a variable of type 'volatile sig_atomic_t'.
109  */
110 
111 /* Pipe for IPC from async signals handlers. */
112 static int	sig_pipe[2];
113 
114 /* Set if need to check all active rules. */
115 volatile sig_atomic_t need_check_flag = 0;
116 
117 /* Different flags for signal handlers. */
118 static volatile sig_atomic_t signal_flag = 0;
119 static volatile sig_atomic_t reconf_flag = 0;
120 static volatile sig_atomic_t shutdown_flag = 0;
121 static volatile sig_atomic_t sigchld_flag = 0;
122 
123 /* ac_get_rule_stat() generation counter. */
124 static unsigned int stat_generation;
125 
126 #ifdef WITH_PTHREAD
127 int
Sigprocmask(int how,const sigset_t * set,sigset_t * oset)128 Sigprocmask(int how, const sigset_t *set, sigset_t *oset)
129 {
130 	int error;
131 
132 	error = pthread_sigmask(how, set, oset);
133 	if (error != 0) {
134 		errno = error;
135 		return (-1);
136 	}
137 	return (0);
138 }
139 #endif
140 
141 /*
142  * Mark a file descriptor as non-blocking.
143  */
144 int
fd_set_nonblock(int fd)145 fd_set_nonblock(int fd)
146 {
147 	int val;
148 
149 	val = fcntl(fd, F_GETFL, 0);
150 	if (val < 0) {
151 		logmsg(IPA_LOG_ERR, "fd_set_nonblock: fcntl(%d, F_GETFL)", fd);
152 		return (-1);
153 	}
154 	if (fcntl(fd, F_SETFL, val | O_NONBLOCK) < 0) {
155 		logmsg(IPA_LOG_ERR, "fd_set_nonblock: fcntl(%d, F_SETFL)", fd);
156 		return (-1);
157 	}
158 	return (0);
159 }
160 
161 static int
init_sig_pipe(void)162 init_sig_pipe(void)
163 {
164 	static char called_before = 0;
165 
166 	if (called_before) {
167 		/* Close previously opened pipe. */
168 		if (close(sig_pipe[0]) < 0 || close(sig_pipe[1]) < 0) {
169 			logmsg(IPA_LOG_ERR, "init_sig_pipe: close");
170 			return (-1);
171 		}
172 	} else
173 		called_before = 1;
174 
175 	/* Create new pipe for IPC from signal handlers. */
176 	if (pipe(sig_pipe) < 0) {
177 		logmsg(IPA_LOG_ERR, "init_sig_pipe: pipe");
178 		return (-1);
179 	}
180 
181 	/* Mark write- and read-ends as non-blocking. */
182 	if (fd_set_nonblock(sig_pipe[0]) < 0 ||
183 	    fd_set_nonblock(sig_pipe[1]) < 0) {
184 		logbt("init_sig_pipe");
185 		return (-1);
186 	}
187 
188 	return (0);
189 }
190 
191 /*
192  * Initialize everything.
193  */
194 int
init_all(void)195 init_all(void)
196 {
197 	if (init_time_values() < 0)
198 		goto failed;
199 
200 	/* Flush mask of read-ready descriptors. */
201 	FD_ZERO(&readfdset);
202 	maxfd = 0;
203 
204 	/* Initialize signal IPC pipe, add read-end to readfdset. */
205 	if (init_sig_pipe() < 0)
206 		goto failed;
207 	FD_SET(sig_pipe[0], &readfdset);
208 	if (maxfd < sig_pipe[0])
209 		maxfd = sig_pipe[0];
210 
211 	/* Set pipes for stdout and stderr om readfdset. */
212 	FD_SET(stdout_read, &readfdset);
213 	if (maxfd < stdout_read)
214 		maxfd = stdout_read;
215 	if (!debug) {
216 		FD_SET(stderr_read, &readfdset);
217 		if (maxfd < stderr_read)
218 			maxfd = stderr_read;
219 	}
220 
221 	/* Initialize ctl. */
222 	if (ctl_enable) {
223 		if (init_ctl() < 0)
224 			goto failed;
225 		FD_SET(ctl_listenfd, &readfdset);
226 		if (ctl_listenfd > maxfd)
227 			maxfd = ctl_listenfd;
228 	}
229 
230 	++maxfd;
231 
232 	if (pre_init_ac_mods() < 0)
233 		goto failed;
234 	if (pre_init_db_mods() < 0)
235 		goto failed;
236 
237 #ifdef WITH_AUTORULES
238 	if (init_autorules() < 0)
239 		goto failed;
240 #endif
241 
242 #ifdef WITH_RULES
243 	/* Initialize active rules queue. */
244 	init_active_rules();
245 
246 	/* Initialize acg_mzone, needed only if there are static rules. */
247 	if (!SLIST_EMPTY(&acg_list)) {
248 		acg_mzone = mzone_init(MZONE_NAME(acg), "Reverse ac_gather_*",
249 		    0, sizeof(struct acg), ACG_NSIZE, ACG_NALLOC);
250 		if (acg_mzone == NULL)
251 			goto failed;
252 		has_ac_gather = 1;
253 	} else {
254 		acg_mzone = NULL;
255 		has_ac_gather = 0;
256 	}
257 
258 	/* Initialize static rules, limits and thresholds. */
259 	if (init_rules(1) < 0)
260 		goto failed;
261 
262 	if (acg_mzone != NULL) {
263 		/*
264 		 * If any rule was not caught by ac_gather_* and
265 		 * there no autorules, then can deinitialize acg_mzone.
266 		 */
267 		if (mzone_is_empty(acg_mzone) && nautorules == 0) {
268 			mzone_deinit(acg_mzone);
269 			acg_mzone = NULL;
270 		}
271 	}
272 #endif /* WITH_RULES */
273 
274 	if (init_ac_mods() < 0)
275 		goto failed;
276 	if (init_db_mods() < 0)
277 		goto failed;
278 
279 	return (0);
280 
281 failed:
282 	logbt("init_all");
283 	return (-1);
284 }
285 
286 /*
287  * Deinitialize everything ignoring errors.  Do the same steps as in
288  * init_all(), but in reverse order.
289  */
290 int
deinit_all(void)291 deinit_all(void)
292 {
293 	int rv;
294 
295 	rv = 0;
296 	if (deinit_rules() < 0)
297 		rv = -1;
298 #ifdef WITH_AUTORULES
299 	if (deinit_autorules() < 0)
300 		rv = -1;
301 #endif
302 	if (deinit_ac_mods() < 0)
303 		rv = -1;
304 	if (deinit_db_mods() < 0)
305 		rv = -1;
306 	if (deinit_ctl() < 0)
307 		rv = -1;
308 	if (rv < 0) {
309 		logbt("deinit_all");
310 		return (-1);
311 	}
312 
313 	/* Validate internal structures after deinitialization. */
314 	if (!TAILQ_EMPTY(&rules_active)) {
315 		logmsgx(IPA_LOG_ERR, "internal error: deinit_all: "
316 		    "rules_active is not empty");
317 		return (-1);
318 	}
319 	if (!TAILQ_EMPTY_HEAD_VALID(&rules_active)) {
320 		logmsgx(IPA_LOG_ERR, "internal error: deinit_all: empty "
321 		    "rules_active list is in inconsistent state");
322 		return (-1);
323 	}
324 	if (!TAILQ_EMPTY(&rules_inactive)) {
325 		logmsgx(IPA_LOG_ERR, "internal error: deinit_all: "
326 		    "rules_inactive is not empty");
327 		return (-1);
328 	}
329 	if (!TAILQ_EMPTY_HEAD_VALID(&rules_inactive)) {
330 		logmsgx(IPA_LOG_ERR, "internal error: deinit_all: empty "
331 		    "rules_inactive list is in inconsistent state");
332 		return (-1);
333 	}
334 #ifdef WITH_ANY_LIMITS
335 	if (!wpid_hash_is_empty()) {
336 		logmsgx(IPA_LOG_ERR, "internal error: deinit_all: wpid_hash "
337 		    "is not empty");
338 		return (-1);
339 	}
340 #endif
341 	return (0);
342 }
343 
344 /*
345  * Free all resources allocated during work and validate internal
346  * structures after releasing memory.
347  */
348 int
free_all(void)349 free_all(void)
350 {
351 	unsigned int n;
352 	int rv;
353 
354 	if (shell_path != shell_path_default)
355 		mem_free(shell_path, m_parser);
356 	if (shell_arg1 != shell_arg1_default)
357 		mem_free(shell_arg1, m_parser);
358 
359 	free_rules();
360 	if (!TAILQ_EMPTY(&rules_list)) {
361 		logmsgx(IPA_LOG_ERR, "internal error: free_all: "
362 		    "rules_list is not empty");
363 		return (-1);
364 	}
365 	if (!TAILQ_EMPTY_HEAD_VALID(&rules_list)) {
366 		logmsgx(IPA_LOG_ERR, "internal error: free_all: "
367 		    "empty rules_list list is in inconsistent state");
368 		return (-1);
369 	}
370 	if (!rules_hash_is_empty()) {
371 		logmsgx(IPA_LOG_ERR, "internal error: free_all: "
372 		    "rules_hash is not empty");
373 		return (-1);
374 	}
375 	n = mzone_nused(rule_mzone);
376 	if (n != 0) {
377 		logmsgx(IPA_LOG_ERR, "internal error: free_all: "
378 		    "rule_mzone is not empty: %u", n);
379 		return (-1);
380 	}
381 	mzone_deinit(rule_mzone);
382 
383 	free_rulepats();
384 
385 #ifdef WITH_LIMITS
386 	if (limit_mzone != NULL) {
387 		n = mzone_nused(limit_mzone);
388 		if (n != 0) {
389 			logmsgx(IPA_LOG_ERR, "internal error: free_all: "
390 			    "limit_mzone is not empty: %u", n);
391 			return (-1);
392 		}
393 		mzone_deinit(limit_mzone);
394 	}
395 #endif
396 #ifdef WITH_SUBLIMITS
397 	if (sublimit_mzone != NULL) {
398 		n = mzone_nused(sublimit_mzone);
399 		if (n != 0) {
400 			logmsgx(IPA_LOG_ERR, "internal error: free_all: "
401 			    "sublimit_mzone is not empty: %u", n);
402 			return (-1);
403 		}
404 		mzone_deinit(sublimit_mzone);
405 	}
406 #endif
407 #ifdef WITH_THRESHOLDS
408 	if (threshold_mzone != NULL) {
409 		n = mzone_nused(threshold_mzone);
410 		if (n != 0) {
411 			logmsgx(IPA_LOG_ERR, "internal error: free_all: "
412 			    "threshold_mzone is not empty: %u", n);
413 			return (-1);
414 		}
415 		mzone_deinit(threshold_mzone);
416 	}
417 #endif
418 
419 	mzone_deinit(tevent_mzone);
420 
421 	if (cmd_mzone != NULL) {
422 		cmds_free(&cmds_global[RC_STARTUP]);
423 		cmds_free(&cmds_global[RC_SHUTDOWN]);
424 		n = mzone_nused(cmd_mzone);
425 		if (n != 0) {
426 			logmsgx(IPA_LOG_ERR, "internal error: free_all: "
427 			    "cmd_mzone is not empty: %u", n);
428 			return (-1);
429 		}
430 		mzone_deinit(cmd_mzone);
431 	}
432 
433 	free_worktimes();
434 
435 #ifdef WITH_RULES
436 	if (acg_mzone != NULL) {
437 		n = mzone_nused(acg_mzone);
438 		if (n != 0) {
439 			logmsgx(IPA_LOG_ERR, "internal error: free_all: "
440 			    "acg_mzone is not empty: %u", n);
441 			return (-1);
442 		}
443 		mzone_deinit(acg_mzone);
444 	}
445 	if (ictl_mzone != NULL) {
446 		n = mzone_nused(ictl_mzone);
447 		if (n != 0) {
448 			logmsgx(IPA_LOG_ERR, "internal error: free_all: "
449 			    "cmd_ictl is not empty: %u", n);
450 			return (-1);
451 		}
452 		mzone_deinit(ictl_mzone);
453 	}
454 #endif
455 
456 	free_ac_lists();
457 	free_db_lists();
458 
459 	mem_free(parser_str_buf, m_parser);
460 	parser_str_buf = NULL;
461 
462 	memfunc_deinit_1(0);
463 
464 	rv = unload_all_mods();
465 	if (rv < 0)
466 		logmsgx(IPA_LOG_ERR, "free_all: cannot correctly unload "
467 		    "all modules");
468 
469 	memfunc_deinit_2(0);
470 	return (rv);
471 }
472 
473 /*
474  * Some of children exited.
475  */
476 static int
check_child(void)477 check_child(void)
478 {
479 #ifdef WITH_ANY_LIMITS
480 	struct wpid *wpid;
481 #endif
482 	pid_t pid;
483 	int status;
484 
485 	/* SUSv3 claims that pid_t is signed integer, so can use `>' here. */
486 	while ((pid = waitpid((pid_t)0, &status, WNOHANG)) > 0) {
487 #ifdef WITH_ANY_LIMITS
488 		/*
489 		 * Find appropriate wpid structure for exited child and
490 		 * remove it from the hash table (zeroing its pid field).
491 		 * Sometimes wpid structure does not exist for child's PID,
492 		 * since we already use it for another child for the same
493 		 * section or reconfiguration occurred.
494 		 */
495 		wpid = wpid_lookup_pid(pid);
496 		if (wpid != NULL) {
497 			if (wpid->debug_exec)
498 				log_term_wpid(wpid);
499 			wpid_hash_rem(wpid);
500 		} else
501 #endif
502 		if (global_debug_exec)
503 			logdbg("child %ld exited", (long)pid);
504 
505 		/* The child in normal situation should return EXIT_SUCCESS. */
506 		if (WIFEXITED(status)) {
507 			if (WEXITSTATUS(status) != EXIT_SUCCESS) {
508 				logmsgx(IPA_LOG_ERR, "check_child: exit "
509 				    "status from child %ld is %d", (long)pid,
510 				    WEXITSTATUS(status));
511 				return (-1);
512 			}
513 		} else if (WIFSIGNALED(status)) {
514 			logmsgx(IPA_LOG_ERR, "check_child: abnormal "
515 			    "termination of child %ld signal %d%s", (long)pid,
516 			    WTERMSIG(status), WCOREDUMP(status) ?
517 			    " (core file generated)" : "");
518 			return (-1);
519 		} else {
520 			logmsgx(IPA_LOG_ERR, "check_child: termination of "
521 			    "child %ld unknown status", (long)pid);
522 			return (-1);
523 		}
524 	}
525 
526 	if (pid == (pid_t)-1 && errno != ECHILD) {
527 		logmsg(IPA_LOG_ERR, "check_child: waitpid");
528 		return (-1);
529 	}
530 	return (0);
531 }
532 
533 /*
534  * This routine is called when a new day came (newday_flag != 0),
535  * or when there is a reconfiguration, or when there are some
536  * problems with system time or local time (newday_flag == 0).
537  * The first_call flag is used only for debugging.
538  */
539 static int
newday(int first_call)540 newday(int first_call)
541 {
542 	ipa_tm curdate_save;
543 	struct tm tm;
544 	struct rule *rule;
545 	struct tevent *tevent;
546 
547 	main_check_sec = SECONDS_IN_DAY;
548 	curdate_save = curdate;
549 
550 	/* Get time of a new day, month can be out of range. */
551 	tm = curdate;
552 	tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
553 	tm.tm_mday++;
554 	tm.tm_mon--;
555 	tm.tm_year -= 1900;
556 	tm.tm_isdst = -1;
557 
558 	/* mktime(3) has to fix all incorrect ranges. */
559 	newdaytime = mktime(&tm);
560 	if (newdaytime == (time_t)-1) {
561 		/* This should not happen for 00:00:00. */
562 		logmsg(IPA_LOG_ERR, "newday: mktime failed");
563 		return (-1);
564 	}
565 
566 	if (newday_flag) {
567 		/*
568 		 * We simulate that it is 00:00:00 now, but this can be
569 		 * incorrect, nevertheless in most cases it should be almost
570 		 * correct, anyway this is reflected in the database only,
571 		 * everywhere cursec is used to compare some event's time
572 		 * with current time.
573 		 */
574 		curdate.tm_sec = curdate.tm_min = curdate.tm_hour = 0;
575 	}
576 
577 	worktimes_newday(!first_call);
578 
579 	/* Initialize all tevents. */
580 	SLIST_FOREACH(tevent, &tevents_list, link) {
581 		tevent->event_sec = 0;
582 		while ((tevent->event_sec += tevent->event_step) <= cursec)
583 			if (debug_time > 1)
584 				logdbg("newday: skipping time event %s for "
585 				    "time interval %s",
586 				    sec_str(tevent->event_sec),
587 				    time_str(tevent->event_step));
588 	}
589 
590 	TAILQ_FOREACH(rule, &rules_list, list) {
591 		rule->check_sec = newday_flag ?
592 		    rule->update_tevent->event_sec : cursec;
593 #ifdef WITH_LIMITS
594 		if (limits_newday(rule) < 0)
595 			goto failed;
596 #endif
597 #ifdef WITH_THRESHOLDS
598 		if (thresholds_newday(rule) < 0)
599 			goto failed;
600 #endif
601 		if (WT_IS_INACTIVE(rule->worktime)) {
602 			if (RULE_IS_ACTIVE(rule))
603 				if (set_rule_inactive(rule) < 0)
604 					goto failed;
605 		} else {
606 			if (RULE_IS_INACTIVE(rule)) {
607 				if (set_rule_active(rule) < 0)
608 					goto failed;
609 			} else if (rule->newstat) {
610 				/*
611 				 * Since during startup ictl command could be
612 				 * called and a rule can have ac_gather_*
613 				 * parameters, then need to append current
614 				 * value of rule's counter.
615 				 */
616 				if (db_append_rule(rule, &rule->cnt, 0) < 0)
617 					goto failed;
618 			} else {
619 				if (db_append_rule(rule, &uint64_zero, 1) < 0)
620 					goto failed;
621 			}
622 			if (rule->check_sec > rule->worktime->inactive_sec)
623 				rule->check_sec = rule->worktime->inactive_sec;
624 			if (rule->check_sec > rule->append_sec)
625 				rule->check_sec = rule->append_sec;
626 		}
627 		if (main_check_sec > rule->check_sec)
628 			main_check_sec = rule->check_sec;
629 	}
630 
631 	sort_inactive_rules();
632 
633 	if (main_check_sec > rules_inactive_check_sec)
634 		main_check_sec = rules_inactive_check_sec;
635 
636 #ifdef WITH_AUTORULES
637 	if (autorules_newday() < 0)
638 		goto failed;
639 	if (main_check_sec > autorules_active_check_sec)
640 		main_check_sec = autorules_active_check_sec;
641 	if (main_check_sec > autorules_inactive_check_sec)
642 		main_check_sec = autorules_inactive_check_sec;
643 #endif
644 
645 	curdate = curdate_save;
646 	return (0);
647 
648 failed:
649 	logbt("newday");
650 	return (-1);
651 }
652 
653 /*
654  * Do accounting for one rule.
655  */
656 static int
do_ac_rule(struct rule * rule)657 do_ac_rule(struct rule *rule)
658 {
659 	uint64_t chunk;
660 	const struct ac_elem *ac;
661 	int addition;
662 #ifdef WITH_ANY_LIMITS
663 	unsigned int check_sec;
664 #endif
665 
666 	/* Get statistics from all modules. */
667 	STAILQ_FOREACH(ac, rule->ac_list, link) {
668 		if (ac->ipa_ac_mod->ac_get_rule_stat(stat_generation,
669 		    rule->newstat, rule->no, &addition, &chunk) < 0)
670 			goto failed;
671 		if (rule->newstat) {
672 			/*
673 			 * If this is a new statistics, then ignore chunk
674 			 * from the module.
675 			 */
676 			if (chunk > 0)
677 				logmsgx(IPA_LOG_ERR, "module %s: rule %s: "
678 				    "ac_get_rule_stat(1) returned non-zero "
679 				    "chunk %"PRIu64", it will be ignoreed",
680 				    ac->mod_file, rule->name, chunk);
681 			chunk = 0;
682 		} else {
683 			if (addition) {
684 				if (rule_add_chunk(rule, &chunk) < 0)
685 					goto failed;
686 			} else {
687 				if (rule_sub_chunk(rule, &chunk) < 0)
688 					goto failed;
689 			}
690 		}
691 	}
692 
693 	/* Update statistics and schedule next checks. */
694 	rule->newstat = 0;
695 	if (db_update_rule(rule, &rule->cnt) < 0)
696 		goto failed;
697 	rule->check_sec = rule->update_tevent->event_sec;
698 
699 #ifdef WITH_LIMITS
700 	if (!STAILQ_EMPTY(&rule->limits)) {
701 		if (check_limits(rule, &check_sec) < 0)
702 			goto failed;
703 		if (rule->check_sec > check_sec)
704 			rule->check_sec = check_sec;
705 	}
706 #endif
707 #ifdef WITH_THRESHOLDS
708 	if (!STAILQ_EMPTY(&rule->thresholds)) {
709 		if (check_thresholds(rule, &check_sec) < 0)
710 			goto failed;
711 		if (rule->check_sec > check_sec)
712 			rule->check_sec = check_sec;
713 	}
714 #endif
715 
716 	if (rule->append_sec <= cursec && !newday_flag) {
717 		/*
718 		 * Append a new record, but do not append it
719 		 * at the end of the day.
720 		 */
721 		if (db_append_rule(rule, &uint64_zero, 1) < 0)
722 			goto failed;
723 	}
724 	if (WT_IS_INACTIVE(rule->worktime)) {
725 		/* Rule became inactive. */
726 		if (!newday_flag)
727 			if (set_rule_inactive(rule) < 0)
728 				goto failed;
729 	} else {
730 		/* Rule is still active. */
731 		if (rule->check_sec > rule->worktime->inactive_sec)
732 			rule->check_sec = rule->worktime->inactive_sec;
733 		if (rule->check_sec > rule->append_sec)
734 			rule->check_sec = rule->append_sec;
735 		if (main_check_sec > rule->check_sec)
736 			main_check_sec = rule->check_sec;
737 		if (debug_time)
738 			logdbg("do_ac_rule: rule %s: next check_sec %s",
739 			    rule->name, sec_str(rule->check_sec));
740 	}
741 	return (0);
742 
743 failed:
744 	logbt("do_ac_rule");
745 	return (-1);
746 }
747 
748 /*
749  * Do accounting for all rules, limits, sublimits and thresholds,
750  * check limits and thresholds events here, calculate new main_check_sec.
751  */
752 static int
do_ac(void)753 do_ac(void)
754 {
755 	struct rule *rule, *rule_next;
756 	struct tevent *tevent;
757 
758 	main_check_sec = SECONDS_IN_DAY;
759 
760 	/* Schedule next tevents. */
761 	SLIST_FOREACH(tevent, &tevents_list, link) {
762 		if (tevent->event_sec > cursec)
763 			continue;
764 		/* Need to adjust time event. */
765 		while ((tevent->event_sec += tevent->event_step) <= cursec)
766 			if (debug_time > 1)
767 				logdbg("do_ac: skipping time event %s for "
768 				    "time interval %s",
769 				    sec_str(tevent->event_sec),
770 				    time_str(tevent->event_step));
771 	}
772 
773 	/* Check worktimes events. */
774 	if (worktimes_check_sec <= cursec)
775 		worktimes_check();
776 
777 	/* Check inactive rules. */
778 	if (rules_inactive_check_sec <= cursec)
779 		if (check_inactive_rules() < 0)
780 			goto failed;
781 
782 #ifdef WITH_AUTORULES
783 	/* Check inactive autorules. */
784 	if (autorules_inactive_check_sec <= cursec)
785 		if (check_inactive_autorules() < 0)
786 			goto failed;
787 #endif
788 
789 	TAILQ_FOREACH_SAFE(rule, &rules_active, queue, rule_next) {
790 		if (debug_time)
791 			logdbg("do_ac: rule %s: check_sec %s",
792 			    rule->name, sec_str(rule->check_sec));
793 		if (rule->check_sec > cursec) {
794 			/* It is not a time to check this rule. */
795 			if (main_check_sec > rule->check_sec)
796 				main_check_sec = rule->check_sec;
797 		} else if (do_ac_rule(rule) < 0)
798 			goto failed;
799 	}
800 
801 	if (main_check_sec > rules_inactive_check_sec)
802 		main_check_sec = rules_inactive_check_sec;
803 
804 #ifdef WITH_AUTORULES
805 	if (autorules_active_check_sec <= cursec)
806 		if (check_active_autorules() < 0)
807 			goto failed;
808 	if (main_check_sec > autorules_active_check_sec)
809 		main_check_sec = autorules_active_check_sec;
810 	if (main_check_sec > autorules_inactive_check_sec)
811 		main_check_sec = autorules_inactive_check_sec;
812 #endif
813 
814 	return (0);
815 
816 failed:
817 	logbt("do_ac");
818 	return (-1);
819 }
820 
821 /*
822  * Do accounting when some time related problem occurred.
823  */
824 static int
do_ac_prev(void)825 do_ac_prev(void)
826 {
827 	logmsgx(IPA_LOG_WARNING, "saving current statistics with old "
828 	    "timestamps...");
829 
830 	/* Force updating of all statistics with old timestamps. */
831 	set_rules_for_check();
832 	if (do_ac() < 0)
833 		goto failed;
834 
835 	/* Set current values for time related variables. */
836 	cursec = cursec_new;
837 	curdate = curdate_new;
838 	curwday = curdate.tm_wday;
839 
840 	/* Reinitialize limits and thresholds. */
841 	if (init_rules(0) < 0)
842 		goto failed;
843 
844 	logmsgx(IPA_LOG_WARNING, "appending new records for all rules...");
845 	if (newday(0) < 0)
846 		goto failed;
847 
848 	/* Wakeup immediatelly. */
849 	main_check_sec = cursec;
850 	return (0);
851 
852 failed:
853 	logbt("do_ac_prev");
854 	return (-1);
855 }
856 
857 /*
858  * Do accounting at the end of day (change of time is not more than one day).
859  */
860 static int
do_ac_newday(void)861 do_ac_newday(void)
862 {
863 	double d;
864 
865 	if (curdate_new.tm_mday == curdate.tm_mday) {
866 		logmsgx(IPA_LOG_ERR, "internal error: do_ac_newday: "
867 		    "new day was expected");
868 		return (-1);
869 	}
870 
871 	/* A new day came. */
872 	if (debug_time)
873 		logdbg("do_ac_newday: new day");
874 
875 	d = difftime(curtime, newdaytime);
876 	if (d > sensitive_time)
877 		logmsgx(IPA_LOG_WARNING, "new day came, but woke too late: "
878 		    "delta %.0fs is greater than \"sensitive_time\" %us",
879 		    d, sensitive_time);
880 
881 	/* Make fake previous_day/24:00:00 time, curwday is not changed. */
882 	curdate.tm_sec = curdate.tm_min = 0;
883 	curdate.tm_hour = HOURS_IN_DAY;
884 	cursec = SECONDS_IN_DAY;
885 
886 	/* And update all active rules. */
887 	newday_flag = 1;
888 	set_rules_for_check();
889 	if (do_ac() < 0)
890 		goto failed;
891 
892 	/* Set current values for time related variables. */
893 	cursec = cursec_new;
894 	curdate = curdate_new;
895 	curwday = curdate.tm_wday;
896 	if (newday(0) < 0)
897 		goto failed;
898 	newday_flag = 0;
899 	return (0);
900 
901 failed:
902 	logbt("do_ac_newday");
903 	return (-1);
904 }
905 
906 /*
907  * Do accounting (usual function call at the same day).
908  */
909 static int
do_ac_curr(void)910 do_ac_curr(void)
911 {
912 	/* Set current values for time related variables. */
913 	cursec = cursec_new;
914 	curdate = curdate_new;
915 
916 	if (debug_time) {
917 		logdbg("do_ac_curr: cursec %s", sec_str(cursec));
918 		logdbg("do_ac_curr: main_check_sec %s",
919 		    sec_str(main_check_sec));
920 	}
921 
922 	if (cursec > main_check_sec) {
923 		unsigned int d;
924 
925 		d = cursec - main_check_sec;
926 		if (d > sensitive_time) {
927 			logmsgx(IPA_LOG_WARNING, "time changed too quickly "
928 			    "during sleeping or woke too late: delta %s is "
929 			    "greater than \"sensitive_time\" %us",
930 			    time_str(d), sensitive_time);
931 			logmsgx(IPA_LOG_WARNING, "timestamps in database "
932 			    "will be inaccurate");
933 		}
934 		if (d <= FAKE_TIME_DELTA) {
935 			/*
936 			 * Fake time, anyway timestamps are fake
937 			 * in most cases.
938 			 */
939 			cursec = main_check_sec;
940 			sec_to_time(cursec, &curdate);
941 		}
942 	}
943 
944 	if (do_ac() < 0) {
945 		logbt("do_ac_curr");
946 		return (-1);
947 	}
948 	return (0);
949 }
950 
951 static int
dump_finished(void)952 dump_finished(void)
953 {
954 	logmsgx(IPA_LOG_INFO, "statistics were successfully saved to database");
955 	if (ctl_send_answer() < 0) {
956 		logbt("dump_finished");
957 		return (-1);
958 	}
959 	if (sleep_after_dump != 0) {
960 		logmsgx(IPA_LOG_INFO, "sleeping after \"dump\" and ignoring "
961 		    "signals and commands at least %u seconds...",
962 		    sleep_after_dump);
963 		(void)sleep(sleep_after_dump);
964 	}
965 	return (0);
966 }
967 
968 /*
969  * Sleep and wait for a signal or a ctl command.  Sleep time is adjusted
970  * and after each sleep local time is checked for problems.
971  */
972 static int
suspend_work(void)973 suspend_work(void)
974 {
975 	fd_set rs;
976 	struct timeval tv;
977 	int rv, nready, sleepsec, errno_save;
978 	char ch, adj_sleep;
979 
980 	do {
981 		rv = new_time_values();
982 		if (rv < 0)
983 			goto failed;
984 		adj_sleep = 0;
985 		if (rv == 0 && difftime(curtime, newdaytime) < 0.0) {
986 			/* No time related problem and not a new day. */
987 			cursec = cursec_new;
988 			curdate = curdate_new;
989 			if (main_check_sec > cursec + wakeup_time) {
990 				sleepsec = wakeup_time;
991 				adj_sleep = 1;
992 			} else {
993 				sleepsec = main_check_sec - cursec;
994 				if (sleepsec < 0) {
995 					if (-sleepsec > (int)sensitive_time)
996 						logmsgx(IPA_LOG_WARNING,
997 						    "can't correctly schedule "
998 						    "time events: delta %ds "
999 						    "is greater than "
1000 						    "\"sensitive_time \" %us",
1001 						    sleepsec, sensitive_time);
1002 					sleepsec = 0;
1003 				}
1004 			}
1005 		} else {
1006 			/* Time related problem occurred or a new day came. */
1007 			sleepsec = 0;
1008 		}
1009 
1010 		if (debug_time)
1011 			logdbg("sleeping %s%s, main_check_sec %s",
1012 			    time_str(sleepsec), adj_sleep ? " (adjusted)" : "",
1013 			    sec_str(main_check_sec));
1014 
1015 		tv.tv_sec = (time_t)sleepsec;
1016 		tv.tv_usec = 0;
1017 
1018 		for (;;) {
1019 			/* Unmask all signals. */
1020 			if (Sigprocmask(SIG_SETMASK, &zeromask,
1021 			    (sigset_t *)NULL) < 0) {
1022 				logmsg(IPA_LOG_ERR, "suspend_work: "
1023 				    SIGPROCMASK_NAME "(SIG_SETMASK) for "
1024 				    "zeromask");
1025 				return (-1);
1026 			}
1027 			rs = readfdset;
1028 			nready = select(maxfd, &rs, (fd_set *)NULL,
1029 			    (fd_set *)NULL, &tv);
1030 			errno_save = errno;
1031 			/* Mask expected signals. */
1032 			if (Sigprocmask(SIG_SETMASK, &sigmask,
1033 			    (sigset_t *)NULL) < 0) {
1034 				logmsg(IPA_LOG_ERR, "suspend_work: "
1035 				    SIGPROCMASK_NAME "(SIG_SETMASK) for "
1036 				    "sigmask");
1037 				return (-1);
1038 			}
1039 			if (nready < 0) {
1040 				if (errno_save == EINTR) {
1041 					tv.tv_sec = 0;
1042 					tv.tv_usec = 0;
1043 					continue;
1044 				}
1045 				errno = errno_save;
1046 				logmsg(IPA_LOG_ERR, "suspend_work: select");
1047 				return (-1);
1048 			}
1049 			break;
1050 		}
1051 
1052 		if (nready == 0) {
1053 			/* Timeout, that is normal sleep. */
1054 			goto done;
1055 		}
1056 
1057 		/* Check stdout and stderr streams. */
1058 		if (!debug && FD_ISSET(stderr_read, &rs)) {
1059 			--nready;
1060 			log_stderr();
1061 		}
1062 		if (FD_ISSET(stdout_read, &rs)) {
1063 			--nready;
1064 			log_stdout();
1065 		}
1066 		if (nready == 0)
1067 			goto done;
1068 
1069 		if (signal_flag) {
1070 			signal_flag = 0;
1071 			while (read(sig_pipe[0], &ch, 1) > 0)
1072 				;
1073 			if (sigchld_flag) {
1074 				sigchld_flag = 0;
1075 				if (check_child() < 0)
1076 					goto failed;
1077 				--nready;
1078 			}
1079 			if (shutdown_flag) {
1080 				logmsgx(IPA_LOG_INFO, "caught signal %d, "
1081 				    "shutdown...", (int)shutdown_flag);
1082 				set_rules_for_check();
1083 				/* Ignore possible another queued signals. */
1084 				return (0);
1085 			}
1086 			if (reconf_flag) {
1087 				logmsgx(IPA_LOG_INFO, "caught signal %d, "
1088 				    "reconfiguring...", SIGHUP);
1089 				set_rules_for_check();
1090 				return (0);
1091 			}
1092 			if (nready == 0)
1093 				goto done;
1094 		}
1095 
1096 		/*
1097 		 * Check for a connection to ctl_listenfd only
1098 		 * if there was not poll in select (simplifying code).
1099 		 */
1100 		if (FD_ISSET(ctl_listenfd, &rs) && sleepsec != 0) {
1101 			/*
1102 			 * Since ctl command can update statistics in
1103 			 * database, need to update curdate and cursec.
1104 			 */
1105 			rv = new_time_values();
1106 			if (rv < 0)
1107 				goto failed;
1108 			if (rv == 1 ||
1109 			    difftime(curtime, newdaytime) >= 0.0) {
1110 				/*
1111 				 * Time related problem occurred or
1112 				 * a new day came.
1113 				 */
1114 				return (0);
1115 			}
1116 			cursec = cursec_new;
1117 			curdate = curdate_new;
1118 			if (ctl_query() < 0)
1119 				goto failed;
1120 			if (freeze_flag) {
1121 				freeze_flag = 0;
1122 				if (freeze_time > 0) {
1123 					logmsgx(IPA_LOG_INFO, "freezing, "
1124 					    "ignoring signals and commands "
1125 					    "at least %u seconds...",
1126 					    freeze_time);
1127 					sleep(freeze_time);
1128 					logmsgx(IPA_LOG_INFO, "continue "
1129 					    "working");
1130 				} else
1131 					logmsgx(IPA_LOG_WARNING, "do not "
1132 					    "freeze, since \"freeze_time\" "
1133 					    "is not defined");
1134 			}
1135 			if (main_check_sec == cursec) {
1136 				/* It's time to wakeup immediately. */
1137 				return (0);
1138 			}
1139 		}
1140 done:		;
1141 	} while (adj_sleep);
1142 	return (0);
1143 
1144 failed:
1145 	logbt("suspend_work");
1146 	return (-1);
1147 }
1148 
1149 /*
1150  * The main function for accounting, it is called from the main()
1151  * and does all of the work.  Return codes are the following:
1152  *   0 -- normal exit;
1153  *   1 -- need to reconfigure;
1154  *  -1 -- some error occurred.
1155  */
1156 int
ipa_main(void)1157 ipa_main(void)
1158 {
1159 	int rv;
1160 
1161 	if (!debug)
1162 		log_stderr();
1163 	log_stdout();
1164 
1165 	logmsgx(IPA_LOG_INFO, "start working");
1166 
1167 	if (newday(1) < 0)
1168 		goto failed;
1169 
1170 	for (stat_generation = 1;; stat_generation += 2) {
1171 		rv = new_time_values();
1172 		if (rv < 0)
1173 			break;
1174 
1175 		/* Call ac_get_stat() for all accounting modules. */
1176 		if (ac_get_stat() < 0)
1177 			break;
1178 
1179 		/* Perform all accounting related tasks. */
1180 		if (rv == 0) {
1181 			/* No time related problem. */
1182 			if (difftime(curtime, newdaytime) < 0.0) {
1183 				if (do_ac_curr() < 0)
1184 					break;
1185 			} else {
1186 				if (do_ac_newday() < 0)
1187 					break;
1188 			}
1189 		} else {
1190 			/* There is time related problem. */
1191 			if (do_ac_prev() < 0)
1192 				break;
1193 		}
1194 
1195 		/* Check signals events. */
1196 		if (shutdown_flag)
1197 			return (0);
1198 		if (reconf_flag) {
1199 			reconf_flag = 0;
1200 			return (1);
1201 		}
1202 
1203 		if (dump_flag) {
1204 			/* There was ctl command "dump". */
1205 			dump_flag = 0;
1206 			if (dump_finished() < 0)
1207 				break;
1208 		}
1209 
1210 		/* Sleep and wait for a signal or a ctl command. */
1211 		if (suspend_work() < 0)
1212 			break;
1213 	}
1214 failed:
1215 	logbt("ipa_main");
1216 	return (-1);
1217 }
1218 
1219 /*
1220  * SIGINT and/or SIGTERM handler.
1221  */
1222 void
sig_term(int signo)1223 sig_term(int signo)
1224 {
1225 	(void)write(sig_pipe[1], "", 1);
1226 	signal_flag = 1;
1227 	need_check_flag = 1;
1228 	shutdown_flag = signo;
1229 }
1230 
1231 /*
1232  * SIGHUP handler, cause reconfiguring.
1233  */
1234 /* ARGSUSED */
1235 void
sig_hup(int signo ATTR_UNUSED)1236 sig_hup(int signo ATTR_UNUSED)
1237 {
1238 	(void)write(sig_pipe[1], "", 1);
1239 	signal_flag = 1;
1240 	reconf_flag = 1;
1241 	need_check_flag = 1;
1242 }
1243 
1244 /*
1245  * SIGCHLD handler.
1246  */
1247 /* ARGSUSED */
1248 void
sig_chld(int signo ATTR_UNUSED)1249 sig_chld(int signo ATTR_UNUSED)
1250 {
1251 	(void)write(sig_pipe[1], "", 1);
1252 	signal_flag = 1;
1253 	sigchld_flag = 1;
1254 }
1255 
1256 #if defined(WITH_THRESHOLDS) || defined(WITH_SUBLIMITS)
1257 /*
1258  * Return part of val.
1259  */
1260 uint64_t
uint64_per_cent(const uint64_t * val,unsigned int per_cent)1261 uint64_per_cent(const uint64_t *val, unsigned int per_cent)
1262 {
1263 	uint64_t res;
1264 
1265 	if (*val > UINT64_MAX / per_cent) {
1266 		res = *val / 100;
1267 		res *= per_cent;
1268 	} else {
1269 		res = *val * per_cent;
1270 		res /= 100;
1271 	}
1272 	return (res);
1273 }
1274 #endif /* WITH_THRESHOLDS || WITH_SUBLIMITS */
1275