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@
34 #include <sys/param.h>
35 #include <servers/bootstrap.h>
36 #include <bootstrap_priv.h>
37 #include <mach/mach.h>
42 #include <xpc/private.h>
43 #include <os/assumes.h>
44 #include <vproc_priv.h>
46 #include <asl_private.h>
49 #include <asl_store.h>
50 #include "asl_common.h"
52 #define DEFAULT_MAX_SIZE 150000000
53 #define IOBUFSIZE 4096
55 #define DO_ASLDB 0x00000001
56 #define DO_MODULE 0x00000002
57 #define DO_CHECKPT 0x00000004
59 #define DEBUG_FLAG_MASK 0xfffffff0
60 #define DEBUG_LEVEL_MASK 0x0000000f
61 #define DEBUG_STDERR 0x00000010
62 #define DEBUG_ASL 0x00000020
64 #define AUX_URL_MINE "file:///var/log/asl/"
65 #define AUX_URL_MINE_LEN 20
67 /* length of "file://" */
68 #define AUX_URL_PATH_OFFSET 7
70 extern kern_return_t _asl_server_query
74 mach_msg_type_number_t requestCnt
,
79 mach_msg_type_number_t
*replyCnt
,
82 security_token_t
*token
86 static time_t module_ttl
;
87 static uint32_t debug
;
89 static int asl_aux_fd
= -1;
90 static aslclient aslc
;
91 static mach_port_t asl_server_port
;
92 static xpc_connection_t listener
;
93 static dispatch_queue_t serverq
;
95 typedef struct name_list_s
99 struct name_list_s
*next
;
103 keep_str(uint8_t mask
)
108 memset(str
, 0, sizeof(str
));
109 if (mask
& 0x01) str
[x
++] = '0';
110 if (mask
& 0x02) str
[x
++] = '1';
111 if (mask
& 0x04) str
[x
++] = '2';
112 if (mask
& 0x08) str
[x
++] = '3';
113 if (mask
& 0x10) str
[x
++] = '4';
114 if (mask
& 0x20) str
[x
++] = '5';
115 if (mask
& 0x40) str
[x
++] = '6';
116 if (mask
& 0x80) str
[x
++] = '7';
117 if (x
== 0) str
[x
++] = '-';
122 set_debug(int flag
, const char *str
)
126 if (str
== NULL
) x
= ASL_LEVEL_ERR
;
127 else if (((str
[0] == 'L') || (str
[0] == 'l')) && ((str
[1] >= '0') && (str
[1] <= '7')) && (str
[2] == '\0')) x
= atoi(str
+1);
128 else if ((str
[0] >= '0') && (str
[0] <= '7') && (str
[1] == '\0')) x
= ASL_LEVEL_CRIT
+ atoi(str
);
129 else x
= ASL_LEVEL_ERR
;
132 else if (x
> 7) x
= 7;
134 level
= debug
& DEBUG_LEVEL_MASK
;
135 if (x
> level
) level
= x
;
137 debug
= debug
& DEBUG_FLAG_MASK
;
143 debug_log(int level
, const char *str
, ...)
147 if ((debug
& DEBUG_STDERR
) && (level
<= (debug
& DEBUG_LEVEL_MASK
)))
150 vfprintf(stderr
, str
, v
);
154 if (debug
& DEBUG_ASL
)
160 aslc
= asl_open("aslmanager", "syslog", 0);
161 asl_msg_t
*msg
= asl_msg_new(ASL_TYPE_MSG
);
163 asl_msg_set_key_val(msg
, ASL_KEY_MSG
, "Status Report");
164 asl_msg_set_key_val(msg
, ASL_KEY_LEVEL
, ASL_STRING_NOTICE
);
165 asl_create_auxiliary_file((asl_object_t
)msg
, "Status Report", "public.text", &asl_aux_fd
);
166 asl_msg_release(msg
);
170 vasprintf(&line
, str
, v
);
173 if (line
!= NULL
) write(asl_aux_fd
, line
, strlen(line
));
178 __attribute__((noreturn
)) static void
179 xpc_server_exit(int status
)
181 xpc_connection_cancel(listener
);
182 xpc_release(listener
);
183 dispatch_release(serverq
);
188 add_to_name_list(name_list_t
*l
, const char *name
, size_t size
)
192 if (name
== NULL
) return l
;
194 e
= (name_list_t
*)calloc(1, sizeof(name_list_t
));
195 if (e
== NULL
) return NULL
;
197 e
->name
= strdup(name
);
206 /* list is sorted by name (i.e. primarily by timestamp) */
207 if (l
== NULL
) return e
;
209 if (strcmp(e
->name
, l
->name
) <= 0)
215 for (x
= l
; (x
->next
!= NULL
) && (strcmp(e
->name
, x
->next
->name
) > 0) ; x
= x
->next
);
223 free_name_list(name_list_t
*l
)
238 * Copy ASL files by reading and writing each record.
241 copy_asl_file(const char *src
, const char *dst
, mode_t mode
)
249 if (src
== NULL
) return ASL_STATUS_INVALID_ARG
;
250 if (dst
== NULL
) return ASL_STATUS_INVALID_ARG
;
253 status
= asl_file_open_read(src
, &f
);
254 if (status
!= ASL_STATUS_OK
) return status
;
259 res
= asl_file_match(f
, NULL
, &mid
, 0, 0, 0, 1);
262 if (res
== NULL
) return ASL_STATUS_OK
;
263 rcount
= asl_msg_list_count(res
);
266 asl_msg_list_release(res
);
267 return ASL_STATUS_OK
;
271 status
= asl_file_open_write(dst
, mode
, -1, -1, &f
);
272 if (status
!= ASL_STATUS_OK
) return status
;
273 if (f
== ASL_STATUS_OK
) return ASL_STATUS_FAILED
;
275 f
->flags
= ASL_FILE_FLAG_PRESERVE_MSG_ID
;
277 for (i
= 0; i
< rcount
; i
++)
280 status
= asl_file_save(f
, asl_msg_list_get_index(res
, i
), &mid
);
281 if (status
!= ASL_STATUS_OK
) break;
289 copy_compress_file(asl_out_dst_data_t
*asldst
, const char *src
, const char *dst
)
296 in
= open(src
, O_RDONLY
, 0);
297 if (in
< 0) return -1;
299 out
= open(dst
, O_WRONLY
| O_CREAT
, asldst
->mode
);
300 if (out
>= 0) out
= asl_out_dst_set_access(out
, asldst
);
307 gz
= gzdopen(out
, "w");
316 n
= read(in
, buf
, sizeof(buf
));
317 if (n
> 0) gzwrite(gz
, buf
, n
);
318 } while (n
== IOBUFSIZE
);
328 filesystem_rename(const char *src
, const char *dst
)
332 debug_log(ASL_LEVEL_NOTICE
, " rename %s ---> %s\n", src
, dst
);
333 if (dryrun
== 1) return;
335 status
= rename(src
, dst
);
336 if (status
!= 0) debug_log(ASL_LEVEL_ERR
, " FAILED status %d errno %d [%s] rename %s ---> %s\n", status
, errno
, strerror(errno
), src
, dst
);
340 filesystem_unlink(const char *path
)
344 debug_log(ASL_LEVEL_NOTICE
, " remove %s\n", path
);
345 if (dryrun
== 1) return;
347 status
= unlink(path
);
348 if (status
!= 0) debug_log(ASL_LEVEL_ERR
, " FAILED status %d errno %d [%s] unlink %s\n", status
, errno
, strerror(errno
), path
);
352 filesystem_truncate(const char *path
)
356 debug_log(ASL_LEVEL_NOTICE
, " truncate %s\n", path
);
357 if (dryrun
== 1) return;
359 status
= truncate(path
, 0);
360 if (status
!= 0) debug_log(ASL_LEVEL_ERR
, " FAILED status %d errno %d [%s] unlink %s\n", status
, errno
, strerror(errno
), path
);
364 filesystem_rmdir(const char *path
)
368 debug_log(ASL_LEVEL_NOTICE
, " remove directory %s\n", path
);
369 if (dryrun
== 1) return;
371 status
= rmdir(path
);
372 if (status
!= 0) debug_log(ASL_LEVEL_ERR
, " FAILED status %d errno %d [%s] rmdir %s\n", status
, errno
, strerror(errno
), path
);
376 filesystem_copy(asl_out_dst_data_t
*asldst
, const char *src
, const char *dst
, uint32_t flags
)
380 if ((src
== NULL
) || (dst
== NULL
)) return 0;
382 dot
= strrchr(src
, '.');
383 if ((dot
!= NULL
) && (!strcmp(dot
, ".gz"))) flags
&= ~MODULE_FLAG_COMPRESS
;
385 if (((flags
& MODULE_FLAG_COMPRESS
) == 0) && (!strcmp(src
, dst
))) return 0;
387 if (flags
& MODULE_FLAG_TYPE_ASL
) debug_log(ASL_LEVEL_NOTICE
, " copy asl %s ---> %s\n", src
, dst
);
388 else if (flags
& MODULE_FLAG_COMPRESS
) debug_log(ASL_LEVEL_NOTICE
, " copy compress %s ---> %s.gz\n", src
, dst
);
389 else debug_log(ASL_LEVEL_NOTICE
, " copy %s ---> %s\n", src
, dst
);
391 if (dryrun
== 1) return 0;
393 if (flags
& MODULE_FLAG_TYPE_ASL
)
395 uint32_t status
= copy_asl_file(src
, dst
, asldst
->mode
);
398 debug_log(ASL_LEVEL_ERR
, " FAILED status %u [%s] asl copy %s ---> %s\n", status
, asl_core_error(status
), src
, dst
);
402 else if (flags
& MODULE_FLAG_COMPRESS
)
404 char gzdst
[MAXPATHLEN
];
406 snprintf(gzdst
, sizeof(gzdst
), "%s.gz", dst
);
408 int status
= copy_compress_file(asldst
, src
, gzdst
);
411 debug_log(ASL_LEVEL_ERR
, " FAILED status %d errno %d [%s] copy & compress %s ---> %s\n", status
, errno
, strerror(errno
), src
, dst
);
417 int status
= copyfile(src
, dst
, NULL
, COPYFILE_ALL
| COPYFILE_RECURSIVE
);
420 debug_log(ASL_LEVEL_ERR
, " FAILED status %d errno %d [%s] copy %s ---> %s\n", status
, errno
, strerror(errno
), src
, dst
);
429 remove_directory(const char *path
)
436 if (dp
== NULL
) return 0;
438 while ((dent
= readdir(dp
)) != NULL
)
440 if ((!strcmp(dent
->d_name
, ".")) || (!strcmp(dent
->d_name
, ".."))) continue;
441 asprintf(&str
, "%s/%s", path
, dent
->d_name
);
444 filesystem_unlink(str
);
451 filesystem_rmdir(path
);
457 * Determine the age (in whole days) of a YMD file from its name.
458 * Also determines UID and GID from ".Unnn.Gnnn" part of file name.
461 ymd_file_age(const char *name
, time_t now
, uid_t
*u
, gid_t
*g
)
468 if (name
== NULL
) return 0;
470 if (now
== 0) now
= time(NULL
);
472 memset(&ftime
, 0, sizeof(struct tm
));
475 /* name is YYYY.MM.DD.<...> */
477 if ((name
[0] < '0') || (name
[0] > '9')) return 0;
478 ftime
.tm_year
= 1000 * (name
[0] - '0');
480 if ((name
[1] < '0') || (name
[1] > '9')) return 0;
481 ftime
.tm_year
+= 100 * (name
[1] - '0');
483 if ((name
[2] < '0') || (name
[2] > '9')) return 0;
484 ftime
.tm_year
+= 10 * (name
[2] - '0');
486 if ((name
[3] < '0') || (name
[3] > '9')) return 0;
487 ftime
.tm_year
+= name
[3] - '0';
488 ftime
.tm_year
-= 1900;
490 if (name
[4] != '.') return 0;
492 if ((name
[5] < '0') || (name
[5] > '9')) return 0;
493 ftime
.tm_mon
= 10 * (name
[5] - '0');
495 if ((name
[6] < '0') || (name
[6] > '9')) return 0;
496 ftime
.tm_mon
+= name
[6] - '0';
499 if (name
[7] != '.') return 0;
501 if ((name
[8] < '0') || (name
[8] > '9')) return 0;
502 ftime
.tm_mday
= 10 * (name
[8] - '0');
504 if ((name
[9] < '0') || (name
[9] > '9')) return 0;
505 ftime
.tm_mday
+= name
[9] - '0';
507 if (name
[10] != '.') return 0;
509 created
= mktime(&ftime
);
510 if (created
> now
) return 0;
512 days
= (now
- created
) / 86400;
517 p
= strchr(name
+10, 'U');
518 if (p
!= NULL
) *u
= atoi(p
+1);
524 p
= strchr(name
+10, 'G');
525 if (p
!= NULL
) *g
= atoi(p
+1);
532 aux_url_callback(const char *url
)
534 if (url
== NULL
) return;
535 if (!strncmp(url
, AUX_URL_MINE
, AUX_URL_MINE_LEN
)) filesystem_unlink(url
+ AUX_URL_PATH_OFFSET
);
539 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
)
541 asl_file_t
*f
= NULL
;
542 uint8_t km
= keep_mask
;
543 uint32_t status
, len
, dstcount
= 0;
544 char src
[MAXPATHLEN
];
545 char dst
[MAXPATHLEN
];
547 if (snprintf(src
, MAXPATHLEN
, "%s/%s", path
, name
) >= MAXPATHLEN
) return ASL_STATUS_FAILED
;
548 if (snprintf(dst
, MAXPATHLEN
, "%s/%s", path
, name
) >= MAXPATHLEN
) return ASL_STATUS_FAILED
;
549 len
= strlen(src
) - 3;
550 snprintf(dst
+ len
, 4, "tmp");
552 //TODO: check if src file is already filtered
553 debug_log(ASL_LEVEL_NOTICE
, " filter %s %s ---> %s\n", src
, keep_str(km
), dst
);
555 status
= ASL_STATUS_OK
;
559 status
= asl_file_open_read(name
, &f
);
560 if (status
!= ASL_STATUS_OK
) return status
;
562 status
= asl_file_filter_level(f
, dst
, keep_mask
, ymd_mode
, ymd_uid
, ymd_gid
, &dstcount
, aux_url_callback
);
566 filesystem_unlink(src
);
567 if ((status
!= ASL_STATUS_OK
) || (dstcount
== 0)) filesystem_unlink(dst
);
568 else filesystem_rename(dst
, src
);
574 * Used to set config parameters.
575 * Line format "= name value"
578 _aslmanager_set_param(asl_out_dst_data_t
*dst
, char *s
)
583 if (s
== NULL
) return;
584 if (s
[0] == '\0') return;
586 /* skip '=' and whitespace */
588 while ((*s
== ' ') || (*s
== '\t')) s
++;
590 l
= explode(s
, " \t");
591 if (l
== NULL
) return;
593 for (count
= 0; l
[count
] != NULL
; count
++);
595 /* name is required */
602 /* value is required */
609 if (!strcasecmp(l
[0], "aslmanager_debug"))
612 set_debug(DEBUG_ASL
, l
[1]);
614 else if (!strcasecmp(l
[0], "store_ttl"))
616 /* = store_ttl days */
617 dst
->ttl
[LEVEL_ALL
] = (time_t)atoll(l
[1]);
619 else if (!strcasecmp(l
[0], "module_ttl"))
621 /* = module_ttl days */
622 module_ttl
= (time_t)atoll(l
[1]);
624 else if (!strcasecmp(l
[0], "max_store_size"))
626 /* = max_file_size bytes */
627 dst
->all_max
= atoi(l
[1]);
629 else if (!strcasecmp(l
[0], "archive"))
631 free(dst
->rotate_dir
);
632 dst
->rotate_dir
= NULL
;
634 /* = archive {0|1} path */
635 if (!strcmp(l
[1], "1"))
637 if (l
[2] == NULL
) dst
->rotate_dir
= strdup(PATH_ASL_ARCHIVE
);
638 else dst
->rotate_dir
= strdup(l
[2]);
641 else if (!strcasecmp(l
[0], "store_path"))
645 dst
->path
= strdup(l
[1]);
647 else if (!strcasecmp(l
[0], "archive_mode"))
649 dst
->mode
= strtol(l
[1], NULL
, 0);
650 if ((dst
->mode
== 0) && (errno
== EINVAL
)) dst
->mode
= 0400;
657 directory_size(const char *path
)
666 if (dp
== NULL
) return 0;
669 while ((dent
= readdir(dp
)) != NULL
)
671 if ((!strcmp(dent
->d_name
, ".")) || (!strcmp(dent
->d_name
, ".."))) continue;
673 memset(&sb
, 0, sizeof(struct stat
));
675 asprintf(&str
, "%s/%s", path
, dent
->d_name
);
677 if ((str
!= NULL
) && (stat(str
, &sb
) == 0) && S_ISREG(sb
.st_mode
))
689 process_asl_data_store(asl_out_dst_data_t
*dst
)
691 int32_t today_ymd_stringlen
, expire_ymd_stringlen
;
692 time_t now
, ttl
, ymd_expire
;
694 char today_ymd_string
[32], expire_ymd_string
[32], *str
;
697 name_list_t
*ymd_list
, *bb_list
, *aux_list
, *bb_aux_list
, *e
;
698 size_t file_size
, store_size
;
707 if (dst
== NULL
) return 0;
708 if (dst
->path
== NULL
) return 0;
710 debug_log(ASL_LEVEL_NOTICE
, "----------------------------------------\n");
711 debug_log(ASL_LEVEL_NOTICE
, "Processing data store %s\n", dst
->path
);
713 if (dst
->rotate_dir
!= NULL
)
716 memset(&sb
, 0, sizeof(struct stat
));
717 if (stat(dst
->rotate_dir
, &sb
) == 0)
719 /* must be a directory */
720 if (!S_ISDIR(sb
.st_mode
))
722 debug_log(ASL_LEVEL_ERR
, "aslmanager error: archive %s is not a directory", dst
->rotate_dir
);
730 /* archive doesn't exist - create it */
731 if (mkdir(dst
->rotate_dir
, 0755) != 0)
733 debug_log(ASL_LEVEL_ERR
, "aslmanager error: can't create archive %s: %s\n", dst
->rotate_dir
, strerror(errno
));
739 /* stat failed for some other reason */
740 debug_log(ASL_LEVEL_ERR
, "aslmanager error: can't stat archive %s: %s\n", dst
->rotate_dir
, strerror(errno
));
748 /* determine current time */
751 /* ttl 0 means files never expire */
753 ttl
= dst
->ttl
[LEVEL_ALL
] * SECONDS_PER_DAY
;
755 if ((ttl
> 0) && (ttl
<= now
)) ymd_expire
= now
- ttl
;
757 /* construct today's date as YYYY.MM.DD */
758 memset(&ctm
, 0, sizeof(struct tm
));
759 if (localtime_r((const time_t *)&now
, &ctm
) == NULL
) return -1;
761 snprintf(today_ymd_string
, sizeof(today_ymd_string
), "%d.%02d.%02d.", ctm
.tm_year
+ 1900, ctm
.tm_mon
+ 1, ctm
.tm_mday
);
762 today_ymd_stringlen
= strlen(today_ymd_string
);
764 /* construct regular file expiry date as YYYY.MM.DD */
765 memset(&ctm
, 0, sizeof(struct tm
));
766 if (localtime_r((const time_t *)&ymd_expire
, &ctm
) == NULL
) return -1;
768 snprintf(expire_ymd_string
, sizeof(expire_ymd_string
), "%d.%02d.%02d.", ctm
.tm_year
+ 1900, ctm
.tm_mon
+ 1, ctm
.tm_mday
);
769 expire_ymd_stringlen
= strlen(expire_ymd_string
);
771 debug_log(ASL_LEVEL_NOTICE
, "Expiry Date %s\n", expire_ymd_string
);
773 dp
= opendir(dst
->path
);
774 if (dp
== NULL
) return -1;
776 /* gather a list of YMD files, AUX dirs, BB.AUX dirs, and BB files */
777 while ((dent
= readdir(dp
)) != NULL
)
779 memset(&sb
, 0, sizeof(struct stat
));
781 if (stat(dent
->d_name
, &sb
) == 0) file_size
= sb
.st_size
;
783 if ((dent
->d_name
[0] >= '0') && (dent
->d_name
[0] <= '9'))
785 ymd_list
= add_to_name_list(ymd_list
, dent
->d_name
, file_size
);
786 store_size
+= file_size
;
788 else if (!strncmp(dent
->d_name
, "AUX.", 4) && (dent
->d_name
[4] >= '0') && (dent
->d_name
[4] <= '9') && S_ISDIR(sb
.st_mode
))
790 file_size
= directory_size(dent
->d_name
);
791 aux_list
= add_to_name_list(aux_list
, dent
->d_name
, file_size
);
792 store_size
+= file_size
;
794 else if (!strncmp(dent
->d_name
, "BB.AUX.", 7) && (dent
->d_name
[7] >= '0') && (dent
->d_name
[7] <= '9') && S_ISDIR(sb
.st_mode
))
796 file_size
= directory_size(dent
->d_name
);
797 bb_aux_list
= add_to_name_list(bb_aux_list
, dent
->d_name
, file_size
);
798 store_size
+= file_size
;
800 else if (!strncmp(dent
->d_name
, "BB.", 3) && (dent
->d_name
[3] >= '0') && (dent
->d_name
[3] <= '9'))
802 bb_list
= add_to_name_list(bb_list
, dent
->d_name
, file_size
);
803 store_size
+= file_size
;
805 else if ((!strcmp(dent
->d_name
, ".")) || (!strcmp(dent
->d_name
, "..")))
807 else if ((!strcmp(dent
->d_name
, "StoreData")) || (!strcmp(dent
->d_name
, "SweepStore")))
811 debug_log(ASL_LEVEL_ERR
, "aslmanager: unexpected file %s in ASL data store\n", dent
->d_name
);
817 debug_log(ASL_LEVEL_NOTICE
, "Data Store Size = %lu\n", store_size
);
818 debug_log(ASL_LEVEL_NOTICE
, "Data Store YMD Files\n");
819 for (e
= ymd_list
; e
!= NULL
; e
= e
->next
) debug_log(ASL_LEVEL_NOTICE
, " %s %lu\n", e
->name
, e
->size
);
820 debug_log(ASL_LEVEL_NOTICE
, "Data Store AUX Directories\n");
821 for (e
= aux_list
; e
!= NULL
; e
= e
->next
) debug_log(ASL_LEVEL_NOTICE
, " %s %lu\n", e
->name
, e
->size
);
822 debug_log(ASL_LEVEL_NOTICE
, "Data Store BB.AUX Directories\n");
823 for (e
= bb_aux_list
; e
!= NULL
; e
= e
->next
) debug_log(ASL_LEVEL_NOTICE
, " %s %lu\n", e
->name
, e
->size
);
824 debug_log(ASL_LEVEL_NOTICE
, "Data Store BB Files\n");
825 for (e
= bb_list
; e
!= NULL
; e
= e
->next
) debug_log(ASL_LEVEL_NOTICE
, " %s %lu\n", e
->name
, e
->size
);
827 /* Delete/achive expired YMD files */
828 debug_log(ASL_LEVEL_NOTICE
, "Start YMD File Scan\n");
833 if (strncmp(e
->name
, expire_ymd_string
, expire_ymd_stringlen
) <= 0)
835 /* file has expired, archive it if required, then unlink it */
836 if (dst
->rotate_dir
!= NULL
)
839 asprintf(&str
, "%s/%s", dst
->rotate_dir
, e
->name
);
840 if (str
== NULL
) return -1;
842 filesystem_copy(dst
, e
->name
, str
, 0);
846 filesystem_unlink(e
->name
);
847 store_size
-= e
->size
;
852 /* check if there are any per-level TTLs and filter the file if required */
853 uint32_t i
, bit
, keep_mask
;
856 mode_t ymd_mode
= 0600;
857 uint32_t age
= ymd_file_age(e
->name
, now
, &ymd_uid
, &ymd_gid
);
861 keep_mask
= 0x000000ff;
863 for (i
= 0; i
<= 7; i
++)
865 if ((dst
->ttl
[i
] > 0) && (age
>= dst
->ttl
[i
])) keep_mask
&= ~bit
;
869 memset(&sb
, 0, sizeof(struct stat
));
870 if (stat(e
->name
, &sb
) == 0) ymd_mode
= sb
.st_mode
& 0777;
872 if (keep_mask
!= 0x000000ff) ymd_file_filter(e
->name
, dst
->path
, keep_mask
, ymd_mode
, ymd_uid
, ymd_gid
);
879 debug_log(ASL_LEVEL_NOTICE
, "Finished YMD File Scan\n");
881 /* Delete/achive expired YMD AUX directories */
882 debug_log(ASL_LEVEL_NOTICE
, "Start AUX Directory Scan\n");
887 /* stop when a file name/date is after the expire date */
888 if (strncmp(e
->name
+ 4, expire_ymd_string
, expire_ymd_stringlen
) > 0) break;
890 if (dst
->rotate_dir
!= NULL
)
893 asprintf(&str
, "%s/%s", dst
->rotate_dir
, e
->name
);
894 if (str
== NULL
) return -1;
896 filesystem_copy(dst
, e
->name
, str
, 0);
900 remove_directory(e
->name
);
901 store_size
-= e
->size
;
907 debug_log(ASL_LEVEL_NOTICE
, "Finished AUX Directory Scan\n");
909 /* Delete/achive expired BB.AUX directories */
910 debug_log(ASL_LEVEL_NOTICE
, "Start BB.AUX Directory Scan\n");
915 /* stop when a file name/date is after the expire date */
916 if (strncmp(e
->name
+ 7, today_ymd_string
, today_ymd_stringlen
) > 0) break;
918 if (dst
->rotate_dir
!= NULL
)
921 asprintf(&str
, "%s/%s", dst
->rotate_dir
, e
->name
);
922 if (str
== NULL
) return -1;
924 filesystem_copy(dst
, e
->name
, str
, 0);
928 remove_directory(e
->name
);
929 store_size
-= e
->size
;
935 debug_log(ASL_LEVEL_NOTICE
, "Finished BB.AUX Directory Scan\n");
937 /* Delete/achive expired BB files */
938 debug_log(ASL_LEVEL_NOTICE
, "Start BB Scan\n");
943 /* stop when a file name/date is after the expire date */
944 if (strncmp(e
->name
+ 3, today_ymd_string
, today_ymd_stringlen
) > 0) break;
946 if (dst
->rotate_dir
!= NULL
)
949 asprintf(&str
, "%s/%s", dst
->rotate_dir
, e
->name
);
950 if (str
== NULL
) return -1;
952 /* syslog -x [str] -f [e->name] */
953 filesystem_copy(dst
, e
->name
, str
, 0);
957 filesystem_unlink(e
->name
);
958 store_size
-= e
->size
;
964 debug_log(ASL_LEVEL_NOTICE
, "Finished BB Scan\n");
966 if (dst
->all_max
> 0)
968 /* if data store is over max_size, delete/archive more YMD files */
969 if (store_size
> dst
->all_max
) debug_log(ASL_LEVEL_NOTICE
, "Additional YMD Scan\n");
972 while ((e
!= NULL
) && (store_size
> dst
->all_max
))
976 if (strncmp(e
->name
, today_ymd_string
, today_ymd_stringlen
) == 0)
978 /* do not touch active file YYYY.MM.DD.asl */
979 if (strcmp(e
->name
+ today_ymd_stringlen
, "asl") == 0)
986 if (dst
->rotate_dir
!= NULL
)
989 asprintf(&str
, "%s/%s", dst
->rotate_dir
, e
->name
);
990 if (str
== NULL
) return -1;
992 /* syslog -x [str] -f [e->name] */
993 filesystem_copy(dst
, e
->name
, str
, 0);
997 filesystem_unlink(e
->name
);
998 store_size
-= e
->size
;
1005 /* if data store is over dst->all_max, delete/archive more BB files */
1006 if (store_size
> dst
->all_max
) debug_log(ASL_LEVEL_NOTICE
, "Additional BB Scan\n");
1009 while ((e
!= NULL
) && (store_size
> dst
->all_max
))
1013 if (dst
->rotate_dir
!= NULL
)
1016 asprintf(&str
, "%s/%s", dst
->rotate_dir
, e
->name
);
1017 if (str
== NULL
) return -1;
1019 /* syslog -x [str] -f [e->name] */
1020 filesystem_copy(dst
, e
->name
, str
, 0);
1024 filesystem_unlink(e
->name
);
1025 store_size
-= e
->size
;
1033 free_name_list(ymd_list
);
1034 free_name_list(bb_list
);
1035 free_name_list(aux_list
);
1036 free_name_list(bb_aux_list
);
1038 debug_log(ASL_LEVEL_NOTICE
, "Data Store Size = %lu\n", store_size
);
1043 /* move sequenced source files to dst dir, renaming as we go */
1045 module_copy_rename(asl_out_dst_data_t
*dst
)
1047 asl_out_file_list_t
*src_list
, *dst_list
, *f
, *dst_last
;
1048 char *base
, *dst_dir
;
1049 char fpathsrc
[MAXPATHLEN
], fpathdst
[MAXPATHLEN
];
1050 uint32_t src_count
, dst_count
;
1053 if (dst
== NULL
) return -1;
1054 if (dst
->path
== NULL
) return -1;
1056 base
= strrchr(dst
->path
, '/');
1057 if (base
== NULL
) return -1;
1059 src_list
= asl_list_src_files(dst
);
1062 debug_log(ASL_LEVEL_INFO
, " no src files\n");
1066 debug_log(ASL_LEVEL_INFO
, " src files\n");
1069 for (f
= src_list
; f
!= NULL
; f
= f
->next
)
1071 debug_log(ASL_LEVEL_INFO
, " %s\n", f
->name
);
1075 dst_list
= asl_list_dst_files(dst
);
1080 dst_dir
= dst
->rotate_dir
;
1081 if (dst_dir
== NULL
) dst_dir
= dst
->path
;
1084 dst_last
= dst_list
;
1086 if (dst_list
== NULL
) debug_log(ASL_LEVEL_INFO
, " no dst files\n");
1087 else debug_log(ASL_LEVEL_INFO
, " dst files\n");
1089 for (f
= dst_list
; f
!= NULL
; f
= f
->next
)
1091 debug_log(ASL_LEVEL_INFO
, " %s\n", f
->name
);
1096 if (dst
->flags
& MODULE_FLAG_STYLE_SEQ
)
1098 for (f
= dst_last
; f
!= NULL
; f
= f
->prev
)
1101 char *dot
= strrchr(f
->name
, '.');
1102 if ((dot
!= NULL
) && (!strcmp(dot
, ".gz"))) is_gz
= 1;
1104 snprintf(fpathsrc
, sizeof(fpathsrc
), "%s/%s", dst_dir
, f
->name
);
1105 snprintf(fpathdst
, sizeof(fpathdst
), "%s/%s.%d%s", dst_dir
, base
, f
->seq
+src_count
, (is_gz
== 1) ? ".gz" : "");
1106 filesystem_rename(fpathsrc
, fpathdst
);
1109 for (f
= src_list
, x
= 0; f
!= NULL
; f
= f
->next
, x
++)
1111 snprintf(fpathsrc
, sizeof(fpathsrc
), "%s/%s", dst
->path
, f
->name
);
1112 snprintf(fpathdst
, sizeof(fpathdst
), "%s/%s.%d", dst_dir
, base
, x
);
1113 moved
= filesystem_copy(dst
, fpathsrc
, fpathdst
, dst
->flags
);
1116 if (dst
->flags
& MODULE_FLAG_TRUNCATE
) filesystem_truncate(fpathsrc
);
1117 else filesystem_unlink(fpathsrc
);
1123 for (f
= src_list
; f
!= NULL
; f
= f
->next
)
1125 /* final / active base stamped file looks like a checkpointed file - ignore it */
1126 if ((dst
->flags
& MODULE_FLAG_BASESTAMP
) && (f
->next
== NULL
)) break;
1128 snprintf(fpathsrc
, sizeof(fpathsrc
), "%s/%s", dst
->path
, f
->name
);
1130 /* MODULE_FLAG_EXTERNAL files are not decorated with a timestamp */
1131 if (dst
->flags
& MODULE_FLAG_EXTERNAL
)
1135 asl_make_timestamp(f
->ftime
, dst
->flags
, tstamp
, sizeof(tstamp
));
1136 snprintf(fpathdst
, sizeof(fpathdst
), "%s/%s.%s", dst_dir
, base
, tstamp
);
1140 snprintf(fpathdst
, sizeof(fpathdst
), "%s/%s", dst_dir
, f
->name
);
1143 moved
= filesystem_copy(dst
, fpathsrc
, fpathdst
, dst
->flags
);
1146 if (dst
->flags
& MODULE_FLAG_TRUNCATE
) filesystem_truncate(fpathsrc
);
1147 else filesystem_unlink(fpathsrc
);
1152 asl_out_file_list_free(src_list
);
1153 asl_out_file_list_free(dst_list
);
1155 if (base
!= NULL
) *--base
= '/';
1160 /* delete expired files */
1162 module_expire(asl_out_dst_data_t
*dst
)
1164 asl_out_file_list_t
*dst_list
, *f
;
1165 char *base
, *dst_dir
, fpath
[MAXPATHLEN
];
1166 time_t now
, ttl
, cutoff
;
1168 if (dst
== NULL
) return -1;
1169 if (dst
->path
== NULL
) return -1;
1170 if (dst
->ttl
[LEVEL_ALL
] == 0) return 0;
1173 if (module_ttl
> 0) ttl
= module_ttl
;
1174 else ttl
= dst
->ttl
[LEVEL_ALL
];
1176 ttl
*= SECONDS_PER_DAY
;
1179 if (ttl
> now
) return 0;
1183 base
= strrchr(dst
->path
, '/');
1184 if (base
== NULL
) return -1;
1186 dst_list
= asl_list_dst_files(dst
);
1190 dst_dir
= dst
->rotate_dir
;
1191 if (dst_dir
== NULL
) dst_dir
= dst
->path
;
1193 if (dst_list
== NULL
)
1195 debug_log(ASL_LEVEL_INFO
, " no dst files\n");
1199 debug_log(ASL_LEVEL_INFO
, " dst files\n");
1200 for (f
= dst_list
; f
!= NULL
; f
= f
->next
) debug_log(ASL_LEVEL_INFO
, " %s\n", f
->name
);
1203 for (f
= dst_list
; f
!= NULL
; f
= f
->next
)
1205 if (f
->ftime
<= cutoff
)
1207 snprintf(fpath
, sizeof(fpath
), "%s/%s", dst_dir
, f
->name
);
1208 filesystem_unlink(fpath
);
1212 asl_out_file_list_free(dst_list
);
1214 if (base
!= NULL
) *base
= '/';
1219 /* check all_max size and delete files (oldest first) to stay within size limit */
1221 module_check_size(asl_out_dst_data_t
*dst
)
1223 asl_out_file_list_t
*dst_list
, *f
, *dst_end
;
1224 char *base
, *dst_dir
, fpath
[MAXPATHLEN
];
1227 if (dst
== NULL
) return -1;
1228 if (dst
->path
== NULL
) return -1;
1230 if (dst
->all_max
== 0) return 0;
1232 dst_list
= asl_list_dst_files(dst
);
1233 if (dst_list
== NULL
)
1235 debug_log(ASL_LEVEL_INFO
, " no dst files\n");
1240 dst_dir
= dst
->rotate_dir
;
1241 if (dst_dir
== NULL
)
1243 dst_dir
= dst
->path
;
1244 base
= strrchr(dst
->path
, '/');
1247 asl_out_file_list_free(dst_list
);
1254 debug_log(ASL_LEVEL_INFO
, " dst files\n");
1256 for (f
= dst_list
; f
!= NULL
; f
= f
->next
)
1259 debug_log(ASL_LEVEL_INFO
, " %s size %lu\n", f
->name
, f
->size
);
1263 for (f
= dst_list
; f
!= NULL
; f
= f
->next
) total
+= f
->size
;
1265 for (f
= dst_end
; (total
> dst
->all_max
) && (f
!= NULL
); f
= f
->prev
)
1267 snprintf(fpath
, sizeof(fpath
), "%s/%s", dst_dir
, f
->name
);
1268 filesystem_unlink(fpath
);
1272 asl_out_file_list_free(dst_list
);
1274 if (base
!= NULL
) *base
= '/';
1281 process_module(asl_out_module_t
*mod
)
1285 if (mod
== NULL
) return -1;
1287 debug_log(ASL_LEVEL_NOTICE
, "----------------------------------------\n");
1288 debug_log(ASL_LEVEL_NOTICE
, "Processing module %s\n", (mod
->name
== NULL
) ? "asl.conf" : mod
->name
);
1290 for (r
= mod
->ruleset
; r
!= NULL
; r
= r
->next
)
1292 if (r
->action
== ACTION_OUT_DEST
)
1296 debug_log(ASL_LEVEL_NOTICE
, "NULL dst data for output rule - skipped\n");
1298 else if (r
->dst
->flags
& MODULE_FLAG_ROTATE
)
1300 debug_log(ASL_LEVEL_NOTICE
, "Checking file %s\n", r
->dst
->path
);
1301 debug_log(ASL_LEVEL_NOTICE
, "- Rename, move to destination directory, and compress as required\n");
1303 module_copy_rename(r
->dst
);
1305 if (r
->dst
->ttl
[LEVEL_ALL
] > 0)
1307 debug_log(ASL_LEVEL_NOTICE
, "- Check for expired files - TTL = %d days\n", r
->dst
->ttl
[LEVEL_ALL
]);
1308 module_expire(r
->dst
);
1311 if (r
->dst
->all_max
> 0)
1313 debug_log(ASL_LEVEL_NOTICE
, "- Check total storage used - MAX = %lu\n", r
->dst
->all_max
);
1314 module_check_size(r
->dst
);
1317 else if ((r
->dst
->flags
& MODULE_FLAG_TYPE_ASL_DIR
) && (r
->dst
->ttl
[LEVEL_ALL
] > 0))
1319 process_asl_data_store(r
->dst
);
1324 debug_log(ASL_LEVEL_NOTICE
, "Finished processing module %s\n", (mod
->name
== NULL
) ? "asl.conf" : mod
->name
);
1329 control_query(asl_msg_t
*a
)
1331 asl_msg_list_t
*out
;
1332 char *qstr
, *str
, *res
;
1333 uint32_t len
, reslen
, status
;
1334 uint64_t cmax
, qmin
;
1335 kern_return_t kstatus
;
1337 security_token_t sec
;
1339 if (asl_server_port
== MACH_PORT_NULL
)
1341 bootstrap_look_up2(bootstrap_port
, ASL_SERVICE_NAME
, &asl_server_port
, 0, BOOTSTRAP_PRIVILEGED_SERVER
);
1342 if (asl_server_port
== MACH_PORT_NULL
) return NULL
;
1345 qstr
= asl_msg_to_string((asl_msg_t
*)a
, &len
);
1350 asprintf(&str
, "1\nQ [= ASLOption control]\n");
1354 asprintf(&str
, "1\n%s [= ASLOption control]\n", qstr
);
1358 if (str
== NULL
) return NULL
;
1360 /* length includes trailing nul */
1361 len
= strlen(str
) + 1;
1370 status
= ASL_STATUS_OK
;
1372 kstatus
= vm_allocate(mach_task_self(), (vm_address_t
*)&vmstr
, len
, TRUE
);
1373 if (kstatus
!= KERN_SUCCESS
) return NULL
;
1375 memmove(vmstr
, str
, len
);
1379 kstatus
= _asl_server_query(asl_server_port
, vmstr
, len
, qmin
, 1, 0, (caddr_t
*)&res
, &reslen
, &cmax
, (int *)&status
, &sec
);
1380 if (kstatus
!= KERN_SUCCESS
) return NULL
;
1382 if (res
== NULL
) return NULL
;
1384 out
= asl_msg_list_from_string(res
);
1385 vm_deallocate(mach_task_self(), (vm_address_t
)res
, reslen
);
1391 checkpoint(const char *name
)
1393 /* send checkpoint message to syslogd */
1394 debug_log(ASL_LEVEL_NOTICE
, "Checkpoint module %s\n", (name
== NULL
) ? "*" : name
);
1395 if (dryrun
!= 0) return 0;
1397 asl_msg_t
*qmsg
= asl_msg_new(ASL_TYPE_QUERY
);
1399 asl_msg_list_t
*res
;
1401 asprintf(&tmp
, "%s checkpoint", (name
== NULL
) ? "*" : name
);
1402 asl_msg_set_key_val_op(qmsg
, "action", tmp
, ASL_QUERY_OP_EQUAL
);
1405 res
= control_query(qmsg
);
1407 asl_msg_list_release(res
);
1412 cli_main(int argc
, char *argv
[])
1415 asl_out_module_t
*mod
, *m
;
1417 asl_out_dst_data_t store
, *asl_store_dst
= NULL
;
1418 const char *mname
= NULL
;
1422 if (argc
== 0) debug
= DEBUG_ASL
;
1423 else debug
= DEBUG_STDERR
;
1425 debug_log(ASL_LEVEL_ERR
, "aslmanager must be run by root\n");
1429 module_ttl
= DEFAULT_TTL
;
1431 /* cobble up a dst_data with defaults and parameter settings */
1432 memset(&store
, 0, sizeof(store
));
1433 store
.ttl
[LEVEL_ALL
] = DEFAULT_TTL
;
1434 store
.all_max
= DEFAULT_MAX_SIZE
;
1436 for (i
= 1; i
< argc
; i
++)
1438 if (!strcmp(argv
[i
], "-s"))
1440 if (((i
+ 1) < argc
) && (argv
[i
+ 1][0] != '-'))
1442 store
.path
= strdup(argv
[++i
]);
1443 asl_store_dst
= &store
;
1448 /* get parameters from asl.conf */
1449 mod
= asl_out_module_init();
1453 for (r
= mod
->ruleset
; r
!= NULL
; r
= r
->next
)
1455 if ((asl_store_dst
== NULL
) && (r
->action
== ACTION_OUT_DEST
) && (!strcmp(r
->dst
->path
, PATH_ASL_STORE
)))
1456 asl_store_dst
= r
->dst
;
1459 for (r
= mod
->ruleset
; r
!= NULL
; r
= r
->next
)
1461 if (r
->action
== ACTION_SET_PARAM
)
1463 if (r
->query
== NULL
) _aslmanager_set_param(asl_store_dst
, r
->options
);
1468 work
= DO_ASLDB
| DO_MODULE
;
1470 for (i
= 1; i
< argc
; i
++)
1472 if (!strcmp(argv
[i
], "-a"))
1474 if (((i
+ 1) < argc
) && (argv
[i
+ 1][0] != '-')) asl_store_dst
->rotate_dir
= strdup(argv
[++i
]);
1475 else asl_store_dst
->rotate_dir
= strdup(PATH_ASL_ARCHIVE
);
1476 asl_store_dst
->mode
= 0400;
1478 else if (!strcmp(argv
[i
], "-store_ttl"))
1480 if (((i
+ 1) < argc
) && (argv
[i
+ 1][0] != '-')) asl_store_dst
->ttl
[LEVEL_ALL
] = atoi(argv
[++i
]);
1482 else if (!strcmp(argv
[i
], "-module_ttl"))
1484 if (((i
+ 1) < argc
) && (argv
[i
+ 1][0] != '-')) module_ttl
= atoi(argv
[++i
]);
1486 else if (!strcmp(argv
[i
], "-ttl"))
1488 if (((i
+ 1) < argc
) && (argv
[i
+ 1][0] != '-')) module_ttl
= asl_store_dst
->ttl
[LEVEL_ALL
] = atoi(argv
[++i
]);
1490 else if (!strcmp(argv
[i
], "-size"))
1492 if (((i
+ 1) < argc
) && (argv
[i
+ 1][0] != '-')) asl_store_dst
->all_max
= asl_str_to_size(argv
[++i
]);
1494 else if (!strcmp(argv
[i
], "-checkpoint"))
1498 else if (!strcmp(argv
[i
], "-module"))
1502 /* optional name follows -module */
1505 if (argv
[i
+ 1][0] != '-') mname
= argv
[++i
];
1508 else if (!strcmp(argv
[i
], "-asldb"))
1512 else if (!strcmp(argv
[i
], "-d"))
1514 if (((i
+ i
) < argc
) && (argv
[i
+1][0] != '-')) set_debug(DEBUG_STDERR
, argv
[++i
]);
1515 else set_debug(DEBUG_STDERR
, NULL
);
1517 else if (!strcmp(argv
[i
], "-dd"))
1521 if (((i
+ i
) < argc
) && (argv
[i
+1][0] != '-')) set_debug(DEBUG_STDERR
, argv
[++i
]);
1522 else set_debug(DEBUG_STDERR
, NULL
);
1526 if (asl_store_dst
->path
== NULL
) asl_store_dst
->path
= strdup(PATH_ASL_STORE
);
1528 debug_log(ASL_LEVEL_ERR
, "aslmanager starting%s\n", (dryrun
== 1) ? " dryrun" : "");
1530 if (work
& DO_ASLDB
) process_asl_data_store(asl_store_dst
);
1532 if (work
& DO_MODULE
)
1534 if (work
& DO_CHECKPT
) checkpoint(mname
);
1538 for (m
= mod
; m
!= NULL
; m
= m
->next
)
1540 if ((mname
== NULL
) || ((m
->name
!= NULL
) && (!strcmp(m
->name
, mname
))))
1548 asl_out_module_free(mod
);
1550 debug_log(ASL_LEVEL_NOTICE
, "----------------------------------------\n");
1551 debug_log(ASL_LEVEL_ERR
, "aslmanager finished%s\n", (dryrun
== 1) ? " dryrun" : "");
1552 if (asl_aux_fd
>= 0) asl_close_auxiliary_file(asl_aux_fd
);
1558 accept_connection(xpc_connection_t peer
)
1560 xpc_connection_set_event_handler(peer
, ^(xpc_object_t request
) {
1561 if (xpc_get_type(request
) == XPC_TYPE_DICTIONARY
)
1563 uid_t uid
= xpc_connection_get_euid(peer
);
1565 /* send a reply immediately */
1566 xpc_object_t reply
= xpc_dictionary_create_reply(request
);
1567 xpc_connection_send_message(peer
, reply
);
1571 * Some day, we may use the dictionary to pass parameters
1572 * to aslmanager, but for now, we ignore the input.
1574 if (uid
== 0) cli_main(0, NULL
);
1576 else if (xpc_get_type(request
) == XPC_TYPE_ERROR
)
1581 dispatch_async(serverq
, ^__attribute__((noreturn
)) { xpc_server_exit(0); });
1584 xpc_connection_resume(peer
);
1588 main(int argc
, char *argv
[])
1590 int64_t is_managed
= 0;
1592 vproc_swap_integer(NULL
, VPROC_GSK_IS_MANAGED
, NULL
, &is_managed
);
1594 if (is_managed
== 0) return cli_main(argc
, argv
);
1597 serverq
= dispatch_queue_create("aslmanager", NULL
);
1598 xpc_track_activity();
1600 /* Handle incoming messages. */
1601 listener
= xpc_connection_create_mach_service("com.apple.aslmanager", serverq
, XPC_CONNECTION_MACH_SERVICE_LISTENER
);
1602 xpc_connection_set_event_handler(listener
, ^(xpc_object_t peer
) {
1603 if (xpc_get_type(peer
) == XPC_TYPE_CONNECTION
) accept_connection(peer
);
1605 xpc_connection_resume(listener
);