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