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@
33 #include <sys/param.h>
36 #include <membership.h>
38 #include <TargetConditionals.h>
39 #include <configuration_profile.h>
40 #include <os/variant_private.h>
44 #include "asl_common.h"
47 #define _PATH_ASL_CONF "/etc/asl.conf"
48 #define _PATH_ASL_CONF_DIR "/etc/asl"
50 #define PATH_VAR_LOG "/var/log/"
51 #define PATH_VAR_LOG_LEN 9
53 #define PATH_LIBRARY_LOGS "/Library/Logs/"
54 #define PATH_LIBRARY_LOGS_LEN 14
56 #if !TARGET_OS_SIMULATOR
57 #define _PATH_ASL_CONF_LOCAL_DIR "/usr/local/etc/asl"
60 //#define DEBUG_LIST_FILES 1
62 static const char *asl_out_action_name
[] =
86 #define forever for(;;)
87 #define KEYMATCH(S,K) ((strncasecmp(S, K, strlen(K)) == 0))
90 xpc_object_to_asl_msg(xpc_object_t xobj
)
92 __block asl_msg_t
*out
;
94 if (xobj
== NULL
) return NULL
;
95 if (xpc_get_type(xobj
) != XPC_TYPE_DICTIONARY
) return NULL
;
97 out
= asl_msg_new(ASL_TYPE_MSG
);
98 xpc_dictionary_apply(xobj
, ^bool(const char *key
, xpc_object_t xval
) {
101 if (xpc_get_type(xval
) == XPC_TYPE_NULL
)
103 asl_msg_set_key_val_op(out
, key
, NULL
, 0);
105 else if (xpc_get_type(xval
) == XPC_TYPE_BOOL
)
107 if (xpc_bool_get_value(xval
)) asl_msg_set_key_val_op(out
, key
, "1", 0);
108 else asl_msg_set_key_val_op(out
, key
, "0", 0);
110 else if (xpc_get_type(xval
) == XPC_TYPE_INT64
)
112 snprintf(tmp
, sizeof(tmp
), "%lld", xpc_int64_get_value(xval
));
113 asl_msg_set_key_val_op(out
, key
, tmp
, 0);
115 else if (xpc_get_type(xval
) == XPC_TYPE_UINT64
)
117 snprintf(tmp
, sizeof(tmp
), "%llu", xpc_uint64_get_value(xval
));
118 asl_msg_set_key_val_op(out
, key
, tmp
, 0);
120 else if (xpc_get_type(xval
) == XPC_TYPE_DOUBLE
)
122 snprintf(tmp
, sizeof(tmp
), "%f", xpc_double_get_value(xval
));
123 asl_msg_set_key_val_op(out
, key
, tmp
, 0);
125 else if (xpc_get_type(xval
) == XPC_TYPE_DATE
)
127 snprintf(tmp
, sizeof(tmp
), "%lld", xpc_date_get_value(xval
));
128 asl_msg_set_key_val_op(out
, key
, tmp
, 0);
130 else if (xpc_get_type(xval
) == XPC_TYPE_DATA
)
132 size_t len
= xpc_data_get_length(xval
);
133 char *encoded
= asl_core_encode_buffer(xpc_data_get_bytes_ptr(xval
), len
);
134 asl_msg_set_key_val_op(out
, key
, encoded
, 0);
137 else if (xpc_get_type(xval
) == XPC_TYPE_STRING
)
139 asl_msg_set_key_val_op(out
, key
, xpc_string_get_string_ptr(xval
), 0);
141 else if (xpc_get_type(xval
) == XPC_TYPE_UUID
)
144 uuid_unparse(xpc_uuid_get_bytes(xval
), us
);
145 asl_msg_set_key_val_op(out
, key
, us
, 0);
147 else if (xpc_get_type(xval
) == XPC_TYPE_FD
)
149 /* XPC_TYPE_FD is not supported */
150 asl_msg_set_key_val_op(out
, key
, "{XPC_TYPE_FD}", 0);
152 else if (xpc_get_type(xval
) == XPC_TYPE_SHMEM
)
154 /* XPC_TYPE_SHMEM is not supported */
155 asl_msg_set_key_val_op(out
, key
, "{XPC_TYPE_SHMEM}", 0);
157 else if (xpc_get_type(xval
) == XPC_TYPE_ARRAY
)
159 /* XPC_TYPE_ARRAY is not supported */
160 asl_msg_set_key_val_op(out
, key
, "{XPC_TYPE_ARRAY}", 0);
162 else if (xpc_get_type(xval
) == XPC_TYPE_DICTIONARY
)
164 /* XPC_TYPE_DICTIONARY is not supported */
165 asl_msg_set_key_val_op(out
, key
, "{XPC_TYPE_DICTIONARY}", 0);
167 else if (xpc_get_type(xval
) == XPC_TYPE_ERROR
)
169 /* XPC_TYPE_ERROR is not supported */
170 asl_msg_set_key_val_op(out
, key
, "{XPC_TYPE_ERROR}", 0);
175 asl_msg_set_key_val_op(out
, key
, "{XPC_TYPE_???}", 0);
185 configuration_profile_to_asl_msg(const char *ident
)
187 xpc_object_t xobj
= configuration_profile_copy_property_list(ident
);
188 asl_msg_t
*out
= xpc_object_to_asl_msg(xobj
);
189 if (xobj
!= NULL
) xpc_release(xobj
);
193 /* strdup + skip leading and trailing whitespace */
195 _strdup_clean(const char *s
)
198 const char *first
, *last
;
201 if (s
== NULL
) return NULL
;
204 while ((*first
== ' ') || (*first
== '\t')) first
++;
206 if (len
== 0) return NULL
;
208 last
= first
+ len
- 1;
209 while ((len
> 0) && ((*last
== ' ') || (*last
== '\t')))
215 if (len
== 0) return NULL
;
217 out
= malloc(len
+ 1);
218 if (out
== NULL
) return NULL
;
220 memcpy(out
, first
, len
);
226 _insert_string(char *s
, char **l
, uint32_t x
)
230 if (s
== NULL
) return l
;
233 l
= (char **)malloc(2 * sizeof(char *));
234 if (l
== NULL
) return NULL
;
247 for (i
= 0; l
[i
] != NULL
; i
++);
249 /* len includes the NULL at the end of the list */
252 l
= (char **)reallocf(l
, (len
+ 1) * sizeof(char *));
253 if (l
== NULL
) return NULL
;
255 if ((x
>= (len
- 1)) || (x
== IndexNull
))
257 l
[len
- 1] = strdup(s
);
258 if (l
[len
- 1] == NULL
)
268 for (i
= len
; i
> x
; i
--) l
[i
] = l
[i
- 1];
270 if (l
[x
] == NULL
) return NULL
;
276 explode(const char *s
, const char *delim
)
283 if (s
== NULL
) return NULL
;
291 for (i
= 0; p
[i
] != '\0'; i
++)
295 /* not inside a quoted string: check for delimiters and quotes */
296 if (strchr(delim
, p
[i
]) != NULL
) break;
297 else if (p
[i
] == '\'') quote
= p
[i
];
298 else if (p
[i
] == '"') quote
= p
[i
];
302 /* inside a quoted string - look for matching quote */
303 if (p
[i
] == quote
) quote
= '\0';
309 if (t
== NULL
) return NULL
;
311 for (i
= 0; i
< n
; i
++) t
[i
] = p
[i
];
313 l
= _insert_string(t
, l
, IndexNull
);
316 if (p
[i
] == '\0') return l
;
317 if (p
[i
+ 1] == '\0') l
= _insert_string("", l
, IndexNull
);
325 free_string_list(char **l
)
329 if (l
== NULL
) return;
330 for (i
= 0; l
[i
] != NULL
; i
++) free(l
[i
]);
335 get_line_from_file(FILE *f
)
340 out
= fgetln(f
, &len
);
341 if (out
== NULL
) return NULL
;
342 if (len
== 0) return NULL
;
345 if (s
== NULL
) return NULL
;
349 if (s
[len
- 1] != '\n') len
++;
355 next_word_from_string(char **s
)
357 char *a
, *p
, *e
, *out
, s0
;
358 int quote1
, quote2
, len
;
360 if (s
== NULL
) return NULL
;
361 if (*s
== NULL
) return NULL
;
370 /* allow whole word to be contained in quotes */
406 if (quote1
== 0) quote1
= 1;
412 if (quote2
== 0) quote2
= 1;
416 if (((*p
== ' ') || (*p
== '\t')) && (quote1
== 0) && (quote2
== 0))
430 /* check for quoted string */
431 if (((s0
== '\'') || (s0
== '"')) && (s0
== a
[len
-1])) len
--;
433 if (len
== 0) return NULL
;
435 out
= malloc(len
+ 1);
436 if (out
== NULL
) return NULL
;
444 asl_out_dest_for_path(asl_out_module_t
*m
, const char *path
)
446 if (m
== NULL
) return NULL
;
447 if (path
== NULL
) return NULL
;
451 asl_out_rule_t
*r
= m
->ruleset
;
454 if ((r
->action
== ACTION_OUT_DEST
) && (r
->dst
!= NULL
) && (r
->dst
->path
!= NULL
) && (streq(r
->dst
->path
, path
))) return r
->dst
;
465 * Create a directory path.
467 * mlist provides owner, group, and access mode, which are required for "non-standard"
468 * directories. Directories for standard paths (/var/log or /Library/Logs) default
469 * to root/admin/0755 if there is no mlist rule for them.
472 _asl_common_make_dir_path(asl_out_module_t
*mlist
, uint32_t flags
, const char *path
)
476 asl_string_t
*processed_path
;
479 if (path
== NULL
) return 0;
481 path_parts
= explode(path
, "/");
482 if (path_parts
== NULL
) return 0;
484 processed_path
= asl_string_new(ASL_ENCODE_NONE
);
487 if (path
[0] == '/') i
= 1;
489 for (; path_parts
[i
] != NULL
; i
++)
494 asl_out_dst_data_t
*dst
;
497 asl_string_append_char_no_encoding(processed_path
, '/');
498 asl_string_append_no_encoding(processed_path
, path_parts
[i
]);
499 tmp
= asl_string_bytes(processed_path
);
501 memset(&sb
, 0, sizeof(struct stat
));
502 status
= lstat(tmp
, &sb
);
503 if ((status
== 0) && S_ISLNK(sb
.st_mode
))
505 char real
[MAXPATHLEN
];
506 if (realpath(tmp
, real
) == NULL
)
508 asl_string_release(processed_path
);
509 free_string_list(path_parts
);
513 memset(&sb
, 0, sizeof(struct stat
));
514 status
= stat(real
, &sb
);
519 if (!S_ISDIR(sb
.st_mode
))
521 /* path component is not a directory! */
522 asl_string_release(processed_path
);
523 free_string_list(path_parts
);
527 /* exists and is a directory or a link to a directory */
530 else if (errno
!= ENOENT
)
532 /* unexpected status from stat() */
533 asl_string_release(processed_path
);
534 free_string_list(path_parts
);
538 dst
= asl_out_dest_for_path(mlist
, tmp
);
539 if ((dst
== NULL
) && (flags
& MODULE_FLAG_NONSTD_DIR
))
541 /* no rule to create a non-standard path component! */
542 asl_string_release(processed_path
);
543 free_string_list(path_parts
);
551 if (mode
== 010000) mode
= 0755;
555 status
= mkdir(tmp
, mode
);
558 #if !TARGET_OS_SIMULATOR
564 if (dst
->nuid
> 0) u
= dst
->uid
[0];
565 if (dst
->ngid
> 0) g
= dst
->gid
[0];
572 asl_string_release(processed_path
);
573 free_string_list(path_parts
);
579 asl_out_mkpath(asl_out_module_t
*mlist
, asl_out_rule_t
*r
)
581 char tmp
[MAXPATHLEN
], *p
;
585 if (r
== NULL
) return -1;
586 if (r
->dst
== NULL
) return -1;
587 if (r
->dst
->path
== NULL
) return -1;
589 snprintf(tmp
, sizeof(tmp
), "%s", r
->dst
->path
);
591 if (r
->action
!= ACTION_ASL_DIR
)
593 p
= strrchr(tmp
, '/');
594 if (p
== NULL
) return -1;
598 memset(&sb
, 0, sizeof(struct stat
));
599 status
= stat(tmp
, &sb
);
602 if (S_ISDIR(sb
.st_mode
)) return 0;
608 uint32_t dirflag
= r
->dst
->flags
& MODULE_FLAG_NONSTD_DIR
;
609 status
= _asl_common_make_dir_path(mlist
, dirflag
, tmp
);
617 asl_make_database_dir(const char *dir
, char **out
)
619 const char *asldir
, *path
;
625 if (out
!= NULL
) *out
= NULL
;
627 asldir
= asl_filesystem_path(ASL_PLACE_DATABASE
);
628 if (asldir
== NULL
) return -1;
632 /* create the database directory itself */
637 if (strchr(dir
, '/') != NULL
) return -1;
639 asprintf(&str
, "%s/%s", asldir
, dir
);
640 if (str
== NULL
) return -1;
644 memset(&sb
, 0, sizeof(struct stat
));
646 status
= stat(path
, &sb
);
649 if (S_ISDIR(sb
.st_mode
))
651 if (out
== NULL
) free(str
);
667 status
= mkdir(path
, 0755);
672 if (out
== NULL
) free(str
);
681 asl_make_timestamp(time_t stamp
, uint32_t flags
, char *buf
, size_t len
)
686 if (buf
== NULL
) return;
688 if (flags
& MODULE_NAME_STYLE_STAMP_UTC
)
690 memset(&t
, 0, sizeof(t
));
691 gmtime_r(&stamp
, &t
);
692 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
);
694 else if (flags
& MODULE_NAME_STYLE_STAMP_UTC_B
)
696 memset(&t
, 0, sizeof(t
));
697 gmtime_r(&stamp
, &t
);
698 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
);
700 else if (flags
& MODULE_NAME_STYLE_STAMP_LCL
)
703 memset(&t
, 0, sizeof(t
));
704 localtime_r(&stamp
, &t
);
706 if ((neg
= (t
.tm_gmtoff
< 0))) t
.tm_gmtoff
*= -1;
714 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
);
715 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
);
716 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
);
718 else if (flags
& MODULE_NAME_STYLE_STAMP_LCL_B
)
721 memset(&t
, 0, sizeof(t
));
722 localtime_r(&stamp
, &t
);
724 if ((neg
= (t
.tm_gmtoff
< 0))) t
.tm_gmtoff
*= -1;
732 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
);
733 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
);
734 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
);
738 snprintf(buf
, len
, "%c%llu", STYLE_SEC_PREFIX_CHAR
, (unsigned long long)stamp
);
743 asl_dst_make_current_name(asl_out_dst_data_t
*dst
, uint32_t xflags
, char *buf
, size_t len
)
747 if (dst
== NULL
) return;
748 if (buf
== NULL
) return;
750 xflags
|= dst
->flags
;
752 if (dst
->timestamp
== 0) dst
->timestamp
= time(NULL
);
753 asl_make_timestamp(dst
->timestamp
, dst
->style_flags
, tstamp
, sizeof(tstamp
));
755 if (xflags
& MODULE_FLAG_TYPE_ASL_DIR
)
757 snprintf(buf
, len
, "%s.%s", dst
->current_name
, tstamp
);
759 else if (xflags
& MODULE_FLAG_BASESTAMP
)
761 if ((dst
->dir
!= NULL
) && (dst
->style_flags
& MODULE_NAME_STYLE_FORMAT_BSE
))
763 snprintf(buf
, len
, "%s/%s.%s.%s", dst
->dir
, dst
->base
, tstamp
, dst
->ext
);
767 snprintf(buf
, len
, "%s.%s", dst
->path
, tstamp
);
772 snprintf(buf
, len
, "%s", dst
->path
);
777 asl_check_option(asl_msg_t
*msg
, const char *opt
)
782 if (msg
== NULL
) return 0;
783 if (opt
== NULL
) return 0;
786 if (len
== 0) return 0;
788 p
= asl_msg_get_val_for_key(msg
, ASL_KEY_OPTION
);
789 if (p
== NULL
) return 0;
793 while ((*p
== ' ') || (*p
== '\t') || (*p
== ',')) p
++;
794 if (*p
== '\0') return 0;
796 if (strncasecmp(p
, opt
, len
) == 0)
799 if ((*p
== ' ') || (*p
== '\t') || (*p
== ',') || (*p
== '\0')) return 1;
802 while ((*p
!= ' ') && (*p
!= '\t') && (*p
!= ',') && (*p
!= '\0')) p
++;
809 asl_out_dst_data_release(asl_out_dst_data_t
*dst
)
811 if (dst
== NULL
) return;
813 if (dst
->refcount
> 0) dst
->refcount
--;
814 if (dst
->refcount
> 0) return;
818 free(dst
->current_name
);
821 free(dst
->rotate_dir
);
823 #if !TARGET_OS_SIMULATOR
831 asl_out_dst_data_retain(asl_out_dst_data_t
*dst
)
833 if (dst
== NULL
) return NULL
;
838 /* set owner, group, mode, and acls for a file */
840 asl_out_dst_set_access(int fd
, asl_out_dst_data_t
*dst
)
842 #if !TARGET_OS_SIMULATOR
845 #if !TARGET_OS_IPHONE
855 if (dst
== NULL
) return -1;
856 if (fd
< 0) return -1;
858 #if TARGET_OS_SIMULATOR
862 if (dst
->nuid
> 0) fuid
= dst
->uid
[0];
863 if (dst
->ngid
> 0) fgid
= dst
->gid
[0];
865 fchown(fd
, fuid
, fgid
);
870 if (os_variant_is_basesystem("com.apple.syslog")) {
875 for (i
= 0; i
< dst
->ngid
; i
++)
877 if (dst
->gid
[i
] == -2) continue;
880 * Don't bother setting group access if this is
881 * file's group and the file is group-readable.
883 if ((dst
->gid
[i
] == fgid
) && (dst
->mode
& 00040)) continue;
885 status
= mbr_gid_to_uuid(dst
->gid
[i
], uuid
);
892 status
= acl_create_entry_np(&acl
, &entry
, ACL_FIRST_ENTRY
);
893 if (status
!= 0) goto asl_file_create_return
;
895 status
= acl_set_tag_type(entry
, ACL_EXTENDED_ALLOW
);
896 if (status
!= 0) goto asl_file_create_return
;
898 status
= acl_set_qualifier(entry
, &uuid
);
899 if (status
!= 0) goto asl_file_create_return
;
901 status
= acl_get_permset(entry
, &perms
);
902 if (status
!= 0) goto asl_file_create_return
;
904 status
= acl_add_perm(perms
, ACL_READ_DATA
);
905 if (status
!= 0) goto asl_file_create_return
;
908 for (i
= 0; i
< dst
->nuid
; i
++)
910 if (dst
->uid
[i
] == -2) continue;
913 * Don't bother setting user access if this is
914 * file's owner and the file is owner-readable.
916 if ((dst
->uid
[i
] == fuid
) && (dst
->mode
& 00400)) continue;
918 status
= mbr_uid_to_uuid(dst
->uid
[i
], uuid
);
925 status
= acl_create_entry_np(&acl
, &entry
, ACL_FIRST_ENTRY
);
926 if (status
!= 0) goto asl_file_create_return
;
928 status
= acl_set_tag_type(entry
, ACL_EXTENDED_ALLOW
);
929 if (status
!= 0) goto asl_file_create_return
;
931 status
= acl_set_qualifier(entry
, &uuid
);
932 if (status
!= 0) goto asl_file_create_return
;
934 status
= acl_get_permset(entry
, &perms
);
935 if (status
!= 0) goto asl_file_create_return
;
937 status
= acl_add_perm(perms
, ACL_READ_DATA
);
938 if (status
!= 0) goto asl_file_create_return
;
941 status
= acl_set_fd(fd
, acl
);
948 asl_file_create_return
:
952 #endif /* !TARGET_OS_IPHONE */
953 #endif /* !TARGET_OS_SIMULATOR */
956 /* create a file with acls */
958 asl_out_dst_file_create_open(asl_out_dst_data_t
*dst
, char **pathp
)
962 char outpath
[MAXPATHLEN
];
964 if (dst
== NULL
) return -1;
965 if (dst
->path
== NULL
) return -1;
967 asl_dst_make_current_name(dst
, 0, outpath
, sizeof(outpath
));
968 free(dst
->current_name
);
970 dst
->current_name
= strdup(outpath
);
971 if (dst
->current_name
== NULL
) return -1;
973 if (pathp
!= NULL
) *pathp
= strdup(outpath
);
975 memset(&sb
, 0, sizeof(struct stat
));
976 status
= stat(outpath
, &sb
);
979 /* must be a regular file */
980 if (!S_ISREG(sb
.st_mode
)) return -1;
983 fd
= open(outpath
, O_RDWR
| O_APPEND
| O_EXCL
, 0);
985 if (dst
->timestamp
== 0) dst
->timestamp
= sb
.st_birthtimespec
.tv_sec
;
986 if (dst
->timestamp
== 0) dst
->timestamp
= sb
.st_mtimespec
.tv_sec
;
987 dst
->size
= sb
.st_size
;
989 if ((dst
->flags
& MODULE_FLAG_BASESTAMP
) && (dst
->flags
& MODULE_FLAG_SYMLINK
)) symlink(outpath
, dst
->path
);
992 else if (errno
!= ENOENT
)
994 /* stat error other than non-existant file */
998 fd
= open(outpath
, O_RDWR
| O_CREAT
| O_EXCL
, (dst
->mode
& 00666));
999 if (fd
< 0) return -1;
1001 dst
->timestamp
= time(NULL
);
1003 fd
= asl_out_dst_set_access(fd
, dst
);
1004 if (fd
< 0) unlink(outpath
);
1006 if ((dst
->flags
& MODULE_FLAG_BASESTAMP
) && (dst
->flags
& MODULE_FLAG_SYMLINK
))
1008 /* remove old symlink, make a new link to the "current" file */
1010 symlink(outpath
, dst
->path
);
1017 asl_out_module_free(asl_out_module_t
*m
)
1019 asl_out_rule_t
*r
, *n
;
1020 asl_out_module_t
*x
;
1034 if (r
->dst
!= NULL
) asl_out_dst_data_release(r
->dst
);
1036 if (r
->query
!= NULL
) asl_msg_release(r
->query
);
1048 asl_out_module_new(const char *name
)
1050 asl_out_module_t
*out
= (asl_out_module_t
*)calloc(1, sizeof(asl_out_module_t
));
1052 if (out
== NULL
) return NULL
;
1053 if (name
== NULL
) return NULL
;
1055 out
->name
= strdup(name
);
1056 if (out
->name
== NULL
)
1062 out
->flags
= MODULE_FLAG_ENABLED
;
1067 /* Skip over query */
1069 _asl_out_module_find_action(char *s
)
1074 if (p
== NULL
) return NULL
;
1076 /* Skip command character (?, Q, *, or =) */
1082 while ((*p
== ' ') || (*p
== '\t')) p
++;
1084 if (*p
== '\0') return NULL
;
1085 if (*p
!= '[') return p
;
1087 /* skip to closing ] */
1101 /* skip whitespace */
1102 while ((*p
== ' ') || (*p
== '\t')) p
++;
1108 * Parse parameter setting line
1111 * evaluated once when module is initialized
1113 * = [query] param options
1114 * evaluated for each message, param set if message matches query
1116 * = param [File path]
1117 * evaluated once when module is initialized
1118 * evaluated when change notification received for path
1120 * = param [Plist path] ...
1121 * evaluated once when module is initialized
1122 * evaluated when change notification received for path
1124 * = param [Profile name] ...
1125 * evaluated once when module is initialized
1126 * evaluated when change notification received for profile
1128 static asl_out_rule_t
*
1129 _asl_out_module_parse_set_param(asl_out_module_t
*m
, char *s
)
1132 asl_out_rule_t
*out
, *rule
;
1134 if (m
== NULL
) return NULL
;
1136 out
= (asl_out_rule_t
*)calloc(1, sizeof(asl_out_rule_t
));
1137 if (out
== NULL
) return NULL
;
1140 while ((*q
== ' ') || (*q
== '\'')) q
++;
1141 out
->action
= ACTION_SET_PARAM
;
1145 /* = [query] param options */
1146 act
= _asl_out_module_find_action(s
);
1153 out
->options
= _strdup_clean(act
);
1156 if (*p
== ']') p
= act
;
1160 out
->query
= asl_msg_from_string(s
);
1161 if (out
->query
== NULL
)
1174 /* = param options */
1175 out
->options
= _strdup_clean(q
);
1179 /* = param [query] */
1180 if (streq_len(p
, "[File ", 6) || streq_len(p
, "[File\t", 6)) out
->action
= ACTION_SET_FILE
;
1181 else if (streq_len(p
, "[Plist ", 7) || streq_len(p
, "[Plist\t", 7)) out
->action
= ACTION_SET_PLIST
;
1182 else if (streq_len(p
, "[Profile ", 9) || streq_len(p
, "[Profile\t", 9)) out
->action
= ACTION_SET_PROF
;
1186 out
->options
= _strdup_clean(q
);
1191 out
->query
= asl_msg_from_string(p
);
1192 if (out
->query
== NULL
)
1201 if (m
->ruleset
== NULL
) m
->ruleset
= out
;
1204 for (rule
= m
->ruleset
; rule
->next
!= NULL
; rule
= rule
->next
);
1211 #if !TARGET_OS_SIMULATOR
1213 _dst_add_uid(asl_out_dst_data_t
*dst
, char *s
)
1218 if (dst
== NULL
) return;
1219 if (s
== NULL
) return;
1223 #if TARGET_OS_IPHONE
1226 struct passwd
* pw
= getpwnam("mobile");
1234 for (i
= 0 ; i
< dst
->nuid
; i
++)
1236 if (dst
->uid
[i
] == uid
) return;
1239 dst
->uid
= reallocf(dst
->uid
, (dst
->nuid
+ 1) * sizeof(uid_t
));
1240 if (dst
->uid
== NULL
)
1246 dst
->uid
[dst
->nuid
++] = uid
;
1250 _dst_add_gid(asl_out_dst_data_t
*dst
, char *s
)
1255 if (dst
== NULL
) return;
1256 if (s
== NULL
) return;
1260 #if TARGET_OS_IPHONE
1263 struct passwd
* pw
= getpwnam("mobile");
1271 for (i
= 0 ; i
< dst
->ngid
; i
++)
1273 if (dst
->gid
[i
] == gid
) return;
1276 dst
->gid
= reallocf(dst
->gid
, (dst
->ngid
+ 1) * sizeof(gid_t
));
1277 if (dst
->gid
== NULL
)
1283 dst
->gid
[dst
->ngid
++] = gid
;
1285 #endif /* !TARGET_OS_SIMULATOR */
1288 _dst_format_string(char *s
)
1293 if (s
== NULL
) return NULL
;
1297 /* format string can be enclosed by quotes */
1298 if ((len
>= 2) && ((s
[0] == '\'') || (s
[0] == '"')) && (s
[len
-1] == s
[0]))
1305 for (i
= 0; i
< len
; i
++) if (s
[i
] == '\\') n
++;
1307 fmt
= malloc(1 + len
- n
);
1308 if (fmt
== NULL
) return NULL
;
1310 for (i
= 0, n
= 0; i
< len
; i
++) if (s
[i
] != '\\') fmt
[n
++] = s
[i
];
1316 _dst_path_match(const char *newpath
, const char *existingpath
)
1318 if (newpath
== NULL
) return (existingpath
== NULL
);
1319 if (existingpath
== NULL
) return false;
1320 if (newpath
[0] == '/') return (strcmp(newpath
, existingpath
) == 0);
1322 const char *trailing
= strrchr(existingpath
, '/');
1323 if (trailing
== NULL
) return (strcmp(newpath
, existingpath
) == 0);
1325 return (strcmp(newpath
, trailing
) == 0);
1329 _parse_stamp_string(const char *in
)
1334 if (in
== NULL
) return 0;
1336 for (x
= 0; (((in
[x
] >= 'a') && (in
[x
] <= 'z')) || (in
[x
] == '-')) && (x
< 11); x
++) buf
[x
] = in
[x
];
1339 if (streq(buf
, "sec") || streq(buf
, "seconds")) return MODULE_NAME_STYLE_STAMP_SEC
;
1340 if (streq(buf
, "zulu") || streq(buf
, "utc")) return MODULE_NAME_STYLE_STAMP_UTC
;
1341 if (streq(buf
, "utc-b") || streq(buf
, "utc-basic")) return MODULE_NAME_STYLE_STAMP_UTC_B
;
1342 if (streq(buf
, "local") || streq(buf
, "lcl")) return MODULE_NAME_STYLE_STAMP_LCL
;
1343 if (streq(buf
, "local-b") || streq(buf
, "lcl-b") || streq(buf
, "local-basic") || streq(buf
, "lcl-basic")) return MODULE_NAME_STYLE_STAMP_LCL_B
;
1344 if (streq(buf
, "#") || streq(buf
, "seq") || streq(buf
, "sequence"))return MODULE_NAME_STYLE_STAMP_SEQ
;
1350 * Parse a file-rotation naming style.
1352 * Legacy: sec / seconds, utc / date / zulu [-b], local / lcl [-b], # / seq / sequence
1353 * We scan the whole line and match to one of these.
1355 * New scheme: 2 or 3 components: base and style, or base, style, and extension.
1356 * these define a name format. base is the file name without a leading directory path
1357 * and with no extension (e.g. "foo"). style is one of the styles above. extension is
1358 * the file name extension (e.g. "log", "txt", etc).
1365 * The leading base name may be ommitted (E.G. ".lcl.log", ".log.seq")
1366 * If the leading base name AND extension are omitted, it is taken from the path. E.G. ".lcl", ".seq"
1368 * If we get input without a stamp spec, we default to "sec".
1371 _parse_dst_style(asl_out_dst_data_t
*dst
, const char *in
)
1376 if ((dst
== NULL
) || (in
== NULL
)) return -1;
1378 /* check for base. or just . for shorthand */
1386 if (dst
->base
== NULL
) return -1;
1388 len
= strlen(dst
->base
);
1389 if (streq_len(in
, dst
->base
, len
) && (in
[len
] == '.')) p
= in
+ len
+ 1;
1394 /* input does not start with '.' or base, so this is legacy style */
1395 dst
->style_flags
= _parse_stamp_string(in
);
1396 if (dst
->style_flags
== 0) return -1;
1398 if (dst
->ext
== NULL
) dst
->style_flags
|= MODULE_NAME_STYLE_FORMAT_BS
;
1399 else dst
->style_flags
|= MODULE_NAME_STYLE_FORMAT_BES
;
1404 /* look for another dot in the name */
1405 for (q
= p
; (*q
!= '.') && (*q
!= ' ') && (*q
!= '\t') && (*q
!= '\0'); q
++);
1406 if (*q
!= '.') q
= NULL
;
1410 /* we require a stamp spec, so we are expecting base.stamp */
1411 dst
->style_flags
= _parse_stamp_string(p
);
1413 if (dst
->style_flags
== 0) return -1;
1416 * We got a valid stamp style ("base.stamp").
1417 * Note that we might have skipped the extention if the file name was "foo.log".
1418 * That's OK - syslogd writes "foo.log", but the rotated files are e.g. foo.20141018T1745Z.
1420 dst
->style_flags
|= MODULE_NAME_STYLE_FORMAT_BS
;
1424 /* set q to the char past the dot */
1427 /* either base.stamp.ext or base.ext.stamp */
1428 if (dst
->ext
== NULL
) return -1;
1430 len
= strlen(dst
->ext
);
1431 if (streq_len(p
, dst
->ext
, len
) && (p
[len
] == '.'))
1433 /* got base.ext.stamp */
1434 dst
->style_flags
= _parse_stamp_string(q
);
1435 if (dst
->style_flags
== 0) return -1;
1437 dst
->style_flags
|= MODULE_NAME_STYLE_FORMAT_BES
;
1441 /* must be base.stamp.ext */
1442 if (strneq_len(q
, dst
->ext
, len
)) return -1;
1444 dst
->style_flags
= _parse_stamp_string(p
);
1445 if (dst
->style_flags
== 0) return -1;
1447 dst
->style_flags
|= MODULE_NAME_STYLE_FORMAT_BSE
;
1451 static asl_out_dst_data_t
*
1452 _asl_out_module_parse_dst(asl_out_module_t
*m
, char *s
, mode_t def_mode
)
1454 asl_out_rule_t
*out
, *rule
;
1455 asl_out_dst_data_t
*dst
;
1456 char *p
, *dot
, *opts
, *path
;
1458 int has_dotdot
, recursion_limit
;
1459 uint32_t i
, flags
= 0;
1461 if (m
== NULL
) return NULL
;
1462 if (s
== NULL
) return NULL
;
1464 /* skip whitespace */
1465 while ((*s
== ' ') || (*s
== '\t')) s
++;
1468 path
= next_word_from_string(&opts
);
1469 if (path
== NULL
) return NULL
;
1472 * Check path for ".." component (not permitted).
1473 * Also substitute environment variables.
1476 path_parts
= explode(path
, "/");
1477 asl_string_t
*processed_path
= asl_string_new(ASL_ENCODE_NONE
);
1478 recursion_limit
= 5;
1480 while ((recursion_limit
> 0) && (path_parts
!= NULL
) && (processed_path
!= NULL
))
1485 for (i
= 0; path_parts
[i
] != NULL
; i
++)
1487 if (streq_len(path_parts
[i
], "$ENV(", 5))
1489 char *p
= strchr(path_parts
[i
], ')');
1490 if (p
!= NULL
) *p
= '\0';
1491 char *env_val
= getenv(path_parts
[i
] + 5);
1492 if (env_val
!= NULL
)
1496 if (env_val
[0] != '/') asl_string_append_char_no_encoding(processed_path
, '/');
1497 asl_string_append_no_encoding(processed_path
, env_val
);
1504 if (path_parts
[0][0] != '\0') asl_string_append_no_encoding(processed_path
, path_parts
[i
]);
1508 asl_string_append_char_no_encoding(processed_path
, '/');
1509 asl_string_append_no_encoding(processed_path
, path_parts
[i
]);
1513 if ((has_dotdot
== 0) && streq(path_parts
[i
], "..")) has_dotdot
= 1;
1516 free_string_list(path_parts
);
1519 if ((did_sub
== 1) && (has_dotdot
== 0))
1521 /* substitution might have added a ".." so check the new path */
1523 path
= asl_string_release_return_bytes(processed_path
);
1524 processed_path
= asl_string_new(ASL_ENCODE_NONE
);
1525 path_parts
= explode(path
, "/");
1531 free_string_list(path_parts
);
1534 if ((has_dotdot
!= 0) || (recursion_limit
== 0))
1536 asl_string_release(processed_path
);
1540 path
= asl_string_release_return_bytes(processed_path
);
1542 /* check if there's already a dst for this path */
1543 for (rule
= m
->ruleset
; rule
!= NULL
; rule
= rule
->next
)
1545 if (rule
->action
!= ACTION_OUT_DEST
) continue;
1548 if (dst
== NULL
) continue;
1550 if (_dst_path_match(path
, dst
->path
))
1557 flags
|= MODULE_FLAG_NONSTD_DIR
;
1562 const char *log_root
= "/var/log";
1564 #if TARGET_OS_SIMULATOR
1565 log_root
= getenv("SIMULATOR_LOG_ROOT");
1566 if (log_root
== NULL
) log_root
= "/tmp/log";
1569 if (streq(m
->name
, ASL_MODULE_NAME
))
1571 asprintf(&path
, "%s/%s", log_root
, t
);
1575 asprintf(&path
, "%s/module/%s/%s", log_root
, m
->name
, t
);
1579 flags
&= ~MODULE_FLAG_NONSTD_DIR
;
1584 * Standard log directories get marked so that syslogd
1585 * will create them without explicit rules.
1587 if (streq_len(path
, PATH_VAR_LOG
, PATH_VAR_LOG_LEN
)) flags
&= ~MODULE_FLAG_NONSTD_DIR
;
1588 else if (streq_len(path
, PATH_LIBRARY_LOGS
, PATH_LIBRARY_LOGS_LEN
)) flags
&= ~MODULE_FLAG_NONSTD_DIR
;
1591 out
= (asl_out_rule_t
*)calloc(1, sizeof(asl_out_rule_t
));
1592 dst
= (asl_out_dst_data_t
*)calloc(1, sizeof(asl_out_dst_data_t
));
1593 if ((out
== NULL
) || (dst
== NULL
))
1604 p
= strrchr(dst
->path
, '/');
1608 dst
->dir
= strdup(dst
->path
);
1612 dst
->mode
= def_mode
;
1613 dst
->ttl
[LEVEL_ALL
] = DEFAULT_TTL
;
1614 dst
->flags
= flags
| MODULE_FLAG_COALESCE
;
1617 * Break out base and extension (if present) from path.
1618 * Note this only supports a '.' as a separator.
1620 p
= strrchr(path
, '/');
1621 if (p
== NULL
) p
= path
;
1624 dot
= strrchr(path
, '.');
1628 dst
->ext
= strdup(dot
+ 1);
1631 dst
->base
= strdup(p
);
1632 if (dot
!= NULL
) *dot
= '.';
1634 while (NULL
!= (p
= next_word_from_string(&opts
)))
1636 if (KEYMATCH(p
, "mode=")) dst
->mode
= strtol(p
+5, NULL
, 0);
1637 #if !TARGET_OS_SIMULATOR
1638 else if (KEYMATCH(p
, "uid=")) _dst_add_uid(dst
, p
+4);
1639 else if (KEYMATCH(p
, "gid=")) _dst_add_gid(dst
, p
+4);
1641 else if (KEYMATCH(p
, "fmt=")) dst
->fmt
= _dst_format_string(p
+4);
1642 else if (KEYMATCH(p
, "format=")) dst
->fmt
= _dst_format_string(p
+7);
1643 else if (KEYMATCH(p
, "dest=")) dst
->rotate_dir
= _strdup_clean(p
+5);
1644 else if (KEYMATCH(p
, "dst=")) dst
->rotate_dir
= _strdup_clean(p
+4);
1645 else if (KEYMATCH(p
, "coalesce="))
1647 if (KEYMATCH(p
+9, "0")) dst
->flags
&= ~MODULE_FLAG_COALESCE
;
1648 else if (KEYMATCH(p
+9, "off")) dst
->flags
&= ~MODULE_FLAG_COALESCE
;
1649 else if (KEYMATCH(p
+9, "false")) dst
->flags
&= ~MODULE_FLAG_COALESCE
;
1651 else if (KEYMATCH(p
, "compress")) dst
->flags
|= MODULE_FLAG_COMPRESS
;
1652 else if (KEYMATCH(p
, "extern")) dst
->flags
|= MODULE_FLAG_EXTERNAL
;
1653 else if (KEYMATCH(p
, "truncate")) dst
->flags
|= MODULE_FLAG_TRUNCATE
;
1654 else if (KEYMATCH(p
, "dir")) dst
->flags
|= MODULE_FLAG_TYPE_ASL_DIR
;
1655 else if (KEYMATCH(p
, "soft")) dst
->flags
|= MODULE_FLAG_SOFT_WRITE
;
1656 else if (KEYMATCH(p
, "file_max=")) dst
->file_max
= asl_core_str_to_size(p
+9);
1657 else if (KEYMATCH(p
, "all_max=")) dst
->all_max
= asl_core_str_to_size(p
+8);
1658 else if (KEYMATCH(p
, "style=") || KEYMATCH(p
, "rotate="))
1660 const char *x
= p
+ 6;
1663 if (_parse_dst_style(dst
, x
) == 0) dst
->flags
|= MODULE_FLAG_ROTATE
;
1665 else if (KEYMATCH(p
, "rotate"))
1667 if (dst
->ext
== NULL
) dst
->style_flags
= MODULE_NAME_STYLE_FORMAT_BS
| MODULE_NAME_STYLE_STAMP_SEC
;
1668 else dst
->style_flags
= MODULE_NAME_STYLE_FORMAT_BES
| MODULE_NAME_STYLE_STAMP_SEC
;
1670 dst
->flags
|= MODULE_FLAG_ROTATE
;
1672 else if (KEYMATCH(p
, "crashlog"))
1674 /* crashlog implies rotation */
1675 dst
->flags
|= MODULE_FLAG_ROTATE
;
1676 dst
->flags
|= MODULE_FLAG_CRASHLOG
;
1677 dst
->flags
|= MODULE_FLAG_BASESTAMP
;
1678 dst
->flags
&= ~MODULE_FLAG_COALESCE
;
1680 else if (KEYMATCH(p
, "basestamp"))
1682 dst
->flags
|= MODULE_FLAG_BASESTAMP
;
1684 else if (KEYMATCH(p
, "link") || KEYMATCH(p
, "symlink"))
1686 dst
->flags
|= MODULE_FLAG_SYMLINK
;
1688 else if (KEYMATCH(p
, "ttl"))
1693 dst
->ttl
[LEVEL_ALL
] = asl_core_str_to_time(p
+4, SECONDS_PER_DAY
);
1695 else if ((*q
>= '0') && (*q
<= '7') && (*(q
+1) == '='))
1697 uint32_t x
= *q
- '0';
1698 dst
->ttl
[x
] = asl_core_str_to_time(p
+5, SECONDS_PER_DAY
);
1701 else if (KEYMATCH(p
, "size_only"))
1703 dst
->flags
|= MODULE_FLAG_SIZE_ONLY
;
1710 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
1711 /* check for crashreporter files */
1712 if ((KEYMATCH(dst
->path
, _PATH_CRASHREPORTER
)) || (KEYMATCH(dst
->path
, _PATH_CRASHREPORTER_MOBILE_1
)) || (KEYMATCH(dst
->path
, _PATH_CRASHREPORTER_MOBILE_2
)))
1714 dst
->flags
|= MODULE_FLAG_ROTATE
;
1715 dst
->flags
|= MODULE_FLAG_CRASHLOG
;
1716 dst
->flags
|= MODULE_FLAG_BASESTAMP
;
1717 dst
->flags
&= ~MODULE_FLAG_COALESCE
;
1721 /* ttl[LEVEL_ALL] must be max of all level-specific ttls */
1722 for (i
= 0; i
<= 7; i
++) if (dst
->ttl
[i
] > dst
->ttl
[LEVEL_ALL
]) dst
->ttl
[LEVEL_ALL
] = dst
->ttl
[i
];
1724 /* default text file format is "std" */
1725 if (dst
->fmt
== NULL
) dst
->fmt
= strdup("std");
1727 /* duplicate compression is only possible for std and bsd formats */
1728 if (strcmp(dst
->fmt
, "std") && strcmp(dst
->fmt
, "bsd")) dst
->flags
&= ~MODULE_FLAG_COALESCE
;
1730 /* note if format is one of std, bsd, or msg */
1731 if (streq(dst
->fmt
, "std") || streq(dst
->fmt
, "bsd") || streq(dst
->fmt
, "msg")) dst
->flags
|= MODULE_FLAG_STD_BSD_MSG
;
1733 /* MODULE_NAME_STYLE_STAMP_SEQ can not be used with MODULE_FLAG_BASESTAMP */
1734 if ((dst
->flags
& MODULE_FLAG_BASESTAMP
) && (dst
->flags
& MODULE_NAME_STYLE_STAMP_SEQ
))
1736 dst
->flags
&= ~MODULE_NAME_STYLE_STAMP_SEQ
;
1737 dst
->flags
|= MODULE_NAME_STYLE_STAMP_SEC
;
1740 /* set time format for raw output */
1741 if (streq(dst
->fmt
, "raw")) dst
->tfmt
= "sec";
1743 /* check for ASL_PLACE_DATABASE_DEFAULT */
1744 if (streq(dst
->path
, ASL_PLACE_DATABASE_DEFAULT
))
1746 dst
->flags
= MODULE_FLAG_TYPE_ASL_DIR
;
1749 /* set file_max to all_max if it is zero */
1750 if (dst
->file_max
== 0) dst
->file_max
= dst
->all_max
;
1752 /* size_only option requires non-zero file_max and all_max settings */
1753 if ((dst
->flags
& MODULE_FLAG_SIZE_ONLY
) && (dst
->file_max
== 0 || dst
->all_max
== 0))
1755 dst
->flags
&= ~MODULE_FLAG_SIZE_ONLY
;
1758 /* size_only option cannot be used with crashlog option */
1759 if ((dst
->flags
& MODULE_FLAG_SIZE_ONLY
) && (dst
->flags
& MODULE_FLAG_CRASHLOG
))
1761 dst
->flags
&= ~MODULE_FLAG_SIZE_ONLY
;
1764 out
->action
= ACTION_OUT_DEST
;
1767 /* dst rules go first */
1768 out
->next
= m
->ruleset
;
1774 static asl_out_rule_t
*
1775 _asl_out_module_parse_query_action(asl_out_module_t
*m
, char *s
)
1778 asl_out_rule_t
*out
, *rule
;
1780 if (m
== NULL
) return NULL
;
1782 out
= (asl_out_rule_t
*)calloc(1, sizeof(asl_out_rule_t
));
1783 if (out
== NULL
) return NULL
;
1785 act
= _asl_out_module_find_action(s
);
1786 if (act
== NULL
) return NULL
;
1788 /* find whitespace delimiter */
1789 p
= strchr(act
, ' ');
1790 if (p
== NULL
) p
= strchr(act
, '\t');
1791 if (p
!= NULL
) *p
= '\0';
1793 if (strcaseeq(act
, "ignore")) out
->action
= ACTION_IGNORE
;
1794 else if (strcaseeq(act
, "skip")) out
->action
= ACTION_SKIP
;
1795 else if (strcaseeq(act
, "claim")) out
->action
= ACTION_CLAIM
;
1796 else if (strcaseeq(act
, "notify")) out
->action
= ACTION_NOTIFY
;
1797 else if (strcaseeq(act
, "file")) out
->action
= ACTION_FILE
;
1798 else if (strcaseeq(act
, "asl_file")) out
->action
= ACTION_ASL_FILE
;
1799 else if (strcaseeq(act
, "directory")) out
->action
= ACTION_ASL_DIR
;
1800 else if (strcaseeq(act
, "dir")) out
->action
= ACTION_ASL_DIR
;
1801 else if (strcaseeq(act
, "asl_directory")) out
->action
= ACTION_ASL_DIR
;
1802 else if (strcaseeq(act
, "asl_dir")) out
->action
= ACTION_ASL_DIR
;
1803 else if (strcaseeq(act
, "store_dir")) out
->action
= ACTION_ASL_DIR
;
1804 else if (strcaseeq(act
, "store_directory")) out
->action
= ACTION_ASL_DIR
;
1805 else if (strcaseeq(act
, "control")) out
->action
= ACTION_CONTROL
;
1806 else if (strcaseeq(act
, "save")) out
->action
= ACTION_ASL_STORE
;
1807 else if (strcaseeq(act
, "store")) out
->action
= ACTION_ASL_STORE
;
1808 else if (strcaseeq(act
, "access")) out
->action
= ACTION_ACCESS
;
1809 else if (strcaseeq(act
, "set")) out
->action
= ACTION_SET_KEY
;
1810 else if (strcaseeq(act
, "unset")) out
->action
= ACTION_UNSET_KEY
;
1811 else if (streq(m
->name
, ASL_MODULE_NAME
))
1813 /* actions only allowed in com.apple.asl */
1814 if (strcaseeq(act
, "broadcast")) out
->action
= ACTION_BROADCAST
;
1815 else if (strcaseeq(act
, "forward")) out
->action
= ACTION_FORWARD
;
1818 if (out
->action
== ACTION_NONE
)
1824 /* options follow delimited (now zero) */
1827 /* skip whitespace */
1828 while ((*p
== ' ') || (*p
== '\t')) p
++;
1830 out
->options
= _strdup_clean(p
+1);
1832 if (out
->options
== NULL
)
1845 out
->query
= asl_msg_new(ASL_TYPE_QUERY
);
1850 out
->query
= asl_msg_from_string(s
);
1853 if (out
->query
== NULL
)
1860 /* store /some/path means save to an asl file */
1861 if (out
->action
== ACTION_ASL_STORE
)
1863 if (out
->options
== NULL
) out
->dst
= asl_out_dst_data_retain(_asl_out_module_parse_dst(m
, ASL_PLACE_DATABASE_DEFAULT
, 0755));
1864 else if (streq_len(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));
1865 else if (out
->options
!= NULL
) out
->action
= ACTION_ASL_FILE
;
1868 if ((out
->action
== ACTION_FILE
) || (out
->action
== ACTION_ASL_FILE
) || (out
->action
== ACTION_ASL_DIR
))
1870 mode_t def_mode
= 0644;
1871 if (out
->action
== ACTION_ASL_DIR
) def_mode
= 0755;
1873 out
->dst
= asl_out_dst_data_retain(_asl_out_module_parse_dst(m
, out
->options
, def_mode
));
1874 if (out
->dst
== NULL
)
1876 out
->action
= ACTION_NONE
;
1881 * dst might have been set up by a previous ACTION_OUT_DEST ('>') rule with no mode.
1882 * If so, mode would be 010000. Set it now, since we know whether it is a file or dir.
1884 if (out
->dst
->mode
== 010000) out
->dst
->mode
= def_mode
;
1886 if ((out
->action
== ACTION_FILE
) && (out
->dst
!= NULL
) && (out
->dst
->fmt
!= NULL
) && (strcaseeq(out
->dst
->fmt
, "asl")))
1888 out
->action
= ACTION_ASL_FILE
;
1891 if ((out
->action
== ACTION_ASL_FILE
) && (out
->dst
!= NULL
))
1893 out
->dst
->flags
|= MODULE_FLAG_TYPE_ASL
;
1896 if (out
->action
== ACTION_ASL_DIR
)
1898 /* coalesce is meaningless for ASL directories */
1899 out
->dst
->flags
&= ~MODULE_FLAG_COALESCE
;
1901 out
->dst
->flags
|= MODULE_FLAG_TYPE_ASL_DIR
;
1903 /* set style bits for basestamp asl_dirs */
1904 if (((out
->dst
->style_flags
& MODULE_NAME_STYLE_STAMP_MASK
) == 0) && (out
->dst
->flags
& MODULE_FLAG_BASESTAMP
)) out
->dst
->style_flags
|= MODULE_NAME_STYLE_STAMP_LCL_B
;
1907 /* only ACTION_FILE and ACTION_ASL_FILE may rotate */
1908 if ((out
->action
!= ACTION_FILE
) && (out
->action
!= ACTION_ASL_FILE
))
1910 out
->dst
->flags
&= ~MODULE_FLAG_ROTATE
;
1913 #if !TARGET_OS_SIMULATOR
1914 if (out
->dst
->nuid
== 0) _dst_add_uid(out
->dst
, "0");
1915 if (out
->dst
->ngid
== 0) _dst_add_gid(out
->dst
, "80");
1919 if (m
->ruleset
== NULL
) m
->ruleset
= out
;
1922 for (rule
= m
->ruleset
; rule
->next
!= NULL
; rule
= rule
->next
);
1930 asl_out_module_parse_line(asl_out_module_t
*m
, char *s
)
1932 while ((*s
== ' ') || (*s
== '\t')) s
++;
1934 if ((*s
== 'Q') || (*s
== '?') || (*s
== '*'))
1936 return _asl_out_module_parse_query_action(m
, s
);
1940 return _asl_out_module_parse_set_param(m
, s
);
1944 _asl_out_module_parse_dst(m
, s
+ 1, 010000);
1951 asl_out_module_init_from_file(const char *name
, FILE *f
)
1953 asl_out_module_t
*out
;
1956 if (f
== NULL
) return NULL
;
1958 out
= asl_out_module_new(name
);
1959 if (out
== NULL
) return NULL
;
1961 /* read and parse config file */
1962 while (NULL
!= (line
= get_line_from_file(f
)))
1964 asl_out_module_parse_line(out
, line
);
1971 static asl_out_module_t
*
1972 _asl_out_module_find(asl_out_module_t
*list
, const char *name
)
1974 asl_out_module_t
*x
;
1976 if (list
== NULL
) return NULL
;
1977 if (name
== NULL
) return NULL
;
1979 for (x
= list
; x
!= NULL
; x
= x
->next
)
1981 if ((x
->name
!= NULL
) && (streq(x
->name
, name
))) return x
;
1988 _asl_out_module_read_and_merge_dir(asl_out_module_t
**list
, const char *path
, uint32_t flags
)
1993 asl_out_module_t
*last
, *x
;
1995 if (list
== NULL
) return;
1996 if (path
== NULL
) return;
2001 while (last
->next
!= NULL
) last
= last
->next
;
2007 while (NULL
!= (ent
= readdir(d
)))
2009 if (ent
->d_name
[0] != '.')
2011 /* merge: skip this file if we already have a module with this name */
2012 if (_asl_out_module_find(*list
, ent
->d_name
) != NULL
) continue;
2014 char tmp
[MAXPATHLEN
];
2015 snprintf(tmp
, sizeof(tmp
), "%s/%s", path
, ent
->d_name
);
2016 f
= fopen(tmp
, "r");
2019 x
= asl_out_module_init_from_file(ent
->d_name
, f
);
2026 if (streq(ent
->d_name
, ASL_MODULE_NAME
))
2028 /* com.apple.asl goes at the head of the list */
2031 if (last
== NULL
) last
= *list
;
2033 else if (*list
== NULL
)
2053 asl_out_module_init(void)
2055 asl_out_module_t
*out
= NULL
;
2057 #if TARGET_OS_SIMULATOR
2058 char *sim_root_path
, *sim_resources_path
;
2059 char *asl_conf
, *asl_conf_dir
, *asl_conf_local_dir
;
2061 sim_root_path
= getenv("IPHONE_SIMULATOR_ROOT");
2062 if (sim_root_path
== NULL
) return NULL
;
2064 sim_resources_path
= getenv("IPHONE_SHARED_RESOURCES_DIRECTORY");
2065 if (sim_resources_path
== NULL
) return NULL
;
2067 asprintf(&asl_conf
, "%s%s", sim_root_path
, _PATH_ASL_CONF
);
2068 asprintf(&asl_conf_dir
, "%s%s", sim_root_path
, _PATH_ASL_CONF_DIR
);
2069 asprintf(&asl_conf_local_dir
, "%s%s", sim_resources_path
, _PATH_ASL_CONF_DIR
);
2071 _asl_out_module_read_and_merge_dir(&out
, asl_conf_local_dir
, MODULE_FLAG_LOCAL
);
2072 free(asl_conf_local_dir
);
2074 _asl_out_module_read_and_merge_dir(&out
, asl_conf_dir
, 0);
2077 _asl_out_module_read_and_merge_dir(&out
, _PATH_ASL_CONF_LOCAL_DIR
, MODULE_FLAG_LOCAL
);
2078 _asl_out_module_read_and_merge_dir(&out
, _PATH_ASL_CONF_DIR
, 0);
2081 if (_asl_out_module_find(out
, ASL_MODULE_NAME
) == NULL
)
2083 /* system just has old-style /etc/asl.conf */
2084 #if TARGET_OS_SIMULATOR
2085 FILE *f
= fopen(asl_conf
, "r");
2088 FILE *f
= fopen(_PATH_ASL_CONF
, "r");
2092 asl_out_module_t
*x
= asl_out_module_init_from_file(ASL_MODULE_NAME
, f
);
2109 asl_out_module_rule_to_string(asl_out_rule_t
*r
)
2116 asprintf(&out
, "NULL rule");
2120 str
= asl_msg_to_string(r
->query
, &len
);
2122 asprintf(&out
, " %s%s%s%s%s",
2123 asl_out_action_name
[r
->action
],
2124 (r
->query
== NULL
) ? "" : " ",
2125 (r
->query
== NULL
) ? "" : str
,
2126 (r
->options
== NULL
) ? "" : " ",
2127 (r
->options
== NULL
) ? "" : r
->options
);
2134 _stamp_style_name(uint32_t flags
)
2136 if (flags
& MODULE_NAME_STYLE_STAMP_SEC
) return "<seconds>";
2137 if (flags
& MODULE_NAME_STYLE_STAMP_SEQ
) return "<sequence>";
2138 if (flags
& MODULE_NAME_STYLE_STAMP_UTC
) return "<utc>";
2139 if (flags
& MODULE_NAME_STYLE_STAMP_UTC_B
) return "<utc-basic>";
2140 if (flags
& MODULE_NAME_STYLE_STAMP_LCL
) return "<local>";
2141 if (flags
& MODULE_NAME_STYLE_STAMP_LCL_B
) return "<local-basic>";
2149 asl_out_module_print(FILE *f
, asl_out_module_t
*m
)
2151 asl_out_rule_t
*r
, *n
;
2152 asl_out_dst_data_t
*o
;
2153 uint32_t i
, ttlnset
;
2157 for (r
= m
->ruleset
; r
!= NULL
; r
= n
)
2160 char *str
= asl_msg_to_string(r
->query
, &len
);
2162 fprintf(f
, " %s", asl_out_action_name
[r
->action
]);
2163 if (r
->query
!= NULL
) fprintf(f
, " %s", str
);
2164 if (r
->options
!= NULL
) fprintf(f
, " %s", r
->options
);
2165 if (r
->action
== ACTION_OUT_DEST
)
2170 fprintf(f
, " data: NULL");
2174 fprintf(f
, "%s\n", o
->path
);
2175 fprintf(f
, " rules: %u\n", o
->refcount
- 1);
2176 fprintf(f
, " dest: %s\n", (o
->rotate_dir
== NULL
) ? "(none)" : o
->rotate_dir
);
2177 fprintf(f
, " format: %s\n", (o
->fmt
== NULL
) ? "std" : o
->fmt
);
2178 fprintf(f
, " time_format: %s\n", (o
->tfmt
== NULL
) ? "lcl" : o
->tfmt
);
2179 fprintf(f
, " flags: 0x%08x", o
->flags
);
2184 if (o
->flags
& MODULE_FLAG_ENABLED
)
2186 fprintf(f
, "%cenabled", c
);
2189 if (o
->flags
& MODULE_FLAG_SOFT_WRITE
)
2191 fprintf(f
, "%csoft", c
);
2194 if (o
->flags
& MODULE_FLAG_ROTATE
)
2196 fprintf(f
, "%crotate", c
);
2199 if (o
->flags
& MODULE_FLAG_COALESCE
)
2201 fprintf(f
, "%ccoalesce", c
);
2204 if (o
->flags
& MODULE_FLAG_COMPRESS
)
2206 fprintf(f
, "%ccompress", c
);
2209 if (o
->flags
& MODULE_FLAG_BASESTAMP
)
2211 fprintf(f
, "%cbasestamp", c
);
2214 if (o
->flags
& MODULE_FLAG_SYMLINK
)
2216 fprintf(f
, "%csymlink", c
);
2219 if (o
->flags
& MODULE_FLAG_NONSTD_DIR
)
2221 fprintf(f
, "%cnon-std_dir", c
);
2224 if (o
->flags
& MODULE_FLAG_EXTERNAL
)
2226 fprintf(f
, "%cexternal", c
);
2229 if (o
->flags
& MODULE_FLAG_CRASHLOG
)
2231 fprintf(f
, "%ccrashlog", c
);
2234 if (o
->flags
& MODULE_FLAG_TYPE_ASL
)
2236 fprintf(f
, "%casl_file", c
);
2239 if (o
->flags
& MODULE_FLAG_TYPE_ASL_DIR
)
2241 fprintf(f
, "%casl_directory", c
);
2244 if (o
->flags
& MODULE_FLAG_SIZE_ONLY
)
2246 fprintf(f
, "%csize_only", c
);
2253 if (o
->flags
& MODULE_FLAG_ROTATE
)
2255 fprintf(f
, " rotatation style: ");
2256 if (o
->style_flags
& MODULE_NAME_STYLE_FORMAT_BS
)
2258 fprintf(f
, "[base=%s].%s\n", o
->base
, _stamp_style_name(o
->style_flags
));
2260 else if (o
->style_flags
& MODULE_NAME_STYLE_FORMAT_BES
)
2262 fprintf(f
, "[base=%s].[ext=%s].%s\n", o
->base
, o
->ext
, _stamp_style_name(o
->style_flags
));
2264 else if (o
->style_flags
& MODULE_NAME_STYLE_FORMAT_BSE
)
2266 fprintf(f
, "[base=%s].%s.[ext=%s]\n", o
->base
, _stamp_style_name(o
->style_flags
), o
->ext
);
2270 fprintf(f
, "0x%08x\n", o
->style_flags
);
2274 asl_core_time_to_str(o
->ttl
[LEVEL_ALL
], tstr
, sizeof(tstr
));
2275 fprintf(f
, " ttl: %s\n", tstr
);
2278 for (i
= 0; (i
<= 7) & (ttlnset
== 0); i
++) if (o
->ttl
[i
] != 0) ttlnset
= 1;
2281 for (i
= 0; i
<= 7; i
++)
2283 time_t x
= o
->ttl
[i
];
2284 if (x
== 0) x
= o
->ttl
[LEVEL_ALL
];
2285 asl_core_time_to_str(x
, tstr
, sizeof(tstr
));
2287 fprintf(f
, " [%d %s]", i
, tstr
);
2293 fprintf(f
, " mode: 0%o\n", o
->mode
);
2294 fprintf(f
, " file_max: %lu\n", o
->file_max
);
2295 fprintf(f
, " all_max: %lu\n", o
->all_max
);
2296 #if !TARGET_OS_SIMULATOR
2297 fprintf(f
, " uid:");
2298 for (i
= 0; i
< o
->nuid
; i
++) fprintf(f
, " %d", o
->uid
[i
]);
2300 fprintf(f
, " gid:");
2301 for (i
= 0; i
< o
->ngid
; i
++) fprintf(f
, " %d", o
->gid
[i
]);
2314 asl_out_file_list_free(asl_out_file_list_t
*l
)
2316 asl_out_file_list_t
*n
;
2318 if (l
== NULL
) return;
2330 * Checks input name for one of the forms:
2331 * MODULE_NAME_STYLE_FORMAT_BS base (src only) or base.stamp[.gz]
2332 * MODULE_NAME_STYLE_FORMAT_BES base.ext (src only) or base.ext.stamp[.gz]
2333 * MODULE_NAME_STYLE_FORMAT_BSE base.ext (src only) or base.stamp.ext[.gz]
2335 * name == base[.ext] is allowed if src is true.
2336 * base.gz is not allowed.
2337 * Output parameter stamp must be freed by caller.
2340 _check_file_name(const char *name
, const char *base
, const char *ext
, uint32_t flags
, bool src
, char **stamp
)
2342 size_t baselen
, extlen
;
2346 #ifdef DEBUG_LIST_FILES
2347 fprintf(stderr
, "_check_file_name name=%s base=%s ext=%s flags=0x%08x %s\n", name
, base
, (ext
== NULL
) ? "(NULL)" : ext
, flags
, src
? "src" : "dst");
2350 if (name
== NULL
) return false;
2351 if (base
== NULL
) return false;
2353 baselen
= strlen(base
);
2354 if (baselen
== 0) return false;
2357 if (ext
!= NULL
) extlen
= strlen(ext
);
2359 if (stamp
!= NULL
) *stamp
= NULL
;
2361 if (strneq_len(name
, base
, baselen
))
2363 #ifdef DEBUG_LIST_FILES
2364 fprintf(stderr
, " base match failed [%u]\n", __LINE__
);
2369 z
= strrchr(name
, '.');
2370 if ((z
!= NULL
) && streq(z
, ".gz")) isgz
= true;
2372 #ifdef DEBUG_LIST_FILES
2373 fprintf(stderr
, "z=%s isgz=%s\n", (z
== NULL
) ? "NULL" : z
, isgz
? "true" : "false");
2378 if (flags
& MODULE_NAME_STYLE_FORMAT_BS
)
2380 #ifdef DEBUG_LIST_FILES
2381 fprintf(stderr
, " MODULE_NAME_STYLE_FORMAT_BS\n");
2385 /* name == base OK if src is true */
2386 #ifdef DEBUG_LIST_FILES
2387 fprintf(stderr
, " name == base %s [%u]\n", src
? "OK" : "failed", __LINE__
);
2392 /* expecting p == .stamp[.gz] */
2395 #ifdef DEBUG_LIST_FILES
2396 fprintf(stderr
, " expecting dot - failed [%u]\n", __LINE__
);
2405 /* base.gz is not allowed */
2406 #ifdef DEBUG_LIST_FILES
2407 fprintf(stderr
, " got base.gz - failed [%u]\n", __LINE__
);
2412 /* got base.stamp[.gz] */
2416 char *x
= strchr(*stamp
, '.');
2417 if (x
!= NULL
) *x
= '\0';
2420 #ifdef DEBUG_LIST_FILES
2421 fprintf(stderr
, " got base.stamp%s - OK\n", isgz
? ".gz" : "");
2425 else if (flags
& MODULE_NAME_STYLE_FORMAT_BES
)
2427 #ifdef DEBUG_LIST_FILES
2428 fprintf(stderr
, " MODULE_NAME_STYLE_FORMAT_BES\n");
2432 #ifdef DEBUG_LIST_FILES
2433 fprintf(stderr
, " expecting dot - failed [%u]\n", __LINE__
);
2440 if (strneq_len(p
, ext
, extlen
))
2442 #ifdef DEBUG_LIST_FILES
2443 fprintf(stderr
, " ext match failed [%u]\n", __LINE__
);
2448 /* expecting p == .ext[.stamp][.gz] */
2453 /* name == base.ext OK if src is true */
2454 #ifdef DEBUG_LIST_FILES
2455 fprintf(stderr
, " name == base.ext %s [%u]\n", src
? "OK" : "failed", __LINE__
);
2460 /* expecting p == .stamp[.gz] */
2463 #ifdef DEBUG_LIST_FILES
2464 fprintf(stderr
, " expecting dot - failed [%u]\n", __LINE__
);
2473 /* base.ext.gz is not allowed */
2474 #ifdef DEBUG_LIST_FILES
2475 fprintf(stderr
, " got base.ext.gz - failed [%u]\n", __LINE__
);
2480 /* got base.ext.stamp[.gz] */
2484 char *x
= strchr(*stamp
, '.');
2485 if (x
!= NULL
) *x
= '\0';
2488 #ifdef DEBUG_LIST_FILES
2489 fprintf(stderr
, " got base.ext.stamp%s - OK\n", isgz
? ".gz" : "");
2493 else if (flags
& MODULE_NAME_STYLE_FORMAT_BSE
)
2495 #ifdef DEBUG_LIST_FILES
2496 fprintf(stderr
, " MODULE_NAME_STYLE_FORMAT_BSE name=%s base=%s ext=%s flags=0x%08x %s\n", name
, base
, (ext
== NULL
) ? "(NULL)" : ext
, flags
, src
? "src" : "dst");
2501 #ifdef DEBUG_LIST_FILES
2502 fprintf(stderr
, " expecting dot - failed [%u]\n", __LINE__
);
2509 if (streq_len(p
, ext
, extlen
))
2514 /* name == base.ext OK if src is true */
2515 #ifdef DEBUG_LIST_FILES
2516 fprintf(stderr
, " name == base.ext %s [%u]\n", src
? "OK" : "failed", __LINE__
);
2522 /* expecting p == stamp.ext[.gz] */
2526 if (strneq_len(z
- extlen
, ext
, extlen
))
2528 #ifdef DEBUG_LIST_FILES
2529 fprintf(stderr
, " ext match (%s) isgz failed [%u]\n", z
-extlen
, __LINE__
);
2536 if (strneq_len(z
+ 1, ext
, extlen
))
2538 #ifdef DEBUG_LIST_FILES
2539 fprintf(stderr
, " ext match (%s) failed [%u]\n", z
, __LINE__
);
2545 /* got base.stamp.ext[.gz] */
2549 char *x
= strchr(*stamp
, '.');
2550 if (x
!= NULL
) *x
= '\0';
2553 #ifdef DEBUG_LIST_FILES
2554 fprintf(stderr
, " got base.stamp.ext%s - OK\n", isgz
? ".gz" : "");
2559 #ifdef DEBUG_LIST_FILES
2560 fprintf(stderr
, " unknown format - failed\n");
2567 * Find files in a directory (dir) that all have a common prefix (base).
2568 * Bits in flags further control the search.
2570 * MODULE_NAME_STYLE_STAMP_SEQ means a numeric sequence number is expected, although not required.
2571 * E.g. foo.log foo.log.0
2573 * MODULE_NAME_STYLE_STAMP_SEC also means a numeric sequence number is required following an 'T' character.
2574 * The numeric value is the file's timestamp in seconds. E.g foo.log.T1335200452
2576 * MODULE_NAME_STYLE_STAMP_UTC requires a date/time component as the file's timestamp.
2577 * E.g. foo.2012-04-06T15:30:00Z
2579 * MODULE_NAME_STYLE_STAMP_UTC_B requires a date/time component as the file's timestamp.
2580 * E.g. foo.20120406T153000Z
2582 * MODULE_NAME_STYLE_STAMP_LCL requires a date/time component as the file's timestamp.
2583 * E.g. foo.2012-04-06T15:30:00-7
2585 * MODULE_NAME_STYLE_STAMP_LCL_B requires a date/time component as the file's timestamp.
2586 * E.g. foo.20120406T153000-07
2589 _parse_stamp_style(char *stamp
, uint32_t flags
, uint32_t *sp
, time_t *tp
)
2596 long utc_offset
= 0;
2599 /* check for NULL (no stamp) */
2600 if (stamp
== NULL
) return STAMP_STYLE_NULL
;
2602 /* check for MODULE_NAME_STYLE_STAMP_SEC (foo.T12345678) */
2603 if (stamp
[0] == 'T')
2605 n
= atoi(stamp
+ 1);
2606 if ((n
== 0) && strcmp(stamp
+ 1, "0")) return STAMP_STYLE_INVALID
;
2607 if (tp
!= NULL
) *tp
= (time_t)n
;
2609 return STAMP_STYLE_SEC
;
2612 /* check for MODULE_NAME_STYLE_STAMP_SEQ (foo.0 or foo.2.gz) */
2614 for (i
= 0; digits
&& (stamp
[i
] != '\0'); i
++) digits
= (stamp
[i
] >= '0') && (stamp
[i
] <= '9');
2616 if (!digits
&& (streq(stamp
+ i
, ".gz"))) digits
= true;
2621 if (sp
!= NULL
) *sp
= (uint32_t)n
;
2622 return STAMP_STYLE_SEQ
;
2625 /* check for MODULE_NAME_STYLE_STAMP_UTC, UTC_B, LCL, or LCL_B */
2626 memset(&t
, 0, sizeof(t
));
2630 if ((flags
& MODULE_NAME_STYLE_STAMP_UTC
) || (flags
& MODULE_NAME_STYLE_STAMP_LCL
))
2632 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
);
2634 else if ((flags
& MODULE_NAME_STYLE_STAMP_UTC_B
) || (flags
& MODULE_NAME_STYLE_STAMP_LCL_B
))
2636 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
);
2640 #ifdef DEBUG_LIST_FILES
2641 fprintf(stderr
, "_parse_stamp_style fail %u\n", __LINE__
);
2643 return STAMP_STYLE_INVALID
;
2648 #ifdef DEBUG_LIST_FILES
2649 fprintf(stderr
, "_parse_stamp_style fail %u\n", __LINE__
);
2651 return STAMP_STYLE_INVALID
;
2658 else if ((zone
== '-') || (zone
== '+'))
2660 if (n
>= 8) utc_offset
+= (3600 * h
);
2661 if (n
>= 9) utc_offset
+= (60 * m
);
2662 if (n
== 10) utc_offset
+= s
;
2663 if (zone
== '+') utc_offset
*= -1;
2665 else if ((zone
>= 'A') && (zone
<= 'Z'))
2667 if (zone
< 'J') utc_offset
= 3600 * ((zone
- 'A') + 1);
2668 else if ((zone
>= 'K') && (zone
<= 'M')) utc_offset
= 3600 * (zone
- 'A');
2669 else if (zone
<= 'Y') utc_offset
= -3600 * ((zone
- 'N') + 1);
2671 else if ((zone
>= 'a') && (zone
<= 'z'))
2673 if (zone
< 'j') utc_offset
= 3600 * ((zone
- 'a') + 1);
2674 else if ((zone
>= 'k') && (zone
<= 'm')) utc_offset
= 3600 * (zone
- 'a');
2675 else if (zone
<= 'y') utc_offset
= -3600 * ((zone
- 'n') + 1);
2679 #ifdef DEBUG_LIST_FILES
2680 fprintf(stderr
, "_parse_stamp_style fail %u\n", __LINE__
);
2682 return STAMP_STYLE_INVALID
;
2687 t
.tm_sec
+= utc_offset
;
2690 if ((zone
== 'J') || (zone
== 'j')) ftime
= mktime(&t
);
2691 else ftime
= timegm(&t
);
2693 if (tp
!= NULL
) *tp
= ftime
;
2695 return STAMP_STYLE_ISO8601
;
2698 asl_out_file_list_t
*
2699 asl_list_log_files(const char *dir
, const char *base
, const char *ext
, uint32_t flags
, bool src
)
2703 char path
[MAXPATHLEN
];
2708 asl_out_file_list_t
*out
, *x
, *y
;
2710 #ifdef DEBUG_LIST_FILES
2711 fprintf(stderr
, "asl_list_log_files dir=%s base=%s ext=%s flags=0x%08x %s\n", dir
, base
, (ext
== NULL
) ? "(NULL)" : ext
, flags
, src
? "src" : "dst");
2714 if (dir
== NULL
) return NULL
;
2715 if (base
== NULL
) return NULL
;
2720 if (d
== NULL
) return NULL
;
2722 while (NULL
!= (ent
= readdir(d
)))
2726 bool stat_ok
= false;
2728 check
= _check_file_name(ent
->d_name
, base
, ext
, flags
, src
, &stamp
);
2729 if (!check
) continue;
2734 pstyle
= _parse_stamp_style(stamp
, flags
, &seq
, &ftime
);
2737 if (pstyle
== STAMP_STYLE_INVALID
) continue;
2739 snprintf(path
, sizeof(path
), "%s/%s", dir
, ent
->d_name
);
2740 memset(&sb
, 0, sizeof(sb
));
2741 if (lstat(path
, &sb
) == 0)
2743 /* ignore symlinks (created for basestamp / symlink files) */
2745 if ((sb
.st_mode
& S_IFMT
) == S_IFLNK
) continue;
2748 fstyle
= STAMP_STYLE_NULL
;
2749 if (flags
& MODULE_NAME_STYLE_STAMP_SEC
) fstyle
= STAMP_STYLE_SEC
;
2750 else if (flags
& MODULE_NAME_STYLE_STAMP_SEQ
) fstyle
= STAMP_STYLE_SEQ
;
2751 else if ((flags
& MODULE_NAME_STYLE_STAMP_UTC
) || (flags
& MODULE_NAME_STYLE_STAMP_LCL
)) fstyle
= STAMP_STYLE_ISO8601
;
2752 else if ((flags
& MODULE_NAME_STYLE_STAMP_UTC_B
) || (flags
& MODULE_NAME_STYLE_STAMP_LCL_B
)) fstyle
= STAMP_STYLE_ISO8601
;
2754 #ifdef DEBUG_LIST_FILES
2755 fprintf(stderr
, "%s %s %u fstyle %u pstyle %u\n", __func__
, path
, __LINE__
, fstyle
, pstyle
);
2759 * accept the file if:
2760 * style is STAMP_STYLE_NULL (no timestamp)
2761 * src is true and style is STAMP_STYLE_SEC
2762 * actual style matches the style implied by the input flags
2766 if (pstyle
== STAMP_STYLE_NULL
) check
= true;
2767 if ((pstyle
== STAMP_STYLE_SEC
) && src
) check
= true;
2768 if (pstyle
== fstyle
) check
= true;
2772 #ifdef DEBUG_LIST_FILES
2773 fprintf(stderr
, "%s reject %s at line %u\n", __func__
, path
, __LINE__
);
2778 x
= (asl_out_file_list_t
*)calloc(1, sizeof(asl_out_file_list_t
));
2781 asl_out_file_list_free(out
);
2785 x
->name
= strdup(ent
->d_name
);
2792 x
->size
= sb
.st_size
;
2793 if (pstyle
== STAMP_STYLE_SEQ
)
2795 x
->ftime
= sb
.st_birthtimespec
.tv_sec
;
2796 if (x
->ftime
== 0) x
->ftime
= sb
.st_mtimespec
.tv_sec
;
2800 if (pstyle
== STAMP_STYLE_SEQ
)
2802 #ifdef DEBUG_LIST_FILES
2803 fprintf(stderr
, "asl_list_log_files SEQ %s %u %ld\n", path
, x
->seq
, x
->ftime
);
2809 else if ((x
->seq
== IndexNull
) || ((x
->seq
> out
->seq
) && (out
->seq
!= IndexNull
)))
2817 for (y
= out
; y
!= NULL
; y
= y
->next
)
2819 if (y
->next
== NULL
)
2825 else if ((x
->seq
> y
->next
->seq
) && (y
->next
->seq
!= IndexNull
))
2838 #ifdef DEBUG_LIST_FILES
2839 fprintf(stderr
, "asl_list_log_files TIME %s %ld\n", path
, x
->ftime
);
2845 else if (x
->ftime
< out
->ftime
)
2853 for (y
= out
; y
!= NULL
; y
= y
->next
)
2855 if (y
->next
== NULL
)
2861 else if (x
->ftime
< y
->next
->ftime
)
2879 * List the source files for an output asl_out_dst_data_t
2881 asl_out_file_list_t
*
2882 asl_list_src_files(asl_out_dst_data_t
*dst
)
2885 #ifdef DEBUG_LIST_FILES
2886 fprintf(stderr
, "asl_list_src_files\n");
2889 if (dst
== NULL
) return NULL
;
2890 if (dst
->path
== NULL
) return NULL
;
2891 if (dst
->base
== NULL
) return NULL
;
2894 * MODULE_FLAG_EXTERNAL means some process other than syslogd writes the file.
2895 * We check for its existence, and that it is non-zero in size.
2897 if (dst
->flags
& MODULE_FLAG_EXTERNAL
)
2901 memset(&sb
, 0, sizeof(struct stat
));
2903 if (stat(dst
->path
, &sb
) == 0)
2905 if (S_ISREG(sb
.st_mode
) && (sb
.st_size
!= 0))
2907 asl_out_file_list_t
*out
= (asl_out_file_list_t
*)calloc(1, sizeof(asl_out_file_list_t
));
2910 char *p
= strrchr(dst
->path
, '/');
2911 if (p
== NULL
) p
= dst
->path
;
2913 out
->name
= strdup(p
);
2914 out
->ftime
= sb
.st_birthtimespec
.tv_sec
;
2915 if (out
->ftime
== 0) out
->ftime
= sb
.st_mtimespec
.tv_sec
;
2924 if ((dst
->rotate_dir
== NULL
) && (dst
->flags
& MODULE_FLAG_BASESTAMP
) && ((dst
->flags
& MODULE_FLAG_COMPRESS
) == 0))
2926 /* files do not move to a dest dir, get renamed, or get compressed - nothing to do */
2931 * MODULE_NAME_STYLE_STAMP_SEC format is used for sequenced (MODULE_NAME_STYLE_STAMP_SEQ) files.
2932 * aslmanager converts the file names.
2934 flags
= dst
->style_flags
;
2935 if (flags
& MODULE_NAME_STYLE_STAMP_SEQ
)
2937 flags
&= ~MODULE_NAME_STYLE_STAMP_SEQ
;
2938 flags
|= MODULE_NAME_STYLE_STAMP_SEC
;
2941 return asl_list_log_files(dst
->dir
, dst
->base
, dst
->ext
, flags
, true);
2945 * List the destination files for an output asl_out_dst_data_t
2947 asl_out_file_list_t
*
2948 asl_list_dst_files(asl_out_dst_data_t
*dst
)
2951 #ifdef DEBUG_LIST_FILES
2952 fprintf(stderr
, "asl_list_dst_files\n");
2955 if (dst
== NULL
) return NULL
;
2956 if (dst
->path
== NULL
) return NULL
;
2957 if (dst
->base
== NULL
) return NULL
;
2959 dst_dir
= dst
->rotate_dir
;
2960 if (dst_dir
== NULL
) dst_dir
= dst
->dir
;
2962 return asl_list_log_files(dst_dir
, dst
->base
, dst
->ext
, dst
->style_flags
, false);
2966 asl_secure_open_dir(const char *path
)
2971 if (path
== NULL
) return -1;
2972 if (path
[0] != '/') return -1;
2974 path_parts
= explode(path
+ 1, "/");
2975 if (path_parts
== NULL
) return 0;
2977 fd
= open("/", O_RDONLY
| O_NOFOLLOW
, 0);
2980 free_string_list(path_parts
);
2984 for (i
= 0; path_parts
[i
] != NULL
; i
++)
2986 int fd_next
, status
;
2989 fd_next
= openat(fd
, path_parts
[i
], O_RDONLY
| O_NOFOLLOW
, 0);
2994 free_string_list(path_parts
);
2998 memset(&sb
, 0, sizeof(sb
));
3000 status
= fstat(fd
, &sb
);
3003 free_string_list(path_parts
);
3007 if (!S_ISDIR(sb
.st_mode
))
3009 free_string_list(path_parts
);
3014 free_string_list(path_parts
);
3019 asl_secure_chown_chmod_dir(const char *path
, uid_t uid
, gid_t gid
, mode_t mode
)
3023 fd
= asl_secure_open_dir(path
);
3024 if (fd
< 0) return fd
;
3026 status
= fchown(fd
, uid
, gid
);
3033 if (mode
>= 0) status
= fchmod(fd
, mode
);
3036 if (status
< 0) return -1;