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 *)>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 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