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