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