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_time.c,v 1.3 2011/01/23 18:42:35 simon Exp $";
32 #endif /* !lint */
33
34
35 #include <limits.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39
40 #include "ipa_mod.h"
41
42 #include "queue.h"
43
44 #include "dlapi.h"
45 #include "confcommon.h"
46 #include "memfunc.h"
47
48 #include "ipa_time.h"
49
50 #include "ipa_log.h"
51 #include "ipa_main.h"
52
53 /*
54 * This file contains different time manipulation functions. I do not
55 * use standard library time manipulation functions, they cannot be used
56 * directly and should be wrapped to fix time zone and summer time related
57 * errors, since ipa(8) does not need information about time zone and
58 * summer time, I found that implementing own functions is better,
59 * than writing wrappers for standard library time functions.
60 *
61 * May be for(;;) loops are not optimal, but for most cases
62 * they are not executed and the ideas of algorithms are easy.
63 */
64
65 unsigned int wakeup_time; /* wakeup_time parameter. */
66 unsigned int freeze_time; /* freeze_time parameter. */
67 unsigned int sleep_after_dump; /* sleep_after_dump parameter. */
68 unsigned int sensitive_time; /* sensitive_time parameter. */
69 signed char debug_time; /* debug_time parameter. */
70 signed char debug_worktime; /* debug_worktime parameter. */
71
72 unsigned int worktimes_check_sec; /* When to call worktimes_check(). */
73
74 const struct worktime *global_worktime; /* global { worktime } */
75
76 struct worktime worktime_default; /* 00:00-24:00 for all days. */
77 static struct tint_set tint_set_default;
78
79 const struct tevent *global_update_tevent; /* global { update_time } */
80 const struct tevent *global_append_tevent; /* global { append_time } */
81
82 const char *const wdays[] = {
83 "Sunday", "Monday", "Tuesday", "Wednesday",
84 "Thursday", "Friday", "Saturday"
85 };
86
87 const char *const active_msg[] = {
88 "inactive", /* 0 */
89 "active" /* 1 */
90 };
91
92 ipa_mzone *tevent_mzone; /* Mzone for all struct tevent{}. */
93 struct tevents_list tevents_list; /* List of all tevents. */
94
95 ipa_mzone *tint_mzone; /* Mzone for all struct tint{}. */
96 struct tint_sets tint_sets; /* List of all tint_sets. */
97
98 ipa_mzone *worktime_mzone; /* Mzone for all worktimes. */
99 struct worktimes_list worktimes_list; /* List of all worktimes. */
100
101 time_t curtime; /* Current time. */
102 ipa_tm curdate, curdate_new; /* Current human localdate. */
103 unsigned int cursec, cursec_new; /* Current time in seconds. */
104 unsigned int curwday; /* Current week day. */
105 char newday_flag; /* Set if new day came. */
106
107 #ifdef WITH_ANY_LIMITS
108 /*
109 * Non-reentrant functions for convert ipa_tm{} value to human readable
110 * value and return a pointer to allocated buffer with this value.
111 * If an error occurred, then a pointer to static string with error
112 * message is returned.
113 */
114 static const char *
tm_str_buf(const ipa_tm * tm,unsigned int i)115 tm_str_buf(const ipa_tm *tm, unsigned int i)
116 {
117 static char buf[2][TM_STR_SIZE];
118
119 if (snprintf(buf[i], TM_STR_SIZE, "%d.%02d.%02d/%02d:%02d:%02d",
120 tm->tm_year, tm->tm_mon, tm->tm_mday,
121 tm->tm_hour, tm->tm_min, tm->tm_sec) < 0)
122 return ("(tm_str_buf: snprintf failed)");
123 return (buf[i]);
124 }
125
126 const char *
tm_str(const ipa_tm * tm)127 tm_str(const ipa_tm *tm)
128 {
129 return (tm_str_buf(tm, 0));
130 }
131
132 const char *
tm_str2(const ipa_tm * tm)133 tm_str2(const ipa_tm *tm)
134 {
135 return (tm_str_buf(tm, 1));
136 }
137
138 /*
139 * Return last month day in the given year/mon.
140 */
141 static int
last_mday(int year,int mon)142 last_mday(int year, int mon)
143 {
144 static int const last_mday_arr[] =
145 /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
146 { 0, 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
147
148 if (mon == 2) {
149 /* February */
150 return ((year % 4 == 0 &&
151 (year % 100 != 0 || year % 400 == 0)) ? 29 : 28);
152 }
153 return (last_mday_arr[mon]);
154 }
155
156 /*
157 * Check ipa_tm date for correct values, ignore nonexistent
158 * time (for example when time zone is changed as +1h, then some
159 * time does not exist).
160 */
161 int
check_ipa_tm(const ipa_tm * tm)162 check_ipa_tm(const ipa_tm *tm)
163 {
164 const int mon = tm->tm_mon;
165 const int mday = tm->tm_mday;
166
167 if (mon == 0 || mon > MONTHES_IN_YEAR || mday > 31 ||
168 mday == 0 || mday > last_mday(tm->tm_year, mon) ||
169 tm->tm_hour > HOURS_IN_DAY - 1 ||
170 tm->tm_min > MINUTES_IN_HOUR - 1 ||
171 tm->tm_sec > SECONDS_IN_MINUTE - 1)
172 return (-1);
173 return (0);
174 }
175
176 /*
177 * If time in tm is 24:00:00, then convert it to 00:00:00 of the next day.
178 */
179 void
fix_240000(ipa_tm * tm)180 fix_240000(ipa_tm *tm)
181 {
182 /* When tm_hour is 24, then tm_min and tm_sec == 0. */
183 if (tm->tm_hour == HOURS_IN_DAY) {
184 /* 24:00:00 --> next day 00:00:00 */
185 tm->tm_hour = 0;
186 if (tm->tm_mday != last_mday(tm->tm_year, tm->tm_mon))
187 tm->tm_mday++;
188 else {
189 tm->tm_mday = 1;
190 if (tm->tm_mon != MONTHES_IN_YEAR)
191 tm->tm_mon++;
192 else {
193 tm->tm_mon = 1;
194 tm->tm_year++;
195 }
196 }
197 }
198 }
199
200 /*
201 * Compare two ipa_tm tm1 and tm2 structures:
202 * if (tm1 == tm2)
203 * return (0);
204 * if (tm1 > tm2)
205 * return (1);
206 * if (tm1 < tm2)
207 * return (-1);
208 * This function correctly works with 24:00:00 time.
209 */
210 int
cmp_ipa_tm(const ipa_tm * tm1,const ipa_tm * tm2)211 cmp_ipa_tm(const ipa_tm *tm1, const ipa_tm *tm2)
212 {
213 /*
214 * We do not use mktime(3), because there can be problem
215 * with time zone and summer time.
216 */
217 if (tm1->tm_year > tm2->tm_year)
218 return (1);
219 if (tm1->tm_year == tm2->tm_year) {
220 if (tm1->tm_mon > tm2->tm_mon)
221 return (1);
222 if (tm1->tm_mon == tm2->tm_mon) {
223 if (tm1->tm_mday > tm2->tm_mday)
224 return (1);
225 if (tm1->tm_mday == tm2->tm_mday) {
226 if (tm1->tm_hour > tm2->tm_hour)
227 return (1);
228 if (tm1->tm_hour == tm2->tm_hour) {
229 if (tm1->tm_min > tm2->tm_min)
230 return (1);
231 if (tm1->tm_min == tm2->tm_min) {
232 if (tm1->tm_sec > tm2->tm_sec)
233 return (1);
234 if (tm1->tm_sec == tm2->tm_sec)
235 return (0);
236 }
237 }
238 }
239 }
240 }
241 return (-1);
242 }
243
244 /*
245 * Return difference in seconds between tm1 and tm0: tm1 - tm0,
246 * tm1 is expected to be greater than tm0.
247 */
248 unsigned int
ipa_tm_diff(const ipa_tm * tm1_arg,const ipa_tm * tm0_arg)249 ipa_tm_diff(const ipa_tm *tm1_arg, const ipa_tm *tm0_arg)
250 {
251 ipa_tm tm1, tm0;
252 unsigned int result, sec1, sec0;
253
254 tm1 = *tm1_arg;
255 tm0 = *tm0_arg;
256
257 sec1 = tm1.tm_hour * SECONDS_IN_HOUR +
258 tm1.tm_min * SECONDS_IN_MINUTE + tm1.tm_sec;
259 sec0 = tm0.tm_hour * SECONDS_IN_HOUR +
260 tm0.tm_min * SECONDS_IN_MINUTE + tm0.tm_sec;
261
262 if (sec1 >= sec0)
263 result = sec1 - sec0;
264 else {
265 result = HOURS_IN_DAY * SECONDS_IN_HOUR - sec0;
266 result += sec1;
267 if (tm0.tm_mday != last_mday(tm0.tm_year, tm0.tm_mon))
268 tm0.tm_mday++;
269 else {
270 tm0.tm_mday = 1;
271 if (tm0.tm_mon != MONTHES_IN_YEAR)
272 tm0.tm_mon++;
273 else {
274 tm0.tm_mon = 1;
275 tm0.tm_year++;
276 }
277 }
278 }
279
280 for (; tm0.tm_year < tm1.tm_year; tm0.tm_year++) {
281 for (; tm0.tm_mon <= MONTHES_IN_YEAR; tm0.tm_mon++) {
282 result += (last_mday(tm0.tm_year, tm0.tm_mon) -
283 tm0.tm_mday + 1) * SECONDS_IN_DAY;
284 tm0.tm_mday = 1;
285 }
286 tm0.tm_mon = 1;
287 }
288
289 for (; tm0.tm_mon < tm1.tm_mon; tm0.tm_mon++) {
290 result += (last_mday(tm0.tm_year, tm0.tm_mon) -
291 tm0.tm_mday + 1) * SECONDS_IN_DAY;
292 tm0.tm_mday = 1;
293 }
294
295 result += (tm1.tm_mday - tm0.tm_mday) * SECONDS_IN_DAY;
296 return (result);
297 }
298 #endif /* WITH_ANY_LIMITS */
299
300 #ifdef WITH_LIMITS
301 /*
302 * Add some seconds to ipa_tm variable ignoring any time zone issues
303 * without using any standard time functions from the library.
304 */
305 static void
ipa_tm_add(ipa_tm * tm,unsigned int addsec)306 ipa_tm_add(ipa_tm *tm, unsigned int addsec)
307 {
308 int t, lmday, year, mon, mday;
309
310 year = tm->tm_year;
311 mon = tm->tm_mon;
312 mday = tm->tm_mday;
313
314 t = addsec / SECONDS_IN_DAY;
315 addsec %= SECONDS_IN_DAY;
316 if (t != 0) {
317 for (;;) {
318 lmday = last_mday(year, mon);
319 if (mday + t > lmday) {
320 t -= lmday - mday;
321 if (mon != MONTHES_IN_YEAR)
322 mon++;
323 else {
324 mon = 1;
325 year++;
326 }
327 mday = 1;
328 if (--t == 0)
329 break;
330 } else {
331 mday += t;
332 break;
333 }
334 }
335 }
336 if (addsec != 0) {
337 t = tm->tm_hour * SECONDS_IN_HOUR +
338 tm->tm_min * SECONDS_IN_MINUTE + tm->tm_sec;
339 if (t + addsec >= SECONDS_IN_DAY) {
340 t += addsec - SECONDS_IN_DAY;
341 if (mday != last_mday(year, mon))
342 mday++;
343 else {
344 if (mon != MONTHES_IN_YEAR)
345 mon++;
346 else {
347 mon = 1;
348 year++;
349 }
350 mday = 1;
351 }
352 } else
353 t += addsec;
354 tm->tm_hour = t / SECONDS_IN_HOUR;
355 t %= SECONDS_IN_HOUR;
356 tm->tm_min = t / SECONDS_IN_MINUTE;
357 tm->tm_sec = t % SECONDS_IN_MINUTE;
358 }
359
360 tm->tm_year = year;
361 tm->tm_mon = mon;
362 tm->tm_mday = mday;
363 }
364
365 /*
366 * Convert given time in tm according to +upto.
367 * tm->tm_wday field must have correct value for the given date.
368 */
369 static void
ipa_tm_upto(ipa_tm * tm,char upto)370 ipa_tm_upto(ipa_tm *tm, char upto)
371 {
372 if (upto == TEXP_UPTO_SIMPLE)
373 return;
374
375 /* +M or +m -> +h -> +D -> +W */
376
377 if (upto == TEXP_UPTO_MONTH) {
378 /* Up to the end of month. */
379 if (tm->tm_mon != MONTHES_IN_YEAR)
380 tm->tm_mon++;
381 else {
382 tm->tm_mon = 1;
383 tm->tm_year++;
384 }
385 tm->tm_mday = 1;
386 tm->tm_hour = tm->tm_min = tm->tm_sec = 0;
387 return;
388 }
389
390 /* Up to the end of minute. */
391 tm->tm_sec = 0;
392 if (tm->tm_min == MINUTES_IN_HOUR - 1) {
393 tm->tm_min = 0;
394 tm->tm_hour++;
395 fix_240000(tm);
396 } else
397 tm->tm_min++;
398 if (upto == TEXP_UPTO_MINUTE)
399 return;
400
401 /* Up to the end of hour. */
402 if (tm->tm_min != 0) {
403 tm->tm_min = 0;
404 tm->tm_hour++;
405 fix_240000(tm);
406 }
407 if (upto == TEXP_UPTO_HOUR)
408 return;
409
410 /* Up to the end of day. */
411 if (tm->tm_hour != 0) {
412 tm->tm_hour = HOURS_IN_DAY;
413 fix_240000(tm);
414 }
415 if (upto == TEXP_UPTO_DAY)
416 return;
417
418 /* Up to the end of week. */
419 if (tm->tm_wday != 0)
420 ipa_tm_add(tm, SECONDS_IN_DAY * (DAYS_IN_WEEK - tm->tm_wday));
421 }
422
423 /*
424 * Convert given time in tm according to texp.
425 */
426 void
ipa_tm_texp(ipa_tm * tm,const struct texp * texp)427 ipa_tm_texp(ipa_tm *tm, const struct texp *texp)
428 {
429 const char right_side = texp->side;
430
431 if (!right_side)
432 ipa_tm_upto(tm, texp->upto);
433 if (texp->seconds != 0)
434 ipa_tm_add(tm, texp->seconds);
435 if (right_side)
436 ipa_tm_upto(tm, texp->upto);
437 }
438
439 /*
440 * Set tm_wday in the given structure. mktime(3) ignores tm_wday
441 * and tm_wday is set on success. It is assumed, that 00:00:01 time
442 * exists in all time zones.
443 */
444 int
set_wday(ipa_tm * tmp)445 set_wday(ipa_tm *tmp)
446 {
447 struct tm tm;
448
449 tm = *tmp;
450 tm.tm_year -= 1900;
451 tm.tm_mon--;
452 tm.tm_hour = tm.tm_min = 0;
453 tm.tm_sec = 1;
454 tm.tm_isdst = -1;
455 if (mktime(&tm) == (time_t)-1) {
456 logmsg(IPA_LOG_ERR, "set_wday: mktime failed");
457 return (-1);
458 }
459 tmp->tm_wday = tm.tm_wday;
460 return (0);
461 }
462 #endif /* WITH_LIMITS */
463
464 #ifdef WITH_THRESHOLDS
465 /*
466 * Subtract some seconds from ipa_tm variable ignoring any time zone issues
467 * without using any standard time functions from the library.
468 */
469 void
ipa_tm_sub(ipa_tm * tm,unsigned int subsec)470 ipa_tm_sub(ipa_tm *tm, unsigned int subsec)
471 {
472 int t, year, mon, mday;
473
474 year = tm->tm_year;
475 mon = tm->tm_mon;
476 mday = tm->tm_mday;
477
478 t = subsec / SECONDS_IN_DAY;
479 subsec %= SECONDS_IN_DAY;
480 if (t != 0) {
481 for (;;) {
482 if (mday > t) {
483 mday -= t;
484 break;
485 } else {
486 if (mon != 1)
487 mon--;
488 else {
489 mon = MONTHES_IN_YEAR;
490 year--;
491 }
492 t -= mday;
493 mday = last_mday(year, mon);
494 if (t == 0)
495 break;
496 }
497 }
498 }
499 if (subsec != 0) {
500 t = tm->tm_hour * SECONDS_IN_HOUR +
501 tm->tm_min * SECONDS_IN_MINUTE + tm->tm_sec;
502 if (t >= subsec)
503 t -= subsec;
504 else {
505 t = SECONDS_IN_DAY - (subsec - t);
506 if (mday > 1)
507 mday--;
508 else {
509 if (mon != 1)
510 mon--;
511 else {
512 mon = MONTHES_IN_YEAR;
513 year--;
514 }
515 mday = last_mday(year, mon);
516 }
517 }
518 tm->tm_hour = t / SECONDS_IN_HOUR;
519 t %= SECONDS_IN_HOUR;
520 tm->tm_min = t / SECONDS_IN_MINUTE;
521 tm->tm_sec = t % SECONDS_IN_MINUTE;
522 }
523
524 tm->tm_year = year;
525 tm->tm_mon = mon;
526 tm->tm_mday = mday;
527 }
528 #endif /* WITH_THRESHOLDS */
529
530 /*
531 * Non-reentrant function for converting number of seconds to hours,
532 * minutes and seconds and return a pointer to allocated buffer with
533 * this value. If an error occurred, then static string with error
534 * message is returned.
535 */
536 const char *
time_str(unsigned int t)537 time_str(unsigned int t)
538 {
539 static char buf[TIME_STR_SIZE];
540
541 unsigned int h, m, s;
542 int rv;
543
544 h = t / SECONDS_IN_HOUR;
545 t %= SECONDS_IN_HOUR;
546 m = t / SECONDS_IN_MINUTE;
547 s = t % SECONDS_IN_MINUTE;
548 if (h == 0) {
549 if (m == 0)
550 rv = snprintf(buf, sizeof(buf), "%us", s);
551 else if (s == 0)
552 rv = snprintf(buf, sizeof(buf), "%um", m);
553 else
554 rv = snprintf(buf, sizeof(buf), "%um %02us", m, s);
555 } else
556 rv = snprintf(buf, sizeof(buf), "%uh %02um %02us", h, m, s);
557 return (rv < 0 ? "(time_str: snprintf failed)" : buf);
558 }
559
560 /*
561 * Non-reentrant function for converting number of seconds to time.
562 */
563 const char *
sec_str(unsigned int sec)564 sec_str(unsigned int sec)
565 {
566 static char buf[SEC_STR_SIZE];
567
568 unsigned int h, m, s;
569
570 if (sec == EVENT_NOT_SCHEDULED)
571 return ("xx:xx:xx");
572 if (sec > SECONDS_IN_DAY)
573 return ("??:??:??");
574
575 h = sec / SECONDS_IN_HOUR;
576 sec %= SECONDS_IN_HOUR;
577 m = sec / SECONDS_IN_MINUTE;
578 s = sec % SECONDS_IN_MINUTE;
579
580 if (snprintf(buf, sizeof(buf), "%02u:%02u:%02u", h, m, s) < 0)
581 return ("(sec_str: snprintf failed)");
582 return (buf);
583 }
584
585 /*
586 * Non-reentrant function for converting time interval to string.
587 */
588 const char *
tint_str(const struct tint * tint)589 tint_str(const struct tint *tint)
590 {
591 static char buf[sizeof("xx:xx-xx:xx")];
592
593 unsigned int h1, m1, h2, m2;
594
595 h1 = tint->sec1 / SECONDS_IN_HOUR;
596 m1 = (tint->sec1 % SECONDS_IN_HOUR) / SECONDS_IN_MINUTE;
597 h2 = tint->sec2 / SECONDS_IN_HOUR;
598 m2 = (tint->sec2 % SECONDS_IN_HOUR) / SECONDS_IN_MINUTE;
599
600 if (snprintf(buf, sizeof(buf), "%02u:%02u-%02u:%02u",
601 h1, m1, h2, m2) < 0)
602 return ("(tint_str: snprintf failed)");
603 return (buf);
604 }
605
606 /*
607 * Convert seconds since midnight to hours, minutes and seconds.
608 */
609 void
sec_to_time(unsigned int sec,ipa_tm * tm)610 sec_to_time(unsigned int sec, ipa_tm *tm)
611 {
612 int h, m, s;
613
614 h = sec / SECONDS_IN_HOUR;
615 sec %= SECONDS_IN_HOUR;
616 m = sec / SECONDS_IN_MINUTE;
617 s = sec % SECONDS_IN_MINUTE;
618
619 tm->tm_hour = h;
620 tm->tm_min = m;
621 tm->tm_sec = s;
622 }
623
624 void
init_worktime_default(void)625 init_worktime_default(void)
626 {
627 static struct tint tint;
628
629 unsigned int wday;
630
631 tint.sec1 = 0;
632 tint.sec2 = SECONDS_IN_DAY;
633
634 STAILQ_INIT(&tint_set_default.list);
635 STAILQ_INSERT_HEAD(&tint_set_default.list, &tint, link);
636
637 STAILQ_INSERT_HEAD(&tint_sets, &tint_set_default, link);
638
639 for (wday = 0; wday < DAYS_IN_WEEK; ++wday)
640 worktime_default.tint_list[wday] = &tint_set_default.list;
641
642 /* See comment in parse_generic_worktime(). */
643 worktime_default.wt_flags = WT_FLAG_ACTIVE;
644 }
645
646 /*
647 * Release memory help by tint_set, including struct tint_set{}.
648 */
649 void
free_tint_set(struct tint_set * set)650 free_tint_set(struct tint_set *set)
651 {
652 struct tint *tint, *tint_next;
653
654 STAILQ_FOREACH_SAFE(tint, &set->list, link, tint_next)
655 mzone_free(tint_mzone, tint);
656 mem_free(set, m_anon);
657 }
658
659 /*
660 * Free all worktimes and worktime mzone.
661 */
662 void
free_worktimes(void)663 free_worktimes(void)
664 {
665 struct tint_set *set, *set_next;
666
667 /* Skip first tint, since it is tint_set_default. */
668 STAILQ_REMOVE_HEAD(&tint_sets, link);
669
670 /* Free other tint_sets. */
671 STAILQ_FOREACH_SAFE(set, &tint_sets, link, set_next)
672 free_tint_set(set);
673
674 mzone_deinit(tint_mzone);
675 mzone_deinit(worktime_mzone);
676 }
677
678 /*
679 * Find next time interval for worktime starting from tint_start.
680 */
681 static void
find_next_tint(struct worktime * wt,const struct tint * tint_start,int report_skip)682 find_next_tint(struct worktime *wt, const struct tint *tint_start,
683 int report_skip)
684 {
685 const struct tint *tint;
686
687 for (tint = tint_start; tint != NULL; tint = STAILQ_NEXT(tint, link)) {
688 if (cursec < tint->sec1) {
689 /* x [ ] */
690 WT_SET_INACTIVE(wt);
691 if (worktimes_check_sec > tint->sec1)
692 worktimes_check_sec = tint->sec1;
693 if (debug_worktime)
694 logdbg("find_next_tint: %s left before start "
695 "of %s %s worktime interval",
696 time_str(tint->sec1 - cursec),
697 wdays[curwday], tint_str(tint));
698 break;
699 }
700 if (tint->sec1 <= cursec && cursec < tint->sec2) {
701 /* [ x ] */
702 WT_SET_ACTIVE(wt);
703 if (worktimes_check_sec > tint->sec2)
704 worktimes_check_sec = tint->sec2;
705 if (debug_worktime)
706 logdbg("find_next_tint: %s left before end "
707 "of %s %s worktime interval",
708 time_str(tint->sec2 - cursec),
709 wdays[curwday], tint_str(tint));
710 break;
711 }
712 /* [ ] x */
713 if (report_skip)
714 logmsgx(IPA_LOG_WARNING, "find_next_tint: skipping "
715 "%s %s worktime interval", wdays[curwday],
716 tint_str(tint));
717 else if (debug_worktime)
718 logdbg("find_next_tint: skipping %s %s worktime "
719 "interval", wdays[curwday], tint_str(tint));
720 }
721 if (tint == NULL) {
722 /* There is no more time intervals for current week day. */
723 WT_SET_INACTIVE(wt);
724 wt->active_sec = wt->inactive_sec = EVENT_NOT_SCHEDULED;
725 if (debug_worktime)
726 logdbg("find_next_tint: there is no more worktime "
727 "intervals for %s (current week day)",
728 wdays[curwday]);
729 } else {
730 wt->active_sec = tint->sec1;
731 wt->inactive_sec = tint->sec2;
732 }
733
734 /* Real tint or NULL for debugging purpose. */
735 wt->curtint = tint;
736 }
737
738 /*
739 * Check if current week day is set in worktime, find current
740 * active or inactive time interval.
741 */
742 void
worktimes_newday(int report_skip)743 worktimes_newday(int report_skip)
744 {
745 const struct tint_list *list;
746 struct worktime *wt;
747
748 worktimes_check_sec = EVENT_NOT_SCHEDULED;
749
750 SLIST_FOREACH(wt, &worktimes_list, link) {
751 list = wt->tint_list[curwday];
752 if (!STAILQ_EMPTY(list)) {
753 /* Current week day is set in worktime. */
754 find_next_tint(wt, STAILQ_FIRST(list), report_skip);
755 continue;
756 } else {
757 /* Current week day is not set in worktime. */
758 WT_SET_INACTIVE(wt);
759 /* Set NULL for debugging purpose. */
760 wt->curtint = NULL;
761 wt->active_sec = wt->inactive_sec = EVENT_NOT_SCHEDULED;
762 if (debug_worktime)
763 logdbg("worktimes_newday: %s (current week "
764 "day) is not set in worktime",
765 wdays[curwday]);
766 }
767 }
768
769 if (debug_worktime)
770 logdbg("worktimes_newday: worktimes_check_sec %s",
771 sec_str(worktimes_check_sec));
772 }
773
774 static unsigned int
worktime_check(struct worktime * wt)775 worktime_check(struct worktime *wt)
776 {
777 unsigned int delta;
778
779 if (WT_IS_ACTIVE(wt)) {
780 /* Is active. */
781 if (wt->inactive_sec > cursec)
782 return (wt->inactive_sec);
783 /* It's time to make it inactive. */
784 delta = cursec - wt->inactive_sec;
785 find_next_tint(wt, STAILQ_NEXT(wt->curtint, link), 1);
786 } else {
787 /* Is inactive. */
788 if (wt->active_sec > cursec)
789 return (wt->active_sec);
790 /* It's time to make it active. */
791 delta = cursec - wt->active_sec;
792 find_next_tint(wt, wt->curtint, 1);
793 }
794
795 if (delta > sensitive_time)
796 logmsgx(IPA_LOG_WARNING, "worktime_check: worktime interval "
797 "%s %s became %s too late: delta %s is greater than "
798 "sensitive_time %us", wdays[curwday],
799 WT_IS_ACTIVE(wt) ? "inactive" : "active",
800 tint_str(wt->curtint), time_str(delta), sensitive_time);
801
802 return (EVENT_NOT_SCHEDULED);
803 }
804
805 /*
806 * Check if current time interval is still active, if it is
807 * time to make inactive time interval active.
808 */
809 void
worktimes_check(void)810 worktimes_check(void)
811 {
812 struct worktime *wt;
813 unsigned int rv;
814
815 worktimes_check_sec = EVENT_NOT_SCHEDULED;
816
817 SLIST_FOREACH(wt, &worktimes_list, link) {
818 rv = worktime_check(wt);
819 if (worktimes_check_sec > rv)
820 worktimes_check_sec = rv;
821 }
822
823 if (debug_worktime)
824 logdbg("worktimes_checks: worktimes_check_sec %s",
825 sec_str(worktimes_check_sec));
826 }
827
828 /*
829 * Try to find already registered worktime with the same settings as
830 * in wt1. If such worktime exist, then return its pointer, else
831 * return original one.
832 */
833 const struct worktime *
find_worktime(struct worktime * wt1)834 find_worktime(struct worktime *wt1)
835 {
836 const struct tint_list * const *list1;
837 const struct tint_list * const *list2;
838 struct worktime *wt2;
839 unsigned int wday;
840
841 /* Check if there is already the same worktime. */
842 SLIST_FOREACH(wt2, &worktimes_list, link) {
843 list1 = wt1->tint_list;
844 list2 = wt2->tint_list;
845 for (wday = 0; wday < DAYS_IN_WEEK; ++wday) {
846 if (*list1 != *list2)
847 break;
848 ++list1;
849 ++list2;
850 }
851 if (wday == DAYS_IN_WEEK) {
852 /* The same worktime was found --> free original one. */
853 mzone_free(worktime_mzone, wt1);
854 return (wt2);
855 }
856 }
857
858 /* New worktime, add it to the list. */
859 SLIST_INSERT_HEAD(&worktimes_list, wt1, link);
860 return (wt1);
861 }
862
863 #ifdef WITH_ANY_LIMITS
864 /*
865 * Return 0 if wt2 is a subset of wt1, else return -1.
866 * Time intervals in limits' or thresholds' worktimes must be
867 * subsets of time intervals in their rule's worktime.
868 */
869 int
check_worktime_subset(const struct worktime * wt1,const struct worktime * wt2)870 check_worktime_subset(const struct worktime *wt1, const struct worktime *wt2)
871 {
872 const struct tint *tint1, *tint2;
873 const struct tint_list * const *list1;
874 const struct tint_list * const *list2;
875 unsigned int wday;
876
877 if (wt1 == NULL || wt2 == NULL || wt1 == wt2)
878 return (0);
879
880 list1 = wt1->tint_list;
881 list2 = wt2->tint_list;
882
883 for (wday = 0; wday < DAYS_IN_WEEK; ++list1, ++list2, ++wday) {
884 if (*list1 == *list2)
885 /* The same tint_list. */
886 continue;
887
888 if (STAILQ_EMPTY(*list2))
889 /* This day in wt2 is not for accounting. */
890 continue;
891
892 if (STAILQ_EMPTY(*list1))
893 /*
894 * This day in wt1 is not for accounting, but this
895 * day in wt2 is for accounting.
896 */
897 return (-1);
898
899 /* [ ] -- intervals in tint1, { } -- intervals in tint1. */
900 tint2 = STAILQ_FIRST(*list2);
901 STAILQ_FOREACH(tint1, *list1, link)
902 for (;tint2 != NULL; tint2 = STAILQ_NEXT(tint2, link)) {
903 if (tint2->sec1 >= tint1->sec1 &&
904 tint2->sec2 <= tint1->sec2)
905 /* [ { } ] */
906 continue;
907 if (tint2->sec1 > tint1->sec2)
908 /* [ ] { } */
909 break;
910 /* { } [ ] or { }[ ] */
911 return (-1);
912 }
913 if (tint2 != NULL)
914 /* [ ] { } */
915 return (-1);
916 }
917
918 return (0);
919 }
920 #endif /* WITH_ANY_LIMITS */
921
922 /*
923 * Initialize time related values.
924 */
925 int
init_time_values(void)926 init_time_values(void)
927 {
928 ipa_tm tm;
929 time_t t;
930
931 if (time(&t) == (time_t)-1) {
932 logmsg(IPA_LOG_ERR, "init_time_values: time failed");
933 return (-1);
934 }
935 if (localtime_r(&t, (struct tm *)&tm) == NULL) {
936 logmsg(IPA_LOG_ERR, "init_time_values: localtime_r failed");
937 return (-1);
938 }
939 tm.tm_year += 1900;
940 tm.tm_mon++;
941 cursec = TIME_TO_SEC(&tm);
942 curdate = tm;
943 curwday = tm.tm_wday;
944 curtime = t;
945 newday_flag = 0;
946 return (0);
947 }
948
949 /*
950 * Get new time related values and verify that everything is correct
951 * with time and local date.
952 */
953 int
new_time_values(void)954 new_time_values(void)
955 {
956 ipa_tm tm;
957 double d;
958 time_t t;
959 int rv;
960
961 /* Get new current time. */
962 if (time(&t) == (time_t)-1) {
963 logmsg(IPA_LOG_ERR, "new_time_values: time failed");
964 return (-1);
965 }
966 if (localtime_r(&t, (struct tm *)&tm) == NULL) {
967 logmsg(IPA_LOG_ERR, "new_time_values: localtime_r failed");
968 return (-1);
969 }
970
971 tm.tm_year += 1900;
972 tm.tm_mon++;
973 rv = 0;
974
975 /* Check whether new time is less than old time. */
976 d = difftime(t, curtime);
977 if (d < 0.0) {
978 logmsgx(IPA_LOG_WARNING, "time goes back: delta %.0fs", d);
979 rv = 1;
980 }
981
982 /*
983 * Check whether time changes too quickly, note that ipa
984 * does not sleep more than 1 day.
985 */
986 if (d > SECONDS_IN_DAY) {
987 logmsgx(IPA_LOG_WARNING, "time changes too quickly, worked "
988 "or slept too much: delta %.0fs is greater than 1 day", d);
989 rv = 1;
990 }
991
992 #ifdef HAVE_TM_GMTOFF
993 /* Check whether offset from UTC was changed. */
994 if (tm.tm_gmtoff != curdate.tm_gmtoff) {
995 logmsgx(IPA_LOG_WARNING, "offset from UTC changed from "
996 "%ld to %ld seconds", (long)curdate.tm_gmtoff,
997 (long)tm.tm_gmtoff);
998 rv = 1;
999 }
1000 #endif
1001
1002 /* Check whether Daylight Saving flag was changed. */
1003 if (tm.tm_isdst != curdate.tm_isdst) {
1004 logmsgx(IPA_LOG_WARNING, "Daylight Saving flag changed "
1005 "from %d to %d", curdate.tm_isdst, tm.tm_isdst);
1006 rv = 1;
1007 }
1008
1009 if (rv != 0)
1010 logmsgx(IPA_LOG_WARNING, "some time related problem occurred");
1011
1012 curtime = t;
1013 curdate_new = tm;
1014 cursec_new = TIME_TO_SEC(&tm);
1015
1016 return (rv);
1017 }
1018