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>
26 #include <sys/types.h>
28 #include <sys/socket.h>
43 #include <membership.h>
44 #include <configuration_profile.h>
46 #include <xpc/private.h>
48 #define _PATH_WALL "/usr/bin/wall"
49 #define NOTIFY_PATH_SERVICE "com.apple.system.notify.service.path:0x87:"
51 #define MY_ID "asl_action"
53 /* XXX add to asl.h */
54 #define ASL_KEY_MODULE "ASLModule"
56 #define MAX_FAILURES 5
58 #define ACTION_STATUS_ERROR -1
59 #define ACTION_STATUS_OK 0
61 #define IDLE_CLOSE 300
63 #define forever for(;;)
65 static dispatch_queue_t asl_action_queue
;
66 static dispatch_source_t checkpoint_timer
;
67 static time_t sweep_time
= 0;
69 #if TARGET_OS_EMBEDDED
70 static dispatch_queue_t crashlog_queue
;
71 static dispatch_source_t crashlog_sentinel_src
;
72 static int crashlog_sentinel_fd
= -1;
73 static time_t crashmover_state
= 0;
74 static int crashmover_token
= -1;
77 typedef struct store_data
86 dispatch_source_t monitor
;
87 } asl_action_store_data_t
;
89 typedef struct file_data
96 dispatch_source_t dup_timer
;
97 dispatch_source_t monitor
;
98 } asl_action_file_data_t
;
100 typedef struct set_param_data
103 } asl_action_set_param_data_t
;
105 static int action_asl_store_count
;
106 static bool store_has_logged
;
108 extern void db_save_message(aslmsg m
);
111 static int _act_file_checkpoint_all(uint32_t force
);
112 static void _asl_action_post_process_rule(asl_out_module_t
*m
, asl_out_rule_t
*r
);
113 static void _asl_action_close_idle_files(time_t idle_time
);
116 _act_out_set_param(asl_out_module_t
*m
, char *x
, bool eval
)
120 uint32_t count
, intval
;
122 l
= explode(s
, " \t");
123 if (l
== NULL
) return;
125 for (count
= 0; l
[count
] != NULL
; count
++);
132 if (!strcasecmp(l
[0], "enable"))
135 if (count
< 2) intval
= 1;
136 else intval
= atoi(l
[1]);
138 if (!eval
) intval
= (intval
== 0) ? 1 : 0;
140 if (intval
== 0) m
->flags
&= ~MODULE_FLAG_ENABLED
;
141 else m
->flags
|= MODULE_FLAG_ENABLED
;
144 else if (!strcasecmp(l
[0], "disable"))
146 /* = disable [1|0] */
147 if (count
< 2) intval
= 1;
148 else intval
= atoi(l
[1]);
150 if (!eval
) intval
= (intval
== 0) ? 1 : 0;
152 if (intval
!= 0) m
->flags
&= ~MODULE_FLAG_ENABLED
;
153 else m
->flags
|= MODULE_FLAG_ENABLED
;
159 if (!strcmp(m
->name
, ASL_MODULE_NAME
))
161 /* Other parameters may be set by com.apple.asl module */
162 control_set_param(x
, eval
);
167 _act_notify(asl_out_module_t
*m
, asl_out_rule_t
*r
)
169 if (m
== NULL
) return;
170 if ((m
->flags
& MODULE_FLAG_ENABLED
) == 0) return;
172 if (r
== NULL
) return;
173 if (r
->options
== NULL
) return;
175 notify_post(r
->options
);
179 _act_broadcast(asl_out_module_t
*m
, asl_out_rule_t
*r
, aslmsg msg
)
181 #if !TARGET_OS_EMBEDDED
185 if (m
== NULL
) return;
186 if ((m
->flags
& MODULE_FLAG_ENABLED
) == 0) return;
188 if (m
->name
== NULL
) return;
189 if (r
== NULL
) return;
190 if (msg
== NULL
) return;
192 /* only base module (asl.conf) may broadcast */
193 if (strcmp(m
->name
, ASL_MODULE_NAME
)) return;
196 if (val
== NULL
) val
= asl_get(msg
, ASL_KEY_MSG
);
197 if (val
== NULL
) return;
199 pw
= popen(_PATH_WALL
, "w");
202 asldebug("%s: error sending wall message: %s\n", MY_ID
, strerror(errno
));
206 fprintf(pw
, "%s", val
);
212 _act_access_control(asl_out_module_t
*m
, asl_out_rule_t
*r
, aslmsg msg
)
217 if (m
== NULL
) return;
218 if (m
->name
== NULL
) return;
219 if (r
== NULL
) return;
220 if (msg
== NULL
) return;
222 /* only base module (asl.conf) may set access controls */
223 if (strcmp(m
->name
, ASL_MODULE_NAME
)) return;
225 ruid
= atoi(r
->options
);
227 p
= strchr(r
->options
, ' ');
228 if (p
== NULL
) p
= strchr(r
->options
, '\t');
236 if (ruid
!= -1) asl_set(msg
, ASL_KEY_READ_UID
, r
->options
);
239 if (rgid
!= -1) asl_set(msg
, ASL_KEY_READ_GID
, p
);
245 #if TARGET_OS_EMBEDDED
247 _crashlog_sentinel_init(void)
249 char path
[MAXPATHLEN
];
251 if (crashlog_sentinel_src
!= NULL
) return;
253 snprintf(path
, sizeof(path
), "%s/com.apple.asl.%ld", _PATH_CRASHREPORTER
, time(NULL
));
255 crashlog_sentinel_fd
= open(path
, O_WRONLY
| O_CREAT
);
256 if (crashlog_sentinel_fd
< 0)
259 asprintf(&str
, "[Sender syslogd] [Level 3] [PID %u] [Facility syslog] [Message Sentinel %s create/open failed (%s)]", global
.pid
, path
, strerror(errno
));
260 internal_log_message(str
);
265 close(crashlog_sentinel_fd
);
267 crashlog_sentinel_fd
= open(path
, O_EVTONLY
, 0);
268 if (crashlog_sentinel_fd
< 0)
271 asprintf(&str
, "[Sender syslogd] [Level 3] [PID %u] [Facility syslog] [Message Sentinel %s event/open failed (%s)]", global
.pid
, path
, strerror(errno
));
272 internal_log_message(str
);
277 crashlog_sentinel_src
= dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE
, (uintptr_t)crashlog_sentinel_fd
, DISPATCH_VNODE_DELETE
, asl_action_queue
);
278 if (crashlog_sentinel_src
== NULL
)
281 asprintf(&str
, "[Sender syslogd] [Level 3] [PID %u] [Facility syslog] [Message Sentinel %s dispatch_source_create failed]", global
.pid
, path
);
282 internal_log_message(str
);
284 close(crashlog_sentinel_fd
);
285 crashlog_sentinel_fd
= -1;
289 dispatch_source_set_event_handler(crashlog_sentinel_src
, ^{
290 if (crashmover_state
!= 0)
292 asldebug("CrashMover inactive / sentinel deleted: resuming crashlog queue\n");
293 dispatch_resume(crashlog_queue
);
294 crashmover_state
= 0;
297 if (crashlog_sentinel_src
!= NULL
)
299 dispatch_source_cancel(crashlog_sentinel_src
);
300 dispatch_release(crashlog_sentinel_src
);
303 crashlog_sentinel_src
= NULL
;
305 close(crashlog_sentinel_fd
);
306 crashlog_sentinel_fd
= -1;
307 _crashlog_sentinel_init();
310 dispatch_resume(crashlog_sentinel_src
);
311 asldebug("Created CrashLog Sentinel: %s\n", path
);
315 _crashlog_queue_check(void)
318 * Check whether the crashlog queue has been suspended for too long.
319 * We allow the crashlog quque to be suspended for 60 seconds.
320 * After that, we start logging again. This prevents syslogd from
321 * filling memory due to a suspended queue. CrashMover really shoud
322 * take no more than a second or two to finish.
324 if (crashmover_state
== 0) return;
325 if ((time(NULL
) - crashmover_state
) <= 60) return;
327 asldebug("CrashMover timeout: resuming crashlog queue\n");
328 dispatch_resume(crashlog_queue
);
329 crashmover_state
= 0;
332 * crashlog_sentinel_src should never be NULL, but if
333 * _crashlog_sentinel_init failed for some strange reason,
334 * it will be NULL here.
336 if (crashlog_sentinel_src
!= NULL
)
338 dispatch_source_cancel(crashlog_sentinel_src
);
339 dispatch_release(crashlog_sentinel_src
);
342 crashlog_sentinel_src
= NULL
;
344 close(crashlog_sentinel_fd
);
345 crashlog_sentinel_fd
= -1;
346 _crashlog_sentinel_init();
351 _act_dst_close(asl_out_rule_t
*r
)
353 if (r
== NULL
) return;
354 if (r
->dst
== NULL
) return;
355 if (r
->dst
->private == NULL
) return;
357 if ((r
->action
== ACTION_ASL_DIR
) || (r
->action
== ACTION_ASL_FILE
))
359 asl_action_store_data_t
*sdata
= (asl_action_store_data_t
*)r
->dst
->private;
360 if (sdata
->store
!= NULL
) asl_file_close(sdata
->store
);
363 if (sdata
->storedata
!= NULL
) fclose(sdata
->storedata
);
364 sdata
->storedata
= NULL
;
366 if (sdata
->monitor
!= NULL
)
368 dispatch_source_cancel(sdata
->monitor
);
369 dispatch_release(sdata
->monitor
);
370 sdata
->monitor
= NULL
;
373 else if (r
->action
== ACTION_FILE
)
375 asl_action_file_data_t
*fdata
= (asl_action_file_data_t
*)r
->dst
->private;
376 if (fdata
->fd
>= 0) close(fdata
->fd
);
379 if (fdata
->monitor
!= NULL
)
381 dispatch_source_cancel(fdata
->monitor
);
382 dispatch_release(fdata
->monitor
);
383 fdata
->monitor
= NULL
;
389 _act_store_file_setup(asl_out_module_t
*m
, asl_out_rule_t
*r
)
392 asl_action_store_data_t
*sdata
;
393 char dstpath
[MAXPATHLEN
];
395 if (r
== NULL
) return ASL_STATUS_INVALID_STORE
;
396 if (r
->dst
== NULL
) return ASL_STATUS_INVALID_STORE
;
397 if (r
->dst
->private == NULL
) return ASL_STATUS_INVALID_STORE
;
399 sdata
= (asl_action_store_data_t
*)r
->dst
->private;
400 if (sdata
->store
== NULL
)
402 /* create path if necessary */
405 int fd
= asl_out_dst_file_create_open(r
->dst
);
408 asldebug("_act_store_file_setup: asl_out_dst_file_create_open failed %d %s\n", errno
, strerror(errno
));
409 return ASL_STATUS_WRITE_FAILED
;
413 asl_make_dst_filename(r
->dst
, dstpath
, sizeof(dstpath
));
414 status
= asl_file_open_write(dstpath
, 0, -1, -1, &(sdata
->store
));
415 if (status
!= ASL_STATUS_OK
) return status
;
417 sdata
->monitor
= dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE
, fileno(sdata
->store
->store
), DISPATCH_VNODE_DELETE
, asl_action_queue
);
418 if (sdata
->monitor
!= NULL
)
420 dispatch_source_set_event_handler(sdata
->monitor
, ^{ _act_dst_close(r
); });
421 dispatch_resume(sdata
->monitor
);
424 status
= asl_file_read_set_position(sdata
->store
, ASL_FILE_POSITION_LAST
);
425 if (status
!= ASL_STATUS_OK
)
427 asldebug("_act_store_file_setup: asl_file_read_set_position failed %d %s\n", status
, asl_core_error(status
));
431 sdata
->next_id
= sdata
->store
->cursor_xid
+ 1;
432 if (fseek(sdata
->store
->store
, 0, SEEK_END
) != 0)
434 asldebug("_act_store_file_setup: fseek failed %d %s\n", errno
, strerror(errno
));
435 return ASL_STATUS_WRITE_FAILED
;
443 return ASL_STATUS_OK
;
447 * _act_store_dir_setup
449 * Creates store directory if it does not exist
450 * Creates StoreData file if it does not exist
451 * Reads ASL Message ID from StoreData file
452 * Writes ASL Message ID + 1 to StoreData file
453 * Opens current day file (e.g. "/foo/bar/2012.04.06.asl")
456 _act_store_dir_setup(asl_out_module_t
*m
, asl_out_rule_t
*r
, time_t tick
)
464 asl_action_store_data_t
*sdata
;
466 if (r
== NULL
) return ASL_STATUS_INVALID_STORE
;
467 if (r
->dst
== NULL
) return ASL_STATUS_INVALID_STORE
;
468 if (r
->dst
->private == NULL
) return ASL_STATUS_INVALID_STORE
;
469 if (r
->dst
->path
== NULL
) return ASL_STATUS_INVALID_STORE
;
471 sdata
= (asl_action_store_data_t
*)r
->dst
->private;
473 /* get / set message id from StoreData file */
476 if (sdata
->storedata
== NULL
)
478 memset(&sb
, 0, sizeof(struct stat
));
479 status
= stat(r
->dst
->path
, &sb
);
482 /* must be a directory */
483 if (!S_ISDIR(sb
.st_mode
)) return ASL_STATUS_INVALID_STORE
;
485 else if (errno
== ENOENT
)
487 /* doesn't exist - create it */
488 mask
= umask(S_IWGRP
| S_IWOTH
);
489 status
= mkpath_np(r
->dst
->path
, 0755);
490 if (status
== 0) status
= chmod(r
->dst
->path
, r
->dst
->mode
);
493 if (status
!= 0) return ASL_STATUS_WRITE_FAILED
;
494 #if !TARGET_IPHONE_SIMULATOR
495 if (chown(r
->dst
->path
, r
->dst
->uid
[0], r
->dst
->gid
[0]) != 0) return ASL_STATUS_WRITE_FAILED
;
500 /* Unexpected stat error */
501 return ASL_STATUS_FAILED
;
505 asprintf(&path
, "%s/%s", r
->dst
->path
, FILE_ASL_STORE_DATA
);
506 if (path
== NULL
) return ASL_STATUS_NO_MEMORY
;
508 memset(&sb
, 0, sizeof(struct stat
));
509 status
= stat(path
, &sb
);
512 /* StoreData exists: open and read last xid */
513 sdata
->storedata
= fopen(path
, "r+");
514 if (sdata
->storedata
== NULL
)
517 return ASL_STATUS_FAILED
;
520 if (fread(&xid
, sizeof(uint64_t), 1, sdata
->storedata
) != 1)
523 fclose(sdata
->storedata
);
524 sdata
->storedata
= NULL
;
525 return ASL_STATUS_READ_FAILED
;
528 else if (errno
== ENOENT
)
530 /* StoreData does not exist: create it */
531 sdata
->storedata
= fopen(path
, "w");
532 if (sdata
->storedata
== NULL
)
535 return ASL_STATUS_FAILED
;
538 #if !TARGET_IPHONE_SIMULATOR
539 if (chown(path
, r
->dst
->uid
[0], r
->dst
->gid
[0]) != 0)
542 return ASL_STATUS_WRITE_FAILED
;
548 /* Unexpected stat error */
550 return ASL_STATUS_FAILED
;
553 sdata
->monitor
= dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE
, fileno(sdata
->storedata
), DISPATCH_VNODE_DELETE
, asl_action_queue
);
554 if (sdata
->monitor
!= NULL
)
556 dispatch_source_set_event_handler(sdata
->monitor
, ^{ _act_dst_close(r
); });
557 dispatch_resume(sdata
->monitor
);
564 rewind(sdata
->storedata
);
565 if (fread(&xid
, sizeof(uint64_t), 1, sdata
->storedata
) != 1)
567 fclose(sdata
->storedata
);
568 sdata
->storedata
= NULL
;
569 return ASL_STATUS_READ_FAILED
;
573 xid
= asl_core_ntohq(xid
);
575 sdata
->next_id
= xid
;
577 xid
= asl_core_htonq(xid
);
578 rewind(sdata
->storedata
);
579 status
= fwrite(&xid
, sizeof(uint64_t), 1, sdata
->storedata
);
582 fclose(sdata
->storedata
);
583 sdata
->storedata
= NULL
;
584 return ASL_STATUS_WRITE_FAILED
;
587 memset(&ctm
, 0, sizeof(struct tm
));
589 if (localtime_r((const time_t *)&tick
, &ctm
) == NULL
) return ASL_STATUS_FAILED
;
590 if ((sdata
->store
!= NULL
) && (sdata
->p_year
== ctm
.tm_year
) && (sdata
->p_month
== ctm
.tm_mon
) && (sdata
->p_day
== ctm
.tm_mday
))
592 return ASL_STATUS_OK
;
595 if (sdata
->store
!= NULL
) asl_file_close(sdata
->store
);
598 r
->dst
->fname
= NULL
;
600 r
->dst
->stamp
= tick
;
602 sdata
->p_year
= ctm
.tm_year
;
603 sdata
->p_month
= ctm
.tm_mon
;
604 sdata
->p_day
= ctm
.tm_mday
;
606 asprintf(&(r
->dst
->fname
), "%s/%d.%02d.%02d.asl", r
->dst
->path
, ctm
.tm_year
+ 1900, ctm
.tm_mon
+ 1, ctm
.tm_mday
);
607 if (r
->dst
->fname
== NULL
) return ASL_STATUS_NO_MEMORY
;
610 status
= ASL_STATUS_OK
;
611 if (sdata
->store
== NULL
) {
612 #if TARGET_IPHONE_SIMULATOR
616 uid_t uid
= r
->dst
->uid
[0];
617 gid_t gid
= r
->dst
->gid
[0];
619 status
= asl_file_open_write(r
->dst
->fname
, (r
->dst
->mode
& 0666), uid
, gid
, &(sdata
->store
));
623 if (status
!= ASL_STATUS_OK
) return status
;
625 if (fseek(sdata
->store
->store
, 0, SEEK_END
) != 0) return ASL_STATUS_FAILED
;
627 return ASL_STATUS_OK
;
631 _asl_action_store_data_free(asl_action_store_data_t
*sdata
)
633 if (sdata
== NULL
) return;
635 if (sdata
->store
!= NULL
) asl_file_close(sdata
->store
);
638 if (sdata
->storedata
!= NULL
) fclose(sdata
->storedata
);
639 sdata
->storedata
= NULL
;
645 _asl_action_file_data_free(asl_action_file_data_t
*fdata
)
647 if (fdata
== NULL
) return;
649 if (fdata
->dup_timer
!= NULL
)
651 if (fdata
->last_count
== 0)
654 * The timer exists, but last_count is zero, so the timer is suspended.
655 * Sources must not be released in when suspended.
656 * So we resume it so that we can release it.
658 dispatch_resume(fdata
->dup_timer
);
661 dispatch_release(fdata
->dup_timer
);
664 free(fdata
->last_msg
);
665 if (fdata
->fd
>= 0) close(fdata
->fd
);
670 _asl_action_set_param_data_free(asl_action_set_param_data_t
*spdata
)
672 if (spdata
!= NULL
) notify_cancel(spdata
->token
);
677 _asl_action_save_failed(const char *where
, asl_out_module_t
*m
, asl_out_rule_t
*r
, uint32_t status
)
679 if (r
->dst
->flags
& MODULE_FLAG_SOFT_WRITE
) return;
682 asldebug("%s: %s save to %s failed: %s\n", where
, m
->name
, r
->dst
->path
, asl_core_error(status
));
684 /* disable further activity after multiple failures */
685 if (r
->dst
->fails
> MAX_FAILURES
)
688 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
));
689 internal_log_message(str
);
692 if (r
->action
== ACTION_FILE
) _asl_action_file_data_free((asl_action_file_data_t
*)r
->dst
->private);
693 else _asl_action_store_data_free((asl_action_store_data_t
*)r
->dst
->private);
695 r
->dst
->private = NULL
;
696 r
->action
= ACTION_NONE
;
701 _act_store_file(asl_out_module_t
*m
, asl_out_rule_t
*r
, aslmsg msg
)
703 asl_action_store_data_t
*sdata
;
707 if (r
== NULL
) return ACTION_STATUS_ERROR
;
708 if (r
->dst
== NULL
) return ACTION_STATUS_ERROR
;
709 if (r
->dst
->private == NULL
) return ACTION_STATUS_ERROR
;
711 sdata
= (asl_action_store_data_t
*)r
->dst
->private;
713 /* check dst for file_max & etc */
714 if (r
->dst
->flags
& MODULE_FLAG_ROTATE
)
716 if (asl_out_dst_checkpoint(r
->dst
, CHECKPOINT_TEST
) != 0)
719 asl_trigger_aslmanager();
723 status
= _act_store_file_setup(m
, r
);
724 if (status
== ASL_STATUS_OK
)
726 sdata
->last_time
= time(NULL
);
729 mid
= sdata
->next_id
;
731 /* save message to file and update dst size */
732 status
= asl_file_save(sdata
->store
, msg
, &mid
);
733 if (status
== ASL_STATUS_OK
) r
->dst
->size
= sdata
->store
->file_size
;
736 if (status
!= ASL_STATUS_OK
)
738 _asl_action_save_failed("_act_store_file", m
, r
, status
);
739 return ACTION_STATUS_ERROR
;
742 return ACTION_STATUS_OK
;
746 _act_store_dir(asl_out_module_t
*m
, asl_out_rule_t
*r
, aslmsg msg
)
748 asl_action_store_data_t
*sdata
;
754 if (r
== NULL
) return ACTION_STATUS_ERROR
;
755 if (r
->dst
== NULL
) return ACTION_STATUS_ERROR
;
756 if (r
->dst
->private == NULL
) return ACTION_STATUS_ERROR
;
758 sdata
= (asl_action_store_data_t
*)r
->dst
->private;
760 val
= asl_get(msg
, ASL_KEY_TIME
);
761 if (val
== NULL
) return ACTION_STATUS_ERROR
;
763 /* check dst for file_max & etc */
764 if (asl_out_dst_checkpoint(r
->dst
, CHECKPOINT_TEST
) != 0)
767 asl_trigger_aslmanager();
772 status
= _act_store_dir_setup(m
, r
, tick
);
773 if (status
== ASL_STATUS_OK
)
775 sdata
->last_time
= time(NULL
);
778 mid
= sdata
->next_id
;
779 status
= asl_file_save(sdata
->store
, msg
, &mid
);
780 if (status
== ASL_STATUS_OK
) r
->dst
->size
= sdata
->store
->file_size
;
783 if (status
!= ASL_STATUS_OK
)
785 _asl_action_save_failed("_act_store_dir", m
, r
, status
);
786 return ACTION_STATUS_ERROR
;
789 return ACTION_STATUS_OK
;
793 _act_store_final(asl_out_module_t
*m
, asl_out_rule_t
*r
, aslmsg msg
)
795 if (r
->action
== ACTION_ASL_DIR
) _act_store_dir(m
, r
, msg
);
796 else _act_store_file(m
, r
, msg
);
800 * Save a message to an ASL format file (ACTION_ASL_FILE)
801 * or to an ASL directory (ACTION_ASL_DIR).
804 _act_store(asl_out_module_t
*m
, asl_out_rule_t
*r
, aslmsg msg
)
806 if (r
== NULL
) return;
807 if (msg
== NULL
) return;
808 if (m
== NULL
) return;
809 if ((m
->flags
& MODULE_FLAG_ENABLED
) == 0) return;
810 if (r
->dst
== NULL
) return;
812 if (r
->dst
->flags
& MODULE_FLAG_HAS_LOGGED
) return;
813 r
->dst
->flags
|= MODULE_FLAG_HAS_LOGGED
;
815 #if TARGET_OS_EMBEDDED
816 if (r
->dst
->flags
& MODULE_FLAG_CRASHLOG
)
818 _crashlog_queue_check();
819 asl_msg_retain((asl_msg_t
*)msg
);
820 dispatch_async(crashlog_queue
, ^{
821 _act_store_final(m
, r
, msg
);
822 asl_msg_release((asl_msg_t
*)msg
);
828 _act_store_final(m
, r
, msg
);
832 _send_repeat_msg(asl_out_rule_t
*r
)
834 asl_action_file_data_t
*fdata
;
837 time_t now
= time(NULL
);
839 if (r
== NULL
) return -1;
840 if (r
->dst
== NULL
) return -1;
841 if (r
->dst
->private == NULL
) return -1;
843 fdata
= (asl_action_file_data_t
*)r
->dst
->private;
845 free(fdata
->last_msg
);
846 fdata
->last_msg
= NULL
;
848 if (fdata
->last_count
== 0) return 0;
851 dispatch_suspend(fdata
->dup_timer
);
853 memset(vt
, 0, sizeof(vt
));
858 asprintf(&msg
, "%s --- last message repeated %u time%s ---\n", vt
+ 4, fdata
->last_count
, (fdata
->last_count
== 1) ? "" : "s");
859 fdata
->last_count
= 0;
860 fdata
->last_time
= now
;
861 if (msg
== NULL
) return -1;
863 if (fdata
->fd
< 0) fdata
->fd
= asl_out_dst_file_create_open(r
->dst
);
866 status
= write(fdata
->fd
, msg
, len
);
869 if ((status
< 0) || (status
< len
))
871 asldebug("%s: error writing repeat message (%s): %s\n", MY_ID
, r
->dst
->path
, strerror(errno
));
879 _act_file_open(asl_out_module_t
*m
, asl_out_rule_t
*r
)
881 asl_action_file_data_t
*fdata
;
883 if (r
== NULL
) return -1;
884 if (r
->dst
== NULL
) return -1;
885 if (r
->dst
->private == NULL
) return -1;
887 fdata
= (asl_action_file_data_t
*)r
->dst
->private;
890 fdata
->fd
= asl_out_dst_file_create_open(r
->dst
);
894 * lazy path creation: create path and retry
895 * asl_out_dst_file_create_open doesn not create the path
899 fdata
->fd
= asl_out_dst_file_create_open(r
->dst
);
904 fdata
->monitor
= dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE
, fdata
->fd
, DISPATCH_VNODE_DELETE
, asl_action_queue
);
905 if (fdata
->monitor
!= NULL
)
907 dispatch_source_set_event_handler(fdata
->monitor
, ^{ _act_dst_close(r
); });
908 dispatch_resume(fdata
->monitor
);
919 struct timespec midnight
;
925 if (checkpoint_timer
!= NULL
) return;
936 midnight
.tv_nsec
= 0;
938 checkpoint_timer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, asl_action_queue
);
939 dispatch_source_set_timer(checkpoint_timer
, dispatch_walltime(&midnight
, 0), NSEC_PER_SEC
* SEC_PER_DAY
, 0);
940 dispatch_source_set_event_handler(checkpoint_timer
, ^{ _act_file_checkpoint_all(CHECKPOINT_FORCE
); });
941 dispatch_resume(checkpoint_timer
);
944 /* check if a module path (mpath) matches a user path (upath) */
946 _act_file_equal(const char *mpath
, const char *upath
)
950 /* NULL upath means user wants to match all files */
951 if (upath
== NULL
) return true;
953 if (mpath
== NULL
) return false;
955 /* check for exact match */
956 if (!strcmp(mpath
, upath
)) return true;
958 /* upath may be the last component of mpath */
959 slash
= strrchr(mpath
, '/');
960 if (slash
== NULL
) return false;
962 if (!strcmp(slash
+ 1, upath
)) return true;
967 _act_file_checkpoint(asl_out_module_t
*m
, const char *path
, uint32_t force
)
970 int did_checkpoint
= 0;
972 if (m
== NULL
) return 0;
975 for (r
= m
->ruleset
; r
!= NULL
; r
= r
->next
)
977 if ((r
->action
== ACTION_FILE
) || (r
->action
== ACTION_ASL_FILE
))
979 if (r
->dst
->flags
& MODULE_FLAG_ROTATE
)
981 if (_act_file_equal(r
->dst
->path
, path
))
983 if (force
& CHECKPOINT_CRASH
)
985 if (r
->dst
->flags
& MODULE_FLAG_CRASHLOG
)
987 if (asl_out_dst_checkpoint(r
->dst
, CHECKPOINT_FORCE
) > 0)
996 if (asl_out_dst_checkpoint(r
->dst
, force
) > 0)
1007 return did_checkpoint
;
1011 _act_file_checkpoint_all(uint32_t force
)
1013 asl_out_module_t
*m
;
1014 int did_checkpoint
= 0;
1016 for (m
= global
.asl_out_module
; m
!= NULL
; m
= m
->next
)
1018 if (_act_file_checkpoint(m
, NULL
, force
) > 0) did_checkpoint
= 1;
1021 asl_trigger_aslmanager();
1023 return did_checkpoint
;
1027 _act_file_final(asl_out_module_t
*m
, asl_out_rule_t
*r
, aslmsg msg
)
1029 asl_action_file_data_t
*fdata
;
1031 uint32_t len
, msg_hash
= 0;
1036 * If print format is std, bsd, or msg, then skip messages with
1037 * no ASL_KEY_MSG, or without a value for it.
1039 if (r
->dst
->flags
& MODULE_FLAG_STD_BSD_MSG
)
1041 const char *msgval
= NULL
;
1042 if (asl_msg_lookup((asl_msg_t
*)msg
, ASL_KEY_MSG
, &msgval
, NULL
) != 0) return;
1043 if (msgval
== NULL
) return;
1046 fdata
= (asl_action_file_data_t
*)r
->dst
->private;
1052 str
= asl_format_message((asl_msg_t
*)msg
, r
->dst
->fmt
, r
->dst
->tfmt
, ASL_ENCODE_SAFE
, &len
);
1054 if (r
->dst
->flags
& MODULE_FLAG_COALESCE
)
1056 if (fdata
->dup_timer
== NULL
)
1058 /* create a timer to flush dups on this file */
1059 fdata
->dup_timer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, asl_action_queue
);
1060 dispatch_source_set_event_handler(fdata
->dup_timer
, ^{ _send_repeat_msg(r
); });
1063 if ((global
.bsd_max_dup_time
> 0) && (str
!= NULL
) && (fdata
->last_msg
!= NULL
))
1065 msg_hash
= asl_core_string_hash(str
+ 16, len
- 16);
1066 if ((fdata
->last_hash
== msg_hash
) && (!strcmp(fdata
->last_msg
, str
+ 16)))
1068 if ((now
- fdata
->last_time
) < global
.bsd_max_dup_time
) is_dup
= 1;
1075 if (fdata
->last_count
== 0)
1077 /* start the timer */
1078 dispatch_source_set_timer(fdata
->dup_timer
, dispatch_time(DISPATCH_TIME_NOW
, NSEC_PER_SEC
* global
.bsd_max_dup_time
), DISPATCH_TIME_FOREVER
, 0);
1079 dispatch_resume(fdata
->dup_timer
);
1082 fdata
->last_count
++;
1086 if (_act_file_open(m
, r
) < 0)
1088 _asl_action_save_failed("_act_file", m
, r
, ASL_STATUS_FAILED
);
1098 * The current message is not a duplicate. If fdata->last_count > 0
1099 * we need to write a "last message repeated N times" log entry.
1100 * _send_repeat_msg will free last_msg and do nothing if
1101 * last_count == 0, but we test and free here to avoid a function call.
1103 if (fdata
->last_count
> 0)
1105 _send_repeat_msg(r
);
1109 free(fdata
->last_msg
);
1110 fdata
->last_msg
= NULL
;
1113 /* check dst for file_max & etc */
1114 if (r
->dst
->flags
& MODULE_FLAG_ROTATE
)
1116 int ckpt
= asl_out_dst_checkpoint(r
->dst
, CHECKPOINT_TEST
);
1120 asl_trigger_aslmanager();
1122 if (_act_file_open(m
, r
) < 0)
1124 _asl_action_save_failed("_act_file", m
, r
, ASL_STATUS_FAILED
);
1135 if (str
!= NULL
) fdata
->last_msg
= strdup(str
+ 16);
1137 fdata
->last_hash
= msg_hash
;
1138 fdata
->last_count
= 0;
1139 fdata
->last_time
= now
;
1141 if ((str
!= NULL
) && (len
> 1))
1143 /* write line to file and update dst size */
1144 size_t bytes
= write(fdata
->fd
, str
, len
- 1);
1145 if (bytes
> 0) r
->dst
->size
+= bytes
;
1153 _act_file(asl_out_module_t
*m
, asl_out_rule_t
*r
, aslmsg msg
)
1155 if (r
== NULL
) return;
1156 if (msg
== NULL
) return;
1157 if (m
== NULL
) return;
1158 if ((m
->flags
& MODULE_FLAG_ENABLED
) == 0) return;
1159 if (r
->dst
== NULL
) return;
1160 if (r
->dst
->private == NULL
) return;
1162 if (r
->dst
->flags
& MODULE_FLAG_HAS_LOGGED
) return;
1164 r
->dst
->flags
|= MODULE_FLAG_HAS_LOGGED
;
1166 #if TARGET_OS_EMBEDDED
1167 if (r
->dst
->flags
& MODULE_FLAG_CRASHLOG
)
1169 _crashlog_queue_check();
1170 asl_msg_retain((asl_msg_t
*)msg
);
1171 dispatch_async(crashlog_queue
, ^{
1172 _act_file_final(m
, r
, msg
);
1173 asl_msg_release((asl_msg_t
*)msg
);
1179 _act_file_final(m
, r
, msg
);
1183 _act_forward(asl_out_module_t
*m
, asl_out_rule_t
*r
, aslmsg msg
)
1185 /* To do: <rdar://problem/6130747> Add a "forward" action to asl.conf */
1189 _act_control(asl_out_module_t
*m
, asl_out_rule_t
*r
, aslmsg msg
)
1193 if (m
== NULL
) return;
1194 if (r
== NULL
) return;
1195 p
= asl_get(msg
, ASL_KEY_MODULE
);
1197 if (r
->options
== NULL
) return;
1199 if (!strcmp(r
->options
, "enable"))
1201 m
->flags
|= MODULE_FLAG_ENABLED
;
1203 else if (!strcmp(r
->options
, "disable"))
1205 m
->flags
&= ~MODULE_FLAG_ENABLED
;
1207 else if ((!strcmp(r
->options
, "checkpoint")) || (!strcmp(r
->options
, "rotate")))
1209 _act_file_checkpoint(m
, NULL
, CHECKPOINT_FORCE
);
1214 _send_to_asl_store(aslmsg msg
)
1216 if ((global
.asl_out_module
!= NULL
) && ((global
.asl_out_module
->flags
& MODULE_FLAG_ENABLED
) == 0)) return;
1218 if (store_has_logged
) return;
1219 store_has_logged
= true;
1221 db_save_message(msg
);
1225 _asl_out_process_message(asl_out_module_t
*m
, aslmsg msg
)
1229 if (m
== NULL
) return 1;
1230 if (msg
== NULL
) return 1;
1232 /* reset flag bit used for duplicate avoidance */
1233 for (r
= m
->ruleset
; r
!= NULL
; r
= r
->next
)
1235 if ((r
->action
== ACTION_FILE
) || (r
->action
== ACTION_ASL_DIR
) || (r
->action
== ACTION_ASL_FILE
))
1237 if (r
->dst
!= NULL
) r
->dst
->flags
&= MODULE_FLAG_CLEAR_LOGGED
;
1241 for (r
= m
->ruleset
; r
!= NULL
; r
= r
->next
)
1243 if (r
->query
== NULL
) continue;
1245 /* ACTION_SET_FILE, ACTION_SET_PLIST, and ACTION_SET_PROF are handled independently */
1246 if ((r
->action
== ACTION_SET_FILE
) || (r
->action
== ACTION_SET_PLIST
) || (r
->action
== ACTION_SET_PROF
)) continue;
1249 * ACTION_CLAIM during processing is a filter. It will only be here if the option "only"
1250 * was supplied. In this case we test the message against the query. If it does not
1251 * match, we skip the message.
1253 if (r
->action
== ACTION_CLAIM
)
1255 if ((asl_msg_cmp(r
->query
, (asl_msg_t
*)msg
) != 1)) return 0;
1258 if ((asl_msg_cmp(r
->query
, (asl_msg_t
*)msg
) == 1))
1260 if (r
->action
== ACTION_NONE
) continue;
1261 else if (r
->action
== ACTION_IGNORE
) return 1;
1262 else if (r
->action
== ACTION_SKIP
) return 0;
1263 else if (r
->action
== ACTION_ASL_STORE
) _send_to_asl_store(msg
);
1264 else if (r
->action
== ACTION_ACCESS
) _act_access_control(m
, r
, msg
);
1265 else if (r
->action
== ACTION_NOTIFY
) _act_notify(m
, r
);
1266 else if (r
->action
== ACTION_BROADCAST
) _act_broadcast(m
, r
, msg
);
1267 else if (r
->action
== ACTION_FORWARD
) _act_forward(m
, r
, msg
);
1268 else if (r
->action
== ACTION_CONTROL
) _act_control(m
, r
, msg
);
1269 else if (r
->action
== ACTION_SET_PARAM
) _act_out_set_param(m
, r
->options
, true);
1270 else if ((r
->action
== ACTION_ASL_FILE
) || (r
->action
== ACTION_ASL_DIR
)) _act_store(m
, r
, msg
);
1271 else if (r
->action
== ACTION_FILE
) _act_file(m
, r
, msg
);
1279 asl_out_message(aslmsg msg
)
1281 OSAtomicIncrement32(&global
.asl_queue_count
);
1282 asl_msg_retain((asl_msg_t
*)msg
);
1284 dispatch_async(asl_action_queue
, ^{
1287 time_t now
= time(NULL
);
1288 asl_out_module_t
*m
= global
.asl_out_module
;
1290 store_has_logged
= false;
1292 p
= asl_get(msg
, ASL_KEY_MODULE
);
1295 if ((action_asl_store_count
== 0) || (asl_check_option(msg
, ASL_OPT_STORE
) == 1)) _send_to_asl_store(msg
);
1297 ignore
= _asl_out_process_message(m
, msg
);
1300 if (m
!= NULL
) m
= m
->next
;
1303 _asl_out_process_message(m
, msg
);
1310 if (m
!= NULL
) m
= m
->next
;
1313 if (!strcmp(p
, m
->name
)) _asl_out_process_message(m
, msg
);
1318 asl_msg_release((asl_msg_t
*)msg
);
1319 OSAtomicDecrement32(&global
.asl_queue_count
);
1321 if ((now
- sweep_time
) >= IDLE_CLOSE
)
1323 _asl_action_close_idle_files(IDLE_CLOSE
);
1330 _asl_action_profile_test(asl_out_module_t
*m
, asl_out_rule_t
*r
)
1336 /* ident is first message key */
1337 asl_msg_fetch((asl_msg_t
*)r
->query
, 0, &ident
, NULL
, NULL
);
1340 r
->action
= ACTION_NONE
;
1344 profile
= configuration_profile_to_asl_msg(ident
);
1345 eval
= (asl_msg_cmp(r
->query
, profile
) == 1);
1346 _act_out_set_param(m
, r
->options
, eval
);
1347 asl_msg_release(profile
);
1349 return strdup(ident
);
1353 _asl_action_file_test(asl_out_module_t
*m
, asl_out_rule_t
*r
)
1360 /* path is first message key */
1361 asl_msg_fetch((asl_msg_t
*)r
->query
, 0, &path
, NULL
, NULL
);
1364 r
->action
= ACTION_NONE
;
1368 memset(&sb
, 0, sizeof(struct stat
));
1369 status
= stat(path
, &sb
);
1370 eval
= (status
== 0);
1371 _act_out_set_param(m
, r
->options
, eval
);
1377 _asl_action_handle_file_change_notification(int t
)
1379 asl_out_module_t
*m
;
1382 for (m
= global
.asl_out_module
; m
!= NULL
; m
= m
->next
)
1384 for (r
= m
->ruleset
; r
!= NULL
; r
= r
->next
)
1386 if (r
->action
== ACTION_SET_FILE
)
1388 asl_action_set_param_data_t
*spdata
= (asl_action_set_param_data_t
*)r
->private;
1389 if ((spdata
!= NULL
) && (spdata
->token
== t
))
1391 _asl_action_file_test(m
, r
);
1395 else if (r
->action
== ACTION_SET_PLIST
)
1397 asl_action_set_param_data_t
*spdata
= (asl_action_set_param_data_t
*)r
->private;
1398 if ((spdata
!= NULL
) && (spdata
->token
== t
))
1400 char *str
= _asl_action_profile_test(m
, r
);
1405 else if (r
->action
== ACTION_SET_PROF
)
1407 asl_action_set_param_data_t
*spdata
= (asl_action_set_param_data_t
*)r
->private;
1408 if ((spdata
!= NULL
) && (spdata
->token
== t
))
1410 char *str
= _asl_action_profile_test(m
, r
);
1418 asl_out_module_free(m
);
1422 _asl_action_post_process_rule(asl_out_module_t
*m
, asl_out_rule_t
*r
)
1424 if ((m
== NULL
) || (r
== NULL
)) return;
1426 if (m
!= global
.asl_out_module
)
1428 /* check if any previous module has used this destination */
1429 asl_out_module_t
*n
;
1432 if ((r
->dst
!= NULL
) && (r
->dst
->path
!= NULL
))
1434 for (n
= global
.asl_out_module
; search
&& (n
!= NULL
) && (n
!= m
); n
= n
->next
)
1437 for (s
= n
->ruleset
; search
&& (s
!= NULL
); s
= s
->next
)
1439 if (s
->action
== ACTION_OUT_DEST
)
1441 if ((s
->dst
!= NULL
) && (s
->dst
->path
!= NULL
) && (!strcmp(r
->dst
->path
, s
->dst
->path
)))
1443 /* rule r of module m is using previously used dst of rule s of module n */
1444 asl_out_dst_data_release(r
->dst
);
1447 if (r
->action
== ACTION_OUT_DEST
)
1450 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
);
1451 internal_log_message(str
);
1456 r
->dst
= asl_out_dst_data_retain(s
->dst
);
1467 if (r
->action
== ACTION_SET_PARAM
)
1469 if (r
->query
== NULL
) _act_out_set_param(m
, r
->options
, true);
1471 else if (r
->action
== ACTION_CLAIM
)
1473 /* becomes ACTION_SKIP in com.apple.asl config */
1474 if (m
!= global
.asl_out_module
)
1476 asl_out_rule_t
*rule
= (asl_out_rule_t
*)calloc(1, sizeof(asl_out_rule_t
));
1480 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
);
1481 internal_log_message(str
);
1484 rule
->query
= asl_msg_copy(r
->query
);
1485 rule
->action
= ACTION_SKIP
;
1486 rule
->next
= global
.asl_out_module
->ruleset
;
1487 global
.asl_out_module
->ruleset
= rule
;
1491 * After adding ACTION_SKIP to com.apple.asl module, the claim becomes a no-op in this module
1492 * UNLESS the claim includes the option "only". In that case, the claim becomes a filter:
1493 * any messages that DO NOT match the claim are skipped by this module.
1495 if (r
->options
== NULL
) r
->action
= ACTION_NONE
;
1496 else if (strcmp(r
->options
, "only") != 0) r
->action
= ACTION_NONE
;
1499 else if (r
->action
== ACTION_ASL_STORE
)
1501 action_asl_store_count
++;
1503 else if (r
->action
== ACTION_ASL_DIR
)
1505 if (r
->dst
->private == NULL
) r
->dst
->private = (asl_action_store_data_t
*)calloc(1, sizeof(asl_action_store_data_t
));
1507 else if (r
->action
== ACTION_ASL_FILE
)
1509 if (r
->dst
->private == NULL
)r
->dst
->private = (asl_action_store_data_t
*)calloc(1, sizeof(asl_action_store_data_t
));
1511 else if (r
->action
== ACTION_FILE
)
1513 if (r
->dst
->private == NULL
) r
->dst
->private = (asl_action_file_data_t
*)calloc(1, sizeof(asl_action_file_data_t
));
1514 if (r
->dst
->private != NULL
) ((asl_action_file_data_t
*)(r
->dst
->private))->fd
= -1;
1516 else if (r
->action
== ACTION_SET_PLIST
)
1518 char *ident
=_asl_action_profile_test(m
, r
);
1519 char *notify_key
= configuration_profile_create_notification_key(ident
);
1522 if (notify_key
!= NULL
)
1525 asl_action_set_param_data_t
*spdata
;
1527 status
= notify_register_dispatch(notify_key
, &token
, asl_action_queue
, ^(int t
){
1528 _asl_action_handle_file_change_notification(t
);
1533 spdata
= (asl_action_set_param_data_t
*)calloc(1, sizeof(asl_action_set_param_data_t
));
1536 notify_cancel(token
);
1540 spdata
->token
= token
;
1541 r
->private = spdata
;
1545 else if (r
->action
== ACTION_SET_PROF
)
1547 char *ident
=_asl_action_profile_test(m
, r
);
1548 char *notify_key
= configuration_profile_create_notification_key(ident
);
1551 if (notify_key
!= NULL
)
1554 asl_action_set_param_data_t
*spdata
;
1556 status
= notify_register_dispatch(notify_key
, &token
, asl_action_queue
, ^(int t
){
1557 _asl_action_handle_file_change_notification(t
);
1562 spdata
= (asl_action_set_param_data_t
*)calloc(1, sizeof(asl_action_set_param_data_t
));
1565 notify_cancel(token
);
1569 spdata
->token
= token
;
1570 r
->private = spdata
;
1574 else if (r
->action
== ACTION_SET_FILE
)
1577 const char *path
=_asl_action_file_test(m
, r
);
1581 asprintf(¬ify_key
, "%s%s", NOTIFY_PATH_SERVICE
, path
);
1582 if (notify_key
!= NULL
)
1585 asl_action_set_param_data_t
*spdata
;
1587 status
= notify_register_dispatch(notify_key
, &token
, asl_action_queue
, ^(int t
){
1588 _asl_action_handle_file_change_notification(t
);
1593 spdata
= (asl_action_set_param_data_t
*)calloc(1, sizeof(asl_action_set_param_data_t
));
1596 notify_cancel(token
);
1600 spdata
->token
= token
;
1601 r
->private = spdata
;
1609 _asl_action_configure()
1612 asl_out_module_t
*m
;
1615 if (global
.asl_out_module
== NULL
) global
.asl_out_module
= asl_out_module_init();
1616 if (global
.asl_out_module
== NULL
) return;
1618 if (global
.debug
!= 0)
1621 if (global
.debug_file
== NULL
) dfp
= fopen(_PATH_SYSLOGD_LOG
, "a");
1622 else dfp
= fopen(global
.debug_file
, "a");
1625 for (m
= global
.asl_out_module
; m
!= NULL
; m
= m
->next
)
1627 fprintf(dfp
, "module: %s%s\n", (m
->name
== NULL
) ? "<unknown>" : m
->name
, (m
->flags
& MODULE_FLAG_LOCAL
) ? " (local)" : "");
1628 asl_out_module_print(dfp
, m
);
1635 asldebug("%s: init\n", MY_ID
);
1637 action_asl_store_count
= 0;
1639 for (m
= global
.asl_out_module
; m
!= NULL
; m
= m
->next
)
1641 for (r
= m
->ruleset
; r
!= NULL
; r
= r
->next
)
1643 _asl_action_post_process_rule(m
, r
);
1644 if (r
->dst
!= NULL
) flags
|= (r
->dst
->flags
& (MODULE_FLAG_ROTATE
| MODULE_FLAG_CRASHLOG
));
1648 if (global
.debug
!= 0)
1651 if (global
.debug_file
== NULL
) dfp
= fopen(_PATH_SYSLOGD_LOG
, "a");
1652 else dfp
= fopen(global
.debug_file
, "a");
1655 for (m
= global
.asl_out_module
; m
!= NULL
; m
= m
->next
)
1657 fprintf(dfp
, "module: %s%s\n", (m
->name
== NULL
) ? "<unknown>" : m
->name
, (m
->flags
& MODULE_FLAG_LOCAL
) ? " (local)" : "");
1658 asl_out_module_print(dfp
, m
);
1665 sweep_time
= time(NULL
);
1667 if (flags
& MODULE_FLAG_ROTATE
)
1669 _act_file_checkpoint_all(CHECKPOINT_TEST
);
1670 if (checkpoint_timer
== NULL
) _start_cycling();
1673 #if TARGET_OS_EMBEDDED
1674 if (flags
& MODULE_FLAG_CRASHLOG
) _crashlog_sentinel_init();
1679 asl_action_init(void)
1681 static dispatch_once_t once
;
1683 dispatch_once(&once
, ^{
1684 asl_action_queue
= dispatch_queue_create("ASL Action Queue", NULL
);
1685 #if TARGET_OS_EMBEDDED
1686 crashlog_queue
= dispatch_queue_create("iOS CrashLog Queue", NULL
);
1687 notify_register_dispatch(CRASH_MOVER_WILL_START_NOTIFICATION
, &crashmover_token
, asl_action_queue
, ^(int unused
){
1688 if (crashmover_state
== 0)
1690 asldebug("CrashMover active: suspending crashlog queue and closing files\n");
1691 crashmover_state
= time(NULL
);
1692 dispatch_suspend(crashlog_queue
);
1693 _asl_action_close_idle_files(0);
1699 _asl_action_configure();
1708 _asl_action_free_modules(asl_out_module_t
*m
)
1711 asl_out_module_t
*x
;
1714 * asl_common frees a list of modules with asl_out_module_free.
1715 * This loop frees the private data attached some modules.
1717 for (x
= m
; x
!= NULL
; x
= x
->next
)
1719 for (r
= x
->ruleset
; r
!= NULL
; r
= r
->next
)
1721 if ((r
->action
== ACTION_ASL_FILE
) || (r
->action
== ACTION_ASL_DIR
))
1725 _asl_action_store_data_free((asl_action_store_data_t
*)r
->dst
->private);
1726 r
->dst
->private = NULL
;
1729 else if (r
->action
== ACTION_FILE
)
1733 asl_action_file_data_t
*fdata
= (asl_action_file_data_t
*)r
->dst
->private;
1736 /* flush repeat message if necessary */
1737 if (fdata
->last_count
> 0) _send_repeat_msg(r
);
1738 _asl_action_file_data_free(fdata
);
1739 r
->dst
->private = NULL
;
1743 else if (r
->action
== ACTION_SET_PLIST
)
1745 _asl_action_set_param_data_free((asl_action_set_param_data_t
*)r
->private);
1747 else if (r
->action
== ACTION_SET_PROF
)
1749 _asl_action_set_param_data_free((asl_action_set_param_data_t
*)r
->private);
1754 asl_out_module_free(m
);
1758 _asl_action_close_internal(void)
1760 #if TARGET_OS_EMBEDDED
1761 dispatch_source_cancel(crashlog_sentinel_src
);
1762 dispatch_release(crashlog_sentinel_src
);
1763 crashlog_sentinel_src
= NULL
;
1764 close(crashlog_sentinel_fd
);
1765 if (crashmover_state
!= 0)
1767 dispatch_resume(crashlog_queue
);
1768 crashmover_state
= 0;
1771 /* wait for the crashlog_queue to flush before _asl_action_free_modules() */
1772 dispatch_sync(crashlog_queue
, ^{ crashlog_sentinel_fd
= -1; });
1775 _asl_action_free_modules(global
.asl_out_module
);
1776 global
.asl_out_module
= NULL
;
1777 sweep_time
= time(NULL
);
1783 _asl_action_close_idle_files(time_t idle_time
)
1785 asl_out_module_t
*m
;
1786 time_t now
= time(NULL
);
1788 for (m
= global
.asl_out_module
; m
!= NULL
; m
= m
->next
)
1792 for (r
= m
->ruleset
; r
!= NULL
; r
= r
->next
)
1796 if ((r
->dst
!= NULL
) && (r
->dst
->flags
& MODULE_FLAG_CRASHLOG
))
1799 if (r
->action
!= ACTION_ASL_DIR
) asl_out_dst_checkpoint(r
->dst
, CHECKPOINT_FORCE
);
1802 else if ((r
->action
== ACTION_ASL_DIR
) || (r
->action
== ACTION_ASL_FILE
))
1806 asl_action_store_data_t
*sdata
= (asl_action_store_data_t
*)r
->dst
->private;
1807 if ((sdata
!= NULL
) && (sdata
->store
!= NULL
) && ((now
- sdata
->last_time
) >= idle_time
)) _act_dst_close(r
);
1810 else if (r
->action
== ACTION_FILE
)
1814 asl_action_file_data_t
*fdata
= (asl_action_file_data_t
*)r
->dst
->private;
1815 if ((fdata
!= NULL
) && (fdata
->fd
>= 0) && ((now
- fdata
->last_time
) >= idle_time
)) _act_dst_close(r
);
1823 asl_action_close(void)
1825 dispatch_async(asl_action_queue
, ^{
1826 _asl_action_close_internal();
1833 asl_action_reset(void)
1835 dispatch_async(asl_action_queue
, ^{
1836 _asl_action_close_internal();
1844 _asl_action_module_with_name(const char *name
)
1846 asl_out_module_t
*m
;
1848 if (global
.asl_out_module
== NULL
) return NULL
;
1849 if (name
== NULL
) return global
.asl_out_module
;
1851 for (m
= global
.asl_out_module
; m
!= NULL
; m
= m
->next
)
1853 if ((m
->name
!= NULL
) && (!strcmp(m
->name
, name
))) return m
;
1860 * called from control_message
1861 * Used to control modules dynamically.
1862 * Line format "@ module param [value ...]"
1864 * Note this is synchronous on asl_action queue.
1867 asl_action_control_set_param(const char *s
)
1873 if (s
== NULL
) return -1;
1874 if (s
[0] == '\0') return 0;
1876 /* skip '@' and whitespace */
1878 while ((*s
== ' ') || (*s
== '\t')) s
++;
1880 l
= explode(s
, " \t");
1881 if (l
!= NULL
) for (count
= 0; l
[count
] != NULL
; count
++);
1883 /* at least 2 parameters (l[0] = module, l[1] = param) required */
1884 if (count
< 2) return -1;
1886 if (global
.asl_out_module
== NULL
)
1888 asldebug("asl_action_control_set_param: no modules loaded\n");
1892 /* create / modify a module */
1893 if ((!strcasecmp(l
[1], "define")) && (strcmp(l
[0], "*")))
1898 asldebug("asl_action_control_set_param: memory allocation failed\n");
1902 dispatch_sync(asl_action_queue
, ^{
1903 asl_out_module_t
*m
;
1906 /* skip name, whitespace, "define" */
1907 while ((*p
!= ' ') && (*p
!= '\t')) p
++;
1908 while ((*p
== ' ') || (*p
== '\t')) p
++;
1909 while ((*p
!= ' ') && (*p
!= '\t')) p
++;
1911 m
= _asl_action_module_with_name(l
[0]);
1914 asl_out_module_t
*x
;
1916 m
= asl_out_module_new(l
[0]);
1917 for (x
= global
.asl_out_module
; x
->next
!= NULL
; x
= x
->next
);
1921 r
= asl_out_module_parse_line(m
, p
);
1924 _asl_action_post_process_rule(m
, r
);
1925 if ((r
->dst
!= NULL
) && (r
->dst
->flags
& MODULE_FLAG_ROTATE
))
1927 _act_file_checkpoint_all(CHECKPOINT_TEST
);
1928 if (checkpoint_timer
== NULL
) _start_cycling();
1934 free_string_list(l
);
1938 dispatch_sync(asl_action_queue
, ^{
1941 asl_out_module_t
*m
;
1943 if (!strcmp(l
[0], "*"))
1946 m
= _asl_action_module_with_name(NULL
);
1950 m
= _asl_action_module_with_name(l
[0]);
1955 if (!strcasecmp(l
[1], "enable"))
1959 /* don't do enable for ASL_MODULE_NAME if input name is "*" */
1960 if ((do_all
== 0) || (strcmp(m
->name
, ASL_MODULE_NAME
)))
1962 /* @ module enable {0|1} */
1963 if (count
> 2) intval
= atoi(l
[2]);
1965 if (intval
== 0) m
->flags
&= ~MODULE_FLAG_ENABLED
;
1966 else m
->flags
|= MODULE_FLAG_ENABLED
;
1969 else if (!strcasecmp(l
[1], "checkpoint"))
1971 /* @ module checkpoint [file] */
1972 if (count
> 2) _act_file_checkpoint(m
, l
[2], CHECKPOINT_FORCE
);
1973 else _act_file_checkpoint(m
, NULL
, CHECKPOINT_FORCE
);
1976 if (do_all
== 1) m
= m
->next
;
1982 free_string_list(l
);
1987 asl_action_file_checkpoint(const char *module, const char *path
)
1989 /* Note this is synchronous on asl_action queue */
1990 dispatch_sync(asl_action_queue
, ^{
1991 asl_out_module_t
*m
= _asl_action_module_with_name(module);
1992 _act_file_checkpoint(m
, path
, CHECKPOINT_FORCE
);