/*- * Copyright (c) 2003 Andrey Simonenko * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #ifndef lint static const char rcsid[] ATTR_UNUSED = "@(#)$Id: ipa_time.c,v 1.2.2.3 2007/05/11 16:29:59 simon Exp $"; #endif /* !lint */ #include #include #include #include #include #include "ipa_mod.h" #include "dlapi.h" #include "confcommon.h" #include "memfunc.h" #include "ipa_time.h" #include "ipa_log.h" #include "ipa_main.h" /* * This file contains different time manipulation functions. I don't * use standard library time manipulation functions, they can't be used * directly and should be wrapped to fix time zone and summer time related * errors, since ipa(8) doesn't need information about time zone and * summer time, I found that implementing own functions is better, * than writing wrappers for standard library time functions. * * May be for(;;) loops are not optimal, but for most cases * they are not executed and the ideas of algorithms are easy. */ u_int wakeup_time; /* wakeup_time parameter. */ u_int freeze_time; /* freeze_time parameter. */ u_int sleep_after_dump; /* sleep_after_dump parameter. */ u_int sensitive_time; /* sensitive_time parameter. */ int debug_time; /* debug_time parameter. */ int debug_worktime; /* debug_worktime parameter. */ u_int worktimes_check_sec; /* When to call worktimes_check(). */ const struct worktime *global_worktime; /* global { worktime } */ struct worktime worktime_default; /* 00:00-24:00 for all days. */ static struct tint_set tint_set_default; const struct tevent *global_update_tevent; /* global { update_time } */ const struct tevent *global_append_tevent; /* global { append_time } */ const char *const wdays[] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; const char *const active_msg[] = { "inactive", /* 0 */ "active" /* 1 */ }; ipa_mzone *tevent_mzone; /* Mzone for all struct tevent{}. */ struct tevents_list tevents_list; /* List of all tevents. */ ipa_mzone *tint_mzone; /* Mzone for all struct tint{}. */ struct tint_sets tint_sets; /* List of all tint_sets. */ ipa_mzone *worktime_mzone; /* Mzone for all worktimes. */ struct worktimes_list worktimes_list; /* List of all worktimes. */ ipa_tm curdate; /* Current human localdate. */ u_int cursec; /* Current time in seconds since midnight. */ u_int curwday; /* Current week day. */ int newday_flag = 0; /* Non-zero if new day came. */ #ifdef WITH_ANY_LIMITS char *ipa_tm_buf = NULL; /* Pointer to buffer used in ipa_tm_to_buf(). */ #endif char *tdiff_buf = NULL; /* Pointer to buffer used in tdiff_to_buf(). */ static int const last_mday_arr[] = /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */ { 0, 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; #ifdef WITH_ANY_LIMITS /* * Non-reentrant function for convert ipa_tm{} value to human readable * value and return a pointer to allocated buffer with this value. * If an error occurred, then a pointer to static string with error * message is returned. */ const char * ipa_tm_to_buf(const ipa_tm *tm) { mem_free(ipa_tm_buf, m_anon); if (mem_asprintf(m_anon, &ipa_tm_buf, "%d.%02d.%02d/%02d:%02d:%02d", tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec) < 0) return "(ipa_tm_to_buf: mem_asprintf: no memory)"; return ipa_tm_buf; } /* * Return last month day in the given year/mon. */ static int last_mday(int year, int mon) { if (mon == 2) /* February */ return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) ? 29 : 28; return last_mday_arr[mon]; } /* * Check ipa_tm date for correct values, ignore nonexistent * time (for example when time zone is changed as +1h, then some * time does not exist). */ int check_ipa_tm(const ipa_tm *tm) { int mon = tm->tm_mon; int mday = tm->tm_mday; if (mon == 0 || mon > 12 || mday > 31 || mday == 0 || mday > last_mday(tm->tm_year, mon) || tm->tm_hour > 23 || tm->tm_min > 59 || tm->tm_sec > 59) return -1; return 0; } /* * If time in tm is 24:00:00, then convert it to 00:00:00 of the next day. */ void fix_240000(ipa_tm *tm) { /* When tm_hour is 24, then tm_min and tm_sec == 0. */ if (tm->tm_hour == HOURS_IN_DAY) { /* 24:00:00 --> next day 00:00:00 */ tm->tm_hour = 0; if (tm->tm_mday == last_mday(tm->tm_year, tm->tm_mon)) { tm->tm_mday = 1; if (tm->tm_mon == MONTHES_IN_YEAR) { tm->tm_mon = 1; tm->tm_year++; } else tm->tm_mon++; } else tm->tm_mday++; } } /* * Compare two ipa_tm tm1 and tm2 structures: * if (tm1 == tm2) * return 0; * if (tm1 > tm2) * return 1; * if (tm1 < tm2) * return -1; * This function correctly works with 24:00:00 time. */ int cmp_ipa_tm(const ipa_tm *tm1, const ipa_tm *tm2) { /* * We don't use mktime(3), because there can be problem * with time zone and summer time. */ if (tm1->tm_year > tm2->tm_year) return 1; if (tm1->tm_year == tm2->tm_year) { if (tm1->tm_mon > tm2->tm_mon) return 1; if (tm1->tm_mon == tm2->tm_mon) { if (tm1->tm_mday > tm2->tm_mday) return 1; if (tm1->tm_mday == tm2->tm_mday) { if (tm1->tm_hour > tm2->tm_hour) return 1; if (tm1->tm_hour == tm2->tm_hour) { if (tm1->tm_min > tm2->tm_min) return 1; if (tm1->tm_min == tm2->tm_min) { if (tm1->tm_sec > tm2->tm_sec) return 1; if (tm1->tm_sec == tm2->tm_sec) return 0; } } } } } return -1; } /* * Return difference in seconds between tm1 and tm0: tm1 - tm0, * tm1 is expected to be greater than tm0. */ u_int ipa_tm_diff(const ipa_tm *tm1_arg, const ipa_tm *tm0_arg) { u_int result, sec1, sec0; ipa_tm tm1, tm0; tm1 = *tm1_arg; tm0 = *tm0_arg; sec1 = tm1.tm_hour * SECONDS_IN_HOUR + tm1.tm_min * SECONDS_IN_MINUTE + tm1.tm_sec; sec0 = tm0.tm_hour * SECONDS_IN_HOUR + tm0.tm_min * SECONDS_IN_MINUTE + tm0.tm_sec; if (sec1 > sec0) result = sec1 - sec0; else { result = HOURS_IN_DAY * SECONDS_IN_HOUR - sec0; result += sec1; if (tm0.tm_mday == last_mday(tm0.tm_year, tm0.tm_mon)) { tm0.tm_mday = 1; if (tm0.tm_mon == MONTHES_IN_YEAR) { tm0.tm_mon = 1; tm0.tm_year++; } else tm0.tm_mon++; } else tm0.tm_mday++; } for (; tm0.tm_year < tm1.tm_year; ++tm0.tm_year) { for (; tm0.tm_mon <= MONTHES_IN_YEAR; tm0.tm_mon++) { result += (last_mday(tm0.tm_year, tm0.tm_mon) - tm0.tm_mday + 1) * SECONDS_IN_DAY; tm0.tm_mday = 1; } tm0.tm_mon = 1; } for (; tm0.tm_mon < tm1.tm_mon; tm0.tm_mon++) { result += (last_mday(tm0.tm_year, tm0.tm_mon) - tm0.tm_mday + 1) * SECONDS_IN_DAY; tm0.tm_mday = 1; } result += (tm1.tm_mday - tm0.tm_mday) * SECONDS_IN_DAY; return result; } #endif /* WITH_ANY_LIMITS */ #ifdef WITH_LIMITS /* * Add some seconds to ipa_tm variable ignoring any time zone issues * without using any standard time functions from the library. */ void ipa_tm_addtime(ipa_tm *tm, u_int addsec) { int t, lmday, year, mon, mday, hour, min, sec; year = tm->tm_year; mon = tm->tm_mon; mday = tm->tm_mday; hour = tm->tm_hour; min = tm->tm_min; sec = tm->tm_sec; t = addsec / SECONDS_IN_DAY; addsec -= t * SECONDS_IN_DAY; if (t != 0) { for (;;) { lmday = last_mday(year, mon); if (mday + t > lmday) { t -= lmday - mday; if (mon == MONTHES_IN_YEAR) { mon = 1; year++; } else mon++; mday = 1; if (--t == 0) break; } else { mday += t; break; } } } if (addsec != 0) { t = hour * SECONDS_IN_HOUR + min * SECONDS_IN_MINUTE + sec; if (t + addsec >= SECONDS_IN_DAY) { t += addsec - SECONDS_IN_DAY; lmday = last_mday(year, mon); if (++mday > lmday) { if (mon == MONTHES_IN_YEAR) { mon = 1; year++; } else mon++; mday = 1; } } else t += addsec; hour = t / SECONDS_IN_HOUR; t -= hour * SECONDS_IN_HOUR; min = t / SECONDS_IN_MINUTE; sec = t - min * SECONDS_IN_MINUTE; } tm->tm_year = year; tm->tm_mon = mon; tm->tm_mday = mday; tm->tm_hour = hour; tm->tm_min = min; tm->tm_sec = sec; } /* * Convert given time in tm according to +upto. */ static void ipa_tm_upto(ipa_tm *tm, char upto) { if (upto == TEXP_UPTO_SIMPLE) return; /* +M or +m -> +h -> +D -> +W */ if (upto == TEXP_UPTO_MONTH) { /* +M */ /* Up to the end of month. */ if (tm->tm_mon == MONTHES_IN_YEAR) { tm->tm_mon = 1; tm->tm_year++; } else tm->tm_mon++; tm->tm_mday = 1; tm->tm_hour = tm->tm_min = tm->tm_sec = 0; return; } /* Up to the end of minute. */ ipa_tm_addtime(tm, SECONDS_IN_MINUTE - tm->tm_sec); if (upto == TEXP_UPTO_MINUTE) /* +m */ return; tm->tm_min++; /* Up to the end of hour. */ ipa_tm_addtime(tm, SECONDS_IN_MINUTE * (MINUTES_IN_HOUR - tm->tm_min)); if (upto == TEXP_UPTO_HOUR) /* +h */ return; tm->tm_hour++; /* Up to the end of day. */ ipa_tm_addtime(tm, SECONDS_IN_HOUR * (HOURS_IN_DAY - tm->tm_hour)); if (upto == TEXP_UPTO_DAY) /* +D */ return; tm->tm_wday++; /* Up to the end of week. */ if (tm->tm_wday != 1) /* +W */ ipa_tm_addtime(tm, SECONDS_IN_DAY * (DAYS_IN_WEEK - tm->tm_wday)); } /* * Convert given time in tm according to texp. */ void ipa_tm_texp(ipa_tm *tm, const struct texp *texp) { int right_side = texp->side; if (!right_side) ipa_tm_upto(tm, texp->upto); ipa_tm_addtime(tm, texp->seconds); if (right_side) ipa_tm_upto(tm, texp->upto); } /* * Set tm_wday in the given structure. mktime(3) ignores tm_wday * and tm_wday is set on success. It is assumed, that 00:00:01 time * exists in all time zones. */ int set_wday(ipa_tm *tm_ptr) { struct tm tm; tm = *tm_ptr; tm.tm_year -= 1900; tm.tm_mon--; tm.tm_hour = tm.tm_min = 0; tm.tm_sec = 1; tm.tm_isdst = -1; if (mktime(&tm) == (time_t)-1) { logmsg(IPA_LOG_ERR, "set_wday: mktime failed"); return -1; } tm_ptr->tm_wday = tm.tm_wday; return 0; } #endif /* WITH_LIMITS */ #ifdef WITH_THRESHOLDS /* * Subtract some seconds from ipa_tm variable ignoring any time zone issues * without using any standard time functions from the library. */ void ipa_tm_subtime(ipa_tm *tm, u_int subsec) { int t, year, mon, mday, hour, min, sec; year = tm->tm_year; mon = tm->tm_mon; mday = tm->tm_mday; hour = tm->tm_hour; min = tm->tm_min; sec = tm->tm_sec; t = subsec / SECONDS_IN_DAY; subsec -= t * SECONDS_IN_DAY; if (t != 0) { for (;;) { if (mday > t) { mday -= t; break; } else { if (mon == 1) { mon = MONTHES_IN_YEAR; year--; } else mon--; t -= mday; mday = last_mday(year, mon); if (t == 0) break; } } } if (subsec != 0) { t = hour * SECONDS_IN_HOUR + min * SECONDS_IN_MINUTE + sec; if (t >= subsec) t -= subsec; else { t = SECONDS_IN_DAY - (subsec - t); if (--mday == 0) { if (mon == 1) { mon = 12; year--; } else mon--; mday = last_mday(year, mon); } } hour = t / SECONDS_IN_HOUR; t -= hour * SECONDS_IN_HOUR; min = t / SECONDS_IN_MINUTE; sec = t - min * SECONDS_IN_MINUTE; } tm->tm_year = year; tm->tm_mon = mon; tm->tm_mday = mday; tm->tm_hour = hour; tm->tm_min = min; tm->tm_sec = sec; } #endif /* WITH_THRESHOLDS */ /* * Non-reentrant function for converting number of seconds to hours, * minutes and seconds and return a pointer to allocated buffer with * this value. If an error occurred, then static string with error * message is returned. */ const char * time_to_buf(u_int t) { static char buf[BUF_TIME_SIZE]; int rv; u_int h, m, s; h = t / SECONDS_IN_HOUR; t -= h * SECONDS_IN_HOUR; m = t / SECONDS_IN_MINUTE; s = t - m * SECONDS_IN_MINUTE; if (h == 0) { if (m == 0) rv = snprintf(buf, sizeof buf, "%us", s); else { if (s == 0) rv = snprintf(buf, sizeof buf, "%um", m); else rv = snprintf(buf, sizeof buf, "%um %02us", m, s); } } else rv = snprintf(buf, sizeof buf, "%uh %02um %02us", h, m, s); return rv < 0 ? "(time_to_buf: snprintf failed)" : buf; } /* * The same as time_to_buf(), but argument is double and can be * negative. */ const char * tdiff_to_buf(double t) { const char *sign; mem_free(tdiff_buf, m_anon); if (t > UINT_MAX) return mem_asprintf(m_anon, &tdiff_buf, "%.0f seconds", t) < 0 ? "(tdiff_to_buf: mem_asprintf failed)" : tdiff_buf; if (t < 0.0) { t = -t; sign = "-"; } else sign = ""; return mem_asprintf(m_anon, &tdiff_buf, "%s%s", sign, time_to_buf((u_int)t)) < 0 ? "(tdiff_to_buf: mem_asprintf failed)" : tdiff_buf; } /* * Non-reentrant function for converting number of seconds to time. */ const char * sec_to_buf(u_int sec) { static char buf[BUF_SEC_SIZE]; u_int h, m, s; if (sec == SECONDS_IN_WEEK) return "xx:xx:xx"; if (sec <= SECONDS_IN_DAY) { h = sec / SECONDS_IN_HOUR; sec -= h * SECONDS_IN_HOUR; m = sec / SECONDS_IN_MINUTE; s = sec - m * SECONDS_IN_MINUTE; return snprintf(buf, sizeof buf, "%02u:%02u:%02u", h, m, s) < 0 ? "(sec_to_buf: snprintf failed)" : buf; } return "??:??:??"; } /* * Non-reentrant function for converting time interval to string. */ const char * tint_to_buf(const struct tint *tint) { static char buf[12]; /* xx:xx-xx:xx */ u_int h1, m1, h2, m2; h1 = tint->sec1 / SECONDS_IN_HOUR; m1 = (tint->sec1 - h1 * SECONDS_IN_HOUR) / SECONDS_IN_MINUTE; h2 = tint->sec2 / SECONDS_IN_HOUR; m2 = (tint->sec2 - h2 * SECONDS_IN_HOUR) / SECONDS_IN_MINUTE; return snprintf(buf, sizeof buf, "%02u:%02u-%02u:%02u", h1, m1, h2, m2) < 0 ? "(tint_to_buf: snprintf failed)" : buf; } /* * Convert seconds since midnight to hours, minutes and seconds. */ void sec_to_time(u_int sec, ipa_tm *tm) { int h, m, s; h = sec / SECONDS_IN_HOUR; sec -= h * SECONDS_IN_HOUR; m = sec / SECONDS_IN_MINUTE; s = sec - m * SECONDS_IN_MINUTE; tm->tm_hour = h; tm->tm_min = m; tm->tm_sec = s; } void init_worktime_default(void) { static struct tint tint; u_int wday; struct tint_list *list = &tint_set_default.list; tint.sec1 = 0; tint.sec2 = SECONDS_IN_DAY; STAILQ_INIT(list); STAILQ_INSERT_HEAD(list, &tint, link); STAILQ_INSERT_HEAD(&tint_sets, &tint_set_default, link); for (wday = 0; wday < DAYS_IN_WEEK; ++wday) worktime_default.tint_list[wday] = list; } /* * Release memory help by tint_set, including struct tint_set{}. */ void free_tint_set(struct tint_set *set) { struct tint *tint, *tint_next; for (tint = STAILQ_FIRST(&set->list); tint != NULL; tint = tint_next) { tint_next = STAILQ_NEXT(tint, link); mzone_free(tint_mzone, tint); } mem_free(set, m_anon); } /* * Free all worktimes and worktime mzone. */ void free_worktimes(void) { struct tint_set *set, *set_next; /* Skip first tint, since it is tint_set_default. */ STAILQ_REMOVE_HEAD(&tint_sets, link); /* Free other tint_sets. */ for (set = STAILQ_FIRST(&tint_sets); set != NULL; set = set_next) { set_next = STAILQ_NEXT(set, link); free_tint_set(set); } mzone_deinit(tint_mzone); mzone_deinit(worktime_mzone); } /* * Find next time interval for worktime starting from tint_start. */ static void find_next_tint(struct worktime *wt, const struct tint *tint_start, int report_skipping) { const struct tint *tint; for (tint = tint_start; tint != NULL; tint = STAILQ_NEXT(tint, link)) { if (cursec < tint->sec1) { /* x [ ] */ wt->is_active = INACTIVE_FLAG; if (worktimes_check_sec > tint->sec1) worktimes_check_sec = tint->sec1; if (debug_worktime) logmsgx(IPA_LOG_INFO, "find_next_tint: %s left before start of %s %s worktime interval", time_to_buf(tint->sec1 - cursec), wdays[curwday], tint_to_buf(tint)); break; } if (tint->sec1 <= cursec && cursec < tint->sec2) { /* [ x ] */ wt->is_active = ACTIVE_FLAG; if (worktimes_check_sec > tint->sec2) worktimes_check_sec = tint->sec2; if (debug_worktime) logmsgx(IPA_LOG_INFO, "find_next_tint: %s left before end of %s %s worktime interval", time_to_buf(tint->sec2 - cursec), wdays[curwday], tint_to_buf(tint)); break; } /* [ ] x */ if (report_skipping || debug_worktime) logmsgx(IPA_LOG_WARNING, "find_next_tint: skipping %s %s worktime interval", wdays[curwday], tint_to_buf(tint)); } if (tint == NULL) { /* There is no more time intervals for current week day. */ wt->is_active = INACTIVE_FLAG; wt->active_sec = wt->inactive_sec = SECONDS_IN_WEEK; if (debug_worktime) logmsgx(IPA_LOG_INFO, "find_next_tint: there is no more worktime intervals for %s (current week day)", wdays[curwday]); } else { wt->active_sec = tint->sec1; wt->inactive_sec = tint->sec2; } /* Real tint or NULL for debugging purpose. */ wt->curtint = tint; } /* * Check if current week day is set in worktime, find current * active or inactive time interval. */ void worktimes_newday(int report_skipping) { struct worktime *wt; const struct tint_list *list; worktimes_check_sec = SECONDS_IN_WEEK; /* Hint. */ SLIST_FOREACH(wt, &worktimes_list, link) { list = wt->tint_list[curwday]; if (!STAILQ_EMPTY(list)) { /* Current week day is set in worktime. */ find_next_tint(wt, STAILQ_FIRST(list), report_skipping); } else { /* Current week day is not set in worktime. */ wt->is_active = INACTIVE_FLAG; /* Set NULL for debugging purpose. */ wt->curtint = NULL; wt->active_sec = wt->inactive_sec = SECONDS_IN_WEEK; if (debug_worktime) logmsgx(IPA_LOG_INFO, "worktimes_newday: %s (current week day) is not set in worktime", wdays[curwday]); } } if (debug_worktime) logmsgx(IPA_LOG_INFO, "worktimes_checks: worktimes_check_sec %s", sec_to_buf(worktimes_check_sec)); } /* * Check if current time interval is still active, if it is * time to make inactive time interval active. */ void worktimes_check(void) { u_int delta; struct worktime *wt; worktimes_check_sec = SECONDS_IN_WEEK; /* Hint. */ SLIST_FOREACH(wt, &worktimes_list, link) if (IS_ACTIVE(wt)) { /* Is active. */ if (wt->inactive_sec <= cursec) { /* It's time to make it inactive. */ delta = cursec - wt->inactive_sec; if (delta > sensitive_time) logmsgx(IPA_LOG_WARNING, "worktime interval %s %s became inactive too late: delta %s is greater than \"sensitive_time\" %u seconds", wdays[curwday], tint_to_buf(wt->curtint), time_to_buf(delta), sensitive_time); find_next_tint(wt, STAILQ_NEXT(wt->curtint, link), 1); } else { if (worktimes_check_sec > wt->inactive_sec) worktimes_check_sec = wt->inactive_sec; } } else { /* Is inactive. */ if (wt->active_sec <= cursec) { /* It's time to make it active. */ delta = cursec - wt->active_sec; if (delta > sensitive_time) logmsgx(IPA_LOG_WARNING, "worktime interval %s %s became active too late: delta %s is greater than \"sensitive_time\" %u seconds", wdays[curwday], tint_to_buf(wt->curtint), time_to_buf(delta), sensitive_time); find_next_tint(wt, wt->curtint, 1); } else { if (worktimes_check_sec > wt->active_sec) worktimes_check_sec = wt->active_sec; } } if (debug_worktime) logmsgx(IPA_LOG_INFO, "worktimes_checks: worktimes_check_sec %s", sec_to_buf(worktimes_check_sec)); } /* * Try to find already registered worktime with the same settings as * in wt1. If such worktime exist, then return its pointer, else * return original one. */ const struct worktime * find_worktime(struct worktime *wt1) { u_int wday; struct worktime *wt2; const struct tint_list * const *list1; const struct tint_list * const *list2; /* Check if there is already the same worktime. */ SLIST_FOREACH(wt2, &worktimes_list, link) { list1 = wt1->tint_list; list2 = wt2->tint_list; for (wday = 0; wday < DAYS_IN_WEEK; ++wday) { if (*list1 != *list2) break; ++list1; ++list2; } if (wday == DAYS_IN_WEEK) { /* The same worktime was found --> free original one. */ mzone_free(worktime_mzone, wt1); return wt2; } } /* New worktime, add it to the list. */ SLIST_INSERT_HEAD(&worktimes_list, wt1, link); return wt1; } #ifdef WITH_ANY_LIMITS /* * Return 0 if wt2 is a subset of wt1, else return -1. * Time intervals in limits' or thresholds' worktimes must be * subsets of time intervals in their rule's worktime. */ int check_worktime_subset(const struct worktime *wt1, const struct worktime *wt2) { u_int wday; const struct tint *tint1, *tint2; const struct tint_list * const *list1; const struct tint_list * const *list2; if (wt1 == NULL || wt2 == NULL || wt1 == wt2) return 0; list1 = wt1->tint_list; list2 = wt2->tint_list; for (wday = 0; wday < DAYS_IN_WEEK; ++list1, ++list2, ++wday) { if (*list1 == *list2) /* The same tint_list. */ continue; if (STAILQ_EMPTY(*list2)) /* This day in wt2 is not for accounting. */ continue; if (STAILQ_EMPTY(*list1)) /* This day in wt1 is not for accounting, but this day in wt2 is for accounting. */ return -1; /* [ ] -- intervals in tint1, { } -- intervals in tint1. */ tint2 = STAILQ_FIRST(*list2); STAILQ_FOREACH(tint1, *list1, link) for (; tint2 != NULL; tint2 = STAILQ_NEXT(tint2, link)) { if (tint2->sec1 >= tint1->sec1 && tint2->sec2 <= tint1->sec2) /* [ { } ]*/ continue; if (tint2->sec1 > tint1->sec2) /* [ ] { } */ break; /* { } [ ] or { }[ ] */ return -1; } if (tint2 != NULL) /* [ ] { } */ return -1; } return 0; } #endif /* WITH_ANY_LIMITS */