]>
Commit | Line | Data |
---|---|---|
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 | ||
64 | extern time_t asl_parse_time(const char *str); | |
65 | extern 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 | ||
92 | typedef 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 | ||
113 | static 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 | ||
135 | static 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 | ||
157 | static 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 | ||
166 | static 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 | ||
175 | static 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 | ||
184 | static 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 | ||
193 | static 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 | ||
202 | static 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 | */ | |
228 | static int | |
229 | slot_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 */ | |
257 | static uint32_t | |
258 | slotlist_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 */ | |
287 | static uint32_t | |
288 | slotlist_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 | ||
321 | static uint32_t | |
322 | slotlist_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 | ||
399 | static uint32_t | |
400 | slotlist_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 | ||
439 | static uint32_t | |
440 | slotlist_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 | ||
496 | static uint32_t | |
497 | slotlist_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 | ||
571 | uint32_t | |
572 | asl_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 | ||
675 | uint32_t | |
676 | asl_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 | ||
696 | static char * | |
697 | record_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 | ||
716 | static void | |
717 | record_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 | */ | |
740 | static uint32_t | |
741 | get_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 | ||
763 | static void | |
764 | record_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 | ||
774 | static uint64_t | |
775 | new_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 | */ | |
798 | static uint32_t | |
799 | save_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 | */ | |
890 | static uint32_t | |
891 | string_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 | */ | |
979 | uint32_t | |
980 | string_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 | */ | |
1030 | static void | |
1031 | string_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 | ||
1082 | static uint32_t | |
1083 | string_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 | ||
1163 | static uint32_t | |
1164 | string_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 | ||
1201 | static uint32_t | |
1202 | check_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 | ||
1213 | static uint32_t | |
1214 | check_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 | ||
1236 | static uint32_t | |
1237 | check_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 | ||
1261 | static uint32_t | |
1262 | pmsg_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 | ||
1417 | static uint32_t | |
1418 | pmsg_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 | ||
1509 | static void | |
1510 | free_pmsg(pmsg_t *p) | |
1511 | { | |
1512 | if (p == NULL) return; | |
1513 | if (p->kvlist != NULL) free(p->kvlist); | |
1514 | free(p); | |
1515 | } | |
1516 | ||
1517 | static uint32_t | |
1518 | pmsg_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 | ||
1542 | static uint32_t | |
1543 | msg_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 | */ | |
1842 | static uint32_t | |
1843 | store_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 | */ | |
1908 | static uint64_t | |
1909 | string_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 | ||
1968 | static uint32_t | |
1969 | record_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 | */ | |
2028 | static uint32_t | |
2029 | id_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 | ||
2078 | static uint32_t | |
2079 | string_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 | ||
2089 | static uint32_t | |
2090 | message_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 | */ | |
2123 | static uint32_t | |
2124 | message_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 | ||
2357 | uint32_t | |
2358 | asl_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 | ||
2384 | uint32_t | |
2385 | asl_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 | ||
2396 | uint32_t | |
2397 | asl_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 | ||
2418 | static uint32_t | |
2419 | query_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 | ||
2683 | uint32_t | |
2684 | msg_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 | ||
2774 | static uint32_t | |
2775 | next_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 | ||
2802 | static uint32_t | |
2803 | query_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 | ||
2853 | static void | |
2854 | match_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 | */ | |
2902 | static uint32_t | |
2903 | match_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 | ||
3045 | uint32_t | |
3046 | asl_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 | ||
3054 | uint32_t | |
3055 | asl_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 | */ | |
3086 | uint32_t | |
3087 | asl_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 | ||
3293 | static uint32_t | |
3294 | write_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 | ||
3311 | static uint64_t | |
3312 | oldest_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 | */ | |
3328 | uint32_t | |
3329 | asl_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 | ||
3375 | static uint32_t | |
3376 | archive_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 | ||
3469 | static uint32_t | |
3470 | archive_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 | ||
3529 | static uint32_t | |
3530 | archive_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 | ||
3605 | static char * | |
3606 | asl_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 | */ | |
3621 | uint32_t | |
3622 | asl_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 | ||
3872 | const char * | |
3873 | asl_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 | } |