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_IPHONE && !TARGET_OS_SIMULATOR)
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_IPHONE && !TARGET_OS_SIMULATOR)
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_IPHONE && !TARGET_OS_SIMULATOR)
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_OS_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
;
545 bool size_only
= false;
547 if (r
== NULL
) return 0;
548 if (r
->dst
== NULL
) return 0;
550 fn
= r
->dst
->current_name
;
553 if (r
->dst
->path
== NULL
) return 0;
554 asl_dst_make_current_name(r
->dst
, 0, tmpcurrent_name
, sizeof(tmpcurrent_name
));
555 fn
= tmpcurrent_name
;
558 if ((force
== CHECKPOINT_TEST
) || (r
->dst
->flags
& MODULE_FLAG_SIZE_ONLY
))
563 if (size_only
&& (r
->dst
->file_max
== 0)) return 0;
565 if ((r
->dst
->size
== 0) || (r
->dst
->timestamp
== 0))
569 memset(&sb
, 0, sizeof(struct stat
));
571 if (stat(fn
, &sb
) < 0)
573 if (errno
== ENOENT
) return 0;
577 if (r
->dst
->timestamp
== 0) r
->dst
->timestamp
= sb
.st_birthtimespec
.tv_sec
;
578 if (r
->dst
->timestamp
== 0) r
->dst
->timestamp
= sb
.st_mtimespec
.tv_sec
;
579 r
->dst
->size
= sb
.st_size
;
582 if (size_only
&& (r
->dst
->size
< r
->dst
->file_max
)) return 0;
584 if (r
->dst
->flags
& MODULE_FLAG_BASESTAMP
)
586 _act_dst_close(r
, DST_CLOSE_CHECKPOINT
);
590 char srcpath
[MAXPATHLEN
];
591 char dstpath
[MAXPATHLEN
];
593 snprintf(srcpath
, sizeof(srcpath
), "%s", fn
);
595 r
->dst
->timestamp
= time(NULL
);
596 asl_dst_make_current_name(r
->dst
, MODULE_FLAG_BASESTAMP
, dstpath
, sizeof(dstpath
));
598 _act_dst_close(r
, DST_CLOSE_CHECKPOINT
);
599 if (strneq(srcpath
, dstpath
))
601 rename(srcpath
, dstpath
);
602 asldebug("CHECKPOINT RENAME %s %s\n", srcpath
, dstpath
);
606 r
->dst
->timestamp
= 0;
612 * Open today's ASL file in an ASL directory
613 * - Checks date and closes a currently open file if it has the wrong date
614 * - Opens today's file
617 _asl_dir_today_open(asl_out_rule_t
*r
, const time_t *tick
)
624 if (r
== NULL
) return -1;
625 if (r
->dst
== NULL
) return -1;
627 status
= _asl_dir_create(r
);
630 asldebug("_asl_dir_today_open: No directory at path %s\n", r
->dst
->path
);
634 asl_action_asl_store_data_t
*as_data
= (asl_action_asl_store_data_t
*)r
->dst
->private;
636 memset(&ctm
, 0, sizeof(struct tm
));
640 tick
= (const time_t *)&now
;
643 if (localtime_r(tick
, &ctm
) == NULL
)
645 asldebug("_asl_dir_today_open: localtime_r error %s\n", strerror(errno
));
649 /* checks file_max and closes if required */
650 status
= _act_checkpoint(r
, CHECKPOINT_TEST
);
651 if (status
== 1) asl_trigger_aslmanager();
653 if (as_data
->aslfile
!= NULL
)
656 if ((as_data
->p_year
== ctm
.tm_year
) && (as_data
->p_month
== ctm
.tm_mon
) && (as_data
->p_day
== ctm
.tm_mday
)) return 0;
658 /* Wrong date, close the current file */
659 _asl_dir_today_close(r
);
664 if (r
->dst
->flags
& MODULE_FLAG_BASESTAMP
)
671 tick
= (const time_t *)&now
;
674 asl_make_timestamp(now
, r
->dst
->style_flags
, tstamp
, sizeof(tstamp
));
675 asprintf(&(r
->dst
->current_name
), "%s/%s.asl", r
->dst
->path
, tstamp
);
679 asprintf(&(r
->dst
->current_name
), "%s/%d.%02d.%02d.asl", r
->dst
->path
, ctm
.tm_year
+ 1900, ctm
.tm_mon
+ 1, ctm
.tm_mday
);
683 if (r
->dst
->current_name
== NULL
)
685 asldebug("_asl_dir_today_open: asprintf error %s\n", strerror(errno
));
689 #if TARGET_OS_SIMULATOR
693 uid_t uid
= r
->dst
->uid
[0];
694 gid_t gid
= r
->dst
->gid
[0];
698 status
= asl_file_open_write(r
->dst
->current_name
, (r
->dst
->mode
& 00666), uid
, gid
, &(as_data
->aslfile
));
701 if (status
!= ASL_STATUS_OK
)
703 asldebug("_asl_dir_today_open: asl_file_open_write %s error %s\n", r
->dst
->current_name
, asl_core_error(status
));
704 free(r
->dst
->current_name
);
705 r
->dst
->current_name
= NULL
;
709 if (fseek(as_data
->aslfile
->store
, 0, SEEK_END
) != 0)
711 asldebug("_asl_dir_today_open: fseek %s error %s\n", r
->dst
->current_name
, strerror(errno
));
712 free(r
->dst
->current_name
);
713 r
->dst
->current_name
= NULL
;
717 as_data
->p_year
= ctm
.tm_year
;
718 as_data
->p_month
= ctm
.tm_mon
;
719 as_data
->p_day
= ctm
.tm_mday
;
721 /* create aslfile_monitor */
722 int fd
= fileno(as_data
->aslfile
->store
);
723 asl_file_t
*aslf
= as_data
->aslfile
;
725 as_data
->aslfile_monitor
= dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE
, fd
, DISPATCH_VNODE_DELETE
, asl_action_queue
);
726 if (as_data
->aslfile_monitor
!= NULL
)
728 dispatch_source_set_event_handler(as_data
->aslfile_monitor
, ^{
729 _act_dst_close(r
, DST_CLOSE_DELETED
);
732 dispatch_source_set_cancel_handler(as_data
->aslfile_monitor
, ^{
733 asldebug("cancel/close ASL file fd %d\n", fd
);
734 asl_file_close(aslf
);
737 dispatch_resume(as_data
->aslfile_monitor
);
740 asldebug("_asl_dir_today_open ASL file %s fd %d\n", r
->dst
->current_name
, fd
);
746 _asl_file_close(asl_out_rule_t
*r
)
748 if (r
== NULL
) return;
749 if (r
->dst
== NULL
) return;
751 asl_action_asl_file_data_t
*af_data
= (asl_action_asl_file_data_t
*)r
->dst
->private;
752 if (af_data
->aslfile
== NULL
) return;
754 if (af_data
->pending
!= 0)
757 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
);
758 internal_log_message(str
);
762 if (af_data
->monitor
== NULL
)
765 * This should never happen, but _asl_file_open allows
766 * dispatch_source_create to fail silently. If there is no dispatch
767 * source, we just close the file.
769 asldebug("close ASL fd %d\n", fileno(af_data
->aslfile
->store
));
770 asl_file_close(af_data
->aslfile
);
775 * The monitor cancel handler will close the file.
777 dispatch_source_cancel(af_data
->monitor
);
778 dispatch_release(af_data
->monitor
);
779 af_data
->monitor
= NULL
;
782 af_data
->aslfile
= NULL
;
786 _asl_file_open(asl_out_rule_t
*r
)
790 if (r
== NULL
) return -1;
791 if (r
->dst
== NULL
) return -1;
793 asl_action_asl_file_data_t
*af_data
= (asl_action_asl_file_data_t
*)r
->dst
->private;
794 if (af_data
->aslfile
!= NULL
) return 0;
796 /* create path if necessary */
797 status
= asl_out_mkpath(global
.asl_out_module
, r
);
800 asldebug("_asl_file_open: asl_out_mkpath %s failed\n", r
->dst
->path
);
804 fd
= _act_file_create_open(r
->dst
);
807 asldebug("_asl_file_open: _act_file_create_open %s failed %d %s\n", r
->dst
->current_name
, errno
, strerror(errno
));
813 if (r
->dst
->current_name
== NULL
) return -1;
815 status
= asl_file_open_write(r
->dst
->current_name
, 0, -1, -1, &(af_data
->aslfile
));
816 if (status
!= ASL_STATUS_OK
)
818 asldebug("_asl_file_open: asl_file_open_write %s failed %d %s\n", r
->dst
->current_name
, errno
, strerror(errno
));
823 fd
= fileno(af_data
->aslfile
->store
);
824 asl_file_t
*aslf
= af_data
->aslfile
;
826 af_data
->monitor
= dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE
, fd
, DISPATCH_VNODE_DELETE
, asl_action_queue
);
827 if (af_data
->monitor
!= NULL
)
829 dispatch_source_set_event_handler(af_data
->monitor
, ^{
830 _act_dst_close(r
, DST_CLOSE_DELETED
);
833 dispatch_source_set_cancel_handler(af_data
->monitor
, ^{
834 asldebug("cancel/close ASL file fd %d\n", fd
);
835 asl_file_close(aslf
);
838 dispatch_resume(af_data
->monitor
);
841 asldebug("_asl_file_open ASL file %s fd %d\n", r
->dst
->current_name
, fd
);
846 _text_file_close(asl_out_rule_t
*r
)
848 asl_action_file_data_t
*f_data
= (asl_action_file_data_t
*)r
->dst
->private;
849 if (f_data
->fd
< 0) return;
851 if (f_data
->pending
!= 0)
854 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
);
855 internal_log_message(str
);
859 if (f_data
->monitor
== NULL
)
862 * This should never happen, but _text_file_open allows
863 * dispatch_source_create to fail silently. If there is no dispatch
864 * source, we just close the file.
866 asldebug("close fd %d\n", f_data
->fd
);
872 * The monitor cancel handler will close the file.
874 dispatch_source_cancel(f_data
->monitor
);
875 dispatch_release(f_data
->monitor
);
876 f_data
->monitor
= NULL
;
883 _text_file_open(asl_out_rule_t
*r
)
885 asl_action_file_data_t
*f_data
= (asl_action_file_data_t
*)r
->dst
->private;
889 f_data
->fd
= _act_file_create_open(r
->dst
);
893 * lazy path creation: create path and retry
894 * _act_file_create_open does not create the path
897 int status
= asl_out_mkpath(global
.asl_out_module
, r
);
898 if (status
!= 0) return -1;
900 f_data
->fd
= _act_file_create_open(r
->dst
);
903 if (f_data
->fd
< 0) return -1;
905 f_data
->monitor
= dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE
, f_data
->fd
, DISPATCH_VNODE_DELETE
, asl_action_queue
);
906 if (f_data
->monitor
!= NULL
)
908 int ffd
= f_data
->fd
;
910 dispatch_source_set_event_handler(f_data
->monitor
, ^{
911 asldebug("dispatch_source DISPATCH_VNODE_DELETE fd %d\n", ffd
);
912 _act_dst_close(r
, DST_CLOSE_DELETED
);
915 dispatch_source_set_cancel_handler(f_data
->monitor
, ^{
916 asldebug("cancel/close file fd %d\n", ffd
);
920 dispatch_resume(f_data
->monitor
);
928 _act_dst_open(asl_out_rule_t
*r
, const time_t *tick
, uint64_t xid
)
930 if (r
== NULL
) return -1;
931 if (r
->dst
== NULL
) return -1;
932 if (r
->dst
->private == NULL
) return -1;
934 if (r
->action
== ACTION_ASL_DIR
)
936 if (_asl_dir_today_open(r
, tick
) != 0)
938 asldebug("_act_dst_open:_asl_dir_today_open %s FAILED!\n", r
->dst
->path
);
942 if (_asl_dir_storedata_open(r
, xid
) != 0)
944 asldebug("_act_dst_open:_asl_dir_storedata_open %s FAILED! Closing today file\n", r
->dst
->path
);
945 _asl_dir_today_close(r
);
952 if (r
->action
== ACTION_ASL_FILE
)
954 return _asl_file_open(r
);
957 if (r
->action
== ACTION_FILE
)
959 return _text_file_open(r
);
966 _act_dst_close(asl_out_rule_t
*r
, int why
)
968 if (r
== NULL
) return;
969 if (r
->dst
== NULL
) return;
970 if (r
->dst
->private == NULL
) return;
972 if (r
->action
== ACTION_ASL_DIR
)
974 asldebug("_act_dst_close: %s ASL DIR %s\n", why_str
[why
], r
->dst
->path
);
975 if (why
!= DST_CLOSE_CHECKPOINT
) _asl_dir_storedata_close(r
);
976 _asl_dir_today_close(r
);
978 else if (r
->action
== ACTION_ASL_FILE
)
980 asldebug("_act_dst_close: %s ASL FILE %s\n", why_str
[why
], (r
->dst
->current_name
== NULL
) ? r
->dst
->path
: r
->dst
->current_name
);
983 else if (r
->action
== ACTION_FILE
)
985 asldebug("_act_dst_close: %s FILE %s\n", why_str
[why
], (r
->dst
->current_name
== NULL
) ? r
->dst
->path
: r
->dst
->current_name
);
991 _act_store_file_setup(asl_out_module_t
*m
, asl_out_rule_t
*r
)
994 asl_action_asl_file_data_t
*af_data
;
996 if (r
== NULL
) return ASL_STATUS_INVALID_STORE
;
997 if (r
->dst
== NULL
) return ASL_STATUS_INVALID_STORE
;
998 if (r
->dst
->private == NULL
) return ASL_STATUS_INVALID_STORE
;
1000 af_data
= (asl_action_asl_file_data_t
*)r
->dst
->private;
1001 if (af_data
->aslfile
!= NULL
)
1004 return ASL_STATUS_OK
;
1007 if (_act_dst_open(r
, NULL
, 0) != 0) return ASL_STATUS_WRITE_FAILED
;
1009 status
= asl_file_read_set_position(af_data
->aslfile
, ASL_FILE_POSITION_LAST
);
1010 if (status
!= ASL_STATUS_OK
)
1012 asldebug("_act_store_file_setup: asl_file_read_set_position failed %d %s\n", status
, asl_core_error(status
));
1013 _act_dst_close(r
, DST_CLOSE_ERROR
);
1017 af_data
->next_id
= af_data
->aslfile
->cursor_xid
+ 1;
1018 if (fseek(af_data
->aslfile
->store
, 0, SEEK_END
) != 0)
1020 asldebug("_act_store_file_setup: fseek failed %d %s\n", errno
, strerror(errno
));
1021 _act_dst_close(r
, DST_CLOSE_ERROR
);
1022 return ASL_STATUS_WRITE_FAILED
;
1025 return ASL_STATUS_OK
;
1029 * _act_store_dir_setup
1031 * Opens StoreData file and today's file
1032 * Reads ASL Message ID from StoreData file
1033 * Writes ASL Message ID + 1 to StoreData file
1036 _act_store_dir_setup(asl_out_module_t
*m
, asl_out_rule_t
*r
, time_t tick
)
1040 asl_action_asl_store_data_t
*as_data
;
1042 if (r
== NULL
) return ASL_STATUS_INVALID_STORE
;
1043 if (r
->dst
== NULL
) return ASL_STATUS_INVALID_STORE
;
1044 if (r
->dst
->private == NULL
) return ASL_STATUS_INVALID_STORE
;
1045 if (r
->dst
->path
== NULL
) return ASL_STATUS_INVALID_STORE
;
1047 as_data
= (asl_action_asl_store_data_t
*)r
->dst
->private;
1049 if (_act_dst_open(r
, NULL
, as_data
->next_id
) != 0)
1051 asldebug("_act_store_dir_setup: _act_dst_open %s failed\n", r
->dst
->path
);
1052 return ASL_STATUS_WRITE_FAILED
;
1055 /* get / set message id from StoreData file */
1057 rewind(as_data
->storedata
);
1058 if (fread(&xid
, sizeof(uint64_t), 1, as_data
->storedata
) != 1)
1060 asldebug("_act_store_dir_setup: storedata read failed %d %s\n", errno
, strerror(errno
));
1061 _act_dst_close(r
, DST_CLOSE_ERROR
);
1062 return ASL_STATUS_READ_FAILED
;
1065 xid
= asl_core_ntohq(xid
);
1067 as_data
->next_id
= xid
;
1069 xid
= asl_core_htonq(xid
);
1070 rewind(as_data
->storedata
);
1071 status
= fwrite(&xid
, sizeof(uint64_t), 1, as_data
->storedata
);
1074 asldebug("_act_store_dir_setup: storedata write failed %d %s\n", errno
, strerror(errno
));
1075 _act_dst_close(r
, DST_CLOSE_ERROR
);
1076 return ASL_STATUS_WRITE_FAILED
;
1079 fflush(as_data
->storedata
);
1081 if (fseek(as_data
->aslfile
->store
, 0, SEEK_END
) != 0)
1083 asldebug("_act_store_dir_setup: aslfile fseek failed %d %s\n", errno
, strerror(errno
));
1084 _act_dst_close(r
, DST_CLOSE_ERROR
);
1085 return ASL_STATUS_FAILED
;
1088 return ASL_STATUS_OK
;
1092 _asl_action_asl_store_data_free(asl_action_asl_store_data_t
*as_data
)
1094 if (as_data
== NULL
) return;
1099 _asl_action_asl_file_data_free(asl_action_asl_file_data_t
*af_data
)
1101 if (af_data
== NULL
) return;
1106 _asl_action_file_data_free(asl_action_file_data_t
*f_data
)
1108 if (f_data
== NULL
) return;
1110 if (f_data
->dup_timer
!= NULL
)
1112 if (f_data
->last_count
== 0)
1115 * The timer exists, but last_count is zero, so the timer is suspended.
1116 * Sources must not be released in when suspended.
1117 * So we resume it so that we can release it.
1119 dispatch_resume(f_data
->dup_timer
);
1122 dispatch_release(f_data
->dup_timer
);
1125 free(f_data
->last_msg
);
1130 _asl_action_set_param_data_free(asl_action_set_param_data_t
*spdata
)
1132 if (spdata
!= NULL
) notify_cancel(spdata
->token
);
1137 _asl_action_save_failed(const char *where
, asl_out_module_t
*m
, asl_out_rule_t
*r
, uint32_t status
)
1139 if (r
->dst
->flags
& MODULE_FLAG_SOFT_WRITE
) return;
1142 asldebug("%s: %s save to %s failed: %s\n", where
, m
->name
, r
->dst
->path
, asl_core_error(status
));
1144 /* disable further activity after multiple failures */
1145 if (r
->dst
->fails
> MAX_FAILURES
)
1148 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
));
1149 internal_log_message(str
);
1152 if (r
->action
== ACTION_ASL_DIR
) _asl_action_asl_store_data_free((asl_action_asl_store_data_t
*)r
->dst
->private);
1153 else if (r
->action
== ACTION_ASL_FILE
) _asl_action_asl_file_data_free((asl_action_asl_file_data_t
*)r
->dst
->private);
1154 else if (r
->action
== ACTION_FILE
) _asl_action_file_data_free((asl_action_file_data_t
*)r
->dst
->private);
1156 r
->dst
->private = NULL
;
1157 r
->action
= ACTION_NONE
;
1162 * Save a message in an ASL file.
1165 _act_store_file(asl_out_module_t
*m
, asl_out_rule_t
*r
, asl_msg_t
*msg
)
1167 asl_action_asl_file_data_t
*af_data
;
1171 if (r
== NULL
) return ACTION_STATUS_ERROR
;
1172 if (r
->dst
== NULL
) return ACTION_STATUS_ERROR
;
1173 if (r
->dst
->private == NULL
) return ACTION_STATUS_ERROR
;
1175 af_data
= (asl_action_asl_file_data_t
*)r
->dst
->private;
1176 if (af_data
->pending
> 0) af_data
->pending
--;
1178 status
= _act_store_file_setup(m
, r
);
1179 if (status
== ASL_STATUS_OK
)
1181 af_data
->last_time
= time(NULL
);
1184 mid
= af_data
->next_id
;
1186 /* save message to file and update dst size */
1187 status
= asl_file_save(af_data
->aslfile
, msg
, &mid
);
1188 if (status
== ASL_STATUS_OK
)
1190 r
->dst
->size
= af_data
->aslfile
->file_size
;
1192 if (_act_checkpoint(r
, CHECKPOINT_TEST
) == 1) asl_trigger_aslmanager();
1196 if (status
!= ASL_STATUS_OK
)
1198 _asl_action_save_failed("_act_store_file", m
, r
, status
);
1199 return ACTION_STATUS_ERROR
;
1202 return ACTION_STATUS_OK
;
1206 * Save a message in an ASL data store.
1209 _act_store_dir(asl_out_module_t
*m
, asl_out_rule_t
*r
, asl_msg_t
*msg
)
1211 asl_action_asl_store_data_t
*as_data
;
1217 if (r
== NULL
) return ACTION_STATUS_ERROR
;
1218 if (r
->dst
== NULL
) return ACTION_STATUS_ERROR
;
1219 if (r
->dst
->private == NULL
) return ACTION_STATUS_ERROR
;
1221 as_data
= (asl_action_asl_store_data_t
*)r
->dst
->private;
1222 if (as_data
->pending
> 0) as_data
->pending
--;
1224 val
= asl_msg_get_val_for_key(msg
, ASL_KEY_TIME
);
1225 if (val
== NULL
) return ACTION_STATUS_ERROR
;
1229 status
= _act_store_dir_setup(m
, r
, tick
);
1230 if (status
== ASL_STATUS_OK
)
1232 as_data
->last_time
= time(NULL
);
1235 mid
= as_data
->next_id
;
1236 status
= asl_file_save(as_data
->aslfile
, msg
, &mid
);
1237 if (status
== ASL_STATUS_OK
) r
->dst
->size
= as_data
->aslfile
->file_size
;
1238 else asldebug("_act_store_dir asl_file_save FAILED %s\n", asl_core_error(status
));
1239 //TODO: checkpoint here?
1241 * Currently, the checkpoint is in _asl_dir_today_open().
1242 * Moving it here would be in keeping with the way that
1243 * _act_store_file() and _act_file_final() do checkpoints.
1247 if (status
!= ASL_STATUS_OK
)
1249 _asl_action_save_failed("_act_store_dir", m
, r
, status
);
1250 return ACTION_STATUS_ERROR
;
1253 return ACTION_STATUS_OK
;
1257 _act_store_final(asl_out_module_t
*m
, asl_out_rule_t
*r
, asl_msg_t
*msg
)
1259 if (r
->action
== ACTION_ASL_DIR
) _act_store_dir(m
, r
, msg
);
1260 else _act_store_file(m
, r
, msg
);
1264 * Save a message to an ASL format file (ACTION_ASL_FILE)
1265 * or to an ASL directory (ACTION_ASL_DIR).
1268 _act_store(asl_out_module_t
*m
, asl_out_rule_t
*r
, asl_msg_t
*msg
)
1270 if (r
== NULL
) return;
1271 if (r
->dst
== NULL
) return;
1272 if (msg
== NULL
) return;
1273 if (m
== NULL
) return;
1274 if ((m
->flags
& MODULE_FLAG_ENABLED
) == 0) return;
1276 if (r
->dst
->flags
& MODULE_FLAG_HAS_LOGGED
) return;
1278 r
->dst
->flags
|= MODULE_FLAG_HAS_LOGGED
;
1279 if (r
->action
== ACTION_ASL_DIR
)
1281 asl_action_asl_store_data_t
*as_data
= (asl_action_asl_store_data_t
*)r
->dst
->private;
1282 if (as_data
!= NULL
) as_data
->pending
++;
1284 else if (r
->action
== ACTION_ASL_FILE
)
1286 asl_action_asl_file_data_t
*af_data
= (asl_action_asl_file_data_t
*)r
->dst
->private;
1287 if (af_data
!= NULL
) af_data
->pending
++;
1290 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
1291 if (r
->dst
->flags
& MODULE_FLAG_CRASHLOG
)
1293 _crashlog_queue_check();
1294 asl_msg_retain(msg
);
1295 dispatch_async(crashlog_queue
, ^{
1296 dispatch_async(asl_action_queue
, ^{
1297 _act_store_final(m
, r
, msg
);
1298 asl_msg_release(msg
);
1305 _act_store_final(m
, r
, msg
);
1309 _send_repeat_msg(asl_out_rule_t
*r
)
1311 asl_action_file_data_t
*f_data
;
1314 time_t now
= time(NULL
);
1316 if (r
== NULL
) return -1;
1317 if (r
->dst
== NULL
) return -1;
1318 if (r
->dst
->private == NULL
) return -1;
1320 f_data
= (asl_action_file_data_t
*)r
->dst
->private;
1322 free(f_data
->last_msg
);
1323 f_data
->last_msg
= NULL
;
1325 if (f_data
->last_count
== 0) return 0;
1327 /* stop the timer */
1328 dispatch_suspend(f_data
->dup_timer
);
1330 memset(vt
, 0, sizeof(vt
));
1335 asprintf(&msg
, "%s --- last message repeated %u time%s ---\n", vt
+ 4, f_data
->last_count
, (f_data
->last_count
== 1) ? "" : "s");
1336 f_data
->last_count
= 0;
1337 f_data
->last_time
= now
;
1338 if (msg
== NULL
) return -1;
1340 if (f_data
->fd
< 0) f_data
->fd
= _act_file_create_open(r
->dst
);
1343 status
= write(f_data
->fd
, msg
, len
);
1346 if ((status
< 0) || (status
< len
))
1348 asldebug("%s: error writing repeat message (%s): %s\n", MY_ID
, r
->dst
->current_name
, strerror(errno
));
1358 struct timespec midnight
;
1364 if (checkpoint_timer
!= NULL
) return;
1366 localtime_r(&x
, &t
);
1374 midnight
.tv_sec
= x
;
1375 midnight
.tv_nsec
= 0;
1377 checkpoint_timer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, asl_action_queue
);
1378 dispatch_source_set_timer(checkpoint_timer
, dispatch_walltime(&midnight
, 0), NSEC_PER_SEC
* SEC_PER_DAY
, 0);
1379 dispatch_source_set_event_handler(checkpoint_timer
, ^{ _act_file_checkpoint_all(CHECKPOINT_FORCE
); });
1380 dispatch_resume(checkpoint_timer
);
1383 /* check if a module path (mpath) matches a user path (upath) */
1385 _act_file_equal(const char *mpath
, const char *upath
)
1389 /* NULL upath means user wants to match all files */
1390 if (upath
== NULL
) return true;
1392 if (mpath
== NULL
) return false;
1394 /* check for exact match */
1395 if (!strcmp(mpath
, upath
)) return true;
1397 /* upath may be the last component of mpath */
1398 slash
= strrchr(mpath
, '/');
1399 if (slash
== NULL
) return false;
1401 if (!strcmp(slash
+ 1, upath
)) return true;
1406 _act_file_checkpoint(asl_out_module_t
*m
, const char *path
, uint32_t force
)
1409 int did_checkpoint
= 0;
1411 if (m
== NULL
) return 0;
1413 for (r
= m
->ruleset
; r
!= NULL
; r
= r
->next
)
1415 if ((r
->action
== ACTION_FILE
) || (r
->action
== ACTION_ASL_FILE
))
1417 if (r
->dst
->flags
& MODULE_FLAG_ROTATE
)
1419 if (_act_file_equal(r
->dst
->path
, path
))
1421 if (_act_checkpoint(r
, force
) > 0)
1424 _act_dst_close(r
, DST_CLOSE_CHECKPOINT
);
1431 return did_checkpoint
;
1435 _act_file_checkpoint_all(uint32_t force
)
1437 asl_out_module_t
*m
;
1438 int did_checkpoint
= 0;
1440 for (m
= global
.asl_out_module
; m
!= NULL
; m
= m
->next
)
1442 if (_act_file_checkpoint(m
, NULL
, force
) > 0) did_checkpoint
= 1;
1445 asl_trigger_aslmanager();
1447 return did_checkpoint
;
1451 * Save a message in a plain text file.
1454 _act_file_final(asl_out_module_t
*m
, asl_out_rule_t
*r
, asl_msg_t
*msg
)
1456 asl_action_file_data_t
*f_data
;
1458 uint32_t len
, msg_hash
= 0;
1462 if (r
== NULL
) return;
1463 if (r
->dst
== NULL
) return;
1464 if (r
->dst
->private == NULL
) return;
1466 f_data
= (asl_action_file_data_t
*)r
->dst
->private;
1467 if (f_data
->pending
> 0) f_data
->pending
--;
1470 * If print format is std, bsd, or msg, then skip messages with
1471 * no ASL_KEY_MSG, or without a value for it.
1473 if (r
->dst
->flags
& MODULE_FLAG_STD_BSD_MSG
)
1475 const char *msgval
= NULL
;
1476 if (asl_msg_lookup(msg
, ASL_KEY_MSG
, &msgval
, NULL
) != 0) return;
1477 if (msgval
== NULL
) return;
1484 str
= asl_format_message(msg
, r
->dst
->fmt
, r
->dst
->tfmt
, ASL_ENCODE_SAFE
, &len
);
1486 if (r
->dst
->flags
& MODULE_FLAG_COALESCE
)
1488 if (f_data
->dup_timer
== NULL
)
1490 /* create a timer to flush dups on this file */
1491 f_data
->dup_timer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, asl_action_queue
);
1492 dispatch_source_set_event_handler(f_data
->dup_timer
, ^{ _send_repeat_msg(r
); });
1495 if ((global
.bsd_max_dup_time
> 0) && (str
!= NULL
) && (f_data
->last_msg
!= NULL
))
1497 msg_hash
= asl_core_string_hash(str
+ 16, len
- 16);
1498 if ((f_data
->last_hash
== msg_hash
) && (!strcmp(f_data
->last_msg
, str
+ 16)))
1500 if ((now
- f_data
->last_time
) < global
.bsd_max_dup_time
) is_dup
= 1;
1507 if (f_data
->last_count
== 0)
1509 /* start the timer */
1510 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);
1511 dispatch_resume(f_data
->dup_timer
);
1514 f_data
->last_count
++;
1518 if (_act_dst_open(r
, NULL
, 0) != 0)
1520 _asl_action_save_failed("_act_file", m
, r
, ASL_STATUS_FAILED
);
1530 * The current message is not a duplicate. If f_data->last_count > 0
1531 * we need to write a "last message repeated N times" log entry.
1532 * _send_repeat_msg will free last_msg and do nothing if
1533 * last_count == 0, but we test and free here to avoid a function call.
1535 if (f_data
->last_count
> 0)
1537 _send_repeat_msg(r
);
1541 free(f_data
->last_msg
);
1542 f_data
->last_msg
= NULL
;
1545 if (str
!= NULL
) f_data
->last_msg
= strdup(str
+ 16);
1547 f_data
->last_hash
= msg_hash
;
1548 f_data
->last_count
= 0;
1549 f_data
->last_time
= now
;
1551 if ((str
!= NULL
) && (len
> 1))
1553 /* write line to file and update dst size */
1554 size_t bytes
= write(f_data
->fd
, str
, len
- 1);
1555 if (bytes
> 0) r
->dst
->size
+= bytes
;
1557 if (_act_checkpoint(r
, CHECKPOINT_TEST
) == 1) asl_trigger_aslmanager();
1565 _act_file(asl_out_module_t
*m
, asl_out_rule_t
*r
, asl_msg_t
*msg
)
1567 asl_action_file_data_t
*f_data
;
1569 if (r
== NULL
) return;
1570 if (msg
== NULL
) return;
1571 if (m
== NULL
) return;
1572 if ((m
->flags
& MODULE_FLAG_ENABLED
) == 0) return;
1573 if (r
->dst
== NULL
) return;
1574 if (r
->dst
->private == NULL
) return;
1576 if (r
->dst
->flags
& MODULE_FLAG_HAS_LOGGED
) return;
1578 r
->dst
->flags
|= MODULE_FLAG_HAS_LOGGED
;
1579 f_data
= (asl_action_file_data_t
*)r
->dst
->private;
1580 if (f_data
!= NULL
) f_data
->pending
++;
1582 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
1583 if (r
->dst
->flags
& MODULE_FLAG_CRASHLOG
)
1585 _crashlog_queue_check();
1586 asl_msg_retain(msg
);
1587 dispatch_async(crashlog_queue
, ^{
1588 dispatch_async(asl_action_queue
, ^{
1589 _act_file_final(m
, r
, msg
);
1590 asl_msg_release(msg
);
1597 _act_file_final(m
, r
, msg
);
1601 _act_forward(asl_out_module_t
*m
, asl_out_rule_t
*r
, asl_msg_t
*msg
)
1603 /* To do: <rdar://problem/6130747> Add a "forward" action to asl.conf */
1607 _act_control(asl_out_module_t
*m
, asl_out_rule_t
*r
, asl_msg_t
*msg
)
1611 if (m
== NULL
) return;
1612 if (r
== NULL
) return;
1614 p
= asl_msg_get_val_for_key(msg
, ASL_KEY_MODULE
);
1616 if (r
->options
== NULL
) return;
1618 if (!strcmp(r
->options
, "enable"))
1620 m
->flags
|= MODULE_FLAG_ENABLED
;
1622 else if (!strcmp(r
->options
, "disable"))
1624 m
->flags
&= ~MODULE_FLAG_ENABLED
;
1626 else if ((!strcmp(r
->options
, "checkpoint")) || (!strcmp(r
->options
, "rotate")))
1628 _act_file_checkpoint(m
, NULL
, CHECKPOINT_FORCE
);
1633 _send_to_asl_store(asl_msg_t
*msg
)
1635 if ((global
.asl_out_module
!= NULL
) && ((global
.asl_out_module
->flags
& MODULE_FLAG_ENABLED
) == 0)) return;
1637 if (store_has_logged
) return;
1638 store_has_logged
= true;
1640 db_save_message(msg
);
1644 _asl_out_process_message(asl_out_module_t
*m
, asl_msg_t
*msg
)
1648 if (m
== NULL
) return 1;
1649 if (msg
== NULL
) return 1;
1651 /* reset flag bit used for duplicate avoidance */
1652 for (r
= m
->ruleset
; r
!= NULL
; r
= r
->next
)
1654 if ((r
->action
== ACTION_FILE
) || (r
->action
== ACTION_ASL_DIR
) || (r
->action
== ACTION_ASL_FILE
))
1656 if (r
->dst
!= NULL
) r
->dst
->flags
&= MODULE_FLAG_CLEAR_LOGGED
;
1660 for (r
= m
->ruleset
; r
!= NULL
; r
= r
->next
)
1662 if (r
->query
== NULL
) continue;
1664 /* ACTION_SET_FILE, ACTION_SET_PLIST, and ACTION_SET_PROF are handled independently */
1665 if ((r
->action
== ACTION_SET_FILE
) || (r
->action
== ACTION_SET_PLIST
) || (r
->action
== ACTION_SET_PROF
)) continue;
1668 * ACTION_CLAIM during processing is a filter. It will only be here if the option "only"
1669 * was supplied. In this case we test the message against the query. If it does not
1670 * match, we skip the message.
1672 if (r
->action
== ACTION_CLAIM
)
1674 if ((asl_msg_cmp(r
->query
, msg
) != 1)) return 0;
1677 if ((asl_msg_cmp(r
->query
, msg
) == 1))
1679 if (r
->action
== ACTION_NONE
) continue;
1680 else if (r
->action
== ACTION_IGNORE
) return 1;
1681 else if (r
->action
== ACTION_SKIP
) return 0;
1682 else if (r
->action
== ACTION_ASL_STORE
) _send_to_asl_store(msg
);
1683 else if (r
->action
== ACTION_ACCESS
) _act_access_control(m
, r
, msg
);
1684 else if (r
->action
== ACTION_SET_KEY
) _act_set_key(m
, r
, msg
);
1685 else if (r
->action
== ACTION_UNSET_KEY
) _act_unset_key(m
, r
, msg
);
1686 else if (r
->action
== ACTION_NOTIFY
) _act_notify(m
, r
);
1687 else if (r
->action
== ACTION_BROADCAST
) _act_broadcast(m
, r
, msg
);
1688 else if (r
->action
== ACTION_FORWARD
) _act_forward(m
, r
, msg
);
1689 else if (r
->action
== ACTION_CONTROL
) _act_control(m
, r
, msg
);
1690 else if (r
->action
== ACTION_SET_PARAM
) _act_out_set_param(m
, r
->options
, true);
1691 else if ((r
->action
== ACTION_ASL_FILE
) || (r
->action
== ACTION_ASL_DIR
)) _act_store(m
, r
, msg
);
1692 else if (r
->action
== ACTION_FILE
) _act_file(m
, r
, msg
);
1700 asl_out_message(asl_msg_t
*msg
, int64_t msize
)
1702 OSAtomicIncrement32(&global
.asl_queue_count
);
1703 asl_msg_retain(msg
);
1705 dispatch_async(asl_action_queue
, ^{
1708 time_t now
= time(NULL
);
1709 asl_out_module_t
*m
= global
.asl_out_module
;
1711 store_has_logged
= false;
1713 p
= asl_msg_get_val_for_key(msg
, ASL_KEY_MODULE
);
1716 if ((action_asl_store_count
== 0) || (asl_check_option(msg
, ASL_OPT_STORE
) == 1)) _send_to_asl_store(msg
);
1718 ignore
= _asl_out_process_message(m
, msg
);
1721 if (m
!= NULL
) m
= m
->next
;
1724 _asl_out_process_message(m
, msg
);
1731 if (m
!= NULL
) m
= m
->next
;
1734 if (!strcmp(p
, m
->name
)) _asl_out_process_message(m
, msg
);
1739 p
= asl_msg_get_val_for_key(msg
, ASL_KEY_FINAL_NOTIFICATION
);
1740 if (p
!= NULL
) asl_msg_set_key_val(msg
, ASL_KEY_FREE_NOTE
, p
);
1742 /* chain to the next output module (done this way to make queue size accounting easier */
1743 #if !TARGET_OS_SIMULATOR
1744 if (global
.bsd_out_enabled
) bsd_out_message(msg
, msize
);
1745 else OSAtomicAdd64(-1ll * msize
, &global
.memory_size
);
1747 OSAtomicAdd64(-1ll * msize
, &global
.memory_size
);
1750 asl_msg_release(msg
);
1751 OSAtomicDecrement32(&global
.asl_queue_count
);
1753 if ((now
- sweep_time
) >= IDLE_CLOSE
)
1755 _asl_action_close_idle_files(IDLE_CLOSE
);
1762 _asl_action_profile_test(asl_out_module_t
*m
, asl_out_rule_t
*r
)
1768 /* ident is first message key */
1769 asl_msg_fetch(r
->query
, 0, &ident
, NULL
, NULL
);
1772 r
->action
= ACTION_NONE
;
1776 profile
= configuration_profile_to_asl_msg(ident
);
1777 eval
= (asl_msg_cmp(r
->query
, profile
) == 1);
1778 _act_out_set_param(m
, r
->options
, eval
);
1779 asl_msg_release(profile
);
1781 return strdup(ident
);
1785 _asl_action_file_test(asl_out_module_t
*m
, asl_out_rule_t
*r
)
1792 /* path is first message key */
1793 asl_msg_fetch(r
->query
, 0, &path
, NULL
, NULL
);
1796 r
->action
= ACTION_NONE
;
1800 memset(&sb
, 0, sizeof(struct stat
));
1801 status
= stat(path
, &sb
);
1802 eval
= (status
== 0);
1803 _act_out_set_param(m
, r
->options
, eval
);
1809 _asl_action_handle_file_change_notification(int t
)
1811 asl_out_module_t
*m
;
1814 for (m
= global
.asl_out_module
; m
!= NULL
; m
= m
->next
)
1816 for (r
= m
->ruleset
; r
!= NULL
; r
= r
->next
)
1818 if (r
->action
== ACTION_SET_FILE
)
1820 asl_action_set_param_data_t
*spdata
= (asl_action_set_param_data_t
*)r
->private;
1821 if ((spdata
!= NULL
) && (spdata
->token
== t
))
1823 _asl_action_file_test(m
, r
);
1827 else if (r
->action
== ACTION_SET_PLIST
)
1829 asl_action_set_param_data_t
*spdata
= (asl_action_set_param_data_t
*)r
->private;
1830 if ((spdata
!= NULL
) && (spdata
->token
== t
))
1832 char *str
= _asl_action_profile_test(m
, r
);
1837 else if (r
->action
== ACTION_SET_PROF
)
1839 asl_action_set_param_data_t
*spdata
= (asl_action_set_param_data_t
*)r
->private;
1840 if ((spdata
!= NULL
) && (spdata
->token
== t
))
1842 char *str
= _asl_action_profile_test(m
, r
);
1850 asl_out_module_free(m
);
1854 _asl_action_post_process_rule(asl_out_module_t
*m
, asl_out_rule_t
*r
)
1856 if ((m
== NULL
) || (r
== NULL
)) return;
1858 if (m
!= global
.asl_out_module
)
1860 /* check if any previous module has used this destination */
1861 asl_out_module_t
*n
;
1864 if ((r
->dst
!= NULL
) && (r
->dst
->path
!= NULL
))
1866 for (n
= global
.asl_out_module
; search
&& (n
!= NULL
) && (n
!= m
); n
= n
->next
)
1869 for (s
= n
->ruleset
; search
&& (s
!= NULL
); s
= s
->next
)
1871 if (s
->action
== ACTION_OUT_DEST
)
1873 if ((s
->dst
!= NULL
) && (s
->dst
->path
!= NULL
) && (!strcmp(r
->dst
->path
, s
->dst
->path
)))
1875 /* rule r of module m is using previously used dst of rule s of module n */
1876 asl_out_dst_data_release(r
->dst
);
1879 if (r
->action
== ACTION_OUT_DEST
)
1882 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
);
1883 internal_log_message(str
);
1888 r
->dst
= asl_out_dst_data_retain(s
->dst
);
1899 if (r
->action
== ACTION_SET_PARAM
)
1901 if (r
->query
== NULL
) _act_out_set_param(m
, r
->options
, true);
1903 else if (r
->action
== ACTION_CLAIM
)
1905 /* becomes ACTION_SKIP in com.apple.asl config */
1906 if (m
!= global
.asl_out_module
)
1908 asl_out_rule_t
*rule
= (asl_out_rule_t
*)calloc(1, sizeof(asl_out_rule_t
));
1912 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
);
1913 internal_log_message(str
);
1916 rule
->query
= asl_msg_copy(r
->query
);
1917 rule
->action
= ACTION_SKIP
;
1918 rule
->next
= global
.asl_out_module
->ruleset
;
1919 global
.asl_out_module
->ruleset
= rule
;
1923 * After adding ACTION_SKIP to com.apple.asl module, the claim becomes a no-op in this module
1924 * UNLESS the claim includes the option "only". In that case, the claim becomes a filter:
1925 * any messages that DO NOT match the claim are skipped by this module.
1927 if (r
->options
== NULL
) r
->action
= ACTION_NONE
;
1928 else if (strcmp(r
->options
, "only") != 0) r
->action
= ACTION_NONE
;
1931 else if (r
->action
== ACTION_ASL_STORE
)
1933 action_asl_store_count
++;
1935 else if (r
->action
== ACTION_ASL_DIR
)
1937 if (r
->dst
->private == NULL
) r
->dst
->private = (asl_action_asl_store_data_t
*)calloc(1, sizeof(asl_action_asl_store_data_t
));
1939 else if (r
->action
== ACTION_ASL_FILE
)
1941 if (r
->dst
->private == NULL
)r
->dst
->private = (asl_action_asl_file_data_t
*)calloc(1, sizeof(asl_action_asl_file_data_t
));
1943 else if (r
->action
== ACTION_FILE
)
1945 if (r
->dst
->private == NULL
) r
->dst
->private = (asl_action_file_data_t
*)calloc(1, sizeof(asl_action_file_data_t
));
1946 if (r
->dst
->private != NULL
) ((asl_action_file_data_t
*)(r
->dst
->private))->fd
= -1;
1948 else if (r
->action
== ACTION_SET_PLIST
)
1950 char *ident
=_asl_action_profile_test(m
, r
);
1951 char *notify_key
= configuration_profile_create_notification_key(ident
);
1954 if (notify_key
!= NULL
)
1957 asl_action_set_param_data_t
*spdata
;
1959 status
= notify_register_dispatch(notify_key
, &token
, asl_action_queue
, ^(int t
){
1960 _asl_action_handle_file_change_notification(t
);
1965 spdata
= (asl_action_set_param_data_t
*)calloc(1, sizeof(asl_action_set_param_data_t
));
1968 notify_cancel(token
);
1972 spdata
->token
= token
;
1973 r
->private = spdata
;
1977 else if (r
->action
== ACTION_SET_PROF
)
1979 char *ident
=_asl_action_profile_test(m
, r
);
1980 char *notify_key
= configuration_profile_create_notification_key(ident
);
1983 if (notify_key
!= NULL
)
1986 asl_action_set_param_data_t
*spdata
;
1988 status
= notify_register_dispatch(notify_key
, &token
, asl_action_queue
, ^(int t
){
1989 _asl_action_handle_file_change_notification(t
);
1994 spdata
= (asl_action_set_param_data_t
*)calloc(1, sizeof(asl_action_set_param_data_t
));
1997 notify_cancel(token
);
2001 spdata
->token
= token
;
2002 r
->private = spdata
;
2006 else if (r
->action
== ACTION_SET_FILE
)
2009 const char *path
=_asl_action_file_test(m
, r
);
2013 asprintf(¬ify_key
, "%s%s", NOTIFY_PATH_SERVICE
, path
);
2014 if (notify_key
!= NULL
)
2017 asl_action_set_param_data_t
*spdata
;
2019 status
= notify_register_dispatch(notify_key
, &token
, asl_action_queue
, ^(int t
){
2020 _asl_action_handle_file_change_notification(t
);
2025 spdata
= (asl_action_set_param_data_t
*)calloc(1, sizeof(asl_action_set_param_data_t
));
2028 notify_cancel(token
);
2032 spdata
->token
= token
;
2033 r
->private = spdata
;
2041 _asl_action_configure()
2044 asl_out_module_t
*m
;
2047 if (global
.asl_out_module
== NULL
) global
.asl_out_module
= asl_out_module_init();
2048 if (global
.asl_out_module
== NULL
) return;
2050 asldebug("%s: init\n", MY_ID
);
2052 action_asl_store_count
= 0;
2054 for (m
= global
.asl_out_module
; m
!= NULL
; m
= m
->next
)
2056 for (r
= m
->ruleset
; r
!= NULL
; r
= r
->next
)
2058 _asl_action_post_process_rule(m
, r
);
2059 if (r
->dst
!= NULL
) flags
|= (r
->dst
->flags
& (MODULE_FLAG_ROTATE
| MODULE_FLAG_CRASHLOG
));
2063 if (global
.debug
!= 0)
2066 if (global
.debug_file
== NULL
) dfp
= fopen(_PATH_SYSLOGD_LOG
, "a");
2067 else dfp
= fopen(global
.debug_file
, "a");
2070 for (m
= global
.asl_out_module
; m
!= NULL
; m
= m
->next
)
2072 fprintf(dfp
, "module: %s%s\n", (m
->name
== NULL
) ? "<unknown>" : m
->name
, (m
->flags
& MODULE_FLAG_LOCAL
) ? " (local)" : "");
2073 asl_out_module_print(dfp
, m
);
2080 sweep_time
= time(NULL
);
2082 if (flags
& MODULE_FLAG_ROTATE
)
2084 _act_file_checkpoint_all(CHECKPOINT_TEST
);
2085 if (checkpoint_timer
== NULL
) _start_cycling();
2090 asl_action_init(void)
2092 static dispatch_once_t once
;
2094 dispatch_once(&once
, ^{
2095 asl_action_queue
= dispatch_queue_create("ASL Action Queue", NULL
);
2096 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
2097 crashlog_queue
= dispatch_queue_create("iOS CrashLog Queue", NULL
);
2098 notify_register_dispatch(CRASH_MOVER_SERVICE
, &crashmover_token
, asl_action_queue
, ^(int unused
) {
2099 uint64_t cmstate
= 0;
2100 uint64_t oldstate
= (crashmover_state
== 0) ? 0llu : 1llu;
2102 uint32_t status
= notify_get_state(crashmover_token
, &cmstate
);
2105 if (cmstate
!= oldstate
)
2107 crashmover_state
= 0;
2108 if (cmstate
== 1) crashmover_state
= time(NULL
);
2110 if (crashmover_state
== 0)
2112 asldebug("CrashMover finished\n");
2113 dispatch_resume(crashlog_queue
);
2117 asldebug("CrashMover active: suspending crashlog queue and closing files\n");
2118 dispatch_suspend(crashlog_queue
);
2119 _asl_action_close_idle_files(0);
2127 _asl_action_configure();
2133 * Close outputs and free modules.
2136 _asl_action_free_modules(asl_out_module_t
*m
)
2139 asl_out_module_t
*x
;
2142 * asl_common frees a list of modules with asl_out_module_free.
2143 * This loop frees the private data attached some modules.
2145 for (x
= m
; x
!= NULL
; x
= x
->next
)
2147 for (r
= x
->ruleset
; r
!= NULL
; r
= r
->next
)
2149 if (r
->action
== ACTION_ASL_DIR
)
2151 _act_dst_close(r
, DST_CLOSE_SHUTDOWN
);
2154 _asl_action_asl_store_data_free((asl_action_asl_store_data_t
*)r
->dst
->private);
2155 r
->dst
->private = NULL
;
2158 else if (r
->action
== ACTION_ASL_FILE
)
2160 _act_dst_close(r
, DST_CLOSE_SHUTDOWN
);
2163 _asl_action_asl_file_data_free((asl_action_asl_file_data_t
*)r
->dst
->private);
2164 r
->dst
->private = NULL
;
2167 else if (r
->action
== ACTION_FILE
)
2169 _act_dst_close(r
, DST_CLOSE_SHUTDOWN
);
2172 asl_action_file_data_t
*f_data
= (asl_action_file_data_t
*)r
->dst
->private;
2175 /* flush repeat message if necessary */
2176 if (f_data
->last_count
> 0) _send_repeat_msg(r
);
2177 _asl_action_file_data_free(f_data
);
2178 r
->dst
->private = NULL
;
2182 else if (r
->action
== ACTION_SET_PLIST
)
2184 _asl_action_set_param_data_free((asl_action_set_param_data_t
*)r
->private);
2186 else if (r
->action
== ACTION_SET_PROF
)
2188 _asl_action_set_param_data_free((asl_action_set_param_data_t
*)r
->private);
2190 else if (r
->action
== ACTION_SET_FILE
)
2192 _asl_action_set_param_data_free((asl_action_set_param_data_t
*)r
->private);
2197 asl_out_module_free(m
);
2201 _asl_action_close_internal(void)
2203 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
2204 if (crashmover_state
!= 0)
2206 dispatch_resume(crashlog_queue
);
2207 crashmover_state
= 0;
2210 /* wait for the crashlog_queue to flush before _asl_action_free_modules() */
2211 dispatch_sync(crashlog_queue
, ^{ int x
= 0; if (x
== 1) x
= 2; });
2214 _asl_action_free_modules(global
.asl_out_module
);
2215 global
.asl_out_module
= NULL
;
2216 sweep_time
= time(NULL
);
2222 _asl_action_close_idle_files(time_t idle_time
)
2224 asl_out_module_t
*m
;
2225 time_t now
= time(NULL
);
2227 for (m
= global
.asl_out_module
; m
!= NULL
; m
= m
->next
)
2231 for (r
= m
->ruleset
; r
!= NULL
; r
= r
->next
)
2235 if ((r
->dst
!= NULL
) && (r
->dst
->flags
& MODULE_FLAG_CRASHLOG
))
2237 _act_dst_close(r
, DST_CLOSE_IDLE
);
2238 //TODO: can r->action even be ACTION_ASL_DIR?
2239 /* if not, we can avoid the extra check here */
2240 if (r
->action
!= ACTION_ASL_DIR
) _act_checkpoint(r
, CHECKPOINT_FORCE
);
2243 else if (r
->action
== ACTION_ASL_DIR
)
2247 asl_action_asl_store_data_t
*as_data
= (asl_action_asl_store_data_t
*)r
->dst
->private;
2248 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
);
2251 else if (r
->action
== ACTION_ASL_FILE
)
2255 asl_action_asl_file_data_t
*af_data
= (asl_action_asl_file_data_t
*)r
->dst
->private;
2256 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
);
2259 else if (r
->action
== ACTION_FILE
)
2263 asl_action_file_data_t
*f_data
= (asl_action_file_data_t
*)r
->dst
->private;
2264 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
);
2272 asl_action_close(void)
2274 dispatch_async(asl_action_queue
, ^{
2275 _asl_action_close_internal();
2282 asl_action_reset(void)
2284 dispatch_async(asl_action_queue
, ^{
2285 _asl_action_close_internal();
2293 _asl_action_module_with_name(const char *name
)
2295 asl_out_module_t
*m
;
2297 if (global
.asl_out_module
== NULL
) return NULL
;
2298 if (name
== NULL
) return global
.asl_out_module
;
2300 for (m
= global
.asl_out_module
; m
!= NULL
; m
= m
->next
)
2302 if ((m
->name
!= NULL
) && (!strcmp(m
->name
, name
))) return m
;
2309 * called from control_message
2310 * Used to control modules dynamically.
2311 * Line format "@ module param [value ...]"
2313 * Note this is synchronous on asl_action queue.
2316 asl_action_control_set_param(const char *s
)
2321 if (s
== NULL
) return -1;
2322 if (s
[0] == '\0') return 0;
2324 /* skip '@' and whitespace */
2326 while ((*s
== ' ') || (*s
== '\t')) s
++;
2328 l
= explode(s
, " \t");
2329 if (l
!= NULL
) for (count
= 0; l
[count
] != NULL
; count
++);
2331 /* at least 2 parameters (l[0] = module, l[1] = param) required */
2334 free_string_list(l
);
2338 if (global
.asl_out_module
== NULL
)
2340 asldebug("asl_action_control_set_param: no modules loaded\n");
2341 free_string_list(l
);
2345 /* create / modify a module */
2346 if ((!strcasecmp(l
[1], "define")) && (strcmp(l
[0], "*")))
2348 char *str
= strdup(s
);
2351 asldebug("asl_action_control_set_param: memory allocation failed\n");
2352 free_string_list(l
);
2356 dispatch_sync(asl_action_queue
, ^{
2357 asl_out_module_t
*m
;
2361 /* skip name, whitespace, "define" */
2362 while ((*p
!= ' ') && (*p
!= '\t')) p
++;
2363 while ((*p
== ' ') || (*p
== '\t')) p
++;
2364 while ((*p
!= ' ') && (*p
!= '\t')) p
++;
2366 m
= _asl_action_module_with_name(l
[0]);
2369 asl_out_module_t
*x
;
2371 m
= asl_out_module_new(l
[0]);
2372 for (x
= global
.asl_out_module
; x
->next
!= NULL
; x
= x
->next
);
2376 r
= asl_out_module_parse_line(m
, p
);
2379 _asl_action_post_process_rule(m
, r
);
2380 if ((r
->dst
!= NULL
) && (r
->dst
->flags
& MODULE_FLAG_ROTATE
))
2382 _act_file_checkpoint_all(CHECKPOINT_TEST
);
2383 if (checkpoint_timer
== NULL
) _start_cycling();
2389 free_string_list(l
);
2393 dispatch_sync(asl_action_queue
, ^{
2396 asl_out_module_t
*m
;
2398 if (!strcmp(l
[0], "*"))
2401 m
= _asl_action_module_with_name(NULL
);
2405 m
= _asl_action_module_with_name(l
[0]);
2410 if (!strcasecmp(l
[1], "enable"))
2414 /* don't do enable for ASL_MODULE_NAME if input name is "*" */
2415 if ((do_all
== 0) || (strcmp(m
->name
, ASL_MODULE_NAME
)))
2417 /* @ module enable {0|1} */
2418 if (count
> 2) intval
= atoi(l
[2]);
2420 if (intval
== 0) m
->flags
&= ~MODULE_FLAG_ENABLED
;
2421 else m
->flags
|= MODULE_FLAG_ENABLED
;
2424 else if (!strcasecmp(l
[1], "checkpoint"))
2426 /* @ module checkpoint [file] */
2427 if (count
> 2) _act_file_checkpoint(m
, l
[2], CHECKPOINT_FORCE
);
2428 else _act_file_checkpoint(m
, NULL
, CHECKPOINT_FORCE
);
2431 if (do_all
== 1) m
= m
->next
;
2437 free_string_list(l
);
2442 asl_action_out_module_query(asl_msg_t
*q
, asl_msg_t
*m
, bool all
)
2444 dispatch_sync(asl_action_queue
, ^{
2445 asl_out_module_t
*om
;
2447 for (om
= global
.asl_out_module
; om
!= NULL
; om
= om
->next
)
2449 if (all
|| (0 == asl_msg_lookup(q
, om
->name
, NULL
, NULL
)))
2451 val
= om
->flags
& MODULE_FLAG_ENABLED
? "enabled" : "disabled";
2452 if (om
->name
== NULL
) asl_msg_set_key_val(m
, "asl.conf", val
);
2453 else asl_msg_set_key_val(m
, om
->name
, val
);