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
->current_name
, 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;
532 free(r
->dst
->current_name
);
533 r
->dst
->current_name
= NULL
;
535 as_data
->aslfile
= NULL
;
542 _act_checkpoint(asl_out_rule_t
*r
, uint32_t force
)
544 char tmpcurrent_name
[MAXPATHLEN
], *fn
;
546 if (r
== NULL
) return 0;
547 if (r
->dst
== NULL
) return 0;
549 fn
= r
->dst
->current_name
;
552 if (r
->dst
->path
== NULL
) return 0;
553 asl_dst_make_current_name(r
->dst
, 0, tmpcurrent_name
, sizeof(tmpcurrent_name
));
554 fn
= tmpcurrent_name
;
557 if ((force
== CHECKPOINT_TEST
) && (r
->dst
->file_max
== 0)) return 0;
559 if ((r
->dst
->size
== 0) || (r
->dst
->timestamp
== 0))
563 memset(&sb
, 0, sizeof(struct stat
));
565 if (stat(fn
, &sb
) < 0)
567 if (errno
== ENOENT
) return 0;
571 if (r
->dst
->timestamp
== 0) r
->dst
->timestamp
= sb
.st_birthtimespec
.tv_sec
;
572 if (r
->dst
->timestamp
== 0) r
->dst
->timestamp
= 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
];
587 snprintf(srcpath
, sizeof(srcpath
), "%s", fn
);
589 r
->dst
->timestamp
= time(NULL
);
590 asl_dst_make_current_name(r
->dst
, MODULE_FLAG_BASESTAMP
, dstpath
, sizeof(dstpath
));
592 _act_dst_close(r
, DST_CLOSE_CHECKPOINT
);
593 if (strneq(srcpath
, dstpath
))
595 rename(srcpath
, dstpath
);
596 asldebug("CHECKPOINT RENAME %s %s\n", srcpath
, dstpath
);
600 r
->dst
->timestamp
= 0;
606 * Open today's ASL file in an ASL directory
607 * - Checks date and closes a currently open file if it has the wrong date
608 * - Opens today's file
611 _asl_dir_today_open(asl_out_rule_t
*r
, const time_t *tick
)
618 if (r
== NULL
) return -1;
619 if (r
->dst
== NULL
) return -1;
621 status
= _asl_dir_create(r
);
624 asldebug("_asl_dir_today_open: No directory at path %s\n", r
->dst
->path
);
628 asl_action_asl_store_data_t
*as_data
= (asl_action_asl_store_data_t
*)r
->dst
->private;
630 memset(&ctm
, 0, sizeof(struct tm
));
634 tick
= (const time_t *)&now
;
637 if (localtime_r(tick
, &ctm
) == NULL
)
639 asldebug("_asl_dir_today_open: localtime_r error %s\n", strerror(errno
));
643 /* checks file_max and closes if required */
644 status
= _act_checkpoint(r
, CHECKPOINT_TEST
);
645 if (status
== 1) asl_trigger_aslmanager();
647 if (as_data
->aslfile
!= NULL
)
650 if ((as_data
->p_year
== ctm
.tm_year
) && (as_data
->p_month
== ctm
.tm_mon
) && (as_data
->p_day
== ctm
.tm_mday
)) return 0;
652 /* Wrong date, close the current file */
653 _asl_dir_today_close(r
);
658 if (r
->dst
->flags
& MODULE_FLAG_BASESTAMP
)
665 tick
= (const time_t *)&now
;
668 asl_make_timestamp(now
, r
->dst
->style_flags
, tstamp
, sizeof(tstamp
));
669 asprintf(&(r
->dst
->current_name
), "%s/%s.asl", r
->dst
->path
, tstamp
);
673 asprintf(&(r
->dst
->current_name
), "%s/%d.%02d.%02d.asl", r
->dst
->path
, ctm
.tm_year
+ 1900, ctm
.tm_mon
+ 1, ctm
.tm_mday
);
677 if (r
->dst
->current_name
== NULL
)
679 asldebug("_asl_dir_today_open: asprintf error %s\n", strerror(errno
));
683 #if TARGET_IPHONE_SIMULATOR
687 uid_t uid
= r
->dst
->uid
[0];
688 gid_t gid
= r
->dst
->gid
[0];
692 status
= asl_file_open_write(r
->dst
->current_name
, (r
->dst
->mode
& 00666), uid
, gid
, &(as_data
->aslfile
));
695 if (status
!= ASL_STATUS_OK
)
697 asldebug("_asl_dir_today_open: asl_file_open_write %s error %s\n", r
->dst
->current_name
, asl_core_error(status
));
698 free(r
->dst
->current_name
);
699 r
->dst
->current_name
= NULL
;
703 if (fseek(as_data
->aslfile
->store
, 0, SEEK_END
) != 0)
705 asldebug("_asl_dir_today_open: fseek %s error %s\n", r
->dst
->current_name
, strerror(errno
));
706 free(r
->dst
->current_name
);
707 r
->dst
->current_name
= NULL
;
711 as_data
->p_year
= ctm
.tm_year
;
712 as_data
->p_month
= ctm
.tm_mon
;
713 as_data
->p_day
= ctm
.tm_mday
;
715 /* create aslfile_monitor */
716 int fd
= fileno(as_data
->aslfile
->store
);
717 asl_file_t
*aslf
= as_data
->aslfile
;
719 as_data
->aslfile_monitor
= dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE
, fd
, DISPATCH_VNODE_DELETE
, asl_action_queue
);
720 if (as_data
->aslfile_monitor
!= NULL
)
722 dispatch_source_set_event_handler(as_data
->aslfile_monitor
, ^{
723 _act_dst_close(r
, DST_CLOSE_DELETED
);
726 dispatch_source_set_cancel_handler(as_data
->aslfile_monitor
, ^{
727 asldebug("cancel/close ASL file fd %d\n", fd
);
728 asl_file_close(aslf
);
731 dispatch_resume(as_data
->aslfile_monitor
);
734 asldebug("_asl_dir_today_open ASL file %s fd %d\n", r
->dst
->current_name
, fd
);
740 _asl_file_close(asl_out_rule_t
*r
)
742 if (r
== NULL
) return;
743 if (r
->dst
== NULL
) return;
745 asl_action_asl_file_data_t
*af_data
= (asl_action_asl_file_data_t
*)r
->dst
->private;
746 if (af_data
->aslfile
== NULL
) return;
748 if (af_data
->pending
!= 0)
751 asprintf(&str
, "[Sender syslogd] [Level 4] [PID %u] [Facility syslog] [Message ASL File %s was closed with %d pending messages]", global
.pid
, r
->dst
->current_name
, af_data
->pending
);
752 internal_log_message(str
);
756 if (af_data
->monitor
== NULL
)
759 * This should never happen, but _asl_file_open allows
760 * dispatch_source_create to fail silently. If there is no dispatch
761 * source, we just close the file.
763 asldebug("close ASL fd %d\n", fileno(af_data
->aslfile
->store
));
764 asl_file_close(af_data
->aslfile
);
769 * The monitor cancel handler will close the file.
771 dispatch_source_cancel(af_data
->monitor
);
772 dispatch_release(af_data
->monitor
);
773 af_data
->monitor
= NULL
;
776 af_data
->aslfile
= NULL
;
780 _asl_file_open(asl_out_rule_t
*r
)
784 if (r
== NULL
) return -1;
785 if (r
->dst
== NULL
) return -1;
787 asl_action_asl_file_data_t
*af_data
= (asl_action_asl_file_data_t
*)r
->dst
->private;
788 if (af_data
->aslfile
!= NULL
) return 0;
790 /* create path if necessary */
791 status
= asl_out_mkpath(global
.asl_out_module
, r
);
794 asldebug("_asl_file_open: asl_out_mkpath %s failed\n", r
->dst
->path
);
798 fd
= _act_file_create_open(r
->dst
);
801 asldebug("_asl_file_open: _act_file_create_open %s failed %d %s\n", r
->dst
->current_name
, errno
, strerror(errno
));
807 if (r
->dst
->current_name
== NULL
) return -1;
809 status
= asl_file_open_write(r
->dst
->current_name
, 0, -1, -1, &(af_data
->aslfile
));
810 if (status
!= ASL_STATUS_OK
)
812 asldebug("_asl_file_open: asl_file_open_write %s failed %d %s\n", r
->dst
->current_name
, errno
, strerror(errno
));
817 fd
= fileno(af_data
->aslfile
->store
);
818 asl_file_t
*aslf
= af_data
->aslfile
;
820 af_data
->monitor
= dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE
, fd
, DISPATCH_VNODE_DELETE
, asl_action_queue
);
821 if (af_data
->monitor
!= NULL
)
823 dispatch_source_set_event_handler(af_data
->monitor
, ^{
824 _act_dst_close(r
, DST_CLOSE_DELETED
);
827 dispatch_source_set_cancel_handler(af_data
->monitor
, ^{
828 asldebug("cancel/close ASL file fd %d\n", fd
);
829 asl_file_close(aslf
);
832 dispatch_resume(af_data
->monitor
);
835 asldebug("_asl_file_open ASL file %s fd %d\n", r
->dst
->current_name
, fd
);
840 _text_file_close(asl_out_rule_t
*r
)
842 asl_action_file_data_t
*f_data
= (asl_action_file_data_t
*)r
->dst
->private;
843 if (f_data
->fd
< 0) return;
845 if (f_data
->pending
!= 0)
848 asprintf(&str
, "[Sender syslogd] [Level 4] [PID %u] [Facility syslog] [Message File %s was closed with %d pending messages]", global
.pid
, r
->dst
->current_name
, f_data
->pending
);
849 internal_log_message(str
);
853 if (f_data
->monitor
== NULL
)
856 * This should never happen, but _text_file_open allows
857 * dispatch_source_create to fail silently. If there is no dispatch
858 * source, we just close the file.
860 asldebug("close fd %d\n", f_data
->fd
);
866 * The monitor cancel handler will close the file.
868 dispatch_source_cancel(f_data
->monitor
);
869 dispatch_release(f_data
->monitor
);
870 f_data
->monitor
= NULL
;
877 _text_file_open(asl_out_rule_t
*r
)
879 asl_action_file_data_t
*f_data
= (asl_action_file_data_t
*)r
->dst
->private;
883 f_data
->fd
= _act_file_create_open(r
->dst
);
887 * lazy path creation: create path and retry
888 * _act_file_create_open does not create the path
891 int status
= asl_out_mkpath(global
.asl_out_module
, r
);
892 if (status
!= 0) return -1;
894 f_data
->fd
= _act_file_create_open(r
->dst
);
897 if (f_data
->fd
< 0) return -1;
899 f_data
->monitor
= dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE
, f_data
->fd
, DISPATCH_VNODE_DELETE
, asl_action_queue
);
900 if (f_data
->monitor
!= NULL
)
902 int ffd
= f_data
->fd
;
904 dispatch_source_set_event_handler(f_data
->monitor
, ^{
905 asldebug("dispatch_source DISPATCH_VNODE_DELETE fd %d\n", ffd
);
906 _act_dst_close(r
, DST_CLOSE_DELETED
);
909 dispatch_source_set_cancel_handler(f_data
->monitor
, ^{
910 asldebug("cancel/close file fd %d\n", ffd
);
914 dispatch_resume(f_data
->monitor
);
922 _act_dst_open(asl_out_rule_t
*r
, const time_t *tick
, uint64_t xid
)
924 if (r
== NULL
) return -1;
925 if (r
->dst
== NULL
) return -1;
926 if (r
->dst
->private == NULL
) return -1;
928 if (r
->action
== ACTION_ASL_DIR
)
930 if (_asl_dir_today_open(r
, tick
) != 0)
932 asldebug("_act_dst_open:_asl_dir_today_open %s FAILED!\n", r
->dst
->path
);
936 if (_asl_dir_storedata_open(r
, xid
) != 0)
938 asldebug("_act_dst_open:_asl_dir_storedata_open %s FAILED! Closing today file\n", r
->dst
->path
);
939 _asl_dir_today_close(r
);
946 if (r
->action
== ACTION_ASL_FILE
)
948 return _asl_file_open(r
);
951 if (r
->action
== ACTION_FILE
)
953 return _text_file_open(r
);
960 _act_dst_close(asl_out_rule_t
*r
, int why
)
962 if (r
== NULL
) return;
963 if (r
->dst
== NULL
) return;
964 if (r
->dst
->private == NULL
) return;
966 if (r
->action
== ACTION_ASL_DIR
)
968 asldebug("_act_dst_close: %s ASL DIR %s\n", why_str
[why
], r
->dst
->path
);
969 if (why
!= DST_CLOSE_CHECKPOINT
) _asl_dir_storedata_close(r
);
970 _asl_dir_today_close(r
);
972 else if (r
->action
== ACTION_ASL_FILE
)
974 asldebug("_act_dst_close: %s ASL FILE %s\n", why_str
[why
], (r
->dst
->current_name
== NULL
) ? r
->dst
->path
: r
->dst
->current_name
);
977 else if (r
->action
== ACTION_FILE
)
979 asldebug("_act_dst_close: %s FILE %s\n", why_str
[why
], (r
->dst
->current_name
== NULL
) ? r
->dst
->path
: r
->dst
->current_name
);
985 _act_store_file_setup(asl_out_module_t
*m
, asl_out_rule_t
*r
)
988 asl_action_asl_file_data_t
*af_data
;
990 if (r
== NULL
) return ASL_STATUS_INVALID_STORE
;
991 if (r
->dst
== NULL
) return ASL_STATUS_INVALID_STORE
;
992 if (r
->dst
->private == NULL
) return ASL_STATUS_INVALID_STORE
;
994 af_data
= (asl_action_asl_file_data_t
*)r
->dst
->private;
995 if (af_data
->aslfile
!= NULL
)
998 return ASL_STATUS_OK
;
1001 if (_act_dst_open(r
, NULL
, 0) != 0) return ASL_STATUS_WRITE_FAILED
;
1003 status
= asl_file_read_set_position(af_data
->aslfile
, ASL_FILE_POSITION_LAST
);
1004 if (status
!= ASL_STATUS_OK
)
1006 asldebug("_act_store_file_setup: asl_file_read_set_position failed %d %s\n", status
, asl_core_error(status
));
1007 _act_dst_close(r
, DST_CLOSE_ERROR
);
1011 af_data
->next_id
= af_data
->aslfile
->cursor_xid
+ 1;
1012 if (fseek(af_data
->aslfile
->store
, 0, SEEK_END
) != 0)
1014 asldebug("_act_store_file_setup: fseek failed %d %s\n", errno
, strerror(errno
));
1015 _act_dst_close(r
, DST_CLOSE_ERROR
);
1016 return ASL_STATUS_WRITE_FAILED
;
1019 return ASL_STATUS_OK
;
1023 * _act_store_dir_setup
1025 * Opens StoreData file and today's file
1026 * Reads ASL Message ID from StoreData file
1027 * Writes ASL Message ID + 1 to StoreData file
1030 _act_store_dir_setup(asl_out_module_t
*m
, asl_out_rule_t
*r
, time_t tick
)
1034 asl_action_asl_store_data_t
*as_data
;
1036 if (r
== NULL
) return ASL_STATUS_INVALID_STORE
;
1037 if (r
->dst
== NULL
) return ASL_STATUS_INVALID_STORE
;
1038 if (r
->dst
->private == NULL
) return ASL_STATUS_INVALID_STORE
;
1039 if (r
->dst
->path
== NULL
) return ASL_STATUS_INVALID_STORE
;
1041 as_data
= (asl_action_asl_store_data_t
*)r
->dst
->private;
1043 if (_act_dst_open(r
, NULL
, as_data
->next_id
) != 0)
1045 asldebug("_act_store_dir_setup: _act_dst_open %s failed\n", r
->dst
->path
);
1046 return ASL_STATUS_WRITE_FAILED
;
1049 /* get / set message id from StoreData file */
1051 rewind(as_data
->storedata
);
1052 if (fread(&xid
, sizeof(uint64_t), 1, as_data
->storedata
) != 1)
1054 asldebug("_act_store_dir_setup: storedata read failed %d %s\n", errno
, strerror(errno
));
1055 _act_dst_close(r
, DST_CLOSE_ERROR
);
1056 return ASL_STATUS_READ_FAILED
;
1059 xid
= asl_core_ntohq(xid
);
1061 as_data
->next_id
= xid
;
1063 xid
= asl_core_htonq(xid
);
1064 rewind(as_data
->storedata
);
1065 status
= fwrite(&xid
, sizeof(uint64_t), 1, as_data
->storedata
);
1068 asldebug("_act_store_dir_setup: storedata write failed %d %s\n", errno
, strerror(errno
));
1069 _act_dst_close(r
, DST_CLOSE_ERROR
);
1070 return ASL_STATUS_WRITE_FAILED
;
1073 fflush(as_data
->storedata
);
1075 if (fseek(as_data
->aslfile
->store
, 0, SEEK_END
) != 0)
1077 asldebug("_act_store_dir_setup: aslfile fseek failed %d %s\n", errno
, strerror(errno
));
1078 _act_dst_close(r
, DST_CLOSE_ERROR
);
1079 return ASL_STATUS_FAILED
;
1082 return ASL_STATUS_OK
;
1086 _asl_action_asl_store_data_free(asl_action_asl_store_data_t
*as_data
)
1088 if (as_data
== NULL
) return;
1093 _asl_action_asl_file_data_free(asl_action_asl_file_data_t
*af_data
)
1095 if (af_data
== NULL
) return;
1100 _asl_action_file_data_free(asl_action_file_data_t
*f_data
)
1102 if (f_data
== NULL
) return;
1104 if (f_data
->dup_timer
!= NULL
)
1106 if (f_data
->last_count
== 0)
1109 * The timer exists, but last_count is zero, so the timer is suspended.
1110 * Sources must not be released in when suspended.
1111 * So we resume it so that we can release it.
1113 dispatch_resume(f_data
->dup_timer
);
1116 dispatch_release(f_data
->dup_timer
);
1119 free(f_data
->last_msg
);
1124 _asl_action_set_param_data_free(asl_action_set_param_data_t
*spdata
)
1126 if (spdata
!= NULL
) notify_cancel(spdata
->token
);
1131 _asl_action_save_failed(const char *where
, asl_out_module_t
*m
, asl_out_rule_t
*r
, uint32_t status
)
1133 if (r
->dst
->flags
& MODULE_FLAG_SOFT_WRITE
) return;
1136 asldebug("%s: %s save to %s failed: %s\n", where
, m
->name
, r
->dst
->path
, asl_core_error(status
));
1138 /* disable further activity after multiple failures */
1139 if (r
->dst
->fails
> MAX_FAILURES
)
1142 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
));
1143 internal_log_message(str
);
1146 if (r
->action
== ACTION_ASL_DIR
) _asl_action_asl_store_data_free((asl_action_asl_store_data_t
*)r
->dst
->private);
1147 else if (r
->action
== ACTION_ASL_FILE
) _asl_action_asl_file_data_free((asl_action_asl_file_data_t
*)r
->dst
->private);
1148 else if (r
->action
== ACTION_FILE
) _asl_action_file_data_free((asl_action_file_data_t
*)r
->dst
->private);
1150 r
->dst
->private = NULL
;
1151 r
->action
= ACTION_NONE
;
1156 * Save a message in an ASL file.
1159 _act_store_file(asl_out_module_t
*m
, asl_out_rule_t
*r
, asl_msg_t
*msg
)
1161 asl_action_asl_file_data_t
*af_data
;
1165 if (r
== NULL
) return ACTION_STATUS_ERROR
;
1166 if (r
->dst
== NULL
) return ACTION_STATUS_ERROR
;
1167 if (r
->dst
->private == NULL
) return ACTION_STATUS_ERROR
;
1169 af_data
= (asl_action_asl_file_data_t
*)r
->dst
->private;
1170 if (af_data
->pending
> 0) af_data
->pending
--;
1172 status
= _act_store_file_setup(m
, r
);
1173 if (status
== ASL_STATUS_OK
)
1175 af_data
->last_time
= time(NULL
);
1178 mid
= af_data
->next_id
;
1180 /* save message to file and update dst size */
1181 status
= asl_file_save(af_data
->aslfile
, msg
, &mid
);
1182 if (status
== ASL_STATUS_OK
)
1184 r
->dst
->size
= af_data
->aslfile
->file_size
;
1186 if (_act_checkpoint(r
, CHECKPOINT_TEST
) == 1) asl_trigger_aslmanager();
1190 if (status
!= ASL_STATUS_OK
)
1192 _asl_action_save_failed("_act_store_file", m
, r
, status
);
1193 return ACTION_STATUS_ERROR
;
1196 return ACTION_STATUS_OK
;
1200 * Save a message in an ASL data store.
1203 _act_store_dir(asl_out_module_t
*m
, asl_out_rule_t
*r
, asl_msg_t
*msg
)
1205 asl_action_asl_store_data_t
*as_data
;
1211 if (r
== NULL
) return ACTION_STATUS_ERROR
;
1212 if (r
->dst
== NULL
) return ACTION_STATUS_ERROR
;
1213 if (r
->dst
->private == NULL
) return ACTION_STATUS_ERROR
;
1215 as_data
= (asl_action_asl_store_data_t
*)r
->dst
->private;
1216 if (as_data
->pending
> 0) as_data
->pending
--;
1218 val
= asl_msg_get_val_for_key(msg
, ASL_KEY_TIME
);
1219 if (val
== NULL
) return ACTION_STATUS_ERROR
;
1223 status
= _act_store_dir_setup(m
, r
, tick
);
1224 if (status
== ASL_STATUS_OK
)
1226 as_data
->last_time
= time(NULL
);
1229 mid
= as_data
->next_id
;
1230 status
= asl_file_save(as_data
->aslfile
, msg
, &mid
);
1231 if (status
== ASL_STATUS_OK
) r
->dst
->size
= as_data
->aslfile
->file_size
;
1232 else asldebug("_act_store_dir asl_file_save FAILED %s\n", asl_core_error(status
));
1233 //TODO: checkpoint here?
1235 * Currently, the checkpoint is in _asl_dir_today_open().
1236 * Moving it here would be in keeping with the way that
1237 * _act_store_file() and _act_file_final() do checkpoints.
1241 if (status
!= ASL_STATUS_OK
)
1243 _asl_action_save_failed("_act_store_dir", m
, r
, status
);
1244 return ACTION_STATUS_ERROR
;
1247 return ACTION_STATUS_OK
;
1251 _act_store_final(asl_out_module_t
*m
, asl_out_rule_t
*r
, asl_msg_t
*msg
)
1253 if (r
->action
== ACTION_ASL_DIR
) _act_store_dir(m
, r
, msg
);
1254 else _act_store_file(m
, r
, msg
);
1258 * Save a message to an ASL format file (ACTION_ASL_FILE)
1259 * or to an ASL directory (ACTION_ASL_DIR).
1262 _act_store(asl_out_module_t
*m
, asl_out_rule_t
*r
, asl_msg_t
*msg
)
1264 if (r
== NULL
) return;
1265 if (r
->dst
== NULL
) return;
1266 if (msg
== NULL
) return;
1267 if (m
== NULL
) return;
1268 if ((m
->flags
& MODULE_FLAG_ENABLED
) == 0) return;
1270 if (r
->dst
->flags
& MODULE_FLAG_HAS_LOGGED
) return;
1272 r
->dst
->flags
|= MODULE_FLAG_HAS_LOGGED
;
1273 if (r
->action
== ACTION_ASL_DIR
)
1275 asl_action_asl_store_data_t
*as_data
= (asl_action_asl_store_data_t
*)r
->dst
->private;
1276 if (as_data
!= NULL
) as_data
->pending
++;
1278 else if (r
->action
== ACTION_ASL_FILE
)
1280 asl_action_asl_file_data_t
*af_data
= (asl_action_asl_file_data_t
*)r
->dst
->private;
1281 if (af_data
!= NULL
) af_data
->pending
++;
1284 #if TARGET_OS_EMBEDDED
1285 if (r
->dst
->flags
& MODULE_FLAG_CRASHLOG
)
1287 _crashlog_queue_check();
1288 asl_msg_retain(msg
);
1289 dispatch_async(crashlog_queue
, ^{
1290 dispatch_async(asl_action_queue
, ^{
1291 _act_store_final(m
, r
, msg
);
1292 asl_msg_release(msg
);
1299 _act_store_final(m
, r
, msg
);
1303 _send_repeat_msg(asl_out_rule_t
*r
)
1305 asl_action_file_data_t
*f_data
;
1308 time_t now
= time(NULL
);
1310 if (r
== NULL
) return -1;
1311 if (r
->dst
== NULL
) return -1;
1312 if (r
->dst
->private == NULL
) return -1;
1314 f_data
= (asl_action_file_data_t
*)r
->dst
->private;
1316 free(f_data
->last_msg
);
1317 f_data
->last_msg
= NULL
;
1319 if (f_data
->last_count
== 0) return 0;
1321 /* stop the timer */
1322 dispatch_suspend(f_data
->dup_timer
);
1324 memset(vt
, 0, sizeof(vt
));
1329 asprintf(&msg
, "%s --- last message repeated %u time%s ---\n", vt
+ 4, f_data
->last_count
, (f_data
->last_count
== 1) ? "" : "s");
1330 f_data
->last_count
= 0;
1331 f_data
->last_time
= now
;
1332 if (msg
== NULL
) return -1;
1334 if (f_data
->fd
< 0) f_data
->fd
= _act_file_create_open(r
->dst
);
1337 status
= write(f_data
->fd
, msg
, len
);
1340 if ((status
< 0) || (status
< len
))
1342 asldebug("%s: error writing repeat message (%s): %s\n", MY_ID
, r
->dst
->current_name
, strerror(errno
));
1352 struct timespec midnight
;
1358 if (checkpoint_timer
!= NULL
) return;
1360 localtime_r(&x
, &t
);
1368 midnight
.tv_sec
= x
;
1369 midnight
.tv_nsec
= 0;
1371 checkpoint_timer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, asl_action_queue
);
1372 dispatch_source_set_timer(checkpoint_timer
, dispatch_walltime(&midnight
, 0), NSEC_PER_SEC
* SEC_PER_DAY
, 0);
1373 dispatch_source_set_event_handler(checkpoint_timer
, ^{ _act_file_checkpoint_all(CHECKPOINT_FORCE
); });
1374 dispatch_resume(checkpoint_timer
);
1377 /* check if a module path (mpath) matches a user path (upath) */
1379 _act_file_equal(const char *mpath
, const char *upath
)
1383 /* NULL upath means user wants to match all files */
1384 if (upath
== NULL
) return true;
1386 if (mpath
== NULL
) return false;
1388 /* check for exact match */
1389 if (!strcmp(mpath
, upath
)) return true;
1391 /* upath may be the last component of mpath */
1392 slash
= strrchr(mpath
, '/');
1393 if (slash
== NULL
) return false;
1395 if (!strcmp(slash
+ 1, upath
)) return true;
1400 _act_file_checkpoint(asl_out_module_t
*m
, const char *path
, uint32_t force
)
1403 int did_checkpoint
= 0;
1405 if (m
== NULL
) return 0;
1408 for (r
= m
->ruleset
; r
!= NULL
; r
= r
->next
)
1410 if ((r
->action
== ACTION_FILE
) || (r
->action
== ACTION_ASL_FILE
))
1412 if (r
->dst
->flags
& MODULE_FLAG_ROTATE
)
1414 if (_act_file_equal(r
->dst
->path
, path
))
1416 if (force
& CHECKPOINT_CRASH
)
1418 if (r
->dst
->flags
& MODULE_FLAG_CRASHLOG
)
1420 if (_act_checkpoint(r
, CHECKPOINT_FORCE
) > 0)
1423 _act_dst_close(r
, DST_CLOSE_CHECKPOINT
);
1429 if (_act_checkpoint(r
, force
) > 0)
1432 _act_dst_close(r
, DST_CLOSE_CHECKPOINT
);
1440 return did_checkpoint
;
1444 _act_file_checkpoint_all(uint32_t force
)
1446 asl_out_module_t
*m
;
1447 int did_checkpoint
= 0;
1449 for (m
= global
.asl_out_module
; m
!= NULL
; m
= m
->next
)
1451 if (_act_file_checkpoint(m
, NULL
, force
) > 0) did_checkpoint
= 1;
1454 asl_trigger_aslmanager();
1456 return did_checkpoint
;
1460 * Save a message in a plain text file.
1463 _act_file_final(asl_out_module_t
*m
, asl_out_rule_t
*r
, asl_msg_t
*msg
)
1465 asl_action_file_data_t
*f_data
;
1467 uint32_t len
, msg_hash
= 0;
1471 if (r
== NULL
) return;
1472 if (r
->dst
== NULL
) return;
1473 if (r
->dst
->private == NULL
) return;
1475 f_data
= (asl_action_file_data_t
*)r
->dst
->private;
1476 if (f_data
->pending
> 0) f_data
->pending
--;
1479 * If print format is std, bsd, or msg, then skip messages with
1480 * no ASL_KEY_MSG, or without a value for it.
1482 if (r
->dst
->flags
& MODULE_FLAG_STD_BSD_MSG
)
1484 const char *msgval
= NULL
;
1485 if (asl_msg_lookup(msg
, ASL_KEY_MSG
, &msgval
, NULL
) != 0) return;
1486 if (msgval
== NULL
) return;
1493 str
= asl_format_message(msg
, r
->dst
->fmt
, r
->dst
->tfmt
, ASL_ENCODE_SAFE
, &len
);
1495 if (r
->dst
->flags
& MODULE_FLAG_COALESCE
)
1497 if (f_data
->dup_timer
== NULL
)
1499 /* create a timer to flush dups on this file */
1500 f_data
->dup_timer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, asl_action_queue
);
1501 dispatch_source_set_event_handler(f_data
->dup_timer
, ^{ _send_repeat_msg(r
); });
1504 if ((global
.bsd_max_dup_time
> 0) && (str
!= NULL
) && (f_data
->last_msg
!= NULL
))
1506 msg_hash
= asl_core_string_hash(str
+ 16, len
- 16);
1507 if ((f_data
->last_hash
== msg_hash
) && (!strcmp(f_data
->last_msg
, str
+ 16)))
1509 if ((now
- f_data
->last_time
) < global
.bsd_max_dup_time
) is_dup
= 1;
1516 if (f_data
->last_count
== 0)
1518 /* start the timer */
1519 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);
1520 dispatch_resume(f_data
->dup_timer
);
1523 f_data
->last_count
++;
1527 if (_act_dst_open(r
, NULL
, 0) != 0)
1529 _asl_action_save_failed("_act_file", m
, r
, ASL_STATUS_FAILED
);
1539 * The current message is not a duplicate. If f_data->last_count > 0
1540 * we need to write a "last message repeated N times" log entry.
1541 * _send_repeat_msg will free last_msg and do nothing if
1542 * last_count == 0, but we test and free here to avoid a function call.
1544 if (f_data
->last_count
> 0)
1546 _send_repeat_msg(r
);
1550 free(f_data
->last_msg
);
1551 f_data
->last_msg
= NULL
;
1554 if (str
!= NULL
) f_data
->last_msg
= strdup(str
+ 16);
1556 f_data
->last_hash
= msg_hash
;
1557 f_data
->last_count
= 0;
1558 f_data
->last_time
= now
;
1560 if ((str
!= NULL
) && (len
> 1))
1562 /* write line to file and update dst size */
1563 size_t bytes
= write(f_data
->fd
, str
, len
- 1);
1564 if (bytes
> 0) r
->dst
->size
+= bytes
;
1566 if (_act_checkpoint(r
, CHECKPOINT_TEST
) == 1) asl_trigger_aslmanager();
1574 _act_file(asl_out_module_t
*m
, asl_out_rule_t
*r
, asl_msg_t
*msg
)
1576 asl_action_file_data_t
*f_data
;
1578 if (r
== NULL
) return;
1579 if (msg
== NULL
) return;
1580 if (m
== NULL
) return;
1581 if ((m
->flags
& MODULE_FLAG_ENABLED
) == 0) return;
1582 if (r
->dst
== NULL
) return;
1583 if (r
->dst
->private == NULL
) return;
1585 if (r
->dst
->flags
& MODULE_FLAG_HAS_LOGGED
) return;
1587 r
->dst
->flags
|= MODULE_FLAG_HAS_LOGGED
;
1588 f_data
= (asl_action_file_data_t
*)r
->dst
->private;
1589 if (f_data
!= NULL
) f_data
->pending
++;
1591 #if TARGET_OS_EMBEDDED
1592 if (r
->dst
->flags
& MODULE_FLAG_CRASHLOG
)
1594 _crashlog_queue_check();
1595 asl_msg_retain(msg
);
1596 dispatch_async(crashlog_queue
, ^{
1597 dispatch_async(asl_action_queue
, ^{
1598 _act_file_final(m
, r
, msg
);
1599 asl_msg_release(msg
);
1606 _act_file_final(m
, r
, msg
);
1610 _act_forward(asl_out_module_t
*m
, asl_out_rule_t
*r
, asl_msg_t
*msg
)
1612 /* To do: <rdar://problem/6130747> Add a "forward" action to asl.conf */
1616 _act_control(asl_out_module_t
*m
, asl_out_rule_t
*r
, asl_msg_t
*msg
)
1620 if (m
== NULL
) return;
1621 if (r
== NULL
) return;
1623 p
= asl_msg_get_val_for_key(msg
, ASL_KEY_MODULE
);
1625 if (r
->options
== NULL
) return;
1627 if (!strcmp(r
->options
, "enable"))
1629 m
->flags
|= MODULE_FLAG_ENABLED
;
1631 else if (!strcmp(r
->options
, "disable"))
1633 m
->flags
&= ~MODULE_FLAG_ENABLED
;
1635 else if ((!strcmp(r
->options
, "checkpoint")) || (!strcmp(r
->options
, "rotate")))
1637 _act_file_checkpoint(m
, NULL
, CHECKPOINT_FORCE
);
1642 _send_to_asl_store(asl_msg_t
*msg
)
1644 if ((global
.asl_out_module
!= NULL
) && ((global
.asl_out_module
->flags
& MODULE_FLAG_ENABLED
) == 0)) return;
1646 if (store_has_logged
) return;
1647 store_has_logged
= true;
1649 db_save_message(msg
);
1653 _asl_out_process_message(asl_out_module_t
*m
, asl_msg_t
*msg
)
1657 if (m
== NULL
) return 1;
1658 if (msg
== NULL
) return 1;
1660 /* reset flag bit used for duplicate avoidance */
1661 for (r
= m
->ruleset
; r
!= NULL
; r
= r
->next
)
1663 if ((r
->action
== ACTION_FILE
) || (r
->action
== ACTION_ASL_DIR
) || (r
->action
== ACTION_ASL_FILE
))
1665 if (r
->dst
!= NULL
) r
->dst
->flags
&= MODULE_FLAG_CLEAR_LOGGED
;
1669 for (r
= m
->ruleset
; r
!= NULL
; r
= r
->next
)
1671 if (r
->query
== NULL
) continue;
1673 /* ACTION_SET_FILE, ACTION_SET_PLIST, and ACTION_SET_PROF are handled independently */
1674 if ((r
->action
== ACTION_SET_FILE
) || (r
->action
== ACTION_SET_PLIST
) || (r
->action
== ACTION_SET_PROF
)) continue;
1677 * ACTION_CLAIM during processing is a filter. It will only be here if the option "only"
1678 * was supplied. In this case we test the message against the query. If it does not
1679 * match, we skip the message.
1681 if (r
->action
== ACTION_CLAIM
)
1683 if ((asl_msg_cmp(r
->query
, msg
) != 1)) return 0;
1686 if ((asl_msg_cmp(r
->query
, msg
) == 1))
1688 if (r
->action
== ACTION_NONE
) continue;
1689 else if (r
->action
== ACTION_IGNORE
) return 1;
1690 else if (r
->action
== ACTION_SKIP
) return 0;
1691 else if (r
->action
== ACTION_ASL_STORE
) _send_to_asl_store(msg
);
1692 else if (r
->action
== ACTION_ACCESS
) _act_access_control(m
, r
, msg
);
1693 else if (r
->action
== ACTION_SET_KEY
) _act_set_key(m
, r
, msg
);
1694 else if (r
->action
== ACTION_UNSET_KEY
) _act_unset_key(m
, r
, msg
);
1695 else if (r
->action
== ACTION_NOTIFY
) _act_notify(m
, r
);
1696 else if (r
->action
== ACTION_BROADCAST
) _act_broadcast(m
, r
, msg
);
1697 else if (r
->action
== ACTION_FORWARD
) _act_forward(m
, r
, msg
);
1698 else if (r
->action
== ACTION_CONTROL
) _act_control(m
, r
, msg
);
1699 else if (r
->action
== ACTION_SET_PARAM
) _act_out_set_param(m
, r
->options
, true);
1700 else if ((r
->action
== ACTION_ASL_FILE
) || (r
->action
== ACTION_ASL_DIR
)) _act_store(m
, r
, msg
);
1701 else if (r
->action
== ACTION_FILE
) _act_file(m
, r
, msg
);
1709 asl_out_message(asl_msg_t
*msg
, int64_t msize
)
1711 OSAtomicIncrement32(&global
.asl_queue_count
);
1712 asl_msg_retain(msg
);
1714 dispatch_async(asl_action_queue
, ^{
1717 time_t now
= time(NULL
);
1718 asl_out_module_t
*m
= global
.asl_out_module
;
1720 store_has_logged
= false;
1722 p
= asl_msg_get_val_for_key(msg
, ASL_KEY_MODULE
);
1725 if ((action_asl_store_count
== 0) || (asl_check_option(msg
, ASL_OPT_STORE
) == 1)) _send_to_asl_store(msg
);
1727 ignore
= _asl_out_process_message(m
, msg
);
1730 if (m
!= NULL
) m
= m
->next
;
1733 _asl_out_process_message(m
, msg
);
1740 if (m
!= NULL
) m
= m
->next
;
1743 if (!strcmp(p
, m
->name
)) _asl_out_process_message(m
, msg
);
1748 p
= asl_msg_get_val_for_key(msg
, ASL_KEY_FINAL_NOTIFICATION
);
1749 if (p
!= NULL
) asl_msg_set_key_val(msg
, ASL_KEY_FREE_NOTE
, p
);
1751 /* chain to the next output module (done this way to make queue size accounting easier */
1752 #if !TARGET_IPHONE_SIMULATOR
1753 if (global
.bsd_out_enabled
) bsd_out_message(msg
, msize
);
1754 else OSAtomicAdd64(-1ll * msize
, &global
.memory_size
);
1756 OSAtomicAdd64(-1ll * msize
, &global
.memory_size
);
1759 asl_msg_release(msg
);
1760 OSAtomicDecrement32(&global
.asl_queue_count
);
1762 if ((now
- sweep_time
) >= IDLE_CLOSE
)
1764 _asl_action_close_idle_files(IDLE_CLOSE
);
1771 _asl_action_profile_test(asl_out_module_t
*m
, asl_out_rule_t
*r
)
1777 /* ident is first message key */
1778 asl_msg_fetch(r
->query
, 0, &ident
, NULL
, NULL
);
1781 r
->action
= ACTION_NONE
;
1785 profile
= configuration_profile_to_asl_msg(ident
);
1786 eval
= (asl_msg_cmp(r
->query
, profile
) == 1);
1787 _act_out_set_param(m
, r
->options
, eval
);
1788 asl_msg_release(profile
);
1790 return strdup(ident
);
1794 _asl_action_file_test(asl_out_module_t
*m
, asl_out_rule_t
*r
)
1801 /* path is first message key */
1802 asl_msg_fetch(r
->query
, 0, &path
, NULL
, NULL
);
1805 r
->action
= ACTION_NONE
;
1809 memset(&sb
, 0, sizeof(struct stat
));
1810 status
= stat(path
, &sb
);
1811 eval
= (status
== 0);
1812 _act_out_set_param(m
, r
->options
, eval
);
1818 _asl_action_handle_file_change_notification(int t
)
1820 asl_out_module_t
*m
;
1823 for (m
= global
.asl_out_module
; m
!= NULL
; m
= m
->next
)
1825 for (r
= m
->ruleset
; r
!= NULL
; r
= r
->next
)
1827 if (r
->action
== ACTION_SET_FILE
)
1829 asl_action_set_param_data_t
*spdata
= (asl_action_set_param_data_t
*)r
->private;
1830 if ((spdata
!= NULL
) && (spdata
->token
== t
))
1832 _asl_action_file_test(m
, r
);
1836 else if (r
->action
== ACTION_SET_PLIST
)
1838 asl_action_set_param_data_t
*spdata
= (asl_action_set_param_data_t
*)r
->private;
1839 if ((spdata
!= NULL
) && (spdata
->token
== t
))
1841 char *str
= _asl_action_profile_test(m
, r
);
1846 else if (r
->action
== ACTION_SET_PROF
)
1848 asl_action_set_param_data_t
*spdata
= (asl_action_set_param_data_t
*)r
->private;
1849 if ((spdata
!= NULL
) && (spdata
->token
== t
))
1851 char *str
= _asl_action_profile_test(m
, r
);
1859 asl_out_module_free(m
);
1863 _asl_action_post_process_rule(asl_out_module_t
*m
, asl_out_rule_t
*r
)
1865 if ((m
== NULL
) || (r
== NULL
)) return;
1867 if (m
!= global
.asl_out_module
)
1869 /* check if any previous module has used this destination */
1870 asl_out_module_t
*n
;
1873 if ((r
->dst
!= NULL
) && (r
->dst
->path
!= NULL
))
1875 for (n
= global
.asl_out_module
; search
&& (n
!= NULL
) && (n
!= m
); n
= n
->next
)
1878 for (s
= n
->ruleset
; search
&& (s
!= NULL
); s
= s
->next
)
1880 if (s
->action
== ACTION_OUT_DEST
)
1882 if ((s
->dst
!= NULL
) && (s
->dst
->path
!= NULL
) && (!strcmp(r
->dst
->path
, s
->dst
->path
)))
1884 /* rule r of module m is using previously used dst of rule s of module n */
1885 asl_out_dst_data_release(r
->dst
);
1888 if (r
->action
== ACTION_OUT_DEST
)
1891 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
);
1892 internal_log_message(str
);
1897 r
->dst
= asl_out_dst_data_retain(s
->dst
);
1908 if (r
->action
== ACTION_SET_PARAM
)
1910 if (r
->query
== NULL
) _act_out_set_param(m
, r
->options
, true);
1912 else if (r
->action
== ACTION_CLAIM
)
1914 /* becomes ACTION_SKIP in com.apple.asl config */
1915 if (m
!= global
.asl_out_module
)
1917 asl_out_rule_t
*rule
= (asl_out_rule_t
*)calloc(1, sizeof(asl_out_rule_t
));
1921 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
);
1922 internal_log_message(str
);
1925 rule
->query
= asl_msg_copy(r
->query
);
1926 rule
->action
= ACTION_SKIP
;
1927 rule
->next
= global
.asl_out_module
->ruleset
;
1928 global
.asl_out_module
->ruleset
= rule
;
1932 * After adding ACTION_SKIP to com.apple.asl module, the claim becomes a no-op in this module
1933 * UNLESS the claim includes the option "only". In that case, the claim becomes a filter:
1934 * any messages that DO NOT match the claim are skipped by this module.
1936 if (r
->options
== NULL
) r
->action
= ACTION_NONE
;
1937 else if (strcmp(r
->options
, "only") != 0) r
->action
= ACTION_NONE
;
1940 else if (r
->action
== ACTION_ASL_STORE
)
1942 action_asl_store_count
++;
1944 else if (r
->action
== ACTION_ASL_DIR
)
1946 if (r
->dst
->private == NULL
) r
->dst
->private = (asl_action_asl_store_data_t
*)calloc(1, sizeof(asl_action_asl_store_data_t
));
1948 else if (r
->action
== ACTION_ASL_FILE
)
1950 if (r
->dst
->private == NULL
)r
->dst
->private = (asl_action_asl_file_data_t
*)calloc(1, sizeof(asl_action_asl_file_data_t
));
1952 else if (r
->action
== ACTION_FILE
)
1954 if (r
->dst
->private == NULL
) r
->dst
->private = (asl_action_file_data_t
*)calloc(1, sizeof(asl_action_file_data_t
));
1955 if (r
->dst
->private != NULL
) ((asl_action_file_data_t
*)(r
->dst
->private))->fd
= -1;
1957 else if (r
->action
== ACTION_SET_PLIST
)
1959 char *ident
=_asl_action_profile_test(m
, r
);
1960 char *notify_key
= configuration_profile_create_notification_key(ident
);
1963 if (notify_key
!= NULL
)
1966 asl_action_set_param_data_t
*spdata
;
1968 status
= notify_register_dispatch(notify_key
, &token
, asl_action_queue
, ^(int t
){
1969 _asl_action_handle_file_change_notification(t
);
1974 spdata
= (asl_action_set_param_data_t
*)calloc(1, sizeof(asl_action_set_param_data_t
));
1977 notify_cancel(token
);
1981 spdata
->token
= token
;
1982 r
->private = spdata
;
1986 else if (r
->action
== ACTION_SET_PROF
)
1988 char *ident
=_asl_action_profile_test(m
, r
);
1989 char *notify_key
= configuration_profile_create_notification_key(ident
);
1992 if (notify_key
!= NULL
)
1995 asl_action_set_param_data_t
*spdata
;
1997 status
= notify_register_dispatch(notify_key
, &token
, asl_action_queue
, ^(int t
){
1998 _asl_action_handle_file_change_notification(t
);
2003 spdata
= (asl_action_set_param_data_t
*)calloc(1, sizeof(asl_action_set_param_data_t
));
2006 notify_cancel(token
);
2010 spdata
->token
= token
;
2011 r
->private = spdata
;
2015 else if (r
->action
== ACTION_SET_FILE
)
2018 const char *path
=_asl_action_file_test(m
, r
);
2022 asprintf(¬ify_key
, "%s%s", NOTIFY_PATH_SERVICE
, path
);
2023 if (notify_key
!= NULL
)
2026 asl_action_set_param_data_t
*spdata
;
2028 status
= notify_register_dispatch(notify_key
, &token
, asl_action_queue
, ^(int t
){
2029 _asl_action_handle_file_change_notification(t
);
2034 spdata
= (asl_action_set_param_data_t
*)calloc(1, sizeof(asl_action_set_param_data_t
));
2037 notify_cancel(token
);
2041 spdata
->token
= token
;
2042 r
->private = spdata
;
2050 _asl_action_configure()
2053 asl_out_module_t
*m
;
2056 if (global
.asl_out_module
== NULL
) global
.asl_out_module
= asl_out_module_init();
2057 if (global
.asl_out_module
== NULL
) return;
2059 asldebug("%s: init\n", MY_ID
);
2061 action_asl_store_count
= 0;
2063 for (m
= global
.asl_out_module
; m
!= NULL
; m
= m
->next
)
2065 for (r
= m
->ruleset
; r
!= NULL
; r
= r
->next
)
2067 _asl_action_post_process_rule(m
, r
);
2068 if (r
->dst
!= NULL
) flags
|= (r
->dst
->flags
& (MODULE_FLAG_ROTATE
| MODULE_FLAG_CRASHLOG
));
2072 if (global
.debug
!= 0)
2075 if (global
.debug_file
== NULL
) dfp
= fopen(_PATH_SYSLOGD_LOG
, "a");
2076 else dfp
= fopen(global
.debug_file
, "a");
2079 for (m
= global
.asl_out_module
; m
!= NULL
; m
= m
->next
)
2081 fprintf(dfp
, "module: %s%s\n", (m
->name
== NULL
) ? "<unknown>" : m
->name
, (m
->flags
& MODULE_FLAG_LOCAL
) ? " (local)" : "");
2082 asl_out_module_print(dfp
, m
);
2089 sweep_time
= time(NULL
);
2091 if (flags
& MODULE_FLAG_ROTATE
)
2093 _act_file_checkpoint_all(CHECKPOINT_TEST
);
2094 if (checkpoint_timer
== NULL
) _start_cycling();
2099 asl_action_init(void)
2101 static dispatch_once_t once
;
2103 dispatch_once(&once
, ^{
2104 asl_action_queue
= dispatch_queue_create("ASL Action Queue", NULL
);
2105 #if TARGET_OS_EMBEDDED
2106 crashlog_queue
= dispatch_queue_create("iOS CrashLog Queue", NULL
);
2107 notify_register_dispatch(CRASH_MOVER_SERVICE
, &crashmover_token
, asl_action_queue
, ^(int unused
) {
2108 uint64_t cmstate
= 0;
2109 uint64_t oldstate
= (crashmover_state
== 0) ? 0llu : 1llu;
2111 uint32_t status
= notify_get_state(crashmover_token
, &cmstate
);
2114 if (cmstate
!= oldstate
)
2116 crashmover_state
= 0;
2117 if (cmstate
== 1) crashmover_state
= time(NULL
);
2119 if (crashmover_state
== 0)
2121 asldebug("CrashMover finished\n");
2122 dispatch_resume(crashlog_queue
);
2126 asldebug("CrashMover active: suspending crashlog queue and closing files\n");
2127 dispatch_suspend(crashlog_queue
);
2128 _asl_action_close_idle_files(0);
2136 _asl_action_configure();
2142 * Close outputs and free modules.
2145 _asl_action_free_modules(asl_out_module_t
*m
)
2148 asl_out_module_t
*x
;
2151 * asl_common frees a list of modules with asl_out_module_free.
2152 * This loop frees the private data attached some modules.
2154 for (x
= m
; x
!= NULL
; x
= x
->next
)
2156 for (r
= x
->ruleset
; r
!= NULL
; r
= r
->next
)
2158 if (r
->action
== ACTION_ASL_DIR
)
2160 _act_dst_close(r
, DST_CLOSE_SHUTDOWN
);
2163 _asl_action_asl_store_data_free((asl_action_asl_store_data_t
*)r
->dst
->private);
2164 r
->dst
->private = NULL
;
2167 else if (r
->action
== ACTION_ASL_FILE
)
2169 _act_dst_close(r
, DST_CLOSE_SHUTDOWN
);
2172 _asl_action_asl_file_data_free((asl_action_asl_file_data_t
*)r
->dst
->private);
2173 r
->dst
->private = NULL
;
2176 else if (r
->action
== ACTION_FILE
)
2178 _act_dst_close(r
, DST_CLOSE_SHUTDOWN
);
2181 asl_action_file_data_t
*f_data
= (asl_action_file_data_t
*)r
->dst
->private;
2184 /* flush repeat message if necessary */
2185 if (f_data
->last_count
> 0) _send_repeat_msg(r
);
2186 _asl_action_file_data_free(f_data
);
2187 r
->dst
->private = NULL
;
2191 else if (r
->action
== ACTION_SET_PLIST
)
2193 _asl_action_set_param_data_free((asl_action_set_param_data_t
*)r
->private);
2195 else if (r
->action
== ACTION_SET_PROF
)
2197 _asl_action_set_param_data_free((asl_action_set_param_data_t
*)r
->private);
2199 else if (r
->action
== ACTION_SET_FILE
)
2201 _asl_action_set_param_data_free((asl_action_set_param_data_t
*)r
->private);
2206 asl_out_module_free(m
);
2210 _asl_action_close_internal(void)
2212 #if TARGET_OS_EMBEDDED
2213 if (crashmover_state
!= 0)
2215 dispatch_resume(crashlog_queue
);
2216 crashmover_state
= 0;
2219 /* wait for the crashlog_queue to flush before _asl_action_free_modules() */
2220 dispatch_sync(crashlog_queue
, ^{ int x
= 0; if (x
== 1) x
= 2; });
2223 _asl_action_free_modules(global
.asl_out_module
);
2224 global
.asl_out_module
= NULL
;
2225 sweep_time
= time(NULL
);
2231 _asl_action_close_idle_files(time_t idle_time
)
2233 asl_out_module_t
*m
;
2234 time_t now
= time(NULL
);
2236 for (m
= global
.asl_out_module
; m
!= NULL
; m
= m
->next
)
2240 for (r
= m
->ruleset
; r
!= NULL
; r
= r
->next
)
2244 if ((r
->dst
!= NULL
) && (r
->dst
->flags
& MODULE_FLAG_CRASHLOG
))
2246 _act_dst_close(r
, DST_CLOSE_IDLE
);
2247 //TODO: can r->action even be ACTION_ASL_DIR?
2248 /* if not, we can avoid the extra check here */
2249 if (r
->action
!= ACTION_ASL_DIR
) _act_checkpoint(r
, CHECKPOINT_FORCE
);
2252 else if (r
->action
== ACTION_ASL_DIR
)
2256 asl_action_asl_store_data_t
*as_data
= (asl_action_asl_store_data_t
*)r
->dst
->private;
2257 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
);
2260 else if (r
->action
== ACTION_ASL_FILE
)
2264 asl_action_asl_file_data_t
*af_data
= (asl_action_asl_file_data_t
*)r
->dst
->private;
2265 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
);
2268 else if (r
->action
== ACTION_FILE
)
2272 asl_action_file_data_t
*f_data
= (asl_action_file_data_t
*)r
->dst
->private;
2273 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
);
2281 asl_action_close(void)
2283 dispatch_async(asl_action_queue
, ^{
2284 _asl_action_close_internal();
2291 asl_action_reset(void)
2293 dispatch_async(asl_action_queue
, ^{
2294 _asl_action_close_internal();
2302 _asl_action_module_with_name(const char *name
)
2304 asl_out_module_t
*m
;
2306 if (global
.asl_out_module
== NULL
) return NULL
;
2307 if (name
== NULL
) return global
.asl_out_module
;
2309 for (m
= global
.asl_out_module
; m
!= NULL
; m
= m
->next
)
2311 if ((m
->name
!= NULL
) && (!strcmp(m
->name
, name
))) return m
;
2318 * called from control_message
2319 * Used to control modules dynamically.
2320 * Line format "@ module param [value ...]"
2322 * Note this is synchronous on asl_action queue.
2325 asl_action_control_set_param(const char *s
)
2330 if (s
== NULL
) return -1;
2331 if (s
[0] == '\0') return 0;
2333 /* skip '@' and whitespace */
2335 while ((*s
== ' ') || (*s
== '\t')) s
++;
2337 l
= explode(s
, " \t");
2338 if (l
!= NULL
) for (count
= 0; l
[count
] != NULL
; count
++);
2340 /* at least 2 parameters (l[0] = module, l[1] = param) required */
2343 free_string_list(l
);
2347 if (global
.asl_out_module
== NULL
)
2349 asldebug("asl_action_control_set_param: no modules loaded\n");
2350 free_string_list(l
);
2354 /* create / modify a module */
2355 if ((!strcasecmp(l
[1], "define")) && (strcmp(l
[0], "*")))
2357 char *str
= strdup(s
);
2360 asldebug("asl_action_control_set_param: memory allocation failed\n");
2361 free_string_list(l
);
2365 dispatch_sync(asl_action_queue
, ^{
2366 asl_out_module_t
*m
;
2370 /* skip name, whitespace, "define" */
2371 while ((*p
!= ' ') && (*p
!= '\t')) p
++;
2372 while ((*p
== ' ') || (*p
== '\t')) p
++;
2373 while ((*p
!= ' ') && (*p
!= '\t')) p
++;
2375 m
= _asl_action_module_with_name(l
[0]);
2378 asl_out_module_t
*x
;
2380 m
= asl_out_module_new(l
[0]);
2381 for (x
= global
.asl_out_module
; x
->next
!= NULL
; x
= x
->next
);
2385 r
= asl_out_module_parse_line(m
, p
);
2388 _asl_action_post_process_rule(m
, r
);
2389 if ((r
->dst
!= NULL
) && (r
->dst
->flags
& MODULE_FLAG_ROTATE
))
2391 _act_file_checkpoint_all(CHECKPOINT_TEST
);
2392 if (checkpoint_timer
== NULL
) _start_cycling();
2398 free_string_list(l
);
2402 dispatch_sync(asl_action_queue
, ^{
2405 asl_out_module_t
*m
;
2407 if (!strcmp(l
[0], "*"))
2410 m
= _asl_action_module_with_name(NULL
);
2414 m
= _asl_action_module_with_name(l
[0]);
2419 if (!strcasecmp(l
[1], "enable"))
2423 /* don't do enable for ASL_MODULE_NAME if input name is "*" */
2424 if ((do_all
== 0) || (strcmp(m
->name
, ASL_MODULE_NAME
)))
2426 /* @ module enable {0|1} */
2427 if (count
> 2) intval
= atoi(l
[2]);
2429 if (intval
== 0) m
->flags
&= ~MODULE_FLAG_ENABLED
;
2430 else m
->flags
|= MODULE_FLAG_ENABLED
;
2433 else if (!strcasecmp(l
[1], "checkpoint"))
2435 /* @ module checkpoint [file] */
2436 if (count
> 2) _act_file_checkpoint(m
, l
[2], CHECKPOINT_FORCE
);
2437 else _act_file_checkpoint(m
, NULL
, CHECKPOINT_FORCE
);
2440 if (do_all
== 1) m
= m
->next
;
2446 free_string_list(l
);
2451 asl_action_file_checkpoint(const char *module, const char *path
)
2453 /* Note this is synchronous on asl_action queue */
2454 dispatch_sync(asl_action_queue
, ^{
2455 asl_out_module_t
*m
= _asl_action_module_with_name(module);
2456 _act_file_checkpoint(m
, path
, CHECKPOINT_FORCE
);
2463 asl_action_out_module_query(asl_msg_t
*q
, asl_msg_t
*m
, bool all
)
2465 dispatch_sync(asl_action_queue
, ^{
2466 asl_out_module_t
*om
;
2468 for (om
= global
.asl_out_module
; om
!= NULL
; om
= om
->next
)
2470 if (all
|| (0 == asl_msg_lookup(q
, om
->name
, NULL
, NULL
)))
2472 val
= om
->flags
& MODULE_FLAG_ENABLED
? "enabled" : "disabled";
2473 if (om
->name
== NULL
) asl_msg_set_key_val(m
, "asl.conf", val
);
2474 else asl_msg_set_key_val(m
, om
->name
, val
);