/*- * Copyright (c) 2005 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: ipastat_time.c,v 1.1.4.3 2007/05/11 16:29:59 simon Exp $"; #endif /* !lint */ #include #include #include #include #include #include #include #include #include "ipa_mod.h" #include "dlapi.h" #include "confcommon.h" #include "memfunc.h" #include "ipastat_time.h" #include "ipastat_log.h" #include "ipastat_main.h" /* List of all opt_tint structures. */ struct opt_tint_list opt_tint_list = STAILQ_HEAD_INITIALIZER(opt_tint_list); static struct opt_tint opt_tint_default; /* Default opt_tint. */ /* Regular expressions for checking time interval. */ #define PAT_TINT_FORM "^[^-]+(-[^-]+)?$" #define PAT_TINT_PART "^\ (\ [[:digit:]]{4,}(\\.([[:digit:]]{1,2}|[[:alpha:]]{3}))?(\\.[[:digit:]]{1,2})?|\ ([[:digit:]]{1,2}|[[:alpha:]]{3})?(\\.[[:digit:]]{1,2})?\ )?\ (\ /[[:digit:]]{1,2}(:[[:digit:]]{1,2})?(:[[:digit:]]{1,2})?\ )?$" static regex_t reg_tint_form; /* Compiled PAT_TINT_FORM. */ static regex_t reg_tint_part; /* Compiled PAT_TINT_PART. */ static ipa_tm curdate; /* Current date. */ 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 }; static const char month_name[][3] = { "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" }; /* * Initialized various local date related variables. */ int init_time_data(void) { time_t t; /* Get current date. */ if (time(&t) == (time_t)-1) { logmsg(IPA_LOG_ERR, "init_time_data: time failed"); return -1; } if (localtime_r(&t, (struct tm *)&curdate) == NULL) { logmsg(IPA_LOG_ERR, "init_time_data: localtime_r failed"); return -1; } curdate.tm_year += 1900; curdate.tm_mon++; return 0; } /* * Compare two ipa_tm tm1 and tm2 structures: * if (tm1 == tm2) * return 0; * if (tm1 > tm2) * return 1; * if (tm1 < tm2) * return -1; */ static 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 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]; } /* * Output error message, that month, day, hours, minutes or seconds are * specified incorrect in some part of time interval. */ static void wrong_tint_part(const char *what, int right) { logmsgx(IPA_LOG_ERR, "parse_tint_part: wrong format of time interval's %s part: incorrect %s value", right ? "right" : "left", what); } static int parse_tint_part(char *s, ipa_tm *tm, int right) { int n, val1, val2, val3; char *ptr; /* * Initial value for time in the left part, for the right * part these values are completed at the end of this function. */ tm->tm_hour = tm->tm_min = tm->tm_sec = 0; if (*s != '/') { /* Date is specified. */ if (*s == '.') { /* .DD */ errno = 0; if (sscanf(++s, "%u", &tm->tm_mday) != 1) { logmsgx(IPA_LOG_ERR, "parse_tint_part: sscanf(\"%s\", %%u): failed", s); return -1; } } else { /* Check if there is named month. */ for (ptr = s; *ptr != '\0'; ++ptr) if (isalpha(*ptr)) { for (n = 0; n < 12; ++n) if (strncasecmp(ptr, month_name[n], 3) == 0) { errno = 0; if (sprintf(ptr, "%02d", n + 1) != 2) { logmsgx(IPA_LOG_ERR, "parse_tint_part: sprintf failed"); return -1; } ptr += 2; do { *ptr = *(ptr + 1); } while (*++ptr != '\0'); break; } if (n == 12) { logmsgx(IPA_LOG_ERR, "parse_tint_part: wrong format of time interval: unknown month name"); return -1; } break; } errno = 0; if ( (n = sscanf(s, "%u.%u.%u", &val1, &val2, &val3)) < 1) { logmsgx(IPA_LOG_ERR, "parse_tint_part: sscanf(\"%s\", %%u.%%u.%%u) failed", s); return -1; } if ( (ptr = strchr(s, '.')) == NULL) if ( (ptr = strchr(s, '/')) == NULL) ptr = strchr(s, '\0'); switch (n) { case 3: /* YYYY.MM.DD */ tm->tm_year = val1; tm->tm_mon = val2; tm->tm_mday = val3; break; case 2: if (ptr - s >= 4) { /* YYYY.MM */ tm->tm_year = val1; tm->tm_mon = val2; tm->tm_mday = right ? last_mday(val1, val2) : 1; } else { /* MM.DD */ tm->tm_mon = val1; tm->tm_mday = val2; } break; default: /* 1 */ if (ptr - s >= 4) { /* YYYY */ tm->tm_year = val1; if (right) { tm->tm_mon = 12; tm->tm_mday = last_mday(val1, 12); } else tm->tm_mon = tm->tm_mday = 1; } else { /* MM */ tm->tm_mon = val1; tm->tm_mday = right ? last_mday(tm->tm_year, val1) : 1; } } } if (tm->tm_mon == 0 || tm->tm_mon > MONTHES_IN_YEAR) { logmsgx(IPA_LOG_ERR, "parse_tint_part: wrong month %d", tm->tm_mon); return -1; } if (tm->tm_mday == 0 || tm->tm_mday > 31) { logmsgx(IPA_LOG_ERR, "parse_tint_part: wrong month day %d", tm->tm_mday); return -1; } } n = 0; if ( (s = strchr(s, '/')) != NULL) { /* Time is specified. */ errno = 0; if ( (n = sscanf(++s, "%u:%u:%u", &val1, &val2, &val3)) < 1) { logmsgx(IPA_LOG_ERR, "parse_tint_part: sscanf(\"%s\", %%u:%%u:%%u) failed", s); return -1; } if (n >= 1) tm->tm_hour = val1; /* hh */ if (n >= 2) tm->tm_min = val2; /* hh:mm */ if (n == 3) tm->tm_sec = val3; /* hh:mm:ss */ /* Validate month, day, hours, minutes and seconds. */ if (tm->tm_mon == 0 || tm->tm_mon > 12) { wrong_tint_part("month", right); return -1; } if (tm->tm_mday == 0 || tm->tm_mday > last_mday(tm->tm_year, tm->tm_mon)) { wrong_tint_part("day", right); return -1; } if (tm->tm_hour > 23 && !(tm->tm_hour == HOURS_IN_DAY && tm->tm_min == 0 && tm->tm_sec == 0)) { wrong_tint_part("hours", right); return -1; } if (tm->tm_min > 59) { wrong_tint_part("minutes", right); return -1; } if (tm->tm_sec > 59) { wrong_tint_part("seconds", right); return -1; } } /* Complete value of time for the right part, if needed. */ if (right) switch (n) { case 2: /* hh:mm */ if (++tm->tm_min > 59) tm->tm_min = 0; else break; /* FALLTHROUGH */ case 1: /* hh */ if (++tm->tm_hour <= 23) break; /* FALLTHROUGH */ case 0: tm->tm_hour = HOURS_IN_DAY; } return 0; } /* * Add optional time interval (-q -i|I ) and parse this * interval. */ int add_opt_tint(char *tint_str, int exact) { static int inited = 0; char *right_part; struct tm *tm1, *tm2; struct opt_tint *opt_tint; if (!inited) { /* Build regular expressions. */ if ( (re_errcode = regcomp(®_tint_form, PAT_TINT_FORM, REG_EXTENDED|REG_NOSUB)) != 0) { re_form_errbuf(); logmsgx(IPA_LOG_ERR, "add_opt_tint: regcomp(%s): %s", PAT_TINT_FORM, re_errbuf); return -1; } if ( (re_errcode = regcomp(®_tint_part, PAT_TINT_PART, REG_EXTENDED|REG_NOSUB)) != 0) { re_form_errbuf(); logmsgx(IPA_LOG_ERR, "add_opt_tint: regcomp(%s): %s", PAT_TINT_PART, re_errbuf); return -1; } inited = 1; } /* Validate time interval format. */ if (regexec(®_tint_form, tint_str, 0, (regmatch_t *)NULL, 0) != 0) { logmsgx(IPA_LOG_ERR, "add_opt_tint: wrong format of time interval \"%s\"", tint_str); return -1; } /* Split time interval into parts. */ if ( (right_part = strchr(tint_str, '-')) != NULL) /* Split time interval into left and right parts. */ *right_part++ = '\0'; else /* Left and right parts are the same as whole time interval. */ right_part = tint_str; if (regexec(®_tint_part, tint_str, 0, (regmatch_t *)NULL, 0) != 0) { logmsgx(IPA_LOG_ERR, "add_opt_tint: cannot recognize format of time interval's left part \"%s\"", tint_str); return -1; } if (right_part != tint_str) if (regexec(®_tint_part, right_part, 0, (regmatch_t *)NULL, 0) != 0) { logmsgx(IPA_LOG_ERR, "add_opt_tint: cannot recognize format of time interval's right part \"%s\"", right_part); return -1; } if ( (opt_tint = mem_malloc(sizeof *opt_tint, m_anon)) == NULL) { logmsgx(IPA_LOG_ERR, "add_opt_tint: mem_malloc failed"); return -1; } opt_tint->tm1 = opt_tint->tm2 = curdate; tm1 = &opt_tint->tm1; tm2 = &opt_tint->tm2; if (parse_tint_part(tint_str, tm1, 0) < 0 || parse_tint_part(right_part, tm2, 1) < 0) return -1; if (cmp_ipa_tm(&opt_tint->tm1, &opt_tint->tm2) > 0) { logmsgx(IPA_LOG_ERR, "add_opt_tint: first timestamp (%d.%02d.%02d/%02d:%02d:%02d) should be less than or equal to second timestamp (%d.%02d.%02d/%02d:%02d:%02d)", tm1->tm_year, tm1->tm_mon, tm1->tm_mday, tm1->tm_hour, tm1->tm_min, tm1->tm_sec, tm2->tm_year, tm2->tm_mon, tm2->tm_mday, tm2->tm_hour, tm2->tm_min, tm2->tm_sec); return -1; } opt_tint->exact = exact; STAILQ_INSERT_TAIL(&opt_tint_list, opt_tint, link); return 0; } /* * Release memory previously allocated by add_opt_tint(). */ void free_opt_tint_list(void) { struct opt_tint *opt_tint, *opt_tint_next; if (!STAILQ_EMPTY(&opt_tint_list) && STAILQ_FIRST(&opt_tint_list) != &opt_tint_default) { for (opt_tint = STAILQ_FIRST(&opt_tint_list); opt_tint != NULL; opt_tint = opt_tint_next) { opt_tint_next = STAILQ_NEXT(opt_tint, link); mem_free(opt_tint, m_anon); } regfree(®_tint_form); regfree(®_tint_part); } } /* * If there are not any -i options, than create default one, * which is equal to the current month. */ void init_opt_tint_default(void) { ipa_tm *tm1, *tm2; tm1 = &opt_tint_default.tm1; tm2 = &opt_tint_default.tm2; tm1->tm_year = tm2->tm_year = curdate.tm_year; tm1->tm_mon = tm2->tm_mon = curdate.tm_mon; tm1->tm_mday = 1; tm2->tm_mday = last_mday(curdate.tm_year, curdate.tm_mon); tm1->tm_hour = tm1->tm_min = tm1->tm_sec = tm2->tm_min = tm2->tm_sec = 0; tm2->tm_hour = HOURS_IN_DAY; STAILQ_INSERT_HEAD(&opt_tint_list, &opt_tint_default, link); }