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 * 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 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 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 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 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 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 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 488 days_in_year(int year) 489 { 490 if (isleap(year)) 491 return (366); 492 493 return (365); 494 } 495