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@
36 #include <asl_private.h>
39 #include <asl_store.h>
41 #define SECONDS_PER_DAY 86400
42 #define DEFAULT_MAX_SIZE 150000000
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
, (aslmsg
)(res
->msg
[i
]), &mid
);
152 if (status
!= ASL_STATUS_OK
) break;
160 do_dir_archive(const char *indir
, const char *outdir
)
162 return copyfile(indir
, outdir
, NULL
, COPYFILE_ALL
| COPYFILE_RECURSIVE
);
166 remove_directory(const char *path
)
174 if (dp
== NULL
) return 0;
176 while ((dent
= readdir(dp
)) != NULL
)
178 if ((!strcmp(dent
->d_name
, ".")) || (!strcmp(dent
->d_name
, ".."))) continue;
179 asprintf(&str
, "%s/%s", path
, dent
->d_name
);
182 status
= unlink(str
);
189 status
= rmdir(path
);
196 _insertString(char *s
, char **l
, uint32_t x
)
200 if (s
== NULL
) return l
;
203 l
= (char **)malloc(2 * sizeof(char *));
204 if (l
== NULL
) return NULL
;
217 for (i
= 0; l
[i
] != NULL
; i
++);
218 len
= i
+ 1; /* count the NULL on the end of the list too! */
220 l
= (char **)reallocf(l
, (len
+ 1) * sizeof(char *));
221 if (l
== NULL
) return NULL
;
223 if ((x
>= (len
- 1)) || (x
== IndexNull
))
225 l
[len
- 1] = strdup(s
);
226 if (l
[len
- 1] == NULL
)
236 for (i
= len
; i
> x
; i
--) l
[i
] = l
[i
- 1];
238 if (l
[x
] == NULL
) return NULL
;
244 explode(const char *s
, const char *delim
)
251 if (s
== NULL
) return NULL
;
259 for (i
= 0; p
[i
] != '\0'; i
++)
263 /* not inside a quoted string: check for delimiters and quotes */
264 if (strchr(delim
, p
[i
]) != NULL
) break;
265 else if (p
[i
] == '\'') quote
= p
[i
];
266 else if (p
[i
] == '"') quote
= p
[i
];
270 /* inside a quoted string - look for matching quote */
271 if (p
[i
] == quote
) quote
= '\0';
277 if (t
== NULL
) return NULL
;
279 for (i
= 0; i
< n
; i
++) t
[i
] = p
[i
];
281 l
= _insertString(t
, l
, IndexNull
);
284 if (p
[i
] == '\0') return l
;
285 if (p
[i
+ 1] == '\0') l
= _insertString("", l
, IndexNull
);
297 if (l
== NULL
) return;
298 for (i
= 0; l
[i
] != NULL
; i
++) free(l
[i
]);
303 * Used to sed config parameters.
304 * Line format "= name value"
307 _parse_set_param(char *s
)
312 if (s
== NULL
) return;
313 if (s
[0] == '\0') return;
315 /* skip '=' and whitespace */
317 while ((*s
== ' ') || (*s
== '\t')) s
++;
319 l
= explode(s
, " \t");
320 if (l
== NULL
) return;
322 for (count
= 0; l
[count
] != NULL
; count
++);
324 /* name is required */
331 /* value is required */
338 if (!strcasecmp(l
[0], "aslmanager_debug"))
343 else if (!strcasecmp(l
[0], "store_ttl"))
345 /* = store_ttl days */
346 ttl
= SECONDS_PER_DAY
* (time_t)atoll(l
[1]);
348 else if (!strcasecmp(l
[0], "max_store_size"))
350 /* = max_file_size bytes */
351 max_size
= atoi(l
[1]);
353 else if (!strcasecmp(l
[0], "archive"))
355 /* = archive {0|1} path */
356 if (!strcmp(l
[1], "1"))
358 if (l
[2] == NULL
) archive
= PATH_ASL_ARCHIVE
;
359 else archive
= strdup(l
[2]); /* never freed */
363 else if (!strcasecmp(l
[0], "store_path"))
366 store_dir
= strdup(l
[1]); /* never freed */
368 else if (!strcasecmp(l
[0], "archive_mode"))
370 archive_mode
= strtol(l
[1], NULL
, 0);
371 if ((archive_mode
== 0) && (errno
== EINVAL
)) archive_mode
= 0400;
380 if (s
== NULL
) return;
381 while ((*s
== ' ') || (*s
== '\t')) s
++;
384 * First non-whitespace char is the rule type.
385 * aslmanager only checks "=" (set parameter) rules.
387 if (*s
== '=') _parse_set_param(s
);
391 get_line_from_file(FILE *f
)
396 out
= fgetln(f
, &len
);
397 if (out
== NULL
) return NULL
;
398 if (len
== 0) return NULL
;
401 if (s
== NULL
) return NULL
;
410 _parse_config_file(const char *name
)
415 cf
= fopen(name
, "r");
416 if (cf
== NULL
) return 1;
418 while (NULL
!= (line
= get_line_from_file(cf
)))
430 directory_size(const char *path
)
439 if (dp
== NULL
) return 0;
442 while ((dent
= readdir(dp
)) != NULL
)
444 if ((!strcmp(dent
->d_name
, ".")) || (!strcmp(dent
->d_name
, ".."))) continue;
446 memset(&sb
, 0, sizeof(struct stat
));
448 asprintf(&str
, "%s/%s", path
, dent
->d_name
);
450 if ((str
!= NULL
) && (stat(str
, &sb
) == 0) && S_ISREG(sb
.st_mode
))
462 main(int argc
, const char *argv
[])
464 int i
, today_ymd_stringlen
, expire_ymd_stringlen
;
465 time_t now
, ymd_expire
;
467 char today_ymd_string
[32], expire_ymd_string
[32], *str
;
470 name_list_t
*ymd_list
, *bb_list
, *aux_list
, *bb_aux_list
, *e
;
472 size_t file_size
, store_size
;
480 ttl
= DEFAULT_TTL
* SECONDS_PER_DAY
;
481 max_size
= DEFAULT_MAX_SIZE
;
485 for (i
= 1; i
< argc
; i
++)
487 if (!strcmp(argv
[i
], "-a"))
489 if (((i
+ 1) < argc
) && (argv
[i
+ 1][0] != '-')) archive
= (char *)argv
[++i
];
490 else archive
= PATH_ASL_ARCHIVE
;
492 else if (!strcmp(argv
[i
], "-s"))
494 if (((i
+ 1) < argc
) && (argv
[i
+ 1][0] != '-')) store_dir
= (char *)argv
[++i
];
496 else if (!strcmp(argv
[i
], "-ttl"))
498 if (((i
+ 1) < argc
) && (argv
[i
+ 1][0] != '-')) ttl
= atoi(argv
[++i
]) * SECONDS_PER_DAY
;
500 else if (!strcmp(argv
[i
], "-size"))
502 if (((i
+ 1) < argc
) && (argv
[i
+ 1][0] != '-')) max_size
= atoi(argv
[++i
]);
504 else if (!strcmp(argv
[i
], "-d"))
510 _parse_config_file(_PATH_ASL_CONF
);
512 if (debug
== 1) printf("aslmanager starting\n");
517 memset(&sb
, 0, sizeof(struct stat
));
518 if (stat(archive
, &sb
) == 0)
520 /* must be a directory */
521 if (!S_ISDIR(sb
.st_mode
))
523 fprintf(stderr
, "aslmanager error: archive %s is not a directory", archive
);
531 /* archive doesn't exist - create it */
532 if (mkdir(archive
, 0755) != 0)
534 fprintf(stderr
, "aslmanager error: can't create archive %s: %s\n", archive
, strerror(errno
));
540 /* stat failed for some other reason */
541 fprintf(stderr
, "aslmanager error: can't stat archive %s: %s\n", archive
, strerror(errno
));
549 /* determine current time */
552 /* ttl 0 means files never expire */
554 if (ttl
> 0) ymd_expire
= now
- ttl
;
556 /* construct today's date as YYYY.MM.DD */
557 memset(&ctm
, 0, sizeof(struct tm
));
558 if (localtime_r((const time_t *)&now
, &ctm
) == NULL
) return -1;
560 snprintf(today_ymd_string
, sizeof(today_ymd_string
), "%d.%02d.%02d.", ctm
.tm_year
+ 1900, ctm
.tm_mon
+ 1, ctm
.tm_mday
);
561 today_ymd_stringlen
= strlen(today_ymd_string
);
563 /* construct regular file expiry date as YYYY.MM.DD */
564 memset(&ctm
, 0, sizeof(struct tm
));
565 if (localtime_r((const time_t *)&ymd_expire
, &ctm
) == NULL
) return -1;
567 snprintf(expire_ymd_string
, sizeof(expire_ymd_string
), "%d.%02d.%02d.", ctm
.tm_year
+ 1900, ctm
.tm_mon
+ 1, ctm
.tm_mday
);
568 expire_ymd_stringlen
= strlen(expire_ymd_string
);
570 if (debug
== 1) printf("Expiry Date %s\n", expire_ymd_string
);
572 dp
= opendir(store_dir
);
573 if (dp
== NULL
) return -1;
575 /* gather a list of YMD files, AUX dirs, BB.AUX dirs, and BB files */
576 while ((dent
= readdir(dp
)) != NULL
)
578 memset(&sb
, 0, sizeof(struct stat
));
580 if (stat(dent
->d_name
, &sb
) == 0) file_size
= sb
.st_size
;
582 if ((dent
->d_name
[0] >= '0') && (dent
->d_name
[0] <= '9'))
584 ymd_list
= add_to_list(ymd_list
, dent
->d_name
, file_size
);
585 store_size
+= file_size
;
587 else if (!strncmp(dent
->d_name
, "AUX.", 4) && (dent
->d_name
[4] >= '0') && (dent
->d_name
[4] <= '9') && S_ISDIR(sb
.st_mode
))
589 file_size
= directory_size(dent
->d_name
);
590 aux_list
= add_to_list(aux_list
, dent
->d_name
, file_size
);
591 store_size
+= file_size
;
593 else if (!strncmp(dent
->d_name
, "BB.AUX.", 7) && (dent
->d_name
[7] >= '0') && (dent
->d_name
[7] <= '9') && S_ISDIR(sb
.st_mode
))
595 file_size
= directory_size(dent
->d_name
);
596 bb_aux_list
= add_to_list(bb_aux_list
, dent
->d_name
, file_size
);
597 store_size
+= file_size
;
599 else if (!strncmp(dent
->d_name
, "BB.", 3) && (dent
->d_name
[3] >= '0') && (dent
->d_name
[3] <= '9'))
601 bb_list
= add_to_list(bb_list
, dent
->d_name
, file_size
);
602 store_size
+= file_size
;
604 else if ((!strcmp(dent
->d_name
, ".")) || (!strcmp(dent
->d_name
, "..")))
606 else if ((!strcmp(dent
->d_name
, "StoreData")) || (!strcmp(dent
->d_name
, "SweepStore")))
610 fprintf(stderr
, "aslmanager: unexpected file %s in ASL data store\n", dent
->d_name
);
618 printf("Data Store Size = %lu\n", store_size
);
619 printf("Data Store YMD Files\n");
620 for (e
= ymd_list
; e
!= NULL
; e
= e
->next
) printf(" %s %lu\n", e
->name
, e
->size
);
621 printf("Data Store AUX Directories\n");
622 for (e
= aux_list
; e
!= NULL
; e
= e
->next
) printf(" %s %lu\n", e
->name
, e
->size
);
623 printf("Data Store BB.AUX Directories\n");
624 for (e
= bb_aux_list
; e
!= NULL
; e
= e
->next
) printf(" %s %lu\n", e
->name
, e
->size
);
625 printf("Data Store BB Files\n");
626 for (e
= bb_list
; e
!= NULL
; e
= e
->next
) printf(" %s %lu\n", e
->name
, e
->size
);
629 /* Delete/achive expired YMD files */
630 if (debug
== 1) printf("Start YMD File Scan\n");
635 /* stop when a file name/date is after the expire date */
636 if (strncmp(e
->name
, expire_ymd_string
, expire_ymd_stringlen
) > 0) break;
641 asprintf(&str
, "%s/%s", archive
, e
->name
);
642 if (str
== NULL
) return -1;
644 if (debug
== 1) printf(" copy %s ---> %s\n", e
->name
, str
);
645 status
= do_copy(e
->name
, str
, archive_mode
);
649 if (debug
== 1) printf(" unlink %s\n", e
->name
);
652 store_size
-= e
->size
;
658 if (debug
== 1) printf("Finished YMD FILE Scan\n");
660 /* Delete/achive expired YMD AUX directories */
661 if (debug
== 1) printf("Start AUX Directory Scan\n");
666 /* stop when a file name/date is after the expire date */
667 if (strncmp(e
->name
+ 4, expire_ymd_string
, expire_ymd_stringlen
) > 0) break;
672 asprintf(&str
, "%s/%s", archive
, e
->name
);
673 if (str
== NULL
) return -1;
675 if (debug
== 1) printf(" copy %s ---> %s\n", e
->name
, str
);
676 do_dir_archive(e
->name
, str
);
680 if (debug
== 1) printf(" Remove %s\n", e
->name
);
681 remove_directory(e
->name
);
683 store_size
-= e
->size
;
689 if (debug
== 1) printf("Finished AUX Directory Scan\n");
691 /* Delete/achive expired BB.AUX directories */
692 if (debug
== 1) printf("Start BB.AUX Directory Scan\n");
697 /* stop when a file name/date is after the expire date */
698 if (strncmp(e
->name
+ 7, today_ymd_string
, today_ymd_stringlen
) > 0) break;
703 asprintf(&str
, "%s/%s", archive
, e
->name
);
704 if (str
== NULL
) return -1;
706 if (debug
== 1) printf(" copy %s ---> %s\n", e
->name
, str
);
707 do_dir_archive(e
->name
, str
);
711 if (debug
== 1) printf(" remove %s\n", e
->name
);
712 remove_directory(e
->name
);
714 store_size
-= e
->size
;
720 if (debug
== 1) printf("Finished BB.AUX Directory Scan\n");
722 /* Delete/achive expired BB files */
723 if (debug
== 1) printf("Start BB Scan\n");
728 /* stop when a file name/date is after the expire date */
729 if (strncmp(e
->name
+ 3, today_ymd_string
, today_ymd_stringlen
) > 0) break;
734 asprintf(&str
, "%s/%s", archive
, e
->name
);
735 if (str
== NULL
) return -1;
737 /* syslog -x [str] -f [e->name] */
738 if (debug
== 1) printf(" copy %s ---> %s\n", e
->name
, str
);
739 status
= do_copy(e
->name
, str
, archive_mode
);
743 if (debug
== 1) printf(" unlink %s\n", e
->name
);
746 store_size
-= e
->size
;
752 if (debug
== 1) printf("Finished BB Scan\n");
754 /* if data store is over max_size, delete/archive more YMD files */
755 if ((debug
== 1) && (store_size
> max_size
)) printf("Additional YMD Scan\n");
758 while ((e
!= NULL
) && (store_size
> max_size
))
762 /* stop when we get to today's files */
763 if (strncmp(e
->name
, today_ymd_string
, today_ymd_stringlen
) == 0) break;
768 asprintf(&str
, "%s/%s", archive
, e
->name
);
769 if (str
== NULL
) return -1;
771 /* syslog -x [str] -f [e->name] */
772 if (debug
== 1) printf(" copy %s ---> %s\n", e
->name
, str
);
773 status
= do_copy(e
->name
, str
, archive_mode
);
777 if (debug
== 1) printf(" unlink %s\n", e
->name
);
780 store_size
-= e
->size
;
787 /* if data store is over max_size, delete/archive more BB files */
788 if ((debug
== 1) && (store_size
> max_size
)) printf("Additional BB Scan\n");
791 while ((e
!= NULL
) && (store_size
> max_size
))
798 asprintf(&str
, "%s/%s", archive
, e
->name
);
799 if (str
== NULL
) return -1;
801 /* syslog -x [str] -f [e->name] */
802 if (debug
== 1) printf(" copy %s ---> %s\n", e
->name
, str
);
803 status
= do_copy(e
->name
, str
, archive_mode
);
807 if (debug
== 1) printf(" unlink %s\n", e
->name
);
810 store_size
-= e
->size
;
822 printf("Data Store Size = %lu\n", store_size
);
823 printf("aslmanager finished\n");