]> git.saurik.com Git - apple/syslog.git/blob - libsystem_asl.tproj/src/asl.c
syslog-267.tar.gz
[apple/syslog.git] / libsystem_asl.tproj / src / asl.c
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 */
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
93 /* notify SPI */
94 uint32_t notify_register_plain(const char *name, int *out_token);
95
96 /* fork handling in asl_fd.c */
97 extern void _asl_redirect_fork_child(void);
98
99 typedef struct
100 {
101 int fd;
102 asl_msg_t *msg;
103 dispatch_semaphore_t sem;
104 } asl_aux_context_t;
105
106 typedef 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
127 static 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 */
137 void
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 */
159 char *
160 asl_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
172 static 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
215 static 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
241 static 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
250 asl_object_t
251 asl_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
262 asl_object_t
263 asl_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
268 void
269 asl_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 */
301 int
302 asl_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 */
325 int
326 asl_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 */
350 int
351 asl_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 */
357 int
358 asl_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 */
366 int
367 asl_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
389 int
390 asl_remove_output(asl_object_t client, int fd)
391 {
392 return asl_remove_output_file(client, fd);
393 }
394
395 int
396 asl_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 */
402 int
403 asl_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 */
439 uint32_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 */
549 static 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 */
645 int
646 asl_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 */
665 int
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 */
688 int
689 asl_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 */
711 int
712 asl_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 */
730 int
731 asl_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 */
799 asl_msg_t *
800 asl_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 */
901 asl_msg_t *
902 asl_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
947 static 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
1216 asl_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
1228 static 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 */
1257 static 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
1441 int
1442 asl_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
1450 int
1451 asl_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 */
1462 int
1463 asl_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
1533 asl_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 */
1581 int
1582 asl_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
1613 asl_object_t
1614 asl_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 }