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 #if TARGET_IPHONE_SIMULATOR
46 const char *store_path
;
48 static const char *store_path
= PATH_ASL_STORE
;
52 * Cache the output file for BB writes.
53 * we write messages in the order in which they were generated,
54 * so we are almost guaranteed to use the cache in most cases.
56 static asl_file_t
*cache_file
= NULL
;
57 static uid_t cache_uid
= -1;
58 static uid_t cache_gid
= -1;
59 static time_t cache_bb
= 0;
61 typedef struct name_list_s
64 struct name_list_s
*next
;
68 add_to_list(name_list_t
*l
, const char *name
)
72 if (name
== NULL
) return l
;
74 e
= (name_list_t
*)calloc(1, sizeof(name_list_t
));
75 if (e
== NULL
) return NULL
;
77 e
->name
= strdup(name
);
84 /* list is sorted by name (i.e. primarily by timestamp) */
85 if (l
== NULL
) return e
;
87 if (strcmp(e
->name
, l
->name
) <= 0)
93 for (x
= l
; (x
->next
!= NULL
) && (strcmp(e
->name
, x
->next
->name
) > 0) ; x
= x
->next
);
101 free_list(name_list_t
*l
)
116 /* find all messages that have an ASLExpireTime key */
118 do_ASLExpireTime_search(asl_store_t
*s
, asl_search_result_t
**out
)
120 asl_search_result_t q
, *query
, *res
;
125 qm
[0] = asl_msg_new(ASL_TYPE_QUERY
);
126 if (qm
[0] == NULL
) return ASL_STATUS_NO_MEMORY
;
133 if (asl_msg_set_key_val_op(qm
[0], ASL_KEY_EXPIRE_TIME
, NULL
, ASL_QUERY_OP_TRUE
) != 0)
135 asl_msg_release(qm
[0]);
136 return ASL_STATUS_NO_MEMORY
;
141 status
= asl_store_match(s
, query
, out
, &mid
, 0, 0, 1);
143 asl_msg_release(qm
[0]);
147 /* remove all messages that have an ASLExpireTime key */
149 do_ASLExpireTime_filter(const char *name
)
152 asl_file_t
*in
, *out
;
155 char *inpath
, *outpath
;
158 if (name
== NULL
) return ASL_STATUS_INVALID_ARG
;
162 asprintf(&inpath
, "%s/%s", store_path
, name
);
163 if (inpath
== NULL
) return ASL_STATUS_NO_MEMORY
;
165 memset(&sb
, 0, sizeof(struct stat
));
166 if (stat(inpath
, &sb
) < 0)
169 return ASL_STATUS_INVALID_STORE
;
172 status
= asl_file_open_read(inpath
, &in
);
173 if (status
!= ASL_STATUS_OK
)
176 return ASL_STATUS_OK
;
181 asprintf(&outpath
, "%s/%s", store_path
, TEMP_NAME
);
186 return ASL_STATUS_NO_MEMORY
;
189 status
= asl_file_open_write(outpath
, sb
.st_mode
, sb
.st_uid
, sb
.st_gid
, &out
);
190 if (status
!= ASL_STATUS_OK
)
198 out
->flags
= ASL_FILE_FLAG_PRESERVE_MSG_ID
;
201 while (asl_file_fetch_next(in
, &msg
) == ASL_STATUS_OK
)
203 if (msg
== NULL
) break;
207 if (asl_get(msg
, ASL_KEY_EXPIRE_TIME
) == NULL
) status
= asl_file_save(out
, msg
, &mid
);
212 if (status
!= ASL_STATUS_OK
) break;
219 rename(outpath
, inpath
);
227 /* qsort compare function for sorting by message ID */
229 sort_compare(const void *a
, const void *b
)
234 va
= asl_get(*(aslmsg
*)a
, ASL_KEY_MSG_ID
);
235 vb
= asl_get(*(aslmsg
*)b
, ASL_KEY_MSG_ID
);
237 if (va
== NULL
) return -1;
238 if (vb
== NULL
) return 1;
243 if (na
< nb
) return -1;
244 if (na
> nb
) return 1;
248 /* save a message to an appropriately named BB file */
250 save_bb_msg(aslmsg msg
)
257 char *path
, *tstring
;
263 if (msg
== NULL
) return ASL_STATUS_OK
;
265 val
= asl_get(msg
, ASL_KEY_EXPIRE_TIME
);
266 if (val
== NULL
) return ASL_STATUS_INVALID_ARG
;
267 msg_time
= asl_parse_time(val
);
269 val
= asl_get(msg
, ASL_KEY_READ_UID
);
271 if (val
!= NULL
) ruid
= atoi(val
);
273 val
= asl_get(msg
, ASL_KEY_READ_GID
);
275 if (val
!= NULL
) rgid
= atoi(val
);
277 if (localtime_r((const time_t *)&msg_time
, &ctm
) == NULL
) return ASL_STATUS_FAILED
;
280 * This supports 12 monthy "Best Before" buckets.
281 * We advance the actual expiry time to day zero of the following month.
282 * mktime() is clever enough to know that you actually mean the last day
283 * of the previous month. What we get back from localtime is the last
284 * day of the month in which the message expires, which we use in the name.
296 if (ruid
!= -1) u
= ruid
;
297 if (rgid
!= -1) g
= rgid
;
301 if (cache_file
!= NULL
)
303 if ((cache_uid
== u
) && (cache_gid
== g
) && (cache_bb
== bb
))
309 asl_file_close(cache_file
);
319 if (localtime_r((const time_t *)&bb
, &ctm
) == NULL
) return ASL_STATUS_FAILED
;
322 asprintf(&tstring
, "%s/BB.%d.%02d.%02d", store_path
, ctm
.tm_year
+ 1900, ctm
.tm_mon
+ 1, ctm
.tm_mday
);
323 if (tstring
== NULL
) return ASL_STATUS_NO_MEMORY
;
332 asprintf(&path
, "%s.asl", tstring
);
337 asprintf(&path
, "%s.G%d.asl", tstring
, g
);
345 asprintf(&path
, "%s.U%d.asl", tstring
, u
);
350 asprintf(&path
, "%s.U%d.G%u.asl", tstring
, u
, g
);
354 if (path
== NULL
) return ASL_STATUS_NO_MEMORY
;
356 status
= asl_file_open_write(path
, m
, u
, g
, &out
);
358 if (status
!= ASL_STATUS_OK
) return status
;
359 if (out
== NULL
) return ASL_STATUS_FAILED
;
361 out
->flags
= ASL_FILE_FLAG_PRESERVE_MSG_ID
;
369 status
= asl_file_save(out
, msg
, &mid
);
378 uint32_t store_flags
;
383 asprintf(&path
, "%s/%s", store_path
, FILE_ASL_STORE_DATA
);
385 sd
= fopen(path
, "a");
387 if (sd
== NULL
) return ASL_STATUS_WRITE_FAILED
;
389 store_flags
= STORE_DATA_FLAGS
;
390 status
= fwrite(&store_flags
, sizeof(uint32_t), 1, sd
);
393 if (status
!= 1) return ASL_STATUS_WRITE_FAILED
;
395 return ASL_STATUS_OK
;
399 * Utility to convert a data store with LongTTL files into
400 * a store with Best Before files.
402 * Returns quickly if the data store has already been converted.
404 * Older versions of the data store included messages with non-standard time-to-live
405 * records in the daily data files (yyyy.mm.dd.asl). When the files expired, aslmanager
406 * first copied messages with ASLExpireTime keys to a LongTTL file, then deleted the
407 * original data file.
409 * We now write ASLExpireTime messages to a Best Before file (BB.yyyy.mm.dd.asl)
410 * and aslmanager just deletes these files after the Best Before date has passed.
412 * If StoreData is bigger than 8 bytes, the store has been converted. Do nothing.
415 * Search the store for messages that have an ASLExpireTime.
416 * Sort by ASLMessageID
417 * Remove all BB.* files and all LongTTL.* files
418 * Write the ASLExpireTime messages into a new set of BB files
419 * Re-write each YMD file without messages that have an ASLExpireTime
420 * Add a new 4-byte flags field to StoreData
424 bb_convert(const char *name
)
429 asl_search_result_t
*expire_time_records
;
433 name_list_t
*list
, *e
;
436 if (name
!= NULL
) store_path
= name
;
438 /* StoreData must exist */
440 asprintf(&path
, "%s/%s", store_path
, FILE_ASL_STORE_DATA
);
441 if (path
== NULL
) return ASL_STATUS_NO_MEMORY
;
443 memset(&sb
, 0, sizeof(struct stat
));
446 if (i
!= 0) return ASL_STATUS_INVALID_STORE
;
448 /* must be a regular file */
449 if (!S_ISREG(sb
.st_mode
)) return ASL_STATUS_INVALID_STORE
;
451 /* check is the store has already been converted */
452 if (sb
.st_size
> sizeof(uint64_t)) return ASL_STATUS_OK
;
454 /* find ASLExpireTime messages */
455 status
= asl_store_open_read(store_path
, &store
);
456 if (status
!= ASL_STATUS_OK
) return status
;
458 expire_time_records
= NULL
;
459 status
= do_ASLExpireTime_search(store
, &expire_time_records
);
461 asl_store_close(store
);
462 if (status
!= ASL_STATUS_OK
) return status
;
464 /* unlink BB.* and LongTTL.* */
465 dp
= opendir(store_path
);
466 if (dp
== NULL
) return ASL_STATUS_READ_FAILED
;
468 while ((dent
= readdir(dp
)) != NULL
)
470 if ((!strncmp(dent
->d_name
, "BB.", 3)) || (!strncmp(dent
->d_name
, "LongTTL.", 8)))
473 asprintf(&path
, "%s/%s", store_path
, dent
->d_name
);
477 return ASL_STATUS_NO_MEMORY
;
487 if ((expire_time_records
== NULL
) || (expire_time_records
->count
== 0)) return finish_conversion();
489 /* sort by ASLMessageID */
490 qsort(expire_time_records
->msg
, expire_time_records
->count
, sizeof(aslmsg
), sort_compare
);
492 /* save the ASLExpireTime messages into a new set of BB files */
493 for (i
= 0; i
< expire_time_records
->count
; i
++)
495 status
= save_bb_msg((aslmsg
)expire_time_records
->msg
[i
]);
496 if (status
!= ASL_STATUS_OK
)
498 if (cache_file
!= NULL
) asl_file_close(cache_file
);
503 if (cache_file
!= NULL
) asl_file_close(cache_file
);
505 aslresponse_free(expire_time_records
);
507 /* Re-write each YMD file without messages that have an ASLExpireTime */
508 dp
= opendir(store_path
);
509 if (dp
== NULL
) return ASL_STATUS_READ_FAILED
;
513 while ((dent
= readdir(dp
)) != NULL
)
515 if ((dent
->d_name
[0] < '0') || (dent
->d_name
[0] > '9')) continue;
516 list
= add_to_list(list
, dent
->d_name
);
521 for (e
= list
; e
!= NULL
; e
= e
->next
)
523 status
= do_ASLExpireTime_filter(e
->name
);
524 if (status
!= ASL_STATUS_OK
)
533 return finish_conversion();