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 *
do_invalid(void)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
local_to_gm(struct tm * tme)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 *)>ime, (void *)gmtime(&secs), sizeof (gtime));
102 (void) memcpy((void *)<ime, (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(>ime);
109 lsecs = tm_to_secs(<ime);
110 secs = lsecs - gsecs;
111 gsecs -= secs;
112 (void) memcpy((void *)<ime, (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(<ime);
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 *)<ime, (void *)localtime(&gsecs),
139 sizeof (ltime));
140 r2secs = tm_to_secs(<ime);
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
tm_to_secs(struct tm * tme)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
check_time(struct tm * tme)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
parse_time(char * str,int opt)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
derive_date(char * str,struct tm * tme)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
derive_str(time_t clock,char * buf)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
days_in_year(int year)490 days_in_year(int year)
491 {
492 if (isleap(year))
493 return (366);
494
495 return (365);
496 }
497