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