2 * Copyright (c) 2007-2008 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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
22 * @APPLE_LICENSE_HEADER_END@
30 #include <sys/types.h>
34 #include <asl_private.h>
36 #include <asl_store.h>
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
);
44 #define SECONDS_PER_DAY 86400
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.
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
56 * An external tool runs daily and deletes "old" files.
60 * The base directory contains a data file which stores
63 * | MAX_ID (uint64_t) |
67 asl_store_open_write(const char *basedir
, asl_store_t
**s
)
79 if (s
== NULL
) return ASL_STATUS_INVALID_ARG
;
81 if (basedir
== NULL
) basedir
= PATH_ASL_STORE
;
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
;
88 asprintf(&path
, "%s/%s", basedir
, FILE_ASL_STORE_DATA
);
89 if (path
== NULL
) return ASL_STATUS_NO_MEMORY
;
93 memset(&sb
, 0, sizeof(struct stat
));
94 if (stat(path
, &sb
) != 0)
99 return ASL_STATUS_FAILED
;
102 sd
= fopen(path
, "w+");
105 if (sd
== NULL
) return ASL_STATUS_FAILED
;
109 if (fwrite(&last_id
, sizeof(uint64_t), 1, sd
) != 1)
112 return ASL_STATUS_WRITE_FAILED
;
117 sd
= fopen(path
, "r+");
120 if (sd
== NULL
) return ASL_STATUS_FAILED
;
121 if (fread(&last_id
, sizeof(uint64_t), 1, sd
) != 1)
124 return ASL_STATUS_READ_FAILED
;
127 last_id
= asl_core_ntohq(last_id
);
130 memset(&ctm
, 0, sizeof(struct tm
));
133 if (localtime_r((const time_t *)&now
, &ctm
) == NULL
)
136 return ASL_STATUS_FAILED
;
140 asprintf(&subpath
, "%s/%d.%02d.%02d", basedir
, ctm
.tm_year
+ 1900, ctm
.tm_mon
+ 1, ctm
.tm_mday
);
144 return ASL_STATUS_NO_MEMORY
;
148 asprintf(&path
, "%s.asl", subpath
);
153 return ASL_STATUS_NO_MEMORY
;
157 status
= asl_file_open_write(path
, 0644, 0, 0, &db
);
159 if ((status
!= ASL_STATUS_OK
) || (db
== NULL
))
162 return ASL_STATUS_FAILED
;
165 out
= (asl_store_t
*)calloc(1, sizeof(asl_store_t
));
170 return ASL_STATUS_NO_MEMORY
;
173 if (basedir
== NULL
) out
->base_dir
= strdup(PATH_ASL_STORE
);
174 else out
->base_dir
= strdup(basedir
);
176 if (out
->base_dir
== NULL
)
181 return ASL_STATUS_NO_MEMORY
;
188 out
->start_today
= mktime(&ctm
);
189 out
->start_tomorrow
= out
->start_today
+ SECONDS_PER_DAY
;
192 out
->next_id
= last_id
+ 1;
194 for (i
= 0; i
< FILE_CACHE_SIZE
; i
++)
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;
202 return ASL_STATUS_OK
;
206 asl_store_statistics(asl_store_t
*s
, aslmsg
*msg
)
211 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
212 if (msg
== NULL
) return ASL_STATUS_INVALID_ARG
;
214 out
= (aslmsg
)calloc(1, sizeof(asl_msg_t
));
215 if (out
== NULL
) return ASL_STATUS_NO_MEMORY
;
217 snprintf(str
, sizeof(str
), "%u", s
->db
->string_count
);
218 asl_set(out
, "StringCount", str
);
221 return ASL_STATUS_OK
;
225 asl_store_open_read(const char *basedir
, asl_store_t
**s
)
230 if (s
== NULL
) return ASL_STATUS_INVALID_ARG
;
232 if (basedir
== NULL
) basedir
= PATH_ASL_STORE
;
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
;
238 out
= (asl_store_t
*)calloc(1, sizeof(asl_store_t
));
239 if (out
== NULL
) return ASL_STATUS_NO_MEMORY
;
241 if (basedir
== NULL
) out
->base_dir
= strdup(PATH_ASL_STORE
);
242 else out
->base_dir
= strdup(basedir
);
244 if (out
->base_dir
== NULL
)
247 return ASL_STATUS_NO_MEMORY
;
251 return ASL_STATUS_OK
;
255 asl_store_max_file_size(asl_store_t
*s
, size_t max
)
257 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
259 s
->max_file_size
= max
;
260 return ASL_STATUS_OK
;
264 asl_store_file_closeall(asl_store_t
*s
)
268 if (s
== NULL
) return;
270 for (i
= 0; i
< FILE_CACHE_SIZE
; i
++)
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;
283 asl_store_close(asl_store_t
*s
)
285 if (s
== NULL
) return ASL_STATUS_OK
;
287 if (s
->base_dir
!= NULL
) free(s
->base_dir
);
289 asl_file_close(s
->db
);
290 asl_store_file_closeall(s
);
291 if (s
->storedata
!= NULL
) fclose(s
->storedata
);
295 return ASL_STATUS_OK
;
299 asl_store_signal_sweep(asl_store_t
*s
)
304 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
306 asprintf(&str
, "%s/%s", s
->base_dir
, FILE_ASL_STORE_SWEEP_SEMAPHORE
);
307 if (str
== NULL
) return ASL_STATUS_NO_MEMORY
;
309 semfd
= open(str
, O_WRONLY
| O_CREAT
| O_NONBLOCK
, 0644);
312 if (semfd
< 0) return ASL_STATUS_WRITE_FAILED
;
315 return ASL_STATUS_OK
;
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.
324 asl_store_file_cache_lru(asl_store_t
*s
, time_t now
)
329 if (s
== NULL
) return 0;
332 min
= now
- FILE_CACHE_TTL
;
334 for (i
= 0; i
< FILE_CACHE_SIZE
; i
++)
336 if (s
->file_cache
[i
].ts
< min
)
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;
347 if (s
->file_cache
[i
].ts
< s
->file_cache
[x
].ts
) x
= i
;
354 asl_store_sweep_file_cache(asl_store_t
*s
)
356 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
358 asl_store_file_cache_lru(s
, time(NULL
));
359 return ASL_STATUS_OK
;
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
)
371 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
373 /* see if the file is already open and in the cache */
374 for (i
= 0; i
< FILE_CACHE_SIZE
; i
++)
376 if ((s
->file_cache
[i
].u
== ruid
) && (s
->file_cache
[i
].g
== rgid
) && (s
->file_cache
[i
].f
!= NULL
))
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
;
394 asprintf(&path
, "%s.asl", subpath
);
400 asprintf(&path
, "%s.G%d.asl", subpath
, g
);
409 asprintf(&path
, "%s.U%d.asl", subpath
, u
);
415 asprintf(&path
, "%s.U%d.G%u.asl", subpath
, u
, g
);
419 if (path
== NULL
) return ASL_STATUS_NO_MEMORY
;
422 status
= asl_file_open_write(path
, m
, u
, g
, &out
);
423 if (status
!= ASL_STATUS_OK
)
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
);
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
);
441 return ASL_STATUS_OK
;
445 asl_store_file_path(asl_store_t
*s
, asl_file_t
*f
)
449 if (s
== NULL
) return NULL
;
451 for (i
= 0; i
< FILE_CACHE_SIZE
; i
++)
453 if (s
->file_cache
[i
].f
== f
)
455 if (s
->file_cache
[i
].path
== NULL
) return NULL
;
456 return strdup(s
->file_cache
[i
].path
);
464 asl_store_file_close(asl_store_t
*s
, asl_file_t
*f
)
468 if (s
== NULL
) return;
470 for (i
= 0; i
< FILE_CACHE_SIZE
; i
++)
472 if (s
->file_cache
[i
].f
== f
)
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;
487 asl_store_save(asl_store_t
*s
, aslmsg msg
)
491 char *path
, *subpath
;
496 uint32_t status
, check_cache
;
501 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
502 if (msg
== NULL
) return ASL_STATUS_INVALID_ARG
;
506 val
= asl_get(msg
, ASL_KEY_TIME
);
508 if (val
== NULL
) t
= now
;
509 else t
= asl_parse_time(val
);
511 if (t
>= s
->start_tomorrow
)
513 if (now
>= s
->start_tomorrow
)
517 status
= asl_store_open_write(s
->base_dir
, &tmp
);
518 asl_file_close(s
->db
);
520 if (status
!= ASL_STATUS_OK
)
522 fclose(s
->storedata
);
529 s
->start_today
= tmp
->start_today
;
530 s
->start_tomorrow
= tmp
->start_tomorrow
;
532 fclose(tmp
->storedata
);
535 status
= asl_store_signal_sweep(s
);
536 /* allow this to fail quietly */
540 val
= asl_get(msg
, ASL_KEY_READ_UID
);
542 if (val
!= NULL
) ruid
= atoi(val
);
544 val
= asl_get(msg
, ASL_KEY_READ_GID
);
546 if (val
!= NULL
) rgid
= atoi(val
);
548 if (fseeko(s
->storedata
, 0, SEEK_SET
) != 0) return ASL_STATUS_WRITE_FAILED
;
550 xid
= asl_core_htonq(s
->next_id
);
551 if (fwrite(&xid
, sizeof(uint64_t), 1, s
->storedata
) != 1) return ASL_STATUS_WRITE_FAILED
;
557 if ((s
->last_write
+ FILE_CACHE_TTL
) <= now
) check_cache
= 1;
561 if ((t
>= s
->start_today
) && (t
< s
->start_tomorrow
) && (ruid
== -1) && (rgid
== -1))
563 status
= asl_file_save(s
->db
, msg
, &xid
);
564 if (check_cache
== 1) asl_store_file_cache_lru(s
, now
);
568 if (localtime_r((const time_t *)&t
, &ctm
) == NULL
) return ASL_STATUS_FAILED
;
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
;
574 status
= asl_store_file_open_write(s
, subpath
, ruid
, rgid
, &f
, now
, check_cache
);
578 if (status
!= ASL_STATUS_OK
) return status
;
580 status
= asl_file_save(f
, msg
, &xid
);
581 if (status
!= ASL_STATUS_OK
) return status
;
583 fsize
= asl_file_size(f
);
584 ftime
= asl_file_ctime(f
);
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
))
589 status
= ASL_STATUS_OK
;
591 path
= asl_store_file_path(s
, f
);
594 asl_store_file_close(s
, f
);
598 asprintf(&subpath
, "%s.%llu", path
, ftime
);
601 status
= ASL_STATUS_NO_MEMORY
;
605 if (rename(path
, subpath
) != 0) status
= ASL_STATUS_FAILED
;
612 if (status
== ASL_STATUS_OK
) status
= asl_store_signal_sweep(s
);
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
)
626 asl_file_list_t
*files
;
628 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
629 if (res
== NULL
) return ASL_STATUS_INVALID_ARG
;
634 * Open all readable files
636 dp
= opendir(s
->base_dir
);
637 if (dp
== NULL
) return ASL_STATUS_READ_FAILED
;
639 while ((dent
= readdir(dp
)) != NULL
)
641 if (dent
->d_name
[0] == '.') continue;
644 asprintf(&path
, "%s/%s", s
->base_dir
, dent
->d_name
);
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;
651 files
= asl_file_list_add(files
, f
);
656 status
= asl_file_list_match_timeout(files
, query
, res
, last_id
, start_id
, count
, direction
, usec
);
657 asl_file_list_close(files
);
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
)
664 return asl_store_match_timeout(s
, query
, res
, last_id
, start_id
, count
, direction
, 0);
668 asl_store_match_start(asl_store_t
*s
, uint64_t start_id
, int32_t direction
)
675 asl_file_list_t
*files
;
677 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
679 if (s
->work
!= NULL
) asl_file_list_match_end(s
->work
);
685 * Open all readable files
687 dp
= opendir(s
->base_dir
);
688 if (dp
== NULL
) return ASL_STATUS_READ_FAILED
;
690 while ((dent
= readdir(dp
)) != NULL
)
692 if (dent
->d_name
[0] == '.') continue;
695 asprintf(&path
, "%s/%s", s
->base_dir
, dent
->d_name
);
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;
702 files
= asl_file_list_add(files
, f
);
707 s
->work
= asl_file_list_match_start(files
, start_id
, direction
);
708 if (s
->work
== NULL
) return ASL_STATUS_FAILED
;
710 return ASL_STATUS_OK
;
714 asl_store_match_next(asl_store_t
*s
, aslresponse query
, aslresponse
*res
, uint32_t count
)
716 if (s
== NULL
) return ASL_STATUS_INVALID_STORE
;
717 if (s
->work
== NULL
) return ASL_STATUS_OK
;
719 return asl_file_list_match_next(s
->work
, query
, res
, count
);