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 <string.h>
25#include <stdint.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <ctype.h>
29#include <unistd.h>
30#include <stdarg.h>
31#include <syslog.h>
32#include <errno.h>
33#include <limits.h>
34#include <time.h>
35#include <sys/stat.h>
36#include <sys/time.h>
37#include <sys/fcntl.h>
38#include <sys/param.h>
39#include <sys/fileport.h>
40#include <crt_externs.h>
41#include <asl.h>
42#include <regex.h>
43#include <notify.h>
44#include <mach/mach.h>
45#include <mach/std_types.h>
46#include <mach/mig.h>
47#include <mach/mach_types.h>
48#include <sys/types.h>
49#include <servers/bootstrap.h>
50#include <bootstrap_priv.h>
51#include <pthread.h>
52#include <dispatch/dispatch.h>
53#include <libkern/OSAtomic.h>
54#include <os/activity.h>
55#include <asl_ipc.h>
56#include <asl_client.h>
57#include <asl_core.h>
58#include <asl_msg.h>
59#include <asl_msg_list.h>
60#include <asl_store.h>
61#include <asl_private.h>
62
63#define forever for(;;)
64
65#define FETCH_BATCH	256
66
67#define LEVEL_MASK   0x0000000f
68#define EVAL_MASK    0x000000f0
69#define EVAL_IGNORE  0x00000000
70#define EVAL_ASLFILE 0x00000010
71#define EVAL_SEND    0x00000020
72#define EVAL_TUNNEL  0x00000040
73#define EVAL_FILE    0x00000080
74#define EVAL_QUOTA   0x00000100
75
76/*
77 * Clients get a max of 36000 messages per hour.
78 * Their quota gets refilled at a rate of 10 messages per second.
79 */
80#define QUOTA_MPH 36000
81#define QUOTA_MPS 10
82#define QUOTA_MSG_INTERVAL 60
83#define NOQUOTA_ENV "ASL_QUOTA_DISABLED"
84#define QUOTA_DISABLED_MSG "*** MESSAGE QUOTA DISABLED FOR THIS PROCESS ***"
85#define QUOTA_MSG "*** LOG MESSAGE QUOTA EXCEEDED - SOME MESSAGES FROM THIS PROCESS HAVE BEEN DISCARDED ***"
86#define QUOTA_LEVEL 2
87#define QUOTA_LEVEL_STR "2"
88
89/* forward */
90static ASL_STATUS _asl_send_message(asl_object_t obj, uint32_t eval, asl_msg_t *msg, const char *mstring);
91__private_extern__ asl_client_t *_asl_open_default();
92
93/* notify SPI */
94uint32_t notify_register_plain(const char *name, int *out_token);
95
96/* fork handling in asl_fd.c */
97extern void _asl_redirect_fork_child(void);
98
99typedef struct
100{
101	int fd;
102	asl_msg_t *msg;
103	dispatch_semaphore_t sem;
104} asl_aux_context_t;
105
106typedef struct
107{
108	int notify_count;
109	int rc_change_token;
110	int notify_token;
111	int master_token;
112	uint64_t proc_filter;
113	uint64_t master_filter;
114	time_t last_send;
115	time_t last_oq_msg;
116	uint32_t quota;
117	mach_port_t server_port;
118	char *sender;
119	pthread_mutex_t lock;
120	int aux_count;
121	asl_aux_context_t **aux_ctx;
122	asl_client_t *asl;
123} _asl_global_t;
124
125__private_extern__ _asl_global_t _asl_global = {0, -1, -1, -1, 0LL, 0LL, 0LL, 0LL, 0, MACH_PORT_NULL, NULL, PTHREAD_MUTEX_INITIALIZER, 0, NULL, NULL};
126
127static const char *level_to_number_string[] = {"0", "1", "2", "3", "4", "5", "6", "7"};
128
129#define ASL_SERVICE_NAME "com.apple.system.logger"
130
131/*
132 * Called from the child process inside fork() to clean up
133 * inherited state from the parent process.
134 *
135 * NB. A lock isn't required, since we're single threaded in this call.
136 */
137void
138_asl_fork_child()
139{
140	_asl_global.notify_count = 0;
141	_asl_global.rc_change_token = -1;
142	_asl_global.master_token = -1;
143	_asl_global.notify_token = -1;
144	_asl_global.quota = 0;
145	_asl_global.last_send = 0;
146	_asl_global.last_oq_msg = 0;
147
148	_asl_global.server_port = MACH_PORT_NULL;
149
150	pthread_mutex_init(&(_asl_global.lock), NULL);
151
152	_asl_redirect_fork_child();
153}
154
155/*
156 * asl_remote_notify_name: returns the notification key for remote-control filter
157 * changes for this process.
158 */
159char *
160asl_remote_notify_name()
161{
162	pid_t pid = getpid();
163	uid_t euid = geteuid();
164	char *str = NULL;
165
166	if (euid == 0) asprintf(&str, "%s.%d", NOTIFY_PREFIX_SYSTEM, pid);
167	else asprintf(&str, "user.uid.%d.syslog.%d", euid, pid);
168
169	return str;
170}
171
172static ASL_STATUS
173_asl_notify_open(int do_lock)
174{
175	char *notify_name;
176	uint32_t status;
177
178	if (do_lock != 0) pthread_mutex_lock(&_asl_global.lock);
179
180	_asl_global.notify_count++;
181
182	if (_asl_global.notify_token != -1)
183	{
184		if (do_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
185		return ASL_STATUS_OK;
186	}
187
188	if (_asl_global.rc_change_token == -1)
189	{
190		status = notify_register_check(NOTIFY_RC, &_asl_global.rc_change_token);
191		if (status != NOTIFY_STATUS_OK) _asl_global.rc_change_token = -1;
192	}
193
194	if (_asl_global.master_token == -1)
195	{
196		status = notify_register_plain(NOTIFY_SYSTEM_MASTER, &_asl_global.master_token);
197		if (status != NOTIFY_STATUS_OK) _asl_global.master_token = -1;
198	}
199
200	notify_name = asl_remote_notify_name();
201	if (notify_name != NULL)
202	{
203		status = notify_register_plain(notify_name, &_asl_global.notify_token);
204		free(notify_name);
205		if (status != NOTIFY_STATUS_OK) _asl_global.notify_token = -1;
206	}
207
208	if (do_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
209
210	if (_asl_global.notify_token == -1) return ASL_STATUS_FAILED;
211	return ASL_STATUS_OK;
212}
213
214#ifdef UNDEF
215static void
216_asl_notify_close()
217{
218	pthread_mutex_lock(&_asl_global.lock);
219
220	if (_asl_global.notify_count > 0) _asl_global.notify_count--;
221
222	if (_asl_global.notify_count > 0)
223	{
224		pthread_mutex_unlock(&_asl_global.lock);
225		return;
226	}
227
228	if (_asl_global.rc_change_token >= 0) notify_cancel(_asl_global.rc_change_token);
229	_asl_global.rc_change_token = -1;
230
231	if (_asl_global.master_token >= 0) notify_cancel(_asl_global.master_token);
232	_asl_global.master_token = -1;
233
234	if (_asl_global.notify_token >= 0) notify_cancel(_asl_global.notify_token);
235	_asl_global.notify_token = -1;
236
237	pthread_mutex_unlock(&_asl_global.lock);
238}
239#endif
240
241static void
242_asl_global_init(int reset)
243{
244	_asl_global.server_port = asl_core_get_service_port(reset);
245}
246
247#pragma mark -
248#pragma mark asl_client
249
250asl_object_t
251asl_open(const char *ident, const char *facility, uint32_t opts)
252{
253	asl_client_t *asl = asl_client_open(ident, facility, opts);
254	if (asl == NULL) return NULL;
255
256	_asl_global_init(0);
257	if (!(opts & ASL_OPT_NO_REMOTE)) _asl_notify_open(1);
258
259	return (asl_object_t)asl;
260}
261
262asl_object_t
263asl_open_from_file(int fd, const char *ident, const char *facility)
264{
265	return (asl_object_t)asl_client_open_from_file(fd, ident, facility);
266}
267
268void
269asl_close(asl_object_t obj)
270{
271	asl_release(obj);
272}
273
274__private_extern__ asl_client_t *
275_asl_open_default()
276{
277	static dispatch_once_t once;
278
279	dispatch_once(&once, ^{
280		/*
281		 * Do a sleight-of-hand with ASL_OPT_NO_REMOTE to avoid a deadlock
282		 * since asl_open(xxx, yyy, 0) calls _asl_notify_open(1)
283		 * which locks _asl_global.lock.
284		 */
285		_asl_global.asl = (asl_client_t *)asl_open(NULL, NULL, ASL_OPT_NO_REMOTE);
286
287		/* Reset options to clear ASL_OPT_NO_REMOTE bit */
288		if (_asl_global.asl != NULL) _asl_global.asl->options = 0;
289
290		/* Now call _asl_notify_open(0) to finish the work */
291		_asl_notify_open(0);
292	});
293
294	return _asl_global.asl;
295}
296
297/*
298 * asl_add_file: write log messages to the given file descriptor
299 * Log messages will be written to this file as well as to the server.
300 */
301int
302asl_add_output_file(asl_object_t client, int fd, const char *mfmt, const char *tfmt, int filter, int text_encoding)
303{
304	int status, use_global_lock = 0;
305	asl_client_t *asl;
306
307	if ((client != NULL) && (asl_get_type(client) != ASL_TYPE_CLIENT)) return -1;
308
309	asl = (asl_client_t *)client;
310	if (asl == NULL)
311	{
312		asl = _asl_open_default();
313		if (asl == NULL) return -1;
314		pthread_mutex_lock(&_asl_global.lock);
315		use_global_lock = 1;
316	}
317
318	status = asl_client_add_output_file(asl, fd, mfmt, tfmt, filter, text_encoding);
319
320	if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
321	return (status == ASL_STATUS_OK) ? 0 : -1;
322}
323
324/* returns previous filter value or -1 on error */
325int
326asl_set_output_file_filter(asl_object_t client, int fd, int filter)
327{
328	uint32_t last;
329	int use_global_lock = 0;
330	asl_client_t *asl;
331
332	if ((client != NULL) && (asl_get_type(client) != ASL_TYPE_CLIENT)) return -1;
333
334	asl = (asl_client_t *)client;
335	if (asl == NULL)
336	{
337		asl = _asl_open_default();
338		if (asl == NULL) return -1;
339		pthread_mutex_lock(&_asl_global.lock);
340		use_global_lock = 1;
341	}
342
343	last = asl_client_set_output_file_filter(asl, fd, filter);
344
345	if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
346	return last;
347}
348
349/* SPI - Deprecated */
350int
351asl_add_output(asl_object_t client, int fd, const char *mfmt, const char *tfmt, uint32_t text_encoding)
352{
353	return asl_add_output_file(client, fd, mfmt, tfmt, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG), text_encoding);
354}
355
356/* SPI - Deprecated */
357int
358asl_add_log_file(asl_object_t client, int fd)
359{
360	return asl_add_output_file(client, fd, ASL_MSG_FMT_STD, ASL_TIME_FMT_LCL, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG), ASL_ENCODE_SAFE);
361}
362
363/*
364 * asl_remove_output: stop writing log messages to the given file descriptor
365 */
366int
367asl_remove_output_file(asl_object_t client, int fd)
368{
369	int status, use_global_lock = 0;
370	asl_client_t *asl;
371
372	if ((client != NULL) && (asl_get_type(client) != ASL_TYPE_CLIENT)) return -1;
373
374	asl = (asl_client_t *)client;
375	if (asl == NULL)
376	{
377		asl = _asl_open_default();
378		if (asl == NULL) return -1;
379		pthread_mutex_lock(&_asl_global.lock);
380		use_global_lock = 1;
381	}
382
383	status = asl_client_remove_output_file(asl, fd);
384
385	if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
386	return (status == ASL_STATUS_OK) ? 0 : -1;
387}
388
389int
390asl_remove_output(asl_object_t client, int fd)
391{
392	return asl_remove_output_file(client, fd);
393}
394
395int
396asl_remove_log_file(asl_object_t client, int fd)
397{
398	return asl_remove_output_file(client, fd);
399}
400
401/* returns previous filter value or -1 on error */
402int
403asl_set_filter(asl_object_t client, int f)
404{
405	int last, use_global_lock = 0;
406	asl_client_t *asl;
407
408	if ((client != NULL) && (asl_get_type(client) != ASL_TYPE_CLIENT)) return -1;
409
410	asl = (asl_client_t *)client;
411	if (asl == NULL)
412	{
413		asl = _asl_open_default();
414		if (asl == NULL) return -1;
415		pthread_mutex_lock(&_asl_global.lock);
416		use_global_lock = 1;
417	}
418
419	last = asl_client_set_filter(asl, f);
420
421	if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
422	return last;
423}
424
425
426#pragma mark -
427#pragma mark message sending
428
429/*
430 * Evaluate client / message / level to determine what to do with a message.
431 * Checks filters, tunneling, and log files.  Returns EVAL_IGNORE if the message
432 * can be ignored.  Otherwise it returns the bits below, ORed with the level.
433 *
434 * EVAL_ASLFILE - will write to an asl file (see asl_open_from_file)
435 * EVAL_SEND - will send to syslogd
436 * EVAL_TUNNEL - will send to syslogd with tunneling enabled
437 * EVAL_FILE - will write to file
438 */
439uint32_t
440_asl_evaluate_send(asl_object_t client, asl_object_t m, int slevel)
441{
442	asl_client_t *asl;
443	asl_msg_t *msg = (asl_msg_t *)m;
444	uint32_t level, lmask, filter, status, tunnel;
445	int check;
446	uint64_t v64;
447	const char *val;
448
449	level = ASL_LEVEL_DEBUG;
450	if (slevel >= 0) level = slevel;
451
452	val = NULL;
453	if ((asl_msg_lookup(msg, ASL_KEY_LEVEL, &val, NULL) == 0) && (val != NULL)) level = atoi(val);
454
455	if (level < ASL_LEVEL_EMERG) level = ASL_LEVEL_EMERG;
456	else if (level > ASL_LEVEL_DEBUG) level = ASL_LEVEL_DEBUG;
457
458	if ((client != NULL) && (asl_get_type(client) != ASL_TYPE_CLIENT))
459	{
460		/* sending to something other than a client */
461		return (level | EVAL_SEND);
462	}
463
464	asl = (asl_client_t *)client;
465	if (asl == NULL)
466	{
467		asl = _asl_open_default();
468		if (asl == NULL) return EVAL_IGNORE;
469	}
470
471	if (asl->aslfile != NULL) return (level | EVAL_ASLFILE);
472
473	lmask = ASL_FILTER_MASK(level);
474
475	filter = asl->filter & 0xff;
476	tunnel = (asl->filter & ASL_FILTER_MASK_TUNNEL) >> 8;
477
478	if (!(asl->options & ASL_OPT_NO_REMOTE))
479	{
480		pthread_mutex_lock(&_asl_global.lock);
481
482		if (_asl_global.rc_change_token >= 0)
483		{
484			/* initialize or re-check process-specific and master filters  */
485			check = 0;
486			status = notify_check(_asl_global.rc_change_token, &check);
487			if ((status == NOTIFY_STATUS_OK) && (check != 0))
488			{
489				if (_asl_global.master_token >= 0)
490				{
491					v64 = 0;
492					status = notify_get_state(_asl_global.master_token, &v64);
493					if (status == NOTIFY_STATUS_OK) _asl_global.master_filter = v64;
494				}
495
496				if (_asl_global.notify_token >= 0)
497				{
498					v64 = 0;
499					status = notify_get_state(_asl_global.notify_token, &v64);
500					if (status == NOTIFY_STATUS_OK) _asl_global.proc_filter = v64;
501				}
502			}
503		}
504
505		pthread_mutex_unlock(&_asl_global.lock);
506		/* master filter overrides local filter */
507		if (_asl_global.master_filter != 0)
508		{
509			filter = _asl_global.master_filter;
510			tunnel = 1;
511		}
512
513		/* process-specific filter overrides local and master */
514		if (_asl_global.proc_filter != 0)
515		{
516			filter = _asl_global.proc_filter;
517			tunnel = 1;
518		}
519	}
520
521	if ((filter != 0) && ((filter & lmask) != 0))
522	{
523		level |= EVAL_SEND;
524		if (tunnel != 0) level |= EVAL_TUNNEL;
525		if (asl->out_count > 0) level |= EVAL_FILE;
526
527		return level;
528	}
529
530	if ((asl->options & ASL_OPT_SYSLOG_LEGACY) && (filter != 0) && ((filter & lmask) == 0))
531	{
532		return EVAL_IGNORE;
533	}
534
535	if (asl->out_count > 0) return (level | EVAL_FILE);
536
537	return EVAL_IGNORE;
538}
539
540/*
541 * _asl_lib_vlog
542 * Internal routine used by asl_vlog.
543 * msg:  an asl messsage
544 * eval: log level and send flags for the message
545 * format: A formating string
546 * ap: va_list for the format
547 * returns 0 for success, non-zero for failure
548 */
549static ASL_STATUS
550_asl_lib_vlog(asl_object_t obj, uint32_t eval, asl_object_t msg, const char *format, va_list ap)
551{
552	int saved_errno = errno;
553	int status;
554	char *str, *fmt, estr[NL_TEXTMAX];
555	uint32_t i, len, elen, expand;
556
557	if (format == NULL) return ASL_STATUS_INVALID_ARG;
558
559	/* insert strerror for %m */
560	len = 0;
561	elen = 0;
562
563	expand = 0;
564	for (i = 0; format[i] != '\0'; i++)
565	{
566		if (format[i] == '%')
567		{
568			if (format[i+1] == '\0') len++;
569			else if (format[i+1] == 'm')
570			{
571				expand = 1;
572				strerror_r(saved_errno, estr, sizeof(estr));
573				elen = strlen(estr);
574				len += elen;
575				i++;
576			}
577			else
578			{
579				len += 2;
580				i++;
581			}
582		}
583		else len++;
584	}
585
586	fmt = (char *)format;
587
588	if (expand != 0)
589	{
590		fmt = malloc(len + 1);
591		if (fmt == NULL)
592		{
593			if (estr != NULL) free(estr);
594			return ASL_STATUS_NO_MEMORY;
595		}
596
597		len = 0;
598
599		for (i = 0; format[i] != '\0'; i++)
600		{
601			if (format[i] == '%')
602			{
603				if (format[i+1] == '\0')
604				{
605				}
606				else if ((format[i+1] == 'm') && (elen != 0))
607				{
608					memcpy(fmt+len, estr, elen);
609					len += elen;
610					i++;
611				}
612				else
613				{
614					fmt[len++] = format[i++];
615					fmt[len++] = format[i];
616				}
617			}
618			else fmt[len++] = format[i];
619		}
620
621		fmt[len] = '\0';
622	}
623
624	str = NULL;
625	vasprintf(&str, fmt, ap);
626	if (expand != 0) free(fmt);
627
628	if (str == NULL) return ASL_STATUS_NO_MEMORY;
629
630	status = _asl_send_message(obj, eval, (asl_msg_t *)msg, str);
631	free(str);
632
633	return status;
634}
635
636/*
637 * asl_vlog
638 * Similar to asl_log, but take a va_list instead of a list of arguments.
639 * msg:  an asl message
640 * level: the log level of the associated message
641 * format: A formating string
642 * ap: va_list for the format
643 * returns 0 for success, non-zero for failure
644 */
645int
646asl_vlog(asl_object_t client, asl_object_t msg, int level, const char *format, va_list ap)
647{
648	uint32_t eval = _asl_evaluate_send(client, msg, level);
649	if (eval == EVAL_IGNORE) return 0;
650
651	ASL_STATUS status = _asl_lib_vlog(client, eval, msg, format, ap);
652	return (status == ASL_STATUS_OK) ? 0 : -1;
653}
654
655/*
656 * _asl_lib_log
657 * SPI used by ASL_PREFILTER_LOG. Converts format arguments to a va_list and
658 * forwards the call to _asl_lib_vlog.
659 * msg:  an asl message
660 * eval: log level and send flags for the message
661 * format: A formating string
662 * ... args for format
663 * returns 0 for success, non-zero for failure
664 */
665int
666_asl_lib_log(asl_object_t client, uint32_t eval, asl_object_t msg, const char *format, ...)
667{
668	int status;
669	if (eval == EVAL_IGNORE) return 0;
670
671	va_list ap;
672	va_start(ap, format);
673	status = _asl_lib_vlog(client, eval, msg, format, ap);
674	va_end(ap);
675
676	return status;
677}
678
679/*
680 * asl_log
681 * Processes an ASL log message.
682 * msg:  an asl message
683 * level: the log level of the associated message
684 * format: A formating string
685 * ... args for format
686 * returns 0 for success, non-zero for failure
687 */
688int
689asl_log(asl_object_t client, asl_object_t msg, int level, const char *format, ...)
690{
691	ASL_STATUS status;
692	uint32_t eval = _asl_evaluate_send(client, msg, level);
693	if (eval == EVAL_IGNORE) return 0;
694
695	va_list ap;
696	va_start(ap, format);
697	status = _asl_lib_vlog(client, eval, msg, format, ap);
698	va_end(ap);
699
700	return (status == ASL_STATUS_OK) ? 0 : -1;
701}
702
703/*
704 * asl_log_message
705 * Like asl_log, supplies NULL client and msg.
706 * level: the log level of the associated message
707 * format: A formating string
708 * ... args for format
709 * returns 0 for success, non-zero for failure
710 */
711int
712asl_log_message(int level, const char *format, ...)
713{
714	int status;
715	uint32_t eval = _asl_evaluate_send(NULL, NULL, level);
716	if (eval == EVAL_IGNORE) return 0;
717
718	va_list ap;
719	va_start(ap, format);
720	status = _asl_lib_vlog(NULL, eval, NULL, format, ap);
721	va_end(ap);
722
723	return (status == ASL_STATUS_OK) ? 0 : -1;
724}
725
726/*
727 * asl_get_filter: gets the values for the local, master, and remote filters,
728 * and indicates which one is active.
729 */
730int
731asl_get_filter(asl_object_t client, int *local, int *master, int *remote, int *active)
732{
733	asl_client_t *asl, *asl_default;
734	int l, m, r, x;
735	int status, check;
736	uint64_t v64;
737
738	if ((client != NULL) && (asl_get_type(client) != ASL_TYPE_CLIENT)) return -1;
739
740	l = 0;
741	m = 0;
742	r = 0;
743	x = 0;
744
745	asl_default = _asl_open_default();
746
747	asl = (asl_client_t *)client;
748	if (asl == NULL) asl = asl_default;
749	if (asl != NULL) l = asl->filter & 0xff;
750
751	if ((asl_default != NULL) && (!(asl_default->options & ASL_OPT_NO_REMOTE)))
752	{
753		pthread_mutex_lock(&_asl_global.lock);
754
755		if (_asl_global.rc_change_token >= 0)
756		{
757			/* initialize or re-check process-specific and master filters  */
758			check = 0;
759			status = notify_check(_asl_global.rc_change_token, &check);
760			if ((status == NOTIFY_STATUS_OK) && (check != 0))
761			{
762				if (_asl_global.master_token >= 0)
763				{
764					v64 = 0;
765					status = notify_get_state(_asl_global.master_token, &v64);
766					if (status == NOTIFY_STATUS_OK) _asl_global.master_filter = v64;
767				}
768
769				if (_asl_global.notify_token >= 0)
770				{
771					v64 = 0;
772					status = notify_get_state(_asl_global.notify_token, &v64);
773					if (status == NOTIFY_STATUS_OK) _asl_global.proc_filter = v64;
774				}
775			}
776		}
777
778		m = _asl_global.master_filter;
779		if (m != 0) x = 1;
780
781		r = _asl_global.proc_filter;
782		if (r != 0) x = 2;
783
784		pthread_mutex_unlock(&_asl_global.lock);
785	}
786
787	if (local != NULL) *local = l;
788	if (master != NULL) *master = m;
789	if (remote != NULL) *remote = r;
790	if (active != NULL) *active = x;
791
792	return 0;
793}
794
795/*
796 * Sets Host, PID, UID, GID, and OSActivityID values in a new message.
797 * Also sets Level, Time, TimeNanoSec, Sender, Facility and Message if provided.
798 */
799asl_msg_t *
800asl_base_msg(asl_client_t *asl, uint32_t level, const struct timeval *tv, const char *sstr, const char *fstr, const char *mstr)
801{
802	char aux_val[64];
803	char aux_host[_POSIX_HOST_NAME_MAX];
804	asl_msg_t *aux;
805	int status;
806	unsigned int osacount = 1;
807	os_activity_t osaid = 0;
808
809	aux = asl_msg_new(ASL_TYPE_MSG);
810	if (aux == NULL) return NULL;
811
812	/* Level */
813	if (level <= 7) asl_msg_set_key_val(aux, ASL_KEY_LEVEL, level_to_number_string[level]);
814
815	/* Time and TimeNanoSec */
816	if (tv != NULL)
817	{
818		snprintf(aux_val, sizeof(aux_val), "%lu", tv->tv_sec);
819		asl_msg_set_key_val(aux, ASL_KEY_TIME, aux_val);
820
821		snprintf(aux_val, sizeof(aux_val), "%d", tv->tv_usec * 1000);
822		asl_msg_set_key_val(aux, ASL_KEY_TIME_NSEC, aux_val);
823	}
824
825	/* Message */
826	if (mstr != NULL) asl_msg_set_key_val(aux, ASL_KEY_MSG, mstr);
827
828	/* Host */
829	memset(&aux_host, 0, _POSIX_HOST_NAME_MAX);
830	if (gethostname(aux_host, _POSIX_HOST_NAME_MAX) == 0) asl_msg_set_key_val(aux, ASL_KEY_HOST, aux_host);
831
832	/* PID */
833	snprintf(aux_val, sizeof(aux_val), "%u", getpid());
834	asl_msg_set_key_val(aux, ASL_KEY_PID, aux_val);
835
836	/* UID */
837	snprintf(aux_val, sizeof(aux_val), "%d", getuid());
838	asl_msg_set_key_val(aux, ASL_KEY_UID, aux_val);
839
840	/* GID */
841	snprintf(aux_val, sizeof(aux_val), "%d", getgid());
842	asl_msg_set_key_val(aux, ASL_KEY_GID, aux_val);
843
844	/* OSActivityID */
845	if (os_activity_get_active(&osaid, &osacount) == 1)
846	{
847		snprintf(aux_val, sizeof(aux_val), "0x%016llx", (uint64_t)osaid);
848		asl_msg_set_key_val(aux, ASL_KEY_OS_ACTIVITY_ID, aux_val);
849	}
850
851	/* Sender */
852	if (sstr == NULL)
853	{
854		/* See if the client has a value for ASL_KEY_SENDER */
855		status = asl_msg_lookup((asl_msg_t *)asl->kvdict, ASL_KEY_SENDER, &sstr, NULL);
856		if ((status != 0) || (sstr == NULL))
857		{
858			sstr = NULL;
859
860			/* See if the global cache has a value for ASL_KEY_SENDER */
861			if (_asl_global.sender == NULL)
862			{
863				/* Get the process name with _NSGetArgv */
864				char *name = *(*_NSGetArgv());
865				if (name != NULL)
866				{
867					char *x = strrchr(name, '/');
868					if (x != NULL) x++;
869					else x = name;
870
871					/* Set the cache value */
872					pthread_mutex_lock(&_asl_global.lock);
873					if (_asl_global.sender == NULL) _asl_global.sender = strdup(x);
874					pthread_mutex_unlock(&_asl_global.lock);
875				}
876			}
877
878			if (_asl_global.sender != NULL) asl_msg_set_key_val(aux, ASL_KEY_SENDER, _asl_global.sender);
879			else asl_msg_set_key_val(aux, ASL_KEY_SENDER, "Unknown");
880		}
881	}
882
883	if (sstr != NULL) asl_msg_set_key_val(aux, ASL_KEY_SENDER, sstr);
884
885	/* Facility */
886	if (fstr == NULL)
887	{
888		status = asl_msg_lookup((asl_msg_t *)asl->kvdict, ASL_KEY_FACILITY, &fstr, NULL);
889		if (status != 0) fstr = NULL;
890	}
891
892	if (fstr != NULL) asl_msg_set_key_val(aux, ASL_KEY_FACILITY, fstr);
893
894	return aux;
895}
896
897#ifdef NOTDEF
898/*
899 * Possibly useful someday...
900 */
901asl_msg_t *
902asl_prepared_message(asl_client_t *asl, asl_msg_t *msg)
903{
904	uint32_t i, len, level, outstatus;
905	const char *val, *sstr, *fstr;
906	struct timeval tval = {0, 0};
907	int status;
908	asl_msg_t *out;
909
910	if (asl == NULL)
911	{
912		asl = _asl_open_default();
913		if (asl == NULL) return NULL;
914	}
915
916	status = gettimeofday(&tval, NULL);
917	if (status != 0)
918	{
919		time_t tick = time(NULL);
920		tval.tv_sec = tick;
921		tval.tv_usec = 0;
922	}
923
924	val = NULL;
925	status = asl_msg_lookup(msg, ASL_KEY_LEVEL, &val, NULL);
926	if (status != 0) val = NULL;
927
928	level = ASL_LEVEL_DEBUG;
929	if (val != NULL) level = atoi(val);
930	if (level > ASL_LEVEL_DEBUG) level = ASL_LEVEL_DEBUG;
931
932	sstr = NULL;
933	status = asl_msg_lookup(msg, ASL_KEY_SENDER, &sstr, NULL);
934	if (status != 0) sstr = NULL;
935
936	fstr = NULL;
937	status = asl_msg_lookup(msg, ASL_KEY_FACILITY, &fstr, NULL);
938	if (status != 0) fstr = NULL;
939
940	out = asl_base_msg(asl, level, &tval, sstr, fstr, NULL);
941	out = asl_msg_merge(out, msg);
942
943	return out;
944}
945#endif
946
947static ASL_STATUS
948_asl_send_message(asl_object_t obj, uint32_t eval, asl_msg_t *msg, const char *mstr)
949{
950	uint32_t i, len, level, lmask, outstatus, objtype;
951	const char *sstr, *fstr;
952	struct timeval tval = {0, 0};
953	int status;
954	int use_global_lock = 0;
955	kern_return_t kstatus;
956	asl_msg_t *sendmsg;
957	asl_msg_t *qd_msg = NULL;
958	asl_client_t *asl = NULL;
959	static dispatch_once_t noquota_once;
960
961	if (eval == EVAL_IGNORE) return ASL_STATUS_OK;
962
963	if (obj == NULL)
964	{
965		asl = _asl_open_default();
966		if (asl == NULL) return ASL_STATUS_FAILED;
967		use_global_lock = 1;
968		objtype = ASL_TYPE_CLIENT;
969	}
970	else
971	{
972		objtype = asl_get_type(obj);
973		if (objtype == ASL_TYPE_CLIENT) asl = (asl_client_t *)obj;
974		else asl = _asl_open_default();
975	}
976
977	level = eval & LEVEL_MASK;
978	if (level > 7) level = 7;
979	eval &= EVAL_MASK;
980	lmask = ASL_FILTER_MASK(level);
981
982	if ((objtype == ASL_TYPE_CLIENT) && (asl->aslfile != NULL)) use_global_lock = 1;
983
984	status = gettimeofday(&tval, NULL);
985	if (status != 0)
986	{
987		time_t tick = time(NULL);
988		tval.tv_sec = tick;
989		tval.tv_usec = 0;
990	}
991
992	sstr = NULL;
993	status = asl_msg_lookup(msg, ASL_KEY_SENDER, &sstr, NULL);
994	if (status != 0) sstr = NULL;
995
996	fstr = NULL;
997	status = asl_msg_lookup(msg, ASL_KEY_FACILITY, &fstr, NULL);
998	if (status != 0) fstr = NULL;
999
1000	sendmsg = asl_base_msg(asl, level, &tval, sstr, fstr, mstr);
1001	if (sendmsg == NULL) return ASL_STATUS_FAILED;
1002
1003	/* Set "ASLOption store" if tunneling */
1004	if (eval & EVAL_TUNNEL)
1005	{
1006		const char *val = NULL;
1007		status = asl_msg_lookup(msg, ASL_KEY_OPTION, &val, NULL);
1008		if ((status != 0) || (val == NULL))
1009		{
1010			asl_msg_set_key_val(sendmsg, ASL_KEY_OPTION, ASL_OPT_STORE);
1011		}
1012		else
1013		{
1014			char *option = NULL;
1015			asprintf(&option, "%s %s", ASL_OPT_STORE, val);
1016			asl_msg_set_key_val(sendmsg, ASL_KEY_OPTION, option);
1017			free(option);
1018		}
1019	}
1020
1021	outstatus = -1;
1022
1023	if (use_global_lock != 0) pthread_mutex_lock(&_asl_global.lock);
1024
1025	sendmsg = asl_msg_merge(sendmsg, msg);
1026
1027	if (objtype != ASL_TYPE_CLIENT)
1028	{
1029		asl_append(obj, (asl_object_t)sendmsg);
1030		asl_msg_release(sendmsg);
1031		if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
1032		return ASL_STATUS_OK;
1033	}
1034
1035	/*
1036	 * If there is an aslfile this is a stand-alone file client.
1037	 * Just save to the file.
1038	 */
1039	if (asl->aslfile != NULL)
1040	{
1041		outstatus = ASL_STATUS_FAILED;
1042
1043		if (sendmsg != NULL)
1044		{
1045			outstatus = asl_file_save(asl->aslfile, sendmsg, &(asl->aslfileid));
1046			asl->aslfileid++;
1047		}
1048
1049		asl_msg_release(sendmsg);
1050
1051		if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
1052		return outstatus;
1053	}
1054
1055	_asl_global_init(0);
1056	outstatus = 0;
1057
1058	/*
1059	 * ASL message quota
1060	 * Quotas are disabled if:
1061	 * - a remote control filter is in place (EVAL_TUNNEL)
1062	 * - Environment variable ASL_QUOTA_DISABLED == 1
1063	 * - /etc/asl/.noquota existed at the time that the process started
1064	 *
1065	 * Note that we just check /etc/asl/.noquota once, since it would be
1066	 * expensive to stat() for every log message.
1067	 */
1068
1069	dispatch_once(&noquota_once, ^{
1070		struct stat sb;
1071		memset(&sb, 0, sizeof(struct stat));
1072		if (stat(NOQUOTA_FILE_PATH, &sb) == 0) _asl_global.quota = UINT32_MAX;
1073	});
1074
1075	if (_asl_global.quota != UINT32_MAX)
1076	{
1077		const char *qtest = getenv(NOQUOTA_ENV);
1078		if ((qtest != NULL) && (!strcmp(qtest, "1")))
1079		{
1080			_asl_global.quota = UINT32_MAX;
1081
1082			qd_msg = asl_base_msg(asl, QUOTA_LEVEL, &tval, sstr, fstr, QUOTA_DISABLED_MSG);
1083			asl_msg_set_key_val(qd_msg, ASL_KEY_OPTION, ASL_OPT_STORE);
1084		}
1085	}
1086
1087	if (((eval & EVAL_TUNNEL) == 0) && (_asl_global.quota != UINT32_MAX))
1088	{
1089		time_t last_send = _asl_global.last_send;
1090		time_t last_oq = _asl_global.last_oq_msg;
1091		uint32_t qcurr = _asl_global.quota;
1092		time_t delta;
1093		uint32_t qinc, qnew;
1094
1095		qnew = qcurr;
1096
1097		/* add QUOTA_MPS to quota for each second we've been idle */
1098		if (tval.tv_sec > last_send)
1099		{
1100			delta = tval.tv_sec - last_send;
1101
1102			qinc = QUOTA_MPH;
1103			if (delta < (QUOTA_MPH / QUOTA_MPS)) qinc = delta * QUOTA_MPS;
1104
1105			qnew = MIN(QUOTA_MPH, qcurr + qinc);
1106			OSAtomicCompareAndSwapLongBarrier(last_send, tval.tv_sec, (long *)&_asl_global.last_send);
1107		}
1108
1109		if (qnew == 0)
1110		{
1111			if ((tval.tv_sec - last_oq) > QUOTA_MSG_INTERVAL)
1112			{
1113				eval |= EVAL_QUOTA;
1114				OSAtomicCompareAndSwapLongBarrier(last_oq, tval.tv_sec, (long *)&_asl_global.last_oq_msg);
1115			}
1116			else
1117			{
1118				eval &= ~EVAL_SEND;
1119			}
1120		}
1121		else
1122		{
1123			OSAtomicCompareAndSwap32Barrier(qcurr, qnew - 1, (int32_t *)&_asl_global.quota);
1124		}
1125	}
1126
1127	if ((_asl_global.server_port != MACH_PORT_NULL) && (eval & EVAL_SEND))
1128	{
1129		asl_string_t *send_str;
1130		const char *str;
1131		size_t vmsize;
1132
1133		if (eval & EVAL_QUOTA)
1134		{
1135			asl_msg_set_key_val(sendmsg, ASL_KEY_LEVEL, QUOTA_LEVEL_STR);
1136			asl_msg_set_key_val(sendmsg, ASL_KEY_MSG, QUOTA_MSG);
1137		}
1138
1139		if (qd_msg != NULL)
1140		{
1141			send_str = asl_msg_to_string_raw(ASL_STRING_MIG, qd_msg, "raw");
1142			len = asl_string_length(send_str);
1143			vmsize = asl_string_allocated_size(send_str);
1144			str = asl_string_release_return_bytes(send_str);
1145			if (len != 0) kstatus = _asl_server_message(_asl_global.server_port, (caddr_t)str, len);
1146			if ((str != NULL) && (vmsize != 0)) vm_deallocate(mach_task_self(), (vm_address_t)str, vmsize);
1147			asl_msg_release(qd_msg);
1148		}
1149
1150		send_str = asl_msg_to_string_raw(ASL_STRING_MIG, sendmsg, "raw");
1151		len = asl_string_length(send_str);
1152		vmsize = asl_string_allocated_size(send_str);
1153		str = asl_string_release_return_bytes(send_str);
1154
1155		if (len != 0)
1156		{
1157			/* send a mach message to syslogd */
1158			kstatus = _asl_server_message(_asl_global.server_port, (caddr_t)str, len);
1159			if (kstatus != KERN_SUCCESS)
1160			{
1161				/* retry once if the call failed */
1162				_asl_global_init(1);
1163				kstatus = _asl_server_message(_asl_global.server_port, (caddr_t)str, len);
1164				if (kstatus != KERN_SUCCESS)
1165				{
1166					vm_deallocate(mach_task_self(), (vm_address_t)str, vmsize);
1167					outstatus = -1;
1168				}
1169			}
1170		}
1171		else if (vmsize >0) vm_deallocate(mach_task_self(), (vm_address_t)str, vmsize);
1172	}
1173
1174	if ((sendmsg != NULL) && (asl->out_count > 0))
1175	{
1176		/* write to file descriptors */
1177		for (i = 0; i < asl->out_count; i++)
1178		{
1179			if ((asl->out_list[i].fd >= 0) && (asl->out_list[i].filter != 0) && ((asl->out_list[i].filter & lmask) != 0))
1180			{
1181				char *str;
1182
1183				len = 0;
1184				str = asl_format_message(sendmsg, asl->out_list[i].mfmt, asl->out_list[i].tfmt, asl->out_list[i].encoding, &len);
1185				if (str == NULL) continue;
1186
1187				status = write(asl->out_list[i].fd, str, len - 1);
1188				if (status < 0)
1189				{
1190					/* soft error for fd 2 (stderr) */
1191					if (asl->out_list[i].fd != 2) outstatus = -1;
1192					asl->out_list[i].fd = -1;
1193				}
1194
1195				free(str);
1196			}
1197		}
1198	}
1199
1200	asl_msg_release(sendmsg);
1201
1202	if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
1203
1204	return outstatus;
1205}
1206
1207/*
1208 * asl_send: send a message
1209 * This routine may be used instead of asl_log() or asl_vlog() if asl_set()
1210 * has been used to set all of a message's attributes.
1211 * eval:  hints about what to do with the message
1212 * msg:  an asl message
1213 * returns 0 for success, non-zero for failure
1214 */
1215__private_extern__ ASL_STATUS
1216asl_client_internal_send(asl_object_t obj, asl_object_t msg)
1217{
1218	int status = ASL_STATUS_OK;
1219	uint32_t eval = _asl_evaluate_send(obj, msg, -1);
1220	if (eval != 0) status = _asl_send_message(obj, eval, (asl_msg_t *)msg, NULL);
1221
1222	return status;
1223}
1224
1225#pragma mark -
1226#pragma mark auxiliary files and URLs
1227
1228static ASL_STATUS
1229_asl_aux_save_context(asl_aux_context_t *ctx)
1230{
1231	if (ctx == NULL) return ASL_STATUS_FAILED;
1232
1233	pthread_mutex_lock(&_asl_global.lock);
1234
1235	_asl_global.aux_ctx = (asl_aux_context_t **)reallocf(_asl_global.aux_ctx, (_asl_global.aux_count + 1) * sizeof(asl_aux_context_t *));
1236	if (_asl_global.aux_ctx == NULL)
1237	{
1238		_asl_global.aux_count = 0;
1239		pthread_mutex_unlock(&_asl_global.lock);
1240		return ASL_STATUS_FAILED;
1241	}
1242
1243	_asl_global.aux_ctx[_asl_global.aux_count++] = ctx;
1244
1245	pthread_mutex_unlock(&_asl_global.lock);
1246
1247	return ASL_STATUS_OK;
1248}
1249
1250/*
1251 * Creates an auxiliary file that may be used to save arbitrary data.  The ASL message msg
1252 * will be saved at the time that the auxiliary file is created.  The message will include
1253 * any keys and values found in msg, and it will include the title and Uniform Type
1254 * Identifier specified.  Output parameter out_fd will contain the file descriptor of the
1255 * new auxiliary file.
1256 */
1257static ASL_STATUS
1258_asl_auxiliary(asl_msg_t *msg, const char *title, const char *uti, const char *url, int *out_fd)
1259{
1260	asl_msg_t *aux;
1261	asl_string_t *send_str;
1262	const char *str;
1263	fileport_t fileport;
1264	kern_return_t kstatus;
1265	size_t len, vmsize;
1266	uint32_t newurllen, where;
1267	int status, fd, fdpair[2];
1268	caddr_t newurl;
1269	dispatch_queue_t pipe_q;
1270	dispatch_io_t pipe_channel;
1271	dispatch_semaphore_t sem;
1272
1273	aux = asl_msg_new(ASL_TYPE_MSG);
1274
1275	if (url != NULL) asl_msg_set_key_val(aux, ASL_KEY_AUX_URL, url);
1276	if (title != NULL) asl_msg_set_key_val(aux, ASL_KEY_AUX_TITLE, title);
1277	if (uti == NULL) asl_msg_set_key_val(aux, ASL_KEY_AUX_UTI, "public.data");
1278	else asl_msg_set_key_val(aux, ASL_KEY_AUX_UTI, uti);
1279
1280	aux = asl_msg_merge(aux, msg);
1281
1282	/* if (out_fd == NULL), this is from asl_log_auxiliary_location */
1283	if (out_fd == NULL)
1284	{
1285		uint32_t eval = _asl_evaluate_send(NULL, (asl_object_t)aux, -1);
1286		status = _asl_send_message(NULL, eval, aux, NULL);
1287		asl_msg_release(aux);
1288		return status;
1289	}
1290
1291	where = asl_store_location();
1292	if (where == ASL_STORE_LOCATION_MEMORY)
1293	{
1294		/* create a pipe */
1295		asl_aux_context_t *ctx = (asl_aux_context_t *)calloc(1, sizeof(asl_aux_context_t));
1296		if (ctx == NULL) return ASL_STATUS_FAILED;
1297
1298		status = pipe(fdpair);
1299		if (status < 0)
1300		{
1301			free(ctx);
1302			return ASL_STATUS_FAILED;
1303		}
1304
1305		/* give read end to dispatch_io_read */
1306		fd = fdpair[0];
1307		sem = dispatch_semaphore_create(0);
1308		ctx->sem = sem;
1309		ctx->fd = fdpair[1];
1310
1311		status = _asl_aux_save_context(ctx);
1312		if (status != ASL_STATUS_OK)
1313		{
1314			close(fdpair[0]);
1315			close(fdpair[1]);
1316			dispatch_release(sem);
1317			free(ctx);
1318			return ASL_STATUS_FAILED;
1319		}
1320
1321		pipe_q = dispatch_queue_create("ASL_AUX_PIPE_Q", NULL);
1322		pipe_channel = dispatch_io_create(DISPATCH_IO_STREAM, fd, pipe_q, ^(int err){
1323			close(fd);
1324		});
1325
1326		*out_fd = fdpair[1];
1327
1328		dispatch_io_set_low_water(pipe_channel, SIZE_MAX);
1329
1330		dispatch_io_read(pipe_channel, 0, SIZE_MAX, pipe_q, ^(bool done, dispatch_data_t pipedata, int err){
1331			if (err == 0)
1332			{
1333				size_t len = dispatch_data_get_size(pipedata);
1334				if (len > 0)
1335				{
1336					const char *bytes = NULL;
1337					char *encoded;
1338					uint32_t eval;
1339
1340					dispatch_data_t md = dispatch_data_create_map(pipedata, (const void **)&bytes, &len);
1341					encoded = asl_core_encode_buffer(bytes, len);
1342					asl_msg_set_key_val(aux, ASL_KEY_AUX_DATA, encoded);
1343					free(encoded);
1344					eval = _asl_evaluate_send(NULL, (asl_object_t)aux, -1);
1345					_asl_send_message(NULL, eval, aux, NULL);
1346					asl_msg_release(aux);
1347					dispatch_release(md);
1348				}
1349			}
1350
1351			if (done)
1352			{
1353				dispatch_semaphore_signal(sem);
1354				dispatch_release(pipe_channel);
1355				dispatch_release(pipe_q);
1356			}
1357		});
1358
1359		return ASL_STATUS_OK;
1360	}
1361
1362	_asl_global_init(0);
1363	if (_asl_global.server_port == MACH_PORT_NULL) return ASL_STATUS_FAILED;
1364
1365	send_str = asl_msg_to_string_raw(ASL_STRING_MIG, aux, "raw");
1366	len = asl_string_length(send_str);
1367	vmsize = asl_string_allocated_size(send_str);
1368	str = asl_string_release_return_bytes(send_str);
1369
1370	if (len == 0)
1371	{
1372		asl_msg_release(aux);
1373		vm_deallocate(mach_task_self(), (vm_address_t)str, vmsize);
1374		return ASL_STATUS_FAILED;
1375	}
1376
1377	status = 0;
1378	fileport = MACH_PORT_NULL;
1379	status = KERN_SUCCESS;
1380
1381	kstatus = _asl_server_create_aux_link(_asl_global.server_port, (caddr_t)str, len, &fileport, &newurl, &newurllen, &status);
1382	if (kstatus != KERN_SUCCESS)
1383	{
1384		/* retry once if the call failed */
1385		_asl_global_init(1);
1386		kstatus = _asl_server_create_aux_link(_asl_global.server_port, (caddr_t)str, len, &fileport, &newurl, &newurllen, &status);
1387		if (kstatus != KERN_SUCCESS)
1388		{
1389			vm_deallocate(mach_task_self(), (vm_address_t)str, vmsize);
1390			asl_msg_release(aux);
1391			return ASL_STATUS_FAILED;
1392		}
1393	}
1394
1395	if (status != 0)
1396	{
1397		asl_msg_release(aux);
1398		return status;
1399	}
1400
1401	if (newurl != NULL)
1402	{
1403		asl_msg_set_key_val(aux, ASL_KEY_AUX_URL, newurl);
1404		vm_deallocate(mach_task_self(), (vm_address_t)newurl, newurllen);
1405	}
1406
1407	if (fileport == MACH_PORT_NULL)
1408	{
1409		asl_msg_release(aux);
1410		return ASL_STATUS_FAILED;
1411	}
1412
1413	fd = fileport_makefd(fileport);
1414	mach_port_deallocate(mach_task_self(), fileport);
1415	if (fd < 0)
1416	{
1417		asl_msg_release(aux);
1418		status = -1;
1419	}
1420	else
1421	{
1422		asl_aux_context_t *ctx = (asl_aux_context_t *)calloc(1, sizeof(asl_aux_context_t));
1423		if (ctx == NULL)
1424		{
1425			status = -1;
1426		}
1427		else
1428		{
1429			*out_fd = fd;
1430
1431			ctx->fd = fd;
1432			ctx->msg = aux;
1433
1434			status = _asl_aux_save_context(ctx);
1435		}
1436	}
1437
1438	return status;
1439}
1440
1441int
1442asl_create_auxiliary_file(asl_object_t msg, const char *title, const char *uti, int *out_fd)
1443{
1444	if (out_fd == NULL) return -1;
1445
1446	ASL_STATUS status = _asl_auxiliary((asl_msg_t *)msg, title, uti, NULL, out_fd);
1447	return (status == ASL_STATUS_OK) ? 0 : -1;
1448}
1449
1450int
1451asl_log_auxiliary_location(asl_object_t msg, const char *title, const char *uti, const char *url)
1452{
1453	ASL_STATUS status = _asl_auxiliary((asl_msg_t *)msg, title, uti, url, NULL);
1454	return (status == ASL_STATUS_OK) ? 0 : -1;
1455}
1456
1457/*
1458 * Close an auxiliary file.
1459 * Sends the cached auxiliary message to syslogd.
1460 * Returns 0 on success, -1 on error.
1461 */
1462int
1463asl_close_auxiliary_file(int fd)
1464{
1465	int i, j, status;
1466	asl_msg_t *aux_msg;
1467	dispatch_semaphore_t aux_sem = NULL;
1468
1469	pthread_mutex_lock(&(_asl_global.lock));
1470
1471	aux_msg = NULL;
1472	status = -1;
1473
1474	for (i = 0; i < _asl_global.aux_count; i++)
1475	{
1476		if (_asl_global.aux_ctx[i]->fd == fd)
1477		{
1478			status = 0;
1479
1480			aux_msg = _asl_global.aux_ctx[i]->msg;
1481			aux_sem = _asl_global.aux_ctx[i]->sem;
1482
1483			free(_asl_global.aux_ctx[i]);
1484
1485			for (j = i + 1; j < _asl_global.aux_count; i++, j++)
1486			{
1487				_asl_global.aux_ctx[i] = _asl_global.aux_ctx[j];
1488			}
1489
1490			_asl_global.aux_count--;
1491
1492			if (_asl_global.aux_count == 0)
1493			{
1494				free(_asl_global.aux_ctx);
1495				_asl_global.aux_ctx = NULL;
1496			}
1497			else
1498			{
1499				_asl_global.aux_ctx = (asl_aux_context_t **)reallocf(_asl_global.aux_ctx, _asl_global.aux_count * sizeof(asl_aux_context_t *));
1500				if (_asl_global.aux_ctx == NULL)
1501				{
1502					_asl_global.aux_count = 0;
1503					status = -1;
1504				}
1505			}
1506
1507			break;
1508		}
1509	}
1510
1511	pthread_mutex_unlock(&(_asl_global.lock));
1512
1513	close(fd);
1514
1515	if (aux_msg != NULL)
1516	{
1517		uint32_t eval = _asl_evaluate_send(NULL, (asl_object_t)aux_msg, -1);
1518		if (_asl_send_message(NULL, eval, aux_msg, NULL) != ASL_STATUS_OK) status = -1;
1519		asl_msg_release(aux_msg);
1520	}
1521
1522	if (aux_sem != NULL)
1523	{
1524		dispatch_semaphore_wait(aux_sem, DISPATCH_TIME_FOREVER);
1525		dispatch_release(aux_sem);
1526	}
1527
1528	return status;
1529}
1530
1531#pragma mark -
1532
1533asl_msg_t *
1534_asl_server_control_query(void)
1535{
1536	asl_msg_list_t *list = NULL;
1537	char *res;
1538	uint32_t len, reslen, status;
1539	uint64_t cmax, qmin;
1540	kern_return_t kstatus;
1541	caddr_t vmstr;
1542	asl_msg_t *m = NULL;
1543	static const char ctlstr[] = "1\nQ [= ASLOption control]\n";
1544
1545	_asl_global_init(0);
1546	if (_asl_global.server_port == MACH_PORT_NULL) return NULL;
1547
1548	len = strlen(ctlstr) + 1;
1549
1550	qmin = 0;
1551	cmax = 0;
1552	res = NULL;
1553	reslen = 0;
1554
1555	kstatus = vm_allocate(mach_task_self(), (vm_address_t *)&vmstr, len, TRUE);
1556	if (kstatus != KERN_SUCCESS) return NULL;
1557
1558	memmove(vmstr, ctlstr, len);
1559
1560	status = 0;
1561	kstatus = _asl_server_query_2(_asl_global.server_port, vmstr, len, qmin, FETCH_BATCH, 0, (caddr_t *)&res, &reslen, &cmax, (int *)&status);
1562	if (kstatus != KERN_SUCCESS)
1563	{
1564		/* retry once if the call failed */
1565		_asl_global_init(1);
1566		kstatus = _asl_server_query_2(_asl_global.server_port, vmstr, len, qmin, FETCH_BATCH, 0, (caddr_t *)&res, &reslen, &cmax, (int *)&status);
1567	}
1568
1569	list = asl_msg_list_from_string(res);
1570	vm_deallocate(mach_task_self(), (vm_address_t)res, reslen);
1571
1572	if (list == NULL) return NULL;
1573	if (list->count > 0) m = asl_msg_retain(list->msg[0]);
1574	asl_msg_list_release(list);
1575	return m;
1576}
1577
1578/*
1579 * Returns ASL_STORE_LOCATION_FILE or ASL_STORE_LOCATION_MEMORY
1580 */
1581int
1582asl_store_location()
1583{
1584	kern_return_t kstatus;
1585	char *res;
1586	uint32_t reslen, status;
1587	uint64_t cmax;
1588
1589	_asl_global_init(0);
1590	if (_asl_global.server_port == MACH_PORT_NULL) return ASL_STORE_LOCATION_FILE;
1591
1592	res = NULL;
1593	reslen = 0;
1594	cmax = 0;
1595	status = ASL_STATUS_OK;
1596
1597	kstatus = _asl_server_query_2(_asl_global.server_port, NULL, 0, 0, -1, 0, (caddr_t *)&res, &reslen, &cmax, (int *)&status);
1598	if (kstatus != KERN_SUCCESS)
1599	{
1600		/* retry once if the call failed */
1601		_asl_global_init(1);
1602		kstatus = _asl_server_query_2(_asl_global.server_port, NULL, 0, 0, -1, 0, (caddr_t *)&res, &reslen, &cmax, (int *)&status);
1603	}
1604
1605	/* res should never be returned, but just to be certain we don't leak VM ... */
1606	if (res != NULL) vm_deallocate(mach_task_self(), (vm_address_t)res, reslen);
1607
1608	if (kstatus != KERN_SUCCESS) return ASL_STORE_LOCATION_FILE;
1609	if (status == ASL_STATUS_OK) return ASL_STORE_LOCATION_MEMORY;
1610	return ASL_STORE_LOCATION_FILE;
1611}
1612
1613asl_object_t
1614asl_open_path(const char *path, uint32_t opts)
1615{
1616	struct stat sb;
1617	asl_file_t *fout = NULL;
1618	asl_store_t *sout = NULL;
1619
1620	if (opts == 0) opts = ASL_OPT_OPEN_READ;
1621
1622	if (opts & ASL_OPT_OPEN_READ)
1623	{
1624		if (path == NULL)
1625		{
1626			if (asl_store_open_read(ASL_PLACE_DATABASE_DEFAULT, &sout) != ASL_STATUS_OK) return NULL;
1627			return (asl_object_t)sout;
1628		}
1629
1630		memset(&sb, 0, sizeof(struct stat));
1631		if (stat(path, &sb) < 0) return NULL;
1632
1633		if (sb.st_mode & S_IFREG)
1634		{
1635			if (asl_file_open_read(path, &fout) != ASL_STATUS_OK) return NULL;
1636			return (asl_object_t)fout;
1637		}
1638		else if (sb.st_mode & S_IFDIR)
1639		{
1640			if (asl_store_open_read(path, &sout) != ASL_STATUS_OK) return NULL;
1641			return (asl_object_t)sout;
1642		}
1643
1644		return NULL;
1645	}
1646	else if (opts & ASL_OPT_OPEN_WRITE)
1647	{
1648		if (path == NULL) return NULL;
1649
1650		memset(&sb, 0, sizeof(struct stat));
1651		if (stat(path, &sb) < 0)
1652		{
1653			if (errno != ENOENT) return NULL;
1654
1655			if (opts & ASL_OPT_CREATE_STORE)
1656			{
1657				if (asl_store_open_write(path, &sout) != ASL_STATUS_OK) return NULL;
1658				return (asl_object_t)fout;
1659			}
1660			else
1661			{
1662				if (asl_file_open_write(path, 0644, geteuid(), getegid(), &fout) != ASL_STATUS_OK) return NULL;
1663				return (asl_object_t)fout;
1664			}
1665		}
1666		else if (sb.st_mode & S_IFREG)
1667		{
1668			if (asl_file_open_write(path, 0644, geteuid(), getegid(), &fout) != ASL_STATUS_OK) return NULL;
1669			return (asl_object_t)fout;
1670		}
1671		else if (sb.st_mode & S_IFDIR)
1672		{
1673			if (asl_store_open_write(path, &sout) != ASL_STATUS_OK) return NULL;
1674			return (asl_object_t)sout;
1675		}
1676	}
1677
1678	return NULL;
1679}
1680