2 * Copyright (c) 2012 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>
37 #include <membership.h>
39 #include <TargetConditionals.h>
40 #include <configuration_profile.h>
44 #include "asl_common.h"
46 #define _PATH_ASL_CONF "/etc/asl.conf"
47 #define _PATH_ASL_CONF_DIR "/etc/asl"
49 #define PATH_VAR_LOG "/var/log/"
50 #define PATH_VAR_LOG_LEN 9
52 #define PATH_LIBRARY_LOGS "/Library/Logs/"
53 #define PATH_LIBRARY_LOGS_LEN 14
55 #if !TARGET_IPHONE_SIMULATOR
56 #define _PATH_ASL_CONF_LOCAL_DIR "/usr/local/etc/asl"
59 static const char *asl_out_action_name
[] =
83 #define forever for(;;)
84 #define KEYMATCH(S,K) ((strncasecmp(S, K, strlen(K)) == 0))
86 #define STAMP_STYLE_INVALID -1
87 #define STAMP_STYLE_NULL 0
88 #define STAMP_STYLE_SEC 1
89 #define STAMP_STYLE_SEQ 2
90 #define STAMP_STYLE_UTC_OR_LCL 3
93 xpc_object_to_asl_msg(xpc_object_t xobj
)
95 __block asl_msg_t
*out
;
97 if (xobj
== NULL
) return NULL
;
98 if (xpc_get_type(xobj
) != XPC_TYPE_DICTIONARY
) return NULL
;
100 out
= asl_msg_new(ASL_TYPE_MSG
);
101 xpc_dictionary_apply(xobj
, ^bool(const char *key
, xpc_object_t xval
) {
104 if (xpc_get_type(xval
) == XPC_TYPE_NULL
)
106 asl_msg_set_key_val_op(out
, key
, NULL
, 0);
108 else if (xpc_get_type(xval
) == XPC_TYPE_BOOL
)
110 if (xpc_bool_get_value(xval
)) asl_msg_set_key_val_op(out
, key
, "1", 0);
111 else asl_msg_set_key_val_op(out
, key
, "0", 0);
113 else if (xpc_get_type(xval
) == XPC_TYPE_INT64
)
115 snprintf(tmp
, sizeof(tmp
), "%lld", xpc_int64_get_value(xval
));
116 asl_msg_set_key_val_op(out
, key
, tmp
, 0);
118 else if (xpc_get_type(xval
) == XPC_TYPE_UINT64
)
120 snprintf(tmp
, sizeof(tmp
), "%llu", xpc_uint64_get_value(xval
));
121 asl_msg_set_key_val_op(out
, key
, tmp
, 0);
123 else if (xpc_get_type(xval
) == XPC_TYPE_DOUBLE
)
125 snprintf(tmp
, sizeof(tmp
), "%f", xpc_double_get_value(xval
));
126 asl_msg_set_key_val_op(out
, key
, tmp
, 0);
128 else if (xpc_get_type(xval
) == XPC_TYPE_DATE
)
130 snprintf(tmp
, sizeof(tmp
), "%lld", xpc_date_get_value(xval
));
131 asl_msg_set_key_val_op(out
, key
, tmp
, 0);
133 else if (xpc_get_type(xval
) == XPC_TYPE_DATA
)
135 size_t len
= xpc_data_get_length(xval
);
136 char *encoded
= asl_core_encode_buffer(xpc_data_get_bytes_ptr(xval
), len
);
137 asl_msg_set_key_val_op(out
, key
, encoded
, 0);
140 else if (xpc_get_type(xval
) == XPC_TYPE_STRING
)
142 asl_msg_set_key_val_op(out
, key
, xpc_string_get_string_ptr(xval
), 0);
144 else if (xpc_get_type(xval
) == XPC_TYPE_UUID
)
147 uuid_unparse(xpc_uuid_get_bytes(xval
), us
);
148 asl_msg_set_key_val_op(out
, key
, us
, 0);
150 else if (xpc_get_type(xval
) == XPC_TYPE_FD
)
152 /* XPC_TYPE_FD is not supported */
153 asl_msg_set_key_val_op(out
, key
, "{XPC_TYPE_FD}", 0);
155 else if (xpc_get_type(xval
) == XPC_TYPE_SHMEM
)
157 /* XPC_TYPE_SHMEM is not supported */
158 asl_msg_set_key_val_op(out
, key
, "{XPC_TYPE_SHMEM}", 0);
160 else if (xpc_get_type(xval
) == XPC_TYPE_ARRAY
)
162 /* XPC_TYPE_ARRAY is not supported */
163 asl_msg_set_key_val_op(out
, key
, "{XPC_TYPE_ARRAY}", 0);
165 else if (xpc_get_type(xval
) == XPC_TYPE_DICTIONARY
)
167 /* XPC_TYPE_DICTIONARY is not supported */
168 asl_msg_set_key_val_op(out
, key
, "{XPC_TYPE_DICTIONARY}", 0);
170 else if (xpc_get_type(xval
) == XPC_TYPE_ERROR
)
172 /* XPC_TYPE_ERROR is not supported */
173 asl_msg_set_key_val_op(out
, key
, "{XPC_TYPE_ERROR}", 0);
178 asl_msg_set_key_val_op(out
, key
, "{XPC_TYPE_???}", 0);
188 configuration_profile_to_asl_msg(const char *ident
)
190 xpc_object_t xobj
= configuration_profile_copy_property_list(ident
);
191 asl_msg_t
*out
= xpc_object_to_asl_msg(xobj
);
192 if (xobj
!= NULL
) xpc_release(xobj
);
196 /* strdup + skip leading and trailing whitespace */
198 _strdup_clean(const char *s
)
201 const char *first
, *last
;
204 if (s
== NULL
) return NULL
;
207 while ((*first
== ' ') || (*first
== '\t')) first
++;
209 if (len
== 0) return NULL
;
211 last
= first
+ len
- 1;
212 while ((len
> 0) && ((*last
== ' ') || (*last
== '\t')))
218 if (len
== 0) return NULL
;
220 out
= malloc(len
+ 1);
221 if (out
== NULL
) return NULL
;
223 memcpy(out
, first
, len
);
229 _insert_string(char *s
, char **l
, uint32_t x
)
233 if (s
== NULL
) return l
;
236 l
= (char **)malloc(2 * sizeof(char *));
237 if (l
== NULL
) return NULL
;
250 for (i
= 0; l
[i
] != NULL
; i
++);
252 /* len includes the NULL at the end of the list */
255 l
= (char **)reallocf(l
, (len
+ 1) * sizeof(char *));
256 if (l
== NULL
) return NULL
;
258 if ((x
>= (len
- 1)) || (x
== IndexNull
))
260 l
[len
- 1] = strdup(s
);
261 if (l
[len
- 1] == NULL
)
271 for (i
= len
; i
> x
; i
--) l
[i
] = l
[i
- 1];
273 if (l
[x
] == NULL
) return NULL
;
279 explode(const char *s
, const char *delim
)
286 if (s
== NULL
) return NULL
;
294 for (i
= 0; p
[i
] != '\0'; i
++)
298 /* not inside a quoted string: check for delimiters and quotes */
299 if (strchr(delim
, p
[i
]) != NULL
) break;
300 else if (p
[i
] == '\'') quote
= p
[i
];
301 else if (p
[i
] == '"') quote
= p
[i
];
305 /* inside a quoted string - look for matching quote */
306 if (p
[i
] == quote
) quote
= '\0';
312 if (t
== NULL
) return NULL
;
314 for (i
= 0; i
< n
; i
++) t
[i
] = p
[i
];
316 l
= _insert_string(t
, l
, IndexNull
);
319 if (p
[i
] == '\0') return l
;
320 if (p
[i
+ 1] == '\0') l
= _insert_string("", l
, IndexNull
);
328 free_string_list(char **l
)
332 if (l
== NULL
) return;
333 for (i
= 0; l
[i
] != NULL
; i
++) free(l
[i
]);
338 get_line_from_file(FILE *f
)
343 out
= fgetln(f
, &len
);
344 if (out
== NULL
) return NULL
;
345 if (len
== 0) return NULL
;
348 if (s
== NULL
) return NULL
;
352 if (s
[len
- 1] != '\n') len
++;
358 next_word_from_string(char **s
)
360 char *a
, *p
, *e
, *out
, s0
;
361 int quote1
, quote2
, len
;
363 if (s
== NULL
) return NULL
;
364 if (*s
== NULL
) return NULL
;
373 /* allow whole word to be contained in quotes */
409 if (quote1
== 0) quote1
= 1;
415 if (quote2
== 0) quote2
= 1;
419 if (((*p
== ' ') || (*p
== '\t')) && (quote1
== 0) && (quote2
== 0))
433 /* check for quoted string */
434 if (((s0
== '\'') || (s0
== '"')) && (s0
== a
[len
-1])) len
--;
436 if (len
== 0) return NULL
;
438 out
= malloc(len
+ 1);
439 if (out
== NULL
) return NULL
;
446 static asl_out_dst_data_t
*
447 _asl_out_dest_for_path(asl_out_module_t
*m
, const char *path
)
449 if (m
== NULL
) return NULL
;
450 if (path
== NULL
) return NULL
;
454 asl_out_rule_t
*r
= m
->ruleset
;
457 if ((r
->action
== ACTION_OUT_DEST
) && (r
->dst
!= NULL
) && (r
->dst
->path
!= NULL
) && (!strcmp(r
->dst
->path
, path
))) return r
->dst
;
468 * Create a directory path.
470 * mlist provides owner, group, and access mode, which are required for "non-standard"
471 * directories. Directories for standard paths (/var/log or /Library/Logs) default
472 * to root/admin/0755 if there is no mlist rule for them.
475 _asl_common_make_dir_path(asl_out_module_t
*mlist
, uint32_t flags
, const char *path
)
479 asl_string_t
*processed_path
;
482 if (path
== NULL
) return 0;
484 path_parts
= explode(path
, "/");
485 if (path_parts
== NULL
) return 0;
487 processed_path
= asl_string_new(ASL_ENCODE_NONE
);
490 if (path
[0] == '/') i
= 1;
492 for (; path_parts
[i
] != NULL
; i
++)
497 asl_out_dst_data_t
*dst
;
500 asl_string_append_char_no_encoding(processed_path
, '/');
501 asl_string_append_no_encoding(processed_path
, path_parts
[i
]);
502 tmp
= asl_string_bytes(processed_path
);
504 memset(&sb
, 0, sizeof(struct stat
));
505 status
= lstat(tmp
, &sb
);
506 if ((status
== 0) && S_ISLNK(sb
.st_mode
))
508 char real
[MAXPATHLEN
];
509 if (realpath(tmp
, real
) == NULL
)
511 asl_string_release(processed_path
);
512 free_string_list(path_parts
);
516 memset(&sb
, 0, sizeof(struct stat
));
517 status
= stat(real
, &sb
);
522 if (!S_ISDIR(sb
.st_mode
))
524 /* path component is not a directory! */
525 asl_string_release(processed_path
);
526 free_string_list(path_parts
);
530 /* exists and is a directory or a link to a directory */
533 else if (errno
!= ENOENT
)
535 /* unexpected status from stat() */
536 asl_string_release(processed_path
);
537 free_string_list(path_parts
);
541 dst
= _asl_out_dest_for_path(mlist
, tmp
);
542 if ((dst
== NULL
) && (flags
& MODULE_FLAG_NONSTD_DIR
))
544 /* no rule to create a non-standard path component! */
545 asl_string_release(processed_path
);
546 free_string_list(path_parts
);
554 if (mode
== 010000) mode
= 0755;
558 status
= mkdir(tmp
, mode
);
561 #if !TARGET_IPHONE_SIMULATOR
567 if (dst
->nuid
> 0) u
= dst
->uid
[0];
568 if (dst
->ngid
> 0) g
= dst
->gid
[0];
575 asl_string_release(processed_path
);
576 free_string_list(path_parts
);
582 asl_out_mkpath(asl_out_module_t
*mlist
, asl_out_rule_t
*r
)
584 char tmp
[MAXPATHLEN
], *p
;
588 if (r
== NULL
) return -1;
589 if (r
->dst
== NULL
) return -1;
590 if (r
->dst
->path
== NULL
) return -1;
592 snprintf(tmp
, sizeof(tmp
), "%s", r
->dst
->path
);
594 if (r
->action
!= ACTION_ASL_DIR
)
596 p
= strrchr(tmp
, '/');
597 if (p
== NULL
) return -1;
601 memset(&sb
, 0, sizeof(struct stat
));
602 status
= stat(tmp
, &sb
);
605 if (S_ISDIR(sb
.st_mode
)) return 0;
611 uint32_t dirflag
= r
->dst
->flags
& MODULE_FLAG_NONSTD_DIR
;
612 status
= _asl_common_make_dir_path(mlist
, dirflag
, tmp
);
620 asl_make_timestamp(time_t stamp
, uint32_t flags
, char *buf
, size_t len
)
625 if (buf
== NULL
) return;
627 if (flags
& MODULE_FLAG_STYLE_UTC
)
629 memset(&t
, 0, sizeof(t
));
630 gmtime_r(&stamp
, &t
);
631 snprintf(buf
, len
, "%d-%02d-%02dT%02d:%02d:%02dZ", t
.tm_year
+ 1900, t
.tm_mon
+ 1, t
.tm_mday
, t
.tm_hour
, t
.tm_min
, t
.tm_sec
);
633 else if (flags
& MODULE_FLAG_STYLE_UTC_B
)
635 memset(&t
, 0, sizeof(t
));
636 gmtime_r(&stamp
, &t
);
637 snprintf(buf
, len
, "%d%02d%02dT%02d%02d%02dZ", t
.tm_year
+ 1900, t
.tm_mon
+ 1, t
.tm_mday
, t
.tm_hour
, t
.tm_min
, t
.tm_sec
);
639 else if (flags
& MODULE_FLAG_STYLE_LCL
)
642 memset(&t
, 0, sizeof(t
));
643 localtime_r(&stamp
, &t
);
645 if ((neg
= (t
.tm_gmtoff
< 0))) t
.tm_gmtoff
*= -1;
653 if (s
> 0) snprintf(buf
, len
, "%d-%02d-%02dT%02d:%02d:%02d%c%u:%02u:%02u", t
.tm_year
+ 1900, t
.tm_mon
+ 1, t
.tm_mday
, t
.tm_hour
, t
.tm_min
, t
.tm_sec
, neg
? '-' : '+', h
, m
, s
);
654 else if (m
> 0) snprintf(buf
, len
, "%d-%02d-%02dT%02d:%02d:%02d%c%u:%02u", t
.tm_year
+ 1900, t
.tm_mon
+ 1, t
.tm_mday
, t
.tm_hour
, t
.tm_min
, t
.tm_sec
, neg
? '-' : '+', h
, m
);
655 else snprintf(buf
, len
, "%d-%02d-%02dT%02d:%02d:%02d%c%u", t
.tm_year
+ 1900, t
.tm_mon
+ 1, t
.tm_mday
, t
.tm_hour
, t
.tm_min
, t
.tm_sec
, neg
? '-' : '+', h
);
657 else if (flags
& MODULE_FLAG_STYLE_LCL_B
)
660 memset(&t
, 0, sizeof(t
));
661 localtime_r(&stamp
, &t
);
663 if ((neg
= (t
.tm_gmtoff
< 0))) t
.tm_gmtoff
*= -1;
671 if (s
> 0) snprintf(buf
, len
, "%d%02d%02dT%02d%02d%02d%c%02u%02u%02u", t
.tm_year
+ 1900, t
.tm_mon
+ 1, t
.tm_mday
, t
.tm_hour
, t
.tm_min
, t
.tm_sec
, neg
? '-' : '+', h
, m
, s
);
672 else if (m
> 0) snprintf(buf
, len
, "%d%02d%02dT%02d%02d%02d%c%02u%02u", t
.tm_year
+ 1900, t
.tm_mon
+ 1, t
.tm_mday
, t
.tm_hour
, t
.tm_min
, t
.tm_sec
, neg
? '-' : '+', h
, m
);
673 else snprintf(buf
, len
, "%d%02d%02dT%02d%02d%02d%c%02u", t
.tm_year
+ 1900, t
.tm_mon
+ 1, t
.tm_mday
, t
.tm_hour
, t
.tm_min
, t
.tm_sec
, neg
? '-' : '+', h
);
677 snprintf(buf
, len
, "%c%lu", STYLE_SEC_PREFIX_CHAR
, stamp
);
682 asl_make_dst_filename(asl_out_dst_data_t
*dst
, char *buf
, size_t len
)
684 if (dst
== NULL
) return;
685 if (buf
== NULL
) return;
687 if (dst
->flags
& (MODULE_FLAG_BASESTAMP
| MODULE_FLAG_TYPE_ASL_DIR
))
690 const char *name
= dst
->path
;
692 if (dst
->flags
& MODULE_FLAG_TYPE_ASL_DIR
) name
= dst
->fname
;
694 if (dst
->stamp
== 0) dst
->stamp
= time(NULL
);
695 asl_make_timestamp(dst
->stamp
, dst
->flags
, tstamp
, sizeof(tstamp
));
696 snprintf(buf
, len
, "%s.%s", name
, tstamp
);
700 snprintf(buf
, len
, "%s", dst
->path
);
705 asl_check_option(asl_msg_t
*msg
, const char *opt
)
710 if (msg
== NULL
) return 0;
711 if (opt
== NULL
) return 0;
714 if (len
== 0) return 0;
716 p
= asl_msg_get_val_for_key(msg
, ASL_KEY_OPTION
);
717 if (p
== NULL
) return 0;
721 while ((*p
== ' ') || (*p
== '\t') || (*p
== ',')) p
++;
722 if (*p
== '\0') return 0;
724 if (strncasecmp(p
, opt
, len
) == 0)
727 if ((*p
== ' ') || (*p
== '\t') || (*p
== ',') || (*p
== '\0')) return 1;
730 while ((*p
!= ' ') && (*p
!= '\t') && (*p
!= ',') && (*p
!= '\0')) p
++;
737 asl_out_dst_data_release(asl_out_dst_data_t
*dst
)
739 if (dst
== NULL
) return;
741 if (dst
->refcount
> 0) dst
->refcount
--;
742 if (dst
->refcount
> 0) return;
746 free(dst
->rotate_dir
);
748 #if !TARGET_IPHONE_SIMULATOR
756 asl_out_dst_data_retain(asl_out_dst_data_t
*dst
)
758 if (dst
== NULL
) return NULL
;
763 /* set owner, group, mode, and acls for a file */
765 asl_out_dst_set_access(int fd
, asl_out_dst_data_t
*dst
)
767 #if !TARGET_IPHONE_SIMULATOR
770 #if !TARGET_OS_EMBEDDED
780 if (dst
== NULL
) return -1;
781 if (fd
< 0) return -1;
783 #if TARGET_IPHONE_SIMULATOR
787 if (dst
->nuid
> 0) fuid
= dst
->uid
[0];
788 if (dst
->ngid
> 0) fgid
= dst
->gid
[0];
790 fchown(fd
, fuid
, fgid
);
792 #if TARGET_OS_EMBEDDED
797 for (i
= 0; i
< dst
->ngid
; i
++)
799 if (dst
->gid
[i
] == -2) continue;
802 * Don't bother setting group access if this is
803 * file's group and the file is group-readable.
805 if ((dst
->gid
[i
] == fgid
) && (dst
->mode
& 00040)) continue;
807 status
= mbr_gid_to_uuid(dst
->gid
[i
], uuid
);
814 status
= acl_create_entry_np(&acl
, &entry
, ACL_FIRST_ENTRY
);
815 if (status
!= 0) goto asl_file_create_return
;
817 status
= acl_set_tag_type(entry
, ACL_EXTENDED_ALLOW
);
818 if (status
!= 0) goto asl_file_create_return
;
820 status
= acl_set_qualifier(entry
, &uuid
);
821 if (status
!= 0) goto asl_file_create_return
;
823 status
= acl_get_permset(entry
, &perms
);
824 if (status
!= 0) goto asl_file_create_return
;
826 status
= acl_add_perm(perms
, ACL_READ_DATA
);
827 if (status
!= 0) goto asl_file_create_return
;
830 for (i
= 0; i
< dst
->nuid
; i
++)
832 if (dst
->uid
[i
] == -2) continue;
835 * Don't bother setting user access if this is
836 * file's owner and the file is owner-readable.
838 if ((dst
->uid
[i
] == fuid
) && (dst
->mode
& 00400)) continue;
840 status
= mbr_uid_to_uuid(dst
->uid
[i
], uuid
);
847 status
= acl_create_entry_np(&acl
, &entry
, ACL_FIRST_ENTRY
);
848 if (status
!= 0) goto asl_file_create_return
;
850 status
= acl_set_tag_type(entry
, ACL_EXTENDED_ALLOW
);
851 if (status
!= 0) goto asl_file_create_return
;
853 status
= acl_set_qualifier(entry
, &uuid
);
854 if (status
!= 0) goto asl_file_create_return
;
856 status
= acl_get_permset(entry
, &perms
);
857 if (status
!= 0) goto asl_file_create_return
;
859 status
= acl_add_perm(perms
, ACL_READ_DATA
);
860 if (status
!= 0) goto asl_file_create_return
;
863 status
= acl_set_fd(fd
, acl
);
870 asl_file_create_return
:
874 #endif /* !TARGET_OS_EMBEDDED */
875 #endif /* !TARGET_IPHONE_SIMULATOR */
878 /* create a file with acls */
880 asl_out_dst_file_create_open(asl_out_dst_data_t
*dst
, char **pathp
)
884 char outpath
[MAXPATHLEN
];
886 if (dst
== NULL
) return -1;
887 if (dst
->path
== NULL
) return -1;
889 asl_make_dst_filename(dst
, outpath
, sizeof(outpath
));
890 if (dst
->fname
!= NULL
) free(dst
->fname
);
892 dst
->fname
= strdup(outpath
);
893 if (dst
->fname
== NULL
) return -1;
895 if (pathp
!= NULL
) *pathp
= strdup(outpath
);
897 memset(&sb
, 0, sizeof(struct stat
));
898 status
= stat(outpath
, &sb
);
901 /* must be a regular file */
902 if (!S_ISREG(sb
.st_mode
)) return -1;
905 fd
= open(outpath
, O_RDWR
| O_APPEND
| O_EXCL
, 0);
907 if (dst
->stamp
== 0) dst
->stamp
= sb
.st_birthtimespec
.tv_sec
;
908 if (dst
->stamp
== 0) dst
->stamp
= sb
.st_mtimespec
.tv_sec
;
909 dst
->size
= sb
.st_size
;
913 else if (errno
!= ENOENT
)
915 /* stat error other than non-existant file */
919 fd
= open(outpath
, O_RDWR
| O_CREAT
| O_EXCL
, (dst
->mode
& 00666));
920 if (fd
< 0) return -1;
922 dst
->stamp
= time(NULL
);
924 fd
= asl_out_dst_set_access(fd
, dst
);
925 if (fd
< 0) unlink(outpath
);
931 asl_out_module_free(asl_out_module_t
*m
)
933 asl_out_rule_t
*r
, *n
;
948 if (r
->dst
!= NULL
) asl_out_dst_data_release(r
->dst
);
950 if (r
->query
!= NULL
) asl_msg_release(r
->query
);
962 asl_out_module_new(const char *name
)
964 asl_out_module_t
*out
= (asl_out_module_t
*)calloc(1, sizeof(asl_out_module_t
));
966 if (out
== NULL
) return NULL
;
967 if (name
== NULL
) return NULL
;
969 out
->name
= strdup(name
);
970 if (out
->name
== NULL
)
976 out
->flags
= MODULE_FLAG_ENABLED
;
981 /* Skip over query */
983 _asl_out_module_find_action(char *s
)
988 if (p
== NULL
) return NULL
;
990 /* Skip command character (?, Q, *, or =) */
996 while ((*p
== ' ') || (*p
== '\t')) p
++;
998 if (*p
== '\0') return NULL
;
999 if (*p
!= '[') return p
;
1001 /* skip to closing ] */
1015 /* skip whitespace */
1016 while ((*p
== ' ') || (*p
== '\t')) p
++;
1022 * Parse parameter setting line
1025 * evaluated once when module is initialized
1027 * = [query] param options
1028 * evaluated for each message, param set if message matches query
1030 * = param [File path]
1031 * evaluated once when module is initialized
1032 * evaluated when change notification received for path
1034 * = param [Plist path] ...
1035 * evaluated once when module is initialized
1036 * evaluated when change notification received for path
1038 * = param [Profile name] ...
1039 * evaluated once when module is initialized
1040 * evaluated when change notification received for profile
1042 static asl_out_rule_t
*
1043 _asl_out_module_parse_set_param(asl_out_module_t
*m
, char *s
)
1046 asl_out_rule_t
*out
, *rule
;
1048 if (m
== NULL
) return NULL
;
1050 out
= (asl_out_rule_t
*)calloc(1, sizeof(asl_out_rule_t
));
1051 if (out
== NULL
) return NULL
;
1054 while ((*q
== ' ') || (*q
== '\'')) q
++;
1055 out
->action
= ACTION_SET_PARAM
;
1059 /* = [query] param options */
1060 act
= _asl_out_module_find_action(s
);
1067 out
->options
= _strdup_clean(act
);
1070 if (*p
== ']') p
= act
;
1074 out
->query
= asl_msg_from_string(s
);
1075 if (out
->query
== NULL
)
1088 /* = param options */
1089 out
->options
= _strdup_clean(q
);
1093 /* = param [query] */
1094 if ((!strncmp(p
, "[File ", 6)) || (!strncmp(p
, "[File\t", 6))) out
->action
= ACTION_SET_FILE
;
1095 else if ((!strncmp(p
, "[Plist ", 7)) || (!strncmp(p
, "[Plist\t", 7))) out
->action
= ACTION_SET_PLIST
;
1096 else if ((!strncmp(p
, "[Profile ", 9)) || (!strncmp(p
, "[Profile\t", 9))) out
->action
= ACTION_SET_PROF
;
1100 out
->options
= _strdup_clean(q
);
1105 out
->query
= asl_msg_from_string(p
);
1106 if (out
->query
== NULL
)
1115 if (m
->ruleset
== NULL
) m
->ruleset
= out
;
1118 for (rule
= m
->ruleset
; rule
->next
!= NULL
; rule
= rule
->next
);
1125 #if !TARGET_IPHONE_SIMULATOR
1127 _dst_add_uid(asl_out_dst_data_t
*dst
, char *s
)
1132 if (dst
== NULL
) return;
1133 if (s
== NULL
) return;
1137 for (i
= 0 ; i
< dst
->nuid
; i
++)
1139 if (dst
->uid
[i
] == uid
) return;
1142 dst
->uid
= reallocf(dst
->uid
, (dst
->nuid
+ 1) * sizeof(uid_t
));
1143 if (dst
->uid
== NULL
)
1149 dst
->uid
[dst
->nuid
++] = uid
;
1153 _dst_add_gid(asl_out_dst_data_t
*dst
, char *s
)
1158 if (dst
== NULL
) return;
1159 if (s
== NULL
) return;
1163 for (i
= 0 ; i
< dst
->ngid
; i
++)
1165 if (dst
->gid
[i
] == gid
) return;
1168 dst
->gid
= reallocf(dst
->gid
, (dst
->ngid
+ 1) * sizeof(gid_t
));
1169 if (dst
->gid
== NULL
)
1175 dst
->gid
[dst
->ngid
++] = gid
;
1177 #endif /* !TARGET_IPHONE_SIMULATOR */
1180 _dst_format_string(char *s
)
1185 if (s
== NULL
) return NULL
;
1189 /* format string can be enclosed by quotes */
1190 if ((len
>= 2) && ((s
[0] == '\'') || (s
[0] == '"')) && (s
[len
-1] == s
[0]))
1197 for (i
= 0; i
< len
; i
++) if (s
[i
] == '\\') n
++;
1199 fmt
= malloc(1 + len
- n
);
1200 if (fmt
== NULL
) return NULL
;
1202 for (i
= 0, n
= 0; i
< len
; i
++) if (s
[i
] != '\\') fmt
[n
++] = s
[i
];
1208 asl_str_to_size(char *s
)
1213 if (s
== NULL
) return 0;
1216 if (len
== 0) return 0;
1220 if (x
> 90) x
-= 32;
1221 if (x
== 'K') n
= 1ll << 10;
1222 else if (x
== 'M') n
= 1ll << 20;
1223 else if (x
== 'G') n
= 1ll << 30;
1230 _dst_path_match(const char *newpath
, const char *existingpath
)
1232 if (newpath
== NULL
) return (existingpath
== NULL
);
1233 if (existingpath
== NULL
) return false;
1234 if (newpath
[0] == '/') return (strcmp(newpath
, existingpath
) == 0);
1236 const char *trailing
= strrchr(existingpath
, '/');
1237 if (trailing
== NULL
) return (strcmp(newpath
, existingpath
) == 0);
1239 return (strcmp(newpath
, trailing
) == 0);
1242 static asl_out_dst_data_t
*
1243 _asl_out_module_parse_dst(asl_out_module_t
*m
, char *s
, mode_t def_mode
)
1245 asl_out_rule_t
*out
, *rule
;
1246 asl_out_dst_data_t
*dst
;
1247 char *p
, *opts
, *path
;
1249 int has_dotdot
, recursion_limit
;
1250 uint32_t i
, flags
= 0;
1252 if (m
== NULL
) return NULL
;
1253 if (s
== NULL
) return NULL
;
1255 /* skip whitespace */
1256 while ((*s
== ' ') || (*s
== '\t')) s
++;
1259 path
= next_word_from_string(&opts
);
1260 if (path
== NULL
) return NULL
;
1263 * Check path for ".." component (not permitted).
1264 * Also substitute environment variables.
1267 path_parts
= explode(path
, "/");
1268 asl_string_t
*processed_path
= asl_string_new(ASL_ENCODE_NONE
);
1269 recursion_limit
= 5;
1271 while ((recursion_limit
> 0) && (path_parts
!= NULL
) && (processed_path
!= NULL
))
1276 for (i
= 0; path_parts
[i
] != NULL
; i
++)
1278 if (!strncmp(path_parts
[i
], "$ENV(", 5))
1280 char *p
= strchr(path_parts
[i
], ')');
1281 if (p
!= NULL
) *p
= '\0';
1282 char *env_val
= getenv(path_parts
[i
] + 5);
1283 if (env_val
!= NULL
)
1287 if (env_val
[0] != '/') asl_string_append_char_no_encoding(processed_path
, '/');
1288 asl_string_append_no_encoding(processed_path
, env_val
);
1295 if (path_parts
[0][0] != '\0') asl_string_append_no_encoding(processed_path
, path_parts
[i
]);
1299 asl_string_append_char_no_encoding(processed_path
, '/');
1300 asl_string_append_no_encoding(processed_path
, path_parts
[i
]);
1304 if ((has_dotdot
== 0) && (!strcmp(path_parts
[i
], ".."))) has_dotdot
= 1;
1307 free_string_list(path_parts
);
1310 if ((did_sub
== 1) && (has_dotdot
== 0))
1312 /* substitution might have added a ".." so check the new path */
1314 path
= asl_string_release_return_bytes(processed_path
);
1315 processed_path
= asl_string_new(ASL_ENCODE_NONE
);
1316 path_parts
= explode(path
, "/");
1322 free_string_list(path_parts
);
1325 if ((has_dotdot
!= 0) || (recursion_limit
== 0))
1327 asl_string_release(processed_path
);
1331 path
= asl_string_release_return_bytes(processed_path
);
1333 /* check if there's already a dst for this path */
1334 for (rule
= m
->ruleset
; rule
!= NULL
; rule
= rule
->next
)
1336 if (rule
->action
!= ACTION_OUT_DEST
) continue;
1339 if (dst
== NULL
) continue;
1341 if (_dst_path_match(path
, dst
->path
))
1348 flags
|= MODULE_FLAG_NONSTD_DIR
;
1353 const char *log_root
= "/var/log";
1355 #if TARGET_IPHONE_SIMULATOR
1356 log_root
= getenv("SIMULATOR_LOG_ROOT");
1357 if (log_root
== NULL
) log_root
= "/tmp/log";
1360 if (!strcmp(m
->name
, ASL_MODULE_NAME
))
1362 asprintf(&path
, "%s/%s", log_root
, t
);
1366 asprintf(&path
, "%s/module/%s/%s", log_root
, m
->name
, t
);
1370 flags
&= ~MODULE_FLAG_NONSTD_DIR
;
1375 * Standard log directories get marked so that syslogd
1376 * will create them without explicit rules.
1378 if (!strncmp(path
, PATH_VAR_LOG
, PATH_VAR_LOG_LEN
)) flags
&= ~MODULE_FLAG_NONSTD_DIR
;
1379 else if (!strncmp(path
, PATH_LIBRARY_LOGS
, PATH_LIBRARY_LOGS_LEN
)) flags
&= ~MODULE_FLAG_NONSTD_DIR
;
1382 out
= (asl_out_rule_t
*)calloc(1, sizeof(asl_out_rule_t
));
1383 dst
= (asl_out_dst_data_t
*)calloc(1, sizeof(asl_out_dst_data_t
));
1384 if ((out
== NULL
) || (dst
== NULL
))
1394 dst
->mode
= def_mode
;
1395 dst
->ttl
[LEVEL_ALL
] = DEFAULT_TTL
;
1396 dst
->flags
= flags
| MODULE_FLAG_COALESCE
;
1398 while (NULL
!= (p
= next_word_from_string(&opts
)))
1400 if (KEYMATCH(p
, "mode=")) dst
->mode
= strtol(p
+5, NULL
, 0);
1401 #if !TARGET_IPHONE_SIMULATOR
1402 else if (KEYMATCH(p
, "uid=")) _dst_add_uid(dst
, p
+4);
1403 else if (KEYMATCH(p
, "gid=")) _dst_add_gid(dst
, p
+4);
1405 else if (KEYMATCH(p
, "fmt=")) dst
->fmt
= _dst_format_string(p
+4);
1406 else if (KEYMATCH(p
, "format=")) dst
->fmt
= _dst_format_string(p
+7);
1407 else if (KEYMATCH(p
, "dest=")) dst
->rotate_dir
= _strdup_clean(p
+5);
1408 else if (KEYMATCH(p
, "dst=")) dst
->rotate_dir
= _strdup_clean(p
+4);
1409 else if (KEYMATCH(p
, "coalesce="))
1411 if (KEYMATCH(p
+9, "0")) dst
->flags
&= ~MODULE_FLAG_COALESCE
;
1412 else if (KEYMATCH(p
+9, "off")) dst
->flags
&= ~MODULE_FLAG_COALESCE
;
1413 else if (KEYMATCH(p
+9, "false")) dst
->flags
&= ~MODULE_FLAG_COALESCE
;
1415 else if (KEYMATCH(p
, "compress")) dst
->flags
|= MODULE_FLAG_COMPRESS
;
1416 else if (KEYMATCH(p
, "extern")) dst
->flags
|= MODULE_FLAG_EXTERNAL
;
1417 else if (KEYMATCH(p
, "truncate")) dst
->flags
|= MODULE_FLAG_TRUNCATE
;
1418 else if (KEYMATCH(p
, "dir")) dst
->flags
|= MODULE_FLAG_TYPE_ASL_DIR
;
1419 else if (KEYMATCH(p
, "soft")) dst
->flags
|= MODULE_FLAG_SOFT_WRITE
;
1420 else if (KEYMATCH(p
, "file_max=")) dst
->file_max
= asl_str_to_size(p
+9);
1421 else if (KEYMATCH(p
, "all_max=")) dst
->all_max
= asl_str_to_size(p
+8);
1422 else if (KEYMATCH(p
, "style=") || KEYMATCH(p
, "rotate="))
1424 const char *x
= p
+ 6;
1426 if (KEYMATCH(p
, "rotate=")) x
++;
1428 dst
->flags
|= MODULE_FLAG_ROTATE
;
1430 if (KEYMATCH(x
, "sec") || KEYMATCH(x
, "seconds"))
1432 dst
->flags
|= MODULE_FLAG_STYLE_SEC
;
1434 else if (KEYMATCH(x
, "utc") || KEYMATCH(x
, "date") || KEYMATCH(x
, "zulu"))
1436 const char *dash
= strchr(x
, '-');
1437 if ((dash
!= NULL
) && (*(dash
+ 1) == 'b')) dst
->flags
|= MODULE_FLAG_STYLE_UTC_B
;
1438 else dst
->flags
|= MODULE_FLAG_STYLE_UTC
;
1440 else if (KEYMATCH(x
, "local") || KEYMATCH(x
, "lcl"))
1442 const char *dash
= strchr(x
, '-');
1443 if ((dash
!= NULL
) && (*(dash
+ 1) == 'b')) dst
->flags
|= MODULE_FLAG_STYLE_LCL_B
;
1444 else dst
->flags
|= MODULE_FLAG_STYLE_LCL
;
1446 else if (KEYMATCH(x
, "#") || KEYMATCH(x
, "seq") || KEYMATCH(x
, "sequence"))
1448 dst
->flags
|= MODULE_FLAG_STYLE_SEQ
;
1452 dst
->flags
|= MODULE_FLAG_STYLE_SEC
;
1455 else if (KEYMATCH(p
, "rotate")) dst
->flags
|= MODULE_FLAG_ROTATE
;
1456 else if (KEYMATCH(p
, "crashlog"))
1458 /* crashlog implies rotation */
1459 dst
->flags
|= MODULE_FLAG_ROTATE
;
1460 dst
->flags
|= MODULE_FLAG_CRASHLOG
;
1461 dst
->flags
|= MODULE_FLAG_BASESTAMP
;
1462 dst
->flags
&= ~MODULE_FLAG_COALESCE
;
1464 else if (KEYMATCH(p
, "basestamp"))
1466 dst
->flags
|= MODULE_FLAG_BASESTAMP
;
1468 else if (KEYMATCH(p
, "ttl"))
1473 dst
->ttl
[LEVEL_ALL
] = strtol(p
+4, NULL
, 0);
1475 else if ((*q
>= '0') && (*q
<= '7') && (*(q
+1) == '='))
1477 uint32_t x
= *q
- '0';
1478 dst
->ttl
[x
] = strtol(p
+5, NULL
, 0);
1486 #if TARGET_OS_EMBEDDED
1487 /* check for crashreporter files */
1488 if ((KEYMATCH(dst
->path
, _PATH_CRASHREPORTER
)) || (KEYMATCH(dst
->path
, _PATH_CRASHREPORTER_MOBILE
)))
1490 dst
->flags
|= MODULE_FLAG_ROTATE
;
1491 dst
->flags
|= MODULE_FLAG_CRASHLOG
;
1492 dst
->flags
|= MODULE_FLAG_BASESTAMP
;
1493 dst
->flags
&= ~MODULE_FLAG_COALESCE
;
1497 /* ttl[LEVEL_ALL] must be max of all level-specific ttls */
1498 for (i
= 0; i
<= 7; i
++) if (dst
->ttl
[i
] > dst
->ttl
[LEVEL_ALL
]) dst
->ttl
[LEVEL_ALL
] = dst
->ttl
[i
];
1500 /* default text file format is "std" */
1501 if (dst
->fmt
== NULL
) dst
->fmt
= strdup("std");
1503 /* duplicate compression is only possible for std and bsd formats */
1504 if (strcmp(dst
->fmt
, "std") && strcmp(dst
->fmt
, "bsd")) dst
->flags
&= ~MODULE_FLAG_COALESCE
;
1506 /* note if format is one of std, bsd, or msg */
1507 if (!strcmp(dst
->fmt
, "std") || !strcmp(dst
->fmt
, "bsd") || !strcmp(dst
->fmt
, "msg")) dst
->flags
|= MODULE_FLAG_STD_BSD_MSG
;
1509 /* MODULE_FLAG_STYLE_SEQ can not be used with MODULE_FLAG_BASESTAMP */
1510 if ((dst
->flags
& MODULE_FLAG_BASESTAMP
) && (dst
->flags
& MODULE_FLAG_STYLE_SEQ
))
1512 dst
->flags
&= ~MODULE_FLAG_STYLE_SEQ
;
1513 dst
->flags
|= MODULE_FLAG_STYLE_SEC
;
1516 /* set time format for raw output */
1517 if (!strcmp(dst
->fmt
, "raw")) dst
->tfmt
= "sec";
1519 /* check for ASL_PLACE_DATABASE_DEFAULT */
1520 if (!strcmp(dst
->path
, ASL_PLACE_DATABASE_DEFAULT
))
1522 dst
->flags
= MODULE_FLAG_TYPE_ASL_DIR
;
1525 out
->action
= ACTION_OUT_DEST
;
1528 /* dst rules go first */
1529 out
->next
= m
->ruleset
;
1535 static asl_out_rule_t
*
1536 _asl_out_module_parse_query_action(asl_out_module_t
*m
, char *s
)
1539 asl_out_rule_t
*out
, *rule
;
1541 if (m
== NULL
) return NULL
;
1543 out
= (asl_out_rule_t
*)calloc(1, sizeof(asl_out_rule_t
));
1544 if (out
== NULL
) return NULL
;
1546 act
= _asl_out_module_find_action(s
);
1547 if (act
== NULL
) return NULL
;
1549 /* find whitespace delimiter */
1550 p
= strchr(act
, ' ');
1551 if (p
== NULL
) p
= strchr(act
, '\t');
1552 if (p
!= NULL
) *p
= '\0';
1554 if (!strcasecmp(act
, "ignore")) out
->action
= ACTION_IGNORE
;
1555 else if (!strcasecmp(act
, "skip")) out
->action
= ACTION_SKIP
;
1556 else if (!strcasecmp(act
, "claim")) out
->action
= ACTION_CLAIM
;
1557 else if (!strcasecmp(act
, "notify")) out
->action
= ACTION_NOTIFY
;
1558 else if (!strcasecmp(act
, "file")) out
->action
= ACTION_FILE
;
1559 else if (!strcasecmp(act
, "asl_file")) out
->action
= ACTION_ASL_FILE
;
1560 else if (!strcasecmp(act
, "directory")) out
->action
= ACTION_ASL_DIR
;
1561 else if (!strcasecmp(act
, "dir")) out
->action
= ACTION_ASL_DIR
;
1562 else if (!strcasecmp(act
, "asl_directory")) out
->action
= ACTION_ASL_DIR
;
1563 else if (!strcasecmp(act
, "asl_dir")) out
->action
= ACTION_ASL_DIR
;
1564 else if (!strcasecmp(act
, "store_dir")) out
->action
= ACTION_ASL_DIR
;
1565 else if (!strcasecmp(act
, "store_directory")) out
->action
= ACTION_ASL_DIR
;
1566 else if (!strcasecmp(act
, "control")) out
->action
= ACTION_CONTROL
;
1567 else if (!strcasecmp(act
, "save")) out
->action
= ACTION_ASL_STORE
;
1568 else if (!strcasecmp(act
, "store")) out
->action
= ACTION_ASL_STORE
;
1569 else if (!strcasecmp(act
, "access")) out
->action
= ACTION_ACCESS
;
1570 else if (!strcasecmp(act
, "set")) out
->action
= ACTION_SET_KEY
;
1571 else if (!strcasecmp(act
, "unset")) out
->action
= ACTION_UNSET_KEY
;
1572 else if (!strcmp(m
->name
, ASL_MODULE_NAME
))
1574 /* actions only allowed in com.apple.asl */
1575 if (!strcasecmp(act
, "broadcast")) out
->action
= ACTION_BROADCAST
;
1576 else if (!strcasecmp(act
, "forward")) out
->action
= ACTION_FORWARD
;
1579 if (out
->action
== ACTION_NONE
)
1585 /* options follow delimited (now zero) */
1588 /* skip whitespace */
1589 while ((*p
== ' ') || (*p
== '\t')) p
++;
1591 out
->options
= _strdup_clean(p
+1);
1593 if (out
->options
== NULL
)
1606 out
->query
= asl_msg_new(ASL_TYPE_QUERY
);
1611 out
->query
= asl_msg_from_string(s
);
1614 if (out
->query
== NULL
)
1621 /* store /some/path means save to an asl file */
1622 if (out
->action
== ACTION_ASL_STORE
)
1624 if (out
->options
== NULL
) out
->dst
= asl_out_dst_data_retain(_asl_out_module_parse_dst(m
, ASL_PLACE_DATABASE_DEFAULT
, 0755));
1625 else if (!strncmp(out
->options
, ASL_PLACE_DATABASE_DEFAULT
, strlen(ASL_PLACE_DATABASE_DEFAULT
))) out
->dst
= asl_out_dst_data_retain(_asl_out_module_parse_dst(m
, out
->options
, 0755));
1626 else if (out
->options
!= NULL
) out
->action
= ACTION_ASL_FILE
;
1629 if ((out
->action
== ACTION_FILE
) || (out
->action
== ACTION_ASL_FILE
) || (out
->action
== ACTION_ASL_DIR
))
1631 mode_t def_mode
= 0644;
1632 if (out
->action
== ACTION_ASL_DIR
) def_mode
= 0755;
1634 out
->dst
= asl_out_dst_data_retain(_asl_out_module_parse_dst(m
, out
->options
, def_mode
));
1635 if (out
->dst
== NULL
)
1637 out
->action
= ACTION_NONE
;
1642 * dst might have been set up by a previous ACTION_OUT_DEST ('>') rule with no mode.
1643 * If so, mode would be 010000. Set it now, since we know whether it is a file or dir.
1645 if (out
->dst
->mode
== 010000) out
->dst
->mode
= def_mode
;
1647 if ((out
->action
== ACTION_FILE
) && (out
->dst
!= NULL
) && (out
->dst
->fmt
!= NULL
) && (!strcasecmp(out
->dst
->fmt
, "asl")))
1649 out
->action
= ACTION_ASL_FILE
;
1652 if ((out
->action
== ACTION_ASL_FILE
) && (out
->dst
!= NULL
))
1654 out
->dst
->flags
|= MODULE_FLAG_TYPE_ASL
;
1657 if (out
->action
== ACTION_ASL_DIR
)
1659 /* coalesce is meaningless for ASL directories */
1660 out
->dst
->flags
&= ~MODULE_FLAG_COALESCE
;
1662 /* no compression at this point */
1663 out
->dst
->flags
&= ~MODULE_FLAG_COMPRESS
;
1665 out
->dst
->flags
|= MODULE_FLAG_TYPE_ASL_DIR
;
1667 /* set style bits for basestamp asl_dirs */
1668 if (((out
->dst
->flags
& MODULE_FLAG_STYLE_BITS
) == 0) && (out
->dst
->flags
& MODULE_FLAG_BASESTAMP
)) out
->dst
->flags
|= MODULE_FLAG_STYLE_LCL_B
;
1671 /* only ACTION_FILE and ACTION_ASL_FILE may rotate */
1672 if ((out
->action
!= ACTION_FILE
) && (out
->action
!= ACTION_ASL_FILE
))
1674 out
->dst
->flags
&= ~MODULE_FLAG_ROTATE
;
1677 #if !TARGET_IPHONE_SIMULATOR
1678 if (out
->dst
->nuid
== 0) _dst_add_uid(out
->dst
, "0");
1679 if (out
->dst
->ngid
== 0) _dst_add_gid(out
->dst
, "80");
1683 if (m
->ruleset
== NULL
) m
->ruleset
= out
;
1686 for (rule
= m
->ruleset
; rule
->next
!= NULL
; rule
= rule
->next
);
1694 asl_out_module_parse_line(asl_out_module_t
*m
, char *s
)
1696 while ((*s
== ' ') || (*s
== '\t')) s
++;
1698 if ((*s
== 'Q') || (*s
== '?') || (*s
== '*'))
1700 return _asl_out_module_parse_query_action(m
, s
);
1704 return _asl_out_module_parse_set_param(m
, s
);
1708 _asl_out_module_parse_dst(m
, s
+ 1, 010000);
1715 asl_out_module_init_from_file(const char *name
, FILE *f
)
1717 asl_out_module_t
*out
;
1720 if (f
== NULL
) return NULL
;
1722 out
= asl_out_module_new(name
);
1723 if (out
== NULL
) return NULL
;
1725 /* read and parse config file */
1726 while (NULL
!= (line
= get_line_from_file(f
)))
1728 asl_out_module_parse_line(out
, line
);
1735 static asl_out_module_t
*
1736 _asl_out_module_find(asl_out_module_t
*list
, const char *name
)
1738 asl_out_module_t
*x
;
1740 if (list
== NULL
) return NULL
;
1741 if (name
== NULL
) return NULL
;
1743 for (x
= list
; x
!= NULL
; x
= x
->next
)
1745 if ((x
->name
!= NULL
) && (!strcmp(x
->name
, name
))) return x
;
1752 _asl_out_module_read_and_merge_dir(asl_out_module_t
**list
, const char *path
, uint32_t flags
)
1757 asl_out_module_t
*last
, *x
;
1759 if (list
== NULL
) return;
1760 if (path
== NULL
) return;
1765 while (last
->next
!= NULL
) last
= last
->next
;
1771 while (NULL
!= (ent
= readdir(d
)))
1773 if ((ent
->d_name
!= NULL
) && (ent
->d_name
[0] != '.'))
1775 /* merge: skip this file if we already have a module with this name */
1776 if (_asl_out_module_find(*list
, ent
->d_name
) != NULL
) continue;
1778 char tmp
[MAXPATHLEN
];
1779 snprintf(tmp
, sizeof(tmp
), "%s/%s", path
, ent
->d_name
);
1780 f
= fopen(tmp
, "r");
1783 x
= asl_out_module_init_from_file(ent
->d_name
, f
);
1790 if (!strcmp(ent
->d_name
, ASL_MODULE_NAME
))
1792 /* com.apple.asl goes at the head of the list */
1795 if (last
== NULL
) last
= *list
;
1797 else if (*list
== NULL
)
1817 asl_out_module_init(void)
1819 asl_out_module_t
*out
= NULL
;
1821 #if TARGET_IPHONE_SIMULATOR
1822 char *sim_root_path
, *sim_resources_path
;
1823 char *asl_conf
, *asl_conf_dir
, *asl_conf_local_dir
;
1825 sim_root_path
= getenv("IPHONE_SIMULATOR_ROOT");
1826 assert(sim_root_path
);
1828 sim_resources_path
= getenv("IPHONE_SHARED_RESOURCES_DIRECTORY");
1829 assert(sim_resources_path
);
1831 asprintf(&asl_conf
, "%s%s", sim_root_path
, _PATH_ASL_CONF
);
1832 asprintf(&asl_conf_dir
, "%s%s", sim_root_path
, _PATH_ASL_CONF_DIR
);
1833 asprintf(&asl_conf_local_dir
, "%s%s", sim_resources_path
, _PATH_ASL_CONF_DIR
);
1835 _asl_out_module_read_and_merge_dir(&out
, asl_conf_local_dir
, MODULE_FLAG_LOCAL
);
1836 free(asl_conf_local_dir
);
1838 _asl_out_module_read_and_merge_dir(&out
, asl_conf_dir
, 0);
1841 _asl_out_module_read_and_merge_dir(&out
, _PATH_ASL_CONF_LOCAL_DIR
, MODULE_FLAG_LOCAL
);
1842 _asl_out_module_read_and_merge_dir(&out
, _PATH_ASL_CONF_DIR
, 0);
1845 if (_asl_out_module_find(out
, ASL_MODULE_NAME
) == NULL
)
1847 /* system just has old-style /etc/asl.conf */
1848 #if TARGET_IPHONE_SIMULATOR
1849 FILE *f
= fopen(asl_conf
, "r");
1852 FILE *f
= fopen(_PATH_ASL_CONF
, "r");
1856 asl_out_module_t
*x
= asl_out_module_init_from_file(ASL_MODULE_NAME
, f
);
1873 asl_out_module_rule_to_string(asl_out_rule_t
*r
)
1880 asprintf(&out
, "NULL rule");
1884 str
= asl_msg_to_string(r
->query
, &len
);
1886 asprintf(&out
, " %s%s%s%s%s",
1887 asl_out_action_name
[r
->action
],
1888 (r
->query
== NULL
) ? "" : " ",
1889 (r
->query
== NULL
) ? "" : str
,
1890 (r
->options
== NULL
) ? "" : " ",
1891 (r
->options
== NULL
) ? "" : r
->options
);
1901 asl_out_module_print(FILE *f
, asl_out_module_t
*m
)
1903 asl_out_rule_t
*r
, *n
;
1904 asl_out_dst_data_t
*o
;
1905 uint32_t i
, ttlnset
;
1908 for (r
= m
->ruleset
; r
!= NULL
; r
= n
)
1911 char *str
= asl_msg_to_string(r
->query
, &len
);
1913 fprintf(f
, " %s", asl_out_action_name
[r
->action
]);
1914 if (r
->query
!= NULL
) fprintf(f
, " %s", str
);
1915 if (r
->options
!= NULL
) fprintf(f
, " %s", r
->options
);
1916 if (r
->action
== ACTION_OUT_DEST
)
1921 fprintf(f
, " data: NULL");
1925 fprintf(f
, "%s\n", o
->path
);
1926 fprintf(f
, " rules: %u\n", o
->refcount
- 1);
1927 fprintf(f
, " dest: %s\n", (o
->rotate_dir
== NULL
) ? "(none)" : o
->rotate_dir
);
1928 fprintf(f
, " format: %s\n", (o
->fmt
== NULL
) ? "std" : o
->fmt
);
1929 fprintf(f
, " time_format: %s\n", (o
->tfmt
== NULL
) ? "lcl" : o
->tfmt
);
1930 fprintf(f
, " flags: 0x%08x", o
->flags
);
1935 if (o
->flags
& MODULE_FLAG_ENABLED
)
1937 fprintf(f
, "%cenabled", c
);
1940 if (o
->flags
& MODULE_FLAG_SOFT_WRITE
)
1942 fprintf(f
, "%csoft", c
);
1945 if (o
->flags
& MODULE_FLAG_ROTATE
)
1947 fprintf(f
, "%crotate", c
);
1950 if (o
->flags
& MODULE_FLAG_COALESCE
)
1952 fprintf(f
, "%ccoalesce", c
);
1955 if (o
->flags
& MODULE_FLAG_COMPRESS
)
1957 fprintf(f
, "%ccompress", c
);
1960 if (o
->flags
& MODULE_FLAG_STYLE_SEC
)
1962 fprintf(f
, "%cseconds", c
);
1965 if (o
->flags
& MODULE_FLAG_STYLE_SEQ
)
1967 fprintf(f
, "%csequence", c
);
1970 if (o
->flags
& MODULE_FLAG_STYLE_UTC
)
1972 fprintf(f
, "%cutc", c
);
1975 if (o
->flags
& MODULE_FLAG_STYLE_UTC_B
)
1977 fprintf(f
, "%cutc-basic", c
);
1980 if (o
->flags
& MODULE_FLAG_STYLE_LCL
)
1982 fprintf(f
, "%clocal", c
);
1985 if (o
->flags
& MODULE_FLAG_STYLE_LCL_B
)
1987 fprintf(f
, "%clocal-basic", c
);
1990 if (o
->flags
& MODULE_FLAG_BASESTAMP
)
1992 fprintf(f
, "%cbasestamp", c
);
1995 if (o
->flags
& MODULE_FLAG_NONSTD_DIR
)
1997 fprintf(f
, "%cnon-std_dir", c
);
2000 if (o
->flags
& MODULE_FLAG_EXTERNAL
)
2002 fprintf(f
, "%cexternal", c
);
2005 if (o
->flags
& MODULE_FLAG_CRASHLOG
)
2007 fprintf(f
, "%ccrashlog", c
);
2010 if (o
->flags
& MODULE_FLAG_TYPE_ASL
)
2012 fprintf(f
, "%casl_file", c
);
2015 if (o
->flags
& MODULE_FLAG_TYPE_ASL_DIR
)
2017 fprintf(f
, "%casl_directory", c
);
2024 fprintf(f
, " ttl: %u", o
->ttl
[LEVEL_ALL
]);
2026 for (i
= 0; (i
<= 7) & (ttlnset
== 0); i
++) if (o
->ttl
[i
] != 0) ttlnset
= 1;
2027 if (ttlnset
!= 0) for (i
= 0; i
<= 7; i
++) printf(" [%d %d]", i
, (o
->ttl
[i
] == 0) ? o
->ttl
[LEVEL_ALL
] : o
->ttl
[i
]);
2030 fprintf(f
, " mode: 0%o\n", o
->mode
);
2031 fprintf(f
, " file_max: %lu\n", o
->file_max
);
2032 fprintf(f
, " all_max: %lu\n", o
->all_max
);
2033 #if !TARGET_IPHONE_SIMULATOR
2034 fprintf(f
, " uid:");
2035 for (i
= 0; i
< o
->nuid
; i
++) fprintf(f
, " %d", o
->uid
[i
]);
2037 fprintf(f
, " gid:");
2038 for (i
= 0; i
< o
->ngid
; i
++) fprintf(f
, " %d", o
->gid
[i
]);
2051 asl_out_file_list_free(asl_out_file_list_t
*l
)
2053 asl_out_file_list_t
*n
;
2055 if (l
== NULL
) return;
2067 * Checks input name for the form base[.stamp][.gz]
2068 * name == base is allowed if src is true.
2069 * base.gz is not allowed.
2070 * Output parameter stamp must be freed by caller.
2073 _check_file_name(const char *name
, const char *base
, bool src
, char **stamp
)
2075 size_t baselen
, nparts
;
2076 const char *p
, *q
, *part
[2];
2079 if (name
== NULL
) return false;
2080 if (base
== NULL
) return false;
2082 baselen
= strlen(base
);
2083 if (baselen
== 0) return false;
2085 if (stamp
!= NULL
) *stamp
= NULL
;
2087 if (strncmp(name
, base
, baselen
)) return false;
2091 /* name == base not allowed (it's the "active" file) */
2092 if (*p
== '\0') return false;
2094 /* name must be base.something */
2095 if (*p
!= '.') return false;
2097 /* maximum of 2 parts (stamp and gz) */
2099 for (q
= p
; *q
!= '\0'; q
++)
2103 if (nparts
== 2) return false;
2104 part
[nparts
++] = q
+ 1;
2108 if (nparts
== 0) return false;
2110 isgz
= strcmp(part
[nparts
- 1], "gz") == 0;
2112 /* no compressed files in src */
2113 if (src
&& isgz
) return false;
2115 /* expecting base.stamp or base.stamp.gz */
2119 /* compressed files must have a stamp (base.gz is not allowed) */
2120 if (isgz
) return false;
2122 /* got base.stamp */
2123 if (stamp
!= NULL
) *stamp
= strdup(part
[0]);
2127 /* expecting base.stamp.gz */
2128 if (!isgz
) return false;
2130 /* got base.stamp.gz */
2133 *stamp
= strdup(part
[0]);
2134 char *x
= strchr(*stamp
, '.');
2135 if (x
!= NULL
) *x
= '\0';
2142 * Find files in a directory (dir) that all have a common prefix (base).
2143 * Bits in flags further control the search.
2145 * MODULE_FLAG_STYLE_SEQ means a numeric sequence number is expected, although not required.
2146 * E.g. foo.log foo.log.0
2148 * MODULE_FLAG_STYLE_SEC also means a numeric sequence number is required following an 'T' character.
2149 * The numeric value is the file's timestamp in seconds. E.g foo.log.T1335200452
2151 * MODULE_FLAG_STYLE_UTC requires a date/time component as the file's timestamp.
2152 * E.g. foo.2012-04-06T15:30:00Z
2154 * MODULE_FLAG_STYLE_UTC_B requires a date/time component as the file's timestamp.
2155 * E.g. foo.20120406T153000Z
2157 * MODULE_FLAG_STYLE_LCL requires a date/time component as the file's timestamp.
2158 * E.g. foo.2012-04-06T15:30:00-7
2160 * MODULE_FLAG_STYLE_LCL_B requires a date/time component as the file's timestamp.
2161 * E.g. foo.20120406T153000-07
2164 _parse_stamp_style(char *stamp
, uint32_t flags
, uint32_t *sp
, time_t *tp
)
2171 long utc_offset
= 0;
2174 /* check for NULL (no stamp) */
2175 if (stamp
== NULL
) return STAMP_STYLE_NULL
;
2177 /* check for MODULE_FLAG_STYLE_SEC (foo.T12345678) */
2178 if (stamp
[0] == 'T')
2180 n
= atoi(stamp
+ 1);
2181 if ((n
== 0) && strcmp(stamp
+ 1, "0")) return STAMP_STYLE_INVALID
;
2182 if (tp
!= NULL
) *tp
= (time_t)n
;
2184 return STAMP_STYLE_SEC
;
2187 /* check for MODULE_FLAG_STYLE_SEQ (foo.0 or foo.2.gz) */
2189 for (i
= 0; digits
&& (stamp
[i
] != '\0'); i
++) digits
= (stamp
[i
] >= '0') && (stamp
[i
] <= '9');
2191 if (!digits
&& (!strcmp(stamp
+ i
, ".gz"))) digits
= true;
2196 if (sp
!= NULL
) *sp
= (uint32_t)n
;
2197 return STAMP_STYLE_SEQ
;
2200 /* check for MODULE_FLAG_STYLE_UTC, UTC_B, LCL, or LCL_B */
2201 memset(&t
, 0, sizeof(t
));
2205 if ((flags
& MODULE_FLAG_STYLE_UTC
) || (flags
& MODULE_FLAG_STYLE_LCL
))
2207 n
= sscanf(stamp
, "%d-%d-%dT%d:%d:%d%c%u:%u:%u", &t
.tm_year
, &t
.tm_mon
, &t
.tm_mday
, &t
.tm_hour
, &t
.tm_min
, &t
.tm_sec
, &zone
, &h
, &m
, &s
);
2209 else if ((flags
& MODULE_FLAG_STYLE_UTC_B
) || (flags
& MODULE_FLAG_STYLE_LCL_B
))
2211 n
= sscanf(stamp
, "%4d%2d%2dT%2d%2d%2d%c%2u%2u%2u", &t
.tm_year
, &t
.tm_mon
, &t
.tm_mday
, &t
.tm_hour
, &t
.tm_min
, &t
.tm_sec
, &zone
, &h
, &m
, &s
);
2215 return STAMP_STYLE_INVALID
;
2218 if (n
< 6) return STAMP_STYLE_INVALID
;
2224 else if ((zone
== '-') || (zone
== '+'))
2226 if (n
>= 8) utc_offset
+= (3600 * h
);
2227 if (n
>= 9) utc_offset
+= (60 * m
);
2228 if (n
== 10) utc_offset
+= s
;
2229 if (zone
== '-') utc_offset
*= -1;
2231 else if ((zone
>= 'A') && (zone
<= 'Z'))
2233 if (zone
< 'J') utc_offset
= 3600 * ((zone
- 'A') + 1);
2234 else if ((zone
>= 'K') && (zone
<= 'M')) utc_offset
= 3600 * (zone
- 'A');
2235 else if (zone
<= 'Y') utc_offset
= -3600 * ((zone
- 'N') + 1);
2237 else if ((zone
>= 'a') && (zone
<= 'z'))
2239 if (zone
< 'j') utc_offset
= 3600 * ((zone
- 'a') + 1);
2240 else if ((zone
>= 'k') && (zone
<= 'm')) utc_offset
= 3600 * (zone
- 'a');
2241 else if (zone
<= 'y') utc_offset
= -3600 * ((zone
- 'n') + 1);
2245 return STAMP_STYLE_INVALID
;
2250 t
.tm_sec
+= utc_offset
;
2253 if ((zone
== 'J') || (zone
== 'j')) ftime
= mktime(&t
);
2254 else ftime
= timegm(&t
);
2256 if (tp
!= NULL
) *tp
= ftime
;
2258 return STAMP_STYLE_UTC_OR_LCL
;
2261 asl_out_file_list_t
*
2262 asl_list_log_files(const char *dir
, const char *base
, bool src
, uint32_t flags
)
2266 char path
[MAXPATHLEN
];
2271 asl_out_file_list_t
*out
, *x
, *y
;
2273 if (dir
== NULL
) return NULL
;
2274 if (base
== NULL
) return NULL
;
2279 if (d
== NULL
) return NULL
;
2281 while (NULL
!= (ent
= readdir(d
)))
2286 if (ent
->d_name
== NULL
) continue;
2288 check
= _check_file_name(ent
->d_name
, base
, src
, &stamp
);
2289 if (!check
) continue;
2294 pstyle
= _parse_stamp_style(stamp
, flags
, &seq
, &ftime
);
2297 if (pstyle
== STAMP_STYLE_INVALID
) continue;
2299 fstyle
= STAMP_STYLE_NULL
;
2300 if (flags
& MODULE_FLAG_STYLE_SEC
) fstyle
= STAMP_STYLE_SEC
;
2301 else if (flags
& MODULE_FLAG_STYLE_SEQ
) fstyle
= STAMP_STYLE_SEQ
;
2302 else if ((flags
& MODULE_FLAG_STYLE_UTC
) || (flags
& MODULE_FLAG_STYLE_LCL
)) fstyle
= STAMP_STYLE_UTC_OR_LCL
;
2303 else if ((flags
& MODULE_FLAG_STYLE_UTC_B
) || (flags
& MODULE_FLAG_STYLE_LCL_B
)) fstyle
= STAMP_STYLE_UTC_OR_LCL
;
2306 * accept the file if:
2307 * style is STAMP_STYLE_NULL (no timestamp)
2308 * src is true and style is STAMP_STYLE_SEC
2309 * actual style matches the style implied by the input flags
2313 if (pstyle
== STAMP_STYLE_NULL
) check
= true;
2314 if ((pstyle
== STAMP_STYLE_SEC
) && src
) check
= true;
2315 if (pstyle
== fstyle
) check
= true;
2317 if (!check
) continue;
2319 x
= (asl_out_file_list_t
*)calloc(1, sizeof(asl_out_file_list_t
));
2322 asl_out_file_list_free(out
);
2326 x
->name
= strdup(ent
->d_name
);
2330 memset(&sb
, 0, sizeof(sb
));
2331 snprintf(path
, sizeof(path
), "%s/%s", dir
, ent
->d_name
);
2332 if (stat(path
, &sb
) == 0)
2334 x
->size
= sb
.st_size
;
2335 if (pstyle
== STAMP_STYLE_SEQ
)
2337 x
->ftime
= sb
.st_birthtimespec
.tv_sec
;
2338 if (x
->ftime
== 0) x
->ftime
= sb
.st_mtimespec
.tv_sec
;
2342 if (pstyle
== STAMP_STYLE_SEQ
)
2348 else if ((x
->seq
== IndexNull
) || ((x
->seq
< out
->seq
) && (out
->seq
!= IndexNull
)))
2356 for (y
= out
; y
!= NULL
; y
= y
->next
)
2358 if (y
->next
== NULL
)
2364 else if ((x
->seq
< y
->next
->seq
) && (y
->next
->seq
!= IndexNull
))
2381 else if (x
->ftime
< out
->ftime
)
2389 for (y
= out
; y
!= NULL
; y
= y
->next
)
2391 if (y
->next
== NULL
)
2397 else if (x
->ftime
< y
->next
->ftime
)
2415 * List the source files for an output asl_out_dst_data_t
2417 asl_out_file_list_t
*
2418 asl_list_src_files(asl_out_dst_data_t
*dst
)
2421 uint32_t flags
= MODULE_FLAG_STYLE_SEC
;
2422 asl_out_file_list_t
*out
;
2424 if (dst
== NULL
) return NULL
;
2425 if (dst
->path
== NULL
) return NULL
;
2428 * MODULE_FLAG_EXTERNAL means some process other than syslogd writes the file.
2429 * We check for its existence, and that it is non-zero in size.
2431 if (dst
->flags
& MODULE_FLAG_EXTERNAL
)
2435 memset(&sb
, 0, sizeof(struct stat
));
2437 if (stat(dst
->path
, &sb
) == 0)
2439 if (S_ISREG(sb
.st_mode
) && (sb
.st_size
!= 0))
2441 out
= (asl_out_file_list_t
*)calloc(1, sizeof(asl_out_file_list_t
));
2444 char *p
= strrchr(dst
->path
, '/');
2445 if (p
== NULL
) p
= dst
->path
;
2447 out
->name
= strdup(p
);
2448 out
->ftime
= sb
.st_birthtimespec
.tv_sec
;
2449 if (out
->ftime
== 0) out
->ftime
= sb
.st_mtimespec
.tv_sec
;
2459 * Checkpoint / source format may be one of:
2460 * MODULE_FLAG_STYLE_SEC (foo.T12345678.log),
2461 * MODULE_FLAG_STYLE_UTC (foo.20120-06-24T12:34:56Z.log)
2462 * MODULE_FLAG_STYLE_UTC_B (foo.201200624T123456Z.log)
2463 * MODULE_FLAG_STYLE_LCL (foo.20120-06-24T12:34:56-7.log)
2464 * MODULE_FLAG_STYLE_LCL_B (foo.201200624T123456-07.log)
2466 * MODULE_FLAG_STYLE_SEC format is used for sequenced (MODULE_FLAG_STYLE_SEQ) files.
2467 * aslmanager converts the file names.
2470 if (dst
->flags
& MODULE_FLAG_STYLE_UTC
) flags
= MODULE_FLAG_STYLE_UTC
;
2471 else if (dst
->flags
& MODULE_FLAG_STYLE_UTC_B
) flags
= MODULE_FLAG_STYLE_UTC_B
;
2472 else if (dst
->flags
& MODULE_FLAG_STYLE_LCL
) flags
= MODULE_FLAG_STYLE_LCL
;
2473 else if (dst
->flags
& MODULE_FLAG_STYLE_LCL_B
) flags
= MODULE_FLAG_STYLE_LCL_B
;
2475 if ((dst
->rotate_dir
== NULL
) && ((dst
->flags
& MODULE_FLAG_STYLE_SEQ
) == 0) && ((dst
->flags
& MODULE_FLAG_COMPRESS
) == 0))
2477 /* files do not move to a dest dir, get renamed, or get compressed - nothing to do */
2481 base
= strrchr(dst
->path
, '/');
2482 if (base
== NULL
) return NULL
;
2487 out
= asl_list_log_files(dst
->path
, base
, true, flags
);
2489 if (base
!= NULL
) *--base
= '/';
2495 * List the destination files for an output asl_out_dst_data_t
2497 asl_out_file_list_t
*
2498 asl_list_dst_files(asl_out_dst_data_t
*dst
)
2500 char *base
, *dst_dir
;
2501 asl_out_file_list_t
*out
;
2503 if (dst
== NULL
) return NULL
;
2504 if (dst
->path
== NULL
) return NULL
;
2506 base
= strrchr(dst
->path
, '/');
2507 if (base
== NULL
) return NULL
;
2512 dst_dir
= dst
->rotate_dir
;
2513 if (dst_dir
== NULL
) dst_dir
= dst
->path
;
2515 out
= asl_list_log_files(dst_dir
, base
, false, dst
->flags
);
2517 if (base
!= NULL
) *--base
= '/';
2523 asl_secure_open_dir(const char *path
)
2528 if (path
== NULL
) return -1;
2529 if (path
[0] != '/') return -1;
2531 path_parts
= explode(path
+ 1, "/");
2532 if (path_parts
== NULL
) return 0;
2534 fd
= open("/", O_RDONLY
| O_NOFOLLOW
, 0);
2537 free_string_list(path_parts
);
2541 for (i
= 0; path_parts
[i
] != NULL
; i
++)
2543 int fd_next
, status
;
2546 fd_next
= openat(fd
, path_parts
[i
], O_RDONLY
| O_NOFOLLOW
, 0);
2551 free_string_list(path_parts
);
2555 memset(&sb
, 0, sizeof(sb
));
2557 status
= fstat(fd
, &sb
);
2560 free_string_list(path_parts
);
2564 if (!S_ISDIR(sb
.st_mode
))
2566 free_string_list(path_parts
);
2571 free_string_list(path_parts
);
2576 asl_secure_chown_chmod_dir(const char *path
, uid_t uid
, gid_t gid
, mode_t mode
)
2580 fd
= asl_secure_open_dir(path
);
2581 if (fd
< 0) return fd
;
2583 status
= fchown(fd
, uid
, gid
);
2590 if (mode
>= 0) status
= fchmod(fd
, mode
);
2593 if (status
< 0) return -1;