2 * Copyright (c) 2007-2009 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 150000000
44 #define IndexNull (uint32_t)-1
45 #define _PATH_ASL_CONF "/etc/asl.conf"
48 static char *archive
= NULL
;
49 static char *store_dir
= PATH_ASL_STORE
;
51 static size_t max_size
;
52 static mode_t archive_mode
= 0400;
55 typedef struct name_list_s
59 struct name_list_s
*next
;
63 add_to_list(name_list_t
*l
, const char *name
, size_t size
)
67 if (name
== NULL
) return l
;
69 e
= (name_list_t
*)calloc(1, sizeof(name_list_t
));
70 if (e
== NULL
) return NULL
;
72 e
->name
= strdup(name
);
81 /* list is sorted by name (i.e. primarily by timestamp) */
82 if (l
== NULL
) return e
;
84 if (strcmp(e
->name
, l
->name
) <= 0)
90 for (x
= l
; (x
->next
!= NULL
) && (strcmp(e
->name
, x
->next
->name
) > 0) ; x
= x
->next
);
98 free_list(name_list_t
*l
)
114 do_copy(const char *infile
, const char *outfile
, mode_t mode
)
116 asl_search_result_t
*res
;
121 if (infile
== NULL
) return ASL_STATUS_INVALID_ARG
;
122 if (outfile
== NULL
) return ASL_STATUS_INVALID_ARG
;
125 status
= asl_file_open_read(infile
, &f
);
126 if (status
!= ASL_STATUS_OK
) return status
;
131 status
= asl_file_match(f
, NULL
, &res
, &mid
, 0, 0, 1);
134 if (status
!= ASL_STATUS_OK
) return status
;
137 aslresponse_free(res
);
138 return ASL_STATUS_OK
;
142 status
= asl_file_open_write(outfile
, mode
, -1, -1, &f
);
143 if (status
!= ASL_STATUS_OK
) return status
;
144 if (f
== ASL_STATUS_OK
) return ASL_STATUS_FAILED
;
146 f
->flags
= ASL_FILE_FLAG_UNLIMITED_CACHE
| ASL_FILE_FLAG_PRESERVE_MSG_ID
;
148 for (i
= 0; i
< res
->count
; i
++)
151 status
= asl_file_save(f
, res
->msg
[i
], &mid
);
152 if (status
!= ASL_STATUS_OK
) break;
160 _insertString(char *s
, char **l
, uint32_t x
)
164 if (s
== NULL
) return l
;
167 l
= (char **)malloc(2 * sizeof(char *));
168 if (l
== NULL
) return NULL
;
181 for (i
= 0; l
[i
] != NULL
; i
++);
182 len
= i
+ 1; /* count the NULL on the end of the list too! */
184 l
= (char **)reallocf(l
, (len
+ 1) * sizeof(char *));
185 if (l
== NULL
) return NULL
;
187 if ((x
>= (len
- 1)) || (x
== IndexNull
))
189 l
[len
- 1] = strdup(s
);
190 if (l
[len
- 1] == NULL
)
200 for (i
= len
; i
> x
; i
--) l
[i
] = l
[i
- 1];
202 if (l
[x
] == NULL
) return NULL
;
208 explode(const char *s
, const char *delim
)
215 if (s
== NULL
) return NULL
;
223 for (i
= 0; p
[i
] != '\0'; i
++)
227 /* not inside a quoted string: check for delimiters and quotes */
228 if (strchr(delim
, p
[i
]) != NULL
) break;
229 else if (p
[i
] == '\'') quote
= p
[i
];
230 else if (p
[i
] == '"') quote
= p
[i
];
234 /* inside a quoted string - look for matching quote */
235 if (p
[i
] == quote
) quote
= '\0';
241 if (t
== NULL
) return NULL
;
243 for (i
= 0; i
< n
; i
++) t
[i
] = p
[i
];
245 l
= _insertString(t
, l
, IndexNull
);
248 if (p
[i
] == '\0') return l
;
249 if (p
[i
+ 1] == '\0') l
= _insertString("", l
, IndexNull
);
261 if (l
== NULL
) return;
262 for (i
= 0; l
[i
] != NULL
; i
++) free(l
[i
]);
267 * Used to sed config parameters.
268 * Line format "= name value"
271 _parse_set_param(char *s
)
276 if (s
== NULL
) return;
277 if (s
[0] == '\0') return;
279 /* skip '=' and whitespace */
281 while ((*s
== ' ') || (*s
== '\t')) s
++;
283 l
= explode(s
, " \t");
284 if (l
== NULL
) return;
286 for (count
= 0; l
[count
] != NULL
; count
++);
288 /* name is required */
295 /* value is required */
302 if (!strcasecmp(l
[0], "aslmanager_debug"))
307 else if (!strcasecmp(l
[0], "store_ttl"))
309 /* = store_ttl days */
310 ttl
= SECONDS_PER_DAY
* (time_t)atoll(l
[1]);
312 else if (!strcasecmp(l
[0], "max_store_size"))
314 /* = max_file_size bytes */
315 max_size
= atoi(l
[1]);
317 else if (!strcasecmp(l
[0], "archive"))
319 /* = archive {0|1} path */
320 if (!strcmp(l
[1], "1"))
322 if (l
[2] == NULL
) archive
= PATH_ASL_ARCHIVE
;
323 else archive
= strdup(l
[2]); /* never freed */
327 else if (!strcasecmp(l
[0], "store_path"))
330 store_dir
= strdup(l
[1]); /* never freed */
332 else if (!strcasecmp(l
[0], "archive_mode"))
334 archive_mode
= strtol(l
[1], NULL
, 0);
335 if ((archive_mode
== 0) && (errno
== EINVAL
)) archive_mode
= 0400;
344 if (s
== NULL
) return;
345 while ((*s
== ' ') || (*s
== '\t')) s
++;
348 * First non-whitespace char is the rule type.
349 * aslmanager only checks "=" (set parameter) rules.
351 if (*s
== '=') _parse_set_param(s
);
355 get_line_from_file(FILE *f
)
360 out
= fgetln(f
, &len
);
361 if (out
== NULL
) return NULL
;
362 if (len
== 0) return NULL
;
365 if (s
== NULL
) return NULL
;
374 _parse_config_file(const char *name
)
379 cf
= fopen(name
, "r");
380 if (cf
== NULL
) return 1;
382 while (NULL
!= (line
= get_line_from_file(cf
)))
394 main(int argc
, const char *argv
[])
396 int i
, today_ymd_stringlen
, expire_ymd_stringlen
;
397 time_t now
, ymd_expire
;
399 char today_ymd_string
[32], expire_ymd_string
[32], *str
;
402 name_list_t
*ymd_list
, *bb_list
, *e
;
404 size_t file_size
, store_size
;
410 ttl
= DEFAULT_TTL
* SECONDS_PER_DAY
;
411 max_size
= DEFAULT_MAX_SIZE
;
415 for (i
= 1; i
< argc
; i
++)
417 if (!strcmp(argv
[i
], "-a"))
419 if (((i
+ 1) < argc
) && (argv
[i
+ 1][0] != '-')) archive
= (char *)argv
[++i
];
420 else archive
= PATH_ASL_ARCHIVE
;
422 else if (!strcmp(argv
[i
], "-s"))
424 if (((i
+ 1) < argc
) && (argv
[i
+ 1][0] != '-')) store_dir
= (char *)argv
[++i
];
426 else if (!strcmp(argv
[i
], "-ttl"))
428 if (((i
+ 1) < argc
) && (argv
[i
+ 1][0] != '-')) ttl
= atoi(argv
[++i
]) * SECONDS_PER_DAY
;
430 else if (!strcmp(argv
[i
], "-size"))
432 if (((i
+ 1) < argc
) && (argv
[i
+ 1][0] != '-')) max_size
= atoi(argv
[++i
]);
434 else if (!strcmp(argv
[i
], "-d"))
440 _parse_config_file(_PATH_ASL_CONF
);
442 if (debug
== 1) printf("aslmanager starting\n");
447 memset(&sb
, 0, sizeof(struct stat
));
448 if (stat(archive
, &sb
) == 0)
450 /* must be a directory */
451 if ((sb
.st_mode
& S_IFDIR
) == 0)
453 fprintf(stderr
, "aslmanager error: archive %s is not a directory", archive
);
461 /* archive doesn't exist - create it */
462 if (mkdir(archive
, 0755) != 0)
464 fprintf(stderr
, "aslmanager error: can't create archive %s: %s\n", archive
, strerror(errno
));
470 /* stat failed for some other reason */
471 fprintf(stderr
, "aslmanager error: can't stat archive %s: %s\n", archive
, strerror(errno
));
479 /* determine current time */
482 /* ttl 0 means files never expire */
484 if (ttl
> 0) ymd_expire
= now
- ttl
;
486 /* construct today's date as YYYY.MM.DD */
487 memset(&ctm
, 0, sizeof(struct tm
));
488 if (localtime_r((const time_t *)&now
, &ctm
) == NULL
) return -1;
490 snprintf(today_ymd_string
, sizeof(today_ymd_string
), "%d.%02d.%02d.", ctm
.tm_year
+ 1900, ctm
.tm_mon
+ 1, ctm
.tm_mday
);
491 today_ymd_stringlen
= strlen(today_ymd_string
);
493 /* construct regular file expiry date as YYYY.MM.DD */
494 memset(&ctm
, 0, sizeof(struct tm
));
495 if (localtime_r((const time_t *)&ymd_expire
, &ctm
) == NULL
) return -1;
497 snprintf(expire_ymd_string
, sizeof(expire_ymd_string
), "%d.%02d.%02d.", ctm
.tm_year
+ 1900, ctm
.tm_mon
+ 1, ctm
.tm_mday
);
498 expire_ymd_stringlen
= strlen(expire_ymd_string
);
500 if (debug
== 1) printf("Expiry Date %s\n", expire_ymd_string
);
502 dp
= opendir(store_dir
);
503 if (dp
== NULL
) return -1;
505 /* gather a list of YMD files and BB files */
506 while ((dent
= readdir(dp
)) != NULL
)
508 memset(&sb
, 0, sizeof(struct stat
));
510 if (stat(dent
->d_name
, &sb
) == 0) file_size
= sb
.st_size
;
512 if ((dent
->d_name
[0] >= '0') && (dent
->d_name
[0] <= '9'))
514 ymd_list
= add_to_list(ymd_list
, dent
->d_name
, file_size
);
515 store_size
+= file_size
;
517 else if (!strncmp(dent
->d_name
, "BB.", 3) && (dent
->d_name
[3] >= '0') && (dent
->d_name
[3] <= '9'))
519 bb_list
= add_to_list(bb_list
, dent
->d_name
, file_size
);
520 store_size
+= file_size
;
522 else if ((!strcmp(dent
->d_name
, ".")) || (!strcmp(dent
->d_name
, "..")))
524 else if ((!strcmp(dent
->d_name
, "StoreData")) || (!strcmp(dent
->d_name
, "SweepStore")))
528 fprintf(stderr
, "aslmanager: unexpected file %s in ASL data store\n", dent
->d_name
);
536 printf("Data Store Size = %lu\n", store_size
);
537 printf("Data Store YMD Files\n");
538 for (e
= ymd_list
; e
!= NULL
; e
= e
->next
) printf(" %s %lu\n", e
->name
, e
->size
);
539 printf("Data Store BB Files\n");
540 for (e
= bb_list
; e
!= NULL
; e
= e
->next
) printf(" %s %lu\n", e
->name
, e
->size
);
543 /* Delete/achive expired YMD files */
544 if (debug
== 1) printf("Start YMD Scan\n");
549 /* stop when a file name/date is after the expire date */
550 if (strncmp(e
->name
, expire_ymd_string
, expire_ymd_stringlen
) > 0) break;
555 asprintf(&str
, "%s/%s", archive
, e
->name
);
556 if (str
== NULL
) return -1;
558 if (debug
== 1) printf(" copy %s ---> %s\n", e
->name
, str
);
559 status
= do_copy(e
->name
, str
, archive_mode
);
563 if (debug
== 1) printf(" unlink %s\n", e
->name
);
566 store_size
-= e
->size
;
572 if (debug
== 1) printf("Finished YMD Scan\n");
574 /* Delete/achive expired BB files */
575 if (debug
== 1) printf("Start BB Scan\n");
580 /* stop when a file name/date is after the expire date */
581 if (strncmp(e
->name
+ 3, today_ymd_string
, today_ymd_stringlen
) > 0) break;
586 asprintf(&str
, "%s/%s", archive
, e
->name
);
587 if (str
== NULL
) return -1;
589 /* syslog -x [str] -f [e->name] */
590 if (debug
== 1) printf(" copy %s ---> %s\n", e
->name
, str
);
591 status
= do_copy(e
->name
, str
, archive_mode
);
595 if (debug
== 1) printf(" unlink %s\n", e
->name
);
598 store_size
-= e
->size
;
604 if (debug
== 1) printf("Finished BB Scan\n");
606 /* if data store is over max_size, delete/archive more YMD files */
607 if ((debug
== 1) && (store_size
> max_size
)) printf("Additional YMD Scan\n");
610 while ((e
!= NULL
) && (store_size
> max_size
))
614 /* stop when we get to today's files */
615 if (strncmp(e
->name
, today_ymd_string
, today_ymd_stringlen
) == 0) break;
620 asprintf(&str
, "%s/%s", archive
, e
->name
);
621 if (str
== NULL
) return -1;
623 /* syslog -x [str] -f [e->name] */
624 if (debug
== 1) printf(" copy %s ---> %s\n", e
->name
, str
);
625 status
= do_copy(e
->name
, str
, archive_mode
);
629 if (debug
== 1) printf(" unlink %s\n", e
->name
);
632 store_size
-= e
->size
;
639 /* if data store is over max_size, delete/archive more BB files */
640 if ((debug
== 1) && (store_size
> max_size
)) printf("Additional BB Scan\n");
643 while ((e
!= NULL
) && (store_size
> max_size
))
650 asprintf(&str
, "%s/%s", archive
, e
->name
);
651 if (str
== NULL
) return -1;
653 /* syslog -x [str] -f [e->name] */
654 if (debug
== 1) printf(" copy %s ---> %s\n", e
->name
, str
);
655 status
= do_copy(e
->name
, str
, archive_mode
);
659 if (debug
== 1) printf(" unlink %s\n", e
->name
);
662 store_size
-= e
->size
;
674 printf("Data Store Size = %lu\n", store_size
);
675 printf("aslmanager finished\n");