xref: /NextBSD/usr.sbin/asl/asl_action.c (revision 33da5adc555b3bc29986eeadca03829e4ad06b1e)
1 /*
2  * Copyright (c) 2004-2013 Apple Inc. All rights reserved.
3  *
4  * @APPLE_LICENSE_HEADER_START@
5  *
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
11  * file.
12  *
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.
20  *
21  * @APPLE_LICENSE_HEADER_END@
22  */
23 
24 #include <TargetConditionals.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/socket.h>
28 #include <sys/un.h>
29 #include <sys/uio.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <fcntl.h>
35 #include <errno.h>
36 #include <netdb.h>
37 #include <notify.h>
38 #include <pthread.h>
39 #include <sys/acl.h>
40 #include <dirent.h>
41 #include <time.h>
42 //#include <membership.h>
43 //#include <configuration_profile.h>
44 #include "daemon.h"
45 //#include <xpc/private.h>
46 
47 #define _PATH_WALL "/usr/bin/wall"
48 
49 #define MY_ID "asl_action"
50 
51 #define MAX_FAILURES 5
52 
53 #define ACTION_STATUS_ERROR  -1
54 #define ACTION_STATUS_OK      0
55 
56 #define IDLE_CLOSE 300
57 
58 #define PRIVATE_FLAG_NO_CLOSE    0x00000001 /* File or Store is closed by a cancellation handler */
59 
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[] =
66 {
67 	"checkpoint",
68 	"deleted",
69 	"error",
70 	"idle",
71 	"shutdown"
72 };
73 
74 #define forever for(;;)
75 
76 static dispatch_queue_t asl_action_queue;
77 static dispatch_source_t checkpoint_timer;
78 static time_t sweep_time = 0;
79 
80 #if TARGET_OS_EMBEDDED
81 #ifndef CRASH_MOVER_SERVICE
82 #define CRASH_MOVER_SERVICE "com.apple.crash_mover"
83 #endif
84 static dispatch_queue_t crashlog_queue;
85 static time_t crashmover_state = 0;
86 static int crashmover_token = -1;
87 #endif
88 
89 typedef struct asl_file_data
90 {
91 	uint64_t next_id;
92 	asl_file_t *aslfile;
93 	time_t last_time;
94 	uint32_t flags;
95 	uint32_t pending;
96 	dispatch_source_t monitor;
97 } asl_action_asl_file_data_t;
98 
99 typedef struct asl_store_data
100 {
101 	FILE *storedata;
102 	asl_file_t *aslfile;
103 	uint64_t next_id;
104 	time_t last_time;
105 	uint32_t flags;
106 	uint32_t pending;
107 	uint32_t p_year;
108 	uint32_t p_month;
109 	uint32_t p_day;
110 	dispatch_source_t storedata_monitor;
111 	dispatch_source_t aslfile_monitor;
112 } asl_action_asl_store_data_t;
113 
114 typedef struct file_data
115 {
116 	int fd;
117 	uint32_t flags;
118 	time_t last_time;
119 	uint32_t last_count;
120 	uint32_t last_hash;
121 	uint32_t pending;
122 	char *last_msg;
123 	dispatch_source_t dup_timer;
124 	dispatch_source_t monitor;
125 } asl_action_file_data_t;
126 
127 typedef struct set_param_data
128 {
129 	int token;
130 } asl_action_set_param_data_t;
131 
132 static int action_asl_store_count;
133 static bool store_has_logged;
134 
135 extern void db_save_message(asl_msg_t *m);
136 
137 /* forward */
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);
142 
143 static void
_act_out_set_param(asl_out_module_t * m,char * x,bool eval)144 _act_out_set_param(asl_out_module_t *m, char *x, bool eval)
145 {
146 	char *s = x;
147 	char **l;
148 	uint32_t count, intval;
149 
150 	l = explode(s, " \t");
151 	if (l == NULL) return;
152 
153 	for (count = 0; l[count] != NULL; count++);
154 	if (count == 0)
155 	{
156 		free_string_list(l);
157 		return;
158 	}
159 
160 	if (!strcasecmp(l[0], "enable"))
161 	{
162 		/* = enable [1|0] */
163 		if (count < 2) intval = 1;
164 		else intval = atoi(l[1]);
165 
166 		if (!eval) intval = (intval == 0) ? 1 : 0;
167 
168 		if (intval == 0) m->flags &= ~MODULE_FLAG_ENABLED;
169 		else m->flags|= MODULE_FLAG_ENABLED;
170 		free_string_list(l);
171 		return;
172 	}
173 	else if (!strcasecmp(l[0], "disable"))
174 	{
175 		/* = disable [1|0] */
176 		if (count < 2) intval = 1;
177 		else intval = atoi(l[1]);
178 
179 		if (!eval) intval = (intval == 0) ? 1 : 0;
180 
181 		if (intval != 0) m->flags &= ~MODULE_FLAG_ENABLED;
182 		else m->flags|= MODULE_FLAG_ENABLED;
183 		free_string_list(l);
184 		return;
185 	}
186 
187 	free_string_list(l);
188 
189 	if (!strcmp(m->name, ASL_MODULE_NAME))
190 	{
191 		/* Other parameters may be set by com.apple.asl module */
192 		control_set_param(x, eval);
193 	}
194 }
195 
196 static void
_act_notify(asl_out_module_t * m,asl_out_rule_t * r)197 _act_notify(asl_out_module_t *m, asl_out_rule_t *r)
198 {
199 	if (m == NULL) return;
200 	if ((m->flags & MODULE_FLAG_ENABLED) == 0) return;
201 
202 	if (r == NULL) return;
203 	if (r->options == NULL) return;
204 
205 	// XXX notify_post(r->options);
206 }
207 
208 static void
_act_broadcast(asl_out_module_t * m,asl_out_rule_t * r,asl_msg_t * msg)209 _act_broadcast(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
210 {
211 #if !TARGET_OS_EMBEDDED
212 	FILE *pw;
213 	const char *val;
214 
215 	if (m == NULL) return;
216 	if ((m->flags & MODULE_FLAG_ENABLED) == 0) return;
217 
218 	if (m->name == NULL) return;
219 	if (r == NULL) return;
220 	if (msg == NULL) return;
221 
222 	/* only base module (asl.conf) may broadcast */
223 	if (strcmp(m->name, ASL_MODULE_NAME)) return;
224 
225 	val = r->options;
226 	if (val == NULL) val = asl_msg_get_val_for_key(msg, ASL_KEY_MSG);
227 	if (val == NULL) return;
228 
229 	pw = popen(_PATH_WALL, "w");
230 	if (pw == NULL)
231 	{
232 		asldebug("%s: error sending wall message: %s\n", MY_ID, strerror(errno));
233 		return;
234 	}
235 
236 	fprintf(pw, "%s", val);
237 	pclose(pw);
238 #endif
239 }
240 
241 static void
_act_set_key(asl_out_module_t * m,asl_out_rule_t * r,asl_msg_t * msg)242 _act_set_key(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
243 {
244 	/* Placeholder */
245 }
246 
247 static void
_act_unset_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)
249 {
250 	/* Placeholder */
251 }
252 
253 static void
_act_access_control(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)
255 {
256 	int32_t ruid, rgid;
257 	char *p;
258 
259 	if (m == NULL) return;
260 	if (m->name == NULL) return;
261 	if (r == NULL) return;
262 	if (msg == NULL) return;
263 
264 	/* only base module (asl.conf) may set access controls */
265 	if (strcmp(m->name, ASL_MODULE_NAME)) return;
266 
267 	ruid = atoi(r->options);
268 	rgid = -1;
269 	p = strchr(r->options, ' ');
270 	if (p == NULL) p = strchr(r->options, '\t');
271 	if (p != NULL)
272 	{
273 		*p = '\0';
274 		p++;
275 		rgid = atoi(p);
276 	}
277 
278 	if (ruid != -1) asl_msg_set_key_val(msg, ASL_KEY_READ_UID, r->options);
279 	if (p != NULL)
280 	{
281 		if (rgid != -1) asl_msg_set_key_val(msg, ASL_KEY_READ_GID, p);
282 		p--;
283 		*p = ' ';
284 	}
285 }
286 
287 #if TARGET_OS_EMBEDDED
288 static void
_crashlog_queue_check(void)289 _crashlog_queue_check(void)
290 {
291 	/*
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.
297 	 */
298 	if (crashmover_state == 0) return;
299 	if ((time(NULL) - crashmover_state) <= 60) return;
300 
301 	asldebug("CrashMover timeout: resuming crashlog queue\n");
302 	dispatch_resume(crashlog_queue);
303 	crashmover_state = 0;
304 }
305 #endif
306 
307 /*
308  * cover routine for asl_out_dst_file_create_open()
309  */
310 static int
_act_file_create_open(asl_out_dst_data_t * dst)311 _act_file_create_open(asl_out_dst_data_t *dst)
312 {
313 	return asl_out_dst_file_create_open(dst, NULL);
314 }
315 
316 /*
317  * Checks and creates (if required) a directory for an ASL data store.
318  */
319 static int
_asl_dir_create(asl_out_rule_t * r)320 _asl_dir_create(asl_out_rule_t *r)
321 {
322 	struct stat sb;
323 	int status;
324 
325 	memset(&sb, 0, sizeof(struct stat));
326 	status = stat(r->dst->path, &sb);
327 	if (status == 0)
328 	{
329 		/* Store path must be a directory */
330 		if (!S_ISDIR(sb.st_mode))
331 		{
332 			asldebug("_asl_dir_create: expected a directory at path %s\n", r->dst->path);
333 			return -1;
334 		}
335 	}
336 	else if (errno == ENOENT)
337 	{
338 		/* Directory doesn't exists - try to create it */
339 		status = asl_out_mkpath(global.asl_out_module, r);
340 		if (status != 0)
341 		{
342 			asldebug("_asl_dir_create: asl_out_mkpath failed: %s\n", r->dst->path);
343 			return -1;
344 		}
345 	}
346 	else
347 	{
348 		/* Unexpected stat error */
349 		asldebug("_asl_dir_create: stat error %s\n", strerror(errno));
350 		return -1;
351 	}
352 
353 	return 0;
354 }
355 
356 /*
357  * Close an ASL Directory StoreData file
358  * - cancel the dispatch source for the file
359  */
360 static void
_asl_dir_storedata_close(asl_out_rule_t * r)361 _asl_dir_storedata_close(asl_out_rule_t *r)
362 {
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;
365 
366 	if (as_data->storedata_monitor == NULL)
367 	{
368 		/*
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.
372 		 */
373 		asldebug("close ASL storedata fd %d\n", fileno(as_data->storedata));
374 		fclose(as_data->storedata);
375 	}
376 	else
377 	{
378 		/*
379 		 * The storedata_monitor cancel handler will close the file.
380 		 */
381 		dispatch_source_cancel(as_data->storedata_monitor);
382 		dispatch_release(as_data->storedata_monitor);
383 
384 	}
385 
386 	asldebug("_asl_dir_storedata_close %p\n", as_data->storedata);
387 	as_data->storedata = NULL;
388 }
389 
390 /*
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
395  */
396 static int
_asl_dir_storedata_open(asl_out_rule_t * r,uint64_t xid)397 _asl_dir_storedata_open(asl_out_rule_t *r, uint64_t xid)
398 {
399 	struct stat sb;
400 	int status;
401 	char dstpath[MAXPATHLEN];
402 
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;
405 
406 	status = _asl_dir_create(r);
407 	if (status != 0)
408 	{
409 		asldebug("_asl_dir_storedata_open: No directory at path %s\n", r->dst->path);
410 		return -1;
411 	}
412 
413 	/* StoreData file is not open */
414 	snprintf(dstpath, sizeof(dstpath), "%s/%s", r->dst->path, FILE_ASL_STORE_DATA);
415 
416 	memset(&sb, 0, sizeof(struct stat));
417 	status = stat(dstpath, &sb);
418 	if (status == 0)
419 	{
420 		/* StoreData file exists */
421 		as_data->storedata = fopen(dstpath, "r+");
422 		if (as_data->storedata == NULL)
423 		{
424 			asldebug("_asl_dir_storedata_open: fopen existing %s: %s\n", dstpath, strerror(errno));
425 			return -1;
426 		}
427 	}
428 	else if (errno == ENOENT)
429 	{
430 		/*
431 		 * StoreData file does not exist.
432 		 * Create a new StoreData with a given xid.
433 		 */
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)
438 		{
439 			asldebug("_asl_dir_storedata_open: fopen new %s: %s\n", dstpath, strerror(errno));
440 			return -1;
441 		}
442 
443 		uint64_t xout = asl_core_htonq(xid);
444 		status = fwrite(&xout, sizeof(uint64_t), 1, as_data->storedata);
445 		if (status != 1)
446 		{
447 			asldebug("_asl_dir_storedata_open: storedata write failed %d %s\n", errno, strerror(errno));
448 			return -1;
449 		}
450 
451 #if !TARGET_IPHONE_SIMULATOR
452 		if (chown(dstpath, r->dst->uid[0], r->dst->gid[0]) != 0)
453 		{
454 			asldebug("_asl_dir_storedata_open: chown %d %d new %s: %s\n", dstpath, r->dst->uid[0], r->dst->gid[0], strerror(errno));
455 			return -1;
456 		}
457 #endif
458 	}
459 	else
460 	{
461 		/* Unexpected stat error */
462 		asldebug("_asl_dir_storedata_open: stat error %s\n", strerror(errno));
463 		return -1;
464 	}
465 
466 	/* create storedata_monitor */
467 	int fd = fileno(as_data->storedata);
468 	FILE *sdfp = as_data->storedata;
469 
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)
472 	{
473 		dispatch_source_set_event_handler(as_data->storedata_monitor, ^{
474 			_act_dst_close(r, DST_CLOSE_DELETED);
475 		});
476 
477 		dispatch_source_set_cancel_handler(as_data->storedata_monitor, ^{
478 			asldebug("cancel/close ASL storedata fd %d\n", fd);
479 			fclose(sdfp);
480 		});
481 
482 		dispatch_resume(as_data->storedata_monitor);
483 	}
484 
485 	asldebug("_asl_dir_storedata_open ASL storedata %s fd %d\n", dstpath, fd);
486 	return 0;
487 }
488 
489 /*
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
493  */
494 static void
_asl_dir_today_close(asl_out_rule_t * r)495 _asl_dir_today_close(asl_out_rule_t *r)
496 {
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;
499 
500 	if (as_data->pending != 0)
501 	{
502 		char *str = NULL;
503 		asprintf(&str, "[Sender syslogd] [Level 4] [PID %u] [Facility syslog] [Message ASL Store %s was closed with %d pending messages]", global.pid, r->dst->fname, as_data->pending);
504 		internal_log_message(str);
505 		free(str);
506 	}
507 
508 	if (as_data->aslfile_monitor == NULL)
509 	{
510 		/*
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.
514 		 */
515 		asldebug("close ASL fd %d\n", fileno(as_data->aslfile->store));
516 		asl_file_close(as_data->aslfile);
517 	}
518 	else
519 	{
520 		/*
521 		 * The aslfile_monitor cancel handler will close the file.
522 		 */
523 		dispatch_source_cancel(as_data->aslfile_monitor);
524 		dispatch_release(as_data->aslfile_monitor);
525 		as_data->aslfile_monitor = NULL;
526 	}
527 
528 	as_data->p_year = 0;
529 	as_data->p_month = 0;
530 	as_data->p_day = 0;
531 
532 	free(r->dst->fname);
533 	r->dst->fname = NULL;
534 
535 	as_data->aslfile = NULL;
536 }
537 
538 /*
539  * Check file size.
540  */
541 static int
_act_checkpoint(asl_out_rule_t * r,uint32_t force)542 _act_checkpoint(asl_out_rule_t *r, uint32_t force)
543 {
544 	char tmpfname[MAXPATHLEN], *fn;
545 
546 	if (r == NULL) return 0;
547 	if (r->dst == NULL) return 0;
548 
549 	fn = r->dst->fname;
550 	if (fn == NULL)
551 	{
552 		if (r->dst->path == NULL) return 0;
553 		asl_make_dst_filename(r->dst, tmpfname, sizeof(tmpfname));
554 		fn = tmpfname;
555 	}
556 
557 	if ((force == CHECKPOINT_TEST) && (r->dst->file_max == 0)) return 0;
558 
559 	if ((r->dst->size == 0) || (r->dst->stamp == 0))
560 	{
561 		struct stat sb;
562 
563 		memset(&sb, 0, sizeof(struct stat));
564 
565 		if (stat(fn, &sb) < 0)
566 		{
567 			if (errno == ENOENT) return 0;
568 			return -1;
569 		}
570 
571 		if (r->dst->stamp == 0) r->dst->stamp = sb.st_birthtimespec.tv_sec;
572 		if (r->dst->stamp == 0) r->dst->stamp = sb.st_mtimespec.tv_sec;
573 		r->dst->size = sb.st_size;
574 	}
575 
576 	if ((force == CHECKPOINT_TEST) && (r->dst->size < r->dst->file_max)) return 0;
577 
578 	if (r->dst->flags & MODULE_FLAG_BASESTAMP)
579 	{
580 		_act_dst_close(r, DST_CLOSE_CHECKPOINT);
581 	}
582 	else
583 	{
584 		char srcpath[MAXPATHLEN];
585 		char dstpath[MAXPATHLEN];
586 		char tstamp[32];
587 
588 		if (r->dst->stamp == 0) r->dst->stamp = time(NULL);
589 		asl_make_timestamp(r->dst->stamp, r->dst->flags, tstamp, sizeof(tstamp));
590 
591 		snprintf(srcpath, sizeof(srcpath), "%s", fn);
592 		snprintf(dstpath, sizeof(dstpath), "%s.%s", fn, tstamp);
593 
594 		_act_dst_close(r, DST_CLOSE_CHECKPOINT);
595 		rename(srcpath, dstpath);
596 	}
597 
598 	r->dst->stamp = 0;
599 	r->dst->size = 0;
600 	return 1;
601 }
602 
603 /*
604  * Open today's ASL file in an ASL directory
605  * - Checks date and closes a currently open file if it has the wrong date
606  * - Opens today's file
607  */
608 static int
_asl_dir_today_open(asl_out_rule_t * r,const time_t * tick)609 _asl_dir_today_open(asl_out_rule_t *r, const time_t *tick)
610 {
611 	int status;
612 	mode_t mask;
613 	struct tm ctm;
614 	time_t now;
615 
616 	if (r == NULL) return -1;
617 	if (r->dst == NULL) return -1;
618 
619 	status = _asl_dir_create(r);
620 	if (status != 0)
621 	{
622 		asldebug("_asl_dir_today_open: No directory at path %s\n", r->dst->path);
623 		return -1;
624 	}
625 
626 	asl_action_asl_store_data_t *as_data = (asl_action_asl_store_data_t *)r->dst->private;
627 
628 	memset(&ctm, 0, sizeof(struct tm));
629 	if (tick == NULL)
630 	{
631 		now = time(NULL);
632 		tick = (const time_t *)&now;
633 	}
634 
635 	if (localtime_r(tick, &ctm) == NULL)
636 	{
637 		asldebug("_asl_dir_today_open: localtime_r error %s\n", strerror(errno));
638 		return -1;
639 	}
640 
641 	/* checks file_max and closes if required */
642 	status = _act_checkpoint(r, CHECKPOINT_TEST);
643 	if (status == 1) trigger_aslmanager();
644 
645 	if (as_data->aslfile != NULL)
646 	{
647 		/* Check the date */
648 		if ((as_data->p_year == ctm.tm_year) && (as_data->p_month == ctm.tm_mon) && (as_data->p_day == ctm.tm_mday)) return 0;
649 
650 		/* Wrong date, close the current file */
651 		_asl_dir_today_close(r);
652 	}
653 
654 	/* Open data file */
655 
656 	if (r->dst->flags & MODULE_FLAG_BASESTAMP)
657 	{
658 		char tstamp[32];
659 
660 		if (tick == NULL)
661 		{
662 			now = time(NULL);
663 			tick = (const time_t *)&now;
664 		}
665 
666 		asl_make_timestamp(now, r->dst->flags, tstamp, sizeof(tstamp));
667 		asprintf(&(r->dst->fname), "%s/%s.asl", r->dst->path, tstamp);
668 	}
669 	else
670 	{
671 		asprintf(&(r->dst->fname), "%s/%d.%02d.%02d.asl", r->dst->path, ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday);
672 	}
673 
674 
675 	if (r->dst->fname == NULL)
676 	{
677 		asldebug("_asl_dir_today_open: asprintf error %s\n", strerror(errno));
678 		return -1;
679 	}
680 
681 #if TARGET_IPHONE_SIMULATOR
682 	uid_t uid = -1;
683 	gid_t gid = -1;
684 #else
685 	uid_t uid = r->dst->uid[0];
686 	gid_t gid = r->dst->gid[0];
687 #endif
688 
689 	mask = umask(0);
690 	status = asl_file_open_write(r->dst->fname, (r->dst->mode & 00666), uid, gid, &(as_data->aslfile));
691 	umask(mask);
692 
693 	if (status != ASL_STATUS_OK)
694 	{
695 		asldebug("_asl_dir_today_open: asl_file_open_write %s error %s\n", r->dst->fname, asl_core_error(status));
696 		free(r->dst->fname);
697 		r->dst->fname = NULL;
698 		return -1;
699 	}
700 
701 	if (fseek(as_data->aslfile->store, 0, SEEK_END) != 0)
702 	{
703 		asldebug("_asl_dir_today_open: fseek %s error %s\n", r->dst->fname, strerror(errno));
704 		free(r->dst->fname);
705 		r->dst->fname = NULL;
706 		return -1;
707 	}
708 
709 	as_data->p_year = ctm.tm_year;
710 	as_data->p_month = ctm.tm_mon;
711 	as_data->p_day = ctm.tm_mday;
712 
713 	/* create aslfile_monitor */
714 	int fd = fileno(as_data->aslfile->store);
715 	asl_file_t *aslf = as_data->aslfile;
716 
717 	as_data->aslfile_monitor = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, DISPATCH_VNODE_DELETE, asl_action_queue);
718 	if (as_data->aslfile_monitor != NULL)
719 	{
720 		dispatch_source_set_event_handler(as_data->aslfile_monitor, ^{
721 			_act_dst_close(r, DST_CLOSE_DELETED);
722 		});
723 
724 		dispatch_source_set_cancel_handler(as_data->aslfile_monitor, ^{
725 			asldebug("cancel/close ASL file fd %d\n", fd);
726 			asl_file_close(aslf);
727 		});
728 
729 		dispatch_resume(as_data->aslfile_monitor);
730 	}
731 
732 	asldebug("_asl_dir_today_open ASL file %s fd %d\n", r->dst->fname, fd);
733 
734 	return 0;
735 }
736 
737 static void
_asl_file_close(asl_out_rule_t * r)738 _asl_file_close(asl_out_rule_t *r)
739 {
740 	if (r == NULL) return;
741 	if (r->dst == NULL) return;
742 
743 	asl_action_asl_file_data_t *af_data = (asl_action_asl_file_data_t *)r->dst->private;
744 	if (af_data->aslfile == NULL) return;
745 
746 	if (af_data->pending != 0)
747 	{
748 		char *str = NULL;
749 		asprintf(&str, "[Sender syslogd] [Level 4] [PID %u] [Facility syslog] [Message ASL File %s was closed with %d pending messages]", global.pid, r->dst->fname, af_data->pending);
750 		internal_log_message(str);
751 		free(str);
752 	}
753 
754 	if (af_data->monitor == NULL)
755 	{
756 		/*
757 		 * This should never happen, but _asl_file_open allows
758 		 * dispatch_source_create to fail silently.  If there is no dispatch
759 		 * source, we just close the file.
760 		 */
761 		asldebug("close ASL fd %d\n", fileno(af_data->aslfile->store));
762 		asl_file_close(af_data->aslfile);
763 	}
764 	else
765 	{
766 		/*
767 		 * The monitor cancel handler will close the file.
768 		 */
769 		dispatch_source_cancel(af_data->monitor);
770 		dispatch_release(af_data->monitor);
771 		af_data->monitor = NULL;
772 	}
773 
774 	af_data->aslfile = NULL;
775 }
776 
777 static int
_asl_file_open(asl_out_rule_t * r)778 _asl_file_open(asl_out_rule_t *r)
779 {
780 	int fd, status;
781 
782 	if (r == NULL) return -1;
783 	if (r->dst == NULL) return -1;
784 
785 	asl_action_asl_file_data_t *af_data = (asl_action_asl_file_data_t *)r->dst->private;
786 	if (af_data->aslfile != NULL) return 0;
787 
788 	/* create path if necessary */
789 	status = asl_out_mkpath(global.asl_out_module, r);
790 	if (status != 0)
791 	{
792 		asldebug("_asl_file_open: asl_out_mkpath %s failed\n", r->dst->path);
793 		return -1;
794 	}
795 
796 	fd = _act_file_create_open(r->dst);
797 	if (fd < 0)
798 	{
799 		asldebug("_asl_file_open: _act_file_create_open %s failed %d %s\n", r->dst->fname, errno, strerror(errno));
800 		return -1;
801 	}
802 
803 	close(fd);
804 
805 	if (r->dst->fname == NULL) return -1;
806 
807 	status = asl_file_open_write(r->dst->fname, 0, -1, -1, &(af_data->aslfile));
808 	if (status != ASL_STATUS_OK)
809 	{
810 		asldebug("_asl_file_open: asl_file_open_write %s failed %d %s\n", r->dst->fname, errno, strerror(errno));
811 		return -1;
812 	}
813 
814 	/* create monitor */
815 	fd = fileno(af_data->aslfile->store);
816 	asl_file_t *aslf = af_data->aslfile;
817 
818 	af_data->monitor = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, DISPATCH_VNODE_DELETE, asl_action_queue);
819 	if (af_data->monitor != NULL)
820 	{
821 		dispatch_source_set_event_handler(af_data->monitor, ^{
822 			_act_dst_close(r, DST_CLOSE_DELETED);
823 		});
824 
825 		dispatch_source_set_cancel_handler(af_data->monitor, ^{
826 			asldebug("cancel/close ASL file fd %d\n", fd);
827 			asl_file_close(aslf);
828 		});
829 
830 		dispatch_resume(af_data->monitor);
831 	}
832 
833 	asldebug("_asl_file_open ASL file %s fd %d\n", r->dst->fname, fd);
834 	return 0;
835 }
836 
837 static void
_text_file_close(asl_out_rule_t * r)838 _text_file_close(asl_out_rule_t *r)
839 {
840 	asl_action_file_data_t *f_data = (asl_action_file_data_t *)r->dst->private;
841 	if (f_data->fd < 0) return;
842 
843 	if (f_data->pending != 0)
844 	{
845 		char *str = NULL;
846 		asprintf(&str, "[Sender syslogd] [Level 4] [PID %u] [Facility syslog] [Message File %s was closed with %d pending messages]", global.pid, r->dst->fname, f_data->pending);
847 		internal_log_message(str);
848 		free(str);
849 	}
850 
851 	if (f_data->monitor == NULL)
852 	{
853 		/*
854 		 * This should never happen, but _text_file_open allows
855 		 * dispatch_source_create to fail silently.  If there is no dispatch
856 		 * source, we just close the file.
857 		 */
858 		asldebug("close fd %d\n", f_data->fd);
859 		close(f_data->fd);
860 	}
861 	else
862 	{
863 		/*
864 		 * The monitor cancel handler will close the file.
865 		 */
866 		dispatch_source_cancel(f_data->monitor);
867 		dispatch_release(f_data->monitor);
868 		f_data->monitor = NULL;
869 	}
870 
871 	f_data->fd = -1;
872 }
873 
874 static int
_text_file_open(asl_out_rule_t * r)875 _text_file_open(asl_out_rule_t *r)
876 {
877 	asl_action_file_data_t *f_data = (asl_action_file_data_t *)r->dst->private;
878 
879 	if (f_data->fd < 0)
880 	{
881 		f_data->fd = _act_file_create_open(r->dst);
882 		if (f_data->fd < 0)
883 		{
884 			/*
885 			 * lazy path creation: create path and retry
886 			 * _act_file_create_open does not create the path
887 			 * so we do it here.
888 			 */
889 			int status = asl_out_mkpath(global.asl_out_module, r);
890 			if (status != 0) return -1;
891 
892 			f_data->fd = _act_file_create_open(r->dst);
893 		}
894 
895 		if (f_data->fd < 0) return -1;
896 
897 		f_data->monitor = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, f_data->fd, DISPATCH_VNODE_DELETE, asl_action_queue);
898 		if (f_data->monitor != NULL)
899 		{
900 			int ffd = f_data->fd;
901 
902 			dispatch_source_set_event_handler(f_data->monitor, ^{
903 				asldebug("dispatch_source DISPATCH_VNODE_DELETE fd %d\n", ffd);
904 				_act_dst_close(r, DST_CLOSE_DELETED);
905 			});
906 
907 			dispatch_source_set_cancel_handler(f_data->monitor, ^{
908 				asldebug("cancel/close file fd %d\n", ffd);
909 				close(ffd);
910 			});
911 
912 			dispatch_resume(f_data->monitor);
913 		}
914 	}
915 
916 	return 0;
917 }
918 
919 static int
_act_dst_open(asl_out_rule_t * r,const time_t * tick,uint64_t xid)920 _act_dst_open(asl_out_rule_t *r, const time_t *tick, uint64_t xid)
921 {
922 	if (r == NULL) return -1;
923 	if (r->dst == NULL) return -1;
924 	if (r->dst->private == NULL) return -1;
925 
926 	if (r->action == ACTION_ASL_DIR)
927 	{
928 		if (_asl_dir_today_open(r, tick) != 0)
929 		{
930 			asldebug("_act_dst_open:_asl_dir_today_open %s FAILED!\n", r->dst->path);
931 			return -1;
932 		}
933 
934 		if (_asl_dir_storedata_open(r, xid) != 0)
935 		{
936 			asldebug("_act_dst_open:_asl_dir_storedata_open %s FAILED!  Closing today file\n", r->dst->path);
937 			_asl_dir_today_close(r);
938 			return -1;
939 		}
940 
941 		return 0;
942 	}
943 
944 	if (r->action == ACTION_ASL_FILE)
945 	{
946 		return _asl_file_open(r);
947 	}
948 
949 	if (r->action == ACTION_FILE)
950 	{
951 		return _text_file_open(r);
952 	}
953 
954 	return -1;
955 }
956 
957 static void
_act_dst_close(asl_out_rule_t * r,int why)958 _act_dst_close(asl_out_rule_t *r, int why)
959 {
960 	if (r == NULL) return;
961 	if (r->dst == NULL) return;
962 	if (r->dst->private == NULL) return;
963 
964 	if (r->action == ACTION_ASL_DIR)
965 	{
966 		asldebug("_act_dst_close: %s ASL DIR %s\n", why_str[why], r->dst->path);
967 		if (why != DST_CLOSE_CHECKPOINT) _asl_dir_storedata_close(r);
968 		_asl_dir_today_close(r);
969 	}
970 	else if (r->action == ACTION_ASL_FILE)
971 	{
972 		asldebug("_act_dst_close: %s ASL FILE %s\n", why_str[why], (r->dst->fname == NULL) ? r->dst->path : r->dst->fname);
973 		_asl_file_close(r);
974 	}
975 	else if (r->action == ACTION_FILE)
976 	{
977 		asldebug("_act_dst_close: %s FILE %s\n", why_str[why], (r->dst->fname == NULL) ? r->dst->path : r->dst->fname);
978 		_text_file_close(r);
979 	}
980 }
981 
982 static uint32_t
_act_store_file_setup(asl_out_module_t * m,asl_out_rule_t * r)983 _act_store_file_setup(asl_out_module_t *m, asl_out_rule_t *r)
984 {
985 	uint32_t status;
986 	asl_action_asl_file_data_t *af_data;
987 
988 	if (r == NULL) return ASL_STATUS_INVALID_STORE;
989 	if (r->dst == NULL) return ASL_STATUS_INVALID_STORE;
990 	if (r->dst->private == NULL) return ASL_STATUS_INVALID_STORE;
991 
992 	af_data = (asl_action_asl_file_data_t *)r->dst->private;
993 	if (af_data->aslfile != NULL)
994 	{
995 		af_data->next_id++;
996 		return ASL_STATUS_OK;
997 	}
998 
999 	if (_act_dst_open(r, NULL, 0) != 0) return ASL_STATUS_WRITE_FAILED;
1000 
1001 	status = asl_file_read_set_position(af_data->aslfile, ASL_FILE_POSITION_LAST);
1002 	if (status != ASL_STATUS_OK)
1003 	{
1004 		asldebug("_act_store_file_setup: asl_file_read_set_position failed %d %s\n", status, asl_core_error(status));
1005 		_act_dst_close(r, DST_CLOSE_ERROR);
1006 		return status;
1007 	}
1008 
1009 	af_data->next_id = af_data->aslfile->cursor_xid + 1;
1010 	if (fseek(af_data->aslfile->store, 0, SEEK_END) != 0)
1011 	{
1012 		asldebug("_act_store_file_setup: fseek failed %d %s\n", errno, strerror(errno));
1013 		_act_dst_close(r, DST_CLOSE_ERROR);
1014 		return ASL_STATUS_WRITE_FAILED;
1015 	}
1016 
1017 	return ASL_STATUS_OK;
1018 }
1019 
1020 /*
1021  * _act_store_dir_setup
1022  *
1023  * Opens StoreData file and today's file
1024  * Reads ASL Message ID from StoreData file
1025  * Writes ASL Message ID + 1 to StoreData file
1026  */
1027 static uint32_t
_act_store_dir_setup(asl_out_module_t * m,asl_out_rule_t * r,time_t tick)1028 _act_store_dir_setup(asl_out_module_t *m, asl_out_rule_t *r, time_t tick)
1029 {
1030 	uint64_t xid;
1031 	int status;
1032 	asl_action_asl_store_data_t *as_data;
1033 
1034 	if (r == NULL) return ASL_STATUS_INVALID_STORE;
1035 	if (r->dst == NULL) return ASL_STATUS_INVALID_STORE;
1036 	if (r->dst->private == NULL) return ASL_STATUS_INVALID_STORE;
1037 	if (r->dst->path == NULL) return ASL_STATUS_INVALID_STORE;
1038 
1039 	as_data = (asl_action_asl_store_data_t *)r->dst->private;
1040 
1041 	if (_act_dst_open(r, NULL, as_data->next_id) != 0)
1042 	{
1043 		asldebug("_act_store_dir_setup: _act_dst_open %s failed\n", r->dst->path);
1044 		return ASL_STATUS_WRITE_FAILED;
1045 	}
1046 
1047 	/* get / set message id from StoreData file */
1048 	xid = 0;
1049 	rewind(as_data->storedata);
1050 	if (fread(&xid, sizeof(uint64_t), 1, as_data->storedata) != 1)
1051 	{
1052 		asldebug("_act_store_dir_setup: storedata read failed %d %s\n", errno, strerror(errno));
1053 		_act_dst_close(r, DST_CLOSE_ERROR);
1054 		return ASL_STATUS_READ_FAILED;
1055 	}
1056 
1057 	xid = asl_core_ntohq(xid);
1058 	xid++;
1059 	as_data->next_id = xid;
1060 
1061 	xid = asl_core_htonq(xid);
1062 	rewind(as_data->storedata);
1063 	status = fwrite(&xid, sizeof(uint64_t), 1, as_data->storedata);
1064 	if (status != 1)
1065 	{
1066 		asldebug("_act_store_dir_setup: storedata write failed %d %s\n", errno, strerror(errno));
1067 		_act_dst_close(r, DST_CLOSE_ERROR);
1068 		return ASL_STATUS_WRITE_FAILED;
1069 	}
1070 
1071 	fflush(as_data->storedata);
1072 
1073 	if (fseek(as_data->aslfile->store, 0, SEEK_END) != 0)
1074 	{
1075 		asldebug("_act_store_dir_setup: aslfile fseek failed %d %s\n", errno, strerror(errno));
1076 		_act_dst_close(r, DST_CLOSE_ERROR);
1077 		return ASL_STATUS_FAILED;
1078 	}
1079 
1080 	return ASL_STATUS_OK;
1081 }
1082 
1083 static void
_asl_action_asl_store_data_free(asl_action_asl_store_data_t * as_data)1084 _asl_action_asl_store_data_free(asl_action_asl_store_data_t *as_data)
1085 {
1086 	if (as_data == NULL) return;
1087 	free(as_data);
1088 }
1089 
1090 static void
_asl_action_asl_file_data_free(asl_action_asl_file_data_t * af_data)1091 _asl_action_asl_file_data_free(asl_action_asl_file_data_t *af_data)
1092 {
1093 	if (af_data == NULL) return;
1094 	free(af_data);
1095 }
1096 
1097 static void
_asl_action_file_data_free(asl_action_file_data_t * f_data)1098 _asl_action_file_data_free(asl_action_file_data_t *f_data)
1099 {
1100 	if (f_data == NULL) return;
1101 
1102 	if (f_data->dup_timer != NULL)
1103 	{
1104 		if (f_data->last_count == 0)
1105 		{
1106 			/*
1107 			 * The timer exists, but last_count is zero, so the timer is suspended.
1108 			 * Sources must not be released in when suspended.
1109 			 * So we resume it so that we can release it.
1110 			 */
1111 			dispatch_resume(f_data->dup_timer);
1112 		}
1113 
1114 		dispatch_release(f_data->dup_timer);
1115 	}
1116 
1117 	free(f_data->last_msg);
1118 	free(f_data);
1119 }
1120 
1121 static void
_asl_action_set_param_data_free(asl_action_set_param_data_t * spdata)1122 _asl_action_set_param_data_free(asl_action_set_param_data_t *spdata)
1123 {
1124 // XXX	if (spdata != NULL) notify_cancel(spdata->token);
1125 	free(spdata);
1126 }
1127 
1128 static void
_asl_action_save_failed(const char * where,asl_out_module_t * m,asl_out_rule_t * r,uint32_t status)1129 _asl_action_save_failed(const char *where, asl_out_module_t *m, asl_out_rule_t *r, uint32_t status)
1130 {
1131 	if (r->dst->flags & MODULE_FLAG_SOFT_WRITE) return;
1132 
1133 	r->dst->fails++;
1134 	asldebug("%s: %s save to %s failed: %s\n", where, m->name, r->dst->path, asl_core_error(status));
1135 
1136 	/* disable further activity after multiple failures */
1137 	if (r->dst->fails > MAX_FAILURES)
1138 	{
1139 		char *str = NULL;
1140 		asprintf(&str, "[Sender syslogd] [Level 3] [PID %u] [Facility syslog] [Message Disabling module %s writes to %s following %u failures (%s)]", global.pid, m->name, r->dst->path, r->dst->fails, asl_core_error(status));
1141 		internal_log_message(str);
1142 		free(str);
1143 
1144 		if (r->action == ACTION_ASL_DIR) _asl_action_asl_store_data_free((asl_action_asl_store_data_t *)r->dst->private);
1145 		else if (r->action == ACTION_ASL_FILE) _asl_action_asl_file_data_free((asl_action_asl_file_data_t *)r->dst->private);
1146 		else if (r->action == ACTION_FILE) _asl_action_file_data_free((asl_action_file_data_t *)r->dst->private);
1147 
1148 		r->dst->private = NULL;
1149 		r->action = ACTION_NONE;
1150 	}
1151 }
1152 
1153 /*
1154  * Save a message in an ASL file.
1155  */
1156 static int
_act_store_file(asl_out_module_t * m,asl_out_rule_t * r,asl_msg_t * msg)1157 _act_store_file(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
1158 {
1159 	asl_action_asl_file_data_t *af_data;
1160 	uint32_t status;
1161 	uint64_t mid;
1162 
1163 	if (r == NULL) return ACTION_STATUS_ERROR;
1164 	if (r->dst == NULL) return ACTION_STATUS_ERROR;
1165 	if (r->dst->private == NULL) return ACTION_STATUS_ERROR;
1166 
1167 	af_data = (asl_action_asl_file_data_t *)r->dst->private;
1168 	if (af_data->pending > 0) af_data->pending--;
1169 
1170 	status = _act_store_file_setup(m, r);
1171 	if (status == ASL_STATUS_OK)
1172 	{
1173 		af_data->last_time = time(NULL);
1174 
1175 		r->dst->fails = 0;
1176 		mid = af_data->next_id;
1177 
1178 		/* save message to file and update dst size */
1179 		status = asl_file_save(af_data->aslfile, msg, &mid);
1180 		if (status == ASL_STATUS_OK)
1181 		{
1182 			r->dst->size = af_data->aslfile->file_size;
1183 
1184 			if (_act_checkpoint(r, CHECKPOINT_TEST) == 1) trigger_aslmanager();
1185 		}
1186 	}
1187 
1188 	if (status != ASL_STATUS_OK)
1189 	{
1190 		_asl_action_save_failed("_act_store_file", m, r, status);
1191 		return ACTION_STATUS_ERROR;
1192 	}
1193 
1194 	return ACTION_STATUS_OK;
1195 }
1196 
1197 /*
1198  * Save a message in an ASL data store.
1199  */
1200 static int
_act_store_dir(asl_out_module_t * m,asl_out_rule_t * r,asl_msg_t * msg)1201 _act_store_dir(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
1202 {
1203 	asl_action_asl_store_data_t *as_data;
1204 	uint32_t status;
1205 	uint64_t mid;
1206 	const char *val;
1207 	time_t tick;
1208 
1209 	if (r == NULL) return ACTION_STATUS_ERROR;
1210 	if (r->dst == NULL) return ACTION_STATUS_ERROR;
1211 	if (r->dst->private == NULL) return ACTION_STATUS_ERROR;
1212 
1213 	as_data = (asl_action_asl_store_data_t *)r->dst->private;
1214 	if (as_data->pending > 0) as_data->pending--;
1215 
1216 	val = asl_msg_get_val_for_key(msg, ASL_KEY_TIME);
1217 	if (val == NULL) return ACTION_STATUS_ERROR;
1218 
1219 	tick = atol(val);
1220 
1221 	status = _act_store_dir_setup(m, r, tick);
1222 	if (status == ASL_STATUS_OK)
1223 	{
1224 		as_data->last_time = time(NULL);
1225 
1226 		r->dst->fails = 0;
1227 		mid = as_data->next_id;
1228 		status = asl_file_save(as_data->aslfile, msg, &mid);
1229 		if (status == ASL_STATUS_OK) r->dst->size = as_data->aslfile->file_size;
1230 		else asldebug("_act_store_dir asl_file_save FAILED %s\n", asl_core_error(status));
1231 		//TODO: checkpoint here?
1232 		/*
1233 		 * Currently, the checkpoint is in _asl_dir_today_open().
1234 		 * Moving it here would be in keeping with the way that
1235 		 * _act_store_file() and _act_file_final() do checkpoints.
1236 		 */
1237 	}
1238 
1239 	if (status != ASL_STATUS_OK)
1240 	{
1241 		_asl_action_save_failed("_act_store_dir", m, r, status);
1242 		return ACTION_STATUS_ERROR;
1243 	}
1244 
1245 	return ACTION_STATUS_OK;
1246 }
1247 
1248 static void
_act_store_final(asl_out_module_t * m,asl_out_rule_t * r,asl_msg_t * msg)1249 _act_store_final(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
1250 {
1251 	if (r->action == ACTION_ASL_DIR) _act_store_dir(m, r, msg);
1252 	else _act_store_file(m, r, msg);
1253 }
1254 
1255 /*
1256  * Save a message to an ASL format file (ACTION_ASL_FILE)
1257  * or to an ASL directory (ACTION_ASL_DIR).
1258  */
1259 static void
_act_store(asl_out_module_t * m,asl_out_rule_t * r,asl_msg_t * msg)1260 _act_store(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
1261 {
1262 	if (r == NULL) return;
1263 	if (r->dst == NULL) return;
1264 	if (msg == NULL) return;
1265 	if (m == NULL) return;
1266 	if ((m->flags & MODULE_FLAG_ENABLED) == 0) return;
1267 
1268 	if (r->dst->flags & MODULE_FLAG_HAS_LOGGED) return;
1269 
1270 	r->dst->flags |= MODULE_FLAG_HAS_LOGGED;
1271 	if (r->action == ACTION_ASL_DIR)
1272 	{
1273 		asl_action_asl_store_data_t *as_data = (asl_action_asl_store_data_t *)r->dst->private;
1274 		if (as_data != NULL) as_data->pending++;
1275 	}
1276 	else if (r->action == ACTION_ASL_FILE)
1277 	{
1278 		asl_action_asl_file_data_t *af_data = (asl_action_asl_file_data_t *)r->dst->private;
1279 		if (af_data != NULL) af_data->pending++;
1280 	}
1281 
1282 #if TARGET_OS_EMBEDDED
1283 	if (r->dst->flags & MODULE_FLAG_CRASHLOG)
1284 	{
1285 		_crashlog_queue_check();
1286 		asl_msg_retain(msg);
1287 		dispatch_async(crashlog_queue, ^{
1288 			dispatch_async(asl_action_queue, ^{
1289 				_act_store_final(m, r, msg);
1290 				asl_msg_release(msg);
1291 			});
1292 		});
1293 		return;
1294 	}
1295 #endif
1296 
1297 	_act_store_final(m, r, msg);
1298 }
1299 
1300 static int
_send_repeat_msg(asl_out_rule_t * r)1301 _send_repeat_msg(asl_out_rule_t *r)
1302 {
1303 	asl_action_file_data_t *f_data;
1304 	char vt[32], *msg;
1305 	int len, status;
1306 	time_t now = time(NULL);
1307 
1308 	if (r == NULL) return -1;
1309 	if (r->dst == NULL) return -1;
1310 	if (r->dst->private == NULL) return -1;
1311 
1312 	f_data = (asl_action_file_data_t *)r->dst->private;
1313 
1314 	free(f_data->last_msg);
1315 	f_data->last_msg = NULL;
1316 
1317 	if (f_data->last_count == 0) return 0;
1318 
1319 	/* stop the timer */
1320 	dispatch_suspend(f_data->dup_timer);
1321 
1322 	memset(vt, 0, sizeof(vt));
1323 	ctime_r(&now, vt);
1324 	vt[19] = '\0';
1325 
1326 	msg = NULL;
1327 	asprintf(&msg, "%s --- last message repeated %u time%s ---\n", vt + 4, f_data->last_count, (f_data->last_count == 1) ? "" : "s");
1328 	f_data->last_count = 0;
1329 	f_data->last_time = now;
1330 	if (msg == NULL) return -1;
1331 
1332 	if (f_data->fd < 0) f_data->fd = _act_file_create_open(r->dst);
1333 
1334 	len = strlen(msg);
1335 	status = write(f_data->fd, msg, len);
1336 	free(msg);
1337 
1338 	if ((status < 0) || (status < len))
1339 	{
1340 		asldebug("%s: error writing repeat message (%s): %s\n", MY_ID, r->dst->fname, strerror(errno));
1341 		return -1;
1342 	}
1343 
1344 	return 0;
1345 }
1346 
1347 static void
_start_cycling()1348 _start_cycling()
1349 {
1350 	struct timespec midnight;
1351 	struct tm t;
1352 	time_t x;
1353 
1354 	x = time(NULL);
1355 
1356 	if (checkpoint_timer != NULL) return;
1357 
1358 	localtime_r(&x, &t);
1359 
1360 	t.tm_sec = 0;
1361 	t.tm_min = 0;
1362 	t.tm_hour = 0;
1363 	t.tm_mday++;
1364 
1365 	x = mktime(&t);
1366 	midnight.tv_sec = x;
1367 	midnight.tv_nsec = 0;
1368 
1369 	checkpoint_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, asl_action_queue);
1370 	dispatch_source_set_timer(checkpoint_timer, dispatch_walltime(&midnight, 0), NSEC_PER_SEC * SEC_PER_DAY, 0);
1371 	dispatch_source_set_event_handler(checkpoint_timer, ^{ _act_file_checkpoint_all(CHECKPOINT_FORCE); });
1372 	dispatch_resume(checkpoint_timer);
1373 }
1374 
1375 /* check if a module path (mpath) matches a user path (upath) */
1376 static bool
_act_file_equal(const char * mpath,const char * upath)1377 _act_file_equal(const char *mpath, const char *upath)
1378 {
1379 	const char *slash;
1380 
1381 	/* NULL upath means user wants to match all files */
1382 	if (upath == NULL) return true;
1383 
1384 	if (mpath == NULL) return false;
1385 
1386 	/* check for exact match */
1387 	if (!strcmp(mpath, upath)) return true;
1388 
1389 	/* upath may be the last component of mpath */
1390 	slash = strrchr(mpath, '/');
1391 	if (slash == NULL) return false;
1392 
1393 	if (!strcmp(slash + 1, upath)) return true;
1394 	return false;
1395 }
1396 
1397 static int
_act_file_checkpoint(asl_out_module_t * m,const char * path,uint32_t force)1398 _act_file_checkpoint(asl_out_module_t *m, const char *path, uint32_t force)
1399 {
1400 	asl_out_rule_t *r;
1401 	int did_checkpoint = 0;
1402 
1403 	if (m == NULL) return 0;
1404 
1405 
1406 	for (r = m->ruleset; r != NULL; r = r->next)
1407 	{
1408 		if ((r->action == ACTION_FILE) || (r->action == ACTION_ASL_FILE))
1409 		{
1410 			if (r->dst->flags & MODULE_FLAG_ROTATE)
1411 			{
1412 				if (_act_file_equal(r->dst->path, path))
1413 				{
1414 					if (force & CHECKPOINT_CRASH)
1415 					{
1416 						if (r->dst->flags & MODULE_FLAG_CRASHLOG)
1417 						{
1418 							if (_act_checkpoint(r, CHECKPOINT_FORCE) > 0)
1419 							{
1420 								did_checkpoint = 1;
1421 								_act_dst_close(r, DST_CLOSE_CHECKPOINT);
1422 							}
1423 						}
1424 					}
1425 					else
1426 					{
1427 						if (_act_checkpoint(r, force) > 0)
1428 						{
1429 							did_checkpoint = 1;
1430 							_act_dst_close(r, DST_CLOSE_CHECKPOINT);
1431 						}
1432 					}
1433 				}
1434 			}
1435 		}
1436 	}
1437 
1438 	return did_checkpoint;
1439 }
1440 
1441 static int
_act_file_checkpoint_all(uint32_t force)1442 _act_file_checkpoint_all(uint32_t force)
1443 {
1444 	asl_out_module_t *m;
1445 	int did_checkpoint = 0;
1446 
1447 	for (m = global.asl_out_module; m != NULL; m = m->next)
1448 	{
1449 		if (_act_file_checkpoint(m, NULL, force) > 0) did_checkpoint = 1;
1450 	}
1451 
1452 	trigger_aslmanager();
1453 
1454 	return did_checkpoint;
1455 }
1456 
1457 /*
1458  * Save a message in a plain text file.
1459  */
1460 static void
_act_file_final(asl_out_module_t * m,asl_out_rule_t * r,asl_msg_t * msg)1461 _act_file_final(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
1462 {
1463 	asl_action_file_data_t *f_data;
1464 	int is_dup;
1465 	uint32_t len, msg_hash = 0;
1466 	char *str;
1467 	time_t now;
1468 
1469 	if (r->dst->private == NULL) return;
1470 
1471 	f_data = (asl_action_file_data_t *)r->dst->private;
1472 	if (f_data->pending > 0) f_data->pending--;
1473 
1474 	/*
1475 	 * If print format is std, bsd, or msg, then skip messages with
1476 	 * no ASL_KEY_MSG, or without a value for it.
1477 	 */
1478 	if (r->dst->flags & MODULE_FLAG_STD_BSD_MSG)
1479 	{
1480 		const char *msgval = NULL;
1481 		if (asl_msg_lookup(msg, ASL_KEY_MSG, &msgval, NULL) != 0) return;
1482 		if (msgval == NULL) return;
1483 	}
1484 
1485 	now = time(NULL);
1486 
1487 	is_dup = 0;
1488 
1489 	str = asl_format_message(msg, r->dst->fmt, r->dst->tfmt, ASL_ENCODE_SAFE, &len);
1490 
1491 	if (r->dst->flags & MODULE_FLAG_COALESCE)
1492 	{
1493 		if (f_data->dup_timer == NULL)
1494 		{
1495 			/* create a timer to flush dups on this file */
1496 			f_data->dup_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, asl_action_queue);
1497 			dispatch_source_set_event_handler(f_data->dup_timer, ^{ _send_repeat_msg(r); });
1498 		}
1499 
1500 		if ((global.bsd_max_dup_time > 0) && (str != NULL) && (f_data->last_msg != NULL))
1501 		{
1502 			msg_hash = asl_core_string_hash(str + 16, len - 16);
1503 			if ((f_data->last_hash == msg_hash) && (!strcmp(f_data->last_msg, str + 16)))
1504 			{
1505 				if ((now - f_data->last_time) < global.bsd_max_dup_time) is_dup = 1;
1506 			}
1507 		}
1508 	}
1509 
1510 	if (is_dup == 1)
1511 	{
1512 		if (f_data->last_count == 0)
1513 		{
1514 			/* start the timer */
1515 			dispatch_source_set_timer(f_data->dup_timer, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * global.bsd_max_dup_time), DISPATCH_TIME_FOREVER, 0);
1516 			dispatch_resume(f_data->dup_timer);
1517 		}
1518 
1519 		f_data->last_count++;
1520 	}
1521 	else
1522 	{
1523 		if (_act_dst_open(r, NULL, 0) != 0)
1524 		{
1525 			_asl_action_save_failed("_act_file", m, r, ASL_STATUS_FAILED);
1526 			free(str);
1527 			return;
1528 		}
1529 		else
1530 		{
1531 			r->dst->fails = 0;
1532 		}
1533 
1534 		/*
1535 		 * The current message is not a duplicate.  If f_data->last_count > 0
1536 		 * we need to write a "last message repeated N times" log entry.
1537 		 * _send_repeat_msg will free last_msg and do nothing if
1538 		 * last_count == 0, but we test and free here to avoid a function call.
1539 		 */
1540 		if (f_data->last_count > 0)
1541 		{
1542 			_send_repeat_msg(r);
1543 		}
1544 		else
1545 		{
1546 			free(f_data->last_msg);
1547 			f_data->last_msg = NULL;
1548 		}
1549 
1550 		if (str != NULL) f_data->last_msg = strdup(str + 16);
1551 
1552 		f_data->last_hash = msg_hash;
1553 		f_data->last_count = 0;
1554 		f_data->last_time = now;
1555 
1556 		if ((str != NULL) && (len > 1))
1557 		{
1558 			/* write line to file and update dst size */
1559 			size_t bytes = write(f_data->fd, str, len - 1);
1560 			if (bytes > 0) r->dst->size += bytes;
1561 
1562 			if (_act_checkpoint(r, CHECKPOINT_TEST) == 1) trigger_aslmanager();
1563 		}
1564 	}
1565 
1566 	free(str);
1567 }
1568 
1569 static void
_act_file(asl_out_module_t * m,asl_out_rule_t * r,asl_msg_t * msg)1570 _act_file(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
1571 {
1572 	asl_action_file_data_t *f_data;
1573 
1574 	if (r == NULL) return;
1575 	if (msg == NULL) return;
1576 	if (m == NULL) return;
1577 	if ((m->flags & MODULE_FLAG_ENABLED) == 0) return;
1578 	if (r->dst == NULL) return;
1579 	if (r->dst->private == NULL) return;
1580 
1581 	if (r->dst->flags & MODULE_FLAG_HAS_LOGGED) return;
1582 
1583 	r->dst->flags |= MODULE_FLAG_HAS_LOGGED;
1584 	f_data = (asl_action_file_data_t *)r->dst->private;
1585 	if (f_data != NULL) f_data->pending++;
1586 
1587 #if TARGET_OS_EMBEDDED
1588 	if (r->dst->flags & MODULE_FLAG_CRASHLOG)
1589 	{
1590 		_crashlog_queue_check();
1591 		asl_msg_retain(msg);
1592 		dispatch_async(crashlog_queue, ^{
1593 			dispatch_async(asl_action_queue, ^{
1594 				_act_file_final(m, r, msg);
1595 				asl_msg_release(msg);
1596 			});
1597 		});
1598 		return;
1599 	}
1600 #endif
1601 
1602 	_act_file_final(m, r, msg);
1603 }
1604 
1605 static void
_act_forward(asl_out_module_t * m,asl_out_rule_t * r,asl_msg_t * msg)1606 _act_forward(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
1607 {
1608 	/* To do: <rdar://problem/6130747> Add a "forward" action to asl.conf */
1609 }
1610 
1611 static void
_act_control(asl_out_module_t * m,asl_out_rule_t * r,asl_msg_t * msg)1612 _act_control(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
1613 {
1614 	const char *p;
1615 
1616 	if (m == NULL) return;
1617 	if (r == NULL) return;
1618 
1619 	p = asl_msg_get_val_for_key(msg, ASL_KEY_MODULE);
1620 
1621 	if (r->options == NULL) return;
1622 
1623 	if (!strcmp(r->options, "enable"))
1624 	{
1625 		m->flags |= MODULE_FLAG_ENABLED;
1626 	}
1627 	else if (!strcmp(r->options, "disable"))
1628 	{
1629 		m->flags &= ~MODULE_FLAG_ENABLED;
1630 	}
1631 	else if ((!strcmp(r->options, "checkpoint")) || (!strcmp(r->options, "rotate")))
1632 	{
1633 		_act_file_checkpoint(m, NULL, CHECKPOINT_FORCE);
1634 	}
1635 }
1636 
1637 static void
_send_to_asl_store(asl_msg_t * msg)1638 _send_to_asl_store(asl_msg_t *msg)
1639 {
1640 	if ((global.asl_out_module != NULL) && ((global.asl_out_module->flags & MODULE_FLAG_ENABLED) == 0)) return;
1641 
1642 	if (store_has_logged) return;
1643 	store_has_logged = true;
1644 
1645 	db_save_message(msg);
1646 }
1647 
1648 static int
_asl_out_process_message(asl_out_module_t * m,asl_msg_t * msg)1649 _asl_out_process_message(asl_out_module_t *m, asl_msg_t *msg)
1650 {
1651 	asl_out_rule_t *r;
1652 
1653 	if (m == NULL) return 1;
1654 	if (msg == NULL) return 1;
1655 
1656 	/* reset flag bit used for duplicate avoidance */
1657 	for (r = m->ruleset; r != NULL; r = r->next)
1658 	{
1659 		if ((r->action == ACTION_FILE) || (r->action == ACTION_ASL_DIR) || (r->action == ACTION_ASL_FILE))
1660 		{
1661 			if (r->dst != NULL) r->dst->flags &= MODULE_FLAG_CLEAR_LOGGED;
1662 		}
1663 	}
1664 
1665 	for (r = m->ruleset; r != NULL; r = r->next)
1666 	{
1667 		if (r->query == NULL) continue;
1668 
1669 		/* ACTION_SET_FILE, ACTION_SET_PLIST, and ACTION_SET_PROF are handled independently  */
1670 		if ((r->action == ACTION_SET_FILE) || (r->action == ACTION_SET_PLIST) || (r->action == ACTION_SET_PROF)) continue;
1671 
1672 		/*
1673 		 * ACTION_CLAIM during processing is a filter.  It will only be here if the option "only"
1674 		 * was supplied.  In this case we test the message against the query.  If it does not
1675 		 * match, we skip the message.
1676 		 */
1677 		if (r->action == ACTION_CLAIM)
1678 		{
1679 			if ((asl_msg_cmp(r->query, msg) != 1)) return 0;
1680 		}
1681 
1682 		if ((asl_msg_cmp(r->query, msg) == 1))
1683 		{
1684 			if (r->action == ACTION_NONE) continue;
1685 			else if (r->action == ACTION_IGNORE) return 1;
1686 			else if (r->action == ACTION_SKIP) return 0;
1687 			else if (r->action == ACTION_ASL_STORE) _send_to_asl_store(msg);
1688 			else if (r->action == ACTION_ACCESS) _act_access_control(m, r, msg);
1689 			else if (r->action == ACTION_SET_KEY) _act_set_key(m, r, msg);
1690 			else if (r->action == ACTION_UNSET_KEY) _act_unset_key(m, r, msg);
1691 			else if (r->action == ACTION_NOTIFY) _act_notify(m, r);
1692 			else if (r->action == ACTION_BROADCAST) _act_broadcast(m, r, msg);
1693 			else if (r->action == ACTION_FORWARD) _act_forward(m, r, msg);
1694 			else if (r->action == ACTION_CONTROL) _act_control(m, r, msg);
1695 			else if (r->action == ACTION_SET_PARAM) _act_out_set_param(m, r->options, true);
1696 			else if ((r->action == ACTION_ASL_FILE) || (r->action == ACTION_ASL_DIR)) _act_store(m, r, msg);
1697 			else if (r->action == ACTION_FILE) _act_file(m, r, msg);
1698 		}
1699 	}
1700 
1701 	return 0;
1702 }
1703 
1704 void
asl_out_message(asl_msg_t * msg)1705 asl_out_message(asl_msg_t *msg)
1706 {
1707 	OSAtomicIncrement32(&global.asl_queue_count);
1708 	asl_msg_retain(msg);
1709 
1710 	dispatch_async(asl_action_queue, ^{
1711 		int ignore = 0;
1712 		const char *p;
1713 		time_t now = time(NULL);
1714 		asl_out_module_t *m = global.asl_out_module;
1715 
1716 		store_has_logged = false;
1717 
1718 		p = asl_msg_get_val_for_key(msg, ASL_KEY_MODULE);
1719 		if (p == NULL)
1720 		{
1721 			if ((action_asl_store_count == 0) || (asl_check_option(msg, ASL_OPT_STORE) == 1)) _send_to_asl_store(msg);
1722 
1723 			ignore = _asl_out_process_message(m, msg);
1724 			if (ignore == 0)
1725 			{
1726 				if (m != NULL) m = m->next;
1727 				while (m != NULL)
1728 				{
1729 					_asl_out_process_message(m, msg);
1730 					m = m->next;
1731 				}
1732 			}
1733 		}
1734 		else
1735 		{
1736 			if (m != NULL) m = m->next;
1737 			while (m != NULL)
1738 			{
1739 				if (!strcmp(p, m->name)) _asl_out_process_message(m, msg);
1740 				m = m->next;
1741 			}
1742 		}
1743 
1744 		p = asl_msg_get_val_for_key(msg, ASL_KEY_FINAL_NOTIFICATION);
1745 		if (p != NULL) asl_msg_set_key_val(msg, ASL_KEY_FREE_NOTE, p);
1746 
1747 		asl_msg_release(msg);
1748 		OSAtomicDecrement32(&global.asl_queue_count);
1749 
1750 		if ((now - sweep_time) >= IDLE_CLOSE)
1751 		{
1752 			_asl_action_close_idle_files(IDLE_CLOSE);
1753 			sweep_time = now;
1754 		}
1755 	});
1756 }
1757 
1758 static char *
_asl_action_profile_test(asl_out_module_t * m,asl_out_rule_t * r)1759 _asl_action_profile_test(asl_out_module_t *m, asl_out_rule_t *r)
1760 {
1761 	const char *ident;
1762 	asl_msg_t *profile;
1763 	bool eval;
1764 
1765 	/* ident is first message key */
1766 	asl_msg_fetch(r->query, 0, &ident, NULL, NULL);
1767 	if (ident == NULL)
1768 	{
1769 		r->action = ACTION_NONE;
1770 		return NULL;
1771 	}
1772 
1773 #if 0
1774 	profile = configuration_profile_to_asl_msg(ident);
1775 #endif
1776 	eval = (asl_msg_cmp(r->query, profile) == 1);
1777 	_act_out_set_param(m, r->options, eval);
1778 	asl_msg_release(profile);
1779 
1780 	return strdup(ident);
1781 }
1782 
1783 static const char *
_asl_action_file_test(asl_out_module_t * m,asl_out_rule_t * r)1784 _asl_action_file_test(asl_out_module_t *m, asl_out_rule_t *r)
1785 {
1786 	const char *path;
1787 	struct stat sb;
1788 	int status;
1789 	bool eval;
1790 
1791 	/* path is first message key */
1792 	asl_msg_fetch(r->query, 0, &path, NULL, NULL);
1793 	if (path == NULL)
1794 	{
1795 		r->action = ACTION_NONE;
1796 		return NULL;
1797 	}
1798 
1799 	memset(&sb, 0, sizeof(struct stat));
1800 	status = stat(path, &sb);
1801 	eval = (status == 0);
1802 	_act_out_set_param(m, r->options, eval);
1803 
1804 	return path;
1805 }
1806 
1807 static void
_asl_action_handle_file_change_notification(int t)1808 _asl_action_handle_file_change_notification(int t)
1809 {
1810 	asl_out_module_t *m;
1811 	asl_out_rule_t *r;
1812 
1813 	for (m = global.asl_out_module; m != NULL; m = m->next)
1814 	{
1815 		for (r = m->ruleset; r != NULL; r = r->next)
1816 		{
1817 			if (r->action == ACTION_SET_FILE)
1818 			{
1819 				asl_action_set_param_data_t *spdata = (asl_action_set_param_data_t *)r->private;
1820 				if ((spdata != NULL) && (spdata->token == t))
1821 				{
1822 					_asl_action_file_test(m, r);
1823 					return;
1824 				}
1825 			}
1826 			else if (r->action == ACTION_SET_PLIST)
1827 			{
1828 				asl_action_set_param_data_t *spdata = (asl_action_set_param_data_t *)r->private;
1829 				if ((spdata != NULL) && (spdata->token == t))
1830 				{
1831 					char *str = _asl_action_profile_test(m, r);
1832 					free(str);
1833 					return;
1834 				}
1835 			}
1836 			else if (r->action == ACTION_SET_PROF)
1837 			{
1838 				asl_action_set_param_data_t *spdata = (asl_action_set_param_data_t *)r->private;
1839 				if ((spdata != NULL) && (spdata->token == t))
1840 				{
1841 					char *str = _asl_action_profile_test(m, r);
1842 					free(str);
1843 					return;
1844 				}
1845 			}
1846 		}
1847 	}
1848 
1849 	asl_out_module_free(m);
1850 }
1851 
1852 static void
_asl_action_post_process_rule(asl_out_module_t * m,asl_out_rule_t * r)1853 _asl_action_post_process_rule(asl_out_module_t *m, asl_out_rule_t *r)
1854 {
1855 	if ((m == NULL) || (r == NULL)) return;
1856 
1857 	if (m != global.asl_out_module)
1858 	{
1859 		/* check if any previous module has used this destination */
1860 		asl_out_module_t *n;
1861 		bool search = true;
1862 
1863 		if ((r->dst != NULL) && (r->dst->path != NULL))
1864 		{
1865 			for (n = global.asl_out_module; search && (n != NULL) && (n != m); n = n->next)
1866 			{
1867 				asl_out_rule_t *s;
1868 				for (s = n->ruleset; search && (s != NULL); s = s->next)
1869 				{
1870 					if (s->action == ACTION_OUT_DEST)
1871 					{
1872 						if ((s->dst != NULL) && (s->dst->path != NULL) && (!strcmp(r->dst->path, s->dst->path)))
1873 						{
1874 							/* rule r of module m is using previously used dst of rule s of module n */
1875 							asl_out_dst_data_release(r->dst);
1876 							r->dst = NULL;
1877 
1878 							if (r->action == ACTION_OUT_DEST)
1879 							{
1880 								char *str = NULL;
1881 								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);
1882 								internal_log_message(str);
1883 								free(str);
1884 							}
1885 							else
1886 							{
1887 								r->dst = asl_out_dst_data_retain(s->dst);
1888 							}
1889 
1890 							search = false;
1891 						}
1892 					}
1893 				}
1894 			}
1895 		}
1896 	}
1897 
1898 	if (r->action == ACTION_SET_PARAM)
1899 	{
1900 		if (r->query == NULL) _act_out_set_param(m, r->options, true);
1901 	}
1902 	else if (r->action == ACTION_CLAIM)
1903 	{
1904 		/* becomes ACTION_SKIP in com.apple.asl config */
1905 		if (m != global.asl_out_module)
1906 		{
1907 			asl_out_rule_t *rule = (asl_out_rule_t *)calloc(1, sizeof(asl_out_rule_t));
1908 			if (rule != NULL)
1909 			{
1910 				char *str = NULL;
1911 				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);
1912 				internal_log_message(str);
1913 				free(str);
1914 
1915 				rule->query = asl_msg_copy(r->query);
1916 				rule->action = ACTION_SKIP;
1917 				rule->next = global.asl_out_module->ruleset;
1918 				global.asl_out_module->ruleset = rule;
1919 			}
1920 
1921 			/*
1922 			 * After adding ACTION_SKIP to com.apple.asl module, the claim becomes a no-op in this module
1923 			 * UNLESS the claim includes the option "only".  In that case, the claim becomes a filter:
1924 			 * any messages that DO NOT match the claim are skipped by this module.
1925 			 */
1926 			if (r->options == NULL) r->action = ACTION_NONE;
1927 			else if (strcmp(r->options, "only") != 0) r->action = ACTION_NONE;
1928 		}
1929 	}
1930 	else if (r->action == ACTION_ASL_STORE)
1931 	{
1932 		action_asl_store_count++;
1933 	}
1934 	else if (r->action == ACTION_ASL_DIR)
1935 	{
1936 		if (r->dst->private == NULL) r->dst->private = (asl_action_asl_store_data_t *)calloc(1, sizeof(asl_action_asl_store_data_t));
1937 	}
1938 	else if (r->action == ACTION_ASL_FILE)
1939 	{
1940 		if (r->dst->private == NULL)r->dst->private = (asl_action_asl_file_data_t *)calloc(1, sizeof(asl_action_asl_file_data_t));
1941 	}
1942 	else if (r->action == ACTION_FILE)
1943 	{
1944 		if (r->dst->private == NULL) r->dst->private = (asl_action_file_data_t *)calloc(1, sizeof(asl_action_file_data_t));
1945 		if (r->dst->private != NULL) ((asl_action_file_data_t *)(r->dst->private))->fd = -1;
1946 	}
1947 	else if (r->action == ACTION_SET_PLIST)
1948 	{
1949 		char *ident =_asl_action_profile_test(m, r);
1950 		char *notify_key = NULL; // XXX configuration_profile_create_notification_key(ident);
1951 		free(ident);
1952 
1953 		if (notify_key != NULL)
1954 		{
1955 			int status, token;
1956 			asl_action_set_param_data_t *spdata;
1957 
1958 			status = notify_register_dispatch(notify_key, &token, asl_action_queue, ^(int t){
1959 				_asl_action_handle_file_change_notification(t);
1960 			});
1961 
1962 			free(notify_key);
1963 
1964 			spdata = (asl_action_set_param_data_t *)calloc(1, sizeof(asl_action_set_param_data_t));
1965 			if (spdata == NULL)
1966 			{
1967 				// XXX notify_cancel(token);
1968 			}
1969 			else
1970 			{
1971 				spdata->token = token;
1972 				r->private = spdata;
1973 			}
1974 		}
1975 	}
1976 	else if (r->action == ACTION_SET_PROF)
1977 	{
1978 		char *ident =_asl_action_profile_test(m, r);
1979 		char *notify_key = NULL; // XXX configuration_profile_create_notification_key(ident);
1980 		free(ident);
1981 
1982 		if (notify_key != NULL)
1983 		{
1984 			int status, token;
1985 			asl_action_set_param_data_t *spdata;
1986 
1987 //			status = notify_register_dispatch(notify_key, &token, asl_action_queue, ^(int t){
1988 //				_asl_action_handle_file_change_notification(t);
1989 // XXX			});
1990 
1991 			free(notify_key);
1992 
1993 			spdata = (asl_action_set_param_data_t *)calloc(1, sizeof(asl_action_set_param_data_t));
1994 			if (spdata == NULL)
1995 			{
1996 				// XXX notify_cancel(token);
1997 			}
1998 			else
1999 			{
2000 				spdata->token = token;
2001 				r->private = spdata;
2002 			}
2003 		}
2004 	}
2005 	else if (r->action == ACTION_SET_FILE)
2006 	{
2007 		char *notify_key;
2008 		const char *path =_asl_action_file_test(m, r);
2009 
2010 		if (path != NULL)
2011 		{
2012 			asprintf(&notify_key, "%s%s", NOTIFY_PATH_SERVICE, path);
2013 			if (notify_key != NULL)
2014 			{
2015 				int status, token;
2016 				asl_action_set_param_data_t *spdata;
2017 
2018 				// XXX status = notify_register_dispatch(notify_key, &token, asl_action_queue, ^(int t){
2019 				//	_asl_action_handle_file_change_notification(t);
2020 				// });
2021 
2022 				free(notify_key);
2023 
2024 				spdata = (asl_action_set_param_data_t *)calloc(1, sizeof(asl_action_set_param_data_t));
2025 				if (spdata == NULL)
2026 				{
2027 					// XXX notify_cancel(token);
2028 				}
2029 				else
2030 				{
2031 					spdata->token = token;
2032 					r->private = spdata;
2033 				}
2034 			}
2035 		}
2036 	}
2037 }
2038 
2039 static void
_asl_action_configure()2040 _asl_action_configure()
2041 {
2042 	asl_out_rule_t *r;
2043 	asl_out_module_t *m;
2044 	uint32_t flags = 0;
2045 
2046 	if (global.asl_out_module == NULL) global.asl_out_module = asl_out_module_init();
2047 	if (global.asl_out_module == NULL) return;
2048 
2049 	asldebug("%s: init\n", MY_ID);
2050 
2051 	action_asl_store_count = 0;
2052 
2053 	for (m = global.asl_out_module; m != NULL; m = m->next)
2054 	{
2055 		for (r = m->ruleset; r != NULL; r = r->next)
2056 		{
2057 			_asl_action_post_process_rule(m, r);
2058 			if (r->dst != NULL) flags |= (r->dst->flags & (MODULE_FLAG_ROTATE | MODULE_FLAG_CRASHLOG));
2059 		}
2060 	}
2061 
2062 	if (global.debug != 0)
2063 	{
2064 		FILE *dfp;
2065 		if (global.debug_file == NULL) dfp = fopen(_PATH_SYSLOGD_LOG, "a");
2066 		else dfp = fopen(global.debug_file, "a");
2067 		if (dfp != NULL)
2068 		{
2069 			for (m = global.asl_out_module; m != NULL; m = m->next)
2070 			{
2071 				fprintf(dfp, "module: %s%s\n", (m->name == NULL) ? "<unknown>" : m->name, (m->flags & MODULE_FLAG_LOCAL) ? " (local)" : "");
2072 				asl_out_module_print(dfp, m);
2073 				fprintf(dfp, "\n");
2074 			}
2075 			fclose(dfp);
2076 		}
2077 	}
2078 
2079 	sweep_time = time(NULL);
2080 
2081 	if (flags & MODULE_FLAG_ROTATE)
2082 	{
2083 		_act_file_checkpoint_all(CHECKPOINT_TEST);
2084 		if (checkpoint_timer == NULL) _start_cycling();
2085 	}
2086 }
2087 
2088 int
asl_action_init(void)2089 asl_action_init(void)
2090 {
2091 	static dispatch_once_t once;
2092 
2093 	dispatch_once(&once, ^{
2094 		asl_action_queue = dispatch_queue_create("ASL Action Queue", NULL);
2095 #if TARGET_OS_EMBEDDED
2096 		crashlog_queue = dispatch_queue_create("iOS CrashLog Queue", NULL);
2097 		notify_register_dispatch(CRASH_MOVER_SERVICE, &crashmover_token, asl_action_queue, ^(int unused) {
2098 			uint64_t cmstate = 0;
2099 			uint64_t oldstate = (crashmover_state == 0) ? 0llu : 1llu;
2100 
2101 			uint32_t status = 1; // XXX notify_get_state(crashmover_token, &cmstate);
2102 			if (status == 0)
2103 			{
2104 				if (cmstate != oldstate)
2105 				{
2106 					crashmover_state = 0;
2107 					if (cmstate == 1) crashmover_state = time(NULL);
2108 
2109 					if (crashmover_state == 0)
2110 					{
2111 						asldebug("CrashMover finished\n");
2112 						dispatch_resume(crashlog_queue);
2113 					}
2114 					else
2115 					{
2116 						asldebug("CrashMover active: suspending crashlog queue and closing files\n");
2117 						dispatch_suspend(crashlog_queue);
2118 						_asl_action_close_idle_files(0);
2119 					}
2120 				}
2121 			}
2122 		});
2123 #endif
2124 	});
2125 
2126 	_asl_action_configure();
2127 
2128 	return 0;
2129 }
2130 
2131 /*
2132  * Close outputs and free modules.
2133  */
2134 static void
_asl_action_free_modules(asl_out_module_t * m)2135 _asl_action_free_modules(asl_out_module_t *m)
2136 {
2137 	asl_out_rule_t *r;
2138 	asl_out_module_t *x;
2139 
2140 	/*
2141 	 * asl_common frees a list of modules with asl_out_module_free.
2142 	 * This loop frees the private data attached some modules.
2143 	 */
2144 	for (x = m; x != NULL; x = x->next)
2145 	{
2146 		for (r = x->ruleset; r != NULL; r = r->next)
2147 		{
2148 			if (r->action == ACTION_ASL_DIR)
2149 			{
2150 				_act_dst_close(r, DST_CLOSE_SHUTDOWN);
2151 				if (r->dst != NULL)
2152 				{
2153 					_asl_action_asl_store_data_free((asl_action_asl_store_data_t *)r->dst->private);
2154 					r->dst->private = NULL;
2155 				}
2156 			}
2157 			else if (r->action == ACTION_ASL_FILE)
2158 			{
2159 				_act_dst_close(r, DST_CLOSE_SHUTDOWN);
2160 				if (r->dst != NULL)
2161 				{
2162 					_asl_action_asl_file_data_free((asl_action_asl_file_data_t *)r->dst->private);
2163 					r->dst->private = NULL;
2164 				}
2165 			}
2166 			else if (r->action == ACTION_FILE)
2167 			{
2168 				_act_dst_close(r, DST_CLOSE_SHUTDOWN);
2169 				if (r->dst != NULL)
2170 				{
2171 					asl_action_file_data_t *f_data = (asl_action_file_data_t *)r->dst->private;
2172 					if (f_data != NULL)
2173 					{
2174 						/* flush repeat message if necessary */
2175 						if (f_data->last_count > 0) _send_repeat_msg(r);
2176 						_asl_action_file_data_free(f_data);
2177 						r->dst->private = NULL;
2178 					}
2179 				}
2180 			}
2181 			else if (r->action == ACTION_SET_PLIST)
2182 			{
2183 				_asl_action_set_param_data_free((asl_action_set_param_data_t *)r->private);
2184 			}
2185 			else if (r->action == ACTION_SET_PROF)
2186 			{
2187 				_asl_action_set_param_data_free((asl_action_set_param_data_t *)r->private);
2188 			}
2189 			else if (r->action == ACTION_SET_FILE)
2190 			{
2191 				_asl_action_set_param_data_free((asl_action_set_param_data_t *)r->private);
2192 			}
2193 		}
2194 	}
2195 
2196 	asl_out_module_free(m);
2197 }
2198 
2199 static int
_asl_action_close_internal(void)2200 _asl_action_close_internal(void)
2201 {
2202 #if TARGET_OS_EMBEDDED
2203 	if (crashmover_state != 0)
2204 	{
2205 		dispatch_resume(crashlog_queue);
2206 		crashmover_state = 0;
2207 	}
2208 
2209 	/* wait for the crashlog_queue to flush before _asl_action_free_modules() */
2210 	dispatch_sync(crashlog_queue, ^{ int x = 0; if (x == 1) x = 2; });
2211 #endif
2212 
2213 	_asl_action_free_modules(global.asl_out_module);
2214 	global.asl_out_module = NULL;
2215 	sweep_time = time(NULL);
2216 
2217 	return 0;
2218 }
2219 
2220 static void
_asl_action_close_idle_files(time_t idle_time)2221 _asl_action_close_idle_files(time_t idle_time)
2222 {
2223 	asl_out_module_t *m;
2224 	time_t now = time(NULL);
2225 
2226 	for (m = global.asl_out_module; m != NULL; m = m->next)
2227 	{
2228 		asl_out_rule_t *r;
2229 
2230 		for (r = m->ruleset; r != NULL; r = r->next)
2231 		{
2232 			if (idle_time == 0)
2233 			{
2234 				if ((r->dst != NULL) && (r->dst->flags & MODULE_FLAG_CRASHLOG))
2235 				{
2236 					_act_dst_close(r, DST_CLOSE_IDLE);
2237 					//TODO: can r->action even be ACTION_ASL_DIR?
2238 					/* if not, we can avoid the extra check here */
2239 					if (r->action != ACTION_ASL_DIR) _act_checkpoint(r, CHECKPOINT_FORCE);
2240 				}
2241 			}
2242 			else if (r->action == ACTION_ASL_DIR)
2243 			{
2244 				if (r->dst != NULL)
2245 				{
2246 					asl_action_asl_store_data_t *as_data = (asl_action_asl_store_data_t *)r->dst->private;
2247 					if ((as_data != NULL) && (as_data->aslfile != NULL) && (as_data->pending == 0) && ((now - as_data->last_time) >= idle_time)) _act_dst_close(r, DST_CLOSE_IDLE);
2248 				}
2249 			}
2250 			else if (r->action == ACTION_ASL_FILE)
2251 			{
2252 				if (r->dst != NULL)
2253 				{
2254 					asl_action_asl_file_data_t *af_data = (asl_action_asl_file_data_t *)r->dst->private;
2255 					if ((af_data != NULL) && (af_data->aslfile != NULL) && (af_data->pending == 0) && ((now - af_data->last_time) >= idle_time)) _act_dst_close(r, DST_CLOSE_IDLE);
2256 				}
2257 			}
2258 			else if (r->action == ACTION_FILE)
2259 			{
2260 				if (r->dst != NULL)
2261 				{
2262 					asl_action_file_data_t *f_data = (asl_action_file_data_t *)r->dst->private;
2263 					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);
2264 				}
2265 			}
2266 		}
2267 	}
2268 }
2269 
2270 int
asl_action_close(void)2271 asl_action_close(void)
2272 {
2273 	dispatch_async(asl_action_queue, ^{
2274 		_asl_action_close_internal();
2275 	});
2276 
2277 	return 0;
2278 }
2279 
2280 int
asl_action_reset(void)2281 asl_action_reset(void)
2282 {
2283 	dispatch_async(asl_action_queue, ^{
2284 		_asl_action_close_internal();
2285 		asl_action_init();
2286 	});
2287 
2288 	return 0;
2289 }
2290 
2291 asl_out_module_t *
_asl_action_module_with_name(const char * name)2292 _asl_action_module_with_name(const char *name)
2293 {
2294 	asl_out_module_t *m;
2295 
2296 	if (global.asl_out_module == NULL) return NULL;
2297 	if (name == NULL) return global.asl_out_module;
2298 
2299 	for (m = global.asl_out_module; m != NULL; m = m->next)
2300 	{
2301 		if ((m->name != NULL) && (!strcmp(m->name, name))) return m;
2302 	}
2303 
2304 	return NULL;
2305 }
2306 
2307 /*
2308  * called from control_message
2309  * Used to control modules dynamically.
2310  * Line format "@ module param [value ...]"
2311  *
2312  * Note this is synchronous on asl_action queue.
2313  */
2314 int
asl_action_control_set_param(const char * s)2315 asl_action_control_set_param(const char *s)
2316 {
2317 	__block char **l;
2318 	__block char *p;
2319 	uint32_t count = 0;
2320 
2321 	if (s == NULL) return -1;
2322 	if (s[0] == '\0') return 0;
2323 
2324 	/* skip '@' and whitespace */
2325 	if (*s == '@') s++;
2326 	while ((*s == ' ') || (*s == '\t')) s++;
2327 
2328 	l = explode(s, " \t");
2329 	if (l != NULL) for (count = 0; l[count] != NULL; count++);
2330 
2331 	/* at least 2 parameters (l[0] = module, l[1] = param) required */
2332 	if (count < 2)
2333 	{
2334 		free_string_list(l);
2335 		return -1;
2336 	}
2337 
2338 	if (global.asl_out_module == NULL)
2339 	{
2340 		asldebug("asl_action_control_set_param: no modules loaded\n");
2341 		free_string_list(l);
2342 		return -1;
2343 	}
2344 
2345 	/* create / modify a module */
2346 	if ((!strcasecmp(l[1], "define")) && (strcmp(l[0], "*")))
2347 	{
2348 		p = strdup(s);
2349 		if (p == NULL)
2350 		{
2351 			asldebug("asl_action_control_set_param: memory allocation failed\n");
2352 			free_string_list(l);
2353 			return -1;
2354 		}
2355 
2356 		dispatch_sync(asl_action_queue, ^{
2357 			asl_out_module_t *m;
2358 			asl_out_rule_t *r;
2359 
2360 			/* skip name, whitespace, "define" */
2361 			while ((*p != ' ') && (*p != '\t')) p++;
2362 			while ((*p == ' ') || (*p == '\t')) p++;
2363 			while ((*p != ' ') && (*p != '\t')) p++;
2364 
2365 			m = _asl_action_module_with_name(l[0]);
2366 			if (m == NULL)
2367 			{
2368 				asl_out_module_t *x;
2369 
2370 				m = asl_out_module_new(l[0]);
2371 				for (x = global.asl_out_module; x->next != NULL; x = x->next);
2372 				x->next = m;
2373 			}
2374 
2375 			r = asl_out_module_parse_line(m, p);
2376 			if (r != NULL)
2377 			{
2378 				_asl_action_post_process_rule(m, r);
2379 				if ((r->dst != NULL) && (r->dst->flags & MODULE_FLAG_ROTATE))
2380 				{
2381 					_act_file_checkpoint_all(CHECKPOINT_TEST);
2382 					if (checkpoint_timer == NULL) _start_cycling();
2383 				}
2384 			}
2385 		});
2386 
2387 		free(p);
2388 		free_string_list(l);
2389 		return 0;
2390 	}
2391 
2392 	dispatch_sync(asl_action_queue, ^{
2393 		uint32_t intval;
2394 		int do_all = 0;
2395 		asl_out_module_t *m;
2396 
2397 		if (!strcmp(l[0], "*"))
2398 		{
2399 			do_all = 1;
2400 			m = _asl_action_module_with_name(NULL);
2401 		}
2402 		else
2403 		{
2404 			m = _asl_action_module_with_name(l[0]);
2405 		}
2406 
2407 		while (m != NULL)
2408 		{
2409 			if (!strcasecmp(l[1], "enable"))
2410 			{
2411 				intval = 1;
2412 
2413 				/* don't do enable for ASL_MODULE_NAME if input name is "*" */
2414 				if ((do_all == 0) || (strcmp(m->name, ASL_MODULE_NAME)))
2415 				{
2416 					/* @ module enable {0|1} */
2417 					if (count > 2) intval = atoi(l[2]);
2418 
2419 					if (intval == 0) m->flags &= ~MODULE_FLAG_ENABLED;
2420 					else m->flags |= MODULE_FLAG_ENABLED;
2421 				}
2422 			}
2423 			else if (!strcasecmp(l[1], "checkpoint"))
2424 			{
2425 				/* @ module checkpoint [file] */
2426 				if (count > 2) _act_file_checkpoint(m, l[2], CHECKPOINT_FORCE);
2427 				else _act_file_checkpoint(m, NULL, CHECKPOINT_FORCE);
2428 			}
2429 
2430 			if (do_all == 1) m = m->next;
2431 			else m = NULL;
2432 		}
2433 
2434 	});
2435 
2436 	free_string_list(l);
2437 	return 0;
2438 }
2439 
2440 int
asl_action_file_checkpoint(const char * module,const char * path)2441 asl_action_file_checkpoint(const char *module, const char *path)
2442 {
2443 	/* Note this is synchronous on asl_action queue */
2444 	dispatch_sync(asl_action_queue, ^{
2445 		asl_out_module_t *m = _asl_action_module_with_name(module);
2446 		_act_file_checkpoint(m, path, CHECKPOINT_FORCE);
2447 	});
2448 
2449 	return 0;
2450 }
2451