1/* 2 * Copyright (c) 2007-2013 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24#include <ctype.h> 25#include <stdio.h> 26#include <stdbool.h> 27#include <asl.h> 28#include <asl_string.h> 29#include <asl_core.h> 30#include <asl_private.h> 31#include <string.h> 32#include <membership.h> 33#include <pthread.h> 34#include <libkern/OSAtomic.h> 35#include <servers/bootstrap.h> 36#include <bootstrap_priv.h> 37#include <mach/kern_return.h> 38#include <mach/mach_init.h> 39#include <mach/mach_vm.h> 40#include <mach/vm_map.h> 41#include <mach/vm_param.h> 42#include <dispatch/dispatch.h> 43 44const char *ASL_LEVEL_TO_STRING[] = 45{ 46 ASL_STRING_EMERG, 47 ASL_STRING_ALERT, 48 ASL_STRING_CRIT, 49 ASL_STRING_ERR, 50 ASL_STRING_WARNING, 51 ASL_STRING_NOTICE, 52 ASL_STRING_INFO, 53 ASL_STRING_DEBUG 54}; 55 56static char *asl_filesystem_path_database = NULL; 57static char *asl_filesystem_path_archive = NULL; 58 59/* 60 * Message ID generation 61 */ 62static uint64_t _asl_core_msg_next_id = 1; 63static pthread_mutex_t core_lock = PTHREAD_MUTEX_INITIALIZER; 64 65#define mix(a, b, c) \ 66{ \ 67 a -= b; a -= c; a ^= (c>>13); \ 68 b -= c; b -= a; b ^= (a<< 8); \ 69 c -= a; c -= b; c ^= (b>>13); \ 70 a -= b; a -= c; a ^= (c>>12); \ 71 b -= c; b -= a; b ^= (a<<16); \ 72 c -= a; c -= b; c ^= (b>> 5); \ 73 a -= b; a -= c; a ^= (c>> 3); \ 74 b -= c; b -= a; b ^= (a<<10); \ 75 c -= a; c -= b; c ^= (b>>15); \ 76} 77 78/* 79 * Get ASL server mach port. 80 * reset != 0 flushes cached port. 81 * reset < 0 returns MACH_PORT_NULL 82 */ 83mach_port_t 84asl_core_get_service_port(int reset) 85{ 86 static mach_port_t server_port = MACH_PORT_NULL; 87 mach_port_t tmp; 88 kern_return_t kstatus; 89 90 if ((reset != 0) && (server_port != MACH_PORT_NULL)) 91 { 92 mach_port_t tmp = server_port; 93 server_port = MACH_PORT_NULL; 94 mach_port_deallocate(mach_task_self(), tmp); 95 } 96 97 if (reset < 0) return MACH_PORT_NULL; 98 99 if (server_port != MACH_PORT_NULL) return server_port; 100 101 tmp = MACH_PORT_NULL; 102 char *str = getenv("ASL_DISABLE"); 103 if ((str != NULL) && (!strcmp(str, "1"))) return MACH_PORT_NULL; 104 105 kstatus = bootstrap_look_up2(bootstrap_port, ASL_SERVICE_NAME, &tmp, 0, BOOTSTRAP_PRIVILEGED_SERVER); 106 if ((kstatus != KERN_SUCCESS) || (tmp == MACH_PORT_NULL)) return MACH_PORT_NULL; 107 108 if (!OSAtomicCompareAndSwap32Barrier(MACH_PORT_NULL, tmp, (int32_t *)&server_port)) 109 { 110 mach_port_deallocate(mach_task_self(), tmp); 111 } 112 113 return server_port; 114} 115 116/* 117 * Hash is used to improve string search. 118 */ 119uint32_t 120asl_core_string_hash(const char *s, uint32_t inlen) 121{ 122 uint32_t a, b, c, l, len; 123 124 if (s == NULL) return 0; 125 126 l = inlen; 127 if (l == 0) 128 { 129 if (s[0] == '\0') return 0; 130 l = strlen(s); 131 } 132 133 len = l; 134 a = b = 0x9e3779b9; 135 c = 0; 136 137 while (len >= 12) 138 { 139 a += (s[0] + ((uint32_t)s[1]<<8) + ((uint32_t)s[ 2]<<16) + ((uint32_t)s[ 3]<<24)); 140 b += (s[4] + ((uint32_t)s[5]<<8) + ((uint32_t)s[ 6]<<16) + ((uint32_t)s[ 7]<<24)); 141 c += (s[8] + ((uint32_t)s[9]<<8) + ((uint32_t)s[10]<<16) + ((uint32_t)s[11]<<24)); 142 143 mix(a, b, c); 144 145 s += 12; 146 len -= 12; 147 } 148 149 c += l; 150 switch(len) 151 { 152 case 11: c += ((uint32_t)s[10]<<24); 153 case 10: c += ((uint32_t)s[9]<<16); 154 case 9 : c += ((uint32_t)s[8]<<8); 155 156 case 8 : b += ((uint32_t)s[7]<<24); 157 case 7 : b += ((uint32_t)s[6]<<16); 158 case 6 : b += ((uint32_t)s[5]<<8); 159 case 5 : b += s[4]; 160 161 case 4 : a += ((uint32_t)s[3]<<24); 162 case 3 : a += ((uint32_t)s[2]<<16); 163 case 2 : a += ((uint32_t)s[1]<<8); 164 case 1 : a += s[0]; 165 } 166 167 mix(a, b, c); 168 169 if (c == 0) c = 1; 170 return c; 171} 172 173const char * 174asl_core_error(uint32_t code) 175{ 176 switch (code) 177 { 178 case ASL_STATUS_OK: return "Operation Succeeded"; 179 case ASL_STATUS_INVALID_ARG: return "Invalid Argument"; 180 case ASL_STATUS_INVALID_STORE: return "Invalid Data Store"; 181 case ASL_STATUS_INVALID_STRING: return "Invalid String"; 182 case ASL_STATUS_INVALID_ID: return "Invalid ID Number"; 183 case ASL_STATUS_INVALID_MESSAGE: return "Invalid Message"; 184 case ASL_STATUS_NOT_FOUND: return "Not Found"; 185 case ASL_STATUS_READ_FAILED: return "Read Operation Failed"; 186 case ASL_STATUS_WRITE_FAILED: return "Write Operation Failed"; 187 case ASL_STATUS_NO_MEMORY: return "System Memory Allocation Failed"; 188 case ASL_STATUS_ACCESS_DENIED: return "Access Denied"; 189 case ASL_STATUS_READ_ONLY: return "Read Only Access"; 190 case ASL_STATUS_WRITE_ONLY: return "Write Only Access"; 191 case ASL_STATUS_MATCH_FAILED: return "Match Failed"; 192 case ASL_STATUS_NO_RECORDS: return "No More Records"; 193 } 194 195 return "Operation Failed"; 196} 197 198const char * 199asl_core_level_to_string(uint32_t level) 200{ 201 if (level > ASL_LEVEL_DEBUG) return "invalid"; 202 return ASL_LEVEL_TO_STRING[level]; 203} 204 205static ASL_STATUS 206asl_core_check_user_access(int32_t msgu, int32_t readu) 207{ 208 /* -1 means anyone may read */ 209 if (msgu == -1) return ASL_STATUS_OK; 210 211 /* Check for exact match */ 212 if (msgu == readu) return ASL_STATUS_OK; 213 214 return ASL_STATUS_ACCESS_DENIED; 215} 216 217static ASL_STATUS 218asl_core_check_group_access(int32_t msgg, int32_t readu, int32_t readg) 219{ 220 int check; 221 uuid_t uu, gu; 222 223 /* -1 means anyone may read */ 224 if (msgg == -1) return ASL_STATUS_OK; 225 226 /* Check for exact match */ 227 if (msgg == readg) return ASL_STATUS_OK; 228 229 /* Check if user (u) is in read group (msgg) */ 230 mbr_uid_to_uuid(readu, uu); 231 mbr_gid_to_uuid(msgg, gu); 232 233 check = 0; 234 mbr_check_membership(uu, gu, &check); 235 if (check != 0) return ASL_STATUS_OK; 236 237 return ASL_STATUS_ACCESS_DENIED; 238} 239 240ASL_STATUS 241asl_core_check_access(int32_t msgu, int32_t msgg, int32_t readu, int32_t readg, uint16_t flags) 242{ 243 uint16_t uset, gset; 244 245 /* root (uid 0) may always read */ 246 if (readu == 0) return ASL_STATUS_OK; 247 248 uset = flags & ASL_MSG_FLAG_READ_UID_SET; 249 gset = flags & ASL_MSG_FLAG_READ_GID_SET; 250 251 /* if no access controls are set, anyone may read */ 252 if ((uset | gset) == 0) return ASL_STATUS_OK; 253 254 /* if only uid is set, then access is only by uid match */ 255 if ((uset != 0) && (gset == 0)) return asl_core_check_user_access(msgu, readu); 256 257 /* if only gid is set, then access is only by gid match */ 258 if ((uset == 0) && (gset != 0)) return asl_core_check_group_access(msgg, readu, readg); 259 260 /* both uid and gid are set - check user, then group */ 261 if ((asl_core_check_user_access(msgu, readu)) == ASL_STATUS_OK) return ASL_STATUS_OK; 262 return asl_core_check_group_access(msgg, readu, readg); 263} 264 265uint64_t 266asl_core_htonq(uint64_t n) 267{ 268#ifdef __BIG_ENDIAN__ 269 return n; 270#else 271 u_int32_t t; 272 union 273 { 274 u_int64_t q; 275 u_int32_t l[2]; 276 } x; 277 278 x.q = n; 279 t = x.l[0]; 280 x.l[0] = htonl(x.l[1]); 281 x.l[1] = htonl(t); 282 283 return x.q; 284#endif 285} 286 287uint64_t 288asl_core_ntohq(uint64_t n) 289{ 290#ifdef __BIG_ENDIAN__ 291 return n; 292#else 293 u_int32_t t; 294 union 295 { 296 u_int64_t q; 297 u_int32_t l[2]; 298 } x; 299 300 x.q = n; 301 t = x.l[0]; 302 x.l[0] = ntohl(x.l[1]); 303 x.l[1] = ntohl(t); 304 305 return x.q; 306#endif 307} 308 309uint64_t 310asl_core_new_msg_id(uint64_t start) 311{ 312 uint64_t out; 313 314 pthread_mutex_lock(&core_lock); 315 316 if (start != 0) _asl_core_msg_next_id = start; 317 318 out = _asl_core_msg_next_id; 319 _asl_core_msg_next_id++; 320 321 pthread_mutex_unlock(&core_lock); 322 323 return out; 324} 325 326const char * 327asl_filesystem_path(uint32_t place) 328{ 329 static dispatch_once_t once; 330 331 dispatch_once(&once, ^{ 332 char *asl_var_log = NULL; 333 const char *const_asl_var_log = "/var/log"; 334 335#if TARGET_IPHONE_SIMULATOR 336 asl_var_log = getenv("SIMULATOR_LOG_ROOT"); 337#endif 338 339 if (asl_var_log != NULL) const_asl_var_log = (const char *)asl_var_log; 340 341 asprintf(&asl_filesystem_path_database, "%s/asl", const_asl_var_log); 342 asprintf(&asl_filesystem_path_archive, "%s/asl.archive", const_asl_var_log); 343 }); 344 345 switch (place) 346 { 347 case ASL_PLACE_DATABASE: 348 { 349 if (asl_filesystem_path_database == NULL) return ASL_PLACE_DATABASE_DEFAULT; 350 return asl_filesystem_path_database; 351 } 352 case ASL_PLACE_ARCHIVE: 353 { 354 if (asl_filesystem_path_archive == NULL) return ASL_PLACE_ARCHIVE_DEFAULT; 355 return asl_filesystem_path_archive; 356 } 357 default: 358 { 359 return NULL; 360 } 361 } 362 363 return NULL; 364} 365 366#pragma mark - 367#pragma mark data buffer encoding 368 369/* 370 * asl_core_encode_buffer 371 * encode arbitrary data as a C string without embedded zero (nul) characters 372 * 373 * The routine computes a histogram of the input buffer and finds 374 * the two least frequently used non-nul chars (L[0] and L[1]). 375 * 376 * L[0] is used to stand in for nul. 377 * L[1] is used as the escape character. 378 * Occurrences of nul in the data are encoded as L[0] 379 * Occurrences of L[0] in the data are encoded as the sequence L[1] 1. 380 * Occurrences of L[1] in the data are encoded as the sequence L[1] 2. 381 * 382 * The output string is preceded by L[0] L[1], and is nul terminated. 383 * The output length is 2 + n + N(L[0]) + N(L[1]) + 1 384 * where N(x) is the number of occurrences of x in the input string. 385 * The worst case occurs when all characters are equally frequent, 386 * In that case the output size will less that 1% larger than the input. 387 */ 388char * 389asl_core_encode_buffer(const char *in, uint32_t len) 390{ 391 char *str; 392 uint32_t i, j, k, outlen, breakit, min, hist[256]; 393 uint32_t lfu[2], save[2]; 394 uint8_t v; 395 396 if (in == NULL) return NULL; 397 if (len == 0) return NULL; 398 399 memset(hist, 0, sizeof(hist)); 400 save[0] = 0; 401 save[1] = 0; 402 403 for (i = 0; i < len; i++) 404 { 405 v = in[i]; 406 hist[v]++; 407 } 408 409 for (j = 0; j < 2; j++) 410 { 411 lfu[j] = 1; 412 min = hist[1]; 413 414 for (i = 2; i < 256; i++) 415 { 416 if (hist[i] < min) 417 { 418 lfu[j] = i; 419 min = hist[i]; 420 421 /* 422 * Stop if there are no occurances or character i in the input. 423 * The minimum will never be less than zero. 424 */ 425 if (min == 0) break; 426 427 /* 428 * When looking for the second least frequently used character, 429 * stop scanning if we hit the same minimum as we saw in the first 430 * pass. There will be no smaller values. 431 */ 432 if ((j == 1) && (min == save[0])) break; 433 } 434 } 435 436 save[j] = hist[lfu[j]]; 437 hist[lfu[j]] = (uint32_t)-1; 438 } 439 440 outlen = 2 + len + save[0] + save[1] + 1; 441 442 str = malloc(outlen); 443 if (str == NULL) return NULL; 444 445 str[outlen - 1] = '\0'; 446 447 str[0] = lfu[0]; 448 str[1] = lfu[1]; 449 450 j = 2; 451 452 for (i = 0; i < len; i++) 453 { 454 v = in[i]; 455 if (v == 0) 456 { 457 str[j++] = lfu[0]; 458 continue; 459 } 460 461 breakit = 0; 462 for (k = 0; (k < 2) && (breakit == 0); k++) 463 { 464 if (v == lfu[k]) 465 { 466 str[j++] = lfu[1]; 467 str[j++] = k + 1; 468 breakit = 1; 469 } 470 } 471 472 if (breakit == 1) continue; 473 474 str[j++] = v; 475 } 476 477 return str; 478} 479 480/* 481 * asl_core_decode_buffer 482 * decode a string produced by asl_encode_buffer to recreate the original data 483 */ 484int32_t 485asl_core_decode_buffer(const char *in, char **buf, uint32_t *len) 486{ 487 uint8_t v; 488 uint32_t i, j, n, outlen; 489 uint8_t lfu[2]; 490 char *out; 491 492 if (buf == NULL) return -1; 493 if (len == NULL) return -1; 494 495 lfu[0] = in[0]; 496 lfu[1] = in[1]; 497 498 outlen = 0; 499 500 /* strip trailing nul */ 501 n = strlen(in); 502 503 /* determine output length and check for invalid input */ 504 for (i = 2; i < n; i++) 505 { 506 v = in[i]; 507 if (v == lfu[1]) 508 { 509 i++; 510 if (i == n) return -1; 511 512 v = in[i]; 513 if ((v < 1) || (v > 2)) return -1; 514 515 outlen++; 516 } 517 else outlen++; 518 } 519 520 if (outlen == 0) return -1; 521 522 out = malloc(outlen); 523 if (out == NULL) return -1; 524 525 j = 0; 526 for (i = 2; i < n; i++) 527 { 528 v = in[i]; 529 if (v == lfu[0]) 530 { 531 out[j++] = 0; 532 } 533 else if (v == lfu[1]) 534 { 535 i++; 536 v = in[i]; 537 out[j++] = lfu[v - 1]; 538 } 539 else out[j++] = v; 540 } 541 542 *len = outlen; 543 *buf = out; 544 return 0; 545} 546 547#pragma mark - 548#pragma mark time parsing 549 550/* 551 * utility for converting a time string into a time_t 552 * we only deal with the following formats: 553 * dotted form YYYY.MM.DD hh:mm:ss UTC 554 * ctime() form Mth dd hh:mm:ss (e.g. Aug 25 09:54:37) 555 * absolute form - # seconds since the epoch (e.g. 1095789191) 556 * relative time - seconds before or after now (e.g. -300, +43200) 557 * relative time - days/hours/minutes/seconds before or after now (e.g. -1d, +6h, +30m, -10s) 558 * ISO8601 - YYYY[-]MM[-]DDTHH[:]MM[:]SS optionally followed by Z or +/-HH[[:]MM] 559 */ 560 561/* 562 * light(er)-weight replcaement (in place of regex) for asl_core_parse_time 563 */ 564 565#define MFLAG_INCLUDE 0x00000001 566#define MFLAG_EXCLUDE 0x00000002 567 568#define DIGITS "0123456789" 569#define WHITESPACE " " 570 571#define SECONDS_PER_MINUTE 60 572#define SECONDS_PER_HOUR 3600 573#define SECONDS_PER_DAY 86400 574#define SECONDS_PER_WEEK 604800 575 576/* 577 * Match between mincount and maxcount characters in or not in mset. 578 * maxcount == 0 means no limit. 579 * 580 */ 581bool 582asl_core_str_match(const char *target, const char *mset, uint32_t mincount, uint32_t maxcount, uint32_t flags, uint32_t *length) 583{ 584 const char *x; 585 uint32_t n; 586 587 if (length == NULL) length = &n; 588 589 if (target == NULL) return (mincount == 0); 590 591 for (x = target, *length = 0; *x != '\0'; x++, *length = *length + 1) 592 { 593 char *s; 594 595 if ((*length == maxcount) && (maxcount > 0)) return true; 596 if (mset == NULL) continue; 597 598 s = strchr(mset, *x); 599 if ((s == NULL) && (flags & MFLAG_EXCLUDE)) continue; 600 if ((s != NULL) && (flags & MFLAG_INCLUDE)) continue; 601 602 break; 603 } 604 605 return (*length >= mincount); 606} 607 608bool 609asl_core_str_match_char(const char *target, const char c, uint32_t mincount, uint32_t flags, uint32_t *length) 610{ 611 uint32_t n; 612 613 if (length == NULL) length = &n; 614 *length = 0; 615 616 if (target == NULL) return (mincount == 0); 617 618 if ((*target == c) && (flags & MFLAG_INCLUDE)) *length = 1; 619 if ((*target != c) && (flags & MFLAG_EXCLUDE)) *length = 1; 620 621 return (*length >= mincount); 622} 623 624uint32_t 625asl_core_str_to_uint32(const char *target, uint32_t length) 626{ 627 uint32_t i, d, out = 0; 628 629 for (i = 0; i < length; i++) 630 { 631 d = target[i] - '0'; 632 out = (out * 10) + d; 633 } 634 635 return out; 636} 637 638static bool 639asl_core_str_match_absolute_or_relative_time(const char *target, time_t *tval, uint32_t *tlen) 640{ 641 uint32_t len; 642 int32_t val, sign = 1; 643 bool test; 644 const char *p; 645 time_t start = 0; 646 647 if (target == NULL) return false; 648 649 /* [+-] */ 650 p = target; 651 test = asl_core_str_match(p, "+-", 0, 1, MFLAG_INCLUDE, &len); 652 if (!test) return false; 653 654 if (len == 1) 655 { 656 /* relative time */ 657 start = time(NULL); 658 if (*p == '-') sign = -1; 659 } 660 661 /* [0-9]+ */ 662 p += len; 663 test = asl_core_str_match(p, DIGITS, 1, 0, MFLAG_INCLUDE, &len); 664 if (!test) return false; 665 val = asl_core_str_to_uint32(p, len); 666 667 /* [shmdw] */ 668 p += len; 669 test = asl_core_str_match(p, "SsMmHhDdWw", 0, 1, MFLAG_INCLUDE, &len); 670 if (!test) return false; 671 672 if ((*p == 'M') || (*p == 'm')) val *= SECONDS_PER_MINUTE; 673 else if ((*p == 'H') || (*p == 'h')) val *= SECONDS_PER_HOUR; 674 else if ((*p == 'D') || (*p == 'd')) val *= SECONDS_PER_DAY; 675 else if ((*p == 'W') || (*p == 'w')) val *= SECONDS_PER_WEEK; 676 677 /* matched string must be followed by space, tab, newline (not counted in length) */ 678 p += len; 679 if (*p != '\0') 680 { 681 test = asl_core_str_match(p, " \t\n", 1, 1, MFLAG_INCLUDE, &len); 682 if (!test) return false; 683 } 684 685 if (tlen != NULL) *tlen = p - target; 686 if (tval != NULL) *tval = start + (sign * val); 687 688 return true; 689} 690 691static int 692_month_num(const char *s) 693{ 694 if (!strncasecmp(s, "jan", 3)) return 0; 695 if (!strncasecmp(s, "feb", 3)) return 1; 696 if (!strncasecmp(s, "mar", 3)) return 2; 697 if (!strncasecmp(s, "apr", 3)) return 3; 698 if (!strncasecmp(s, "may", 3)) return 4; 699 if (!strncasecmp(s, "jun", 3)) return 5; 700 if (!strncasecmp(s, "jul", 3)) return 6; 701 if (!strncasecmp(s, "aug", 3)) return 7; 702 if (!strncasecmp(s, "sep", 3)) return 8; 703 if (!strncasecmp(s, "oct", 3)) return 9; 704 if (!strncasecmp(s, "nov", 3)) return 10; 705 if (!strncasecmp(s, "dec", 3)) return 11; 706 return -1; 707 708} 709 710/* 711 * Match ctime() format - Mth [D]D [h]h:mm:ss 712 */ 713bool 714asl_core_str_match_c_time(const char *target, time_t *tval, uint32_t *tlen) 715{ 716 uint32_t len, y; 717 bool test; 718 const char *p; 719 struct tm t; 720 time_t now; 721 722 if (target == NULL) return false; 723 memset(&t, 0, sizeof(t)); 724 725 /* determine current date */ 726 now = time(NULL); 727 localtime_r(&now, &t); 728 y = t.tm_year; 729 memset(&t, 0, sizeof(t)); 730 t.tm_year = y; 731 732 /* Mth */ 733 p = target; 734 t.tm_mon = _month_num(p); 735 len = 3; 736 if (t.tm_mon == -1) return false; 737 738 /* whitespace */ 739 p += len; 740 test = asl_core_str_match(p, WHITESPACE, 1, 0, MFLAG_INCLUDE, &len); 741 if (!test) return false; 742 743 /* [D]D */ 744 p += len; 745 test = asl_core_str_match(p, DIGITS, 1, 2, MFLAG_INCLUDE, &len); 746 if (!test) return false; 747 t.tm_mday = asl_core_str_to_uint32(p, len); 748 if (t.tm_mday > 31) return false; 749 750 /* whitespace */ 751 p += len; 752 test = asl_core_str_match(p, WHITESPACE, 1, 0, MFLAG_INCLUDE, &len); 753 if (!test) return false; 754 755 /* [h]h */ 756 p += len; 757 test = asl_core_str_match(p, DIGITS, 1, 2, MFLAG_INCLUDE, &len); 758 if (!test) return false; 759 t.tm_hour = asl_core_str_to_uint32(p, len); 760 if (t.tm_hour > 23) return false; 761 762 /* : */ 763 p += len; 764 if (*p != ':') return false; 765 len = 1; 766 767 /* mm */ 768 p += len; 769 test = asl_core_str_match(p, DIGITS, 2, 2, MFLAG_INCLUDE, &len); 770 if (!test) return false; 771 t.tm_min = asl_core_str_to_uint32(p, len); 772 if (t.tm_min > 59) return false; 773 774 /* : */ 775 p += len; 776 if (*p != ':') return false; 777 len = 1; 778 779 /* ss */ 780 p += len; 781 test = asl_core_str_match(p, DIGITS, 2, 2, MFLAG_INCLUDE, &len); 782 if (!test) return false; 783 t.tm_sec = asl_core_str_to_uint32(p, len); 784 if (t.tm_sec > 59) return false; 785 786 /* matched string must be followed by space, tab, newline (not counted in length) */ 787 p += len; 788 if (*p != '\0') 789 { 790 test = asl_core_str_match(p, " \t\n", 1, 1, MFLAG_INCLUDE, &len); 791 if (!test) return false; 792 } 793 794 t.tm_isdst = -1; 795 796 if (tlen != NULL) *tlen = p - target; 797 if (tval != NULL) *tval = mktime(&t); 798 799 return true; 800} 801 802/* 803 * Match YYYY.[M]M.[D]D [h]h:mm:ss UTC 804 */ 805static bool 806asl_core_str_match_dotted_time(const char *target, time_t *tval, uint32_t *tlen) 807{ 808 uint32_t len; 809 bool test; 810 const char *p; 811 struct tm t; 812 813 if (target == NULL) return false; 814 memset(&t, 0, sizeof(t)); 815 816 /* YYYY */ 817 p = target; 818 test = asl_core_str_match(p, DIGITS, 4, 4, MFLAG_INCLUDE, &len); 819 if (!test) return false; 820 t.tm_year = asl_core_str_to_uint32(p, len) - 1900; 821 822 /* . */ 823 p += len; 824 if (*p != '.') return false; 825 len = 1; 826 827 /* [M]M */ 828 p += len; 829 test = asl_core_str_match(p, DIGITS, 1, 2, MFLAG_INCLUDE, &len); 830 if (!test) return false; 831 t.tm_mon = asl_core_str_to_uint32(p, len); 832 if (t.tm_mon < 1) return false; 833 if (t.tm_mon > 12) return false; 834 t.tm_mon -= 1; 835 836 /* . */ 837 p += len; 838 if (*p != '.') return false; 839 len = 1; 840 841 /* [D]D */ 842 p += len; 843 test = asl_core_str_match(p, DIGITS, 1, 2, MFLAG_INCLUDE, &len); 844 if (!test) return false; 845 t.tm_mday = asl_core_str_to_uint32(p, len); 846 if (t.tm_mday > 31) return false; 847 848 /* whitespace */ 849 p += len; 850 test = asl_core_str_match(p, WHITESPACE, 1, 0, MFLAG_INCLUDE, &len); 851 if (!test) return false; 852 853 /* [h]h */ 854 p += len; 855 test = asl_core_str_match(p, DIGITS, 1, 2, MFLAG_INCLUDE, &len); 856 if (!test) return false; 857 t.tm_hour = asl_core_str_to_uint32(p, len); 858 if (t.tm_hour > 23) return false; 859 860 /* : */ 861 p += len; 862 if (*p != ':') return false; 863 len = 1; 864 865 /* mm */ 866 p += len; 867 test = asl_core_str_match(p, DIGITS, 2, 2, MFLAG_INCLUDE, &len); 868 if (!test) return false; 869 t.tm_min = asl_core_str_to_uint32(p, len); 870 if (t.tm_min > 59) return false; 871 872 /* : */ 873 p += len; 874 if (*p != ':') return false; 875 len = 1; 876 877 /* ss */ 878 p += len; 879 test = asl_core_str_match(p, DIGITS, 2, 2, MFLAG_INCLUDE, &len); 880 if (!test) return false; 881 t.tm_sec = asl_core_str_to_uint32(p, len); 882 if (t.tm_sec > 59) return false; 883 884 /* whitespace */ 885 p += len; 886 test = asl_core_str_match(p, WHITESPACE, 1, 0, MFLAG_INCLUDE, &len); 887 if (!test) return false; 888 889 /* UTC */ 890 p += len; 891 if (strncmp(p, "UTC", 3)) return false; 892 len = 3; 893 894 /* matched string must be followed by space, tab, newline (not counted in length) */ 895 p += len; 896 if (*p != '\0') 897 { 898 test = asl_core_str_match(p, " \t\n", 1, 1, MFLAG_INCLUDE, &len); 899 if (!test) return false; 900 } 901 902 if (tlen != NULL) *tlen = p - target; 903 if (tval != NULL) *tval = timegm(&t); 904 905 return true; 906} 907 908/* 909 * Match YYYY[-]MM[-]DD[Tt]hh[:]hh[:]ss[Zz] or YYYY[-]MM[-]DD[Tt]hh[:]hh[:]ss[+-][H]H[[:]MM] 910 */ 911static bool 912asl_core_str_match_iso_8601_time(const char *target, time_t *tval, uint32_t *tlen) 913{ 914 uint32_t len; 915 bool test; 916 const char *p; 917 struct tm t; 918 int32_t tzh, tzs, sign = -1; 919 920 if (target == NULL) return false; 921 memset(&t, 0, sizeof(t)); 922 923 /* YYYY */ 924 p = target; 925 test = asl_core_str_match(p, DIGITS, 4, 4, MFLAG_INCLUDE, &len); 926 if (!test) return false; 927 t.tm_year = asl_core_str_to_uint32(p, len) - 1900; 928 929 /* [-] */ 930 p += len; 931 test = asl_core_str_match_char(p, '-', 0, MFLAG_INCLUDE, &len); 932 if (!test) return false; 933 934 /* MM */ 935 p += len; 936 test = asl_core_str_match(p, DIGITS, 2, 2, MFLAG_INCLUDE, &len); 937 if (!test) return false; 938 t.tm_mon = asl_core_str_to_uint32(p, len); 939 if (t.tm_mon < 1) return false; 940 if (t.tm_mon > 12) return false; 941 t.tm_mon -= 1; 942 943 /* [-] */ 944 p += len; 945 test = asl_core_str_match_char(p, '-', 0, MFLAG_INCLUDE, &len); 946 if (!test) return false; 947 948 /* DD */ 949 p += len; 950 test = asl_core_str_match(p, DIGITS, 2, 2, MFLAG_INCLUDE, &len); 951 if (!test) return false; 952 t.tm_mday = asl_core_str_to_uint32(p, len); 953 if (t.tm_mday > 31) return false; 954 955 /* T or t */ 956 p += len; 957 test = asl_core_str_match(p, "Tt", 1, 1, MFLAG_INCLUDE, &len); 958 if (!test) return false; 959 960 /* hh */ 961 p += len; 962 test = asl_core_str_match(p, DIGITS, 2, 2, MFLAG_INCLUDE, &len); 963 if (!test) return false; 964 t.tm_hour = asl_core_str_to_uint32(p, len); 965 if (t.tm_hour > 23) return false; 966 967 /* [:] */ 968 p += len; 969 test = asl_core_str_match_char(p, ':', 0, MFLAG_INCLUDE, &len); 970 if (!test) return false; 971 972 /* mm */ 973 p += len; 974 test = asl_core_str_match(p, DIGITS, 2, 2, MFLAG_INCLUDE, &len); 975 if (!test) return false; 976 t.tm_min = asl_core_str_to_uint32(p, len); 977 if (t.tm_min > 59) return false; 978 979 /* [:] */ 980 p += len; 981 test = asl_core_str_match_char(p, ':', 0, MFLAG_INCLUDE, &len); 982 if (!test) return false; 983 984 /* ss */ 985 p += len; 986 test = asl_core_str_match(p, DIGITS, 2, 2, MFLAG_INCLUDE, &len); 987 if (!test) return false; 988 t.tm_sec = asl_core_str_to_uint32(p, len); 989 if (t.tm_sec > 59) return false; 990 991 p += len; 992 993 /* default to local time if we hit the end of the string */ 994 if ((*p == '\0') || (*p == ' ') || (*p == '\t') || (*p == '\n')) 995 { 996 t.tm_isdst = -1; 997 998 if (tlen != NULL) *tlen = p - target; 999 if (tval != NULL) *tval = mktime(&t); 1000 1001 return true; 1002 } 1003 1004 /* Z, z, +, or - */ 1005 test = asl_core_str_match(p, "Zz+-", 1, 1, MFLAG_INCLUDE, &len); 1006 if (!test) return false; 1007 1008 if ((*p == 'Z') || (*p == 'z')) 1009 { 1010 /* matched string must be followed by space, tab, newline (not counted in length) */ 1011 p += len; 1012 if (*p != '\0') 1013 { 1014 test = asl_core_str_match(p, " \t\n", 1, 1, MFLAG_INCLUDE, &len); 1015 if (!test) return false; 1016 } 1017 1018 if (tlen != NULL) *tlen = p - target; 1019 if (tval != NULL) *tval = timegm(&t); 1020 1021 return true; 1022 } 1023 1024 if (*p == '-') sign = 1; 1025 1026 /* [h]h */ 1027 p += len; 1028 test = asl_core_str_match(p, DIGITS, 1, 2, MFLAG_INCLUDE, &len); 1029 if (!test) return false; 1030 tzh = asl_core_str_to_uint32(p, len); 1031 if (tzh > 23) return false; 1032 1033 /* [:] */ 1034 p += len; 1035 test = asl_core_str_match_char(p, ':', 0, MFLAG_INCLUDE, &len); 1036 if (!test) return false; 1037 1038 /* mm */ 1039 p += len; 1040 test = asl_core_str_match(p, DIGITS, 0, 2, MFLAG_INCLUDE, &len); 1041 if (!test) return false; 1042 tzs = asl_core_str_to_uint32(p, len); 1043 if (tzs > 59) return false; 1044 1045 t.tm_sec += (sign * (tzh * SECONDS_PER_HOUR) + (tzs * SECONDS_PER_MINUTE)); 1046 1047 /* matched string must be followed by space, tab, newline (not counted in length) */ 1048 p += len; 1049 if (*p != '\0') 1050 { 1051 test = asl_core_str_match(p, " \t\n", 1, 1, MFLAG_INCLUDE, &len); 1052 if (!test) return false; 1053 } 1054 1055 if (tlen != NULL) *tlen = p - target; 1056 if (tval != NULL) *tval = timegm(&t); 1057 1058 return true; 1059} 1060 1061time_t 1062asl_core_parse_time(const char *in, uint32_t *tlen) 1063{ 1064 time_t tval = 0; 1065 uint32_t inlen; 1066 1067 if (tlen != NULL) *tlen = 0; 1068 1069 if (in == NULL) return -1; 1070 1071 /* 1072 * Heuristics to determine the string format. 1073 * Warning: this code must be checked and may need to be adjusted if new formats are added. 1074 */ 1075 inlen = strlen(in); 1076 if (inlen == 0) return -1; 1077 1078 /* leading plus or minus means it must be a relative time */ 1079 if ((in[0] == '+') || (in[0] == '-')) 1080 { 1081 if (asl_core_str_match_absolute_or_relative_time(in, &tval, tlen)) return tval; 1082 return -1; 1083 } 1084 1085 /* leading alphabetic char means it must be ctime() format */ 1086 if (((in[0] >= 'a') && (in[0] <= 'z')) || ((in[0] >= 'A') && (in[0] <= 'Z'))) 1087 { 1088 if (asl_core_str_match_c_time(in, &tval, tlen)) return tval; 1089 return -1; 1090 } 1091 1092 /* only absolute, dotted, or iso8601 formats at this point */ 1093 1094 /* one to for chars means it must be absolute */ 1095 if (inlen < 5) 1096 { 1097 if (asl_core_str_match_absolute_or_relative_time(in, &tval, tlen)) return tval; 1098 return -1; 1099 } 1100 1101 /* check for dot */ 1102 if (in[4] == '.') 1103 { 1104 if (asl_core_str_match_dotted_time(in, &tval, tlen)) return tval; 1105 return -1; 1106 } 1107 1108 /* only absolute or iso8601 at this point */ 1109 1110 /* check for absolute first, since that's quicker */ 1111 if (asl_core_str_match_absolute_or_relative_time(in, &tval, tlen)) return tval; 1112 1113 if (asl_core_str_match_iso_8601_time(in, &tval, tlen)) return tval; 1114 1115 return -1; 1116} 1117 1118/* 1119 * asl_parse_time is old SPI used all over the place. 1120 */ 1121time_t 1122asl_parse_time(const char *in) 1123{ 1124 return asl_core_parse_time(in, NULL); 1125} 1126