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