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
;
405 if (s
[len
- 1] != '\n') len
++;
411 _parse_config_file(const char *name
)
416 cf
= fopen(name
, "r");
417 if (cf
== NULL
) return 1;
419 while (NULL
!= (line
= get_line_from_file(cf
)))
431 directory_size(const char *path
)
440 if (dp
== NULL
) return 0;
443 while ((dent
= readdir(dp
)) != NULL
)
445 if ((!strcmp(dent
->d_name
, ".")) || (!strcmp(dent
->d_name
, ".."))) continue;
447 memset(&sb
, 0, sizeof(struct stat
));
449 asprintf(&str
, "%s/%s", path
, dent
->d_name
);
451 if ((str
!= NULL
) && (stat(str
, &sb
) == 0) && S_ISREG(sb
.st_mode
))
463 main(int argc
, const char *argv
[])
465 int i
, today_ymd_stringlen
, expire_ymd_stringlen
;
466 time_t now
, ymd_expire
;
468 char today_ymd_string
[32], expire_ymd_string
[32], *str
;
471 name_list_t
*ymd_list
, *bb_list
, *aux_list
, *bb_aux_list
, *e
;
473 size_t file_size
, store_size
;
481 ttl
= DEFAULT_TTL
* SECONDS_PER_DAY
;
482 max_size
= DEFAULT_MAX_SIZE
;
486 for (i
= 1; i
< argc
; i
++)
488 if (!strcmp(argv
[i
], "-a"))
490 if (((i
+ 1) < argc
) && (argv
[i
+ 1][0] != '-')) archive
= (char *)argv
[++i
];
491 else archive
= PATH_ASL_ARCHIVE
;
493 else if (!strcmp(argv
[i
], "-s"))
495 if (((i
+ 1) < argc
) && (argv
[i
+ 1][0] != '-')) store_dir
= (char *)argv
[++i
];
497 else if (!strcmp(argv
[i
], "-ttl"))
499 if (((i
+ 1) < argc
) && (argv
[i
+ 1][0] != '-')) ttl
= atoi(argv
[++i
]) * SECONDS_PER_DAY
;
501 else if (!strcmp(argv
[i
], "-size"))
503 if (((i
+ 1) < argc
) && (argv
[i
+ 1][0] != '-')) max_size
= atoi(argv
[++i
]);
505 else if (!strcmp(argv
[i
], "-d"))
511 _parse_config_file(_PATH_ASL_CONF
);
513 if (debug
== 1) printf("aslmanager starting\n");
518 memset(&sb
, 0, sizeof(struct stat
));
519 if (stat(archive
, &sb
) == 0)
521 /* must be a directory */
522 if (!S_ISDIR(sb
.st_mode
))
524 fprintf(stderr
, "aslmanager error: archive %s is not a directory", archive
);
532 /* archive doesn't exist - create it */
533 if (mkdir(archive
, 0755) != 0)
535 fprintf(stderr
, "aslmanager error: can't create archive %s: %s\n", archive
, strerror(errno
));
541 /* stat failed for some other reason */
542 fprintf(stderr
, "aslmanager error: can't stat archive %s: %s\n", archive
, strerror(errno
));
550 /* determine current time */
553 /* ttl 0 means files never expire */
555 if (ttl
> 0) ymd_expire
= now
- ttl
;
557 /* construct today's date as YYYY.MM.DD */
558 memset(&ctm
, 0, sizeof(struct tm
));
559 if (localtime_r((const time_t *)&now
, &ctm
) == NULL
) return -1;
561 snprintf(today_ymd_string
, sizeof(today_ymd_string
), "%d.%02d.%02d.", ctm
.tm_year
+ 1900, ctm
.tm_mon
+ 1, ctm
.tm_mday
);
562 today_ymd_stringlen
= strlen(today_ymd_string
);
564 /* construct regular file expiry date as YYYY.MM.DD */
565 memset(&ctm
, 0, sizeof(struct tm
));
566 if (localtime_r((const time_t *)&ymd_expire
, &ctm
) == NULL
) return -1;
568 snprintf(expire_ymd_string
, sizeof(expire_ymd_string
), "%d.%02d.%02d.", ctm
.tm_year
+ 1900, ctm
.tm_mon
+ 1, ctm
.tm_mday
);
569 expire_ymd_stringlen
= strlen(expire_ymd_string
);
571 if (debug
== 1) printf("Expiry Date %s\n", expire_ymd_string
);
573 dp
= opendir(store_dir
);
574 if (dp
== NULL
) return -1;
576 /* gather a list of YMD files, AUX dirs, BB.AUX dirs, and BB files */
577 while ((dent
= readdir(dp
)) != NULL
)
579 memset(&sb
, 0, sizeof(struct stat
));
581 if (stat(dent
->d_name
, &sb
) == 0) file_size
= sb
.st_size
;
583 if ((dent
->d_name
[0] >= '0') && (dent
->d_name
[0] <= '9'))
585 ymd_list
= add_to_list(ymd_list
, dent
->d_name
, file_size
);
586 store_size
+= file_size
;
588 else if (!strncmp(dent
->d_name
, "AUX.", 4) && (dent
->d_name
[4] >= '0') && (dent
->d_name
[4] <= '9') && S_ISDIR(sb
.st_mode
))
590 file_size
= directory_size(dent
->d_name
);
591 aux_list
= add_to_list(aux_list
, dent
->d_name
, file_size
);
592 store_size
+= file_size
;
594 else if (!strncmp(dent
->d_name
, "BB.AUX.", 7) && (dent
->d_name
[7] >= '0') && (dent
->d_name
[7] <= '9') && S_ISDIR(sb
.st_mode
))
596 file_size
= directory_size(dent
->d_name
);
597 bb_aux_list
= add_to_list(bb_aux_list
, dent
->d_name
, file_size
);
598 store_size
+= file_size
;
600 else if (!strncmp(dent
->d_name
, "BB.", 3) && (dent
->d_name
[3] >= '0') && (dent
->d_name
[3] <= '9'))
602 bb_list
= add_to_list(bb_list
, dent
->d_name
, file_size
);
603 store_size
+= file_size
;
605 else if ((!strcmp(dent
->d_name
, ".")) || (!strcmp(dent
->d_name
, "..")))
607 else if ((!strcmp(dent
->d_name
, "StoreData")) || (!strcmp(dent
->d_name
, "SweepStore")))
611 fprintf(stderr
, "aslmanager: unexpected file %s in ASL data store\n", dent
->d_name
);
619 printf("Data Store Size = %lu\n", store_size
);
620 printf("Data Store YMD Files\n");
621 for (e
= ymd_list
; e
!= NULL
; e
= e
->next
) printf(" %s %lu\n", e
->name
, e
->size
);
622 printf("Data Store AUX Directories\n");
623 for (e
= aux_list
; e
!= NULL
; e
= e
->next
) printf(" %s %lu\n", e
->name
, e
->size
);
624 printf("Data Store BB.AUX Directories\n");
625 for (e
= bb_aux_list
; e
!= NULL
; e
= e
->next
) printf(" %s %lu\n", e
->name
, e
->size
);
626 printf("Data Store BB Files\n");
627 for (e
= bb_list
; e
!= NULL
; e
= e
->next
) printf(" %s %lu\n", e
->name
, e
->size
);
630 /* Delete/achive expired YMD files */
631 if (debug
== 1) printf("Start YMD File Scan\n");
636 /* stop when a file name/date is after the expire date */
637 if (strncmp(e
->name
, expire_ymd_string
, expire_ymd_stringlen
) > 0) break;
642 asprintf(&str
, "%s/%s", archive
, e
->name
);
643 if (str
== NULL
) return -1;
645 if (debug
== 1) printf(" copy %s ---> %s\n", e
->name
, str
);
646 status
= do_copy(e
->name
, str
, archive_mode
);
650 if (debug
== 1) printf(" unlink %s\n", e
->name
);
653 store_size
-= e
->size
;
659 if (debug
== 1) printf("Finished YMD FILE Scan\n");
661 /* Delete/achive expired YMD AUX directories */
662 if (debug
== 1) printf("Start AUX Directory Scan\n");
667 /* stop when a file name/date is after the expire date */
668 if (strncmp(e
->name
+ 4, expire_ymd_string
, expire_ymd_stringlen
) > 0) break;
673 asprintf(&str
, "%s/%s", archive
, e
->name
);
674 if (str
== NULL
) return -1;
676 if (debug
== 1) printf(" copy %s ---> %s\n", e
->name
, str
);
677 do_dir_archive(e
->name
, str
);
681 if (debug
== 1) printf(" Remove %s\n", e
->name
);
682 remove_directory(e
->name
);
684 store_size
-= e
->size
;
690 if (debug
== 1) printf("Finished AUX Directory Scan\n");
692 /* Delete/achive expired BB.AUX directories */
693 if (debug
== 1) printf("Start BB.AUX Directory Scan\n");
698 /* stop when a file name/date is after the expire date */
699 if (strncmp(e
->name
+ 7, today_ymd_string
, today_ymd_stringlen
) > 0) break;
704 asprintf(&str
, "%s/%s", archive
, e
->name
);
705 if (str
== NULL
) return -1;
707 if (debug
== 1) printf(" copy %s ---> %s\n", e
->name
, str
);
708 do_dir_archive(e
->name
, str
);
712 if (debug
== 1) printf(" remove %s\n", e
->name
);
713 remove_directory(e
->name
);
715 store_size
-= e
->size
;
721 if (debug
== 1) printf("Finished BB.AUX Directory Scan\n");
723 /* Delete/achive expired BB files */
724 if (debug
== 1) printf("Start BB Scan\n");
729 /* stop when a file name/date is after the expire date */
730 if (strncmp(e
->name
+ 3, today_ymd_string
, today_ymd_stringlen
) > 0) break;
735 asprintf(&str
, "%s/%s", archive
, e
->name
);
736 if (str
== NULL
) return -1;
738 /* syslog -x [str] -f [e->name] */
739 if (debug
== 1) printf(" copy %s ---> %s\n", e
->name
, str
);
740 status
= do_copy(e
->name
, str
, archive_mode
);
744 if (debug
== 1) printf(" unlink %s\n", e
->name
);
747 store_size
-= e
->size
;
753 if (debug
== 1) printf("Finished BB Scan\n");
755 /* if data store is over max_size, delete/archive more YMD files */
756 if ((debug
== 1) && (store_size
> max_size
)) printf("Additional YMD Scan\n");
759 while ((e
!= NULL
) && (store_size
> max_size
))
763 /* stop when we get to today's files */
764 if (strncmp(e
->name
, today_ymd_string
, today_ymd_stringlen
) == 0) break;
769 asprintf(&str
, "%s/%s", archive
, e
->name
);
770 if (str
== NULL
) return -1;
772 /* syslog -x [str] -f [e->name] */
773 if (debug
== 1) printf(" copy %s ---> %s\n", e
->name
, str
);
774 status
= do_copy(e
->name
, str
, archive_mode
);
778 if (debug
== 1) printf(" unlink %s\n", e
->name
);
781 store_size
-= e
->size
;
788 /* if data store is over max_size, delete/archive more BB files */
789 if ((debug
== 1) && (store_size
> max_size
)) printf("Additional BB Scan\n");
792 while ((e
!= NULL
) && (store_size
> max_size
))
799 asprintf(&str
, "%s/%s", archive
, e
->name
);
800 if (str
== NULL
) return -1;
802 /* syslog -x [str] -f [e->name] */
803 if (debug
== 1) printf(" copy %s ---> %s\n", e
->name
, str
);
804 status
= do_copy(e
->name
, str
, archive_mode
);
808 if (debug
== 1) printf(" unlink %s\n", e
->name
);
811 store_size
-= e
->size
;
823 printf("Data Store Size = %lu\n", store_size
);
824 printf("aslmanager finished\n");