]>
Commit | Line | Data |
---|---|---|
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 |
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 | static char *asl_filesystem_path_database = NULL; | |
57 | static char *asl_filesystem_path_archive = NULL; | |
81582353 A |
58 | |
59 | /* | |
60 | * Message ID generation | |
61 | */ | |
62 | static uint64_t _asl_core_msg_next_id = 1; | |
63 | static 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 | */ | |
83 | mach_port_t | |
84 | asl_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 | */ | |
119 | uint32_t | |
120 | asl_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 | ||
173 | const char * | |
174 | asl_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 |
198 | const char * |
199 | asl_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 | ||
205 | static ASL_STATUS | |
81582353 A |
206 | asl_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 | 217 | static ASL_STATUS |
81582353 A |
218 | asl_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 | 240 | ASL_STATUS |
81582353 A |
241 | asl_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 | ||
265 | uint64_t | |
266 | asl_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 | ||
287 | uint64_t | |
288 | asl_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 | ||
309 | uint64_t | |
310 | asl_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 |
326 | const char * |
327 | asl_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 | */ | |
388 | char * | |
389 | asl_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 | */ | |
484 | int32_t | |
485 | asl_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 | */ | |
581 | bool | |
582 | asl_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 |
608 | bool |
609 | asl_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 |
624 | uint32_t |
625 | asl_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 |
638 | static bool |
639 | asl_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 |
691 | static 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 | */ | |
713 | bool | |
714 | asl_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 | */ | |
805 | static bool | |
806 | asl_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 | */ | |
911 | static bool | |
912 | asl_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 |
1061 | time_t |
1062 | asl_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 | */ | |
1121 | time_t | |
1122 | asl_parse_time(const char *in) | |
81582353 | 1123 | { |
f3df4c03 | 1124 | return asl_core_parse_time(in, NULL); |
81582353 | 1125 | } |