2 * Copyright (c) 2015 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@
28 #include <asl_private.h>
31 #include <asl_store.h>
36 #include <sys/param.h>
42 #include <servers/bootstrap.h>
43 #include <bootstrap_priv.h>
44 #include <mach/mach.h>
47 #include <dispatch/dispatch.h>
49 #include <xpc/private.h>
50 #include <os/assumes.h>
51 #include <vproc_priv.h>
52 #include <libkern/OSAtomic.h>
58 extern uint32_t debug
;
60 extern dispatch_queue_t work_queue
;
62 static mach_port_t asl_server_port
;
63 static aslclient aslc
;
64 static int asl_aux_fd
= -1;
67 keep_str(uint8_t mask
)
72 memset(str
, 0, sizeof(str
));
73 if (mask
& 0x01) str
[x
++] = '0';
74 if (mask
& 0x02) str
[x
++] = '1';
75 if (mask
& 0x04) str
[x
++] = '2';
76 if (mask
& 0x08) str
[x
++] = '3';
77 if (mask
& 0x10) str
[x
++] = '4';
78 if (mask
& 0x20) str
[x
++] = '5';
79 if (mask
& 0x40) str
[x
++] = '6';
80 if (mask
& 0x80) str
[x
++] = '7';
81 if (x
== 0) str
[x
++] = '-';
86 set_debug(int flag
, const char *str
)
90 if (str
== NULL
) x
= ASL_LEVEL_ERR
;
91 else if (((str
[0] == 'L') || (str
[0] == 'l')) && ((str
[1] >= '0') && (str
[1] <= '7')) && (str
[2] == '\0')) x
= atoi(str
+1);
92 else if ((str
[0] >= '0') && (str
[0] <= '7') && (str
[1] == '\0')) x
= ASL_LEVEL_CRIT
+ atoi(str
);
93 else x
= ASL_LEVEL_ERR
;
96 else if (x
> 7) x
= 7;
98 level
= debug
& DEBUG_LEVEL_MASK
;
99 if (x
> level
) level
= x
;
101 debug
= debug
& DEBUG_FLAG_MASK
;
107 debug_log(int level
, char *str
, ...)
112 time_t now
= time(NULL
);
113 memset(ts
, 0, sizeof(ts
));
114 strftime(ts
, sizeof(ts
), "%b %e %T", localtime(&now
));
116 if ((debug
& DEBUG_STDERR
) && (level
<= (debug
& DEBUG_LEVEL_MASK
)))
118 fprintf(stderr
, "%s: ", ts
);
120 vfprintf(stderr
, str
, v
);
124 if ((debug
& DEBUG_FILE
) && (debugfp
!= NULL
))
126 fprintf(debugfp
, "%s: ", ts
);
128 vfprintf(debugfp
, str
, v
);
132 if (debug
& DEBUG_ASL
)
138 aslc
= asl_open("aslmanager", "syslog", 0);
139 asl_msg_t
*msg
= asl_msg_new(ASL_TYPE_MSG
);
141 asl_msg_set_key_val(msg
, ASL_KEY_MSG
, "Status Report");
142 asl_msg_set_key_val(msg
, ASL_KEY_LEVEL
, ASL_STRING_NOTICE
);
143 asl_create_auxiliary_file((asl_object_t
)msg
, "Status Report", "public.text", &asl_aux_fd
);
144 asl_msg_release(msg
);
148 vasprintf(&line
, str
, v
);
153 write(asl_aux_fd
, ts
, strlen(ts
));
154 write(asl_aux_fd
, line
, strlen(line
));
164 if (asl_aux_fd
>= 0) asl_close_auxiliary_file(asl_aux_fd
);
165 if (debugfp
!= NULL
) fclose(debugfp
);
169 add_to_name_list(name_list_t
*l
, const char *name
, size_t size
, uint32_t flags
)
173 if (name
== NULL
) return l
;
175 e
= (name_list_t
*)calloc(1, sizeof(name_list_t
));
176 if (e
== NULL
) return NULL
;
178 e
->name
= strdup(name
);
188 /* list is sorted by name (i.e. primarily by timestamp) */
189 if (l
== NULL
) return e
;
191 if (strcmp(e
->name
, l
->name
) <= 0)
197 for (x
= l
; (x
->next
!= NULL
) && (strcmp(e
->name
, x
->next
->name
) > 0) ; x
= x
->next
);
205 free_name_list(name_list_t
*l
)
221 copy_compress_file(asl_out_dst_data_t
*asldst
, const char *src
, const char *dst
)
228 in
= open(src
, O_RDONLY
, 0);
229 if (in
< 0) return -1;
231 out
= open(dst
, O_WRONLY
| O_CREAT
, asldst
->mode
& 0666);
232 if (out
>= 0) out
= asl_out_dst_set_access(out
, asldst
);
239 gz
= gzdopen(out
, "w");
248 n
= read(in
, buf
, sizeof(buf
));
249 if (n
> 0) gzwrite(gz
, buf
, n
);
250 } while (n
== IOBUFSIZE
);
260 filesystem_rename(const char *src
, const char *dst
)
264 debug_log(ASL_LEVEL_NOTICE
, " rename %s ---> %s\n", src
, dst
);
267 status
= rename(src
, dst
);
268 if (status
!= 0) debug_log(ASL_LEVEL_ERR
, " FAILED status %d errno %d [%s] rename %s ---> %s\n", status
, errno
, strerror(errno
), src
, dst
);
272 filesystem_unlink(const char *path
)
276 debug_log(ASL_LEVEL_NOTICE
, " remove %s\n", path
);
279 status
= unlink(path
);
280 if (status
!= 0) debug_log(ASL_LEVEL_ERR
, " FAILED status %d errno %d [%s] unlink %s\n", status
, errno
, strerror(errno
), path
);
284 filesystem_truncate(const char *path
)
288 debug_log(ASL_LEVEL_NOTICE
, " truncate %s\n", path
);
291 status
= truncate(path
, 0);
292 if (status
!= 0) debug_log(ASL_LEVEL_ERR
, " FAILED status %d errno %d [%s] unlink %s\n", status
, errno
, strerror(errno
), path
);
296 filesystem_rmdir(const char *path
)
300 debug_log(ASL_LEVEL_NOTICE
, " remove directory %s\n", path
);
303 status
= rmdir(path
);
304 if (status
!= 0) debug_log(ASL_LEVEL_ERR
, " FAILED status %d errno %d [%s] rmdir %s\n", status
, errno
, strerror(errno
), path
);
308 * Copy ASL files by reading and writing each record.
311 copy_asl_file(const char *src
, const char *dst
, mode_t mode
)
313 asl_file_t
*fin
, *fout
;
316 if (src
== NULL
) return ASL_STATUS_INVALID_ARG
;
317 if (dst
== NULL
) return ASL_STATUS_INVALID_ARG
;
320 status
= asl_file_open_read(src
, &fin
);
321 if (status
!= ASL_STATUS_OK
) return status
;
324 status
= asl_file_open_write(dst
, mode
, -1, -1, &fout
);
325 if (status
!= ASL_STATUS_OK
)
334 return ASL_STATUS_FAILED
;
337 fout
->flags
= ASL_FILE_FLAG_PRESERVE_MSG_ID
;
339 status
= asl_file_read_set_position(fin
, ASL_FILE_POSITION_FIRST
);
340 if (status
!= ASL_STATUS_OK
)
343 asl_file_close(fout
);
344 return ASL_STATUS_READ_FAILED
;
347 while (status
== ASL_STATUS_OK
)
350 asl_msg_t
*msg
= NULL
;
352 status
= asl_file_fetch_next(fin
, &msg
);
355 status
= ASL_STATUS_OK
;
359 if (status
!= ASL_STATUS_OK
) break;
361 status
= asl_file_save(fout
, msg
, &mid
);
362 asl_msg_release(msg
);
366 asl_file_close(fout
);
372 filesystem_copy(asl_out_dst_data_t
*asldst
, const char *src
, const char *dst
, uint32_t flags
)
376 if ((src
== NULL
) || (dst
== NULL
)) return 0;
378 dot
= strrchr(src
, '.');
379 if ((dot
!= NULL
) && (!strcmp(dot
, ".gz"))) flags
&= ~MODULE_FLAG_COMPRESS
;
381 if (((flags
& MODULE_FLAG_COMPRESS
) == 0) && (!strcmp(src
, dst
))) return 0;
383 if (flags
& MODULE_FLAG_TYPE_ASL
) debug_log(ASL_LEVEL_NOTICE
, " copy asl %s ---> %s\n", src
, dst
);
384 else if (flags
& MODULE_FLAG_COMPRESS
) debug_log(ASL_LEVEL_NOTICE
, " copy compress %s ---> %s.gz\n", src
, dst
);
385 else debug_log(ASL_LEVEL_NOTICE
, " copy %s ---> %s\n", src
, dst
);
387 if (dryrun
) return 0;
389 if (flags
& MODULE_FLAG_TYPE_ASL
)
391 uint32_t status
= copy_asl_file(src
, dst
, asldst
->mode
);
394 debug_log(ASL_LEVEL_ERR
, " FAILED status %u [%s] asl copy %s ---> %s\n", status
, asl_core_error(status
), src
, dst
);
398 else if (flags
& MODULE_FLAG_COMPRESS
)
400 char gzdst
[MAXPATHLEN
];
402 snprintf(gzdst
, sizeof(gzdst
), "%s.gz", dst
);
404 int status
= copy_compress_file(asldst
, src
, gzdst
);
407 debug_log(ASL_LEVEL_ERR
, " FAILED status %d errno %d [%s] copy & compress %s ---> %s\n", status
, errno
, strerror(errno
), src
, dst
);
413 int status
= copyfile(src
, dst
, NULL
, COPYFILE_ALL
| COPYFILE_RECURSIVE
);
416 debug_log(ASL_LEVEL_ERR
, " FAILED status %d errno %d [%s] copy %s ---> %s\n", status
, errno
, strerror(errno
), src
, dst
);
425 filesystem_reset_ctime(const char *path
)
427 struct attrlist attr_list
;
430 debug_log(ASL_LEVEL_NOTICE
, " reset ctime %s\n", path
);
432 memset(&attr_list
, 0, sizeof(attr_list
));
433 attr_list
.bitmapcount
= ATTR_BIT_MAP_COUNT
;
434 attr_list
.commonattr
= ATTR_CMN_CRTIME
;
436 memset(&now
, 0, sizeof(now
));
437 now
.tv_sec
= time(NULL
);
439 return setattrlist(path
, &attr_list
, &now
, sizeof(now
), 0);
443 remove_directory(const char *path
)
450 if (dp
== NULL
) return 0;
452 while ((dent
= readdir(dp
)) != NULL
)
454 if ((!strcmp(dent
->d_name
, ".")) || (!strcmp(dent
->d_name
, ".."))) continue;
455 asprintf(&str
, "%s/%s", path
, dent
->d_name
);
458 filesystem_unlink(str
);
465 filesystem_rmdir(path
);
471 directory_size(const char *path
)
480 if (dp
== NULL
) return 0;
483 while ((dent
= readdir(dp
)) != NULL
)
485 if ((!strcmp(dent
->d_name
, ".")) || (!strcmp(dent
->d_name
, ".."))) continue;
487 memset(&sb
, 0, sizeof(struct stat
));
489 asprintf(&str
, "%s/%s", path
, dent
->d_name
);
491 if ((str
!= NULL
) && (stat(str
, &sb
) == 0) && S_ISREG(sb
.st_mode
))
503 parse_ymd_name(const char *name
)
507 int32_t tzh
, tzm
, sign
= -1;
511 if (name
== NULL
) return -1;
515 if ((*x
== 'T') || (*x
== 't'))
519 if ((created
== 0) && (*x
!= '0')) return -1;
522 if (x
== NULL
) return -1;
527 memset(&ftime
, 0, sizeof(ftime
));
529 if ((*x
< '0') || (*x
> '9')) return 1;
530 ftime
.tm_year
= 1000 * (*x
++ - '0');
532 if ((*x
< '0') || (*x
> '9')) return 1;
533 ftime
.tm_year
+= 100 * (*x
++ - '0');
535 if ((*x
< '0') || (*x
> '9')) return 1;
536 ftime
.tm_year
+= 10 * (*x
++ - '0');
538 if ((*x
< '0') || (*x
> '9')) return 1;
539 ftime
.tm_year
+= *x
++ - '0';
540 ftime
.tm_year
-= 1900;
549 if ((*x
< '0') || (*x
> '9')) return 1;
550 ftime
.tm_mon
= 10 * (*x
++ - '0');
552 if ((*x
< '0') || (*x
> '9')) return 1;
553 ftime
.tm_mon
+= *x
++ - '0';
556 if ((*x
== '-') || (*x
== '.')) x
++;
558 if ((*x
< '0') || (*x
> '9')) return 1;
559 ftime
.tm_mday
= 10 * (*x
++ - '0');
561 if ((*x
< '0') || (*x
> '9')) return 1;
562 ftime
.tm_mday
+= *x
++ - '0';
566 if ((*x
!= '.') && (*x
!= '\0')) return -1;
568 /* assume the file was created at midnight */
574 created
= mktime(&ftime
);
578 if ((*x
!= 'T') && (*x
!= 't')) return 1;
581 if ((*x
< '0') || (*x
> '9')) return 1;
582 ftime
.tm_hour
= 10 * (*x
++ - '0');
584 if ((*x
< '0') || (*x
> '9')) return 1;
585 ftime
.tm_hour
+= *x
++ - '0';
589 if ((*x
< '0') || (*x
> '9')) return 1;
590 ftime
.tm_min
= 10 * (*x
++ - '0');
592 if ((*x
< '0') || (*x
> '9')) return 1;
593 ftime
.tm_min
+= *x
++ - '0';
597 if ((*x
< '0') || (*x
> '9')) return 1;
598 ftime
.tm_sec
= 10 * (*x
++ - '0');
600 if ((*x
< '0') || (*x
> '9')) return 1;
601 ftime
.tm_sec
+= *x
++ - '0';
603 if ((*x
== 'Z') || (*x
== 'z'))
605 created
= timegm(&ftime
);
609 if ((*x
!= '+') && (*x
!= '-')) return 1;
611 if (*x
== '-') sign
= 1;
614 if ((*x
< '0') || (*x
> '9')) return 1;
615 tzh
= 10 * (*x
++ - '0');
617 if ((*x
< '0') || (*x
> '9')) tzh
/= 10;
618 else tzh
+= *x
++ - '0';
620 if (tzh
> 23) return 1;
623 if ((*x
== ':') || ((*x
>= '0') && (*x
<= '9')))
625 if (*x
!= ':') tzm
= 10 * (*x
- '0');
628 if ((*x
< '0') || (*x
> '9'))return -1;
631 if (tzm
> 59) return -1;
634 ftime
.tm_sec
+= (sign
* (tzh
* SECONDS_PER_HOUR
) + (tzm
* SECONDS_PER_MINUTE
));
636 if ((*x
!= '.') && (*x
!= '\0')) return -1;
638 created
= timegm(&ftime
);
644 * Determine the age (in seconds) of a YMD file from its name.
645 * Also determines UID and GID from ".Unnn.Gnnn" part of file name.
648 ymd_file_age(const char *name
, time_t now
, uid_t
*u
, gid_t
*g
)
655 if (name
== NULL
) return 0;
657 if (now
== 0) now
= time(NULL
);
659 memset(&ftime
, 0, sizeof(struct tm
));
661 created
= parse_ymd_name(name
);
662 if (created
< 0) return 0;
663 if (created
> now
) return 0;
664 seconds
= now
- created
;
669 p
= strchr(name
, 'U');
670 if (p
!= NULL
) *u
= atoi(p
+1);
676 p
= strchr(name
, 'G');
677 if (p
!= NULL
) *g
= atoi(p
+1);
685 aux_url_callback(const char *url
)
687 if (url
== NULL
) return;
688 if (!strncmp(url
, AUX_URL_MINE
, AUX_URL_MINE_LEN
)) filesystem_unlink(url
+ AUX_URL_PATH_OFFSET
);
692 ymd_file_filter(const char *name
, const char *path
, uint32_t keep_mask
, mode_t ymd_mode
, uid_t ymd_uid
, gid_t ymd_gid
)
694 asl_file_t
*f
= NULL
;
695 uint8_t km
= keep_mask
;
696 uint32_t status
, len
, dstcount
= 0;
697 char src
[MAXPATHLEN
];
698 char dst
[MAXPATHLEN
];
700 if (snprintf(src
, MAXPATHLEN
, "%s/%s", path
, name
) >= MAXPATHLEN
) return ASL_STATUS_FAILED
;
701 if (snprintf(dst
, MAXPATHLEN
, "%s/%s", path
, name
) >= MAXPATHLEN
) return ASL_STATUS_FAILED
;
702 len
= strlen(src
) - 3;
703 snprintf(dst
+ len
, 4, "tmp");
705 //TODO: check if src file is already filtered
706 debug_log(ASL_LEVEL_NOTICE
, " filter %s %s ---> %s\n", src
, keep_str(km
), dst
);
708 status
= ASL_STATUS_OK
;
712 status
= asl_file_open_read(name
, &f
);
713 if (status
!= ASL_STATUS_OK
) return status
;
715 status
= asl_file_filter_level(f
, dst
, keep_mask
, ymd_mode
, ymd_uid
, ymd_gid
, &dstcount
, aux_url_callback
);
719 filesystem_unlink(src
);
720 if ((status
!= ASL_STATUS_OK
) || (dstcount
== 0)) filesystem_unlink(dst
);
721 else filesystem_rename(dst
, src
);
727 process_asl_data_store(asl_out_dst_data_t
*dst
, asl_out_dst_data_t
*opts
)
729 time_t now
, midnight
, since_midnight
;
733 name_list_t
*ymd_list
, *bb_list
, *aux_list
, *bb_aux_list
, *e
;
734 size_t file_size
, store_size
;
746 if (dst
== NULL
) return 0;
747 if (dst
->path
== NULL
) return 0;
749 ttl
= dst
->ttl
[LEVEL_ALL
];
750 if ((opts
!= NULL
) && (opts
->ttl
[LEVEL_ALL
] > 0)) ttl
= opts
->ttl
[LEVEL_ALL
];
752 size_t all_max
= dst
->all_max
;
753 if ((opts
!= NULL
) && (opts
->all_max
> 0)) all_max
= opts
->all_max
;
755 debug_log(ASL_LEVEL_NOTICE
, "----------------------------------------\n");
756 debug_log(ASL_LEVEL_NOTICE
, "Processing data store %s\n", dst
->path
);
758 if (dst
->rotate_dir
!= NULL
)
761 memset(&sb
, 0, sizeof(struct stat
));
762 if (stat(dst
->rotate_dir
, &sb
) == 0)
764 /* must be a directory */
765 if (!S_ISDIR(sb
.st_mode
))
767 debug_log(ASL_LEVEL_ERR
, "aslmanager error: archive %s is not a directory", dst
->rotate_dir
);
775 /* archive doesn't exist - create it */
776 if (mkdir(dst
->rotate_dir
, 0755) != 0)
778 debug_log(ASL_LEVEL_ERR
, "aslmanager error: can't create archive %s: %s\n", dst
->rotate_dir
, strerror(errno
));
784 /* stat failed for some other reason */
785 debug_log(ASL_LEVEL_ERR
, "aslmanager error: can't stat archive %s: %s\n", dst
->rotate_dir
, strerror(errno
));
793 /* determine current time */
796 localtime_r(&now
, &t_tmp
);
802 midnight
= mktime(&t_tmp
);
803 since_midnight
= now
- midnight
;
805 dp
= opendir(dst
->path
);
806 if (dp
== NULL
) return -1;
808 /* gather a list of YMD files, AUX dirs, BB.AUX dirs, and BB files */
809 while ((dent
= readdir(dp
)) != NULL
)
811 uint32_t file_flags
= 0;
814 memset(&sb
, 0, sizeof(struct stat
));
816 if (stat(dent
->d_name
, &sb
) == 0) file_size
= sb
.st_size
;
818 dot
= strrchr(dent
->d_name
, '.');
819 if ((dot
!= NULL
) && !strcmp(dot
, ".gz")) file_flags
|= NAME_LIST_FLAG_COMPRESSED
;
821 if ((dent
->d_name
[0] >= '0') && (dent
->d_name
[0] <= '9'))
823 ymd_list
= add_to_name_list(ymd_list
, dent
->d_name
, file_size
, file_flags
);
824 store_size
+= file_size
;
826 else if (((dent
->d_name
[0] == 'T') || (dent
->d_name
[0] == 't')) && (dent
->d_name
[1] >= '0') && (dent
->d_name
[1] <= '9'))
828 ymd_list
= add_to_name_list(ymd_list
, dent
->d_name
, file_size
, file_flags
);
829 store_size
+= file_size
;
831 else if (!strncmp(dent
->d_name
, "AUX.", 4) && (dent
->d_name
[4] >= '0') && (dent
->d_name
[4] <= '9') && S_ISDIR(sb
.st_mode
))
833 file_size
= directory_size(dent
->d_name
);
834 aux_list
= add_to_name_list(aux_list
, dent
->d_name
, file_size
, file_flags
);
835 store_size
+= file_size
;
837 else if (!strncmp(dent
->d_name
, "BB.AUX.", 7) && (dent
->d_name
[7] >= '0') && (dent
->d_name
[7] <= '9') && S_ISDIR(sb
.st_mode
))
839 file_size
= directory_size(dent
->d_name
);
840 bb_aux_list
= add_to_name_list(bb_aux_list
, dent
->d_name
, file_size
, file_flags
);
841 store_size
+= file_size
;
843 else if (!strncmp(dent
->d_name
, "BB.", 3) && (dent
->d_name
[3] >= '0') && (dent
->d_name
[3] <= '9'))
845 bb_list
= add_to_name_list(bb_list
, dent
->d_name
, file_size
, file_flags
);
846 store_size
+= file_size
;
848 else if ((!strcmp(dent
->d_name
, ".")) || (!strcmp(dent
->d_name
, "..")))
850 else if ((!strcmp(dent
->d_name
, "StoreData")) || (!strcmp(dent
->d_name
, "SweepStore")))
852 else if (!strcmp(dent
->d_name
, ASL_INTERNAL_LOGS_DIR
))
856 debug_log(ASL_LEVEL_ERR
, "aslmanager: unexpected file %s in ASL data store\n", dent
->d_name
);
862 debug_log(ASL_LEVEL_NOTICE
, "Data Store Size = %lu\n", store_size
);
863 asl_core_time_to_str(ttl
, tstr
, sizeof(tstr
));
864 debug_log(ASL_LEVEL_NOTICE
, "Data Store YMD Files (TTL = %s)\n", tstr
);
865 for (e
= ymd_list
; e
!= NULL
; e
= e
->next
)
867 uint32_t age
= ymd_file_age(e
->name
, now
, NULL
, NULL
);
868 asl_core_time_to_str(age
, tstr
, sizeof(tstr
));
869 debug_log(ASL_LEVEL_NOTICE
, " %s %lu (age %s%s)\n", e
->name
, e
->size
, tstr
, (age
> ttl
) ? " - expired" : "");
872 debug_log(ASL_LEVEL_NOTICE
, "Data Store AUX Directories\n");
873 for (e
= aux_list
; e
!= NULL
; e
= e
->next
)
875 uint32_t age
= ymd_file_age(e
->name
+ 4, now
, NULL
, NULL
) / SECONDS_PER_DAY
;
876 asl_core_time_to_str(age
, tstr
, sizeof(tstr
));
877 debug_log(ASL_LEVEL_NOTICE
, " %s %lu (age %s)\n", e
->name
, e
->size
, tstr
, (age
> ttl
) ? " - expired" : "");
880 debug_log(ASL_LEVEL_NOTICE
, "Data Store BB.AUX Directories\n");
881 for (e
= bb_aux_list
; e
!= NULL
; e
= e
->next
)
883 uint32_t age
= ymd_file_age(e
->name
+ 7, now
, NULL
, NULL
);
884 asl_core_time_to_str(age
, tstr
, sizeof(tstr
));
885 debug_log(ASL_LEVEL_NOTICE
, " %s %lu (age %s)\n", e
->name
, e
->size
, tstr
, ((age
/ SECONDS_PER_DAY
) > 0) ? " - expired" : "");
888 debug_log(ASL_LEVEL_NOTICE
, "Data Store BB Files\n");
889 for (e
= bb_list
; e
!= NULL
; e
= e
->next
)
891 uint32_t age
= ymd_file_age(e
->name
+ 3, now
, NULL
, NULL
) / SECONDS_PER_DAY
;
892 asl_core_time_to_str(age
, tstr
, sizeof(tstr
));
893 debug_log(ASL_LEVEL_NOTICE
, " %s %lu (age %s)\n", e
->name
, e
->size
, tstr
, ((age
/ SECONDS_PER_DAY
) > 0) ? " - expired" : "");
896 /* Delete/achive expired YMD files */
897 debug_log(ASL_LEVEL_NOTICE
, "Start YMD File Scan\n");
904 uint32_t age
= ymd_file_age(e
->name
, now
, &ymd_uid
, &ymd_gid
);
908 /* file has expired, archive it if required, then unlink it */
909 if (dst
->rotate_dir
!= NULL
)
912 asprintf(&str
, "%s/%s", dst
->rotate_dir
, e
->name
);
913 if (str
== NULL
) return -1;
915 filesystem_copy(dst
, e
->name
, str
, 0);
919 filesystem_unlink(e
->name
);
920 store_size
-= e
->size
;
923 else if ((e
->flags
& NAME_LIST_FLAG_COMPRESSED
) == 0)
925 uint32_t i
, bit
, keep_mask
;
926 mode_t ymd_mode
= 0600;
928 /* check if there are any per-level TTLs and filter the file if required */
931 keep_mask
= 0x000000ff;
933 for (i
= 0; i
<= 7; i
++)
935 if ((dst
->ttl
[i
] > 0) && (age
>= dst
->ttl
[i
])) keep_mask
&= ~bit
;
939 memset(&sb
, 0, sizeof(struct stat
));
940 if (stat(e
->name
, &sb
) == 0) ymd_mode
= sb
.st_mode
& 0777;
942 if (keep_mask
!= 0x000000ff) ymd_file_filter(e
->name
, dst
->path
, keep_mask
, ymd_mode
, ymd_uid
, ymd_gid
);
945 if ((age
> since_midnight
) && (dst
->flags
& MODULE_FLAG_COMPRESS
))
947 char gzdst
[MAXPATHLEN
];
949 snprintf(gzdst
, sizeof(gzdst
), "%s.gz", e
->name
);
950 debug_log(ASL_LEVEL_NOTICE
, " compress %s ---> %s\n", e
->name
, gzdst
);
954 int status
= copy_compress_file(dst
, e
->name
, gzdst
);
957 filesystem_unlink(e
->name
);
961 debug_log(ASL_LEVEL_ERR
, " FAILED status %d errno %d [%s] compress %s ---> %s\n", status
, errno
, strerror(errno
), e
->name
, gzdst
);
971 debug_log(ASL_LEVEL_NOTICE
, "Finished YMD File Scan\n");
973 /* Delete/achive expired YMD AUX directories */
974 debug_log(ASL_LEVEL_NOTICE
, "Start AUX Directory Scan\n");
979 uint32_t age
= ymd_file_age(e
->name
+ 4, now
, NULL
, NULL
);
983 if (dst
->rotate_dir
!= NULL
)
986 asprintf(&str
, "%s/%s", dst
->rotate_dir
, e
->name
);
987 if (str
== NULL
) return -1;
989 filesystem_copy(dst
, e
->name
, str
, 0);
993 remove_directory(e
->name
);
994 store_size
-= e
->size
;
1001 debug_log(ASL_LEVEL_NOTICE
, "Finished AUX Directory Scan\n");
1003 /* Delete/achive expired BB.AUX directories */
1004 debug_log(ASL_LEVEL_NOTICE
, "Start BB.AUX Directory Scan\n");
1009 uint32_t age
= ymd_file_age(e
->name
+ 7, now
, NULL
, NULL
);
1013 if (dst
->rotate_dir
!= NULL
)
1016 asprintf(&str
, "%s/%s", dst
->rotate_dir
, e
->name
);
1017 if (str
== NULL
) return -1;
1019 filesystem_copy(dst
, e
->name
, str
, 0);
1023 remove_directory(e
->name
);
1024 store_size
-= e
->size
;
1031 debug_log(ASL_LEVEL_NOTICE
, "Finished BB.AUX Directory Scan\n");
1033 /* Delete/achive expired BB files */
1034 debug_log(ASL_LEVEL_NOTICE
, "Start BB Scan\n");
1039 uint32_t age
= ymd_file_age(e
->name
+ 3, now
, NULL
, NULL
);
1043 if (dst
->rotate_dir
!= NULL
)
1046 asprintf(&str
, "%s/%s", dst
->rotate_dir
, e
->name
);
1047 if (str
== NULL
) return -1;
1049 /* syslog -x [str] -f [e->name] */
1050 filesystem_copy(dst
, e
->name
, str
, 0);
1054 filesystem_unlink(e
->name
);
1055 store_size
-= e
->size
;
1062 debug_log(ASL_LEVEL_NOTICE
, "Finished BB Scan\n");
1066 /* if data store is over max_size, delete/archive more YMD files */
1067 if (store_size
> all_max
) debug_log(ASL_LEVEL_NOTICE
, "Additional YMD Scan\n");
1070 while ((e
!= NULL
) && (store_size
> all_max
))
1074 uint32_t age
= ymd_file_age(e
->name
, now
, NULL
, NULL
);
1077 /* do not touch active file YYYY.MM.DD.asl */
1082 if (dst
->rotate_dir
!= NULL
)
1085 asprintf(&str
, "%s/%s", dst
->rotate_dir
, e
->name
);
1086 if (str
== NULL
) return -1;
1088 /* syslog -x [str] -f [e->name] */
1089 filesystem_copy(dst
, e
->name
, str
, 0);
1093 filesystem_unlink(e
->name
);
1094 store_size
-= e
->size
;
1101 /* if data store is over all_max, delete/archive more BB files */
1102 if (store_size
> all_max
) debug_log(ASL_LEVEL_NOTICE
, "Additional BB Scan\n");
1105 while ((e
!= NULL
) && (store_size
> all_max
))
1109 if (dst
->rotate_dir
!= NULL
)
1112 asprintf(&str
, "%s/%s", dst
->rotate_dir
, e
->name
);
1113 if (str
== NULL
) return -1;
1115 /* syslog -x [str] -f [e->name] */
1116 filesystem_copy(dst
, e
->name
, str
, 0);
1120 filesystem_unlink(e
->name
);
1121 store_size
-= e
->size
;
1129 free_name_list(ymd_list
);
1130 free_name_list(bb_list
);
1131 free_name_list(aux_list
);
1132 free_name_list(bb_aux_list
);
1134 debug_log(ASL_LEVEL_NOTICE
, "Data Store Size = %lu\n", store_size
);
1139 /* move sequenced source files to dst dir, renaming as we go */
1141 module_copy_rename(asl_out_dst_data_t
*dst
)
1143 asl_out_file_list_t
*src_list
, *dst_list
, *f
;
1145 char fpathsrc
[MAXPATHLEN
], fpathdst
[MAXPATHLEN
];
1146 uint32_t src_count
, dst_count
;
1149 if (dst
== NULL
) return -1;
1150 if (dst
->path
== NULL
) return -1;
1152 src_list
= asl_list_src_files(dst
);
1155 * Note: the unmarked file (e.g. system.log) is included in src_list.
1156 * If it is from a MODULE_FLAG_EXTERNAL dst and it is less than 24 hours old,
1157 * we ignore it. If it is not external, we also ignore it since syslogd will
1158 * checkpoint it to create system.log.Tnnnnnnnnnn.
1160 if ((src_list
!= NULL
) && (src_list
->stamp
== STAMP_STYLE_NULL
))
1162 bool ignore_it
= false;
1164 if (dst
->flags
& MODULE_FLAG_EXTERNAL
)
1166 if ((time(NULL
) - src_list
->ftime
) < SECONDS_PER_DAY
)
1168 debug_log(ASL_LEVEL_INFO
, " ignore src file %s since it is external and less than a day old\n", src_list
->name
);
1174 debug_log(ASL_LEVEL_INFO
, " ignore src file %s since it is internal and syslogd will checkpoint it when it needs to be renamed\n", src_list
->name
);
1180 asl_out_file_list_t
*first
= src_list
;
1181 src_list
= src_list
->next
;
1183 asl_out_file_list_free(first
);
1187 if (src_list
== NULL
)
1189 debug_log(ASL_LEVEL_INFO
, " no src files\n");
1193 debug_log(ASL_LEVEL_INFO
, " src files\n");
1196 for (f
= src_list
; f
!= NULL
; f
= f
->next
)
1198 debug_log(ASL_LEVEL_INFO
, " %s\n", f
->name
);
1202 dst_list
= asl_list_dst_files(dst
);
1204 dst_dir
= dst
->rotate_dir
;
1205 if (dst_dir
== NULL
) dst_dir
= dst
->dir
;
1209 if (dst_list
== NULL
) debug_log(ASL_LEVEL_INFO
, " no dst files\n");
1210 else debug_log(ASL_LEVEL_INFO
, " dst files\n");
1212 for (f
= dst_list
; f
!= NULL
; f
= f
->next
)
1214 debug_log(ASL_LEVEL_INFO
, " %s\n", f
->name
);
1218 if (dst
->style_flags
& MODULE_NAME_STYLE_STAMP_SEQ
)
1220 for (f
= dst_list
; f
!= NULL
; f
= f
->next
)
1223 char *dot
= strrchr(f
->name
, '.');
1224 if ((dot
!= NULL
) && (!strcmp(dot
, ".gz"))) is_gz
= 1;
1226 snprintf(fpathsrc
, sizeof(fpathsrc
), "%s/%s", dst_dir
, f
->name
);
1228 if (dst
->style_flags
& MODULE_NAME_STYLE_FORMAT_BS
)
1230 snprintf(fpathdst
, sizeof(fpathdst
), "%s/%s.%d%s", dst_dir
, dst
->base
, f
->seq
+src_count
, (is_gz
== 1) ? ".gz" : "");
1232 else if (dst
->style_flags
& MODULE_NAME_STYLE_FORMAT_BES
)
1234 snprintf(fpathdst
, sizeof(fpathdst
), "%s/%s.%s.%d%s", dst_dir
, dst
->base
, dst
->ext
, f
->seq
+src_count
, (is_gz
== 1) ? ".gz" : "");
1236 else if (dst
->style_flags
& MODULE_NAME_STYLE_FORMAT_BSE
)
1238 snprintf(fpathdst
, sizeof(fpathdst
), "%s/%s.%d.%s%s", dst_dir
, dst
->base
, f
->seq
+src_count
, dst
->ext
, (is_gz
== 1) ? ".gz" : "");
1241 filesystem_rename(fpathsrc
, fpathdst
);
1244 for (f
= src_list
, x
= 0; f
!= NULL
; f
= f
->next
, x
++)
1246 snprintf(fpathsrc
, sizeof(fpathsrc
), "%s/%s", dst
->dir
, f
->name
);
1248 if (dst
->style_flags
& MODULE_NAME_STYLE_FORMAT_BS
)
1250 snprintf(fpathdst
, sizeof(fpathdst
), "%s/%s.%d", dst_dir
, dst
->base
, x
);
1252 else if (dst
->style_flags
& MODULE_NAME_STYLE_FORMAT_BES
)
1254 snprintf(fpathdst
, sizeof(fpathdst
), "%s/%s.%s.%d", dst_dir
, dst
->base
, dst
->ext
, x
);
1256 else if (dst
->style_flags
& MODULE_NAME_STYLE_FORMAT_BSE
)
1258 snprintf(fpathdst
, sizeof(fpathdst
), "%s/%s.%d.%s", dst_dir
, dst
->base
, x
, dst
->ext
);
1261 moved
= filesystem_copy(dst
, fpathsrc
, fpathdst
, dst
->flags
);
1264 if (dst
->flags
& MODULE_FLAG_TRUNCATE
)
1266 filesystem_truncate(fpathsrc
);
1267 filesystem_reset_ctime(fpathsrc
);
1271 filesystem_unlink(fpathsrc
);
1278 for (f
= src_list
; f
!= NULL
; f
= f
->next
)
1280 /* final / active base stamped file looks like a checkpointed file - ignore it */
1281 if ((dst
->flags
& MODULE_FLAG_BASESTAMP
) && (f
->next
== NULL
)) break;
1283 snprintf(fpathsrc
, sizeof(fpathsrc
), "%s/%s", dst
->dir
, f
->name
);
1285 /* MODULE_FLAG_EXTERNAL files are not decorated with a timestamp */
1286 if (dst
->flags
& MODULE_FLAG_EXTERNAL
)
1290 asl_make_timestamp(f
->ftime
, dst
->style_flags
, tstamp
, sizeof(tstamp
));
1292 if (dst
->style_flags
& MODULE_NAME_STYLE_FORMAT_BS
)
1294 snprintf(fpathdst
, sizeof(fpathdst
), "%s/%s.%s", dst_dir
, dst
->base
, tstamp
);
1296 else if (dst
->style_flags
& MODULE_NAME_STYLE_FORMAT_BES
)
1298 snprintf(fpathdst
, sizeof(fpathdst
), "%s/%s.%s.%s", dst_dir
, dst
->base
, dst
->ext
, tstamp
);
1300 else if (dst
->style_flags
& MODULE_NAME_STYLE_FORMAT_BSE
)
1302 snprintf(fpathdst
, sizeof(fpathdst
), "%s/%s.%s.%s", dst_dir
, dst
->base
, tstamp
, dst
->ext
);
1308 snprintf(fpathdst
, sizeof(fpathdst
), "%s/%s", dst_dir
, f
->name
);
1311 moved
= filesystem_copy(dst
, fpathsrc
, fpathdst
, dst
->flags
);
1314 if (dst
->flags
& MODULE_FLAG_TRUNCATE
) filesystem_truncate(fpathsrc
);
1315 else filesystem_unlink(fpathsrc
);
1320 asl_out_file_list_free(src_list
);
1321 asl_out_file_list_free(dst_list
);
1326 /* delete expired files */
1328 module_expire(asl_out_dst_data_t
*dst
, asl_out_dst_data_t
*opts
)
1330 asl_out_file_list_t
*dst_list
, *f
;
1331 char *base
, *dst_dir
, fpath
[MAXPATHLEN
];
1332 time_t now
, ttl
, age
;
1334 if (dst
== NULL
) return -1;
1335 if (dst
->path
== NULL
) return -1;
1336 if (dst
->ttl
[LEVEL_ALL
] == 0) return 0;
1338 ttl
= dst
->ttl
[LEVEL_ALL
];
1339 if ((opts
!= NULL
) && (opts
->ttl
[LEVEL_ALL
] > 0)) ttl
= opts
->ttl
[LEVEL_ALL
];
1342 if (ttl
> now
) return 0;
1344 base
= strrchr(dst
->path
, '/');
1345 if (base
== NULL
) return -1;
1347 dst_list
= asl_list_dst_files(dst
);
1351 dst_dir
= dst
->rotate_dir
;
1352 if (dst_dir
== NULL
) dst_dir
= dst
->dir
;
1354 if (dst_list
== NULL
)
1356 debug_log(ASL_LEVEL_INFO
, " no dst files\n");
1360 debug_log(ASL_LEVEL_INFO
, " dst files\n");
1361 for (f
= dst_list
; f
!= NULL
; f
= f
->next
)
1364 age
= now
- f
->ftime
;
1366 asl_core_time_to_str(age
, tstr
, sizeof(tstr
));
1367 debug_log(ASL_LEVEL_INFO
, " %s (age %s%s)\n", f
->name
, tstr
, (age
> ttl
) ? " - expired" : "");
1371 for (f
= dst_list
; f
!= NULL
; f
= f
->next
)
1373 age
= now
- f
->ftime
;
1376 snprintf(fpath
, sizeof(fpath
), "%s/%s", dst_dir
, f
->name
);
1377 filesystem_unlink(fpath
);
1381 asl_out_file_list_free(dst_list
);
1383 if (base
!= NULL
) *base
= '/';
1389 * Check all_max size and delete files (oldest first) to stay within size limit.
1390 * If query is true, then just report total size.
1393 module_check_size(asl_out_dst_data_t
*dst
, asl_out_dst_data_t
*opts
, bool query
, size_t *msize
)
1395 asl_out_file_list_t
*dst_list
, *f
, *dst_end
;
1396 char *dst_dir
, fpath
[MAXPATHLEN
];
1399 size_t all_max
= dst
->all_max
;
1400 if ((opts
!= NULL
) && (opts
->all_max
> 0)) all_max
= opts
->all_max
;
1402 if (dst
== NULL
) return -1;
1403 if (dst
->path
== NULL
) return -1;
1405 if (all_max
== 0) return 0;
1407 dst_list
= asl_list_dst_files(dst
);
1408 if (dst_list
== NULL
)
1410 debug_log(ASL_LEVEL_INFO
, " no dst files\n");
1414 dst_dir
= dst
->rotate_dir
;
1415 if (dst_dir
== NULL
) dst_dir
= dst
->dir
;
1417 debug_log(ASL_LEVEL_INFO
, " dst files\n");
1419 for (f
= dst_list
; f
!= NULL
; f
= f
->next
)
1422 debug_log(ASL_LEVEL_INFO
, " %s size %lu\n", f
->name
, f
->size
);
1426 for (f
= dst_list
; f
!= NULL
; f
= f
->next
) total
+= f
->size
;
1430 for (f
= dst_list
; (total
> all_max
) && (f
!= NULL
); f
= f
->next
)
1432 snprintf(fpath
, sizeof(fpath
), "%s/%s", dst_dir
, f
->name
);
1433 filesystem_unlink(fpath
);
1438 if (msize
!= NULL
) *msize
= total
;
1440 asl_out_file_list_free(dst_list
);
1446 process_dst(asl_out_dst_data_t
*dst
, asl_out_dst_data_t
*opts
)
1448 uint32_t ttl
= dst
->ttl
[LEVEL_ALL
];
1449 if ((opts
!= NULL
) && (opts
->ttl
[LEVEL_ALL
] > 0)) ttl
= opts
->ttl
[LEVEL_ALL
];
1451 size_t all_max
= dst
->all_max
;
1452 if ((opts
!= NULL
) && (opts
->all_max
> 0)) all_max
= opts
->all_max
;
1456 debug_log(ASL_LEVEL_NOTICE
, "NULL dst data for output rule - skipped\n");
1458 else if (dst
->flags
& MODULE_FLAG_ROTATE
)
1460 debug_log(ASL_LEVEL_NOTICE
, "Checking file %s\n", dst
->path
);
1461 debug_log(ASL_LEVEL_NOTICE
, "- Rename, move to destination directory, and compress as required\n");
1463 module_copy_rename(dst
);
1465 if ((ttl
> 0) && !(dst
->flags
& MODULE_FLAG_SIZE_ONLY
))
1469 asl_core_time_to_str(ttl
, tstr
, sizeof(tstr
));
1470 debug_log(ASL_LEVEL_NOTICE
, "- Check for expired files - TTL = %s\n", tstr
);
1471 module_expire(dst
, opts
);
1476 debug_log(ASL_LEVEL_NOTICE
, "- Check total storage used - MAX = %lu\n", all_max
);
1477 module_check_size(dst
, opts
, false, NULL
);
1480 else if ((dst
->flags
& MODULE_FLAG_TYPE_ASL_DIR
) && (ttl
> 0))
1482 process_asl_data_store(dst
, opts
);
1489 process_module(asl_out_module_t
*mod
, asl_out_dst_data_t
*opts
)
1494 if (mod
== NULL
) return -1;
1496 if (opts
!= NULL
) flags
= opts
->flags
;
1498 debug_log(ASL_LEVEL_NOTICE
, "----------------------------------------\n");
1499 debug_log(ASL_LEVEL_NOTICE
, "Processing module %s\n", (mod
->name
== NULL
) ? "asl.conf" : mod
->name
);
1501 for (r
= mod
->ruleset
; r
!= NULL
; r
= r
->next
)
1503 if (r
->action
== ACTION_OUT_DEST
)
1505 if ((flags
== 0) || ((flags
& r
->dst
->flags
) != 0)) process_dst(r
->dst
, opts
);
1509 debug_log(ASL_LEVEL_NOTICE
, "Finished processing module %s\n", (mod
->name
== NULL
) ? "asl.conf" : mod
->name
);
1514 control_query(asl_msg_t
*a
)
1516 asl_msg_list_t
*out
;
1517 char *qstr
, *str
, *res
;
1518 uint32_t len
, reslen
, status
;
1519 uint64_t cmax
, qmin
;
1520 kern_return_t kstatus
;
1523 if (asl_server_port
== MACH_PORT_NULL
)
1525 kstatus
= bootstrap_look_up2(bootstrap_port
, ASL_SERVICE_NAME
, &asl_server_port
, 0, BOOTSTRAP_PRIVILEGED_SERVER
);
1526 if (asl_server_port
== MACH_PORT_NULL
) return NULL
;
1529 qstr
= asl_msg_to_string((asl_msg_t
*)a
, &len
);
1534 asprintf(&str
, "1\nQ [= ASLOption control]\n");
1538 asprintf(&str
, "1\n%s [= ASLOption control]\n", qstr
);
1542 if (str
== NULL
) return NULL
;
1544 /* length includes trailing nul */
1545 len
= strlen(str
) + 1;
1552 status
= ASL_STATUS_OK
;
1554 kstatus
= vm_allocate(mach_task_self(), (vm_address_t
*)&vmstr
, len
, VM_FLAGS_ANYWHERE
| VM_MAKE_TAG(VM_MEMORY_ASL
));
1555 if (kstatus
!= KERN_SUCCESS
) return NULL
;
1557 memmove(vmstr
, str
, len
);
1561 kstatus
= _asl_server_query_2(asl_server_port
, vmstr
, len
, qmin
, 1, 0, (caddr_t
*)&res
, &reslen
, &cmax
, (int *)&status
);
1562 if (kstatus
!= KERN_SUCCESS
) return NULL
;
1564 if (res
== NULL
) return NULL
;
1566 out
= asl_msg_list_from_string(res
);
1567 vm_deallocate(mach_task_self(), (vm_address_t
)res
, reslen
);
1573 checkpoint(const char *name
)
1575 /* send checkpoint message to syslogd */
1576 debug_log(ASL_LEVEL_NOTICE
, "Checkpoint module %s\n", (name
== NULL
) ? "*" : name
);
1577 if (dryrun
) return 0;
1579 asl_msg_t
*qmsg
= asl_msg_new(ASL_TYPE_QUERY
);
1581 asl_msg_list_t
*res
;
1583 asprintf(&tmp
, "%s checkpoint", (name
== NULL
) ? "*" : name
);
1584 asl_msg_set_key_val_op(qmsg
, "action", tmp
, ASL_QUERY_OP_EQUAL
);
1587 res
= control_query(qmsg
);
1589 asl_msg_list_release(res
);