2 * Copyright (c) 2004-2013 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 <TargetConditionals.h>
25 #include <sys/types.h>
27 #include <sys/socket.h>
42 #include <membership.h>
43 #include <configuration_profile.h>
45 #include <xpc/private.h>
47 #define _PATH_WALL "/usr/bin/wall"
49 #define MY_ID "asl_action"
51 #define MAX_FAILURES 5
53 #define ACTION_STATUS_ERROR -1
54 #define ACTION_STATUS_OK 0
56 #define IDLE_CLOSE 300
58 #define PRIVATE_FLAG_NO_CLOSE 0x00000001 /* File or Store is closed by a cancellation handler */
60 #define DST_CLOSE_CHECKPOINT 0
61 #define DST_CLOSE_DELETED 1
62 #define DST_CLOSE_ERROR 2
63 #define DST_CLOSE_IDLE 3
64 #define DST_CLOSE_SHUTDOWN 4
65 static const char *why_str
[] =
74 #define forever for(;;)
76 static dispatch_queue_t asl_action_queue
;
77 static dispatch_source_t checkpoint_timer
;
78 static time_t sweep_time
= 0;
80 #if TARGET_OS_EMBEDDED
81 #ifndef CRASH_MOVER_SERVICE
82 #define CRASH_MOVER_SERVICE "com.apple.crash_mover"
84 static dispatch_queue_t crashlog_queue
;
85 static time_t crashmover_state
= 0;
86 static int crashmover_token
= -1;
89 typedef struct asl_file_data
96 dispatch_source_t monitor
;
97 } asl_action_asl_file_data_t
;
99 typedef struct asl_store_data
110 dispatch_source_t storedata_monitor
;
111 dispatch_source_t aslfile_monitor
;
112 } asl_action_asl_store_data_t
;
114 typedef struct file_data
123 dispatch_source_t dup_timer
;
124 dispatch_source_t monitor
;
125 } asl_action_file_data_t
;
127 typedef struct set_param_data
130 } asl_action_set_param_data_t
;
132 static int action_asl_store_count
;
133 static bool store_has_logged
;
135 extern void db_save_message(asl_msg_t
*m
);
138 static int _act_file_checkpoint_all(uint32_t force
);
139 static void _asl_action_post_process_rule(asl_out_module_t
*m
, asl_out_rule_t
*r
);
140 static void _asl_action_close_idle_files(time_t idle_time
);
141 static void _act_dst_close(asl_out_rule_t
*r
, int why
);
144 _act_out_set_param(asl_out_module_t
*m
, char *x
, bool eval
)
148 uint32_t count
, intval
;
150 l
= explode(s
, " \t");
151 if (l
== NULL
) return;
153 for (count
= 0; l
[count
] != NULL
; count
++);
160 if (!strcasecmp(l
[0], "enable"))
163 if (count
< 2) intval
= 1;
164 else intval
= atoi(l
[1]);
166 if (!eval
) intval
= (intval
== 0) ? 1 : 0;
168 if (intval
== 0) m
->flags
&= ~MODULE_FLAG_ENABLED
;
169 else m
->flags
|= MODULE_FLAG_ENABLED
;
173 else if (!strcasecmp(l
[0], "disable"))
175 /* = disable [1|0] */
176 if (count
< 2) intval
= 1;
177 else intval
= atoi(l
[1]);
179 if (!eval
) intval
= (intval
== 0) ? 1 : 0;
181 if (intval
!= 0) m
->flags
&= ~MODULE_FLAG_ENABLED
;
182 else m
->flags
|= MODULE_FLAG_ENABLED
;
189 if (!strcmp(m
->name
, ASL_MODULE_NAME
))
191 /* Other parameters may be set by com.apple.asl module */
192 control_set_param(x
, eval
);
197 _act_notify(asl_out_module_t
*m
, asl_out_rule_t
*r
)
199 if (m
== NULL
) return;
200 if ((m
->flags
& MODULE_FLAG_ENABLED
) == 0) return;
202 if (r
== NULL
) return;
203 if (r
->options
== NULL
) return;
205 notify_post(r
->options
);
209 _act_broadcast(asl_out_module_t
*m
, asl_out_rule_t
*r
, asl_msg_t
*msg
)
211 #if !TARGET_OS_EMBEDDED
215 if (m
== NULL
) return;
216 if ((m
->flags
& MODULE_FLAG_ENABLED
) == 0) return;
218 if (m
->name
== NULL
) return;
219 if (r
== NULL
) return;
220 if (msg
== NULL
) return;
222 /* only base module (asl.conf) may broadcast */
223 if (strcmp(m
->name
, ASL_MODULE_NAME
)) return;
226 if (val
== NULL
) val
= asl_msg_get_val_for_key(msg
, ASL_KEY_MSG
);
227 if (val
== NULL
) return;
229 pw
= popen(_PATH_WALL
, "w");
232 asldebug("%s: error sending wall message: %s\n", MY_ID
, strerror(errno
));
236 fprintf(pw
, "%s", val
);
242 _act_set_key(asl_out_module_t
*m
, asl_out_rule_t
*r
, asl_msg_t
*msg
)
248 _act_unset_key(asl_out_module_t
*m
, asl_out_rule_t
*r
, asl_msg_t
*msg
)
254 _act_access_control(asl_out_module_t
*m
, asl_out_rule_t
*r
, asl_msg_t
*msg
)
259 if (m
== NULL
) return;
260 if (m
->name
== NULL
) return;
261 if (r
== NULL
) return;
262 if (msg
== NULL
) return;
264 /* only base module (asl.conf) may set access controls */
265 if (strcmp(m
->name
, ASL_MODULE_NAME
)) return;
267 ruid
= atoi(r
->options
);
269 p
= strchr(r
->options
, ' ');
270 if (p
== NULL
) p
= strchr(r
->options
, '\t');
278 if (ruid
!= -1) asl_msg_set_key_val(msg
, ASL_KEY_READ_UID
, r
->options
);
281 if (rgid
!= -1) asl_msg_set_key_val(msg
, ASL_KEY_READ_GID
, p
);
287 #if TARGET_OS_EMBEDDED
289 _crashlog_queue_check(void)
292 * Check whether the crashlog queue has been suspended for too long.
293 * We allow the crashlog quque to be suspended for 60 seconds.
294 * After that, we start logging again. This prevents syslogd from
295 * filling memory due to a suspended queue. CrashMover really shoud
296 * take no more than a second or two to finish.
298 if (crashmover_state
== 0) return;
299 if ((time(NULL
) - crashmover_state
) <= 60) return;
301 asldebug("CrashMover timeout: resuming crashlog queue\n");
302 dispatch_resume(crashlog_queue
);
303 crashmover_state
= 0;
308 * cover routine for asl_out_dst_file_create_open()
311 _act_file_create_open(asl_out_dst_data_t
*dst
)
313 return asl_out_dst_file_create_open(dst
, NULL
);
317 * Checks and creates (if required) a directory for an ASL data store.
320 _asl_dir_create(asl_out_rule_t
*r
)
325 memset(&sb
, 0, sizeof(struct stat
));
326 status
= stat(r
->dst
->path
, &sb
);
329 /* Store path must be a directory */
330 if (!S_ISDIR(sb
.st_mode
))
332 asldebug("_asl_dir_create: expected a directory at path %s\n", r
->dst
->path
);
336 else if (errno
== ENOENT
)
338 /* Directory doesn't exists - try to create it */
339 status
= asl_out_mkpath(global
.asl_out_module
, r
);
342 asldebug("_asl_dir_create: asl_out_mkpath failed: %s\n", r
->dst
->path
);
348 /* Unexpected stat error */
349 asldebug("_asl_dir_create: stat error %s\n", strerror(errno
));
357 * Close an ASL Directory StoreData file
358 * - cancel the dispatch source for the file
361 _asl_dir_storedata_close(asl_out_rule_t
*r
)
363 asl_action_asl_store_data_t
*as_data
= (asl_action_asl_store_data_t
*)r
->dst
->private;
364 if (as_data
->storedata
== NULL
) return;
366 if (as_data
->storedata_monitor
== NULL
)
369 * This should never happen, but _asl_dir_storedata_open allows
370 * dispatch_source_create to fail silently. If there is no dispatch
371 * source, we just close the file.
373 asldebug("close ASL storedata fd %d\n", fileno(as_data
->storedata
));
374 fclose(as_data
->storedata
);
379 * The storedata_monitor cancel handler will close the file.
381 dispatch_source_cancel(as_data
->storedata_monitor
);
382 dispatch_release(as_data
->storedata_monitor
);
386 asldebug("_asl_dir_storedata_close %p\n", as_data
->storedata
);
387 as_data
->storedata
= NULL
;
391 * Open an ASL Directory StoreData file
392 * - check directory existance and create it if necessary
393 * - check for StoreData file and create it (with given xid) if necessary.
394 * - create a dispatch source to watch for StoreData file deletion
397 _asl_dir_storedata_open(asl_out_rule_t
*r
, uint64_t xid
)
401 char dstpath
[MAXPATHLEN
];
403 asl_action_asl_store_data_t
*as_data
= (asl_action_asl_store_data_t
*)r
->dst
->private;
404 if (as_data
->storedata
!= NULL
) return 0;
406 status
= _asl_dir_create(r
);
409 asldebug("_asl_dir_storedata_open: No directory at path %s\n", r
->dst
->path
);
413 /* StoreData file is not open */
414 snprintf(dstpath
, sizeof(dstpath
), "%s/%s", r
->dst
->path
, FILE_ASL_STORE_DATA
);
416 memset(&sb
, 0, sizeof(struct stat
));
417 status
= stat(dstpath
, &sb
);
420 /* StoreData file exists */
421 as_data
->storedata
= fopen(dstpath
, "r+");
422 if (as_data
->storedata
== NULL
)
424 asldebug("_asl_dir_storedata_open: fopen existing %s: %s\n", dstpath
, strerror(errno
));
428 else if (errno
== ENOENT
)
431 * StoreData file does not exist.
432 * Create a new StoreData with a given xid.
434 //TODO: This should return a failure if there are any ASL files.
435 /* that would require reading the directory, thus a performance hit */
436 as_data
->storedata
= fopen(dstpath
, "w+");
437 if (as_data
->storedata
== NULL
)
439 asldebug("_asl_dir_storedata_open: fopen new %s: %s\n", dstpath
, strerror(errno
));
443 uint64_t xout
= asl_core_htonq(xid
);
444 status
= fwrite(&xout
, sizeof(uint64_t), 1, as_data
->storedata
);
447 asldebug("_asl_dir_storedata_open: storedata write failed %d %s\n", errno
, strerror(errno
));
451 #if !TARGET_IPHONE_SIMULATOR
452 if (chown(dstpath
, r
->dst
->uid
[0], r
->dst
->gid
[0]) != 0)
454 asldebug("_asl_dir_storedata_open: chown %d %d new %s: %s\n", dstpath
, r
->dst
->uid
[0], r
->dst
->gid
[0], strerror(errno
));
461 /* Unexpected stat error */
462 asldebug("_asl_dir_storedata_open: stat error %s\n", strerror(errno
));
466 /* create storedata_monitor */
467 int fd
= fileno(as_data
->storedata
);
468 FILE *sdfp
= as_data
->storedata
;
470 as_data
->storedata_monitor
= dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE
, fd
, DISPATCH_VNODE_DELETE
, asl_action_queue
);
471 if (as_data
->storedata_monitor
!= NULL
)
473 dispatch_source_set_event_handler(as_data
->storedata_monitor
, ^{
474 _act_dst_close(r
, DST_CLOSE_DELETED
);
477 dispatch_source_set_cancel_handler(as_data
->storedata_monitor
, ^{
478 asldebug("cancel/close ASL storedata fd %d\n", fd
);
482 dispatch_resume(as_data
->storedata_monitor
);
485 asldebug("_asl_dir_storedata_open ASL storedata %s fd %d\n", dstpath
, fd
);
490 * Close an ASL Directory ASL file
491 * - cancel the dispatch source for the file
492 * - clears file name and timestamp in asl_action_asl_store_data_t structue
495 _asl_dir_today_close(asl_out_rule_t
*r
)
497 asl_action_asl_store_data_t
*as_data
= (asl_action_asl_store_data_t
*)r
->dst
->private;
498 if (as_data
->aslfile
== NULL
) return;
500 if (as_data
->pending
!= 0)
503 asprintf(&str
, "[Sender syslogd] [Level 4] [PID %u] [Facility syslog] [Message ASL Store %s was closed with %d pending messages]", global
.pid
, r
->dst
->fname
, as_data
->pending
);
504 internal_log_message(str
);
508 if (as_data
->aslfile_monitor
== NULL
)
511 * This should never happen, but _asl_dir_today_open allows
512 * dispatch_source_create to fail silently. If there is no dispatch
513 * source, we just close the file.
515 asldebug("close ASL fd %d\n", fileno(as_data
->aslfile
->store
));
516 asl_file_close(as_data
->aslfile
);
521 * The aslfile_monitor cancel handler will close the file.
523 dispatch_source_cancel(as_data
->aslfile_monitor
);
524 dispatch_release(as_data
->aslfile_monitor
);
525 as_data
->aslfile_monitor
= NULL
;
529 as_data
->p_month
= 0;
533 r
->dst
->fname
= NULL
;
535 as_data
->aslfile
= NULL
;
542 _act_checkpoint(asl_out_rule_t
*r
, uint32_t force
)
544 char tmpfname
[MAXPATHLEN
], *fn
;
546 if (r
== NULL
) return 0;
547 if (r
->dst
== NULL
) return 0;
552 if (r
->dst
->path
== NULL
) return 0;
553 asl_make_dst_filename(r
->dst
, tmpfname
, sizeof(tmpfname
));
557 if ((force
== CHECKPOINT_TEST
) && (r
->dst
->file_max
== 0)) return 0;
559 if ((r
->dst
->size
== 0) || (r
->dst
->stamp
== 0))
563 memset(&sb
, 0, sizeof(struct stat
));
565 if (stat(fn
, &sb
) < 0)
567 if (errno
== ENOENT
) return 0;
571 if (r
->dst
->stamp
== 0) r
->dst
->stamp
= sb
.st_birthtimespec
.tv_sec
;
572 if (r
->dst
->stamp
== 0) r
->dst
->stamp
= sb
.st_mtimespec
.tv_sec
;
573 r
->dst
->size
= sb
.st_size
;
576 if ((force
== CHECKPOINT_TEST
) && (r
->dst
->size
< r
->dst
->file_max
)) return 0;
578 if (r
->dst
->flags
& MODULE_FLAG_BASESTAMP
)
580 _act_dst_close(r
, DST_CLOSE_CHECKPOINT
);
584 char srcpath
[MAXPATHLEN
];
585 char dstpath
[MAXPATHLEN
];
588 if (r
->dst
->stamp
== 0) r
->dst
->stamp
= time(NULL
);
589 asl_make_timestamp(r
->dst
->stamp
, r
->dst
->flags
, tstamp
, sizeof(tstamp
));
591 snprintf(srcpath
, sizeof(srcpath
), "%s", fn
);
592 snprintf(dstpath
, sizeof(dstpath
), "%s.%s", fn
, tstamp
);
594 _act_dst_close(r
, DST_CLOSE_CHECKPOINT
);
595 rename(srcpath
, dstpath
);
604 * Open today's ASL file in an ASL directory
605 * - Checks date and closes a currently open file if it has the wrong date
606 * - Opens today's file
609 _asl_dir_today_open(asl_out_rule_t
*r
, const time_t *tick
)
616 if (r
== NULL
) return -1;
617 if (r
->dst
== NULL
) return -1;
619 status
= _asl_dir_create(r
);
622 asldebug("_asl_dir_today_open: No directory at path %s\n", r
->dst
->path
);
626 asl_action_asl_store_data_t
*as_data
= (asl_action_asl_store_data_t
*)r
->dst
->private;
628 memset(&ctm
, 0, sizeof(struct tm
));
632 tick
= (const time_t *)&now
;
635 if (localtime_r(tick
, &ctm
) == NULL
)
637 asldebug("_asl_dir_today_open: localtime_r error %s\n", strerror(errno
));
641 /* checks file_max and closes if required */
642 status
= _act_checkpoint(r
, CHECKPOINT_TEST
);
643 if (status
== 1) trigger_aslmanager();
645 if (as_data
->aslfile
!= NULL
)
648 if ((as_data
->p_year
== ctm
.tm_year
) && (as_data
->p_month
== ctm
.tm_mon
) && (as_data
->p_day
== ctm
.tm_mday
)) return 0;
650 /* Wrong date, close the current file */
651 _asl_dir_today_close(r
);
656 if (r
->dst
->flags
& MODULE_FLAG_BASESTAMP
)
663 tick
= (const time_t *)&now
;
666 asl_make_timestamp(now
, r
->dst
->flags
, tstamp
, sizeof(tstamp
));
667 asprintf(&(r
->dst
->fname
), "%s/%s.asl", r
->dst
->path
, tstamp
);
671 asprintf(&(r
->dst
->fname
), "%s/%d.%02d.%02d.asl", r
->dst
->path
, ctm
.tm_year
+ 1900, ctm
.tm_mon
+ 1, ctm
.tm_mday
);
675 if (r
->dst
->fname
== NULL
)
677 asldebug("_asl_dir_today_open: asprintf error %s\n", strerror(errno
));
681 #if TARGET_IPHONE_SIMULATOR
685 uid_t uid
= r
->dst
->uid
[0];
686 gid_t gid
= r
->dst
->gid
[0];
690 status
= asl_file_open_write(r
->dst
->fname
, (r
->dst
->mode
& 00666), uid
, gid
, &(as_data
->aslfile
));
693 if (status
!= ASL_STATUS_OK
)
695 asldebug("_asl_dir_today_open: asl_file_open_write %s error %s\n", r
->dst
->fname
, asl_core_error(status
));
697 r
->dst
->fname
= NULL
;
701 if (fseek(as_data
->aslfile
->store
, 0, SEEK_END
) != 0)
703 asldebug("_asl_dir_today_open: fseek %s error %s\n", r
->dst
->fname
, strerror(errno
));
705 r
->dst
->fname
= NULL
;
709 as_data
->p_year
= ctm
.tm_year
;
710 as_data
->p_month
= ctm
.tm_mon
;
711 as_data
->p_day
= ctm
.tm_mday
;
713 /* create aslfile_monitor */
714 int fd
= fileno(as_data
->aslfile
->store
);
715 asl_file_t
*aslf
= as_data
->aslfile
;
717 as_data
->aslfile_monitor
= dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE
, fd
, DISPATCH_VNODE_DELETE
, asl_action_queue
);
718 if (as_data
->aslfile_monitor
!= NULL
)
720 dispatch_source_set_event_handler(as_data
->aslfile_monitor
, ^{
721 _act_dst_close(r
, DST_CLOSE_DELETED
);
724 dispatch_source_set_cancel_handler(as_data
->aslfile_monitor
, ^{
725 asldebug("cancel/close ASL file fd %d\n", fd
);
726 asl_file_close(aslf
);
729 dispatch_resume(as_data
->aslfile_monitor
);
732 asldebug("_asl_dir_today_open ASL file %s fd %d\n", r
->dst
->fname
, fd
);
738 _asl_file_close(asl_out_rule_t
*r
)
740 if (r
== NULL
) return;
741 if (r
->dst
== NULL
) return;
743 asl_action_asl_file_data_t
*af_data
= (asl_action_asl_file_data_t
*)r
->dst
->private;
744 if (af_data
->aslfile
== NULL
) return;
746 if (af_data
->pending
!= 0)
749 asprintf(&str
, "[Sender syslogd] [Level 4] [PID %u] [Facility syslog] [Message ASL File %s was closed with %d pending messages]", global
.pid
, r
->dst
->fname
, af_data
->pending
);
750 internal_log_message(str
);
754 if (af_data
->monitor
== NULL
)
757 * This should never happen, but _asl_file_open allows
758 * dispatch_source_create to fail silently. If there is no dispatch
759 * source, we just close the file.
761 asldebug("close ASL fd %d\n", fileno(af_data
->aslfile
->store
));
762 asl_file_close(af_data
->aslfile
);
767 * The monitor cancel handler will close the file.
769 dispatch_source_cancel(af_data
->monitor
);
770 dispatch_release(af_data
->monitor
);
771 af_data
->monitor
= NULL
;
774 af_data
->aslfile
= NULL
;
778 _asl_file_open(asl_out_rule_t
*r
)
782 if (r
== NULL
) return -1;
783 if (r
->dst
== NULL
) return -1;
785 asl_action_asl_file_data_t
*af_data
= (asl_action_asl_file_data_t
*)r
->dst
->private;
786 if (af_data
->aslfile
!= NULL
) return 0;
788 /* create path if necessary */
789 status
= asl_out_mkpath(global
.asl_out_module
, r
);
792 asldebug("_asl_file_open: asl_out_mkpath %s failed\n", r
->dst
->path
);
796 fd
= _act_file_create_open(r
->dst
);
799 asldebug("_asl_file_open: _act_file_create_open %s failed %d %s\n", r
->dst
->fname
, errno
, strerror(errno
));
805 if (r
->dst
->fname
== NULL
) return -1;
807 status
= asl_file_open_write(r
->dst
->fname
, 0, -1, -1, &(af_data
->aslfile
));
808 if (status
!= ASL_STATUS_OK
)
810 asldebug("_asl_file_open: asl_file_open_write %s failed %d %s\n", r
->dst
->fname
, errno
, strerror(errno
));
815 fd
= fileno(af_data
->aslfile
->store
);
816 asl_file_t
*aslf
= af_data
->aslfile
;
818 af_data
->monitor
= dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE
, fd
, DISPATCH_VNODE_DELETE
, asl_action_queue
);
819 if (af_data
->monitor
!= NULL
)
821 dispatch_source_set_event_handler(af_data
->monitor
, ^{
822 _act_dst_close(r
, DST_CLOSE_DELETED
);
825 dispatch_source_set_cancel_handler(af_data
->monitor
, ^{
826 asldebug("cancel/close ASL file fd %d\n", fd
);
827 asl_file_close(aslf
);
830 dispatch_resume(af_data
->monitor
);
833 asldebug("_asl_file_open ASL file %s fd %d\n", r
->dst
->fname
, fd
);
838 _text_file_close(asl_out_rule_t
*r
)
840 asl_action_file_data_t
*f_data
= (asl_action_file_data_t
*)r
->dst
->private;
841 if (f_data
->fd
< 0) return;
843 if (f_data
->pending
!= 0)
846 asprintf(&str
, "[Sender syslogd] [Level 4] [PID %u] [Facility syslog] [Message File %s was closed with %d pending messages]", global
.pid
, r
->dst
->fname
, f_data
->pending
);
847 internal_log_message(str
);
851 if (f_data
->monitor
== NULL
)
854 * This should never happen, but _text_file_open allows
855 * dispatch_source_create to fail silently. If there is no dispatch
856 * source, we just close the file.
858 asldebug("close fd %d\n", f_data
->fd
);
864 * The monitor cancel handler will close the file.
866 dispatch_source_cancel(f_data
->monitor
);
867 dispatch_release(f_data
->monitor
);
868 f_data
->monitor
= NULL
;
875 _text_file_open(asl_out_rule_t
*r
)
877 asl_action_file_data_t
*f_data
= (asl_action_file_data_t
*)r
->dst
->private;
881 f_data
->fd
= _act_file_create_open(r
->dst
);
885 * lazy path creation: create path and retry
886 * _act_file_create_open does not create the path
889 int status
= asl_out_mkpath(global
.asl_out_module
, r
);
890 if (status
!= 0) return -1;
892 f_data
->fd
= _act_file_create_open(r
->dst
);
895 if (f_data
->fd
< 0) return -1;
897 f_data
->monitor
= dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE
, f_data
->fd
, DISPATCH_VNODE_DELETE
, asl_action_queue
);
898 if (f_data
->monitor
!= NULL
)
900 int ffd
= f_data
->fd
;
902 dispatch_source_set_event_handler(f_data
->monitor
, ^{
903 asldebug("dispatch_source DISPATCH_VNODE_DELETE fd %d\n", ffd
);
904 _act_dst_close(r
, DST_CLOSE_DELETED
);
907 dispatch_source_set_cancel_handler(f_data
->monitor
, ^{
908 asldebug("cancel/close file fd %d\n", ffd
);
912 dispatch_resume(f_data
->monitor
);
920 _act_dst_open(asl_out_rule_t
*r
, const time_t *tick
, uint64_t xid
)
922 if (r
== NULL
) return -1;
923 if (r
->dst
== NULL
) return -1;
924 if (r
->dst
->private == NULL
) return -1;
926 if (r
->action
== ACTION_ASL_DIR
)
928 if (_asl_dir_today_open(r
, tick
) != 0)
930 asldebug("_act_dst_open:_asl_dir_today_open %s FAILED!\n", r
->dst
->path
);
934 if (_asl_dir_storedata_open(r
, xid
) != 0)
936 asldebug("_act_dst_open:_asl_dir_storedata_open %s FAILED! Closing today file\n", r
->dst
->path
);
937 _asl_dir_today_close(r
);
944 if (r
->action
== ACTION_ASL_FILE
)
946 return _asl_file_open(r
);
949 if (r
->action
== ACTION_FILE
)
951 return _text_file_open(r
);
958 _act_dst_close(asl_out_rule_t
*r
, int why
)
960 if (r
== NULL
) return;
961 if (r
->dst
== NULL
) return;
962 if (r
->dst
->private == NULL
) return;
964 if (r
->action
== ACTION_ASL_DIR
)
966 asldebug("_act_dst_close: %s ASL DIR %s\n", why_str
[why
], r
->dst
->path
);
967 if (why
!= DST_CLOSE_CHECKPOINT
) _asl_dir_storedata_close(r
);
968 _asl_dir_today_close(r
);
970 else if (r
->action
== ACTION_ASL_FILE
)
972 asldebug("_act_dst_close: %s ASL FILE %s\n", why_str
[why
], (r
->dst
->fname
== NULL
) ? r
->dst
->path
: r
->dst
->fname
);
975 else if (r
->action
== ACTION_FILE
)
977 asldebug("_act_dst_close: %s FILE %s\n", why_str
[why
], (r
->dst
->fname
== NULL
) ? r
->dst
->path
: r
->dst
->fname
);
983 _act_store_file_setup(asl_out_module_t
*m
, asl_out_rule_t
*r
)
986 asl_action_asl_file_data_t
*af_data
;
988 if (r
== NULL
) return ASL_STATUS_INVALID_STORE
;
989 if (r
->dst
== NULL
) return ASL_STATUS_INVALID_STORE
;
990 if (r
->dst
->private == NULL
) return ASL_STATUS_INVALID_STORE
;
992 af_data
= (asl_action_asl_file_data_t
*)r
->dst
->private;
993 if (af_data
->aslfile
!= NULL
)
996 return ASL_STATUS_OK
;
999 if (_act_dst_open(r
, NULL
, 0) != 0) return ASL_STATUS_WRITE_FAILED
;
1001 status
= asl_file_read_set_position(af_data
->aslfile
, ASL_FILE_POSITION_LAST
);
1002 if (status
!= ASL_STATUS_OK
)
1004 asldebug("_act_store_file_setup: asl_file_read_set_position failed %d %s\n", status
, asl_core_error(status
));
1005 _act_dst_close(r
, DST_CLOSE_ERROR
);
1009 af_data
->next_id
= af_data
->aslfile
->cursor_xid
+ 1;
1010 if (fseek(af_data
->aslfile
->store
, 0, SEEK_END
) != 0)
1012 asldebug("_act_store_file_setup: fseek failed %d %s\n", errno
, strerror(errno
));
1013 _act_dst_close(r
, DST_CLOSE_ERROR
);
1014 return ASL_STATUS_WRITE_FAILED
;
1017 return ASL_STATUS_OK
;
1021 * _act_store_dir_setup
1023 * Opens StoreData file and today's file
1024 * Reads ASL Message ID from StoreData file
1025 * Writes ASL Message ID + 1 to StoreData file
1028 _act_store_dir_setup(asl_out_module_t
*m
, asl_out_rule_t
*r
, time_t tick
)
1032 asl_action_asl_store_data_t
*as_data
;
1034 if (r
== NULL
) return ASL_STATUS_INVALID_STORE
;
1035 if (r
->dst
== NULL
) return ASL_STATUS_INVALID_STORE
;
1036 if (r
->dst
->private == NULL
) return ASL_STATUS_INVALID_STORE
;
1037 if (r
->dst
->path
== NULL
) return ASL_STATUS_INVALID_STORE
;
1039 as_data
= (asl_action_asl_store_data_t
*)r
->dst
->private;
1041 if (_act_dst_open(r
, NULL
, as_data
->next_id
) != 0)
1043 asldebug("_act_store_dir_setup: _act_dst_open %s failed\n", r
->dst
->path
);
1044 return ASL_STATUS_WRITE_FAILED
;
1047 /* get / set message id from StoreData file */
1049 rewind(as_data
->storedata
);
1050 if (fread(&xid
, sizeof(uint64_t), 1, as_data
->storedata
) != 1)
1052 asldebug("_act_store_dir_setup: storedata read failed %d %s\n", errno
, strerror(errno
));
1053 _act_dst_close(r
, DST_CLOSE_ERROR
);
1054 return ASL_STATUS_READ_FAILED
;
1057 xid
= asl_core_ntohq(xid
);
1059 as_data
->next_id
= xid
;
1061 xid
= asl_core_htonq(xid
);
1062 rewind(as_data
->storedata
);
1063 status
= fwrite(&xid
, sizeof(uint64_t), 1, as_data
->storedata
);
1066 asldebug("_act_store_dir_setup: storedata write failed %d %s\n", errno
, strerror(errno
));
1067 _act_dst_close(r
, DST_CLOSE_ERROR
);
1068 return ASL_STATUS_WRITE_FAILED
;
1071 fflush(as_data
->storedata
);
1073 if (fseek(as_data
->aslfile
->store
, 0, SEEK_END
) != 0)
1075 asldebug("_act_store_dir_setup: aslfile fseek failed %d %s\n", errno
, strerror(errno
));
1076 _act_dst_close(r
, DST_CLOSE_ERROR
);
1077 return ASL_STATUS_FAILED
;
1080 return ASL_STATUS_OK
;
1084 _asl_action_asl_store_data_free(asl_action_asl_store_data_t
*as_data
)
1086 if (as_data
== NULL
) return;
1091 _asl_action_asl_file_data_free(asl_action_asl_file_data_t
*af_data
)
1093 if (af_data
== NULL
) return;
1098 _asl_action_file_data_free(asl_action_file_data_t
*f_data
)
1100 if (f_data
== NULL
) return;
1102 if (f_data
->dup_timer
!= NULL
)
1104 if (f_data
->last_count
== 0)
1107 * The timer exists, but last_count is zero, so the timer is suspended.
1108 * Sources must not be released in when suspended.
1109 * So we resume it so that we can release it.
1111 dispatch_resume(f_data
->dup_timer
);
1114 dispatch_release(f_data
->dup_timer
);
1117 free(f_data
->last_msg
);
1122 _asl_action_set_param_data_free(asl_action_set_param_data_t
*spdata
)
1124 if (spdata
!= NULL
) notify_cancel(spdata
->token
);
1129 _asl_action_save_failed(const char *where
, asl_out_module_t
*m
, asl_out_rule_t
*r
, uint32_t status
)
1131 if (r
->dst
->flags
& MODULE_FLAG_SOFT_WRITE
) return;
1134 asldebug("%s: %s save to %s failed: %s\n", where
, m
->name
, r
->dst
->path
, asl_core_error(status
));
1136 /* disable further activity after multiple failures */
1137 if (r
->dst
->fails
> MAX_FAILURES
)
1140 asprintf(&str
, "[Sender syslogd] [Level 3] [PID %u] [Facility syslog] [Message Disabling module %s writes to %s following %u failures (%s)]", global
.pid
, m
->name
, r
->dst
->path
, r
->dst
->fails
, asl_core_error(status
));
1141 internal_log_message(str
);
1144 if (r
->action
== ACTION_ASL_DIR
) _asl_action_asl_store_data_free((asl_action_asl_store_data_t
*)r
->dst
->private);
1145 else if (r
->action
== ACTION_ASL_FILE
) _asl_action_asl_file_data_free((asl_action_asl_file_data_t
*)r
->dst
->private);
1146 else if (r
->action
== ACTION_FILE
) _asl_action_file_data_free((asl_action_file_data_t
*)r
->dst
->private);
1148 r
->dst
->private = NULL
;
1149 r
->action
= ACTION_NONE
;
1154 * Save a message in an ASL file.
1157 _act_store_file(asl_out_module_t
*m
, asl_out_rule_t
*r
, asl_msg_t
*msg
)
1159 asl_action_asl_file_data_t
*af_data
;
1163 if (r
== NULL
) return ACTION_STATUS_ERROR
;
1164 if (r
->dst
== NULL
) return ACTION_STATUS_ERROR
;
1165 if (r
->dst
->private == NULL
) return ACTION_STATUS_ERROR
;
1167 af_data
= (asl_action_asl_file_data_t
*)r
->dst
->private;
1168 if (af_data
->pending
> 0) af_data
->pending
--;
1170 status
= _act_store_file_setup(m
, r
);
1171 if (status
== ASL_STATUS_OK
)
1173 af_data
->last_time
= time(NULL
);
1176 mid
= af_data
->next_id
;
1178 /* save message to file and update dst size */
1179 status
= asl_file_save(af_data
->aslfile
, msg
, &mid
);
1180 if (status
== ASL_STATUS_OK
)
1182 r
->dst
->size
= af_data
->aslfile
->file_size
;
1184 if (_act_checkpoint(r
, CHECKPOINT_TEST
) == 1) trigger_aslmanager();
1188 if (status
!= ASL_STATUS_OK
)
1190 _asl_action_save_failed("_act_store_file", m
, r
, status
);
1191 return ACTION_STATUS_ERROR
;
1194 return ACTION_STATUS_OK
;
1198 * Save a message in an ASL data store.
1201 _act_store_dir(asl_out_module_t
*m
, asl_out_rule_t
*r
, asl_msg_t
*msg
)
1203 asl_action_asl_store_data_t
*as_data
;
1209 if (r
== NULL
) return ACTION_STATUS_ERROR
;
1210 if (r
->dst
== NULL
) return ACTION_STATUS_ERROR
;
1211 if (r
->dst
->private == NULL
) return ACTION_STATUS_ERROR
;
1213 as_data
= (asl_action_asl_store_data_t
*)r
->dst
->private;
1214 if (as_data
->pending
> 0) as_data
->pending
--;
1216 val
= asl_msg_get_val_for_key(msg
, ASL_KEY_TIME
);
1217 if (val
== NULL
) return ACTION_STATUS_ERROR
;
1221 status
= _act_store_dir_setup(m
, r
, tick
);
1222 if (status
== ASL_STATUS_OK
)
1224 as_data
->last_time
= time(NULL
);
1227 mid
= as_data
->next_id
;
1228 status
= asl_file_save(as_data
->aslfile
, msg
, &mid
);
1229 if (status
== ASL_STATUS_OK
) r
->dst
->size
= as_data
->aslfile
->file_size
;
1230 else asldebug("_act_store_dir asl_file_save FAILED %s\n", asl_core_error(status
));
1231 //TODO: checkpoint here?
1233 * Currently, the checkpoint is in _asl_dir_today_open().
1234 * Moving it here would be in keeping with the way that
1235 * _act_store_file() and _act_file_final() do checkpoints.
1239 if (status
!= ASL_STATUS_OK
)
1241 _asl_action_save_failed("_act_store_dir", m
, r
, status
);
1242 return ACTION_STATUS_ERROR
;
1245 return ACTION_STATUS_OK
;
1249 _act_store_final(asl_out_module_t
*m
, asl_out_rule_t
*r
, asl_msg_t
*msg
)
1251 if (r
->action
== ACTION_ASL_DIR
) _act_store_dir(m
, r
, msg
);
1252 else _act_store_file(m
, r
, msg
);
1256 * Save a message to an ASL format file (ACTION_ASL_FILE)
1257 * or to an ASL directory (ACTION_ASL_DIR).
1260 _act_store(asl_out_module_t
*m
, asl_out_rule_t
*r
, asl_msg_t
*msg
)
1262 if (r
== NULL
) return;
1263 if (r
->dst
== NULL
) return;
1264 if (msg
== NULL
) return;
1265 if (m
== NULL
) return;
1266 if ((m
->flags
& MODULE_FLAG_ENABLED
) == 0) return;
1268 if (r
->dst
->flags
& MODULE_FLAG_HAS_LOGGED
) return;
1270 r
->dst
->flags
|= MODULE_FLAG_HAS_LOGGED
;
1271 if (r
->action
== ACTION_ASL_DIR
)
1273 asl_action_asl_store_data_t
*as_data
= (asl_action_asl_store_data_t
*)r
->dst
->private;
1274 if (as_data
!= NULL
) as_data
->pending
++;
1276 else if (r
->action
== ACTION_ASL_FILE
)
1278 asl_action_asl_file_data_t
*af_data
= (asl_action_asl_file_data_t
*)r
->dst
->private;
1279 if (af_data
!= NULL
) af_data
->pending
++;
1282 #if TARGET_OS_EMBEDDED
1283 if (r
->dst
->flags
& MODULE_FLAG_CRASHLOG
)
1285 _crashlog_queue_check();
1286 asl_msg_retain(msg
);
1287 dispatch_async(crashlog_queue
, ^{
1288 dispatch_async(asl_action_queue
, ^{
1289 _act_store_final(m
, r
, msg
);
1290 asl_msg_release(msg
);
1297 _act_store_final(m
, r
, msg
);
1301 _send_repeat_msg(asl_out_rule_t
*r
)
1303 asl_action_file_data_t
*f_data
;
1306 time_t now
= time(NULL
);
1308 if (r
== NULL
) return -1;
1309 if (r
->dst
== NULL
) return -1;
1310 if (r
->dst
->private == NULL
) return -1;
1312 f_data
= (asl_action_file_data_t
*)r
->dst
->private;
1314 free(f_data
->last_msg
);
1315 f_data
->last_msg
= NULL
;
1317 if (f_data
->last_count
== 0) return 0;
1319 /* stop the timer */
1320 dispatch_suspend(f_data
->dup_timer
);
1322 memset(vt
, 0, sizeof(vt
));
1327 asprintf(&msg
, "%s --- last message repeated %u time%s ---\n", vt
+ 4, f_data
->last_count
, (f_data
->last_count
== 1) ? "" : "s");
1328 f_data
->last_count
= 0;
1329 f_data
->last_time
= now
;
1330 if (msg
== NULL
) return -1;
1332 if (f_data
->fd
< 0) f_data
->fd
= _act_file_create_open(r
->dst
);
1335 status
= write(f_data
->fd
, msg
, len
);
1338 if ((status
< 0) || (status
< len
))
1340 asldebug("%s: error writing repeat message (%s): %s\n", MY_ID
, r
->dst
->fname
, strerror(errno
));
1350 struct timespec midnight
;
1356 if (checkpoint_timer
!= NULL
) return;
1358 localtime_r(&x
, &t
);
1366 midnight
.tv_sec
= x
;
1367 midnight
.tv_nsec
= 0;
1369 checkpoint_timer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, asl_action_queue
);
1370 dispatch_source_set_timer(checkpoint_timer
, dispatch_walltime(&midnight
, 0), NSEC_PER_SEC
* SEC_PER_DAY
, 0);
1371 dispatch_source_set_event_handler(checkpoint_timer
, ^{ _act_file_checkpoint_all(CHECKPOINT_FORCE
); });
1372 dispatch_resume(checkpoint_timer
);
1375 /* check if a module path (mpath) matches a user path (upath) */
1377 _act_file_equal(const char *mpath
, const char *upath
)
1381 /* NULL upath means user wants to match all files */
1382 if (upath
== NULL
) return true;
1384 if (mpath
== NULL
) return false;
1386 /* check for exact match */
1387 if (!strcmp(mpath
, upath
)) return true;
1389 /* upath may be the last component of mpath */
1390 slash
= strrchr(mpath
, '/');
1391 if (slash
== NULL
) return false;
1393 if (!strcmp(slash
+ 1, upath
)) return true;
1398 _act_file_checkpoint(asl_out_module_t
*m
, const char *path
, uint32_t force
)
1401 int did_checkpoint
= 0;
1403 if (m
== NULL
) return 0;
1406 for (r
= m
->ruleset
; r
!= NULL
; r
= r
->next
)
1408 if ((r
->action
== ACTION_FILE
) || (r
->action
== ACTION_ASL_FILE
))
1410 if (r
->dst
->flags
& MODULE_FLAG_ROTATE
)
1412 if (_act_file_equal(r
->dst
->path
, path
))
1414 if (force
& CHECKPOINT_CRASH
)
1416 if (r
->dst
->flags
& MODULE_FLAG_CRASHLOG
)
1418 if (_act_checkpoint(r
, CHECKPOINT_FORCE
) > 0)
1421 _act_dst_close(r
, DST_CLOSE_CHECKPOINT
);
1427 if (_act_checkpoint(r
, force
) > 0)
1430 _act_dst_close(r
, DST_CLOSE_CHECKPOINT
);
1438 return did_checkpoint
;
1442 _act_file_checkpoint_all(uint32_t force
)
1444 asl_out_module_t
*m
;
1445 int did_checkpoint
= 0;
1447 for (m
= global
.asl_out_module
; m
!= NULL
; m
= m
->next
)
1449 if (_act_file_checkpoint(m
, NULL
, force
) > 0) did_checkpoint
= 1;
1452 trigger_aslmanager();
1454 return did_checkpoint
;
1458 * Save a message in a plain text file.
1461 _act_file_final(asl_out_module_t
*m
, asl_out_rule_t
*r
, asl_msg_t
*msg
)
1463 asl_action_file_data_t
*f_data
;
1465 uint32_t len
, msg_hash
= 0;
1469 if (r
->dst
->private == NULL
) return;
1471 f_data
= (asl_action_file_data_t
*)r
->dst
->private;
1472 if (f_data
->pending
> 0) f_data
->pending
--;
1475 * If print format is std, bsd, or msg, then skip messages with
1476 * no ASL_KEY_MSG, or without a value for it.
1478 if (r
->dst
->flags
& MODULE_FLAG_STD_BSD_MSG
)
1480 const char *msgval
= NULL
;
1481 if (asl_msg_lookup(msg
, ASL_KEY_MSG
, &msgval
, NULL
) != 0) return;
1482 if (msgval
== NULL
) return;
1489 str
= asl_format_message(msg
, r
->dst
->fmt
, r
->dst
->tfmt
, ASL_ENCODE_SAFE
, &len
);
1491 if (r
->dst
->flags
& MODULE_FLAG_COALESCE
)
1493 if (f_data
->dup_timer
== NULL
)
1495 /* create a timer to flush dups on this file */
1496 f_data
->dup_timer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, asl_action_queue
);
1497 dispatch_source_set_event_handler(f_data
->dup_timer
, ^{ _send_repeat_msg(r
); });
1500 if ((global
.bsd_max_dup_time
> 0) && (str
!= NULL
) && (f_data
->last_msg
!= NULL
))
1502 msg_hash
= asl_core_string_hash(str
+ 16, len
- 16);
1503 if ((f_data
->last_hash
== msg_hash
) && (!strcmp(f_data
->last_msg
, str
+ 16)))
1505 if ((now
- f_data
->last_time
) < global
.bsd_max_dup_time
) is_dup
= 1;
1512 if (f_data
->last_count
== 0)
1514 /* start the timer */
1515 dispatch_source_set_timer(f_data
->dup_timer
, dispatch_time(DISPATCH_TIME_NOW
, NSEC_PER_SEC
* global
.bsd_max_dup_time
), DISPATCH_TIME_FOREVER
, 0);
1516 dispatch_resume(f_data
->dup_timer
);
1519 f_data
->last_count
++;
1523 if (_act_dst_open(r
, NULL
, 0) != 0)
1525 _asl_action_save_failed("_act_file", m
, r
, ASL_STATUS_FAILED
);
1535 * The current message is not a duplicate. If f_data->last_count > 0
1536 * we need to write a "last message repeated N times" log entry.
1537 * _send_repeat_msg will free last_msg and do nothing if
1538 * last_count == 0, but we test and free here to avoid a function call.
1540 if (f_data
->last_count
> 0)
1542 _send_repeat_msg(r
);
1546 free(f_data
->last_msg
);
1547 f_data
->last_msg
= NULL
;
1550 if (str
!= NULL
) f_data
->last_msg
= strdup(str
+ 16);
1552 f_data
->last_hash
= msg_hash
;
1553 f_data
->last_count
= 0;
1554 f_data
->last_time
= now
;
1556 if ((str
!= NULL
) && (len
> 1))
1558 /* write line to file and update dst size */
1559 size_t bytes
= write(f_data
->fd
, str
, len
- 1);
1560 if (bytes
> 0) r
->dst
->size
+= bytes
;
1562 if (_act_checkpoint(r
, CHECKPOINT_TEST
) == 1) trigger_aslmanager();
1570 _act_file(asl_out_module_t
*m
, asl_out_rule_t
*r
, asl_msg_t
*msg
)
1572 asl_action_file_data_t
*f_data
;
1574 if (r
== NULL
) return;
1575 if (msg
== NULL
) return;
1576 if (m
== NULL
) return;
1577 if ((m
->flags
& MODULE_FLAG_ENABLED
) == 0) return;
1578 if (r
->dst
== NULL
) return;
1579 if (r
->dst
->private == NULL
) return;
1581 if (r
->dst
->flags
& MODULE_FLAG_HAS_LOGGED
) return;
1583 r
->dst
->flags
|= MODULE_FLAG_HAS_LOGGED
;
1584 f_data
= (asl_action_file_data_t
*)r
->dst
->private;
1585 if (f_data
!= NULL
) f_data
->pending
++;
1587 #if TARGET_OS_EMBEDDED
1588 if (r
->dst
->flags
& MODULE_FLAG_CRASHLOG
)
1590 _crashlog_queue_check();
1591 asl_msg_retain(msg
);
1592 dispatch_async(crashlog_queue
, ^{
1593 dispatch_async(asl_action_queue
, ^{
1594 _act_file_final(m
, r
, msg
);
1595 asl_msg_release(msg
);
1602 _act_file_final(m
, r
, msg
);
1606 _act_forward(asl_out_module_t
*m
, asl_out_rule_t
*r
, asl_msg_t
*msg
)
1608 /* To do: <rdar://problem/6130747> Add a "forward" action to asl.conf */
1612 _act_control(asl_out_module_t
*m
, asl_out_rule_t
*r
, asl_msg_t
*msg
)
1616 if (m
== NULL
) return;
1617 if (r
== NULL
) return;
1619 p
= asl_msg_get_val_for_key(msg
, ASL_KEY_MODULE
);
1621 if (r
->options
== NULL
) return;
1623 if (!strcmp(r
->options
, "enable"))
1625 m
->flags
|= MODULE_FLAG_ENABLED
;
1627 else if (!strcmp(r
->options
, "disable"))
1629 m
->flags
&= ~MODULE_FLAG_ENABLED
;
1631 else if ((!strcmp(r
->options
, "checkpoint")) || (!strcmp(r
->options
, "rotate")))
1633 _act_file_checkpoint(m
, NULL
, CHECKPOINT_FORCE
);
1638 _send_to_asl_store(asl_msg_t
*msg
)
1640 if ((global
.asl_out_module
!= NULL
) && ((global
.asl_out_module
->flags
& MODULE_FLAG_ENABLED
) == 0)) return;
1642 if (store_has_logged
) return;
1643 store_has_logged
= true;
1645 db_save_message(msg
);
1649 _asl_out_process_message(asl_out_module_t
*m
, asl_msg_t
*msg
)
1653 if (m
== NULL
) return 1;
1654 if (msg
== NULL
) return 1;
1656 /* reset flag bit used for duplicate avoidance */
1657 for (r
= m
->ruleset
; r
!= NULL
; r
= r
->next
)
1659 if ((r
->action
== ACTION_FILE
) || (r
->action
== ACTION_ASL_DIR
) || (r
->action
== ACTION_ASL_FILE
))
1661 if (r
->dst
!= NULL
) r
->dst
->flags
&= MODULE_FLAG_CLEAR_LOGGED
;
1665 for (r
= m
->ruleset
; r
!= NULL
; r
= r
->next
)
1667 if (r
->query
== NULL
) continue;
1669 /* ACTION_SET_FILE, ACTION_SET_PLIST, and ACTION_SET_PROF are handled independently */
1670 if ((r
->action
== ACTION_SET_FILE
) || (r
->action
== ACTION_SET_PLIST
) || (r
->action
== ACTION_SET_PROF
)) continue;
1673 * ACTION_CLAIM during processing is a filter. It will only be here if the option "only"
1674 * was supplied. In this case we test the message against the query. If it does not
1675 * match, we skip the message.
1677 if (r
->action
== ACTION_CLAIM
)
1679 if ((asl_msg_cmp(r
->query
, msg
) != 1)) return 0;
1682 if ((asl_msg_cmp(r
->query
, msg
) == 1))
1684 if (r
->action
== ACTION_NONE
) continue;
1685 else if (r
->action
== ACTION_IGNORE
) return 1;
1686 else if (r
->action
== ACTION_SKIP
) return 0;
1687 else if (r
->action
== ACTION_ASL_STORE
) _send_to_asl_store(msg
);
1688 else if (r
->action
== ACTION_ACCESS
) _act_access_control(m
, r
, msg
);
1689 else if (r
->action
== ACTION_SET_KEY
) _act_set_key(m
, r
, msg
);
1690 else if (r
->action
== ACTION_UNSET_KEY
) _act_unset_key(m
, r
, msg
);
1691 else if (r
->action
== ACTION_NOTIFY
) _act_notify(m
, r
);
1692 else if (r
->action
== ACTION_BROADCAST
) _act_broadcast(m
, r
, msg
);
1693 else if (r
->action
== ACTION_FORWARD
) _act_forward(m
, r
, msg
);
1694 else if (r
->action
== ACTION_CONTROL
) _act_control(m
, r
, msg
);
1695 else if (r
->action
== ACTION_SET_PARAM
) _act_out_set_param(m
, r
->options
, true);
1696 else if ((r
->action
== ACTION_ASL_FILE
) || (r
->action
== ACTION_ASL_DIR
)) _act_store(m
, r
, msg
);
1697 else if (r
->action
== ACTION_FILE
) _act_file(m
, r
, msg
);
1705 asl_out_message(asl_msg_t
*msg
)
1707 OSAtomicIncrement32(&global
.asl_queue_count
);
1708 asl_msg_retain(msg
);
1710 dispatch_async(asl_action_queue
, ^{
1713 time_t now
= time(NULL
);
1714 asl_out_module_t
*m
= global
.asl_out_module
;
1716 store_has_logged
= false;
1718 p
= asl_msg_get_val_for_key(msg
, ASL_KEY_MODULE
);
1721 if ((action_asl_store_count
== 0) || (asl_check_option(msg
, ASL_OPT_STORE
) == 1)) _send_to_asl_store(msg
);
1723 ignore
= _asl_out_process_message(m
, msg
);
1726 if (m
!= NULL
) m
= m
->next
;
1729 _asl_out_process_message(m
, msg
);
1736 if (m
!= NULL
) m
= m
->next
;
1739 if (!strcmp(p
, m
->name
)) _asl_out_process_message(m
, msg
);
1744 p
= asl_msg_get_val_for_key(msg
, ASL_KEY_FINAL_NOTIFICATION
);
1745 if (p
!= NULL
) asl_msg_set_key_val(msg
, ASL_KEY_FREE_NOTE
, p
);
1747 asl_msg_release(msg
);
1748 OSAtomicDecrement32(&global
.asl_queue_count
);
1750 if ((now
- sweep_time
) >= IDLE_CLOSE
)
1752 _asl_action_close_idle_files(IDLE_CLOSE
);
1759 _asl_action_profile_test(asl_out_module_t
*m
, asl_out_rule_t
*r
)
1765 /* ident is first message key */
1766 asl_msg_fetch(r
->query
, 0, &ident
, NULL
, NULL
);
1769 r
->action
= ACTION_NONE
;
1773 profile
= configuration_profile_to_asl_msg(ident
);
1774 eval
= (asl_msg_cmp(r
->query
, profile
) == 1);
1775 _act_out_set_param(m
, r
->options
, eval
);
1776 asl_msg_release(profile
);
1778 return strdup(ident
);
1782 _asl_action_file_test(asl_out_module_t
*m
, asl_out_rule_t
*r
)
1789 /* path is first message key */
1790 asl_msg_fetch(r
->query
, 0, &path
, NULL
, NULL
);
1793 r
->action
= ACTION_NONE
;
1797 memset(&sb
, 0, sizeof(struct stat
));
1798 status
= stat(path
, &sb
);
1799 eval
= (status
== 0);
1800 _act_out_set_param(m
, r
->options
, eval
);
1806 _asl_action_handle_file_change_notification(int t
)
1808 asl_out_module_t
*m
;
1811 for (m
= global
.asl_out_module
; m
!= NULL
; m
= m
->next
)
1813 for (r
= m
->ruleset
; r
!= NULL
; r
= r
->next
)
1815 if (r
->action
== ACTION_SET_FILE
)
1817 asl_action_set_param_data_t
*spdata
= (asl_action_set_param_data_t
*)r
->private;
1818 if ((spdata
!= NULL
) && (spdata
->token
== t
))
1820 _asl_action_file_test(m
, r
);
1824 else if (r
->action
== ACTION_SET_PLIST
)
1826 asl_action_set_param_data_t
*spdata
= (asl_action_set_param_data_t
*)r
->private;
1827 if ((spdata
!= NULL
) && (spdata
->token
== t
))
1829 char *str
= _asl_action_profile_test(m
, r
);
1834 else if (r
->action
== ACTION_SET_PROF
)
1836 asl_action_set_param_data_t
*spdata
= (asl_action_set_param_data_t
*)r
->private;
1837 if ((spdata
!= NULL
) && (spdata
->token
== t
))
1839 char *str
= _asl_action_profile_test(m
, r
);
1847 asl_out_module_free(m
);
1851 _asl_action_post_process_rule(asl_out_module_t
*m
, asl_out_rule_t
*r
)
1853 if ((m
== NULL
) || (r
== NULL
)) return;
1855 if (m
!= global
.asl_out_module
)
1857 /* check if any previous module has used this destination */
1858 asl_out_module_t
*n
;
1861 if ((r
->dst
!= NULL
) && (r
->dst
->path
!= NULL
))
1863 for (n
= global
.asl_out_module
; search
&& (n
!= NULL
) && (n
!= m
); n
= n
->next
)
1866 for (s
= n
->ruleset
; search
&& (s
!= NULL
); s
= s
->next
)
1868 if (s
->action
== ACTION_OUT_DEST
)
1870 if ((s
->dst
!= NULL
) && (s
->dst
->path
!= NULL
) && (!strcmp(r
->dst
->path
, s
->dst
->path
)))
1872 /* rule r of module m is using previously used dst of rule s of module n */
1873 asl_out_dst_data_release(r
->dst
);
1876 if (r
->action
== ACTION_OUT_DEST
)
1879 asprintf(&str
, "[Sender syslogd] [Level 5] [PID %u] [Message Configuration Notice:\nASL Module \"%s\" sharing output destination \"%s\" with ASL Module \"%s\".\nOutput parameters from ASL Module \"%s\" override any specified in ASL Module \"%s\".] [UID 0] [GID 0] [Facility syslog]", global
.pid
, m
->name
, s
->dst
->path
, n
->name
, n
->name
, m
->name
);
1880 internal_log_message(str
);
1885 r
->dst
= asl_out_dst_data_retain(s
->dst
);
1896 if (r
->action
== ACTION_SET_PARAM
)
1898 if (r
->query
== NULL
) _act_out_set_param(m
, r
->options
, true);
1900 else if (r
->action
== ACTION_CLAIM
)
1902 /* becomes ACTION_SKIP in com.apple.asl config */
1903 if (m
!= global
.asl_out_module
)
1905 asl_out_rule_t
*rule
= (asl_out_rule_t
*)calloc(1, sizeof(asl_out_rule_t
));
1909 asprintf(&str
, "[Sender syslogd] [Level 5] [PID %u] [Message Configuration Notice:\nASL Module \"%s\" claims selected messages.\nThose messages may not appear in standard system log files or in the ASL database.] [UID 0] [GID 0] [Facility syslog]", global
.pid
, m
->name
);
1910 internal_log_message(str
);
1913 rule
->query
= asl_msg_copy(r
->query
);
1914 rule
->action
= ACTION_SKIP
;
1915 rule
->next
= global
.asl_out_module
->ruleset
;
1916 global
.asl_out_module
->ruleset
= rule
;
1920 * After adding ACTION_SKIP to com.apple.asl module, the claim becomes a no-op in this module
1921 * UNLESS the claim includes the option "only". In that case, the claim becomes a filter:
1922 * any messages that DO NOT match the claim are skipped by this module.
1924 if (r
->options
== NULL
) r
->action
= ACTION_NONE
;
1925 else if (strcmp(r
->options
, "only") != 0) r
->action
= ACTION_NONE
;
1928 else if (r
->action
== ACTION_ASL_STORE
)
1930 action_asl_store_count
++;
1932 else if (r
->action
== ACTION_ASL_DIR
)
1934 if (r
->dst
->private == NULL
) r
->dst
->private = (asl_action_asl_store_data_t
*)calloc(1, sizeof(asl_action_asl_store_data_t
));
1936 else if (r
->action
== ACTION_ASL_FILE
)
1938 if (r
->dst
->private == NULL
)r
->dst
->private = (asl_action_asl_file_data_t
*)calloc(1, sizeof(asl_action_asl_file_data_t
));
1940 else if (r
->action
== ACTION_FILE
)
1942 if (r
->dst
->private == NULL
) r
->dst
->private = (asl_action_file_data_t
*)calloc(1, sizeof(asl_action_file_data_t
));
1943 if (r
->dst
->private != NULL
) ((asl_action_file_data_t
*)(r
->dst
->private))->fd
= -1;
1945 else if (r
->action
== ACTION_SET_PLIST
)
1947 char *ident
=_asl_action_profile_test(m
, r
);
1948 char *notify_key
= configuration_profile_create_notification_key(ident
);
1951 if (notify_key
!= NULL
)
1954 asl_action_set_param_data_t
*spdata
;
1956 status
= notify_register_dispatch(notify_key
, &token
, asl_action_queue
, ^(int t
){
1957 _asl_action_handle_file_change_notification(t
);
1962 spdata
= (asl_action_set_param_data_t
*)calloc(1, sizeof(asl_action_set_param_data_t
));
1965 notify_cancel(token
);
1969 spdata
->token
= token
;
1970 r
->private = spdata
;
1974 else if (r
->action
== ACTION_SET_PROF
)
1976 char *ident
=_asl_action_profile_test(m
, r
);
1977 char *notify_key
= configuration_profile_create_notification_key(ident
);
1980 if (notify_key
!= NULL
)
1983 asl_action_set_param_data_t
*spdata
;
1985 status
= notify_register_dispatch(notify_key
, &token
, asl_action_queue
, ^(int t
){
1986 _asl_action_handle_file_change_notification(t
);
1991 spdata
= (asl_action_set_param_data_t
*)calloc(1, sizeof(asl_action_set_param_data_t
));
1994 notify_cancel(token
);
1998 spdata
->token
= token
;
1999 r
->private = spdata
;
2003 else if (r
->action
== ACTION_SET_FILE
)
2006 const char *path
=_asl_action_file_test(m
, r
);
2010 asprintf(¬ify_key
, "%s%s", NOTIFY_PATH_SERVICE
, path
);
2011 if (notify_key
!= NULL
)
2014 asl_action_set_param_data_t
*spdata
;
2016 status
= notify_register_dispatch(notify_key
, &token
, asl_action_queue
, ^(int t
){
2017 _asl_action_handle_file_change_notification(t
);
2022 spdata
= (asl_action_set_param_data_t
*)calloc(1, sizeof(asl_action_set_param_data_t
));
2025 notify_cancel(token
);
2029 spdata
->token
= token
;
2030 r
->private = spdata
;
2038 _asl_action_configure()
2041 asl_out_module_t
*m
;
2044 if (global
.asl_out_module
== NULL
) global
.asl_out_module
= asl_out_module_init();
2045 if (global
.asl_out_module
== NULL
) return;
2047 asldebug("%s: init\n", MY_ID
);
2049 action_asl_store_count
= 0;
2051 for (m
= global
.asl_out_module
; m
!= NULL
; m
= m
->next
)
2053 for (r
= m
->ruleset
; r
!= NULL
; r
= r
->next
)
2055 _asl_action_post_process_rule(m
, r
);
2056 if (r
->dst
!= NULL
) flags
|= (r
->dst
->flags
& (MODULE_FLAG_ROTATE
| MODULE_FLAG_CRASHLOG
));
2060 if (global
.debug
!= 0)
2063 if (global
.debug_file
== NULL
) dfp
= fopen(_PATH_SYSLOGD_LOG
, "a");
2064 else dfp
= fopen(global
.debug_file
, "a");
2067 for (m
= global
.asl_out_module
; m
!= NULL
; m
= m
->next
)
2069 fprintf(dfp
, "module: %s%s\n", (m
->name
== NULL
) ? "<unknown>" : m
->name
, (m
->flags
& MODULE_FLAG_LOCAL
) ? " (local)" : "");
2070 asl_out_module_print(dfp
, m
);
2077 sweep_time
= time(NULL
);
2079 if (flags
& MODULE_FLAG_ROTATE
)
2081 _act_file_checkpoint_all(CHECKPOINT_TEST
);
2082 if (checkpoint_timer
== NULL
) _start_cycling();
2087 asl_action_init(void)
2089 static dispatch_once_t once
;
2091 dispatch_once(&once
, ^{
2092 asl_action_queue
= dispatch_queue_create("ASL Action Queue", NULL
);
2093 #if TARGET_OS_EMBEDDED
2094 crashlog_queue
= dispatch_queue_create("iOS CrashLog Queue", NULL
);
2095 notify_register_dispatch(CRASH_MOVER_SERVICE
, &crashmover_token
, asl_action_queue
, ^(int unused
) {
2096 uint64_t cmstate
= 0;
2097 uint64_t oldstate
= (crashmover_state
== 0) ? 0llu : 1llu;
2099 uint32_t status
= notify_get_state(crashmover_token
, &cmstate
);
2102 if (cmstate
!= oldstate
)
2104 crashmover_state
= 0;
2105 if (cmstate
== 1) crashmover_state
= time(NULL
);
2107 if (crashmover_state
== 0)
2109 asldebug("CrashMover finished\n");
2110 dispatch_resume(crashlog_queue
);
2114 asldebug("CrashMover active: suspending crashlog queue and closing files\n");
2115 dispatch_suspend(crashlog_queue
);
2116 _asl_action_close_idle_files(0);
2124 _asl_action_configure();
2130 * Close outputs and free modules.
2133 _asl_action_free_modules(asl_out_module_t
*m
)
2136 asl_out_module_t
*x
;
2139 * asl_common frees a list of modules with asl_out_module_free.
2140 * This loop frees the private data attached some modules.
2142 for (x
= m
; x
!= NULL
; x
= x
->next
)
2144 for (r
= x
->ruleset
; r
!= NULL
; r
= r
->next
)
2146 if (r
->action
== ACTION_ASL_DIR
)
2148 _act_dst_close(r
, DST_CLOSE_SHUTDOWN
);
2151 _asl_action_asl_store_data_free((asl_action_asl_store_data_t
*)r
->dst
->private);
2152 r
->dst
->private = NULL
;
2155 else if (r
->action
== ACTION_ASL_FILE
)
2157 _act_dst_close(r
, DST_CLOSE_SHUTDOWN
);
2160 _asl_action_asl_file_data_free((asl_action_asl_file_data_t
*)r
->dst
->private);
2161 r
->dst
->private = NULL
;
2164 else if (r
->action
== ACTION_FILE
)
2166 _act_dst_close(r
, DST_CLOSE_SHUTDOWN
);
2169 asl_action_file_data_t
*f_data
= (asl_action_file_data_t
*)r
->dst
->private;
2172 /* flush repeat message if necessary */
2173 if (f_data
->last_count
> 0) _send_repeat_msg(r
);
2174 _asl_action_file_data_free(f_data
);
2175 r
->dst
->private = NULL
;
2179 else if (r
->action
== ACTION_SET_PLIST
)
2181 _asl_action_set_param_data_free((asl_action_set_param_data_t
*)r
->private);
2183 else if (r
->action
== ACTION_SET_PROF
)
2185 _asl_action_set_param_data_free((asl_action_set_param_data_t
*)r
->private);
2187 else if (r
->action
== ACTION_SET_FILE
)
2189 _asl_action_set_param_data_free((asl_action_set_param_data_t
*)r
->private);
2194 asl_out_module_free(m
);
2198 _asl_action_close_internal(void)
2200 #if TARGET_OS_EMBEDDED
2201 if (crashmover_state
!= 0)
2203 dispatch_resume(crashlog_queue
);
2204 crashmover_state
= 0;
2207 /* wait for the crashlog_queue to flush before _asl_action_free_modules() */
2208 dispatch_sync(crashlog_queue
, ^{ int x
= 0; if (x
== 1) x
= 2; });
2211 _asl_action_free_modules(global
.asl_out_module
);
2212 global
.asl_out_module
= NULL
;
2213 sweep_time
= time(NULL
);
2219 _asl_action_close_idle_files(time_t idle_time
)
2221 asl_out_module_t
*m
;
2222 time_t now
= time(NULL
);
2224 for (m
= global
.asl_out_module
; m
!= NULL
; m
= m
->next
)
2228 for (r
= m
->ruleset
; r
!= NULL
; r
= r
->next
)
2232 if ((r
->dst
!= NULL
) && (r
->dst
->flags
& MODULE_FLAG_CRASHLOG
))
2234 _act_dst_close(r
, DST_CLOSE_IDLE
);
2235 //TODO: can r->action even be ACTION_ASL_DIR?
2236 /* if not, we can avoid the extra check here */
2237 if (r
->action
!= ACTION_ASL_DIR
) _act_checkpoint(r
, CHECKPOINT_FORCE
);
2240 else if (r
->action
== ACTION_ASL_DIR
)
2244 asl_action_asl_store_data_t
*as_data
= (asl_action_asl_store_data_t
*)r
->dst
->private;
2245 if ((as_data
!= NULL
) && (as_data
->aslfile
!= NULL
) && (as_data
->pending
== 0) && ((now
- as_data
->last_time
) >= idle_time
)) _act_dst_close(r
, DST_CLOSE_IDLE
);
2248 else if (r
->action
== ACTION_ASL_FILE
)
2252 asl_action_asl_file_data_t
*af_data
= (asl_action_asl_file_data_t
*)r
->dst
->private;
2253 if ((af_data
!= NULL
) && (af_data
->aslfile
!= NULL
) && (af_data
->pending
== 0) && ((now
- af_data
->last_time
) >= idle_time
)) _act_dst_close(r
, DST_CLOSE_IDLE
);
2256 else if (r
->action
== ACTION_FILE
)
2260 asl_action_file_data_t
*f_data
= (asl_action_file_data_t
*)r
->dst
->private;
2261 if ((f_data
!= NULL
) && (f_data
->fd
>= 0) && (f_data
->pending
== 0) && ((now
- f_data
->last_time
) >= idle_time
)) _act_dst_close(r
, DST_CLOSE_IDLE
);
2269 asl_action_close(void)
2271 dispatch_async(asl_action_queue
, ^{
2272 _asl_action_close_internal();
2279 asl_action_reset(void)
2281 dispatch_async(asl_action_queue
, ^{
2282 _asl_action_close_internal();
2290 _asl_action_module_with_name(const char *name
)
2292 asl_out_module_t
*m
;
2294 if (global
.asl_out_module
== NULL
) return NULL
;
2295 if (name
== NULL
) return global
.asl_out_module
;
2297 for (m
= global
.asl_out_module
; m
!= NULL
; m
= m
->next
)
2299 if ((m
->name
!= NULL
) && (!strcmp(m
->name
, name
))) return m
;
2306 * called from control_message
2307 * Used to control modules dynamically.
2308 * Line format "@ module param [value ...]"
2310 * Note this is synchronous on asl_action queue.
2313 asl_action_control_set_param(const char *s
)
2319 if (s
== NULL
) return -1;
2320 if (s
[0] == '\0') return 0;
2322 /* skip '@' and whitespace */
2324 while ((*s
== ' ') || (*s
== '\t')) s
++;
2326 l
= explode(s
, " \t");
2327 if (l
!= NULL
) for (count
= 0; l
[count
] != NULL
; count
++);
2329 /* at least 2 parameters (l[0] = module, l[1] = param) required */
2332 free_string_list(l
);
2336 if (global
.asl_out_module
== NULL
)
2338 asldebug("asl_action_control_set_param: no modules loaded\n");
2339 free_string_list(l
);
2343 /* create / modify a module */
2344 if ((!strcasecmp(l
[1], "define")) && (strcmp(l
[0], "*")))
2349 asldebug("asl_action_control_set_param: memory allocation failed\n");
2350 free_string_list(l
);
2354 dispatch_sync(asl_action_queue
, ^{
2355 asl_out_module_t
*m
;
2358 /* skip name, whitespace, "define" */
2359 while ((*p
!= ' ') && (*p
!= '\t')) p
++;
2360 while ((*p
== ' ') || (*p
== '\t')) p
++;
2361 while ((*p
!= ' ') && (*p
!= '\t')) p
++;
2363 m
= _asl_action_module_with_name(l
[0]);
2366 asl_out_module_t
*x
;
2368 m
= asl_out_module_new(l
[0]);
2369 for (x
= global
.asl_out_module
; x
->next
!= NULL
; x
= x
->next
);
2373 r
= asl_out_module_parse_line(m
, p
);
2376 _asl_action_post_process_rule(m
, r
);
2377 if ((r
->dst
!= NULL
) && (r
->dst
->flags
& MODULE_FLAG_ROTATE
))
2379 _act_file_checkpoint_all(CHECKPOINT_TEST
);
2380 if (checkpoint_timer
== NULL
) _start_cycling();
2386 free_string_list(l
);
2390 dispatch_sync(asl_action_queue
, ^{
2393 asl_out_module_t
*m
;
2395 if (!strcmp(l
[0], "*"))
2398 m
= _asl_action_module_with_name(NULL
);
2402 m
= _asl_action_module_with_name(l
[0]);
2407 if (!strcasecmp(l
[1], "enable"))
2411 /* don't do enable for ASL_MODULE_NAME if input name is "*" */
2412 if ((do_all
== 0) || (strcmp(m
->name
, ASL_MODULE_NAME
)))
2414 /* @ module enable {0|1} */
2415 if (count
> 2) intval
= atoi(l
[2]);
2417 if (intval
== 0) m
->flags
&= ~MODULE_FLAG_ENABLED
;
2418 else m
->flags
|= MODULE_FLAG_ENABLED
;
2421 else if (!strcasecmp(l
[1], "checkpoint"))
2423 /* @ module checkpoint [file] */
2424 if (count
> 2) _act_file_checkpoint(m
, l
[2], CHECKPOINT_FORCE
);
2425 else _act_file_checkpoint(m
, NULL
, CHECKPOINT_FORCE
);
2428 if (do_all
== 1) m
= m
->next
;
2434 free_string_list(l
);
2439 asl_action_file_checkpoint(const char *module, const char *path
)
2441 /* Note this is synchronous on asl_action queue */
2442 dispatch_sync(asl_action_queue
, ^{
2443 asl_out_module_t
*m
= _asl_action_module_with_name(module);
2444 _act_file_checkpoint(m
, path
, CHECKPOINT_FORCE
);