]> git.saurik.com Git - apple/syslog.git/blame - aslcommon/asl_store.c
syslog-64.tar.gz
[apple/syslog.git] / aslcommon / asl_store.c
CommitLineData
5dd30d76
A
1#include <asl_store.h>
2#include <asl_private.h>
3#include <stdlib.h>
4#include <sys/file.h>
5#include <sys/stat.h>
6#include <sys/errno.h>
7#include <string.h>
8#include <membership.h>
9#include <mach/mach.h>
10#include <sys/syslimits.h>
11#include <sys/types.h>
12#include <time.h>
13#include <sys/mman.h>
14
15#define forever for(;;)
16
17#define FILE_MODE 0600
18
19/*
20 * Magic Cookie for database files.
21 * MAXIMUM 12 CHARS! (DB_HEADER_VERS_OFFSET)
22 */
23#define ASL_DB_COOKIE "ASL DB"
24#define ASL_DB_COOKIE_LEN 6
25
26#define ASL_INDEX_NULL 0xffffffff
27
28#define DB_HLEN_EMPTY 0
29#define DB_HLEN_HEADER 13
30#define DB_HLEN_MESSAGE 13
31#define DB_HLEN_KVLIST 9
32#define DB_HLEN_STRING 25
33#define DB_HLEN_STRCONT 5
34
35#define MSG_OFF_KEY_TYPE 0
36#define MSG_OFF_KEY_NEXT 1
37#define MSG_OFF_KEY_ID 5
38#define MSG_OFF_KEY_RUID 13
39#define MSG_OFF_KEY_RGID 17
40#define MSG_OFF_KEY_TIME 21
41#define MSG_OFF_KEY_HOST 29
42#define MSG_OFF_KEY_SENDER 37
43#define MSG_OFF_KEY_FACILITY 45
44#define MSG_OFF_KEY_LEVEL 53
45#define MSG_OFF_KEY_PID 57
46#define MSG_OFF_KEY_UID 61
47#define MSG_OFF_KEY_GID 65
48#define MSG_OFF_KEY_MSG 69
49#define MSG_OFF_KEY_FLAGS 77
50
51#define mix(a, b, c) \
52{ \
53 a -= b; a -= c; a ^= (c>>13); \
54 b -= c; b -= a; b ^= (a<< 8); \
55 c -= a; c -= b; c ^= (b>>13); \
56 a -= b; a -= c; a ^= (c>>12); \
57 b -= c; b -= a; b ^= (a<<16); \
58 c -= a; c -= b; c ^= (b>> 5); \
59 a -= b; a -= c; a ^= (c>> 3); \
60 b -= c; b -= a; b ^= (a<<10); \
61 c -= a; c -= b; c ^= (b>>15); \
62}
63
64extern time_t asl_parse_time(const char *str);
65extern int asl_msg_cmp(asl_msg_t *a, asl_msg_t *b);
66
67#define asl_msg_list_t asl_search_result_t
68
69#define PMSG_SEL_TIME 0x0001
70#define PMSG_SEL_HOST 0x0002
71#define PMSG_SEL_SENDER 0x0004
72#define PMSG_SEL_FACILITY 0x0008
73#define PMSG_SEL_MESSAGE 0x0010
74#define PMSG_SEL_LEVEL 0x0020
75#define PMSG_SEL_PID 0x0040
76#define PMSG_SEL_UID 0x0080
77#define PMSG_SEL_GID 0x0100
78#define PMSG_SEL_RUID 0x0200
79#define PMSG_SEL_RGID 0x0400
80
81#define PMSG_FETCH_ALL 0
82#define PMSG_FETCH_STD 1
83#define PMSG_FETCH_KV 2
84
85#define Q_NULL 100001
86#define Q_FAST 100002
87#define Q_SLOW 100003
88#define Q_FAIL 100004
89
90#define ARCHIVE_DELETE_VS_COPY_PERCENT 5
91
92typedef struct
93{
94 uint16_t kselect;
95 uint16_t vselect;
96 uint64_t msgid;
97 uint64_t time;
98 uint64_t host;
99 uint64_t sender;
100 uint64_t facility;
101 uint64_t message;
102 uint32_t level;
103 uint32_t pid;
104 int32_t uid;
105 int32_t gid;
106 int32_t ruid;
107 int32_t rgid;
108 uint32_t next;
109 uint32_t kvcount;
110 uint64_t *kvlist;
111} pmsg_t;
112
113static uint64_t
114_asl_htonq(uint64_t n)
115{
116#ifdef __BIG_ENDIAN__
117 return n;
118#else
119 u_int32_t t;
120 union
121 {
122 u_int64_t q;
123 u_int32_t l[2];
124 } x;
125
126 x.q = n;
127 t = x.l[0];
128 x.l[0] = htonl(x.l[1]);
129 x.l[1] = htonl(t);
130
131 return x.q;
132#endif
133}
134
135static uint64_t
136_asl_ntohq(uint64_t n)
137{
138#ifdef __BIG_ENDIAN__
139 return n;
140#else
141 u_int32_t t;
142 union
143 {
144 u_int64_t q;
145 u_int32_t l[2];
146 } x;
147
148 x.q = n;
149 t = x.l[0];
150 x.l[0] = ntohl(x.l[1]);
151 x.l[1] = ntohl(t);
152
153 return x.q;
154#endif
155}
156
157static uint16_t
158_asl_get_16(char *h)
159{
160 uint16_t x;
161
162 memcpy(&x, h, 2);
163 return ntohs(x);
164}
165
166static void
167_asl_put_16(uint16_t i, char *h)
168{
169 uint16_t x;
170
171 x = htons(i);
172 memcpy(h, &x, 2);
173}
174
175static uint32_t
176_asl_get_32(char *h)
177{
178 uint32_t x;
179
180 memcpy(&x, h, 4);
181 return ntohl(x);
182}
183
184static void
185_asl_put_32(uint32_t i, char *h)
186{
187 uint32_t x;
188
189 x = htonl(i);
190 memcpy(h, &x, 4);
191}
192
193static uint64_t
194_asl_get_64(char *h)
195{
196 uint64_t x;
197
198 memcpy(&x, h, 8);
199 return _asl_ntohq(x);
200}
201
202static void
203_asl_put_64(uint64_t i, char *h)
204{
205 uint64_t x;
206
207 x = _asl_htonq(i);
208 memcpy(h, &x, 8);
209}
210
211#define header_get_next(h) _asl_get_32(h + 1)
212#define header_get_id(h) _asl_get_64(h + 5)
213#define header_get_refcount(h) _asl_get_32(h + 13)
214#define header_get_hash(h) _asl_get_32(h + 17)
215
216#define header_put_next(i, h) _asl_put_32(i, h + 1)
217#define header_put_id(i, h) _asl_put_64(i, h + 5)
218#define header_put_refcount(i, h) _asl_put_32(i, h + 13)
219#define header_put_hash(i, h) _asl_put_32(i, h + 17)
220
221/*
222 * callback for sorting slotlist
223 * primary sort is by xid
224 * secondary sort is by slot, which happens when xid is 0
225 * this allows us to quickly find xids (using binary search on the xid key)
226 * it's also used to find slots quickly from record_chain_free()
227 */
228static int
229slot_comp(const void *a, const void *b)
230{
231 slot_info_t *ai, *bi;
232
233 if (a == NULL)
234 {
235 if (b == NULL) return 0;
236 return -1;
237 }
238
239 if (b == NULL) return 1;
240
241 ai = (slot_info_t *)a;
242 bi = (slot_info_t *)b;
243
244 if (ai->xid < bi->xid) return -1;
245
246 if (ai->xid == bi->xid)
247 {
248 if (ai->slot < bi->slot) return -1;
249 if (ai->slot == bi->slot) return 0;
250 return 1;
251 }
252
253 return 1;
254}
255
256/* find a slot (with xid 0) in the slot list */
257static uint32_t
258slotlist_find_xid0_slot(asl_store_t *s, uint32_t slot)
259{
260 uint32_t top, bot, mid, range;
261
262 if (s == NULL) return ASL_INDEX_NULL;
263 if (s->slot_zero_count == 0) return ASL_INDEX_NULL;
264
265 top = s->slot_zero_count - 1;
266 bot = 0;
267 mid = top / 2;
268
269 range = top - bot;
270 while (range > 1)
271 {
272 if (slot == s->slotlist[mid].slot) return mid;
273 else if (slot < s->slotlist[mid].slot) top = mid;
274 else bot = mid;
275
276 range = top - bot;
277 mid = bot + (range / 2);
278 }
279
280 if (slot == s->slotlist[top].slot) return top;
281 if (slot == s->slotlist[bot].slot) return bot;
282
283 return ASL_INDEX_NULL;
284}
285
286/* find an xid in the slot list */
287static uint32_t
288slotlist_find(asl_store_t *s, uint64_t xid, uint32_t slot, int32_t direction)
289{
290 uint32_t top, bot, mid, range;
291
292 if (s == NULL) return ASL_INDEX_NULL;
293 if (s->slotlist_count == 0) return ASL_INDEX_NULL;
294
295 /* special case for xid 0: binary search for slot */
296 if (xid == 0) return slotlist_find_xid0_slot(s, slot);
297
298 top = s->slotlist_count - 1;
299 bot = 0;
300 mid = top / 2;
301
302 range = top - bot;
303 while (range > 1)
304 {
305 if (xid == s->slotlist[mid].xid) return mid;
306 else if (xid < s->slotlist[mid].xid) top = mid;
307 else bot = mid;
308
309 range = top - bot;
310 mid = bot + (range / 2);
311 }
312
313 if (xid == s->slotlist[top].xid) return top;
314 if (xid == s->slotlist[bot].xid) return bot;
315
316 if (direction == 0) return ASL_INDEX_NULL;
317 if (direction < 0) return bot;
318 return top;
319}
320
321static uint32_t
322slotlist_insert(asl_store_t *s, uint8_t type, uint32_t slot, uint64_t xid, uint32_t hash)
323{
324 int i, j, k;
325
326 if (s == NULL) return ASL_STATUS_INVALID_STORE;
327 if (s->slotlist_count == 0)
328 {
329 s->slotlist = (slot_info_t *)calloc(1, sizeof(slot_info_t));
330 if (s->slotlist == NULL) return ASL_STATUS_NO_MEMORY;
331
332 s->slotlist[0].type = type;
333 s->slotlist[0].slot = slot;
334 s->slotlist[0].xid = xid;
335 s->slotlist[0].hash = hash;
336 s->slotlist_count = 1;
337 if (xid == 0) s->slot_zero_count = 1;
338 return ASL_STATUS_OK;
339 }
340
341 s->slotlist = (slot_info_t *)reallocf(s->slotlist, (s->slotlist_count + 1) * sizeof(slot_info_t));
342 if (s->slotlist == NULL)
343 {
344 s->slotlist_count = 0;
345 s->slot_zero_count = 0;
346 return ASL_STATUS_NO_MEMORY;
347 }
348
349 /*
350 * slotlist is sorted in increasing order by xid
351 * there may be multiple xid 0 entries (empty slots) which are further sorted by slot
352 */
353 if (xid == 0)
354 {
355 /* update empty count */
356 s->slot_zero_count++;
357
358 for (i = 0; (i < s->slotlist_count) && (s->slotlist[i].xid == 0); i++)
359 {
360 if (slot <= s->slotlist[i].slot) break;
361 }
362 }
363 else
364 {
365 i = s->slotlist_count - 1;
366 if (xid > s->slotlist[i].xid)
367 {
368 /* append XID at end of slotlist */
369 i++;
370 }
371 else
372 {
373 /* usually we are adding records, so it's likely that the new xid will be large */
374 for (i = s->slotlist_count; i > 0; i--)
375 {
376 if (xid > s->slotlist[i - 1].xid) break;
377 }
378 }
379 }
380
381 for (j = s->slotlist_count; j > i; j--)
382 {
383 k = j - 1;
384 s->slotlist[j].type = s->slotlist[k].type;
385 s->slotlist[j].slot = s->slotlist[k].slot;
386 s->slotlist[j].xid = s->slotlist[k].xid;
387 s->slotlist[j].hash = s->slotlist[k].hash;
388 }
389
390 s->slotlist[i].type = type;
391 s->slotlist[i].slot = slot;
392 s->slotlist[i].xid = xid;
393 s->slotlist[i].hash = hash;
394 s->slotlist_count++;
395
396 return ASL_STATUS_OK;
397}
398
399static uint32_t
400slotlist_delete(asl_store_t *s, uint32_t where)
401{
402 uint32_t i, j, n;
403
404 if (s->slotlist_count == 0) return ASL_STATUS_OK;
405
406 n = s->slotlist_count - 1;
407 if (n == 0)
408 {
409 free(s->slotlist);
410 s->slotlist = NULL;
411 s->slotlist_count = 0;
412 s->slot_zero_count = 0;
413 return ASL_STATUS_OK;
414 }
415
416 if (s->slotlist[where].xid == 0) s->slot_zero_count--;
417
418 for (i = where, j = i + 1; i < n; i++, j++)
419 {
420 s->slotlist[i].type = s->slotlist[j].type;
421 s->slotlist[i].slot = s->slotlist[j].slot;
422 s->slotlist[i].xid = s->slotlist[j].xid;
423 s->slotlist[i].hash = s->slotlist[j].hash;
424 }
425
426 s->slotlist_count = n;
427 s->slotlist = (slot_info_t *)reallocf(s->slotlist, s->slotlist_count * sizeof(slot_info_t));
428
429 if (s->slotlist == NULL)
430 {
431 s->slotlist_count = 0;
432 s->slot_zero_count = 0;
433 return ASL_STATUS_NO_MEMORY;
434 }
435
436 return ASL_STATUS_OK;
437}
438
439static uint32_t
440slotlist_make_empty(asl_store_t *s, uint32_t where)
441{
442 uint32_t i, j, slot;
443
444 if (s->slotlist_count == 0) return ASL_STATUS_OK;
445 if (where > s->slotlist_count) return ASL_STATUS_OK;
446
447 /*
448 * Special case for asl_store_archive.
449 * Since we expect to be doing lots of deletions during an archive call,
450 * this routine only marks the type as empty.
451 * asl_store_archive cleans up the slotlist when it is finished.
452 */
453 if (s->flags & ASL_STORE_FLAG_DEFER_SORT)
454 {
455 s->slotlist[where].type = DB_TYPE_EMPTY;
456 s->slotlist[where].hash = 0;
457
458 s->empty_count++;
459
460 return ASL_STATUS_OK;
461 }
462
463 slot = s->slotlist[where].slot;
464
465 /* primary sort by xid */
466 for (i = where, j = where - 1; (i > 0) && (s->slotlist[j].xid != 0); i--, j--)
467 {
468 s->slotlist[i].type = s->slotlist[j].type;
469 s->slotlist[i].slot = s->slotlist[j].slot;
470 s->slotlist[i].xid = s->slotlist[j].xid;
471 s->slotlist[i].hash = s->slotlist[j].hash;
472 }
473
474 /* xid 0 entries sorted by slot */
475 for (j = i - 1; (i > 0) && (s->slotlist[j].slot > slot); i--, j--)
476 {
477 s->slotlist[i].type = s->slotlist[j].type;
478 s->slotlist[i].slot = s->slotlist[j].slot;
479 s->slotlist[i].xid = s->slotlist[j].xid;
480 s->slotlist[i].hash = s->slotlist[j].hash;
481 }
482
483 s->slotlist[i].type = DB_TYPE_EMPTY;
484 s->slotlist[i].slot = slot;
485 s->slotlist[i].xid = 0;
486 s->slotlist[i].hash = 0;
487
488 s->empty_count++;
489
490 /* new xid=0 count */
491 for (s->slot_zero_count = 0; (s->slot_zero_count < s->slotlist_count) && (s->slotlist[s->slot_zero_count].xid == 0); s->slot_zero_count++);
492
493 return ASL_STATUS_OK;
494}
495
496static uint32_t
497slotlist_init(asl_store_t *s)
498{
499 uint32_t i, si, status, hash, addslot;
500 uint64_t xid, tick;
501 uint8_t t;
502 char tmp[DB_RECORD_LEN];
503
504 s->empty_count = 0;
505
506 /* Start at first slot after the header */
507 status = fseek(s->db, DB_RECORD_LEN, SEEK_SET);
508 if (status != 0) return ASL_STATUS_READ_FAILED;
509
510 s->slotlist = (slot_info_t *)calloc(s->record_count, sizeof(slot_info_t));
511 if (s->slotlist == NULL) return ASL_STATUS_NO_MEMORY;
512
513 si = 0;
514
515 for (i = 1; i < s->record_count; i++)
516 {
517 status = fread(tmp, DB_RECORD_LEN, 1, s->db);
518 if (status != 1) return ASL_STATUS_READ_FAILED;
519
520 t = tmp[0];
521 addslot = 0;
522 xid = 0;
523 hash = 0;
524
525 if (t == DB_TYPE_EMPTY)
526 {
527 addslot = 1;
528 s->empty_count++;
529 }
530
531 if (t == DB_TYPE_STRING)
532 {
533 addslot = 1;
534 s->string_count++;
535 xid = header_get_id(tmp);
536 hash = header_get_hash(tmp);
537 }
538
539 if (t == DB_TYPE_MESSAGE)
540 {
541 addslot = 1;
542 s->message_count++;
543 xid = header_get_id(tmp);
544 tick = _asl_get_64(tmp + MSG_OFF_KEY_TIME);
545 if (tick > s->max_time) s->max_time = tick;
546 }
547
548 if (addslot == 1)
549 {
550 s->slotlist[si].type = t;
551 s->slotlist[si].slot = i;
552 s->slotlist[si].xid = xid;
553 s->slotlist[si].hash = hash;
554 si++;
555 }
556 }
557
558 s->slotlist = (slot_info_t *)reallocf(s->slotlist, si * sizeof(slot_info_t));
559 if (s->slotlist == NULL) return ASL_STATUS_NO_MEMORY;
560 s->slotlist_count = si;
561
562 /* slotlist is sorted by xid */
563 qsort((void *)s->slotlist, s->slotlist_count, sizeof(slot_info_t), slot_comp);
564
565 /* new xid=0 count */
566 for (s->slot_zero_count = 0; (s->slot_zero_count < s->slotlist_count) && (s->slotlist[s->slot_zero_count].xid == 0); s->slot_zero_count++);
567
568 return ASL_STATUS_OK;
569}
570
571uint32_t
572asl_store_open(const char *path, uint32_t flags, asl_store_t **out)
573{
574 asl_store_t *s;
575 struct stat sb;
576 int status, i, j, fd;
577 char cbuf[DB_RECORD_LEN];
578 off_t fsize;
579 uint64_t next;
580
581 memset(&sb, 0, sizeof(struct stat));
582 status = stat(path, &sb);
583
584 fsize = 0;
585
586 if (status < 0)
587 {
588 if (errno != ENOENT) return ASL_STATUS_FAILED;
589
590 fd = open(path, O_RDWR | O_CREAT | O_EXCL, FILE_MODE);
591 if (fd < 0) return ASL_STATUS_FAILED;
592
593 memset(cbuf, 0, DB_RECORD_LEN);
594 memcpy(cbuf, ASL_DB_COOKIE, ASL_DB_COOKIE_LEN);
595
596 _asl_put_32(DB_VERSION, cbuf + DB_HEADER_VERS_OFFSET);
597
598 /* record IDs start at 1 */
599 _asl_put_64(1, cbuf + DB_HEADER_MAXID_OFFSET);
600
601 status = write(fd, cbuf, DB_RECORD_LEN);
602 close(fd);
603 if (status != DB_RECORD_LEN) return ASL_STATUS_FAILED;
604
605 fsize = DB_RECORD_LEN;
606 }
607 else
608 {
609 fsize = sb.st_size;
610 }
611
612 s = (asl_store_t *)calloc(1, sizeof(asl_store_t));
613 if (s == NULL) return ASL_STATUS_NO_MEMORY;
614
615 s->flags = flags;
616
617 for (i = 0; i < RECORD_CACHE_SIZE; i++)
618 {
619 s->rcache[i] = malloc(DB_RECORD_LEN);
620 if (s->rcache[i] == NULL)
621 {
622 for (j = 0; j < i; j++) free(s->rcache[j]);
623 free(s);
624 return ASL_STATUS_NO_MEMORY;
625 }
626 }
627
628 s->db = NULL;
629 if (flags & ASL_STORE_FLAG_READ_ONLY) s->db = fopen(path, "r");
630 else s->db = fopen(path, "r+");
631 if (s->db == NULL)
632 {
633 free(s);
634 return ASL_STATUS_INVALID_STORE;
635 }
636
637 memset(cbuf, 0, DB_RECORD_LEN);
638 status = fread(cbuf, DB_RECORD_LEN, 1, s->db);
639 if (status != 1)
640 {
641 fclose(s->db);
642 free(s);
643 return ASL_STATUS_READ_FAILED;
644 }
645
646 /* Check the database Magic Cookie */
647 if (strncmp(cbuf, ASL_DB_COOKIE, ASL_DB_COOKIE_LEN))
648 {
649 fclose(s->db);
650 free(s);
651 return ASL_STATUS_INVALID_STORE;
652 }
653
654 next = 0;
655 memcpy(&next, cbuf + DB_HEADER_MAXID_OFFSET, 8);
656 s->next_id = _asl_ntohq(next);
657
658 s->record_count = fsize / DB_RECORD_LEN;
659
660 status = slotlist_init(s);
661
662 for (i = 0; i < STRING_CACHE_SIZE; i++)
663 {
664 s->string_cache[i].index = ASL_INDEX_NULL;
665 s->string_cache[i].refcount = 0;
666 s->string_cache[i].str = NULL;
667 }
668
669 s->db_path = strdup(path);
670
671 *out = s;
672 return ASL_STATUS_OK;
673}
674
675uint32_t
676asl_store_close(asl_store_t *s)
677{
678 uint32_t i;
679
680 if (s == NULL) return ASL_STATUS_INVALID_STORE;
681
682 if (s->slotlist != NULL) free(s->slotlist);
683 for (i = 0; i < RECORD_CACHE_SIZE; i++) free(s->rcache[i]);
684 for (i = 0; i < STRING_CACHE_SIZE; i++)
685 {
686 if (s->string_cache[i].str != NULL) free(s->string_cache[i].str);
687 }
688
689 if (s->db_path != NULL) free(s->db_path);
690 if (s->db != NULL) fclose(s->db);
691 free(s);
692
693 return ASL_STATUS_OK;
694}
695
696static char *
697record_buffer_alloc(asl_store_t *s)
698{
699 uint32_t i;
700
701 if (s == NULL) return calloc(1, DB_RECORD_LEN);
702
703 for (i = 0; i < RECORD_CACHE_SIZE; i++)
704 {
705 if (s->rcache_state[i] == 0)
706 {
707 s->rcache_state[i] = 1;
708 memset(s->rcache[i], 0, DB_RECORD_LEN);
709 return s->rcache[i];
710 }
711 }
712
713 return calloc(1, DB_RECORD_LEN);
714}
715
716static void
717record_buffer_free(asl_store_t *s, char *p)
718{
719 uint32_t i;
720
721 if (s == NULL) return free(p);
722
723 for (i = 0; i < RECORD_CACHE_SIZE; i++)
724 {
725 if (s->rcache[i] == p)
726 {
727 s->rcache_state[i] = 0;
728 return;
729 }
730 }
731
732 free(p);
733}
734
735/*
736 * Finds a free (DB_TYPE_EMPTY) record slot.
737 * Returns the index of the free entry in the slotlist.
738 * Returns ASL_INDEX_NULL if no free slots are available (next write should be at end of file).
739 */
740static uint32_t
741get_free_slot(asl_store_t *s)
742{
743 uint32_t i;
744
745 if (s == NULL) return ASL_INDEX_NULL;
746
747 if (s->empty_count == 0) return ASL_INDEX_NULL;
748
749 for (i = 0; i < s->slotlist_count; i++)
750 {
751 if (s->slotlist[i].type == DB_TYPE_EMPTY)
752 {
753 s->empty_count--;
754 return i;
755 }
756 }
757
758 /* impossible */
759 s->empty_count = 0;
760 return ASL_INDEX_NULL;
761}
762
763static void
764record_list_free(asl_store_t *s, char **list)
765{
766 uint32_t i;
767
768 if (list == NULL) return;
769
770 for (i = 0; list[i] != NULL; i++) record_buffer_free(s, list[i]);
771 free(list);
772}
773
774static uint64_t
775new_id(asl_store_t *s)
776{
777 int status;
778 uint64_t n, out;
779
780 if (s == NULL) return ASL_REF_NULL;
781
782 status = fseek(s->db, DB_HEADER_MAXID_OFFSET, SEEK_SET);
783 if (status < 0) return ASL_REF_NULL;
784
785 out = s->next_id;
786 s->next_id++;
787
788 n = _asl_htonq(s->next_id);
789 status = fwrite(&n, 8, 1, s->db);
790 if (status != 1) return ASL_REF_NULL;
791 return out;
792}
793
794/*
795 * Write each record in the list to a slot in the database.
796 * Fills in "next" index.
797 */
798static uint32_t
799save_record_list(asl_store_t *s, char **list, uint32_t *start)
800{
801 uint32_t i, n, status, si, slot, next, rcount, hash;
802 uint8_t type;
803 off_t offset;
804
805 if (s == NULL) return ASL_STATUS_INVALID_STORE;
806
807 if (list == NULL) return ASL_STATUS_OK;
808
809 for (n = 0; list[n] != NULL; n++);
810
811 if (n == 0) return ASL_STATUS_OK;
812
813 rcount = s->record_count;
814 si = get_free_slot(s);
815
816 /* update slotlist */
817 type = list[0][0];
818
819 if (type == DB_TYPE_STRING) s->string_count++;
820 if (type == DB_TYPE_MESSAGE) s->message_count++;
821
822 next = ASL_INDEX_NULL;
823 if (si == ASL_INDEX_NULL)
824 {
825 next = s->record_count;
826 }
827 else if (si > s->slotlist_count)
828 {
829 return ASL_STATUS_FAILED;
830 }
831 else
832 {
833 next = s->slotlist[si].slot;
834 slotlist_delete(s, si);
835 }
836
837 hash = 0;
838 if (type == DB_TYPE_STRING) hash = header_get_hash(list[0]);
839
840 status = slotlist_insert(s, type, next, header_get_id(list[0]), hash);
841 if (status != ASL_STATUS_OK) return status;
842
843 *start = next;
844
845 for (i = 0; i < n; i++)
846 {
847 slot = next;
848
849 next = 0;
850 if ((i + 1) < n)
851 {
852 si = get_free_slot(s);
853 if (si == ASL_INDEX_NULL)
854 {
855 next = s->record_count + 1;
856 }
857 else if (next > s->slotlist_count)
858 {
859 return ASL_STATUS_FAILED;
860 }
861 else
862 {
863 type = list[i + 1][0];
864 next = s->slotlist[si].slot;
865 slotlist_delete(s, si);
866 }
867 }
868
869 offset = slot * DB_RECORD_LEN;
870 status = fseek(s->db, offset, SEEK_SET);
871 if (status < 0) return ASL_STATUS_WRITE_FAILED;
872
873 header_put_next(next, list[i]);
874
875 status = fwrite(list[i], DB_RECORD_LEN, 1, s->db);
876 if (status != 1) return ASL_STATUS_WRITE_FAILED;
877
878 if (si == ASL_INDEX_NULL) s->record_count++;
879 }
880
881 fflush(s->db);
882
883 return ASL_STATUS_OK;
884}
885
886/*
887 * Converts a string into a NULL-terminated list of records.
888 * Sets sid to new string ID.
889 */
890static uint32_t
891string_encode(asl_store_t *s, uint32_t hash, const char *str, uint64_t *sid, char ***list)
892{
893 char **outlist, *p;
894 const char *t;
895 uint32_t length, remaining, i, n, x;
896
897 if (s == NULL) return ASL_STATUS_INVALID_STORE;
898 if (str == NULL) return ASL_STATUS_INVALID_STRING;
899
900 *sid = new_id(s);
901 if (*sid == ASL_REF_NULL) return ASL_STATUS_FAILED;
902
903 length = strlen(str) + 1;
904 remaining = length;
905
906 x = DB_RECORD_LEN - DB_HLEN_STRING;
907 if (remaining < x) x = remaining;
908 remaining -= x;
909 n = 1;
910
911 x = DB_RECORD_LEN - DB_HLEN_STRCONT;
912 n += (remaining + x - 1) / x;
913
914 outlist = (char **)calloc(n + 1, sizeof(char *));
915 if (outlist == NULL) return ASL_STATUS_NO_MEMORY;
916
917 for (i = 0; i < n; i++)
918 {
919 outlist[i] = record_buffer_alloc(s);
920 if (outlist[i] == NULL)
921 {
922 n = i;
923 for (i = 0; i < n; i++) record_buffer_free(s, outlist[i]);
924 free(outlist);
925 return ASL_STATUS_NO_MEMORY;
926 }
927 }
928
929 *list = outlist;
930
931 outlist[0][0] = (char)DB_TYPE_STRING;
932 p = outlist[0] + 5;
933
934 /* sid */
935 _asl_put_64(*sid, p);
936 p += 8;
937
938 /* refcount */
939 _asl_put_32(1, p);
940 p += 4;
941
942 /* hash */
943 _asl_put_32(hash, p);
944 p += 4;
945
946 /* string length (includes trailing nul) */
947 _asl_put_32(length, p);
948 p += 4;
949
950 t = str;
951 remaining = length;
952
953 x = DB_RECORD_LEN - DB_HLEN_STRING;
954 if (remaining < x) x = remaining;
955 memcpy(p, t, x);
956
957 t += x;
958 remaining -= x;
959
960 x = DB_RECORD_LEN - DB_HLEN_STRCONT;
961 for (i = 1; i < n; i++)
962 {
963 outlist[i][0] = (char)DB_TYPE_STRCONT;
964 p = outlist[i] + 5;
965
966 if (remaining < x) x = remaining;
967 memcpy(p, t, x);
968
969 t += x;
970 remaining -= x;
971 }
972
973 return ASL_STATUS_OK;
974}
975
976/*
977 * Hash is used to improve string search.
978 */
979uint32_t
980string_hash(const char *s, uint32_t inlen)
981{
982 uint32_t a, b, c, l, len;
983
984 if (s == NULL) return 0;
985
986 l = inlen;
987
988 len = l;
989 a = b = 0x9e3779b9;
990 c = 0;
991
992 while (len >= 12)
993 {
994 a += (s[0] + ((uint32_t)s[1]<<8) + ((uint32_t)s[ 2]<<16) + ((uint32_t)s[ 3]<<24));
995 b += (s[4] + ((uint32_t)s[5]<<8) + ((uint32_t)s[ 6]<<16) + ((uint32_t)s[ 7]<<24));
996 c += (s[8] + ((uint32_t)s[9]<<8) + ((uint32_t)s[10]<<16) + ((uint32_t)s[11]<<24));
997
998 mix(a, b, c);
999
1000 s += 12;
1001 len -= 12;
1002 }
1003
1004 c += l;
1005 switch(len)
1006 {
1007 case 11: c += ((uint32_t)s[10]<<24);
1008 case 10: c += ((uint32_t)s[9]<<16);
1009 case 9 : c += ((uint32_t)s[8]<<8);
1010
1011 case 8 : b += ((uint32_t)s[7]<<24);
1012 case 7 : b += ((uint32_t)s[6]<<16);
1013 case 6 : b += ((uint32_t)s[5]<<8);
1014 case 5 : b += s[4];
1015
1016 case 4 : a += ((uint32_t)s[3]<<24);
1017 case 3 : a += ((uint32_t)s[2]<<16);
1018 case 2 : a += ((uint32_t)s[1]<<8);
1019 case 1 : a += s[0];
1020 }
1021
1022 mix(a, b, c);
1023
1024 return c;
1025}
1026
1027/*
1028 * Write refcount to database and update string cache.
1029 */
1030static void
1031string_set_refcount(asl_store_t *s, uint32_t index, const char *str, uint32_t refcount)
1032{
1033 uint32_t slot, i, min, status, v32;
1034 off_t offset;
1035
1036 if (s == NULL) return;
1037
1038 /* update the database */
1039 slot = s->slotlist[index].slot;
1040
1041 offset = (slot * DB_RECORD_LEN) + 13;
1042 status = fseek(s->db, offset, SEEK_SET);
1043
1044 if (status < 0) return;
1045
1046 v32 = htonl(refcount);
1047 status = fwrite(&v32, 4, 1, s->db);
1048
1049 min = 0;
1050
1051 /* if the string is in the string cache, update the refcount there */
1052 for (i = 0; i < STRING_CACHE_SIZE; i++)
1053 {
1054 if (s->string_cache[i].index == index)
1055 {
1056 s->string_cache[i].refcount = refcount;
1057 return;
1058 }
1059
1060 /* locate the minimum refcount while we're looping */
1061 if (s->string_cache[i].refcount < s->string_cache[min].refcount) min = i;
1062 }
1063
1064 /* bail out if the refcount is too low */
1065 if (s->string_cache[min].refcount > refcount) return;
1066
1067 /* replace the current minimum */
1068 if (s->string_cache[min].str != NULL) free(s->string_cache[min].str);
1069
1070 s->string_cache[min].index = index;
1071 s->string_cache[min].refcount = refcount;
1072 s->string_cache[min].str = strdup(str);
1073
1074 if (s->string_cache[min].str == NULL)
1075 {
1076 s->string_cache[min].index = ASL_INDEX_NULL;
1077 s->string_cache[min].refcount = 0;
1078 return;
1079 }
1080}
1081
1082static uint32_t
1083string_fetch_slot(asl_store_t *s, uint32_t slot, char **out, uint32_t *refcount)
1084{
1085 off_t offset;
1086 uint8_t type;
1087 uint32_t status, next, len, x, remaining;
1088 char *outstr, *p, tmp[DB_RECORD_LEN];
1089
1090 if (s == NULL) return ASL_STATUS_INVALID_STORE;
1091 if (out == NULL) return ASL_STATUS_INVALID_ARG;
1092
1093 *out = NULL;
1094 offset = slot * DB_RECORD_LEN;
1095 status = fseek(s->db, offset, SEEK_SET);
1096
1097 if (status < 0) return ASL_STATUS_READ_FAILED;
1098
1099 status = fread(tmp, DB_RECORD_LEN, 1, s->db);
1100 if (status != 1) return ASL_STATUS_READ_FAILED;
1101
1102 type = tmp[0];
1103 if (type != DB_TYPE_STRING) return ASL_STATUS_INVALID_STRING;
1104
1105 len = _asl_get_32(tmp + 21);
1106 if (len == 0) return ASL_STATUS_OK;
1107
1108 *refcount = _asl_get_32(tmp + 13);
1109
1110 next = header_get_next(tmp);
1111
1112 outstr = calloc(1, len);
1113 if (outstr == NULL) return ASL_STATUS_NO_MEMORY;
1114
1115 p = outstr;
1116 remaining = len;
1117
1118 x = DB_RECORD_LEN - DB_HLEN_STRING;
1119 if (x > remaining) x = remaining;
1120
1121 memcpy(p, tmp + DB_HLEN_STRING, x);
1122 p += x;
1123 remaining -= x;
1124
1125 while ((next != 0) && (remaining > 0))
1126 {
1127 offset = next * DB_RECORD_LEN;
1128 status = fseek(s->db, offset, SEEK_SET);
1129
1130 if (status < 0)
1131 {
1132 free(outstr);
1133 return ASL_STATUS_READ_FAILED;
1134 }
1135
1136 status = fread(tmp, DB_RECORD_LEN, 1, s->db);
1137 if (status != 1)
1138 {
1139 free(outstr);
1140 return ASL_STATUS_READ_FAILED;
1141 }
1142
1143 next = header_get_next(tmp);
1144
1145 x = DB_RECORD_LEN - DB_HLEN_STRCONT;
1146 if (x > remaining) x = remaining;
1147
1148 memcpy(p, tmp + DB_HLEN_STRCONT, x);
1149 p += x;
1150 remaining -= x;
1151 }
1152
1153 if ((next != 0) || (remaining != 0))
1154 {
1155 free(outstr);
1156 return ASL_STATUS_READ_FAILED;
1157 }
1158
1159 *out = outstr;
1160 return ASL_STATUS_OK;
1161}
1162
1163static uint32_t
1164string_fetch_sid(asl_store_t *s, uint64_t sid, char **out)
1165{
1166 uint32_t i, len, ref;
1167 uint64_t nsid;
1168 uint8_t inls;
1169 char *p;
1170
1171 if (s == NULL) return ASL_STATUS_INVALID_STORE;
1172 if (out == NULL) return ASL_STATUS_INVALID_ARG;
1173
1174 *out = NULL;
1175 if (sid == ASL_REF_NULL) return ASL_STATUS_OK;
1176
1177 ref = 0;
1178
1179 inls = 0;
1180 nsid = _asl_htonq(sid);
1181 memcpy(&inls, &nsid, 1);
1182 if (inls & 0x80)
1183 {
1184 /* inline string */
1185 inls &= 0x0f;
1186 len = inls;
1187 *out = calloc(1, len);
1188 if (*out == NULL) return ASL_STATUS_NO_MEMORY;
1189 p = 1 + (char *)&nsid;
1190 memcpy(*out, p, len);
1191 return ASL_STATUS_OK;
1192 }
1193
1194 /* Find the string in the database */
1195 i = slotlist_find(s, sid, 0, 0);
1196 if (i == ASL_INDEX_NULL) return ASL_STATUS_NOT_FOUND;
1197
1198 return string_fetch_slot(s, s->slotlist[i].slot, out, &ref);
1199}
1200
1201static uint32_t
1202check_user_access(int32_t msgu, int32_t u)
1203{
1204 /* -1 means anyone may read */
1205 if (msgu == -1) return ASL_STATUS_OK;
1206
1207 /* Check for exact match */
1208 if (msgu == u) return ASL_STATUS_OK;
1209
1210 return ASL_STATUS_ACCESS_DENIED;
1211}
1212
1213static uint32_t
1214check_group_access(int32_t msgg, int32_t u, int32_t g)
1215{
1216 int check;
1217 uuid_t uu, gu;
1218
1219 /* -1 means anyone may read */
1220 if (msgg == -1) return ASL_STATUS_OK;
1221
1222 /* Check for exact match */
1223 if (msgg == g) return ASL_STATUS_OK;
1224
1225 /* Check if user (u) is in read group (msgg) */
1226 mbr_uid_to_uuid(u, uu);
1227 mbr_gid_to_uuid(msgg, gu);
1228
1229 check = 0;
1230 mbr_check_membership(uu, gu, &check);
1231 if (check != 0) return ASL_STATUS_OK;
1232
1233 return ASL_STATUS_ACCESS_DENIED;
1234}
1235
1236static uint32_t
1237check_access(int32_t msgu, int32_t msgg, int32_t u, int32_t g, uint16_t flags)
1238{
1239 uint16_t uset, gset;
1240
1241 /* root (uid 0) may always read */
1242 if (u == 0) return ASL_STATUS_OK;
1243
1244 uset = flags & ASL_MSG_FLAG_READ_UID_SET;
1245 gset = flags & ASL_MSG_FLAG_READ_GID_SET;
1246
1247 /* if no access controls are set, anyone may read */
1248 if ((uset | gset) == 0) return ASL_STATUS_OK;
1249
1250 /* if only uid is set, then access is only by uid match */
1251 if ((uset != 0) && (gset == 0)) return check_user_access(msgu, u);
1252
1253 /* if only gid is set, then access is only by gid match */
1254 if ((uset == 0) && (gset != 0)) return check_group_access(msgg, u, g);
1255
1256 /* both uid and gid are set - check user, then group */
1257 if ((check_user_access(msgu, u)) == ASL_STATUS_OK) return ASL_STATUS_OK;
1258 return check_group_access(msgg, u, g);
1259}
1260
1261static uint32_t
1262pmsg_fetch(asl_store_t *s, uint32_t slot, int32_t ruid, int32_t rgid, uint32_t action, pmsg_t **pmsg)
1263{
1264 off_t offset;
1265 uint32_t status, i, n, v32, next;
1266 int32_t msgu, msgg;
1267 uint64_t msgid;
1268 uint16_t flags;
1269 pmsg_t *out;
1270 char *p, tmp[DB_RECORD_LEN];
1271
1272 if (s == NULL) return ASL_STATUS_INVALID_STORE;
1273 if (pmsg == NULL) return ASL_STATUS_INVALID_ARG;
1274
1275 out = NULL;
1276
1277 if ((action == PMSG_FETCH_ALL) || (action == PMSG_FETCH_STD))
1278 {
1279 *pmsg = NULL;
1280
1281 offset = slot * DB_RECORD_LEN;
1282 status = fseek(s->db, offset, SEEK_SET);
1283
1284 if (status < 0) return ASL_STATUS_READ_FAILED;
1285
1286 status = fread(tmp, DB_RECORD_LEN, 1, s->db);
1287 if (status != 1) return ASL_STATUS_READ_FAILED;
1288
1289 msgid = _asl_get_64(tmp + MSG_OFF_KEY_ID);
1290 msgu = _asl_get_32(tmp + MSG_OFF_KEY_RUID);
1291 msgg = _asl_get_32(tmp + MSG_OFF_KEY_RGID);
1292 flags = _asl_get_16(tmp + MSG_OFF_KEY_FLAGS);
1293
1294 status = check_access(msgu, msgg, ruid, rgid, flags);
1295 if (status != ASL_STATUS_OK) return status;
1296
1297 out = (pmsg_t *)calloc(1, sizeof(pmsg_t));
1298 if (out == NULL) return ASL_STATUS_NO_MEMORY;
1299
1300
1301 p = tmp + 21;
1302
1303 /* ID */
1304 out->msgid = msgid;
1305
1306 /* ReadUID */
1307 out->ruid = msgu;
1308
1309 /* ReadGID */
1310 out->rgid = msgg;
1311
1312 /* Time */
1313 out->time = _asl_get_64(p);
1314 p += 8;
1315
1316 /* Host */
1317 out->host = _asl_get_64(p);
1318 p += 8;
1319
1320 /* Sender */
1321 out->sender = _asl_get_64(p);
1322 p += 8;
1323
1324 /* Facility */
1325 out->facility = _asl_get_64(p);
1326 p += 8;
1327
1328 /* Level */
1329 out->level = _asl_get_32(p);
1330 p += 4;
1331
1332 /* PID */
1333 out->pid = _asl_get_32(p);
1334 p += 4;
1335
1336 /* UID */
1337 out->uid = _asl_get_32(p);
1338 p += 4;
1339
1340 /* GID */
1341 out->gid = _asl_get_32(p);
1342 p += 4;
1343
1344 /* Message */
1345 out->message = _asl_get_64(p);
1346 p += 8;
1347
1348 next = header_get_next(tmp);
1349 out->next = next;
1350
1351 if (action == PMSG_FETCH_STD)
1352 {
1353 /* caller only wants "standard" keys */
1354 *pmsg = out;
1355 return ASL_STATUS_OK;
1356 }
1357
1358 *pmsg = out;
1359 }
1360 else
1361 {
1362 out = *pmsg;
1363 }
1364
1365 n = 0;
1366 next = out->next;
1367
1368 while (next != 0)
1369 {
1370 offset = next * DB_RECORD_LEN;
1371 status = fseek(s->db, offset, SEEK_SET);
1372 if (status < 0)
1373 {
1374 *pmsg = NULL;
1375 free(out);
1376 return ASL_STATUS_READ_FAILED;
1377 }
1378
1379 status = fread(tmp, DB_RECORD_LEN, 1, s->db);
1380 if (status != 1)
1381 {
1382 *pmsg = NULL;
1383 free(out);
1384 return ASL_STATUS_READ_FAILED;
1385 }
1386
1387 if (out->kvcount == 0)
1388 {
1389 v32 = _asl_get_32(tmp + 5);
1390 out->kvcount = v32 * 2;
1391 out->kvlist = (uint64_t *)calloc(out->kvcount, sizeof(uint64_t));
1392 if (out->kvlist == NULL)
1393 {
1394 *pmsg = NULL;
1395 free(out);
1396 return ASL_STATUS_NO_MEMORY;
1397 }
1398 }
1399
1400 p = tmp + 9;
1401
1402 for (i = 0; (i < 4) && (n < out->kvcount); i++)
1403 {
1404 out->kvlist[n++] = _asl_get_64(p);
1405 p += 8;
1406
1407 out->kvlist[n++] = _asl_get_64(p);
1408 p += 8;
1409 }
1410
1411 next = header_get_next(tmp);
1412 }
1413
1414 return ASL_STATUS_OK;
1415}
1416
1417static uint32_t
1418pmsg_match(asl_store_t *s, pmsg_t *q, pmsg_t *m)
1419{
1420 uint32_t i, j;
1421
1422 if (s == NULL) return 0;
1423 if (q == NULL) return 1;
1424 if (m == NULL) return 0;
1425
1426 if (q->kselect & PMSG_SEL_TIME)
1427 {
1428 if (q->time == ASL_REF_NULL) return 0;
1429 if ((q->vselect & PMSG_SEL_TIME) && (q->time != m->time)) return 0;
1430 }
1431
1432 if (q->kselect & PMSG_SEL_HOST)
1433 {
1434 if (q->host == ASL_REF_NULL) return 0;
1435 if ((q->vselect & PMSG_SEL_HOST) && (q->host != m->host)) return 0;
1436 }
1437
1438 if (q->kselect & PMSG_SEL_SENDER)
1439 {
1440 if (q->sender == ASL_REF_NULL) return 0;
1441 if ((q->vselect & PMSG_SEL_SENDER) && (q->sender != m->sender)) return 0;
1442 }
1443
1444 if (q->kselect & PMSG_SEL_FACILITY)
1445 {
1446 if (q->facility == ASL_REF_NULL) return 0;
1447 if ((q->vselect & PMSG_SEL_FACILITY) && (q->facility != m->facility)) return 0;
1448 }
1449
1450 if (q->kselect & PMSG_SEL_MESSAGE)
1451 {
1452 if (q->message == ASL_REF_NULL) return 0;
1453 if ((q->vselect & PMSG_SEL_MESSAGE) && (q->message != m->message)) return 0;
1454 }
1455
1456 if (q->kselect & PMSG_SEL_LEVEL)
1457 {
1458 if (q->level == ASL_INDEX_NULL) return 0;
1459 if ((q->vselect & PMSG_SEL_LEVEL) && (q->level != m->level)) return 0;
1460 }
1461
1462 if (q->kselect & PMSG_SEL_PID)
1463 {
1464 if (q->pid == -1) return 0;
1465 if ((q->vselect & PMSG_SEL_PID) && (q->pid != m->pid)) return 0;
1466 }
1467
1468 if (q->kselect & PMSG_SEL_UID)
1469 {
1470 if (q->uid == -2) return 0;
1471 if ((q->vselect & PMSG_SEL_UID) && (q->uid != m->uid)) return 0;
1472 }
1473
1474 if (q->kselect & PMSG_SEL_GID)
1475 {
1476 if (q->gid == -2) return 0;
1477 if ((q->vselect & PMSG_SEL_GID) && (q->gid != m->gid)) return 0;
1478 }
1479
1480 if (q->kselect & PMSG_SEL_RUID)
1481 {
1482 if (q->ruid == -1) return 0;
1483 if ((q->vselect & PMSG_SEL_RUID) && (q->ruid != m->ruid)) return 0;
1484 }
1485
1486 if (q->kselect & PMSG_SEL_RGID)
1487 {
1488 if (q->rgid == -1) return 0;
1489 if ((q->vselect & PMSG_SEL_RGID) && (q->rgid != m->rgid)) return 0;
1490 }
1491
1492 for (i = 0; i < q->kvcount; i += 2)
1493 {
1494 for (j = 0; j < m->kvcount; j += 2)
1495 {
1496 if (q->kvlist[i] == m->kvlist[j])
1497 {
1498 if (q->kvlist[i + 1] == m->kvlist[j + 1]) break;
1499 return 0;
1500 }
1501 }
1502
1503 if (j >= m->kvcount) return 0;
1504 }
1505
1506 return 1;
1507}
1508
1509static void
1510free_pmsg(pmsg_t *p)
1511{
1512 if (p == NULL) return;
1513 if (p->kvlist != NULL) free(p->kvlist);
1514 free(p);
1515}
1516
1517static uint32_t
1518pmsg_fetch_by_id(asl_store_t *s, uint64_t msgid, int32_t ruid, int32_t rgid, pmsg_t **pmsg, uint32_t *slot)
1519{
1520 uint32_t i, status;
1521
1522 if (s == NULL) return ASL_STATUS_INVALID_STORE;
1523 if (msgid == ASL_REF_NULL) return ASL_STATUS_INVALID_ARG;
1524 if (slot == NULL) return ASL_STATUS_INVALID_ARG;
1525
1526 *slot = ASL_INDEX_NULL;
1527
1528 i = slotlist_find(s, msgid, 0, 0);
1529 if (i == ASL_INDEX_NULL) return ASL_STATUS_INVALID_ID;
1530
1531 *slot = s->slotlist[i].slot;
1532
1533 /* read the message */
1534 *pmsg = NULL;
1535 status = pmsg_fetch(s, s->slotlist[i].slot, ruid, rgid, PMSG_FETCH_ALL, pmsg);
1536 if (status != ASL_STATUS_OK) return status;
1537 if (pmsg == NULL) return ASL_STATUS_FAILED;
1538
1539 return status;
1540}
1541
1542static uint32_t
1543msg_decode(asl_store_t *s, pmsg_t *pmsg, asl_msg_t **out)
1544{
1545 uint32_t status, i, n;
1546 char *key, *val;
1547 asl_msg_t *msg;
1548
1549 if (s == NULL) return ASL_STATUS_INVALID_STORE;
1550 if (out == NULL) return ASL_STATUS_INVALID_ARG;
1551 if (pmsg == NULL) return ASL_STATUS_INVALID_ARG;
1552
1553 *out = NULL;
1554
1555 msg = (asl_msg_t *)calloc(1, sizeof(asl_msg_t));
1556 if (msg == NULL) return ASL_STATUS_NO_MEMORY;
1557
1558 msg->type = ASL_TYPE_MSG;
1559 msg->count = 0;
1560 if (pmsg->time != ASL_REF_NULL) msg->count++;
1561 if (pmsg->host != ASL_REF_NULL) msg->count++;
1562 if (pmsg->sender != ASL_REF_NULL) msg->count++;
1563 if (pmsg->facility != ASL_REF_NULL) msg->count++;
1564 if (pmsg->message != ASL_REF_NULL) msg->count++;
1565 if (pmsg->level != ASL_INDEX_NULL) msg->count++;
1566 if (pmsg->pid != -1) msg->count++;
1567 if (pmsg->uid != -2) msg->count++;
1568 if (pmsg->gid != -2) msg->count++;
1569 if (pmsg->ruid != -1) msg->count++;
1570 if (pmsg->rgid != -1) msg->count++;
1571
1572 msg->count += pmsg->kvcount / 2;
1573
1574 if (msg->count == 0)
1575 {
1576 free(msg);
1577 return ASL_STATUS_INVALID_MESSAGE;
1578 }
1579
1580 /* Message ID */
1581 msg->count += 1;
1582
1583 msg->key = (char **)calloc(msg->count, sizeof(char *));
1584 if (msg->key == NULL)
1585 {
1586 free(msg);
1587 return ASL_STATUS_NO_MEMORY;
1588 }
1589
1590 msg->val = (char **)calloc(msg->count, sizeof(char *));
1591 if (msg->val == NULL)
1592 {
1593 free(msg->key);
1594 free(msg);
1595 return ASL_STATUS_NO_MEMORY;
1596 }
1597
1598 n = 0;
1599
1600 /* Time */
1601 if (pmsg->time != ASL_REF_NULL)
1602 {
1603 msg->key[n] = strdup(ASL_KEY_TIME);
1604 if (msg->key[n] == NULL)
1605 {
1606 asl_free(msg);
1607 return ASL_STATUS_NO_MEMORY;
1608 }
1609
1610 asprintf(&(msg->val[n]), "%llu", pmsg->time);
1611 if (msg->val[n] == NULL)
1612 {
1613 asl_free(msg);
1614 return ASL_STATUS_NO_MEMORY;
1615 }
1616 n++;
1617 }
1618
1619 /* Host */
1620 if (pmsg->host != ASL_REF_NULL)
1621 {
1622 msg->key[n] = strdup(ASL_KEY_HOST);
1623 if (msg->key[n] == NULL)
1624 {
1625 asl_free(msg);
1626 return ASL_STATUS_NO_MEMORY;
1627 }
1628
1629 status = string_fetch_sid(s, pmsg->host, &(msg->val[n]));
1630 n++;
1631 }
1632
1633 /* Sender */
1634 if (pmsg->sender != ASL_REF_NULL)
1635 {
1636 msg->key[n] = strdup(ASL_KEY_SENDER);
1637 if (msg->key[n] == NULL)
1638 {
1639 asl_free(msg);
1640 return ASL_STATUS_NO_MEMORY;
1641 }
1642
1643 status = string_fetch_sid(s, pmsg->sender, &(msg->val[n]));
1644 n++;
1645 }
1646
1647 /* Facility */
1648 if (pmsg->facility != ASL_REF_NULL)
1649 {
1650 msg->key[n] = strdup(ASL_KEY_FACILITY);
1651 if (msg->key[n] == NULL)
1652 {
1653 asl_free(msg);
1654 return ASL_STATUS_NO_MEMORY;
1655 }
1656
1657 status = string_fetch_sid(s, pmsg->facility, &(msg->val[n]));
1658 n++;
1659 }
1660
1661 /* Level */
1662 if (pmsg->level != ASL_INDEX_NULL)
1663 {
1664 msg->key[n] = strdup(ASL_KEY_LEVEL);
1665 if (msg->key[n] == NULL)
1666 {
1667 asl_free(msg);
1668 return ASL_STATUS_NO_MEMORY;
1669 }
1670
1671 asprintf(&(msg->val[n]), "%u", pmsg->level);
1672 if (msg->val[n] == NULL)
1673 {
1674 asl_free(msg);
1675 return ASL_STATUS_NO_MEMORY;
1676 }
1677 n++;
1678 }
1679
1680 /* PID */
1681 if (pmsg->pid != -1)
1682 {
1683 msg->key[n] = strdup(ASL_KEY_PID);
1684 if (msg->key[n] == NULL)
1685 {
1686 asl_free(msg);
1687 return ASL_STATUS_NO_MEMORY;
1688 }
1689
1690 asprintf(&(msg->val[n]), "%d", pmsg->pid);
1691 if (msg->val[n] == NULL)
1692 {
1693 asl_free(msg);
1694 return ASL_STATUS_NO_MEMORY;
1695 }
1696 n++;
1697 }
1698
1699 /* UID */
1700 if (pmsg->uid != -2)
1701 {
1702 msg->key[n] = strdup(ASL_KEY_UID);
1703 if (msg->key[n] == NULL)
1704 {
1705 asl_free(msg);
1706 return ASL_STATUS_NO_MEMORY;
1707 }
1708
1709 asprintf(&(msg->val[n]), "%d", pmsg->uid);
1710 if (msg->val[n] == NULL)
1711 {
1712 asl_free(msg);
1713 return ASL_STATUS_NO_MEMORY;
1714 }
1715 n++;
1716 }
1717
1718 /* GID */
1719 if (pmsg->gid != -2)
1720 {
1721 msg->key[n] = strdup(ASL_KEY_GID);
1722 if (msg->key[n] == NULL)
1723 {
1724 asl_free(msg);
1725 return ASL_STATUS_NO_MEMORY;
1726 }
1727
1728 asprintf(&(msg->val[n]), "%d", pmsg->gid);
1729 if (msg->val[n] == NULL)
1730 {
1731 asl_free(msg);
1732 return ASL_STATUS_NO_MEMORY;
1733 }
1734 n++;
1735 }
1736
1737 /* Message */
1738 if (pmsg->message != ASL_REF_NULL)
1739 {
1740 msg->key[n] = strdup(ASL_KEY_MSG);
1741 if (msg->key[n] == NULL)
1742 {
1743 asl_free(msg);
1744 return ASL_STATUS_NO_MEMORY;
1745 }
1746
1747 status = string_fetch_sid(s, pmsg->message, &(msg->val[n]));
1748 n++;
1749 }
1750
1751 /* ReadUID */
1752 if (pmsg->ruid != -1)
1753 {
1754 msg->key[n] = strdup(ASL_KEY_READ_UID);
1755 if (msg->key[n] == NULL)
1756 {
1757 asl_free(msg);
1758 return ASL_STATUS_NO_MEMORY;
1759 }
1760
1761 asprintf(&(msg->val[n]), "%d", pmsg->ruid);
1762 if (msg->val[n] == NULL)
1763 {
1764 asl_free(msg);
1765 return ASL_STATUS_NO_MEMORY;
1766 }
1767 n++;
1768 }
1769
1770 /* ReadGID */
1771 if (pmsg->rgid != -1)
1772 {
1773 msg->key[n] = strdup(ASL_KEY_READ_GID);
1774 if (msg->key[n] == NULL)
1775 {
1776 asl_free(msg);
1777 return ASL_STATUS_NO_MEMORY;
1778 }
1779
1780 asprintf(&(msg->val[n]), "%d", pmsg->rgid);
1781 if (msg->val[n] == NULL)
1782 {
1783 asl_free(msg);
1784 return ASL_STATUS_NO_MEMORY;
1785 }
1786 n++;
1787 }
1788
1789 /* Message ID */
1790 msg->key[n] = strdup(ASL_KEY_MSG_ID);
1791 if (msg->key[n] == NULL)
1792 {
1793 asl_free(msg);
1794 return ASL_STATUS_NO_MEMORY;
1795 }
1796
1797 asprintf(&(msg->val[n]), "%llu", pmsg->msgid);
1798 if (msg->val[n] == NULL)
1799 {
1800 asl_free(msg);
1801 return ASL_STATUS_NO_MEMORY;
1802 }
1803 n++;
1804
1805 /* Key - Value List */
1806 for (i = 0; i < pmsg->kvcount; i++)
1807 {
1808 key = NULL;
1809 status = string_fetch_sid(s, pmsg->kvlist[i++], &key);
1810 if (status != ASL_STATUS_OK)
1811 {
1812 if (key != NULL) free(key);
1813 continue;
1814 }
1815
1816 val = NULL;
1817 status = string_fetch_sid(s, pmsg->kvlist[i], &val);
1818 if (status != ASL_STATUS_OK)
1819 {
1820 if (key != NULL) free(key);
1821 if (val != NULL) free(val);
1822 continue;
1823 }
1824
1825 status = asl_set((aslmsg)msg, key, val);
1826 if (key != NULL) free(key);
1827 if (val != NULL) free(val);
1828 if (status != 0)
1829 {
1830 asl_free(msg);
1831 return ASL_STATUS_FAILED;
1832 }
1833 }
1834
1835 *out = msg;
1836 return ASL_STATUS_OK;
1837}
1838
1839/*
1840 * Finds string either in the string cache or in the database
1841 */
1842static uint32_t
1843store_string_find(asl_store_t *s, uint32_t hash, const char *str, uint32_t *index, uint32_t *refcount)
1844{
1845 uint32_t i, status, test;
1846 char *tmp;
1847
1848 if (s == NULL) return ASL_STATUS_INVALID_STORE;
1849 if (str == NULL) return ASL_STATUS_INVALID_ARG;
1850 if (index == NULL) return ASL_STATUS_INVALID_ARG;
1851 if (refcount == NULL) return ASL_STATUS_INVALID_ARG;
1852 if (s->slotlist == NULL) return ASL_STATUS_FAILED;
1853
1854 /* check the cache */
1855 for (i = 0; i < STRING_CACHE_SIZE; i++)
1856 {
1857 if (s->string_cache[i].index == ASL_INDEX_NULL) continue;
1858
1859 test = s->slotlist[s->string_cache[i].index].hash;
1860 if (test != hash) continue;
1861
1862 if (s->string_cache[i].str == NULL)
1863 {
1864 /* can't happen, but clean up anyway */
1865 s->string_cache[i].index = ASL_INDEX_NULL;
1866 s->string_cache[i].refcount = 0;
1867 continue;
1868 }
1869
1870 if (strcmp(s->string_cache[i].str, str)) continue;
1871
1872 /* Bingo */
1873 *index = s->string_cache[i].index;
1874 *refcount = s->string_cache[i].refcount;
1875 return ASL_STATUS_OK;
1876 }
1877
1878 /* check the database */
1879 for (i = 0; i < s->slotlist_count; i++)
1880 {
1881 if ((s->slotlist[i].type != DB_TYPE_STRING) || (s->slotlist[i].hash != hash)) continue;
1882
1883 /* read the whole string */
1884 tmp = NULL;
1885 *refcount = 0;
1886 status = string_fetch_slot(s, s->slotlist[i].slot, &tmp, refcount);
1887 if (status != ASL_STATUS_OK) return status;
1888 if (tmp == NULL) return ASL_STATUS_FAILED;
1889
1890 status = strcmp(tmp, str);
1891 free(tmp);
1892 if (status != 0) continue;
1893
1894 /* Bingo! */
1895 *index = i;
1896 return ASL_STATUS_OK;
1897 }
1898
1899 *refcount = 0;
1900 return ASL_STATUS_FAILED;
1901}
1902
1903/*
1904 * Looks up a string ID number.
1905 * Creates the string if necessary.
1906 * Increments the string refcount.
1907 */
1908static uint64_t
1909string_retain(asl_store_t *s, const char *str, uint32_t create)
1910{
1911 uint32_t status, hash, index, slot, refcount, len;
1912 uint64_t nsid, sid;
1913 char **recordlist, *p;
1914 uint8_t inls;
1915
1916 if (s == NULL) return ASL_REF_NULL;
1917 if (str == NULL) return ASL_REF_NULL;
1918
1919 sid = ASL_REF_NULL;
1920 index = ASL_INDEX_NULL;
1921 slot = ASL_INDEX_NULL;
1922 refcount = 0;
1923
1924 len = strlen(str);
1925 if (len < 8)
1926 {
1927 /* inline string */
1928 inls = len;
1929 inls |= 0x80;
1930
1931 nsid = 0;
1932 p = (char *)&nsid;
1933 memcpy(p, &inls, 1);
1934 memcpy(p + 1, str, len);
1935 sid = _asl_ntohq(nsid);
1936 return sid;
1937 }
1938
1939 hash = string_hash(str, len);
1940
1941 /* check the database */
1942 status = store_string_find(s, hash, str, &index, &refcount);
1943 if (status == ASL_STATUS_OK)
1944 {
1945 if (index == ASL_INDEX_NULL) return ASL_REF_NULL;
1946 if (create == 0) return s->slotlist[index].xid;
1947
1948 refcount++;
1949 string_set_refcount(s, index, str, refcount);
1950 return s->slotlist[index].xid;
1951 }
1952
1953 if (create == 0) return ASL_REF_NULL;
1954
1955 /* create the string */
1956 recordlist = NULL;
1957 status = string_encode(s, hash, str, &sid, &recordlist);
1958 if (status != ASL_STATUS_OK) return ASL_REF_NULL;
1959 if (recordlist == NULL) return ASL_REF_NULL;
1960
1961 status = save_record_list(s, recordlist, &slot);
1962 record_list_free(s, recordlist);
1963 if (status != ASL_STATUS_OK) return status;
1964
1965 return sid;
1966}
1967
1968static uint32_t
1969record_chain_free(asl_store_t *s, uint64_t xid, uint32_t slot, uint8_t type)
1970{
1971 uint32_t status, next, where;
1972 off_t offset;
1973 char zdb[DB_RECORD_LEN];
1974
1975 if (s == NULL) return ASL_STATUS_INVALID_STORE;
1976
1977 if ((type == DB_TYPE_STRING) && (s->string_count > 0)) s->string_count--;
1978 if ((type == DB_TYPE_MESSAGE) && (s->message_count > 0)) s->message_count--;
1979
1980 memset(zdb, 0, DB_RECORD_LEN);
1981
1982 /*
1983 * Walk the chain and mark each slot as free.
1984 *
1985 * This is tricky:
1986 * We need to know the index in the slot list for each slot used in the record.
1987 * We are given the xid for the record, which we pass to slotlist_find
1988 * to get the index of the first slot. The slotlist entries for all subsequent
1989 * slots will have xid 0, since they are of type DB_TYPE_KVLIST or DB_TYPE_STRCONT.
1990 * Since slotlist has a secondary sort by slot, we can do a binary search within the
1991 * xid 0 entries with slotlist_find to get the slotlist index of those slots.
1992 */
1993 where = slotlist_find(s, xid, 0, 0);
1994
1995 next = slot;
1996 while (next != 0)
1997 {
1998 /* update slotlist */
1999 slotlist_make_empty(s, where);
2000
2001 offset = next * DB_RECORD_LEN;
2002
2003 status = fseek(s->db, offset + 1, SEEK_SET);
2004 if (status < 0) return ASL_STATUS_WRITE_FAILED;
2005
2006 status = fread(&next, 4, 1, s->db);
2007 if (status != 1) return ASL_STATUS_WRITE_FAILED;
2008 next = ntohl(next);
2009
2010 status = fseek(s->db, offset, SEEK_SET);
2011 if (status < 0) return ASL_STATUS_WRITE_FAILED;
2012
2013 status = fwrite(zdb, DB_RECORD_LEN, 1, s->db);
2014 if (status != 1) return ASL_STATUS_WRITE_FAILED;
2015
2016 if (next != 0) where = slotlist_find_xid0_slot(s, next);
2017 }
2018
2019 return ASL_STATUS_OK;
2020}
2021
2022/*
2023 * Removes records.
2024 *
2025 * Decrements string refcount.
2026 * Removes string if refcount becomes zero.
2027 */
2028static uint32_t
2029id_release(asl_store_t *s, uint64_t xid, uint32_t slot, uint8_t type)
2030{
2031 uint32_t status, refcount, v32;
2032 uint64_t x;
2033 char head[17];
2034 off_t offset;
2035
2036 if (s == NULL) return ASL_STATUS_INVALID_STORE;
2037 if (xid == ASL_REF_NULL) return ASL_STATUS_INVALID_ID;
2038 if (slot == ASL_INDEX_NULL) return ASL_STATUS_INVALID_ARG;
2039
2040 /* Note that record_chain_free() updates slotlist */
2041
2042 offset = slot * DB_RECORD_LEN;
2043 status = fseek(s->db, offset, SEEK_SET);
2044 if (status < 0) return ASL_STATUS_WRITE_FAILED;
2045
2046 status = fread(head, 17, 1, s->db);
2047 if (status != 1) return ASL_STATUS_WRITE_FAILED;
2048
2049 if (head[0] != type) return ASL_STATUS_FAILED;
2050
2051 x = header_get_id(head);
2052
2053 if (x != xid) return ASL_STATUS_FAILED;
2054
2055 /* small kludge: only strings are refcounted */
2056 refcount = 1;
2057 if (type == DB_TYPE_STRING) refcount = header_get_refcount(head);
2058
2059 refcount--;
2060 if (refcount == 0)
2061 {
2062 return record_chain_free(s, xid, slot, type);
2063 }
2064 else
2065 {
2066 offset += 13;
2067 status = fseek(s->db, offset, SEEK_SET);
2068 if (status < 0) return ASL_STATUS_WRITE_FAILED;
2069
2070 v32 = htonl(refcount);
2071 status = fwrite(&v32, 4, 1, s->db);
2072 if (status != 1) return ASL_STATUS_WRITE_FAILED;
2073
2074 return ASL_STATUS_OK;
2075 }
2076}
2077
2078static uint32_t
2079string_release(asl_store_t *s, uint64_t sid)
2080{
2081 uint32_t i;
2082
2083 i = slotlist_find(s, sid, 0, 0);
2084 if (i == ASL_INDEX_NULL) return ASL_STATUS_INVALID_ID;
2085
2086 return id_release(s, sid, s->slotlist[i].slot, DB_TYPE_STRING);
2087}
2088
2089static uint32_t
2090message_release(asl_store_t *s, uint64_t xid)
2091{
2092 uint32_t i, slot, status;
2093 pmsg_t *pmsg;
2094
2095 pmsg = NULL;
2096 slot = ASL_INDEX_NULL;
2097
2098 /* read message and release strings */
2099 status = pmsg_fetch_by_id(s, xid, 0, 0, &pmsg, &slot);
2100 if (status != ASL_STATUS_OK) return status;
2101 if (pmsg == NULL) return ASL_STATUS_READ_FAILED;
2102
2103 string_release(s, pmsg->host);
2104 string_release(s, pmsg->sender);
2105 string_release(s, pmsg->facility);
2106 string_release(s, pmsg->message);
2107 for (i = 0; i < pmsg->kvcount; i++) string_release(s, pmsg->kvlist[i]);
2108 free_pmsg(pmsg);
2109
2110 return id_release(s, xid, slot, DB_TYPE_MESSAGE);
2111}
2112
2113/*
2114 * Convert msg into a database record (or list of records if additional key/value pairs are present).
2115 * Returns a NULL-terminated list of records.
2116 *
2117 * Sets the message access control flags as follows:
2118 * If ruid is specified (anthing other than -1), then we use that as the ReadUID and set the uid access flag
2119 * Else if msg contains a ReadUID, we use that as the ReadUID and set the uid access flag
2120 * Else ReadUID is -1 and we don't set the uid access flag.
2121 * Same logic for ReadGID.
2122 */
2123static uint32_t
2124message_encode(asl_store_t *s, asl_msg_t *msg, int32_t ruid, int32_t rgid, uint64_t *msgid, char ***list)
2125{
2126 char **outlist, *std, *kvl, *p;
2127 uint32_t i, kvcount, kvn, len;
2128 uint32_t level;
2129 uint16_t flags;
2130 int32_t pid, uid, gid, muid, mgid;
2131 uint64_t time_val, host_sid, sender_sid, facility_sid, message_sid, k, v;
2132
2133 if (s == NULL) return ASL_STATUS_INVALID_STORE;
2134 if (msg == NULL) return ASL_STATUS_INVALID_MESSAGE;
2135 if (list == NULL) return ASL_STATUS_INVALID_ARG;
2136
2137 len = 2;
2138
2139 outlist = (char **)calloc(len, sizeof(char *));
2140 if (outlist == NULL) return ASL_STATUS_NO_MEMORY;
2141
2142 std = record_buffer_alloc(s);
2143 if (std == NULL)
2144 {
2145 free(outlist);
2146 return ASL_STATUS_NO_MEMORY;
2147 }
2148
2149 *msgid = new_id(s);
2150 if (*msgid == ASL_REF_NULL)
2151 {
2152 free(outlist);
2153 free(std);
2154 return ASL_STATUS_FAILED;
2155 }
2156
2157 flags = 0;
2158
2159 muid = -1;
2160 if (ruid != -1)
2161 {
2162 muid = ruid;
2163 flags |= ASL_MSG_FLAG_READ_UID_SET;
2164 }
2165
2166 mgid = -1;
2167 if (rgid != -1)
2168 {
2169 mgid = rgid;
2170 flags |= ASL_MSG_FLAG_READ_GID_SET;
2171 }
2172
2173 outlist[0] = std;
2174
2175 flags = 0;
2176 level = ASL_INDEX_NULL;
2177 pid = -1;
2178 uid = -2;
2179 gid = -2;
2180 host_sid = ASL_REF_NULL;
2181 sender_sid = ASL_REF_NULL;
2182 facility_sid = ASL_REF_NULL;
2183 message_sid = ASL_REF_NULL;
2184 time_val = ASL_REF_NULL;
2185
2186 kvcount = 0;
2187 kvn = 0;
2188 kvl = NULL;
2189 p = NULL;
2190
2191 for (i = 0; i < msg->count; i++)
2192 {
2193 if (msg->key[i] == NULL) continue;
2194
2195 else if (!strcmp(msg->key[i], ASL_KEY_TIME))
2196 {
2197 if (msg->val[i] != NULL) time_val = asl_parse_time(msg->val[i]);
2198 }
2199 else if (!strcmp(msg->key[i], ASL_KEY_HOST))
2200 {
2201 if (msg->val[i] != NULL) host_sid = string_retain(s, msg->val[i], 1);
2202 }
2203 else if (!strcmp(msg->key[i], ASL_KEY_SENDER))
2204 {
2205 if (msg->val[i] != NULL) sender_sid = string_retain(s, msg->val[i], 1);
2206 }
2207 else if (!strcmp(msg->key[i], ASL_KEY_PID))
2208 {
2209 if (msg->val[i] != NULL) pid = atoi(msg->val[i]);
2210 }
2211 else if (!strcmp(msg->key[i], ASL_KEY_UID))
2212 {
2213 if (msg->val[i] != NULL) uid = atoi(msg->val[i]);
2214 }
2215 else if (!strcmp(msg->key[i], ASL_KEY_GID))
2216 {
2217 if (msg->val[i] != NULL) gid = atoi(msg->val[i]);
2218 }
2219 else if (!strcmp(msg->key[i], ASL_KEY_LEVEL))
2220 {
2221 if (msg->val[i] != NULL) level = atoi(msg->val[i]);
2222 }
2223 else if (!strcmp(msg->key[i], ASL_KEY_MSG))
2224 {
2225 if (msg->val[i] != NULL) message_sid = string_retain(s, msg->val[i], 1);
2226 }
2227 else if (!strcmp(msg->key[i], ASL_KEY_FACILITY))
2228 {
2229 if (msg->val[i] != NULL) facility_sid = string_retain(s, msg->val[i], 1);
2230 }
2231 else if (!strcmp(msg->key[i], ASL_KEY_READ_UID))
2232 {
2233 if (((flags & ASL_MSG_FLAG_READ_UID_SET) == 0) && (msg->val[i] != NULL))
2234 {
2235 muid = atoi(msg->val[i]);
2236 flags |= ASL_MSG_FLAG_READ_UID_SET;
2237 }
2238 }
2239 else if (!strcmp(msg->key[i], ASL_KEY_READ_GID))
2240 {
2241 if (((flags & ASL_MSG_FLAG_READ_GID_SET) == 0) && (msg->val[i] != NULL))
2242 {
2243 mgid = atoi(msg->val[i]);
2244 flags |= ASL_MSG_FLAG_READ_GID_SET;
2245 }
2246 }
2247 else if (!strcmp(msg->key[i], ASL_KEY_MSG_ID))
2248 {
2249 /* Ignore */
2250 continue;
2251 }
2252 else
2253 {
2254 k = string_retain(s, msg->key[i], 1);
2255 if (k == ASL_REF_NULL) continue;
2256
2257 v = ASL_REF_NULL;
2258 if (msg->val[i] != NULL) v = string_retain(s, msg->val[i], 1);
2259
2260 if (kvl == NULL)
2261 {
2262 outlist = (char **)reallocf(outlist, (len + 1) * sizeof(char *));
2263 if (outlist == NULL)
2264 {
2265 free(std);
2266 return ASL_STATUS_NO_MEMORY;
2267 }
2268
2269 kvl = record_buffer_alloc(s);
2270 if (kvl == NULL)
2271 {
2272 record_list_free(s, outlist);
2273 return ASL_STATUS_NO_MEMORY;
2274 }
2275
2276 kvl[0] = DB_TYPE_KVLIST;
2277 p = kvl + 9;
2278
2279 outlist[len - 1] = kvl;
2280 outlist[len] = NULL;
2281 len++;
2282 }
2283
2284 kvcount++;
2285
2286 _asl_put_64(k, p);
2287 kvn++;
2288 p += 8;
2289
2290 _asl_put_64(v, p);
2291 kvn++;
2292 p += 8;
2293
2294 if (kvn >= 8)
2295 {
2296 kvl = NULL;
2297 kvn = 0;
2298 }
2299 }
2300 }
2301
2302 /* encode kvcount in first kvlist record */
2303 if (kvcount > 0)
2304 {
2305 kvl = outlist[1];
2306 _asl_put_32(kvcount, kvl + 5);
2307 }
2308
2309 /* encode std */
2310 std[0] = DB_TYPE_MESSAGE;
2311 p = std + 5;
2312
2313 _asl_put_64(*msgid, p);
2314 p += 8;
2315
2316 _asl_put_32(muid, p);
2317 p += 4;
2318
2319 _asl_put_32(mgid, p);
2320 p += 4;
2321
2322 _asl_put_64(time_val, p);
2323 p += 8;
2324
2325 _asl_put_64(host_sid, p);
2326 p += 8;
2327
2328 _asl_put_64(sender_sid, p);
2329 p += 8;
2330
2331 _asl_put_64(facility_sid, p);
2332 p += 8;
2333
2334 _asl_put_32(level, p);
2335 p += 4;
2336
2337 _asl_put_32(pid, p);
2338 p += 4;
2339
2340 _asl_put_32(uid, p);
2341 p += 4;
2342
2343 _asl_put_32(gid, p);
2344 p += 4;
2345
2346 _asl_put_64(message_sid, p);
2347 p += 8;
2348
2349 _asl_put_16(flags, p);
2350 p += 4;
2351
2352 *list = outlist;
2353
2354 return ASL_STATUS_OK;
2355}
2356
2357uint32_t
2358asl_store_save(asl_store_t *s, asl_msg_t *msg, int32_t ruid, int32_t rgid, uint64_t *msgid)
2359{
2360 char **list;
2361 uint32_t status, slot;
2362 uint64_t tick;
2363
2364 if (s == NULL) return ASL_STATUS_INVALID_STORE;
2365 if (s->flags & ASL_STORE_FLAG_READ_ONLY) return ASL_STATUS_READ_ONLY;
2366
2367 list = NULL;
2368 status = message_encode(s, msg, ruid, rgid, msgid, &list);
2369 if ((status != ASL_STATUS_OK) || (list == NULL)) return status;
2370
2371 slot = 0;
2372 status = save_record_list(s, list, &slot);
2373 tick = _asl_get_64(list[0] + MSG_OFF_KEY_TIME);
2374
2375 /* if time has gone backwards, unset the flag */
2376 if (tick < s->max_time) s->flags &= ~ASL_STORE_FLAG_TIME_SORTED;
2377 else s->max_time = tick;
2378
2379 record_list_free(s, list);
2380
2381 return status;
2382}
2383
2384uint32_t
2385asl_store_remove(asl_store_t *s, uint64_t msgid)
2386{
2387 uint32_t status;
2388
2389 if (s == NULL) return ASL_STATUS_INVALID_STORE;
2390 if (s->flags & ASL_STORE_FLAG_READ_ONLY) return ASL_STATUS_READ_ONLY;
2391
2392 status = message_release(s, msgid);
2393 return status;
2394}
2395
2396uint32_t
2397asl_store_fetch(asl_store_t *s, uint64_t msgid, int32_t ruid, int32_t rgid, asl_msg_t **msg)
2398{
2399 uint32_t status, slot;
2400 pmsg_t *pmsg;
2401
2402 if (s == NULL) return ASL_STATUS_INVALID_STORE;
2403 if (msgid == ASL_REF_NULL) return ASL_STATUS_INVALID_ARG;
2404
2405 pmsg = NULL;
2406 slot = ASL_INDEX_NULL;
2407
2408 status = pmsg_fetch_by_id(s, msgid, ruid, rgid, &pmsg, &slot);
2409 if (status != ASL_STATUS_OK) return status;
2410 if (pmsg == NULL) return ASL_STATUS_FAILED;
2411
2412 status = msg_decode(s, pmsg, msg);
2413 free_pmsg(pmsg);
2414
2415 return status;
2416}
2417
2418static uint32_t
2419query_to_pmsg(asl_store_t *s, asl_msg_t *q, pmsg_t **p)
2420{
2421 pmsg_t *out;
2422 uint32_t i, j;
2423 uint64_t ksid, vsid;
2424
2425 if (s == NULL) return ASL_STATUS_INVALID_STORE;
2426 if (p == NULL) return ASL_STATUS_INVALID_ARG;
2427
2428 if (q == NULL) return Q_NULL;
2429 if (q->count == 0) return Q_NULL;
2430
2431 *p = NULL;
2432
2433 if (q->op != NULL)
2434 {
2435 for (i = 0; i < q->count; i++) if (q->op[i] != ASL_QUERY_OP_EQUAL) return Q_SLOW;
2436 }
2437
2438 out = (pmsg_t *)calloc(1, sizeof(pmsg_t));
2439 if (out == NULL) return ASL_STATUS_NO_MEMORY;
2440
2441 for (i = 0; i < q->count; i++)
2442 {
2443 if (q->key[i] == NULL) continue;
2444
2445 else if (!strcmp(q->key[i], ASL_KEY_TIME))
2446 {
2447 if (out->kselect & PMSG_SEL_TIME)
2448 {
2449 free_pmsg(out);
2450 return Q_SLOW;
2451 }
2452
2453 out->kselect |= PMSG_SEL_TIME;
2454 if (q->val[i] != NULL)
2455 {
2456 out->vselect |= PMSG_SEL_TIME;
2457 out->time = asl_parse_time(q->val[i]);
2458 }
2459 }
2460 else if (!strcmp(q->key[i], ASL_KEY_HOST))
2461 {
2462 if (out->kselect & PMSG_SEL_HOST)
2463 {
2464 free_pmsg(out);
2465 return Q_SLOW;
2466 }
2467
2468 out->kselect |= PMSG_SEL_HOST;
2469 if (q->val[i] != NULL)
2470 {
2471 out->vselect |= PMSG_SEL_HOST;
2472 out->host = string_retain(s, q->val[i], 0);
2473 if (out->host == ASL_REF_NULL)
2474 {
2475 free_pmsg(out);
2476 return Q_FAIL;
2477 }
2478 }
2479 }
2480 else if (!strcmp(q->key[i], ASL_KEY_SENDER))
2481 {
2482 if (out->kselect & PMSG_SEL_SENDER)
2483 {
2484 free_pmsg(out);
2485 return Q_SLOW;
2486 }
2487
2488 out->kselect |= PMSG_SEL_SENDER;
2489 if (q->val[i] != NULL)
2490 {
2491 out->vselect |= PMSG_SEL_SENDER;
2492 out->sender = string_retain(s, q->val[i], 0);
2493 if (out->sender == ASL_REF_NULL)
2494 {
2495 free_pmsg(out);
2496 return Q_FAIL;
2497 }
2498 }
2499 }
2500 else if (!strcmp(q->key[i], ASL_KEY_PID))
2501 {
2502 if (out->kselect & PMSG_SEL_PID)
2503 {
2504 free_pmsg(out);
2505 return Q_SLOW;
2506 }
2507
2508 out->kselect |= PMSG_SEL_PID;
2509 if (q->val[i] != NULL)
2510 {
2511 out->vselect |= PMSG_SEL_PID;
2512 out->pid = atoi(q->val[i]);
2513 }
2514 }
2515 else if (!strcmp(q->key[i], ASL_KEY_UID))
2516 {
2517 if (out->kselect & PMSG_SEL_UID)
2518 {
2519 free_pmsg(out);
2520 return Q_SLOW;
2521 }
2522
2523 out->kselect |= PMSG_SEL_UID;
2524 if (q->val[i] != NULL)
2525 {
2526 out->vselect |= PMSG_SEL_UID;
2527 out->uid = atoi(q->val[i]);
2528 }
2529 }
2530 else if (!strcmp(q->key[i], ASL_KEY_GID))
2531 {
2532 if (out->kselect & PMSG_SEL_GID)
2533 {
2534 free_pmsg(out);
2535 return Q_SLOW;
2536 }
2537
2538 out->kselect |= PMSG_SEL_GID;
2539 if (q->val[i] != NULL)
2540 {
2541 out->vselect |= PMSG_SEL_GID;
2542 out->gid = atoi(q->val[i]);
2543 }
2544 }
2545 else if (!strcmp(q->key[i], ASL_KEY_LEVEL))
2546 {
2547 if (out->kselect & PMSG_SEL_LEVEL)
2548 {
2549 free_pmsg(out);
2550 return Q_SLOW;
2551 }
2552
2553 out->kselect |= PMSG_SEL_LEVEL;
2554 if (q->val[i] != NULL)
2555 {
2556 out->vselect |= PMSG_SEL_LEVEL;
2557 out->level = atoi(q->val[i]);
2558 }
2559 }
2560 else if (!strcmp(q->key[i], ASL_KEY_MSG))
2561 {
2562 if (out->kselect & PMSG_SEL_MESSAGE)
2563 {
2564 free_pmsg(out);
2565 return Q_SLOW;
2566 }
2567
2568 out->kselect |= PMSG_SEL_MESSAGE;
2569 if (q->val[i] != NULL)
2570 {
2571 out->vselect |= PMSG_SEL_MESSAGE;
2572 out->message = string_retain(s, q->val[i], 0);
2573 if (out->message == ASL_REF_NULL)
2574 {
2575 free_pmsg(out);
2576 return Q_FAIL;
2577 }
2578 }
2579 }
2580 else if (!strcmp(q->key[i], ASL_KEY_FACILITY))
2581 {
2582 if (out->kselect & PMSG_SEL_FACILITY)
2583 {
2584 free_pmsg(out);
2585 return Q_SLOW;
2586 }
2587
2588 out->kselect |= PMSG_SEL_FACILITY;
2589 if (q->val[i] != NULL)
2590 {
2591 out->vselect |= PMSG_SEL_FACILITY;
2592 out->facility = string_retain(s, q->val[i], 0);
2593 if (out->facility == ASL_REF_NULL)
2594 {
2595 free_pmsg(out);
2596 return Q_FAIL;
2597 }
2598 }
2599 }
2600 else if (!strcmp(q->key[i], ASL_KEY_READ_UID))
2601 {
2602 if (out->kselect & PMSG_SEL_RUID)
2603 {
2604 free_pmsg(out);
2605 return Q_SLOW;
2606 }
2607
2608 out->kselect |= PMSG_SEL_RUID;
2609 if (q->val[i] != NULL)
2610 {
2611 out->vselect |= PMSG_SEL_RUID;
2612 out->ruid = atoi(q->val[i]);
2613 }
2614 }
2615 else if (!strcmp(q->key[i], ASL_KEY_READ_GID))
2616 {
2617 if (out->kselect & PMSG_SEL_RGID)
2618 {
2619 free_pmsg(out);
2620 return Q_SLOW;
2621 }
2622
2623 out->kselect |= PMSG_SEL_RGID;
2624 if (q->val[i] != NULL)
2625 {
2626 out->vselect |= PMSG_SEL_RGID;
2627 out->rgid = atoi(q->val[i]);
2628 }
2629 }
2630 else
2631 {
2632 ksid = string_retain(s, q->key[i], 0);
2633 if (ksid == ASL_REF_NULL)
2634 {
2635 free_pmsg(out);
2636 return Q_FAIL;
2637 }
2638
2639 for (j = 0; j < out->kvcount; j += 2)
2640 {
2641 if (out->kvlist[j] == ksid)
2642 {
2643 free_pmsg(out);
2644 return Q_SLOW;
2645 }
2646 }
2647
2648 vsid = ASL_REF_NULL;
2649 if (q->val[i] != NULL)
2650 {
2651 vsid = string_retain(s, q->val[i], 0);
2652 if (ksid == ASL_REF_NULL)
2653 {
2654 free_pmsg(out);
2655 return Q_FAIL;
2656 }
2657 }
2658
2659 if (out->kvcount == 0)
2660 {
2661 out->kvlist = (uint64_t *)calloc(2, sizeof(uint64_t));
2662 }
2663 else
2664 {
2665 out->kvlist = (uint64_t *)reallocf(out->kvlist, (out->kvcount + 2) * sizeof(uint64_t));
2666 }
2667
2668 if (out->kvlist == NULL)
2669 {
2670 free_pmsg(out);
2671 return ASL_STATUS_NO_MEMORY;
2672 }
2673
2674 out->kvlist[out->kvcount++] = ksid;
2675 out->kvlist[out->kvcount++] = vsid;
2676 }
2677 }
2678
2679 *p = out;
2680 return Q_FAST;
2681}
2682
2683uint32_t
2684msg_match(asl_store_t *s, uint32_t qtype, pmsg_t *qp, asl_msg_t *q, int32_t ruid, int32_t rgid, uint32_t slot, pmsg_t **iopm, asl_msg_t **iomsg, asl_msg_list_t **res, uint32_t *didmatch)
2685{
2686 uint32_t status, what;
2687
2688 *didmatch = 0;
2689
2690 if (qtype == Q_FAIL) return ASL_STATUS_OK;
2691
2692 if (qtype == Q_NULL)
2693 {
2694 if (*iopm == NULL)
2695 {
2696 status = pmsg_fetch(s, slot, ruid, rgid, PMSG_FETCH_ALL, iopm);
2697 if (status != ASL_STATUS_OK) return status;
2698 if (*iopm == NULL) return ASL_STATUS_FAILED;
2699 }
2700 }
2701 else if (qtype == Q_FAST)
2702 {
2703 if (qp == NULL) return ASL_STATUS_INVALID_ARG;
2704
2705 what = PMSG_FETCH_STD;
2706 if (qp->kvcount > 0) what = PMSG_FETCH_ALL;
2707
2708 if (*iopm == NULL)
2709 {
2710 status = pmsg_fetch(s, slot, ruid, rgid, what, iopm);
2711 if (status != ASL_STATUS_OK) return status;
2712 if (*iopm == NULL) return ASL_STATUS_FAILED;
2713 }
2714
2715 status = pmsg_match(s, qp, *iopm);
2716 if (status == 1)
2717 {
2718 if ((what == PMSG_FETCH_STD) && ((*iopm)->next != 0) && ((*iopm)->kvcount == 0))
2719 {
2720 status = pmsg_fetch(s, slot, ruid, rgid, PMSG_FETCH_KV, iopm);
2721 if (status != ASL_STATUS_OK) return status;
2722 if (*iopm == NULL) return ASL_STATUS_FAILED;
2723 }
2724 }
2725 else return ASL_STATUS_OK;
2726 }
2727 else if (qtype == Q_SLOW)
2728 {
2729 if (*iomsg == NULL)
2730 {
2731 if (*iopm == NULL)
2732 {
2733 status = pmsg_fetch(s, slot, ruid, rgid, PMSG_FETCH_ALL, iopm);
2734 if (status != ASL_STATUS_OK) return status;
2735 if (*iopm == NULL) return ASL_STATUS_FAILED;
2736 }
2737
2738 status = msg_decode(s, *iopm, iomsg);
2739 if (status == ASL_STATUS_INVALID_MESSAGE) return ASL_STATUS_OK;
2740 if (status != ASL_STATUS_OK) return status;
2741 if (*iomsg == NULL) return ASL_STATUS_FAILED;
2742 }
2743
2744 status = 0;
2745 if (asl_msg_cmp(q, *iomsg) != 0) status = 1;
2746 if (status == 0) return ASL_STATUS_OK;
2747 }
2748
2749 *didmatch = 1;
2750
2751 if (res == NULL) return ASL_STATUS_OK;
2752
2753 if (*iomsg == NULL)
2754 {
2755 status = msg_decode(s, *iopm, iomsg);
2756 if (status == ASL_STATUS_INVALID_MESSAGE)
2757 {
2758 *didmatch = 0;
2759 return ASL_STATUS_OK;
2760 }
2761
2762 if (status != ASL_STATUS_OK) return status;
2763 }
2764
2765 if ((*res)->count == 0) (*res)->msg = (asl_msg_t **)calloc(1, sizeof(asl_msg_t *));
2766 else (*res)->msg = (asl_msg_t **)reallocf((*res)->msg, (1 + (*res)->count) * sizeof(asl_msg_t *));
2767 if ((*res)->msg == NULL) return ASL_STATUS_NO_MEMORY;
2768
2769 (*res)->msg[(*res)->count++] = *iomsg;
2770
2771 return ASL_STATUS_OK;
2772}
2773
2774static uint32_t
2775next_search_slot(asl_store_t *s, uint32_t last_si, int32_t direction)
2776{
2777 uint32_t i;
2778
2779 if (direction >= 0)
2780 {
2781 for (i = last_si + 1; i < s->slotlist_count; i++)
2782 {
2783 if (s->slotlist[i].type == DB_TYPE_MESSAGE) return i;
2784 }
2785
2786 return ASL_INDEX_NULL;
2787 }
2788
2789 if (last_si == 0) return ASL_INDEX_NULL;
2790 if (last_si > s->slotlist_count) return ASL_INDEX_NULL;
2791
2792 for (i = last_si - 1; i > 0; i--)
2793 {
2794 if (s->slotlist[i].type == DB_TYPE_MESSAGE) return i;
2795 }
2796
2797 if (s->slotlist[0].type == DB_TYPE_MESSAGE) return 0;
2798
2799 return ASL_INDEX_NULL;
2800}
2801
2802static uint32_t
2803query_list_to_pmsg_list(asl_store_t *s, asl_msg_list_t *query, uint32_t *match, pmsg_t ***qp, uint32_t **qtype, uint32_t *count)
2804{
2805 pmsg_t **outp, *pm;
2806 uint32_t i, j, *outt;
2807 *match = 0;
2808 *qp = NULL;
2809 *qtype = 0;
2810 *count = 0;
2811
2812 if (query == NULL) return ASL_STATUS_OK;
2813 if (match == NULL) return ASL_STATUS_INVALID_ARG;
2814 if (qp == NULL) return ASL_STATUS_INVALID_ARG;
2815 if (qtype == NULL) return ASL_STATUS_INVALID_ARG;
2816 if (query->msg == NULL) return ASL_STATUS_INVALID_ARG;
2817 if (query->count == 0) return ASL_STATUS_OK;
2818
2819 outp = (pmsg_t **)calloc(query->count, sizeof(pmsg_t *));
2820 if (outp == NULL) return ASL_STATUS_NO_MEMORY;
2821
2822 outt = (uint32_t *)calloc(query->count, sizeof(uint32_t));
2823 if (outt == NULL)
2824 {
2825 free(outp);
2826 return ASL_STATUS_NO_MEMORY;
2827 }
2828
2829 *match = 1;
2830
2831 for (i = 0; i < query->count; i++)
2832 {
2833 pm = NULL;
2834 outt[i] = query_to_pmsg(s, query->msg[i], &pm);
2835 if (outt[i] <= ASL_STATUS_FAILED)
2836 {
2837 if (pm != NULL) free_pmsg(pm);
2838 for (j = 0; j < i; j++) free_pmsg(outp[j]);
2839 free(outp);
2840 free(outt);
2841 return ASL_STATUS_NO_MEMORY;
2842 }
2843
2844 outp[i] = pm;
2845 }
2846
2847 *count = query->count;
2848 *qp = outp;
2849 *qtype = outt;
2850 return ASL_STATUS_OK;
2851}
2852
2853static void
2854match_worker_cleanup(pmsg_t **ql, uint32_t *qt, uint32_t n, asl_msg_list_t **res)
2855{
2856 uint32_t i;
2857
2858 if (ql != NULL)
2859 {
2860 for (i = 0; i < n; i++) free_pmsg(ql[i]);
2861 free(ql);
2862 }
2863
2864 if (qt != NULL) free(qt);
2865
2866 if (res != NULL)
2867 {
2868 for (i = 0; i < (*res)->count; i++) asl_free((*res)->msg[i]);
2869 free(*res);
2870 }
2871}
2872
2873 /*
2874 * Input to asl_store_match is a list of queries.
2875 * A record in the store matches if it matches any query (i.e. query list is "OR"ed)
2876 *
2877 * If counting up (direction is positive) find first record with ID > start_id.
2878 * Else if counting down (direction is negative) find first record with ID < start_id.
2879 *
2880 * Set match flag on.
2881 * If any query is NULL, set match flog off (skips matching below).
2882 * Else if all queries only check "standard" keys, set std flag to on.
2883 *
2884 * If a query only tests equality, convert it to a pmsg_t. The conversion routine
2885 * checks for string values that are NOT in the database. If a string is not found,
2886 * the conversion fails and the query is markes as "never matches". Otherwise,
2887 * the query is marked "fast".
2888 *
2889 * If all queries are marked as "never matches", return NULL.
2890 *
2891 * match loop:
2892 * fetch record (with std flag)
2893 * if match flag is off, decode record and add it to result.
2894 * else for each query:
2895 * if query is NULL (shouldn't happen) decode record and add it to result. Return to match loop.
2896 * else if query never matches, ignore it.
2897 * else if query is fast, use pmsg_match. If it succeeds, decode record and add it to result. Return to match loop.
2898 * else decode record and use asl_cmp. If it succeeds, add record to result. Return to match loop.
2899 *
2900 * return results.
2901 */
2902static uint32_t
2903match_worker(asl_store_t *s, asl_msg_list_t *query, asl_msg_list_t **res, uint64_t *last_id, uint64_t **idlist, uint32_t *idcount, uint64_t start_id, int32_t count, int32_t direction, int32_t ruid, int32_t rgid)
2904{
2905 uint32_t mx, si, slot, i, qcount, match, didmatch, status, *qtype;
2906 uint64_t xid;
2907 pmsg_t **qp, *iopmsg;
2908 asl_msg_t *iomsg;
2909
2910 if (s == NULL) return ASL_STATUS_INVALID_STORE;
2911 if ((res == NULL) && (idlist == NULL)) return ASL_STATUS_INVALID_ARG;
2912 if (last_id == NULL) return ASL_STATUS_INVALID_ARG;
2913 if (idcount == NULL) return ASL_STATUS_INVALID_ARG;
2914
2915 if (res != NULL) *res = NULL;
2916 if (idlist != NULL) *idlist = NULL;
2917
2918 mx = 0;
2919
2920 if (direction < 0) direction = -1;
2921 else direction = 1;
2922
2923 si = ASL_INDEX_NULL;
2924 if ((direction == -1) && (start_id == ASL_REF_NULL)) si = s->slotlist_count;
2925 else si = slotlist_find(s, start_id, 0, direction);
2926
2927 si = next_search_slot(s, si, direction);
2928 if (si == ASL_INDEX_NULL) return ASL_STATUS_OK;
2929 if (si >= s->slotlist_count) return ASL_STATUS_FAILED;
2930
2931 slot = s->slotlist[si].slot;
2932
2933 status = query_list_to_pmsg_list(s, query, &match, &qp, &qtype, &qcount);
2934 if (status != ASL_STATUS_OK) return status;
2935
2936 /*
2937 * initialize result list if we've been asked to return messages
2938 */
2939 if (res != NULL)
2940 {
2941 *res = (asl_msg_list_t *)calloc(1, sizeof(asl_msg_list_t));
2942 if (*res == NULL)
2943 {
2944 match_worker_cleanup(qp, qtype, qcount, NULL);
2945 return ASL_STATUS_NO_MEMORY;
2946 }
2947 }
2948
2949 /*
2950 * loop through records
2951 */
2952 *idcount = 0;
2953 while ((count == 0) || (*idcount < count))
2954 {
2955 if (si == ASL_INDEX_NULL) break;
2956 if (si >= s->slotlist_count) break;
2957
2958 slot = s->slotlist[si].slot;
2959 xid = s->slotlist[si].xid;
2960
2961 *last_id = xid;
2962
2963 iopmsg = NULL;
2964 iomsg = NULL;
2965
2966 didmatch = 0;
2967 if (match == 0)
2968 {
2969 status = msg_match(s, Q_NULL, NULL, NULL, ruid, rgid, slot, &iopmsg, &iomsg, res, &didmatch);
2970 free_pmsg(iopmsg);
2971 if (didmatch == 0)
2972 {
2973 asl_free(iomsg);
2974 iomsg = NULL;
2975 }
2976 else
2977 {
2978 if (idlist != NULL)
2979 {
2980 if (*idlist == NULL) *idlist = (uint64_t *)calloc(1, sizeof(uint64_t));
2981 else *idlist = (uint64_t *)reallocf(*idlist, (*idcount + 1) * sizeof(uint64_t));
2982 if (*idlist == NULL) status = ASL_STATUS_NO_MEMORY;
2983 else (*idlist)[*idcount] = xid;
2984 }
2985
2986 (*idcount)++;
2987 }
2988
2989 if (status == ASL_STATUS_ACCESS_DENIED)
2990 {
2991 si = next_search_slot(s, si, direction);
2992 continue;
2993 }
2994 else if (status != ASL_STATUS_OK)
2995 {
2996 match_worker_cleanup(qp, qtype, qcount, res);
2997 return status;
2998 }
2999 }
3000 else
3001 {
3002 for (i = 0; i < qcount; i++)
3003 {
3004 status = msg_match(s, qtype[i], qp[i], query->msg[i], ruid, rgid, slot, &iopmsg, &iomsg, res, &didmatch);
3005 if (status == ASL_STATUS_ACCESS_DENIED) break;
3006 else if (status != ASL_STATUS_OK)
3007 {
3008 free_pmsg(iopmsg);
3009 asl_free(iomsg);
3010 match_worker_cleanup(qp, qtype, qcount, res);
3011 return status;
3012 }
3013
3014 if (didmatch == 1)
3015 {
3016 if (idlist != NULL)
3017 {
3018 if (*idlist == NULL) *idlist = (uint64_t *)calloc(1, sizeof(uint64_t));
3019 else *idlist = (uint64_t *)reallocf(*idlist, (*idcount + 1) * sizeof(uint64_t));
3020 if (*idlist == NULL)
3021 {
3022 match_worker_cleanup(qp, qtype, qcount, res);
3023 return ASL_STATUS_NO_MEMORY;
3024 }
3025
3026 (*idlist)[*idcount] = xid;
3027 }
3028
3029 (*idcount)++;
3030 break;
3031 }
3032 }
3033
3034 free_pmsg(iopmsg);
3035 if ((didmatch == 0) || (res == NULL)) asl_free(iomsg);
3036 }
3037
3038 si = next_search_slot(s, si, direction);
3039 }
3040
3041 match_worker_cleanup(qp, qtype, qcount, NULL);
3042 return status;
3043}
3044
3045uint32_t
3046asl_store_match(asl_store_t *s, asl_msg_list_t *query, asl_msg_list_t **res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction, int32_t ruid, int32_t rgid)
3047{
3048 uint32_t idcount;
3049
3050 idcount = 0;
3051 return match_worker(s, query, res, last_id, NULL, &idcount, start_id, count, direction, ruid, rgid);
3052}
3053
3054uint32_t
3055asl_store_prune(asl_store_t *s, asl_msg_list_t *prune)
3056{
3057 uint64_t *idlist, max_id;
3058 uint32_t status, i, idcount;
3059
3060 if (s == NULL) return ASL_STATUS_INVALID_STORE;
3061 if (s->flags & ASL_STORE_FLAG_READ_ONLY) return ASL_STATUS_READ_ONLY;
3062
3063 if (prune == NULL) return ASL_STATUS_OK;
3064
3065 idlist = NULL;
3066 idcount = 0;
3067 max_id = 0;
3068
3069 status = match_worker(s, prune, NULL, &max_id, &idlist, &idcount, 0, 0, 1, 0, 0);
3070 if (status != ASL_STATUS_OK)
3071 {
3072 if (idlist != NULL) free(idlist);
3073 return status;
3074 }
3075
3076 for (i = 0; i < idcount; i++) message_release(s, idlist[i]);
3077 if (idlist != NULL) free(idlist);
3078
3079 return ASL_STATUS_OK;
3080}
3081
3082/*
3083 * Compact the database.
3084 * Removes NULL and EMPTY records by copying records to the front of the file.
3085 */
3086uint32_t
3087asl_store_compact(asl_store_t *s)
3088{
3089 char tmp[DB_RECORD_LEN];
3090 int status;
3091 uint8_t t;
3092 uint32_t i, j, nrecords, next, slcount, old_slcount, *record_map;
3093 off_t offset;
3094 slot_info_t *old_slot_list;
3095 size_t vmrecord_map_len, vmslot_list_len;
3096 void *vmrecord_map, *vmslot_list;
3097
3098 if (s == NULL) return ASL_STATUS_INVALID_STORE;
3099 if (s->flags & ASL_STORE_FLAG_READ_ONLY) return ASL_STATUS_READ_ONLY;
3100
3101 status = fseek(s->db, DB_RECORD_LEN, SEEK_SET);
3102 if (status < 0) return ASL_STATUS_READ_FAILED;
3103
3104 /*
3105 * record map is a mapping from pre-compaction record number to post-compaction record number.
3106 * We allocate it in VM rather than on the malloc heap to keep from creating a lot of
3107 * empty pages.
3108 */
3109 nrecords = s->record_count;
3110
3111 vmrecord_map_len = nrecords * sizeof(uint32_t);
3112 vmrecord_map = mmap(0, vmrecord_map_len, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
3113
3114 record_map = (uint32_t *)vmrecord_map;
3115 if (record_map == NULL) return ASL_STATUS_NO_MEMORY;
3116
3117 record_map[0] = 0;
3118
3119 /* size of post-compaction slotlist */
3120 slcount = 0;
3121
3122 /* first pass: create the record map (N.B. starting at 1 skips the header) */
3123 for (i = 1, j = 1; i < nrecords; i++)
3124 {
3125 record_map[i] = 0;
3126
3127 status = fread(tmp, DB_RECORD_LEN, 1, s->db);
3128 if (status != 1)
3129 {
3130 munmap(vmrecord_map, vmrecord_map_len);
3131 return ASL_STATUS_READ_FAILED;
3132 }
3133
3134 t = tmp[0];
3135
3136 if (t == DB_TYPE_HEADER)
3137 {
3138 munmap(vmrecord_map, vmrecord_map_len);
3139 return ASL_STATUS_INVALID_STORE;
3140 }
3141
3142 /*
3143 * Only messages, kvlists, strings, and string continuations get copied.
3144 * Empty, null, and unrecognized record types (i.e. corruption in the database)
3145 * are skipped. This compresses out gaps and deletes bad records.
3146 */
3147 if ((t != DB_TYPE_MESSAGE) && (t != DB_TYPE_KVLIST) && (t != DB_TYPE_STRING) && (t != DB_TYPE_STRCONT)) continue;
3148
3149 /* count to get size of new slotlist */
3150 if ((t == DB_TYPE_STRING) || (t == DB_TYPE_MESSAGE)) slcount++;
3151
3152 record_map[i] = j++;
3153 }
3154
3155 /* second pass: copy records and fix "next" indexes */
3156 for (i = 1; i < nrecords; i++)
3157 {
3158 offset = i * DB_RECORD_LEN;
3159 status = fseek(s->db, offset, SEEK_SET);
3160 if (status < 0)
3161 {
3162 munmap(vmrecord_map, vmrecord_map_len);
3163 return ASL_STATUS_READ_FAILED;
3164 }
3165
3166 status = fread(tmp, DB_RECORD_LEN, 1, s->db);
3167 if (status != 1)
3168 {
3169 munmap(vmrecord_map, vmrecord_map_len);
3170 return ASL_STATUS_READ_FAILED;
3171 }
3172
3173 t = tmp[0];
3174
3175 /* only copy messages, kvlists, strings, and string continuations */
3176 if ((t != DB_TYPE_MESSAGE) && (t != DB_TYPE_KVLIST) && (t != DB_TYPE_STRING) && (t != DB_TYPE_STRCONT)) continue;
3177
3178 next = _asl_get_32(tmp + 1);
3179
3180 if (next > nrecords) next = 0;
3181 else next = record_map[next];
3182
3183 _asl_put_32(next, tmp + 1);
3184
3185 offset = record_map[i] * DB_RECORD_LEN;
3186 status = fseek(s->db, offset, SEEK_SET);
3187 if (status < 0)
3188 {
3189 munmap(vmrecord_map, vmrecord_map_len);
3190 return ASL_STATUS_READ_FAILED;
3191 }
3192
3193 status = fwrite(tmp, DB_RECORD_LEN, 1, s->db);
3194 if (status != 1)
3195 {
3196 munmap(vmrecord_map, vmrecord_map_len);
3197 return ASL_STATUS_WRITE_FAILED;
3198 }
3199 }
3200
3201 /* truncate file */
3202 s->record_count = j;
3203 offset = s->record_count * DB_RECORD_LEN;
3204
3205 status = fseek(s->db, 0, SEEK_SET);
3206 if (status < 0)
3207 {
3208 munmap(vmrecord_map, vmrecord_map_len);
3209 return ASL_STATUS_READ_FAILED;
3210 }
3211
3212 status = ftruncate(fileno(s->db), offset);
3213 if (status != 0)
3214 {
3215 munmap(vmrecord_map, vmrecord_map_len);
3216 return ASL_STATUS_WRITE_FAILED;
3217 }
3218
3219 /*
3220 * build new slotlist
3221 *
3222 * We start by allocating and copying the old slotlist into VM.
3223 * Then we realloc the old slotlist to become the new slotlist.
3224 * Then we build the new slotlist from the values in VM.
3225 * Finally we deallocate the VM.
3226 * This is done so that we don't create a large malloc heap.
3227 */
3228
3229 vmslot_list_len = s->slotlist_count * sizeof(slot_info_t);
3230
3231 vmslot_list = mmap(0, vmslot_list_len, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
3232 old_slot_list = (slot_info_t *)vmslot_list;
3233 if (old_slot_list == NULL)
3234 {
3235 munmap(vmrecord_map, vmrecord_map_len);
3236 return ASL_STATUS_NO_MEMORY;
3237 }
3238
3239 old_slcount = s->slotlist_count;
3240
3241 /* copy old values to VM */
3242 for (i = 0; i < s->slotlist_count; i++)
3243 {
3244 old_slot_list[i].type = s->slotlist[i].type;
3245 old_slot_list[i].slot = s->slotlist[i].slot;
3246 old_slot_list[i].xid = s->slotlist[i].xid;
3247 old_slot_list[i].hash = s->slotlist[i].hash;
3248 }
3249
3250 s->slotlist = (slot_info_t *)reallocf(s->slotlist, slcount * sizeof(slot_info_t));
3251 if (s->slotlist == NULL)
3252 {
3253 munmap(vmrecord_map, vmrecord_map_len);
3254 munmap(vmslot_list, vmslot_list_len);
3255 return ASL_STATUS_NO_MEMORY;
3256 }
3257
3258 s->slotlist_count = slcount;
3259
3260 /* create the new compacted slotlist */
3261 for (i = 0, j = 0; i < old_slcount; i++)
3262 {
3263 t = old_slot_list[i].type;
3264 if ((t == DB_TYPE_STRING) || (t == DB_TYPE_MESSAGE))
3265 {
3266 s->slotlist[j].type = t;
3267 s->slotlist[j].slot = record_map[old_slot_list[i].slot];
3268 s->slotlist[j].xid = old_slot_list[i].xid;
3269 s->slotlist[j].hash = old_slot_list[i].hash;
3270 j++;
3271 }
3272 }
3273
3274 munmap(vmslot_list, vmslot_list_len);
3275
3276 s->empty_count = 0;
3277
3278 /* fix string cache index (which indexes into slotlist) */
3279 for (i = 0; i < STRING_CACHE_SIZE; i++)
3280 {
3281 if (s->string_cache[i].index == ASL_INDEX_NULL) continue;
3282 s->string_cache[i].index = record_map[s->string_cache[i].index];
3283 }
3284
3285 /* new xid=0 count */
3286 for (s->slot_zero_count = 0; (s->slot_zero_count < s->slotlist_count) && (s->slotlist[s->slot_zero_count].xid == 0); s->slot_zero_count++);
3287
3288 munmap(vmrecord_map, vmrecord_map_len);
3289
3290 return ASL_STATUS_OK;
3291}
3292
3293static uint32_t
3294write_to_archive(asl_store_t *s, asl_store_t *a, uint64_t msgid)
3295{
3296 uint32_t status;
3297 uint64_t xid;
3298 aslmsg msg;
3299
3300 if (s == NULL) return ASL_STATUS_INVALID_STORE;
3301 if (a == NULL) return ASL_STATUS_OK;
3302
3303 status = asl_store_fetch(s, msgid, 0, 0, &msg);
3304 if (status != ASL_STATUS_OK) return status;
3305
3306 status = asl_store_save(a, msg, -1, -1, &xid);
3307 asl_free(msg);
3308 return status;
3309}
3310
3311static uint64_t
3312oldest_id(asl_store_t *s)
3313{
3314 uint32_t si;
3315
3316 if (s == NULL) return ASL_STATUS_INVALID_STORE;
3317
3318 si = next_search_slot(s, ASL_INDEX_NULL, 1);
3319 if (si == ASL_INDEX_NULL) return ASL_REF_NULL;
3320
3321 return s->slotlist[si].xid;
3322}
3323
3324/*
3325 * Archive/remove oldest messages to make the database <= max_size
3326 * This is slow - messages are removed one at a time.
3327 */
3328uint32_t
3329asl_store_truncate(asl_store_t *s, uint64_t max_size, const char *archive)
3330{
3331 uint32_t max_slots, curr_used;
3332 uint32_t status;
3333 uint64_t old;
3334 asl_store_t *a;
3335
3336 if (s == NULL) return ASL_STATUS_INVALID_STORE;
3337 if (s->flags & ASL_STORE_FLAG_READ_ONLY) return ASL_STATUS_READ_ONLY;
3338
3339 if (max_size == 0) return ASL_STATUS_OK;
3340
3341 a = NULL;
3342
3343 max_slots = (max_size + DB_RECORD_LEN - 1) / DB_RECORD_LEN;
3344 curr_used = s->record_count - s->empty_count;
3345
3346 if ((curr_used > max_slots) && (archive != NULL))
3347 {
3348 status = asl_store_open(archive, 0, &a);
3349 if (status != ASL_STATUS_OK) return status;
3350 }
3351
3352 while (curr_used > max_slots)
3353 {
3354 old = oldest_id(s);
3355 if (old == ASL_REF_NULL) return ASL_STATUS_FAILED;
3356
3357 if (archive != NULL)
3358 {
3359 status = write_to_archive(s, a, old);
3360 if (status != ASL_STATUS_OK) return status;
3361 }
3362
3363 status = message_release(s, old);
3364 if (status != ASL_STATUS_OK) return status;
3365
3366 curr_used = s->record_count - s->empty_count;
3367 }
3368
3369 if (archive != NULL) asl_store_close(a);
3370
3371 status = asl_store_compact(s);
3372 return status;
3373}
3374
3375static uint32_t
3376archive_time_worker(asl_store_t *s, uint64_t cut_time, uint64_t **idlist, uint16_t **flags, uint32_t *idcount)
3377{
3378 uint32_t si, slot, status, check_sort;
3379 uint64_t xid, t, lastt;
3380 uint16_t rflags;
3381 char tmp[DB_RECORD_LEN];
3382 off_t offset;
3383
3384 if (s == NULL) return ASL_STATUS_INVALID_STORE;
3385 if (idlist == NULL) return ASL_STATUS_INVALID_ARG;
3386 if (flags == NULL) return ASL_STATUS_INVALID_ARG;
3387 if (idcount == NULL) return ASL_STATUS_INVALID_ARG;
3388
3389 *idlist = NULL;
3390 *flags = NULL;
3391 *idcount = 0;
3392 si = ASL_INDEX_NULL;
3393
3394 lastt = 0;
3395 check_sort = 1;
3396
3397 /*
3398 * loop through records
3399 *
3400 * Note that next_search_slot() traverses slotlist, which is sorted by id numder.
3401 * If the ASL_STORE_FLAG_TIME_SORTED flag is not set, we must search the whole database.
3402 * If the flag is set, timestamps will increase with xid, so we stop when we get
3403 * a message with time > cut_time.
3404 */
3405 forever
3406 {
3407 si = next_search_slot(s, si, 1);
3408 if (si == ASL_INDEX_NULL) break;
3409
3410 slot = s->slotlist[si].slot;
3411 xid = s->slotlist[si].xid;
3412
3413 offset = slot * DB_RECORD_LEN;
3414 status = fseek(s->db, offset, SEEK_SET);
3415
3416 if (status < 0) return ASL_STATUS_READ_FAILED;
3417
3418 status = fread(tmp, DB_RECORD_LEN, 1, s->db);
3419 if (status != 1) return ASL_STATUS_READ_FAILED;
3420
3421 t = _asl_get_64(tmp + MSG_OFF_KEY_TIME);
3422
3423 if (lastt > t) check_sort = 0;
3424
3425 if (t > cut_time)
3426 {
3427 if (s->flags & ASL_STORE_FLAG_TIME_SORTED) return ASL_STATUS_OK;
3428 continue;
3429 }
3430
3431 rflags = _asl_get_16(tmp + MSG_OFF_KEY_FLAGS);
3432
3433 if (*idlist == NULL)
3434 {
3435 *idlist = (uint64_t *)calloc(1, sizeof(uint64_t));
3436 *flags = (uint16_t *)calloc(1, sizeof(uint16_t));
3437 }
3438 else
3439 {
3440 *idlist = (uint64_t *)reallocf(*idlist, (*idcount + 1) * sizeof(uint64_t));
3441 *flags = (uint16_t *)reallocf(*flags, (*idcount + 1) * sizeof(uint16_t));
3442 }
3443
3444 if (*idlist == NULL)
3445 {
3446 if (*flags != NULL) free(*flags);
3447 *flags = NULL;
3448 return ASL_STATUS_NO_MEMORY;
3449 }
3450
3451 if (*flags == NULL)
3452 {
3453 if (*idlist != NULL) free(*idlist);
3454 *idlist = NULL;
3455 return ASL_STATUS_NO_MEMORY;
3456 }
3457
3458 (*idlist)[*idcount] = xid;
3459 (*flags)[*idcount] = rflags;
3460 (*idcount)++;
3461 }
3462
3463 /* if timestamps increase with xid, set the flag to improve subsequent search performance */
3464 if (check_sort == 1) s->flags |= ASL_STORE_FLAG_TIME_SORTED;
3465
3466 return ASL_STATUS_OK;
3467}
3468
3469static uint32_t
3470archive_time_inverse(asl_store_t *s, uint64_t cut_time, uint64_t **idlist, uint32_t *idcount)
3471{
3472 uint32_t si, slot, status;
3473 uint64_t xid, t, lastt;
3474 char tmp[DB_RECORD_LEN];
3475 off_t offset;
3476
3477 if (s == NULL) return ASL_STATUS_INVALID_STORE;
3478 if (idlist == NULL) return ASL_STATUS_INVALID_ARG;
3479 if (idcount == NULL) return ASL_STATUS_INVALID_ARG;
3480
3481 *idlist = NULL;
3482 *idcount = 0;
3483 si = ASL_INDEX_NULL;
3484
3485 lastt = 0;
3486
3487 /*
3488 * loop through records
3489 */
3490 forever
3491 {
3492 si = next_search_slot(s, si, 1);
3493 if (si == ASL_INDEX_NULL) break;
3494
3495 slot = s->slotlist[si].slot;
3496 xid = s->slotlist[si].xid;
3497
3498 offset = slot * DB_RECORD_LEN;
3499 status = fseek(s->db, offset, SEEK_SET);
3500
3501 if (status < 0) return ASL_STATUS_READ_FAILED;
3502
3503 status = fread(tmp, DB_RECORD_LEN, 1, s->db);
3504 if (status != 1) return ASL_STATUS_READ_FAILED;
3505
3506 t = _asl_get_64(tmp + MSG_OFF_KEY_TIME);
3507
3508 if (t <= cut_time) continue;
3509
3510 if (*idlist == NULL)
3511 {
3512 *idlist = (uint64_t *)calloc(1, sizeof(uint64_t));
3513 }
3514 else
3515 {
3516 *idlist = (uint64_t *)reallocf(*idlist, (*idcount + 1) * sizeof(uint64_t));
3517 }
3518
3519 if (*idlist == NULL) return ASL_STATUS_NO_MEMORY;
3520
3521
3522 (*idlist)[*idcount] = xid;
3523 (*idcount)++;
3524 }
3525
3526 return ASL_STATUS_OK;
3527}
3528
3529static uint32_t
3530archive_release(asl_store_t *s, uint64_t xid, uint64_t cut_time, uint64_t expire_ref)
3531{
3532 uint32_t i, slot, status;
3533 uint16_t rflags;
3534 pmsg_t *pmsg;
3535 uint64_t expire_time;
3536 char *str, tmp[DB_RECORD_LEN];
3537 off_t offset;
3538
3539 pmsg = NULL;
3540 slot = ASL_INDEX_NULL;
3541
3542 /* read message and release strings */
3543 status = pmsg_fetch_by_id(s, xid, 0, 0, &pmsg, &slot);
3544 if (status != ASL_STATUS_OK) return status;
3545 if (pmsg == NULL) return ASL_STATUS_READ_FAILED;
3546
3547 if (expire_ref != ASL_REF_NULL)
3548 {
3549 for (i = 0; i < pmsg->kvcount; i += 2)
3550 {
3551 if (pmsg->kvlist[i] == expire_ref)
3552 {
3553 str = NULL;
3554 status = string_fetch_sid(s, pmsg->kvlist[i + 1], &str);
3555 if (status != ASL_STATUS_OK) return status;
3556 if (str != NULL)
3557 {
3558 expire_time = 0;
3559 /* relative time not allowed - that would be cheating! */
3560 if (str[0] != '+') expire_time = asl_parse_time(str);
3561 free(str);
3562
3563 if (expire_time > cut_time)
3564 {
3565 /* expires in the future - mark as "do not archive" and don't release */
3566 free_pmsg(pmsg);
3567
3568 offset = slot * DB_RECORD_LEN;
3569 status = fseek(s->db, offset, SEEK_SET);
3570 if (status < 0) return ASL_STATUS_READ_FAILED;
3571
3572 status = fread(tmp, DB_RECORD_LEN, 1, s->db);
3573 if (status != 1) return ASL_STATUS_READ_FAILED;
3574
3575 rflags = _asl_get_16(tmp + MSG_OFF_KEY_FLAGS);
3576 if ((rflags & ASL_MSG_FLAG_DO_NOT_ARCHIVE) == 0)
3577 {
3578 rflags |= ASL_MSG_FLAG_DO_NOT_ARCHIVE;
3579 _asl_put_16(rflags, tmp + MSG_OFF_KEY_FLAGS);
3580
3581 status = fseek(s->db, offset, SEEK_SET);
3582 if (status < 0) return ASL_STATUS_WRITE_FAILED;
3583
3584 status = fwrite(tmp, DB_RECORD_LEN, 1, s->db);
3585 if (status != 1) return ASL_STATUS_WRITE_FAILED;
3586 }
3587
3588 return ASL_STATUS_OK;
3589 }
3590 }
3591 }
3592 }
3593 }
3594
3595 string_release(s, pmsg->host);
3596 string_release(s, pmsg->sender);
3597 string_release(s, pmsg->facility);
3598 string_release(s, pmsg->message);
3599 for (i = 0; i < pmsg->kvcount; i++) string_release(s, pmsg->kvlist[i]);
3600 free_pmsg(pmsg);
3601
3602 return id_release(s, xid, slot, DB_TYPE_MESSAGE);
3603}
3604
3605static char *
3606asl_store_mk_tmp_path()
3607{
3608 char tmp[PATH_MAX], *path;
3609
3610 if (confstr(_CS_DARWIN_USER_TEMP_DIR, tmp, sizeof(tmp)) <= 0) return NULL;
3611
3612 path = NULL;
3613 asprintf(&path, "%sasl.%d.tmp", tmp, getpid());
3614 return path;
3615}
3616
3617/*
3618 * Moves messages added at or before cut_time to an archive,
3619 * or delete them if archive_name is NULL.
3620 */
3621uint32_t
3622asl_store_archive(asl_store_t *s, uint64_t cut_time, const char *archive_name)
3623{
3624 asl_store_t *archive, *newstore;
3625 char *path, *newmapped;
3626 uint16_t *flags;
3627 uint32_t status, i, archive_count, save_count;
3628 uint64_t expire_ref, *archive_list, *save_list;
3629 size_t dbsize;
3630
3631 if (s == NULL) return ASL_STATUS_INVALID_STORE;
3632 if (s->flags & ASL_STORE_FLAG_READ_ONLY) return ASL_STATUS_READ_ONLY;
3633
3634 if (cut_time == 0) return ASL_STATUS_OK;
3635
3636 archive_count = 0;
3637 archive_list = NULL;
3638 save_count = 0;
3639 save_list = NULL;
3640 flags = NULL;
3641
3642 s->flags = 0;
3643
3644 status = archive_time_worker(s, cut_time, &archive_list, &flags, &archive_count);
3645 if (status != ASL_STATUS_OK) return status;
3646
3647 if ((archive_list == NULL) || (archive_count == 0))
3648 {
3649 if (archive_list != NULL) free(archive_list);
3650 return ASL_STATUS_OK;
3651 }
3652
3653 archive = NULL;
3654 if (archive_name != NULL)
3655 {
3656 status = asl_store_open(archive_name, 0, &archive);
3657 if (status != ASL_STATUS_OK) return status;
3658 }
3659
3660 if (archive != NULL)
3661 {
3662 for (i = 0; i < archive_count; i++)
3663 {
3664 if (flags[i] & ASL_MSG_FLAG_DO_NOT_ARCHIVE) continue;
3665
3666 status = write_to_archive(s, archive, archive_list[i]);
3667 if (status != ASL_STATUS_OK)
3668 {
3669 free(archive_list);
3670 asl_store_close(archive);
3671 return status;
3672 }
3673 }
3674
3675 asl_store_close(archive);
3676 }
3677
3678 /*
3679 * Deleting large numbers of records is slow.
3680 * If the number of records to be deleted is at least 1000, and
3681 * is ARCHIVE_DELETE_VS_COPY_PERCENT or above, we copy the records
3682 * that should remain to a temporary archive, then replace the
3683 * database with the temporary one.
3684 * Note that we need to know the name of the current DB file.
3685 */
3686 path = NULL;
3687 if ((archive_count >= 1000) && (((archive_count * 100) / s->message_count) >= ARCHIVE_DELETE_VS_COPY_PERCENT) && (s->db_path != NULL)) path = asl_store_mk_tmp_path();
3688 if (path != NULL)
3689 {
3690 status = unlink(path);
3691 if ((status != 0) && (errno != ENOENT))
3692 {
3693 free(path);
3694 path = NULL;
3695 }
3696 }
3697
3698 if (path != NULL)
3699 {
3700 if (archive_list != NULL) free(archive_list);
3701 archive_list = NULL;
3702 archive_count = 0;
3703
3704 newstore = NULL;
3705 status = asl_store_open(path, 0, &newstore);
3706 free(path);
3707 path = NULL;
3708 if (status != ASL_STATUS_OK) return status;
3709
3710 /* Set the next_id so that the archive will have ids bigger than the current archive. */
3711 newstore->next_id = s->next_id;
3712
3713 /* get a list of records that we want to keep */
3714 status = archive_time_inverse(s, cut_time, &save_list, &save_count);
3715 if (status != ASL_STATUS_OK)
3716 {
3717 asl_store_close(newstore);
3718 return status;
3719 }
3720
3721 if ((save_list == NULL) || (save_count == 0))
3722 {
3723 if (save_list != NULL) free(save_list);
3724 asl_store_close(newstore);
3725 return ASL_STATUS_OK;
3726 }
3727
3728 /* save to the temp archive */
3729 for (i = 0; i < save_count; i++)
3730 {
3731 status = write_to_archive(s, newstore, save_list[i]);
3732 if (status != ASL_STATUS_OK)
3733 {
3734 if (save_list != NULL) free(save_list);
3735 asl_store_close(newstore);
3736 return status;
3737 }
3738 }
3739
3740 free(save_list);
3741 save_list = NULL;
3742
3743 /* try rename since it's fast, but may fail (e.g. files are on different filesystems) */
3744 fclose(s->db);
3745 status = rename(newstore->db_path, s->db_path);
3746 if (status == 0)
3747 {
3748 /* success */
3749 s->db = fopen(s->db_path, "r+");
3750 if (s->db == NULL)
3751 {
3752 /* Disaster! Can't open the database! */
3753 asl_store_close(newstore);
3754 return ASL_STATUS_FAILED;
3755 }
3756 }
3757 else
3758 {
3759 /* rename failed, copy the data */
3760 s->db = fopen(s->db_path, "r+");
3761 if (s->db == NULL)
3762 {
3763 /* Disaster! Can't open the database! */
3764 asl_store_close(newstore);
3765 return ASL_STATUS_FAILED;
3766 }
3767
3768 dbsize = newstore->record_count * DB_RECORD_LEN;
3769 newmapped = mmap(0, dbsize, PROT_READ, MAP_PRIVATE, fileno(newstore->db), 0);
3770 if (newmapped == (void *)-1)
3771 {
3772 asl_store_close(newstore);
3773 return ASL_STATUS_FAILED;
3774 }
3775
3776 fseek(s->db, 0, SEEK_SET);
3777 status = ftruncate(fileno(s->db), 0);
3778 if (status != ASL_STATUS_OK)
3779 {
3780 asl_store_close(newstore);
3781 return status;
3782 }
3783
3784 status = fwrite(newmapped, dbsize, 1, s->db);
3785 munmap(newmapped, dbsize);
3786 if (status == 0)
3787 {
3788 asl_store_close(newstore);
3789 return ASL_STATUS_FAILED;
3790 }
3791 }
3792
3793 /* swap data in the store handles */
3794 if (s->slotlist != NULL) free(s->slotlist);
3795 s->slotlist = newstore->slotlist;
3796 newstore->slotlist = NULL;
3797
3798 for (i = 0; i < RECORD_CACHE_SIZE; i++)
3799 {
3800 free(s->rcache[i]);
3801 s->rcache[i] = newstore->rcache[i];
3802 newstore->rcache[i] = NULL;
3803 s->rcache_state[i] = newstore->rcache_state[i];
3804 }
3805
3806 for (i = 0; i < STRING_CACHE_SIZE; i++)
3807 {
3808 s->string_cache[i].index = newstore->string_cache[i].index;
3809 s->string_cache[i].refcount = newstore->string_cache[i].refcount;
3810 if (s->string_cache[i].str != NULL) free(s->string_cache[i].str);
3811 s->string_cache[i].str = newstore->string_cache[i].str;
3812 newstore->string_cache[i].str = NULL;
3813 }
3814
3815 s->flags = newstore->flags;
3816 s->record_count = newstore->record_count;
3817 s->message_count = newstore->message_count;
3818 s->string_count = newstore->string_count;
3819 s->empty_count = newstore->empty_count;
3820 s->next_id = newstore->next_id;
3821 s->max_time = newstore->max_time;
3822 s->slotlist_count = newstore->slotlist_count;
3823 s->slot_zero_count = newstore->slot_zero_count;
3824
3825 fclose(newstore->db);
3826 unlink(newstore->db_path);
3827 free(newstore->db_path);
3828 free(newstore);
3829
3830 return ASL_STATUS_OK;
3831 }
3832
3833 expire_ref = string_retain(s, ASL_KEY_EXPIRE_TIME, 0);
3834
3835 /*
3836 * This flag turns off most of the code in slotlist_make_empty.
3837 * We get much better performace while we delete records,
3838 * but the slotlist has to be repaired and re-sorted afterwards.
3839 */
3840 s->flags |= ASL_STORE_FLAG_DEFER_SORT;
3841
3842 for (i = 0; i < archive_count; i++)
3843 {
3844 status = archive_release(s, archive_list[i], cut_time, expire_ref);
3845 if (status != ASL_STATUS_OK) return status;
3846 }
3847
3848 s->flags &= ~ASL_STORE_FLAG_DEFER_SORT;
3849
3850 free(archive_list);
3851 archive_list = NULL;
3852 archive_count = 0;
3853
3854 free(flags);
3855 flags = NULL;
3856
3857 /* zero xids for slots that became empty during archive release */
3858 for (i = 0; i < s->slotlist_count; i++)
3859 {
3860 if (s->slotlist[i].type == DB_TYPE_EMPTY) s->slotlist[i].xid = 0;
3861 }
3862
3863 /* re-sort and determine the zero count */
3864 qsort((void *)s->slotlist, s->slotlist_count, sizeof(slot_info_t), slot_comp);
3865
3866 /* new xid=0 count */
3867 for (s->slot_zero_count = 0; (s->slot_zero_count < s->slotlist_count) && (s->slotlist[s->slot_zero_count].xid == 0); s->slot_zero_count++);
3868
3869 return ASL_STATUS_OK;
3870}
3871
3872const char *
3873asl_store_error(uint32_t code)
3874{
3875 switch (code)
3876 {
3877 case ASL_STATUS_OK: return "Operation Succeeded";
3878 case ASL_STATUS_INVALID_ARG: return "Invalid Argument";
3879 case ASL_STATUS_INVALID_STORE: return "Invalid Data Store";
3880 case ASL_STATUS_INVALID_STRING: return "Invalid String";
3881 case ASL_STATUS_INVALID_ID: return "Invalid ID Number";
3882 case ASL_STATUS_INVALID_MESSAGE: return "Invalid Message";
3883 case ASL_STATUS_NOT_FOUND: return "Not Found";
3884 case ASL_STATUS_READ_FAILED: return "Read Operation Failed";
3885 case ASL_STATUS_WRITE_FAILED: return "Write Operation Failed";
3886 case ASL_STATUS_NO_MEMORY: return "System Memory Allocation Failed";
3887 case ASL_STATUS_ACCESS_DENIED: return "Access Denied";
3888 case ASL_STATUS_READ_ONLY: return "Read Only Access";
3889 }
3890
3891 return "Operation Failed";
3892}