/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 1987-2000 by Sun Microsystems, Inc.
* All rights reserved.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Time management functions for auditreduce.
*/
#include "auditr.h"
#include <locale.h>
#include <libintl.h>
int derive_date(char *, struct tm *);
void derive_str(time_t, char *);
int parse_time(char *, int);
time_t tm_to_secs(struct tm *);
static int check_time(struct tm *);
static int days_in_year(int);
static char *do_invalid(void);
static time_t local_to_gm(struct tm *);
static char *invalid_inter = NULL;
/*
* Array of days per month.
*/
static int days_month[] = {
31, 28, 31, 30, 31, 30,
31, 31, 30, 31, 30, 31 };
char *
do_invalid(void)
{
if (invalid_inter == NULL)
invalid_inter = gettext("invalid date/time format -");
return (invalid_inter);
}
/*
* .func local_to_gm - local time to gm time.
* .desc Convert a local time to Greenwhich Mean Time.
* The local time is in the struct tm (time.h) format, which
* is easily got from an ASCII input format (10:30:33 Jan 3, 1983).
* It works by assuming that the given local time is a GMT time and
* then asking the system for the corresponding local time. It then
* takes the difference between those two as the correction for
* time zones and daylight savings time. This is accurate unless
* the time the user asked for is near a DST switch. Then a
* correction is applied - it is assumed that if we can produce
* a GMT that, when run through localtime(), is equivalent to the
* user's original input, we have an accurate GMT. The applied
* correction simply adjusts the GMT by the amount that the derived
* localtime was off. See?
* It should be noted that when there is DST there is one local hour
* a year when time occurs twice (in the fall) and one local hour a
* year when time never occurs (in the spring).
* memcpy() is used because the calls to gmtime() and localtime()
* return pointers to static structures that are overwritten at each
* call.
* .call ret = local_to_gm(tme).
* .arg tme - ptr to struct tm (see time.h) containing local time.
* .ret time_t - seconds since epoch of equivalent GMT.
*/
time_t
local_to_gm(struct tm *tme)
{
time_t secs, gsecs, lsecs, save_gsecs;
time_t r1secs, r2secs;
struct tm ltime, gtime;
/*
* Get the input time in local and gmtime assuming the input
* was GMT (which it probably wasn't).
*/
r1secs = secs = tm_to_secs(tme);
(void) memcpy((void *)&gtime, (void *)gmtime(&secs), sizeof (gtime));
(void) memcpy((void *)&ltime, (void *)localtime(&secs), sizeof (ltime));
/*
* Get the local and gmtime in seconds, from the above tm structures.
* Calculate difference between local and GMT.
*/
gsecs = tm_to_secs(&gtime);
lsecs = tm_to_secs(&ltime);
secs = lsecs - gsecs;
gsecs -= secs;
(void) memcpy((void *)&ltime, (void *)localtime(&gsecs),
sizeof (ltime));
/*
* Now get a computed local time from the computed gmtime.
*/
save_gsecs = gsecs;
r2secs = tm_to_secs(&ltime);
/*
* If the user given local time is != computed local time then
* we need to try a correction.
*/
if (r1secs != r2secs) {
/*
* Use the difference between give localtime and computed
* localtime as our correction.
*/
if (r2secs > r1secs) {
gsecs -= r2secs - r1secs;
} else {
gsecs += r1secs - r2secs;
}
/*
* And try the comparison again...
*/
(void) memcpy((void *)&ltime, (void *)localtime(&gsecs),
sizeof (ltime));
r2secs = tm_to_secs(&ltime);
/*
* If the correction fails then we are on a DST line
* and the user-given local time never happened.
* Do the best we can.
*/
if (r1secs != r2secs) {
gsecs = save_gsecs;
}
}
return (gsecs);
}
/*
* .func tm_to_secs - convert to seconds.
* .desc Convert a tm time structure (time.h) into seconds since
* Jan 1, 1970 00:00:00. The time is assumed to be GMT and
* so no daylight savings time correction is applied. That
* is left up to the system calls (localtime(), gmtime()).
* .call ret = tm_to_secs(tme).
* .arg tme - ptr to tm structure.
* .ret time_t - number of seconds.
*/
time_t
tm_to_secs(struct tm *tme)
{
int leap_year = FALSE;
int days = 0;
time_t num_sec = 0;
int sec = tme->tm_sec;
int min = tme->tm_min;
int hour = tme->tm_hour;
int day = tme->tm_mday;
int month = tme->tm_mon;
int year = tme->tm_year + 1900;
if (days_in_year(year) == 366)
leap_year = TRUE;
while (year > 1970) {
num_sec += days_in_year(--year) * 24 * 60 * 60;
}
while (month > 0) {
days = days_month[--month];
if (leap_year && month == 1) { /* 1 is February */
days++;
}
num_sec += days * 24 * 60 * 60;
}
num_sec += --day * 24 * 60 * 60;
num_sec += hour * 60 * 60;
num_sec += min * 60;
num_sec += sec;
return (num_sec);
}
/*
* .func check_time - check tm structure.
* .desc Check the time in a tm structure to see if all of the fields
* are within range.
* .call err = check_time(tme).
* .arg tme - ptr to struct tm (see time.h).
* .ret 0 - time is ok.
* .ret -1 - time had a problem (description in error_str).
*/
int
check_time(struct tm *tme)
{
error_str = NULL;
if (tme->tm_sec < 0 || tme->tm_sec > 59) {
(void) sprintf(errbuf,
gettext("seconds out of range (%d)"), tme->tm_sec + 1);
error_str = errbuf;
} else if (tme->tm_min < 0 || tme->tm_min > 59) {
(void) sprintf(errbuf,
gettext("minutes out of range (%d)"), tme->tm_min + 1);
error_str = errbuf;
} else if (tme->tm_hour < 0 || tme->tm_hour > 23) {
(void) sprintf(errbuf,
gettext("hours out of range (%d)"), tme->tm_hour + 1);
error_str = errbuf;
} else if (tme->tm_mon < 0 || tme->tm_mon > 11) {
(void) sprintf(errbuf,
gettext("months out of range (%d)"), tme->tm_mon + 1);
error_str = errbuf;
} else if (tme->tm_year < 0) {
(void) sprintf(errbuf,
gettext("years out of range (%d)"), tme->tm_year);
error_str = errbuf;
} else if (tme->tm_mday < 1 || tme->tm_mday > days_month[tme->tm_mon]) {
if (!(days_in_year(tme->tm_year + 1900) == 366 &&
tme->tm_mon == 1 &&
tme->tm_mday == 29)) { /* leap year and February */
(void) sprintf(errbuf,
gettext("days out of range (%d)"), tme->tm_mday);
error_str = errbuf;
}
} else if (tme->tm_wday < 0 || tme->tm_wday > 6) {
(void) sprintf(errbuf,
gettext("weekday out of range (%d)"), tme->tm_wday);
error_str = errbuf;
} else if (tme->tm_yday < 0 || tme->tm_yday > 365) {
(void) sprintf(errbuf,
gettext("day of year out of range (%d)"), tme->tm_yday);
error_str = errbuf;
}
if (error_str == NULL)
return (0);
else
return (-1);
}
/*
* .func parse_time.
* .desc Parse a user time from the command line. The user time is assumed
* to be local time.
* Supported formats currently are:
* 1. +xt - where x is a number and t is a type.
* types are - 's' second, 'm' minute, 'h' hour, and 'd' day.
* 2. yymmdd - yyyymmdd.
* yymmddhh - yyyymmddhh.
* yymmddhhmm - yyyymmddhhmm.
* yymmddhhmmss - yyyymmddhhmmss.
* .call err = parse_time(str, opt).
* .arg str - ptr to user input string.
* .arg opt - time option being processed.
* .ret 0 - succesful.
* .ret -1 - failure (error message in error_str).
*/
int
parse_time(char *str, int opt)
{
int ret, len, factor;
char *strxx;
long lnum;
struct tm thentime;
len = strlen(str);
/*
* If the strlen < 6 then in the "-b +2d" type of format.
*/
if (len < 6) {
if (*str++ != '+') {
(void) sprintf(errbuf, gettext("%s needs '+' (%s)"),
do_invalid(), str);
error_str = errbuf;
return (-1);
}
if (opt != 'b') {
(void) sprintf(errbuf,
gettext("%s only allowed with 'b' option (%s)"),
do_invalid(), str);
error_str = errbuf;
return (-1);
}
if (m_after == 0) {
(void) sprintf(errbuf,
gettext("must have -a to use -b +nx form (%s)"),
str);
error_str = errbuf;
return (-1);
}
/*
* Find out what type of offset it is - 's' 'm' 'h' or 'd'.
* Make sure that the offset is all numbers.
*/
if ((strxx = strpbrk(str, "dhms")) == NULL) {
(void) sprintf(errbuf,
gettext("%s needs 'd', 'h', 'm', or 's' (%s)"),
do_invalid(), str);
error_str = errbuf;
return (-1);
} else {
ret = *strxx;
*strxx = '\0';
}
if (strlen(str) != strspn(str, "0123456789")) {
(void) sprintf(errbuf,
gettext("%s non-numeric offset (%s)"),
do_invalid(), str);
error_str = errbuf;
return (-1);
}
factor = 1; /* seconds is default */
if (ret == 'd') /* days */
factor = 24 * 60 * 60;
else if (ret == 'h') /* hours */
factor = 60 * 60;
else if (ret == 'm') /* minutes */
factor = 60;
lnum = atol(str);
m_before = m_after + (lnum * factor);
return (0);
}
/*
* Must be a specific date/time format.
*/
if (derive_date(str, &thentime))
return (-1);
/*
* For 'd' option clear out the hh:mm:ss to get to the start of the day.
* Then add one day's worth of seconds to get the 'b' time.
*/
if (opt == 'd') {
thentime.tm_sec = 0;
thentime.tm_min = 0;
thentime.tm_hour = 0;
m_after = local_to_gm(&thentime);
m_before = m_after + (24 * 60 * 60);
} else if (opt == 'a') {
m_after = local_to_gm(&thentime);
} else if (opt == 'b') {
m_before = local_to_gm(&thentime);
}
return (0);
}
/*
* .func derive_date.
* .desc Derive a date/time structure (tm) from a string.
* String is in one of these formats:
* [yy]yymmddhhmmss
* [yy]yymmddhhmm
* [yy]yymmddhh
* [yy]yymmdd
* .call ret = derive_date(str, tme).
* .arg str - ptr to input string.
* .arg tme - ptr to tm structure (time.h).
* .ret 0 - no errors in string.
* .ret -1 - errors in string (description in error_str).
*/
int
derive_date(char *str, struct tm *tme)
{
char *strs;
char *digits = "0123456789";
size_t len;
struct tm nowtime;
len = strlen(str);
if (len != strspn(str, digits)) {
(void) sprintf(errbuf, gettext("%s not all digits (%s)"),
do_invalid(), str);
error_str = errbuf;
return (-1);
}
if (len % 2) {
(void) sprintf(errbuf, gettext("%s odd number of digits (%s)"),
do_invalid(), str);
error_str = errbuf;
return (-1);
}
/*
* May need larger string storage to add '19' or '20'.
*/
strs = (char *)a_calloc(1, len + 4);
/*
* Get current time to see what century it is.
*/
(void) memcpy((char *)&nowtime, (char *)gmtime(&time_now),
sizeof (nowtime));
/*
* If the year does not begin with '19' or '20', then report
* an error and abort.
*/
if ((str[0] != '1' || str[1] != '9') && /* 19XX */
(str[0] != '2' || str[1] != '0')) { /* 20XX */
(void) sprintf(errbuf, gettext("invalid year (%c%c%c%c)"),
str[0], str[1], str[2], str[3]);
error_str = errbuf;
free(strs);
return (-1);
}
len = strlen(str); /* may have changed */
if (len < 8 || len > 14) {
(void) sprintf(errbuf,
gettext("invalid date/time length (%s)"), str);
error_str = errbuf;
free(strs);
return (-1);
}
/* unspecified values go to 0 */
(void) memset((void *) tme, 0, (size_t)sizeof (*tme));
(void) strncpy(strs, str, 4);
strs[4] = '\0';
tme->tm_year = atoi(strs) - 1900; /* get the year */
(void) strncpy(strs, str + 4, 2);
strs[2] = '\0';
tme->tm_mon = atoi(strs) - 1; /* get months */
(void) strncpy(strs, str + 6, 2);
strs[2] = '\0';
tme->tm_mday = atoi(strs); /* get days */
if (len >= 10) { /* yyyymmddhh */
(void) strncpy(strs, str + 8, 2);
strs[2] = '\0';
tme->tm_hour = atoi(strs); /* get hours */
}
if (len >= 12) { /* yyyymmddhhmm */
(void) strncpy(strs, str + 10, 2);
strs[2] = '\0';
tme->tm_min = atoi(strs); /* get minutes */
}
if (len >= 14) { /* yyyymmddhhmmss */
(void) strncpy(strs, str + 12, 2);
strs[2] = '\0';
tme->tm_sec = atoi(strs); /* get seconds */
}
free(strs);
return (check_time(tme)); /* lastly check the ranges */
}
/*
* .func derive_str - derive string.
* .desc Derive a string representation of a time for a filename.
* The output is in the 14 character format yyyymmddhhmmss.
* .call derive_str(clock, buf).
* .arg clock - seconds since epoch.
* .arg buf - place to put resultant string.
* .ret void.
*/
void
derive_str(time_t clock, char *buf)
{
struct tm gtime;
(void) memcpy((void *) & gtime, (void *)gmtime(&clock), sizeof (gtime));
(void) sprintf(buf, "%4d", gtime.tm_year + 1900);
(void) sprintf(buf + 4, "%.2d", gtime.tm_mon + 1);
(void) sprintf(buf + 6, "%.2d", gtime.tm_mday);
(void) sprintf(buf + 8, "%.2d", gtime.tm_hour);
(void) sprintf(buf + 10, "%.2d", gtime.tm_min);
(void) sprintf(buf + 12, "%.2d", gtime.tm_sec);
buf[14] = '\0';
}
int
days_in_year(int year)
{
if (isleap(year))
return (366);
return (365);
}