]> git.saurik.com Git - apple/libc.git/blob - gen/asl_store.c
Libc-498.1.5.tar.gz
[apple/libc.git] / gen / asl_store.c
1 /*
2 * Copyright (c) 2007-2008 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * "Portions Copyright (c) 2007 Apple Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License'). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
20 * under the License."
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <dirent.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33 #include <asl.h>
34 #include <asl_private.h>
35 #include <asl_core.h>
36 #include <asl_store.h>
37 #include <notify.h>
38
39 extern time_t asl_parse_time(const char *str);
40 extern uint64_t asl_file_cursor(asl_file_t *s);
41 extern uint32_t asl_file_match_start(asl_file_t *s, uint64_t start_id, int32_t direction);
42 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);
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 * There are also files for long-TTL (> 1 day) messages.
50 *
51 * Messages with no access controls are saved in YYYY.MM.DD.asl
52 * Messages with access limited to UID U are saved in YYYY.MM.DD.uU.asl
53 * Messages with access limited to GID G are saved in YYYY.MM.DD.gG.asl
54 * Messages with access limited to UID U and GID G are saved in YYYY.MM.DD.uU.gG.asl
55 *
56 * An external tool runs daily and deletes "old" files.
57 */
58
59 /*
60 * The base directory contains a data file which stores
61 * the last record ID.
62 *
63 * | MAX_ID (uint64_t) |
64 *
65 */
66 uint32_t
67 asl_store_open_write(const char *basedir, asl_store_t **s)
68 {
69 asl_store_t *out;
70 asl_file_t *db;
71 struct stat sb;
72 uint32_t i, status;
73 char *path, *subpath;
74 time_t now;
75 struct tm ctm;
76 FILE *sd;
77 uint64_t last_id;
78
79 if (s == NULL) return ASL_STATUS_INVALID_ARG;
80
81 if (basedir == NULL) basedir = PATH_ASL_STORE;
82
83 memset(&sb, 0, sizeof(struct stat));
84 if (stat(basedir, &sb) != 0) return ASL_STATUS_INVALID_STORE;
85 if ((sb.st_mode & S_IFDIR) == 0) return ASL_STATUS_INVALID_STORE;
86
87 path = NULL;
88 asprintf(&path, "%s/%s", basedir, FILE_ASL_STORE_DATA);
89 if (path == NULL) return ASL_STATUS_NO_MEMORY;
90
91 sd = NULL;
92
93 memset(&sb, 0, sizeof(struct stat));
94 if (stat(path, &sb) != 0)
95 {
96 if (errno != ENOENT)
97 {
98 free(path);
99 return ASL_STATUS_FAILED;
100 }
101
102 sd = fopen(path, "w+");
103 free(path);
104
105 if (sd == NULL) return ASL_STATUS_FAILED;
106
107 last_id = 0;
108
109 if (fwrite(&last_id, sizeof(uint64_t), 1, sd) != 1)
110 {
111 fclose(sd);
112 return ASL_STATUS_WRITE_FAILED;
113 }
114 }
115 else
116 {
117 sd = fopen(path, "r+");
118 free(path);
119
120 if (sd == NULL) return ASL_STATUS_FAILED;
121 if (fread(&last_id, sizeof(uint64_t), 1, sd) != 1)
122 {
123 fclose(sd);
124 return ASL_STATUS_READ_FAILED;
125 }
126
127 last_id = asl_core_ntohq(last_id);
128 }
129
130 memset(&ctm, 0, sizeof(struct tm));
131 now = time(NULL);
132
133 if (localtime_r((const time_t *)&now, &ctm) == NULL)
134 {
135 fclose(sd);
136 return ASL_STATUS_FAILED;
137 }
138
139 subpath = NULL;
140 asprintf(&subpath, "%s/%d.%02d.%02d", basedir, ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday);
141 if (subpath == NULL)
142 {
143 fclose(sd);
144 return ASL_STATUS_NO_MEMORY;
145 }
146
147 path = NULL;
148 asprintf(&path, "%s.asl", subpath);
149 free(subpath);
150 if (path == NULL)
151 {
152 fclose(sd);
153 return ASL_STATUS_NO_MEMORY;
154 }
155
156 db = NULL;
157 status = asl_file_open_write(path, 0644, 0, 0, &db);
158 free(path);
159 if ((status != ASL_STATUS_OK) || (db == NULL))
160 {
161 fclose(sd);
162 return ASL_STATUS_FAILED;
163 }
164
165 out = (asl_store_t *)calloc(1, sizeof(asl_store_t));
166 if (out == NULL)
167 {
168 fclose(sd);
169 asl_file_close(db);
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 asl_file_close(db);
180 free(out);
181 return ASL_STATUS_NO_MEMORY;
182 }
183
184 ctm.tm_sec = 0;
185 ctm.tm_min = 0;
186 ctm.tm_hour = 0;
187
188 out->start_today = mktime(&ctm);
189 out->start_tomorrow = out->start_today + SECONDS_PER_DAY;
190 out->db = db;
191 out->storedata = sd;
192 out->next_id = last_id + 1;
193
194 for (i = 0; i < FILE_CACHE_SIZE; i++)
195 {
196 memset(&out->file_cache[i], 0, sizeof(asl_cached_file_t));
197 out->file_cache[i].u = -1;
198 out->file_cache[i].g = -1;
199 }
200
201 *s = out;
202 return ASL_STATUS_OK;
203 }
204
205 uint32_t
206 asl_store_statistics(asl_store_t *s, aslmsg *msg)
207 {
208 aslmsg out;
209 char str[256];
210
211 if (s == NULL) return ASL_STATUS_INVALID_STORE;
212 if (msg == NULL) return ASL_STATUS_INVALID_ARG;
213
214 out = (aslmsg)calloc(1, sizeof(asl_msg_t));
215 if (out == NULL) return ASL_STATUS_NO_MEMORY;
216
217 snprintf(str, sizeof(str), "%u", s->db->string_count);
218 asl_set(out, "StringCount", str);
219
220 *msg = out;
221 return ASL_STATUS_OK;
222 }
223
224 uint32_t
225 asl_store_open_read(const char *basedir, asl_store_t **s)
226 {
227 asl_store_t *out;
228 struct stat sb;
229
230 if (s == NULL) return ASL_STATUS_INVALID_ARG;
231
232 if (basedir == NULL) basedir = PATH_ASL_STORE;
233
234 memset(&sb, 0, sizeof(struct stat));
235 if (stat(basedir, &sb) != 0) return ASL_STATUS_INVALID_STORE;
236 if ((sb.st_mode & S_IFDIR) == 0) return ASL_STATUS_INVALID_STORE;
237
238 out = (asl_store_t *)calloc(1, sizeof(asl_store_t));
239 if (out == NULL) return ASL_STATUS_NO_MEMORY;
240
241 if (basedir == NULL) out->base_dir = strdup(PATH_ASL_STORE);
242 else out->base_dir = strdup(basedir);
243
244 if (out->base_dir == NULL)
245 {
246 free(out);
247 return ASL_STATUS_NO_MEMORY;
248 }
249
250 *s = out;
251 return ASL_STATUS_OK;
252 }
253
254 uint32_t
255 asl_store_max_file_size(asl_store_t *s, size_t max)
256 {
257 if (s == NULL) return ASL_STATUS_INVALID_STORE;
258
259 s->max_file_size = max;
260 return ASL_STATUS_OK;
261 }
262
263 void
264 asl_store_file_closeall(asl_store_t *s)
265 {
266 uint32_t i;
267
268 if (s == NULL) return;
269
270 for (i = 0; i < FILE_CACHE_SIZE; i++)
271 {
272 if (s->file_cache[i].f != NULL) asl_file_close(s->file_cache[i].f);
273 s->file_cache[i].f = NULL;
274 if (s->file_cache[i].path != NULL) free(s->file_cache[i].path);
275 s->file_cache[i].path = NULL;
276 s->file_cache[i].u = -1;
277 s->file_cache[i].g = -1;
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_file_close(s->db);
290 asl_store_file_closeall(s);
291 if (s->storedata != NULL) fclose(s->storedata);
292
293 free(s);
294
295 return ASL_STATUS_OK;
296 }
297
298 uint32_t
299 asl_store_signal_sweep(asl_store_t *s)
300 {
301 char *str;
302 int semfd;
303
304 if (s == NULL) return ASL_STATUS_INVALID_STORE;
305
306 asprintf(&str, "%s/%s", s->base_dir, FILE_ASL_STORE_SWEEP_SEMAPHORE);
307 if (str == NULL) return ASL_STATUS_NO_MEMORY;
308
309 semfd = open(str, O_WRONLY | O_CREAT | O_NONBLOCK, 0644);
310 free(str);
311
312 if (semfd < 0) return ASL_STATUS_WRITE_FAILED;
313
314 close(semfd);
315 return ASL_STATUS_OK;
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)
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 (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].ts = 0;
345 }
346
347 if (s->file_cache[i].ts < s->file_cache[x].ts) x = i;
348 }
349
350 return x;
351 }
352
353 uint32_t
354 asl_store_sweep_file_cache(asl_store_t *s)
355 {
356 if (s == NULL) return ASL_STATUS_INVALID_STORE;
357
358 asl_store_file_cache_lru(s, time(NULL));
359 return ASL_STATUS_OK;
360 }
361
362 static uint32_t
363 asl_store_file_open_write(asl_store_t *s, char *subpath, int32_t ruid, int32_t rgid, asl_file_t **f, time_t now, uint32_t check_cache)
364 {
365 char *path;
366 mode_t m;
367 int32_t i, x, u, g;
368 uint32_t status;
369 asl_file_t *out;
370
371 if (s == NULL) return ASL_STATUS_INVALID_STORE;
372
373 /* see if the file is already open and in the cache */
374 for (i = 0; i < FILE_CACHE_SIZE; i++)
375 {
376 if ((s->file_cache[i].u == ruid) && (s->file_cache[i].g == rgid) && (s->file_cache[i].f != NULL))
377 {
378 s->file_cache[i].ts = now;
379 *f = s->file_cache[i].f;
380 if (check_cache == 1) asl_store_file_cache_lru(s, now);
381 return ASL_STATUS_OK;
382 }
383 }
384
385 path = NULL;
386 u = 0;
387 g = 0;
388 m = 0644;
389
390 if (ruid == -1)
391 {
392 if (rgid == -1)
393 {
394 asprintf(&path, "%s.asl", subpath);
395 }
396 else
397 {
398 g = rgid;
399 m = 0640;
400 asprintf(&path, "%s.G%d.asl", subpath, g);
401 }
402 }
403 else
404 {
405 u = ruid;
406 if (rgid == -1)
407 {
408 m = 0600;
409 asprintf(&path, "%s.U%d.asl", subpath, u);
410 }
411 else
412 {
413 g = rgid;
414 m = 0640;
415 asprintf(&path, "%s.U%d.G%u.asl", subpath, u, g);
416 }
417 }
418
419 if (path == NULL) return ASL_STATUS_NO_MEMORY;
420
421 out = NULL;
422 status = asl_file_open_write(path, m, u, g, &out);
423 if (status != ASL_STATUS_OK)
424 {
425 free(path);
426 return status;
427 }
428
429 x = asl_store_file_cache_lru(s, now);
430 if (s->file_cache[x].f != NULL) asl_file_close(s->file_cache[x].f);
431 if (s->file_cache[x].path != NULL) free(s->file_cache[x].path);
432
433 s->file_cache[x].f = out;
434 s->file_cache[x].path = path;
435 s->file_cache[x].u = ruid;
436 s->file_cache[x].g = rgid;
437 s->file_cache[x].ts = time(NULL);
438
439 *f = out;
440
441 return ASL_STATUS_OK;
442 }
443
444 char *
445 asl_store_file_path(asl_store_t *s, asl_file_t *f)
446 {
447 uint32_t i;
448
449 if (s == NULL) return NULL;
450
451 for (i = 0; i < FILE_CACHE_SIZE; i++)
452 {
453 if (s->file_cache[i].f == f)
454 {
455 if (s->file_cache[i].path == NULL) return NULL;
456 return strdup(s->file_cache[i].path);
457 }
458 }
459
460 return NULL;
461 }
462
463 void
464 asl_store_file_close(asl_store_t *s, asl_file_t *f)
465 {
466 uint32_t i;
467
468 if (s == NULL) return;
469
470 for (i = 0; i < FILE_CACHE_SIZE; i++)
471 {
472 if (s->file_cache[i].f == f)
473 {
474 asl_file_close(s->file_cache[i].f);
475 s->file_cache[i].f = NULL;
476 if (s->file_cache[i].path != NULL) free(s->file_cache[i].path);
477 s->file_cache[i].path = NULL;
478 s->file_cache[i].u = -1;
479 s->file_cache[i].g = -1;
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 t, now;
491 char *path, *subpath;
492 const char *val;
493 uid_t ruid;
494 gid_t rgid;
495 asl_file_t *f;
496 uint32_t status, check_cache;
497 asl_store_t *tmp;
498 uint64_t xid, ftime;
499 size_t fsize;
500
501 if (s == NULL) return ASL_STATUS_INVALID_STORE;
502 if (msg == NULL) return ASL_STATUS_INVALID_ARG;
503
504 now = time(NULL);
505
506 val = asl_get(msg, ASL_KEY_TIME);
507 t = 0;
508 if (val == NULL) t = now;
509 else t = asl_parse_time(val);
510
511 if (t >= s->start_tomorrow)
512 {
513 if (now >= s->start_tomorrow)
514 {
515 /* new day begins */
516 tmp = NULL;
517 status = asl_store_open_write(s->base_dir, &tmp);
518 asl_file_close(s->db);
519 s->db = NULL;
520 if (status != ASL_STATUS_OK)
521 {
522 fclose(s->storedata);
523 free(s->base_dir);
524 free(s);
525 return status;
526 }
527
528 s->db = tmp->db;
529 s->start_today = tmp->start_today;
530 s->start_tomorrow = tmp->start_tomorrow;
531 free(tmp->base_dir);
532 fclose(tmp->storedata);
533 free(tmp);
534
535 status = asl_store_signal_sweep(s);
536 /* allow this to fail quietly */
537 }
538 }
539
540 val = asl_get(msg, ASL_KEY_READ_UID);
541 ruid = -1;
542 if (val != NULL) ruid = atoi(val);
543
544 val = asl_get(msg, ASL_KEY_READ_GID);
545 rgid = -1;
546 if (val != NULL) rgid = atoi(val);
547
548 if (fseeko(s->storedata, 0, SEEK_SET) != 0) return ASL_STATUS_WRITE_FAILED;
549
550 xid = asl_core_htonq(s->next_id);
551 if (fwrite(&xid, sizeof(uint64_t), 1, s->storedata) != 1) return ASL_STATUS_WRITE_FAILED;
552
553 xid = s->next_id;
554 s->next_id++;
555
556 check_cache = 0;
557 if ((s->last_write + FILE_CACHE_TTL) <= now) check_cache = 1;
558
559 s->last_write = now;
560
561 if ((t >= s->start_today) && (t < s->start_tomorrow) && (ruid == -1) && (rgid == -1))
562 {
563 status = asl_file_save(s->db, msg, &xid);
564 if (check_cache == 1) asl_store_file_cache_lru(s, now);
565 return status;
566 }
567
568 if (localtime_r((const time_t *)&t, &ctm) == NULL) return ASL_STATUS_FAILED;
569
570 asprintf(&subpath, "%s/%d.%02d.%02d", s->base_dir, ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday);
571 if (subpath == NULL) return ASL_STATUS_NO_MEMORY;
572
573 f = NULL;
574 status = asl_store_file_open_write(s, subpath, ruid, rgid, &f, now, check_cache);
575 free(subpath);
576 subpath = NULL;
577
578 if (status != ASL_STATUS_OK) return status;
579
580 status = asl_file_save(f, msg, &xid);
581 if (status != ASL_STATUS_OK) return status;
582
583 fsize = asl_file_size(f);
584 ftime = asl_file_ctime(f);
585
586 /* if file is larger than max_file_size, rename it and create semaphore file in the store */
587 if ((s->max_file_size != 0) && (fsize > s->max_file_size))
588 {
589 status = ASL_STATUS_OK;
590
591 path = asl_store_file_path(s, f);
592 subpath = NULL;
593
594 asl_store_file_close(s, f);
595
596 if (path != NULL)
597 {
598 asprintf(&subpath, "%s.%llu", path, ftime);
599 if (subpath == NULL)
600 {
601 status = ASL_STATUS_NO_MEMORY;
602 }
603 else
604 {
605 if (rename(path, subpath) != 0) status = ASL_STATUS_FAILED;
606 free(subpath);
607 }
608
609 free(path);
610 }
611
612 if (status == ASL_STATUS_OK) status = asl_store_signal_sweep(s);
613 }
614
615 return status;
616 }
617
618 uint32_t
619 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)
620 {
621 DIR *dp;
622 struct dirent *dent;
623 uint32_t status;
624 asl_file_t *f;
625 char *path;
626 asl_file_list_t *files;
627
628 if (s == NULL) return ASL_STATUS_INVALID_STORE;
629 if (res == NULL) return ASL_STATUS_INVALID_ARG;
630
631 files = NULL;
632
633 /*
634 * Open all readable files
635 */
636 dp = opendir(s->base_dir);
637 if (dp == NULL) return ASL_STATUS_READ_FAILED;
638
639 while ((dent = readdir(dp)) != NULL)
640 {
641 if (dent->d_name[0] == '.') continue;
642
643 path = NULL;
644 asprintf(&path, "%s/%s", s->base_dir, dent->d_name);
645
646 /* 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 */
647 status = asl_file_open_read(path, &f);
648 if (path != NULL) free(path);
649 if ((status != ASL_STATUS_OK) || (f == NULL)) continue;
650
651 files = asl_file_list_add(files, f);
652 }
653
654 closedir(dp);
655
656 status = asl_file_list_match_timeout(files, query, res, last_id, start_id, count, direction, usec);
657 asl_file_list_close(files);
658 return status;
659 }
660
661 uint32_t
662 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)
663 {
664 return asl_store_match_timeout(s, query, res, last_id, start_id, count, direction, 0);
665 }
666
667 uint32_t
668 asl_store_match_start(asl_store_t *s, uint64_t start_id, int32_t direction)
669 {
670 DIR *dp;
671 struct dirent *dent;
672 uint32_t status;
673 asl_file_t *f;
674 char *path;
675 asl_file_list_t *files;
676
677 if (s == NULL) return ASL_STATUS_INVALID_STORE;
678
679 if (s->work != NULL) asl_file_list_match_end(s->work);
680 s->work = NULL;
681
682 files = NULL;
683
684 /*
685 * Open all readable files
686 */
687 dp = opendir(s->base_dir);
688 if (dp == NULL) return ASL_STATUS_READ_FAILED;
689
690 while ((dent = readdir(dp)) != NULL)
691 {
692 if (dent->d_name[0] == '.') continue;
693
694 path = NULL;
695 asprintf(&path, "%s/%s", s->base_dir, dent->d_name);
696
697 /* 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 */
698 status = asl_file_open_read(path, &f);
699 if (path != NULL) free(path);
700 if ((status != ASL_STATUS_OK) || (f == NULL)) continue;
701
702 files = asl_file_list_add(files, f);
703 }
704
705 closedir(dp);
706
707 s->work = asl_file_list_match_start(files, start_id, direction);
708 if (s->work == NULL) return ASL_STATUS_FAILED;
709
710 return ASL_STATUS_OK;
711 }
712
713 uint32_t
714 asl_store_match_next(asl_store_t *s, aslresponse query, aslresponse *res, uint32_t count)
715 {
716 if (s == NULL) return ASL_STATUS_INVALID_STORE;
717 if (s->work == NULL) return ASL_STATUS_OK;
718
719 return asl_file_list_match_next(s->work, query, res, count);
720 }