]>
Commit | Line | Data |
---|---|---|
b5d655f7 | 1 | /* |
ad3c9f2a | 2 | * Copyright (c) 2007-2011 Apple Inc. All rights reserved. |
b5d655f7 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
1f2f436a A |
5 | * |
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 | |
11 | * file. | |
b5d655f7 A |
12 | * |
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, | |
1f2f436a A |
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. | |
b5d655f7 A |
20 | * |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | #include <asl_core.h> | |
25 | #include <asl_file.h> | |
26 | #include <fcntl.h> | |
27 | #include <stdlib.h> | |
28 | #include <stddef.h> | |
29 | #include <stdio.h> | |
30 | #include <unistd.h> | |
31 | #include <sys/errno.h> | |
32 | #include <string.h> | |
33 | #include <sys/stat.h> | |
1f2f436a A |
34 | #include <sys/types.h> |
35 | #include <sys/acl.h> | |
36 | #include <membership.h> | |
b5d655f7 A |
37 | #include <time.h> |
38 | #include <sys/time.h> | |
39 | #include <asl_private.h> | |
40 | #include <asl_legacy1.h> | |
ad3c9f2a | 41 | #include <TargetConditionals.h> |
b5d655f7 A |
42 | |
43 | extern time_t asl_parse_time(const char *str); | |
1f2f436a | 44 | extern int asl_msg_cmp(aslmsg a, aslmsg b); |
b5d655f7 A |
45 | |
46 | #define forever for(;;) | |
47 | #define MILLION 1000000 | |
48 | ||
49 | /* | |
50 | * MSG and STR records have (at least) a type (uint16_t) and a length (uint32_t) | |
51 | * type and level are both 16 bit fields so that alignment isn't a pain. | |
52 | */ | |
53 | #define RECORD_COMMON_LEN 6 | |
54 | #define RECORD_TYPE_LEN 2 | |
34e8f829 | 55 | #define BUFFER_OFFSET_KVCOUNT 56 |
b5d655f7 A |
56 | |
57 | #define SCRATCH_BUFFER_SIZE (MSG_RECORD_FIXED_LENGTH + (20 * sizeof(uint64_t))) | |
58 | ||
59 | typedef struct | |
60 | { | |
61 | uint64_t next; | |
62 | uint64_t mid; | |
63 | uint64_t time; | |
64 | uint32_t nano; | |
65 | uint16_t level; | |
66 | uint16_t flags; | |
67 | uint32_t pid; | |
68 | uint32_t uid; | |
69 | uint32_t gid; | |
70 | uint32_t ruid; | |
71 | uint32_t rgid; | |
72 | uint32_t refpid; | |
73 | uint32_t kvcount; | |
74 | uint64_t host; | |
75 | uint64_t sender; | |
76 | uint64_t facility; | |
77 | uint64_t message; | |
78 | uint64_t refproc; | |
79 | uint64_t session; | |
80 | uint64_t prev; | |
81 | } file_record_t; | |
82 | ||
83 | typedef struct | |
84 | { | |
85 | asl_file_list_t *list; | |
86 | int dir; | |
87 | } asl_file_match_token_t; | |
88 | ||
89 | static uint16_t | |
90 | _asl_get_16(char *h) | |
91 | { | |
92 | uint16_t x; | |
93 | ||
94 | memcpy(&x, h, 2); | |
95 | return ntohs(x); | |
96 | } | |
97 | ||
98 | static void | |
99 | _asl_put_16(uint16_t i, char *h) | |
100 | { | |
101 | uint16_t x; | |
102 | ||
103 | x = htons(i); | |
104 | memcpy(h, &x, 2); | |
105 | } | |
106 | ||
107 | static uint32_t | |
108 | _asl_get_32(char *h) | |
109 | { | |
110 | uint32_t x; | |
111 | ||
112 | memcpy(&x, h, 4); | |
113 | return ntohl(x); | |
114 | } | |
115 | ||
116 | static void | |
117 | _asl_put_32(uint32_t i, char *h) | |
118 | { | |
119 | uint32_t x; | |
120 | ||
121 | x = htonl(i); | |
122 | memcpy(h, &x, 4); | |
123 | } | |
124 | ||
125 | static uint64_t | |
126 | _asl_get_64(char *h) | |
127 | { | |
128 | uint64_t x; | |
129 | ||
130 | memcpy(&x, h, 8); | |
131 | return asl_core_ntohq(x); | |
132 | } | |
133 | ||
134 | static void | |
135 | _asl_put_64(uint64_t i, char *h) | |
136 | { | |
137 | uint64_t x; | |
138 | ||
139 | x = asl_core_htonq(i); | |
140 | memcpy(h, &x, 8); | |
141 | } | |
142 | ||
143 | static uint32_t | |
144 | asl_file_read_uint32(asl_file_t *s, off_t off, uint32_t *out) | |
145 | { | |
146 | uint32_t status, val; | |
147 | ||
148 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
149 | if (s->store == NULL) return ASL_STATUS_INVALID_STORE; | |
34e8f829 | 150 | if ((off + sizeof(uint32_t)) > s->file_size) return ASL_STATUS_READ_FAILED; |
b5d655f7 A |
151 | |
152 | status = fseeko(s->store, off, SEEK_SET); | |
153 | if (status != 0) return ASL_STATUS_READ_FAILED; | |
154 | ||
155 | val = 0; | |
156 | ||
157 | status = fread(&val, sizeof(uint32_t), 1, s->store); | |
158 | if (status != 1) return ASL_STATUS_READ_FAILED; | |
159 | ||
160 | if (out != NULL) *out = ntohl(val); | |
161 | return ASL_STATUS_OK; | |
162 | } | |
163 | ||
164 | static uint32_t | |
165 | asl_file_read_uint64(asl_file_t *s, off_t off, uint64_t *out) | |
166 | { | |
167 | uint32_t status; | |
168 | uint64_t val; | |
169 | ||
170 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
171 | if (s->store == NULL) return ASL_STATUS_INVALID_STORE; | |
34e8f829 | 172 | if ((off + sizeof(uint64_t)) > s->file_size) return ASL_STATUS_READ_FAILED; |
b5d655f7 A |
173 | |
174 | status = fseeko(s->store, off, SEEK_SET); | |
175 | if (status != 0) return ASL_STATUS_READ_FAILED; | |
176 | ||
177 | val = 0; | |
178 | ||
179 | status = fread(&val, sizeof(uint64_t), 1, s->store); | |
180 | if (status != 1) return ASL_STATUS_READ_FAILED; | |
181 | ||
182 | if (out != NULL) *out = asl_core_ntohq(val); | |
183 | return ASL_STATUS_OK; | |
184 | } | |
185 | ||
186 | uint32_t | |
187 | asl_file_close(asl_file_t *s) | |
188 | { | |
189 | file_string_t *x; | |
190 | ||
191 | if (s == NULL) return ASL_STATUS_OK; | |
192 | ||
193 | if (s->version == 1) | |
194 | { | |
195 | return asl_legacy1_close((asl_legacy1_t *)s->legacy); | |
196 | } | |
197 | ||
198 | while (s->string_list != NULL) | |
199 | { | |
200 | x = s->string_list->next; | |
201 | free(s->string_list); | |
202 | s->string_list = x; | |
203 | } | |
204 | ||
205 | if (s->store != NULL) fclose(s->store); | |
34e8f829 | 206 | if (s->scratch != NULL) free(s->scratch); |
b5d655f7 A |
207 | |
208 | memset(s, 0, sizeof(asl_file_t)); | |
209 | free(s); | |
210 | ||
211 | return ASL_STATUS_OK; | |
212 | } | |
213 | ||
1f2f436a A |
214 | __private_extern__ uint32_t |
215 | asl_file_open_write_fd(int fd, asl_file_t **s) | |
b5d655f7 A |
216 | { |
217 | time_t now; | |
1f2f436a | 218 | int status; |
b5d655f7 A |
219 | char buf[DB_HEADER_LEN]; |
220 | asl_file_t *out; | |
b5d655f7 | 221 | |
1f2f436a A |
222 | if (fd < 0) return ASL_STATUS_FAILED; |
223 | if (s == NULL) return ASL_STATUS_FAILED; | |
b5d655f7 | 224 | |
1f2f436a A |
225 | out = (asl_file_t *)calloc(1, sizeof(asl_file_t)); |
226 | if (out == NULL) return ASL_STATUS_NO_MEMORY; | |
227 | ||
228 | out->store = fdopen(fd, "w+"); | |
229 | if (out->store == NULL) | |
b5d655f7 | 230 | { |
1f2f436a A |
231 | free(out); |
232 | return ASL_STATUS_FAILED; | |
233 | } | |
b5d655f7 | 234 | |
1f2f436a A |
235 | memset(buf, 0, sizeof(buf)); |
236 | memcpy(buf, ASL_DB_COOKIE, ASL_DB_COOKIE_LEN); | |
b5d655f7 | 237 | |
1f2f436a | 238 | _asl_put_32(DB_VERSION, buf + DB_HEADER_VERS_OFFSET); |
b5d655f7 | 239 | |
1f2f436a A |
240 | now = time(NULL); |
241 | out->dob = now; | |
242 | _asl_put_64(out->dob, buf + DB_HEADER_TIME_OFFSET); | |
34e8f829 | 243 | |
1f2f436a | 244 | _asl_put_32(CACHE_SIZE, buf + DB_HEADER_CSIZE_OFFSET); |
b5d655f7 | 245 | |
1f2f436a A |
246 | status = fwrite(buf, sizeof(buf), 1, out->store); |
247 | if (status != 1) | |
248 | { | |
249 | fclose(out->store); | |
250 | free(out); | |
251 | return ASL_STATUS_FAILED; | |
252 | } | |
34e8f829 | 253 | |
1f2f436a A |
254 | /* flush data */ |
255 | fflush(out->store); | |
b5d655f7 | 256 | |
1f2f436a | 257 | out->file_size = sizeof(buf); |
b5d655f7 | 258 | |
1f2f436a A |
259 | /* scratch buffer for file writes (we test for NULL before using it) */ |
260 | out->scratch = malloc(SCRATCH_BUFFER_SIZE); | |
b5d655f7 | 261 | |
1f2f436a | 262 | *s = out; |
b5d655f7 | 263 | |
1f2f436a A |
264 | return ASL_STATUS_OK; |
265 | } | |
b5d655f7 | 266 | |
1f2f436a A |
267 | __private_extern__ int |
268 | asl_file_create(const char *path, uid_t uid, gid_t gid, mode_t mode) | |
269 | { | |
ad3c9f2a A |
270 | #if TARGET_OS_EMBEDDED |
271 | return open(path, O_RDWR | O_CREAT | O_EXCL, mode); | |
272 | #else | |
1f2f436a A |
273 | acl_t acl; |
274 | uuid_t uuid; | |
275 | acl_entry_t entry; | |
276 | acl_permset_t perms; | |
277 | int status; | |
278 | int fd = -1; | |
b5d655f7 | 279 | |
ad3c9f2a A |
280 | /* -1 means don't set ACL for uid or gid */ |
281 | if ((uid == -1) && (gid == -1)) | |
282 | { | |
283 | return open(path, O_RDWR | O_CREAT | O_EXCL, mode); | |
284 | } | |
285 | ||
1f2f436a | 286 | acl = acl_init(1); |
b5d655f7 | 287 | |
ad3c9f2a | 288 | if ((gid != 0) && (gid != -1)) |
1f2f436a A |
289 | { |
290 | status = mbr_gid_to_uuid(gid, uuid); | |
291 | if (status != 0) goto asl_file_create_return; | |
292 | ||
293 | status = acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY); | |
294 | if (status != 0) goto asl_file_create_return; | |
295 | ||
296 | status = acl_set_tag_type(entry, ACL_EXTENDED_ALLOW); | |
297 | if (status != 0) goto asl_file_create_return; | |
298 | ||
299 | status = acl_set_qualifier(entry, &uuid); | |
300 | if (status != 0) goto asl_file_create_return; | |
301 | ||
302 | status = acl_get_permset(entry, &perms); | |
303 | if (status != 0) goto asl_file_create_return; | |
304 | ||
305 | status = acl_add_perm(perms, ACL_READ_DATA); | |
306 | if (status != 0) goto asl_file_create_return; | |
307 | } | |
308 | ||
ad3c9f2a | 309 | if ((uid != 0) && (uid != -1)) |
1f2f436a A |
310 | { |
311 | status = mbr_uid_to_uuid(uid, uuid); | |
312 | if (status != 0) goto asl_file_create_return; | |
313 | ||
314 | status = acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY); | |
315 | if (status != 0) goto asl_file_create_return; | |
316 | ||
317 | status = acl_set_tag_type(entry, ACL_EXTENDED_ALLOW); | |
318 | if (status != 0) goto asl_file_create_return; | |
319 | ||
320 | status = acl_set_qualifier(entry, &uuid); | |
321 | if (status != 0) goto asl_file_create_return; | |
322 | ||
323 | status = acl_get_permset(entry, &perms); | |
324 | if (status != 0) goto asl_file_create_return; | |
325 | ||
326 | status = acl_add_perm(perms, ACL_READ_DATA); | |
327 | if (status != 0) goto asl_file_create_return; | |
328 | } | |
b5d655f7 A |
329 | |
330 | fd = open(path, O_RDWR | O_CREAT | O_EXCL, mode); | |
1f2f436a | 331 | if (fd < 0) goto asl_file_create_return; |
b5d655f7 | 332 | |
1f2f436a | 333 | status = acl_set_fd(fd, acl); |
b5d655f7 A |
334 | if (status != 0) |
335 | { | |
336 | close(fd); | |
1f2f436a | 337 | fd = -1; |
b5d655f7 | 338 | unlink(path); |
b5d655f7 A |
339 | } |
340 | ||
1f2f436a A |
341 | asl_file_create_return: |
342 | ||
343 | acl_free(acl); | |
344 | return fd; | |
ad3c9f2a | 345 | #endif |
1f2f436a | 346 | } |
b5d655f7 | 347 | |
1f2f436a A |
348 | uint32_t |
349 | asl_file_open_write(const char *path, mode_t mode, uid_t uid, gid_t gid, asl_file_t **s) | |
350 | { | |
351 | int i, status, fd; | |
352 | struct stat sb; | |
353 | char buf[DB_HEADER_LEN]; | |
354 | asl_file_t *out; | |
355 | uint32_t aslstatus, vers, last_len; | |
356 | off_t off; | |
357 | ||
358 | memset(&sb, 0, sizeof(struct stat)); | |
359 | ||
360 | status = stat(path, &sb); | |
361 | if (status == 0) | |
b5d655f7 | 362 | { |
1f2f436a A |
363 | /* must be a plain file */ |
364 | if (!S_ISREG(sb.st_mode)) return ASL_STATUS_INVALID_STORE; | |
b5d655f7 | 365 | |
1f2f436a A |
366 | if (sb.st_size == 0) |
367 | { | |
368 | fd = open(path, O_RDWR | O_EXCL, mode); | |
369 | if (fd < 0) return ASL_STATUS_FAILED; | |
b5d655f7 | 370 | |
1f2f436a A |
371 | return asl_file_open_write_fd(fd, s); |
372 | } | |
373 | else | |
374 | { | |
375 | /* XXX Check that mode, uid, and gid are correct */ | |
376 | out = (asl_file_t *)calloc(1, sizeof(asl_file_t)); | |
377 | if (out == NULL) return ASL_STATUS_NO_MEMORY; | |
b5d655f7 | 378 | |
1f2f436a A |
379 | out->store = fopen(path, "r+"); |
380 | if (out->store == NULL) | |
381 | { | |
382 | free(out); | |
383 | return ASL_STATUS_FAILED; | |
384 | } | |
b5d655f7 | 385 | |
1f2f436a A |
386 | i = fread(buf, DB_HEADER_LEN, 1, out->store); |
387 | if (i < 1) | |
388 | { | |
389 | asl_file_close(out); | |
390 | return ASL_STATUS_READ_FAILED; | |
391 | } | |
b5d655f7 | 392 | |
1f2f436a A |
393 | /* check cookie */ |
394 | if (strncmp(buf, ASL_DB_COOKIE, ASL_DB_COOKIE_LEN)) | |
395 | { | |
396 | asl_file_close(out); | |
397 | return ASL_STATUS_INVALID_STORE; | |
398 | } | |
399 | ||
400 | /* check version */ | |
401 | vers = _asl_get_32(buf + DB_HEADER_VERS_OFFSET); | |
402 | if (vers != DB_VERSION) | |
403 | { | |
404 | asl_file_close(out); | |
405 | return ASL_STATUS_INVALID_STORE; | |
406 | } | |
407 | ||
408 | out->dob = _asl_get_64(buf + DB_HEADER_TIME_OFFSET); | |
409 | out->first = _asl_get_64(buf + DB_HEADER_FIRST_OFFSET); | |
410 | out->last = _asl_get_64(buf + DB_HEADER_LAST_OFFSET); | |
411 | out->file_size = (size_t)sb.st_size; | |
412 | ||
413 | /* | |
414 | * Detect bogus last pointer and check for odd-sized files. | |
415 | * Setting out->last to zero forces asl_file_read_set_position to | |
416 | * follow the linked list of records in the file to the last record. | |
417 | * It's slower, but it's better at preventing crashes in corrupt files. | |
418 | */ | |
419 | ||
420 | /* records are at least MSG_RECORD_FIXED_LENGTH bytes */ | |
421 | if ((out->last + MSG_RECORD_FIXED_LENGTH) > out->file_size) | |
422 | { | |
423 | out->last = 0; | |
424 | } | |
425 | else | |
426 | { | |
427 | /* read last record length and make sure the file is at least that large */ | |
428 | off = out->last + RECORD_TYPE_LEN; | |
429 | status = asl_file_read_uint32(out, off, &last_len); | |
430 | if (status != ASL_STATUS_OK) | |
431 | { | |
432 | asl_file_close(out); | |
433 | return status; | |
434 | } | |
435 | ||
436 | if ((out->last + last_len) > out->file_size) out->last = 0; | |
437 | } | |
438 | ||
439 | aslstatus = asl_file_read_set_position(out, ASL_FILE_POSITION_LAST); | |
440 | if (aslstatus != ASL_STATUS_OK) | |
441 | { | |
442 | asl_file_close(out); | |
443 | return aslstatus; | |
444 | } | |
445 | ||
446 | out->prev = out->cursor; | |
447 | status = fseeko(out->store, 0, SEEK_END); | |
448 | if (status != 0) | |
449 | { | |
450 | asl_file_close(out); | |
451 | return ASL_STATUS_READ_FAILED; | |
452 | } | |
453 | ||
454 | out->file_size = (size_t)ftello(out->store); | |
455 | ||
456 | /* scratch buffer for file writes (we test for NULL before using it) */ | |
457 | out->scratch = malloc(SCRATCH_BUFFER_SIZE); | |
458 | ||
459 | *s = out; | |
460 | ||
461 | return ASL_STATUS_OK; | |
462 | } | |
463 | } | |
464 | else if (errno != ENOENT) | |
b5d655f7 | 465 | { |
1f2f436a | 466 | /* unexpected status */ |
b5d655f7 A |
467 | return ASL_STATUS_FAILED; |
468 | } | |
469 | ||
1f2f436a A |
470 | /* the file does not exist */ |
471 | fd = asl_file_create(path, uid, gid, mode); | |
472 | if (fd < 0) return ASL_STATUS_FAILED; | |
b5d655f7 | 473 | |
1f2f436a A |
474 | aslstatus = asl_file_open_write_fd(fd, s); |
475 | if (aslstatus != ASL_STATUS_OK) unlink(path); | |
b5d655f7 | 476 | |
1f2f436a | 477 | return aslstatus; |
b5d655f7 A |
478 | } |
479 | ||
480 | uint32_t | |
481 | asl_file_compact(asl_file_t *s, const char *path, mode_t mode, uid_t uid, gid_t gid) | |
482 | { | |
483 | asl_file_t *new; | |
484 | struct stat sb; | |
485 | aslmsg m; | |
486 | uint64_t xid; | |
487 | uint32_t status; | |
488 | ||
489 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
490 | if (path == NULL) return ASL_STATUS_INVALID_ARG; | |
491 | ||
492 | if (s->version == 1) return ASL_STATUS_FAILED; | |
493 | ||
494 | memset(&sb, 0, sizeof(struct stat)); | |
495 | ||
496 | if (stat(path, &sb) == 0) return ASL_STATUS_FAILED; | |
497 | if (errno != ENOENT) return ASL_STATUS_FAILED; | |
498 | ||
499 | status = asl_file_read_set_position(s, ASL_FILE_POSITION_FIRST); | |
500 | if (status != ASL_STATUS_OK) return status; | |
501 | ||
502 | new = NULL; | |
503 | status = asl_file_open_write(path, mode, uid, gid, &new); | |
504 | if (status != ASL_STATUS_OK) return status; | |
505 | new->flags = ASL_FILE_FLAG_UNLIMITED_CACHE | ASL_FILE_FLAG_PRESERVE_MSG_ID; | |
506 | ||
507 | while ((status == ASL_STATUS_OK) && (s->cursor != 0)) | |
508 | { | |
509 | m = NULL; | |
510 | status = asl_file_fetch_next(s, &m); | |
511 | if (status != ASL_STATUS_OK) break; | |
512 | ||
513 | xid = 0; | |
514 | status = asl_file_save(new, m, &xid); | |
515 | asl_free(m); | |
516 | } | |
517 | ||
518 | asl_file_close(new); | |
519 | return status; | |
520 | } | |
521 | ||
522 | static uint32_t | |
523 | asl_file_string_encode(asl_file_t *s, const char *str, uint64_t *out) | |
524 | { | |
525 | uint32_t i, hash, len, x32; | |
526 | file_string_t *sp, *sx, *sl; | |
527 | uint64_t x64; | |
528 | uint8_t inls; | |
529 | uint16_t type; | |
530 | off_t off; | |
531 | char *p; | |
532 | ||
533 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
534 | if (str == NULL) return ASL_STATUS_INVALID_ARG; | |
535 | ||
536 | len = strlen(str); | |
537 | ||
538 | /* inline strings */ | |
539 | if (len < 8) | |
540 | { | |
541 | /* inline string */ | |
542 | inls = len; | |
543 | inls |= 0x80; | |
544 | ||
545 | x64 = 0; | |
546 | p = (char *)&x64; | |
547 | memcpy(p, &inls, 1); | |
548 | memcpy(p + 1, str, len); | |
549 | *out = asl_core_ntohq(x64); | |
550 | return ASL_STATUS_OK; | |
551 | } | |
552 | ||
553 | /* check the cache */ | |
554 | hash = asl_core_string_hash(str, len); | |
555 | ||
556 | sp = NULL; | |
557 | for (sx = s->string_list; sx != NULL; sx = sx->next) | |
558 | { | |
559 | if ((hash == sx->hash) && (!strcmp(str, sx->str))) | |
560 | { | |
561 | /* Move this string to the head of the list */ | |
562 | if (sp != NULL) | |
563 | { | |
564 | sl = s->string_list; | |
565 | sp->next = sx->next; | |
566 | sx->next = sl; | |
567 | s->string_list = sx; | |
568 | } | |
569 | ||
570 | *out = sx->where; | |
571 | return ASL_STATUS_OK; | |
572 | } | |
573 | ||
574 | sp = sx; | |
575 | } | |
576 | ||
577 | off = ftello(s->store); | |
578 | ||
579 | /* Type */ | |
580 | type = htons(ASL_FILE_TYPE_STR); | |
581 | i = fwrite(&type, sizeof(uint16_t), 1, s->store); | |
582 | if (i != 1) return ASL_STATUS_WRITE_FAILED; | |
b5d655f7 A |
583 | |
584 | /* Length (includes trailing nul) */ | |
585 | x32 = htonl(len + 1); | |
586 | i = fwrite(&x32, sizeof(uint32_t), 1, s->store); | |
587 | if (i != 1) return ASL_STATUS_WRITE_FAILED; | |
b5d655f7 A |
588 | |
589 | /* String data (nul terminated) */ | |
590 | i = fwrite(str, len + 1, 1, s->store); | |
591 | if (i != 1) return ASL_STATUS_WRITE_FAILED; | |
b5d655f7 | 592 | |
1f2f436a A |
593 | /* flush data */ |
594 | fflush(s->store); | |
595 | ||
b5d655f7 A |
596 | /* create file_string_t and insert into the cache */ |
597 | sx = (file_string_t *)calloc(1, offsetof(file_string_t, str) + len + 1); | |
598 | if (sx == NULL) return ASL_STATUS_NO_MEMORY; | |
599 | ||
600 | sx->where = off; | |
601 | sx->hash = hash; | |
602 | sx->next = s->string_list; | |
603 | memcpy(sx->str, str, len); | |
604 | ||
605 | s->string_list = sx; | |
606 | ||
607 | if (((s->flags & ASL_FILE_FLAG_UNLIMITED_CACHE) == 0) && (s->string_count == CACHE_SIZE)) | |
608 | { | |
609 | /* drop last (lru) string from cache */ | |
610 | sp = s->string_list; | |
611 | sx = sp->next; | |
612 | ||
613 | /* NB CACHE_SIZE must be > 1 */ | |
614 | while (sx->next != NULL) | |
615 | { | |
616 | sp = sx; | |
617 | sx = sx->next; | |
618 | } | |
619 | ||
620 | sp->next = NULL; | |
621 | free(sx); | |
622 | } | |
623 | else | |
624 | { | |
625 | s->string_count++; | |
626 | } | |
627 | ||
628 | *out = off; | |
629 | return ASL_STATUS_OK; | |
630 | } | |
631 | ||
632 | /* | |
633 | * Encode an aslmsg as a record structure. | |
634 | * Creates and caches strings. | |
635 | */ | |
636 | uint32_t | |
1f2f436a | 637 | asl_file_save(asl_file_t *s, aslmsg in, uint64_t *mid) |
b5d655f7 A |
638 | { |
639 | char *buf, *p; | |
1f2f436a | 640 | uint32_t i, len, x, status; |
b5d655f7 A |
641 | file_record_t r; |
642 | uint64_t k, v; | |
643 | uint64_t *kvlist; | |
644 | off_t off; | |
1f2f436a A |
645 | asl_msg_t *msg; |
646 | const char *key, *val; | |
b5d655f7 A |
647 | |
648 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
1f2f436a | 649 | if (in == NULL) return ASL_STATUS_INVALID_MESSAGE; |
b5d655f7 A |
650 | |
651 | if (s->flags & ASL_FILE_FLAG_READ_ONLY) return ASL_STATUS_READ_ONLY; | |
652 | ||
1f2f436a A |
653 | msg = (asl_msg_t *)in; |
654 | ||
b5d655f7 A |
655 | memset(&r, 0, sizeof(file_record_t)); |
656 | ||
657 | r.flags = 0; | |
658 | r.level = ASL_LEVEL_DEBUG; | |
659 | r.pid = -1; | |
660 | r.uid = -2; | |
661 | r.gid = -2; | |
662 | r.ruid = -1; | |
663 | r.rgid = -1; | |
664 | r.time = 0; | |
665 | r.nano = 0; | |
666 | r.prev = s->prev; | |
667 | kvlist = NULL; | |
668 | ||
1f2f436a A |
669 | key = NULL; |
670 | val = NULL; | |
671 | ||
672 | for (x = asl_msg_fetch(msg, 0, &key, &val, NULL); x != IndexNull; x = asl_msg_fetch(msg, x, &key, &val, NULL)) | |
b5d655f7 | 673 | { |
1f2f436a | 674 | if (key == NULL) |
b5d655f7 A |
675 | { |
676 | continue; | |
677 | } | |
1f2f436a | 678 | else if (!strcmp(key, ASL_KEY_TIME)) |
b5d655f7 | 679 | { |
1f2f436a | 680 | if (val != NULL) r.time = asl_parse_time(val); |
b5d655f7 | 681 | } |
1f2f436a | 682 | else if (!strcmp(key, ASL_KEY_TIME_NSEC)) |
b5d655f7 | 683 | { |
1f2f436a | 684 | if (val != NULL) r.nano = atoi(val); |
b5d655f7 | 685 | } |
1f2f436a | 686 | else if (!strcmp(key, ASL_KEY_HOST)) |
b5d655f7 | 687 | { |
1f2f436a | 688 | if (val != NULL) |
b5d655f7 | 689 | { |
1f2f436a | 690 | status = asl_file_string_encode(s, val, &(r.host)); |
b5d655f7 A |
691 | if (status != ASL_STATUS_OK) |
692 | { | |
693 | if (kvlist != NULL) free(kvlist); | |
694 | return status; | |
695 | } | |
696 | } | |
697 | } | |
1f2f436a | 698 | else if (!strcmp(key, ASL_KEY_SENDER)) |
b5d655f7 | 699 | { |
1f2f436a | 700 | if (val != NULL) |
b5d655f7 | 701 | { |
1f2f436a | 702 | status = asl_file_string_encode(s, val, &(r.sender)); |
b5d655f7 A |
703 | if (status != ASL_STATUS_OK) |
704 | { | |
705 | if (kvlist != NULL) free(kvlist); | |
706 | return status; | |
707 | } | |
708 | } | |
709 | } | |
1f2f436a | 710 | else if (!strcmp(key, ASL_KEY_PID)) |
b5d655f7 | 711 | { |
1f2f436a | 712 | if (val != NULL) r.pid = atoi(val); |
b5d655f7 | 713 | } |
1f2f436a | 714 | else if (!strcmp(key, ASL_KEY_REF_PID)) |
b5d655f7 | 715 | { |
1f2f436a | 716 | if (val != NULL) r.refpid = atoi(val); |
b5d655f7 | 717 | } |
1f2f436a | 718 | else if (!strcmp(key, ASL_KEY_UID)) |
b5d655f7 | 719 | { |
1f2f436a | 720 | if (val != NULL) r.uid = atoi(val); |
b5d655f7 | 721 | } |
1f2f436a | 722 | else if (!strcmp(key, ASL_KEY_GID)) |
b5d655f7 | 723 | { |
1f2f436a | 724 | if (val != NULL) r.gid = atoi(val); |
b5d655f7 | 725 | } |
1f2f436a | 726 | else if (!strcmp(key, ASL_KEY_LEVEL)) |
b5d655f7 | 727 | { |
1f2f436a | 728 | if (val != NULL) r.level = atoi(val); |
b5d655f7 | 729 | } |
1f2f436a | 730 | else if (!strcmp(key, ASL_KEY_MSG)) |
b5d655f7 | 731 | { |
1f2f436a | 732 | if (val != NULL) |
b5d655f7 | 733 | { |
1f2f436a | 734 | status = asl_file_string_encode(s, val, &(r.message)); |
b5d655f7 A |
735 | if (status != ASL_STATUS_OK) |
736 | { | |
737 | if (kvlist != NULL) free(kvlist); | |
738 | return status; | |
739 | } | |
740 | } | |
741 | } | |
1f2f436a | 742 | else if (!strcmp(key, ASL_KEY_FACILITY)) |
b5d655f7 | 743 | { |
1f2f436a | 744 | if (val != NULL) |
b5d655f7 | 745 | { |
1f2f436a | 746 | status = asl_file_string_encode(s, val, &(r.facility)); |
b5d655f7 A |
747 | if (status != ASL_STATUS_OK) |
748 | { | |
749 | if (kvlist != NULL) free(kvlist); | |
750 | return status; | |
751 | } | |
752 | } | |
753 | } | |
1f2f436a | 754 | else if (!strcmp(key, ASL_KEY_REF_PROC)) |
b5d655f7 | 755 | { |
1f2f436a | 756 | if (val != NULL) |
b5d655f7 | 757 | { |
1f2f436a | 758 | status = asl_file_string_encode(s, val, &(r.refproc)); |
b5d655f7 A |
759 | if (status != ASL_STATUS_OK) |
760 | { | |
761 | if (kvlist != NULL) free(kvlist); | |
762 | return status; | |
763 | } | |
764 | } | |
765 | } | |
1f2f436a | 766 | else if (!strcmp(key, ASL_KEY_SESSION)) |
b5d655f7 | 767 | { |
1f2f436a | 768 | if (val != NULL) |
b5d655f7 | 769 | { |
1f2f436a | 770 | status = asl_file_string_encode(s, val, &(r.session)); |
b5d655f7 A |
771 | if (status != ASL_STATUS_OK) |
772 | { | |
773 | if (kvlist != NULL) free(kvlist); | |
774 | return status; | |
775 | } | |
776 | } | |
777 | } | |
1f2f436a | 778 | else if (!strcmp(key, ASL_KEY_READ_UID)) |
b5d655f7 | 779 | { |
1f2f436a | 780 | if (((r.flags & ASL_MSG_FLAG_READ_UID_SET) == 0) && (val != NULL)) |
b5d655f7 | 781 | { |
1f2f436a | 782 | r.ruid = atoi(val); |
b5d655f7 A |
783 | r.flags |= ASL_MSG_FLAG_READ_UID_SET; |
784 | } | |
785 | } | |
1f2f436a | 786 | else if (!strcmp(key, ASL_KEY_READ_GID)) |
b5d655f7 | 787 | { |
1f2f436a | 788 | if (((r.flags & ASL_MSG_FLAG_READ_GID_SET) == 0) && (val != NULL)) |
b5d655f7 | 789 | { |
1f2f436a | 790 | r.rgid = atoi(val); |
b5d655f7 A |
791 | r.flags |= ASL_MSG_FLAG_READ_GID_SET; |
792 | } | |
793 | } | |
1f2f436a | 794 | else if (!strcmp(key, ASL_KEY_MSG_ID)) |
b5d655f7 | 795 | { |
1f2f436a | 796 | if (s->flags & ASL_FILE_FLAG_PRESERVE_MSG_ID) *mid = atoll(val); |
b5d655f7 | 797 | } |
1f2f436a | 798 | else if (!strcmp(key, ASL_KEY_OPTION)) |
34e8f829 A |
799 | { |
800 | /* ignore - we don't save ASLOption */ | |
801 | } | |
b5d655f7 A |
802 | else |
803 | { | |
1f2f436a | 804 | status = asl_file_string_encode(s, key, &k); |
b5d655f7 A |
805 | if (status != ASL_STATUS_OK) |
806 | { | |
807 | if (kvlist != NULL) free(kvlist); | |
808 | return status; | |
809 | } | |
810 | ||
811 | v = 0; | |
1f2f436a | 812 | if (val != NULL) |
b5d655f7 | 813 | { |
1f2f436a | 814 | status = asl_file_string_encode(s, val, &v); |
b5d655f7 A |
815 | if (status != ASL_STATUS_OK) |
816 | { | |
817 | if (kvlist != NULL) free(kvlist); | |
818 | return status; | |
819 | } | |
820 | } | |
821 | ||
822 | if (r.kvcount == 0) | |
823 | { | |
824 | kvlist = (uint64_t *)calloc(2, sizeof(uint64_t)); | |
825 | } | |
826 | else | |
827 | { | |
828 | kvlist = (uint64_t *)reallocf(kvlist, (r.kvcount + 2) * sizeof(uint64_t)); | |
829 | } | |
830 | ||
831 | if (kvlist == NULL) | |
832 | { | |
833 | return ASL_STATUS_NO_MEMORY; | |
834 | } | |
835 | ||
836 | kvlist[r.kvcount++] = k; | |
837 | kvlist[r.kvcount++] = v; | |
838 | } | |
839 | } | |
840 | ||
841 | len = MSG_RECORD_FIXED_LENGTH + (r.kvcount * sizeof(uint64_t)); | |
842 | buf = NULL; | |
843 | ||
844 | /* use the scratch buffer if it exists and is large enough */ | |
845 | if ((s->scratch != NULL) && (len <= SCRATCH_BUFFER_SIZE)) | |
846 | { | |
847 | memset(s->scratch, 0, SCRATCH_BUFFER_SIZE); | |
848 | buf = s->scratch; | |
849 | } | |
850 | else | |
851 | { | |
852 | buf = calloc(1, len); | |
853 | } | |
854 | ||
855 | if (buf == NULL) return ASL_STATUS_NO_MEMORY; | |
856 | ||
857 | if (*mid != 0) | |
858 | { | |
859 | r.mid = *mid; | |
860 | } | |
861 | else | |
862 | { | |
863 | r.mid = asl_core_new_msg_id(0); | |
864 | *mid = r.mid; | |
865 | } | |
866 | ||
867 | p = buf; | |
868 | ||
869 | /* Type */ | |
870 | _asl_put_16(ASL_FILE_TYPE_MSG, p); | |
871 | p += sizeof(uint16_t); | |
872 | ||
873 | /* Length of message (excludes type and length fields) */ | |
874 | _asl_put_32(len - RECORD_COMMON_LEN, p); | |
875 | p += sizeof(uint32_t); | |
876 | ||
877 | /* Message data... */ | |
878 | ||
879 | _asl_put_64(r.next, p); | |
880 | p += sizeof(uint64_t); | |
881 | ||
882 | _asl_put_64(r.mid, p); | |
883 | p += sizeof(uint64_t); | |
884 | ||
885 | _asl_put_64(r.time, p); | |
886 | p += sizeof(uint64_t); | |
887 | ||
888 | _asl_put_32(r.nano, p); | |
889 | p += sizeof(uint32_t); | |
890 | ||
891 | _asl_put_16(r.level, p); | |
892 | p += sizeof(uint16_t); | |
893 | ||
894 | _asl_put_16(r.flags, p); | |
895 | p += sizeof(uint16_t); | |
896 | ||
897 | _asl_put_32(r.pid, p); | |
898 | p += sizeof(uint32_t); | |
899 | ||
900 | _asl_put_32(r.uid, p); | |
901 | p += sizeof(uint32_t); | |
902 | ||
903 | _asl_put_32(r.gid, p); | |
904 | p += sizeof(uint32_t); | |
905 | ||
906 | _asl_put_32(r.ruid, p); | |
907 | p += sizeof(uint32_t); | |
908 | ||
909 | _asl_put_32(r.rgid, p); | |
910 | p += sizeof(uint32_t); | |
911 | ||
912 | _asl_put_32(r.refpid, p); | |
913 | p += sizeof(uint32_t); | |
914 | ||
915 | _asl_put_32(r.kvcount, p); | |
916 | p += sizeof(uint32_t); | |
917 | ||
918 | _asl_put_64(r.host, p); | |
919 | p += sizeof(uint64_t); | |
920 | ||
921 | _asl_put_64(r.sender, p); | |
922 | p += sizeof(uint64_t); | |
923 | ||
924 | _asl_put_64(r.facility, p); | |
925 | p += sizeof(uint64_t); | |
926 | ||
927 | _asl_put_64(r.message, p); | |
928 | p += sizeof(uint64_t); | |
929 | ||
930 | _asl_put_64(r.refproc, p); | |
931 | p += sizeof(uint64_t); | |
932 | ||
933 | _asl_put_64(r.session, p); | |
934 | p += sizeof(uint64_t); | |
935 | ||
936 | for (i = 0; i < r.kvcount; i++) | |
937 | { | |
938 | _asl_put_64(kvlist[i], p); | |
939 | p += sizeof(uint64_t); | |
940 | } | |
941 | ||
942 | _asl_put_64(r.prev, p); | |
943 | p += sizeof(uint64_t); | |
944 | ||
945 | free(kvlist); | |
946 | kvlist = NULL; | |
947 | ||
b5d655f7 A |
948 | /* write record at end of file */ |
949 | status = fseeko(s->store, 0, SEEK_END); | |
950 | if (status != 0) return ASL_STATUS_WRITE_FAILED; | |
951 | ||
952 | s->last = (uint64_t)ftello(s->store); | |
34e8f829 | 953 | |
b5d655f7 A |
954 | v = asl_core_htonq(s->last); |
955 | ||
956 | status = fwrite(buf, len, 1, s->store); | |
957 | fflush(s->store); | |
958 | ||
959 | /* free the buffer if it was allocated here */ | |
960 | if (buf != s->scratch) free(buf); | |
961 | ||
962 | /* seek to "next" field of previous record, write last offset */ | |
963 | off = s->prev + RECORD_COMMON_LEN; | |
964 | if (s->prev == 0) off = DB_HEADER_FIRST_OFFSET; | |
965 | ||
966 | status = fseeko(s->store, off, SEEK_SET); | |
967 | if (status != 0) return ASL_STATUS_WRITE_FAILED; | |
968 | ||
969 | status = fwrite(&v, sizeof(uint64_t), 1, s->store); | |
970 | if (status != 1) return ASL_STATUS_WRITE_FAILED; | |
971 | ||
972 | /* seek to DB_HEADER_LAST_OFFSET, write last record offset */ | |
973 | off = DB_HEADER_LAST_OFFSET; | |
974 | ||
975 | status = fseeko(s->store, off, SEEK_SET); | |
976 | if (status != 0) return ASL_STATUS_WRITE_FAILED; | |
977 | ||
978 | status = fwrite(&v, sizeof(uint64_t), 1, s->store); | |
979 | if (status != 1) return ASL_STATUS_WRITE_FAILED; | |
980 | ||
981 | /* return to the end of the store (this is expected by other routines) */ | |
982 | status = fseeko(s->store, 0, SEEK_END); | |
983 | if (status != 0) return ASL_STATUS_WRITE_FAILED; | |
984 | ||
1f2f436a A |
985 | /* flush data */ |
986 | fflush(s->store); | |
987 | ||
34e8f829 A |
988 | s->file_size = (size_t)ftello(s->store); |
989 | ||
b5d655f7 A |
990 | s->prev = s->last; |
991 | ||
992 | return ASL_STATUS_OK; | |
993 | } | |
994 | ||
995 | static uint32_t | |
34e8f829 | 996 | asl_file_fetch_object(asl_file_t *s, uint64_t where, char **out, uint32_t *outlen) |
b5d655f7 A |
997 | { |
998 | static char ils[9]; | |
999 | char *p; | |
1000 | uint32_t len; | |
1001 | int status; | |
1002 | uint64_t x64; | |
1003 | uint8_t inls; | |
1004 | uint16_t type; | |
1005 | off_t off; | |
1006 | ||
34e8f829 A |
1007 | *out = NULL; |
1008 | *outlen = 0; | |
1009 | ||
b5d655f7 A |
1010 | if (s == NULL) return ASL_STATUS_INVALID_STORE; |
1011 | if (out == NULL) return ASL_STATUS_INVALID_ARG; | |
1012 | if (where == 0) return ASL_STATUS_INVALID_ARG; | |
1013 | ||
b5d655f7 A |
1014 | inls = 0; |
1015 | x64 = asl_core_htonq(where); | |
1016 | memcpy(&inls, &x64, 1); | |
1017 | if (inls & 0x80) | |
1018 | { | |
1019 | /* inline string */ | |
b5d655f7 | 1020 | inls &= 0x0f; |
34e8f829 A |
1021 | if (inls > 7) return ASL_STATUS_INVALID_STORE; |
1022 | ||
b5d655f7 | 1023 | p = 1 + (char *)&x64; |
34e8f829 | 1024 | memset(ils, 0, sizeof(ils)); |
b5d655f7 A |
1025 | memcpy(ils, p, inls); |
1026 | *out = strdup(ils); | |
b5d655f7 | 1027 | if (*out == NULL) return ASL_STATUS_NO_MEMORY; |
34e8f829 A |
1028 | |
1029 | *outlen = inls; | |
b5d655f7 A |
1030 | return ASL_STATUS_OK; |
1031 | } | |
1032 | ||
1033 | off = where; | |
34e8f829 A |
1034 | if ((off + sizeof(uint16_t) + sizeof(uint32_t)) > s->file_size) return ASL_STATUS_READ_FAILED; |
1035 | ||
b5d655f7 A |
1036 | status = fseeko(s->store, off, SEEK_SET); |
1037 | if (status != 0) return ASL_STATUS_READ_FAILED; | |
1038 | ||
1039 | /* Type */ | |
1040 | status = fread(&type, sizeof(uint16_t), 1, s->store); | |
1041 | if (status != 1) return ASL_STATUS_READ_FAILED; | |
34e8f829 | 1042 | off += sizeof(uint16_t); |
b5d655f7 A |
1043 | |
1044 | /* Length */ | |
1045 | len = 0; | |
1046 | status = fread(&len, sizeof(uint32_t), 1, s->store); | |
1047 | if (status != 1) return ASL_STATUS_READ_FAILED; | |
34e8f829 A |
1048 | off += sizeof(uint32_t); |
1049 | ||
b5d655f7 | 1050 | len = ntohl(len); |
34e8f829 | 1051 | if ((off + len) > s->file_size) return ASL_STATUS_READ_FAILED; |
b5d655f7 A |
1052 | |
1053 | *out = calloc(1, len); | |
1054 | if (*out == NULL) return ASL_STATUS_NO_MEMORY; | |
1055 | ||
1056 | status = fread(*out, len, 1, s->store); | |
1057 | if (status != 1) | |
1058 | { | |
1059 | free(*out); | |
1060 | return ASL_STATUS_READ_FAILED; | |
1061 | } | |
1062 | ||
34e8f829 | 1063 | *outlen = len; |
b5d655f7 A |
1064 | return ASL_STATUS_OK; |
1065 | } | |
1066 | ||
1067 | static uint16_t | |
1068 | asl_file_fetch_helper_16(asl_file_t *s, char **p, aslmsg m, const char *key) | |
1069 | { | |
1070 | uint16_t out; | |
1071 | char str[256]; | |
1072 | ||
1073 | out = _asl_get_16(*p); | |
1074 | *p += sizeof(uint16_t); | |
1075 | ||
1076 | if ((m == NULL) || (key == NULL)) return out; | |
1077 | ||
1078 | snprintf(str, sizeof(str), "%hu", out); | |
1079 | asl_set(m, key, str); | |
1080 | ||
1081 | return out; | |
1082 | } | |
1083 | ||
1084 | static uint32_t | |
1085 | asl_file_fetch_helper_32(asl_file_t *s, char **p, aslmsg m, const char *key, int ignore, uint32_t ignoreval) | |
1086 | { | |
1087 | uint32_t out, doit; | |
1088 | char str[256]; | |
1089 | ||
1090 | out = _asl_get_32(*p); | |
1091 | *p += sizeof(uint32_t); | |
1092 | ||
1093 | if ((m == NULL) || (key == NULL)) return out; | |
1094 | ||
1095 | doit = 1; | |
1096 | if ((ignore != 0) && (out == ignoreval)) doit = 0; | |
1097 | if (doit != 0) | |
1098 | { | |
1099 | snprintf(str, sizeof(str), "%u", out); | |
1100 | asl_set(m, key, str); | |
1101 | } | |
1102 | ||
1103 | return out; | |
1104 | } | |
1105 | ||
1106 | static uint64_t | |
1107 | asl_file_fetch_helper_64(asl_file_t *s, char **p, aslmsg m, const char *key) | |
1108 | { | |
1109 | uint64_t out; | |
1110 | char str[256]; | |
1111 | ||
1112 | out = _asl_get_64(*p); | |
1113 | *p += sizeof(uint64_t); | |
1114 | ||
1115 | if ((m == NULL) || (key == NULL)) return out; | |
1116 | ||
1117 | snprintf(str, sizeof(str), "%llu", out); | |
1118 | asl_set(m, key, str); | |
1119 | ||
1120 | return out; | |
1121 | } | |
1122 | ||
1123 | static uint64_t | |
34e8f829 | 1124 | asl_file_fetch_helper_str(asl_file_t *s, char **p, aslmsg m, const char *key, uint32_t *err) |
b5d655f7 A |
1125 | { |
1126 | uint64_t out; | |
1127 | char *val; | |
34e8f829 | 1128 | uint32_t status, len; |
b5d655f7 A |
1129 | |
1130 | out = _asl_get_64(*p); | |
1131 | *p += sizeof(uint64_t); | |
1132 | ||
1133 | val = NULL; | |
34e8f829 A |
1134 | len = 0; |
1135 | status = ASL_STATUS_OK; | |
1136 | if (out != 0) status = asl_file_fetch_object(s, out, &val, &len); | |
1137 | ||
1138 | if (err != NULL) *err = status; | |
b5d655f7 A |
1139 | if ((status == ASL_STATUS_OK) && (val != NULL)) |
1140 | { | |
1141 | asl_set(m, key, val); | |
1142 | free(val); | |
1143 | } | |
1144 | ||
1145 | return out; | |
1146 | } | |
1147 | ||
1148 | static uint32_t | |
1149 | asl_file_fetch_pos(asl_file_t *s, uint64_t where, int dir, aslmsg *msg) | |
1150 | { | |
1151 | char *buf, *p, *k, *v; | |
1152 | file_record_t r; | |
34e8f829 | 1153 | uint32_t i, status, len, buflen, kvn; |
b5d655f7 A |
1154 | uint64_t x64, kv; |
1155 | aslmsg out; | |
1156 | off_t off; | |
1157 | ||
1158 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
1159 | if (msg == NULL) return ASL_STATUS_INVALID_ARG; | |
1160 | if ((s->flags & ASL_FILE_FLAG_READ_ONLY) == 0) return ASL_STATUS_WRITE_ONLY; | |
1161 | ||
1162 | buf = NULL; | |
34e8f829 A |
1163 | buflen = 0; |
1164 | status = asl_file_fetch_object(s, where, &buf, &buflen); | |
1165 | if ((status != ASL_STATUS_OK) || (buf == NULL)) | |
1166 | { | |
1167 | s->cursor = 0; | |
1168 | s->cursor_xid = 0; | |
1169 | return status; | |
1170 | } | |
1171 | ||
1172 | /* check buffer size */ | |
1173 | kvn = _asl_get_32(buf + BUFFER_OFFSET_KVCOUNT); | |
1174 | if (buflen < (MSG_RECORD_FIXED_LENGTH - RECORD_COMMON_LEN + (kvn * sizeof(uint64_t)))) | |
1175 | { | |
1176 | free(buf); | |
1177 | s->cursor = 0; | |
1178 | s->cursor_xid = 0; | |
1179 | return ASL_STATUS_READ_FAILED; | |
1180 | } | |
b5d655f7 A |
1181 | |
1182 | out = asl_new(ASL_TYPE_MSG); | |
1183 | if (out == NULL) return ASL_STATUS_NO_MEMORY; | |
1184 | ||
1185 | memset(&r, 0, sizeof(file_record_t)); | |
1186 | p = buf; | |
1187 | ||
1188 | r.next = asl_file_fetch_helper_64(s, &p, NULL, NULL); | |
1189 | r.mid = asl_file_fetch_helper_64(s, &p, out, ASL_KEY_MSG_ID); | |
1190 | r.time = asl_file_fetch_helper_64(s, &p, out, ASL_KEY_TIME); | |
1191 | r.nano = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_TIME_NSEC, 0, 0); | |
1192 | r.level = asl_file_fetch_helper_16(s, &p, out, ASL_KEY_LEVEL); | |
1193 | r.flags = asl_file_fetch_helper_16(s, &p, NULL, NULL); | |
1194 | r.pid = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_PID, 0, 0); | |
1195 | r.uid = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_UID, 1, (uint32_t)-1); | |
1196 | r.gid = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_GID, 1, (uint32_t)-1); | |
1197 | r.ruid = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_READ_UID, 1, (uint32_t)-1); | |
1198 | r.rgid = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_READ_GID, 1, (uint32_t)-1); | |
1199 | r.refpid = asl_file_fetch_helper_32(s, &p, out, ASL_KEY_REF_PID, 1, 0); | |
1200 | r.kvcount = asl_file_fetch_helper_32(s, &p, NULL, NULL, 0, 0); | |
34e8f829 A |
1201 | |
1202 | status = ASL_STATUS_OK; | |
1203 | r.host = asl_file_fetch_helper_str(s, &p, out, ASL_KEY_HOST, &status); /* 68 */ | |
1204 | if (status == ASL_STATUS_OK) r.sender = asl_file_fetch_helper_str(s, &p, out, ASL_KEY_SENDER, &status); /* 76 */ | |
1205 | if (status == ASL_STATUS_OK) r.facility = asl_file_fetch_helper_str(s, &p, out, ASL_KEY_FACILITY, &status); /* 84 */ | |
1206 | if (status == ASL_STATUS_OK) r.message = asl_file_fetch_helper_str(s, &p, out, ASL_KEY_MSG, &status); /* 92 */ | |
1207 | if (status == ASL_STATUS_OK) r.refproc = asl_file_fetch_helper_str(s, &p, out, ASL_KEY_REF_PROC, &status); /* 100 */ | |
1208 | if (status == ASL_STATUS_OK) r.session = asl_file_fetch_helper_str(s, &p, out, ASL_KEY_SESSION, &status); /* 108 */ | |
1209 | ||
1210 | if (status != ASL_STATUS_OK) | |
1211 | { | |
1212 | asl_free(out); | |
1213 | free(buf); | |
1214 | s->cursor = 0; | |
1215 | s->cursor_xid = 0; | |
1216 | return status; | |
1217 | } | |
1218 | ||
1219 | kvn = r.kvcount / 2; | |
1220 | ||
1221 | for (i = 0; i < kvn; i++) | |
b5d655f7 A |
1222 | { |
1223 | kv = _asl_get_64(p); | |
1224 | p += sizeof(uint64_t); | |
1225 | k = NULL; | |
34e8f829 A |
1226 | len = 0; |
1227 | status = asl_file_fetch_object(s, kv, &k, &len); | |
1228 | if (status != ASL_STATUS_OK) | |
1229 | { | |
1230 | asl_free(out); | |
1231 | free(buf); | |
1232 | s->cursor = 0; | |
1233 | s->cursor_xid = 0; | |
1234 | return status; | |
1235 | } | |
b5d655f7 A |
1236 | |
1237 | kv = _asl_get_64(p); | |
1238 | p += sizeof(uint64_t); | |
1239 | v = NULL; | |
34e8f829 A |
1240 | len = 0; |
1241 | ||
1242 | if (kv != 0) | |
1243 | { | |
1244 | status = asl_file_fetch_object(s, kv, &v, &len); | |
1245 | if (status != ASL_STATUS_OK) | |
1246 | { | |
1247 | asl_free(out); | |
1248 | free(buf); | |
1249 | s->cursor = 0; | |
1250 | s->cursor_xid = 0; | |
1251 | return status; | |
1252 | } | |
1253 | } | |
b5d655f7 A |
1254 | |
1255 | if ((status == ASL_STATUS_OK) && (k != NULL)) | |
1256 | { | |
1257 | asl_set(out, k, v); | |
1258 | if (v != NULL) free(v); | |
1259 | free(k); | |
1260 | } | |
1261 | } | |
1262 | ||
34e8f829 | 1263 | r.prev = asl_file_fetch_helper_64(s, &p, NULL, NULL); /* 116 */ |
b5d655f7 A |
1264 | |
1265 | free(buf); | |
1266 | ||
1267 | if (dir >= 0) s->cursor = r.next; | |
1268 | else s->cursor = r.prev; | |
1269 | ||
1270 | s->cursor_xid = 0; | |
1271 | ||
1272 | if (s->cursor != 0) | |
1273 | { | |
1274 | off = s->cursor + RECORD_COMMON_LEN + sizeof(uint64_t); | |
34e8f829 A |
1275 | if (off > s->file_size) |
1276 | { | |
1277 | asl_free(out); | |
1278 | s->cursor = 0; | |
1279 | s->cursor_xid = 0; | |
1280 | /* | |
1281 | * Next record offset is past the end of the file. | |
1282 | * This is an error, but we allow it to fail quietly | |
1283 | * so that the current record fetch succeeds. | |
1284 | */ | |
1285 | return ASL_STATUS_OK; | |
1286 | } | |
1287 | ||
b5d655f7 | 1288 | status = fseeko(s->store, off, SEEK_SET); |
34e8f829 A |
1289 | if (status != 0) |
1290 | { | |
1291 | asl_free(out); | |
1292 | s->cursor = 0; | |
1293 | s->cursor_xid = 0; | |
1294 | return ASL_STATUS_READ_FAILED; | |
1295 | } | |
b5d655f7 A |
1296 | |
1297 | status = fread(&x64, sizeof(uint64_t), 1, s->store); | |
34e8f829 A |
1298 | if (status != 1) |
1299 | { | |
1300 | asl_free(out); | |
1301 | s->cursor = 0; | |
1302 | s->cursor_xid = 0; | |
1303 | return ASL_STATUS_READ_FAILED; | |
1304 | } | |
b5d655f7 A |
1305 | |
1306 | s->cursor_xid = asl_core_ntohq(x64); | |
1307 | } | |
1308 | ||
1309 | *msg = out; | |
1310 | return ASL_STATUS_OK; | |
1311 | } | |
1312 | ||
1313 | uint32_t | |
1314 | asl_file_open_read(const char *path, asl_file_t **s) | |
1315 | { | |
1316 | asl_file_t *out; | |
1317 | FILE *f; | |
1318 | int i; | |
1f2f436a | 1319 | uint32_t status, vers, last_len; |
b5d655f7 A |
1320 | char buf[DB_HEADER_LEN]; |
1321 | off_t off; | |
1322 | asl_legacy1_t *legacy; | |
34e8f829 A |
1323 | struct stat sb; |
1324 | ||
1325 | memset(&sb, 0, sizeof(struct stat)); | |
1326 | if (stat(path, &sb) != 0) return ASL_STATUS_FAILED; | |
b5d655f7 A |
1327 | |
1328 | f = fopen(path, "r"); | |
1329 | if (f == NULL) | |
1330 | { | |
1331 | if (errno == EACCES) return ASL_STATUS_ACCESS_DENIED; | |
1332 | return ASL_STATUS_FAILED; | |
1333 | } | |
1334 | ||
1335 | i = fread(buf, DB_HEADER_LEN, 1, f); | |
1336 | if (i < 1) | |
1337 | { | |
1338 | fclose(f); | |
1339 | return ASL_STATUS_INVALID_STORE; | |
1340 | } | |
1341 | ||
1342 | /* validate header */ | |
1343 | if (strncmp(buf, ASL_DB_COOKIE, ASL_DB_COOKIE_LEN)) | |
1344 | { | |
1345 | fclose(f); | |
1346 | return ASL_STATUS_INVALID_STORE; | |
1347 | } | |
1348 | ||
1349 | legacy = NULL; | |
1350 | ||
1351 | vers = _asl_get_32(buf + DB_HEADER_VERS_OFFSET); | |
1352 | if (vers == DB_VERSION_LEGACY_1) | |
1353 | { | |
1354 | fclose(f); | |
1355 | status = asl_legacy1_open(path, &legacy); | |
1356 | if (status != ASL_STATUS_OK) return status; | |
1357 | } | |
1358 | ||
1359 | out = (asl_file_t *)calloc(1, sizeof(asl_file_t)); | |
1360 | if (out == NULL) | |
1361 | { | |
1362 | fclose(f); | |
1363 | return ASL_STATUS_NO_MEMORY; | |
1364 | } | |
1365 | ||
1f2f436a | 1366 | out->store = f; |
b5d655f7 A |
1367 | out->flags = ASL_FILE_FLAG_READ_ONLY; |
1368 | out->version = vers; | |
1369 | ||
1370 | if (legacy != NULL) | |
1371 | { | |
1372 | out->flags |= ASL_FILE_FLAG_LEGACY_STORE; | |
1373 | out->legacy = (void *)legacy; | |
1374 | ||
1375 | *s = out; | |
1376 | return ASL_STATUS_OK; | |
1377 | } | |
1378 | ||
1379 | out->first = _asl_get_64(buf + DB_HEADER_FIRST_OFFSET); | |
1380 | out->last = _asl_get_64(buf + DB_HEADER_LAST_OFFSET); | |
34e8f829 A |
1381 | out->file_size = (size_t)sb.st_size; |
1382 | ||
1f2f436a A |
1383 | /* |
1384 | * Detect bogus last pointer and check for odd-sized files. | |
1385 | * Setting out->last to zero forces us to follow the linked | |
1386 | * list of records in the file to the last record. That's | |
1387 | * done in the set_position code. It's a bit slower, but it's | |
1388 | * better at preventing crashes in corrupt files. | |
1389 | */ | |
b5d655f7 | 1390 | |
1f2f436a A |
1391 | /* records are at least MSG_RECORD_FIXED_LENGTH bytes */ |
1392 | if ((out->last + MSG_RECORD_FIXED_LENGTH) > out->file_size) | |
1393 | { | |
1394 | out->last = 0; | |
1395 | } | |
1396 | else | |
1397 | { | |
1398 | /* read last record length and make sure the file is at least that large */ | |
1399 | off = out->last + RECORD_TYPE_LEN; | |
1400 | status = asl_file_read_uint32(out, off, &last_len); | |
1401 | if (status != ASL_STATUS_OK) | |
1402 | { | |
1403 | fclose(out->store); | |
1404 | free(out); | |
1405 | return status; | |
1406 | } | |
1407 | ||
1408 | if ((out->last + last_len) > out->file_size) out->last = 0; | |
1409 | } | |
b5d655f7 A |
1410 | |
1411 | out->cursor = out->first; | |
1412 | if (out->cursor != 0) | |
1413 | { | |
1414 | off = out->cursor + RECORD_COMMON_LEN + sizeof(uint64_t); | |
1415 | status = asl_file_read_uint64(out, off, &(out->cursor_xid)); | |
1416 | if (status != ASL_STATUS_OK) | |
1417 | { | |
1f2f436a A |
1418 | fclose(out->store); |
1419 | free(out); | |
b5d655f7 A |
1420 | return status; |
1421 | } | |
1422 | } | |
1423 | ||
1424 | *s = out; | |
1425 | return ASL_STATUS_OK; | |
1426 | } | |
1427 | ||
1428 | static uint32_t | |
1429 | asl_file_read_set_position_first(asl_file_t *s) | |
1430 | { | |
1431 | uint32_t status; | |
1432 | off_t off; | |
1433 | ||
1434 | s->cursor = s->first; | |
1435 | s->cursor_xid = 0; | |
1436 | ||
1437 | if (s->cursor == 0) return ASL_STATUS_OK; | |
1438 | ||
1439 | /* read ID of the first record */ | |
1440 | off = s->cursor + RECORD_COMMON_LEN + sizeof(uint64_t); | |
1441 | status = asl_file_read_uint64(s, off, &(s->cursor_xid)); | |
1442 | return status; | |
1443 | } | |
1444 | ||
1445 | static uint32_t | |
1446 | asl_file_read_set_position_last(asl_file_t *s) | |
1447 | { | |
1448 | uint64_t next; | |
1449 | uint32_t status; | |
1450 | off_t off; | |
1451 | ||
1452 | /* | |
1453 | * If the file has the offset of the last record, we just go there. | |
1454 | * The last record offset was added to improve performance, so it may | |
1455 | * or may not be there. If we don't have the last record offset, we | |
1456 | * just iterate down the record links to find the last one. | |
1457 | * | |
1458 | * Note that s->last may be zero if the file is empty. | |
1459 | */ | |
1460 | ||
1461 | if (s->last != 0) | |
1462 | { | |
1463 | s->cursor = s->last; | |
1464 | off = s->last + RECORD_COMMON_LEN + sizeof(uint64_t); | |
1465 | ||
1466 | /* read ID of the last record */ | |
1467 | status = asl_file_read_uint64(s, off, &(s->cursor_xid)); | |
1468 | return status; | |
1469 | } | |
1470 | ||
1471 | /* start at the first record and iterate */ | |
1472 | s->cursor = s->first; | |
1473 | s->cursor_xid = 0; | |
1474 | ||
1475 | forever | |
1476 | { | |
1477 | off = s->cursor + RECORD_COMMON_LEN; | |
1478 | next = 0; | |
1479 | ||
1480 | /* read next offset */ | |
1481 | status = asl_file_read_uint64(s, off, &next); | |
1482 | if (status != ASL_STATUS_OK) return status; | |
1483 | ||
34e8f829 | 1484 | /* detect bogus next pointer */ |
1f2f436a | 1485 | if (((next + MSG_RECORD_FIXED_LENGTH) > s->file_size) || (next <= s->cursor)) next = 0; |
34e8f829 | 1486 | |
b5d655f7 A |
1487 | if (next == 0) |
1488 | { | |
1489 | if (s->cursor == 0) return ASL_STATUS_OK; | |
1490 | ||
1491 | off = s->cursor + RECORD_COMMON_LEN + sizeof(uint64_t); | |
1492 | status = asl_file_read_uint64(s, off, &(s->cursor_xid)); | |
1493 | return ASL_STATUS_OK; | |
1494 | } | |
1495 | ||
1496 | s->cursor = next; | |
1497 | } | |
1498 | } | |
1499 | ||
1500 | uint32_t | |
1501 | asl_file_read_set_position(asl_file_t *s, uint32_t pos) | |
1502 | { | |
1f2f436a | 1503 | uint64_t next; |
b5d655f7 A |
1504 | uint32_t len, status; |
1505 | off_t off; | |
1506 | ||
1507 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
1508 | if (s->version == 1) return ASL_STATUS_FAILED; | |
1509 | ||
1510 | if (pos == ASL_FILE_POSITION_FIRST) return asl_file_read_set_position_first(s); | |
1511 | if (pos == ASL_FILE_POSITION_LAST) return asl_file_read_set_position_last(s); | |
1512 | ||
1513 | off = 0; | |
1514 | ||
1515 | if (pos == ASL_FILE_POSITION_PREVIOUS) | |
1516 | { | |
1517 | if (s->cursor == s->first) return ASL_STATUS_NO_RECORDS; | |
1518 | if (s->cursor == 0) return ASL_STATUS_NO_RECORDS; | |
1519 | ||
1520 | off = s->cursor + RECORD_TYPE_LEN; | |
1521 | status = asl_file_read_uint32(s, off, &len); | |
1522 | if (status != ASL_STATUS_OK) return status; | |
1523 | ||
1524 | /* set offset to read the "previous" field at the end of the record */ | |
1525 | off = s->cursor + RECORD_COMMON_LEN + len - sizeof(uint64_t); | |
1526 | } | |
1527 | else if (pos == ASL_FILE_POSITION_NEXT) | |
1528 | { | |
1529 | if (s->cursor == s->last) return ASL_STATUS_NO_RECORDS; | |
1530 | if (s->cursor == 0) return ASL_STATUS_NO_RECORDS; | |
1531 | ||
34e8f829 | 1532 | /* set offset to read the "next" field in the current record */ |
b5d655f7 A |
1533 | off = s->cursor + RECORD_COMMON_LEN; |
1534 | } | |
1535 | else return ASL_STATUS_INVALID_ARG; | |
1536 | ||
1537 | s->cursor_xid = 0; | |
1538 | ||
1539 | /* | |
1540 | * read offset of next / previous | |
1541 | */ | |
1f2f436a A |
1542 | next = 0; |
1543 | status = asl_file_read_uint64(s, off, &next); | |
b5d655f7 A |
1544 | if (status != ASL_STATUS_OK) return ASL_STATUS_READ_FAILED; |
1545 | ||
34e8f829 | 1546 | /* detect bogus next pointer */ |
1f2f436a A |
1547 | if ((next + MSG_RECORD_FIXED_LENGTH) > s->file_size) next = 0; |
1548 | else if ((pos == ASL_FILE_POSITION_PREVIOUS) && (next >= s->cursor)) next = 0; | |
1549 | else if ((pos == ASL_FILE_POSITION_NEXT) && (next <= s->cursor)) next = 0; | |
34e8f829 | 1550 | |
1f2f436a | 1551 | s->cursor = next; |
b5d655f7 A |
1552 | if (s->cursor == 0) return ASL_STATUS_NO_RECORDS; |
1553 | ||
1554 | /* read ID of the record */ | |
1555 | off = s->cursor + RECORD_COMMON_LEN + sizeof(uint64_t); | |
1556 | status = asl_file_read_uint64(s, off, &(s->cursor_xid)); | |
1557 | return status; | |
1558 | } | |
1559 | ||
1560 | uint32_t | |
1561 | asl_file_fetch_next(asl_file_t *s, aslmsg *msg) | |
1562 | { | |
1563 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
1564 | if (s->version == 1) return ASL_STATUS_FAILED; | |
1565 | ||
1566 | return asl_file_fetch_pos(s, s->cursor, 1, msg); | |
1567 | } | |
1568 | ||
1569 | uint32_t | |
1570 | asl_file_fetch_previous(asl_file_t *s, aslmsg *msg) | |
1571 | { | |
1572 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
1573 | if (s->version == 1) return ASL_STATUS_FAILED; | |
1574 | ||
1575 | return asl_file_fetch_pos(s, s->cursor, -1, msg); | |
1576 | } | |
1577 | ||
1578 | uint32_t | |
1579 | asl_file_fetch(asl_file_t *s, uint64_t mid, aslmsg *msg) | |
1580 | { | |
1581 | uint32_t status; | |
1582 | ||
1583 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
1584 | if (msg == NULL) return ASL_STATUS_INVALID_ARG; | |
1585 | if ((s->flags & ASL_FILE_FLAG_READ_ONLY) == 0) return ASL_STATUS_WRITE_ONLY; | |
1586 | ||
1587 | if (s->version == 1) | |
1588 | { | |
1589 | return asl_legacy1_fetch((asl_legacy1_t *)s->legacy, mid, msg); | |
1590 | } | |
1591 | ||
1592 | if (s->cursor_xid == 0) | |
1593 | { | |
1594 | status = asl_file_read_set_position(s, ASL_FILE_POSITION_FIRST); | |
1595 | if (status != ASL_STATUS_OK) return status; | |
1596 | if (s->cursor_xid == 0) return ASL_STATUS_INVALID_ID; | |
1597 | } | |
1598 | ||
1599 | while (s->cursor_xid < mid) | |
1600 | { | |
1601 | status = asl_file_read_set_position(s, ASL_FILE_POSITION_NEXT); | |
1602 | if (status != ASL_STATUS_OK) return status; | |
1603 | if (s->cursor_xid > mid) return ASL_STATUS_INVALID_ID; | |
1604 | if (s->cursor_xid == 0) return ASL_STATUS_INVALID_ID; | |
1605 | } | |
1606 | ||
1607 | while (s->cursor_xid > mid) | |
1608 | { | |
1609 | status = asl_file_read_set_position(s, ASL_FILE_POSITION_PREVIOUS); | |
1610 | if (status != ASL_STATUS_OK) return status; | |
1611 | if (s->cursor_xid < mid) return ASL_STATUS_INVALID_ID; | |
1612 | if (s->cursor_xid == 0) return ASL_STATUS_INVALID_ID; | |
1613 | } | |
1614 | ||
1615 | if (s->cursor_xid != mid) return ASL_STATUS_INVALID_ID; | |
1616 | ||
1617 | return asl_file_fetch_pos(s, s->cursor, 1, msg); | |
1618 | } | |
1619 | ||
1f2f436a | 1620 | __private_extern__ uint64_t |
b5d655f7 A |
1621 | asl_file_cursor(asl_file_t *s) |
1622 | { | |
1623 | if (s == NULL) return 0; | |
1624 | if ((s->flags & ASL_FILE_FLAG_READ_ONLY) == 0) return 0; | |
1625 | if (s->version == 1) return 0; | |
1626 | ||
1627 | return s->cursor_xid; | |
1628 | } | |
1629 | ||
1f2f436a | 1630 | __private_extern__ uint32_t |
b5d655f7 A |
1631 | asl_file_match_start(asl_file_t *s, uint64_t start_id, int32_t direction) |
1632 | { | |
1633 | uint32_t status, d; | |
1634 | ||
1635 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
1636 | if (s->version == 1) return ASL_STATUS_INVALID_STORE; | |
1637 | if ((s->flags & ASL_FILE_FLAG_READ_ONLY) == 0) return ASL_STATUS_WRITE_ONLY; | |
1638 | ||
1639 | d = ASL_FILE_POSITION_NEXT; | |
1640 | if (direction < 0) d = ASL_FILE_POSITION_PREVIOUS; | |
1641 | ||
1642 | /* | |
1643 | * find starting point | |
1644 | */ | |
1645 | status = ASL_STATUS_OK; | |
1646 | if (direction >= 0) status = asl_file_read_set_position(s, ASL_FILE_POSITION_FIRST); | |
1647 | else status = asl_file_read_set_position(s, ASL_FILE_POSITION_LAST); | |
1648 | if (status != ASL_STATUS_OK) return status; | |
1649 | ||
1650 | while ((status == ASL_STATUS_OK) && (((direction >= 0) && (s->cursor_xid < start_id)) || ((direction < 0) && (s->cursor_xid > start_id)))) | |
1651 | { | |
1652 | status = asl_file_read_set_position(s, d); | |
1653 | } | |
1654 | ||
1655 | return status; | |
1656 | } | |
1657 | ||
1f2f436a A |
1658 | __private_extern__ uint32_t |
1659 | asl_file_match_next(asl_file_t *s, aslresponse query, aslmsg *msg, uint64_t *last_id, int32_t direction) | |
b5d655f7 A |
1660 | { |
1661 | uint32_t status, d, i, do_match, did_match; | |
1662 | aslmsg m; | |
1663 | ||
1664 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
1665 | if (msg == NULL) return ASL_STATUS_INVALID_ARG; | |
1666 | if (s->version == 1) return ASL_STATUS_INVALID_STORE; | |
1667 | if ((s->flags & ASL_FILE_FLAG_READ_ONLY) == 0) return ASL_STATUS_WRITE_ONLY; | |
1668 | if (s->cursor == 0) return ASL_STATUS_NO_RECORDS; | |
1669 | ||
1670 | *msg = NULL; | |
1671 | do_match = 1; | |
1672 | ||
1673 | d = ASL_FILE_POSITION_NEXT; | |
1674 | if (direction < 0) d = ASL_FILE_POSITION_PREVIOUS; | |
1675 | ||
1676 | if ((query == NULL) || ((query != NULL) && (query->count == 0))) do_match = 0; | |
1677 | ||
1678 | m = NULL; | |
1679 | ||
1680 | *last_id = s->cursor_xid; | |
1681 | ||
1682 | status = asl_file_fetch_pos(s, s->cursor, direction, &m); | |
1683 | if (status == ASL_STATUS_ACCESS_DENIED) return ASL_STATUS_MATCH_FAILED; | |
1684 | if ((status == ASL_STATUS_INVALID_ARG) && (s->cursor == 0)) return ASL_STATUS_NO_RECORDS; | |
1685 | if (status != ASL_STATUS_OK) return status; | |
1686 | ||
1687 | did_match = 1; | |
1688 | ||
1689 | if (do_match != 0) | |
1690 | { | |
1691 | did_match = 0; | |
1692 | ||
1693 | for (i = 0; (i < query->count) && (did_match == 0); i++) | |
1694 | { | |
1f2f436a | 1695 | did_match = asl_msg_cmp((aslmsg)(query->msg[i]), m); |
b5d655f7 A |
1696 | } |
1697 | } | |
1698 | ||
1699 | if (did_match != 0) | |
1700 | { | |
1701 | *msg = m; | |
1702 | return ASL_STATUS_OK; | |
1703 | } | |
1704 | ||
1705 | asl_free(m); | |
1706 | return ASL_STATUS_MATCH_FAILED; | |
1707 | } | |
1708 | ||
1709 | uint32_t | |
1710 | asl_file_match(asl_file_t *s, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction) | |
1711 | { | |
1712 | uint32_t status, d, i, do_match, did_match, rescount; | |
1f2f436a | 1713 | aslmsg m; |
b5d655f7 A |
1714 | |
1715 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
1716 | if (res == NULL) return ASL_STATUS_INVALID_ARG; | |
1717 | if ((s->flags & ASL_FILE_FLAG_READ_ONLY) == 0) return ASL_STATUS_WRITE_ONLY; | |
1718 | ||
1719 | if (s->version == 1) | |
1720 | { | |
1721 | return asl_legacy1_match((asl_legacy1_t *)s->legacy, query, res, last_id, start_id, count, direction); | |
1722 | } | |
1723 | ||
1724 | do_match = 1; | |
1725 | rescount = 0; | |
1726 | ||
1727 | d = ASL_FILE_POSITION_NEXT; | |
1728 | if (direction < 0) d = ASL_FILE_POSITION_PREVIOUS; | |
1729 | ||
1730 | if ((query == NULL) || ((query != NULL) && (query->count == 0))) do_match = 0; | |
1731 | ||
1732 | /* | |
1733 | * find starting point | |
1734 | */ | |
1735 | status = ASL_STATUS_OK; | |
1736 | if (direction >= 0) status = asl_file_read_set_position(s, ASL_FILE_POSITION_FIRST); | |
1737 | else status = asl_file_read_set_position(s, ASL_FILE_POSITION_LAST); | |
1738 | if (status != ASL_STATUS_OK) return status; | |
1739 | ||
1740 | while ((status == ASL_STATUS_OK) && (((direction >= 0) && (s->cursor_xid < start_id)) || ((direction < 0) && (s->cursor_xid > start_id)))) | |
1741 | { | |
1742 | status = asl_file_read_set_position(s, d); | |
1743 | } | |
1744 | ||
1745 | /* | |
1746 | * loop through records | |
1747 | */ | |
1748 | forever | |
1749 | { | |
1750 | m = NULL; | |
1751 | status = asl_file_fetch_pos(s, s->cursor, direction, &m); | |
1752 | if (status == ASL_STATUS_ACCESS_DENIED) continue; | |
1753 | if (status != ASL_STATUS_OK) break; | |
1754 | ||
1755 | *last_id = s->cursor_xid; | |
1756 | ||
1757 | did_match = 1; | |
1758 | ||
1759 | if (do_match != 0) | |
1760 | { | |
1761 | did_match = 0; | |
1762 | ||
1763 | for (i = 0; (i < query->count) && (did_match == 0); i++) | |
1764 | { | |
1f2f436a | 1765 | did_match = asl_msg_cmp((aslmsg)query->msg[i], m); |
b5d655f7 A |
1766 | } |
1767 | } | |
1768 | ||
1769 | if (did_match == 1) | |
1770 | { | |
1771 | /* append m to res */ | |
1772 | if (*res == NULL) | |
1773 | { | |
1774 | *res = (aslresponse)calloc(1, sizeof(aslresponse)); | |
1775 | if (*res == NULL) return ASL_STATUS_NO_MEMORY; | |
1f2f436a | 1776 | (*res)->msg = (asl_msg_t **)calloc(1, sizeof(aslmsg)); |
b5d655f7 A |
1777 | if ((*res)->msg == NULL) |
1778 | { | |
1779 | free(*res); | |
1780 | return ASL_STATUS_NO_MEMORY; | |
1781 | } | |
1782 | } | |
1783 | else | |
1784 | { | |
1f2f436a | 1785 | (*res)->msg = (asl_msg_t **)reallocf((*res)->msg, ((*res)->count + 1) * sizeof(aslmsg)); |
b5d655f7 A |
1786 | if ((*res)->msg == NULL) |
1787 | { | |
1788 | free(*res); | |
1789 | return ASL_STATUS_NO_MEMORY; | |
1790 | } | |
1791 | } | |
1792 | ||
1f2f436a | 1793 | (*res)->msg[(*res)->count] = (asl_msg_t *)m; |
b5d655f7 A |
1794 | (*res)->count++; |
1795 | ||
1796 | rescount++; | |
1797 | if ((count != 0) && (rescount >= count)) break; | |
1798 | } | |
1799 | else | |
1800 | { | |
1801 | asl_free(m); | |
1802 | } | |
1803 | } | |
1804 | ||
1805 | /* NOT REACHED */ | |
1806 | return ASL_STATUS_OK; | |
1807 | } | |
1808 | ||
1809 | size_t | |
1810 | asl_file_size(asl_file_t *s) | |
1811 | { | |
1812 | if (s == NULL) return 0; | |
1813 | return s->file_size; | |
1814 | } | |
1815 | ||
1816 | uint64_t | |
1817 | asl_file_ctime(asl_file_t *s) | |
1818 | { | |
1819 | if (s == NULL) return 0; | |
1820 | return s->dob; | |
1821 | } | |
1822 | ||
1823 | void | |
1824 | asl_file_list_close(asl_file_list_t *head) | |
1825 | { | |
1826 | asl_file_list_t *next; | |
1827 | ||
1828 | while (head != NULL) | |
1829 | { | |
1830 | next = head->next; | |
1831 | asl_file_close(head->file); | |
1832 | free(head); | |
1833 | head = next; | |
1834 | } | |
1835 | } | |
1836 | ||
1837 | static void | |
1838 | asl_file_list_free(asl_file_list_t *head) | |
1839 | { | |
1840 | asl_file_list_t *next; | |
1841 | ||
1842 | while (head != NULL) | |
1843 | { | |
1844 | next = head->next; | |
1845 | free(head); | |
1846 | head = next; | |
1847 | } | |
1848 | } | |
1849 | ||
1f2f436a | 1850 | static asl_file_list_t * |
b5d655f7 A |
1851 | asl_file_list_insert(asl_file_list_t *list, asl_file_t *f, int32_t dir) |
1852 | { | |
1853 | asl_file_list_t *a, *b, *tmp; | |
1854 | ||
1855 | if (f == NULL) return list; | |
1856 | ||
1857 | tmp = (asl_file_list_t *)calloc(1, sizeof(asl_file_list_t)); | |
1858 | if (tmp == NULL) return NULL; | |
1859 | tmp->file = f; | |
1860 | ||
1861 | if (list == NULL) return tmp; | |
1862 | ||
1863 | a = list; | |
1864 | if (((dir < 0) && (f->cursor_xid > a->file->cursor_xid)) || ((dir >= 0) && (f->cursor_xid < a->file->cursor_xid))) | |
1865 | { | |
1866 | tmp->next = list; | |
1867 | return tmp; | |
1868 | } | |
1869 | ||
1870 | b = a->next; | |
1871 | while (b != NULL) | |
1872 | { | |
1873 | if (((dir < 0) && (f->cursor_xid > b->file->cursor_xid)) || ((dir >= 0) && (f->cursor_xid < b->file->cursor_xid))) | |
1874 | { | |
1875 | tmp->next = b; | |
1876 | a->next = tmp; | |
1877 | return list; | |
1878 | } | |
1879 | ||
1880 | a = b; | |
1881 | b = a->next; | |
1882 | } | |
1883 | ||
1884 | a->next = tmp; | |
1885 | return list; | |
1886 | } | |
1887 | ||
1888 | asl_file_list_t * | |
1889 | asl_file_list_add(asl_file_list_t *list, asl_file_t *f) | |
1890 | { | |
1891 | asl_file_list_t *tmp; | |
1892 | ||
1893 | if (f == NULL) return list; | |
1894 | if (f->version == 1) return list; | |
1895 | ||
1896 | tmp = (asl_file_list_t *)calloc(1, sizeof(asl_file_list_t)); | |
1897 | if (tmp == NULL) return NULL; | |
1898 | tmp->file = f; | |
1899 | ||
1900 | tmp->next = list; | |
1901 | return tmp; | |
1902 | } | |
1903 | ||
1904 | void * | |
1905 | asl_file_list_match_start(asl_file_list_t *list, uint64_t start_id, int32_t direction) | |
1906 | { | |
1907 | uint32_t status; | |
1908 | asl_file_list_t *n; | |
1909 | asl_file_match_token_t *out; | |
1910 | ||
1911 | if (list == NULL) return NULL; | |
1912 | ||
1913 | out = (asl_file_match_token_t *)calloc(1, sizeof(asl_file_match_token_t)); | |
1914 | if (out == NULL) return NULL; | |
1915 | ||
1916 | for (n = list; n != NULL; n = n->next) | |
1917 | { | |
1918 | /* init file for the search */ | |
1919 | status = asl_file_match_start(n->file, start_id, direction); | |
1920 | if (status != ASL_STATUS_OK) continue; | |
1921 | if (n->file->cursor_xid == 0) continue; | |
1922 | ||
1923 | out->list = asl_file_list_insert(out->list, n->file, direction); | |
1924 | } | |
1925 | ||
1926 | out->dir = direction; | |
1927 | return out; | |
1928 | } | |
1929 | ||
1930 | uint32_t | |
1931 | asl_file_list_match_next(void *token, aslresponse query, aslresponse *res, uint32_t count) | |
1932 | { | |
1933 | uint32_t status, rescount; | |
1934 | asl_file_list_t *n; | |
1f2f436a | 1935 | aslmsg m; |
b5d655f7 A |
1936 | asl_file_match_token_t *work; |
1937 | uint64_t last_id; | |
1938 | ||
1939 | if (token == NULL) return ASL_STATUS_OK; | |
1940 | if (res == NULL) return ASL_STATUS_INVALID_ARG; | |
1941 | ||
1942 | work = (asl_file_match_token_t *)token; | |
1943 | ||
1944 | rescount = 0; | |
1945 | last_id = 0; | |
1946 | ||
1947 | while ((work->list != NULL) && ((rescount < count) || (count == 0))) | |
1948 | { | |
1949 | m = NULL; | |
1950 | status = asl_file_match_next(work->list->file, query, &m, &last_id, work->dir); | |
1951 | if (m != NULL) | |
1952 | { | |
1953 | if (*res == NULL) *res = (aslresponse)calloc(1, sizeof(asl_search_result_t)); | |
1954 | if (*res == NULL) | |
1955 | { | |
1956 | asl_file_list_free(work->list); | |
1957 | work->list = NULL; | |
1958 | return ASL_STATUS_NO_MEMORY; | |
1959 | } | |
1960 | ||
1f2f436a A |
1961 | if ((*res)->msg == NULL) (*res)->msg = (asl_msg_t **)calloc(1, sizeof(aslmsg)); |
1962 | else (*res)->msg = (asl_msg_t **)reallocf((*res)->msg, ((*res)->count + 1) * sizeof(aslmsg)); | |
b5d655f7 A |
1963 | if ((*res)->msg == NULL) |
1964 | { | |
1965 | free(*res); | |
1966 | *res = NULL; | |
1967 | asl_file_list_free(work->list); | |
1968 | work->list = NULL; | |
1969 | return ASL_STATUS_NO_MEMORY; | |
1970 | } | |
1971 | ||
1f2f436a | 1972 | (*res)->msg[(*res)->count] = (asl_msg_t *)m; |
b5d655f7 A |
1973 | (*res)->count++; |
1974 | rescount++; | |
1975 | } | |
1976 | ||
34e8f829 | 1977 | if ((status != ASL_STATUS_OK) || (work->list->file->cursor_xid == 0)) |
b5d655f7 A |
1978 | { |
1979 | n = work->list->next; | |
1980 | free(work->list); | |
1981 | work->list = n; | |
1982 | } | |
1983 | ||
1984 | if (work->list != NULL) | |
1985 | { | |
1986 | n = work->list->next; | |
1987 | if (n != NULL) | |
1988 | { | |
1989 | 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))) | |
1990 | { | |
1991 | n = work->list; | |
1992 | work->list = work->list->next; | |
1993 | n->next = NULL; | |
1994 | work->list = asl_file_list_insert(work->list, n->file, work->dir); | |
1995 | free(n); | |
1996 | } | |
1997 | } | |
1998 | } | |
1999 | } | |
2000 | ||
2001 | return ASL_STATUS_OK; | |
2002 | } | |
2003 | ||
2004 | void | |
2005 | asl_file_list_match_end(void *token) | |
2006 | { | |
2007 | asl_file_match_token_t *work; | |
2008 | ||
2009 | if (token == NULL) return; | |
2010 | ||
2011 | work = (asl_file_match_token_t *)token; | |
2012 | asl_file_list_free(work->list); | |
2013 | work->list = NULL; | |
2014 | ||
2015 | free(token); | |
2016 | } | |
2017 | ||
2018 | uint32_t | |
2019 | asl_file_list_match_timeout(asl_file_list_t *list, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction, uint32_t usec) | |
2020 | { | |
2021 | uint32_t status, rescount; | |
2022 | asl_file_list_t *files, *n; | |
1f2f436a | 2023 | aslmsg m; |
b5d655f7 A |
2024 | struct timeval now, finish; |
2025 | ||
2026 | if (list == NULL) return ASL_STATUS_INVALID_ARG; | |
2027 | if (res == NULL) return ASL_STATUS_INVALID_ARG; | |
2028 | if (last_id == NULL) return ASL_STATUS_INVALID_ARG; | |
2029 | ||
2030 | files = NULL; | |
2031 | ||
2032 | for (n = list; n != NULL; n = n->next) | |
2033 | { | |
2034 | /* init file for the search */ | |
2035 | status = asl_file_match_start(n->file, start_id, direction); | |
2036 | if (status != ASL_STATUS_OK) continue; | |
2037 | if (n->file->cursor_xid == 0) continue; | |
2038 | ||
2039 | files = asl_file_list_insert(files, n->file, direction); | |
2040 | } | |
2041 | ||
2042 | if (files == NULL) | |
2043 | { | |
2044 | asl_file_list_free(files); | |
2045 | return ASL_STATUS_OK; | |
2046 | } | |
2047 | ||
2048 | /* start the timer if a timeout was specified */ | |
2049 | memset(&finish, 0, sizeof(struct timeval)); | |
2050 | if (usec != 0) | |
2051 | { | |
2052 | if (gettimeofday(&finish, NULL) == 0) | |
2053 | { | |
2054 | finish.tv_sec += (usec / MILLION); | |
2055 | finish.tv_usec += (usec % MILLION); | |
2056 | if (finish.tv_usec > MILLION) | |
2057 | { | |
2058 | finish.tv_usec -= MILLION; | |
2059 | finish.tv_sec += 1; | |
2060 | } | |
2061 | } | |
2062 | else | |
2063 | { | |
2064 | /* shouldn't happen, but if gettimeofday failed we just run without a timeout */ | |
2065 | memset(&finish, 0, sizeof(struct timeval)); | |
2066 | } | |
2067 | } | |
2068 | ||
2069 | rescount = 0; | |
2070 | while ((files != NULL) && ((rescount < count) || (count == 0))) | |
2071 | { | |
2072 | m = NULL; | |
2073 | status = asl_file_match_next(files->file, query, &m, last_id, direction); | |
2074 | if (m != NULL) | |
2075 | { | |
2076 | if (*res == NULL) *res = (aslresponse)calloc(1, sizeof(asl_search_result_t)); | |
2077 | if (*res == NULL) | |
2078 | { | |
2079 | asl_file_list_free(files); | |
2080 | return ASL_STATUS_NO_MEMORY; | |
2081 | } | |
2082 | ||
1f2f436a A |
2083 | if ((*res)->msg == NULL) (*res)->msg = (asl_msg_t **)calloc(1, sizeof(aslmsg)); |
2084 | else (*res)->msg = (asl_msg_t **)reallocf((*res)->msg, ((*res)->count + 1) * sizeof(aslmsg)); | |
b5d655f7 A |
2085 | if ((*res)->msg == NULL) |
2086 | { | |
2087 | free(*res); | |
2088 | *res = NULL; | |
2089 | asl_file_list_free(files); | |
2090 | return ASL_STATUS_NO_MEMORY; | |
2091 | } | |
2092 | ||
1f2f436a | 2093 | (*res)->msg[(*res)->count] = (asl_msg_t *)m; |
b5d655f7 A |
2094 | (*res)->count++; |
2095 | rescount++; | |
2096 | } | |
2097 | ||
2098 | if (files->file->cursor_xid == 0) | |
2099 | { | |
2100 | n = files->next; | |
2101 | free(files); | |
2102 | files = n; | |
2103 | } | |
2104 | ||
2105 | if (files != NULL) | |
2106 | { | |
2107 | n = files->next; | |
2108 | if (n != NULL) | |
2109 | { | |
2110 | if (((direction < 0) && (files->file->cursor_xid <= n->file->cursor_xid)) || ((direction >= 0) && (files->file->cursor_xid > n->file->cursor_xid))) | |
2111 | { | |
2112 | n = files; | |
2113 | files = files->next; | |
2114 | n->next = NULL; | |
2115 | files = asl_file_list_insert(files, n->file, direction); | |
2116 | free(n); | |
2117 | } | |
2118 | } | |
2119 | } | |
2120 | ||
2121 | /* check the timer */ | |
2122 | if ((finish.tv_sec != 0) && (gettimeofday(&now, NULL) == 0)) | |
2123 | { | |
2124 | if ((now.tv_sec > finish.tv_sec) || ((now.tv_sec == finish.tv_sec) && (now.tv_usec > finish.tv_usec))) break; | |
2125 | } | |
2126 | } | |
2127 | ||
2128 | asl_file_list_free(files); | |
2129 | return ASL_STATUS_OK; | |
2130 | } | |
2131 | ||
2132 | uint32_t | |
2133 | asl_file_list_match(asl_file_list_t *list, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction) | |
2134 | { | |
2135 | return asl_file_list_match_timeout(list, query, res, last_id, start_id, count, direction, 0); | |
2136 | } |