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
);
603 asprintf(&(sd
->path
), "%s/%d.%02d.%02d.asl", sd
->dir
, ctm
.tm_year
+ 1900, ctm
.tm_mon
+ 1, ctm
.tm_mday
);
604 if (sd
->path
== NULL
) return ASL_STATUS_NO_MEMORY
;
606 sd
->p_year
= ctm
.tm_year
;
607 sd
->p_month
= ctm
.tm_mon
;
608 sd
->p_day
= ctm
.tm_mday
;
610 return ASL_STATUS_OK
;
614 _act_store_init(action_rule_t
*r
)
616 struct store_data
*sd
, *xd
;
617 char *str
, *opts
, *p
, *path
;
620 /* check if the store data is already set up */
621 if (r
->data
!= NULL
) return;
624 path
= _next_word(&opts
);
626 if ((path
== NULL
) || (path
[0] != '/'))
629 asprintf(&str
, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Invalid path for \"%s\" action: %s]",
631 ASL_KEY_LEVEL
, ASL_LEVEL_ERR
,
632 ASL_KEY_PID
, getpid(),
634 (r
->action
== ACTION_ASL_FILE
) ? "store" : "store_directory",
635 (path
== NULL
) ? "no path specified" : path
);
637 internal_log_message(str
);
639 r
->action
= ACTION_NONE
;
644 /* check if a previous rule has set up this path (ACTION_ASL_FILE) or dir (ACTION_ASL_DIR) */
645 for (x
= asl_action_rule
; x
!= NULL
; x
= x
->next
)
647 if ((x
->action
== r
->action
) && (x
->data
!= NULL
))
649 xd
= (struct store_data
*)x
->data
;
651 if (r
->action
== ACTION_ASL_DIR
) p
= xd
->dir
;
653 if ((p
!= NULL
) && (!strcmp(path
, p
)))
663 /* set up store data */
664 sd
= (struct store_data
*)calloc(1, sizeof(struct store_data
));
665 if (sd
== NULL
) return;
674 if (r
->action
== ACTION_ASL_DIR
) sd
->dir
= path
;
675 else sd
->path
= path
;
677 while (NULL
!= (p
= _next_word(&opts
)))
679 if (!strcmp(p
, "stayopen"))
681 sd
->flags
|= ACT_STORE_FLAG_STAY_OPEN
;
683 else if (!strcmp(p
, "continue"))
685 sd
->flags
|= ACT_STORE_FLAG_CONTINUE
;
687 else if (!strncmp(p
, "mode=", 5)) sd
->mode
= strtol(p
+5, NULL
, 0);
688 else if (!strncmp(p
, "uid=", 4)) sd
->uid
= atoi(p
+4);
689 else if (!strncmp(p
, "gid=", 4)) sd
->gid
= atoi(p
+4);
699 * Save a message to an ASL format file (ACTION_ASL_FILE)
700 * or to an ASL directory (ACTION_ASL_DIR).
703 _act_store(action_rule_t
*r
, aslmsg msg
)
705 struct store_data
*sd
;
716 if (r
->data
== NULL
) return;
718 sd
= (struct store_data
*)r
->data
;
720 if (sd
->flags
& ACT_FLAG_HAS_LOGGED
) return;
721 sd
->flags
|= ACT_FLAG_HAS_LOGGED
;
723 if (r
->action
== ACTION_ASL_DIR
)
725 val
= asl_get(msg
, ASL_KEY_TIME
);
726 if (val
== NULL
) return;
729 status
= _act_store_dir_setup(sd
, tick
);
730 if (status
!= ASL_STATUS_OK
)
732 asldebug("_act_store_dir_setup %s failed: %s\n", sd
->path
, asl_core_error(status
));
736 /* disable further activity after multiple failures */
737 if (sd
->fails
> MAX_FAILURES
)
740 asprintf(&str
, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Disabling writes to path %s following %u failures (%s)]",
742 ASL_KEY_LEVEL
, ASL_LEVEL_ERR
,
743 ASL_KEY_PID
, getpid(),
744 ASL_KEY_MSG
, sd
->path
, sd
->fails
, asl_core_error(status
));
746 internal_log_message(str
);
749 asl_file_close(sd
->store
);
751 r
->action
= ACTION_NONE
;
761 if (sd
->store
== NULL
)
766 status
= asl_file_open_write(sd
->path
, (sd
->mode
& 0666), sd
->uid
, sd
->gid
, &s
);
769 if ((status
!= ASL_STATUS_OK
) || (s
== NULL
))
771 asldebug("asl_file_open_write %s failed: %s\n", sd
->path
, asl_core_error(status
));
775 /* disable further activity after multiple failures */
776 if (sd
->fails
> MAX_FAILURES
)
779 asprintf(&str
, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Disabling writes to path %s following %u failures (%s)]",
781 ASL_KEY_LEVEL
, ASL_LEVEL_ERR
,
782 ASL_KEY_PID
, getpid(),
783 ASL_KEY_MSG
, sd
->path
, sd
->fails
, asl_core_error(status
));
785 internal_log_message(str
);
788 asl_file_close(sd
->store
);
790 r
->action
= ACTION_NONE
;
794 else if (status
== ASL_STATUS_OK
)
802 if (r
->action
!= ACTION_ASL_DIR
)
804 status
= _act_store_file_setup(sd
);
805 if (status
!= ASL_STATUS_OK
)
807 asldebug("_act_store_file_setup %s failed: %s\n", sd
->path
, asl_core_error(status
));
811 /* disable further activity after multiple failures */
812 if (sd
->fails
> MAX_FAILURES
)
815 asprintf(&str
, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Disabling writes to path %s following %u failures (%s)]",
817 ASL_KEY_LEVEL
, ASL_LEVEL_ERR
,
818 ASL_KEY_PID
, getpid(),
819 ASL_KEY_MSG
, sd
->path
, sd
->fails
, asl_core_error(status
));
821 internal_log_message(str
);
824 asl_file_close(sd
->store
);
826 r
->action
= ACTION_NONE
;
838 status
= asl_file_save(sd
->store
, msg
, &mid
);
839 if (status
!= ASL_STATUS_OK
)
841 asldebug("asl_file_save %s failed: %s\n", sd
->path
, asl_core_error(status
));
845 /* disable further activity after multiple failures */
846 if (sd
->fails
> MAX_FAILURES
)
849 asprintf(&str
, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Disabling writes to path %s following %u failures (%s)]",
851 ASL_KEY_LEVEL
, ASL_LEVEL_ERR
,
852 ASL_KEY_PID
, getpid(),
853 ASL_KEY_MSG
, sd
->path
, sd
->fails
, asl_core_error(status
));
855 internal_log_message(str
);
858 asl_file_close(sd
->store
);
860 r
->action
= ACTION_NONE
;
869 if ((sd
->flags
& ACT_STORE_FLAG_STAY_OPEN
) == 0)
871 asl_file_close(sd
->store
);
875 if ((sd
->flags
& ACT_STORE_FLAG_CONTINUE
) == 0)
877 opts
= (char *)asl_get(msg
, ASL_KEY_OPTION
);
880 asl_set(msg
, ASL_KEY_OPTION
, ASL_OPT_IGNORE
);
885 asprintf(&str
, "%s %s", ASL_OPT_IGNORE
, opts
);
888 asl_set(msg
, ASL_KEY_OPTION
, str
);
896 _act_file_send_repeat_msg(struct file_data
*fdata
)
899 int len
, status
, closeit
;
900 time_t now
= time(NULL
);
902 if (fdata
== NULL
) return -1;
904 free(fdata
->last_msg
);
905 fdata
->last_msg
= NULL
;
907 if (fdata
->last_count
== 0) return 0;
910 dispatch_suspend(fdata
->dup_timer
);
912 memset(vt
, 0, sizeof(vt
));
917 asprintf(&msg
, "%s --- last message repeated %u time%s ---\n", vt
+ 4, fdata
->last_count
, (fdata
->last_count
== 1) ? "" : "s");
918 fdata
->last_count
= 0;
919 if (msg
== NULL
) return -1;
925 fdata
->fd
= _act_file_open(fdata
);
928 asldebug("%s: error opening for repeat message (%s): %s\n", MY_ID
, fdata
->path
, strerror(errno
));
934 status
= write(fdata
->fd
, msg
, len
);
942 if ((status
< 0) || (status
< len
))
944 asldebug("%s: error writing repeat message (%s): %s\n", MY_ID
, fdata
->path
, strerror(errno
));
952 * N.B. This is basic file rotation support.
953 * More rotation options will be added in the future, along
954 * with support in aslmanager for compression and deletion.
957 _act_file_rotate_file_data(struct file_data
*fdata
, time_t now
)
959 char str
[MAXPATHLEN
];
963 if (now
== 0) now
= time(NULL
);
965 /* flush duplicates if pending */
966 _act_file_send_repeat_msg(fdata
);
968 /* sleep to prevent a sub-second rotation */
969 while (now
== fdata
->stamp
)
975 len
= strlen(fdata
->path
);
977 if ((len
> 4) && (!strcasecmp(fdata
->path
+ width
, ".log")))
979 /* ".log" rename: abc.log -> abc.timestamp.log */
980 snprintf(str
, sizeof(str
), "%.*s.%lu.log", width
, fdata
->path
, fdata
->stamp
);
984 snprintf(str
, sizeof(str
), "%s.%lu", fdata
->path
, fdata
->stamp
);
987 rename(fdata
->path
, str
);
993 _act_file_open(struct file_data
*fdata
)
1005 memset(&sb
, 0, sizeof(struct stat
));
1006 status
= stat(fdata
->path
, &sb
);
1009 /* must be a regular file */
1010 if (!S_ISREG(sb
.st_mode
)) return -1;
1012 /* use st_birthtimespec if stamp is zero */
1013 if (fdata
->stamp
== 0) fdata
->stamp
= sb
.st_birthtimespec
.tv_sec
;
1015 /* rotate if over size limit */
1016 if ((fdata
->max_size
> 0) && (sb
.st_size
> fdata
->max_size
))
1018 _act_file_rotate_file_data(fdata
, 0);
1022 /* open existing file */
1023 fd
= open(fdata
->path
, O_RDWR
| O_APPEND
| O_EXCL
, 0);
1027 else if (errno
!= ENOENT
)
1032 #if TARGET_OS_EMBEDDED
1033 return open(fdata
->path
, O_RDWR
| O_CREAT
| O_EXCL
, (fdata
->mode
& 0666));
1038 for (i
= 0; i
< fdata
->ngid
; i
++)
1040 status
= mbr_gid_to_uuid(fdata
->gid
[i
], uuid
);
1044 asprintf(&str
, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Unknown GID %d for \"file\" action: %s]",
1046 ASL_KEY_LEVEL
, ASL_LEVEL_ERR
,
1047 ASL_KEY_PID
, getpid(),
1048 ASL_KEY_MSG
, fdata
->gid
[i
], fdata
->path
);
1050 internal_log_message(str
);
1055 status
= acl_create_entry_np(&acl
, &entry
, ACL_FIRST_ENTRY
);
1056 if (status
!= 0) goto asl_file_create_return
;
1058 status
= acl_set_tag_type(entry
, ACL_EXTENDED_ALLOW
);
1059 if (status
!= 0) goto asl_file_create_return
;
1061 status
= acl_set_qualifier(entry
, &uuid
);
1062 if (status
!= 0) goto asl_file_create_return
;
1064 status
= acl_get_permset(entry
, &perms
);
1065 if (status
!= 0) goto asl_file_create_return
;
1067 status
= acl_add_perm(perms
, ACL_READ_DATA
);
1068 if (status
!= 0) goto asl_file_create_return
;
1071 for (i
= 0; i
< fdata
->nuid
; i
++)
1073 status
= mbr_uid_to_uuid(fdata
->uid
[i
], uuid
);
1077 asprintf(&str
, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Unknown UID %d for \"file\" action: %s]",
1079 ASL_KEY_LEVEL
, ASL_LEVEL_ERR
,
1080 ASL_KEY_PID
, getpid(),
1081 ASL_KEY_MSG
, fdata
->uid
[i
], fdata
->path
);
1083 internal_log_message(str
);
1088 status
= acl_create_entry_np(&acl
, &entry
, ACL_FIRST_ENTRY
);
1089 if (status
!= 0) goto asl_file_create_return
;
1091 status
= acl_set_tag_type(entry
, ACL_EXTENDED_ALLOW
);
1092 if (status
!= 0) goto asl_file_create_return
;
1094 status
= acl_set_qualifier(entry
, &uuid
);
1095 if (status
!= 0) goto asl_file_create_return
;
1097 status
= acl_get_permset(entry
, &perms
);
1098 if (status
!= 0) goto asl_file_create_return
;
1100 status
= acl_add_perm(perms
, ACL_READ_DATA
);
1101 if (status
!= 0) goto asl_file_create_return
;
1105 fd
= open(fdata
->path
, O_RDWR
| O_CREAT
| O_EXCL
, (fdata
->mode
& 0666));
1107 if (fd
< 0) goto asl_file_create_return
;
1110 status
= acl_set_fd(fd
, acl
);
1116 unlink(fdata
->path
);
1119 asl_file_create_return
:
1127 _act_file_rotate(const char *path
)
1130 struct file_data
*fdata
;
1131 time_t now
= time(NULL
);
1133 for (r
= asl_action_rule
; r
!= NULL
; r
= r
->next
)
1135 if (r
->action
== ACTION_FILE
)
1137 fdata
= (struct file_data
*)r
->data
;
1138 if (fdata
->flags
& ACT_FILE_FLAG_ROTATE
)
1140 if ((path
== NULL
) || ((fdata
->path
!= NULL
) && !strcmp(fdata
->path
, path
)))
1142 _act_file_rotate_file_data(fdata
, now
);
1150 _act_file_format_string(char *s
)
1155 if (s
== NULL
) return NULL
;
1159 for (i
= 0; i
< len
; i
++) if (s
[i
] == '\\') n
++;
1161 fmt
= malloc(1 + len
- n
);
1162 if (fmt
== NULL
) return NULL
;
1164 for (i
= 0, n
= 0; i
< len
; i
++) if (s
[i
] != '\\') fmt
[n
++] = s
[i
];
1170 _act_file_max_size(char *s
)
1175 if (s
== NULL
) return 0;
1178 if (len
== 0) return 0;
1182 if (x
> 90) x
-= 32;
1183 if (x
== 'K') n
= 1ll << 10;
1184 else if (x
== 'M') n
= 1ll << 20;
1185 else if (x
== 'G') n
= 1ll << 30;
1186 else if (x
== 'T') n
= 1ll << 40;
1193 _act_file_add_uid(struct file_data
*fdata
, char *s
)
1195 if (fdata
== NULL
) return;
1196 if (s
== NULL
) return;
1198 fdata
->uid
= reallocf(fdata
->uid
, (fdata
->nuid
+ 1) * sizeof(uid_t
));
1199 if (fdata
->uid
== NULL
)
1205 fdata
->uid
[fdata
->nuid
++] = atoi(s
);
1209 _act_file_add_gid(struct file_data
*fdata
, char *s
)
1211 if (fdata
== NULL
) return;
1212 if (s
== NULL
) return;
1214 fdata
->gid
= reallocf(fdata
->gid
, (fdata
->ngid
+ 1) * sizeof(gid_t
));
1215 if (fdata
->gid
== NULL
)
1221 fdata
->gid
[fdata
->ngid
++] = atoi(s
);
1225 _act_file_init(action_rule_t
*r
)
1227 struct file_data
*fdata
, *xdata
;
1228 char *str
, *opts
, *p
, *path
;
1231 /* check if the file data is already set up */
1232 if (r
->data
!= NULL
) return;
1234 /* requires at least a path */
1235 if (r
->options
== NULL
) return;
1237 path
= _next_word(&opts
);
1239 if ((path
== NULL
) || (path
[0] != '/'))
1242 asprintf(&str
, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Invalid path for \"file\" action: %s]",
1244 ASL_KEY_LEVEL
, ASL_LEVEL_ERR
,
1245 ASL_KEY_PID
, getpid(),
1246 ASL_KEY_MSG
, (path
== NULL
) ? "no path specified" : path
);
1248 internal_log_message(str
);
1251 r
->action
= ACTION_NONE
;
1255 /* check if a previous rule has set up this path */
1256 for (x
= asl_action_rule
; x
!= NULL
; x
= x
->next
)
1258 if ((x
->action
== ACTION_FILE
) && (x
->data
!= NULL
))
1260 xdata
= (struct file_data
*)x
->data
;
1261 if ((xdata
->path
!= NULL
) && (!strcmp(path
, xdata
->path
)))
1271 /* set up file data */
1272 fdata
= (struct file_data
*)calloc(1, sizeof(struct file_data
));
1273 if (fdata
== NULL
) return;
1275 fdata
->refcount
= 1;
1280 * mode= set file creation mode
1281 * uid= user added to read ACL
1282 * gid= group added to read ACL
1283 * format= format string (also fmt=)
1284 * no_dup_supress no duplicate supression
1286 * rotate automatic daily rotation
1287 * this is basic rotation - more support is TBD
1290 fdata
->flags
= ACT_FILE_FLAG_DUP_SUPRESS
;
1292 while (NULL
!= (p
= _next_word(&opts
)))
1294 if (!strncmp(p
, "mode=", 5)) fdata
->mode
= strtol(p
+5, NULL
, 0);
1295 else if (!strncmp(p
, "uid=", 4)) _act_file_add_uid(fdata
, p
+4);
1296 else if (!strncmp(p
, "gid=", 4)) _act_file_add_gid(fdata
, p
+4);
1297 else if (!strncmp(p
, "fmt=", 4)) fdata
->fmt
= _act_file_format_string(p
+4);
1298 else if (!strncmp(p
, "format=", 7)) fdata
->fmt
= _act_file_format_string(p
+7);
1299 else if (!strncmp(p
, "no_dup_supress", 14)) fdata
->flags
&= ~ACT_FILE_FLAG_DUP_SUPRESS
;
1300 else if (!strncmp(p
, "rotate", 6)) fdata
->flags
|= ACT_FILE_FLAG_ROTATE
;
1301 else if (!strncmp(p
, "max_size=", 9)) fdata
->max_size
= _act_file_max_size(p
+9);
1307 if (fdata
->fmt
== NULL
) fdata
->fmt
= strdup("std");
1309 /* duplicate compression is only possible for std and bsd formats */
1310 if (strcmp(fdata
->fmt
, "std") && strcmp(fdata
->fmt
, "bsd")) fdata
->flags
&= ~ACT_FILE_FLAG_DUP_SUPRESS
;
1312 /* set time format for raw output */
1313 if (!strcmp(fdata
->fmt
, "raw")) fdata
->tfmt
= "sec";
1319 _act_file(action_rule_t
*r
, aslmsg msg
)
1321 struct file_data
*fdata
;
1323 uint32_t len
, msg_hash
= 0;
1328 if (r
->data
== NULL
) return;
1330 fdata
= (struct file_data
*)r
->data
;
1335 memset(&ctm
, 0, sizeof(struct tm
));
1336 if (localtime_r((const time_t *)&now
, &ctm
) != NULL
)
1341 today
= mktime(&ctm
);
1344 /* check for rotation */
1345 if ((last_file_day
!= 0) && (last_file_day
!= today
))
1347 _act_file_rotate(NULL
);
1350 last_file_day
= today
;
1354 * asl.conf may contain multuple rules for the same file, eg:
1355 * ? [= Facility zippy] /var/log/abc.log
1356 * ? [= Color purple] /var/log/abc.log
1358 * To prevent duplicates we set a flag bit when a message is logged
1359 * to this file, and bail out if it has already been logged.
1360 * Note that asl_out_message clears the flag bit in all file_data
1361 * structures before processing each message.
1363 if (fdata
->flags
& ACT_FLAG_HAS_LOGGED
) return;
1364 fdata
->flags
|= ACT_FLAG_HAS_LOGGED
;
1368 str
= asl_format_message((asl_msg_t
*)msg
, fdata
->fmt
, fdata
->tfmt
, ASL_ENCODE_SAFE
, &len
);
1370 if (fdata
->flags
& ACT_FILE_FLAG_DUP_SUPRESS
)
1372 if (fdata
->dup_timer
== NULL
)
1374 /* create a timer to flush dups on this file */
1375 fdata
->dup_timer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, asl_action_queue
);
1376 dispatch_source_set_event_handler(fdata
->dup_timer
, ^{ _act_file_send_repeat_msg((struct file_data
*)r
->data
); });
1379 if ((global
.bsd_max_dup_time
> 0) && (str
!= NULL
) && (fdata
->last_msg
!= NULL
))
1381 msg_hash
= asl_core_string_hash(str
+ 16, len
- 16);
1382 if ((fdata
->last_hash
== msg_hash
) && (!strcmp(fdata
->last_msg
, str
+ 16)))
1384 if ((now
- fdata
->last_time
) < global
.bsd_max_dup_time
) is_dup
= 1;
1391 if (fdata
->last_count
== 0)
1393 /* start the timer */
1394 dispatch_source_set_timer(fdata
->dup_timer
, dispatch_time(DISPATCH_TIME_NOW
, NSEC_PER_SEC
* global
.bsd_max_dup_time
), DISPATCH_TIME_FOREVER
, 0);
1395 dispatch_resume(fdata
->dup_timer
);
1398 fdata
->last_count
++;
1402 fdata
->fd
= _act_file_open(fdata
);
1405 asldebug("_act_file_open %s failed: %s\n", fdata
->path
, strerror(errno
));
1409 /* disable further activity after multiple failures */
1410 if (fdata
->fails
> MAX_FAILURES
)
1413 asprintf(&tmp
, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Disabling writes to path %s following %u failures (%s)]",
1415 ASL_KEY_LEVEL
, ASL_LEVEL_ERR
,
1416 ASL_KEY_PID
, getpid(),
1417 ASL_KEY_MSG
, fdata
->path
, fdata
->fails
, strerror(errno
));
1419 internal_log_message(tmp
);
1422 r
->action
= ACTION_NONE
;
1433 * The current message is not a duplicate. If fdata->last_count > 0
1434 * we need to write a "last message repeated N times" log entry.
1435 * _act_file_send_repeat_msg will free last_msg and do nothing if
1436 * last_count == 0, but we test and free here to avoid a function call.
1438 if (fdata
->last_count
> 0)
1440 _act_file_send_repeat_msg(fdata
);
1444 free(fdata
->last_msg
);
1445 fdata
->last_msg
= NULL
;
1448 if (str
!= NULL
) fdata
->last_msg
= strdup(str
+ 16);
1450 fdata
->last_hash
= msg_hash
;
1451 fdata
->last_count
= 0;
1452 fdata
->last_time
= now
;
1454 if ((str
!= NULL
) && (len
> 1)) write(fdata
->fd
, str
, len
- 1);
1463 _act_forward(action_rule_t
*r
, aslmsg msg
)
1465 /* To do: <rdar://problem/6130747> Add a "forward" action to asl.conf */
1469 _send_to_asl_store(aslmsg msg
)
1474 /* ASLOption "store" forces a message to be saved */
1475 log_me
= asl_check_option(msg
, ASL_OPT_STORE
);
1478 db_save_message(msg
);
1482 /* if there are no rules, save the message */
1483 if (asl_datastore_rule
== NULL
)
1485 db_save_message(msg
);
1489 for (r
= asl_datastore_rule
; r
!= NULL
; r
= r
->next
)
1491 if (asl_msg_cmp(r
->query
, (asl_msg_t
*)msg
) == 1)
1493 /* if any rule matches, save the message (once!) */
1494 db_save_message(msg
);
1501 _asl_action_message(aslmsg msg
)
1505 if (msg
== NULL
) return;
1507 /* reset flag bit used for file duplicate avoidance */
1508 for (r
= asl_action_rule
; r
!= NULL
; r
= r
->next
)
1510 if ((r
->action
== ACTION_FILE
) && (r
->data
!= NULL
))
1512 ((struct file_data
*)(r
->data
))->flags
&= ACT_FLAG_CLEAR_LOGGED
;
1514 else if (((r
->action
== ACTION_ASL_DIR
) || (r
->action
== ACTION_ASL_FILE
)) && (r
->data
!= NULL
))
1516 ((struct store_data
*)(r
->data
))->flags
&= ACT_FLAG_CLEAR_LOGGED
;
1520 for (r
= asl_action_rule
; r
!= NULL
; r
= r
->next
)
1522 if (asl_msg_cmp(r
->query
, (asl_msg_t
*)msg
) == 1)
1524 if ((r
->action
== ACTION_ASL_FILE
) || (r
->action
== ACTION_ASL_DIR
))
1527 if (asl_check_option(msg
, ASL_OPT_IGNORE
) != 0) return;
1530 if (r
->action
== ACTION_NONE
) continue;
1531 else if (r
->action
== ACTION_IGNORE
) return;
1532 else if (r
->action
== ACTION_ACCESS
) _act_access_control(r
, msg
);
1533 else if (r
->action
== ACTION_NOTIFY
) _act_notify(r
);
1534 else if (r
->action
== ACTION_BROADCAST
) _act_broadcast(r
, msg
);
1535 else if (r
->action
== ACTION_FILE
) _act_file(r
, msg
);
1536 else if (r
->action
== ACTION_FORWARD
) _act_forward(r
, msg
);
1540 if (asl_check_option(msg
, ASL_OPT_IGNORE
) != 0) return;
1542 _send_to_asl_store(msg
);
1546 asl_out_message(aslmsg msg
)
1548 dispatch_flush_continuation_cache();
1550 asl_msg_retain((asl_msg_t
*)msg
);
1552 dispatch_async(asl_action_queue
, ^{
1553 _asl_action_message(msg
);
1554 asl_msg_release((asl_msg_t
*)msg
);
1559 _parse_config_file(const char *name
)
1564 cf
= fopen(name
, "r");
1565 if (cf
== NULL
) return 1;
1567 while (NULL
!= (line
= get_line_from_file(cf
)))
1579 asl_action_init(void)
1581 static dispatch_once_t once
;
1583 asldebug("%s: init\n", MY_ID
);
1584 _parse_config_file(_PATH_ASL_CONF
);
1586 dispatch_once(&once
, ^{
1587 asl_action_queue
= dispatch_queue_create("ASL Action Queue", NULL
);
1594 _asl_action_close_internal(void)
1596 action_rule_t
*r
, *n
;
1597 struct store_data
*sd
;
1598 struct file_data
*fdata
;
1600 for (r
= asl_action_rule
; r
!= NULL
; r
= n
)
1603 if (r
->data
!= NULL
)
1605 if (((r
->action
== ACTION_ASL_FILE
) || (r
->action
== ACTION_ASL_DIR
) || (r
->action
== ACTION_NONE
)))
1607 sd
= (struct store_data
*)r
->data
;
1608 if (sd
->refcount
> 0) sd
->refcount
--;
1609 if (sd
->refcount
== 0)
1611 if (sd
->store
!= NULL
) asl_file_close(sd
->store
);
1612 if (sd
->storedata
!= NULL
) fclose(sd
->storedata
);
1620 if (r
->action
== ACTION_FILE
)
1622 fdata
= (struct file_data
*)r
->data
;
1623 if (fdata
->refcount
> 0) fdata
->refcount
--;
1624 if (fdata
->refcount
== 0)
1626 _act_file_send_repeat_msg(fdata
);
1628 if (fdata
->dup_timer
!= NULL
)
1630 dispatch_source_cancel(fdata
->dup_timer
);
1631 dispatch_resume(fdata
->dup_timer
);
1632 dispatch_release(fdata
->dup_timer
);
1639 free(fdata
->last_msg
);
1645 if (r
->query
!= NULL
) asl_msg_release(r
->query
);
1651 asl_action_rule
= NULL
;
1654 for (r
= asl_datastore_rule
; r
!= NULL
; r
= n
)
1658 if (r
->query
!= NULL
) asl_msg_release(r
->query
);
1664 asl_datastore_rule
= NULL
;
1670 asl_action_close(void)
1672 dispatch_async(asl_action_queue
, ^{
1673 _asl_action_close_internal();
1680 asl_action_reset(void)
1682 dispatch_async(asl_action_queue
, ^{
1683 _asl_action_close_internal();
1691 asl_action_file_rotate(const char *path
)
1694 * The caller may want to know when the rotation has been completed,
1695 * so this is synchronous. Also ensures the string stays intact while we work.
1697 dispatch_sync(asl_action_queue
, ^{
1698 _act_file_rotate(path
);