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