]> git.saurik.com Git - apple/syslog.git/blob - libsystem_asl.tproj/src/asl_msg.c
f0b0c0d2c4b2f57a8c8ad33dc2152c1eefe4d86d
[apple/syslog.git] / libsystem_asl.tproj / src / asl_msg.c
1 /*
2 * Copyright (c) 2009-2012 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 <string.h>
25 #include <stdint.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <ctype.h>
29 #include <unistd.h>
30 #include <stdarg.h>
31 #include <regex.h>
32 #include <syslog.h>
33 #include <errno.h>
34 #include <time.h>
35 #include <sys/time.h>
36 #include <asl.h>
37 #include <asl_private.h>
38 #include <asl_core.h>
39 #include <sys/types.h>
40 #include <libkern/OSAtomic.h>
41 #include <assert.h>
42 #include "asl_msg.h"
43
44 #define TOKEN_NULL 0
45 #define TOKEN_OPEN 1
46 #define TOKEN_CLOSE 2
47 #define TOKEN_WORD 3
48 #define TOKEN_INT 4
49
50 #define MFMT_RAW 0
51 #define MFMT_STD 1
52 #define MFMT_BSD 2
53 #define MFMT_XML 3
54 #define MFMT_STR 4
55 #define MFMT_MSG 5
56
57 #define SEC_PER_HOUR 3600
58
59 #define forever for(;;)
60
61 #define streq(A, B) (strcmp(A, B) == 0)
62 #define streq_len(A, B, C) (strncmp(A, B, C) == 0)
63 #define strneq(A, B) (strcmp(A, B) != 0)
64 #define strcaseeq(A, B) (strcasecmp(A, B) == 0)
65 #define strcaseneq(A, B) (strcasecmp(A, B) != 0)
66
67 #ifndef ASL_KEY_OPTION
68 #define ASL_KEY_OPTION "ASLOption"
69 #endif
70
71 #ifndef ASL_QUERY_OP_FALSE
72 #define ASL_QUERY_OP_FALSE 0
73 #endif
74
75 #define AUX_0_TIME 0x00000001
76 #define AUX_0_TIME_NSEC 0x00000002
77 #define AUX_0_HOST 0x00000004
78 #define AUX_0_SENDER 0x00000008
79 #define AUX_0_FACILITY 0x00000010
80 #define AUX_0_PID 0x00000020
81 #define AUX_0_UID 0x00000040
82 #define AUX_0_GID 0x00000080
83 #define AUX_0_MSG 0x00000100
84 #define AUX_0_OPTION 0x00000200
85 #define AUX_0_LEVEL 0x00000400
86
87 extern time_t asl_parse_time(const char *in);
88
89 /* from asl_util.c */
90 int asl_is_utf8(const char *str);
91 uint8_t *asl_b64_encode(const uint8_t *buf, size_t len);
92
93 static const char *ASLStandardKey[] =
94 {
95 ASL_KEY_TIME,
96 ASL_KEY_TIME_NSEC,
97 ASL_KEY_HOST,
98 ASL_KEY_SENDER,
99 ASL_KEY_FACILITY,
100 ASL_KEY_PID,
101 ASL_KEY_UID,
102 ASL_KEY_GID,
103 ASL_KEY_LEVEL,
104 ASL_KEY_MSG,
105 ASL_KEY_READ_UID,
106 ASL_KEY_READ_GID,
107 ASL_KEY_SESSION,
108 ASL_KEY_REF_PID,
109 ASL_KEY_REF_PROC,
110 ASL_KEY_MSG_ID,
111 ASL_KEY_EXPIRE_TIME,
112 ASL_KEY_OPTION
113 };
114
115 static const char *MTStandardKey[] =
116 {
117 "com.apple.message.domain",
118 "com.apple.message.domain_scope",
119 "com.apple.message.result",
120 "com.apple.message.signature",
121 "com.apple.message.signature2",
122 "com.apple.message.signature3",
123 "com.apple.message.success",
124 "com.apple.message.uuid",
125 "com.apple.message.value",
126 "com.apple.message.value2",
127 "com.apple.message.value3",
128 "com.apple.message.value4",
129 "com.apple.message.value5"
130 };
131
132 static uint16_t
133 _asl_msg_std_key(const char *s, uint32_t len)
134 {
135 if ((len > 18) && (streq_len(s, "com.apple.message.", 18)))
136 {
137 if (streq(s + 18, "domain")) return ASL_MT_KEY_DOMAIN;
138 else if (streq(s + 18, "domain_scope")) return ASL_MT_KEY_SCOPE;
139 else if (streq(s + 18, "result")) return ASL_MT_KEY_RESULT;
140 else if (streq(s + 18, "signature")) return ASL_MT_KEY_SIG;
141 else if (streq(s + 18, "signature2")) return ASL_MT_KEY_SIG2;
142 else if (streq(s + 18, "signature3")) return ASL_MT_KEY_SIG3;
143 else if (streq(s + 18, "success")) return ASL_MT_KEY_SUCCESS;
144 else if (streq(s + 18, "uuid")) return ASL_MT_KEY_UUID;
145 else if (streq(s + 18, "value")) return ASL_MT_KEY_VAL;
146 else if (streq(s + 18, "value2")) return ASL_MT_KEY_VAL2;
147 else if (streq(s + 18, "value3")) return ASL_MT_KEY_VAL3;
148 else if (streq(s + 18, "value4")) return ASL_MT_KEY_VAL4;
149 else if (streq(s + 18, "value5")) return ASL_MT_KEY_VAL5;
150
151 return 0;
152 }
153
154 switch (len)
155 {
156 case 3:
157 {
158 if streq(s, ASL_KEY_PID) return ASL_STD_KEY_PID;
159 else if streq(s, ASL_KEY_UID) return ASL_STD_KEY_UID;
160 else if streq(s, ASL_KEY_GID) return ASL_STD_KEY_GID;
161 }
162 case 4:
163 {
164 if streq(s, ASL_KEY_TIME) return ASL_STD_KEY_TIME;
165 else if streq(s, ASL_KEY_HOST) return ASL_STD_KEY_HOST;
166 }
167 case 5:
168 {
169 if streq(s, ASL_KEY_LEVEL) return ASL_STD_KEY_LEVEL;
170 }
171 case 6:
172 {
173 if streq(s, ASL_KEY_SENDER) return ASL_STD_KEY_SENDER;
174 else if streq(s, ASL_KEY_REF_PID) return ASL_STD_KEY_REF_PID;
175 }
176 case 7:
177 {
178 if streq(s, ASL_KEY_MSG) return ASL_STD_KEY_MESSAGE;
179 else if streq(s, ASL_KEY_SESSION) return ASL_STD_KEY_SESSION;
180 else if streq(s, ASL_KEY_READ_UID) return ASL_STD_KEY_READ_UID;
181 else if streq(s, ASL_KEY_READ_GID) return ASL_STD_KEY_READ_GID;
182 else if streq(s, ASL_KEY_REF_PROC) return ASL_STD_KEY_REF_PROC;
183 }
184 case 8:
185 {
186 if streq(s, ASL_KEY_FACILITY) return ASL_STD_KEY_FACILITY;
187 }
188 case 9:
189 {
190 if streq(s, ASL_KEY_OPTION) return ASL_STD_KEY_OPTION;
191 }
192 case 11:
193 {
194 if streq(s, ASL_KEY_TIME_NSEC) return ASL_STD_KEY_NANO;
195 }
196 case 12:
197 {
198 if streq(s, ASL_KEY_MSG_ID) return ASL_STD_KEY_MSG_ID;
199 }
200 case 13:
201 {
202 if streq(s, ASL_KEY_EXPIRE_TIME) return ASL_STD_KEY_EXPIRE;
203 }
204 default:
205 {
206 return 0;
207 }
208 }
209
210 return 0;
211 }
212
213 static asl_msg_t *
214 _asl_msg_make_page()
215 {
216 asl_msg_t *out;
217 int i;
218
219 out = calloc(1, sizeof(asl_msg_t));
220 if (out == NULL) return NULL;
221
222 for (i = 0; i < ASL_MSG_PAGE_SLOTS; i++)
223 {
224 out->key[i] = ASL_MSG_SLOT_FREE;
225 out->val[i] = ASL_MSG_SLOT_FREE;
226 }
227
228 return out;
229 }
230
231 static const char *
232 _asl_msg_slot_key(asl_msg_t *page, uint32_t slot)
233 {
234 const char *out;
235 uint16_t x;
236
237 if (page == NULL) return NULL;
238 if (slot >= ASL_MSG_PAGE_SLOTS) return NULL;
239
240 if (page->key[slot] == ASL_MSG_SLOT_FREE) return NULL;
241
242 switch (page->key[slot] & ASL_MSG_KV_MASK)
243 {
244 case ASL_MSG_KV_INLINE:
245 {
246 return page->data + page->key[slot];
247 }
248 case ASL_MSG_KV_DICT:
249 {
250 if ((page->key[slot] > ASL_STD_KEY_BASE) && (page->key[slot] <= ASL_STD_KEY_LAST))
251 {
252 x = page->key[slot] - ASL_STD_KEY_BASE - 1;
253 return ASLStandardKey[x];
254 }
255 else if ((page->key[slot] > ASL_MT_KEY_BASE) && (page->key[slot] <= ASL_MT_KEY_LAST))
256 {
257 x = page->key[slot] - ASL_MT_KEY_BASE - 1;
258 return MTStandardKey[x];
259 }
260
261 return NULL;
262 }
263 case ASL_MSG_KV_EXTERN:
264 {
265 x = page->key[slot] & ASL_MSG_OFFSET_MASK;
266 memcpy(&out, page->data + x, sizeof(char *));
267 return out;
268 }
269 }
270
271 return NULL;
272 }
273
274 static const char *
275 _asl_msg_slot_val(asl_msg_t *page, uint32_t slot)
276 {
277 const char *out;
278 uint16_t x, type;
279
280 if (page == NULL) return NULL;
281 if (slot >= ASL_MSG_PAGE_SLOTS) return NULL;
282
283 if (page->val[slot] == ASL_MSG_SLOT_FREE) return NULL;
284
285 type = page->val[slot] & ASL_MSG_KV_MASK;
286
287 if (type == ASL_MSG_KV_INLINE)
288 {
289 return page->data + page->val[slot];
290 }
291 else if (type == ASL_MSG_KV_EXTERN)
292 {
293 x = page->val[slot] & ASL_MSG_OFFSET_MASK;
294 memcpy(&out, page->data + x, sizeof(char *));
295 return out;
296 }
297
298 return NULL;
299 }
300
301 /*
302 * asl_new: create a new log message.
303 */
304 asl_msg_t *
305 asl_msg_new(uint32_t type)
306 {
307 asl_msg_t *out;
308
309 out = _asl_msg_make_page();
310 if (out == NULL) return NULL;
311
312 out->type = type;
313 out->refcount = 1;
314
315 return out;
316 }
317
318 asl_msg_t *
319 asl_msg_retain(asl_msg_t *msg)
320 {
321 int32_t new;
322
323 if (msg == NULL) return NULL;
324
325 new = OSAtomicIncrement32Barrier(&msg->refcount);
326 assert(new >= 1);
327
328 return msg;
329 }
330
331 static void
332 _asl_msg_free(asl_msg_t *page)
333 {
334 uint32_t i;
335 char *p;
336
337 if (page == NULL) return;
338
339 for (i = 0; i < ASL_MSG_PAGE_SLOTS; i++)
340 {
341 if ((page->key[i] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
342 {
343 memcpy(&p, page->data + (page->key[i] & ASL_MSG_OFFSET_MASK), sizeof(char *));
344 free(p);
345 }
346
347 if ((page->val[i] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
348 {
349 memcpy(&p, page->data + (page->val[i] & ASL_MSG_OFFSET_MASK), sizeof(char *));
350 free(p);
351 }
352 }
353
354 free(page);
355 }
356
357 void
358 asl_msg_release(asl_msg_t *msg)
359 {
360 int32_t new;
361 asl_msg_t *next;
362
363 if (msg == NULL) return;
364
365 new = OSAtomicDecrement32Barrier(&msg->refcount);
366 assert(new >= 0);
367
368 if (new > 0) return;
369
370 while (msg != NULL)
371 {
372 next = msg->next;
373 _asl_msg_free(msg);
374 msg = next;
375 }
376 }
377
378 static uint32_t
379 _asl_msg_index(asl_msg_t *msg, const char *key, uint32_t *oslot, asl_msg_t **opage)
380 {
381 uint32_t i, len, slot;
382 uint16_t kx;
383 asl_msg_t *page;
384 const char *kp;
385
386 if (msg == NULL) return IndexNull;
387 if (key == NULL) return IndexNull;
388
389 i = 0;
390 slot = 0;
391 if (oslot != NULL) *oslot = slot;
392
393 page = msg;
394 if (opage != NULL) *opage = page;
395
396 len = strlen(key);
397 kx = _asl_msg_std_key(key, len);
398
399 forever
400 {
401 if (page->key[slot] != ASL_MSG_SLOT_FREE)
402 {
403 if (kx != 0)
404 {
405 if (page->key[slot] == kx) return i;
406 }
407 else if ((page->key[slot] & ASL_MSG_KV_MASK) == ASL_MSG_KV_DICT)
408 {
409 /* page->key[slot] is a dictionary key, but key is not (kx == 0) so skip this slot */
410 }
411 else if ((page->key[slot] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
412 {
413 memcpy(&kp, page->data + (page->key[slot] & ASL_MSG_OFFSET_MASK), sizeof(char *));
414 if (streq(key, kp)) return i;
415 }
416 else
417 {
418 kp = page->data + page->key[slot];
419 if (streq(key, kp)) return i;
420 }
421 }
422
423 i++;
424 slot++;
425 if (oslot != NULL) *oslot = slot;
426
427 if (slot >= ASL_MSG_PAGE_SLOTS)
428 {
429 if (page->next == NULL) return IndexNull;
430
431 slot = 0;
432 if (oslot != NULL) *oslot = slot;
433
434 page = page->next;
435 if (opage != NULL) *opage = page;
436 }
437 }
438
439 return IndexNull;
440 }
441
442 /*
443 * asl_msg_key: iterate over entries
444 * initial value of n should be 0
445 * after that, the value of n should be previously returned value
446 * sets the pointers for the next key, value, and op in the msgionary
447 * returns IndexNull when there are no more entries
448 */
449 static uint32_t
450 _asl_msg_fetch_internal(asl_msg_t *msg, uint32_t n, const char **keyout, const char **valout, uint32_t *opout, asl_msg_t **outpage, uint32_t *outslot)
451 {
452 uint32_t slot;
453 asl_msg_t *page;
454
455 if (msg == NULL) return IndexNull;
456 if (outpage != NULL) *outpage = NULL;
457 if (outslot != NULL) *outslot = IndexNull;
458
459 slot = n;
460 page = msg;
461
462 while (slot >= ASL_MSG_PAGE_SLOTS)
463 {
464 if (page->next == NULL) return IndexNull;
465 page = page->next;
466 slot -= ASL_MSG_PAGE_SLOTS;
467 }
468
469 while (page->key[slot] == ASL_MSG_SLOT_FREE)
470 {
471 slot++;
472 n++;
473
474 if (slot >= ASL_MSG_PAGE_SLOTS)
475 {
476 if (page->next == NULL) return IndexNull;
477 page = page->next;
478 slot = 0;
479 }
480 }
481
482 n++;
483
484 if (keyout != NULL) *keyout = _asl_msg_slot_key(page, slot);
485 if (valout != NULL) *valout = _asl_msg_slot_val(page, slot);
486 if (opout != NULL) *opout = page->op[slot];
487
488 if (outpage != NULL) *outpage = page;
489 if (outslot != NULL) *outslot = slot;
490
491 return n;
492 }
493
494 uint32_t
495 asl_msg_fetch(asl_msg_t *msg, uint32_t n, const char **keyout, const char **valout, uint32_t *opout)
496 {
497 return _asl_msg_fetch_internal(msg, n, keyout, valout, opout, NULL, NULL);
498 }
499
500 static int
501 _asl_msg_new_key_val_op(asl_msg_t *msg, const char *key, const char *val, uint32_t op)
502 {
503 uint32_t slot, keylen, vallen, total;
504 uint16_t kx;
505 asl_msg_t *page, *last;
506 char *extkey, *extval;
507
508 if (msg == NULL) return -1;
509 if (key == NULL) return -1;
510
511 extkey = NULL;
512 extval = NULL;
513
514 keylen = strlen(key);
515 kx = _asl_msg_std_key(key, keylen);
516
517 if (kx == 0) keylen++;
518 else keylen = 0;
519
520 total = keylen;
521
522 vallen = 0;
523 if (val != NULL)
524 {
525 vallen = strlen(val) + 1;
526 total += vallen;
527 }
528
529 /* check if one or both of key and value must be "external" (in its own malloced space) */
530 if (keylen > ASL_MSG_PAGE_DATA_SIZE)
531 {
532 extkey = strdup(key);
533 keylen = sizeof(char *);
534 }
535
536 if (vallen > ASL_MSG_PAGE_DATA_SIZE)
537 {
538 extval = strdup(val);
539 vallen = sizeof(char *);
540 }
541
542 total = keylen + vallen;
543 if ((total > ASL_MSG_PAGE_DATA_SIZE) && (extval == NULL) && (keylen > 0))
544 {
545 extval = strdup(val);
546 vallen = sizeof(char *);
547 total = keylen + vallen;
548 }
549
550 if ((total > ASL_MSG_PAGE_DATA_SIZE) && (extkey == NULL))
551 {
552 extkey = strdup(key);
553 keylen = sizeof(char *);
554 total = keylen + vallen;
555 }
556
557 if (total > ASL_MSG_PAGE_DATA_SIZE)
558 {
559 /* can't happen, but... */
560 if (extkey != NULL) free(extkey);
561 if (extval != NULL) free(extval);
562 return -1;
563 }
564
565 /* find a page with space for the key and value and a free slot*/
566 slot = 0;
567 last = msg;
568
569 for (page = msg; page != NULL; page = page->next)
570 {
571 last = page;
572
573 if (total <= (ASL_MSG_PAGE_DATA_SIZE - page->data_size))
574 {
575 /* check for a free slot */
576 for (slot = 0; (slot < ASL_MSG_PAGE_SLOTS) && (page->key[slot] != ASL_MSG_SLOT_FREE); slot++);
577 if (slot < ASL_MSG_PAGE_SLOTS) break;
578 }
579 }
580
581 if (page == NULL)
582 {
583 /* allocate a new page and attach it */
584 page = _asl_msg_make_page();
585 if (page == NULL)
586 {
587 if (extkey != NULL) free(extkey);
588 if (extval != NULL) free(extval);
589 return -1;
590 }
591
592 last->next = page;
593 slot = 0;
594 }
595
596 /* copy key or external key pointer into page data */
597 if (kx != 0)
598 {
599 page->key[slot] = kx;
600 }
601 else if (extkey == NULL)
602 {
603 page->key[slot] = page->data_size;
604 memcpy(page->data + page->data_size, key, keylen);
605 }
606 else
607 {
608 page->key[slot] = page->data_size | ASL_MSG_KV_EXTERN;
609 memcpy(page->data + page->data_size, &extkey, keylen);
610 }
611
612 page->data_size += keylen;
613
614 /* copy val or external val pointer into page data */
615 page->val[slot] = ASL_MSG_SLOT_FREE;
616
617 if (val != NULL)
618 {
619 if (extval == NULL)
620 {
621 page->val[slot] = page->data_size;
622 memcpy(page->data + page->data_size, val, vallen);
623 }
624 else
625 {
626 page->val[slot] = page->data_size | ASL_MSG_KV_EXTERN;
627 memcpy(page->data + page->data_size, &extval, vallen);
628 }
629
630 page->data_size += vallen;
631 }
632
633 /* set op */
634 page->op[slot] = op;
635
636 /* update page count */
637 page->count++;
638
639 return 0;
640 }
641
642 /*
643 * Most of the code in asl_msg_set_key_val_op is concerned with trying to re-use
644 * space in an asl_msg_t page when given a new value for an existing key.
645 * If the key is new, we just call _asl_msg_new_key_val_op.
646 *
647 * Note that queries can have duplicate keys, so for ASL_TYPE_QUERY we just
648 * call through to _asl_msg_new_key_val_op.
649 */
650 static int
651 _asl_msg_set_kvo(asl_msg_t *msg, const char *key, const char *val, uint32_t op)
652 {
653 uint32_t i, slot, newexternal;
654 asl_msg_t *page;
655 uint32_t intvallen, extvallen, newvallen;
656 char *intval, *extval, *newval;
657
658 if (msg == NULL) return -1;
659 if (key == NULL) return -1;
660
661 slot = IndexNull;
662 page = NULL;
663
664 if ((msg->type == ASL_TYPE_QUERY) || (IndexNull == _asl_msg_index(msg, key, &slot, &page)))
665 {
666 /* add key */
667 return _asl_msg_new_key_val_op(msg, key, val, op);
668 }
669
670 intval = NULL;
671 intvallen = 0;
672
673 extval = NULL;
674 extvallen = 0;
675
676 if (page->val[slot] != ASL_MSG_SLOT_FREE)
677 {
678 if ((page->val[slot] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
679 {
680 i = page->val[slot] & ASL_MSG_OFFSET_MASK;
681 memcpy(&extval, page->data + i, sizeof(char *));
682 extvallen = sizeof(char *);
683 }
684 else
685 {
686 intval = page->data + page->val[slot];
687 intvallen = strlen(intval) + 1;
688 }
689 }
690
691 /* replace val and op for existing entry */
692
693 /* easy case - remove val */
694 if (val == NULL)
695 {
696 if (extval != NULL) free(extval);
697 page->val[slot] = ASL_MSG_SLOT_FREE;
698 if (op != IndexNull) page->op[slot] = op;
699 return 0;
700 }
701
702 /* trivial case - internal val doesn't change */
703 if ((intval != NULL) && (streq(val, intval)))
704 {
705 if (op != IndexNull) page->op[slot] = op;
706 return 0;
707 }
708
709 /* trivial case - external val doesn't change */
710 if ((extval != NULL) && (streq(val, extval)))
711 {
712 if (op != IndexNull) page->op[slot] = op;
713 return 0;
714 }
715
716 /*
717 * special case: we generally don't compress out holes in the data
718 * space, but if this is the last string in the currently used data space
719 * we can just back up the data_size and reset page->val[slot]
720 */
721 i = page->val[slot] & ASL_MSG_OFFSET_MASK;
722 if ((intval != NULL) && ((i + intvallen) == page->data_size))
723 {
724 page->val[slot] = ASL_MSG_SLOT_FREE;
725 page->data_size -= intvallen;
726 intval = NULL;
727 intvallen = 0;
728 }
729 else if ((extval != NULL) && ((i + extvallen) == page->data_size))
730 {
731 page->val[slot] = ASL_MSG_SLOT_FREE;
732 page->data_size -= extvallen;
733 free(extval);
734 extval = NULL;
735 extvallen = 0;
736 }
737
738 /* val changes - see if it needs to be external */
739 newvallen = strlen(val) + 1;
740 newexternal = 0;
741
742 if (newvallen > ASL_MSG_PAGE_DATA_SIZE)
743 {
744 newexternal = 1;
745 newvallen = sizeof(char *);
746 }
747
748 /* check if there is room to change val in place */
749 if (((extval != NULL) && (newvallen <= extvallen)) || ((extval == NULL) && (newvallen <= intvallen)))
750 {
751 if (extval != NULL) free(extval);
752 extval = NULL;
753
754 /* we can re-use the space of the old value */
755 i = page->val[slot] & ASL_MSG_OFFSET_MASK;
756
757 if (newexternal == 1)
758 {
759 /* create an external val and copy in the new pointer */
760 newval = strdup(val);
761 if (newval == NULL) return -1;
762
763 page->val[slot] = i | ASL_MSG_KV_EXTERN;
764 memcpy(page->data + i, &newval, sizeof(char *));
765 }
766 else
767 {
768 /* new internal value */
769 page->val[slot] = i;
770 memcpy(page->data + i, val, newvallen);
771 }
772
773 if (op != IndexNull) page->op[slot] = op;
774 return 0;
775 }
776
777 /* we're done with the old value if it is external - free it now */
778 if (extval != NULL) free(extval);
779 extval = NULL;
780
781 if (newvallen <= (ASL_MSG_PAGE_DATA_SIZE - page->data_size))
782 {
783 /* can't re-use the old space, but there's room on the page */
784 i = page->data_size;
785 page->data_size += newvallen;
786
787 if (newexternal == 1)
788 {
789 /* create an external val and copy in the new pointer */
790 newval = strdup(val);
791 if (newval == NULL) return -1;
792
793 page->val[slot] = i | ASL_MSG_KV_EXTERN;
794 memcpy(page->data + i, &newval, sizeof(char *));
795 }
796 else
797 {
798 /* new internal value */
799 page->val[slot] = i;
800 memcpy(page->data + i, val, newvallen);
801 }
802
803 if (op != IndexNull) page->op[slot] = op;
804 return 0;
805
806 }
807
808 /* no room on this page - free up existing entry and treat this as a new entry */
809 if ((page->key[slot] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
810 {
811 memcpy(&extval, page->data + (page->key[slot] & ASL_MSG_OFFSET_MASK), sizeof(char *));
812 free(extval);
813 }
814
815 page->key[slot] = ASL_MSG_SLOT_FREE;
816 page->val[slot] = ASL_MSG_SLOT_FREE;
817 page->op[slot] = 0;
818
819 return _asl_msg_new_key_val_op(msg, key, val, op);
820 }
821
822 int
823 asl_msg_set_key_val_op(asl_msg_t *msg, const char *key, const char *val, uint32_t op)
824 {
825 char *special, buf[512];
826 uint32_t i, len;
827 int status;
828
829 /* Special case handling */
830 special = NULL;
831
832 /* if query modifier is set but op is zero, default to equality test */
833 if ((op != 0) && ((op & ASL_QUERY_OP_TRUE) == 0)) op |= ASL_QUERY_OP_EQUAL;
834
835 /* convert "Level" values to "0" through "7" */
836 if (streq(key, ASL_KEY_LEVEL))
837 {
838 if (val == NULL) val = "7";
839 else if ((val[0] >= '0') && (val[0] <= '7') && (val[1] == '\0')) /* do nothing */;
840 else if (strcaseeq(val, ASL_STRING_EMERG)) val = "0";
841 else if (strcaseeq(val, ASL_STRING_ALERT)) val = "1";
842 else if (strcaseeq(val, ASL_STRING_CRIT)) val = "2";
843 else if (strcaseeq(val, ASL_STRING_ERR)) val = "3";
844 else if (strcaseeq(val, ASL_STRING_WARNING)) val = "4";
845 else if (strcaseeq(val, ASL_STRING_NOTICE)) val = "5";
846 else if (strcaseeq(val, ASL_STRING_INFO)) val = "6";
847 else if (strcaseeq(val, ASL_STRING_DEBUG)) val = "7";
848 else val = "7";
849 }
850
851 /* strip trailing newlines from "Message" values */
852 if ((streq(key, ASL_KEY_MSG)) && (val != NULL))
853 {
854 len = strlen(val);
855 i = len;
856 while ((i > 0) && (val[i - 1] == '\n')) i--;
857 if (i == 0) val = NULL;
858 else if (i < len)
859 {
860 /* use buf if it is big enough, else malloc a temporary buffer */
861 if (i < sizeof(buf))
862 {
863 memcpy(buf, val, i);
864 buf[i] = 0;
865 val = (const char *)buf;
866 }
867 else
868 {
869 special = malloc(i + 1);
870 if (special == NULL) return -1;
871 memcpy(special, val, i);
872 special[i] = 0;
873 val = (const char *)special;
874 }
875 }
876 }
877
878 status = _asl_msg_set_kvo(msg, key, val, op);
879
880 if (special != NULL) free(special);
881 return status;
882 }
883
884 int
885 asl_msg_set_key_val(asl_msg_t *msg, const char *key, const char *val)
886 {
887 return asl_msg_set_key_val_op(msg, key, val, 0);
888 }
889
890 /*
891 * Merge a key / val into a message (only ASL_TYPE_MSG).
892 * Adds the key / val if the key is not found.
893 * Does not replace the value if the key is found.
894 */
895 static void
896 _asl_msg_merge_key_val_op(asl_msg_t *msg, const char *key, const char *val, uint32_t op)
897 {
898 uint32_t i, slot;
899 asl_msg_t *page;
900
901 if (msg == NULL) return;
902 if (key == NULL) return;
903
904 slot = IndexNull;
905 page = NULL;
906
907 i = _asl_msg_index(msg, key, &slot, &page);
908 if (i != IndexNull) return;
909
910 asl_msg_set_key_val_op(msg, key, val, op);
911 }
912
913 /*
914 * Merge msg into target (does not replace existing keys).
915 * Creates a new asl_msg_t if target is NULL.
916 * Returns target.
917 */
918 asl_msg_t *
919 asl_msg_merge(asl_msg_t *target, asl_msg_t *msg)
920 {
921 uint32_t x, slot, op, isnew = 0;
922 const char *key, *val;
923 asl_msg_t *page;
924
925 if (msg == NULL) return target;
926
927 if (target == NULL)
928 {
929 isnew = 1;
930 target = asl_msg_new(msg->type);
931 }
932
933 for (x = _asl_msg_fetch_internal(msg, 0, &key, &val, &op, &page, &slot); x != IndexNull; x = _asl_msg_fetch_internal(msg, x, &key, &val, &op, &page, &slot))
934 {
935 if (msg->type == ASL_TYPE_MSG) op = 0;
936 if (isnew == 1) asl_msg_set_key_val_op(target, key, val, op);
937 else _asl_msg_merge_key_val_op(target, key, val, op);
938 }
939
940 return target;
941 }
942
943 /*
944 * Copy msg.
945 */
946 asl_msg_t *
947 asl_msg_copy(asl_msg_t *msg)
948 {
949 return asl_msg_merge(NULL, msg);
950 }
951
952 /*
953 * asl_msg_unset
954 * Frees external key and val strings, but does not try to reclaim data space.
955 */
956 void
957 asl_msg_unset(asl_msg_t *msg, const char *key)
958 {
959 uint32_t i, slot;
960 asl_msg_t *page;
961 char *ext;
962
963 if (msg == NULL) return;
964 if (key == NULL) return;
965
966 slot = IndexNull;
967 page = NULL;
968
969 i = _asl_msg_index(msg, key, &slot, &page);
970 if (i == IndexNull) return;
971
972 if ((page->key[slot] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
973 {
974 memcpy(&ext, page->data + (page->key[slot] & ASL_MSG_OFFSET_MASK), sizeof(char *));
975 free(ext);
976 }
977
978 if ((page->val[slot] & ASL_MSG_KV_MASK) == ASL_MSG_KV_EXTERN)
979 {
980 memcpy(&ext, page->data + (page->val[slot] & ASL_MSG_OFFSET_MASK), sizeof(char *));
981 free(ext);
982 }
983
984 page->key[slot] = ASL_MSG_SLOT_FREE;
985 page->val[slot] = ASL_MSG_SLOT_FREE;
986 page->op[slot] = 0;
987
988 page->count--;
989 }
990
991 int
992 asl_msg_lookup(asl_msg_t *msg, const char *key, const char **valout, uint32_t *opout)
993 {
994 uint32_t i, slot;
995 asl_msg_t *page;
996
997 if (valout != NULL) *valout = NULL;
998 if (opout != NULL) *opout = 0;
999
1000 slot = IndexNull;
1001 page = NULL;
1002
1003 i = _asl_msg_index(msg, key, &slot, &page);
1004 if (i == IndexNull) return -1;
1005
1006 if (valout != NULL) *valout = _asl_msg_slot_val(page, slot);
1007 if (opout != NULL) *opout = page->op[slot];
1008
1009 return 0;
1010 }
1011
1012 uint32_t
1013 asl_msg_type(asl_msg_t *msg)
1014 {
1015 if (msg == NULL) return 0;
1016 return msg->type;
1017 }
1018
1019 uint32_t
1020 asl_msg_count(asl_msg_t *msg)
1021 {
1022 uint32_t total;
1023
1024 total = 0;
1025
1026 for (; msg != NULL; msg = msg->next) total += msg->count;
1027 return total;
1028 }
1029
1030 /*
1031 * Compare messages
1032 */
1033 static int
1034 _asl_msg_equal(asl_msg_t *a, asl_msg_t *b)
1035 {
1036 uint32_t x, oa, ob;
1037 const char *key, *va, *vb;
1038
1039 if (asl_msg_count(a) != asl_msg_count(b)) return 0;
1040
1041 key = NULL;
1042 va = NULL;
1043 oa = 0;
1044
1045 for (x = asl_msg_fetch(a, 0, &key, &va, &oa); x != IndexNull; x = asl_msg_fetch(a, x, &key, &va, &oa))
1046 {
1047 if (asl_msg_lookup(b, key, &vb, &ob) != 0) return 0;
1048 if (strcmp(va, vb)) return 0;
1049 if ((a->type == ASL_TYPE_QUERY) && (oa != ob)) return 0;
1050 }
1051
1052 return 1;
1053 }
1054
1055 static int
1056 _asl_isanumber(const char *s)
1057 {
1058 int i;
1059
1060 if (s == NULL) return 0;
1061
1062 i = 0;
1063 if ((s[0] == '-') || (s[0] == '+')) i = 1;
1064
1065 if (s[i] == '\0') return 0;
1066
1067 for (; s[i] != '\0'; i++)
1068 {
1069 if (!isdigit(s[i])) return 0;
1070 }
1071
1072 return 1;
1073 }
1074
1075 static int
1076 _asl_msg_basic_test(uint32_t op, const char *q, const char *m, uint32_t n)
1077 {
1078 int cmp;
1079 uint32_t t;
1080 int64_t nq, nm;
1081 int rflags;
1082 regex_t rex;
1083
1084 t = op & ASL_QUERY_OP_TRUE;
1085
1086 /* NULL value from query or message string fails */
1087 if ((q == NULL) || (m == NULL)) return (t & ASL_QUERY_OP_NOT_EQUAL);
1088
1089 if (op & ASL_QUERY_OP_REGEX)
1090 {
1091 /* greater than or less than make no sense in substring search */
1092 if ((t == ASL_QUERY_OP_GREATER) || (t == ASL_QUERY_OP_LESS)) return 0;
1093
1094 memset(&rex, 0, sizeof(regex_t));
1095
1096 rflags = REG_EXTENDED | REG_NOSUB;
1097 if (op & ASL_QUERY_OP_CASEFOLD) rflags |= REG_ICASE;
1098
1099 /* A bad reqular expression matches nothing */
1100 if (regcomp(&rex, q, rflags) != 0) return (t & ASL_QUERY_OP_NOT_EQUAL);
1101
1102 cmp = regexec(&rex, m, 0, NULL, 0);
1103 regfree(&rex);
1104
1105 if (t == ASL_QUERY_OP_NOT_EQUAL) return (cmp != 0);
1106 return (cmp == 0);
1107 }
1108
1109 if (op & ASL_QUERY_OP_NUMERIC)
1110 {
1111 if (_asl_isanumber(q) == 0) return (t == ASL_QUERY_OP_NOT_EQUAL);
1112 if (_asl_isanumber(m) == 0) return (t == ASL_QUERY_OP_NOT_EQUAL);
1113
1114 nq = atoll(q);
1115 nm = atoll(m);
1116
1117 switch (t)
1118 {
1119 case ASL_QUERY_OP_EQUAL: return (nm == nq);
1120 case ASL_QUERY_OP_GREATER: return (nm > nq);
1121 case ASL_QUERY_OP_GREATER_EQUAL: return (nm >= nq);
1122 case ASL_QUERY_OP_LESS: return (nm < nq);
1123 case ASL_QUERY_OP_LESS_EQUAL: return (nm <= nq);
1124 case ASL_QUERY_OP_NOT_EQUAL: return (nm != nq);
1125 default: return (t == ASL_QUERY_OP_NOT_EQUAL);
1126 }
1127 }
1128
1129 cmp = 0;
1130 if (op & ASL_QUERY_OP_CASEFOLD)
1131 {
1132 if (n == 0) cmp = strcasecmp(m, q);
1133 else cmp = strncasecmp(m, q, n);
1134 }
1135 else
1136 {
1137 if (n == 0) cmp = strcmp(m, q);
1138 else cmp = strncmp(m, q, n);
1139 }
1140
1141 switch (t)
1142 {
1143 case ASL_QUERY_OP_EQUAL: return (cmp == 0);
1144 case ASL_QUERY_OP_GREATER: return (cmp > 0);
1145 case ASL_QUERY_OP_GREATER_EQUAL: return (cmp >= 0);
1146 case ASL_QUERY_OP_LESS: return (cmp < 0);
1147 case ASL_QUERY_OP_LESS_EQUAL: return (cmp <= 0);
1148 case ASL_QUERY_OP_NOT_EQUAL: return (cmp != 0);
1149 }
1150
1151 return (t == ASL_QUERY_OP_NOT_EQUAL);
1152 }
1153
1154 static int
1155 _asl_msg_test_substring(uint32_t op, const char *q, const char *m)
1156 {
1157 uint32_t t, i, d, lm, lq, match, newop;
1158
1159 t = op & ASL_QUERY_OP_TRUE;
1160
1161 lm = 0;
1162 if (m != NULL) lm = strlen(m);
1163
1164 lq = 0;
1165 if (q != NULL) lq = strlen(q);
1166
1167 /* NULL is a substring of any string */
1168 if (lq == 0) return (t & ASL_QUERY_OP_EQUAL);
1169
1170 /* A long string is defined to be not equal to a short string */
1171 if (lq > lm) return (t == ASL_QUERY_OP_NOT_EQUAL);
1172
1173 /* greater than or less than make no sense in substring search */
1174 if ((t == ASL_QUERY_OP_GREATER) || (t == ASL_QUERY_OP_LESS)) return 0;
1175
1176 /*
1177 * We scan the string doing an equality test.
1178 * If the input test is equality, we stop as soon as we hit a match.
1179 * Otherwise we keep scanning the whole message string.
1180 */
1181 newop = op & 0xff0;
1182 newop |= ASL_QUERY_OP_EQUAL;
1183
1184 match = 0;
1185 d = lm - lq;
1186 for (i = 0; i <= d; i++)
1187 {
1188 if (_asl_msg_basic_test(newop, q, m + i, lq) != 0)
1189 {
1190 if (t & ASL_QUERY_OP_EQUAL) return 1;
1191 match++;
1192 }
1193 }
1194
1195 /* If the input test was for equality, no matches were found */
1196 if (t & ASL_QUERY_OP_EQUAL) return 0;
1197
1198 /* The input test was for not equal. Return true if no matches were found */
1199 return (match == 0);
1200 }
1201
1202 static int
1203 _asl_msg_test_prefix(uint32_t op, const char *q, const char *m)
1204 {
1205 uint32_t lm, lq, t;
1206
1207 t = op & ASL_QUERY_OP_TRUE;
1208
1209 lm = 0;
1210 if (m != NULL) lm = strlen(m);
1211
1212 lq = 0;
1213 if (q != NULL) lq = strlen(q);
1214
1215 /* NULL is a prefix of any string */
1216 if (lq == 0) return (t & ASL_QUERY_OP_EQUAL);
1217
1218 /* A long string is defined to be not equal to a short string */
1219 if (lq > lm) return (t == ASL_QUERY_OP_NOT_EQUAL);
1220
1221 /* Compare two equal-length strings */
1222 return _asl_msg_basic_test(op, q, m, lq);
1223 }
1224
1225 static int
1226 _asl_msg_test_suffix(uint32_t op, const char *q, const char *m)
1227 {
1228 uint32_t lm, lq, d, t;
1229
1230 t = op & ASL_QUERY_OP_TRUE;
1231
1232 lm = 0;
1233 if (m != NULL) lm = strlen(m);
1234
1235 lq = 0;
1236 if (q != NULL) lq = strlen(q);
1237
1238 /* NULL is a suffix of any string */
1239 if (lq == 0) return (t & ASL_QUERY_OP_EQUAL);
1240
1241 /* A long string is defined to be not equal to a short string */
1242 if (lq > lm) return (t == ASL_QUERY_OP_NOT_EQUAL);
1243
1244 /* Compare two equal-length strings */
1245 d = lm - lq;
1246 return _asl_msg_basic_test(op, q, m + d, lq);
1247 }
1248
1249 /*
1250 * Splits out prefix, suffix, and substring tests.
1251 * Sends the rest to _asl_msg_basic_test().
1252 */
1253 static int
1254 _asl_msg_test_expression(uint32_t op, const char *q, const char *m)
1255 {
1256 uint32_t t;
1257
1258 t = op & ASL_QUERY_OP_TRUE;
1259 if (t == ASL_QUERY_OP_TRUE) return 1;
1260
1261 if (op & ASL_QUERY_OP_PREFIX)
1262 {
1263 if (op & ASL_QUERY_OP_SUFFIX) return _asl_msg_test_substring(op, q, m);
1264 return _asl_msg_test_prefix(op, q, m);
1265 }
1266 if (op & ASL_QUERY_OP_SUFFIX) return _asl_msg_test_suffix(op, q, m);
1267
1268 return _asl_msg_basic_test(op, q, m, 0);
1269 }
1270
1271 /*
1272 * Special case for comparing time values.
1273 * If both inputs are time strings, this compares the time
1274 * value in seconds. Otherwise it just does normal matching.
1275 */
1276 static int
1277 _asl_msg_test_time_expression(uint32_t op, const char *q, const char *m)
1278 {
1279 time_t tq, tm;
1280 uint32_t t;
1281
1282 if ((op & ASL_QUERY_OP_PREFIX) || (op & ASL_QUERY_OP_SUFFIX) || (op & ASL_QUERY_OP_REGEX)) return _asl_msg_test_expression(op, q, m);
1283 if ((q == NULL) || (m == NULL)) return _asl_msg_test_expression(op, q, m);
1284
1285 tq = asl_parse_time(q);
1286 if (tq < 0) return _asl_msg_test_expression(op, q, m);
1287
1288 tm = asl_parse_time(m);
1289 if (tm < 0) return _asl_msg_test_expression(op, q, m);
1290
1291 t = op & ASL_QUERY_OP_TRUE;
1292
1293 switch (t)
1294 {
1295 case ASL_QUERY_OP_FALSE:
1296 {
1297 return 0;
1298 }
1299 case ASL_QUERY_OP_EQUAL:
1300 {
1301 if (tm == tq) return 1;
1302 return 0;
1303 }
1304 case ASL_QUERY_OP_GREATER:
1305 {
1306 if (tm > tq) return 1;
1307 return 0;
1308 }
1309 case ASL_QUERY_OP_GREATER_EQUAL:
1310 {
1311 if (tm >= tq) return 1;
1312 return 0;
1313 }
1314 case ASL_QUERY_OP_LESS:
1315 {
1316 if (tm < tq) return 1;
1317 return 0;
1318 }
1319 case ASL_QUERY_OP_LESS_EQUAL:
1320 {
1321 if (tm <= tq) return 1;
1322 return 0;
1323 }
1324 case ASL_QUERY_OP_NOT_EQUAL:
1325 {
1326 if (tm != tq) return 1;
1327 return 0;
1328 }
1329 case ASL_QUERY_OP_TRUE:
1330 {
1331 return 1;
1332 }
1333 }
1334
1335 /* NOTREACHED */
1336 return 0;
1337 }
1338
1339 /* test a query against a message */
1340 static int
1341 _asl_msg_test(asl_msg_t *q, asl_msg_t *m)
1342 {
1343 uint32_t i, t, x, op;
1344 int cmp;
1345 const char *kq, *vq, *vm;
1346
1347 /*
1348 * Check each simple expression (key op val) separately.
1349 * The query suceeds (returns 1) if all simple expressions
1350 * succeed (i.e. AND the simple expressions).
1351 */
1352
1353 kq = NULL;
1354 vq = NULL;
1355 op = 0;
1356
1357 for (x = asl_msg_fetch(q, 0, &kq, &vq, &op); x != IndexNull; x = asl_msg_fetch(q, x, &kq, &vq, &op))
1358 {
1359 /* Find query key in the message */
1360 vm = NULL;
1361 i = asl_msg_lookup(m, kq, &vm, NULL);
1362
1363 /* ASL_QUERY_OP_TRUE tests if key is present in the message */
1364 t = op & ASL_QUERY_OP_TRUE;
1365 if (t == ASL_QUERY_OP_TRUE)
1366 {
1367 if (i != 0) return 0;
1368 continue;
1369 }
1370
1371 /* ASL_QUERY_OP_FALSE tests if the key is NOT present in the message */
1372 if (t == ASL_QUERY_OP_FALSE)
1373 {
1374 if (i == 0) return 0;
1375 continue;
1376 }
1377
1378 if (i != 0)
1379 {
1380 /* the message does NOT have query key - fail unless we are testing not equal */
1381 if (t == ASL_QUERY_OP_NOT_EQUAL) continue;
1382 return 0;
1383 }
1384
1385 cmp = 1;
1386 if (streq(kq, ASL_KEY_TIME))
1387 {
1388 cmp = _asl_msg_test_time_expression(op, vq, vm);
1389 }
1390 else
1391 {
1392 cmp = _asl_msg_test_expression(op, vq, vm);
1393 }
1394
1395 if (cmp == 0) return 0;
1396 }
1397
1398 return 1;
1399 }
1400
1401 int
1402 asl_msg_cmp(asl_msg_t *a, asl_msg_t *b)
1403 {
1404
1405 if (a == NULL) return 0;
1406 if (b == NULL) return 0;
1407
1408 if (a->type == b->type) return _asl_msg_equal(a, b);
1409 if (a->type == ASL_TYPE_QUERY) return _asl_msg_test(a, b);
1410 return _asl_msg_test(b, a);
1411 }
1412
1413
1414 static char *
1415 _asl_time_string(const char *infmt, const char *str, const char *nano)
1416 {
1417 time_t tick, off;
1418 struct tm stm;
1419 char *ltime, *out, *p, *q;
1420 char ltbuf[32], nanobuf[16], fmt[32], zstr[8];
1421 time_t zh, zm;
1422 uint32_t subsec = 0;
1423 bool neg;
1424
1425 out = NULL;
1426 memset(zstr, 0, sizeof(zstr));
1427 zh = 0;
1428 zm = 0;
1429 off = 0;
1430 neg = false;
1431
1432 /*
1433 * The longest infmt string we understand is "-hh:mm.N" (8 chars), so
1434 * it is safe to ignore the input if it doesn't fit in the temp buffer.
1435 * The default is local time zone format.
1436 */
1437 if (infmt == NULL)
1438 {
1439 snprintf(fmt, sizeof(fmt), "local");
1440 }
1441 else if (strlen(infmt) >= sizeof (fmt))
1442 {
1443 snprintf(fmt, sizeof(fmt), "local");
1444 }
1445 else
1446 {
1447 snprintf(fmt, sizeof(fmt), "%s", infmt);
1448
1449 p = fmt;
1450 q = strchr(fmt, '.');
1451 if (q != NULL)
1452 {
1453 *q = '\0';
1454 q++;
1455 if (q != '\0') subsec = atoi(q);
1456 }
1457 }
1458
1459 nanobuf[0] = '\0';
1460
1461 if (subsec > 0)
1462 {
1463 uint32_t nsec = 0;
1464 if (nano != NULL) nsec = atoi(nano);
1465 snprintf(nanobuf, sizeof(nanobuf), ".%09u", nsec);
1466 if (subsec > 9) subsec = 9;
1467 nanobuf[subsec + 1] = '\0';
1468 }
1469
1470 tick = 0;
1471 if (str != NULL) tick = asl_parse_time(str);
1472
1473 if ((!strcasecmp(fmt, "lcl")) || (!strcasecmp(fmt, "local")))
1474 {
1475 ltime = ctime_r(&tick, ltbuf);
1476 if (ltime == NULL) return NULL;
1477 ltime[19] = '\0';
1478 asprintf(&out, "%s%s", ltime + 4, nanobuf);
1479 return out;
1480 }
1481
1482 if ((!strcasecmp(fmt, "jz")) || (!strcasecmp(fmt, "iso8601")) || (!strcasecmp(fmt, "iso8601e")))
1483 {
1484 char sep = ' ';
1485 if (!strncasecmp(fmt, "iso8601", 7)) sep = 'T';
1486
1487 if (NULL == localtime_r(&tick, &stm)) return NULL;
1488
1489 off = stm.tm_gmtoff;
1490 if ((neg = (off < 0))) off *= -1;
1491 zh = off / 3600;
1492 off %= 3600;
1493 zm = off / 60;
1494
1495 if (zm == 0) snprintf(zstr, sizeof(zstr), "%c%02ld", neg ? '-' : '+', zh);
1496 else snprintf(zstr, sizeof(zstr), "%c%02ld:%02ld", neg ? '-' : '+', zh, zm);
1497
1498 asprintf(&out, "%d-%02d-%02d%c%02d:%02d:%02d%s%s", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, sep, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf, zstr);
1499 return out;
1500 }
1501
1502 if (!strcasecmp(fmt, "iso8601b"))
1503 {
1504 if (NULL == localtime_r(&tick, &stm)) return NULL;
1505
1506 off = stm.tm_gmtoff;
1507 if ((neg = (off < 0))) off *= -1;
1508 zh = off / 3600;
1509 off %= 3600;
1510 zm = off / 60;
1511
1512 if (zm == 0) snprintf(zstr, sizeof(zstr), "%c%02ld", neg ? '-' : '+', zh);
1513 else snprintf(zstr, sizeof(zstr), "%c%02ld:%02ld", neg ? '-' : '+', zh, zm);
1514
1515 asprintf(&out, "%d%02d%02dT%02d%02d%02d%s%s", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf, zstr);
1516 return out;
1517 }
1518
1519 if ((!strcasecmp(fmt, "sec")) || (!strcasecmp(fmt, "raw")))
1520 {
1521 asprintf(&out, "%lu%s", tick, nanobuf);
1522 return out;
1523 }
1524
1525 if (!strcasecmp(fmt, "j"))
1526 {
1527 if (NULL == localtime_r(&tick, &stm)) return NULL;
1528 asprintf(&out, "%d-%02d-%02d %02d:%02d:%02d%s", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf);
1529 return out;
1530 }
1531
1532 if ((!strcasecmp(fmt, "utc")) || (!strcasecmp(fmt, "zulu")) || (!strcasecmp(fmt, "iso8601z")) || (!strcasecmp(fmt, "iso8601ez")))
1533 {
1534 char sep = ' ';
1535 if (!strncasecmp(fmt, "iso8601", 7)) sep = 'T';
1536
1537 if (NULL == gmtime_r(&tick, &stm)) return NULL;
1538 asprintf(&out, "%d-%02d-%02d%c%02d:%02d:%02d%sZ", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, sep, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf);
1539 return out;
1540 }
1541
1542 if (!strcasecmp(fmt, "iso8601bz"))
1543 {
1544 if (NULL == gmtime_r(&tick, &stm)) return NULL;
1545 asprintf(&out, "%d%02d%02dT%02d%02d%02d%sZ", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf);
1546 return out;
1547 }
1548
1549 if ((fmt[1] == '\0') && (((fmt[0] >= 'a') && (fmt[0] <= 'z')) || ((fmt[0] >= 'A') && (fmt[0] <= 'Z'))))
1550 {
1551 char z = fmt[0];
1552 if (z >= 'a') z -= 32;
1553
1554 if (z == 'Z') off = 0;
1555 else if ((z >= 'A') && (z <= 'I')) off = ((z - 'A') + 1) * SEC_PER_HOUR;
1556 else if ((z >= 'K') && (z <= 'M')) off = (z - 'A') * SEC_PER_HOUR;
1557 else if ((z >= 'N') && (z <= 'Y')) off = ('M' - z) * SEC_PER_HOUR;
1558 else return NULL;
1559 }
1560 else
1561 {
1562 if (fmt[0] == '-') neg = true;
1563 if ((*p == '-') || (*p == '+')) p++;
1564 if ((*p) >= '0' && (*p <= '9'))
1565 {
1566 zh = atoi(p);
1567
1568 p = strchr(p, ':');
1569 if (p != NULL) zm = atoi(p + 1);
1570 }
1571 else
1572 {
1573 return NULL;
1574 }
1575
1576 off = (zh * 3600) + (zm * 60);
1577 if (neg) off *= -1;
1578
1579 if (zm == 0) snprintf(zstr, sizeof(zstr), "%c%02ld", neg ? '-' : '+', zh);
1580 else snprintf(zstr, sizeof(zstr), "%c%02ld:%02ld", neg ? '-' : '+', zh, zm);
1581 }
1582
1583
1584 tick += off;
1585
1586 memset(&stm, 0, sizeof (struct tm));
1587 if (NULL == gmtime_r(&tick, &stm)) return NULL;
1588
1589 if ((fmt[0] >= 'A') && (fmt[0] <= 'Z'))
1590 {
1591 asprintf(&out, "%d-%02d-%02d %02d:%02d:%02d%s%c", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf, fmt[0]);
1592 }
1593 else if ((fmt[0] >= 'a') && (fmt[0] <= 'z'))
1594 {
1595 asprintf(&out, "%d-%02d-%02d %02d:%02d:%02d%s%c", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf, fmt[0] - 32);
1596 }
1597 else
1598 {
1599 asprintf(&out, "%d-%02d-%02d %02d:%02d:%02d%s%s", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, stm.tm_hour, stm.tm_min, stm.tm_sec, nanobuf, zstr);
1600 }
1601
1602 return out;
1603 }
1604
1605 /* called from asl_format_message and _asl_send_message */
1606 __private_extern__ asl_string_t *
1607 asl_msg_to_string_raw(uint32_t encoding, asl_msg_t *msg, const char *tfmt)
1608 {
1609 uint32_t i, x, count;
1610 const char *key, *val, *nano;
1611 asl_string_t *str;
1612
1613 if (msg == NULL) return NULL;
1614
1615 count = asl_msg_count(msg);
1616 if (count == 0) return NULL;
1617
1618 str = asl_string_new(encoding);
1619 if (str == NULL) return NULL;
1620
1621 key = NULL;
1622 val = NULL;
1623 i = 0;
1624
1625 nano = NULL;
1626 asl_msg_lookup(msg, ASL_KEY_TIME_NSEC, &nano, NULL);
1627
1628 for (x = asl_msg_fetch(msg, 0, &key, &val, NULL); x != IndexNull; x = asl_msg_fetch(msg, x, &key, &val, NULL))
1629 {
1630 if (key == NULL) continue;
1631
1632 if (i > 0) asl_string_append_char_no_encoding(str, ' ');
1633
1634 asl_string_append_char_no_encoding(str, '[');
1635 asl_string_append_asl_key(str, key);
1636
1637 if (!strcmp(key, ASL_KEY_TIME))
1638 {
1639 char *vtime = NULL;
1640 asl_string_append_char_no_encoding(str, ' ');
1641
1642 if (val != NULL) vtime = _asl_time_string(tfmt, val, nano);
1643
1644 if (vtime != NULL)
1645 {
1646 asl_string_append_no_encoding(str, vtime);
1647 free(vtime);
1648 }
1649 else
1650 {
1651 asl_string_append_char_no_encoding(str, '0');
1652 }
1653 }
1654 else if (val != NULL)
1655 {
1656 asl_string_append_char_no_encoding(str, ' ');
1657 asl_string_append(str, val);
1658 }
1659
1660 asl_string_append_char_no_encoding(str, ']');
1661
1662 i++;
1663 }
1664
1665 return str;
1666 }
1667
1668 static asl_string_t *
1669 _asl_string_append_asl_msg(asl_string_t *str, asl_msg_t *msg)
1670 {
1671 const char *key, *val;
1672 uint32_t i, op, n;
1673
1674 if (msg == NULL) return str;
1675
1676 if (msg->type == ASL_TYPE_QUERY) asl_string_append(str, "Q ");
1677
1678 i = 0;
1679 n = 0;
1680
1681 forever
1682 {
1683 key = NULL;
1684 val = NULL;
1685
1686 i = asl_msg_fetch(msg, i, &key, &val, &op);
1687 if (key != NULL)
1688 {
1689 if (n != 0) asl_string_append_char_no_encoding(str, ' ');
1690 n++;
1691
1692 asl_string_append_char_no_encoding(str, '[');
1693
1694 if (msg->type == ASL_TYPE_QUERY)
1695 {
1696 asl_string_append_op(str, op);
1697 asl_string_append_char_no_encoding(str, ' ');
1698 }
1699
1700 asl_string_append_asl_key(str, key);
1701
1702 if (val != NULL)
1703 {
1704 asl_string_append_char_no_encoding(str, ' ');
1705 asl_string_append(str, val);
1706 }
1707
1708 asl_string_append_char_no_encoding(str, ']');
1709 }
1710
1711 if (i == IndexNull) break;
1712 }
1713
1714 return str;
1715 }
1716
1717 char *
1718 asl_msg_to_string(asl_msg_t *msg, uint32_t *len)
1719 {
1720 char *out;
1721 asl_string_t *str = asl_string_new(ASL_ENCODE_ASL);
1722 if (str == NULL) return NULL;
1723
1724 str = _asl_string_append_asl_msg(str, msg);
1725 *len = asl_string_length(str);
1726 out = asl_string_free_return_bytes(str);
1727 return out;
1728 }
1729
1730 static uint32_t
1731 _asl_msg_op_from_string(char *o)
1732 {
1733 uint32_t op, i;
1734
1735 op = ASL_QUERY_OP_NULL;
1736
1737 if (o == NULL) return op;
1738
1739 for (i = 0; o[i] != '\0'; i++)
1740 {
1741 if (o[i] == '.') return ASL_QUERY_OP_NULL;
1742 if (o[i] == 'C') op |= ASL_QUERY_OP_CASEFOLD;
1743 if (o[i] == 'R') op |= ASL_QUERY_OP_REGEX;
1744 if (o[i] == 'N') op |= ASL_QUERY_OP_NUMERIC;
1745 if (o[i] == 'S') op |= ASL_QUERY_OP_SUBSTRING;
1746 if (o[i] == 'A') op |= ASL_QUERY_OP_PREFIX;
1747 if (o[i] == 'Z') op |= ASL_QUERY_OP_SUFFIX;
1748 if (o[i] == '<') op |= ASL_QUERY_OP_LESS;
1749 if (o[i] == '>') op |= ASL_QUERY_OP_GREATER;
1750 if (o[i] == '=') op |= ASL_QUERY_OP_EQUAL;
1751 if (o[i] == '!') op |= ASL_QUERY_OP_NOT_EQUAL;
1752 if (o[i] == 'T') op |= ASL_QUERY_OP_TRUE;
1753 }
1754
1755 return op;
1756 }
1757
1758 static char *
1759 _asl_msg_get_next_word(char **p, uint32_t *tt, uint32_t spacedel)
1760 {
1761 char *str, *out, c, oval;
1762 uint32_t i, len, n, outlen;
1763
1764 *tt = TOKEN_NULL;
1765
1766 if (p == NULL) return NULL;
1767 if (*p == NULL) return NULL;
1768 if (**p == '\0') return NULL;
1769
1770 /* skip one space if it's there (word separator) */
1771 if (**p == ' ') (*p)++;
1772
1773 /* skip leading white space */
1774 if (spacedel != 0)
1775 {
1776 while ((**p == ' ') || (**p == '\t')) (*p)++;
1777 }
1778
1779 if (**p == '\0') return NULL;
1780 if (**p == '\n') return NULL;
1781
1782 str = *p;
1783
1784 /* opening [ */
1785 if (**p == '[')
1786 {
1787 *tt = TOKEN_OPEN;
1788
1789 (*p)++;
1790 out = malloc(2);
1791 if (out == NULL) return NULL;
1792
1793 out[0] = '[';
1794 out[1] = '\0';
1795 return out;
1796 }
1797
1798 /* scan for token and calulate it's length (input and decoded output len) */
1799 len = 0;
1800 outlen = 0;
1801
1802 forever
1803 {
1804 c = str[len];
1805
1806 /* stop scanning when we hit a delimiter */
1807 if (((spacedel != 0) && (c == ' ')) || (c == ']') || (c == '\0')) break;
1808
1809 if (c == '\\')
1810 {
1811 len++;
1812 c = str[len];
1813 if ((c == 'a') || (c == 'b') || (c == 't') || (c == 'n') || (c == 'v') || (c == 'f') || (c == 'r') || (c == 's') || (c == '[') || (c == '\\') || (c == ']'))
1814 {
1815 }
1816 else if (c == '^')
1817 {
1818 if (str[++len] == '\0') return NULL;
1819 }
1820 else if (c == 'M')
1821 {
1822 if (str[++len] == '\0') return NULL;
1823 if (str[++len] == '\0') return NULL;
1824 }
1825 else if ((c >= '0') && (c <= '3'))
1826 {
1827 if (str[++len] == '\0') return NULL;
1828 if (str[++len] == '\0') return NULL;
1829 }
1830 else
1831 {
1832 return NULL;
1833 }
1834 }
1835
1836 len++;
1837 outlen++;
1838 }
1839
1840 (*p) += len;
1841
1842 if ((len == 0) && (**p == ']'))
1843 {
1844 *tt = TOKEN_CLOSE;
1845 (*p)++;
1846 out = malloc(2);
1847 if (out == NULL) return NULL;
1848
1849 out[0] = ']';
1850 out[1] = '\0';
1851 return out;
1852 }
1853
1854 *tt = TOKEN_INT;
1855
1856 out = malloc(outlen + 1);
1857 if (out == NULL) return NULL;
1858
1859 n = 0;
1860 for (i = 0; i < len; i++)
1861 {
1862 c = str[i];
1863
1864 if (c == '\\')
1865 {
1866 *tt = TOKEN_WORD;
1867
1868 i++;
1869 c = str[i];
1870 if (c == 'a')
1871 {
1872 out[n++] = '\a';
1873 }
1874 else if (c == 'b')
1875 {
1876 out[n++] = '\b';
1877 }
1878 else if (c == 't')
1879 {
1880 out[n++] = '\t';
1881 }
1882 else if (c == 'n')
1883 {
1884 out[n++] = '\n';
1885 }
1886 else if (c == 'v')
1887 {
1888 out[n++] = '\v';
1889 }
1890 else if (c == 'f')
1891 {
1892 out[n++] = '\f';
1893 }
1894 else if (c == 'r')
1895 {
1896 out[n++] = '\r';
1897 }
1898 else if (c == 's')
1899 {
1900 out[n++] = ' ';
1901 }
1902 else if (c == '[')
1903 {
1904 out[n++] = '[';
1905 }
1906 else if (c == '\\')
1907 {
1908 out[n++] = '\\';
1909 }
1910 else if (c == ']')
1911 {
1912 out[n++] = ']';
1913 }
1914 else if (c == '^')
1915 {
1916 i++;
1917 if (str[i] == '?') out[n++] = 127;
1918 else out[n++] = str[i] - 64;
1919 }
1920 else if (c == 'M')
1921 {
1922 i++;
1923 c = str[i];
1924 if (c == '^')
1925 {
1926 i++;
1927 if (str[i] == '?') out[n++] = 255;
1928 else out[n++] = str[i] + 64;
1929 }
1930 else if (c == '-')
1931 {
1932 i++;
1933 out[n++] = str[i] + 128;
1934 }
1935 else
1936 {
1937 *tt = TOKEN_NULL;
1938 free(out);
1939 return NULL;
1940 }
1941
1942 }
1943 else if ((c >= '0') && (c <= '3'))
1944 {
1945 oval = (c - '0') * 64;
1946
1947 i++;
1948 c = str[i];
1949 if ((c < '0') || (c > '7'))
1950 {
1951 *tt = TOKEN_NULL;
1952 free(out);
1953 return NULL;
1954 }
1955
1956 oval += ((c - '0') * 8);
1957
1958 i++;
1959 c = str[i];
1960 if ((c < '0') || (c > '7'))
1961 {
1962 *tt = TOKEN_NULL;
1963 free(out);
1964 return NULL;
1965 }
1966
1967 oval += (c - '0');
1968
1969 out[n++] = oval;
1970 }
1971 else
1972 {
1973 *tt = TOKEN_NULL;
1974 free(out);
1975 return NULL;
1976 }
1977 }
1978 else
1979 {
1980
1981 if ((c < '0') || (c > '9')) *tt = TOKEN_WORD;
1982 out[n++] = c;
1983 }
1984 }
1985
1986 out[n] = '\0';
1987
1988 return out;
1989 }
1990
1991 asl_msg_t *
1992 asl_msg_from_string(const char *buf)
1993 {
1994 uint32_t tt, type, op;
1995 char *k, *v, *o, *p;
1996 asl_msg_t *out;
1997
1998 if (buf == NULL) return NULL;
1999
2000 type = ASL_TYPE_MSG;
2001 p = (char *)buf;
2002
2003 k = _asl_msg_get_next_word(&p, &tt, 1);
2004 if (k == NULL) return NULL;
2005
2006 if (streq(k, "Q"))
2007 {
2008 type = ASL_TYPE_QUERY;
2009 free(k);
2010
2011 k = _asl_msg_get_next_word(&p, &tt, 1);
2012 }
2013 else if (tt == TOKEN_INT)
2014 {
2015 /* Leading integer is a string length - skip it */
2016 free(k);
2017 k = _asl_msg_get_next_word(&p, &tt, 1);
2018 if (k == NULL) return NULL;
2019 }
2020
2021 out = asl_msg_new(ASL_TYPE_MSG);
2022 if (out == NULL) return NULL;
2023
2024 out->type = type;
2025
2026 /* OPEN WORD [WORD [WORD]] CLOSE */
2027 while (k != NULL)
2028 {
2029 op = ASL_QUERY_OP_NULL;
2030
2031 if (tt != TOKEN_OPEN)
2032 {
2033 asl_msg_release(out);
2034 return NULL;
2035 }
2036
2037 free(k);
2038
2039 /* get op for query type */
2040 if (type == ASL_TYPE_QUERY)
2041 {
2042 o = _asl_msg_get_next_word(&p, &tt, 1);
2043 if ((o == NULL) || (tt != TOKEN_WORD))
2044 {
2045 if (o != NULL) free(o);
2046 asl_msg_release(out);
2047 return NULL;
2048 }
2049
2050 op = _asl_msg_op_from_string(o);
2051 free(o);
2052 }
2053
2054 k = _asl_msg_get_next_word(&p, &tt, 1);
2055 if (tt == TOKEN_INT) tt = TOKEN_WORD;
2056 if ((k == NULL) || (tt != TOKEN_WORD))
2057 {
2058 if (k != NULL) free(k);
2059 asl_msg_release(out);
2060 return NULL;
2061 }
2062
2063 v = _asl_msg_get_next_word(&p, &tt, 0);
2064 if (tt == TOKEN_INT) tt = TOKEN_WORD;
2065 if (v == NULL)
2066 {
2067 asl_msg_set_key_val_op(out, k, NULL, op);
2068 free(k);
2069 break;
2070 }
2071
2072 if (tt == TOKEN_CLOSE)
2073 {
2074 asl_msg_set_key_val_op(out, k, NULL, op);
2075 }
2076 else if (tt == TOKEN_WORD)
2077 {
2078 asl_msg_set_key_val_op(out, k, v, op);
2079 }
2080 else
2081 {
2082 if (k != NULL) free(k);
2083 if (v != NULL) free(v);
2084 asl_msg_release(out);
2085 return NULL;
2086 }
2087
2088 if (k != NULL) free(k);
2089 if (v != NULL) free(v);
2090
2091 if (tt != TOKEN_CLOSE)
2092 {
2093 k = _asl_msg_get_next_word(&p, &tt, 1);
2094 if (k == NULL) break;
2095
2096 if (tt != TOKEN_CLOSE)
2097 {
2098 asl_msg_release(out);
2099 return NULL;
2100 }
2101
2102 free(k);
2103 }
2104
2105 k = _asl_msg_get_next_word(&p, &tt, 1);
2106 if (k == NULL) break;
2107 }
2108
2109 return out;
2110 }
2111
2112 char *
2113 asl_list_to_string(asl_search_result_t *list, uint32_t *len)
2114 {
2115 uint32_t i;
2116 char tmp[16];
2117 char *out;
2118 asl_string_t *str;
2119
2120 if (list == NULL) return NULL;
2121 if (list->count == 0) return NULL;
2122 if (list->msg == NULL) return NULL;
2123
2124 str = asl_string_new(ASL_ENCODE_ASL);
2125 if (str == NULL) return NULL;
2126
2127 snprintf(tmp, sizeof(tmp), "%u", list->count);
2128 asl_string_append(str, tmp);
2129 asl_string_append_char_no_encoding(str, '\n');
2130
2131 for (i = 0; i < list->count; i++)
2132 {
2133 _asl_string_append_asl_msg(str, list->msg[i]);
2134 asl_string_append_char_no_encoding(str, '\n');
2135 }
2136
2137 *len = asl_string_length(str);
2138 out = asl_string_free_return_bytes(str);
2139 return out;
2140 }
2141
2142 asl_search_result_t *
2143 asl_list_from_string(const char *buf)
2144 {
2145 uint32_t i, n;
2146 const char *p;
2147 asl_search_result_t *out;
2148 asl_msg_t *m;
2149
2150 if (buf == NULL) return NULL;
2151 p = buf;
2152
2153 n = atoi(buf);
2154 if (n == 0) return NULL;
2155
2156 out = (asl_search_result_t *)calloc(1, sizeof(asl_search_result_t));
2157 if (out == NULL) return NULL;
2158
2159 out->msg = (asl_msg_t **)calloc(n, sizeof(asl_msg_t *));
2160 if (out->msg == NULL)
2161 {
2162 free(out);
2163 return NULL;
2164 }
2165
2166 for (i = 0; i < n; i++)
2167 {
2168 p = strchr(p, '\n');
2169 if (p == NULL)
2170 {
2171 aslresponse_free((aslresponse)out);
2172 return NULL;
2173 }
2174
2175 p++;
2176
2177 m = asl_msg_from_string(p);
2178 if (m == NULL)
2179 {
2180 aslresponse_free((aslresponse)out);
2181 return NULL;
2182 }
2183
2184 out->msg[i] = (asl_msg_t *)m;
2185 out->count += 1;
2186 }
2187
2188 return out;
2189 }
2190
2191 static const char *
2192 _asl_level_string(int level)
2193 {
2194 if (level == ASL_LEVEL_EMERG) return ASL_STRING_EMERG;
2195 if (level == ASL_LEVEL_ALERT) return ASL_STRING_ALERT;
2196 if (level == ASL_LEVEL_CRIT) return ASL_STRING_CRIT;
2197 if (level == ASL_LEVEL_ERR) return ASL_STRING_ERR;
2198 if (level == ASL_LEVEL_WARNING) return ASL_STRING_WARNING;
2199 if (level == ASL_LEVEL_NOTICE) return ASL_STRING_NOTICE;
2200 if (level == ASL_LEVEL_INFO) return ASL_STRING_INFO;
2201 if (level == ASL_LEVEL_DEBUG) return ASL_STRING_DEBUG;
2202 return "unknown";
2203 }
2204
2205 /*
2206 * Find the value for a key in a message and append a formatted value to str.
2207 * kf may be a simple (no embedded white space) key, or one of (key) or ((key)(format)).
2208 * WARNING: modifies kf!
2209 */
2210 static asl_string_t *
2211 _asl_string_append_value_for_key_format(asl_string_t *str, asl_msg_t *msg, char *kf, const char *tfmt)
2212 {
2213 uint32_t i, get_fmt;
2214 int status;
2215 char *key, *fmt;
2216 const char *mval, *nano;
2217
2218 if (str == NULL) return NULL;
2219 if (msg == NULL) return str;
2220 if (kf == NULL) return str;
2221
2222 key = NULL;
2223 fmt = NULL;
2224 get_fmt = 0;
2225
2226 for (i = 0; kf[i] != '\0'; i++)
2227 {
2228 if (kf[i] == ')')
2229 {
2230 kf[i] = '\0';
2231 get_fmt = 1;
2232 }
2233 else if (kf[i] != '(')
2234 {
2235 if (key == NULL) key = kf + i;
2236 else if ((get_fmt == 1) && (fmt == NULL)) fmt = kf + i;
2237 }
2238 }
2239
2240 if (key == NULL) return str;
2241
2242 nano = NULL;
2243 asl_msg_lookup(msg, ASL_KEY_TIME_NSEC, &nano, NULL);
2244
2245 status = asl_msg_lookup(msg, key, &mval, NULL);
2246 if ((status != 0) || (mval == NULL)) return str;
2247
2248 if (!strcmp(key, ASL_KEY_TIME))
2249 {
2250 char *fval = NULL;
2251
2252 /* format in $((Time)(fmt)) overrides tfmt */
2253 if (fmt == NULL)
2254 {
2255 fval = _asl_time_string(tfmt, mval, nano);
2256 }
2257 else
2258 {
2259 fval = _asl_time_string(fmt, mval, nano);
2260 }
2261
2262 if (fval != NULL)
2263 {
2264 asl_string_append_no_encoding(str, fval);
2265 free(fval);
2266 }
2267 else
2268 {
2269 asl_string_append_char_no_encoding(str, '0');
2270 }
2271
2272 return str;
2273 }
2274
2275 /* Level: num str */
2276 if (!strcmp(key, ASL_KEY_LEVEL))
2277 {
2278 if (fmt == NULL)
2279 {
2280 asl_string_append_no_encoding(str, mval);
2281 }
2282 else if (!strcmp(fmt, "str"))
2283 {
2284 mval = _asl_level_string(atoi(mval));
2285 asl_string_append_no_encoding(str, mval);
2286 }
2287 else
2288 {
2289 asl_string_append_no_encoding(str, mval);
2290 }
2291
2292 return str;
2293 }
2294
2295 return asl_string_append(str, mval);
2296 }
2297
2298 /*
2299 * format a message for printing
2300 * out parameter len returns string length including trailing NUL
2301 */
2302 char *
2303 asl_format_message(asl_msg_t *msg, const char *mfmt, const char *tfmt, uint32_t text_encoding, uint32_t *len)
2304 {
2305 char *out, *vtime, *k, c, skey[512], tfmt_ext[16];
2306 const char *vhost, *vpid, *vsender, *vmessage, *vlevel, *vrefproc, *vrefpid, *v, *key, *val, *nano;
2307 int i, j, l, mf, paren, oval, level;
2308 uint32_t x, cursor;
2309 asl_string_t *str;
2310 uint8_t *b64;
2311
2312 out = NULL;
2313 *len = 0;
2314
2315 if (msg == NULL) return NULL;
2316
2317 mf = MFMT_RAW;
2318
2319 if (mfmt == NULL) mf = MFMT_RAW;
2320 else if (!strcmp(mfmt, ASL_MSG_FMT_RAW)) mf = MFMT_RAW;
2321 else if (!strcmp(mfmt, ASL_MSG_FMT_STD)) mf = MFMT_STD;
2322 else if (!strcmp(mfmt, ASL_MSG_FMT_BSD)) mf = MFMT_BSD;
2323 else if (!strcmp(mfmt, ASL_MSG_FMT_XML)) mf = MFMT_XML;
2324 else if (!strcmp(mfmt, ASL_MSG_FMT_MSG)) mf = MFMT_MSG;
2325 else if ((!strncmp(mfmt, ASL_MSG_FMT_RAW, 3)) && (mfmt[3] == '.'))
2326 {
2327 mf = MFMT_RAW;
2328 if ((tfmt == NULL) && (mfmt[4] != '\0'))
2329 {
2330 snprintf(tfmt_ext, sizeof(tfmt_ext), "sec.%s", mfmt + 4);
2331 tfmt = (const char *)tfmt_ext;
2332 }
2333 }
2334 else if ((!strncmp(mfmt, ASL_MSG_FMT_STD, 3)) && (mfmt[3] == '.'))
2335 {
2336 mf = MFMT_STD;
2337 if ((tfmt == NULL) && (mfmt[4] != '\0'))
2338 {
2339 snprintf(tfmt_ext, sizeof(tfmt_ext), "lcl.%s", mfmt + 4);
2340 tfmt = (const char *)tfmt_ext;
2341 }
2342 }
2343 else if ((!strncmp(mfmt, ASL_MSG_FMT_BSD, 3)) && (mfmt[3] == '.'))
2344 {
2345 mf = MFMT_BSD;
2346 if ((tfmt == NULL) && (mfmt[4] != '\0'))
2347 {
2348 snprintf(tfmt_ext, sizeof(tfmt_ext), "lcl.%s", mfmt + 4);
2349 tfmt = (const char *)tfmt_ext;
2350 }
2351 }
2352 else mf = MFMT_STR;
2353
2354 nano = NULL;
2355 asl_msg_lookup(msg, ASL_KEY_TIME_NSEC, &nano, NULL);
2356
2357 if (mf == MFMT_RAW)
2358 {
2359 str = asl_msg_to_string_raw(text_encoding, msg, tfmt);
2360 asl_string_append_char_no_encoding(str, '\n');
2361
2362 *len = asl_string_length(str);
2363 out = asl_string_free_return_bytes(str);
2364 return out;
2365 }
2366
2367 if (mf == MFMT_MSG)
2368 {
2369 vmessage = NULL;
2370
2371 if (asl_msg_lookup(msg, ASL_KEY_MSG, &vmessage, NULL) != 0) return NULL;
2372
2373 str = asl_string_new(text_encoding);
2374 if (str == NULL) return NULL;
2375
2376 asl_string_append(str, vmessage);
2377 asl_string_append_char_no_encoding(str, '\n');
2378
2379 *len = asl_string_length(str);
2380 out = asl_string_free_return_bytes(str);
2381 return out;
2382 }
2383
2384 if ((mf == MFMT_STD) || (mf == MFMT_BSD))
2385 {
2386 /* COMMON: Mth dd hh:mm:ss host sender[pid] (refproc[refpid])*/
2387 /* BSD: <COMMON>: message */
2388 /* STD: <COMMON> <Level>: message */
2389
2390 v = NULL;
2391 vtime = NULL;
2392 vhost = NULL;
2393 vsender = NULL;
2394 vpid = NULL;
2395 vmessage = NULL;
2396 vlevel = NULL;
2397 vrefproc = NULL;
2398 vrefpid = NULL;
2399
2400 if (asl_msg_lookup(msg, ASL_KEY_TIME, &v, NULL) == 0)
2401 {
2402 vtime = _asl_time_string(tfmt, v, nano);
2403 }
2404
2405 level = 7;
2406 if (asl_msg_lookup(msg, ASL_KEY_LEVEL, &vlevel, NULL) == 0)
2407 {
2408 if (vlevel != NULL) level = atoi(vlevel);
2409 }
2410
2411 if (asl_msg_lookup(msg, ASL_KEY_HOST, &vhost, NULL) == 0)
2412 {
2413 if (vhost == NULL) vhost = "unknown";
2414 }
2415
2416 if (asl_msg_lookup(msg, ASL_KEY_SENDER, &vsender, NULL) == 0)
2417 {
2418 if (vsender == NULL) vsender = "unknown";
2419 }
2420
2421 asl_msg_lookup(msg, ASL_KEY_PID, &vpid, NULL);
2422 asl_msg_lookup(msg, ASL_KEY_MSG, &vmessage, NULL);
2423 asl_msg_lookup(msg, ASL_KEY_REF_PROC, &vrefproc, NULL);
2424 asl_msg_lookup(msg, ASL_KEY_REF_PID, &vrefpid, NULL);
2425
2426 /* COMMON */
2427 str = asl_string_new(text_encoding);
2428 if (str == NULL) return NULL;
2429
2430 if (vtime != NULL)
2431 {
2432 asl_string_append(str, vtime);
2433 free(vtime);
2434 }
2435 else
2436 {
2437 asl_string_append_char_no_encoding(str, '0');
2438 }
2439
2440 asl_string_append_char_no_encoding(str, ' ');
2441 asl_string_append(str, vhost);
2442 asl_string_append_char_no_encoding(str, ' ');
2443 asl_string_append(str, vsender);
2444
2445 if ((vpid != NULL) && (strcmp(vpid, "-1")))
2446 {
2447 asl_string_append_char_no_encoding(str, '[');
2448 asl_string_append(str, vpid);
2449 asl_string_append_char_no_encoding(str, ']');
2450 }
2451
2452 if ((vrefproc != NULL) || (vrefpid != NULL)) asl_string_append_no_encoding(str, " (");
2453
2454 if (vrefproc != NULL) asl_string_append(str, vrefproc);
2455 if (vrefpid != NULL)
2456 {
2457 asl_string_append_char_no_encoding(str, '[');
2458 asl_string_append(str, vrefpid);
2459 asl_string_append_char_no_encoding(str, ']');
2460 }
2461
2462 if ((vrefproc != NULL) || (vrefpid != NULL)) asl_string_append_char_no_encoding(str, ')');
2463
2464 if (mf == MFMT_STD)
2465 {
2466 asl_string_append_no_encoding(str, " <");
2467 asl_string_append(str, _asl_level_string(level));
2468 asl_string_append_char_no_encoding(str, '>');
2469 }
2470
2471 asl_string_append_no_encoding(str, ": ");
2472 if (vmessage != NULL) asl_string_append(str, vmessage);
2473 asl_string_append_char_no_encoding(str, '\n');
2474
2475 *len = asl_string_length(str);
2476 out = asl_string_free_return_bytes(str);
2477 return out;
2478 }
2479
2480 if (mf == MFMT_XML)
2481 {
2482 str = asl_string_new(text_encoding);
2483 if (str == NULL) return NULL;
2484
2485 asl_string_append_char_no_encoding(str, '\t');
2486 asl_string_append_no_encoding(str, "<dict>");
2487 asl_string_append_char_no_encoding(str, '\n');
2488
2489 for (x = asl_msg_fetch(msg, 0, &key, &val, NULL); x != IndexNull; x = asl_msg_fetch(msg, x, &key, &val, NULL))
2490 {
2491 if (asl_is_utf8(key) == 1)
2492 {
2493 asl_string_append_xml_tag(str, "key", key);
2494 if (!strcmp(key, ASL_KEY_TIME))
2495 {
2496 vtime = _asl_time_string(tfmt, val, nano);
2497 if (vtime != NULL)
2498 {
2499 asl_string_append_xml_tag(str, "string", vtime);
2500 free(vtime);
2501 }
2502 else
2503 {
2504 asl_string_append_xml_tag(str, "string", "0");
2505 }
2506 }
2507 else
2508 {
2509 if (asl_is_utf8(val) == 1) asl_string_append_xml_tag(str, "string", val);
2510 else
2511 {
2512 b64 = asl_b64_encode((uint8_t *)val, strlen(val));
2513 asl_string_append_xml_tag(str, "data", (char *)b64);
2514 free(b64);
2515 }
2516 }
2517 }
2518 }
2519
2520 asl_string_append_char_no_encoding(str, '\t');
2521 asl_string_append_no_encoding(str, "</dict>");
2522 asl_string_append_char_no_encoding(str, '\n');
2523
2524 *len = asl_string_length(str);
2525 out = asl_string_free_return_bytes(str);
2526 return out;
2527 }
2528
2529 /*
2530 * Custom format
2531 * The format string may contain arbitrary characters.
2532 * Keys are identified by $Key or $(Key). The value for
2533 * that key is substituted. If there are alterate formats
2534 * for the value (for example a time may be formatted as
2535 * raw seconds, in UTC, or a local timezone), then the
2536 * key may be $((Key)(Format)). "\$" prints a plain "$".
2537 */
2538
2539 str = asl_string_new(text_encoding);
2540 if (str == NULL) return NULL;
2541
2542 /*
2543 * We need enough space to copy any keys found in mfmt.
2544 * The key obviously can't be longer than strlen(mfmt),
2545 * in fact, keys must be shorter, since there's at least a '$'
2546 * in front of the key, so we allocate a buffer with strlen(mfmt).
2547 * If strlen(mfmt) <= sizeof(skey), we use skey to avoid a malloc.
2548 */
2549
2550 x = strlen(mfmt);
2551 if (x <= sizeof(skey))
2552 {
2553 k = skey;
2554 }
2555 else
2556 {
2557 k = malloc(x);
2558 if (k == NULL) return NULL;
2559 }
2560
2561 cursor = 0;
2562
2563 for (i = 0; mfmt[i] != '\0'; i++)
2564 {
2565 if (mfmt[i] == '$')
2566 {
2567 paren = 0;
2568
2569 /* scan key, (key) or ((key)(format)) */
2570 for (j = i + 1; mfmt[j] != 0; j++)
2571 {
2572 if (mfmt[j] == '(')
2573 {
2574 paren++;
2575 }
2576 else if (mfmt[j] == ')')
2577 {
2578 if (paren > 0) paren--;
2579 if (paren == 0)
2580 {
2581 j++;
2582 break;
2583 }
2584 }
2585 else if (((mfmt[j] == ' ') || (mfmt[j] == '\t')) && (paren == 0)) break;
2586 }
2587
2588 /* mfmt[i + 1] is the first char of the key or a '('. mfmt[j] is one char past the end. */
2589 l = j - (i + 1);
2590 memcpy(k, mfmt+i+1, l);
2591 k[l] = '\0';
2592 _asl_string_append_value_for_key_format(str, msg, k, tfmt);
2593
2594 i = j - 1;
2595 continue;
2596 }
2597
2598 if (mfmt[i] == '\\')
2599 {
2600 i++;
2601 if (mfmt[i] == '$') asl_string_append_char_no_encoding(str, '$');
2602 else if (mfmt[i] == 'e') asl_string_append_char_no_encoding(str, '\e');
2603 else if (mfmt[i] == 's') asl_string_append_char_no_encoding(str, ' ');
2604 else if (mfmt[i] == 'a') asl_string_append_char_no_encoding(str, '\a');
2605 else if (mfmt[i] == 'b') asl_string_append_char_no_encoding(str, '\b');
2606 else if (mfmt[i] == 'f') asl_string_append_char_no_encoding(str, '\f');
2607 else if (mfmt[i] == 'n') asl_string_append_char_no_encoding(str, '\n');
2608 else if (mfmt[i] == 'r') asl_string_append_char_no_encoding(str, '\r');
2609 else if (mfmt[i] == 't') asl_string_append_char_no_encoding(str, '\t');
2610 else if (mfmt[i] == 'v') asl_string_append_char_no_encoding(str, '\v');
2611 else if (mfmt[i] == '\'') asl_string_append_char_no_encoding(str, '\'');
2612 else if (mfmt[i] == '\\') asl_string_append_char_no_encoding(str, '\\');
2613 else if (isdigit(mfmt[i]))
2614 {
2615 oval = mfmt[i] - '0';
2616 if (isdigit(mfmt[i+1]))
2617 {
2618 i++;
2619 oval = (oval * 8) + (mfmt[i] - '0');
2620 if (isdigit(mfmt[i+1]))
2621 {
2622 i++;
2623 oval = (oval * 8) + (mfmt[i] - '0');
2624 }
2625 }
2626 c = oval;
2627 asl_string_append_char_no_encoding(str, c);
2628 }
2629 continue;
2630 }
2631
2632 if (mfmt[i] == '\0') break;
2633 asl_string_append_char_no_encoding(str, mfmt[i]);
2634 }
2635
2636 if (k != skey) free(k);
2637
2638 asl_string_append_char_no_encoding(str, '\n');
2639
2640 *len = asl_string_length(str);
2641 out = asl_string_free_return_bytes(str);
2642 return out;
2643 }
2644
2645 /*
2646 * OLD ASLMSG COMPATIBILITY
2647 */
2648 const char *
2649 asl_key(aslmsg msg, uint32_t n)
2650 {
2651 uint32_t slot, i;
2652 asl_msg_t *page;
2653
2654 i = 0;
2655 for (page = (asl_msg_t *)msg; page != NULL; page = page->next)
2656 {
2657 for (slot = 0; slot < ASL_MSG_PAGE_SLOTS; slot++)
2658 {
2659 if (page->key[slot] != ASL_MSG_SLOT_FREE)
2660 {
2661 if (i == n) return _asl_msg_slot_key(page, slot);
2662 i++;
2663 }
2664 }
2665 }
2666
2667 return NULL;
2668 }
2669
2670 aslmsg
2671 asl_new(uint32_t type)
2672 {
2673 return (aslmsg)asl_msg_new(type);
2674 }
2675
2676 int
2677 asl_set(aslmsg msg, const char *key, const char *value)
2678 {
2679 return asl_msg_set_key_val_op((asl_msg_t *)msg, key, value, IndexNull);
2680 }
2681
2682 int
2683 asl_set_query(aslmsg msg, const char *key, const char *value, uint32_t op)
2684 {
2685 return asl_msg_set_key_val_op((asl_msg_t *)msg, key, value, op);
2686 }
2687
2688 int
2689 asl_unset(aslmsg msg, const char *key)
2690 {
2691 asl_msg_unset((asl_msg_t *)msg, key);
2692 return 0;
2693 }
2694
2695 const char *
2696 asl_get(aslmsg msg, const char *key)
2697 {
2698 const char *val;
2699 int status;
2700
2701 val = NULL;
2702 status = asl_msg_lookup((asl_msg_t *)msg, key, &val, NULL);
2703 if (status != 0) return NULL;
2704 return val;
2705 }
2706
2707 void
2708 asl_free(aslmsg msg)
2709 {
2710 asl_msg_release((asl_msg_t *)msg);
2711 }
2712
2713 /* aslresponse */
2714
2715 /*
2716 * aslresponse_next: Iterate over responses returned from asl_search()
2717 * a: a response returned from asl_search();
2718 * returns: The next log message (an aslmsg) or NULL on failure
2719 */
2720 aslmsg
2721 aslresponse_next(aslresponse r)
2722 {
2723 asl_search_result_t *res;
2724 asl_msg_t *m;
2725
2726 res = (asl_search_result_t *)r;
2727 if (res == NULL) return NULL;
2728
2729 if (res->curr >= res->count) return NULL;
2730 m = res->msg[res->curr];
2731 res->curr++;
2732
2733 return (aslmsg)m;
2734 }
2735
2736 /*
2737 * aslresponse_free: Free a response returned from asl_search()
2738 * a: a response returned from asl_search()
2739 */
2740 void
2741 aslresponse_free(aslresponse r)
2742 {
2743 asl_search_result_t *res;
2744 uint32_t i;
2745
2746 res = (asl_search_result_t *)r;
2747 if (res == NULL) return;
2748
2749 for (i = 0; i < res->count; i++) asl_msg_release(res->msg[i]);
2750 free(res->msg);
2751 free(res);
2752 }