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