/*-
* 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 <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#include <sys/queue.h>
#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 <name>) 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);
}
syntax highlighted by Code2HTML, v. 0.9.1