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