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