]> git.saurik.com Git - apple/libc.git/blob - gen/asl_store.c
Libc-763.12.tar.gz
[apple/libc.git] / gen / asl_store.c
1 /*
2 * Copyright (c) 2007-2010 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <dirent.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #include <asl.h>
33 #include <asl_private.h>
34 #include <asl_core.h>
35 #include <asl_store.h>
36 #include <notify.h>
37
38 extern time_t asl_parse_time(const char *str);
39 extern uint64_t asl_file_cursor(asl_file_t *s);
40 extern uint32_t asl_file_match_start(asl_file_t *s, uint64_t start_id, int32_t direction);
41 extern uint32_t asl_file_match_next(asl_file_t *s, aslresponse query, asl_msg_t **msg, uint64_t *last_id, int32_t direction, int32_t ruid, int32_t rgid);
42 extern int asl_file_create(const char *path, uid_t uid, gid_t gid, mode_t mode);
43
44 #define SECONDS_PER_DAY 86400
45
46 /*
47 * The ASL Store is organized as a set of files in a common directory.
48 * Files are prefixed by the date (YYYY.MM.DD) of their contents.
49 *
50 * Messages with no access controls are saved in YYYY.MM.DD.asl
51 * Messages with access limited to UID uuu are saved in YYYY.MM.DD.Uuuu.asl
52 * Messages with access limited to GID ggg are saved in YYYY.MM.DD.Gggg.asl
53 * Messages with access limited to UID uuu and GID ggg are saved in YYYY.MM.DD.Uuuu.Gggg.asl
54 *
55 * Messages that have a value for ASLExpireTime are saved in BB.YYYY.MM.DD.asl
56 * where the timestamp is the "Best Before" date of the file. Access controls
57 * are implemented as above with Uuuu and Gggg in the file name. Note that the
58 * Best Before files are for the last day of the month, so a single file contains
59 * messages that expire in that month.
60 *
61 * An external tool runs daily and deletes "old" files.
62 */
63
64 static time_t
65 _asl_start_today()
66 {
67 time_t now;
68 struct tm ctm;
69
70 memset(&ctm, 0, sizeof(struct tm));
71 now = time(NULL);
72
73 if (localtime_r((const time_t *)&now, &ctm) == NULL) return 0;
74
75 ctm.tm_sec = 0;
76 ctm.tm_min = 0;
77 ctm.tm_hour = 0;
78
79 return mktime(&ctm);
80 }
81
82 /*
83 * The base directory contains a data file which stores
84 * the last record ID.
85 *
86 * | MAX_ID (uint64_t) |
87 *
88 */
89 uint32_t
90 asl_store_open_write(const char *basedir, asl_store_t **s)
91 {
92 asl_store_t *out;
93 struct stat sb;
94 uint32_t i, flags;
95 char *path;
96 FILE *sd;
97 uint64_t last_id;
98 time_t start;
99
100 if (s == NULL) return ASL_STATUS_INVALID_ARG;
101
102 start = _asl_start_today();
103 if (start == 0) return ASL_STATUS_FAILED;
104
105 if (basedir == NULL) basedir = PATH_ASL_STORE;
106
107 memset(&sb, 0, sizeof(struct stat));
108 if (stat(basedir, &sb) != 0) return ASL_STATUS_INVALID_STORE;
109 if (!S_ISDIR(sb.st_mode)) return ASL_STATUS_INVALID_STORE;
110
111 path = NULL;
112 asprintf(&path, "%s/%s", basedir, FILE_ASL_STORE_DATA);
113 if (path == NULL) return ASL_STATUS_NO_MEMORY;
114
115 sd = NULL;
116
117 memset(&sb, 0, sizeof(struct stat));
118 if (stat(path, &sb) != 0)
119 {
120 if (errno != ENOENT)
121 {
122 free(path);
123 return ASL_STATUS_FAILED;
124 }
125
126 sd = fopen(path, "w+");
127 free(path);
128
129 if (sd == NULL) return ASL_STATUS_FAILED;
130
131 last_id = 0;
132
133 /* Create new StoreData file (8 bytes ID + 4 bytes flags) */
134
135 if (fwrite(&last_id, sizeof(uint64_t), 1, sd) != 1)
136 {
137 fclose(sd);
138 return ASL_STATUS_WRITE_FAILED;
139 }
140
141 flags = 0;
142 if (fwrite(&flags, sizeof(uint32_t), 1, sd) != 1)
143 {
144 fclose(sd);
145 return ASL_STATUS_WRITE_FAILED;
146 }
147
148 /* flush data */
149 fflush(sd);
150 }
151 else
152 {
153 sd = fopen(path, "r+");
154 free(path);
155
156 if (sd == NULL) return ASL_STATUS_FAILED;
157 if (fread(&last_id, sizeof(uint64_t), 1, sd) != 1)
158 {
159 fclose(sd);
160 return ASL_STATUS_READ_FAILED;
161 }
162
163 last_id = asl_core_ntohq(last_id);
164 }
165
166 out = (asl_store_t *)calloc(1, sizeof(asl_store_t));
167 if (out == NULL)
168 {
169 fclose(sd);
170 return ASL_STATUS_NO_MEMORY;
171 }
172
173 if (basedir == NULL) out->base_dir = strdup(PATH_ASL_STORE);
174 else out->base_dir = strdup(basedir);
175
176 if (out->base_dir == NULL)
177 {
178 fclose(sd);
179 free(out);
180 return ASL_STATUS_NO_MEMORY;
181 }
182
183 out->start_today = start;
184 out->start_tomorrow = out->start_today + SECONDS_PER_DAY;
185 out->storedata = sd;
186 out->next_id = last_id + 1;
187
188 for (i = 0; i < FILE_CACHE_SIZE; i++)
189 {
190 memset(&out->file_cache[i], 0, sizeof(asl_cached_file_t));
191 out->file_cache[i].u = -1;
192 out->file_cache[i].g = -1;
193 }
194
195 *s = out;
196 return ASL_STATUS_OK;
197 }
198
199 uint32_t
200 asl_store_statistics(asl_store_t *s, aslmsg *msg)
201 {
202 aslmsg out;
203
204 if (s == NULL) return ASL_STATUS_INVALID_STORE;
205 if (msg == NULL) return ASL_STATUS_INVALID_ARG;
206
207 out = asl_new(ASL_TYPE_MSG);
208 if (out == NULL) return ASL_STATUS_NO_MEMORY;
209
210 /* does nothing for now */
211
212 *msg = out;
213 return ASL_STATUS_OK;
214 }
215
216 uint32_t
217 asl_store_open_read(const char *basedir, asl_store_t **s)
218 {
219 asl_store_t *out;
220 struct stat sb;
221
222 if (s == NULL) return ASL_STATUS_INVALID_ARG;
223
224 if (basedir == NULL) basedir = PATH_ASL_STORE;
225
226 memset(&sb, 0, sizeof(struct stat));
227 if (stat(basedir, &sb) != 0) return ASL_STATUS_INVALID_STORE;
228 if (!S_ISDIR(sb.st_mode)) return ASL_STATUS_INVALID_STORE;
229
230 out = (asl_store_t *)calloc(1, sizeof(asl_store_t));
231 if (out == NULL) return ASL_STATUS_NO_MEMORY;
232
233 if (basedir == NULL) out->base_dir = strdup(PATH_ASL_STORE);
234 else out->base_dir = strdup(basedir);
235
236 if (out->base_dir == NULL)
237 {
238 free(out);
239 return ASL_STATUS_NO_MEMORY;
240 }
241
242 *s = out;
243 return ASL_STATUS_OK;
244 }
245
246 uint32_t
247 asl_store_max_file_size(asl_store_t *s, size_t max)
248 {
249 if (s == NULL) return ASL_STATUS_INVALID_STORE;
250
251 s->max_file_size = max;
252 return ASL_STATUS_OK;
253 }
254
255 __private_extern__ void
256 asl_store_file_closeall(asl_store_t *s)
257 {
258 uint32_t i;
259
260 if (s == NULL) return;
261
262 for (i = 0; i < FILE_CACHE_SIZE; i++)
263 {
264 if (s->file_cache[i].f != NULL) asl_file_close(s->file_cache[i].f);
265 s->file_cache[i].f = NULL;
266 if (s->file_cache[i].path != NULL) free(s->file_cache[i].path);
267 s->file_cache[i].path = NULL;
268 s->file_cache[i].u = -1;
269 s->file_cache[i].g = -1;
270 s->file_cache[i].bb = 0;
271 s->file_cache[i].ts = 0;
272 }
273 }
274
275 uint32_t
276 asl_store_close(asl_store_t *s)
277 {
278 if (s == NULL) return ASL_STATUS_OK;
279
280 if (s->base_dir != NULL) free(s->base_dir);
281 s->base_dir = NULL;
282 asl_store_file_closeall(s);
283 if (s->storedata != NULL) fclose(s->storedata);
284
285 free(s);
286
287 return ASL_STATUS_OK;
288 }
289
290 uint32_t
291 asl_store_signal_sweep(asl_store_t *s)
292 {
293 char *str;
294 int semfd;
295 uint64_t xid;
296 uint32_t status;
297
298 if (s == NULL) return ASL_STATUS_INVALID_STORE;
299
300 asprintf(&str, "%s/%s", s->base_dir, FILE_ASL_STORE_SWEEP_SEMAPHORE);
301 if (str == NULL) return ASL_STATUS_NO_MEMORY;
302
303 semfd = open(str, O_WRONLY | O_CREAT | O_NONBLOCK, 0644);
304 free(str);
305
306 if (semfd < 0) return ASL_STATUS_WRITE_FAILED;
307
308 status = ASL_STATUS_OK;
309
310 /* write the current message ID in the SweepStore file */
311 xid = asl_core_htonq(s->next_id - 1);
312 if (write(semfd, &xid, sizeof(uint64_t)) != sizeof(uint64_t)) status = ASL_STATUS_WRITE_FAILED;
313
314 close(semfd);
315 return status;
316 }
317
318 /*
319 * Sweep the file cache.
320 * Close any files that have not been used in the last FILE_CACHE_TTL seconds.
321 * Returns least recently used or unused cache slot.
322 */
323 static uint32_t
324 asl_store_file_cache_lru(asl_store_t *s, time_t now, uint32_t ignorex)
325 {
326 time_t min;
327 uint32_t i, x;
328
329 if (s == NULL) return 0;
330
331 x = 0;
332 min = now - FILE_CACHE_TTL;
333
334 for (i = 0; i < FILE_CACHE_SIZE; i++)
335 {
336 if ((i != ignorex) && (s->file_cache[i].ts < min))
337 {
338 asl_file_close(s->file_cache[i].f);
339 s->file_cache[i].f = NULL;
340 if (s->file_cache[i].path != NULL) free(s->file_cache[i].path);
341 s->file_cache[i].path = NULL;
342 s->file_cache[i].u = -1;
343 s->file_cache[i].g = -1;
344 s->file_cache[i].bb = 0;
345 s->file_cache[i].ts = 0;
346 }
347
348 if (s->file_cache[i].ts < s->file_cache[x].ts) x = i;
349 }
350
351 return x;
352 }
353
354 uint32_t
355 asl_store_sweep_file_cache(asl_store_t *s)
356 {
357 if (s == NULL) return ASL_STATUS_INVALID_STORE;
358
359 asl_store_file_cache_lru(s, time(NULL), FILE_CACHE_SIZE);
360 return ASL_STATUS_OK;
361 }
362
363 static char *
364 asl_store_make_ug_path(const char *dir, const char *base, const char *ext, uid_t ruid, gid_t rgid, uid_t *u, gid_t *g, mode_t *m)
365 {
366 char *path = NULL;
367
368 *u = 0;
369 *g = 0;
370 *m = 0644;
371
372 if (ruid == -1)
373 {
374 if (rgid == -1)
375 {
376 if (ext == NULL) asprintf(&path, "%s/%s", dir, base);
377 else asprintf(&path, "%s/%s.%s", dir, base, ext);
378 }
379 else
380 {
381 *g = rgid;
382 *m = 0600;
383 if (ext == NULL) asprintf(&path, "%s/%s.G%d", dir, base, *g);
384 else asprintf(&path, "%s/%s.G%d.%s", dir, base, *g, ext);
385 }
386 }
387 else
388 {
389 *u = ruid;
390 if (rgid == -1)
391 {
392 *m = 0600;
393 if (ext == NULL) asprintf(&path, "%s/%s.U%d", dir, base, *u);
394 else asprintf(&path, "%s/%s.U%d.%s", dir, base, *u, ext);
395 }
396 else
397 {
398 *g = rgid;
399 *m = 0600;
400 if (ext == NULL) asprintf(&path, "%s/%s.U%d.G%d", dir, base, *u, *g);
401 else asprintf(&path, "%s/%s.U%d.G%u.%s", dir, base, *u, *g, ext);
402 }
403 }
404
405 return path;
406 }
407
408 static uint32_t
409 asl_store_file_open_write(asl_store_t *s, char *tstring, int32_t ruid, int32_t rgid, time_t bb, asl_file_t **f, time_t now, uint32_t check_cache)
410 {
411 char *path;
412 mode_t m;
413 int32_t i, x;
414 uid_t u;
415 gid_t g;
416 uint32_t status;
417 asl_file_t *out;
418
419 if (s == NULL) return ASL_STATUS_INVALID_STORE;
420
421 /* see if the file is already open and in the cache */
422 for (i = 0; i < FILE_CACHE_SIZE; i++)
423 {
424 if ((s->file_cache[i].u == ruid) && (s->file_cache[i].g == rgid) && (s->file_cache[i].bb == bb) && (s->file_cache[i].f != NULL))
425 {
426 s->file_cache[i].ts = now;
427 *f = s->file_cache[i].f;
428 if (check_cache == 1) asl_store_file_cache_lru(s, now, i);
429 return ASL_STATUS_OK;
430 }
431 }
432
433 u = 0;
434 g = 0;
435 m = 0644;
436 path = asl_store_make_ug_path(s->base_dir, tstring, "asl", (uid_t)ruid, (gid_t)rgid, &u, &g, &m);
437 if (path == NULL) return ASL_STATUS_NO_MEMORY;
438
439 out = NULL;
440 status = asl_file_open_write(path, m, u, g, &out);
441 if (status != ASL_STATUS_OK)
442 {
443 free(path);
444 return status;
445 }
446
447 x = asl_store_file_cache_lru(s, now, FILE_CACHE_SIZE);
448 if (s->file_cache[x].f != NULL) asl_file_close(s->file_cache[x].f);
449 if (s->file_cache[x].path != NULL) free(s->file_cache[x].path);
450
451 s->file_cache[x].f = out;
452 s->file_cache[x].path = path;
453 s->file_cache[x].u = ruid;
454 s->file_cache[x].g = rgid;
455 s->file_cache[x].bb = bb;
456 s->file_cache[x].ts = time(NULL);
457
458 *f = out;
459
460 return ASL_STATUS_OK;
461 }
462
463 __private_extern__ char *
464 asl_store_file_path(asl_store_t *s, asl_file_t *f)
465 {
466 uint32_t i;
467
468 if (s == NULL) return NULL;
469
470 for (i = 0; i < FILE_CACHE_SIZE; i++)
471 {
472 if (s->file_cache[i].f == f)
473 {
474 if (s->file_cache[i].path == NULL) return NULL;
475 return strdup(s->file_cache[i].path);
476 }
477 }
478
479 return NULL;
480 }
481
482 __private_extern__ void
483 asl_store_file_close(asl_store_t *s, asl_file_t *f)
484 {
485 uint32_t i;
486
487 if (s == NULL) return;
488 if (f == NULL) return;
489
490 for (i = 0; i < FILE_CACHE_SIZE; i++)
491 {
492 if (s->file_cache[i].f == f)
493 {
494 asl_file_close(s->file_cache[i].f);
495 s->file_cache[i].f = NULL;
496 if (s->file_cache[i].path != NULL) free(s->file_cache[i].path);
497 s->file_cache[i].path = NULL;
498 s->file_cache[i].u = -1;
499 s->file_cache[i].g = -1;
500 s->file_cache[i].bb = 0;
501 s->file_cache[i].ts = 0;
502 return;
503 }
504 }
505 }
506
507 uint32_t
508 asl_store_save(asl_store_t *s, aslmsg msg)
509 {
510 struct tm ctm;
511 time_t msg_time, now, bb;
512 char *path, *tmp_path, *tstring, *scratch;
513 const char *val;
514 uid_t ruid;
515 gid_t rgid;
516 asl_file_t *f;
517 uint32_t status, check_cache, signal_sweep, len;
518 uint64_t xid, ftime;
519 size_t fsize;
520
521 if (s == NULL) return ASL_STATUS_INVALID_STORE;
522 if (msg == NULL) return ASL_STATUS_INVALID_ARG;
523
524 now = time(NULL);
525
526 check_cache = 0;
527 if ((s->last_write + FILE_CACHE_TTL) <= now) check_cache = 1;
528
529 signal_sweep = 0;
530
531 msg_time = 0;
532 val = asl_get(msg, ASL_KEY_TIME);
533 if (val == NULL) msg_time = now;
534 else msg_time = asl_parse_time(val);
535
536 if (msg_time >= s->start_tomorrow)
537 {
538 if (now >= s->start_tomorrow)
539 {
540 /* new day begins */
541 check_cache = 0;
542 signal_sweep = 1;
543 asl_store_file_closeall(s);
544
545 /*
546 * _asl_start_today should never fail, but if it does,
547 * just push forward one day. That will probably be correct, and if
548 * it isn't, the next message that gets saved will push it ahead again
549 * until we get to the right date.
550 */
551 s->start_today = _asl_start_today();
552 if (s->start_today == 0) s->start_today = s->start_tomorrow;
553
554 s->start_tomorrow = s->start_today + SECONDS_PER_DAY;
555 }
556 }
557
558 val = asl_get(msg, ASL_KEY_READ_UID);
559 ruid = -1;
560 if (val != NULL) ruid = atoi(val);
561
562 val = asl_get(msg, ASL_KEY_READ_GID);
563 rgid = -1;
564 if (val != NULL) rgid = atoi(val);
565
566 bb = 0;
567 val = asl_get(msg, ASL_KEY_EXPIRE_TIME);
568 if (val != NULL)
569 {
570 bb = 1;
571 msg_time = asl_parse_time(val);
572 }
573
574 if (fseeko(s->storedata, 0, SEEK_SET) != 0) return ASL_STATUS_WRITE_FAILED;
575
576 xid = asl_core_htonq(s->next_id);
577 if (fwrite(&xid, sizeof(uint64_t), 1, s->storedata) != 1) return ASL_STATUS_WRITE_FAILED;
578
579 /* flush data */
580 fflush(s->storedata);
581
582 xid = s->next_id;
583 s->next_id++;
584
585 s->last_write = now;
586
587 if (localtime_r((const time_t *)&msg_time, &ctm) == NULL) return ASL_STATUS_FAILED;
588
589 tstring = NULL;
590 if (bb == 1)
591 {
592 /*
593 * This supports 12 monthly "Best Before" buckets.
594 * We advance the actual expiry time to day zero of the following month.
595 * mktime() is clever enough to know that you actually mean the last day
596 * of the previous month. What we get back from localtime is the last
597 * day of the month in which the message expires, which we use in the name.
598 */
599 ctm.tm_sec = 0;
600 ctm.tm_min = 0;
601 ctm.tm_hour = 0;
602 ctm.tm_mday = 0;
603 ctm.tm_mon += 1;
604
605 bb = mktime(&ctm);
606
607 if (localtime_r((const time_t *)&bb, &ctm) == NULL) return ASL_STATUS_FAILED;
608 asprintf(&tstring, "BB.%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday);
609 }
610 else
611 {
612 asprintf(&tstring, "%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday);
613 }
614
615 if (tstring == NULL) return ASL_STATUS_NO_MEMORY;
616
617 status = asl_store_file_open_write(s, tstring, ruid, rgid, bb, &f, now, check_cache);
618 free(tstring);
619 tstring = NULL;
620
621 if (status != ASL_STATUS_OK) return status;
622
623 status = asl_file_save(f, msg, &xid);
624 if (status != ASL_STATUS_OK) return status;
625
626 fsize = asl_file_size(f);
627 ftime = asl_file_ctime(f);
628
629 /* if file is larger than max_file_size, rename it and touch semaphore file in the store */
630 if ((s->max_file_size != 0) && (fsize > s->max_file_size))
631 {
632 signal_sweep = 1;
633 status = ASL_STATUS_OK;
634
635 path = asl_store_file_path(s, f);
636
637 asl_store_file_close(s, f);
638
639 if (path != NULL)
640 {
641 tmp_path = NULL;
642
643 len = strlen(path);
644 if ((len >= 4) && (!strcmp(path + len - 4, ".asl")))
645 {
646 /* rename xxxxxxx.asl to xxxxxxx.timestamp.asl */
647 scratch = strdup(path);
648 if (scratch != NULL)
649 {
650 scratch[len - 4] = '\0';
651 asprintf(&tmp_path, "%s.%llu.asl", scratch, ftime);
652 free(scratch);
653
654 }
655 }
656 else
657 {
658 /* append timestamp */
659 asprintf(&tmp_path, "%s.%llu", path, ftime);
660 }
661
662 if (tmp_path == NULL)
663 {
664 status = ASL_STATUS_NO_MEMORY;
665 }
666 else
667 {
668 if (rename(path, tmp_path) != 0) status = ASL_STATUS_FAILED;
669 free(tmp_path);
670 }
671
672 free(path);
673 }
674 }
675
676 if (signal_sweep != 0) asl_store_signal_sweep(s);
677
678 return status;
679 }
680
681 static uint32_t
682 asl_store_mkdir(asl_store_t *s, const char *dir, mode_t m)
683 {
684 char *tstring = NULL;
685 int status;
686 struct stat sb;
687
688 asprintf(&tstring, "%s/%s", s->base_dir, dir);
689 if (tstring == NULL) return ASL_STATUS_NO_MEMORY;
690
691 memset(&sb, 0, sizeof(struct stat));
692 status = stat(tstring, &sb);
693
694 if (status == 0)
695 {
696 /* must be a directory */
697 if (!S_ISDIR(sb.st_mode))
698 {
699 free(tstring);
700 return ASL_STATUS_INVALID_STORE;
701 }
702 }
703 else
704 {
705 if (errno == ENOENT)
706 {
707 /* doesn't exist - create it */
708 if (mkdir(tstring, m) != 0)
709 {
710 free(tstring);
711 return ASL_STATUS_WRITE_FAILED;
712 }
713 }
714 else
715 {
716 /* stat failed for some other reason */
717 free(tstring);
718 return ASL_STATUS_FAILED;
719 }
720 }
721
722 free(tstring);
723 return ASL_STATUS_OK;
724 }
725
726 uint32_t
727 asl_store_open_aux(asl_store_t *s, aslmsg msg, int *out_fd, char **url)
728 {
729 struct tm ctm;
730 time_t msg_time, bb;
731 char *path, *dir, *tstring;
732 const char *val;
733 uid_t ruid, u;
734 gid_t rgid, g;
735 mode_t m;
736 uint32_t status;
737 uint64_t fid;
738 int fd;
739
740 if (s == NULL) return ASL_STATUS_INVALID_STORE;
741 if (msg == NULL) return ASL_STATUS_INVALID_ARG;
742 if (out_fd == NULL) return ASL_STATUS_INVALID_ARG;
743 if (url == NULL) return ASL_STATUS_INVALID_ARG;
744
745 msg_time = time(NULL);
746
747 val = asl_get(msg, ASL_KEY_READ_UID);
748 ruid = -1;
749 if (val != NULL) ruid = atoi(val);
750
751 val = asl_get(msg, ASL_KEY_READ_GID);
752 rgid = -1;
753 if (val != NULL) rgid = atoi(val);
754
755 bb = 0;
756 val = asl_get(msg, ASL_KEY_EXPIRE_TIME);
757 if (val != NULL)
758 {
759 bb = 1;
760 msg_time = asl_parse_time(val);
761 }
762
763 if (localtime_r((const time_t *)&msg_time, &ctm) == NULL) return ASL_STATUS_FAILED;
764
765 dir = NULL;
766 if (bb == 1)
767 {
768 /*
769 * This supports 12 monthly "Best Before" buckets.
770 * We advance the actual expiry time to day zero of the following month.
771 * mktime() is clever enough to know that you actually mean the last day
772 * of the previous month. What we get back from localtime is the last
773 * day of the month in which the message expires, which we use in the name.
774 */
775 ctm.tm_sec = 0;
776 ctm.tm_min = 0;
777 ctm.tm_hour = 0;
778 ctm.tm_mday = 0;
779 ctm.tm_mon += 1;
780
781 bb = mktime(&ctm);
782
783 if (localtime_r((const time_t *)&bb, &ctm) == NULL) return ASL_STATUS_FAILED;
784 asprintf(&dir, "BB.AUX.%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday);
785 }
786 else
787 {
788 asprintf(&dir, "AUX.%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday);
789 }
790
791 if (dir == NULL) return ASL_STATUS_NO_MEMORY;
792
793 status = asl_store_mkdir(s, dir, 0755);
794 if (status != ASL_STATUS_OK)
795 {
796 free(dir);
797 return status;
798 }
799
800 fid = s->next_id;
801 s->next_id++;
802 tstring = NULL;
803
804 asprintf(&tstring, "%s/%llu", dir, fid);
805 free(dir);
806 if (tstring == NULL) return ASL_STATUS_NO_MEMORY;
807
808 u = 0;
809 g = 0;
810 m = 0644;
811 path = asl_store_make_ug_path(s->base_dir, tstring, NULL, ruid, rgid, &u, &g, &m);
812 free(tstring);
813 if (path == NULL) return ASL_STATUS_NO_MEMORY;
814
815 fd = asl_file_create(path, u, g, m);
816 if (fd < 0)
817 {
818 free(path);
819 *out_fd = -1;
820 return ASL_STATUS_WRITE_FAILED;
821 }
822
823 /* URL is file://<path> */
824 *url = NULL;
825 asprintf(url, "file://%s", path);
826 free(path);
827
828 *out_fd = fd;
829
830 return status;
831 }
832
833 uint32_t
834 asl_store_match_timeout(asl_store_t *s, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction, uint32_t usec)
835 {
836 DIR *dp;
837 struct dirent *dent;
838 uint32_t status;
839 asl_file_t *f;
840 char *path;
841 asl_file_list_t *files;
842
843 if (s == NULL) return ASL_STATUS_INVALID_STORE;
844 if (res == NULL) return ASL_STATUS_INVALID_ARG;
845
846 files = NULL;
847
848 /*
849 * Open all readable files
850 */
851 dp = opendir(s->base_dir);
852 if (dp == NULL) return ASL_STATUS_READ_FAILED;
853
854 while ((dent = readdir(dp)) != NULL)
855 {
856 if (dent->d_name[0] == '.') continue;
857
858 path = NULL;
859 asprintf(&path, "%s/%s", s->base_dir, dent->d_name);
860
861 /* NB asl_file_open_read will fail if path is NULL, if the file is not an ASL store file, or if it isn't readable */
862 status = asl_file_open_read(path, &f);
863 if (path != NULL) free(path);
864 if ((status != ASL_STATUS_OK) || (f == NULL)) continue;
865
866 files = asl_file_list_add(files, f);
867 }
868
869 closedir(dp);
870
871 status = asl_file_list_match_timeout(files, query, res, last_id, start_id, count, direction, usec);
872 asl_file_list_close(files);
873 return status;
874 }
875
876 uint32_t
877 asl_store_match(asl_store_t *s, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction)
878 {
879 return asl_store_match_timeout(s, query, res, last_id, start_id, count, direction, 0);
880 }
881
882 uint32_t
883 asl_store_match_start(asl_store_t *s, uint64_t start_id, int32_t direction)
884 {
885 DIR *dp;
886 struct dirent *dent;
887 uint32_t status;
888 asl_file_t *f;
889 char *path;
890 asl_file_list_t *files;
891
892 if (s == NULL) return ASL_STATUS_INVALID_STORE;
893
894 if (s->work != NULL) asl_file_list_match_end(s->work);
895 s->work = NULL;
896
897 files = NULL;
898
899 /*
900 * Open all readable files
901 */
902 dp = opendir(s->base_dir);
903 if (dp == NULL) return ASL_STATUS_READ_FAILED;
904
905 while ((dent = readdir(dp)) != NULL)
906 {
907 if (dent->d_name[0] == '.') continue;
908
909 path = NULL;
910 asprintf(&path, "%s/%s", s->base_dir, dent->d_name);
911
912 /* NB asl_file_open_read will fail if path is NULL, if the file is not an ASL store file, or if it isn't readable */
913 status = asl_file_open_read(path, &f);
914 if (path != NULL) free(path);
915 if ((status != ASL_STATUS_OK) || (f == NULL)) continue;
916
917 files = asl_file_list_add(files, f);
918 }
919
920 closedir(dp);
921
922 s->work = asl_file_list_match_start(files, start_id, direction);
923 if (s->work == NULL) return ASL_STATUS_FAILED;
924
925 return ASL_STATUS_OK;
926 }
927
928 uint32_t
929 asl_store_match_next(asl_store_t *s, aslresponse query, aslresponse *res, uint32_t count)
930 {
931 if (s == NULL) return ASL_STATUS_INVALID_STORE;
932 if (s->work == NULL) return ASL_STATUS_OK;
933
934 return asl_file_list_match_next(s->work, query, res, count);
935 }