Home | History | Annotate | Download | only in auditreduce
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  * Copyright (c) 1987-2000 by Sun Microsystems, Inc.
     24  * All rights reserved.
     25  */
     26 
     27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 /*
     30  * Time management functions for auditreduce.
     31  */
     32 
     33 #include "auditr.h"
     34 #include <locale.h>
     35 #include <libintl.h>
     36 
     37 int	derive_date(char *, struct tm *);
     38 void	derive_str(time_t, char *);
     39 int	parse_time(char *, int);
     40 time_t	tm_to_secs(struct tm *);
     41 
     42 static int	check_time(struct tm *);
     43 static int	days_in_year(int);
     44 static char *do_invalid(void);
     45 static time_t	local_to_gm(struct tm *);
     46 
     47 static char *invalid_inter = NULL;
     48 
     49 /*
     50  * Array of days per month.
     51  */
     52 static int	days_month[] = {
     53 		31, 28, 31, 30, 31, 30,
     54 		31, 31, 30, 31, 30, 31 };
     55 
     56 char *
     57 do_invalid(void)
     58 {
     59 	if (invalid_inter == NULL)
     60 		invalid_inter = gettext("invalid date/time format -");
     61 	return (invalid_inter);
     62 }
     63 
     64 /*
     65  * .func	local_to_gm - local time to gm time.
     66  * .desc	Convert a local time to Greenwhich Mean Time.
     67  *	The local time is in the struct tm (time.h) format, which
     68  *	is easily got from an ASCII input format (10:30:33 Jan 3, 1983).
     69  *	It works by assuming that the given local time is a GMT time and
     70  *	then asking the system for the corresponding local time. It then
     71  *	takes the difference between those two as the correction for
     72  * 	time zones and daylight savings time. This is accurate unless
     73  *	the time the user asked for is near a DST switch. Then a
     74  *	correction is applied - it is assumed that if we can produce
     75  *	a GMT that, when run through localtime(), is equivalent to the
     76  *	user's original input, we have an accurate GMT. The applied
     77  *	correction simply adjusts the GMT by the amount that the derived
     78  *	localtime was off. See?
     79  *	It should be noted that when there is DST there is one local hour
     80  *	a year when time occurs twice (in the fall) and one local hour a
     81  *	year when time never occurs (in the spring).
     82  *	memcpy() is used because the calls to gmtime() and localtime()
     83  *	return pointers to static structures that are overwritten at each
     84  *	call.
     85  * .call	ret = local_to_gm(tme).
     86  * .arg	tme	- ptr to struct tm (see time.h) containing local time.
     87  * .ret	time_t	- seconds since epoch of equivalent GMT.
     88  */
     89 time_t
     90 local_to_gm(struct tm *tme)
     91 {
     92 	time_t secs, gsecs, lsecs, save_gsecs;
     93 	time_t r1secs, r2secs;
     94 	struct tm ltime, gtime;
     95 
     96 	/*
     97 	 * Get the input time in local and gmtime assuming the input
     98 	 * was GMT (which it probably wasn't).
     99 	 */
    100 	r1secs = secs = tm_to_secs(tme);
    101 	(void) memcpy((void *)&gtime, (void *)gmtime(&secs), sizeof (gtime));
    102 	(void) memcpy((void *)&ltime, (void *)localtime(&secs), sizeof (ltime));
    103 
    104 	/*
    105 	 * Get the local and gmtime in seconds, from the above tm structures.
    106 	 * Calculate difference between local and GMT.
    107 	 */
    108 	gsecs = tm_to_secs(&gtime);
    109 	lsecs = tm_to_secs(&ltime);
    110 	secs = lsecs - gsecs;
    111 	gsecs -= secs;
    112 	(void) memcpy((void *)&ltime, (void *)localtime(&gsecs),
    113 	    sizeof (ltime));
    114 
    115 	/*
    116 	 * Now get a computed local time from the computed gmtime.
    117 	 */
    118 	save_gsecs = gsecs;
    119 	r2secs = tm_to_secs(&ltime);
    120 
    121 	/*
    122 	 * If the user given local time is != computed local time then
    123 	 * we need to try a correction.
    124 	 */
    125 	if (r1secs != r2secs) {
    126 		/*
    127 		 * Use the difference between give localtime and computed
    128 		 * localtime as our correction.
    129 		 */
    130 		if (r2secs > r1secs) {
    131 			gsecs -= r2secs - r1secs;
    132 		} else {
    133 			gsecs += r1secs - r2secs;
    134 		}
    135 		/*
    136 		 * And try the comparison again...
    137 		 */
    138 		(void) memcpy((void *)&ltime, (void *)localtime(&gsecs),
    139 		    sizeof (ltime));
    140 		r2secs = tm_to_secs(&ltime);
    141 		/*
    142 		 * If the correction fails then we are on a DST line
    143 		 * and the user-given local time never happened.
    144 		 * Do the best we can.
    145 		 */
    146 		if (r1secs != r2secs) {
    147 			gsecs = save_gsecs;
    148 		}
    149 	}
    150 	return (gsecs);
    151 }
    152 
    153 
    154 /*
    155  * .func	tm_to_secs - convert to seconds.
    156  * .desc	Convert a tm time structure (time.h) into seconds since
    157  *	Jan 1, 1970 00:00:00. The time is assumed to be GMT and
    158  *	so no daylight savings time correction is applied. That
    159  *	is left up to the system calls (localtime(), gmtime()).
    160  * .call	ret = tm_to_secs(tme).
    161  * .arg	tme	- ptr to tm structure.
    162  * .ret	time_t	- number of seconds.
    163  */
    164 time_t
    165 tm_to_secs(struct tm *tme)
    166 {
    167 	int	leap_year = FALSE;
    168 	int	days = 0;
    169 	time_t num_sec = 0;
    170 
    171 	int	sec = tme->tm_sec;
    172 	int	min = tme->tm_min;
    173 	int	hour = tme->tm_hour;
    174 	int	day = tme->tm_mday;
    175 	int	month = tme->tm_mon;
    176 	int	year = tme->tm_year + 1900;
    177 
    178 	if (days_in_year(year) == 366)
    179 		leap_year = TRUE;
    180 
    181 	while (year > 1970) {
    182 		num_sec += days_in_year(--year) * 24 * 60 * 60;
    183 	}
    184 	while (month > 0) {
    185 		days = days_month[--month];
    186 		if (leap_year && month == 1) {	/* 1 is February */
    187 			days++;
    188 		}
    189 		num_sec += days * 24 * 60 * 60;
    190 	}
    191 	num_sec += --day * 24 * 60 * 60;
    192 	num_sec += hour * 60 * 60;
    193 	num_sec += min * 60;
    194 	num_sec += sec;
    195 
    196 	return (num_sec);
    197 }
    198 
    199 
    200 /*
    201  * .func	check_time - check tm structure.
    202  * .desc	Check the time in a tm structure to see if all of the fields
    203  *	are within range.
    204  * .call	err = check_time(tme).
    205  * .arg	tme	- ptr to struct tm (see time.h).
    206  * .ret	0	- time is ok.
    207  * .ret	-1	- time had a problem (description in error_str).
    208  */
    209 int
    210 check_time(struct tm *tme)
    211 {
    212 	error_str = NULL;
    213 
    214 	if (tme->tm_sec < 0 || tme->tm_sec > 59) {
    215 		(void) sprintf(errbuf,
    216 		    gettext("seconds out of range (%d)"), tme->tm_sec + 1);
    217 		error_str = errbuf;
    218 	} else if (tme->tm_min < 0 || tme->tm_min > 59) {
    219 		(void) sprintf(errbuf,
    220 		    gettext("minutes out of range (%d)"), tme->tm_min + 1);
    221 		error_str = errbuf;
    222 	} else if (tme->tm_hour < 0 || tme->tm_hour > 23) {
    223 		(void) sprintf(errbuf,
    224 		    gettext("hours out of range (%d)"), tme->tm_hour + 1);
    225 		error_str = errbuf;
    226 	} else if (tme->tm_mon < 0 || tme->tm_mon > 11) {
    227 		(void) sprintf(errbuf,
    228 		    gettext("months out of range (%d)"), tme->tm_mon + 1);
    229 		error_str = errbuf;
    230 	} else if (tme->tm_year < 0) {
    231 		(void) sprintf(errbuf,
    232 		    gettext("years out of range (%d)"), tme->tm_year);
    233 		error_str = errbuf;
    234 	} else if (tme->tm_mday < 1 || tme->tm_mday > days_month[tme->tm_mon]) {
    235 		if (!(days_in_year(tme->tm_year + 1900) == 366 &&
    236 			tme->tm_mon == 1 &&
    237 			tme->tm_mday == 29)) { /* leap year and February */
    238 			(void) sprintf(errbuf,
    239 			    gettext("days out of range (%d)"), tme->tm_mday);
    240 			error_str = errbuf;
    241 		}
    242 	} else if (tme->tm_wday < 0 || tme->tm_wday > 6) {
    243 		(void) sprintf(errbuf,
    244 		    gettext("weekday out of range (%d)"), tme->tm_wday);
    245 		error_str = errbuf;
    246 	} else if (tme->tm_yday < 0 || tme->tm_yday > 365) {
    247 		(void) sprintf(errbuf,
    248 		    gettext("day of year out of range (%d)"), tme->tm_yday);
    249 		error_str = errbuf;
    250 	}
    251 
    252 	if (error_str == NULL)
    253 		return (0);
    254 	else
    255 		return (-1);
    256 }
    257 
    258 
    259 /*
    260  * .func parse_time.
    261  * .desc Parse a user time from the command line. The user time is assumed
    262  *	to be local time.
    263  *	Supported formats currently are:
    264  *	1. 	+xt	- where x is a number and t is a type.
    265  *		types are - 's' second, 'm' minute, 'h' hour, and 'd' day.
    266  *	2. 	yymmdd - yyyymmdd.
    267  *		yymmddhh - yyyymmddhh.
    268  *		yymmddhhmm - yyyymmddhhmm.
    269  *		yymmddhhmmss - yyyymmddhhmmss.
    270  * .call	err = parse_time(str, opt).
    271  * .arg	str	- ptr to user input string.
    272  * .arg	opt	- time option being processed.
    273  * .ret	0	- succesful.
    274  * .ret	-1	- failure (error message in error_str).
    275  */
    276 int
    277 parse_time(char *str, int opt)
    278 {
    279 	int	ret, len, factor;
    280 	char	*strxx;
    281 	long	lnum;
    282 	struct tm thentime;
    283 
    284 	len = strlen(str);
    285 	/*
    286 	 * If the strlen < 6 then in the "-b +2d" type of format.
    287 	 */
    288 	if (len < 6) {
    289 		if (*str++ != '+') {
    290 			(void) sprintf(errbuf, gettext("%s needs '+' (%s)"),
    291 			    do_invalid(), str);
    292 			error_str = errbuf;
    293 			return (-1);
    294 		}
    295 		if (opt != 'b') {
    296 			(void) sprintf(errbuf,
    297 			    gettext("%s only allowed with 'b' option (%s)"),
    298 			    do_invalid(), str);
    299 			error_str = errbuf;
    300 			return (-1);
    301 		}
    302 		if (m_after == 0) {
    303 			(void) sprintf(errbuf,
    304 			    gettext("must have -a to use -b +nx form (%s)"),
    305 			    str);
    306 			error_str = errbuf;
    307 			return (-1);
    308 		}
    309 		/*
    310 		 * Find out what type of offset it is - 's' 'm' 'h' or 'd'.
    311 		 * Make sure that the offset is all numbers.
    312 		 */
    313 		if ((strxx = strpbrk(str, "dhms")) == NULL) {
    314 			(void) sprintf(errbuf,
    315 			    gettext("%s needs 'd', 'h', 'm', or 's' (%s)"),
    316 			    do_invalid(), str);
    317 			error_str = errbuf;
    318 			return (-1);
    319 		} else {
    320 			ret = *strxx;
    321 			*strxx = '\0';
    322 		}
    323 		if (strlen(str) != strspn(str, "0123456789")) {
    324 			(void) sprintf(errbuf,
    325 			    gettext("%s non-numeric offset (%s)"),
    326 			    do_invalid(), str);
    327 			error_str = errbuf;
    328 			return (-1);
    329 		}
    330 		factor = 1;			/* seconds is default */
    331 		if (ret == 'd')			/* days */
    332 			factor = 24 * 60 * 60;
    333 		else if (ret == 'h')		/* hours */
    334 			factor = 60 * 60;
    335 		else if (ret == 'm')		/* minutes */
    336 			factor = 60;
    337 		lnum = atol(str);
    338 		m_before = m_after + (lnum * factor);
    339 		return (0);
    340 	}
    341 	/*
    342 	 * Must be a specific date/time format.
    343 	 */
    344 	if (derive_date(str, &thentime))
    345 		return (-1);
    346 	/*
    347 	 * For 'd' option clear out the hh:mm:ss to get to the start of the day.
    348 	 * Then add one day's worth of seconds to get the 'b' time.
    349 	 */
    350 	if (opt == 'd') {
    351 		thentime.tm_sec = 0;
    352 		thentime.tm_min = 0;
    353 		thentime.tm_hour = 0;
    354 		m_after = local_to_gm(&thentime);
    355 		m_before = m_after + (24 * 60 * 60);
    356 	} else if (opt == 'a') {
    357 		m_after = local_to_gm(&thentime);
    358 	} else if (opt == 'b') {
    359 		m_before = local_to_gm(&thentime);
    360 	}
    361 	return (0);
    362 }
    363 
    364 
    365 /*
    366  * .func	derive_date.
    367  * .desc	Derive a date/time structure (tm) from a string.
    368  *	String is in one of these formats:
    369  *	[yy]yymmddhhmmss
    370  *	[yy]yymmddhhmm
    371  *	[yy]yymmddhh
    372  *	[yy]yymmdd
    373  * .call	ret = derive_date(str, tme).
    374  * .arg	str	- ptr to input string.
    375  * .arg	tme	- ptr to tm structure (time.h).
    376  * .ret	0	- no errors in string.
    377  * .ret	-1	- errors in string (description in error_str).
    378  */
    379 int
    380 derive_date(char *str, struct tm *tme)
    381 {
    382 	char	*strs;
    383 	char	*digits = "0123456789";
    384 	size_t	len;
    385 	struct tm nowtime;
    386 
    387 	len = strlen(str);
    388 
    389 	if (len != strspn(str, digits)) {
    390 		(void) sprintf(errbuf, gettext("%s not all digits (%s)"),
    391 		    do_invalid(), str);
    392 		error_str = errbuf;
    393 		return (-1);
    394 	}
    395 	if (len % 2) {
    396 		(void) sprintf(errbuf, gettext("%s odd number of digits (%s)"),
    397 		    do_invalid(), str);
    398 		error_str = errbuf;
    399 		return (-1);
    400 	}
    401 	/*
    402 	 * May need larger string storage to add '19' or '20'.
    403 	 */
    404 	strs = (char *)a_calloc(1, len + 4);
    405 
    406 	/*
    407 	 * Get current time to see what century it is.
    408 	 */
    409 	(void) memcpy((char *)&nowtime, (char *)gmtime(&time_now),
    410 	    sizeof (nowtime));
    411 	/*
    412 	 * If the year does not begin with '19' or '20', then report
    413 	 * an error and abort.
    414 	 */
    415 	if ((str[0] != '1' || str[1] != '9') &&		/* 19XX */
    416 	    (str[0] != '2' || str[1] != '0')) {		/* 20XX */
    417 		(void) sprintf(errbuf, gettext("invalid year (%c%c%c%c)"),
    418 		    str[0], str[1], str[2], str[3]);
    419 		error_str = errbuf;
    420 		free(strs);
    421 		return (-1);
    422 	}
    423 
    424 	len = strlen(str);			/* may have changed */
    425 	if (len < 8 || len > 14) {
    426 		(void) sprintf(errbuf,
    427 			gettext("invalid date/time length (%s)"), str);
    428 		error_str = errbuf;
    429 		free(strs);
    430 		return (-1);
    431 	}
    432 	/* unspecified values go to 0 */
    433 	(void) memset((void *) tme, 0, (size_t)sizeof (*tme));
    434 	(void) strncpy(strs, str, 4);
    435 	strs[4] = '\0';
    436 	tme->tm_year = atoi(strs) - 1900;	/* get the year */
    437 	(void) strncpy(strs, str + 4, 2);
    438 	strs[2] = '\0';
    439 	tme->tm_mon = atoi(strs) - 1;		/* get months */
    440 	(void) strncpy(strs, str + 6, 2);
    441 	strs[2] = '\0';
    442 	tme->tm_mday = atoi(strs);		/* get days */
    443 	if (len >= 10) {			/* yyyymmddhh */
    444 		(void) strncpy(strs, str + 8, 2);
    445 		strs[2] = '\0';
    446 		tme->tm_hour = atoi(strs);	/* get hours */
    447 	}
    448 	if (len >= 12) {			/* yyyymmddhhmm */
    449 		(void) strncpy(strs, str + 10, 2);
    450 		strs[2] = '\0';
    451 		tme->tm_min = atoi(strs);	/* get minutes */
    452 	}
    453 	if (len >= 14) {			/* yyyymmddhhmmss */
    454 		(void) strncpy(strs, str + 12, 2);
    455 		strs[2] = '\0';
    456 		tme->tm_sec = atoi(strs);	/* get seconds */
    457 	}
    458 	free(strs);
    459 	return (check_time(tme));		/* lastly check the ranges */
    460 }
    461 
    462 
    463 /*
    464  * .func	derive_str - derive string.
    465  * .desc	Derive a string representation of a time for a filename.
    466  *	The output is in the 14 character format yyyymmddhhmmss.
    467  * .call	derive_str(clock, buf).
    468  * .arg	clock	- seconds since epoch.
    469  * .arg	buf	- place to put resultant string.
    470  * .ret	void.
    471  */
    472 void
    473 derive_str(time_t clock, char *buf)
    474 {
    475 	struct tm gtime;
    476 
    477 	(void) memcpy((void *) & gtime, (void *)gmtime(&clock), sizeof (gtime));
    478 
    479 	(void) sprintf(buf, "%4d", gtime.tm_year + 1900);
    480 	(void) sprintf(buf + 4,  "%.2d", gtime.tm_mon + 1);
    481 	(void) sprintf(buf + 6,  "%.2d", gtime.tm_mday);
    482 	(void) sprintf(buf + 8,  "%.2d", gtime.tm_hour);
    483 	(void) sprintf(buf + 10, "%.2d", gtime.tm_min);
    484 	(void) sprintf(buf + 12, "%.2d", gtime.tm_sec);
    485 	buf[14] = '\0';
    486 }
    487 
    488 
    489 int
    490 days_in_year(int year)
    491 {
    492 	if (isleap(year))
    493 		return (366);
    494 
    495 	return (365);
    496 }
    497