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 #define LONGTTL_TMP_FILE "LongTTL.new"
46 typedef struct name_list_s
50 struct name_list_s
*next
;
54 mgr_exit(const char *store
, int status
)
58 if (store
== NULL
) exit(status
);
61 asprintf(&s
, "%s/%s", store
, FILE_ASL_STORE_SWEEP_SEMAPHORE
);
73 add_to_list(name_list_t
*l
, const char *name
, size_t size
)
77 if (name
== NULL
) return l
;
79 e
= (name_list_t
*)calloc(1, sizeof(name_list_t
));
80 if (e
== NULL
) return NULL
;
82 e
->name
= strdup(name
);
91 /* list is sorted by name (i.e. primarily by timestamp) */
92 if (l
== NULL
) return e
;
94 if (strcmp(e
->name
, l
->name
) <= 0)
100 for (x
= l
; (x
->next
!= NULL
) && (strcmp(e
->name
, x
->next
->name
) > 0) ; x
= x
->next
);
108 free_list(name_list_t
*l
)
124 do_match(const char *infile
, const char *outfile
, int do_ttl
, time_t expire_time
)
126 asl_search_result_t q
, *query
, *res
;
127 asl_msg_t
*m
, *qm
[1];
128 asl_file_t
*in
, *out
;
133 if (infile
== NULL
) return ASL_STATUS_INVALID_ARG
;
134 if (outfile
== NULL
) return ASL_STATUS_INVALID_ARG
;
137 status
= asl_file_open_read(infile
, &in
);
138 if (status
!= ASL_STATUS_OK
) return status
;
150 m
= asl_new(ASL_TYPE_QUERY
);
154 return ASL_STATUS_NO_MEMORY
;
159 if (expire_time
!= 0)
161 snprintf(str
, sizeof(str
), "%llu", (long long unsigned int)expire_time
);
162 if (asl_set_query(m
, ASL_KEY_EXPIRE_TIME
, str
, ASL_QUERY_OP_NUMERIC
| ASL_QUERY_OP_GREATER_EQUAL
) != 0)
166 return ASL_STATUS_NO_MEMORY
;
171 if (asl_set_query(m
, ASL_KEY_EXPIRE_TIME
, NULL
, ASL_QUERY_OP_TRUE
) != 0)
175 return ASL_STATUS_NO_MEMORY
;
182 status
= asl_file_match(in
, query
, &res
, &mid
, 0, 0, 1);
183 if (m
!= NULL
) asl_free(m
);
186 if (status
!= ASL_STATUS_OK
) return status
;
189 * N.B. "ASL_STATUS_NOT_FOUND" is never returned by asl_file_match.
190 * We use it here to signal the caller that no records were found by the match.
192 if (res
== NULL
) return ASL_STATUS_NOT_FOUND
;
195 aslresponse_free(res
);
196 return ASL_STATUS_NOT_FOUND
;
200 status
= asl_file_open_write(outfile
, 0644, -1, -1, &out
);
201 if (status
!= ASL_STATUS_OK
) return status
;
203 out
->flags
= ASL_FILE_FLAG_UNLIMITED_CACHE
| ASL_FILE_FLAG_PRESERVE_MSG_ID
;
205 for (i
= 0; i
< res
->count
; i
++)
208 status
= asl_file_save(out
, res
->msg
[i
], &mid
);
209 if (status
!= ASL_STATUS_OK
) break;
217 main(int argc
, const char *argv
[])
219 int i
, bbstrlen
, debug
;
220 const char *archive
, *store_dir
;
221 time_t now
, best_before
, ttl
;
223 char bbstr
[32], *str
, *p
;
226 name_list_t
*list
, *e
;
228 size_t file_size
, store_size
, max_size
;
234 store_dir
= PATH_ASL_STORE
;
235 ttl
= DEFAULT_TTL
* SECONDS_PER_DAY
;
236 max_size
= DEFAULT_MAX_SIZE
;
240 for (i
= 1; i
< argc
; i
++)
242 if (!strcmp(argv
[i
], "-a"))
244 if (((i
+ 1) < argc
) && (argv
[i
+ 1][0] != '-')) archive
= argv
[++i
];
245 else archive
= PATH_ASL_ARCHIVE
;
247 else if (!strcmp(argv
[i
], "-s"))
249 if (((i
+ 1) < argc
) && (argv
[i
+ 1][0] != '-')) store_dir
= argv
[++i
];
251 else if (!strcmp(argv
[i
], "-ttl"))
253 if (((i
+ 1) < argc
) && (argv
[i
+ 1][0] != '-')) ttl
= atoi(argv
[++i
]) * SECONDS_PER_DAY
;
255 else if (!strcmp(argv
[i
], "-size"))
257 if (((i
+ 1) < argc
) && (argv
[i
+ 1][0] != '-')) max_size
= atoi(argv
[++i
]);
259 else if (!strcmp(argv
[i
], "-d"))
268 memset(&sb
, 0, sizeof(struct stat
));
269 if (stat(archive
, &sb
) == 0)
271 /* must be a directory */
272 if ((sb
.st_mode
& S_IFDIR
) == 0)
274 fprintf(stderr
, "aslmanager error: archive %s is not a directory", archive
);
282 /* archive doesn't exist - create it */
283 if (mkdir(archive
, 0755) != 0)
285 fprintf(stderr
, "aslmanager error: can't create archive %s: %s\n", archive
, strerror(errno
));
291 /* stat failed for some other reason */
292 fprintf(stderr
, "aslmanager error: can't stat archive %s: %s\n", archive
, strerror(errno
));
300 /* determine current time and time TTL ago */
303 if (ttl
> 0) best_before
= now
- ttl
;
305 /* construct best before date as YYYY.MM.DD */
306 memset(&ctm
, 0, sizeof(struct tm
));
307 if (localtime_r((const time_t *)&best_before
, &ctm
) == NULL
) mgr_exit(store_dir
, 1);
309 snprintf(bbstr
, sizeof(bbstr
), "%d.%02d.%02d.", ctm
.tm_year
+ 1900, ctm
.tm_mon
+ 1, ctm
.tm_mday
);
310 bbstrlen
= strlen(bbstr
);
312 if (debug
== 1) printf("Best Before Date %s\n", bbstr
);
314 dp
= opendir(store_dir
);
315 if (dp
== NULL
) mgr_exit(store_dir
, 1);
317 /* gather a list of files for dates before the best before date */
319 while ((dent
= readdir(dp
)) != NULL
)
321 if ((dent
->d_name
[0] < '0') || (dent
->d_name
[0] > '9')) continue;
323 memset(&sb
, 0, sizeof(struct stat
));
325 if (stat(dent
->d_name
, &sb
) == 0) file_size
= sb
.st_size
;
326 store_size
+= file_size
;
328 list
= add_to_list(list
, dent
->d_name
, file_size
);
335 printf("\nData Store Size = %lu\n", store_size
);
336 printf("\nData Store Files\n");
337 for (e
= list
; e
!= NULL
; e
= e
->next
) printf(" %s %lu\n", e
->name
, e
->size
);
340 /* copy messages in each expired file with ASLExpireTime values to LongTTL files */
341 if (debug
== 1) printf("\nStart Scan\n");
346 if ((store_size
<= max_size
) && (strncmp(e
->name
, bbstr
, bbstrlen
) >= 0)) break;
348 /* find '.' after year */
349 p
= strchr(e
->name
, '.');
350 if (p
== NULL
) continue;
352 /* find '.' after month */
355 if (p
== NULL
) continue;
357 /* find '.' after day */
360 if (p
== NULL
) continue;
363 asprintf(&str
, "LongTTL%s", p
);
364 if (str
== NULL
) mgr_exit(store_dir
, 1);
366 /* syslog -x [str] -db [e->name] -k ASLExpireTime */
367 if (debug
== 1) printf(" scan %s ---> %s\n", e
->name
, str
);
368 else status
= do_match(e
->name
, str
, 1, 0);
376 asprintf(&str
, "%s/%s", archive
, e
->name
);
377 if (str
== NULL
) mgr_exit(store_dir
, 1);
379 /* syslog -x [str] -db [e->name] */
380 if (debug
== 1) printf(" copy %s ---> %s\n", e
->name
, str
);
381 else status
= do_match(e
->name
, str
, 0, 0);
385 if (debug
== 1) printf(" unlink %s\n", e
->name
);
386 else unlink(e
->name
);
388 store_size
-= e
->size
;
396 printf("Finished Scan\n");
397 printf("\nData Store Size = %lu\n", store_size
);
403 dp
= opendir(PATH_ASL_STORE
);
404 if (dp
== NULL
) mgr_exit(store_dir
, 1);
406 /* gather a list of LongTTL files */
408 while ((dent
= readdir(dp
)) != NULL
)
410 if (!strcmp(dent
->d_name
, LONGTTL_TMP_FILE
)) unlink(LONGTTL_TMP_FILE
);
411 else if (!strncmp(dent
->d_name
, "LongTTL.", 8)) list
= add_to_list(list
, dent
->d_name
, 0);
418 printf("\nData Store LongTTL Files\n");
419 for (e
= list
; e
!= NULL
; e
= e
->next
) printf(" %s\n", e
->name
);
422 if (debug
== 1) printf("\nScan for expired messages\n");
427 /* syslog -x LongTTL.new -db [e->name] -k ASLExpireTime Nge [now] */
430 printf(" %s\n", e
->name
);
434 status
= do_match(e
->name
, LONGTTL_TMP_FILE
, 1, now
);
436 if (status
== ASL_STATUS_OK
) rename(LONGTTL_TMP_FILE
, e
->name
);
442 if (debug
== 1) printf("Finished scan for expired messages\n");
447 mgr_exit(store_dir
, 0);