2 * Copyright (c) 2007-2013 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
28 #include <asl_string.h>
30 #include <asl_private.h>
32 #include <membership.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>
44 const char *ASL_LEVEL_TO_STRING
[] =
57 * Message ID generation
59 static uint64_t _asl_core_msg_next_id
= 1;
60 static pthread_mutex_t core_lock
= PTHREAD_MUTEX_INITIALIZER
;
62 #define mix(a, b, c) \
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); \
76 * Get ASL server mach port.
77 * reset != 0 flushes cached port.
78 * reset < 0 returns MACH_PORT_NULL
81 asl_core_get_service_port(int reset
)
83 static mach_port_t server_port
= MACH_PORT_NULL
;
85 kern_return_t kstatus
;
87 if ((reset
!= 0) && (server_port
!= MACH_PORT_NULL
))
89 mach_port_t tmp
= server_port
;
90 server_port
= MACH_PORT_NULL
;
91 mach_port_deallocate(mach_task_self(), tmp
);
94 if (reset
< 0) return MACH_PORT_NULL
;
96 if (server_port
!= MACH_PORT_NULL
) return server_port
;
99 char *str
= getenv("ASL_DISABLE");
100 if ((str
!= NULL
) && (!strcmp(str
, "1"))) return MACH_PORT_NULL
;
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
;
105 if (!OSAtomicCompareAndSwap32Barrier(MACH_PORT_NULL
, tmp
, (int32_t *)&server_port
))
107 mach_port_deallocate(mach_task_self(), tmp
);
114 * Hash is used to improve string search.
117 asl_core_string_hash(const char *s
, uint32_t inlen
)
119 uint32_t a
, b
, c
, l
, len
;
121 if (s
== NULL
) return 0;
126 if (s
[0] == '\0') return 0;
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));
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);
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);
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);
171 asl_core_error(uint32_t code
)
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";
192 return "Operation Failed";
196 asl_core_level_to_string(uint32_t level
)
198 if (level
> ASL_LEVEL_DEBUG
) return "invalid";
199 return ASL_LEVEL_TO_STRING
[level
];
203 asl_core_check_user_access(int32_t msgu
, int32_t readu
)
205 /* -1 means anyone may read */
206 if (msgu
== -1) return ASL_STATUS_OK
;
208 /* Check for exact match */
209 if (msgu
== readu
) return ASL_STATUS_OK
;
211 return ASL_STATUS_ACCESS_DENIED
;
215 asl_core_check_group_access(int32_t msgg
, int32_t readu
, int32_t readg
)
220 /* -1 means anyone may read */
221 if (msgg
== -1) return ASL_STATUS_OK
;
223 /* Check for exact match */
224 if (msgg
== readg
) return ASL_STATUS_OK
;
226 /* Check if user (u) is in read group (msgg) */
227 mbr_uid_to_uuid(readu
, uu
);
228 mbr_gid_to_uuid(msgg
, gu
);
231 mbr_check_membership(uu
, gu
, &check
);
232 if (check
!= 0) return ASL_STATUS_OK
;
234 return ASL_STATUS_ACCESS_DENIED
;
238 asl_core_check_access(int32_t msgu
, int32_t msgg
, int32_t readu
, int32_t readg
, uint16_t flags
)
242 /* root (uid 0) may always read */
243 if (readu
== 0) return ASL_STATUS_OK
;
245 uset
= flags
& ASL_MSG_FLAG_READ_UID_SET
;
246 gset
= flags
& ASL_MSG_FLAG_READ_GID_SET
;
248 /* if no access controls are set, anyone may read */
249 if ((uset
| gset
) == 0) return ASL_STATUS_OK
;
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
);
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
);
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
);
263 asl_core_htonq(uint64_t n
)
265 #ifdef __BIG_ENDIAN__
277 x
.l
[0] = htonl(x
.l
[1]);
285 asl_core_ntohq(uint64_t n
)
287 #ifdef __BIG_ENDIAN__
299 x
.l
[0] = ntohl(x
.l
[1]);
307 asl_core_new_msg_id(uint64_t start
)
311 pthread_mutex_lock(&core_lock
);
313 if (start
!= 0) _asl_core_msg_next_id
= start
;
315 out
= _asl_core_msg_next_id
;
316 _asl_core_msg_next_id
++;
318 pthread_mutex_unlock(&core_lock
);
324 asl_filesystem_path(uint32_t place
)
326 static dispatch_once_t once
;
327 static char *asl_filesystem_path_database
= NULL
;
328 static char *asl_filesystem_path_archive
= NULL
;
330 dispatch_once(&once
, ^{
331 char *asl_var_log
= NULL
;
332 const char *const_asl_var_log
= "/var/log";
334 #if TARGET_OS_SIMULATOR
335 asl_var_log
= getenv("SIMULATOR_LOG_ROOT");
338 if (asl_var_log
!= NULL
) const_asl_var_log
= (const char *)asl_var_log
;
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
);
346 case ASL_PLACE_DATABASE
:
348 return asl_filesystem_path_database
;
350 case ASL_PLACE_ARCHIVE
:
352 return asl_filesystem_path_archive
;
364 #pragma mark data buffer encoding
367 * asl_core_encode_buffer
368 * encode arbitrary data as a C string without embedded zero (nul) characters
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]).
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.
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.
386 asl_core_encode_buffer(const char *in
, uint32_t len
)
389 uint32_t i
, j
, k
, outlen
, breakit
, min
, hist
[256];
390 uint32_t lfu
[2], save
[2];
393 if (in
== NULL
) return NULL
;
394 if (len
== 0) return NULL
;
396 memset(hist
, 0, sizeof(hist
));
400 for (i
= 0; i
< len
; i
++)
406 for (j
= 0; j
< 2; j
++)
411 for (i
= 2; i
< 256; i
++)
419 * Stop if there are no occurances or character i in the input.
420 * The minimum will never be less than zero.
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.
429 if ((j
== 1) && (min
== save
[0])) break;
433 save
[j
] = hist
[lfu
[j
]];
434 hist
[lfu
[j
]] = (uint32_t)-1;
437 outlen
= 2 + len
+ save
[0] + save
[1] + 1;
439 str
= malloc(outlen
);
440 if (str
== NULL
) return NULL
;
442 str
[outlen
- 1] = '\0';
449 for (i
= 0; i
< len
; i
++)
459 for (k
= 0; (k
< 2) && (breakit
== 0); k
++)
469 if (breakit
== 1) continue;
478 * asl_core_decode_buffer
479 * decode a string produced by asl_encode_buffer to recreate the original data
482 asl_core_decode_buffer(const char *in
, char **buf
, uint32_t *len
)
485 uint32_t i
, j
, n
, outlen
;
489 if (buf
== NULL
) return -1;
490 if (len
== NULL
) return -1;
497 /* strip trailing nul */
500 /* determine output length and check for invalid input */
501 for (i
= 2; i
< n
; i
++)
507 if (i
== n
) return -1;
510 if ((v
< 1) || (v
> 2)) return -1;
517 if (outlen
== 0) return -1;
519 out
= malloc(outlen
);
520 if (out
== NULL
) return -1;
523 for (i
= 2; i
< n
; i
++)
530 else if (v
== lfu
[1])
534 out
[j
++] = lfu
[v
- 1];
545 #pragma mark time parsing
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]
559 * light(er)-weight replcaement (in place of regex) for asl_core_parse_time
562 #define MFLAG_INCLUDE 0x00000001
563 #define MFLAG_EXCLUDE 0x00000002
565 #define DIGITS "0123456789"
566 #define WHITESPACE " "
568 #define SECONDS_PER_MINUTE 60
569 #define SECONDS_PER_HOUR 3600
570 #define SECONDS_PER_DAY 86400
571 #define SECONDS_PER_WEEK 604800
574 * Match between mincount and maxcount characters in or not in mset.
575 * maxcount == 0 means no limit.
579 asl_core_str_match(const char *target
, const char *mset
, uint32_t mincount
, uint32_t maxcount
, uint32_t flags
, uint32_t *length
)
584 if (length
== NULL
) length
= &n
;
586 if (target
== NULL
) return (mincount
== 0);
588 for (x
= target
, *length
= 0; *x
!= '\0'; x
++, *length
= *length
+ 1)
592 if ((*length
== maxcount
) && (maxcount
> 0)) return true;
593 if (mset
== NULL
) continue;
595 s
= strchr(mset
, *x
);
596 if ((s
== NULL
) && (flags
& MFLAG_EXCLUDE
)) continue;
597 if ((s
!= NULL
) && (flags
& MFLAG_INCLUDE
)) continue;
602 return (*length
>= mincount
);
606 asl_core_str_match_char(const char *target
, const char c
, uint32_t mincount
, uint32_t flags
, uint32_t *length
)
610 if (length
== NULL
) length
= &n
;
613 if (target
== NULL
) return (mincount
== 0);
615 if ((*target
== c
) && (flags
& MFLAG_INCLUDE
)) *length
= 1;
616 if ((*target
!= c
) && (flags
& MFLAG_EXCLUDE
)) *length
= 1;
618 return (*length
>= mincount
);
622 asl_core_str_to_uint32(const char *target
, uint32_t length
)
624 uint32_t i
, d
, out
= 0;
626 for (i
= 0; i
< length
; i
++)
629 out
= (out
* 10) + d
;
636 asl_core_str_to_size(char *s
)
641 if (s
== NULL
) return 0;
644 if (len
== 0) return 0;
649 if (x
== 'K') n
= 1ll << 10;
650 else if (x
== 'M') n
= 1ll << 20;
651 else if (x
== 'G') n
= 1ll << 30;
658 asl_core_str_to_time(char *s
, uint32_t def_n
)
664 if (s
== NULL
) return 0;
667 if (len
== 0) return 0;
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
;
684 asl_core_time_to_str(time_t s
, char *str
, size_t len
)
686 char days
[32], hms
[32];
689 d
= s
/ SECONDS_PER_DAY
;
690 s
%= SECONDS_PER_DAY
;
692 h
= s
/ SECONDS_PER_HOUR
;
693 s
%= SECONDS_PER_HOUR
;
695 m
= s
/ SECONDS_PER_MINUTE
;
696 s
%= SECONDS_PER_MINUTE
;
698 memset(days
, 0, sizeof(days
));
699 if (d
> 0) snprintf(days
, sizeof(days
), "%u day%s", d
, (d
== 1) ? "" : "s");
701 memset(hms
, 0, sizeof(hms
));
702 snprintf(hms
, sizeof(hms
), "%02u:%02u:%02lld", h
, m
, (long long) s
);
704 if ((h
+ m
+ s
) == 0)
706 if (d
== 0) snprintf(str
, len
, "0");
707 else snprintf(str
, len
, "%s", days
);
711 if (d
== 0) snprintf(str
, len
, "%s", hms
);
712 else snprintf(str
, len
, "%s %s", days
, hms
);
716 asl_core_str_match_absolute_or_relative_time(const char *target
, time_t *tval
, uint32_t *tlen
)
719 int32_t val
, sign
= 1;
724 if (target
== NULL
) return false;
728 test
= asl_core_str_match(p
, "+-", 0, 1, MFLAG_INCLUDE
, &len
);
729 if (!test
) return false;
735 if (*p
== '-') sign
= -1;
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
);
746 test
= asl_core_str_match(p
, "SsMmHhDdWw", 0, 1, MFLAG_INCLUDE
, &len
);
747 if (!test
) return false;
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
;
754 /* matched string must be followed by space, tab, newline (not counted in length) */
758 test
= asl_core_str_match(p
, " \t\n", 1, 1, MFLAG_INCLUDE
, &len
);
759 if (!test
) return false;
762 if (tlen
!= NULL
) *tlen
= p
- target
;
763 if (tval
!= NULL
) *tval
= start
+ (sign
* val
);
769 _month_num(const char *s
)
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;
788 * Match ctime() format - Mth [D]D [h]h:mm:ss
791 asl_core_str_match_c_time(const char *target
, time_t *tval
, uint32_t *tlen
)
799 if (target
== NULL
) return false;
800 memset(&t
, 0, sizeof(t
));
802 /* determine current date */
804 localtime_r(&now
, &t
);
806 memset(&t
, 0, sizeof(t
));
811 t
.tm_mon
= _month_num(p
);
813 if (t
.tm_mon
== -1) return false;
817 test
= asl_core_str_match(p
, WHITESPACE
, 1, 0, MFLAG_INCLUDE
, &len
);
818 if (!test
) return false;
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;
829 test
= asl_core_str_match(p
, WHITESPACE
, 1, 0, MFLAG_INCLUDE
, &len
);
830 if (!test
) return false;
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;
841 if (*p
!= ':') return false;
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;
853 if (*p
!= ':') return false;
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;
863 /* matched string must be followed by space, tab, newline (not counted in length) */
867 test
= asl_core_str_match(p
, " \t\n", 1, 1, MFLAG_INCLUDE
, &len
);
868 if (!test
) return false;
873 if (tlen
!= NULL
) *tlen
= p
- target
;
874 if (tval
!= NULL
) *tval
= mktime(&t
);
880 * Match YYYY.[M]M.[D]D [h]h:mm:ss UTC
883 asl_core_str_match_dotted_time(const char *target
, time_t *tval
, uint32_t *tlen
)
890 if (target
== NULL
) return false;
891 memset(&t
, 0, sizeof(t
));
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;
901 if (*p
!= '.') return false;
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;
915 if (*p
!= '.') return false;
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;
927 test
= asl_core_str_match(p
, WHITESPACE
, 1, 0, MFLAG_INCLUDE
, &len
);
928 if (!test
) return false;
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;
939 if (*p
!= ':') return false;
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;
951 if (*p
!= ':') return false;
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;
963 test
= asl_core_str_match(p
, WHITESPACE
, 1, 0, MFLAG_INCLUDE
, &len
);
964 if (!test
) return false;
968 if (strncmp(p
, "UTC", 3)) return false;
971 /* matched string must be followed by space, tab, newline (not counted in length) */
975 test
= asl_core_str_match(p
, " \t\n", 1, 1, MFLAG_INCLUDE
, &len
);
976 if (!test
) return false;
979 if (tlen
!= NULL
) *tlen
= p
- target
;
980 if (tval
!= NULL
) *tval
= timegm(&t
);
986 * Match YYYY[-]MM[-]DD[Tt]hh[:]hh[:]ss[Zz] or YYYY[-]MM[-]DD[Tt]hh[:]hh[:]ss[+-][H]H[[:]MM]
989 asl_core_str_match_iso_8601_time(const char *target
, time_t *tval
, uint32_t *tlen
)
995 int32_t tzh
, tzs
, sign
= -1;
997 if (target
== NULL
) return false;
998 memset(&t
, 0, sizeof(t
));
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;
1008 test
= asl_core_str_match_char(p
, '-', 0, MFLAG_INCLUDE
, &len
);
1009 if (!test
) return false;
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;
1022 test
= asl_core_str_match_char(p
, '-', 0, MFLAG_INCLUDE
, &len
);
1023 if (!test
) return false;
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;
1034 test
= asl_core_str_match(p
, "Tt", 1, 1, MFLAG_INCLUDE
, &len
);
1035 if (!test
) return false;
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;
1046 test
= asl_core_str_match_char(p
, ':', 0, MFLAG_INCLUDE
, &len
);
1047 if (!test
) return false;
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;
1058 test
= asl_core_str_match_char(p
, ':', 0, MFLAG_INCLUDE
, &len
);
1059 if (!test
) return false;
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;
1070 /* default to local time if we hit the end of the string */
1071 if ((*p
== '\0') || (*p
== ' ') || (*p
== '\t') || (*p
== '\n'))
1075 if (tlen
!= NULL
) *tlen
= p
- target
;
1076 if (tval
!= NULL
) *tval
= mktime(&t
);
1082 test
= asl_core_str_match(p
, "Zz+-", 1, 1, MFLAG_INCLUDE
, &len
);
1083 if (!test
) return false;
1085 if ((*p
== 'Z') || (*p
== 'z'))
1087 /* matched string must be followed by space, tab, newline (not counted in length) */
1091 test
= asl_core_str_match(p
, " \t\n", 1, 1, MFLAG_INCLUDE
, &len
);
1092 if (!test
) return false;
1095 if (tlen
!= NULL
) *tlen
= p
- target
;
1096 if (tval
!= NULL
) *tval
= timegm(&t
);
1101 if (*p
== '-') sign
= 1;
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;
1112 test
= asl_core_str_match_char(p
, ':', 0, MFLAG_INCLUDE
, &len
);
1113 if (!test
) return false;
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;
1122 t
.tm_sec
+= (sign
* (tzh
* SECONDS_PER_HOUR
) + (tzs
* SECONDS_PER_MINUTE
));
1124 /* matched string must be followed by space, tab, newline (not counted in length) */
1128 test
= asl_core_str_match(p
, " \t\n", 1, 1, MFLAG_INCLUDE
, &len
);
1129 if (!test
) return false;
1132 if (tlen
!= NULL
) *tlen
= p
- target
;
1133 if (tval
!= NULL
) *tval
= timegm(&t
);
1139 asl_core_parse_time(const char *in
, uint32_t *tlen
)
1144 if (tlen
!= NULL
) *tlen
= 0;
1146 if (in
== NULL
) return -1;
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.
1153 if (inlen
== 0) return -1;
1155 /* leading plus or minus means it must be a relative time */
1156 if ((in
[0] == '+') || (in
[0] == '-'))
1158 if (asl_core_str_match_absolute_or_relative_time(in
, &tval
, tlen
)) return tval
;
1162 /* leading alphabetic char means it must be ctime() format */
1163 if (((in
[0] >= 'a') && (in
[0] <= 'z')) || ((in
[0] >= 'A') && (in
[0] <= 'Z')))
1165 if (asl_core_str_match_c_time(in
, &tval
, tlen
)) return tval
;
1169 /* only absolute, dotted, or iso8601 formats at this point */
1171 /* one to for chars means it must be absolute */
1174 if (asl_core_str_match_absolute_or_relative_time(in
, &tval
, tlen
)) return tval
;
1181 if (asl_core_str_match_dotted_time(in
, &tval
, tlen
)) return tval
;
1185 /* only absolute or iso8601 at this point */
1187 /* check for absolute first, since that's quicker */
1188 if (asl_core_str_match_absolute_or_relative_time(in
, &tval
, tlen
)) return tval
;
1190 if (asl_core_str_match_iso_8601_time(in
, &tval
, tlen
)) return tval
;
1196 * asl_parse_time is old SPI used all over the place.
1199 asl_parse_time(const char *in
)
1201 return asl_core_parse_time(in
, NULL
);