]> git.saurik.com Git - apple/syslog.git/blob - libsystem_asl.tproj/src/asl_store.c
08da1a1d992a022343b14ee9b5cc89a3e2b40942
[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 extern uint64_t asl_file_cursor(asl_file_t *s);
39 extern uint32_t asl_file_match_start(asl_file_t *s, uint64_t start_id, int32_t direction);
40 extern uint32_t asl_file_match_next(asl_file_t *s, asl_msg_list_t *qlist, asl_msg_t **msg, uint64_t *last_id, int32_t direction, int32_t ruid, int32_t rgid);
41 extern int asl_file_create(const char *path, uid_t uid, gid_t gid, mode_t mode);
42
43 #define SECONDS_PER_DAY 86400
44
45 /*
46 * The ASL Store is organized as a set of files in a common directory.
47 * Files are prefixed by the date (YYYY.MM.DD) of their contents.
48 *
49 * Messages with no access controls are saved in YYYY.MM.DD.asl
50 * Messages with access limited to UID uuu are saved in YYYY.MM.DD.Uuuu.asl
51 * Messages with access limited to GID ggg are saved in YYYY.MM.DD.Gggg.asl
52 * Messages with access limited to UID uuu and GID ggg are saved in YYYY.MM.DD.Uuuu.Gggg.asl
53 *
54 * Messages that have a value for ASLExpireTime are saved in BB.YYYY.MM.DD.asl
55 * where the timestamp is the "Best Before" date of the file. Access controls
56 * are implemented as above with Uuuu and Gggg in the file name. Note that the
57 * Best Before files are for the last day of the month, so a single file contains
58 * messages that expire in that month.
59 *
60 * An external tool runs daily and deletes "old" files.
61 */
62
63 static time_t
64 _asl_start_today()
65 {
66 time_t now;
67 struct tm ctm;
68
69 memset(&ctm, 0, sizeof(struct tm));
70 now = time(NULL);
71
72 if (localtime_r((const time_t *)&now, &ctm) == NULL) return 0;
73
74 ctm.tm_sec = 0;
75 ctm.tm_min = 0;
76 ctm.tm_hour = 0;
77
78 return mktime(&ctm);
79 }
80
81 /*
82 * The base directory contains a data file which stores
83 * the last record ID.
84 *
85 * | MAX_ID (uint64_t) |
86 *
87 */
88 ASL_STATUS
89 asl_store_open_write(const char *basedir, asl_store_t **s)
90 {
91 asl_store_t *out;
92 struct stat sb;
93 uint32_t i, flags;
94 char *path;
95 FILE *sd;
96 uint64_t last_id;
97 time_t start;
98
99 if (s == NULL) return ASL_STATUS_INVALID_ARG;
100
101 start = _asl_start_today();
102 if (start == 0) return ASL_STATUS_FAILED;
103
104 if (basedir == NULL) basedir = PATH_ASL_STORE;
105
106 memset(&sb, 0, sizeof(struct stat));
107 if (stat(basedir, &sb) != 0) return ASL_STATUS_INVALID_STORE;
108 if (!S_ISDIR(sb.st_mode)) return ASL_STATUS_INVALID_STORE;
109
110 path = NULL;
111 asprintf(&path, "%s/%s", basedir, FILE_ASL_STORE_DATA);
112 if (path == NULL) return ASL_STATUS_NO_MEMORY;
113
114 sd = NULL;
115
116 memset(&sb, 0, sizeof(struct stat));
117 if (stat(path, &sb) != 0)
118 {
119 if (errno != ENOENT)
120 {
121 free(path);
122 return ASL_STATUS_FAILED;
123 }
124
125 sd = fopen(path, "w+");
126 free(path);
127
128 if (sd == NULL) return ASL_STATUS_FAILED;
129
130 last_id = 0;
131
132 /* Create new StoreData file (8 bytes ID + 4 bytes flags) */
133
134 if (fwrite(&last_id, sizeof(uint64_t), 1, sd) != 1)
135 {
136 fclose(sd);
137 return ASL_STATUS_WRITE_FAILED;
138 }
139
140 flags = 0;
141 if (fwrite(&flags, sizeof(uint32_t), 1, sd) != 1)
142 {
143 fclose(sd);
144 return ASL_STATUS_WRITE_FAILED;
145 }
146
147 /* flush data */
148 fflush(sd);
149 }
150 else
151 {
152 sd = fopen(path, "r+");
153 free(path);
154
155 if (sd == NULL) return ASL_STATUS_FAILED;
156 if (fread(&last_id, sizeof(uint64_t), 1, sd) != 1)
157 {
158 fclose(sd);
159 return ASL_STATUS_READ_FAILED;
160 }
161
162 last_id = asl_core_ntohq(last_id);
163 }
164
165 out = (asl_store_t *)calloc(1, sizeof(asl_store_t));
166 if (out == NULL)
167 {
168 fclose(sd);
169 return ASL_STATUS_NO_MEMORY;
170 }
171
172 out->asl_type = ASL_TYPE_STORE;
173 out->refcount = 1;
174
175 if (basedir == NULL) out->base_dir = strdup(PATH_ASL_STORE);
176 else out->base_dir = strdup(basedir);
177
178 if (out->base_dir == NULL)
179 {
180 fclose(sd);
181 free(out);
182 return ASL_STATUS_NO_MEMORY;
183 }
184
185 out->start_today = start;
186 out->start_tomorrow = out->start_today + SECONDS_PER_DAY;
187 out->storedata = sd;
188 out->next_id = last_id + 1;
189
190 for (i = 0; i < FILE_CACHE_SIZE; i++)
191 {
192 memset(&out->file_cache[i], 0, sizeof(asl_cached_file_t));
193 out->file_cache[i].u = -1;
194 out->file_cache[i].g = -1;
195 }
196
197 *s = out;
198 return ASL_STATUS_OK;
199 }
200
201 uint32_t
202 asl_store_set_flags(asl_store_t *s, uint32_t flags)
203 {
204 if (s == NULL) return 0;
205 uint32_t oldflags = s->flags;
206 s->flags = flags;
207 return oldflags;
208 }
209
210 ASL_STATUS
211 asl_store_statistics(asl_store_t *s, asl_msg_t **msg)
212 {
213 asl_msg_t *out;
214
215 if (s == NULL) return ASL_STATUS_INVALID_STORE;
216 if (msg == NULL) return ASL_STATUS_INVALID_ARG;
217
218 out = asl_msg_new(ASL_TYPE_MSG);
219 if (out == NULL) return ASL_STATUS_NO_MEMORY;
220
221 /* does nothing for now */
222
223 *msg = out;
224 return ASL_STATUS_OK;
225 }
226
227 uint32_t
228 asl_store_open_read(const char *basedir, asl_store_t **s)
229 {
230 asl_store_t *out;
231 struct stat sb;
232
233 if (s == NULL) return ASL_STATUS_INVALID_ARG;
234
235 if (basedir == NULL) basedir = PATH_ASL_STORE;
236
237 memset(&sb, 0, sizeof(struct stat));
238 if (stat(basedir, &sb) != 0) return ASL_STATUS_INVALID_STORE;
239 if (!S_ISDIR(sb.st_mode)) return ASL_STATUS_INVALID_STORE;
240
241 out = (asl_store_t *)calloc(1, sizeof(asl_store_t));
242 if (out == NULL) return ASL_STATUS_NO_MEMORY;
243
244 out->asl_type = ASL_TYPE_STORE;
245 out->refcount = 1;
246
247 if (basedir == NULL) out->base_dir = strdup(PATH_ASL_STORE);
248 else out->base_dir = strdup(basedir);
249
250 if (out->base_dir == NULL)
251 {
252 free(out);
253 return ASL_STATUS_NO_MEMORY;
254 }
255
256 *s = out;
257 return ASL_STATUS_OK;
258 }
259
260 uint32_t
261 asl_store_max_file_size(asl_store_t *s, size_t max)
262 {
263 if (s == NULL) return ASL_STATUS_INVALID_STORE;
264
265 s->max_file_size = max;
266 return ASL_STATUS_OK;
267 }
268
269 __private_extern__ void
270 asl_store_file_closeall(asl_store_t *s)
271 {
272 uint32_t i;
273
274 if (s == NULL) return;
275
276 for (i = 0; i < FILE_CACHE_SIZE; i++)
277 {
278 if (s->file_cache[i].f != NULL) asl_file_close(s->file_cache[i].f);
279 s->file_cache[i].f = NULL;
280 if (s->file_cache[i].path != NULL) free(s->file_cache[i].path);
281 s->file_cache[i].path = NULL;
282 s->file_cache[i].u = -1;
283 s->file_cache[i].g = -1;
284 s->file_cache[i].bb = 0;
285 s->file_cache[i].ts = 0;
286 }
287 }
288
289 asl_store_t *
290 asl_store_retain(asl_store_t *s)
291 {
292 if (s == NULL) return NULL;
293 asl_retain((asl_object_t)s);
294 return s;
295 }
296
297 void
298 asl_store_release(asl_store_t *s)
299 {
300 if (s == NULL) return;
301 asl_release((asl_object_t)s);
302 }
303
304 ASL_STATUS
305 asl_store_close(asl_store_t *s)
306 {
307 if (s == NULL) return ASL_STATUS_OK;
308 asl_release((asl_object_t)s);
309 return ASL_STATUS_OK;
310 }
311
312 static void
313 _asl_store_free_internal(asl_store_t *s)
314 {
315 if (s == NULL) return;
316
317 if (s->base_dir != NULL) free(s->base_dir);
318 s->base_dir = NULL;
319 asl_store_file_closeall(s);
320 if (s->storedata != NULL) fclose(s->storedata);
321
322 free(s);
323 }
324
325 /*
326 * Sweep the file cache.
327 * Close any files that have not been used in the last FILE_CACHE_TTL seconds.
328 * Returns least recently used or unused cache slot.
329 */
330 static uint32_t
331 asl_store_file_cache_lru(asl_store_t *s, time_t now, uint32_t ignorex)
332 {
333 time_t min;
334 uint32_t i, x;
335
336 if (s == NULL) return 0;
337
338 x = 0;
339 min = now - FILE_CACHE_TTL;
340
341 for (i = 0; i < FILE_CACHE_SIZE; i++)
342 {
343 if ((i != ignorex) && (s->file_cache[i].ts < min))
344 {
345 asl_file_close(s->file_cache[i].f);
346 s->file_cache[i].f = NULL;
347 if (s->file_cache[i].path != NULL) free(s->file_cache[i].path);
348 s->file_cache[i].path = NULL;
349 s->file_cache[i].u = -1;
350 s->file_cache[i].g = -1;
351 s->file_cache[i].bb = 0;
352 s->file_cache[i].ts = 0;
353 }
354
355 if (s->file_cache[i].ts < s->file_cache[x].ts) x = i;
356 }
357
358 return x;
359 }
360
361 ASL_STATUS
362 asl_store_sweep_file_cache(asl_store_t *s)
363 {
364 if (s == NULL) return ASL_STATUS_INVALID_STORE;
365
366 asl_store_file_cache_lru(s, time(NULL), FILE_CACHE_SIZE);
367 return ASL_STATUS_OK;
368 }
369
370 static char *
371 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)
372 {
373 char *path = NULL;
374
375 *u = 0;
376 *g = 0;
377 *m = 0644;
378
379 if (ruid == -1)
380 {
381 if (rgid == -1)
382 {
383 if (ext == NULL) asprintf(&path, "%s/%s", dir, base);
384 else asprintf(&path, "%s/%s.%s", dir, base, ext);
385 }
386 else
387 {
388 *g = rgid;
389 *m = 0600;
390 if (ext == NULL) asprintf(&path, "%s/%s.G%d", dir, base, *g);
391 else asprintf(&path, "%s/%s.G%d.%s", dir, base, *g, ext);
392 }
393 }
394 else
395 {
396 *u = ruid;
397 if (rgid == -1)
398 {
399 *m = 0600;
400 if (ext == NULL) asprintf(&path, "%s/%s.U%d", dir, base, *u);
401 else asprintf(&path, "%s/%s.U%d.%s", dir, base, *u, ext);
402 }
403 else
404 {
405 *g = rgid;
406 *m = 0600;
407 if (ext == NULL) asprintf(&path, "%s/%s.U%d.G%d", dir, base, *u, *g);
408 else asprintf(&path, "%s/%s.U%d.G%u.%s", dir, base, *u, *g, ext);
409 }
410 }
411
412 return path;
413 }
414
415 static ASL_STATUS
416 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)
417 {
418 char *path;
419 mode_t m;
420 int32_t i, x;
421 uid_t u;
422 gid_t g;
423 uint32_t status;
424 asl_file_t *out;
425
426 if (s == NULL) return ASL_STATUS_INVALID_STORE;
427
428 /* see if the file is already open and in the cache */
429 for (i = 0; i < FILE_CACHE_SIZE; i++)
430 {
431 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))
432 {
433 s->file_cache[i].ts = now;
434 *f = s->file_cache[i].f;
435 if (check_cache == 1) asl_store_file_cache_lru(s, now, i);
436 return ASL_STATUS_OK;
437 }
438 }
439
440 u = 0;
441 g = 0;
442 m = 0644;
443 path = asl_store_make_ug_path(s->base_dir, tstring, "asl", (uid_t)ruid, (gid_t)rgid, &u, &g, &m);
444 if (path == NULL) return ASL_STATUS_NO_MEMORY;
445
446 out = NULL;
447 status = asl_file_open_write(path, m, u, g, &out);
448 if (status != ASL_STATUS_OK)
449 {
450 free(path);
451 return status;
452 }
453
454 x = asl_store_file_cache_lru(s, now, FILE_CACHE_SIZE);
455 if (s->file_cache[x].f != NULL) asl_file_close(s->file_cache[x].f);
456 if (s->file_cache[x].path != NULL) free(s->file_cache[x].path);
457
458 s->file_cache[x].f = out;
459 s->file_cache[x].path = path;
460 s->file_cache[x].u = ruid;
461 s->file_cache[x].g = rgid;
462 s->file_cache[x].bb = bb;
463 s->file_cache[x].ts = time(NULL);
464
465 *f = out;
466
467 return ASL_STATUS_OK;
468 }
469
470 __private_extern__ char *
471 asl_store_file_path(asl_store_t *s, asl_file_t *f)
472 {
473 uint32_t i;
474
475 if (s == NULL) return NULL;
476
477 for (i = 0; i < FILE_CACHE_SIZE; i++)
478 {
479 if (s->file_cache[i].f == f)
480 {
481 if (s->file_cache[i].path == NULL) return NULL;
482 return strdup(s->file_cache[i].path);
483 }
484 }
485
486 return NULL;
487 }
488
489 __private_extern__ void
490 asl_store_file_close(asl_store_t *s, asl_file_t *f)
491 {
492 uint32_t i;
493
494 if (s == NULL) return;
495 if (f == NULL) return;
496
497 for (i = 0; i < FILE_CACHE_SIZE; i++)
498 {
499 if (s->file_cache[i].f == f)
500 {
501 asl_file_close(s->file_cache[i].f);
502 s->file_cache[i].f = NULL;
503 if (s->file_cache[i].path != NULL) free(s->file_cache[i].path);
504 s->file_cache[i].path = NULL;
505 s->file_cache[i].u = -1;
506 s->file_cache[i].g = -1;
507 s->file_cache[i].bb = 0;
508 s->file_cache[i].ts = 0;
509 return;
510 }
511 }
512 }
513
514 ASL_STATUS
515 asl_store_save(asl_store_t *s, asl_msg_t *msg)
516 {
517 struct tm ctm;
518 time_t msg_time, now, bb;
519 char *path, *tmp_path, *tstring, *scratch;
520 const char *val;
521 uid_t ruid;
522 gid_t rgid;
523 asl_file_t *f;
524 uint32_t status, check_cache, trigger_aslmanager, len;
525 uint64_t xid, ftime;
526 size_t fsize;
527
528 if (s == NULL) return ASL_STATUS_INVALID_STORE;
529 if (msg == NULL) return ASL_STATUS_INVALID_ARG;
530
531 now = time(NULL);
532
533 check_cache = 0;
534 if ((s->last_write + FILE_CACHE_TTL) <= now) check_cache = 1;
535
536 trigger_aslmanager = 0;
537
538 msg_time = 0;
539 val = NULL;
540
541 if (asl_msg_lookup(msg, ASL_KEY_TIME, &val, NULL) != 0) msg_time = now;
542 else if (val == NULL) msg_time = now;
543 else msg_time = asl_core_parse_time(val, NULL);
544
545 if (msg_time >= s->start_tomorrow)
546 {
547 if (now >= s->start_tomorrow)
548 {
549 /* new day begins */
550 check_cache = 0;
551 asl_store_file_closeall(s);
552
553 /*
554 * _asl_start_today should never fail, but if it does,
555 * just push forward one day. That will probably be correct, and if
556 * it isn't, the next message that gets saved will push it ahead again
557 * until we get to the right date.
558 */
559 s->start_today = _asl_start_today();
560 if (s->start_today == 0) s->start_today = s->start_tomorrow;
561
562 s->start_tomorrow = s->start_today + SECONDS_PER_DAY;
563 }
564 }
565
566 ruid = -1;
567 rgid = -1;
568 if ((s->flags & ASL_STORE_FLAG_NO_ACLS) == 0)
569 {
570 val = NULL;
571 if ((asl_msg_lookup(msg, ASL_KEY_READ_UID, &val, NULL) == 0) && (val != NULL)) ruid = atoi(val);
572
573 val = NULL;
574 if ((asl_msg_lookup(msg, ASL_KEY_READ_GID, &val, NULL) == 0) && (val != NULL)) rgid = atoi(val);
575 }
576
577 bb = 0;
578 if ((s->flags & ASL_STORE_FLAG_NO_TTL) == 0)
579 {
580 val = NULL;
581 if ((asl_msg_lookup(msg, ASL_KEY_EXPIRE_TIME, &val, NULL) == 0) && (val != NULL))
582 {
583 bb = 1;
584 msg_time = asl_core_parse_time(val, NULL);
585 }
586 }
587
588 if (fseeko(s->storedata, 0, SEEK_SET) != 0) return ASL_STATUS_WRITE_FAILED;
589
590 xid = asl_core_htonq(s->next_id);
591 if (fwrite(&xid, sizeof(uint64_t), 1, s->storedata) != 1) return ASL_STATUS_WRITE_FAILED;
592
593 /* flush data */
594 fflush(s->storedata);
595
596 xid = s->next_id;
597 s->next_id++;
598
599 s->last_write = now;
600
601 if (localtime_r((const time_t *)&msg_time, &ctm) == NULL) return ASL_STATUS_FAILED;
602
603 tstring = NULL;
604 if (bb == 1)
605 {
606 /*
607 * This supports 12 monthly "Best Before" buckets.
608 * We advance the actual expiry time to day zero of the following month.
609 * mktime() is clever enough to know that you actually mean the last day
610 * of the previous month. What we get back from localtime is the last
611 * day of the month in which the message expires, which we use in the name.
612 */
613 ctm.tm_sec = 0;
614 ctm.tm_min = 0;
615 ctm.tm_hour = 0;
616 ctm.tm_mday = 0;
617 ctm.tm_mon += 1;
618
619 bb = mktime(&ctm);
620
621 if (localtime_r((const time_t *)&bb, &ctm) == NULL) return ASL_STATUS_FAILED;
622 asprintf(&tstring, "BB.%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday);
623 }
624 else
625 {
626 asprintf(&tstring, "%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday);
627 }
628
629 if (tstring == NULL) return ASL_STATUS_NO_MEMORY;
630
631 status = asl_store_file_open_write(s, tstring, ruid, rgid, bb, &f, now, check_cache);
632 free(tstring);
633 tstring = NULL;
634
635 if (status != ASL_STATUS_OK) return status;
636
637 status = asl_file_save(f, msg, &xid);
638 if (status != ASL_STATUS_OK) return status;
639
640 fsize = asl_file_size(f);
641 ftime = asl_file_ctime(f);
642
643 /* if file is larger than max_file_size, rename it and trigger aslmanager */
644 if ((s->max_file_size != 0) && (fsize > s->max_file_size))
645 {
646 trigger_aslmanager = 1;
647 status = ASL_STATUS_OK;
648
649 path = asl_store_file_path(s, f);
650
651 asl_store_file_close(s, f);
652
653 if (path != NULL)
654 {
655 tmp_path = NULL;
656
657 len = strlen(path);
658 if ((len >= 4) && (!strcmp(path + len - 4, ".asl")))
659 {
660 /* rename xxxxxxx.asl to xxxxxxx.timestamp.asl */
661 scratch = strdup(path);
662 if (scratch != NULL)
663 {
664 scratch[len - 4] = '\0';
665 asprintf(&tmp_path, "%s.%llu.asl", scratch, ftime);
666 free(scratch);
667
668 }
669 }
670 else
671 {
672 /* append timestamp */
673 asprintf(&tmp_path, "%s.%llu", path, ftime);
674 }
675
676 if (tmp_path == NULL)
677 {
678 status = ASL_STATUS_NO_MEMORY;
679 }
680 else
681 {
682 if (rename(path, tmp_path) != 0) status = ASL_STATUS_FAILED;
683 free(tmp_path);
684 }
685
686 free(path);
687 }
688 }
689
690 if (trigger_aslmanager != 0) asl_trigger_aslmanager();
691
692 return status;
693 }
694
695 static ASL_STATUS
696 asl_store_mkdir(asl_store_t *s, const char *dir, mode_t m)
697 {
698 char *tstring = NULL;
699 int status;
700 struct stat sb;
701
702 asprintf(&tstring, "%s/%s", s->base_dir, dir);
703 if (tstring == NULL) return ASL_STATUS_NO_MEMORY;
704
705 memset(&sb, 0, sizeof(struct stat));
706 status = stat(tstring, &sb);
707
708 if (status == 0)
709 {
710 /* must be a directory */
711 if (!S_ISDIR(sb.st_mode))
712 {
713 free(tstring);
714 return ASL_STATUS_INVALID_STORE;
715 }
716 }
717 else
718 {
719 if (errno == ENOENT)
720 {
721 /* doesn't exist - create it */
722 if (mkdir(tstring, m) != 0)
723 {
724 free(tstring);
725 return ASL_STATUS_WRITE_FAILED;
726 }
727 }
728 else
729 {
730 /* stat failed for some other reason */
731 free(tstring);
732 return ASL_STATUS_FAILED;
733 }
734 }
735
736 free(tstring);
737 return ASL_STATUS_OK;
738 }
739
740 ASL_STATUS
741 asl_store_open_aux(asl_store_t *s, asl_msg_t *msg, int *out_fd, char **url)
742 {
743 struct tm ctm;
744 time_t msg_time, bb;
745 char *path, *dir, *tstring;
746 const char *val;
747 uid_t ruid, u;
748 gid_t rgid, g;
749 mode_t m;
750 uint32_t status;
751 uint64_t fid;
752 int fd;
753
754 if (s == NULL) return ASL_STATUS_INVALID_STORE;
755 if (msg == NULL) return ASL_STATUS_INVALID_ARG;
756 if (out_fd == NULL) return ASL_STATUS_INVALID_ARG;
757 if (url == NULL) return ASL_STATUS_INVALID_ARG;
758
759 msg_time = time(NULL);
760
761 ruid = -1;
762 rgid = -1;
763 if ((s->flags & ASL_STORE_FLAG_NO_ACLS) == 0)
764 {
765 val = NULL;
766 if ((asl_msg_lookup(msg, ASL_KEY_READ_UID, &val, NULL) == 0) && (val != NULL)) ruid = atoi(val);
767
768 val = NULL;
769 if ((asl_msg_lookup(msg, ASL_KEY_READ_GID, &val, NULL) == 0) && (val != NULL)) rgid = atoi(val);
770 }
771
772 bb = 0;
773 if ((s->flags & ASL_STORE_FLAG_NO_TTL) == 0)
774 {
775 val = NULL;
776 if ((asl_msg_lookup(msg, ASL_KEY_EXPIRE_TIME, &val, NULL) == 0) && (val != NULL))
777 {
778 bb = 1;
779 msg_time = asl_core_parse_time(val, NULL);
780 }
781 }
782
783 if (localtime_r((const time_t *)&msg_time, &ctm) == NULL) return ASL_STATUS_FAILED;
784
785 dir = NULL;
786 if (bb == 1)
787 {
788 /*
789 * This supports 12 monthly "Best Before" buckets.
790 * We advance the actual expiry time to day zero of the following month.
791 * mktime() is clever enough to know that you actually mean the last day
792 * of the previous month. What we get back from localtime is the last
793 * day of the month in which the message expires, which we use in the name.
794 */
795 ctm.tm_sec = 0;
796 ctm.tm_min = 0;
797 ctm.tm_hour = 0;
798 ctm.tm_mday = 0;
799 ctm.tm_mon += 1;
800
801 bb = mktime(&ctm);
802
803 if (localtime_r((const time_t *)&bb, &ctm) == NULL) return ASL_STATUS_FAILED;
804 asprintf(&dir, "BB.AUX.%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday);
805 }
806 else
807 {
808 asprintf(&dir, "AUX.%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday);
809 }
810
811 if (dir == NULL) return ASL_STATUS_NO_MEMORY;
812
813 status = asl_store_mkdir(s, dir, 0755);
814 if (status != ASL_STATUS_OK)
815 {
816 free(dir);
817 return status;
818 }
819
820 fid = s->next_id;
821 s->next_id++;
822 tstring = NULL;
823
824 asprintf(&tstring, "%s/%llu", dir, fid);
825 free(dir);
826 if (tstring == NULL) return ASL_STATUS_NO_MEMORY;
827
828 u = 0;
829 g = 0;
830 m = 0644;
831 path = asl_store_make_ug_path(s->base_dir, tstring, NULL, ruid, rgid, &u, &g, &m);
832 free(tstring);
833 if (path == NULL) return ASL_STATUS_NO_MEMORY;
834
835 fd = asl_file_create(path, u, g, m);
836 if (fd < 0)
837 {
838 free(path);
839 *out_fd = -1;
840 return ASL_STATUS_WRITE_FAILED;
841 }
842
843 /* URL is file://<path> */
844 *url = NULL;
845 asprintf(url, "file://%s", path);
846 free(path);
847
848 *out_fd = fd;
849
850 return status;
851 }
852
853 asl_msg_list_t *
854 asl_store_match(asl_store_t *s, asl_msg_list_t *qlist, uint64_t *last_id, uint64_t start_id, uint32_t count, uint32_t duration, int32_t direction)
855 {
856 DIR *dp;
857 struct dirent *dent;
858 uint32_t status;
859 asl_file_t *f;
860 char *path;
861 asl_file_list_t *files;
862 asl_msg_list_t *res;
863
864 if (s == NULL) return NULL;
865
866 files = NULL;
867
868 /*
869 * Open all readable files
870 */
871 dp = opendir(s->base_dir);
872 if (dp == NULL) return NULL;
873
874 while ((dent = readdir(dp)) != NULL)
875 {
876 if (dent->d_name[0] == '.') continue;
877
878 path = NULL;
879 asprintf(&path, "%s/%s", s->base_dir, dent->d_name);
880
881 /* 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 */
882 status = asl_file_open_read(path, &f);
883 if (path != NULL) free(path);
884 if ((status != ASL_STATUS_OK) || (f == NULL)) continue;
885
886 files = asl_file_list_add(files, f);
887 }
888
889 closedir(dp);
890
891 res = asl_file_list_match(files, qlist, last_id, start_id, count, duration, direction);
892 asl_file_list_close(files);
893 return res;
894 }
895
896 /*
897 * PRIVATE FOR DEV TOOLS SUPPORT
898 * DO NOT USE THIS INTERFACE OTHERWISE
899 *
900 * This is only called by a client that compiled with a 10.9 SDK, but is running
901 * with an new 10.10 libasl.
902 *
903 * Only searches the ASL database, so the store (first parameter) is ignored.
904 *
905 * The query and result are old-style message lists.
906 *
907 */
908 typedef struct
909 {
910 uint32_t count;
911 uint32_t curr;
912 void **msg;
913 } asl_msg_list_v1_t;
914
915 ASL_STATUS
916 asl_store_match_timeout(void *ignored, void *query_v1, void **result_v1, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction, uint32_t usec)
917 {
918 asl_store_t *asldb = NULL;
919 asl_msg_list_v1_t *listv1;
920 asl_msg_list_t *qlist = NULL;
921 uint32_t status, n;
922
923 if (result_v1 == NULL) return ASL_STATUS_FAILED;
924 *result_v1 = NULL;
925
926 status = asl_store_open_read(NULL, &asldb);
927 if (status != ASL_STATUS_OK) return status;
928
929 /* convert query_v1 into an asl_msg_list_t */
930 listv1 = (asl_msg_list_v1_t *)query_v1;
931 if (listv1 != NULL)
932 {
933 if (listv1->count > 0) qlist = (asl_msg_list_t *)asl_new(ASL_TYPE_LIST);
934
935 for (listv1->curr = 0; listv1->curr < listv1->count; listv1->curr++)
936 {
937 asl_append((asl_object_t)qlist, (asl_object_t)listv1->msg[listv1->curr]);
938 }
939 }
940
941 asl_msg_list_t *result = asl_store_match(asldb, qlist, last_id, start_id, count, usec, direction);
942 asl_release((asl_object_t)asldb);
943 asl_release((asl_object_t)qlist);
944
945 if (result == NULL) return ASL_STATUS_OK;
946
947 n = asl_count((asl_object_t)result);
948 if (n == 0)
949 {
950 asl_release((asl_object_t)result);
951 return ASL_STATUS_OK;
952 }
953
954 listv1 = (asl_msg_list_v1_t *)calloc(1, sizeof(asl_msg_list_v1_t));
955 if (listv1 == NULL)
956 {
957 asl_release((asl_object_t)result);
958 return ASL_STATUS_NO_MEMORY;
959 }
960
961 listv1->count = n;
962 listv1->msg = (void **)calloc(listv1->count, sizeof(void *));
963 if (listv1 == NULL)
964 {
965 free(listv1);
966 asl_release((asl_object_t)result);
967 return ASL_STATUS_NO_MEMORY;
968 }
969
970 for (listv1->curr = 0; listv1->curr < listv1->count; listv1->curr++)
971 {
972 listv1->msg[listv1->curr] = asl_retain(asl_next((asl_object_t)result));
973 }
974
975 listv1->curr = 0;
976 *result_v1 = listv1;
977
978 asl_release((asl_object_t)result);
979 return ASL_STATUS_OK;
980 }
981
982 ASL_STATUS
983 asl_store_match_start(asl_store_t *s, uint64_t start_id, int32_t direction)
984 {
985 DIR *dp;
986 struct dirent *dent;
987 uint32_t status;
988 asl_file_t *f;
989 char *path;
990 asl_file_list_t *files;
991
992 if (s == NULL) return ASL_STATUS_INVALID_STORE;
993
994 if (s->work != NULL) asl_file_list_match_end(s->work);
995 s->work = NULL;
996
997 files = NULL;
998
999 /*
1000 * Open all readable files
1001 */
1002 dp = opendir(s->base_dir);
1003 if (dp == NULL) return ASL_STATUS_READ_FAILED;
1004
1005 while ((dent = readdir(dp)) != NULL)
1006 {
1007 if (dent->d_name[0] == '.') continue;
1008
1009 path = NULL;
1010 asprintf(&path, "%s/%s", s->base_dir, dent->d_name);
1011
1012 /*
1013 * NB asl_file_open_read will fail if path is NULL,
1014 * if it is not an ASL store file, or if it isn't readable.
1015 * We expect that.
1016 */
1017 status = asl_file_open_read(path, &f);
1018 if (path != NULL) free(path);
1019 if ((status != ASL_STATUS_OK) || (f == NULL)) continue;
1020
1021 files = asl_file_list_add(files, f);
1022 }
1023
1024 closedir(dp);
1025
1026 s->work = asl_file_list_match_start(files, start_id, direction);
1027 if (s->work == NULL) return ASL_STATUS_FAILED;
1028
1029 return ASL_STATUS_OK;
1030 }
1031
1032 ASL_STATUS
1033 asl_store_match_next(asl_store_t *s, asl_msg_list_t *qlist, asl_msg_list_t **res, uint32_t count)
1034 {
1035 if (s == NULL) return ASL_STATUS_INVALID_STORE;
1036 if (s->work == NULL) return ASL_STATUS_OK;
1037
1038 return asl_file_list_match_next(s->work, qlist, res, count);
1039 }
1040
1041 #pragma mark -
1042 #pragma mark asl_object support
1043
1044 static void
1045 _jump_dealloc(asl_object_private_t *obj)
1046 {
1047 _asl_store_free_internal((asl_store_t *)obj);
1048 }
1049
1050 static asl_object_private_t *
1051 _jump_next(asl_object_private_t *obj)
1052 {
1053 asl_store_t *s = (asl_store_t *)obj;
1054 asl_msg_list_t *list;
1055 asl_msg_t *out = NULL;
1056 uint64_t last = 0;
1057
1058 if (s == NULL) return NULL;
1059 if (s->curr == SIZE_MAX) return NULL;
1060
1061 s->curr++;
1062 list = asl_store_match(s, NULL, &last, s->curr, 1, 0, 1);
1063 if (list == NULL)
1064 {
1065 s->curr = SIZE_MAX;
1066 return NULL;
1067 }
1068
1069 s->curr = last;
1070 out = asl_msg_list_get_index(list, 0);
1071 asl_msg_list_release(list);
1072
1073 return (asl_object_private_t *)out;
1074 }
1075
1076 static asl_object_private_t *
1077 _jump_prev(asl_object_private_t *obj)
1078 {
1079 asl_store_t *s = (asl_store_t *)obj;
1080 asl_msg_list_t *list;
1081 asl_msg_t *out = NULL;
1082 uint64_t last = 0;
1083
1084 if (s == NULL) return NULL;
1085 if (s->curr == 0) return NULL;
1086
1087 s->curr--;
1088 if (s->curr == 0) return NULL;
1089
1090 list = asl_store_match(s, NULL, &last, s->curr, 1, 0, -1);
1091 if (list == NULL)
1092 {
1093 s->curr = 0;
1094 return NULL;
1095 }
1096
1097 s->curr = last;
1098 out = asl_msg_list_get_index(list, 0);
1099 asl_msg_list_release(list);
1100
1101 return (asl_object_private_t *)out;
1102 }
1103
1104 static void
1105 _jump_set_iteration_index(asl_object_private_t *obj, size_t n)
1106 {
1107 asl_store_t *s = (asl_store_t *)obj;
1108 if (s == NULL) return;
1109
1110 s->curr = n;
1111 }
1112
1113 static void
1114 _jump_append(asl_object_private_t *obj, asl_object_private_t *newobj)
1115 {
1116 asl_store_t *s = (asl_store_t *)obj;
1117 int type = asl_get_type((asl_object_t)newobj);
1118 if (s == NULL) return;
1119 if (s->flags & ASL_FILE_FLAG_READ) return;
1120
1121 if (type == ASL_TYPE_LIST)
1122 {
1123 asl_msg_t *msg;
1124 asl_msg_list_reset_iteration((asl_msg_list_t *)newobj, 0);
1125 while (NULL != (msg = asl_msg_list_next((asl_msg_list_t *)newobj)))
1126 {
1127 if (asl_store_save(s, msg) != ASL_STATUS_OK) return;
1128 }
1129 }
1130 else if ((type == ASL_TYPE_MSG) || (type == ASL_TYPE_QUERY))
1131 {
1132 asl_store_save(s, (asl_msg_t *)newobj);
1133 }
1134 }
1135
1136 static asl_object_private_t *
1137 _jump_search(asl_object_private_t *obj, asl_object_private_t *query)
1138 {
1139 asl_store_t *s = (asl_store_t *)obj;
1140 int type = asl_get_type((asl_object_t)query);
1141 asl_msg_list_t *out = NULL;
1142 asl_msg_list_t *ql = NULL;
1143 uint64_t last;
1144 uint32_t status = ASL_STATUS_FAILED;
1145
1146 if (query == NULL)
1147 {
1148 out = asl_store_match(s, NULL, &last, 0, 0, 0, 1);
1149 }
1150 else if (type == ASL_TYPE_LIST)
1151 {
1152 out = asl_store_match(s, (asl_msg_list_t *)query, &last, 0, 0, 0, 1);
1153 }
1154 else if ((type == ASL_TYPE_MSG) || (type == ASL_TYPE_QUERY))
1155 {
1156 ql = asl_msg_list_new();
1157 asl_msg_list_append(ql, query);
1158
1159 out = asl_store_match(s, ql, &last, 0, 0, 0, 1);
1160 asl_msg_list_release(ql);
1161 }
1162
1163 if (status != ASL_STATUS_OK) return NULL;
1164 return (asl_object_private_t *)out;
1165 }
1166
1167 static asl_object_private_t *
1168 _jump_match(asl_object_private_t *obj, asl_object_private_t *qlist, size_t *last, size_t start, size_t count, uint32_t duration, int32_t dir)
1169 {
1170 uint64_t x;
1171 asl_msg_list_t *out;
1172
1173 out = asl_store_match((asl_store_t *)obj, (asl_msg_list_t *)qlist, &x, start, count, duration, dir);
1174 *last = x;
1175 return (asl_object_private_t *)out;
1176 }
1177
1178 __private_extern__ const asl_jump_table_t *
1179 asl_store_jump_table()
1180 {
1181 static const asl_jump_table_t jump =
1182 {
1183 .alloc = NULL,
1184 .dealloc = &_jump_dealloc,
1185 .set_key_val_op = NULL,
1186 .unset_key = NULL,
1187 .get_val_op_for_key = NULL,
1188 .get_key_val_op_at_index = NULL,
1189 .count = NULL,
1190 .next = &_jump_next,
1191 .prev = &_jump_prev,
1192 .get_object_at_index = NULL,
1193 .set_iteration_index = &_jump_set_iteration_index,
1194 .remove_object_at_index = NULL,
1195 .append = &_jump_append,
1196 .prepend = NULL,
1197 .search = &_jump_search,
1198 .match = &_jump_match
1199 };
1200
1201 return &jump;
1202 }