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>
44 #include <os/variant_private.h>
46 #define forever for(;;)
49 * MSG and STR records have (at least) a type (uint16_t) and a length (uint32_t)
50 * type and level are both 16 bit fields so that alignment isn't a pain.
52 #define RECORD_COMMON_LEN 6
53 #define RECORD_TYPE_LEN 2
54 #define BUFFER_OFFSET_KVCOUNT 56
56 #define SCRATCH_BUFFER_SIZE (MSG_RECORD_FIXED_LENGTH + (20 * sizeof(uint64_t)))
84 asl_file_list_t
*list
;
86 } asl_file_match_token_t
;
98 _asl_put_16(uint16_t i
, char *h
)
116 _asl_put_32(uint32_t i
, char *h
)
130 return asl_core_ntohq(x
);
134 _asl_put_64(uint64_t i
, char *h
)
138 x
= asl_core_htonq(i
);
143 asl_file_read_uint32(asl_file_t
*s
, off_t off
, uint32_t *out
)
145 uint32_t status
, val
;
147 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
148 if (s
->store
== NULL
) return ASL_STATUS_INVALID_STORE
;
149 if ((off
+ sizeof(uint32_t)) > s
->file_size
) return ASL_STATUS_READ_FAILED
;
151 status
= fseeko(s
->store
, off
, SEEK_SET
);
152 if (status
!= 0) return ASL_STATUS_READ_FAILED
;
156 status
= fread(&val
, sizeof(uint32_t), 1, s
->store
);
157 if (status
!= 1) return ASL_STATUS_READ_FAILED
;
159 if (out
!= NULL
) *out
= ntohl(val
);
160 return ASL_STATUS_OK
;
164 asl_file_read_uint64(asl_file_t
*s
, off_t off
, uint64_t *out
)
169 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
170 if (s
->store
== NULL
) return ASL_STATUS_INVALID_STORE
;
171 if ((off
+ sizeof(uint64_t)) > s
->file_size
) return ASL_STATUS_READ_FAILED
;
173 status
= fseeko(s
->store
, off
, SEEK_SET
);
174 if (status
!= 0) return ASL_STATUS_READ_FAILED
;
178 status
= fread(&val
, sizeof(uint64_t), 1, s
->store
);
179 if (status
!= 1) return ASL_STATUS_READ_FAILED
;
181 if (out
!= NULL
) *out
= asl_core_ntohq(val
);
182 return ASL_STATUS_OK
;
185 static file_string_t
*
186 file_string_create(asl_file_t
*s
)
188 if ((s
!= NULL
) && (s
->string_spare
!= NULL
))
190 file_string_t
*out
= s
->string_spare
;
191 s
->string_spare
= NULL
;
195 return (file_string_t
*)calloc(1, sizeof(file_string_t
));
199 file_string_dispose(asl_file_t
*s
, file_string_t
*x
)
201 if ((s
!= NULL
) && (s
->string_spare
== NULL
))
204 memset(s
->string_spare
, 0, sizeof(file_string_t
));
214 asl_file_retain(asl_file_t
*s
)
216 if (s
== NULL
) return NULL
;
217 asl_retain((asl_object_t
)s
);
222 asl_file_release(asl_file_t
*s
)
224 if (s
== NULL
) return;
225 asl_release((asl_object_t
)s
);
229 asl_file_close(asl_file_t
*s
)
231 if (s
== NULL
) return ASL_STATUS_OK
;
232 asl_release((asl_object_t
)s
);
233 return ASL_STATUS_OK
;
237 _asl_file_free_internal(asl_file_t
*s
)
241 if (s
== NULL
) return;
245 asl_legacy1_close((asl_legacy1_t
*)s
->legacy
);
249 while (s
->string_list
!= NULL
)
251 x
= s
->string_list
->next
;
252 free(s
->string_list
);
256 free(s
->string_spare
);
258 if (s
->store
!= NULL
) fclose(s
->store
);
259 if (s
->scratch
!= NULL
) free(s
->scratch
);
261 memset(s
, 0, sizeof(asl_file_t
));
265 __private_extern__ ASL_STATUS
266 asl_file_open_write_fd(int fd
, asl_file_t
**s
)
270 char buf
[DB_HEADER_LEN
];
273 if (fd
< 0) return ASL_STATUS_FAILED
;
274 if (s
== NULL
) return ASL_STATUS_FAILED
;
276 out
= (asl_file_t
*)calloc(1, sizeof(asl_file_t
));
277 if (out
== NULL
) return ASL_STATUS_NO_MEMORY
;
279 out
->asl_type
= ASL_TYPE_FILE
;
282 out
->store
= fdopen(fd
, "w+");
283 if (out
->store
== NULL
)
286 return ASL_STATUS_FAILED
;
289 memset(buf
, 0, sizeof(buf
));
290 memcpy(buf
, ASL_DB_COOKIE
, ASL_DB_COOKIE_LEN
);
292 _asl_put_32(DB_VERSION
, buf
+ DB_HEADER_VERS_OFFSET
);
296 _asl_put_64(out
->dob
, buf
+ DB_HEADER_TIME_OFFSET
);
298 _asl_put_32(CACHE_SIZE
, buf
+ DB_HEADER_CSIZE_OFFSET
);
300 status
= fwrite(buf
, sizeof(buf
), 1, out
->store
);
305 return ASL_STATUS_FAILED
;
311 out
->file_size
= sizeof(buf
);
313 /* scratch buffer for file writes (we test for NULL before using it) */
314 out
->scratch
= malloc(SCRATCH_BUFFER_SIZE
);
318 return ASL_STATUS_OK
;
321 __private_extern__
int
322 asl_file_create(const char *path
, uid_t uid
, gid_t gid
, mode_t mode
)
325 return open(path
, O_RDWR
| O_CREAT
| O_EXCL
, mode
);
327 if (os_variant_is_basesystem("com.apple.syslog")) {
328 return open(path
, O_RDWR
| O_CREAT
| O_EXCL
, mode
);
338 /* -1 means don't set ACL for uid or gid */
339 if ((uid
== -1) && (gid
== -1))
341 return open(path
, O_RDWR
| O_CREAT
| O_EXCL
, mode
);
346 if ((gid
!= 0) && (gid
!= -1))
348 status
= mbr_gid_to_uuid(gid
, uuid
);
349 if (status
!= 0) goto asl_file_create_return
;
351 status
= acl_create_entry_np(&acl
, &entry
, ACL_FIRST_ENTRY
);
352 if (status
!= 0) goto asl_file_create_return
;
354 status
= acl_set_tag_type(entry
, ACL_EXTENDED_ALLOW
);
355 if (status
!= 0) goto asl_file_create_return
;
357 status
= acl_set_qualifier(entry
, &uuid
);
358 if (status
!= 0) goto asl_file_create_return
;
360 status
= acl_get_permset(entry
, &perms
);
361 if (status
!= 0) goto asl_file_create_return
;
363 status
= acl_add_perm(perms
, ACL_READ_DATA
);
364 if (status
!= 0) goto asl_file_create_return
;
367 if ((uid
!= 0) && (uid
!= -1))
369 status
= mbr_uid_to_uuid(uid
, uuid
);
370 if (status
!= 0) goto asl_file_create_return
;
372 status
= acl_create_entry_np(&acl
, &entry
, ACL_FIRST_ENTRY
);
373 if (status
!= 0) goto asl_file_create_return
;
375 status
= acl_set_tag_type(entry
, ACL_EXTENDED_ALLOW
);
376 if (status
!= 0) goto asl_file_create_return
;
378 status
= acl_set_qualifier(entry
, &uuid
);
379 if (status
!= 0) goto asl_file_create_return
;
381 status
= acl_get_permset(entry
, &perms
);
382 if (status
!= 0) goto asl_file_create_return
;
384 status
= acl_add_perm(perms
, ACL_READ_DATA
);
385 if (status
!= 0) goto asl_file_create_return
;
388 fd
= open(path
, O_RDWR
| O_CREAT
| O_EXCL
, mode
);
389 if (fd
< 0) goto asl_file_create_return
;
391 status
= acl_set_fd(fd
, acl
);
399 asl_file_create_return
:
407 asl_file_open_write(const char *path
, mode_t mode
, uid_t uid
, gid_t gid
, asl_file_t
**s
)
411 char buf
[DB_HEADER_LEN
];
413 uint32_t aslstatus
, vers
, last_len
;
416 memset(&sb
, 0, sizeof(struct stat
));
418 status
= stat(path
, &sb
);
421 /* must be a plain file */
422 if (!S_ISREG(sb
.st_mode
)) return ASL_STATUS_INVALID_STORE
;
425 * If the file exists, we go with the existing mode, uid, and gid.
430 fd
= open(path
, O_RDWR
| O_EXCL
, mode
);
431 if (fd
< 0) return ASL_STATUS_FAILED
;
433 return asl_file_open_write_fd(fd
, s
);
437 out
= (asl_file_t
*)calloc(1, sizeof(asl_file_t
));
438 if (out
== NULL
) return ASL_STATUS_NO_MEMORY
;
440 out
->asl_type
= ASL_TYPE_FILE
;
443 out
->store
= fopen(path
, "r+");
444 if (out
->store
== NULL
)
447 return ASL_STATUS_FAILED
;
450 i
= fread(buf
, DB_HEADER_LEN
, 1, out
->store
);
454 return ASL_STATUS_READ_FAILED
;
458 if (strncmp(buf
, ASL_DB_COOKIE
, ASL_DB_COOKIE_LEN
))
461 return ASL_STATUS_INVALID_STORE
;
465 vers
= _asl_get_32(buf
+ DB_HEADER_VERS_OFFSET
);
466 if (vers
!= DB_VERSION
)
469 return ASL_STATUS_INVALID_STORE
;
472 out
->dob
= _asl_get_64(buf
+ DB_HEADER_TIME_OFFSET
);
473 out
->first
= _asl_get_64(buf
+ DB_HEADER_FIRST_OFFSET
);
474 out
->last
= _asl_get_64(buf
+ DB_HEADER_LAST_OFFSET
);
475 out
->file_size
= (size_t)sb
.st_size
;
478 * Detect bogus last pointer and check for odd-sized files.
479 * Setting out->last to zero forces asl_file_read_set_position to
480 * follow the linked list of records in the file to the last record.
481 * It's slower, but it's better at preventing crashes in corrupt files.
484 /* records are at least MSG_RECORD_FIXED_LENGTH bytes */
485 if ((out
->last
+ MSG_RECORD_FIXED_LENGTH
) > out
->file_size
)
491 /* read last record length and make sure the file is at least that large */
492 off
= out
->last
+ RECORD_TYPE_LEN
;
493 status
= asl_file_read_uint32(out
, off
, &last_len
);
494 if (status
!= ASL_STATUS_OK
)
500 if ((out
->last
+ last_len
) > out
->file_size
) out
->last
= 0;
505 /* skip type (uint16_t), len (uint32_t), and next (uint64_t) */
506 off
= out
->last
+ sizeof(uint16_t) + sizeof (uint32_t) + sizeof(uint64_t);
507 status
= asl_file_read_uint64(out
, off
, &(out
->last_mid
));
508 if (status
!= ASL_STATUS_OK
)
515 aslstatus
= asl_file_read_set_position(out
, ASL_FILE_POSITION_LAST
);
516 if (aslstatus
!= ASL_STATUS_OK
)
522 out
->prev
= out
->cursor
;
523 status
= fseeko(out
->store
, 0, SEEK_END
);
527 return ASL_STATUS_READ_FAILED
;
530 out
->file_size
= (size_t)ftello(out
->store
);
532 /* scratch buffer for file writes (we test for NULL before using it) */
533 out
->scratch
= malloc(SCRATCH_BUFFER_SIZE
);
537 return ASL_STATUS_OK
;
540 else if (errno
!= ENOENT
)
542 /* unexpected status */
543 return ASL_STATUS_FAILED
;
547 * If the file does not exist, we set the mode, uid, and gid.
550 fd
= asl_file_create(path
, uid
, gid
, mode
);
551 if (fd
< 0) return ASL_STATUS_FAILED
;
553 aslstatus
= asl_file_open_write_fd(fd
, s
);
554 if (aslstatus
!= ASL_STATUS_OK
) unlink(path
);
560 asl_file_compact(asl_file_t
*s
, const char *path
, mode_t mode
, uid_t uid
, gid_t gid
)
568 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
569 if (path
== NULL
) return ASL_STATUS_INVALID_ARG
;
571 if (s
->version
== 1) return ASL_STATUS_FAILED
;
573 memset(&sb
, 0, sizeof(struct stat
));
575 if (stat(path
, &sb
) == 0) return ASL_STATUS_FAILED
;
576 if (errno
!= ENOENT
) return ASL_STATUS_FAILED
;
578 status
= asl_file_read_set_position(s
, ASL_FILE_POSITION_FIRST
);
579 if (status
!= ASL_STATUS_OK
) return status
;
582 status
= asl_file_open_write(path
, mode
, uid
, gid
, &new);
583 if (status
!= ASL_STATUS_OK
) return status
;
584 new->flags
= ASL_FILE_FLAG_UNLIMITED_CACHE
| ASL_FILE_FLAG_PRESERVE_MSG_ID
;
586 while ((status
== ASL_STATUS_OK
) && (s
->cursor
!= 0))
589 status
= asl_file_fetch_next(s
, &m
);
590 if (status
!= ASL_STATUS_OK
) break;
593 status
= asl_file_save(new, m
, &xid
);
602 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
))
608 uint32_t status
, n
= 0;
609 uint32_t matchflag
= flags
& ASL_FILE_FILTER_FLAG_KEEP_MATCHES
;
611 if (dstcount
!= NULL
) *dstcount
= n
;
613 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
614 if (path
== NULL
) return ASL_STATUS_INVALID_ARG
;
616 if (s
->version
== 1) return ASL_STATUS_FAILED
;
618 memset(&sb
, 0, sizeof(struct stat
));
620 if (stat(path
, &sb
) == 0) return ASL_STATUS_FAILED
;
621 if (errno
!= ENOENT
) return ASL_STATUS_FAILED
;
623 status
= asl_file_read_set_position(s
, ASL_FILE_POSITION_FIRST
);
624 if (status
!= ASL_STATUS_OK
) return status
;
627 status
= asl_file_open_write(path
, mode
, uid
, gid
, &new);
628 if (status
!= ASL_STATUS_OK
) return status
;
629 new->flags
= ASL_FILE_FLAG_UNLIMITED_CACHE
| ASL_FILE_FLAG_PRESERVE_MSG_ID
;
631 while ((status
== ASL_STATUS_OK
) && (s
->cursor
!= 0))
634 status
= asl_file_fetch_next(s
, &m
);
635 if (status
!= ASL_STATUS_OK
) break;
638 * asl_msg_cmp_list is supposed to return 1 for a match, 0 otherwise,
639 * but just to be sure we only get a 1 or zero, we do an extra test.
641 uint32_t msgmatch
= (asl_msg_cmp_list(m
, filter
) == 0) ? 0 : 1;
642 if (msgmatch
== matchflag
)
644 status
= asl_file_save(new, m
, &xid
);
645 if (status
== ASL_STATUS_OK
) n
++;
647 else if (aux_callback
!= NULL
)
649 /* check for ASL_KEY_AUX_URL and pass it to callback */
650 const char *auxval
= asl_msg_get_val_for_key(m
, ASL_KEY_AUX_URL
);
651 if (auxval
!= NULL
) aux_callback(auxval
);
658 if (dstcount
!= NULL
) *dstcount
= n
;
663 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
))
669 uint32_t status
, n
= 0;
670 uint8_t kmcur
, kmnew
;
672 if (dstcount
!= NULL
) *dstcount
= n
;
674 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
675 if (path
== NULL
) return ASL_STATUS_INVALID_ARG
;
677 if (s
->version
== 1) return ASL_STATUS_FAILED
;
679 memset(&sb
, 0, sizeof(struct stat
));
681 if (stat(path
, &sb
) == 0) return ASL_STATUS_FAILED
;
682 if (errno
!= ENOENT
) return ASL_STATUS_FAILED
;
686 /* get current filter mask in file's header */
687 status
= fseeko(s
->store
, DB_HEADER_FILTER_MASK_OFFSET
, SEEK_SET
);
688 if (status
!= 0) return ASL_STATUS_READ_FAILED
;
689 fread(&kmcur
, 1, 1, s
->store
);
691 status
= asl_file_read_set_position(s
, ASL_FILE_POSITION_FIRST
);
692 if (status
!= ASL_STATUS_OK
) return status
;
695 status
= asl_file_open_write(path
, mode
, uid
, gid
, &new);
696 if (status
!= ASL_STATUS_OK
) return status
;
698 new->flags
= ASL_FILE_FLAG_UNLIMITED_CACHE
| ASL_FILE_FLAG_PRESERVE_MSG_ID
;
700 while ((status
== ASL_STATUS_OK
) && (s
->cursor
!= 0))
703 status
= asl_file_fetch_next(s
, &m
);
704 if (status
!= ASL_STATUS_OK
) break;
705 if (m
== NULL
) continue;
706 const char *lval
= asl_msg_get_val_for_key(m
, ASL_KEY_LEVEL
);
707 if (lval
== NULL
) continue;
708 uint32_t level_bit
= 0x01 << atoi(lval
);
710 if (level_bit
& keep_mask
)
712 status
= asl_file_save(new, m
, &xid
);
713 if (status
== ASL_STATUS_OK
) n
++;
715 else if (aux_callback
!= NULL
)
717 /* check for ASL_KEY_AUX_URL and pass it to callback */
718 const char *auxval
= asl_msg_get_val_for_key(m
, ASL_KEY_AUX_URL
);
719 if (auxval
!= NULL
) aux_callback(auxval
);
725 kmnew
= kmcur
& kmnew
;
726 status
= fseeko(new->store
, DB_HEADER_FILTER_MASK_OFFSET
, SEEK_SET
);
727 if (status
!= 0) return ASL_STATUS_READ_FAILED
;
728 fwrite(&kmnew
, 1, 1, new->store
);
731 if (dstcount
!= NULL
) *dstcount
= n
;
736 asl_file_string_encode(asl_file_t
*s
, const char *str
, uint64_t *out
)
738 uint32_t i
, hash
, len
, x32
;
739 file_string_t
*sp
, *sx
, *sl
;
746 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
747 if (s
->store
== NULL
) return ASL_STATUS_INVALID_STORE
;
748 if (str
== NULL
) return ASL_STATUS_INVALID_ARG
;
762 memcpy(p
+ 1, str
, len
);
763 *out
= asl_core_ntohq(x64
);
764 return ASL_STATUS_OK
;
767 /* cached strings include trailing nul */
770 if (len
<= CACHE_MAX_STRING_LEN
)
772 /* check the cache */
773 hash
= asl_core_string_hash(str
, len
);
776 for (sx
= s
->string_list
; sx
!= NULL
; sx
= sx
->next
)
778 if ((hash
== sx
->hash
) && (!strcmp(str
, sx
->str
)))
780 /* Move this string to the head of the list */
790 return ASL_STATUS_OK
;
797 off
= ftello(s
->store
);
800 type
= htons(ASL_FILE_TYPE_STR
);
801 i
= fwrite(&type
, sizeof(uint16_t), 1, s
->store
);
802 if (i
!= 1) return ASL_STATUS_WRITE_FAILED
;
804 /* Length (includes trailing nul) */
806 i
= fwrite(&x32
, sizeof(uint32_t), 1, s
->store
);
807 if (i
!= 1) return ASL_STATUS_WRITE_FAILED
;
809 /* String data (nul terminated) */
810 i
= fwrite(str
, len
, 1, s
->store
);
811 if (i
!= 1) return ASL_STATUS_WRITE_FAILED
;
817 * Create file_string_t and insert into the cache, but only if the
818 * string is small. This prevents a huge string from eating memory.
819 * It's unlikely that large strings will be very re-usable.
821 if (len
<= CACHE_MAX_STRING_LEN
)
823 sx
= file_string_create(s
);
824 if (sx
== NULL
) return ASL_STATUS_NO_MEMORY
;
828 sx
->next
= s
->string_list
;
830 /* includes trailing nul */
831 memcpy(sx
->str
, str
, len
);
835 if (((s
->flags
& ASL_FILE_FLAG_UNLIMITED_CACHE
) == 0) && (s
->string_cache_count
== CACHE_SIZE
))
837 /* drop last (lru) string from cache */
841 /* NB CACHE_SIZE must be > 1 */
842 while (sx
->next
!= NULL
)
849 file_string_dispose(s
, sx
);
853 s
->string_cache_count
++;
858 return ASL_STATUS_OK
;
862 * Encode an asl_msg_t *as a record structure.
863 * Creates and caches strings.
865 #define KVSTACK_SIZE 128
868 asl_file_save(asl_file_t
*s
, asl_msg_t
*in
, uint64_t *mid
)
871 uint32_t i
, len
, x
, status
;
874 uint64_t kvstack
[KVSTACK_SIZE
];
875 uint64_t *kvmalloc
= NULL
;
879 const char *key
, *val
;
881 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
882 if (s
->store
== NULL
) return ASL_STATUS_INVALID_STORE
;
883 if (in
== NULL
) return ASL_STATUS_INVALID_MESSAGE
;
885 if (s
->flags
& ASL_FILE_FLAG_READ
) return ASL_STATUS_READ_ONLY
;
887 msg
= (asl_msg_t
*)in
;
889 memset(&r
, 0, sizeof(file_record_t
));
892 if ((mid
!= NULL
) && (*mid
!= 0)) r
.mid
= *mid
;
895 r
.level
= ASL_LEVEL_DEBUG
;
909 for (x
= asl_msg_fetch(msg
, 0, &key
, &val
, NULL
); x
!= IndexNull
; x
= asl_msg_fetch(msg
, x
, &key
, &val
, NULL
))
915 else if (!strcmp(key
, ASL_KEY_TIME
))
917 if (val
!= NULL
) r
.time
= asl_core_parse_time(val
, NULL
);
919 else if (!strcmp(key
, ASL_KEY_TIME_NSEC
))
921 if (val
!= NULL
) r
.nano
= atoi(val
);
923 else if (!strcmp(key
, ASL_KEY_HOST
))
927 status
= asl_file_string_encode(s
, val
, &(r
.host
));
928 if (status
!= ASL_STATUS_OK
)
935 else if (!strcmp(key
, ASL_KEY_SENDER
))
939 status
= asl_file_string_encode(s
, val
, &(r
.sender
));
940 if (status
!= ASL_STATUS_OK
)
947 else if (!strcmp(key
, ASL_KEY_PID
))
949 if (val
!= NULL
) r
.pid
= atoi(val
);
951 else if (!strcmp(key
, ASL_KEY_REF_PID
))
953 if (val
!= NULL
) r
.refpid
= atoi(val
);
955 else if (!strcmp(key
, ASL_KEY_UID
))
957 if (val
!= NULL
) r
.uid
= atoi(val
);
959 else if (!strcmp(key
, ASL_KEY_GID
))
961 if (val
!= NULL
) r
.gid
= atoi(val
);
963 else if (!strcmp(key
, ASL_KEY_LEVEL
))
965 if (val
!= NULL
) r
.level
= atoi(val
);
967 else if (!strcmp(key
, ASL_KEY_MSG
))
971 status
= asl_file_string_encode(s
, val
, &(r
.message
));
972 if (status
!= ASL_STATUS_OK
)
979 else if (!strcmp(key
, ASL_KEY_FACILITY
))
983 status
= asl_file_string_encode(s
, val
, &(r
.facility
));
984 if (status
!= ASL_STATUS_OK
)
991 else if (!strcmp(key
, ASL_KEY_REF_PROC
))
995 status
= asl_file_string_encode(s
, val
, &(r
.refproc
));
996 if (status
!= ASL_STATUS_OK
)
1003 else if (!strcmp(key
, ASL_KEY_SESSION
))
1007 status
= asl_file_string_encode(s
, val
, &(r
.session
));
1008 if (status
!= ASL_STATUS_OK
)
1015 else if (!strcmp(key
, ASL_KEY_READ_UID
))
1017 if (((r
.flags
& ASL_MSG_FLAG_READ_UID_SET
) == 0) && (val
!= NULL
))
1020 r
.flags
|= ASL_MSG_FLAG_READ_UID_SET
;
1023 else if (!strcmp(key
, ASL_KEY_READ_GID
))
1025 if (((r
.flags
& ASL_MSG_FLAG_READ_GID_SET
) == 0) && (val
!= NULL
))
1028 r
.flags
|= ASL_MSG_FLAG_READ_GID_SET
;
1031 else if (!strcmp(key
, ASL_KEY_MSG_ID
))
1033 if (s
->flags
& ASL_FILE_FLAG_PRESERVE_MSG_ID
)
1036 if (mid
!= NULL
) *mid
= r
.mid
;
1039 else if (!strcmp(key
, ASL_KEY_OPTION
))
1041 /* ignore - we don't save ASLOption */
1045 status
= asl_file_string_encode(s
, key
, &k
);
1046 if (status
!= ASL_STATUS_OK
)
1055 status
= asl_file_string_encode(s
, val
, &v
);
1056 if (status
!= ASL_STATUS_OK
)
1063 if (r
.kvcount
>= KVSTACK_SIZE
)
1065 /* out of space for the kvlist on the stack - fall back to malloc */
1066 kvmalloc
= reallocf(kvmalloc
, (r
.kvcount
+ 2) * sizeof(uint64_t));
1067 if (kvmalloc
== NULL
) return ASL_STATUS_NO_MEMORY
;
1071 if (r
.kvcount
== KVSTACK_SIZE
)
1073 /* copy kvstack to kvmalloc */
1074 for (i
= 0; i
< KVSTACK_SIZE
; i
++) kvmalloc
[i
] = kvstack
[i
];
1078 kvlist
[r
.kvcount
++] = k
;
1079 kvlist
[r
.kvcount
++] = v
;
1083 len
= MSG_RECORD_FIXED_LENGTH
+ (r
.kvcount
* sizeof(uint64_t));
1086 /* use the scratch buffer if it exists and is large enough */
1087 if ((s
->scratch
!= NULL
) && (len
<= SCRATCH_BUFFER_SIZE
))
1089 memset(s
->scratch
, 0, SCRATCH_BUFFER_SIZE
);
1094 buf
= calloc(1, len
);
1097 if (buf
== NULL
) return ASL_STATUS_NO_MEMORY
;
1099 if (r
.mid
== UINT64_MAX
)
1101 s
->last_mid
= s
->last_mid
+ 1;
1102 r
.mid
= s
->last_mid
;
1108 _asl_put_16(ASL_FILE_TYPE_MSG
, p
);
1109 p
+= sizeof(uint16_t);
1111 /* Length of message (excludes type and length fields) */
1112 _asl_put_32(len
- RECORD_COMMON_LEN
, p
);
1113 p
+= sizeof(uint32_t);
1115 /* Message data... */
1117 _asl_put_64(r
.next
, p
);
1118 p
+= sizeof(uint64_t);
1120 _asl_put_64(r
.mid
, p
);
1121 p
+= sizeof(uint64_t);
1123 _asl_put_64(r
.time
, p
);
1124 p
+= sizeof(uint64_t);
1126 _asl_put_32(r
.nano
, p
);
1127 p
+= sizeof(uint32_t);
1129 _asl_put_16(r
.level
, p
);
1130 p
+= sizeof(uint16_t);
1132 _asl_put_16(r
.flags
, p
);
1133 p
+= sizeof(uint16_t);
1135 _asl_put_32(r
.pid
, p
);
1136 p
+= sizeof(uint32_t);
1138 _asl_put_32(r
.uid
, p
);
1139 p
+= sizeof(uint32_t);
1141 _asl_put_32(r
.gid
, p
);
1142 p
+= sizeof(uint32_t);
1144 _asl_put_32(r
.ruid
, p
);
1145 p
+= sizeof(uint32_t);
1147 _asl_put_32(r
.rgid
, p
);
1148 p
+= sizeof(uint32_t);
1150 _asl_put_32(r
.refpid
, p
);
1151 p
+= sizeof(uint32_t);
1153 _asl_put_32(r
.kvcount
, p
);
1154 p
+= sizeof(uint32_t);
1156 _asl_put_64(r
.host
, p
);
1157 p
+= sizeof(uint64_t);
1159 _asl_put_64(r
.sender
, p
);
1160 p
+= sizeof(uint64_t);
1162 _asl_put_64(r
.facility
, p
);
1163 p
+= sizeof(uint64_t);
1165 _asl_put_64(r
.message
, p
);
1166 p
+= sizeof(uint64_t);
1168 _asl_put_64(r
.refproc
, p
);
1169 p
+= sizeof(uint64_t);
1171 _asl_put_64(r
.session
, p
);
1172 p
+= sizeof(uint64_t);
1174 for (i
= 0; i
< r
.kvcount
; i
++)
1176 _asl_put_64(kvlist
[i
], p
);
1177 p
+= sizeof(uint64_t);
1180 _asl_put_64(r
.prev
, p
);
1181 p
+= sizeof(uint64_t);
1185 /* write record at end of file */
1186 status
= fseeko(s
->store
, 0, SEEK_END
);
1187 if (status
!= 0) return ASL_STATUS_WRITE_FAILED
;
1189 s
->last
= (uint64_t)ftello(s
->store
);
1191 v
= asl_core_htonq(s
->last
);
1193 status
= fwrite(buf
, len
, 1, s
->store
);
1196 /* free the buffer if it was allocated here */
1197 if (buf
!= s
->scratch
) free(buf
);
1199 /* seek to "next" field of previous record, write last offset */
1200 off
= s
->prev
+ RECORD_COMMON_LEN
;
1201 if (s
->prev
== 0) off
= DB_HEADER_FIRST_OFFSET
;
1203 status
= fseeko(s
->store
, off
, SEEK_SET
);
1204 if (status
!= 0) return ASL_STATUS_WRITE_FAILED
;
1206 status
= fwrite(&v
, sizeof(uint64_t), 1, s
->store
);
1207 if (status
!= 1) return ASL_STATUS_WRITE_FAILED
;
1209 /* seek to DB_HEADER_LAST_OFFSET, write last record offset */
1210 off
= DB_HEADER_LAST_OFFSET
;
1212 status
= fseeko(s
->store
, off
, SEEK_SET
);
1213 if (status
!= 0) return ASL_STATUS_WRITE_FAILED
;
1215 status
= fwrite(&v
, sizeof(uint64_t), 1, s
->store
);
1216 if (status
!= 1) return ASL_STATUS_WRITE_FAILED
;
1218 /* return to the end of the store (this is expected by other routines) */
1219 status
= fseeko(s
->store
, 0, SEEK_END
);
1220 if (status
!= 0) return ASL_STATUS_WRITE_FAILED
;
1225 s
->file_size
= (size_t)ftello(s
->store
);
1229 return ASL_STATUS_OK
;
1233 asl_file_fetch_object(asl_file_t
*s
, uint16_t fetch_type
, uint64_t where
, char **out
, uint32_t *outlen
)
1244 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
1245 if (s
->store
== NULL
) return ASL_STATUS_INVALID_STORE
;
1246 if (out
== NULL
) return ASL_STATUS_INVALID_ARG
;
1247 if (outlen
== NULL
) return ASL_STATUS_INVALID_ARG
;
1248 if (where
== 0) return ASL_STATUS_INVALID_ARG
;
1254 x64
= asl_core_htonq(where
);
1255 memcpy(&inls
, &x64
, 1);
1258 if (fetch_type
!= ASL_FILE_TYPE_STR
) return ASL_STATUS_INVALID_STORE
;
1262 if (inls
> 7) return ASL_STATUS_INVALID_STORE
;
1264 p
= 1 + (char *)&x64
;
1265 memset(ils
, 0, sizeof(ils
));
1266 memcpy(ils
, p
, inls
);
1268 if (*out
== NULL
) return ASL_STATUS_NO_MEMORY
;
1271 return ASL_STATUS_OK
;
1274 if (fetch_type
== ASL_FILE_TYPE_STR
)
1276 /* check the string cache */
1277 file_string_t
*sx
, *sp
;
1280 for (sx
= s
->string_list
; sx
!= NULL
; sx
= sx
->next
)
1282 if (sx
->where
== where
)
1284 *out
= strdup(sx
->str
);
1285 if (*out
== NULL
) return ASL_STATUS_NO_MEMORY
;
1287 /* N.B. hash field is used to hold length when reading */
1290 /* Move this string to the head of the list */
1293 file_string_t
*sl
= s
->string_list
;
1294 sp
->next
= sx
->next
;
1296 s
->string_list
= sx
;
1299 return ASL_STATUS_OK
;
1307 if ((off
+ sizeof(uint16_t) + sizeof(uint32_t)) > s
->file_size
) return ASL_STATUS_READ_FAILED
;
1309 status
= fseeko(s
->store
, off
, SEEK_SET
);
1310 if (status
!= 0) return ASL_STATUS_READ_FAILED
;
1313 status
= fread(&type
, sizeof(uint16_t), 1, s
->store
);
1314 if (status
!= 1) return ASL_STATUS_READ_FAILED
;
1316 off
+= sizeof(uint16_t);
1318 if (type
!= fetch_type
) return ASL_STATUS_INVALID_STORE
;
1322 status
= fread(&len
, sizeof(uint32_t), 1, s
->store
);
1323 if (status
!= 1) return ASL_STATUS_READ_FAILED
;
1324 off
+= sizeof(uint32_t);
1327 if ((off
+ len
) > s
->file_size
) return ASL_STATUS_READ_FAILED
;
1329 *out
= calloc(1, len
);
1330 if (*out
== NULL
) return ASL_STATUS_NO_MEMORY
;
1332 status
= fread(*out
, len
, 1, s
->store
);
1337 return ASL_STATUS_READ_FAILED
;
1342 if ((fetch_type
== ASL_FILE_TYPE_STR
) && (len
<= CACHE_MAX_STRING_LEN
))
1344 file_string_t
*sx
= file_string_create(s
);
1349 /* N.B. hash field is used to hold length when reading */
1351 sx
->next
= s
->string_list
;
1352 memcpy(sx
->str
, *out
, len
);
1354 s
->string_list
= sx
;
1356 if (((s
->flags
& ASL_FILE_FLAG_UNLIMITED_CACHE
) == 0) && (s
->string_cache_count
== CACHE_SIZE
))
1358 /* drop last (lru) string from cache */
1359 file_string_t
*sp
= s
->string_list
;
1362 /* NB CACHE_SIZE must be > 1 */
1363 while (sx
->next
!= NULL
)
1370 file_string_dispose(s
, sx
);
1374 s
->string_cache_count
++;
1379 return ASL_STATUS_OK
;
1383 asl_file_fetch_helper_16(asl_file_t
*s
, char **p
, asl_msg_t
*m
, const char *key
)
1388 out
= _asl_get_16(*p
);
1389 *p
+= sizeof(uint16_t);
1391 if ((m
== NULL
) || (key
== NULL
)) return out
;
1393 snprintf(str
, sizeof(str
), "%hu", out
);
1394 asl_msg_set_key_val(m
, key
, str
);
1400 asl_file_fetch_helper_32(asl_file_t
*s
, char **p
, asl_msg_t
*m
, const char *key
, int ignore
, uint32_t ignoreval
)
1405 out
= _asl_get_32(*p
);
1406 *p
+= sizeof(uint32_t);
1408 if ((m
== NULL
) || (key
== NULL
)) return out
;
1411 if ((ignore
!= 0) && (out
== ignoreval
)) doit
= 0;
1414 snprintf(str
, sizeof(str
), "%u", out
);
1415 asl_msg_set_key_val(m
, key
, str
);
1422 asl_file_fetch_helper_64(asl_file_t
*s
, char **p
, asl_msg_t
*m
, const char *key
)
1427 out
= _asl_get_64(*p
);
1428 *p
+= sizeof(uint64_t);
1430 if ((m
== NULL
) || (key
== NULL
)) return out
;
1432 snprintf(str
, sizeof(str
), "%llu", out
);
1433 asl_msg_set_key_val(m
, key
, str
);
1439 asl_file_fetch_helper_str(asl_file_t
*s
, char **p
, asl_msg_t
*m
, const char *key
, uint32_t *err
)
1443 uint32_t status
, len
;
1445 out
= _asl_get_64(*p
);
1446 *p
+= sizeof(uint64_t);
1450 status
= ASL_STATUS_OK
;
1451 if (out
!= 0) status
= asl_file_fetch_object(s
, ASL_FILE_TYPE_STR
, out
, &val
, &len
);
1453 if (err
!= NULL
) *err
= status
;
1454 if ((status
== ASL_STATUS_OK
) && (val
!= NULL
))
1456 asl_msg_set_key_val(m
, key
, val
);
1464 asl_file_fetch_pos(asl_file_t
*s
, uint64_t where
, int dir
, asl_msg_t
**msg
)
1466 char *buf
, *p
, *k
, *v
;
1468 uint32_t i
, status
, len
, buflen
, kvn
;
1473 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
1474 if (s
->store
== NULL
) return ASL_STATUS_INVALID_STORE
;
1475 if (msg
== NULL
) return ASL_STATUS_INVALID_ARG
;
1476 if ((s
->flags
& ASL_FILE_FLAG_READ
) == 0) return ASL_STATUS_WRITE_ONLY
;
1480 status
= asl_file_fetch_object(s
, ASL_FILE_TYPE_MSG
, where
, &buf
, &buflen
);
1481 if ((status
!= ASL_STATUS_OK
) || (buf
== NULL
))
1488 /* check buffer size */
1489 kvn
= _asl_get_32(buf
+ BUFFER_OFFSET_KVCOUNT
);
1490 if (buflen
< (MSG_RECORD_FIXED_LENGTH
- RECORD_COMMON_LEN
+ (kvn
* sizeof(uint64_t))))
1495 return ASL_STATUS_READ_FAILED
;
1498 out
= asl_msg_new(ASL_TYPE_MSG
);
1502 return ASL_STATUS_NO_MEMORY
;
1505 memset(&r
, 0, sizeof(file_record_t
));
1508 r
.next
= asl_file_fetch_helper_64(s
, &p
, NULL
, NULL
);
1509 r
.mid
= asl_file_fetch_helper_64(s
, &p
, out
, ASL_KEY_MSG_ID
);
1510 r
.time
= asl_file_fetch_helper_64(s
, &p
, out
, ASL_KEY_TIME
);
1511 r
.nano
= asl_file_fetch_helper_32(s
, &p
, out
, ASL_KEY_TIME_NSEC
, 0, 0);
1512 r
.level
= asl_file_fetch_helper_16(s
, &p
, out
, ASL_KEY_LEVEL
);
1513 r
.flags
= asl_file_fetch_helper_16(s
, &p
, NULL
, NULL
);
1514 r
.pid
= asl_file_fetch_helper_32(s
, &p
, out
, ASL_KEY_PID
, 0, 0);
1515 r
.uid
= asl_file_fetch_helper_32(s
, &p
, out
, ASL_KEY_UID
, 1, (uint32_t)-1);
1516 r
.gid
= asl_file_fetch_helper_32(s
, &p
, out
, ASL_KEY_GID
, 1, (uint32_t)-1);
1517 r
.ruid
= asl_file_fetch_helper_32(s
, &p
, out
, ASL_KEY_READ_UID
, 1, (uint32_t)-1);
1518 r
.rgid
= asl_file_fetch_helper_32(s
, &p
, out
, ASL_KEY_READ_GID
, 1, (uint32_t)-1);
1519 r
.refpid
= asl_file_fetch_helper_32(s
, &p
, out
, ASL_KEY_REF_PID
, 1, 0);
1520 r
.kvcount
= asl_file_fetch_helper_32(s
, &p
, NULL
, NULL
, 0, 0);
1522 status
= ASL_STATUS_OK
;
1523 r
.host
= asl_file_fetch_helper_str(s
, &p
, out
, ASL_KEY_HOST
, &status
); /* 68 */
1524 if (status
== ASL_STATUS_OK
) r
.sender
= asl_file_fetch_helper_str(s
, &p
, out
, ASL_KEY_SENDER
, &status
); /* 76 */
1525 if (status
== ASL_STATUS_OK
) r
.facility
= asl_file_fetch_helper_str(s
, &p
, out
, ASL_KEY_FACILITY
, &status
); /* 84 */
1526 if (status
== ASL_STATUS_OK
) r
.message
= asl_file_fetch_helper_str(s
, &p
, out
, ASL_KEY_MSG
, &status
); /* 92 */
1527 if (status
== ASL_STATUS_OK
) r
.refproc
= asl_file_fetch_helper_str(s
, &p
, out
, ASL_KEY_REF_PROC
, &status
); /* 100 */
1528 if (status
== ASL_STATUS_OK
) r
.session
= asl_file_fetch_helper_str(s
, &p
, out
, ASL_KEY_SESSION
, &status
); /* 108 */
1530 if (status
!= ASL_STATUS_OK
)
1532 asl_msg_release(out
);
1539 kvn
= r
.kvcount
/ 2;
1541 for (i
= 0; i
< kvn
; i
++)
1547 kv
= _asl_get_64(p
);
1548 p
+= sizeof(uint64_t);
1550 status
= asl_file_fetch_object(s
, ASL_FILE_TYPE_STR
, kv
, &k
, &len
);
1551 if (status
!= ASL_STATUS_OK
)
1553 asl_msg_release(out
);
1560 kv
= _asl_get_64(p
);
1561 p
+= sizeof(uint64_t);
1566 status
= asl_file_fetch_object(s
, ASL_FILE_TYPE_STR
, kv
, &v
, &len
);
1567 if (status
!= ASL_STATUS_OK
)
1570 asl_msg_release(out
);
1578 if ((status
== ASL_STATUS_OK
) && (k
!= NULL
))
1580 asl_msg_set_key_val(out
, k
, v
);
1587 r
.prev
= asl_file_fetch_helper_64(s
, &p
, NULL
, NULL
); /* 116 */
1593 if ((r
.next
!= 0) && (r
.next
<= s
->cursor
))
1596 * Next offset goes backwards or loops.
1597 * The database is corrupt, but we allow this call to fail
1598 * quietly so that the current record fetch succeeds.
1602 return ASL_STATUS_OK
;
1609 if ((r
.prev
!= 0) && (r
.prev
>= s
->cursor
))
1612 * Prev offset goes forward or loops.
1613 * The database is corrupt, but we allow this call to fail
1614 * quietly so that the current record fetch succeeds.
1618 return ASL_STATUS_OK
;
1628 off
= s
->cursor
+ RECORD_COMMON_LEN
+ sizeof(uint64_t);
1629 if (off
> s
->file_size
)
1634 * Next record offset is past the end of the file.
1635 * This is an error, but we allow it to fail quietly
1636 * so that the current record fetch succeeds.
1639 return ASL_STATUS_OK
;
1642 status
= fseeko(s
->store
, off
, SEEK_SET
);
1645 asl_msg_release(out
);
1648 return ASL_STATUS_READ_FAILED
;
1651 status
= fread(&x64
, sizeof(uint64_t), 1, s
->store
);
1654 asl_msg_release(out
);
1657 return ASL_STATUS_READ_FAILED
;
1660 s
->cursor_xid
= asl_core_ntohq(x64
);
1664 return ASL_STATUS_OK
;
1668 asl_file_open_read(const char *path
, asl_file_t
**s
)
1673 uint32_t status
, vers
, last_len
;
1674 char buf
[DB_HEADER_LEN
];
1676 asl_legacy1_t
*legacy
;
1679 memset(&sb
, 0, sizeof(struct stat
));
1680 if (stat(path
, &sb
) != 0) return ASL_STATUS_FAILED
;
1682 f
= fopen(path
, "r");
1685 if (errno
== EACCES
) return ASL_STATUS_ACCESS_DENIED
;
1686 return ASL_STATUS_FAILED
;
1689 i
= fread(buf
, DB_HEADER_LEN
, 1, f
);
1693 return ASL_STATUS_INVALID_STORE
;
1696 /* validate header */
1697 if (strncmp(buf
, ASL_DB_COOKIE
, ASL_DB_COOKIE_LEN
))
1700 return ASL_STATUS_INVALID_STORE
;
1705 vers
= _asl_get_32(buf
+ DB_HEADER_VERS_OFFSET
);
1706 if (vers
== DB_VERSION_LEGACY_1
)
1709 status
= asl_legacy1_open(path
, &legacy
);
1710 if (status
!= ASL_STATUS_OK
) return status
;
1713 out
= (asl_file_t
*)calloc(1, sizeof(asl_file_t
));
1717 return ASL_STATUS_NO_MEMORY
;
1720 out
->asl_type
= ASL_TYPE_FILE
;
1724 out
->flags
= ASL_FILE_FLAG_READ
;
1725 out
->version
= vers
;
1729 out
->flags
|= ASL_FILE_FLAG_LEGACY_STORE
;
1730 out
->legacy
= (void *)legacy
;
1733 return ASL_STATUS_OK
;
1736 out
->first
= _asl_get_64(buf
+ DB_HEADER_FIRST_OFFSET
);
1737 out
->last
= _asl_get_64(buf
+ DB_HEADER_LAST_OFFSET
);
1738 out
->file_size
= (size_t)sb
.st_size
;
1741 * Detect bogus last pointer and check for odd-sized files.
1742 * Setting out->last to zero forces us to follow the linked
1743 * list of records in the file to the last record. That's
1744 * done in the set_position code. It's a bit slower, but it's
1745 * better at preventing crashes in corrupt files.
1748 /* records are at least MSG_RECORD_FIXED_LENGTH bytes */
1749 if ((out
->last
+ MSG_RECORD_FIXED_LENGTH
) > out
->file_size
)
1755 /* read last record length and make sure the file is at least that large */
1756 off
= out
->last
+ RECORD_TYPE_LEN
;
1757 status
= asl_file_read_uint32(out
, off
, &last_len
);
1758 if (status
!= ASL_STATUS_OK
)
1765 if ((out
->last
+ last_len
) > out
->file_size
) out
->last
= 0;
1768 out
->cursor
= out
->first
;
1769 if (out
->cursor
!= 0)
1771 off
= out
->cursor
+ RECORD_COMMON_LEN
+ sizeof(uint64_t);
1772 status
= asl_file_read_uint64(out
, off
, &(out
->cursor_xid
));
1773 if (status
!= ASL_STATUS_OK
)
1782 return ASL_STATUS_OK
;
1786 asl_file_read_set_position_first(asl_file_t
*s
)
1791 s
->cursor
= s
->first
;
1794 if (s
->cursor
== 0) return ASL_STATUS_OK
;
1796 /* read ID of the first record */
1797 off
= s
->cursor
+ RECORD_COMMON_LEN
+ sizeof(uint64_t);
1798 status
= asl_file_read_uint64(s
, off
, &(s
->cursor_xid
));
1803 asl_file_read_set_position_last(asl_file_t
*s
, int do_count
)
1810 * If the file has the offset of the last record, we just go there.
1811 * The last record offset was added to improve performance, so it may
1812 * or may not be there. If we don't have the last record offset, we
1813 * just iterate down the record links to find the last one.
1815 * Note that s->last may be zero if the file is empty.
1818 if ((s
->last
!= 0) && (do_count
== 0))
1820 s
->cursor
= s
->last
;
1821 off
= s
->last
+ RECORD_COMMON_LEN
+ sizeof(uint64_t);
1823 /* read ID of the last record */
1824 status
= asl_file_read_uint64(s
, off
, &(s
->cursor_xid
));
1828 /* start at the first record and iterate */
1829 s
->cursor
= s
->first
;
1835 off
= s
->cursor
+ RECORD_COMMON_LEN
;
1838 /* read next offset */
1839 status
= asl_file_read_uint64(s
, off
, &next
);
1840 if (status
!= ASL_STATUS_OK
) return status
;
1842 /* detect bogus next pointer */
1843 if (((next
+ MSG_RECORD_FIXED_LENGTH
) > s
->file_size
) || (next
<= s
->cursor
)) next
= 0;
1849 if (s
->cursor
== 0) return ASL_STATUS_OK
;
1851 off
= s
->cursor
+ RECORD_COMMON_LEN
+ sizeof(uint64_t);
1852 status
= asl_file_read_uint64(s
, off
, &(s
->cursor_xid
));
1853 return ASL_STATUS_OK
;
1861 asl_file_read_set_position(asl_file_t
*s
, uint32_t pos
)
1864 uint32_t len
, status
;
1867 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
1868 if (s
->version
== 1) return ASL_STATUS_FAILED
;
1870 if (pos
== ASL_FILE_POSITION_FIRST
) return asl_file_read_set_position_first(s
);
1871 if (pos
== ASL_FILE_POSITION_LAST
) return asl_file_read_set_position_last(s
, 0);
1875 if (pos
== ASL_FILE_POSITION_PREVIOUS
)
1877 if (s
->cursor
== s
->first
) return ASL_STATUS_NO_RECORDS
;
1878 if (s
->cursor
== 0) return ASL_STATUS_NO_RECORDS
;
1880 off
= s
->cursor
+ RECORD_TYPE_LEN
;
1881 status
= asl_file_read_uint32(s
, off
, &len
);
1882 if (status
!= ASL_STATUS_OK
) return status
;
1884 /* set offset to read the "previous" field at the end of the record */
1885 off
= s
->cursor
+ RECORD_COMMON_LEN
+ len
- sizeof(uint64_t);
1887 else if (pos
== ASL_FILE_POSITION_NEXT
)
1889 if (s
->cursor
== s
->last
) return ASL_STATUS_NO_RECORDS
;
1890 if (s
->cursor
== 0) return ASL_STATUS_NO_RECORDS
;
1892 /* set offset to read the "next" field in the current record */
1893 off
= s
->cursor
+ RECORD_COMMON_LEN
;
1895 else return ASL_STATUS_INVALID_ARG
;
1900 * read offset of next / previous
1903 status
= asl_file_read_uint64(s
, off
, &next
);
1904 if (status
!= ASL_STATUS_OK
) return ASL_STATUS_READ_FAILED
;
1906 /* detect bogus next pointer */
1907 if ((next
+ MSG_RECORD_FIXED_LENGTH
) > s
->file_size
) next
= 0;
1908 else if ((pos
== ASL_FILE_POSITION_PREVIOUS
) && (next
>= s
->cursor
)) next
= 0;
1909 else if ((pos
== ASL_FILE_POSITION_NEXT
) && (next
<= s
->cursor
)) next
= 0;
1912 if (s
->cursor
== 0) return ASL_STATUS_NO_RECORDS
;
1914 /* read ID of the record */
1915 off
= s
->cursor
+ RECORD_COMMON_LEN
+ sizeof(uint64_t);
1916 status
= asl_file_read_uint64(s
, off
, &(s
->cursor_xid
));
1921 asl_file_fetch_next(asl_file_t
*s
, asl_msg_t
**msg
)
1923 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
1924 if (s
->version
== 1) return ASL_STATUS_FAILED
;
1926 return asl_file_fetch_pos(s
, s
->cursor
, 1, msg
);
1930 asl_file_fetch_previous(asl_file_t
*s
, asl_msg_t
**msg
)
1932 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
1933 if (s
->version
== 1) return ASL_STATUS_FAILED
;
1935 return asl_file_fetch_pos(s
, s
->cursor
, -1, msg
);
1939 asl_file_fetch(asl_file_t
*s
, uint64_t mid
, asl_msg_t
**msg
)
1943 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
1944 if ((s
->flags
& ASL_FILE_FLAG_READ
) == 0) return ASL_STATUS_WRITE_ONLY
;
1946 if (s
->version
== 1)
1948 if (msg
== NULL
) return ASL_STATUS_OK
;
1949 return asl_legacy1_fetch((asl_legacy1_t
*)s
->legacy
, mid
, msg
);
1952 if (s
->cursor_xid
== 0)
1954 status
= asl_file_read_set_position(s
, ASL_FILE_POSITION_FIRST
);
1955 if (status
!= ASL_STATUS_OK
) return status
;
1956 if (s
->cursor_xid
== 0) return ASL_STATUS_INVALID_ID
;
1959 while (s
->cursor_xid
< mid
)
1961 status
= asl_file_read_set_position(s
, ASL_FILE_POSITION_NEXT
);
1962 if (status
!= ASL_STATUS_OK
) return status
;
1963 if (s
->cursor_xid
> mid
) return ASL_STATUS_INVALID_ID
;
1964 if (s
->cursor_xid
== 0) return ASL_STATUS_INVALID_ID
;
1967 while (s
->cursor_xid
> mid
)
1969 status
= asl_file_read_set_position(s
, ASL_FILE_POSITION_PREVIOUS
);
1970 if (status
!= ASL_STATUS_OK
) return status
;
1971 if (s
->cursor_xid
< mid
) return ASL_STATUS_INVALID_ID
;
1972 if (s
->cursor_xid
== 0) return ASL_STATUS_INVALID_ID
;
1975 if (s
->cursor_xid
!= mid
) return ASL_STATUS_INVALID_ID
;
1977 if (msg
== NULL
) return ASL_STATUS_OK
;
1978 return asl_file_fetch_pos(s
, s
->cursor
, 1, msg
);
1981 __private_extern__
uint64_t
1982 asl_file_cursor(asl_file_t
*s
)
1984 if (s
== NULL
) return 0;
1985 if ((s
->flags
& ASL_FILE_FLAG_READ
) == 0) return 0;
1986 if (s
->version
== 1) return 0;
1988 return s
->cursor_xid
;
1991 __private_extern__ ASL_STATUS
1992 asl_file_match_start(asl_file_t
*s
, uint64_t start
, int32_t direction
)
1996 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
1997 if (s
->version
== 1) return ASL_STATUS_INVALID_STORE
;
1998 if ((s
->flags
& ASL_FILE_FLAG_READ
) == 0) return ASL_STATUS_WRITE_ONLY
;
2000 d
= ASL_FILE_POSITION_NEXT
;
2001 if (direction
< 0) d
= ASL_FILE_POSITION_PREVIOUS
;
2004 * find starting point
2006 status
= ASL_STATUS_OK
;
2007 if (direction
>= 0) status
= asl_file_read_set_position(s
, ASL_FILE_POSITION_FIRST
);
2008 else status
= asl_file_read_set_position(s
, ASL_FILE_POSITION_LAST
);
2009 if (status
!= ASL_STATUS_OK
) return status
;
2011 while ((status
== ASL_STATUS_OK
) && (((direction
>= 0) && (s
->cursor_xid
< start
)) || ((direction
< 0) && (s
->cursor_xid
> start
))))
2013 status
= asl_file_read_set_position(s
, d
);
2019 __private_extern__ ASL_STATUS
2020 asl_file_match_next(asl_file_t
*s
, asl_msg_list_t
*query
, asl_msg_t
**msg
, uint64_t *last
, int32_t direction
)
2022 uint32_t status
, d
, i
, do_match
, did_match
;
2025 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
2026 if (msg
== NULL
) return ASL_STATUS_INVALID_ARG
;
2027 if (s
->version
== 1) return ASL_STATUS_INVALID_STORE
;
2028 if ((s
->flags
& ASL_FILE_FLAG_READ
) == 0) return ASL_STATUS_WRITE_ONLY
;
2029 if (s
->cursor
== 0) return ASL_STATUS_NO_RECORDS
;
2034 if (query
== NULL
) do_match
= 0;
2035 else if (query
->count
== 0) do_match
= 0;
2037 d
= ASL_FILE_POSITION_NEXT
;
2038 if (direction
< 0) d
= ASL_FILE_POSITION_PREVIOUS
;
2042 *last
= s
->cursor_xid
;
2044 status
= asl_file_fetch_pos(s
, s
->cursor
, direction
, &m
);
2045 if (status
== ASL_STATUS_ACCESS_DENIED
) return ASL_STATUS_MATCH_FAILED
;
2046 if ((status
== ASL_STATUS_INVALID_ARG
) && (s
->cursor
== 0)) return ASL_STATUS_NO_RECORDS
;
2047 if (status
!= ASL_STATUS_OK
) return status
;
2055 for (i
= 0; (i
< query
->count
) && (did_match
== 0); i
++)
2057 did_match
= asl_msg_cmp(query
->msg
[i
], m
);
2064 return ASL_STATUS_OK
;
2069 return ASL_STATUS_MATCH_FAILED
;
2073 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
)
2075 uint32_t status
, d
, i
, do_match
, did_match
, rescount
;
2077 struct timeval now
, finish
;
2078 asl_msg_list_t
*out
= NULL
;
2080 if (s
== NULL
) return NULL
;
2081 if ((s
->flags
& ASL_FILE_FLAG_READ
) == 0) return NULL
;
2083 if (s
->version
== 1)
2085 asl_legacy1_match((asl_legacy1_t
*)s
->legacy
, qlist
, &out
, last
, start
, count
, direction
);
2090 if (qlist
== NULL
) do_match
= 0;
2091 else if (qlist
->count
== 0) do_match
= 0;
2095 d
= ASL_FILE_POSITION_NEXT
;
2096 if (direction
< 0) d
= ASL_FILE_POSITION_PREVIOUS
;
2099 * find starting point
2101 status
= ASL_STATUS_OK
;
2102 if (direction
>= 0) status
= asl_file_read_set_position(s
, ASL_FILE_POSITION_FIRST
);
2103 else status
= asl_file_read_set_position(s
, ASL_FILE_POSITION_LAST
);
2104 if (status
!= ASL_STATUS_OK
) return NULL
;
2106 while ((status
== ASL_STATUS_OK
) && (((direction
>= 0) && (s
->cursor_xid
< start
)) || ((direction
< 0) && (s
->cursor_xid
> start
))))
2108 status
= asl_file_read_set_position(s
, d
);
2111 /* start the timer if a duration was specified */
2112 memset(&finish
, 0, sizeof(struct timeval
));
2115 if (gettimeofday(&finish
, NULL
) == 0)
2117 finish
.tv_sec
+= (duration
/ USEC_PER_SEC
);
2118 finish
.tv_usec
+= (duration
% USEC_PER_SEC
);
2119 if (finish
.tv_usec
> USEC_PER_SEC
)
2121 finish
.tv_usec
-= USEC_PER_SEC
;
2127 /* shouldn't happen, but if gettimeofday failed we just run without a timeout */
2128 memset(&finish
, 0, sizeof(struct timeval
));
2133 * loop through records
2138 status
= asl_file_fetch_pos(s
, s
->cursor
, direction
, &m
);
2139 if (status
== ASL_STATUS_ACCESS_DENIED
) continue;
2140 if (status
!= ASL_STATUS_OK
) break;
2142 *last
= s
->cursor_xid
;
2150 for (i
= 0; (i
< qlist
->count
) && (did_match
== 0); i
++)
2152 did_match
= asl_msg_cmp(qlist
->msg
[i
], m
);
2158 /* append m to res */
2161 out
= asl_msg_list_new();
2162 if (out
== NULL
) return NULL
;
2165 asl_msg_list_append(out
, m
);
2167 if ((count
!= 0) && (rescount
>= count
)) break;
2169 /* check the timer */
2170 if ((finish
.tv_sec
!= 0) && (gettimeofday(&now
, NULL
) == 0))
2172 if ((now
.tv_sec
> finish
.tv_sec
) || ((now
.tv_sec
== finish
.tv_sec
) && (now
.tv_usec
> finish
.tv_usec
))) break;
2183 asl_file_size(asl_file_t
*s
)
2185 if (s
== NULL
) return 0;
2186 return s
->file_size
;
2190 asl_file_ctime(asl_file_t
*s
)
2192 if (s
== NULL
) return 0;
2197 asl_file_list_close(asl_file_list_t
*head
)
2199 asl_file_list_t
*next
;
2201 while (head
!= NULL
)
2204 asl_file_close(head
->file
);
2211 asl_file_list_free(asl_file_list_t
*head
)
2213 asl_file_list_t
*next
;
2215 while (head
!= NULL
)
2223 static asl_file_list_t
*
2224 asl_file_list_insert(asl_file_list_t
*list
, asl_file_t
*f
, int32_t dir
)
2226 asl_file_list_t
*a
, *b
, *tmp
;
2228 if (f
== NULL
) return list
;
2230 tmp
= (asl_file_list_t
*)calloc(1, sizeof(asl_file_list_t
));
2231 if (tmp
== NULL
) return NULL
;
2234 if (list
== NULL
) return tmp
;
2237 if (((dir
< 0) && (f
->cursor_xid
> a
->file
->cursor_xid
)) || ((dir
>= 0) && (f
->cursor_xid
< a
->file
->cursor_xid
)))
2246 if (((dir
< 0) && (f
->cursor_xid
> b
->file
->cursor_xid
)) || ((dir
>= 0) && (f
->cursor_xid
< b
->file
->cursor_xid
)))
2262 asl_file_list_add(asl_file_list_t
*list
, asl_file_t
*f
)
2264 asl_file_list_t
*tmp
;
2266 if (f
== NULL
) return list
;
2267 if (f
->version
== 1) return list
;
2269 tmp
= (asl_file_list_t
*)calloc(1, sizeof(asl_file_list_t
));
2270 if (tmp
== NULL
) return NULL
;
2278 asl_file_list_match_start(asl_file_list_t
*list
, uint64_t start
, int32_t direction
)
2282 asl_file_match_token_t
*out
;
2284 if (list
== NULL
) return NULL
;
2286 out
= (asl_file_match_token_t
*)calloc(1, sizeof(asl_file_match_token_t
));
2287 if (out
== NULL
) return NULL
;
2289 for (n
= list
; n
!= NULL
; n
= n
->next
)
2291 /* init file for the search */
2292 status
= asl_file_match_start(n
->file
, start
, direction
);
2293 if (status
!= ASL_STATUS_OK
) continue;
2294 if (n
->file
->cursor_xid
== 0) continue;
2296 out
->list
= asl_file_list_insert(out
->list
, n
->file
, direction
);
2299 out
->dir
= direction
;
2304 asl_file_list_match_next(void *token
, asl_msg_list_t
*qlist
, asl_msg_list_t
**res
, uint32_t count
)
2306 uint32_t status
, rescount
;
2309 asl_file_match_token_t
*work
;
2312 if (token
== NULL
) return ASL_STATUS_OK
;
2313 if (res
== NULL
) return ASL_STATUS_INVALID_ARG
;
2315 work
= (asl_file_match_token_t
*)token
;
2320 while ((work
->list
!= NULL
) && ((rescount
< count
) || (count
== 0)))
2323 status
= asl_file_match_next(work
->list
->file
, qlist
, &m
, &last
, work
->dir
);
2326 if (*res
== NULL
) *res
= asl_msg_list_new();
2329 asl_file_list_free(work
->list
);
2331 return ASL_STATUS_NO_MEMORY
;
2334 asl_msg_list_append(*res
, m
);
2339 if ((status
!= ASL_STATUS_OK
) || (work
->list
->file
->cursor_xid
== 0))
2341 n
= work
->list
->next
;
2346 if (work
->list
!= NULL
)
2348 n
= work
->list
->next
;
2351 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
)))
2354 work
->list
= work
->list
->next
;
2356 work
->list
= asl_file_list_insert(work
->list
, n
->file
, work
->dir
);
2363 return ASL_STATUS_OK
;
2367 asl_file_list_match_end(void *token
)
2369 asl_file_match_token_t
*work
;
2371 if (token
== NULL
) return;
2373 work
= (asl_file_match_token_t
*)token
;
2374 asl_file_list_free(work
->list
);
2381 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
)
2383 uint32_t status
, rescount
;
2384 asl_file_list_t
*files
, *n
;
2386 struct timeval now
, finish
;
2387 asl_msg_list_t
*out
= NULL
;
2389 if (list
== NULL
) return NULL
;
2390 if (last
== NULL
) return NULL
;
2394 for (n
= list
; n
!= NULL
; n
= n
->next
)
2396 /* init file for the search */
2397 status
= asl_file_match_start(n
->file
, start
, direction
);
2398 if (status
!= ASL_STATUS_OK
) continue;
2399 if (n
->file
->cursor_xid
== 0) continue;
2401 files
= asl_file_list_insert(files
, n
->file
, direction
);
2406 asl_file_list_free(files
);
2410 /* start the timer if a timeout was specified */
2411 memset(&finish
, 0, sizeof(struct timeval
));
2414 if (gettimeofday(&finish
, NULL
) == 0)
2416 finish
.tv_sec
+= (duration
/ USEC_PER_SEC
);
2417 finish
.tv_usec
+= (duration
% USEC_PER_SEC
);
2418 if (finish
.tv_usec
> USEC_PER_SEC
)
2420 finish
.tv_usec
-= USEC_PER_SEC
;
2426 /* shouldn't happen, but if gettimeofday failed we just run without a timeout */
2427 memset(&finish
, 0, sizeof(struct timeval
));
2432 while ((files
!= NULL
) && ((rescount
< count
) || (count
== 0)))
2435 status
= asl_file_match_next(files
->file
, qlist
, &m
, last
, direction
);
2438 if (out
== NULL
) out
= asl_msg_list_new();
2441 asl_file_list_free(files
);
2445 asl_msg_list_append(out
, m
);
2450 if (files
->file
->cursor_xid
== 0)
2462 if (((direction
< 0) && (files
->file
->cursor_xid
<= n
->file
->cursor_xid
)) || ((direction
>= 0) && (files
->file
->cursor_xid
> n
->file
->cursor_xid
)))
2465 files
= files
->next
;
2467 files
= asl_file_list_insert(files
, n
->file
, direction
);
2473 /* check the timer */
2474 if ((finish
.tv_sec
!= 0) && (gettimeofday(&now
, NULL
) == 0))
2476 if ((now
.tv_sec
> finish
.tv_sec
) || ((now
.tv_sec
== finish
.tv_sec
) && (now
.tv_usec
> finish
.tv_usec
))) break;
2480 asl_file_list_free(files
);
2485 #pragma mark asl_object support
2488 _jump_dealloc(asl_object_private_t
*obj
)
2490 _asl_file_free_internal((asl_file_t
*)obj
);
2494 _jump_count(asl_object_private_t
*obj
)
2496 asl_file_t
*s
= (asl_file_t
*)obj
;
2497 if (s
== NULL
) return 0;
2498 if ((s
->flags
& ASL_FILE_FLAG_READ
) == 0) return 0;
2500 uint64_t cursor
= s
->cursor
;
2501 uint64_t cursor_xid
= s
->cursor_xid
;
2503 if (asl_file_read_set_position_last((asl_file_t
*)obj
, 1) != ASL_STATUS_OK
) return 0;
2506 s
->cursor_xid
= cursor_xid
;
2507 return s
->msg_count
;
2510 static asl_object_private_t
*
2511 _jump_next(asl_object_private_t
*obj
)
2513 asl_msg_t
*msg
= NULL
;
2514 if (asl_file_fetch_next((asl_file_t
*)obj
, &msg
) != ASL_STATUS_OK
) return NULL
;
2515 return (asl_object_private_t
*)msg
;
2518 static asl_object_private_t
*
2519 _jump_prev(asl_object_private_t
*obj
)
2521 asl_msg_t
*msg
= NULL
;
2522 if (asl_file_fetch_previous((asl_file_t
*)obj
, &msg
) != ASL_STATUS_OK
) return NULL
;
2523 return (asl_object_private_t
*)msg
;
2526 /* we don't really need to support this, but we are generous */
2527 static asl_object_private_t
*
2528 _jump_get_object_at_index(asl_object_private_t
*obj
, size_t n
)
2530 asl_msg_t
*msg
= NULL
;
2532 if (asl_file_fetch((asl_file_t
*)obj
, mid
, &msg
) != ASL_STATUS_OK
) return NULL
;
2533 return (asl_object_private_t
*)msg
;
2537 _jump_set_iteration_index(asl_object_private_t
*obj
, size_t n
)
2539 asl_file_t
*s
= (asl_file_t
*)obj
;
2540 if (s
== NULL
) return;
2541 if ((s
->flags
& ASL_FILE_FLAG_READ
) == 0) return;
2545 asl_file_read_set_position_first(s
);
2547 else if (n
== SIZE_MAX
)
2549 asl_file_read_set_position_last(s
, 0);
2553 /* we don't really need to support this, but we are generous */
2554 asl_file_fetch(s
, n
, NULL
);
2559 _jump_append(asl_object_private_t
*obj
, asl_object_private_t
*newobj
, void *addr
)
2562 asl_file_t
*s
= (asl_file_t
*)obj
;
2563 int type
= asl_get_type((asl_object_t
)newobj
);
2564 if (s
== NULL
) return;
2565 if (s
->flags
& ASL_FILE_FLAG_READ
) return;
2567 if (type
== ASL_TYPE_LIST
)
2570 asl_msg_list_reset_iteration((asl_msg_list_t
*)newobj
, 0);
2571 while (NULL
!= (msg
= asl_msg_list_next((asl_msg_list_t
*)newobj
)))
2573 if (asl_file_save(s
, msg
, &xid
) != ASL_STATUS_OK
) return;
2576 else if ((type
== ASL_TYPE_MSG
) || (type
== ASL_TYPE_QUERY
))
2578 asl_file_save(s
, (asl_msg_t
*)newobj
, &xid
);
2582 static asl_object_private_t
*
2583 _jump_search(asl_object_private_t
*obj
, asl_object_private_t
*query
)
2585 asl_file_t
*s
= (asl_file_t
*)obj
;
2586 int type
= asl_get_type((asl_object_t
)query
);
2587 asl_msg_list_t
*out
= NULL
;
2588 asl_msg_list_t
*ql
= NULL
;
2593 return (asl_object_private_t
*)asl_file_match(s
, NULL
, &last
, 0, 0, 0, 1);
2595 else if (type
== ASL_TYPE_LIST
)
2597 return (asl_object_private_t
*)asl_file_match(s
, (asl_msg_list_t
*)query
, &last
, 0, 0, 0, 1);
2599 else if ((type
== ASL_TYPE_MSG
) || (type
== ASL_TYPE_QUERY
))
2601 ql
= asl_msg_list_new();
2602 asl_msg_list_append(ql
, query
);
2604 out
= asl_file_match(s
, ql
, &last
, 0, 0, 0, 1);
2605 asl_msg_list_release(ql
);
2606 return (asl_object_private_t
*)out
;
2612 static asl_object_private_t
*
2613 _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
)
2616 asl_msg_list_t
*out
= asl_file_match((asl_file_t
*)obj
, (asl_msg_list_t
*)qlist
, &x
, start
, count
, duration
, dir
);
2618 return (asl_object_private_t
*)out
;
2621 __private_extern__
const asl_jump_table_t
*
2622 asl_file_jump_table()
2624 static const asl_jump_table_t jump
=
2627 .dealloc
= &_jump_dealloc
,
2628 .set_key_val_op
= NULL
,
2630 .get_val_op_for_key
= NULL
,
2631 .get_key_val_op_at_index
= NULL
,
2632 .count
= &_jump_count
,
2633 .next
= &_jump_next
,
2634 .prev
= &_jump_prev
,
2635 .get_object_at_index
= &_jump_get_object_at_index
,
2636 .set_iteration_index
= &_jump_set_iteration_index
,
2637 .remove_object_at_index
= NULL
,
2638 .append
= &_jump_append
,
2640 .search
= &_jump_search
,
2641 .match
= &_jump_match