2 * Copyright (c) 2004-2011 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@
24 #include <sys/types.h>
26 #include <sys/socket.h>
39 #include <membership.h>
41 #include <dispatch/private.h>
43 #define _PATH_WALL "/usr/bin/wall"
44 #define _PATH_ASL_CONF "/etc/asl.conf"
45 #define MY_ID "asl_action"
47 #define MAX_FAILURES 5
50 #define ACTION_IGNORE 1
51 #define ACTION_NOTIFY 2
52 #define ACTION_BROADCAST 3
53 #define ACTION_ACCESS 4
54 #define ACTION_ASL_STORE 5 /* Save in main ASL Database */
55 #define ACTION_ASL_FILE 6 /* Save in an ASL format data file */
56 #define ACTION_ASL_DIR 7 /* Save in an ASL directory */
58 #define ACTION_FORWARD 9
60 #define forever for(;;)
62 #define ACT_FLAG_HAS_LOGGED 0x80000000
63 #define ACT_FLAG_CLEAR_LOGGED 0x7fffffff
65 #define ACT_STORE_FLAG_STAY_OPEN 0x00000001
66 #define ACT_STORE_FLAG_CONTINUE 0x00000002
68 #define ACT_FILE_FLAG_DUP_SUPRESS 0x00000001
69 #define ACT_FILE_FLAG_ROTATE 0x00000002
71 static dispatch_queue_t asl_action_queue
;
72 static time_t last_file_day
;
74 typedef struct action_rule_s
80 struct action_rule_s
*next
;
120 dispatch_source_t dup_timer
;
124 static action_rule_t
*asl_action_rule
= NULL
;
125 static action_rule_t
*asl_datastore_rule
= NULL
;
127 static int _parse_config_file(const char *);
128 extern void db_save_message(aslmsg m
);
131 int _act_file_open(struct file_data
*fdata
);
132 static void _act_file_init(action_rule_t
*r
);
133 static void _act_store_init(action_rule_t
*r
);
138 char *a
, *p
, *e
, *out
;
141 if (s
== NULL
) return NULL
;
142 if (*s
== NULL
) return NULL
;
170 if (quote
== 0) quote
= 1;
174 if (((*p
== ' ') || (*p
== '\t')) && (quote
== 0))
187 if (len
== 0) return NULL
;
189 out
= malloc(len
+ 1);
190 if (out
== NULL
) return NULL
;
198 * Config File format:
199 * Set parameter rule - initializes a parameter.
201 * Query rule - if a message matches the query, then the action is invoked.
202 * The rule may be identified by either "?" or "Q".
203 * ? [k v] [k v] ... action args...
204 * Q [k v] [k v] ... action args...
205 * Universal match rule - the action is invoked for all messages
209 /* Skip over query */
211 _find_action(char *s
)
216 if (p
== NULL
) return NULL
;
217 if ((*p
!= 'Q') && (*p
!= '?') && (*p
!= '*')) return NULL
;
224 while ((*p
== ' ') || (*p
== '\t')) p
++;
226 if (*p
== '\0') return NULL
;
227 if (*p
!= '[') return p
;
229 /* skip to closing ] */
247 _parse_query_action(char *s
)
250 action_rule_t
*out
, *rule
;
252 act
= _find_action(s
);
253 if (act
== NULL
) return -1;
255 out
= (action_rule_t
*)calloc(1, sizeof(action_rule_t
));
256 if (out
== NULL
) return -1;
258 p
= strchr(act
, ' ');
259 if (p
!= NULL
) *p
= '\0';
261 if (!strcasecmp(act
, "ignore")) out
->action
= ACTION_IGNORE
;
262 else if (!strcasecmp(act
, "notify")) out
->action
= ACTION_NOTIFY
;
263 else if (!strcasecmp(act
, "broadcast")) out
->action
= ACTION_BROADCAST
;
264 else if (!strcasecmp(act
, "access")) out
->action
= ACTION_ACCESS
;
265 else if (!strcasecmp(act
, "store")) out
->action
= ACTION_ASL_STORE
;
266 else if (!strcasecmp(act
, "save")) out
->action
= ACTION_ASL_STORE
;
267 else if (!strcasecmp(act
, "store_file")) out
->action
= ACTION_ASL_FILE
;
268 else if (!strcasecmp(act
, "store_directory")) out
->action
= ACTION_ASL_DIR
;
269 else if (!strcasecmp(act
, "store_dir")) out
->action
= ACTION_ASL_DIR
;
270 else if (!strcasecmp(act
, "file")) out
->action
= ACTION_FILE
;
271 else if (!strcasecmp(act
, "forward")) out
->action
= ACTION_FORWARD
;
275 out
->options
= strdup(p
+1);
277 if (out
->options
== NULL
)
288 if (s
[0] == '*') out
->query
= asl_msg_new(ASL_TYPE_QUERY
);
292 out
->query
= asl_msg_from_string(s
);
295 if (out
->query
== NULL
)
297 asldebug("out->query is NULL (ERROR)\n");
303 /* store /some/path means save to a file */
304 if ((out
->action
== ACTION_ASL_STORE
) && (out
->options
!= NULL
)) out
->action
= ACTION_ASL_FILE
;
306 if (out
->action
== ACTION_FILE
) _act_file_init(out
);
307 else if ((out
->action
== ACTION_ASL_FILE
) || (out
->action
== ACTION_ASL_DIR
)) _act_store_init(out
);
309 if (out
->action
== ACTION_ASL_STORE
)
311 asldebug("action = ACTION_ASL_STORE\n");
312 if (asl_datastore_rule
== NULL
) asl_datastore_rule
= out
;
315 for (rule
= asl_datastore_rule
; rule
->next
!= NULL
; rule
= rule
->next
);
321 asldebug("action = %d options = %s\n", out
->action
, out
->options
);
322 if (asl_action_rule
== NULL
) asl_action_rule
= out
;
325 for (rule
= asl_action_rule
; rule
->next
!= NULL
; rule
= rule
->next
);
339 if (s
== NULL
) return -1;
340 while ((*s
== ' ') || (*s
== '\t')) s
++;
342 /* First non-whitespace char is the rule type */
348 /* Blank Line or Comment */
355 /* Query-match action */
356 status
= _parse_query_action(s
);
362 status
= control_set_param(s
);
375 asprintf(&str
, "[%s syslogd] [%s %u] [%s %u] [%s Ignoring unrecognized entry in %s: %s] [%s 0] [%s 0] [Facility syslog]",
377 ASL_KEY_LEVEL
, ASL_LEVEL_ERR
,
378 ASL_KEY_PID
, getpid(),
379 ASL_KEY_MSG
, _PATH_ASL_CONF
, s
,
380 ASL_KEY_UID
, ASL_KEY_GID
);
382 internal_log_message(str
);
390 _act_notify(action_rule_t
*r
)
392 if (r
== NULL
) return;
393 if (r
->options
== NULL
) return;
395 notify_post(r
->options
);
399 _act_broadcast(action_rule_t
*r
, aslmsg msg
)
401 #ifndef CONFIG_IPHONE
405 if (r
== NULL
) return;
406 if (msg
== NULL
) return;
409 if (val
== NULL
) val
= asl_get(msg
, ASL_KEY_MSG
);
410 if (val
== NULL
) return;
412 pw
= popen(_PATH_WALL
, "w");
415 asldebug("%s: error sending wall message: %s\n", MY_ID
, strerror(errno
));
419 fprintf(pw
, "%s", val
);
425 _act_access_control(action_rule_t
*r
, aslmsg msg
)
430 ruid
= atoi(r
->options
);
432 p
= strchr(r
->options
, ' ');
433 if (p
== NULL
) p
= strchr(r
->options
, '\t');
441 if (ruid
!= -1) asl_set(msg
, ASL_KEY_READ_UID
, r
->options
);
444 if (rgid
!= -1) asl_set(msg
, ASL_KEY_READ_GID
, p
);
451 _act_store_file_setup(struct store_data
*sd
)
455 if (sd
== NULL
) return ASL_STATUS_INVALID_STORE
;
456 if (sd
->store
== NULL
) return ASL_STATUS_INVALID_STORE
;
457 if (sd
->store
->store
== NULL
) return ASL_STATUS_INVALID_STORE
;
459 status
= asl_file_read_set_position(sd
->store
, ASL_FILE_POSITION_LAST
);
460 if (status
!= ASL_STATUS_OK
) return status
;
462 sd
->next_id
= sd
->store
->cursor_xid
+ 1;
463 if (fseek(sd
->store
->store
, 0, SEEK_END
) != 0) return ASL_STATUS_ACCESS_DENIED
;
465 return ASL_STATUS_OK
;
469 _act_store_dir_setup(struct store_data
*sd
, time_t tick
)
478 if (sd
== NULL
) return ASL_STATUS_INVALID_STORE
;
479 if (sd
->dir
== NULL
) return ASL_STATUS_INVALID_STORE
;
481 /* get / set message id from StoreData file */
484 if (sd
->storedata
== NULL
)
486 memset(&sb
, 0, sizeof(struct stat
));
487 status
= stat(sd
->dir
, &sb
);
490 /* must be a directory */
491 if (!S_ISDIR(sb
.st_mode
)) return ASL_STATUS_INVALID_STORE
;
493 else if (errno
== ENOENT
)
495 /* doesn't exist - create it */
497 status
= mkdir(sd
->dir
, sd
->mode
);
500 if (status
!= 0) return ASL_STATUS_WRITE_FAILED
;
501 if (chown(sd
->dir
, sd
->uid
, sd
->gid
) != 0) return ASL_STATUS_WRITE_FAILED
;
505 /* Unexpected stat error */
506 return ASL_STATUS_FAILED
;
510 asprintf(&path
, "%s/%s", sd
->dir
, FILE_ASL_STORE_DATA
);
511 if (path
== NULL
) return ASL_STATUS_NO_MEMORY
;
513 memset(&sb
, 0, sizeof(struct stat
));
514 status
= stat(path
, &sb
);
517 /* StoreData exists: open and read last xid */
518 sd
->storedata
= fopen(path
, "r+");
519 if (sd
->storedata
== NULL
)
522 return ASL_STATUS_FAILED
;
525 if (fread(&xid
, sizeof(uint64_t), 1, sd
->storedata
) != 1)
528 fclose(sd
->storedata
);
529 sd
->storedata
= NULL
;
530 return ASL_STATUS_READ_FAILED
;
533 else if (errno
== ENOENT
)
535 /* StoreData does not exist: create it */
536 sd
->storedata
= fopen(path
, "w");
537 if (sd
->storedata
== NULL
)
540 return ASL_STATUS_FAILED
;
543 if (chown(path
, sd
->uid
, sd
->gid
) != 0)
546 return ASL_STATUS_WRITE_FAILED
;
551 /* Unexpected stat error */
553 return ASL_STATUS_FAILED
;
560 rewind(sd
->storedata
);
561 if (fread(&xid
, sizeof(uint64_t), 1, sd
->storedata
) != 1)
563 fclose(sd
->storedata
);
564 sd
->storedata
= NULL
;
565 return ASL_STATUS_READ_FAILED
;
569 xid
= asl_core_ntohq(xid
);
573 xid
= asl_core_htonq(xid
);
574 rewind(sd
->storedata
);
575 status
= fwrite(&xid
, sizeof(uint64_t), 1, sd
->storedata
);
578 fclose(sd
->storedata
);
579 sd
->storedata
= NULL
;
580 return ASL_STATUS_WRITE_FAILED
;
583 if ((sd
->flags
& ACT_STORE_FLAG_STAY_OPEN
) == 0)
585 fclose(sd
->storedata
);
586 sd
->storedata
= NULL
;
589 memset(&ctm
, 0, sizeof(struct tm
));
591 if (localtime_r((const time_t *)&tick
, &ctm
) == NULL
) return ASL_STATUS_FAILED
;
592 if ((sd
->p_year
== ctm
.tm_year
) && (sd
->p_month
== ctm
.tm_mon
) && (sd
->p_day
== ctm
.tm_mday
) && (sd
->path
!= NULL
)) return ASL_STATUS_OK
;
594 if (sd
->store
!= NULL
) asl_file_close(sd
->store
);
604 asprintf(&(sd
->path
), "%s/%d.%02d.%02d.asl", sd
->dir
, ctm
.tm_year
+ 1900, ctm
.tm_mon
+ 1, ctm
.tm_mday
);
605 if (sd
->path
== NULL
) return ASL_STATUS_NO_MEMORY
;
607 sd
->p_year
= ctm
.tm_year
;
608 sd
->p_month
= ctm
.tm_mon
;
609 sd
->p_day
= ctm
.tm_mday
;
611 return ASL_STATUS_OK
;
615 _act_store_init(action_rule_t
*r
)
617 struct store_data
*sd
, *xd
;
618 char *str
, *opts
, *p
, *path
;
621 /* check if the store data is already set up */
622 if (r
->data
!= NULL
) return;
625 path
= _next_word(&opts
);
627 if ((path
== NULL
) || (path
[0] != '/'))
630 asprintf(&str
, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Invalid path for \"%s\" action: %s]",
632 ASL_KEY_LEVEL
, ASL_LEVEL_ERR
,
633 ASL_KEY_PID
, getpid(),
635 (r
->action
== ACTION_ASL_FILE
) ? "store" : "store_directory",
636 (path
== NULL
) ? "no path specified" : path
);
638 internal_log_message(str
);
640 r
->action
= ACTION_NONE
;
645 /* check if a previous rule has set up this path (ACTION_ASL_FILE) or dir (ACTION_ASL_DIR) */
646 for (x
= asl_action_rule
; x
!= NULL
; x
= x
->next
)
648 if ((x
->action
== r
->action
) && (x
->data
!= NULL
))
650 xd
= (struct store_data
*)x
->data
;
652 if (r
->action
== ACTION_ASL_DIR
) p
= xd
->dir
;
654 if ((p
!= NULL
) && (!strcmp(path
, p
)))
664 /* set up store data */
665 sd
= (struct store_data
*)calloc(1, sizeof(struct store_data
));
666 if (sd
== NULL
) return;
675 if (r
->action
== ACTION_ASL_DIR
) sd
->dir
= path
;
676 else sd
->path
= path
;
678 while (NULL
!= (p
= _next_word(&opts
)))
680 if (!strcmp(p
, "stayopen"))
682 sd
->flags
|= ACT_STORE_FLAG_STAY_OPEN
;
684 else if (!strcmp(p
, "continue"))
686 sd
->flags
|= ACT_STORE_FLAG_CONTINUE
;
688 else if (!strncmp(p
, "mode=", 5)) sd
->mode
= strtol(p
+5, NULL
, 0);
689 else if (!strncmp(p
, "uid=", 4)) sd
->uid
= atoi(p
+4);
690 else if (!strncmp(p
, "gid=", 4)) sd
->gid
= atoi(p
+4);
700 * Save a message to an ASL format file (ACTION_ASL_FILE)
701 * or to an ASL directory (ACTION_ASL_DIR).
704 _act_store(action_rule_t
*r
, aslmsg msg
)
706 struct store_data
*sd
;
717 if (r
->data
== NULL
) return;
719 sd
= (struct store_data
*)r
->data
;
721 if (sd
->flags
& ACT_FLAG_HAS_LOGGED
) return;
722 sd
->flags
|= ACT_FLAG_HAS_LOGGED
;
724 if (r
->action
== ACTION_ASL_DIR
)
726 val
= asl_get(msg
, ASL_KEY_TIME
);
727 if (val
== NULL
) return;
730 status
= _act_store_dir_setup(sd
, tick
);
731 if (status
!= ASL_STATUS_OK
)
733 asldebug("_act_store_dir_setup %s failed: %s\n", sd
->path
, asl_core_error(status
));
737 /* disable further activity after multiple failures */
738 if (sd
->fails
> MAX_FAILURES
)
741 asprintf(&str
, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Disabling writes to path %s following %u failures (%s)]",
743 ASL_KEY_LEVEL
, ASL_LEVEL_ERR
,
744 ASL_KEY_PID
, getpid(),
745 ASL_KEY_MSG
, sd
->path
, sd
->fails
, asl_core_error(status
));
747 internal_log_message(str
);
750 asl_file_close(sd
->store
);
752 r
->action
= ACTION_NONE
;
762 if (sd
->store
== NULL
)
767 status
= asl_file_open_write(sd
->path
, (sd
->mode
& 0666), sd
->uid
, sd
->gid
, &s
);
770 if ((status
!= ASL_STATUS_OK
) || (s
== NULL
))
772 asldebug("asl_file_open_write %s failed: %s\n", sd
->path
, asl_core_error(status
));
776 /* disable further activity after multiple failures */
777 if (sd
->fails
> MAX_FAILURES
)
780 asprintf(&str
, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Disabling writes to path %s following %u failures (%s)]",
782 ASL_KEY_LEVEL
, ASL_LEVEL_ERR
,
783 ASL_KEY_PID
, getpid(),
784 ASL_KEY_MSG
, sd
->path
, sd
->fails
, asl_core_error(status
));
786 internal_log_message(str
);
789 asl_file_close(sd
->store
);
791 r
->action
= ACTION_NONE
;
795 else if (status
== ASL_STATUS_OK
)
803 if (r
->action
!= ACTION_ASL_DIR
)
805 status
= _act_store_file_setup(sd
);
806 if (status
!= ASL_STATUS_OK
)
808 asldebug("_act_store_file_setup %s failed: %s\n", sd
->path
, asl_core_error(status
));
812 /* disable further activity after multiple failures */
813 if (sd
->fails
> MAX_FAILURES
)
816 asprintf(&str
, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Disabling writes to path %s following %u failures (%s)]",
818 ASL_KEY_LEVEL
, ASL_LEVEL_ERR
,
819 ASL_KEY_PID
, getpid(),
820 ASL_KEY_MSG
, sd
->path
, sd
->fails
, asl_core_error(status
));
822 internal_log_message(str
);
825 asl_file_close(sd
->store
);
827 r
->action
= ACTION_NONE
;
839 status
= asl_file_save(sd
->store
, msg
, &mid
);
840 if (status
!= ASL_STATUS_OK
)
842 asldebug("asl_file_save %s failed: %s\n", sd
->path
, asl_core_error(status
));
846 /* disable further activity after multiple failures */
847 if (sd
->fails
> MAX_FAILURES
)
850 asprintf(&str
, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Disabling writes to path %s following %u failures (%s)]",
852 ASL_KEY_LEVEL
, ASL_LEVEL_ERR
,
853 ASL_KEY_PID
, getpid(),
854 ASL_KEY_MSG
, sd
->path
, sd
->fails
, asl_core_error(status
));
856 internal_log_message(str
);
859 asl_file_close(sd
->store
);
861 r
->action
= ACTION_NONE
;
870 if ((sd
->flags
& ACT_STORE_FLAG_STAY_OPEN
) == 0)
872 asl_file_close(sd
->store
);
876 if ((sd
->flags
& ACT_STORE_FLAG_CONTINUE
) == 0)
878 opts
= (char *)asl_get(msg
, ASL_KEY_OPTION
);
881 asl_set(msg
, ASL_KEY_OPTION
, ASL_OPT_IGNORE
);
886 asprintf(&str
, "%s %s", ASL_OPT_IGNORE
, opts
);
889 asl_set(msg
, ASL_KEY_OPTION
, str
);
897 _act_file_send_repeat_msg(struct file_data
*fdata
)
900 int len
, status
, closeit
;
901 time_t now
= time(NULL
);
903 if (fdata
== NULL
) return -1;
905 free(fdata
->last_msg
);
906 fdata
->last_msg
= NULL
;
908 if (fdata
->last_count
== 0) return 0;
911 dispatch_suspend(fdata
->dup_timer
);
913 memset(vt
, 0, sizeof(vt
));
918 asprintf(&msg
, "%s --- last message repeated %u time%s ---\n", vt
+ 4, fdata
->last_count
, (fdata
->last_count
== 1) ? "" : "s");
919 fdata
->last_count
= 0;
920 if (msg
== NULL
) return -1;
926 fdata
->fd
= _act_file_open(fdata
);
929 asldebug("%s: error opening for repeat message (%s): %s\n", MY_ID
, fdata
->path
, strerror(errno
));
935 status
= write(fdata
->fd
, msg
, len
);
943 if ((status
< 0) || (status
< len
))
945 asldebug("%s: error writing repeat message (%s): %s\n", MY_ID
, fdata
->path
, strerror(errno
));
953 * N.B. This is basic file rotation support.
954 * More rotation options will be added in the future, along
955 * with support in aslmanager for compression and deletion.
958 _act_file_rotate_file_data(struct file_data
*fdata
, time_t now
)
960 char str
[MAXPATHLEN
];
964 if (now
== 0) now
= time(NULL
);
966 /* flush duplicates if pending */
967 _act_file_send_repeat_msg(fdata
);
969 /* sleep to prevent a sub-second rotation */
970 while (now
== fdata
->stamp
)
976 len
= strlen(fdata
->path
);
978 if ((len
> 4) && (!strcasecmp(fdata
->path
+ width
, ".log")))
980 /* ".log" rename: abc.log -> abc.timestamp.log */
981 snprintf(str
, sizeof(str
), "%.*s.%lu.log", width
, fdata
->path
, fdata
->stamp
);
985 snprintf(str
, sizeof(str
), "%s.%lu", fdata
->path
, fdata
->stamp
);
988 rename(fdata
->path
, str
);
994 _act_file_open(struct file_data
*fdata
)
1006 memset(&sb
, 0, sizeof(struct stat
));
1007 status
= stat(fdata
->path
, &sb
);
1010 /* must be a regular file */
1011 if (!S_ISREG(sb
.st_mode
)) return -1;
1013 /* use st_birthtimespec if stamp is zero */
1014 if (fdata
->stamp
== 0) fdata
->stamp
= sb
.st_birthtimespec
.tv_sec
;
1016 /* rotate if over size limit */
1017 if ((fdata
->max_size
> 0) && (sb
.st_size
> fdata
->max_size
))
1019 _act_file_rotate_file_data(fdata
, 0);
1023 /* open existing file */
1024 fd
= open(fdata
->path
, O_RDWR
| O_APPEND
| O_EXCL
, 0);
1028 else if (errno
!= ENOENT
)
1033 #if TARGET_OS_EMBEDDED
1034 return open(fdata
->path
, O_RDWR
| O_CREAT
| O_EXCL
, (fdata
->mode
& 0666));
1039 for (i
= 0; i
< fdata
->ngid
; i
++)
1041 status
= mbr_gid_to_uuid(fdata
->gid
[i
], uuid
);
1045 asprintf(&str
, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Unknown GID %d for \"file\" action: %s]",
1047 ASL_KEY_LEVEL
, ASL_LEVEL_ERR
,
1048 ASL_KEY_PID
, getpid(),
1049 ASL_KEY_MSG
, fdata
->gid
[i
], fdata
->path
);
1051 internal_log_message(str
);
1056 status
= acl_create_entry_np(&acl
, &entry
, ACL_FIRST_ENTRY
);
1057 if (status
!= 0) goto asl_file_create_return
;
1059 status
= acl_set_tag_type(entry
, ACL_EXTENDED_ALLOW
);
1060 if (status
!= 0) goto asl_file_create_return
;
1062 status
= acl_set_qualifier(entry
, &uuid
);
1063 if (status
!= 0) goto asl_file_create_return
;
1065 status
= acl_get_permset(entry
, &perms
);
1066 if (status
!= 0) goto asl_file_create_return
;
1068 status
= acl_add_perm(perms
, ACL_READ_DATA
);
1069 if (status
!= 0) goto asl_file_create_return
;
1072 for (i
= 0; i
< fdata
->nuid
; i
++)
1074 status
= mbr_uid_to_uuid(fdata
->uid
[i
], uuid
);
1078 asprintf(&str
, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Unknown UID %d for \"file\" action: %s]",
1080 ASL_KEY_LEVEL
, ASL_LEVEL_ERR
,
1081 ASL_KEY_PID
, getpid(),
1082 ASL_KEY_MSG
, fdata
->uid
[i
], fdata
->path
);
1084 internal_log_message(str
);
1089 status
= acl_create_entry_np(&acl
, &entry
, ACL_FIRST_ENTRY
);
1090 if (status
!= 0) goto asl_file_create_return
;
1092 status
= acl_set_tag_type(entry
, ACL_EXTENDED_ALLOW
);
1093 if (status
!= 0) goto asl_file_create_return
;
1095 status
= acl_set_qualifier(entry
, &uuid
);
1096 if (status
!= 0) goto asl_file_create_return
;
1098 status
= acl_get_permset(entry
, &perms
);
1099 if (status
!= 0) goto asl_file_create_return
;
1101 status
= acl_add_perm(perms
, ACL_READ_DATA
);
1102 if (status
!= 0) goto asl_file_create_return
;
1106 fd
= open(fdata
->path
, O_RDWR
| O_CREAT
| O_EXCL
, (fdata
->mode
& 0666));
1108 if (fd
< 0) goto asl_file_create_return
;
1111 status
= acl_set_fd(fd
, acl
);
1117 unlink(fdata
->path
);
1120 asl_file_create_return
:
1128 _act_file_rotate(const char *path
)
1131 struct file_data
*fdata
;
1132 time_t now
= time(NULL
);
1134 for (r
= asl_action_rule
; r
!= NULL
; r
= r
->next
)
1136 if (r
->action
== ACTION_FILE
)
1138 fdata
= (struct file_data
*)r
->data
;
1139 if (fdata
->flags
& ACT_FILE_FLAG_ROTATE
)
1141 if ((path
== NULL
) || ((fdata
->path
!= NULL
) && !strcmp(fdata
->path
, path
)))
1143 _act_file_rotate_file_data(fdata
, now
);
1151 _act_file_format_string(char *s
)
1156 if (s
== NULL
) return NULL
;
1160 for (i
= 0; i
< len
; i
++) if (s
[i
] == '\\') n
++;
1162 fmt
= malloc(1 + len
- n
);
1163 if (fmt
== NULL
) return NULL
;
1165 for (i
= 0, n
= 0; i
< len
; i
++) if (s
[i
] != '\\') fmt
[n
++] = s
[i
];
1171 _act_file_max_size(char *s
)
1176 if (s
== NULL
) return 0;
1179 if (len
== 0) return 0;
1183 if (x
> 90) x
-= 32;
1184 if (x
== 'K') n
= 1ll << 10;
1185 else if (x
== 'M') n
= 1ll << 20;
1186 else if (x
== 'G') n
= 1ll << 30;
1187 else if (x
== 'T') n
= 1ll << 40;
1194 _act_file_add_uid(struct file_data
*fdata
, char *s
)
1196 if (fdata
== NULL
) return;
1197 if (s
== NULL
) return;
1199 fdata
->uid
= reallocf(fdata
->uid
, (fdata
->nuid
+ 1) * sizeof(uid_t
));
1200 if (fdata
->uid
== NULL
)
1206 fdata
->uid
[fdata
->nuid
++] = atoi(s
);
1210 _act_file_add_gid(struct file_data
*fdata
, char *s
)
1212 if (fdata
== NULL
) return;
1213 if (s
== NULL
) return;
1215 fdata
->gid
= reallocf(fdata
->gid
, (fdata
->ngid
+ 1) * sizeof(gid_t
));
1216 if (fdata
->gid
== NULL
)
1222 fdata
->gid
[fdata
->ngid
++] = atoi(s
);
1226 _act_file_init(action_rule_t
*r
)
1228 struct file_data
*fdata
, *xdata
;
1229 char *str
, *opts
, *p
, *path
;
1232 /* check if the file data is already set up */
1233 if (r
->data
!= NULL
) return;
1235 /* requires at least a path */
1236 if (r
->options
== NULL
) return;
1238 path
= _next_word(&opts
);
1240 if ((path
== NULL
) || (path
[0] != '/'))
1243 asprintf(&str
, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Invalid path for \"file\" action: %s]",
1245 ASL_KEY_LEVEL
, ASL_LEVEL_ERR
,
1246 ASL_KEY_PID
, getpid(),
1247 ASL_KEY_MSG
, (path
== NULL
) ? "no path specified" : path
);
1249 internal_log_message(str
);
1252 r
->action
= ACTION_NONE
;
1256 /* check if a previous rule has set up this path */
1257 for (x
= asl_action_rule
; x
!= NULL
; x
= x
->next
)
1259 if ((x
->action
== ACTION_FILE
) && (x
->data
!= NULL
))
1261 xdata
= (struct file_data
*)x
->data
;
1262 if ((xdata
->path
!= NULL
) && (!strcmp(path
, xdata
->path
)))
1272 /* set up file data */
1273 fdata
= (struct file_data
*)calloc(1, sizeof(struct file_data
));
1274 if (fdata
== NULL
) return;
1276 fdata
->refcount
= 1;
1281 * mode= set file creation mode
1282 * uid= user added to read ACL
1283 * gid= group added to read ACL
1284 * format= format string (also fmt=)
1285 * no_dup_supress no duplicate supression
1287 * rotate automatic daily rotation
1288 * this is basic rotation - more support is TBD
1291 fdata
->flags
= ACT_FILE_FLAG_DUP_SUPRESS
;
1293 while (NULL
!= (p
= _next_word(&opts
)))
1295 if (!strncmp(p
, "mode=", 5)) fdata
->mode
= strtol(p
+5, NULL
, 0);
1296 else if (!strncmp(p
, "uid=", 4)) _act_file_add_uid(fdata
, p
+4);
1297 else if (!strncmp(p
, "gid=", 4)) _act_file_add_gid(fdata
, p
+4);
1298 else if (!strncmp(p
, "fmt=", 4)) fdata
->fmt
= _act_file_format_string(p
+4);
1299 else if (!strncmp(p
, "format=", 7)) fdata
->fmt
= _act_file_format_string(p
+7);
1300 else if (!strncmp(p
, "no_dup_supress", 14)) fdata
->flags
&= ~ACT_FILE_FLAG_DUP_SUPRESS
;
1301 else if (!strncmp(p
, "rotate", 6)) fdata
->flags
|= ACT_FILE_FLAG_ROTATE
;
1302 else if (!strncmp(p
, "max_size=", 9)) fdata
->max_size
= _act_file_max_size(p
+9);
1308 if (fdata
->fmt
== NULL
) fdata
->fmt
= strdup("std");
1310 /* duplicate compression is only possible for std and bsd formats */
1311 if (strcmp(fdata
->fmt
, "std") && strcmp(fdata
->fmt
, "bsd")) fdata
->flags
&= ~ACT_FILE_FLAG_DUP_SUPRESS
;
1313 /* set time format for raw output */
1314 if (!strcmp(fdata
->fmt
, "raw")) fdata
->tfmt
= "sec";
1320 _act_file(action_rule_t
*r
, aslmsg msg
)
1322 struct file_data
*fdata
;
1324 uint32_t len
, msg_hash
= 0;
1329 if (r
->data
== NULL
) return;
1331 fdata
= (struct file_data
*)r
->data
;
1336 memset(&ctm
, 0, sizeof(struct tm
));
1337 if (localtime_r((const time_t *)&now
, &ctm
) != NULL
)
1342 today
= mktime(&ctm
);
1345 /* check for rotation */
1346 if ((last_file_day
!= 0) && (last_file_day
!= today
))
1348 _act_file_rotate(NULL
);
1351 last_file_day
= today
;
1355 * asl.conf may contain multuple rules for the same file, eg:
1356 * ? [= Facility zippy] /var/log/abc.log
1357 * ? [= Color purple] /var/log/abc.log
1359 * To prevent duplicates we set a flag bit when a message is logged
1360 * to this file, and bail out if it has already been logged.
1361 * Note that asl_out_message clears the flag bit in all file_data
1362 * structures before processing each message.
1364 if (fdata
->flags
& ACT_FLAG_HAS_LOGGED
) return;
1365 fdata
->flags
|= ACT_FLAG_HAS_LOGGED
;
1369 str
= asl_format_message((asl_msg_t
*)msg
, fdata
->fmt
, fdata
->tfmt
, ASL_ENCODE_SAFE
, &len
);
1371 if (fdata
->flags
& ACT_FILE_FLAG_DUP_SUPRESS
)
1373 if (fdata
->dup_timer
== NULL
)
1375 /* create a timer to flush dups on this file */
1376 fdata
->dup_timer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, asl_action_queue
);
1377 dispatch_source_set_event_handler(fdata
->dup_timer
, ^{ _act_file_send_repeat_msg((struct file_data
*)r
->data
); });
1380 if ((global
.bsd_max_dup_time
> 0) && (str
!= NULL
) && (fdata
->last_msg
!= NULL
))
1382 msg_hash
= asl_core_string_hash(str
+ 16, len
- 16);
1383 if ((fdata
->last_hash
== msg_hash
) && (!strcmp(fdata
->last_msg
, str
+ 16)))
1385 if ((now
- fdata
->last_time
) < global
.bsd_max_dup_time
) is_dup
= 1;
1392 if (fdata
->last_count
== 0)
1394 /* start the timer */
1395 dispatch_source_set_timer(fdata
->dup_timer
, dispatch_time(DISPATCH_TIME_NOW
, NSEC_PER_SEC
* global
.bsd_max_dup_time
), DISPATCH_TIME_FOREVER
, 0);
1396 dispatch_resume(fdata
->dup_timer
);
1399 fdata
->last_count
++;
1403 fdata
->fd
= _act_file_open(fdata
);
1406 asldebug("_act_file_open %s failed: %s\n", fdata
->path
, strerror(errno
));
1410 /* disable further activity after multiple failures */
1411 if (fdata
->fails
> MAX_FAILURES
)
1414 asprintf(&tmp
, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Disabling writes to path %s following %u failures (%s)]",
1416 ASL_KEY_LEVEL
, ASL_LEVEL_ERR
,
1417 ASL_KEY_PID
, getpid(),
1418 ASL_KEY_MSG
, fdata
->path
, fdata
->fails
, strerror(errno
));
1420 internal_log_message(tmp
);
1423 r
->action
= ACTION_NONE
;
1434 * The current message is not a duplicate. If fdata->last_count > 0
1435 * we need to write a "last message repeated N times" log entry.
1436 * _act_file_send_repeat_msg will free last_msg and do nothing if
1437 * last_count == 0, but we test and free here to avoid a function call.
1439 if (fdata
->last_count
> 0)
1441 _act_file_send_repeat_msg(fdata
);
1445 free(fdata
->last_msg
);
1446 fdata
->last_msg
= NULL
;
1449 if (str
!= NULL
) fdata
->last_msg
= strdup(str
+ 16);
1451 fdata
->last_hash
= msg_hash
;
1452 fdata
->last_count
= 0;
1453 fdata
->last_time
= now
;
1455 if ((str
!= NULL
) && (len
> 1)) write(fdata
->fd
, str
, len
- 1);
1464 _act_forward(action_rule_t
*r
, aslmsg msg
)
1466 /* To do: <rdar://problem/6130747> Add a "forward" action to asl.conf */
1470 _send_to_asl_store(aslmsg msg
)
1475 /* ASLOption "store" forces a message to be saved */
1476 log_me
= asl_check_option(msg
, ASL_OPT_STORE
);
1479 db_save_message(msg
);
1483 /* if there are no rules, save the message */
1484 if (asl_datastore_rule
== NULL
)
1486 db_save_message(msg
);
1490 for (r
= asl_datastore_rule
; r
!= NULL
; r
= r
->next
)
1492 if (asl_msg_cmp(r
->query
, (asl_msg_t
*)msg
) == 1)
1494 /* if any rule matches, save the message (once!) */
1495 db_save_message(msg
);
1502 _asl_action_message(aslmsg msg
)
1506 if (msg
== NULL
) return;
1508 /* reset flag bit used for file duplicate avoidance */
1509 for (r
= asl_action_rule
; r
!= NULL
; r
= r
->next
)
1511 if ((r
->action
== ACTION_FILE
) && (r
->data
!= NULL
))
1513 ((struct file_data
*)(r
->data
))->flags
&= ACT_FLAG_CLEAR_LOGGED
;
1515 else if (((r
->action
== ACTION_ASL_DIR
) || (r
->action
== ACTION_ASL_FILE
)) && (r
->data
!= NULL
))
1517 ((struct store_data
*)(r
->data
))->flags
&= ACT_FLAG_CLEAR_LOGGED
;
1521 for (r
= asl_action_rule
; r
!= NULL
; r
= r
->next
)
1523 if (asl_msg_cmp(r
->query
, (asl_msg_t
*)msg
) == 1)
1525 if ((r
->action
== ACTION_ASL_FILE
) || (r
->action
== ACTION_ASL_DIR
))
1528 if (asl_check_option(msg
, ASL_OPT_IGNORE
) != 0) return;
1531 if (r
->action
== ACTION_NONE
) continue;
1532 else if (r
->action
== ACTION_IGNORE
) return;
1533 else if (r
->action
== ACTION_ACCESS
) _act_access_control(r
, msg
);
1534 else if (r
->action
== ACTION_NOTIFY
) _act_notify(r
);
1535 else if (r
->action
== ACTION_BROADCAST
) _act_broadcast(r
, msg
);
1536 else if (r
->action
== ACTION_FILE
) _act_file(r
, msg
);
1537 else if (r
->action
== ACTION_FORWARD
) _act_forward(r
, msg
);
1541 if (asl_check_option(msg
, ASL_OPT_IGNORE
) != 0) return;
1543 _send_to_asl_store(msg
);
1547 asl_out_message(aslmsg msg
)
1549 dispatch_flush_continuation_cache();
1551 asl_msg_retain((asl_msg_t
*)msg
);
1553 dispatch_async(asl_action_queue
, ^{
1554 _asl_action_message(msg
);
1555 asl_msg_release((asl_msg_t
*)msg
);
1560 _parse_config_file(const char *name
)
1565 cf
= fopen(name
, "r");
1566 if (cf
== NULL
) return 1;
1568 while (NULL
!= (line
= get_line_from_file(cf
)))
1580 asl_action_init(void)
1582 static dispatch_once_t once
;
1584 asldebug("%s: init\n", MY_ID
);
1585 _parse_config_file(_PATH_ASL_CONF
);
1587 dispatch_once(&once
, ^{
1588 asl_action_queue
= dispatch_queue_create("ASL Action Queue", NULL
);
1595 _asl_action_close_internal(void)
1597 action_rule_t
*r
, *n
;
1598 struct store_data
*sd
;
1599 struct file_data
*fdata
;
1601 for (r
= asl_action_rule
; r
!= NULL
; r
= n
)
1604 if (r
->data
!= NULL
)
1606 if (((r
->action
== ACTION_ASL_FILE
) || (r
->action
== ACTION_ASL_DIR
) || (r
->action
== ACTION_NONE
)))
1608 sd
= (struct store_data
*)r
->data
;
1609 if (sd
->refcount
> 0) sd
->refcount
--;
1610 if (sd
->refcount
== 0)
1612 if (sd
->store
!= NULL
) asl_file_close(sd
->store
);
1613 if (sd
->storedata
!= NULL
) fclose(sd
->storedata
);
1621 if (r
->action
== ACTION_FILE
)
1623 fdata
= (struct file_data
*)r
->data
;
1624 if (fdata
->refcount
> 0) fdata
->refcount
--;
1625 if (fdata
->refcount
== 0)
1627 _act_file_send_repeat_msg(fdata
);
1629 if (fdata
->dup_timer
!= NULL
)
1631 dispatch_source_cancel(fdata
->dup_timer
);
1632 dispatch_resume(fdata
->dup_timer
);
1633 dispatch_release(fdata
->dup_timer
);
1640 free(fdata
->last_msg
);
1646 if (r
->query
!= NULL
) asl_msg_release(r
->query
);
1652 asl_action_rule
= NULL
;
1655 for (r
= asl_datastore_rule
; r
!= NULL
; r
= n
)
1659 if (r
->query
!= NULL
) asl_msg_release(r
->query
);
1665 asl_datastore_rule
= NULL
;
1671 asl_action_close(void)
1673 dispatch_async(asl_action_queue
, ^{
1674 _asl_action_close_internal();
1681 asl_action_reset(void)
1683 dispatch_async(asl_action_queue
, ^{
1684 _asl_action_close_internal();
1692 asl_action_file_rotate(const char *path
)
1695 * The caller may want to know when the rotation has been completed,
1696 * so this is synchronous. Also ensures the string stays intact while we work.
1698 dispatch_sync(asl_action_queue
, ^{
1699 _act_file_rotate(path
);