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 87extern time_t asl_parse_time(const char *in); 88 89/* from asl_util.c */ 90int asl_is_utf8(const char *str); 91uint8_t *asl_b64_encode(const uint8_t *buf, size_t len); 92 93static 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 115static 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 132static 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 213static 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 231static 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 274static 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 */ 304asl_msg_t * 305asl_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 318asl_msg_t * 319asl_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 331static 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 357void 358asl_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 378static 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 */ 449static 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 494uint32_t 495asl_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 500static 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 */ 650static 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 822int 823asl_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 884int 885asl_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 */ 895static 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 */ 918asl_msg_t * 919asl_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 */ 946asl_msg_t * 947asl_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 */ 956void 957asl_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 991int 992asl_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 1012uint32_t 1013asl_msg_type(asl_msg_t *msg) 1014{ 1015 if (msg == NULL) return 0; 1016 return msg->type; 1017} 1018 1019uint32_t 1020asl_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 */ 1033static 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 1055static 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 1075static 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 1154static 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 1202static 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 1225static 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 */ 1253static 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 */ 1276static 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 */ 1340static 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 1401int 1402asl_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 1414static 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 * 1607asl_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 1668static 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 1717char * 1718asl_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 1730static 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 1758static 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 1991asl_msg_t * 1992asl_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 2112char * 2113asl_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 2142asl_search_result_t * 2143asl_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 2191static 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 */ 2210static 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 */ 2302char * 2303asl_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 */ 2648const char * 2649asl_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 2670aslmsg 2671asl_new(uint32_t type) 2672{ 2673 return (aslmsg)asl_msg_new(type); 2674} 2675 2676int 2677asl_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 2682int 2683asl_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 2688int 2689asl_unset(aslmsg msg, const char *key) 2690{ 2691 asl_msg_unset((asl_msg_t *)msg, key); 2692 return 0; 2693} 2694 2695const char * 2696asl_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 2707void 2708asl_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 */ 2720aslmsg 2721aslresponse_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 */ 2740void 2741aslresponse_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} 2753