/* * 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. */ /* * Time management functions for auditreduce. */ #include "auditr.h" #include #include 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 *)>ime, (void *)gmtime(&secs), sizeof (gtime)); (void) memcpy((void *)<ime, (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(>ime); lsecs = tm_to_secs(<ime); secs = lsecs - gsecs; gsecs -= secs; (void) memcpy((void *)<ime, (void *)localtime(&gsecs), sizeof (ltime)); /* * Now get a computed local time from the computed gmtime. */ save_gsecs = gsecs; r2secs = tm_to_secs(<ime); /* * 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 *)<ime, (void *)localtime(&gsecs), sizeof (ltime)); r2secs = tm_to_secs(<ime); /* * 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); }