]> git.saurik.com Git - apple/syslog.git/blame - libsystem_asl.tproj/src/asl_core.c
syslog-267.tar.gz
[apple/syslog.git] / libsystem_asl.tproj / src / asl_core.c
CommitLineData
81582353 1/*
f3df4c03 2 * Copyright (c) 2007-2013 Apple Inc. All rights reserved.
81582353
A
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>
f3df4c03
A
26#include <stdbool.h>
27#include <asl.h>
28#include <asl_string.h>
81582353
A
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>
f3df4c03
A
35#include <servers/bootstrap.h>
36#include <bootstrap_priv.h>
81582353
A
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>
f3df4c03 42#include <dispatch/dispatch.h>
81582353 43
f3df4c03
A
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;
81582353
A
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
f3df4c03
A
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
81582353
A
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
f3df4c03
A
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
81582353
A
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
f3df4c03 217static ASL_STATUS
81582353
A
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
f3df4c03 240ASL_STATUS
81582353
A
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
f3df4c03
A
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
81582353
A
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.
f3df4c03 385 * The worst case occurs when all characters are equally frequent,
81582353
A
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
f3df4c03
A
547#pragma mark -
548#pragma mark time parsing
81582353 549
f3df4c03
A
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 */
81582353 560
f3df4c03
A
561/*
562 * light(er)-weight replcaement (in place of regex) for asl_core_parse_time
563 */
81582353 564
f3df4c03
A
565#define MFLAG_INCLUDE 0x00000001
566#define MFLAG_EXCLUDE 0x00000002
81582353 567
f3df4c03
A
568#define DIGITS "0123456789"
569#define WHITESPACE " "
81582353 570
f3df4c03
A
571#define SECONDS_PER_MINUTE 60
572#define SECONDS_PER_HOUR 3600
573#define SECONDS_PER_DAY 86400
574#define SECONDS_PER_WEEK 604800
81582353 575
f3df4c03
A
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)
81582353 583{
f3df4c03
A
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);
81582353
A
606}
607
f3df4c03
A
608bool
609asl_core_str_match_char(const char *target, const char c, uint32_t mincount, uint32_t flags, uint32_t *length)
81582353 610{
f3df4c03
A
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);
81582353
A
622}
623
f3df4c03
A
624uint32_t
625asl_core_str_to_uint32(const char *target, uint32_t length)
81582353 626{
f3df4c03
A
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;
81582353
A
636}
637
f3df4c03
A
638static bool
639asl_core_str_match_absolute_or_relative_time(const char *target, time_t *tval, uint32_t *tlen)
81582353 640{
f3df4c03
A
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)
81582353 655 {
f3df4c03
A
656 /* relative time */
657 start = time(NULL);
658 if (*p == '-') sign = -1;
81582353 659 }
81582353 660
f3df4c03
A
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')
81582353 680 {
f3df4c03
A
681 test = asl_core_str_match(p, " \t\n", 1, 1, MFLAG_INCLUDE, &len);
682 if (!test) return false;
81582353 683 }
f3df4c03
A
684
685 if (tlen != NULL) *tlen = p - target;
686 if (tval != NULL) *tval = start + (sign * val);
687
688 return true;
81582353
A
689}
690
f3df4c03
A
691static int
692_month_num(const char *s)
81582353 693{
f3df4c03
A
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;
81582353 707
f3df4c03 708}
81582353 709
f3df4c03
A
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;
81582353 765 len = 1;
f3df4c03
A
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')
81582353 789 {
f3df4c03
A
790 test = asl_core_str_match(p, " \t\n", 1, 1, MFLAG_INCLUDE, &len);
791 if (!test) return false;
81582353 792 }
f3df4c03
A
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;
81582353
A
800}
801
f3df4c03
A
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)
81582353 807{
f3df4c03
A
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')
81582353 897 {
f3df4c03
A
898 test = asl_core_str_match(p, " \t\n", 1, 1, MFLAG_INCLUDE, &len);
899 if (!test) return false;
81582353 900 }
f3df4c03
A
901
902 if (tlen != NULL) *tlen = p - target;
903 if (tval != NULL) *tval = timegm(&t);
904
905 return true;
81582353
A
906}
907
f3df4c03
A
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)
81582353 913{
f3df4c03
A
914 uint32_t len;
915 bool test;
81582353 916 const char *p;
f3df4c03
A
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'))
81582353 995 {
f3df4c03
A
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 }
81582353 1003
f3df4c03
A
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')
81582353 1013 {
f3df4c03
A
1014 test = asl_core_str_match(p, " \t\n", 1, 1, MFLAG_INCLUDE, &len);
1015 if (!test) return false;
81582353 1016 }
f3df4c03
A
1017
1018 if (tlen != NULL) *tlen = p - target;
1019 if (tval != NULL) *tval = timegm(&t);
1020
1021 return true;
81582353 1022 }
f3df4c03
A
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;
81582353
A
1059}
1060
f3df4c03
A
1061time_t
1062asl_core_parse_time(const char *in, uint32_t *tlen)
81582353 1063{
f3df4c03
A
1064 time_t tval = 0;
1065 uint32_t inlen;
1066
1067 if (tlen != NULL) *tlen = 0;
81582353 1068
f3df4c03 1069 if (in == NULL) return -1;
81582353 1070
f3df4c03
A
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;
81582353 1077
f3df4c03
A
1078 /* leading plus or minus means it must be a relative time */
1079 if ((in[0] == '+') || (in[0] == '-'))
81582353 1080 {
f3df4c03
A
1081 if (asl_core_str_match_absolute_or_relative_time(in, &tval, tlen)) return tval;
1082 return -1;
81582353
A
1083 }
1084
f3df4c03
A
1085 /* leading alphabetic char means it must be ctime() format */
1086 if (((in[0] >= 'a') && (in[0] <= 'z')) || ((in[0] >= 'A') && (in[0] <= 'Z')))
81582353 1087 {
f3df4c03
A
1088 if (asl_core_str_match_c_time(in, &tval, tlen)) return tval;
1089 return -1;
81582353 1090 }
81582353 1091
f3df4c03
A
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)
81582353 1096 {
f3df4c03
A
1097 if (asl_core_str_match_absolute_or_relative_time(in, &tval, tlen)) return tval;
1098 return -1;
81582353
A
1099 }
1100
f3df4c03
A
1101 /* check for dot */
1102 if (in[4] == '.')
81582353 1103 {
f3df4c03
A
1104 if (asl_core_str_match_dotted_time(in, &tval, tlen)) return tval;
1105 return -1;
81582353
A
1106 }
1107
f3df4c03
A
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;
81582353
A
1116}
1117
f3df4c03
A
1118/*
1119 * asl_parse_time is old SPI used all over the place.
1120 */
1121time_t
1122asl_parse_time(const char *in)
81582353 1123{
f3df4c03 1124 return asl_core_parse_time(in, NULL);
81582353 1125}