2 * Copyright (c) 2009-2010 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
35 #include <asl_private.h>
38 #include <asl_store.h>
40 extern time_t asl_parse_time(const char *);
42 #define TEMP_NAME "_TMP_.asl"
43 #define STORE_DATA_FLAGS 0x00000000
45 static const char *store_path
= PATH_ASL_STORE
;
48 * Cache the output file for BB writes.
49 * we write messages in the order in which they were generated,
50 * so we are almost guaranteed to use the cache in most cases.
52 static asl_file_t
*cache_file
= NULL
;
53 static uid_t cache_uid
= -1;
54 static uid_t cache_gid
= -1;
55 static time_t cache_bb
= 0;
57 typedef struct name_list_s
60 struct name_list_s
*next
;
64 add_to_list(name_list_t
*l
, const char *name
)
68 if (name
== NULL
) return l
;
70 e
= (name_list_t
*)calloc(1, sizeof(name_list_t
));
71 if (e
== NULL
) return NULL
;
73 e
->name
= strdup(name
);
80 /* list is sorted by name (i.e. primarily by timestamp) */
81 if (l
== NULL
) return e
;
83 if (strcmp(e
->name
, l
->name
) <= 0)
89 for (x
= l
; (x
->next
!= NULL
) && (strcmp(e
->name
, x
->next
->name
) > 0) ; x
= x
->next
);
97 free_list(name_list_t
*l
)
112 /* find all messages that have an ASLExpireTime key */
114 do_ASLExpireTime_search(asl_store_t
*s
, asl_search_result_t
**out
)
116 asl_search_result_t q
, *query
, *res
;
121 qm
[0] = asl_msg_new(ASL_TYPE_QUERY
);
122 if (qm
[0] == NULL
) return ASL_STATUS_NO_MEMORY
;
129 if (asl_msg_set_key_val_op(qm
[0], ASL_KEY_EXPIRE_TIME
, NULL
, ASL_QUERY_OP_TRUE
) != 0)
131 asl_msg_release(qm
[0]);
132 return ASL_STATUS_NO_MEMORY
;
137 status
= asl_store_match(s
, query
, out
, &mid
, 0, 0, 1);
139 asl_msg_release(qm
[0]);
143 /* remove all messages that have an ASLExpireTime key */
145 do_ASLExpireTime_filter(const char *name
)
148 asl_file_t
*in
, *out
;
151 char *inpath
, *outpath
;
154 if (name
== NULL
) return ASL_STATUS_INVALID_ARG
;
158 asprintf(&inpath
, "%s/%s", store_path
, name
);
159 if (inpath
== NULL
) return ASL_STATUS_NO_MEMORY
;
161 memset(&sb
, 0, sizeof(struct stat
));
162 if (stat(inpath
, &sb
) < 0)
165 return ASL_STATUS_INVALID_STORE
;
168 status
= asl_file_open_read(inpath
, &in
);
169 if (status
!= ASL_STATUS_OK
)
172 return ASL_STATUS_OK
;
177 asprintf(&outpath
, "%s/%s", store_path
, TEMP_NAME
);
182 return ASL_STATUS_NO_MEMORY
;
185 status
= asl_file_open_write(outpath
, sb
.st_mode
, sb
.st_uid
, sb
.st_gid
, &out
);
186 if (status
!= ASL_STATUS_OK
)
194 out
->flags
= ASL_FILE_FLAG_PRESERVE_MSG_ID
;
197 while (asl_file_fetch_next(in
, &msg
) == ASL_STATUS_OK
)
199 if (msg
== NULL
) break;
203 if (asl_get(msg
, ASL_KEY_EXPIRE_TIME
) == NULL
) status
= asl_file_save(out
, msg
, &mid
);
208 if (status
!= ASL_STATUS_OK
) break;
215 rename(outpath
, inpath
);
223 /* qsort compare function for sorting by message ID */
225 sort_compare(const void *a
, const void *b
)
230 va
= asl_get(*(aslmsg
*)a
, ASL_KEY_MSG_ID
);
231 vb
= asl_get(*(aslmsg
*)b
, ASL_KEY_MSG_ID
);
233 if (va
== NULL
) return -1;
234 if (vb
== NULL
) return 1;
239 if (na
< nb
) return -1;
240 if (na
> nb
) return 1;
244 /* save a message to an appropriately named BB file */
246 save_bb_msg(aslmsg msg
)
253 char *path
, *tstring
;
259 if (msg
== NULL
) return ASL_STATUS_OK
;
261 val
= asl_get(msg
, ASL_KEY_EXPIRE_TIME
);
262 if (val
== NULL
) return ASL_STATUS_INVALID_ARG
;
263 msg_time
= asl_parse_time(val
);
265 val
= asl_get(msg
, ASL_KEY_READ_UID
);
267 if (val
!= NULL
) ruid
= atoi(val
);
269 val
= asl_get(msg
, ASL_KEY_READ_GID
);
271 if (val
!= NULL
) rgid
= atoi(val
);
273 if (localtime_r((const time_t *)&msg_time
, &ctm
) == NULL
) return ASL_STATUS_FAILED
;
276 * This supports 12 monthy "Best Before" buckets.
277 * We advance the actual expiry time to day zero of the following month.
278 * mktime() is clever enough to know that you actually mean the last day
279 * of the previous month. What we get back from localtime is the last
280 * day of the month in which the message expires, which we use in the name.
292 if (ruid
!= -1) u
= ruid
;
293 if (rgid
!= -1) g
= rgid
;
297 if (cache_file
!= NULL
)
299 if ((cache_uid
== u
) && (cache_gid
== g
) && (cache_bb
== bb
))
305 asl_file_close(cache_file
);
315 if (localtime_r((const time_t *)&bb
, &ctm
) == NULL
) return ASL_STATUS_FAILED
;
318 asprintf(&tstring
, "%s/BB.%d.%02d.%02d", store_path
, ctm
.tm_year
+ 1900, ctm
.tm_mon
+ 1, ctm
.tm_mday
);
319 if (tstring
== NULL
) return ASL_STATUS_NO_MEMORY
;
328 asprintf(&path
, "%s.asl", tstring
);
333 asprintf(&path
, "%s.G%d.asl", tstring
, g
);
341 asprintf(&path
, "%s.U%d.asl", tstring
, u
);
346 asprintf(&path
, "%s.U%d.G%u.asl", tstring
, u
, g
);
350 if (path
== NULL
) return ASL_STATUS_NO_MEMORY
;
352 status
= asl_file_open_write(path
, m
, u
, g
, &out
);
354 if (status
!= ASL_STATUS_OK
) return status
;
355 if (out
== NULL
) return ASL_STATUS_FAILED
;
357 out
->flags
= ASL_FILE_FLAG_PRESERVE_MSG_ID
;
365 status
= asl_file_save(out
, msg
, &mid
);
374 uint32_t store_flags
;
379 asprintf(&path
, "%s/%s", store_path
, FILE_ASL_STORE_DATA
);
381 sd
= fopen(path
, "a");
383 if (sd
== NULL
) return ASL_STATUS_WRITE_FAILED
;
385 store_flags
= STORE_DATA_FLAGS
;
386 status
= fwrite(&store_flags
, sizeof(uint32_t), 1, sd
);
389 if (status
!= 1) return ASL_STATUS_WRITE_FAILED
;
391 return ASL_STATUS_OK
;
395 * Utility to convert a data store with LongTTL files into
396 * a store with Best Before files.
398 * Returns quickly if the data store has already been converted.
400 * Older versions of the data store included messages with non-standard time-to-live
401 * records in the daily data files (yyyy.mm.dd.asl). When the files expired, aslmanager
402 * first copied messages with ASLExpireTime keys to a LongTTL file, then deleted the
403 * original data file.
405 * We now write ASLExpireTime messages to a Best Before file (BB.yyyy.mm.dd.asl)
406 * and aslmanager just deletes these files after the Best Before date has passed.
408 * If StoreData is bigger than 8 bytes, the store has been converted. Do nothing.
411 * Search the store for messages that have an ASLExpireTime.
412 * Sort by ASLMessageID
413 * Remove all BB.* files and all LongTTL.* files
414 * Write the ASLExpireTime messages into a new set of BB files
415 * Re-write each YMD file without messages that have an ASLExpireTime
416 * Add a new 4-byte flags field to StoreData
420 bb_convert(const char *name
)
425 asl_search_result_t
*expire_time_records
;
429 name_list_t
*list
, *e
;
432 if (name
!= NULL
) store_path
= name
;
434 /* StoreData must exist */
436 asprintf(&path
, "%s/%s", store_path
, FILE_ASL_STORE_DATA
);
437 if (path
== NULL
) return ASL_STATUS_NO_MEMORY
;
439 memset(&sb
, 0, sizeof(struct stat
));
442 if (i
!= 0) return ASL_STATUS_INVALID_STORE
;
444 /* must be a regular file */
445 if (!S_ISREG(sb
.st_mode
)) return ASL_STATUS_INVALID_STORE
;
447 /* check is the store has already been converted */
448 if (sb
.st_size
> sizeof(uint64_t)) return ASL_STATUS_OK
;
450 /* find ASLExpireTime messages */
451 status
= asl_store_open_read(store_path
, &store
);
452 if (status
!= ASL_STATUS_OK
) return status
;
454 expire_time_records
= NULL
;
455 status
= do_ASLExpireTime_search(store
, &expire_time_records
);
457 asl_store_close(store
);
458 if (status
!= ASL_STATUS_OK
) return status
;
460 /* unlink BB.* and LongTTL.* */
461 dp
= opendir(store_path
);
462 if (dp
== NULL
) return ASL_STATUS_READ_FAILED
;
464 while ((dent
= readdir(dp
)) != NULL
)
466 if ((!strncmp(dent
->d_name
, "BB.", 3)) || (!strncmp(dent
->d_name
, "LongTTL.", 8)))
469 asprintf(&path
, "%s/%s", store_path
, dent
->d_name
);
473 return ASL_STATUS_NO_MEMORY
;
483 if ((expire_time_records
== NULL
) || (expire_time_records
->count
== 0)) return finish_conversion();
485 /* sort by ASLMessageID */
486 qsort(expire_time_records
->msg
, expire_time_records
->count
, sizeof(aslmsg
), sort_compare
);
488 /* save the ASLExpireTime messages into a new set of BB files */
489 for (i
= 0; i
< expire_time_records
->count
; i
++)
491 status
= save_bb_msg((aslmsg
)expire_time_records
->msg
[i
]);
492 if (status
!= ASL_STATUS_OK
)
494 if (cache_file
!= NULL
) asl_file_close(cache_file
);
499 if (cache_file
!= NULL
) asl_file_close(cache_file
);
501 aslresponse_free(expire_time_records
);
503 /* Re-write each YMD file without messages that have an ASLExpireTime */
504 dp
= opendir(store_path
);
505 if (dp
== NULL
) return ASL_STATUS_READ_FAILED
;
509 while ((dent
= readdir(dp
)) != NULL
)
511 if ((dent
->d_name
[0] < '0') || (dent
->d_name
[0] > '9')) continue;
512 list
= add_to_list(list
, dent
->d_name
);
518 for (e
= list
; e
!= NULL
; e
= e
->next
)
520 status
= do_ASLExpireTime_filter(e
->name
);
521 if (status
!= ASL_STATUS_OK
)
530 return finish_conversion();