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 time_t asl_parse_time(const char *str
);
87 extern int asl_msg_cmp(aslmsg a
, aslmsg b
);
89 #define asl_msg_list_t asl_search_result_t
97 _asl_htonq(uint64_t n
)
111 x
.l
[0] = htonl(x
.l
[1]);
119 _asl_ntohq(uint64_t n
)
121 #ifdef __BIG_ENDIAN__
133 x
.l
[0] = ntohl(x
.l
[1]);
164 return _asl_ntohq(x
);
167 #define header_get_next(h) _asl_get_32(h + 1)
168 #define header_get_id(h) _asl_get_64(h + 5)
169 #define header_get_hash(h) _asl_get_32(h + 17)
172 * callback for sorting slotlist
173 * primary sort is by xid
174 * secondary sort is by slot, which happens when xid is 0
175 * this allows us to quickly find xids (using binary search on the xid key)
176 * it's also used to find slots quickly from record_chain_free()
179 slot_comp(const void *a
, const void *b
)
181 asl_legacy1_slot_info_t
*ai
, *bi
;
185 if (b
== NULL
) return 0;
189 if (b
== NULL
) return 1;
191 ai
= (asl_legacy1_slot_info_t
*)a
;
192 bi
= (asl_legacy1_slot_info_t
*)b
;
194 if (ai
->xid
< bi
->xid
) return -1;
196 if (ai
->xid
== bi
->xid
)
198 if (ai
->slot
< bi
->slot
) return -1;
199 if (ai
->slot
== bi
->slot
) return 0;
206 /* find an xid in the slot list */
208 slotlist_find(asl_legacy1_t
*s
, uint64_t xid
, int32_t direction
)
210 uint32_t top
, bot
, mid
, range
;
212 if (s
== NULL
) return ASL_INDEX_NULL
;
213 if (s
->slotlist_count
== 0) return ASL_INDEX_NULL
;
214 if (xid
== 0) return ASL_INDEX_NULL
;
216 top
= s
->slotlist_count
- 1;
223 if (xid
== s
->slotlist
[mid
].xid
) return mid
;
224 else if (xid
< s
->slotlist
[mid
].xid
) top
= mid
;
228 mid
= bot
+ (range
/ 2);
231 if (xid
== s
->slotlist
[top
].xid
) return top
;
232 if (xid
== s
->slotlist
[bot
].xid
) return bot
;
234 if (direction
== 0) return ASL_INDEX_NULL
;
235 if (direction
< 0) return bot
;
240 slotlist_init(asl_legacy1_t
*s
, uint32_t count
)
242 uint32_t i
, si
, status
, hash
, addslot
;
246 char tmp
[DB_RECORD_LEN
];
248 /* Start at first slot after the header */
249 status
= fseek(s
->db
, DB_RECORD_LEN
, SEEK_SET
);
250 if (status
!= 0) return ASL_STATUS_READ_FAILED
;
252 s
->slotlist
= (asl_legacy1_slot_info_t
*)calloc(count
, sizeof(asl_legacy1_slot_info_t
));
253 if (s
->slotlist
== NULL
) return ASL_STATUS_NO_MEMORY
;
257 for (i
= 1; i
< count
; i
++)
259 rcount
= fread(tmp
, DB_RECORD_LEN
, 1, s
->db
);
260 if (rcount
!= 1) return ASL_STATUS_READ_FAILED
;
267 if (t
== DB_TYPE_EMPTY
) addslot
= 1;
269 if (t
== DB_TYPE_STRING
)
272 xid
= header_get_id(tmp
);
273 hash
= header_get_hash(tmp
);
276 if (t
== DB_TYPE_MESSAGE
)
279 xid
= header_get_id(tmp
);
284 s
->slotlist
[si
].type
= t
;
285 s
->slotlist
[si
].slot
= i
;
286 s
->slotlist
[si
].xid
= xid
;
287 s
->slotlist
[si
].hash
= hash
;
292 s
->slotlist
= (asl_legacy1_slot_info_t
*)reallocf(s
->slotlist
, si
* sizeof(asl_legacy1_slot_info_t
));
293 if (s
->slotlist
== NULL
) return ASL_STATUS_NO_MEMORY
;
294 s
->slotlist_count
= si
;
296 /* slotlist is sorted by xid */
297 qsort((void *)s
->slotlist
, s
->slotlist_count
, sizeof(asl_legacy1_slot_info_t
), slot_comp
);
299 return ASL_STATUS_OK
;
303 asl_legacy1_open(const char *path
, asl_legacy1_t
**out
)
309 char cbuf
[DB_RECORD_LEN
];
313 memset(&sb
, 0, sizeof(struct stat
));
314 status
= stat(path
, &sb
);
315 if (status
< 0) return ASL_STATUS_FAILED
;
319 s
= (asl_legacy1_t
*)calloc(1, sizeof(asl_legacy1_t
));
320 if (s
== NULL
) return ASL_STATUS_NO_MEMORY
;
322 s
->db
= fopen(path
, "r");
326 return ASL_STATUS_INVALID_STORE
;
329 memset(cbuf
, 0, DB_RECORD_LEN
);
330 rcount
= fread(cbuf
, DB_RECORD_LEN
, 1, s
->db
);
335 return ASL_STATUS_READ_FAILED
;
338 /* Check the database Magic Cookie */
339 if (strncmp(cbuf
, ASL_DB_COOKIE
, ASL_DB_COOKIE_LEN
))
343 return ASL_STATUS_INVALID_STORE
;
346 count
= fsize
/ DB_RECORD_LEN
;
348 status
= slotlist_init(s
, count
);
351 return ASL_STATUS_OK
;
355 asl_legacy1_close(asl_legacy1_t
*s
)
357 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
359 if (s
->slotlist
!= NULL
) free(s
->slotlist
);
360 if (s
->db
!= NULL
) fclose(s
->db
);
363 return ASL_STATUS_OK
;
367 string_fetch_slot(asl_legacy1_t
*s
, uint32_t slot
, char **out
)
371 uint32_t next
, x
, remaining
;
374 char *outstr
, *p
, tmp
[DB_RECORD_LEN
];
376 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
377 if (out
== NULL
) return ASL_STATUS_INVALID_ARG
;
380 offset
= slot
* DB_RECORD_LEN
;
381 status
= fseek(s
->db
, offset
, SEEK_SET
);
383 if (status
< 0) return ASL_STATUS_READ_FAILED
;
385 rcount
= fread(tmp
, DB_RECORD_LEN
, 1, s
->db
);
386 if (rcount
!= 1) return ASL_STATUS_READ_FAILED
;
389 if (type
!= DB_TYPE_STRING
) return ASL_STATUS_INVALID_STRING
;
391 len
= _asl_get_32(tmp
+ 21);
392 if (len
== 0) return ASL_STATUS_OK
;
394 next
= header_get_next(tmp
);
396 outstr
= calloc(1, len
);
397 if (outstr
== NULL
) return ASL_STATUS_NO_MEMORY
;
402 x
= DB_RECORD_LEN
- DB_HLEN_STRING
;
403 if (x
> remaining
) x
= remaining
;
405 memcpy(p
, tmp
+ DB_HLEN_STRING
, x
);
409 while ((next
!= 0) && (remaining
> 0))
411 offset
= next
* DB_RECORD_LEN
;
412 status
= fseek(s
->db
, offset
, SEEK_SET
);
417 return ASL_STATUS_READ_FAILED
;
420 rcount
= fread(tmp
, DB_RECORD_LEN
, 1, s
->db
);
424 return ASL_STATUS_READ_FAILED
;
427 next
= header_get_next(tmp
);
429 x
= DB_RECORD_LEN
- DB_HLEN_STRCONT
;
430 if (x
> remaining
) x
= remaining
;
432 memcpy(p
, tmp
+ DB_HLEN_STRCONT
, x
);
437 if ((next
!= 0) || (remaining
!= 0))
440 return ASL_STATUS_READ_FAILED
;
444 return ASL_STATUS_OK
;
448 string_fetch_sid(asl_legacy1_t
*s
, uint64_t sid
, char **out
)
450 uint32_t i
, len
, ref
;
455 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
456 if (out
== NULL
) return ASL_STATUS_INVALID_ARG
;
459 if (sid
== ASL_REF_NULL
) return ASL_STATUS_OK
;
464 nsid
= _asl_htonq(sid
);
465 memcpy(&inls
, &nsid
, 1);
471 *out
= calloc(1, len
);
472 if (*out
== NULL
) return ASL_STATUS_NO_MEMORY
;
473 p
= 1 + (char *)&nsid
;
474 memcpy(*out
, p
, len
);
475 return ASL_STATUS_OK
;
478 /* Find the string in the database */
479 i
= slotlist_find(s
, sid
, 0);
480 if (i
== ASL_INDEX_NULL
) return ASL_STATUS_NOT_FOUND
;
482 return string_fetch_slot(s
, s
->slotlist
[i
].slot
, out
);
486 asl_legacy1_fetch_helper_32(asl_legacy1_t
*s
, char **p
, aslmsg m
, const char *key
, int ignore
, uint32_t ignoreval
)
491 out
= _asl_get_32(*p
);
492 *p
+= sizeof(uint32_t);
494 if ((m
== NULL
) || (key
== NULL
)) return out
;
497 if ((ignore
!= 0) && (out
== ignoreval
)) doit
= 0;
500 snprintf(str
, sizeof(str
), "%u", out
);
501 asl_set(m
, key
, str
);
508 asl_legacy1_fetch_helper_64(asl_legacy1_t
*s
, char **p
, aslmsg m
, const char *key
)
513 out
= _asl_get_64(*p
);
514 *p
+= sizeof(uint64_t);
516 if ((m
== NULL
) || (key
== NULL
)) return out
;
518 snprintf(str
, sizeof(str
), "%llu", out
);
519 asl_set(m
, key
, str
);
525 asl_legacy1_fetch_helper_str(asl_legacy1_t
*s
, char **p
, aslmsg m
, const char *key
, uint32_t *err
)
531 out
= _asl_get_64(*p
);
532 *p
+= sizeof(uint64_t);
535 status
= ASL_STATUS_OK
;
536 if (out
!= 0) status
= string_fetch_sid(s
, out
, &val
);
538 if (err
!= NULL
) *err
= status
;
539 if ((status
== ASL_STATUS_OK
) && (val
!= NULL
))
541 asl_set(m
, key
, val
);
549 msg_fetch(asl_legacy1_t
*s
, uint32_t slot
, aslmsg
*out
)
552 uint32_t status
, i
, n
, kvcount
, next
;
558 char *p
, tmp
[DB_RECORD_LEN
], *key
, *val
;
560 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
561 if (out
== NULL
) return ASL_STATUS_INVALID_ARG
;
565 offset
= slot
* DB_RECORD_LEN
;
566 fstatus
= fseek(s
->db
, offset
, SEEK_SET
);
568 if (fstatus
< 0) return ASL_STATUS_READ_FAILED
;
570 rcount
= fread(tmp
, DB_RECORD_LEN
, 1, s
->db
);
571 if (rcount
!= 1) return ASL_STATUS_READ_FAILED
;
573 flags
= _asl_get_16(tmp
+ MSG_OFF_KEY_FLAGS
);
575 msg
= asl_new(ASL_TYPE_MSG
);
576 if (msg
== NULL
) return ASL_STATUS_NO_MEMORY
;
580 asl_legacy1_fetch_helper_64(s
, &p
, msg
, ASL_KEY_MSG_ID
);
581 asl_legacy1_fetch_helper_32(s
, &p
, msg
, ASL_KEY_READ_UID
, 1, (uint32_t)-1);
582 asl_legacy1_fetch_helper_32(s
, &p
, msg
, ASL_KEY_READ_GID
, 1, (uint32_t)-1);
583 asl_legacy1_fetch_helper_64(s
, &p
, msg
, ASL_KEY_TIME
);
584 asl_legacy1_fetch_helper_str(s
, &p
, msg
, ASL_KEY_HOST
, &status
);
585 asl_legacy1_fetch_helper_str(s
, &p
, msg
, ASL_KEY_SENDER
, &status
);
586 asl_legacy1_fetch_helper_str(s
, &p
, msg
, ASL_KEY_FACILITY
, &status
);
587 asl_legacy1_fetch_helper_32(s
, &p
, msg
, ASL_KEY_LEVEL
, 0, 0);
588 asl_legacy1_fetch_helper_32(s
, &p
, msg
, ASL_KEY_PID
, 0, 0);
589 asl_legacy1_fetch_helper_32(s
, &p
, msg
, ASL_KEY_UID
, 0, 0);
590 asl_legacy1_fetch_helper_32(s
, &p
, msg
, ASL_KEY_GID
, 0, 0);
591 asl_legacy1_fetch_helper_str(s
, &p
, msg
, ASL_KEY_MSG
, &status
);
593 next
= header_get_next(tmp
);
600 offset
= next
* DB_RECORD_LEN
;
601 fstatus
= fseek(s
->db
, offset
, SEEK_SET
);
605 return ASL_STATUS_READ_FAILED
;
608 rcount
= fread(tmp
, DB_RECORD_LEN
, 1, s
->db
);
612 return ASL_STATUS_READ_FAILED
;
615 if (kvcount
== 0) kvcount
= _asl_get_32(tmp
+ 5);
619 for (i
= 0; (i
< 4) && (n
< kvcount
); i
++)
622 sid
= _asl_get_64(p
);
624 status
= string_fetch_sid(s
, sid
, &key
);
627 sid
= _asl_get_64(p
);
629 if (status
== ASL_STATUS_OK
) status
= string_fetch_sid(s
, sid
, &val
);
631 if ((status
== ASL_STATUS_OK
) && (key
!= NULL
)) asl_set(msg
, key
, val
);
632 if (key
!= NULL
) free(key
);
633 if (val
!= NULL
) free(val
);
638 next
= header_get_next(tmp
);
642 return ASL_STATUS_OK
;
646 asl_legacy1_fetch(asl_legacy1_t
*s
, uint64_t msgid
, aslmsg
*out
)
650 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
651 if (msgid
== ASL_REF_NULL
) return ASL_STATUS_INVALID_ARG
;
652 if (out
== NULL
) return ASL_STATUS_INVALID_ARG
;
654 i
= slotlist_find(s
, msgid
, 0);
655 if (i
== ASL_INDEX_NULL
) return ASL_STATUS_INVALID_ID
;
657 /* read the message */
658 status
= msg_fetch(s
, s
->slotlist
[i
].slot
, out
);
659 if (status
!= ASL_STATUS_OK
) return status
;
660 if (*out
== NULL
) return ASL_STATUS_FAILED
;
666 next_search_slot(asl_legacy1_t
*s
, uint32_t last_si
, int32_t direction
)
672 for (i
= last_si
+ 1; i
< s
->slotlist_count
; i
++)
674 if (s
->slotlist
[i
].type
== DB_TYPE_MESSAGE
) return i
;
677 return ASL_INDEX_NULL
;
680 if (last_si
== 0) return ASL_INDEX_NULL
;
681 if (last_si
> s
->slotlist_count
) return ASL_INDEX_NULL
;
683 for (i
= last_si
- 1; i
> 0; i
--)
685 if (s
->slotlist
[i
].type
== DB_TYPE_MESSAGE
) return i
;
688 if (s
->slotlist
[0].type
== DB_TYPE_MESSAGE
) return 0;
690 return ASL_INDEX_NULL
;
694 match_worker_cleanup(asl_msg_list_t
**res
)
700 for (i
= 0; i
< (*res
)->count
; i
++) asl_free((aslmsg
)(*res
)->msg
[i
]);
706 * Input to asl_legacy1_match is a list of queries.
707 * A record in the store matches if it matches any query (i.e. query list is "OR"ed)
709 * If counting up (direction is positive) find first record with ID > start_id.
710 * Else if counting down (direction is negative) find first record with ID < start_id.
713 * If any query is NULL, set match flog off (skips matching below).
714 * Else if all queries only check "standard" keys, set std flag to on.
716 * If all queries are marked as "never matches", return NULL.
719 * fetch record (with std flag)
720 * if match flag is off, decode record and add it to result.
721 * else for each query:
722 * if query is NULL (shouldn't happen) decode record and add it to result. Return to match loop.
723 * else if query never matches, ignore it.
724 * else decode record and use asl_cmp. If it succeeds, add record to result. Return to match loop.
729 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
)
731 uint32_t mx
, si
, slot
, i
, qcount
, match
, didmatch
, status
;
735 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
736 if ((res
== NULL
) && (idlist
== NULL
)) return ASL_STATUS_INVALID_ARG
;
737 if (last_id
== NULL
) return ASL_STATUS_INVALID_ARG
;
738 if (idcount
== NULL
) return ASL_STATUS_INVALID_ARG
;
740 if (res
!= NULL
) *res
= NULL
;
741 if (idlist
!= NULL
) *idlist
= NULL
;
745 if (direction
< 0) direction
= -1;
749 if ((direction
== -1) && (start_id
== ASL_REF_NULL
)) si
= s
->slotlist_count
;
750 else si
= slotlist_find(s
, start_id
, direction
);
752 si
= next_search_slot(s
, si
, direction
);
753 if (si
== ASL_INDEX_NULL
) return ASL_STATUS_OK
;
754 if (si
>= s
->slotlist_count
) return ASL_STATUS_FAILED
;
756 slot
= s
->slotlist
[si
].slot
;
761 if (query
== NULL
) match
= 0;
762 else if (query
->count
== 0) match
= 0;
763 else qcount
= query
->count
;
766 * initialize result list if we've been asked to return messages
770 *res
= (asl_msg_list_t
*)calloc(1, sizeof(asl_msg_list_t
));
771 if (*res
== NULL
) return ASL_STATUS_NO_MEMORY
;
774 status
= ASL_STATUS_OK
;
777 * loop through records
780 while ((count
== 0) || (*idcount
< count
))
782 if (si
== ASL_INDEX_NULL
) break;
783 if (si
>= s
->slotlist_count
) break;
785 slot
= s
->slotlist
[si
].slot
;
786 xid
= s
->slotlist
[si
].xid
;
790 status
= msg_fetch(s
, slot
, &msg
);
799 for (i
= 0; i
< qcount
; i
++)
801 didmatch
= asl_msg_cmp((aslmsg
)(query
->msg
[i
]), msg
);
802 if (didmatch
== 1) break;
808 if ((*res
)->count
== 0) (*res
)->msg
= (asl_msg_t
**)calloc(1, sizeof(asl_msg_t
*));
809 else (*res
)->msg
= (asl_msg_t
**)reallocf((*res
)->msg
, (1 + (*res
)->count
) * sizeof(asl_msg_t
*));
810 if ((*res
)->msg
== NULL
)
812 match_worker_cleanup(res
);
813 return ASL_STATUS_NO_MEMORY
;
816 (*res
)->msg
[(*res
)->count
++] = (asl_msg_t
*)msg
;
823 si
= next_search_slot(s
, si
, direction
);
830 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
)
835 return match_worker(s
, query
, res
, last_id
, NULL
, &idcount
, start_id
, count
, direction
);