1 /*-
2 * Copyright (c) 2003 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_limits.c,v 1.2 2011/01/23 18:42:34 simon Exp $";
32 #endif /* !lint */
33
34 #include <sys/types.h>
35
36 #include <errno.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <time.h>
41
42 #include "ipa_mod.h"
43
44 #include "queue.h"
45
46 #include "dlapi.h"
47 #include "confcommon.h"
48 #include "memfunc.h"
49 #include "parser.h"
50
51 #include "ipa_ac.h"
52 #include "ipa_db.h"
53 #include "ipa_ctl.h"
54 #include "ipa_cmd.h"
55 #include "ipa_time.h"
56
57 #include "ipa_conf.h"
58 #include "ipa_log.h"
59 #include "ipa_main.h"
60 #include "ipa_rules.h"
61 #include "ipa_autorules.h"
62
63 unsigned int nstatlimits; /* Number of static limits. */
64 unsigned int ndynlimits; /* Number of dynamic limits. */
65
66 unsigned int nstatsublimits; /* Number of static sublimits. */
67 unsigned int ndynsublimits; /* Number of dynamic sublimits. */
68
69 #ifdef WITH_LIMITS
70
71 signed char global_debug_limit; /* global { debug_limit } */
72 signed char global_debug_limit_init;/* global { debug_limit_init } */
73 signed char global_load_limit; /* global { load_limit } */
74
75 ipa_mzone *limit_mzone; /* Mzone for all struct limit{}. */
76
77 #ifdef WITH_SUBLIMITS
78 ipa_mzone *sublimit_mzone; /* Mzone for all struct sublimit{}. */
79 #endif
80
81 #define set_limit_active(r, l) mod_set_limit_active((r), (l), 1)
82
83 const char *const limit_event_msg[] = {
84 "START", /* 0 */
85 "RESTART", /* 1 */
86 "RESTART_EXEC", /* 2 */
87 "REACH", /* 3 */
88 "REACH_EXEC", /* 4 */
89 "EXPIRE", /* 5 */
90 "EXPIRE_EXEC", /* 6 */
91 "UPDATED", /* 7 */
92 "STARTUP_IF_REACHED", /* 8 */
93 "STARTUP_IF_NOT_REACHED", /* 9 */
94 "SHUTDOWN_IF_REACHED", /* 10 */
95 "SHUTDOWN_IF_NOT_REACHED" /* 11 */
96 };
97
98 #define event_msg limit_event_msg
99 #define EVENT(x) IPA_LIMIT_EVENT_ ## x
100
101 /*
102 * Set limit active or inactive in modules it uses.
103 */
104 int
mod_set_limit_active(const struct rule * rule,struct limit * limit,int active)105 mod_set_limit_active(const struct rule *rule, struct limit *limit, int active)
106 {
107 if ((limit->lim_flags & LIMIT_FLAG_ACTIVE) ==
108 (active ? LIMIT_FLAG_ACTIVE : 0)) {
109 logmsgx(IPA_LOG_ERR, "internal error: "
110 "mod_set_limit_active(%s, %s, %d): limit is already %s",
111 rule->name, limit->name, active, active_msg[active]);
112 return (-1);
113 }
114
115 if (debug_worktime)
116 logdbg("rule %s, limit %s: set limit %s",
117 rule->name, limit->name, active_msg[active]);
118
119 if (active)
120 LIMIT_SET_ACTIVE(limit);
121 else
122 LIMIT_SET_INACTIVE(limit);
123
124 if (ac_set_limit_active(rule, limit, active) < 0)
125 goto failed;
126 if (db_set_limit_active(rule, limit, active) < 0)
127 goto failed;
128
129 return (0);
130
131 failed:
132 logbt("mod_set_limit_active");
133 return (-1);
134 }
135
136 /*
137 * Set limit->event_sec from limit->event_tm.
138 */
139 void
limit_set_event_sec(struct limit * limit)140 limit_set_event_sec(struct limit *limit)
141 {
142 const ipa_tm *tm;
143
144 tm = &limit->event_tm;
145 if (cmp_ipa_tm(tm, &curdate) > 0) {
146 if (tm->tm_year == curdate.tm_year &&
147 tm->tm_mon == curdate.tm_mon &&
148 tm->tm_mday == curdate.tm_mday)
149 limit->event_sec = TIME_TO_SEC(tm);
150 else
151 limit->event_sec = EVENT_NOT_SCHEDULED;
152 } else
153 limit->event_sec = cursec;
154 }
155
156 /*
157 * Add chunk to one limit, if positive counter overflows, then
158 * return -1, since this means incorrect configuration.
159 */
160 int
limit_add_chunk(const struct rule * rule,struct limit * limit,const uint64_t * chunk_ptr)161 limit_add_chunk(const struct rule *rule, struct limit *limit,
162 const uint64_t *chunk_ptr)
163 {
164 uint64_t chunk;
165
166 chunk = *chunk_ptr;
167 if (limit->cnt_neg >= chunk)
168 limit->cnt_neg -= chunk;
169 else {
170 chunk -= limit->cnt_neg;
171 if (limit->cnt > UINT64_MAX - chunk) {
172 logmsgx(IPA_LOG_ERR, "rule %s, limit %s: "
173 "limit_add_chunk: positive counter overflowed",
174 rule->name, limit->name);
175 logmsgx(IPA_LOG_ERR, "this means that something is "
176 "wrong in configuration");
177 return (-1);
178 }
179 limit->cnt += chunk;
180 limit->cnt_neg = 0;
181 }
182 return (0);
183 }
184
185 /*
186 * Add chunk to every not reached and active rule's limit.
187 */
188 int
limits_add_chunk(const struct rule * rule,const uint64_t * chunk_ptr)189 limits_add_chunk(const struct rule *rule, const uint64_t *chunk_ptr)
190 {
191 struct limit *limit;
192
193 STAILQ_FOREACH(limit, &rule->limits, link)
194 if (LIMIT_IS_NOTREACHED(limit) && LIMIT_IS_ACTIVE(limit))
195 if (limit_add_chunk(rule, limit, chunk_ptr) < 0) {
196 logbt("limits_add_chunk");
197 return (-1);
198 }
199 return (0);
200 }
201
202 /*
203 * Subtract chunk from one limit, if negative counter overflows, then
204 * return -1, since this means incorrect configuration.
205 */
206 int
limit_sub_chunk(const struct rule * rule,struct limit * limit,const uint64_t * chunk_ptr)207 limit_sub_chunk(const struct rule *rule, struct limit *limit,
208 const uint64_t *chunk_ptr)
209 {
210 uint64_t chunk;
211
212 chunk = *chunk_ptr;
213 if (limit->cnt >= chunk)
214 limit->cnt -= chunk;
215 else {
216 chunk -= limit->cnt;
217 if (limit->cnt_neg > UINT64_MAX - chunk) {
218 logmsgx(IPA_LOG_ERR, "rule %s, limit %s: "
219 "limit_sub_chunk: negative counter overflowed",
220 rule->name, limit->name);
221 logmsgx(IPA_LOG_ERR, "this means that something is "
222 "wrong in configuration");
223 return (-1);
224 }
225 limit->cnt_neg += chunk;
226 limit->cnt = 0;
227 }
228 return (0);
229 }
230
231 /*
232 * Subtract chunk from every not reached and active rule's limit.
233 */
234 int
limits_sub_chunk(const struct rule * rule,const uint64_t * chunk_ptr)235 limits_sub_chunk(const struct rule *rule, const uint64_t *chunk_ptr)
236 {
237 struct limit *limit;
238
239 STAILQ_FOREACH(limit, &rule->limits, link)
240 if (LIMIT_IS_NOTREACHED(limit) && LIMIT_IS_ACTIVE(limit))
241 if (limit_sub_chunk(rule, limit, chunk_ptr) < 0) {
242 logbt("limits_sub_chunk");
243 return (-1);
244 }
245 return (0);
246 }
247
248 /*
249 * Register a new state for the limit.
250 */
251 static int
new_limit_state(const struct rule * rule,struct limit * limit)252 new_limit_state(const struct rule *rule, struct limit *limit)
253 {
254 struct ipa_limit_state nstat;
255 ipa_tm event_tm;
256 #ifdef WITH_SUBLIMITS
257 struct sublimit *sublimit;
258 #endif
259
260 if (rule->debug_limit_init || rule->debug_limit)
261 logdbg("rule %s, limit %s: register new limit state",
262 rule->name, limit->name);
263
264 event_tm = curdate;
265 /* This function can be called at 24:00:00. */
266 if (newday_flag)
267 fix_240000(&event_tm);
268
269 /* Set limit status as not reached. */
270 LIMIT_SET_NOTREACHED(limit);
271
272 nstat.lim = limit->lim;
273
274 /* Flush limit's counter. */
275 nstat.cnt = limit->cnt = 0;
276
277 nstat.event_date_set = EVENT(START_SET);
278 nstat.event_date[EVENT(START)] = curdate;
279
280 if (limit->restart.restart.upto != TEXP_UPTO_NOTSET) {
281 limit->event_tm = event_tm;
282 ipa_tm_texp(&limit->event_tm, &limit->restart.restart);
283 nstat.event_date_set |= EVENT(RESTART_SET);
284 nstat.event_date[EVENT(RESTART)] = limit->event_tm;
285 limit_set_event_sec(limit);
286 if (rule->debug_limit || rule->debug_limit_init)
287 logdbg("rule %s, limit %s: limit will be restarted "
288 "at %s", rule->name, limit->name,
289 tm_str(&limit->event_tm));
290 } else
291 limit->event_sec = EVENT_NOT_SCHEDULED;
292
293 #ifdef WITH_SUBLIMITS
294 STAILQ_FOREACH(sublimit, &limit->sublimits, link)
295 SUBLIMIT_SET_NOTREACHED(sublimit);
296 #endif
297
298 if (db_set_limit_state(rule, limit, &nstat, 1) < 0) {
299 logbt("new_limit_state");
300 return (-1);
301 }
302 return (0);
303 }
304
305 /*
306 * Check that a limit is not busy and make it busy.
307 */
308 static int
make_limit_busy(const struct rule * rule,struct limit * limit)309 make_limit_busy(const struct rule *rule, struct limit *limit)
310 {
311 if (LIMIT_IS_BUSY(limit)) {
312 logmsgx(IPA_LOG_ERR, "rule %s, limit %s: limit is busy, "
313 "wrong configuration of ictl commands",
314 rule->name, limit->name);
315 return (-1);
316 }
317 LIMIT_SET_BUSY(limit);
318 return (0);
319 }
320
321 /*
322 * Restart a limit.
323 */
324 int
restart_limit(const struct rule * rule,struct limit * limit)325 restart_limit(const struct rule *rule, struct limit *limit)
326 {
327 if (make_limit_busy(rule, limit) < 0)
328 goto failed;
329
330 if (rule->debug_limit)
331 logdbg("rule %s, limit %s: restart_limit: restart limit",
332 rule->name, limit->name);
333
334 if (limit->restart.cmds.has_cmd) {
335 /* restart { exec } */
336 if (run_cmds(rule, &limit->wpid, &limit->restart.cmds,
337 "rule %s { limit %s { restart {}}}", rule->name,
338 limit->name) < 0)
339 goto failed;
340 if (db_limit_event(rule, limit, EVENT(RESTART_EXEC),
341 &curdate) < 0)
342 goto failed;
343 }
344
345 if (ac_limit_event(rule, limit, EVENT(RESTART)) < 0)
346 goto failed;
347
348 if (new_limit_state(rule, limit) < 0)
349 goto failed;
350
351 LIMIT_SET_UNBUSY(limit);
352 return (0);
353
354 failed:
355 logbt("restart_limit");
356 return (-1);
357 }
358
359 /*
360 * Expire a limit.
361 */
362 int
expire_limit(const struct rule * rule,struct limit * limit)363 expire_limit(const struct rule *rule, struct limit *limit)
364 {
365 if (make_limit_busy(rule, limit) < 0)
366 goto failed;
367
368 if (rule->debug_limit)
369 logdbg("rule %s, limit %s: limit expired",
370 rule->name, limit->name);
371
372 if (limit->expire.cmds.has_cmd) {
373 /* expire { exec } */
374 if (run_cmds(rule, &limit->wpid, &limit->expire.cmds,
375 "rule %s { limit %s { expire {}}}", rule->name,
376 limit->name) < 0)
377 goto failed;
378 if (db_limit_event(rule, limit, EVENT(EXPIRE_EXEC),
379 &curdate) < 0)
380 goto failed;
381 }
382
383 if (ac_limit_event(rule, limit, EVENT(EXPIRE)) < 0)
384 goto failed;
385
386 if (new_limit_state(rule, limit) < 0)
387 goto failed;
388
389 LIMIT_SET_UNBUSY(limit);
390
391 if (limit->lim == 0) {
392 /* Such limit is always reached. */
393 if (reach_limit(rule, limit) < 0)
394 goto failed;
395 }
396
397 return (0);
398
399 failed:
400 logbt("expire_limit");
401 return (-1);
402 }
403
404 /*
405 * A limit is reached.
406 */
407 int
reach_limit(const struct rule * rule,struct limit * limit)408 reach_limit(const struct rule *rule, struct limit *limit)
409 {
410 ipa_tm event_tm;
411
412 if (make_limit_busy(rule, limit) < 0)
413 goto failed;
414
415 /* This function can be called at 24:00:00. */
416 event_tm = curdate;
417 if (newday_flag)
418 fix_240000(&event_tm);
419
420 if (rule->debug_limit)
421 logdbg("rule %s, limit %s: limit reached, cnt %"PRIu64,
422 rule->name, limit->name, limit->cnt);
423
424 /* Set limit status as reached. */
425 LIMIT_SET_REACHED(limit);
426
427 if (db_limit_event(rule, limit, EVENT(REACH), &curdate) < 0)
428 goto failed;
429 if (limit->reach.has_cmd) {
430 /* reach { exec } */
431 if (run_cmds(rule, &limit->wpid, &limit->reach,
432 "rule %s { limit %s { reach {}}}", rule->name,
433 limit->name) < 0)
434 goto failed;
435 if (db_limit_event(rule, limit, EVENT(REACH_EXEC),
436 &curdate) < 0)
437 goto failed;
438 }
439 if (ac_limit_event(rule, limit, EVENT(REACH)) < 0)
440 goto failed;
441
442 LIMIT_SET_UNBUSY(limit);
443
444 if (limit->expire.expire.upto != TEXP_UPTO_NOTSET) {
445 /* limit { expire } */
446 limit->event_tm = event_tm;
447 ipa_tm_texp(&limit->event_tm, &limit->expire.expire);
448 limit_set_event_sec(limit);
449 if (rule->debug_limit)
450 logdbg("rule %s, limit %s: limit will expire at %s",
451 rule->name, limit->name, tm_str(&limit->event_tm));
452 if (db_limit_event(rule, limit, EVENT(EXPIRE),
453 &limit->event_tm) < 0)
454 goto failed;
455 if (limit->event_sec == cursec) {
456 /* expire == 0s */
457 if (expire_limit(rule, limit) < 0)
458 goto failed;
459 }
460 } else
461 limit->event_sec = EVENT_NOT_SCHEDULED;
462
463 return (0);
464
465 failed:
466 logbt("reach_limit");
467 return (-1);
468 }
469
470 #ifdef WITH_SUBLIMITS
471 struct sublimit *
sublimit_by_name(const struct limit * limit,const char * name)472 sublimit_by_name(const struct limit *limit, const char *name)
473 {
474 struct sublimit *sublimit;
475
476 STAILQ_FOREACH(sublimit, &limit->sublimits, link)
477 if (strcmp(sublimit->name, name) == 0)
478 break;
479 return (sublimit);
480 }
481
482 /*
483 * A sublimit is reached.
484 */
485 int
reach_sublimit(const struct rule * rule,const struct limit * limit,struct sublimit * sublimit)486 reach_sublimit(const struct rule *rule, const struct limit *limit,
487 struct sublimit *sublimit)
488 {
489 if (rule->debug_limit)
490 logdbg("rule %s, limit %s, sublimit %s: sublimit reached, "
491 "cnt %"PRIu64, rule->name, limit->name, sublimit->name,
492 limit->cnt);
493
494 SUBLIMIT_SET_REACHED(sublimit);
495 if (sublimit->reach.has_cmd) {
496 /* reach { exec } */
497 if (run_cmds(rule, &sublimit->wpid, &sublimit->reach,
498 "rule %s { limit %s { sublimit %s { reach {}}}}",
499 rule->name, limit->name, sublimit->name) < 0) {
500 logbt("reach_sublimit");
501 return (-1);
502 }
503 }
504
505 return (0);
506 }
507
508 /*
509 * Check for limit's sublimits events.
510 */
511 static int
check_sublimits_events(const struct rule * rule,const struct limit * limit)512 check_sublimits_events(const struct rule *rule, const struct limit *limit)
513 {
514 struct sublimit *sublimit;
515
516 STAILQ_FOREACH(sublimit, &limit->sublimits, link) {
517 if (SUBLIMIT_IS_REACHED(sublimit))
518 continue;
519 /* Sublimit is not reached. */
520 if (limit->cnt >= sublimit->lim) {
521 /* Sublimit has just been reached. */
522 if (reach_sublimit(rule, limit, sublimit) < 0) {
523 logbt("check_sublimits_events");
524 return (-1);
525 }
526 }
527 }
528 return (0);
529 }
530 #endif /* WITH_SUBLIMITS */
531
532 /*
533 * Check for rule's limits events.
534 */
535 int
check_limits(const struct rule * rule,unsigned int * check_sec_ptr)536 check_limits(const struct rule *rule, unsigned int *check_sec_ptr)
537 {
538 const struct worktime *wt;
539 struct limit *limit;
540 unsigned int check_sec, d;
541
542 check_sec = EVENT_NOT_SCHEDULED;
543
544 STAILQ_FOREACH(limit, &rule->limits, link) {
545 wt = limit->worktime;
546 if (LIMIT_IS_INACTIVE(limit)) {
547 /* Limit is inactive. */
548 if (wt->active_sec <= cursec) {
549 /* It's time to make limit active. */
550 if (set_limit_active(rule, limit) < 0)
551 goto failed;
552 } else {
553 if (check_sec > wt->active_sec)
554 check_sec = wt->active_sec;
555 continue; /* do not check any time events. */
556 }
557 }
558
559 /* Here limit is active. */
560 if (LIMIT_IS_REACHED(limit)) {
561 /* Limit has been reached already. */
562 if (limit->event_sec <= cursec) {
563 /* { expire } and it's time to expire limit. */
564 d = ipa_tm_diff(&curdate, &limit->event_tm);
565 if (d > sensitive_time)
566 logmsgx(IPA_LOG_WARNING, "rule %s, "
567 "limit %s: expire limit too late: "
568 "delta %s is greater than "
569 "\"sensitive_time\" %u seconds",
570 rule->name, limit->name,
571 time_str(d), sensitive_time);
572 if (expire_limit(rule, limit) < 0)
573 goto failed;
574 }
575 } else {
576 /* Limit is not reached. */
577 if (db_update_limit(rule, limit) < 0)
578 goto failed;
579 #ifdef WITH_SUBLIMITS
580 if (check_sublimits_events(rule, limit) < 0)
581 goto failed;
582 #endif
583 if (limit->cnt >= limit->lim) {
584 /* Limit has just been reached. */
585 if (reach_limit(rule, limit) < 0)
586 goto failed;
587 } else if (limit->event_sec <= cursec) {
588 /* { restart } and it's time to restart. */
589 d = ipa_tm_diff(&curdate, &limit->event_tm);
590 if (d > sensitive_time)
591 logmsgx(IPA_LOG_WARNING, "rule %s, "
592 "limit %s: restart limit too late: "
593 "delta %s is greater than "
594 "\"sensitive_time\" %u seconds",
595 rule->name, limit->name,
596 time_str(d), sensitive_time);
597 if (restart_limit(rule, limit) < 0)
598 goto failed;
599 }
600 }
601
602 if (WT_IS_INACTIVE(wt)) {
603 /* Limit became inactive. */
604 if (!newday_flag) {
605 if (set_limit_inactive(rule, limit) < 0)
606 goto failed;
607 if (check_sec > wt->active_sec)
608 check_sec = wt->active_sec;
609 }
610 } else {
611 /* Limit is still active. */
612 if (check_sec > wt->inactive_sec)
613 check_sec = wt->inactive_sec;
614 if (check_sec > limit->event_sec)
615 check_sec = limit->event_sec;
616 }
617 }
618
619 *check_sec_ptr = check_sec;
620 return (0);
621
622 failed:
623 logbt("check_limits");
624 return (-1);
625 }
626
627 /*
628 * Initialize one limit.
629 */
630 static int
init_limit(const struct rule * rule,struct limit * limit)631 init_limit(const struct rule *rule, struct limit *limit)
632 {
633 struct ipa_limit_state ostat, nstat;
634 ipa_tm tm;
635 const char *rule_name, *limit_name;
636 unsigned int bit, i;
637 signed char debug_limit_init;
638 char error;
639
640 /*
641 * If init_limit() is called second, third... time,
642 * then limit can be inactive.
643 */
644 if (LIMIT_IS_INACTIVE(limit))
645 if (set_limit_active(rule, limit) < 0)
646 goto failed;
647
648 rule_name = rule->name;
649 limit_name = limit->name;
650 limit->cnt_neg = 0;
651 limit->wpid.debug_exec = rule->debug_exec;
652 debug_limit_init = rule->debug_limit_init;
653
654 if (STAILQ_EMPTY(limit->db_list)) {
655 /* "null" database is used. */
656 if (LIMIT_IS_INITED(limit)) {
657 /* Imitate that old state is known. */
658 ostat.lim = limit->lim;
659 ostat.cnt = limit->cnt;
660 ostat.event_date_set = limit->event_date_set;
661 memcpy(ostat.event_date, limit->event_date,
662 sizeof(ostat.event_date));
663 if (debug_limit_init)
664 logdbg("rule %s, limit %s: init_limit: "
665 "continue to use previous limit state",
666 rule_name, limit_name);
667 } else {
668 if (debug_limit_init)
669 logdbg("rule %s, limit %s: init_limit: "
670 "create limit with no database",
671 rule_name, limit_name);
672 goto new_state;
673 }
674 } else {
675 const char *db_name;
676 int rv;
677
678 /* Get limit state from databases. */
679 rv = db_get_limit_state(rule, limit, &ostat, &db_name);
680 if (rv < 0)
681 goto failed;
682 if (db_name == NULL) {
683 logmsgx(IPA_LOG_WARNING, "rule %s, limit %s: "
684 "init_limit: no database used for this limit "
685 "supports db_get_limit_state function",
686 rule_name, limit_name);
687 goto new_state;
688 }
689 if (rv > 0) {
690 if (debug_limit_init)
691 logdbg("rule %s, limit %s: init_limit: "
692 "got state from %s database",
693 rule_name, limit_name, db_name);
694 } else {
695 if (debug_limit_init)
696 logdbg("rule %s, limit %s: init_limit: "
697 "database %s does not have limit state, "
698 "(a new limit)", rule_name, limit_name,
699 db_name);
700 goto new_state;
701 }
702 }
703
704 /* Check and initialize "limit" parameter. */
705 if (debug_limit_init && limit->lim != ostat.lim) {
706 logdbg("rule %s, limit %s: init_limit: configuration "
707 "limit %"PRIu64", database limit %"PRIu64" (will use %s "
708 "value)", rule_name, limit_name, limit->lim, ostat.lim,
709 limit->load_limit ? "database" : "configuration");
710 }
711 if (limit->load_limit) {
712 if (ostat.lim == 0 &&
713 limit->expire.expire.upto == TEXP_UPTO_SIMPLE &&
714 limit->expire.expire.seconds == 0) {
715 logmsgx(IPA_LOG_ERR, "rule %s, limit %s: init_limit: "
716 "loaded \"limit\" parameter is equal to zero "
717 "and \"expire\" parameter is equal to 0s",
718 rule_name, limit_name);
719 return (-1);
720 }
721 limit->lim = ostat.lim;
722 }
723
724 /* START and UPDATED events must be present. */
725 if (!(ostat.event_date_set & EVENT(START_SET))) {
726 logmsgx(IPA_LOG_ERR, "rule %s, limit %s: init_limit: "
727 "EVENT_START is absent", rule_name, limit_name);
728 goto new_state_error;
729 }
730 if (!(ostat.event_date_set & EVENT(UPDATED_SET))) {
731 logmsgx(IPA_LOG_ERR, "rule %s, limit %s: init_limit: "
732 "EVENT_UPDATED is absent", rule_name, limit_name);
733 goto new_state_error;
734 }
735
736 /* RESTART_EXEC and EXPIRE_EXEC mean restarted or expired limit. */
737 if (ostat.event_date_set & EVENT(RESTART_EXEC_SET)) {
738 logmsgx(IPA_LOG_ERR, "rule %s, limit %s: init_limit: "
739 "limit has EVENT_RESTART_EXEC, this is wrong",
740 rule_name, limit_name);
741 goto new_state_error;
742 }
743 if (ostat.event_date_set & EVENT(EXPIRE_EXEC_SET)) {
744 logmsgx(IPA_LOG_ERR, "rule %s, limit %s: init_limit: "
745 "limit has EVENT_EXPIRE_EXEC, this is wrong",
746 rule_name, limit_name);
747 goto new_state_error;
748 }
749
750 /* Set limit status as not reached. */
751 LIMIT_SET_NOTREACHED(limit);
752
753 /* Check START, REACH and UPDATED events. */
754 error = 0;
755 for (i = 0, bit = 1; i < IPA_LIMIT_EVENT_NUM; bit <<= 1, ++i) {
756 if ((ostat.event_date_set & bit) && (i == EVENT(START) ||
757 i == EVENT(REACH) || i == EVENT(UPDATED))) {
758 /* These events could happen at 24:00:00. */
759 tm = ostat.event_date[i];
760 fix_240000(&tm);
761 if (check_ipa_tm(&tm) < 0) {
762 logmsgx(IPA_LOG_WARNING, "rule %s, limit %s: "
763 "init_limit: wrong value of EVENT_%s: %s",
764 rule_name, limit_name, event_msg[i],
765 tm_str(&tm));
766 goto new_state_error;
767 }
768 /* Check whether time goes back. */
769 if (cmp_ipa_tm(&tm, &curdate) > 0) {
770 error = 1;
771 logmsgx(IPA_LOG_WARNING, "rule %s, limit %s: "
772 "init_limit: EVENT_%s %s is greater than "
773 "current time", rule_name, limit_name,
774 event_msg[i], tm_str(&tm));
775 }
776 }
777 }
778
779 /*
780 * If not reached limit has START event greater than
781 * current time, then change START event.
782 */
783 if (error && !(ostat.event_date_set & EVENT(REACH_SET)) &&
784 cmp_ipa_tm(&ostat.event_date[EVENT(START]), &curdate) > 0) {
785 const char *cp;
786
787 cp = tm_str(&ostat.event_date[EVENT(START)]);
788 ostat.event_date[EVENT(START)] = curdate;
789 logmsgx(IPA_LOG_WARNING, "rule %s, limit %s: init_limit: "
790 "start time changed %s -> %s", rule_name, limit_name, cp,
791 tm_str2(&ostat.event_date[EVENT(START)]));
792 }
793
794 /* Set start time of limit. */
795 nstat.event_date_set = EVENT(START_SET);
796 nstat.event_date[EVENT(START)] = ostat.event_date[EVENT(START)];
797
798 if (ostat.cnt < ostat.lim ||
799 !(ostat.event_date_set & EVENT(REACH_SET))) {
800 /*
801 * Limit was not reached or was not mark as reached
802 * with old "limit" parameter.
803 */
804 nstat.lim = limit->lim;
805 if (ostat.cnt >= ostat.lim)
806 logmsgx(IPA_LOG_WARNING, "rule %s, limit %s: "
807 "init_limit: limit was reached in old state, "
808 "but EVENT_REACH was not registered",
809 rule_name, limit_name);
810
811 if (limit->restart.restart.upto != TEXP_UPTO_NOTSET) {
812 /* The "restart" parameter is used. */
813 limit->event_tm = ostat.event_date[EVENT(START)];
814 fix_240000(&limit->event_tm);
815 if (set_wday(&limit->event_tm) < 0) {
816 logmsgx(IPA_LOG_ERR, "rule %s, limit %s: "
817 "init_limit: set_wday failed", rule_name,
818 limit_name);
819 return (-1);
820 }
821 ipa_tm_texp(&limit->event_tm, &limit->restart.restart);
822 nstat.event_date_set |= EVENT(RESTART_SET);
823 nstat.event_date[EVENT(RESTART)] = limit->event_tm;
824 if (!(ostat.event_date_set & EVENT(RESTART_SET)))
825 logmsgx(IPA_LOG_WARNING, "rule %s, limit %s: "
826 "init_limit: limit has not EVENT_RESTART "
827 "in database, but has \"restart\" "
828 "parameter", rule_name, limit_name);
829 else if (cmp_ipa_tm(&limit->event_tm,
830 &ostat.event_date[EVENT(RESTART)]) != 0)
831 logmsgx(IPA_LOG_WARNING, "rule %s, limit %s: "
832 "init_limit: restart time changed %s -> %s",
833 rule_name, limit_name,
834 tm_str(&ostat.event_date[EVENT(RESTART)]),
835 tm_str2(&limit->event_tm));
836 if (rule->debug_limit || debug_limit_init)
837 logdbg("rule %s, limit %s: limit will be "
838 "restarted at %s", rule_name, limit_name,
839 tm_str(&limit->event_tm));
840 } else {
841 if (ostat.event_date_set & EVENT(RESTART_SET))
842 logmsgx(IPA_LOG_WARNING, "rule %s, limit %s: "
843 "init_limit: limit has EVENT_RESTART in "
844 "database, but limit has not \"restart\" "
845 "parameter: forgot about EVENT_RESTART",
846 rule_name, limit_name);
847 limit->event_sec = EVENT_NOT_SCHEDULED;
848 }
849 } else {
850 /*
851 * Limit was reached and marked as reached
852 * with old "limit" parameter.
853 */
854 if (ostat.event_date_set & EVENT(RESTART_SET)) {
855 /*
856 * If limit had restart time in previous state,
857 * then simply copy it.
858 */
859 nstat.event_date_set |= EVENT(RESTART_SET);
860 nstat.event_date[EVENT(RESTART)] =
861 ostat.event_date[EVENT(RESTART)];
862 }
863 nstat.lim = ostat.lim;
864 LIMIT_SET_REACHED(limit);
865 nstat.event_date_set |= EVENT(REACH_SET);
866 nstat.event_date[EVENT(REACH)] = ostat.event_date[EVENT(REACH)];
867 if (ostat.event_date_set & EVENT(REACH_EXEC_SET)) {
868 /*
869 * If limit run any commands, then save this
870 * in the new state.
871 */
872 nstat.event_date_set |= EVENT(REACH_EXEC_SET);
873 nstat.event_date[EVENT(REACH_EXEC)] =
874 ostat.event_date[EVENT(REACH_EXEC)];
875 }
876 if (limit->expire.expire.upto != TEXP_UPTO_NOTSET) {
877 nstat.event_date_set |= EVENT(EXPIRE_SET);
878 limit->event_tm = nstat.event_date[EVENT(REACH)];
879 fix_240000(&limit->event_tm);
880 if (set_wday(&limit->event_tm) < 0) {
881 logmsgx(IPA_LOG_ERR, "rule %s, limit %s: "
882 "init_limit: set_wday failed", rule_name,
883 limit_name);
884 return (-1);
885 }
886 ipa_tm_texp(&limit->event_tm, &limit->expire.expire);
887 nstat.event_date[EVENT(EXPIRE)] = limit->event_tm;
888 if (!(ostat.event_date_set & EVENT(EXPIRE_SET)))
889 logmsgx(IPA_LOG_WARNING, "rule %s, limit %s: "
890 "init_limit: limit has not EVENT_EXPIRE in "
891 "database, but has \"expire\" parameter",
892 rule_name, limit_name);
893 else if (cmp_ipa_tm(&limit->event_tm,
894 &ostat.event_date[EVENT(EXPIRE])) != 0)
895 logmsgx(IPA_LOG_WARNING, "rule %s, limit %s: "
896 "init_limit: expire time is changed "
897 "%s -> %s", rule_name, limit_name,
898 tm_str(&ostat.event_date[EVENT(EXPIRE)]),
899 tm_str2(&limit->event_tm));
900 if (rule->debug_limit || debug_limit_init)
901 logdbg("rule %s, limit %s: reached limit "
902 "will expire at %s", rule_name, limit_name,
903 tm_str(&limit->event_tm));
904 } else {
905 if (ostat.event_date_set & EVENT(EXPIRE_SET))
906 logmsgx(IPA_LOG_WARNING, "rule %s, limit %s: "
907 "init_limit: limit has EVENT_EXPIRE in "
908 "database, but limit has not \"expire\" "
909 "section in configuration: forgot about "
910 "EVENT_EXPIRE", rule_name, limit_name);
911 limit->event_sec = EVENT_NOT_SCHEDULED;
912 }
913 }
914
915 /* Copy counter value from the old state. */
916 limit->cnt = nstat.cnt = ostat.cnt;
917
918 if (debug_limit_init) {
919 logdbg("rule %s, limit %s: init_limit: lim %"PRIu64", "
920 "cnt %"PRIu64, rule_name, limit_name, limit->lim,
921 limit->cnt);
922 logdbg("rule %s, limit %s: init_limit: set limit state",
923 rule_name, limit_name);
924 }
925
926 if (db_set_limit_state(rule, limit, &nstat, 0) < 0)
927 goto failed;
928
929 LIMIT_SET_INITED(limit);
930 return (0);
931
932 new_state_error:
933 logmsgx(IPA_LOG_WARNING, "rule %s, limit %s: init_limit: set new "
934 "limit state, correcting errors found in database",
935 rule_name, limit_name);
936
937 new_state:
938 if (debug_limit_init)
939 logdbg("rule %s, limit %s: init_limit: set new limit state",
940 rule_name, limit_name);
941 if (new_limit_state(rule, limit) < 0)
942 goto failed;
943 LIMIT_SET_INITED(limit);
944 return (0);
945
946 failed:
947 logbt("init_limit");
948 return (-1);
949 }
950
951 #ifdef WITH_SUBLIMITS
952 /*
953 * Initialize all sublimits for one limit.
954 */
955 static void
956 init_sublimits(const struct rule *rule, const struct limit *limit)
957 {
958 struct sublimit *sublimit;
959
960 STAILQ_FOREACH(sublimit, &limit->sublimits, link) {
961 sublimit->wpid.debug_exec = rule->debug_exec;
962 if (sublimit->lim_pc != 0)
963 sublimit->lim = uint64_per_cent(&limit->lim,
964 sublimit->lim_pc);
965 if (sublimit->lim > limit->lim)
966 logmsgx(IPA_LOG_WARNING, "rule %s, limit %s, "
967 "sublimit %s: init_sublimits: sublimit is greater "
968 "than limit %"PRIu64, rule->name, limit->name,
969 sublimit->name, limit->lim);
970 /*
971 * If limit is reached, then "limit" parameter is
972 * not used and sublimit is always reached.
973 */
974 if (LIMIT_IS_REACHED(limit) || limit->cnt >= sublimit->lim)
975 SUBLIMIT_SET_REACHED(sublimit);
976 else
977 SUBLIMIT_SET_NOTREACHED(sublimit);
978 }
979 }
980
981 void
982 sublimit_init_cmds(struct sublimit *sublimit)
983 {
984 cmds_init(&sublimit->reach);
985 cmds_limit_init(&sublimit->rc[RC_STARTUP]);
986 cmds_limit_init(&sublimit->rc[RC_SHUTDOWN]);
987 }
988 #endif /* WITH_SUBLIMITS */
989
990 /*
991 * Initialize all limits in one rule.
992 */
993 int
994 init_limits(const struct rule *rule)
995 {
996 struct limit *limit;
997
998 STAILQ_FOREACH(limit, &rule->limits, link) {
999 if (init_limit(rule, limit) < 0) {
1000 logbt("init_limits");
1001 return (-1);
1002 }
1003 #ifdef WITH_SUBLIMITS
1004 init_sublimits(rule, limit);
1005 #endif
1006 }
1007 return (0);
1008 }
1009
1010 /*
1011 * Return pointer to limit with the given name.
1012 */
1013 struct limit *
1014 limit_by_name(const struct rule *rule, const char *name)
1015 {
1016 struct limit *limit;
1017
1018 STAILQ_FOREACH(limit, &rule->limits, link)
1019 if (strcmp(name, limit->name) == 0)
1020 break;
1021 return (limit);
1022 }
1023
1024 /*
1025 * This function is called from newday().
1026 */
1027 int
1028 limits_newday(struct rule *rule)
1029 {
1030 struct limit *limit;
1031 unsigned int check_sec;
1032
1033 check_sec = EVENT_NOT_SCHEDULED;
1034
1035 STAILQ_FOREACH(limit, &rule->limits, link) {
1036 if (LIMIT_IS_REACHED(limit)) {
1037 /* Is reached. */
1038 if (limit->expire.expire.upto != TEXP_UPTO_NOTSET)
1039 /* limit { expire } */
1040 limit_set_event_sec(limit);
1041 } else {
1042 /* Is not reached. */
1043 if (limit->restart.restart.upto != TEXP_UPTO_NOTSET)
1044 /* limit { restart } */
1045 limit_set_event_sec(limit);
1046 }
1047 if (WT_IS_INACTIVE(limit->worktime)) {
1048 if (LIMIT_IS_ACTIVE(limit))
1049 if (set_limit_inactive(rule, limit) < 0)
1050 goto failed;
1051 if (check_sec > limit->worktime->active_sec)
1052 check_sec = limit->worktime->active_sec;
1053 } else {
1054 if (LIMIT_IS_INACTIVE(limit))
1055 if (set_limit_active(rule, limit) < 0)
1056 goto failed;
1057 if (check_sec > limit->worktime->inactive_sec)
1058 check_sec = limit->worktime->inactive_sec;
1059 if (check_sec > limit->event_sec)
1060 check_sec = limit->event_sec;
1061 }
1062 if (rule->check_sec > check_sec)
1063 rule->check_sec = check_sec;
1064 }
1065 return (0);
1066
1067 failed:
1068 logbt("limits_newday");
1069 return (-1);
1070 }
1071
1072 #ifdef WITH_SUBLIMITS
1073 /*
1074 * Copy sublimits from limit2 to limit1.
1075 */
1076 static int
1077 copy_sublimits(struct rule *rule, struct limit *ldst, const struct limit *lsrc)
1078 {
1079 const struct sublimit *ssrc;
1080 struct sublimit *sdst;
1081 unsigned int count;
1082 int rv;
1083
1084 count = 0;
1085 STAILQ_FOREACH(ssrc, &lsrc->sublimits, link) {
1086 sdst = mzone_alloc(sublimit_mzone);
1087 if (sdst == NULL) {
1088 xlogmsgx(IPA_LOG_ERR, "copy_sublimits: "
1089 "mzone_alloc failed");
1090 return (-1);
1091 }
1092
1093 /* Copy settings from source sublimit. */
1094 *sdst = *ssrc;
1095
1096 /*
1097 * Initialize fields which are pointers to memory,
1098 * which cannot be shared.
1099 */
1100 sublimit_init_cmds(sdst);
1101 sdst->limit = ldst;
1102 sdst->wpid.u.sublimit = sdst;
1103
1104 /* Link just allocated sublimit to limit. */
1105 STAILQ_INSERT_TAIL(&ldst->sublimits, sdst, link);
1106
1107 /*
1108 * If something goes wrong with memory allocation,
1109 * previous settings will allow to deinitialize current rule.
1110 */
1111 rv =
1112 cmds_copy(rule, &sdst->reach, &ssrc->reach) +
1113 cmds_limit_copy(rule, &sdst->rc[0], &sdst->rc[0]) +
1114 cmds_limit_copy(rule, &sdst->rc[1], &ssrc->rc[1]);
1115 if (rv < 0)
1116 return (-1);
1117 ++count;
1118 }
1119 if (RULE_IS_DYNAMIC(rule))
1120 ndynsublimits += count;
1121 else
1122 nstatsublimits += count;
1123 return (0);
1124 }
1125 #endif
1126
1127 /*
1128 * Copy all limits from the given limits list to rule.
1129 */
1130 int
1131 copy_limits(struct rule *rule, const struct limits_list *list)
1132 {
1133 const struct limit *lsrc;
1134 struct limit *ldst;
1135 unsigned int count;
1136 int rv;
1137
1138 count = 0;
1139 STAILQ_FOREACH(lsrc, list, link) {
1140 ldst = mzone_alloc(limit_mzone);
1141 if (ldst == NULL) {
1142 xlogmsgx(IPA_LOG_ERR, "copy_limits: "
1143 "mzone_alloc failed");
1144 return (-1);
1145 }
1146
1147 /* Copy settings from source limit. */
1148 *ldst = *lsrc;
1149
1150 /*
1151 * Initialize fields, which are pointers to memory,
1152 * which cannot be shared.
1153 */
1154 limit_init_cmds(ldst);
1155 ldst->rule = rule;
1156 ldst->wpid.u.limit = ldst;
1157 #ifdef WITH_SUBLIMITS
1158 STAILQ_INIT(&ldst->sublimits);
1159 #endif
1160
1161 /* Link just allocated limit to rule. */
1162 STAILQ_INSERT_TAIL(&rule->limits, ldst, link);
1163
1164 /*
1165 * If something goes wrong with memory allocation,
1166 * previous settings will allow to deinitialize current rule.
1167 */
1168 rv =
1169 cmds_copy(rule, &ldst->restart.cmds, &lsrc->restart.cmds) +
1170 cmds_copy(rule, &ldst->reach, &lsrc->reach) +
1171 cmds_copy(rule, &ldst->expire.cmds, &lsrc->expire.cmds) +
1172 cmds_limit_copy(rule, &ldst->rc[0], &lsrc->rc[0]) +
1173 cmds_limit_copy(rule, &ldst->rc[1], &lsrc->rc[1]);
1174 if (rv < 0)
1175 return (-1);
1176 #ifdef WITH_SUBLIMITS
1177 if (copy_sublimits(rule, ldst, lsrc) < 0)
1178 return (-1);
1179 #endif
1180 ++count;
1181 }
1182 if (RULE_IS_DYNAMIC(rule))
1183 ndynlimits += count;
1184 else
1185 nstatlimits += count;
1186 return (0);
1187 }
1188
1189 #ifdef WITH_SUBLIMITS
1190 static void
1191 free_sublimits(unsigned int rule_flags, struct sublimits_list *list,
1192 int dyn_flag)
1193 {
1194 struct sublimit *sublimit, *sublimit_next;
1195 unsigned int count;
1196
1197 count = 0;
1198 STAILQ_FOREACH_SAFE(sublimit, list, link, sublimit_next) {
1199 if (rule_flags & RULE_FLAG_FREE_LIMITS)
1200 mem_free(sublimit->name, m_anon);
1201 cmds_free(&sublimit->reach);
1202 cmds_limit_free(&sublimit->rc[RC_STARTUP]);
1203 cmds_limit_free(&sublimit->rc[RC_SHUTDOWN]);
1204 mzone_free(sublimit_mzone, sublimit);
1205 ++count;
1206 }
1207 if (dyn_flag)
1208 ndynsublimits -= count;
1209 }
1210 #endif
1211
1212 /*
1213 * Release memory used by a list of limits, rule_flags determines
1214 * which part of limit{} structure to free.
1215 */
1216 void
1217 free_limits(unsigned int rule_flags, struct limits_list *limits,
1218 int dyn_flag)
1219 {
1220 struct limit *limit, *limit_next;
1221 unsigned int count;
1222
1223 count = 0;
1224 STAILQ_FOREACH_SAFE(limit, limits, link, limit_next) {
1225 if (rule_flags & RULE_FLAG_FREE_LIMITS) {
1226 mem_free(limit->name, m_anon);
1227 mem_free(limit->info, m_parser);
1228 }
1229 cmds_free(&limit->restart.cmds);
1230 cmds_free(&limit->reach);
1231 cmds_free(&limit->expire.cmds);
1232 cmds_limit_free(&limit->rc[RC_STARTUP]);
1233 cmds_limit_free(&limit->rc[RC_SHUTDOWN]);
1234 #ifdef WITH_SUBLIMITS
1235 free_sublimits(rule_flags, &limit->sublimits, dyn_flag);
1236 #endif
1237 mzone_free(limit_mzone, limit);
1238 ++count;
1239 }
1240 if (dyn_flag)
1241 ndynlimits -= count;
1242 }
1243
1244 void
1245 limit_init_cmds(struct limit *limit)
1246 {
1247 cmds_init(&limit->restart.cmds);
1248 cmds_init(&limit->reach);
1249 cmds_init(&limit->expire.cmds);
1250 cmds_limit_init(&limit->rc[RC_STARTUP]);
1251 cmds_limit_init(&limit->rc[RC_SHUTDOWN]);
1252 }
1253
1254 /*
1255 * Set default settings and inherit settings from global{} in limit{}.
1256 */
1257 void
1258 limit_inherit(struct limit *limit)
1259 {
1260 #ifdef WITH_SUBLIMITS
1261 struct sublimit *sublimit;
1262 #endif
1263
1264 if (limit->load_limit < 0)
1265 limit->load_limit = global_load_limit;
1266 if (limit->restart.cmds.sync < 0)
1267 limit->restart.cmds.sync = 0;
1268 if (limit->reach.sync < 0)
1269 limit->reach.sync = 0;
1270 if (limit->expire.cmds.sync < 0)
1271 limit->expire.cmds.sync = 0;
1272 cmds_limit_set_sync(&limit->rc[RC_STARTUP]);
1273 cmds_limit_set_sync(&limit->rc[RC_SHUTDOWN]);
1274 #ifdef WITH_SUBLIMITS
1275 STAILQ_FOREACH(sublimit, &limit->sublimits, link) {
1276 if (sublimit->reach.sync < 0)
1277 sublimit->reach.sync = 0;
1278 cmds_limit_set_sync(&sublimit->rc[RC_STARTUP]);
1279 cmds_limit_set_sync(&sublimit->rc[RC_SHUTDOWN]);
1280 }
1281 #endif
1282 }
1283 #endif /* WITH_LIMITS */
1284