2 * Copyright (c) 2007-2008 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 #define SECONDS_PER_DAY 86400
41 #define DEFAULT_MAX_SIZE 51200000
44 typedef struct name_list_s
48 struct name_list_s
*next
;
52 mgr_exit(const char *store
, int status
)
56 if (store
== NULL
) exit(status
);
59 asprintf(&s
, "%s/%s", store
, FILE_ASL_STORE_SWEEP_SEMAPHORE
);
71 add_to_list(name_list_t
*l
, const char *name
, size_t size
)
75 if (name
== NULL
) return l
;
77 e
= (name_list_t
*)calloc(1, sizeof(name_list_t
));
78 if (e
== NULL
) return NULL
;
80 e
->name
= strdup(name
);
89 /* list is sorted by name (i.e. primarily by timestamp) */
90 if (l
== NULL
) return e
;
92 if (strcmp(e
->name
, l
->name
) <= 0)
98 for (x
= l
; (x
->next
!= NULL
) && (strcmp(e
->name
, x
->next
->name
) > 0) ; x
= x
->next
);
106 free_list(name_list_t
*l
)
122 do_match(const char *infile
, const char *outfile
, int do_ttl
, time_t expire_time
)
124 asl_search_result_t q
, *query
, *res
;
125 asl_msg_t
*m
, *qm
[1];
126 asl_file_t
*in
, *out
;
131 if (infile
== NULL
) return ASL_STATUS_INVALID_ARG
;
132 if (outfile
== NULL
) return ASL_STATUS_INVALID_ARG
;
135 status
= asl_file_open_read(infile
, &in
);
136 if (status
!= ASL_STATUS_OK
) return status
;
148 m
= asl_new(ASL_TYPE_QUERY
);
152 return ASL_STATUS_NO_MEMORY
;
157 if (expire_time
!= 0)
159 snprintf(str
, sizeof(str
), "%llu", (long long unsigned int)expire_time
);
160 if (asl_set_query(m
, ASL_KEY_EXPIRE_TIME
, str
, ASL_QUERY_OP_NUMERIC
| ASL_QUERY_OP_GREATER_EQUAL
) != 0)
164 return ASL_STATUS_NO_MEMORY
;
169 if (asl_set_query(m
, ASL_KEY_EXPIRE_TIME
, NULL
, ASL_QUERY_OP_TRUE
) != 0)
173 return ASL_STATUS_NO_MEMORY
;
180 status
= asl_file_match(in
, query
, &res
, &mid
, 0, 0, 1);
181 if (m
!= NULL
) asl_free(m
);
184 if (status
!= ASL_STATUS_OK
) return status
;
187 * N.B. "ASL_STATUS_NOT_FOUND" is never returned by asl_file_match.
188 * We use it here to signal the caller that no records were found by the match.
190 if (res
== NULL
) return ASL_STATUS_NOT_FOUND
;
193 aslresponse_free(res
);
194 return ASL_STATUS_NOT_FOUND
;
198 status
= asl_file_open_write(outfile
, 0644, -1, -1, &out
);
199 if (status
!= ASL_STATUS_OK
) return status
;
201 out
->flags
= ASL_FILE_FLAG_UNLIMITED_CACHE
| ASL_FILE_FLAG_PRESERVE_MSG_ID
;
203 for (i
= 0; i
< res
->count
; i
++)
206 status
= asl_file_save(out
, res
->msg
[i
], &mid
);
207 if (status
!= ASL_STATUS_OK
) break;
215 main(int argc
, const char *argv
[])
217 int i
, bbstrlen
, debug
;
218 const char *archive
, *store_dir
;
219 time_t now
, best_before
, ttl
;
221 char bbstr
[32], *str
, *p
;
224 name_list_t
*list
, *e
;
226 size_t file_size
, store_size
, max_size
;
232 store_dir
= PATH_ASL_STORE
;
233 ttl
= DEFAULT_TTL
* SECONDS_PER_DAY
;
234 max_size
= DEFAULT_MAX_SIZE
;
238 for (i
= 1; i
< argc
; i
++)
240 if (!strcmp(argv
[i
], "-a"))
242 if (((i
+ 1) < argc
) && (argv
[i
+ 1][0] != '-')) archive
= argv
[++i
];
243 else archive
= PATH_ASL_ARCHIVE
;
245 else if (!strcmp(argv
[i
], "-s"))
247 if (((i
+ 1) < argc
) && (argv
[i
+ 1][0] != '-')) store_dir
= argv
[++i
];
249 else if (!strcmp(argv
[i
], "-ttl"))
251 if (((i
+ 1) < argc
) && (argv
[i
+ 1][0] != '-')) ttl
= atoi(argv
[++i
]) * SECONDS_PER_DAY
;
253 else if (!strcmp(argv
[i
], "-size"))
255 if (((i
+ 1) < argc
) && (argv
[i
+ 1][0] != '-')) max_size
= atoi(argv
[++i
]);
257 else if (!strcmp(argv
[i
], "-d"))
266 memset(&sb
, 0, sizeof(struct stat
));
267 if (stat(archive
, &sb
) == 0)
269 /* must be a directory */
270 if ((sb
.st_mode
& S_IFDIR
) == 0)
272 fprintf(stderr
, "aslmanager error: archive %s is not a directory", archive
);
280 /* archive doesn't exist - create it */
281 if (mkdir(archive
, 0755) != 0)
283 fprintf(stderr
, "aslmanager error: can't create archive %s: %s\n", archive
, strerror(errno
));
289 /* stat failed for some other reason */
290 fprintf(stderr
, "aslmanager error: can't stat archive %s: %s\n", archive
, strerror(errno
));
298 /* determine current time and time TTL ago */
301 if (ttl
> 0) best_before
= now
- ttl
;
303 /* construct best before date as YYYY.MM.DD */
304 memset(&ctm
, 0, sizeof(struct tm
));
305 if (localtime_r((const time_t *)&best_before
, &ctm
) == NULL
) mgr_exit(store_dir
, 1);
307 snprintf(bbstr
, sizeof(bbstr
), "%d.%02d.%02d.", ctm
.tm_year
+ 1900, ctm
.tm_mon
+ 1, ctm
.tm_mday
);
308 bbstrlen
= strlen(bbstr
);
310 if (debug
== 1) printf("Best Before Date %s\n", bbstr
);
312 dp
= opendir(store_dir
);
313 if (dp
== NULL
) mgr_exit(store_dir
, 1);
315 /* gather a list of files for dates before the best before date */
317 while ((dent
= readdir(dp
)) != NULL
)
319 if ((dent
->d_name
[0] < '0') || (dent
->d_name
[0] > '9')) continue;
321 memset(&sb
, 0, sizeof(struct stat
));
323 if (stat(dent
->d_name
, &sb
) == 0) file_size
= sb
.st_size
;
324 store_size
+= file_size
;
326 list
= add_to_list(list
, dent
->d_name
, file_size
);
333 printf("\nData Store Size = %lu\n", store_size
);
334 printf("\nData Store Files\n");
335 for (e
= list
; e
!= NULL
; e
= e
->next
) printf(" %s %lu\n", e
->name
, e
->size
);
338 /* copy messages in each expired file with ASLExpireTime values to LongTTL files */
339 if (debug
== 1) printf("\nStart Scan\n");
344 if ((store_size
<= max_size
) && (strncmp(e
->name
, bbstr
, bbstrlen
) >= 0)) break;
346 /* find '.' after year */
347 p
= strchr(e
->name
, '.');
348 if (p
== NULL
) continue;
350 /* find '.' after month */
353 if (p
== NULL
) continue;
355 /* find '.' after day */
358 if (p
== NULL
) continue;
361 asprintf(&str
, "LongTTL%s", p
);
362 if (str
== NULL
) mgr_exit(store_dir
, 1);
364 /* syslog -x [str] -db [e->name] -k ASLExpireTime */
365 if (debug
== 1) printf(" scan %s ---> %s\n", e
->name
, str
);
366 else status
= do_match(e
->name
, str
, 1, 0);
374 asprintf(&str
, "%s/%s", archive
, e
->name
);
375 if (str
== NULL
) mgr_exit(store_dir
, 1);
377 /* syslog -x [str] -db [e->name] */
378 if (debug
== 1) printf(" copy %s ---> %s\n", e
->name
, str
);
379 else status
= do_match(e
->name
, str
, 0, 0);
383 if (debug
== 1) printf(" unlink %s\n", e
->name
);
384 else unlink(e
->name
);
386 store_size
-= e
->size
;
394 printf("Finished Scan\n");
395 printf("\nData Store Size = %lu\n", store_size
);
401 dp
= opendir(PATH_ASL_STORE
);
402 if (dp
== NULL
) mgr_exit(store_dir
, 1);
404 /* gather a list of LongTTL files */
406 while ((dent
= readdir(dp
)) != NULL
)
408 if (!strncmp(dent
->d_name
, "LongTTL.", 8)) list
= add_to_list(list
, dent
->d_name
, 0);
415 printf("\nData Store LongTTL Files\n");
416 for (e
= list
; e
!= NULL
; e
= e
->next
) printf(" %s\n", e
->name
);
419 if (debug
== 1) printf("\nScan for expired messages\n");
424 /* syslog -x LongTTL.new -db [e->name] -k ASLExpireTime Nge [now] */
427 printf(" %s\n", e
->name
);
431 status
= do_match(e
->name
, "LongTTL.new", 1, now
);
433 if (status
== ASL_STATUS_OK
) rename("LongTTL.new", e
->name
);
439 if (debug
== 1) printf("Finished scan for expired messages\n");
444 mgr_exit(store_dir
, 0);