1 /*-
2  * Copyright (c) 2003-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_autorules.c,v 1.3.2.1 2011/11/15 18:12:29 simon Exp $";
32 #endif /* !lint */
33 
34 #include <sys/types.h>
35 
36 #include <regex.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 
41 #include "ipa_mod.h"
42 
43 #include "queue.h"
44 
45 #include "dlapi.h"
46 #include "confcommon.h"
47 #include "memfunc.h"
48 #include "parser.h"
49 
50 #include "ipa_ac.h"
51 #include "ipa_db.h"
52 #include "ipa_time.h"
53 
54 #include "ipa_ctl.h"
55 #include "ipa_cmd.h"
56 #include "ipa_conf.h"
57 #include "ipa_log.h"
58 #include "ipa_main.h"
59 #include "ipa_rules.h"
60 #include "ipa_autorules.h"
61 
62 unsigned int	nautorules;		/* Number of autorules. */
63 unsigned int	ndynrules;		/* Number of dynamic rules. */
64 
65 #ifdef WITH_AUTORULES
66 
67 signed char	debug_autorule;		/* debug_autorule parameter. */
68 
69 struct autorule	*autorules;		/* Array of autorules. */
70 ipa_marray	*autorules_marray;	/* Marray of autorules. */
71 
72 struct autorules_list autorules_list;	/* List of all autorules. */
73 
74 #define AUTORULE(x) &autorules[(x)]	/* Pointer to autorule by number. */
75 
76 static struct rule **rules_ptr;		/* Array of pointers to rules. */
77 ipa_marray	*rules_ptr_marray;	/* Marray for array of ptrs to rules. */
78 
79 #define RULE(x) rules_ptr[(x)]		/* Pointer to rule by number. */
80 
81 /* Time when to check active autorules. */
82 unsigned int	autorules_active_check_sec;
83 
84 /* Time when to check inactive autorules. */
85 unsigned int	autorules_inactive_check_sec;
86 
87 /* Active/inactive autorules queue. */
88 TAILQ_HEAD(autorules_queue, autorule);
89 static struct autorules_queue autorules_active;
90 static struct autorules_queue autorules_inactive;
91 
92 /*
93  * Return pointer to autorule with the given name.
94  */
95 struct autorule *
autorule_by_name(const char * name)96 autorule_by_name(const char *name)
97 {
98 	struct autorule *autorule;
99 	unsigned int i;
100 
101 	for (i = 0, autorule = autorules; i < nautorules; ++autorule, ++i)
102 		if (strcmp(name, autorule->name) == 0)
103 			return (autorule);
104 	return (NULL);
105 }
106 
107 /*
108  * Set autorule active or inactive in modules that it uses.
109  */
110 static int
mod_set_autorule_active(struct autorule * autorule,int active)111 mod_set_autorule_active(struct autorule *autorule, int active)
112 {
113 	if ((autorule->arule_flags & AUTORULE_FLAG_ACTIVE) ==
114 	    (active ? AUTORULE_FLAG_ACTIVE : 0)) {
115 		logmsgx(IPA_LOG_ERR, "internal error: mod_set_autorule_active"
116 		    "(%s, %d): autorule is already %s", autorule->name,
117 		    active, active_msg[active]);
118 		return (-1);
119 	}
120 
121 	if (debug_worktime)
122 		logdbg("autorule %s: set autorule %s",
123 		    autorule->name, active_msg[active]);
124 
125 	if (active)
126 		AUTORULE_SET_ACTIVE(autorule);
127 	else
128 		AUTORULE_SET_INACTIVE(autorule);
129 
130 	if (ac_set_autorule_active(autorule, active) < 0) {
131 		logbt("mod_set_autorule_active");
132 		return (-1);
133 	}
134 
135 	return (0);
136 }
137 
138 /*
139  * Log information about inactive autorules.
140  */
141 static void
show_inactive_autorules(void)142 show_inactive_autorules(void)
143 {
144 	const struct autorule *autorule;
145 
146 	logdbg("inactive autorules list:");
147 	TAILQ_FOREACH(autorule, &autorules_inactive, queue)
148 		logdbg("    active_sec %s, autorule %s",
149 		    sec_str(autorule->worktime->active_sec), autorule->name);
150 }
151 
152 /*
153  * Move am autorule from the active autorules queue to the inactive
154  * autorules queue.
155  */
156 static int
set_autorule_inactive(struct autorule * autorule1)157 set_autorule_inactive(struct autorule *autorule1)
158 {
159 	struct autorule *autorule2;
160 
161 	/* Inform modules that autorule becomes inactive. */
162 	if (mod_set_autorule_active(autorule1, 0) < 0) {
163 		logbt("set_autorule_inactive");
164 		return (-1);
165 	}
166 
167 	/* Remove the autorule from the active autorules queue. */
168 	TAILQ_REMOVE(&autorules_active, autorule1, queue);
169 
170 	/*
171 	 * Add the autorule to the inactive autorules queue,
172 	 * keep that queue sorted.
173 	 */
174 	TAILQ_FOREACH(autorule2, &autorules_inactive, queue)
175 		if (autorule1->worktime->active_sec <
176 		    autorule2->worktime->active_sec) {
177 			TAILQ_INSERT_BEFORE(autorule2, autorule1, queue);
178 			goto done;
179 		}
180 	TAILQ_INSERT_TAIL(&autorules_inactive, autorule1, queue);
181 done:
182 	if (debug_worktime)
183 		show_inactive_autorules();
184 
185 	autorules_inactive_check_sec =
186 	    TAILQ_FIRST(&autorules_inactive)->worktime->active_sec;
187 
188 	return (0);
189 }
190 
191 /*
192  * Move an autorule from the inactive autorules queue to the
193  * active autorules queue.
194  */
195 static int
set_autorule_active(struct autorule * autorule)196 set_autorule_active(struct autorule *autorule)
197 {
198 	/* Inform modules that autorule becomes active. */
199 	if (mod_set_autorule_active(autorule, 1) < 0) {
200 		logbt("set_autorule_active");
201 		return (-1);
202 	}
203 
204 	/* Remove the autorule from the inactive autorules queue. */
205 	TAILQ_REMOVE(&autorules_inactive, autorule, queue);
206 
207 	/* Add the rule to the active autorules queue. */
208 	TAILQ_INSERT_TAIL(&autorules_active, autorule, queue);
209 
210 	/* Get new value of autorules_inactive_check_sec. */
211 	autorules_inactive_check_sec = TAILQ_EMPTY(&autorules_inactive) ?
212 	    EVENT_NOT_SCHEDULED :
213 	    TAILQ_FIRST(&autorules_inactive)->worktime->active_sec;
214 
215 	/* Check if rules_active_check_sec should be modified. */
216 	if (autorules_active_check_sec > autorule->update_tevent->event_sec)
217 		autorules_active_check_sec = autorule->update_tevent->event_sec;
218 	if (autorules_active_check_sec > autorule->worktime->inactive_sec)
219 		autorules_active_check_sec = autorule->worktime->inactive_sec;
220 
221 	return (0);
222 }
223 
224 /*
225  * Sort inactive autorules.
226  */
227 static void
sort_inactive_autorules(void)228 sort_inactive_autorules(void)
229 {
230 	struct autorule *a1, *a1_next, *a2;
231 
232 	if (TAILQ_EMPTY(&autorules_inactive))
233 		autorules_inactive_check_sec = EVENT_NOT_SCHEDULED;
234 	else {
235 		a1 = TAILQ_FIRST(&autorules_inactive);
236 		TAILQ_INIT(&autorules_inactive);
237 		for (; a1 != NULL; a1 = a1_next) {
238 			a1_next = TAILQ_NEXT(a1, queue);
239 			if (a1->worktime->active_sec != EVENT_NOT_SCHEDULED) {
240 				/* Will be active in current day. */
241 				TAILQ_FOREACH(a2, &autorules_inactive, queue)
242 					if (a1->worktime->active_sec <=
243 					    a2->worktime->active_sec) {
244 						TAILQ_INSERT_BEFORE(a2, a1,
245 						    queue);
246 						goto next;
247 					}
248 			}
249 			TAILQ_INSERT_TAIL(&autorules_inactive, a1, queue);
250 next:			;
251 		}
252 		autorules_inactive_check_sec =
253 		    TAILQ_FIRST(&autorules_inactive)->worktime->active_sec;
254 	}
255 
256 	if (debug_worktime)
257 		show_inactive_autorules();
258 }
259 
260 /*
261  * Make initialization for all autorules.
262  */
263 int
init_autorules(void)264 init_autorules(void)
265 {
266 	struct autorule *autorule;
267 	struct rule *rule;
268 
269 	/* Initialize autorules queues. */
270 	TAILQ_INIT(&autorules_inactive);
271 	TAILQ_INIT(&autorules_active);
272 
273 	/* Flush number of dynamic rules. */
274 	ndynrules = 0;
275 
276 	if (nautorules == 0)
277 		return (0);
278 
279 	/* Initialize each autorule in accounting modules. */
280 	STAILQ_FOREACH(autorule, &autorules_list, link) {
281 		if (ac_init_autorule(autorule) < 0) {
282 			logbt("init_autorules");
283 			return (-1);
284 		}
285 		TAILQ_INSERT_TAIL(&autorules_active, autorule, queue);
286 	}
287 
288 	/* Create rules_ptr_marray. */
289 	rules_ptr_marray = marray_init(MARRAY_NAME(rules_ptr),
290 	    "Pointers to rules", 0, (void *)&rules_ptr, sizeof(struct rule *),
291 	    nstatrules + 1, RULE_NALLOC);
292 	if (rules_ptr_marray == NULL) {
293 		logmsgx(IPA_LOG_ERR, "init_autorules: marray_init failed");
294 		return (-1);
295 	}
296 
297 	/* Mark entries in rules_ptr for static rules as used. */
298 	TAILQ_FOREACH(rule, &rules_list, list)
299 		if (marray_alloc(rules_ptr_marray, &rule->no, 1) < 0) {
300 			logmsgx(IPA_LOG_ERR, "init_autorules: "
301 			    "marray_alloc failed");
302 			return (-1);
303 		}
304 
305 	return (0);
306 }
307 
308 /*
309  * Deinitialize one autorule.
310  */
311 static int
deinit_autorule(struct autorule * autorule)312 deinit_autorule(struct autorule *autorule)
313 {
314 	int rv;
315 
316 	/* Deinitialize in modules. */
317 	rv = 0;
318 	if (ac_deinit_autorule(autorule) < 0) {
319 		logbt("deinit_autorule");
320 		rv = -1;
321 	}
322 
323 	/* Decrease ref_count for accounting systems. */
324 	if (AUTORULE_IS_ACTIVE(autorule))
325 		if (ac_dec_ref_count(autorule->ac_list) < 0) {
326 			logmsgx(IPA_LOG_ERR, "autorule %s: deinit_autorule: "
327 			    "ac_dec_ref_count failed", autorule->name);
328 			rv = -1;
329 		}
330 
331 	/* And free all memory used by a autorule. */
332 	mem_free(autorule->name, m_anon);
333 	cmds_rule_free(&autorule->rc[RC_STARTUP]);
334 	cmds_rule_free(&autorule->rc[RC_SHUTDOWN]);
335 #ifdef WITH_LIMITS
336 	free_limits(RULE_FLAG_FREE_LIMITS, &autorule->limits, 0);
337 #endif
338 #ifdef WITH_THRESHOLDS
339 	free_thresholds(RULE_FLAG_FREE_THRESHOLDS, &autorule->thresholds, 0);
340 #endif
341 
342 	return (rv);
343 }
344 
345 /*
346  * Make deinitialization for all autorules.
347  */
348 int
deinit_autorules(void)349 deinit_autorules(void)
350 {
351 	struct autorule *autorule;
352 	int rv;
353 
354 	if (nautorules == 0)
355 		return (0);
356 
357 	rv = 0;
358 	STAILQ_FOREACH(autorule, &autorules_list, link)
359 		if (deinit_autorule(autorule) < 0) {
360 			logbt("deinit_autorules");
361 			rv = -1;
362 		}
363 
364 	marray_deinit(autorules_marray);
365 
366 	if (ndynrules != 0) {
367 		logmsgx(IPA_LOG_ERR, "internal error: deinit_autorules: "
368 		    "ndynrules is not zero: %u", ndynrules);
369 		rv = -1;
370 	}
371 
372 	if (rules_ptr_marray != NULL) {
373 		unsigned int n;
374 
375 		n = marray_nused(rules_ptr_marray);
376 		if (n != 0) {
377 			logmsgx(IPA_LOG_ERR, "internal error: "
378 			    "deinit_autorules: rules_ptr_marray is "
379 			    "not empty: %u", n);
380 			rv = -1;
381 		}
382 		marray_deinit(rules_ptr_marray);
383 	}
384 
385 	return (rv);
386 }
387 
388 /*
389  * Inherit settings for dynamic rule from its autorule, then inherit
390  * everythinh as for static rules.
391  */
392 static int
dyn_rule_inherit(const struct autorule * autorule,struct rule * rule)393 dyn_rule_inherit(const struct autorule *autorule, struct rule *rule)
394 {
395 	unsigned int x;
396 
397 	/* Dynamic rule inherits non-NULL update_tevent from its autorule. */
398 	rule->update_tevent = autorule->update_tevent;
399 
400 	rule->append_tevent = autorule->append_tevent;
401 	rule->worktime = autorule->worktime_rule;
402 
403 	/* Dynamic rule inherits non-NULL ac_list from autorule. */
404 	rule->ac_list = autorule->ac_list;
405 	ac_inc_ref_count(rule->ac_list);
406 
407 	rule->db_list = autorule->db_list;
408 
409 #ifdef WITH_RULES
410 	/* Dynamic rules cannot have ac_gather_* parameters. */
411 	rule->acg_add_pat = rule->acg_sub_pat = NULL;
412 	SLIST_INIT(&rule->acgs);
413 #endif
414 
415 	rule->debug_exec = autorule->debug_exec;
416 	rule_init_cmds(rule);
417 #ifdef WITH_LIMITS
418 	rule->debug_limit = autorule->debug_limit;
419 	rule->debug_limit_init = autorule->debug_limit_init;
420 #endif
421 #ifdef WITH_THRESHOLDS
422 	rule->debug_threshold = autorule->debug_threshold;
423 	rule->debug_threshold_init = autorule->debug_threshold_init;
424 #endif
425 
426 #ifdef CTL_CHECK_CREDS
427 	rule->ctl_rule_acl = autorule->ctl_rule_acl;
428 #endif
429 
430 	/*
431 	 * At this point everything is ready in a rule for possible
432 	 * deinit_dyn_rule() invocation.
433 	 */
434 
435 	for (x = 0; x < 2; ++x) {
436 		if (!autorule->rc[x].cmds.sect_set)
437 			continue;
438 		if (cmds_rule_copy(rule, &rule->rc[x], &autorule->rc[x]) < 0) {
439 			logmsgx(IPA_LOG_ERR, "rule %s: dyn_rule_inherit: "
440 			    "cannot copy all commands from autorule %s "
441 			    "{ %s {}}", rule->name, autorule->name,
442 			    rc_sect_name[x]);
443 			return (-1);
444 		}
445 	}
446 
447 #ifdef WITH_LIMITS
448 	if (!STAILQ_EMPTY(&autorule->limits))
449 		if (copy_limits(rule, &autorule->limits) < 0) {
450 			logmsgx(IPA_LOG_ERR, "rule %s: dyn_rule_inherit: "
451 			    "cannot copy all limits from autorule %s",
452 			    rule->name, autorule->name);
453 			return (-1);
454 		}
455 #endif
456 #ifdef WITH_THRESHOLDS
457 	if (!STAILQ_EMPTY(&autorule->thresholds))
458 		if (copy_thresholds(rule, &autorule->thresholds) < 0) {
459 			logmsgx(IPA_LOG_ERR, "rule %s: dyn_rule_inherit: "
460 			    "cannot copy all thresholds from autorule %s",
461 			    rule->name, autorule->name);
462 			return (-1);
463 		}
464 #endif
465 
466 	/* Inherit settings as for static rules. */
467 	if (rule_inherit(rule) < 0) {
468 		logbt("dyn_rule_inherit");
469 		return (-1);
470 	}
471 
472 	return (0);
473 }
474 
475 /*
476  * Deinitialize one dynamic rule.
477  */
478 int
deinit_dyn_rule(struct rule * rule)479 deinit_dyn_rule(struct rule *rule)
480 {
481 	marray_free(rules_ptr_marray, rule->no);
482 	--ndynrules;
483 	if (deinit_rule(rule) < 0) {
484 		logbt("deinit_dyn_rule");
485 		return (-1);
486 	}
487 	return (0);
488 }
489 
490 /*
491  * Initialize one dynamic rule.
492  */
493 static int
init_dyn_rule(const struct autorule * autorule,struct rule * rule)494 init_dyn_rule(const struct autorule *autorule, struct rule *rule)
495 {
496 	rule->newstat = 0;
497 	if (rule->append_tevent == NULL)
498 		rule->append_sec = EVENT_NOT_SCHEDULED;
499 	if (ac_init_dynrule(autorule, rule) < 0)
500 		goto failed;
501 	if (db_init_dynrule(autorule, rule) < 0)
502 		goto failed;
503 	return (0);
504 
505 failed:
506 	logbt("init_dyn_rule");
507 	return (-1);
508 }
509 
510 /*
511  * Create a dynamic rule from the autorule, this function is invoked
512  * from the ipa_ac_mod's ac_get_stat() function.
513  */
514 int
create_rule(const char * mod_name,unsigned int autoruleno,const char * rule_name,const char * rule_info)515 create_rule(const char *mod_name, unsigned int autoruleno,
516     const char *rule_name, const char *rule_info)
517 {
518 	struct rule *rule;
519 	struct autorule *autorule;
520 #ifdef WITH_LIMITS
521 	struct limit *limit;
522 #endif
523 #ifdef WITH_THRESHOLDS
524 	struct threshold *threshold;
525 #endif
526 	unsigned int ruleno;
527 
528 	/* Validate autorule number. */
529 	if (autoruleno >= nautorules) {
530 		logmsgx(IPA_LOG_ERR, "create_rule: module %s tries to create "
531 		    "dynamic rule from autorule with number %u and such "
532 		    "autorule does not exist", mod_name, autoruleno);
533 		return (-1);
534 	}
535 	autorule = AUTORULE(autoruleno);
536 
537 	/* Check if there is already rule with the given name. */
538 	if (validate_name(rule_name) < 0) {
539 		logmsgx(IPA_LOG_ERR, "autorule %s: create_rule: module %s "
540 		    "specified illegal rule name \"%s\"", autorule->name,
541 		    mod_name, rule_name);
542 		return (-1);
543 	}
544 	rule = rule_by_name(rule_name);
545 	if (rule != NULL) {
546 		logmsgx(IPA_LOG_WARNING, "autorule %s: create_rule: module %s "
547 		    "is trying to create duplicated rule %s",
548 		    autorule->name, mod_name, rule_name);
549 		return (-2);
550 	}
551 
552 	/* Find first unused rule number. */
553 	if (marray_alloc(rules_ptr_marray, &ruleno, 0) < 0) {
554 		logmsgx(IPA_LOG_ERR, "rule %s: create_rule: "
555 		    "marray_alloc failed", rule_name);
556 		return (-1);
557 	}
558 
559 	/* Allocate memory for a rule. */
560 	rule = mzone_alloc(rule_mzone);
561 	if (rule == NULL) {
562 		logmsgx(IPA_LOG_ERR, "rule %s: create_rule: "
563 		    "mzone_alloc failed", rule_name);
564 		marray_free(rules_ptr_marray, ruleno);
565 		return (-1);
566 	}
567 
568 	rule->name = mem_strdup(rule_name, m_anon);
569 	if (rule->name == NULL) {
570 		logmsgx(IPA_LOG_ERR, "rule %s: create_rule: "
571 		    "mem_strdup for rule name failed", rule_name);
572 		mzone_free(rule_mzone, rule);
573 		marray_free(rules_ptr_marray, ruleno);
574 		return (-1);
575 	}
576 
577 	RULE(ruleno) = rule;
578 	rules_hash_add(rule);
579 	TAILQ_INSERT_TAIL(&rules_list, rule, list);
580 
581 	rule->no = ruleno;
582 	rule->cnt_neg = 0;
583 	++ndynrules;
584 #ifdef WITH_RULES
585 	rule->acg_add_pat = rule->acg_sub_pat = NULL;
586 	SLIST_INIT(&rule->acgs);
587 #endif
588 #ifdef WITH_LIMITS
589 	STAILQ_INIT(&rule->limits);
590 #endif
591 #ifdef WITH_THRESHOLDS
592 	STAILQ_INIT(&rule->thresholds);
593 #endif
594 	rule->info = NULL;
595 
596 	rule->rule_flags = RULE_FLAG_ACTIVE | RULE_FLAG_QUEUED |
597 	    RULE_FLAG_DYNAMIC;
598 	queue_active_rule(rule);
599 
600 	if (dyn_rule_inherit(autorule, rule) < 0) {
601 		logmsgx(IPA_LOG_ERR, "rule %s: create_rule: cannot inherit "
602 		    "all settings for dynamic rule", rule_name);
603 		goto failed;
604 	}
605 
606 #ifdef WITH_RULES
607 	if (has_ac_gather)
608 		if (init_acg(rule) < 0)
609 			goto failed;
610 #endif
611 
612 	if (rule_info != NULL) {
613 		rule->info = mem_strdup(rule_info, m_anon);
614 		if (rule->info == NULL) {
615 			logmsgx(IPA_LOG_ERR, "rule %s: create_rule: "
616 			    "mem_strdup for rule_info failed", rule_name);
617 			goto failed;
618 		}
619 	}
620 
621 	if (init_dyn_rule(autorule, rule) < 0)
622 		goto failed;
623 
624 #ifdef WITH_LIMITS
625 	if (init_limits(rule) < 0)
626 		goto failed;
627 #endif
628 #ifdef WITH_THRESHOLDS
629 	if (init_thresholds(rule) < 0)
630 		goto failed;
631 #endif
632 
633 	if (run_cmds_rule(rule, RC_STARTUP) < 0)
634 		goto failed;
635 
636 	/*
637 	 * By default any new rule is active as its limits and thresholds are.
638 	 * First ac_get_rule_stat() for a new dynamic rule returns statistics.
639 	 */
640 
641 #ifdef WITH_LIMITS
642 	STAILQ_FOREACH(limit, &rule->limits, link)
643 		if (WT_IS_INACTIVE(limit->worktime))
644 			if (set_limit_inactive(rule, limit) < 0)
645 				goto failed;
646 #endif
647 
648 #ifdef WITH_THRESHOLDS
649 	STAILQ_FOREACH(threshold, &rule->thresholds, link)
650 		if (WT_IS_INACTIVE(threshold->worktime))
651 			if (set_threshold_inactive(rule, threshold) < 0)
652 				goto failed;
653 #endif
654 
655 	if (WT_IS_INACTIVE(rule->worktime)) {
656 		if (set_rule_inactive(rule) < 0)
657 			goto failed;
658 	} else {
659 		/* If a rule is active, then check it immediately. */
660 		rule->check_sec = 0;
661 		if (db_append_rule(rule, &uint64_zero, 1) < 0)
662 			goto failed;
663 	}
664 
665 	autorule->nrules++;
666 	rule->autoruleno = autoruleno;
667 
668 	if (debug_autorule)
669 		logdbg("create_rule: module %s created dynamic rule %s "
670 		    "number %u from autorule %s", mod_name, rule_name,
671 		    ruleno, autorule->name);
672 	return (0);
673 
674 failed:
675 	(void)deinit_dyn_rule(rule);
676 	free_rule(rule);
677 	logbt("create_rule");
678 	return (-1);
679 }
680 
681 /*
682  * Delete a dynamic rule previously created from an autorule,
683  * this function is invoked from the ipa_ac_mod.
684  */
685 int
delete_rule(const char * mod_name,unsigned int ruleno)686 delete_rule(const char *mod_name, unsigned int ruleno)
687 {
688 	struct autorule *autorule;
689 	struct rule *rule;
690 	int rv;
691 
692 	/* Validate rule number. */
693 	if (ruleno < nstatrules) {
694 		logmsgx(IPA_LOG_ERR, "delete_rule: module %s is trying to "
695 		    "delete rule with number %u and this rule is not dynamic",
696 		    mod_name, ruleno);
697 		return (-1);
698 	}
699 	if (!marray_check_index(rules_ptr_marray, ruleno)) {
700 		logmsgx(IPA_LOG_ERR, "delete_rule: module %s is trying to "
701 		    "delete rule with number %u and this rule does not exist",
702 		    mod_name, ruleno);
703 		return (-1);
704 	}
705 
706 	/* Rule exists and is dynamic, delete it. */
707 	rule = RULE(ruleno);
708 	rv = 0;
709 	if (run_cmds_rule(rule, RC_SHUTDOWN) < 0)
710 		rv = -1;
711 	if (deinit_dyn_rule(rule) < 0)
712 		rv = -1;
713 	if (rv < 0)
714 		logbt("delete_rule");
715 
716 	autorule = AUTORULE(rule->autoruleno);
717 	autorule->nrules--;
718 
719 	if (debug_autorule)
720 		logdbg("delete_rule: module %s deleted rule %s number %u, "
721 		    "created from autorule %s", mod_name, rule->name,
722 		    ruleno, autorule->name);
723 
724 	free_rule(rule);
725 	return (rv);
726 }
727 
728 /*
729  * This function is called from newday().
730  */
731 int
autorules_newday(void)732 autorules_newday(void)
733 {
734 	struct autorule *autorule;
735 
736 	autorules_active_check_sec = EVENT_NOT_SCHEDULED;
737 
738 	STAILQ_FOREACH(autorule, &autorules_list, link) {
739 		if (WT_IS_INACTIVE(autorule->worktime)) {
740 			if (AUTORULE_IS_ACTIVE(autorule))
741 				if (set_autorule_inactive(autorule) < 0)
742 					goto failed;
743 		} else {
744 			if (AUTORULE_IS_INACTIVE(autorule))
745 				if (set_autorule_active(autorule) < 0)
746 					goto failed;
747 			if (autorules_active_check_sec >
748 			    autorule->update_tevent->event_sec)
749 				autorules_active_check_sec =
750 				    autorule->update_tevent->event_sec;
751 			if (autorules_active_check_sec >
752 			    autorule->worktime->inactive_sec)
753 				autorules_active_check_sec =
754 				    autorule->worktime->inactive_sec;
755 		}
756 	}
757 
758 	sort_inactive_autorules();
759 	return (0);
760 
761 failed:
762 	logbt("autorules_newday");
763 	return (-1);
764 }
765 
766 /*
767  * Check whether it is time to make some autorule active.
768  * This function is called when autorules_inactive_check_sec <= cursec.
769  */
770 int
check_inactive_autorules(void)771 check_inactive_autorules(void)
772 {
773 	struct autorule *autorule, *autorule_next;
774 
775 	for (autorule = TAILQ_FIRST(&autorules_inactive); autorule != NULL;
776 	    autorule = autorule_next) {
777 		if (autorule->worktime->inactive_sec <= cursec) {
778 			/* It's time to make autorule active. */
779 			autorule_next = TAILQ_NEXT(autorule, queue);
780 			if (set_autorule_active(autorule) < 0) {
781 				logbt("check_inactive_autorules");
782 				return (-1);
783 			}
784 		} else {
785 			/* Set next time of this function invocation. */
786 			autorules_inactive_check_sec =
787 			    autorule->worktime->active_sec;
788 			return (0);
789 		}
790 	}
791 
792 	/* No more inactive autorules. */
793 	autorules_inactive_check_sec = EVENT_NOT_SCHEDULED;
794 	return (0);
795 }
796 
797 /*
798  * Check whether it is time to make some autorule inactive.
799  * This function is called when autorules_active_check_sec <= cursec.
800  */
801 int
check_active_autorules(void)802 check_active_autorules(void)
803 {
804 	struct autorule *autorule;
805 
806 	autorules_active_check_sec = EVENT_NOT_SCHEDULED;
807 
808 	TAILQ_FOREACH(autorule, &autorules_active, queue) {
809 		if (WT_IS_INACTIVE(autorule->worktime)) {
810 			/* Autorule became inactive. */
811 			if (!newday_flag)
812 				if (set_autorule_inactive(autorule) < 0) {
813 					logbt("check_active_autorules");
814 					return (-1);
815 				}
816 		}  else {
817 			/* Autorule is still active. */
818 			if (autorules_active_check_sec >
819 			    autorule->worktime->inactive_sec)
820 				autorules_active_check_sec =
821 				    autorule->worktime->inactive_sec;
822 			if (autorules_active_check_sec >
823 			    autorule->update_tevent->event_sec)
824 				autorules_active_check_sec =
825 				    autorule->update_tevent->event_sec;
826 		}
827 	}
828 
829 	return (0);
830 }
831 
832 /*
833  * Finish checks with autorules and inherit settings from global{}
834  * in all autorule{} sections.
835  */
836 int
autorules_inherit(void)837 autorules_inherit(void)
838 {
839 #ifdef WITH_LIMITS
840 	struct limit *limit;
841 #endif
842 #ifdef WITH_THRESHOLDS
843 	struct threshold *threshold;
844 #endif
845 	const struct ac_list *ac_list;
846 	struct autorule *autorule;
847 	unsigned int i;
848 
849 	marray_minimize(autorules_marray);
850 	for (i = 0, autorule = autorules; i < nautorules; ++autorule, ++i) {
851 		STAILQ_INSERT_TAIL(&autorules_list, autorule, link);
852 		ac_list = autorule->ac_list != NULL ?
853 		    autorule->ac_list : global_ac_list;
854 		if (ac_list != NULL && !STAILQ_EMPTY(ac_list))
855 			if (STAILQ_NEXT(STAILQ_FIRST(ac_list), link) != NULL) {
856 				logconfe("autorule %s: only one accounting "
857 				    "system can be used", autorule->name);
858 				return (-1);
859 			}
860 		if (mimic_real_config) {
861 			if (autorule->ac_list == NULL) {
862 				autorule->ac_list = global_ac_list;
863 				ac_inc_ref_count(global_ac_list);
864 			}
865 			if (autorule->update_tevent == NULL)
866 				autorule->update_tevent = global_update_tevent;
867 			if (autorule->worktime == NULL)
868 				autorule->worktime = global_worktime;
869 			cmds_rule_set_sync(&autorule->rc[RC_STARTUP]);
870 			cmds_rule_set_sync(&autorule->rc[RC_SHUTDOWN]);
871 		}
872 # ifdef WITH_LIMITS
873 		STAILQ_FOREACH(limit, &autorule->limits, link) {
874 			if (check_worktime_subset(autorule->worktime_rule,
875 			    limit->worktime) < 0) {
876 				logconfe("autorule %s, limit %s: limit's "
877 				    "\"worktime\" must be a subset of "
878 				    "autorule's \"worktime_rule\"",
879 				    autorule->name, limit->name);
880 				return (-1);
881 			}
882 			if (mimic_real_config)
883 				limit_inherit(limit);
884 		}
885 # endif
886 # ifdef WITH_THRESHOLDS
887 		STAILQ_FOREACH(threshold, &autorule->thresholds, link) {
888 			if (check_worktime_subset(autorule->worktime_rule,
889 			    threshold->worktime) < 0) {
890 				logconfe("autorule %s, threshold %s: "
891 				    "threshold's \"worktime\" must be a "
892 				    "subset of autorule's worktime_rule",
893 				    autorule->name, threshold->name);
894 				return (-1);
895 			}
896 			if (mimic_real_config)
897 				threshold_inherit(threshold);
898 		}
899 # endif
900 	}
901 	return (0);
902 }
903 
904 #else /* !WITH_AUTORULES */
905 
906 /* ARGSUSED3 */
907 int
create_rule(const char * mod_name,unsigned int autoruleno,const char * rule_name,const char * rule_info ATTR_UNUSED)908 create_rule(const char *mod_name, unsigned int autoruleno,
909     const char *rule_name, const char *rule_info ATTR_UNUSED)
910 {
911 	logmsgx(IPA_LOG_WARNING, "create_rule: module %s tries to create "
912 	    "rule %s from autorule %u", mod_name, rule_name, autoruleno);
913 	logmsgx(IPA_LOG_WARNING, "create_rule: autorules support "
914 	    "was not compiled");
915 	return (-1);
916 }
917 
918 int
delete_rule(const char * mod_name,unsigned int ruleno)919 delete_rule(const char *mod_name, unsigned int ruleno)
920 {
921 	logmsgx(IPA_LOG_ERR, "delete_rule: module %s tries to delete "
922 	    "rule %u", mod_name, ruleno);
923 	logmsgx(IPA_LOG_ERR, "delete_rule: autorules support "
924 	    "was not compiled");
925 	return (-1);
926 }
927 #endif /* WITH_AUTORULES */
928