2 * Copyright (c) 2007-2011 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@ */
24 #include <asl_legacy1.h>
25 #include <asl_private.h>
29 #include <sys/errno.h>
31 #include <membership.h>
32 #include <mach/mach.h>
33 #include <sys/syslimits.h>
34 #include <sys/types.h>
38 #define forever for(;;)
40 #define FILE_MODE 0600
42 #define DB_RECORD_LEN 80
44 #define DB_HEADER_COOKIE_OFFSET 0
45 #define DB_HEADER_VERS_OFFSET 12
47 #define DB_TYPE_EMPTY 0
48 #define DB_TYPE_HEADER 1
49 #define DB_TYPE_MESSAGE 2
50 #define DB_TYPE_KVLIST 3
51 #define DB_TYPE_STRING 4
52 #define DB_TYPE_STRCONT 5
55 * Magic Cookie for database files.
56 * MAXIMUM 12 CHARS! (DB_HEADER_VERS_OFFSET)
58 #define ASL_DB_COOKIE "ASL DB"
59 #define ASL_DB_COOKIE_LEN 6
61 #define ASL_INDEX_NULL 0xffffffff
63 #define DB_HLEN_EMPTY 0
64 #define DB_HLEN_HEADER 13
65 #define DB_HLEN_MESSAGE 13
66 #define DB_HLEN_KVLIST 9
67 #define DB_HLEN_STRING 25
68 #define DB_HLEN_STRCONT 5
70 #define MSG_OFF_KEY_TYPE 0
71 #define MSG_OFF_KEY_NEXT 1
72 #define MSG_OFF_KEY_ID 5
73 #define MSG_OFF_KEY_RUID 13
74 #define MSG_OFF_KEY_RGID 17
75 #define MSG_OFF_KEY_TIME 21
76 #define MSG_OFF_KEY_HOST 29
77 #define MSG_OFF_KEY_SENDER 37
78 #define MSG_OFF_KEY_FACILITY 45
79 #define MSG_OFF_KEY_LEVEL 53
80 #define MSG_OFF_KEY_PID 57
81 #define MSG_OFF_KEY_UID 61
82 #define MSG_OFF_KEY_GID 65
83 #define MSG_OFF_KEY_MSG 69
84 #define MSG_OFF_KEY_FLAGS 77
86 extern int asl_msg_cmp(asl_msg_t
*a
, asl_msg_t
*b
);
94 _asl_htonq(uint64_t n
)
108 x
.l
[0] = htonl(x
.l
[1]);
116 _asl_ntohq(uint64_t n
)
118 #ifdef __BIG_ENDIAN__
130 x
.l
[0] = ntohl(x
.l
[1]);
161 return _asl_ntohq(x
);
164 #define header_get_next(h) _asl_get_32(h + 1)
165 #define header_get_id(h) _asl_get_64(h + 5)
166 #define header_get_hash(h) _asl_get_32(h + 17)
169 * callback for sorting slotlist
170 * primary sort is by xid
171 * secondary sort is by slot, which happens when xid is 0
172 * this allows us to quickly find xids (using binary search on the xid key)
173 * it's also used to find slots quickly from record_chain_free()
176 slot_comp(const void *a
, const void *b
)
178 asl_legacy1_slot_info_t
*ai
, *bi
;
182 if (b
== NULL
) return 0;
186 if (b
== NULL
) return 1;
188 ai
= (asl_legacy1_slot_info_t
*)a
;
189 bi
= (asl_legacy1_slot_info_t
*)b
;
191 if (ai
->xid
< bi
->xid
) return -1;
193 if (ai
->xid
== bi
->xid
)
195 if (ai
->slot
< bi
->slot
) return -1;
196 if (ai
->slot
== bi
->slot
) return 0;
203 /* find an xid in the slot list */
205 slotlist_find(asl_legacy1_t
*s
, uint64_t xid
, int32_t direction
)
207 uint32_t top
, bot
, mid
, range
;
209 if (s
== NULL
) return ASL_INDEX_NULL
;
210 if (s
->slotlist_count
== 0) return ASL_INDEX_NULL
;
211 if (xid
== 0) return ASL_INDEX_NULL
;
213 top
= s
->slotlist_count
- 1;
220 if (xid
== s
->slotlist
[mid
].xid
) return mid
;
221 else if (xid
< s
->slotlist
[mid
].xid
) top
= mid
;
225 mid
= bot
+ (range
/ 2);
228 if (xid
== s
->slotlist
[top
].xid
) return top
;
229 if (xid
== s
->slotlist
[bot
].xid
) return bot
;
231 if (direction
>= 0) return ASL_INDEX_NULL
;
232 if (direction
< 0) return bot
;
237 slotlist_init(asl_legacy1_t
*s
, uint32_t count
)
239 uint32_t i
, si
, status
, hash
, addslot
;
243 char tmp
[DB_RECORD_LEN
];
245 /* Start at first slot after the header */
246 status
= fseek(s
->db
, DB_RECORD_LEN
, SEEK_SET
);
247 if (status
!= 0) return ASL_STATUS_READ_FAILED
;
249 s
->slotlist
= (asl_legacy1_slot_info_t
*)calloc(count
, sizeof(asl_legacy1_slot_info_t
));
250 if (s
->slotlist
== NULL
) return ASL_STATUS_NO_MEMORY
;
254 for (i
= 1; i
< count
; i
++)
256 rcount
= fread(tmp
, DB_RECORD_LEN
, 1, s
->db
);
257 if (rcount
!= 1) return ASL_STATUS_READ_FAILED
;
264 if (t
== DB_TYPE_EMPTY
) addslot
= 1;
266 if (t
== DB_TYPE_STRING
)
269 xid
= header_get_id(tmp
);
270 hash
= header_get_hash(tmp
);
273 if (t
== DB_TYPE_MESSAGE
)
276 xid
= header_get_id(tmp
);
281 s
->slotlist
[si
].type
= t
;
282 s
->slotlist
[si
].slot
= i
;
283 s
->slotlist
[si
].xid
= xid
;
284 s
->slotlist
[si
].hash
= hash
;
289 s
->slotlist
= (asl_legacy1_slot_info_t
*)reallocf(s
->slotlist
, si
* sizeof(asl_legacy1_slot_info_t
));
290 if (s
->slotlist
== NULL
) return ASL_STATUS_NO_MEMORY
;
291 s
->slotlist_count
= si
;
293 /* slotlist is sorted by xid */
294 qsort((void *)s
->slotlist
, s
->slotlist_count
, sizeof(asl_legacy1_slot_info_t
), slot_comp
);
296 return ASL_STATUS_OK
;
300 asl_legacy1_open(const char *path
, asl_legacy1_t
**out
)
306 char cbuf
[DB_RECORD_LEN
];
310 memset(&sb
, 0, sizeof(struct stat
));
311 status
= stat(path
, &sb
);
312 if (status
< 0) return ASL_STATUS_FAILED
;
316 s
= (asl_legacy1_t
*)calloc(1, sizeof(asl_legacy1_t
));
317 if (s
== NULL
) return ASL_STATUS_NO_MEMORY
;
319 s
->db
= fopen(path
, "r");
323 return ASL_STATUS_INVALID_STORE
;
326 memset(cbuf
, 0, DB_RECORD_LEN
);
327 rcount
= fread(cbuf
, DB_RECORD_LEN
, 1, s
->db
);
332 return ASL_STATUS_READ_FAILED
;
335 /* Check the database Magic Cookie */
336 if (strncmp(cbuf
, ASL_DB_COOKIE
, ASL_DB_COOKIE_LEN
))
340 return ASL_STATUS_INVALID_STORE
;
343 count
= fsize
/ DB_RECORD_LEN
;
345 status
= slotlist_init(s
, count
);
348 return ASL_STATUS_OK
;
352 asl_legacy1_close(asl_legacy1_t
*s
)
354 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
356 if (s
->slotlist
!= NULL
) free(s
->slotlist
);
357 if (s
->db
!= NULL
) fclose(s
->db
);
360 return ASL_STATUS_OK
;
364 string_fetch_slot(asl_legacy1_t
*s
, uint32_t slot
, char **out
)
368 uint32_t next
, x
, remaining
;
371 char *outstr
, *p
, tmp
[DB_RECORD_LEN
];
373 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
374 if (out
== NULL
) return ASL_STATUS_INVALID_ARG
;
377 offset
= slot
* DB_RECORD_LEN
;
378 status
= fseek(s
->db
, offset
, SEEK_SET
);
380 if (status
< 0) return ASL_STATUS_READ_FAILED
;
382 rcount
= fread(tmp
, DB_RECORD_LEN
, 1, s
->db
);
383 if (rcount
!= 1) return ASL_STATUS_READ_FAILED
;
386 if (type
!= DB_TYPE_STRING
) return ASL_STATUS_INVALID_STRING
;
388 len
= _asl_get_32(tmp
+ 21);
389 if (len
== 0) return ASL_STATUS_OK
;
391 next
= header_get_next(tmp
);
393 outstr
= calloc(1, len
);
394 if (outstr
== NULL
) return ASL_STATUS_NO_MEMORY
;
399 x
= DB_RECORD_LEN
- DB_HLEN_STRING
;
400 if (x
> remaining
) x
= remaining
;
402 memcpy(p
, tmp
+ DB_HLEN_STRING
, x
);
406 while ((next
!= 0) && (remaining
> 0))
408 offset
= next
* DB_RECORD_LEN
;
409 status
= fseek(s
->db
, offset
, SEEK_SET
);
414 return ASL_STATUS_READ_FAILED
;
417 rcount
= fread(tmp
, DB_RECORD_LEN
, 1, s
->db
);
421 return ASL_STATUS_READ_FAILED
;
424 next
= header_get_next(tmp
);
426 x
= DB_RECORD_LEN
- DB_HLEN_STRCONT
;
427 if (x
> remaining
) x
= remaining
;
429 memcpy(p
, tmp
+ DB_HLEN_STRCONT
, x
);
434 if ((next
!= 0) || (remaining
!= 0))
437 return ASL_STATUS_READ_FAILED
;
441 return ASL_STATUS_OK
;
445 string_fetch_sid(asl_legacy1_t
*s
, uint64_t sid
, char **out
)
447 uint32_t i
, len
, ref
;
452 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
453 if (out
== NULL
) return ASL_STATUS_INVALID_ARG
;
456 if (sid
== ASL_REF_NULL
) return ASL_STATUS_OK
;
461 nsid
= _asl_htonq(sid
);
462 memcpy(&inls
, &nsid
, 1);
468 *out
= calloc(1, len
);
469 if (*out
== NULL
) return ASL_STATUS_NO_MEMORY
;
470 p
= 1 + (char *)&nsid
;
471 memcpy(*out
, p
, len
);
472 return ASL_STATUS_OK
;
475 /* Find the string in the database */
476 i
= slotlist_find(s
, sid
, 0);
477 if (i
== ASL_INDEX_NULL
) return ASL_STATUS_NOT_FOUND
;
479 return string_fetch_slot(s
, s
->slotlist
[i
].slot
, out
);
483 asl_legacy1_fetch_helper_32(asl_legacy1_t
*s
, char **p
, asl_msg_t
*m
, const char *key
, int ignore
, uint32_t ignoreval
)
488 out
= _asl_get_32(*p
);
489 *p
+= sizeof(uint32_t);
491 if ((m
== NULL
) || (key
== NULL
)) return out
;
494 if ((ignore
!= 0) && (out
== ignoreval
)) doit
= 0;
497 snprintf(str
, sizeof(str
), "%u", out
);
498 asl_msg_set_key_val(m
, key
, str
);
505 asl_legacy1_fetch_helper_64(asl_legacy1_t
*s
, char **p
, asl_msg_t
*m
, const char *key
)
510 out
= _asl_get_64(*p
);
511 *p
+= sizeof(uint64_t);
513 if ((m
== NULL
) || (key
== NULL
)) return out
;
515 snprintf(str
, sizeof(str
), "%llu", out
);
516 asl_msg_set_key_val(m
, key
, str
);
522 asl_legacy1_fetch_helper_str(asl_legacy1_t
*s
, char **p
, asl_msg_t
*m
, const char *key
, uint32_t *err
)
528 out
= _asl_get_64(*p
);
529 *p
+= sizeof(uint64_t);
532 status
= ASL_STATUS_OK
;
533 if (out
!= 0) status
= string_fetch_sid(s
, out
, &val
);
535 if (err
!= NULL
) *err
= status
;
536 if ((status
== ASL_STATUS_OK
) && (val
!= NULL
))
538 asl_msg_set_key_val(m
, key
, val
);
546 msg_fetch(asl_legacy1_t
*s
, uint32_t slot
, asl_msg_t
**out
)
549 uint32_t status
, i
, n
, kvcount
, next
;
555 char *p
, tmp
[DB_RECORD_LEN
], *key
, *val
;
557 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
558 if (out
== NULL
) return ASL_STATUS_INVALID_ARG
;
562 offset
= slot
* DB_RECORD_LEN
;
563 fstatus
= fseek(s
->db
, offset
, SEEK_SET
);
565 if (fstatus
< 0) return ASL_STATUS_READ_FAILED
;
567 rcount
= fread(tmp
, DB_RECORD_LEN
, 1, s
->db
);
568 if (rcount
!= 1) return ASL_STATUS_READ_FAILED
;
570 flags
= _asl_get_16(tmp
+ MSG_OFF_KEY_FLAGS
);
572 msg
= asl_msg_new(ASL_TYPE_MSG
);
573 if (msg
== NULL
) return ASL_STATUS_NO_MEMORY
;
577 asl_legacy1_fetch_helper_64(s
, &p
, msg
, ASL_KEY_MSG_ID
);
578 asl_legacy1_fetch_helper_32(s
, &p
, msg
, ASL_KEY_READ_UID
, 1, (uint32_t)-1);
579 asl_legacy1_fetch_helper_32(s
, &p
, msg
, ASL_KEY_READ_GID
, 1, (uint32_t)-1);
580 asl_legacy1_fetch_helper_64(s
, &p
, msg
, ASL_KEY_TIME
);
581 asl_legacy1_fetch_helper_str(s
, &p
, msg
, ASL_KEY_HOST
, &status
);
582 asl_legacy1_fetch_helper_str(s
, &p
, msg
, ASL_KEY_SENDER
, &status
);
583 asl_legacy1_fetch_helper_str(s
, &p
, msg
, ASL_KEY_FACILITY
, &status
);
584 asl_legacy1_fetch_helper_32(s
, &p
, msg
, ASL_KEY_LEVEL
, 0, 0);
585 asl_legacy1_fetch_helper_32(s
, &p
, msg
, ASL_KEY_PID
, 0, 0);
586 asl_legacy1_fetch_helper_32(s
, &p
, msg
, ASL_KEY_UID
, 0, 0);
587 asl_legacy1_fetch_helper_32(s
, &p
, msg
, ASL_KEY_GID
, 0, 0);
588 asl_legacy1_fetch_helper_str(s
, &p
, msg
, ASL_KEY_MSG
, &status
);
590 next
= header_get_next(tmp
);
597 offset
= next
* DB_RECORD_LEN
;
598 fstatus
= fseek(s
->db
, offset
, SEEK_SET
);
601 asl_msg_release(msg
);
602 return ASL_STATUS_READ_FAILED
;
605 rcount
= fread(tmp
, DB_RECORD_LEN
, 1, s
->db
);
608 asl_msg_release(msg
);
609 return ASL_STATUS_READ_FAILED
;
612 if (kvcount
== 0) kvcount
= _asl_get_32(tmp
+ 5);
616 for (i
= 0; (i
< 4) && (n
< kvcount
); i
++)
619 sid
= _asl_get_64(p
);
621 status
= string_fetch_sid(s
, sid
, &key
);
624 sid
= _asl_get_64(p
);
626 if (status
== ASL_STATUS_OK
) status
= string_fetch_sid(s
, sid
, &val
);
628 if ((status
== ASL_STATUS_OK
) && (key
!= NULL
)) asl_msg_set_key_val(msg
, key
, val
);
629 if (key
!= NULL
) free(key
);
630 if (val
!= NULL
) free(val
);
635 next
= header_get_next(tmp
);
639 return ASL_STATUS_OK
;
643 asl_legacy1_fetch(asl_legacy1_t
*s
, uint64_t msgid
, asl_msg_t
**out
)
647 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
648 if (msgid
== ASL_REF_NULL
) return ASL_STATUS_INVALID_ARG
;
649 if (out
== NULL
) return ASL_STATUS_INVALID_ARG
;
651 i
= slotlist_find(s
, msgid
, 0);
652 if (i
== ASL_INDEX_NULL
) return ASL_STATUS_INVALID_ID
;
654 /* read the message */
655 status
= msg_fetch(s
, s
->slotlist
[i
].slot
, out
);
656 if (status
!= ASL_STATUS_OK
) return status
;
657 if (*out
== NULL
) return ASL_STATUS_FAILED
;
663 next_search_slot(asl_legacy1_t
*s
, uint32_t last_si
, int32_t direction
)
669 for (i
= last_si
+ 1; i
< s
->slotlist_count
; i
++)
671 if (s
->slotlist
[i
].type
== DB_TYPE_MESSAGE
) return i
;
674 return ASL_INDEX_NULL
;
677 if (last_si
== 0) return ASL_INDEX_NULL
;
678 if (last_si
> s
->slotlist_count
) return ASL_INDEX_NULL
;
680 for (i
= last_si
- 1; i
> 0; i
--)
682 if (s
->slotlist
[i
].type
== DB_TYPE_MESSAGE
) return i
;
685 if (s
->slotlist
[0].type
== DB_TYPE_MESSAGE
) return 0;
687 return ASL_INDEX_NULL
;
691 * Input to asl_legacy1_match is a list of queries.
692 * A record in the store matches if it matches any query (i.e. query list is "OR"ed)
694 * If counting up (direction is positive) find first record with ID > start_id.
695 * Else if counting down (direction is negative) find first record with ID < start_id.
698 * If any query is NULL, set match flog off (skips matching below).
699 * Else if all queries only check "standard" keys, set std flag to on.
701 * If all queries are marked as "never matches", return NULL.
704 * fetch record (with std flag)
705 * if match flag is off, decode record and add it to result.
706 * else for each query:
707 * if query is NULL (shouldn't happen) decode record and add it to result. Return to match loop.
708 * else if query never matches, ignore it.
709 * else decode record and use asl_cmp. If it succeeds, add record to result. Return to match loop.
714 match_worker(asl_legacy1_t
*s
, asl_msg_list_t
*query
, asl_msg_list_t
**res
, uint64_t *last_id
, uint64_t **idlist
, uint32_t *idcount
, uint64_t start_id
, int32_t count
, int32_t direction
)
716 uint32_t mx
, si
, slot
, i
, qcount
, match
, didmatch
, status
;
720 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
721 if ((res
== NULL
) && (idlist
== NULL
)) return ASL_STATUS_INVALID_ARG
;
722 if (last_id
== NULL
) return ASL_STATUS_INVALID_ARG
;
723 if (idcount
== NULL
) return ASL_STATUS_INVALID_ARG
;
725 if (res
!= NULL
) *res
= NULL
;
726 if (idlist
!= NULL
) *idlist
= NULL
;
730 if (direction
< 0) direction
= -1;
734 if ((direction
== -1) && (start_id
== ASL_REF_NULL
)) si
= s
->slotlist_count
;
735 else si
= slotlist_find(s
, start_id
, direction
);
737 si
= next_search_slot(s
, si
, direction
);
738 if (si
== ASL_INDEX_NULL
) return ASL_STATUS_OK
;
739 if (si
>= s
->slotlist_count
) return ASL_STATUS_FAILED
;
741 slot
= s
->slotlist
[si
].slot
;
746 if (query
== NULL
) match
= 0;
747 else if (query
->count
== 0) match
= 0;
748 else qcount
= query
->count
;
751 * initialize result list if we've been asked to return messages
755 *res
= asl_msg_list_new();
756 if (*res
== NULL
) return ASL_STATUS_NO_MEMORY
;
759 status
= ASL_STATUS_OK
;
762 * loop through records
765 while ((count
== 0) || (*idcount
< count
))
767 if (si
== ASL_INDEX_NULL
) break;
768 if (si
>= s
->slotlist_count
) break;
770 slot
= s
->slotlist
[si
].slot
;
771 xid
= s
->slotlist
[si
].xid
;
775 status
= msg_fetch(s
, slot
, &msg
);
784 for (i
= 0; i
< qcount
; i
++)
786 didmatch
= asl_msg_cmp(query
->msg
[i
], msg
);
787 if (didmatch
== 1) break;
791 if (didmatch
== 1) asl_msg_list_append(*res
, msg
);
792 asl_msg_release(msg
);
794 si
= next_search_slot(s
, si
, direction
);
801 asl_legacy1_match(asl_legacy1_t
*s
, asl_msg_list_t
*query
, asl_msg_list_t
**res
, uint64_t *last_id
, uint64_t start_id
, uint32_t count
, int32_t direction
)
806 return match_worker(s
, query
, res
, last_id
, NULL
, &idcount
, start_id
, count
, direction
);