2 * Copyright (c) 2007 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * "Portions Copyright (c) 2007 Apple Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License'). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
22 * @APPLE_LICENSE_HEADER_END@
26 #include <asl_legacy1.h>
27 #include <asl_private.h>
31 #include <sys/errno.h>
33 #include <membership.h>
34 #include <mach/mach.h>
35 #include <sys/syslimits.h>
36 #include <sys/types.h>
40 #define forever for(;;)
42 #define FILE_MODE 0600
44 #define DB_RECORD_LEN 80
46 #define DB_HEADER_COOKIE_OFFSET 0
47 #define DB_HEADER_VERS_OFFSET 12
49 #define DB_TYPE_EMPTY 0
50 #define DB_TYPE_HEADER 1
51 #define DB_TYPE_MESSAGE 2
52 #define DB_TYPE_KVLIST 3
53 #define DB_TYPE_STRING 4
54 #define DB_TYPE_STRCONT 5
57 * Magic Cookie for database files.
58 * MAXIMUM 12 CHARS! (DB_HEADER_VERS_OFFSET)
60 #define ASL_DB_COOKIE "ASL DB"
61 #define ASL_DB_COOKIE_LEN 6
63 #define ASL_INDEX_NULL 0xffffffff
65 #define DB_HLEN_EMPTY 0
66 #define DB_HLEN_HEADER 13
67 #define DB_HLEN_MESSAGE 13
68 #define DB_HLEN_KVLIST 9
69 #define DB_HLEN_STRING 25
70 #define DB_HLEN_STRCONT 5
72 #define MSG_OFF_KEY_TYPE 0
73 #define MSG_OFF_KEY_NEXT 1
74 #define MSG_OFF_KEY_ID 5
75 #define MSG_OFF_KEY_RUID 13
76 #define MSG_OFF_KEY_RGID 17
77 #define MSG_OFF_KEY_TIME 21
78 #define MSG_OFF_KEY_HOST 29
79 #define MSG_OFF_KEY_SENDER 37
80 #define MSG_OFF_KEY_FACILITY 45
81 #define MSG_OFF_KEY_LEVEL 53
82 #define MSG_OFF_KEY_PID 57
83 #define MSG_OFF_KEY_UID 61
84 #define MSG_OFF_KEY_GID 65
85 #define MSG_OFF_KEY_MSG 69
86 #define MSG_OFF_KEY_FLAGS 77
88 extern time_t asl_parse_time(const char *str
);
89 extern int asl_msg_cmp(aslmsg a
, aslmsg b
);
91 #define asl_msg_list_t asl_search_result_t
99 _asl_htonq(uint64_t n
)
101 #ifdef __BIG_ENDIAN__
113 x
.l
[0] = htonl(x
.l
[1]);
121 _asl_ntohq(uint64_t n
)
123 #ifdef __BIG_ENDIAN__
135 x
.l
[0] = ntohl(x
.l
[1]);
166 return _asl_ntohq(x
);
169 #define header_get_next(h) _asl_get_32(h + 1)
170 #define header_get_id(h) _asl_get_64(h + 5)
171 #define header_get_hash(h) _asl_get_32(h + 17)
174 * callback for sorting slotlist
175 * primary sort is by xid
176 * secondary sort is by slot, which happens when xid is 0
177 * this allows us to quickly find xids (using binary search on the xid key)
178 * it's also used to find slots quickly from record_chain_free()
181 slot_comp(const void *a
, const void *b
)
183 asl_legacy1_slot_info_t
*ai
, *bi
;
187 if (b
== NULL
) return 0;
191 if (b
== NULL
) return 1;
193 ai
= (asl_legacy1_slot_info_t
*)a
;
194 bi
= (asl_legacy1_slot_info_t
*)b
;
196 if (ai
->xid
< bi
->xid
) return -1;
198 if (ai
->xid
== bi
->xid
)
200 if (ai
->slot
< bi
->slot
) return -1;
201 if (ai
->slot
== bi
->slot
) return 0;
208 /* find an xid in the slot list */
210 slotlist_find(asl_legacy1_t
*s
, uint64_t xid
, int32_t direction
)
212 uint32_t top
, bot
, mid
, range
;
214 if (s
== NULL
) return ASL_INDEX_NULL
;
215 if (s
->slotlist_count
== 0) return ASL_INDEX_NULL
;
216 if (xid
== 0) return ASL_INDEX_NULL
;
218 top
= s
->slotlist_count
- 1;
225 if (xid
== s
->slotlist
[mid
].xid
) return mid
;
226 else if (xid
< s
->slotlist
[mid
].xid
) top
= mid
;
230 mid
= bot
+ (range
/ 2);
233 if (xid
== s
->slotlist
[top
].xid
) return top
;
234 if (xid
== s
->slotlist
[bot
].xid
) return bot
;
236 if (direction
== 0) return ASL_INDEX_NULL
;
237 if (direction
< 0) return bot
;
242 slotlist_init(asl_legacy1_t
*s
, uint32_t count
)
244 uint32_t i
, si
, status
, hash
, addslot
;
247 char tmp
[DB_RECORD_LEN
];
249 /* Start at first slot after the header */
250 status
= fseek(s
->db
, DB_RECORD_LEN
, SEEK_SET
);
251 if (status
!= 0) return ASL_STATUS_READ_FAILED
;
253 s
->slotlist
= (asl_legacy1_slot_info_t
*)calloc(count
, sizeof(asl_legacy1_slot_info_t
));
254 if (s
->slotlist
== NULL
) return ASL_STATUS_NO_MEMORY
;
258 for (i
= 1; i
< count
; i
++)
260 status
= fread(tmp
, DB_RECORD_LEN
, 1, s
->db
);
261 if (status
!= 1) return ASL_STATUS_READ_FAILED
;
268 if (t
== DB_TYPE_EMPTY
) addslot
= 1;
270 if (t
== DB_TYPE_STRING
)
273 xid
= header_get_id(tmp
);
274 hash
= header_get_hash(tmp
);
277 if (t
== DB_TYPE_MESSAGE
)
280 xid
= header_get_id(tmp
);
285 s
->slotlist
[si
].type
= t
;
286 s
->slotlist
[si
].slot
= i
;
287 s
->slotlist
[si
].xid
= xid
;
288 s
->slotlist
[si
].hash
= hash
;
293 s
->slotlist
= (asl_legacy1_slot_info_t
*)reallocf(s
->slotlist
, si
* sizeof(asl_legacy1_slot_info_t
));
294 if (s
->slotlist
== NULL
) return ASL_STATUS_NO_MEMORY
;
295 s
->slotlist_count
= si
;
297 /* slotlist is sorted by xid */
298 qsort((void *)s
->slotlist
, s
->slotlist_count
, sizeof(asl_legacy1_slot_info_t
), slot_comp
);
300 return ASL_STATUS_OK
;
304 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 status
= 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 status
, next
, len
, x
, remaining
;
372 char *outstr
, *p
, tmp
[DB_RECORD_LEN
];
374 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
375 if (out
== NULL
) return ASL_STATUS_INVALID_ARG
;
378 offset
= slot
* DB_RECORD_LEN
;
379 status
= fseek(s
->db
, offset
, SEEK_SET
);
381 if (status
< 0) return ASL_STATUS_READ_FAILED
;
383 status
= fread(tmp
, DB_RECORD_LEN
, 1, s
->db
);
384 if (status
!= 1) return ASL_STATUS_READ_FAILED
;
387 if (type
!= DB_TYPE_STRING
) return ASL_STATUS_INVALID_STRING
;
389 len
= _asl_get_32(tmp
+ 21);
390 if (len
== 0) return ASL_STATUS_OK
;
392 next
= header_get_next(tmp
);
394 outstr
= calloc(1, len
);
395 if (outstr
== NULL
) return ASL_STATUS_NO_MEMORY
;
400 x
= DB_RECORD_LEN
- DB_HLEN_STRING
;
401 if (x
> remaining
) x
= remaining
;
403 memcpy(p
, tmp
+ DB_HLEN_STRING
, x
);
407 while ((next
!= 0) && (remaining
> 0))
409 offset
= next
* DB_RECORD_LEN
;
410 status
= fseek(s
->db
, offset
, SEEK_SET
);
415 return ASL_STATUS_READ_FAILED
;
418 status
= fread(tmp
, DB_RECORD_LEN
, 1, s
->db
);
422 return ASL_STATUS_READ_FAILED
;
425 next
= header_get_next(tmp
);
427 x
= DB_RECORD_LEN
- DB_HLEN_STRCONT
;
428 if (x
> remaining
) x
= remaining
;
430 memcpy(p
, tmp
+ DB_HLEN_STRCONT
, x
);
435 if ((next
!= 0) || (remaining
!= 0))
438 return ASL_STATUS_READ_FAILED
;
442 return ASL_STATUS_OK
;
446 string_fetch_sid(asl_legacy1_t
*s
, uint64_t sid
, char **out
)
448 uint32_t i
, len
, ref
;
453 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
454 if (out
== NULL
) return ASL_STATUS_INVALID_ARG
;
457 if (sid
== ASL_REF_NULL
) return ASL_STATUS_OK
;
462 nsid
= _asl_htonq(sid
);
463 memcpy(&inls
, &nsid
, 1);
469 *out
= calloc(1, len
);
470 if (*out
== NULL
) return ASL_STATUS_NO_MEMORY
;
471 p
= 1 + (char *)&nsid
;
472 memcpy(*out
, p
, len
);
473 return ASL_STATUS_OK
;
476 /* Find the string in the database */
477 i
= slotlist_find(s
, sid
, 0);
478 if (i
== ASL_INDEX_NULL
) return ASL_STATUS_NOT_FOUND
;
480 return string_fetch_slot(s
, s
->slotlist
[i
].slot
, out
);
484 asl_legacy1_fetch_helper_32(asl_legacy1_t
*s
, char **p
, aslmsg m
, const char *key
, int ignore
, uint32_t ignoreval
)
489 out
= _asl_get_32(*p
);
490 *p
+= sizeof(uint32_t);
492 if ((m
== NULL
) || (key
== NULL
)) return out
;
495 if ((ignore
!= 0) && (out
== ignoreval
)) doit
= 0;
498 snprintf(str
, sizeof(str
), "%u", out
);
499 asl_set(m
, key
, str
);
506 asl_legacy1_fetch_helper_64(asl_legacy1_t
*s
, char **p
, aslmsg m
, const char *key
)
511 out
= _asl_get_64(*p
);
512 *p
+= sizeof(uint64_t);
514 if ((m
== NULL
) || (key
== NULL
)) return out
;
516 snprintf(str
, sizeof(str
), "%llu", out
);
517 asl_set(m
, key
, str
);
523 asl_legacy1_fetch_helper_str(asl_legacy1_t
*s
, char **p
, aslmsg m
, const char *key
, uint32_t *err
)
529 out
= _asl_get_64(*p
);
530 *p
+= sizeof(uint64_t);
533 status
= ASL_STATUS_OK
;
534 if (out
!= 0) status
= string_fetch_sid(s
, out
, &val
);
536 if (err
!= NULL
) *err
= status
;
537 if ((status
== ASL_STATUS_OK
) && (val
!= NULL
))
539 asl_set(m
, key
, val
);
547 msg_fetch(asl_legacy1_t
*s
, uint32_t slot
, aslmsg
*out
)
550 uint32_t status
, i
, n
, kvcount
, next
;
554 char *p
, tmp
[DB_RECORD_LEN
], *key
, *val
;
556 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
557 if (out
== NULL
) return ASL_STATUS_INVALID_ARG
;
561 offset
= slot
* DB_RECORD_LEN
;
562 status
= fseek(s
->db
, offset
, SEEK_SET
);
564 if (status
< 0) return ASL_STATUS_READ_FAILED
;
566 status
= fread(tmp
, DB_RECORD_LEN
, 1, s
->db
);
567 if (status
!= 1) return ASL_STATUS_READ_FAILED
;
569 flags
= _asl_get_16(tmp
+ MSG_OFF_KEY_FLAGS
);
571 msg
= asl_new(ASL_TYPE_MSG
);
572 if (msg
== NULL
) return ASL_STATUS_NO_MEMORY
;
576 asl_legacy1_fetch_helper_64(s
, &p
, msg
, ASL_KEY_MSG_ID
);
577 asl_legacy1_fetch_helper_32(s
, &p
, msg
, ASL_KEY_READ_UID
, 1, (uint32_t)-1);
578 asl_legacy1_fetch_helper_32(s
, &p
, msg
, ASL_KEY_READ_GID
, 1, (uint32_t)-1);
579 asl_legacy1_fetch_helper_64(s
, &p
, msg
, ASL_KEY_TIME
);
580 asl_legacy1_fetch_helper_str(s
, &p
, msg
, ASL_KEY_HOST
, &status
);
581 asl_legacy1_fetch_helper_str(s
, &p
, msg
, ASL_KEY_SENDER
, &status
);
582 asl_legacy1_fetch_helper_str(s
, &p
, msg
, ASL_KEY_FACILITY
, &status
);
583 asl_legacy1_fetch_helper_32(s
, &p
, msg
, ASL_KEY_LEVEL
, 0, 0);
584 asl_legacy1_fetch_helper_32(s
, &p
, msg
, ASL_KEY_PID
, 0, 0);
585 asl_legacy1_fetch_helper_32(s
, &p
, msg
, ASL_KEY_UID
, 0, 0);
586 asl_legacy1_fetch_helper_32(s
, &p
, msg
, ASL_KEY_GID
, 0, 0);
587 asl_legacy1_fetch_helper_str(s
, &p
, msg
, ASL_KEY_MSG
, &status
);
589 next
= header_get_next(tmp
);
596 offset
= next
* DB_RECORD_LEN
;
597 status
= fseek(s
->db
, offset
, SEEK_SET
);
601 return ASL_STATUS_READ_FAILED
;
604 status
= fread(tmp
, DB_RECORD_LEN
, 1, s
->db
);
608 return ASL_STATUS_READ_FAILED
;
611 if (kvcount
== 0) kvcount
= _asl_get_32(tmp
+ 5);
615 for (i
= 0; (i
< 4) && (n
< kvcount
); i
++)
618 sid
= _asl_get_64(p
);
620 status
= string_fetch_sid(s
, sid
, &key
);
623 sid
= _asl_get_64(p
);
625 if (status
== ASL_STATUS_OK
) status
= string_fetch_sid(s
, sid
, &val
);
627 if ((status
== ASL_STATUS_OK
) && (key
!= NULL
)) asl_set(msg
, key
, val
);
628 if (key
!= NULL
) free(key
);
629 if (val
!= NULL
) free(val
);
634 next
= header_get_next(tmp
);
638 return ASL_STATUS_OK
;
642 asl_legacy1_fetch(asl_legacy1_t
*s
, uint64_t msgid
, aslmsg
*out
)
646 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
647 if (msgid
== ASL_REF_NULL
) return ASL_STATUS_INVALID_ARG
;
648 if (out
== NULL
) return ASL_STATUS_INVALID_ARG
;
650 i
= slotlist_find(s
, msgid
, 0);
651 if (i
== ASL_INDEX_NULL
) return ASL_STATUS_INVALID_ID
;
653 /* read the message */
654 status
= msg_fetch(s
, s
->slotlist
[i
].slot
, out
);
655 if (status
!= ASL_STATUS_OK
) return status
;
656 if (*out
== NULL
) return ASL_STATUS_FAILED
;
662 next_search_slot(asl_legacy1_t
*s
, uint32_t last_si
, int32_t direction
)
668 for (i
= last_si
+ 1; i
< s
->slotlist_count
; i
++)
670 if (s
->slotlist
[i
].type
== DB_TYPE_MESSAGE
) return i
;
673 return ASL_INDEX_NULL
;
676 if (last_si
== 0) return ASL_INDEX_NULL
;
677 if (last_si
> s
->slotlist_count
) return ASL_INDEX_NULL
;
679 for (i
= last_si
- 1; i
> 0; i
--)
681 if (s
->slotlist
[i
].type
== DB_TYPE_MESSAGE
) return i
;
684 if (s
->slotlist
[0].type
== DB_TYPE_MESSAGE
) return 0;
686 return ASL_INDEX_NULL
;
690 match_worker_cleanup(asl_msg_list_t
**res
)
696 for (i
= 0; i
< (*res
)->count
; i
++) asl_free((aslmsg
)(*res
)->msg
[i
]);
702 * Input to asl_legacy1_match is a list of queries.
703 * A record in the store matches if it matches any query (i.e. query list is "OR"ed)
705 * If counting up (direction is positive) find first record with ID > start_id.
706 * Else if counting down (direction is negative) find first record with ID < start_id.
709 * If any query is NULL, set match flog off (skips matching below).
710 * Else if all queries only check "standard" keys, set std flag to on.
712 * If all queries are marked as "never matches", return NULL.
715 * fetch record (with std flag)
716 * if match flag is off, decode record and add it to result.
717 * else for each query:
718 * if query is NULL (shouldn't happen) decode record and add it to result. Return to match loop.
719 * else if query never matches, ignore it.
720 * else decode record and use asl_cmp. If it succeeds, add record to result. Return to match loop.
725 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
)
727 uint32_t mx
, si
, slot
, i
, qcount
, match
, didmatch
, status
;
731 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
732 if ((res
== NULL
) && (idlist
== NULL
)) return ASL_STATUS_INVALID_ARG
;
733 if (last_id
== NULL
) return ASL_STATUS_INVALID_ARG
;
734 if (idcount
== NULL
) return ASL_STATUS_INVALID_ARG
;
736 if (res
!= NULL
) *res
= NULL
;
737 if (idlist
!= NULL
) *idlist
= NULL
;
741 if (direction
< 0) direction
= -1;
745 if ((direction
== -1) && (start_id
== ASL_REF_NULL
)) si
= s
->slotlist_count
;
746 else si
= slotlist_find(s
, start_id
, direction
);
748 si
= next_search_slot(s
, si
, direction
);
749 if (si
== ASL_INDEX_NULL
) return ASL_STATUS_OK
;
750 if (si
>= s
->slotlist_count
) return ASL_STATUS_FAILED
;
752 slot
= s
->slotlist
[si
].slot
;
757 if (query
== NULL
) match
= 0;
758 else if (query
->count
== 0) match
= 0;
759 else qcount
= query
->count
;
762 * initialize result list if we've been asked to return messages
766 *res
= (asl_msg_list_t
*)calloc(1, sizeof(asl_msg_list_t
));
767 if (*res
== NULL
) return ASL_STATUS_NO_MEMORY
;
770 status
= ASL_STATUS_OK
;
773 * loop through records
776 while ((count
== 0) || (*idcount
< count
))
778 if (si
== ASL_INDEX_NULL
) break;
779 if (si
>= s
->slotlist_count
) break;
781 slot
= s
->slotlist
[si
].slot
;
782 xid
= s
->slotlist
[si
].xid
;
786 status
= msg_fetch(s
, slot
, &msg
);
795 for (i
= 0; i
< qcount
; i
++)
797 didmatch
= asl_msg_cmp((aslmsg
)(query
->msg
[i
]), msg
);
798 if (didmatch
== 1) break;
804 if ((*res
)->count
== 0) (*res
)->msg
= (asl_msg_t
**)calloc(1, sizeof(asl_msg_t
*));
805 else (*res
)->msg
= (asl_msg_t
**)reallocf((*res
)->msg
, (1 + (*res
)->count
) * sizeof(asl_msg_t
*));
806 if ((*res
)->msg
== NULL
)
808 match_worker_cleanup(res
);
809 return ASL_STATUS_NO_MEMORY
;
812 (*res
)->msg
[(*res
)->count
++] = (asl_msg_t
*)msg
;
819 si
= next_search_slot(s
, si
, direction
);
826 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
)
831 return match_worker(s
, query
, res
, last_id
, NULL
, &idcount
, start_id
, count
, direction
);