2 * Copyright (c) 2007-2013 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@
33 #include <sys/errno.h>
36 #include <sys/types.h>
38 #include <membership.h>
41 #include <asl_private.h>
42 #include <asl_legacy1.h>
43 #include <TargetConditionals.h>
45 #define forever for(;;)
48 * MSG and STR records have (at least) a type (uint16_t) and a length (uint32_t)
49 * type and level are both 16 bit fields so that alignment isn't a pain.
51 #define RECORD_COMMON_LEN 6
52 #define RECORD_TYPE_LEN 2
53 #define BUFFER_OFFSET_KVCOUNT 56
55 #define SCRATCH_BUFFER_SIZE (MSG_RECORD_FIXED_LENGTH + (20 * sizeof(uint64_t)))
83 asl_file_list_t
*list
;
85 } asl_file_match_token_t
;
97 _asl_put_16(uint16_t i
, char *h
)
115 _asl_put_32(uint32_t i
, char *h
)
129 return asl_core_ntohq(x
);
133 _asl_put_64(uint64_t i
, char *h
)
137 x
= asl_core_htonq(i
);
142 asl_file_read_uint32(asl_file_t
*s
, off_t off
, uint32_t *out
)
144 uint32_t status
, val
;
146 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
147 if (s
->store
== NULL
) return ASL_STATUS_INVALID_STORE
;
148 if ((off
+ sizeof(uint32_t)) > s
->file_size
) return ASL_STATUS_READ_FAILED
;
150 status
= fseeko(s
->store
, off
, SEEK_SET
);
151 if (status
!= 0) return ASL_STATUS_READ_FAILED
;
155 status
= fread(&val
, sizeof(uint32_t), 1, s
->store
);
156 if (status
!= 1) return ASL_STATUS_READ_FAILED
;
158 if (out
!= NULL
) *out
= ntohl(val
);
159 return ASL_STATUS_OK
;
163 asl_file_read_uint64(asl_file_t
*s
, off_t off
, uint64_t *out
)
168 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
169 if (s
->store
== NULL
) return ASL_STATUS_INVALID_STORE
;
170 if ((off
+ sizeof(uint64_t)) > s
->file_size
) return ASL_STATUS_READ_FAILED
;
172 status
= fseeko(s
->store
, off
, SEEK_SET
);
173 if (status
!= 0) return ASL_STATUS_READ_FAILED
;
177 status
= fread(&val
, sizeof(uint64_t), 1, s
->store
);
178 if (status
!= 1) return ASL_STATUS_READ_FAILED
;
180 if (out
!= NULL
) *out
= asl_core_ntohq(val
);
181 return ASL_STATUS_OK
;
185 asl_file_retain(asl_file_t
*s
)
187 if (s
== NULL
) return NULL
;
188 asl_retain((asl_object_t
)s
);
193 asl_file_release(asl_file_t
*s
)
195 if (s
== NULL
) return;
196 asl_release((asl_object_t
)s
);
200 asl_file_close(asl_file_t
*s
)
202 if (s
== NULL
) return ASL_STATUS_OK
;
203 asl_release((asl_object_t
)s
);
204 return ASL_STATUS_OK
;
208 _asl_file_free_internal(asl_file_t
*s
)
212 if (s
== NULL
) return;
216 asl_legacy1_close((asl_legacy1_t
*)s
->legacy
);
220 while (s
->string_list
!= NULL
)
222 x
= s
->string_list
->next
;
223 free(s
->string_list
);
227 if (s
->store
!= NULL
) fclose(s
->store
);
228 if (s
->scratch
!= NULL
) free(s
->scratch
);
230 memset(s
, 0, sizeof(asl_file_t
));
234 __private_extern__ ASL_STATUS
235 asl_file_open_write_fd(int fd
, asl_file_t
**s
)
239 char buf
[DB_HEADER_LEN
];
242 if (fd
< 0) return ASL_STATUS_FAILED
;
243 if (s
== NULL
) return ASL_STATUS_FAILED
;
245 out
= (asl_file_t
*)calloc(1, sizeof(asl_file_t
));
246 if (out
== NULL
) return ASL_STATUS_NO_MEMORY
;
248 out
->asl_type
= ASL_TYPE_FILE
;
251 out
->store
= fdopen(fd
, "w+");
252 if (out
->store
== NULL
)
255 return ASL_STATUS_FAILED
;
258 memset(buf
, 0, sizeof(buf
));
259 memcpy(buf
, ASL_DB_COOKIE
, ASL_DB_COOKIE_LEN
);
261 _asl_put_32(DB_VERSION
, buf
+ DB_HEADER_VERS_OFFSET
);
265 _asl_put_64(out
->dob
, buf
+ DB_HEADER_TIME_OFFSET
);
267 _asl_put_32(CACHE_SIZE
, buf
+ DB_HEADER_CSIZE_OFFSET
);
269 status
= fwrite(buf
, sizeof(buf
), 1, out
->store
);
274 return ASL_STATUS_FAILED
;
280 out
->file_size
= sizeof(buf
);
282 /* scratch buffer for file writes (we test for NULL before using it) */
283 out
->scratch
= malloc(SCRATCH_BUFFER_SIZE
);
287 return ASL_STATUS_OK
;
290 __private_extern__
int
291 asl_file_create(const char *path
, uid_t uid
, gid_t gid
, mode_t mode
)
294 return open(path
, O_RDWR
| O_CREAT
| O_EXCL
, mode
);
303 /* -1 means don't set ACL for uid or gid */
304 if ((uid
== -1) && (gid
== -1))
306 return open(path
, O_RDWR
| O_CREAT
| O_EXCL
, mode
);
311 if ((gid
!= 0) && (gid
!= -1))
313 status
= mbr_gid_to_uuid(gid
, uuid
);
314 if (status
!= 0) goto asl_file_create_return
;
316 status
= acl_create_entry_np(&acl
, &entry
, ACL_FIRST_ENTRY
);
317 if (status
!= 0) goto asl_file_create_return
;
319 status
= acl_set_tag_type(entry
, ACL_EXTENDED_ALLOW
);
320 if (status
!= 0) goto asl_file_create_return
;
322 status
= acl_set_qualifier(entry
, &uuid
);
323 if (status
!= 0) goto asl_file_create_return
;
325 status
= acl_get_permset(entry
, &perms
);
326 if (status
!= 0) goto asl_file_create_return
;
328 status
= acl_add_perm(perms
, ACL_READ_DATA
);
329 if (status
!= 0) goto asl_file_create_return
;
332 if ((uid
!= 0) && (uid
!= -1))
334 status
= mbr_uid_to_uuid(uid
, uuid
);
335 if (status
!= 0) goto asl_file_create_return
;
337 status
= acl_create_entry_np(&acl
, &entry
, ACL_FIRST_ENTRY
);
338 if (status
!= 0) goto asl_file_create_return
;
340 status
= acl_set_tag_type(entry
, ACL_EXTENDED_ALLOW
);
341 if (status
!= 0) goto asl_file_create_return
;
343 status
= acl_set_qualifier(entry
, &uuid
);
344 if (status
!= 0) goto asl_file_create_return
;
346 status
= acl_get_permset(entry
, &perms
);
347 if (status
!= 0) goto asl_file_create_return
;
349 status
= acl_add_perm(perms
, ACL_READ_DATA
);
350 if (status
!= 0) goto asl_file_create_return
;
353 fd
= open(path
, O_RDWR
| O_CREAT
| O_EXCL
, mode
);
354 if (fd
< 0) goto asl_file_create_return
;
356 status
= acl_set_fd(fd
, acl
);
364 asl_file_create_return
:
372 asl_file_open_write(const char *path
, mode_t mode
, uid_t uid
, gid_t gid
, asl_file_t
**s
)
376 char buf
[DB_HEADER_LEN
];
378 uint32_t aslstatus
, vers
, last_len
;
381 memset(&sb
, 0, sizeof(struct stat
));
383 status
= stat(path
, &sb
);
386 /* must be a plain file */
387 if (!S_ISREG(sb
.st_mode
)) return ASL_STATUS_INVALID_STORE
;
390 * If the file exists, we go with the existing mode, uid, and gid.
395 fd
= open(path
, O_RDWR
| O_EXCL
, mode
);
396 if (fd
< 0) return ASL_STATUS_FAILED
;
398 return asl_file_open_write_fd(fd
, s
);
402 out
= (asl_file_t
*)calloc(1, sizeof(asl_file_t
));
403 if (out
== NULL
) return ASL_STATUS_NO_MEMORY
;
405 out
->asl_type
= ASL_TYPE_FILE
;
408 out
->store
= fopen(path
, "r+");
409 if (out
->store
== NULL
)
412 return ASL_STATUS_FAILED
;
415 i
= fread(buf
, DB_HEADER_LEN
, 1, out
->store
);
419 return ASL_STATUS_READ_FAILED
;
423 if (strncmp(buf
, ASL_DB_COOKIE
, ASL_DB_COOKIE_LEN
))
426 return ASL_STATUS_INVALID_STORE
;
430 vers
= _asl_get_32(buf
+ DB_HEADER_VERS_OFFSET
);
431 if (vers
!= DB_VERSION
)
434 return ASL_STATUS_INVALID_STORE
;
437 out
->dob
= _asl_get_64(buf
+ DB_HEADER_TIME_OFFSET
);
438 out
->first
= _asl_get_64(buf
+ DB_HEADER_FIRST_OFFSET
);
439 out
->last
= _asl_get_64(buf
+ DB_HEADER_LAST_OFFSET
);
440 out
->file_size
= (size_t)sb
.st_size
;
443 * Detect bogus last pointer and check for odd-sized files.
444 * Setting out->last to zero forces asl_file_read_set_position to
445 * follow the linked list of records in the file to the last record.
446 * It's slower, but it's better at preventing crashes in corrupt files.
449 /* records are at least MSG_RECORD_FIXED_LENGTH bytes */
450 if ((out
->last
+ MSG_RECORD_FIXED_LENGTH
) > out
->file_size
)
456 /* read last record length and make sure the file is at least that large */
457 off
= out
->last
+ RECORD_TYPE_LEN
;
458 status
= asl_file_read_uint32(out
, off
, &last_len
);
459 if (status
!= ASL_STATUS_OK
)
465 if ((out
->last
+ last_len
) > out
->file_size
) out
->last
= 0;
468 aslstatus
= asl_file_read_set_position(out
, ASL_FILE_POSITION_LAST
);
469 if (aslstatus
!= ASL_STATUS_OK
)
475 out
->prev
= out
->cursor
;
476 status
= fseeko(out
->store
, 0, SEEK_END
);
480 return ASL_STATUS_READ_FAILED
;
483 out
->file_size
= (size_t)ftello(out
->store
);
485 /* scratch buffer for file writes (we test for NULL before using it) */
486 out
->scratch
= malloc(SCRATCH_BUFFER_SIZE
);
490 return ASL_STATUS_OK
;
493 else if (errno
!= ENOENT
)
495 /* unexpected status */
496 return ASL_STATUS_FAILED
;
500 * If the file does not exist, we set the mode, uid, and gid.
503 fd
= asl_file_create(path
, uid
, gid
, mode
);
504 if (fd
< 0) return ASL_STATUS_FAILED
;
506 aslstatus
= asl_file_open_write_fd(fd
, s
);
507 if (aslstatus
!= ASL_STATUS_OK
) unlink(path
);
513 asl_file_compact(asl_file_t
*s
, const char *path
, mode_t mode
, uid_t uid
, gid_t gid
)
521 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
522 if (path
== NULL
) return ASL_STATUS_INVALID_ARG
;
524 if (s
->version
== 1) return ASL_STATUS_FAILED
;
526 memset(&sb
, 0, sizeof(struct stat
));
528 if (stat(path
, &sb
) == 0) return ASL_STATUS_FAILED
;
529 if (errno
!= ENOENT
) return ASL_STATUS_FAILED
;
531 status
= asl_file_read_set_position(s
, ASL_FILE_POSITION_FIRST
);
532 if (status
!= ASL_STATUS_OK
) return status
;
535 status
= asl_file_open_write(path
, mode
, uid
, gid
, &new);
536 if (status
!= ASL_STATUS_OK
) return status
;
537 new->flags
= ASL_FILE_FLAG_UNLIMITED_CACHE
| ASL_FILE_FLAG_PRESERVE_MSG_ID
;
539 while ((status
== ASL_STATUS_OK
) && (s
->cursor
!= 0))
542 status
= asl_file_fetch_next(s
, &m
);
543 if (status
!= ASL_STATUS_OK
) break;
546 status
= asl_file_save(new, m
, &xid
);
555 asl_file_filter(asl_file_t
*s
, const char *path
, asl_msg_list_t
*filter
, uint32_t flags
, mode_t mode
, uid_t uid
, gid_t gid
, uint32_t *dstcount
, void (*aux_callback
)(const char *auxfile
))
561 uint32_t status
, n
= 0;
562 uint32_t matchflag
= flags
& ASL_FILE_FILTER_FLAG_KEEP_MATCHES
;
564 if (dstcount
!= NULL
) *dstcount
= n
;
566 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
567 if (path
== NULL
) return ASL_STATUS_INVALID_ARG
;
569 if (s
->version
== 1) return ASL_STATUS_FAILED
;
571 memset(&sb
, 0, sizeof(struct stat
));
573 if (stat(path
, &sb
) == 0) return ASL_STATUS_FAILED
;
574 if (errno
!= ENOENT
) return ASL_STATUS_FAILED
;
576 status
= asl_file_read_set_position(s
, ASL_FILE_POSITION_FIRST
);
577 if (status
!= ASL_STATUS_OK
) return status
;
580 status
= asl_file_open_write(path
, mode
, uid
, gid
, &new);
581 if (status
!= ASL_STATUS_OK
) return status
;
582 new->flags
= ASL_FILE_FLAG_UNLIMITED_CACHE
| ASL_FILE_FLAG_PRESERVE_MSG_ID
;
584 while ((status
== ASL_STATUS_OK
) && (s
->cursor
!= 0))
587 status
= asl_file_fetch_next(s
, &m
);
588 if (status
!= ASL_STATUS_OK
) break;
591 * asl_msg_cmp_list is supposed to return 1 for a match, 0 otherwise,
592 * but just to be sure we only get a 1 or zero, we do an extra test.
594 uint32_t msgmatch
= (asl_msg_cmp_list(m
, filter
) == 0) ? 0 : 1;
595 if (msgmatch
== matchflag
)
597 status
= asl_file_save(new, m
, &xid
);
598 if (status
== ASL_STATUS_OK
) n
++;
600 else if (aux_callback
!= NULL
)
602 /* check for ASL_KEY_AUX_URL and pass it to callback */
603 const char *auxval
= asl_msg_get_val_for_key(m
, ASL_KEY_AUX_URL
);
604 if (auxval
!= NULL
) aux_callback(auxval
);
611 if (dstcount
!= NULL
) *dstcount
= n
;
616 asl_file_filter_level(asl_file_t
*s
, const char *path
, uint32_t keep_mask
, mode_t mode
, uid_t uid
, gid_t gid
, uint32_t *dstcount
, void (*aux_callback
)(const char *auxfile
))
622 uint32_t status
, n
= 0;
623 uint8_t kmcur
, kmnew
;
625 if (dstcount
!= NULL
) *dstcount
= n
;
627 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
628 if (path
== NULL
) return ASL_STATUS_INVALID_ARG
;
630 if (s
->version
== 1) return ASL_STATUS_FAILED
;
632 memset(&sb
, 0, sizeof(struct stat
));
634 if (stat(path
, &sb
) == 0) return ASL_STATUS_FAILED
;
635 if (errno
!= ENOENT
) return ASL_STATUS_FAILED
;
639 /* get current filter mask in file's header */
640 status
= fseeko(s
->store
, DB_HEADER_FILTER_MASK_OFFSET
, SEEK_SET
);
641 if (status
!= 0) return ASL_STATUS_READ_FAILED
;
642 fread(&kmcur
, 1, 1, s
->store
);
644 status
= asl_file_read_set_position(s
, ASL_FILE_POSITION_FIRST
);
645 if (status
!= ASL_STATUS_OK
) return status
;
648 status
= asl_file_open_write(path
, mode
, uid
, gid
, &new);
649 if (status
!= ASL_STATUS_OK
) return status
;
651 new->flags
= ASL_FILE_FLAG_UNLIMITED_CACHE
| ASL_FILE_FLAG_PRESERVE_MSG_ID
;
653 while ((status
== ASL_STATUS_OK
) && (s
->cursor
!= 0))
656 status
= asl_file_fetch_next(s
, &m
);
657 if (status
!= ASL_STATUS_OK
) break;
658 if (m
== NULL
) continue;
659 const char *lval
= asl_msg_get_val_for_key(m
, ASL_KEY_LEVEL
);
660 if (lval
== NULL
) continue;
661 uint32_t level_bit
= 0x01 << atoi(lval
);
663 if (level_bit
& keep_mask
)
665 status
= asl_file_save(new, m
, &xid
);
666 if (status
== ASL_STATUS_OK
) n
++;
668 else if (aux_callback
!= NULL
)
670 /* check for ASL_KEY_AUX_URL and pass it to callback */
671 const char *auxval
= asl_msg_get_val_for_key(m
, ASL_KEY_AUX_URL
);
672 if (auxval
!= NULL
) aux_callback(auxval
);
678 kmnew
= kmcur
& kmnew
;
679 status
= fseeko(new->store
, DB_HEADER_FILTER_MASK_OFFSET
, SEEK_SET
);
680 if (status
!= 0) return ASL_STATUS_READ_FAILED
;
681 fwrite(&kmnew
, 1, 1, new->store
);
684 if (dstcount
!= NULL
) *dstcount
= n
;
689 asl_file_string_encode(asl_file_t
*s
, const char *str
, uint64_t *out
)
691 uint32_t i
, hash
, len
, x32
;
692 file_string_t
*sp
, *sx
, *sl
;
699 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
700 if (s
->store
== NULL
) return ASL_STATUS_INVALID_STORE
;
701 if (str
== NULL
) return ASL_STATUS_INVALID_ARG
;
715 memcpy(p
+ 1, str
, len
);
716 *out
= asl_core_ntohq(x64
);
717 return ASL_STATUS_OK
;
720 /* check the cache */
721 hash
= asl_core_string_hash(str
, len
);
724 for (sx
= s
->string_list
; sx
!= NULL
; sx
= sx
->next
)
726 if ((hash
== sx
->hash
) && (!strcmp(str
, sx
->str
)))
728 /* Move this string to the head of the list */
738 return ASL_STATUS_OK
;
744 off
= ftello(s
->store
);
747 type
= htons(ASL_FILE_TYPE_STR
);
748 i
= fwrite(&type
, sizeof(uint16_t), 1, s
->store
);
749 if (i
!= 1) return ASL_STATUS_WRITE_FAILED
;
751 /* Length (includes trailing nul) */
752 x32
= htonl(len
+ 1);
753 i
= fwrite(&x32
, sizeof(uint32_t), 1, s
->store
);
754 if (i
!= 1) return ASL_STATUS_WRITE_FAILED
;
756 /* String data (nul terminated) */
757 i
= fwrite(str
, len
+ 1, 1, s
->store
);
758 if (i
!= 1) return ASL_STATUS_WRITE_FAILED
;
763 /* create file_string_t and insert into the cache */
764 sx
= (file_string_t
*)calloc(1, offsetof(file_string_t
, str
) + len
+ 1);
765 if (sx
== NULL
) return ASL_STATUS_NO_MEMORY
;
769 sx
->next
= s
->string_list
;
770 memcpy(sx
->str
, str
, len
);
774 if (((s
->flags
& ASL_FILE_FLAG_UNLIMITED_CACHE
) == 0) && (s
->string_cache_count
== CACHE_SIZE
))
776 /* drop last (lru) string from cache */
780 /* NB CACHE_SIZE must be > 1 */
781 while (sx
->next
!= NULL
)
792 s
->string_cache_count
++;
796 return ASL_STATUS_OK
;
800 * Encode an asl_msg_t *as a record structure.
801 * Creates and caches strings.
804 asl_file_save(asl_file_t
*s
, asl_msg_t
*in
, uint64_t *mid
)
807 uint32_t i
, len
, x
, status
;
813 const char *key
, *val
;
815 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
816 if (s
->store
== NULL
) return ASL_STATUS_INVALID_STORE
;
817 if (in
== NULL
) return ASL_STATUS_INVALID_MESSAGE
;
819 if (s
->flags
& ASL_FILE_FLAG_READ
) return ASL_STATUS_READ_ONLY
;
821 msg
= (asl_msg_t
*)in
;
823 memset(&r
, 0, sizeof(file_record_t
));
826 r
.level
= ASL_LEVEL_DEBUG
;
840 for (x
= asl_msg_fetch(msg
, 0, &key
, &val
, NULL
); x
!= IndexNull
; x
= asl_msg_fetch(msg
, x
, &key
, &val
, NULL
))
846 else if (!strcmp(key
, ASL_KEY_TIME
))
848 if (val
!= NULL
) r
.time
= asl_core_parse_time(val
, NULL
);
850 else if (!strcmp(key
, ASL_KEY_TIME_NSEC
))
852 if (val
!= NULL
) r
.nano
= atoi(val
);
854 else if (!strcmp(key
, ASL_KEY_HOST
))
858 status
= asl_file_string_encode(s
, val
, &(r
.host
));
859 if (status
!= ASL_STATUS_OK
)
861 if (kvlist
!= NULL
) free(kvlist
);
866 else if (!strcmp(key
, ASL_KEY_SENDER
))
870 status
= asl_file_string_encode(s
, val
, &(r
.sender
));
871 if (status
!= ASL_STATUS_OK
)
873 if (kvlist
!= NULL
) free(kvlist
);
878 else if (!strcmp(key
, ASL_KEY_PID
))
880 if (val
!= NULL
) r
.pid
= atoi(val
);
882 else if (!strcmp(key
, ASL_KEY_REF_PID
))
884 if (val
!= NULL
) r
.refpid
= atoi(val
);
886 else if (!strcmp(key
, ASL_KEY_UID
))
888 if (val
!= NULL
) r
.uid
= atoi(val
);
890 else if (!strcmp(key
, ASL_KEY_GID
))
892 if (val
!= NULL
) r
.gid
= atoi(val
);
894 else if (!strcmp(key
, ASL_KEY_LEVEL
))
896 if (val
!= NULL
) r
.level
= atoi(val
);
898 else if (!strcmp(key
, ASL_KEY_MSG
))
902 status
= asl_file_string_encode(s
, val
, &(r
.message
));
903 if (status
!= ASL_STATUS_OK
)
905 if (kvlist
!= NULL
) free(kvlist
);
910 else if (!strcmp(key
, ASL_KEY_FACILITY
))
914 status
= asl_file_string_encode(s
, val
, &(r
.facility
));
915 if (status
!= ASL_STATUS_OK
)
917 if (kvlist
!= NULL
) free(kvlist
);
922 else if (!strcmp(key
, ASL_KEY_REF_PROC
))
926 status
= asl_file_string_encode(s
, val
, &(r
.refproc
));
927 if (status
!= ASL_STATUS_OK
)
929 if (kvlist
!= NULL
) free(kvlist
);
934 else if (!strcmp(key
, ASL_KEY_SESSION
))
938 status
= asl_file_string_encode(s
, val
, &(r
.session
));
939 if (status
!= ASL_STATUS_OK
)
941 if (kvlist
!= NULL
) free(kvlist
);
946 else if (!strcmp(key
, ASL_KEY_READ_UID
))
948 if (((r
.flags
& ASL_MSG_FLAG_READ_UID_SET
) == 0) && (val
!= NULL
))
951 r
.flags
|= ASL_MSG_FLAG_READ_UID_SET
;
954 else if (!strcmp(key
, ASL_KEY_READ_GID
))
956 if (((r
.flags
& ASL_MSG_FLAG_READ_GID_SET
) == 0) && (val
!= NULL
))
959 r
.flags
|= ASL_MSG_FLAG_READ_GID_SET
;
962 else if (!strcmp(key
, ASL_KEY_MSG_ID
))
964 if (s
->flags
& ASL_FILE_FLAG_PRESERVE_MSG_ID
) *mid
= atoll(val
);
966 else if (!strcmp(key
, ASL_KEY_OPTION
))
968 /* ignore - we don't save ASLOption */
972 status
= asl_file_string_encode(s
, key
, &k
);
973 if (status
!= ASL_STATUS_OK
)
975 if (kvlist
!= NULL
) free(kvlist
);
982 status
= asl_file_string_encode(s
, val
, &v
);
983 if (status
!= ASL_STATUS_OK
)
985 if (kvlist
!= NULL
) free(kvlist
);
992 kvlist
= (uint64_t *)calloc(2, sizeof(uint64_t));
996 kvlist
= (uint64_t *)reallocf(kvlist
, (r
.kvcount
+ 2) * sizeof(uint64_t));
1001 return ASL_STATUS_NO_MEMORY
;
1004 kvlist
[r
.kvcount
++] = k
;
1005 kvlist
[r
.kvcount
++] = v
;
1009 len
= MSG_RECORD_FIXED_LENGTH
+ (r
.kvcount
* sizeof(uint64_t));
1012 /* use the scratch buffer if it exists and is large enough */
1013 if ((s
->scratch
!= NULL
) && (len
<= SCRATCH_BUFFER_SIZE
))
1015 memset(s
->scratch
, 0, SCRATCH_BUFFER_SIZE
);
1020 buf
= calloc(1, len
);
1023 if (buf
== NULL
) return ASL_STATUS_NO_MEMORY
;
1031 r
.mid
= asl_core_new_msg_id(0);
1038 _asl_put_16(ASL_FILE_TYPE_MSG
, p
);
1039 p
+= sizeof(uint16_t);
1041 /* Length of message (excludes type and length fields) */
1042 _asl_put_32(len
- RECORD_COMMON_LEN
, p
);
1043 p
+= sizeof(uint32_t);
1045 /* Message data... */
1047 _asl_put_64(r
.next
, p
);
1048 p
+= sizeof(uint64_t);
1050 _asl_put_64(r
.mid
, p
);
1051 p
+= sizeof(uint64_t);
1053 _asl_put_64(r
.time
, p
);
1054 p
+= sizeof(uint64_t);
1056 _asl_put_32(r
.nano
, p
);
1057 p
+= sizeof(uint32_t);
1059 _asl_put_16(r
.level
, p
);
1060 p
+= sizeof(uint16_t);
1062 _asl_put_16(r
.flags
, p
);
1063 p
+= sizeof(uint16_t);
1065 _asl_put_32(r
.pid
, p
);
1066 p
+= sizeof(uint32_t);
1068 _asl_put_32(r
.uid
, p
);
1069 p
+= sizeof(uint32_t);
1071 _asl_put_32(r
.gid
, p
);
1072 p
+= sizeof(uint32_t);
1074 _asl_put_32(r
.ruid
, p
);
1075 p
+= sizeof(uint32_t);
1077 _asl_put_32(r
.rgid
, p
);
1078 p
+= sizeof(uint32_t);
1080 _asl_put_32(r
.refpid
, p
);
1081 p
+= sizeof(uint32_t);
1083 _asl_put_32(r
.kvcount
, p
);
1084 p
+= sizeof(uint32_t);
1086 _asl_put_64(r
.host
, p
);
1087 p
+= sizeof(uint64_t);
1089 _asl_put_64(r
.sender
, p
);
1090 p
+= sizeof(uint64_t);
1092 _asl_put_64(r
.facility
, p
);
1093 p
+= sizeof(uint64_t);
1095 _asl_put_64(r
.message
, p
);
1096 p
+= sizeof(uint64_t);
1098 _asl_put_64(r
.refproc
, p
);
1099 p
+= sizeof(uint64_t);
1101 _asl_put_64(r
.session
, p
);
1102 p
+= sizeof(uint64_t);
1104 for (i
= 0; i
< r
.kvcount
; i
++)
1106 _asl_put_64(kvlist
[i
], p
);
1107 p
+= sizeof(uint64_t);
1110 _asl_put_64(r
.prev
, p
);
1111 p
+= sizeof(uint64_t);
1116 /* write record at end of file */
1117 status
= fseeko(s
->store
, 0, SEEK_END
);
1118 if (status
!= 0) return ASL_STATUS_WRITE_FAILED
;
1120 s
->last
= (uint64_t)ftello(s
->store
);
1122 v
= asl_core_htonq(s
->last
);
1124 status
= fwrite(buf
, len
, 1, s
->store
);
1127 /* free the buffer if it was allocated here */
1128 if (buf
!= s
->scratch
) free(buf
);
1130 /* seek to "next" field of previous record, write last offset */
1131 off
= s
->prev
+ RECORD_COMMON_LEN
;
1132 if (s
->prev
== 0) off
= DB_HEADER_FIRST_OFFSET
;
1134 status
= fseeko(s
->store
, off
, SEEK_SET
);
1135 if (status
!= 0) return ASL_STATUS_WRITE_FAILED
;
1137 status
= fwrite(&v
, sizeof(uint64_t), 1, s
->store
);
1138 if (status
!= 1) return ASL_STATUS_WRITE_FAILED
;
1140 /* seek to DB_HEADER_LAST_OFFSET, write last record offset */
1141 off
= DB_HEADER_LAST_OFFSET
;
1143 status
= fseeko(s
->store
, off
, SEEK_SET
);
1144 if (status
!= 0) return ASL_STATUS_WRITE_FAILED
;
1146 status
= fwrite(&v
, sizeof(uint64_t), 1, s
->store
);
1147 if (status
!= 1) return ASL_STATUS_WRITE_FAILED
;
1149 /* return to the end of the store (this is expected by other routines) */
1150 status
= fseeko(s
->store
, 0, SEEK_END
);
1151 if (status
!= 0) return ASL_STATUS_WRITE_FAILED
;
1156 s
->file_size
= (size_t)ftello(s
->store
);
1160 return ASL_STATUS_OK
;
1164 asl_file_fetch_object(asl_file_t
*s
, uint64_t where
, char **out
, uint32_t *outlen
)
1175 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
1176 if (s
->store
== NULL
) return ASL_STATUS_INVALID_STORE
;
1177 if (out
== NULL
) return ASL_STATUS_INVALID_ARG
;
1178 if (outlen
== NULL
) return ASL_STATUS_INVALID_ARG
;
1179 if (where
== 0) return ASL_STATUS_INVALID_ARG
;
1185 x64
= asl_core_htonq(where
);
1186 memcpy(&inls
, &x64
, 1);
1191 if (inls
> 7) return ASL_STATUS_INVALID_STORE
;
1193 p
= 1 + (char *)&x64
;
1194 memset(ils
, 0, sizeof(ils
));
1195 memcpy(ils
, p
, inls
);
1197 if (*out
== NULL
) return ASL_STATUS_NO_MEMORY
;
1200 return ASL_STATUS_OK
;
1204 if ((off
+ sizeof(uint16_t) + sizeof(uint32_t)) > s
->file_size
) return ASL_STATUS_READ_FAILED
;
1206 status
= fseeko(s
->store
, off
, SEEK_SET
);
1207 if (status
!= 0) return ASL_STATUS_READ_FAILED
;
1210 status
= fread(&type
, sizeof(uint16_t), 1, s
->store
);
1211 if (status
!= 1) return ASL_STATUS_READ_FAILED
;
1212 off
+= sizeof(uint16_t);
1216 status
= fread(&len
, sizeof(uint32_t), 1, s
->store
);
1217 if (status
!= 1) return ASL_STATUS_READ_FAILED
;
1218 off
+= sizeof(uint32_t);
1221 if ((off
+ len
) > s
->file_size
) return ASL_STATUS_READ_FAILED
;
1223 *out
= calloc(1, len
);
1224 if (*out
== NULL
) return ASL_STATUS_NO_MEMORY
;
1226 status
= fread(*out
, len
, 1, s
->store
);
1231 return ASL_STATUS_READ_FAILED
;
1235 return ASL_STATUS_OK
;
1239 asl_file_fetch_helper_16(asl_file_t
*s
, char **p
, asl_msg_t
*m
, const char *key
)
1244 out
= _asl_get_16(*p
);
1245 *p
+= sizeof(uint16_t);
1247 if ((m
== NULL
) || (key
== NULL
)) return out
;
1249 snprintf(str
, sizeof(str
), "%hu", out
);
1250 asl_msg_set_key_val(m
, key
, str
);
1256 asl_file_fetch_helper_32(asl_file_t
*s
, char **p
, asl_msg_t
*m
, const char *key
, int ignore
, uint32_t ignoreval
)
1261 out
= _asl_get_32(*p
);
1262 *p
+= sizeof(uint32_t);
1264 if ((m
== NULL
) || (key
== NULL
)) return out
;
1267 if ((ignore
!= 0) && (out
== ignoreval
)) doit
= 0;
1270 snprintf(str
, sizeof(str
), "%u", out
);
1271 asl_msg_set_key_val(m
, key
, str
);
1278 asl_file_fetch_helper_64(asl_file_t
*s
, char **p
, asl_msg_t
*m
, const char *key
)
1283 out
= _asl_get_64(*p
);
1284 *p
+= sizeof(uint64_t);
1286 if ((m
== NULL
) || (key
== NULL
)) return out
;
1288 snprintf(str
, sizeof(str
), "%llu", out
);
1289 asl_msg_set_key_val(m
, key
, str
);
1295 asl_file_fetch_helper_str(asl_file_t
*s
, char **p
, asl_msg_t
*m
, const char *key
, uint32_t *err
)
1299 uint32_t status
, len
;
1301 out
= _asl_get_64(*p
);
1302 *p
+= sizeof(uint64_t);
1306 status
= ASL_STATUS_OK
;
1307 if (out
!= 0) status
= asl_file_fetch_object(s
, out
, &val
, &len
);
1309 if (err
!= NULL
) *err
= status
;
1310 if ((status
== ASL_STATUS_OK
) && (val
!= NULL
))
1312 asl_msg_set_key_val(m
, key
, val
);
1320 asl_file_fetch_pos(asl_file_t
*s
, uint64_t where
, int dir
, asl_msg_t
**msg
)
1322 char *buf
, *p
, *k
, *v
;
1324 uint32_t i
, status
, len
, buflen
, kvn
;
1329 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
1330 if (s
->store
== NULL
) return ASL_STATUS_INVALID_STORE
;
1331 if (msg
== NULL
) return ASL_STATUS_INVALID_ARG
;
1332 if ((s
->flags
& ASL_FILE_FLAG_READ
) == 0) return ASL_STATUS_WRITE_ONLY
;
1336 status
= asl_file_fetch_object(s
, where
, &buf
, &buflen
);
1337 if ((status
!= ASL_STATUS_OK
) || (buf
== NULL
))
1344 /* check buffer size */
1345 kvn
= _asl_get_32(buf
+ BUFFER_OFFSET_KVCOUNT
);
1346 if (buflen
< (MSG_RECORD_FIXED_LENGTH
- RECORD_COMMON_LEN
+ (kvn
* sizeof(uint64_t))))
1351 return ASL_STATUS_READ_FAILED
;
1354 out
= asl_msg_new(ASL_TYPE_MSG
);
1358 return ASL_STATUS_NO_MEMORY
;
1361 memset(&r
, 0, sizeof(file_record_t
));
1364 r
.next
= asl_file_fetch_helper_64(s
, &p
, NULL
, NULL
);
1365 r
.mid
= asl_file_fetch_helper_64(s
, &p
, out
, ASL_KEY_MSG_ID
);
1366 r
.time
= asl_file_fetch_helper_64(s
, &p
, out
, ASL_KEY_TIME
);
1367 r
.nano
= asl_file_fetch_helper_32(s
, &p
, out
, ASL_KEY_TIME_NSEC
, 0, 0);
1368 r
.level
= asl_file_fetch_helper_16(s
, &p
, out
, ASL_KEY_LEVEL
);
1369 r
.flags
= asl_file_fetch_helper_16(s
, &p
, NULL
, NULL
);
1370 r
.pid
= asl_file_fetch_helper_32(s
, &p
, out
, ASL_KEY_PID
, 0, 0);
1371 r
.uid
= asl_file_fetch_helper_32(s
, &p
, out
, ASL_KEY_UID
, 1, (uint32_t)-1);
1372 r
.gid
= asl_file_fetch_helper_32(s
, &p
, out
, ASL_KEY_GID
, 1, (uint32_t)-1);
1373 r
.ruid
= asl_file_fetch_helper_32(s
, &p
, out
, ASL_KEY_READ_UID
, 1, (uint32_t)-1);
1374 r
.rgid
= asl_file_fetch_helper_32(s
, &p
, out
, ASL_KEY_READ_GID
, 1, (uint32_t)-1);
1375 r
.refpid
= asl_file_fetch_helper_32(s
, &p
, out
, ASL_KEY_REF_PID
, 1, 0);
1376 r
.kvcount
= asl_file_fetch_helper_32(s
, &p
, NULL
, NULL
, 0, 0);
1378 status
= ASL_STATUS_OK
;
1379 r
.host
= asl_file_fetch_helper_str(s
, &p
, out
, ASL_KEY_HOST
, &status
); /* 68 */
1380 if (status
== ASL_STATUS_OK
) r
.sender
= asl_file_fetch_helper_str(s
, &p
, out
, ASL_KEY_SENDER
, &status
); /* 76 */
1381 if (status
== ASL_STATUS_OK
) r
.facility
= asl_file_fetch_helper_str(s
, &p
, out
, ASL_KEY_FACILITY
, &status
); /* 84 */
1382 if (status
== ASL_STATUS_OK
) r
.message
= asl_file_fetch_helper_str(s
, &p
, out
, ASL_KEY_MSG
, &status
); /* 92 */
1383 if (status
== ASL_STATUS_OK
) r
.refproc
= asl_file_fetch_helper_str(s
, &p
, out
, ASL_KEY_REF_PROC
, &status
); /* 100 */
1384 if (status
== ASL_STATUS_OK
) r
.session
= asl_file_fetch_helper_str(s
, &p
, out
, ASL_KEY_SESSION
, &status
); /* 108 */
1386 if (status
!= ASL_STATUS_OK
)
1388 asl_msg_release(out
);
1395 kvn
= r
.kvcount
/ 2;
1397 for (i
= 0; i
< kvn
; i
++)
1403 kv
= _asl_get_64(p
);
1404 p
+= sizeof(uint64_t);
1406 status
= asl_file_fetch_object(s
, kv
, &k
, &len
);
1407 if (status
!= ASL_STATUS_OK
)
1409 asl_msg_release(out
);
1416 kv
= _asl_get_64(p
);
1417 p
+= sizeof(uint64_t);
1422 status
= asl_file_fetch_object(s
, kv
, &v
, &len
);
1423 if (status
!= ASL_STATUS_OK
)
1426 asl_msg_release(out
);
1434 if ((status
== ASL_STATUS_OK
) && (k
!= NULL
))
1436 asl_msg_set_key_val(out
, k
, v
);
1443 r
.prev
= asl_file_fetch_helper_64(s
, &p
, NULL
, NULL
); /* 116 */
1449 if ((r
.next
!= 0) && (r
.next
<= s
->cursor
))
1452 * Next offset goes backwards or loops.
1453 * The database is corrupt, but we allow this call to fail
1454 * quietly so that the current record fetch succeeds.
1458 return ASL_STATUS_OK
;
1465 if ((r
.prev
!= 0) && (r
.prev
>= s
->cursor
))
1468 * Prev offset goes forward or loops.
1469 * The database is corrupt, but we allow this call to fail
1470 * quietly so that the current record fetch succeeds.
1474 return ASL_STATUS_OK
;
1484 off
= s
->cursor
+ RECORD_COMMON_LEN
+ sizeof(uint64_t);
1485 if (off
> s
->file_size
)
1490 * Next record offset is past the end of the file.
1491 * This is an error, but we allow it to fail quietly
1492 * so that the current record fetch succeeds.
1495 return ASL_STATUS_OK
;
1498 status
= fseeko(s
->store
, off
, SEEK_SET
);
1501 asl_msg_release(out
);
1504 return ASL_STATUS_READ_FAILED
;
1507 status
= fread(&x64
, sizeof(uint64_t), 1, s
->store
);
1510 asl_msg_release(out
);
1513 return ASL_STATUS_READ_FAILED
;
1516 s
->cursor_xid
= asl_core_ntohq(x64
);
1520 return ASL_STATUS_OK
;
1524 asl_file_open_read(const char *path
, asl_file_t
**s
)
1529 uint32_t status
, vers
, last_len
;
1530 char buf
[DB_HEADER_LEN
];
1532 asl_legacy1_t
*legacy
;
1535 memset(&sb
, 0, sizeof(struct stat
));
1536 if (stat(path
, &sb
) != 0) return ASL_STATUS_FAILED
;
1538 f
= fopen(path
, "r");
1541 if (errno
== EACCES
) return ASL_STATUS_ACCESS_DENIED
;
1542 return ASL_STATUS_FAILED
;
1545 i
= fread(buf
, DB_HEADER_LEN
, 1, f
);
1549 return ASL_STATUS_INVALID_STORE
;
1552 /* validate header */
1553 if (strncmp(buf
, ASL_DB_COOKIE
, ASL_DB_COOKIE_LEN
))
1556 return ASL_STATUS_INVALID_STORE
;
1561 vers
= _asl_get_32(buf
+ DB_HEADER_VERS_OFFSET
);
1562 if (vers
== DB_VERSION_LEGACY_1
)
1565 status
= asl_legacy1_open(path
, &legacy
);
1566 if (status
!= ASL_STATUS_OK
) return status
;
1569 out
= (asl_file_t
*)calloc(1, sizeof(asl_file_t
));
1573 return ASL_STATUS_NO_MEMORY
;
1576 out
->asl_type
= ASL_TYPE_FILE
;
1580 out
->flags
= ASL_FILE_FLAG_READ
;
1581 out
->version
= vers
;
1585 out
->flags
|= ASL_FILE_FLAG_LEGACY_STORE
;
1586 out
->legacy
= (void *)legacy
;
1589 return ASL_STATUS_OK
;
1592 out
->first
= _asl_get_64(buf
+ DB_HEADER_FIRST_OFFSET
);
1593 out
->last
= _asl_get_64(buf
+ DB_HEADER_LAST_OFFSET
);
1594 out
->file_size
= (size_t)sb
.st_size
;
1597 * Detect bogus last pointer and check for odd-sized files.
1598 * Setting out->last to zero forces us to follow the linked
1599 * list of records in the file to the last record. That's
1600 * done in the set_position code. It's a bit slower, but it's
1601 * better at preventing crashes in corrupt files.
1604 /* records are at least MSG_RECORD_FIXED_LENGTH bytes */
1605 if ((out
->last
+ MSG_RECORD_FIXED_LENGTH
) > out
->file_size
)
1611 /* read last record length and make sure the file is at least that large */
1612 off
= out
->last
+ RECORD_TYPE_LEN
;
1613 status
= asl_file_read_uint32(out
, off
, &last_len
);
1614 if (status
!= ASL_STATUS_OK
)
1621 if ((out
->last
+ last_len
) > out
->file_size
) out
->last
= 0;
1624 out
->cursor
= out
->first
;
1625 if (out
->cursor
!= 0)
1627 off
= out
->cursor
+ RECORD_COMMON_LEN
+ sizeof(uint64_t);
1628 status
= asl_file_read_uint64(out
, off
, &(out
->cursor_xid
));
1629 if (status
!= ASL_STATUS_OK
)
1638 return ASL_STATUS_OK
;
1642 asl_file_read_set_position_first(asl_file_t
*s
)
1647 s
->cursor
= s
->first
;
1650 if (s
->cursor
== 0) return ASL_STATUS_OK
;
1652 /* read ID of the first record */
1653 off
= s
->cursor
+ RECORD_COMMON_LEN
+ sizeof(uint64_t);
1654 status
= asl_file_read_uint64(s
, off
, &(s
->cursor_xid
));
1659 asl_file_read_set_position_last(asl_file_t
*s
, int do_count
)
1666 * If the file has the offset of the last record, we just go there.
1667 * The last record offset was added to improve performance, so it may
1668 * or may not be there. If we don't have the last record offset, we
1669 * just iterate down the record links to find the last one.
1671 * Note that s->last may be zero if the file is empty.
1674 if ((s
->last
!= 0) && (do_count
== 0))
1676 s
->cursor
= s
->last
;
1677 off
= s
->last
+ RECORD_COMMON_LEN
+ sizeof(uint64_t);
1679 /* read ID of the last record */
1680 status
= asl_file_read_uint64(s
, off
, &(s
->cursor_xid
));
1684 /* start at the first record and iterate */
1685 s
->cursor
= s
->first
;
1691 off
= s
->cursor
+ RECORD_COMMON_LEN
;
1694 /* read next offset */
1695 status
= asl_file_read_uint64(s
, off
, &next
);
1696 if (status
!= ASL_STATUS_OK
) return status
;
1698 /* detect bogus next pointer */
1699 if (((next
+ MSG_RECORD_FIXED_LENGTH
) > s
->file_size
) || (next
<= s
->cursor
)) next
= 0;
1705 if (s
->cursor
== 0) return ASL_STATUS_OK
;
1707 off
= s
->cursor
+ RECORD_COMMON_LEN
+ sizeof(uint64_t);
1708 status
= asl_file_read_uint64(s
, off
, &(s
->cursor_xid
));
1709 return ASL_STATUS_OK
;
1717 asl_file_read_set_position(asl_file_t
*s
, uint32_t pos
)
1720 uint32_t len
, status
;
1723 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
1724 if (s
->version
== 1) return ASL_STATUS_FAILED
;
1726 if (pos
== ASL_FILE_POSITION_FIRST
) return asl_file_read_set_position_first(s
);
1727 if (pos
== ASL_FILE_POSITION_LAST
) return asl_file_read_set_position_last(s
, 0);
1731 if (pos
== ASL_FILE_POSITION_PREVIOUS
)
1733 if (s
->cursor
== s
->first
) return ASL_STATUS_NO_RECORDS
;
1734 if (s
->cursor
== 0) return ASL_STATUS_NO_RECORDS
;
1736 off
= s
->cursor
+ RECORD_TYPE_LEN
;
1737 status
= asl_file_read_uint32(s
, off
, &len
);
1738 if (status
!= ASL_STATUS_OK
) return status
;
1740 /* set offset to read the "previous" field at the end of the record */
1741 off
= s
->cursor
+ RECORD_COMMON_LEN
+ len
- sizeof(uint64_t);
1743 else if (pos
== ASL_FILE_POSITION_NEXT
)
1745 if (s
->cursor
== s
->last
) return ASL_STATUS_NO_RECORDS
;
1746 if (s
->cursor
== 0) return ASL_STATUS_NO_RECORDS
;
1748 /* set offset to read the "next" field in the current record */
1749 off
= s
->cursor
+ RECORD_COMMON_LEN
;
1751 else return ASL_STATUS_INVALID_ARG
;
1756 * read offset of next / previous
1759 status
= asl_file_read_uint64(s
, off
, &next
);
1760 if (status
!= ASL_STATUS_OK
) return ASL_STATUS_READ_FAILED
;
1762 /* detect bogus next pointer */
1763 if ((next
+ MSG_RECORD_FIXED_LENGTH
) > s
->file_size
) next
= 0;
1764 else if ((pos
== ASL_FILE_POSITION_PREVIOUS
) && (next
>= s
->cursor
)) next
= 0;
1765 else if ((pos
== ASL_FILE_POSITION_NEXT
) && (next
<= s
->cursor
)) next
= 0;
1768 if (s
->cursor
== 0) return ASL_STATUS_NO_RECORDS
;
1770 /* read ID of the record */
1771 off
= s
->cursor
+ RECORD_COMMON_LEN
+ sizeof(uint64_t);
1772 status
= asl_file_read_uint64(s
, off
, &(s
->cursor_xid
));
1777 asl_file_fetch_next(asl_file_t
*s
, asl_msg_t
**msg
)
1779 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
1780 if (s
->version
== 1) return ASL_STATUS_FAILED
;
1782 return asl_file_fetch_pos(s
, s
->cursor
, 1, msg
);
1786 asl_file_fetch_previous(asl_file_t
*s
, asl_msg_t
**msg
)
1788 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
1789 if (s
->version
== 1) return ASL_STATUS_FAILED
;
1791 return asl_file_fetch_pos(s
, s
->cursor
, -1, msg
);
1795 asl_file_fetch(asl_file_t
*s
, uint64_t mid
, asl_msg_t
**msg
)
1799 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
1800 if ((s
->flags
& ASL_FILE_FLAG_READ
) == 0) return ASL_STATUS_WRITE_ONLY
;
1802 if (s
->version
== 1)
1804 if (msg
== NULL
) return ASL_STATUS_OK
;
1805 return asl_legacy1_fetch((asl_legacy1_t
*)s
->legacy
, mid
, msg
);
1808 if (s
->cursor_xid
== 0)
1810 status
= asl_file_read_set_position(s
, ASL_FILE_POSITION_FIRST
);
1811 if (status
!= ASL_STATUS_OK
) return status
;
1812 if (s
->cursor_xid
== 0) return ASL_STATUS_INVALID_ID
;
1815 while (s
->cursor_xid
< mid
)
1817 status
= asl_file_read_set_position(s
, ASL_FILE_POSITION_NEXT
);
1818 if (status
!= ASL_STATUS_OK
) return status
;
1819 if (s
->cursor_xid
> mid
) return ASL_STATUS_INVALID_ID
;
1820 if (s
->cursor_xid
== 0) return ASL_STATUS_INVALID_ID
;
1823 while (s
->cursor_xid
> mid
)
1825 status
= asl_file_read_set_position(s
, ASL_FILE_POSITION_PREVIOUS
);
1826 if (status
!= ASL_STATUS_OK
) return status
;
1827 if (s
->cursor_xid
< mid
) return ASL_STATUS_INVALID_ID
;
1828 if (s
->cursor_xid
== 0) return ASL_STATUS_INVALID_ID
;
1831 if (s
->cursor_xid
!= mid
) return ASL_STATUS_INVALID_ID
;
1833 if (msg
== NULL
) return ASL_STATUS_OK
;
1834 return asl_file_fetch_pos(s
, s
->cursor
, 1, msg
);
1837 __private_extern__
uint64_t
1838 asl_file_cursor(asl_file_t
*s
)
1840 if (s
== NULL
) return 0;
1841 if ((s
->flags
& ASL_FILE_FLAG_READ
) == 0) return 0;
1842 if (s
->version
== 1) return 0;
1844 return s
->cursor_xid
;
1847 __private_extern__ ASL_STATUS
1848 asl_file_match_start(asl_file_t
*s
, uint64_t start
, int32_t direction
)
1852 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
1853 if (s
->version
== 1) return ASL_STATUS_INVALID_STORE
;
1854 if ((s
->flags
& ASL_FILE_FLAG_READ
) == 0) return ASL_STATUS_WRITE_ONLY
;
1856 d
= ASL_FILE_POSITION_NEXT
;
1857 if (direction
< 0) d
= ASL_FILE_POSITION_PREVIOUS
;
1860 * find starting point
1862 status
= ASL_STATUS_OK
;
1863 if (direction
>= 0) status
= asl_file_read_set_position(s
, ASL_FILE_POSITION_FIRST
);
1864 else status
= asl_file_read_set_position(s
, ASL_FILE_POSITION_LAST
);
1865 if (status
!= ASL_STATUS_OK
) return status
;
1867 while ((status
== ASL_STATUS_OK
) && (((direction
>= 0) && (s
->cursor_xid
< start
)) || ((direction
< 0) && (s
->cursor_xid
> start
))))
1869 status
= asl_file_read_set_position(s
, d
);
1875 __private_extern__ ASL_STATUS
1876 asl_file_match_next(asl_file_t
*s
, asl_msg_list_t
*query
, asl_msg_t
**msg
, uint64_t *last
, int32_t direction
)
1878 uint32_t status
, d
, i
, do_match
, did_match
;
1881 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
1882 if (msg
== NULL
) return ASL_STATUS_INVALID_ARG
;
1883 if (s
->version
== 1) return ASL_STATUS_INVALID_STORE
;
1884 if ((s
->flags
& ASL_FILE_FLAG_READ
) == 0) return ASL_STATUS_WRITE_ONLY
;
1885 if (s
->cursor
== 0) return ASL_STATUS_NO_RECORDS
;
1890 if (query
== NULL
) do_match
= 0;
1891 else if (query
->count
== 0) do_match
= 0;
1893 d
= ASL_FILE_POSITION_NEXT
;
1894 if (direction
< 0) d
= ASL_FILE_POSITION_PREVIOUS
;
1898 *last
= s
->cursor_xid
;
1900 status
= asl_file_fetch_pos(s
, s
->cursor
, direction
, &m
);
1901 if (status
== ASL_STATUS_ACCESS_DENIED
) return ASL_STATUS_MATCH_FAILED
;
1902 if ((status
== ASL_STATUS_INVALID_ARG
) && (s
->cursor
== 0)) return ASL_STATUS_NO_RECORDS
;
1903 if (status
!= ASL_STATUS_OK
) return status
;
1911 for (i
= 0; (i
< query
->count
) && (did_match
== 0); i
++)
1913 did_match
= asl_msg_cmp(query
->msg
[i
], m
);
1920 return ASL_STATUS_OK
;
1925 return ASL_STATUS_MATCH_FAILED
;
1929 asl_file_match(asl_file_t
*s
, asl_msg_list_t
*qlist
, uint64_t *last
, uint64_t start
, uint32_t count
, uint32_t duration
, int32_t direction
)
1931 uint32_t status
, d
, i
, do_match
, did_match
, rescount
;
1933 struct timeval now
, finish
;
1934 asl_msg_list_t
*out
= NULL
;
1936 if (s
== NULL
) return NULL
;
1937 if ((s
->flags
& ASL_FILE_FLAG_READ
) == 0) return NULL
;
1939 if (s
->version
== 1)
1941 asl_legacy1_match((asl_legacy1_t
*)s
->legacy
, qlist
, &out
, last
, start
, count
, direction
);
1946 if (qlist
== NULL
) do_match
= 0;
1947 else if (qlist
->count
== 0) do_match
= 0;
1951 d
= ASL_FILE_POSITION_NEXT
;
1952 if (direction
< 0) d
= ASL_FILE_POSITION_PREVIOUS
;
1955 * find starting point
1957 status
= ASL_STATUS_OK
;
1958 if (direction
>= 0) status
= asl_file_read_set_position(s
, ASL_FILE_POSITION_FIRST
);
1959 else status
= asl_file_read_set_position(s
, ASL_FILE_POSITION_LAST
);
1960 if (status
!= ASL_STATUS_OK
) return NULL
;
1962 while ((status
== ASL_STATUS_OK
) && (((direction
>= 0) && (s
->cursor_xid
< start
)) || ((direction
< 0) && (s
->cursor_xid
> start
))))
1964 status
= asl_file_read_set_position(s
, d
);
1967 /* start the timer if a duration was specified */
1968 memset(&finish
, 0, sizeof(struct timeval
));
1971 if (gettimeofday(&finish
, NULL
) == 0)
1973 finish
.tv_sec
+= (duration
/ USEC_PER_SEC
);
1974 finish
.tv_usec
+= (duration
% USEC_PER_SEC
);
1975 if (finish
.tv_usec
> USEC_PER_SEC
)
1977 finish
.tv_usec
-= USEC_PER_SEC
;
1983 /* shouldn't happen, but if gettimeofday failed we just run without a timeout */
1984 memset(&finish
, 0, sizeof(struct timeval
));
1989 * loop through records
1994 status
= asl_file_fetch_pos(s
, s
->cursor
, direction
, &m
);
1995 if (status
== ASL_STATUS_ACCESS_DENIED
) continue;
1996 if (status
!= ASL_STATUS_OK
) break;
1998 *last
= s
->cursor_xid
;
2006 for (i
= 0; (i
< qlist
->count
) && (did_match
== 0); i
++)
2008 did_match
= asl_msg_cmp(qlist
->msg
[i
], m
);
2014 /* append m to res */
2017 out
= asl_msg_list_new();
2018 if (out
== NULL
) return NULL
;
2021 asl_msg_list_append(out
, m
);
2023 if ((count
!= 0) && (rescount
>= count
)) break;
2025 /* check the timer */
2026 if ((finish
.tv_sec
!= 0) && (gettimeofday(&now
, NULL
) == 0))
2028 if ((now
.tv_sec
> finish
.tv_sec
) || ((now
.tv_sec
== finish
.tv_sec
) && (now
.tv_usec
> finish
.tv_usec
))) break;
2039 asl_file_size(asl_file_t
*s
)
2041 if (s
== NULL
) return 0;
2042 return s
->file_size
;
2046 asl_file_ctime(asl_file_t
*s
)
2048 if (s
== NULL
) return 0;
2053 asl_file_list_close(asl_file_list_t
*head
)
2055 asl_file_list_t
*next
;
2057 while (head
!= NULL
)
2060 asl_file_close(head
->file
);
2067 asl_file_list_free(asl_file_list_t
*head
)
2069 asl_file_list_t
*next
;
2071 while (head
!= NULL
)
2079 static asl_file_list_t
*
2080 asl_file_list_insert(asl_file_list_t
*list
, asl_file_t
*f
, int32_t dir
)
2082 asl_file_list_t
*a
, *b
, *tmp
;
2084 if (f
== NULL
) return list
;
2086 tmp
= (asl_file_list_t
*)calloc(1, sizeof(asl_file_list_t
));
2087 if (tmp
== NULL
) return NULL
;
2090 if (list
== NULL
) return tmp
;
2093 if (((dir
< 0) && (f
->cursor_xid
> a
->file
->cursor_xid
)) || ((dir
>= 0) && (f
->cursor_xid
< a
->file
->cursor_xid
)))
2102 if (((dir
< 0) && (f
->cursor_xid
> b
->file
->cursor_xid
)) || ((dir
>= 0) && (f
->cursor_xid
< b
->file
->cursor_xid
)))
2118 asl_file_list_add(asl_file_list_t
*list
, asl_file_t
*f
)
2120 asl_file_list_t
*tmp
;
2122 if (f
== NULL
) return list
;
2123 if (f
->version
== 1) return list
;
2125 tmp
= (asl_file_list_t
*)calloc(1, sizeof(asl_file_list_t
));
2126 if (tmp
== NULL
) return NULL
;
2134 asl_file_list_match_start(asl_file_list_t
*list
, uint64_t start
, int32_t direction
)
2138 asl_file_match_token_t
*out
;
2140 if (list
== NULL
) return NULL
;
2142 out
= (asl_file_match_token_t
*)calloc(1, sizeof(asl_file_match_token_t
));
2143 if (out
== NULL
) return NULL
;
2145 for (n
= list
; n
!= NULL
; n
= n
->next
)
2147 /* init file for the search */
2148 status
= asl_file_match_start(n
->file
, start
, direction
);
2149 if (status
!= ASL_STATUS_OK
) continue;
2150 if (n
->file
->cursor_xid
== 0) continue;
2152 out
->list
= asl_file_list_insert(out
->list
, n
->file
, direction
);
2155 out
->dir
= direction
;
2160 asl_file_list_match_next(void *token
, asl_msg_list_t
*qlist
, asl_msg_list_t
**res
, uint32_t count
)
2162 uint32_t status
, rescount
;
2165 asl_file_match_token_t
*work
;
2168 if (token
== NULL
) return ASL_STATUS_OK
;
2169 if (res
== NULL
) return ASL_STATUS_INVALID_ARG
;
2171 work
= (asl_file_match_token_t
*)token
;
2176 while ((work
->list
!= NULL
) && ((rescount
< count
) || (count
== 0)))
2179 status
= asl_file_match_next(work
->list
->file
, qlist
, &m
, &last
, work
->dir
);
2182 if (*res
== NULL
) *res
= asl_msg_list_new();
2185 asl_file_list_free(work
->list
);
2187 return ASL_STATUS_NO_MEMORY
;
2190 asl_msg_list_append(*res
, m
);
2195 if ((status
!= ASL_STATUS_OK
) || (work
->list
->file
->cursor_xid
== 0))
2197 n
= work
->list
->next
;
2202 if (work
->list
!= NULL
)
2204 n
= work
->list
->next
;
2207 if (((work
->dir
< 0) && (work
->list
->file
->cursor_xid
<= n
->file
->cursor_xid
)) || ((work
->dir
>= 0) && (work
->list
->file
->cursor_xid
> n
->file
->cursor_xid
)))
2210 work
->list
= work
->list
->next
;
2212 work
->list
= asl_file_list_insert(work
->list
, n
->file
, work
->dir
);
2219 return ASL_STATUS_OK
;
2223 asl_file_list_match_end(void *token
)
2225 asl_file_match_token_t
*work
;
2227 if (token
== NULL
) return;
2229 work
= (asl_file_match_token_t
*)token
;
2230 asl_file_list_free(work
->list
);
2237 asl_file_list_match(asl_file_list_t
*list
, asl_msg_list_t
*qlist
, uint64_t *last
, uint64_t start
, uint32_t count
, uint32_t duration
, int32_t direction
)
2239 uint32_t status
, rescount
;
2240 asl_file_list_t
*files
, *n
;
2242 struct timeval now
, finish
;
2243 asl_msg_list_t
*out
= NULL
;
2245 if (list
== NULL
) return NULL
;
2246 if (last
== NULL
) return NULL
;
2250 for (n
= list
; n
!= NULL
; n
= n
->next
)
2252 /* init file for the search */
2253 status
= asl_file_match_start(n
->file
, start
, direction
);
2254 if (status
!= ASL_STATUS_OK
) continue;
2255 if (n
->file
->cursor_xid
== 0) continue;
2257 files
= asl_file_list_insert(files
, n
->file
, direction
);
2262 asl_file_list_free(files
);
2266 /* start the timer if a timeout was specified */
2267 memset(&finish
, 0, sizeof(struct timeval
));
2270 if (gettimeofday(&finish
, NULL
) == 0)
2272 finish
.tv_sec
+= (duration
/ USEC_PER_SEC
);
2273 finish
.tv_usec
+= (duration
% USEC_PER_SEC
);
2274 if (finish
.tv_usec
> USEC_PER_SEC
)
2276 finish
.tv_usec
-= USEC_PER_SEC
;
2282 /* shouldn't happen, but if gettimeofday failed we just run without a timeout */
2283 memset(&finish
, 0, sizeof(struct timeval
));
2288 while ((files
!= NULL
) && ((rescount
< count
) || (count
== 0)))
2291 status
= asl_file_match_next(files
->file
, qlist
, &m
, last
, direction
);
2294 if (out
== NULL
) out
= asl_msg_list_new();
2297 asl_file_list_free(files
);
2301 asl_msg_list_append(out
, m
);
2306 if (files
->file
->cursor_xid
== 0)
2318 if (((direction
< 0) && (files
->file
->cursor_xid
<= n
->file
->cursor_xid
)) || ((direction
>= 0) && (files
->file
->cursor_xid
> n
->file
->cursor_xid
)))
2321 files
= files
->next
;
2323 files
= asl_file_list_insert(files
, n
->file
, direction
);
2329 /* check the timer */
2330 if ((finish
.tv_sec
!= 0) && (gettimeofday(&now
, NULL
) == 0))
2332 if ((now
.tv_sec
> finish
.tv_sec
) || ((now
.tv_sec
== finish
.tv_sec
) && (now
.tv_usec
> finish
.tv_usec
))) break;
2336 asl_file_list_free(files
);
2341 #pragma mark asl_object support
2344 _jump_dealloc(asl_object_private_t
*obj
)
2346 _asl_file_free_internal((asl_file_t
*)obj
);
2350 _jump_count(asl_object_private_t
*obj
)
2352 asl_file_t
*s
= (asl_file_t
*)obj
;
2353 if (s
== NULL
) return 0;
2354 if ((s
->flags
& ASL_FILE_FLAG_READ
) == 0) return 0;
2356 uint64_t cursor
= s
->cursor
;
2357 uint64_t cursor_xid
= s
->cursor_xid
;
2359 if (asl_file_read_set_position_last((asl_file_t
*)obj
, 1) != ASL_STATUS_OK
) return 0;
2362 s
->cursor_xid
= cursor_xid
;
2363 return s
->msg_count
;
2366 static asl_object_private_t
*
2367 _jump_next(asl_object_private_t
*obj
)
2369 asl_msg_t
*msg
= NULL
;
2370 if (asl_file_fetch_next((asl_file_t
*)obj
, &msg
) != ASL_STATUS_OK
) return NULL
;
2371 return (asl_object_private_t
*)msg
;
2374 static asl_object_private_t
*
2375 _jump_prev(asl_object_private_t
*obj
)
2377 asl_msg_t
*msg
= NULL
;
2378 if (asl_file_fetch_previous((asl_file_t
*)obj
, &msg
) != ASL_STATUS_OK
) return NULL
;
2379 return (asl_object_private_t
*)msg
;
2382 /* we don't really need to support this, but we are generous */
2383 static asl_object_private_t
*
2384 _jump_get_object_at_index(asl_object_private_t
*obj
, size_t n
)
2386 asl_msg_t
*msg
= NULL
;
2388 if (asl_file_fetch((asl_file_t
*)obj
, mid
, &msg
) != ASL_STATUS_OK
) return NULL
;
2389 return (asl_object_private_t
*)msg
;
2393 _jump_set_iteration_index(asl_object_private_t
*obj
, size_t n
)
2395 asl_file_t
*s
= (asl_file_t
*)obj
;
2396 if (s
== NULL
) return;
2397 if ((s
->flags
& ASL_FILE_FLAG_READ
) == 0) return;
2401 asl_file_read_set_position_first(s
);
2403 else if (n
== SIZE_MAX
)
2405 asl_file_read_set_position_last(s
, 0);
2409 /* we don't really need to support this, but we are generous */
2410 asl_file_fetch(s
, n
, NULL
);
2415 _jump_append(asl_object_private_t
*obj
, asl_object_private_t
*newobj
)
2418 asl_file_t
*s
= (asl_file_t
*)obj
;
2419 int type
= asl_get_type((asl_object_t
)newobj
);
2420 if (s
== NULL
) return;
2421 if (s
->flags
& ASL_FILE_FLAG_READ
) return;
2423 if (type
== ASL_TYPE_LIST
)
2426 asl_msg_list_reset_iteration((asl_msg_list_t
*)newobj
, 0);
2427 while (NULL
!= (msg
= asl_msg_list_next((asl_msg_list_t
*)newobj
)))
2429 if (asl_file_save(s
, msg
, &xid
) != ASL_STATUS_OK
) return;
2432 else if ((type
== ASL_TYPE_MSG
) || (type
== ASL_TYPE_QUERY
))
2434 asl_file_save(s
, (asl_msg_t
*)newobj
, &xid
);
2438 static asl_object_private_t
*
2439 _jump_search(asl_object_private_t
*obj
, asl_object_private_t
*query
)
2441 asl_file_t
*s
= (asl_file_t
*)obj
;
2442 int type
= asl_get_type((asl_object_t
)query
);
2443 asl_msg_list_t
*out
= NULL
;
2444 asl_msg_list_t
*ql
= NULL
;
2449 return (asl_object_private_t
*)asl_file_match(s
, NULL
, &last
, 0, 0, 0, 1);
2451 else if (type
== ASL_TYPE_LIST
)
2453 return (asl_object_private_t
*)asl_file_match(s
, (asl_msg_list_t
*)query
, &last
, 0, 0, 0, 1);
2455 else if ((type
== ASL_TYPE_MSG
) || (type
== ASL_TYPE_QUERY
))
2457 ql
= asl_msg_list_new();
2458 asl_msg_list_append(ql
, query
);
2460 out
= asl_file_match(s
, ql
, &last
, 0, 0, 0, 1);
2461 asl_msg_list_release(ql
);
2462 return (asl_object_private_t
*)out
;
2468 static asl_object_private_t
*
2469 _jump_match(asl_object_private_t
*obj
, asl_object_private_t
*qlist
, size_t *last
, size_t start
, size_t count
, uint32_t duration
, int32_t dir
)
2472 asl_msg_list_t
*out
= asl_file_match((asl_file_t
*)obj
, (asl_msg_list_t
*)qlist
, &x
, start
, count
, duration
, dir
);
2474 return (asl_object_private_t
*)out
;
2477 __private_extern__
const asl_jump_table_t
*
2478 asl_file_jump_table()
2480 static const asl_jump_table_t jump
=
2483 .dealloc
= &_jump_dealloc
,
2484 .set_key_val_op
= NULL
,
2486 .get_val_op_for_key
= NULL
,
2487 .get_key_val_op_at_index
= NULL
,
2488 .count
= &_jump_count
,
2489 .next
= &_jump_next
,
2490 .prev
= &_jump_prev
,
2491 .get_object_at_index
= &_jump_get_object_at_index
,
2492 .set_iteration_index
= &_jump_set_iteration_index
,
2493 .remove_object_at_index
= NULL
,
2494 .append
= &_jump_append
,
2496 .search
= &_jump_search
,
2497 .match
= &_jump_match