]> git.saurik.com Git - apple/syslog.git/blob - libsystem_asl.tproj/src/asl.c
027516e0590c64aa2ea46cfcda8dd08f0ecea37f
[apple/syslog.git] / libsystem_asl.tproj / src / asl.c
1 /*
2 * Copyright (c) 2004-2015 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 <os/log.h>
56 #include <os/log_private.h>
57 #include <asl_ipc.h>
58 #include <asl_client.h>
59 #include <asl_core.h>
60 #include <asl_msg.h>
61 #include <asl_msg_list.h>
62 #include <asl_store.h>
63 #include <asl_private.h>
64
65 #define forever for(;;)
66
67 #define FETCH_BATCH 256
68
69 #define EVAL_DEFAULT_ACTION (EVAL_SEND_ASL | EVAL_SEND_TRACE)
70 #define EVAL_ASL (EVAL_SEND_ASL | EVAL_TEXT_FILE | EVAL_ASL_FILE)
71
72 /*
73 * Clients get a max of 36000 messages per hour.
74 * Their quota gets refilled at a rate of 10 messages per second.
75 */
76 #define QUOTA_MPH 36000
77 #define QUOTA_MPS 10
78 #define QUOTA_MSG_INTERVAL 60
79 #define NOQUOTA_ENV "ASL_QUOTA_DISABLED"
80 #define QUOTA_DISABLED_MSG "*** MESSAGE QUOTA DISABLED FOR THIS PROCESS ***"
81 #define QUOTA_MSG "*** LOG MESSAGE QUOTA EXCEEDED - SOME MESSAGES FROM THIS PROCESS HAVE BEEN DISCARDED ***"
82 #define QUOTA_LEVEL ASL_LEVEL_CRIT
83 #define QUOTA_LEVEL_STR ASL_STRING_CRIT
84
85 /*
86 * Limit the size of messages sent to syslogd.
87 */
88 #define SIZE_LIMIT_MSG "*** ASL MESSAGE SIZE (%u bytes) EXCEEDED MAXIMIMUM SIZE (%u bytes) ***"
89
90 static const os_log_type_t shim_asl_to_log_type[8] = {
91 OS_LOG_TYPE_DEFAULT, // ASL_LEVEL_EMERG
92 OS_LOG_TYPE_DEFAULT, // ASL_LEVEL_ALERT
93 OS_LOG_TYPE_DEFAULT, // ASL_LEVEL_CRIT
94 OS_LOG_TYPE_DEFAULT, // ASL_LEVEL_ERR
95 OS_LOG_TYPE_DEFAULT, // ASL_LEVEL_WARNING
96 OS_LOG_TYPE_DEFAULT, // ASL_LEVEL_NOTICE
97 OS_LOG_TYPE_INFO, // ASL_LEVEL_INFO
98 OS_LOG_TYPE_DEBUG // ASL_LEVEL_DEBUG
99 };
100
101 /* forward */
102 static ASL_STATUS _asl_send_message(asl_object_t obj, uint32_t eval, asl_msg_t *msg, const char *mstring);
103 static ASL_STATUS _asl_send_message_text(asl_client_t *asl, asl_msg_t *sendmsg, asl_object_t obj, uint32_t eval, asl_msg_t *msg, const char *mstring, bool shimmed);
104 __private_extern__ asl_client_t *_asl_open_default();
105
106 /* notify SPI */
107 uint32_t notify_register_plain(const char *name, int *out_token);
108
109 /* fork handling in asl_fd.c */
110 extern void _asl_redirect_fork_child(void);
111
112 typedef struct
113 {
114 int fd;
115 asl_msg_t *msg;
116 dispatch_semaphore_t sem;
117 } asl_aux_context_t;
118
119 typedef struct
120 {
121 int notify_count;
122 int rc_change_token;
123 int notify_token;
124 int master_token;
125 uint64_t proc_filter;
126 uint64_t master_filter;
127 time_t last_send;
128 time_t last_oq_msg;
129 uint32_t quota;
130 dispatch_once_t port_lookup_once;
131 mach_port_t server_port;
132 char *sender;
133 pthread_mutex_t lock;
134 int aux_count;
135 asl_aux_context_t **aux_ctx;
136 asl_client_t *asl;
137 } _asl_global_t;
138
139 __private_extern__ _asl_global_t _asl_global = {0, -1, -1, -1, 0LL, 0LL, 0LL, 0LL, 0, 0, MACH_PORT_NULL, NULL, PTHREAD_MUTEX_INITIALIZER, 0, NULL, NULL};
140
141 static const char *level_to_number_string[] = {"0", "1", "2", "3", "4", "5", "6", "7"};
142
143 #define ASL_SERVICE_NAME "com.apple.system.logger"
144
145 /*
146 * Called from the child process inside fork() to clean up
147 * inherited state from the parent process.
148 *
149 * NB. A lock isn't required, since we're single threaded in this call.
150 */
151 void
152 _asl_fork_child()
153 {
154 _asl_global.notify_count = 0;
155 _asl_global.rc_change_token = -1;
156 _asl_global.master_token = -1;
157 _asl_global.notify_token = -1;
158 _asl_global.quota = 0;
159 _asl_global.last_send = 0;
160 _asl_global.last_oq_msg = 0;
161
162 _asl_global.port_lookup_once = 0;
163 _asl_global.server_port = MACH_PORT_NULL;
164
165 pthread_mutex_init(&(_asl_global.lock), NULL);
166
167 _asl_redirect_fork_child();
168 }
169
170 /*
171 * asl_remote_notify_name: returns the notification key for remote-control filter
172 * changes for this process.
173 */
174 char *
175 asl_remote_notify_name()
176 {
177 pid_t pid = getpid();
178 uid_t euid = geteuid();
179 char *str = NULL;
180
181 if (euid == 0) asprintf(&str, "%s.%d", NOTIFY_PREFIX_SYSTEM, pid);
182 else asprintf(&str, "user.uid.%d.syslog.%d", euid, pid);
183
184 return str;
185 }
186
187 static ASL_STATUS
188 _asl_notify_open(int do_lock)
189 {
190 char *notify_name;
191 uint32_t status;
192
193 if (do_lock != 0) pthread_mutex_lock(&_asl_global.lock);
194
195 _asl_global.notify_count++;
196
197 if (_asl_global.notify_token != -1)
198 {
199 if (do_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
200 return ASL_STATUS_OK;
201 }
202
203 if (_asl_global.rc_change_token == -1)
204 {
205 status = notify_register_check(NOTIFY_RC, &_asl_global.rc_change_token);
206 if (status != NOTIFY_STATUS_OK) _asl_global.rc_change_token = -1;
207 }
208
209 if (_asl_global.master_token == -1)
210 {
211 status = notify_register_plain(NOTIFY_SYSTEM_MASTER, &_asl_global.master_token);
212 if (status != NOTIFY_STATUS_OK) _asl_global.master_token = -1;
213 }
214
215 notify_name = asl_remote_notify_name();
216 if (notify_name != NULL)
217 {
218 status = notify_register_plain(notify_name, &_asl_global.notify_token);
219 free(notify_name);
220 if (status != NOTIFY_STATUS_OK) _asl_global.notify_token = -1;
221 }
222
223 if (do_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
224
225 if (_asl_global.notify_token == -1) return ASL_STATUS_FAILED;
226 return ASL_STATUS_OK;
227 }
228
229 #ifdef UNDEF
230 static void
231 _asl_notify_close()
232 {
233 pthread_mutex_lock(&_asl_global.lock);
234
235 if (_asl_global.notify_count > 0) _asl_global.notify_count--;
236
237 if (_asl_global.notify_count > 0)
238 {
239 pthread_mutex_unlock(&_asl_global.lock);
240 return;
241 }
242
243 if (_asl_global.rc_change_token >= 0) notify_cancel(_asl_global.rc_change_token);
244 _asl_global.rc_change_token = -1;
245
246 if (_asl_global.master_token >= 0) notify_cancel(_asl_global.master_token);
247 _asl_global.master_token = -1;
248
249 if (_asl_global.notify_token >= 0) notify_cancel(_asl_global.notify_token);
250 _asl_global.notify_token = -1;
251
252 pthread_mutex_unlock(&_asl_global.lock);
253 }
254 #endif
255
256 static void
257 _asl_global_init()
258 {
259 dispatch_once(&_asl_global.port_lookup_once, ^{
260 char *str = getenv("ASL_DISABLE");
261 if ((str == NULL) || strcmp(str, "1"))
262 {
263 bootstrap_look_up2(bootstrap_port, ASL_SERVICE_NAME, &_asl_global.server_port, 0, BOOTSTRAP_PRIVILEGED_SERVER);
264 }
265 });
266 }
267
268 mach_port_t
269 asl_core_get_service_port(__unused int reset)
270 {
271 _asl_global_init();
272 return _asl_global.server_port;
273 }
274
275 #pragma mark -
276 #pragma mark asl_client
277
278 asl_object_t
279 asl_open(const char *ident, const char *facility, uint32_t opts)
280 {
281 asl_client_t *asl = asl_client_open(ident, facility, opts);
282 if (asl == NULL) return NULL;
283
284 _asl_global_init();
285 if (!(opts & ASL_OPT_NO_REMOTE)) _asl_notify_open(1);
286
287 return (asl_object_t)asl;
288 }
289
290 asl_object_t
291 asl_open_from_file(int fd, const char *ident, const char *facility)
292 {
293 return (asl_object_t)asl_client_open_from_file(fd, ident, facility);
294 }
295
296 void
297 asl_close(asl_object_t obj)
298 {
299 asl_release(obj);
300 }
301
302 __private_extern__ asl_client_t *
303 _asl_open_default()
304 {
305 static dispatch_once_t once;
306
307 dispatch_once(&once, ^{
308 /*
309 * Do a sleight-of-hand with ASL_OPT_NO_REMOTE to avoid a deadlock
310 * since asl_open(xxx, yyy, 0) calls _asl_notify_open(1)
311 * which locks _asl_global.lock.
312 */
313 _asl_global.asl = (asl_client_t *)asl_open(NULL, NULL, ASL_OPT_NO_REMOTE);
314
315 /* Reset options to clear ASL_OPT_NO_REMOTE bit */
316 if (_asl_global.asl != NULL) _asl_global.asl->options = 0;
317
318 /* Now call _asl_notify_open(0) to finish the work */
319 _asl_notify_open(0);
320 });
321
322 return _asl_global.asl;
323 }
324
325 /*
326 * asl_add_file: write log messages to the given file descriptor
327 * Log messages will be written to this file as well as to the server.
328 */
329 int
330 asl_add_output_file(asl_object_t client, int fd, const char *mfmt, const char *tfmt, int filter, int text_encoding)
331 {
332 int status, use_global_lock = 0;
333 asl_client_t *asl;
334
335 if ((client != NULL) && (asl_get_type(client) != ASL_TYPE_CLIENT)) return -1;
336
337 asl = (asl_client_t *)client;
338 if (asl == NULL)
339 {
340 asl = _asl_open_default();
341 if (asl == NULL) return -1;
342 pthread_mutex_lock(&_asl_global.lock);
343 use_global_lock = 1;
344 }
345
346 status = asl_client_add_output_file(asl, fd, mfmt, tfmt, filter, text_encoding);
347
348 if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
349 return (status == ASL_STATUS_OK) ? 0 : -1;
350 }
351
352 /* returns previous filter value or -1 on error */
353 int
354 asl_set_output_file_filter(asl_object_t client, int fd, int filter)
355 {
356 uint32_t last;
357 int use_global_lock = 0;
358 asl_client_t *asl;
359
360 if ((client != NULL) && (asl_get_type(client) != ASL_TYPE_CLIENT)) return -1;
361
362 asl = (asl_client_t *)client;
363 if (asl == NULL)
364 {
365 asl = _asl_open_default();
366 if (asl == NULL) return -1;
367 pthread_mutex_lock(&_asl_global.lock);
368 use_global_lock = 1;
369 }
370
371 last = asl_client_set_output_file_filter(asl, fd, filter);
372
373 if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
374 return last;
375 }
376
377 /* SPI - Deprecated */
378 int
379 asl_add_output(asl_object_t client, int fd, const char *mfmt, const char *tfmt, uint32_t text_encoding)
380 {
381 return asl_add_output_file(client, fd, mfmt, tfmt, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG), text_encoding);
382 }
383
384 /* SPI - Deprecated */
385 int
386 asl_add_log_file(asl_object_t client, int fd)
387 {
388 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);
389 }
390
391 /*
392 * asl_remove_output: stop writing log messages to the given file descriptor
393 */
394 int
395 asl_remove_output_file(asl_object_t client, int fd)
396 {
397 int status, use_global_lock = 0;
398 asl_client_t *asl;
399
400 if ((client != NULL) && (asl_get_type(client) != ASL_TYPE_CLIENT)) return -1;
401
402 asl = (asl_client_t *)client;
403 if (asl == NULL)
404 {
405 asl = _asl_open_default();
406 if (asl == NULL) return -1;
407 pthread_mutex_lock(&_asl_global.lock);
408 use_global_lock = 1;
409 }
410
411 status = asl_client_remove_output_file(asl, fd);
412
413 if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
414 return (status == ASL_STATUS_OK) ? 0 : -1;
415 }
416
417 int
418 asl_remove_output(asl_object_t client, int fd)
419 {
420 return asl_remove_output_file(client, fd);
421 }
422
423 int
424 asl_remove_log_file(asl_object_t client, int fd)
425 {
426 return asl_remove_output_file(client, fd);
427 }
428
429 /* returns previous filter value or -1 on error */
430 int
431 asl_set_filter(asl_object_t client, int f)
432 {
433 int last, use_global_lock = 0;
434 asl_client_t *asl;
435
436 if ((client != NULL) && (asl_get_type(client) != ASL_TYPE_CLIENT)) return -1;
437
438 asl = (asl_client_t *)client;
439 if (asl == NULL)
440 {
441 asl = _asl_open_default();
442 if (asl == NULL) return -1;
443 pthread_mutex_lock(&_asl_global.lock);
444 use_global_lock = 1;
445 }
446
447 last = asl_client_set_filter(asl, f);
448
449 if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
450 return last;
451 }
452
453
454 #pragma mark -
455 #pragma mark message sending
456
457 /*
458 * Evaluate client / message / level to determine what to do with a message.
459 * Checks filters, tunneling, and log files.
460 * Returns the bits below, ORed with the message level.
461 *
462 * EVAL_SEND_ASL - send this message to syslogd
463 * EVAL_SEND_TRACE - log this message with Activity Tracing
464 * EVAL_ASL_FILE - write to a standalone ASL file (see asl_open_from_file)
465 * EVAL_TEXT_FILE - write this message to a text file / stderr
466 * EVAL_TUNNEL - tunneling enabled when sending to syslogd
467 */
468 uint32_t
469 _asl_evaluate_send(asl_object_t client, asl_object_t m, int slevel)
470 {
471 asl_client_t *asl;
472 asl_msg_t *msg = (asl_msg_t *)m;
473 uint32_t level, lmask, filter, status, tunnel;
474 int check;
475 uint64_t v64;
476 const char *val;
477 uint32_t eval;
478
479 level = ASL_LEVEL_DEBUG;
480 if (slevel >= 0) level = slevel;
481
482 val = NULL;
483 if ((asl_msg_lookup(msg, ASL_KEY_LEVEL, &val, NULL) == 0) && (val != NULL)) level = atoi(val);
484
485 if (level < ASL_LEVEL_EMERG) level = ASL_LEVEL_EMERG;
486 else if (level > ASL_LEVEL_DEBUG) level = ASL_LEVEL_DEBUG;
487
488 if ((client != NULL) && (asl_get_type(client) != ASL_TYPE_CLIENT))
489 {
490 /* sending to something other than a client */
491 return EVAL_ACTIVE | EVAL_SEND_ASL | level;
492 }
493
494 asl = (asl_client_t *)client;
495 if ((asl != NULL) && (asl->aslfile != NULL)) return (EVAL_ASL_FILE | level);
496
497 if (asl == NULL) asl = _asl_open_default();
498 if (asl == NULL)
499 {
500 eval = EVAL_ACTIVE | EVAL_DEFAULT_ACTION | level;
501 eval &= ~EVAL_SEND_ASL;
502 return eval;
503 }
504
505 eval = (asl_client_get_control(asl) & EVAL_ACTION_MASK) | level;
506
507 filter = asl->filter & 0xff;
508 tunnel = (asl->filter & ASL_FILTER_MASK_TUNNEL) >> 8;
509 if (tunnel != 0) eval |= EVAL_TUNNEL;
510
511 if ((asl->options & ASL_OPT_NO_REMOTE) == 0)
512 {
513 pthread_mutex_lock(&_asl_global.lock);
514
515 if (_asl_global.rc_change_token >= 0)
516 {
517 /* initialize or re-check process-specific and master filters */
518 check = 0;
519 status = notify_check(_asl_global.rc_change_token, &check);
520 if ((status == NOTIFY_STATUS_OK) && (check != 0))
521 {
522 if (_asl_global.master_token >= 0)
523 {
524 v64 = 0;
525 status = notify_get_state(_asl_global.master_token, &v64);
526 if (status == NOTIFY_STATUS_OK) _asl_global.master_filter = v64;
527 }
528
529 if (_asl_global.notify_token >= 0)
530 {
531 v64 = 0;
532 status = notify_get_state(_asl_global.notify_token, &v64);
533 if (status == NOTIFY_STATUS_OK) _asl_global.proc_filter = v64;
534 }
535 }
536 }
537
538 if (_asl_global.master_filter & EVAL_ACTIVE)
539 {
540 /* clear bits and set according to master */
541 eval &= ~(EVAL_SEND_ASL | EVAL_SEND_TRACE);
542 eval |= (_asl_global.master_filter & (EVAL_SEND_ASL | EVAL_SEND_TRACE));
543 eval |= EVAL_TUNNEL;
544
545 if ((_asl_global.master_filter & EVAL_LEVEL_MASK) != 0) filter = _asl_global.proc_filter & EVAL_LEVEL_MASK;
546 }
547
548 if (_asl_global.proc_filter & EVAL_ACTIVE)
549 {
550 /* clear bits and set according to proc */
551 eval &= ~(EVAL_SEND_ASL | EVAL_SEND_TRACE | EVAL_TEXT_FILE);
552 eval |= (_asl_global.proc_filter & (EVAL_SEND_ASL | EVAL_SEND_TRACE | EVAL_TEXT_FILE));
553 eval |= EVAL_TUNNEL;
554
555 if ((_asl_global.proc_filter & EVAL_LEVEL_MASK) != 0) filter = _asl_global.proc_filter & EVAL_LEVEL_MASK;
556 }
557
558 pthread_mutex_unlock(&_asl_global.lock);
559 }
560
561 lmask = ASL_FILTER_MASK(level);
562 if ((filter != 0) && ((filter & lmask) == 0)) eval &= ~EVAL_SEND_ASL;
563 if (asl->out_count > 0) eval |= EVAL_TEXT_FILE;
564
565 /* don't send MessageTracer messages to Activity Tracing */
566 val = NULL;
567 if ((asl_msg_lookup(msg, ASL_KEY_MESSAGETRACER, &val, NULL) == 0) && (val != NULL)) eval &= ~EVAL_SEND_TRACE;
568
569 /* don't send PowerManagement messages to Activity Tracing */
570 val = NULL;
571 if ((asl_msg_lookup(msg, ASL_KEY_POWERMANAGEMENT, &val, NULL) == 0) && (val != NULL)) eval &= ~EVAL_SEND_TRACE;
572
573 /* don't send control messages to Activity Tracing */
574 val = NULL;
575 if ((asl_msg_lookup(msg, ASL_KEY_OPTION, &val, NULL) == 0) && (val != NULL)) eval &= ~EVAL_SEND_TRACE;
576
577 /* don't send CFLog messages to Activity Tracing */
578 val = NULL;
579 if ((asl_msg_lookup(msg, ASL_KEY_CFLOG_LOCAL_TIME, &val, NULL) == 0) && (val != NULL)) eval &= ~EVAL_SEND_TRACE;
580
581 val = NULL;
582 if (((asl_msg_lookup(msg, ASL_KEY_FACILITY, &val, NULL) == 0) && (val != NULL)) ||
583 ((asl_msg_lookup(asl->kvdict, ASL_KEY_FACILITY, &val, NULL) == 0) && (val != NULL)))
584 {
585 /* don't send lastlog/utmp messages to Activity Tracing */
586 if (!strcmp(val, FACILITY_LASTLOG) || !strcmp(val, FACILITY_UTMPX)) eval &= ~EVAL_SEND_TRACE;
587
588 /* don't send LOG_INSTALL messages to Activity Tracing */
589 if (!strcmp(val, asl_syslog_faciliy_num_to_name(LOG_INSTALL))) eval &= ~EVAL_SEND_TRACE;
590 }
591
592 return eval;
593 }
594
595 /*
596 * _asl_lib_vlog_text
597 * Internal routine used by asl_vlog.
598 * msg: an asl messsage
599 * eval: log level and send flags for the message
600 * format: A formating string
601 * ap: va_list for the format
602 * returns 0 for success, non-zero for failure
603 */
604
605 __private_extern__ ASL_STATUS
606 _asl_lib_vlog_text(asl_object_t obj, uint32_t eval, asl_object_t msg, const char *format, va_list ap)
607 {
608 int saved_errno = errno;
609 int status;
610 char *str, *fmt, estr[NL_TEXTMAX];
611 uint32_t i, len, elen, expand;
612
613 if (format == NULL) return ASL_STATUS_INVALID_ARG;
614
615 /* insert strerror for %m */
616 len = 0;
617 elen = 0;
618
619 expand = 0;
620 for (i = 0; format[i] != '\0'; i++)
621 {
622 if (format[i] == '%')
623 {
624 if (format[i+1] == '\0') len++;
625 else if (format[i+1] == 'm')
626 {
627 expand = 1;
628 strerror_r(saved_errno, estr, sizeof(estr));
629 elen = strlen(estr);
630 len += elen;
631 i++;
632 }
633 else
634 {
635 len += 2;
636 i++;
637 }
638 }
639 else len++;
640 }
641
642 fmt = (char *)format;
643
644 if (expand != 0)
645 {
646 fmt = malloc(len + 1);
647 if (fmt == NULL) return ASL_STATUS_NO_MEMORY;
648
649 len = 0;
650
651 for (i = 0; format[i] != '\0'; i++)
652 {
653 if (format[i] == '%')
654 {
655 if (format[i+1] == '\0')
656 {
657 }
658 else if ((format[i+1] == 'm') && (elen != 0))
659 {
660 memcpy(fmt+len, estr, elen);
661 len += elen;
662 i++;
663 }
664 else
665 {
666 fmt[len++] = format[i++];
667 fmt[len++] = format[i];
668 }
669 }
670 else fmt[len++] = format[i];
671 }
672
673 fmt[len] = '\0';
674 }
675
676 str = NULL;
677 vasprintf(&str, fmt, ap);
678 if (expand != 0) free(fmt);
679
680 if (str == NULL) return ASL_STATUS_NO_MEMORY;
681
682 status = _asl_send_message_text(NULL, NULL, obj, eval, (asl_msg_t *)msg, str, true);
683 free(str);
684
685 return status;
686 }
687
688 /*
689 * _asl_lib_vlog
690 * Internal routine used by asl_vlog.
691 * msg: an asl messsage
692 * eval: log level and send flags for the message
693 * format: A formating string
694 * ap: va_list for the format
695 * returns 0 for success, non-zero for failure
696 */
697 __private_extern__ ASL_STATUS
698 _asl_lib_vlog(asl_object_t obj, uint32_t eval, asl_object_t msg, const char *format, va_list ap)
699 {
700 int saved_errno = errno;
701 int status;
702 char *str, *fmt, estr[NL_TEXTMAX];
703 uint32_t i, len, elen, expand;
704
705 if (format == NULL) return ASL_STATUS_INVALID_ARG;
706
707 /* insert strerror for %m */
708 len = 0;
709 elen = 0;
710
711 expand = 0;
712 for (i = 0; format[i] != '\0'; i++)
713 {
714 if (format[i] == '%')
715 {
716 if (format[i+1] == '\0') len++;
717 else if (format[i+1] == 'm')
718 {
719 expand = 1;
720 strerror_r(saved_errno, estr, sizeof(estr));
721 elen = strlen(estr);
722 len += elen;
723 i++;
724 }
725 else
726 {
727 len += 2;
728 i++;
729 }
730 }
731 else len++;
732 }
733
734 fmt = (char *)format;
735
736 if (expand != 0)
737 {
738 fmt = malloc(len + 1);
739 if (fmt == NULL) return ASL_STATUS_NO_MEMORY;
740
741 len = 0;
742
743 for (i = 0; format[i] != '\0'; i++)
744 {
745 if (format[i] == '%')
746 {
747 if (format[i+1] == '\0')
748 {
749 }
750 else if ((format[i+1] == 'm') && (elen != 0))
751 {
752 memcpy(fmt+len, estr, elen);
753 len += elen;
754 i++;
755 }
756 else
757 {
758 fmt[len++] = format[i++];
759 fmt[len++] = format[i];
760 }
761 }
762 else fmt[len++] = format[i];
763 }
764
765 fmt[len] = '\0';
766 }
767
768 str = NULL;
769 vasprintf(&str, fmt, ap);
770 if (expand != 0) free(fmt);
771
772 if (str == NULL) return ASL_STATUS_NO_MEMORY;
773
774 status = _asl_send_message(obj, eval, (asl_msg_t *)msg, str);
775 free(str);
776
777 return status;
778 }
779
780 /*
781 * asl_vlog
782 * Similar to asl_log, but take a va_list instead of a list of arguments.
783 * msg: an asl message
784 * level: the log level of the associated message
785 * format: A formating string
786 * ap: va_list for the format
787 * returns 0 for success, non-zero for failure
788 */
789 int
790 asl_vlog(asl_object_t client, asl_object_t msg, int level, const char *format, va_list ap)
791 {
792 ASL_STATUS status = ASL_STATUS_OK;
793 uint32_t eval = _asl_evaluate_send(client, msg, level);
794 void *addr = __builtin_return_address(0);
795
796 if ((eval & EVAL_SEND_TRACE) && os_log_shim_enabled(addr))
797 {
798 va_list ap_copy;
799 if (level < ASL_LEVEL_EMERG) level = ASL_LEVEL_EMERG;
800 if (level > ASL_LEVEL_DEBUG) level = ASL_LEVEL_DEBUG;
801 os_log_type_t type = shim_asl_to_log_type[level];
802
803 va_copy(ap_copy, ap);
804 os_log_with_args(OS_LOG_DEFAULT, type, format, ap_copy, addr);
805 va_end(ap_copy);
806
807 if (eval & EVAL_TEXT_FILE)
808 {
809 status = _asl_lib_vlog_text(client, eval, msg, format, ap);
810 }
811 }
812 else if (eval & EVAL_ASL)
813 {
814 status = _asl_lib_vlog(client, eval, msg, format, ap);
815 }
816
817 return (status == ASL_STATUS_OK) ? 0 : -1;
818 }
819
820 /*
821 * _asl_lib_log
822 * SPI used by legacy (non-shim) ASL_PREFILTER_LOG. Converts format arguments to a va_list and
823 * forwards the call to _asl_lib_vlog.
824 * msg: an asl message
825 * eval: log level and send flags for the message
826 * format: A formating string
827 * ... args for format
828 * returns 0 for success, non-zero for failure
829 */
830 int
831 _asl_lib_log(asl_object_t client, uint32_t eval, asl_object_t msg, const char *format, ...)
832 {
833 int status = 0;
834
835 if (eval & EVAL_ASL)
836 {
837 va_list ap;
838
839 va_start(ap, format);
840 status = _asl_lib_vlog(client, eval, msg, format, ap);
841 va_end(ap);
842 }
843
844 return status;
845 }
846
847 /*
848 * asl_log
849 * Processes an ASL log message.
850 * msg: an asl message
851 * level: the log level of the associated message
852 * format: A formating string
853 * ... args for format
854 * returns 0 for success, non-zero for failure
855 */
856 int
857 asl_log(asl_object_t client, asl_object_t msg, int level, const char *format, ...)
858 {
859 ASL_STATUS status = ASL_STATUS_OK;
860 uint32_t eval = _asl_evaluate_send(client, msg, level);
861 void *addr = __builtin_return_address(0);
862
863 if ((eval & EVAL_SEND_TRACE) && os_log_shim_enabled(addr))
864 {
865 va_list ap;
866 if (level < ASL_LEVEL_EMERG) level = ASL_LEVEL_EMERG;
867 if (level > ASL_LEVEL_DEBUG) level = ASL_LEVEL_DEBUG;
868 os_log_type_t type = shim_asl_to_log_type[level];
869
870 va_start(ap, format);
871 os_log_with_args(OS_LOG_DEFAULT, type, format, ap, addr);
872 va_end(ap);
873
874 if (eval & EVAL_TEXT_FILE)
875 {
876 va_list ap;
877 va_start(ap, format);
878 status = _asl_lib_vlog_text(client, eval, msg, format, ap);
879 va_end(ap);
880 }
881 }
882 else if (eval & EVAL_ASL)
883 {
884 va_list ap;
885 va_start(ap, format);
886 status = _asl_lib_vlog(client, eval, msg, format, ap);
887 va_end(ap);
888 }
889
890 return (status == ASL_STATUS_OK) ? 0 : -1;
891 }
892
893 /*
894 * asl_log_message
895 * Like asl_log, supplies NULL client and msg.
896 * level: the log level of the associated message
897 * format: A formating string
898 * ... args for format
899 * returns 0 for success, non-zero for failure
900 */
901 int
902 asl_log_message(int level, const char *format, ...)
903 {
904 ASL_STATUS status = ASL_STATUS_OK;
905 uint32_t eval = _asl_evaluate_send(NULL, NULL, level);
906 void *addr = __builtin_return_address(0);
907
908 if ((eval & EVAL_SEND_TRACE) && os_log_shim_enabled(addr))
909 {
910 va_list ap;
911 if (level < ASL_LEVEL_EMERG) level = ASL_LEVEL_EMERG;
912 if (level > ASL_LEVEL_DEBUG) level = ASL_LEVEL_DEBUG;
913 os_log_type_t type = shim_asl_to_log_type[level];
914
915 va_start(ap, format);
916 os_log_with_args(OS_LOG_DEFAULT, type, format, ap, addr);
917 va_end(ap);
918
919 if (eval & EVAL_TEXT_FILE)
920 {
921 va_list ap;
922 va_start(ap, format);
923 status = _asl_lib_vlog_text(NULL, eval, NULL, format, ap);
924 va_end(ap);
925 }
926 }
927 else if (eval & EVAL_ASL)
928 {
929 va_list ap;
930 va_start(ap, format);
931 status = _asl_lib_vlog(NULL, eval, NULL, format, ap);
932 va_end(ap);
933 }
934
935 return (status == ASL_STATUS_OK) ? 0 : -1;
936 }
937
938 /*
939 * asl_get_filter: gets the values for the local, master, and remote filters,
940 * and indicates which one is active.
941 */
942 int
943 asl_get_filter(asl_object_t client, int *local, int *master, int *remote, int *active)
944 {
945 asl_client_t *asl, *asl_default;
946 int l, m, r, x;
947 int status, check;
948 uint64_t v64;
949
950 if ((client != NULL) && (asl_get_type(client) != ASL_TYPE_CLIENT)) return -1;
951
952 l = 0;
953 m = 0;
954 r = 0;
955 x = 0;
956
957 asl_default = _asl_open_default();
958
959 asl = (asl_client_t *)client;
960 if (asl == NULL) asl = asl_default;
961 if (asl != NULL) l = asl->filter & EVAL_LEVEL_MASK;
962
963 if ((asl_default != NULL) && (!(asl_default->options & ASL_OPT_NO_REMOTE)))
964 {
965 pthread_mutex_lock(&_asl_global.lock);
966
967 if (_asl_global.rc_change_token >= 0)
968 {
969 /* initialize or re-check process-specific and master filters */
970 check = 0;
971 status = notify_check(_asl_global.rc_change_token, &check);
972 if ((status == NOTIFY_STATUS_OK) && (check != 0))
973 {
974 if (_asl_global.master_token >= 0)
975 {
976 v64 = 0;
977 status = notify_get_state(_asl_global.master_token, &v64);
978 if (status == NOTIFY_STATUS_OK) _asl_global.master_filter = v64;
979 }
980
981 if (_asl_global.notify_token >= 0)
982 {
983 v64 = 0;
984 status = notify_get_state(_asl_global.notify_token, &v64);
985 if (status == NOTIFY_STATUS_OK) _asl_global.proc_filter = v64;
986 }
987 }
988 }
989
990 m = _asl_global.master_filter;
991 if (m != 0) x = 1;
992
993 r = _asl_global.proc_filter;
994 if (r != 0) x = 2;
995
996 pthread_mutex_unlock(&_asl_global.lock);
997 }
998
999 if (local != NULL) *local = l;
1000 if (master != NULL) *master = m;
1001 if (remote != NULL) *remote = r;
1002 if (active != NULL) *active = x;
1003
1004 return 0;
1005 }
1006
1007 /* SPI for SHIM control */
1008 uint32_t
1009 asl_set_local_control(asl_object_t client, uint32_t filter)
1010 {
1011 asl_client_t *asl = (asl_client_t *)client;
1012 if (asl == NULL)
1013 {
1014 asl = _asl_open_default();
1015 if (asl == NULL) return UINT32_MAX;
1016 }
1017 else if (asl_get_type(client) != ASL_TYPE_CLIENT) return UINT32_MAX;
1018
1019 return asl_client_set_control(asl, filter);
1020 }
1021
1022 uint32_t
1023 asl_get_local_control(asl_object_t client)
1024 {
1025 asl_client_t *asl = (asl_client_t *)client;
1026 if (asl == NULL)
1027 {
1028 asl = _asl_open_default();
1029 if (asl == NULL) return UINT32_MAX;
1030 }
1031 else if (asl_get_type(client) != ASL_TYPE_CLIENT) return UINT32_MAX;
1032
1033 return asl_client_get_control(asl);
1034 }
1035
1036 /*
1037 * Sets PID and OSActivityID values in a new message.
1038 * Also sets Level, Time, TimeNanoSec, Sender, Facility and Message if provided.
1039 */
1040 asl_msg_t *
1041 asl_base_msg(asl_client_t *asl, uint32_t level, const struct timeval *tv, const char *sstr, const char *fstr, const char *mstr)
1042 {
1043 char aux_val[64];
1044 asl_msg_t *aux;
1045 int status;
1046 os_activity_id_t osaid;
1047
1048 aux = asl_msg_new(ASL_TYPE_MSG);
1049 if (aux == NULL) return NULL;
1050
1051 /* Level */
1052 if (level <= 7) asl_msg_set_key_val(aux, ASL_KEY_LEVEL, level_to_number_string[level]);
1053
1054 /* Time and TimeNanoSec */
1055 if (tv != NULL)
1056 {
1057 snprintf(aux_val, sizeof(aux_val), "%llu", (unsigned long long) tv->tv_sec);
1058 asl_msg_set_key_val(aux, ASL_KEY_TIME, aux_val);
1059
1060 snprintf(aux_val, sizeof(aux_val), "%d", tv->tv_usec * 1000);
1061 asl_msg_set_key_val(aux, ASL_KEY_TIME_NSEC, aux_val);
1062 }
1063
1064 /* Message */
1065 if (mstr != NULL) asl_msg_set_key_val(aux, ASL_KEY_MSG, mstr);
1066
1067 /* PID */
1068 snprintf(aux_val, sizeof(aux_val), "%u", getpid());
1069 asl_msg_set_key_val(aux, ASL_KEY_PID, aux_val);
1070
1071 /* OSActivityID */
1072 osaid = os_activity_get_identifier(OS_ACTIVITY_CURRENT, NULL);
1073 if (osaid)
1074 {
1075 snprintf(aux_val, sizeof(aux_val), "0x%016llx", osaid);
1076 asl_msg_set_key_val(aux, ASL_KEY_OS_ACTIVITY_ID, aux_val);
1077 }
1078
1079 /* Sender */
1080 if ((sstr == NULL) && (asl != NULL))
1081 {
1082 /* See if the client has a value for ASL_KEY_SENDER */
1083 status = asl_msg_lookup((asl_msg_t *)asl->kvdict, ASL_KEY_SENDER, &sstr, NULL);
1084 if ((status != 0) || (sstr == NULL))
1085 {
1086 sstr = NULL;
1087
1088 /* See if the global cache has a value for ASL_KEY_SENDER */
1089 if (_asl_global.sender == NULL)
1090 {
1091 /* Get the process name with _NSGetArgv */
1092 char *name = *(*_NSGetArgv());
1093 if (name != NULL)
1094 {
1095 char *x = strrchr(name, '/');
1096 if (x != NULL) x++;
1097 else x = name;
1098
1099 /* Set the cache value */
1100 pthread_mutex_lock(&_asl_global.lock);
1101 if (_asl_global.sender == NULL) _asl_global.sender = strdup(x);
1102 pthread_mutex_unlock(&_asl_global.lock);
1103 }
1104 }
1105
1106 if (_asl_global.sender != NULL) asl_msg_set_key_val(aux, ASL_KEY_SENDER, _asl_global.sender);
1107 else asl_msg_set_key_val(aux, ASL_KEY_SENDER, "Unknown");
1108 }
1109 }
1110
1111 if (sstr != NULL) asl_msg_set_key_val(aux, ASL_KEY_SENDER, sstr);
1112
1113 /* Facility */
1114 if ((fstr == NULL) && (asl != NULL))
1115 {
1116 status = asl_msg_lookup((asl_msg_t *)asl->kvdict, ASL_KEY_FACILITY, &fstr, NULL);
1117 if (status != 0) fstr = NULL;
1118 }
1119
1120 if (fstr != NULL) asl_msg_set_key_val(aux, ASL_KEY_FACILITY, fstr);
1121
1122 return aux;
1123 }
1124
1125 #ifdef NOTDEF
1126 /*
1127 * Possibly useful someday...
1128 */
1129 asl_msg_t *
1130 asl_prepared_message(asl_client_t *asl, asl_msg_t *msg)
1131 {
1132 uint32_t i, len, level, outstatus;
1133 const char *val, *sstr, *fstr;
1134 struct timeval tval = {0, 0};
1135 int status;
1136 asl_msg_t *out;
1137
1138 if (asl == NULL)
1139 {
1140 asl = _asl_open_default();
1141 if (asl == NULL) return NULL;
1142 }
1143
1144 status = gettimeofday(&tval, NULL);
1145 if (status != 0)
1146 {
1147 time_t tick = time(NULL);
1148 tval.tv_sec = tick;
1149 tval.tv_usec = 0;
1150 }
1151
1152 val = NULL;
1153 status = asl_msg_lookup(msg, ASL_KEY_LEVEL, &val, NULL);
1154 if (status != 0) val = NULL;
1155
1156 level = ASL_LEVEL_DEBUG;
1157 if (val != NULL) level = atoi(val);
1158 if (level > ASL_LEVEL_DEBUG) level = ASL_LEVEL_DEBUG;
1159
1160 sstr = NULL;
1161 status = asl_msg_lookup(msg, ASL_KEY_SENDER, &sstr, NULL);
1162 if (status != 0) sstr = NULL;
1163
1164 fstr = NULL;
1165 status = asl_msg_lookup(msg, ASL_KEY_FACILITY, &fstr, NULL);
1166 if (status != 0) fstr = NULL;
1167
1168 out = asl_base_msg(asl, level, &tval, sstr, fstr, NULL);
1169 out = asl_msg_merge(out, msg);
1170
1171 return out;
1172 }
1173 #endif
1174
1175 static void
1176 _asl_set_option(asl_msg_t *msg, const char *opt)
1177 {
1178 const char *val = NULL;
1179 uint32_t status;
1180
1181 if (msg == NULL) return;
1182 if (opt == NULL) return;
1183
1184 status = asl_msg_lookup(msg, ASL_KEY_OPTION, &val, NULL);
1185 if ((status != 0) || (val == NULL))
1186 {
1187 asl_msg_set_key_val(msg, ASL_KEY_OPTION, opt);
1188 }
1189 else
1190 {
1191 char *option = NULL;
1192 asprintf(&option, "%s %s", opt, val);
1193 asl_msg_set_key_val(msg, ASL_KEY_OPTION, option);
1194 free(option);
1195 }
1196 }
1197
1198 static ASL_STATUS
1199 _asl_send_message_text(asl_client_t *asl, asl_msg_t *sendmsg, asl_object_t obj, uint32_t eval, asl_msg_t *msg, const char *mstr, bool shimmed)
1200 {
1201 int status;
1202 uint32_t level, lmask;
1203 asl_msg_t *release_sendmsg = NULL;
1204
1205 if (asl == NULL) {
1206 if (obj == NULL) {
1207 asl = _asl_open_default();
1208 if (asl == NULL) return ASL_STATUS_FAILED;
1209 } else {
1210 uint32_t objtype = asl_get_type(obj);
1211 if (objtype == ASL_TYPE_CLIENT) asl = (asl_client_t *)obj;
1212 }
1213 }
1214
1215 level = eval & EVAL_LEVEL_MASK;
1216 if (level > 7) level = 7;
1217 lmask = ASL_FILTER_MASK(level);
1218
1219 if (sendmsg == NULL) {
1220 struct timeval tval = {0, 0};
1221 const char *sstr, *fstr;
1222
1223 status = gettimeofday(&tval, NULL);
1224 if (status != 0) {
1225 time_t tick = time(NULL);
1226 tval.tv_sec = tick;
1227 tval.tv_usec = 0;
1228 }
1229
1230 sstr = NULL;
1231 status = asl_msg_lookup(msg, ASL_KEY_SENDER, &sstr, NULL);
1232 if (status != 0) {
1233 sstr = NULL;
1234 }
1235
1236 fstr = NULL;
1237 status = asl_msg_lookup(msg, ASL_KEY_FACILITY, &fstr, NULL);
1238 if (status != 0) {
1239 fstr = NULL;
1240 }
1241 sendmsg = asl_base_msg(asl, level, &tval, sstr, fstr, mstr);
1242 if (sendmsg == NULL) {
1243 return ASL_STATUS_FAILED;
1244 }
1245
1246 release_sendmsg = sendmsg = asl_msg_merge(sendmsg, msg);
1247 }
1248
1249 /* write to file descriptors */
1250 for (uint32_t i = 0; i < asl->out_count; i++) {
1251 if (shimmed) {
1252 if ((asl->out_list[i].fd != STDOUT_FILENO) && (asl->out_list[i].fd != STDERR_FILENO)) {
1253 continue; // new logging only support stdout/stderr
1254 }
1255 }
1256
1257 if ((asl->out_list[i].fd >= 0) && (asl->out_list[i].filter != 0) && ((asl->out_list[i].filter & lmask) != 0)) {
1258 char *str;
1259
1260 uint32_t len = 0;
1261 str = asl_format_message(sendmsg, asl->out_list[i].mfmt, asl->out_list[i].tfmt, asl->out_list[i].encoding, &len);
1262 if (str == NULL) continue;
1263
1264 status = write(asl->out_list[i].fd, str, len - 1);
1265 if (status < 0)
1266 {
1267 /* soft error for fd 2 (stderr) */
1268 if (asl->out_list[i].fd == 2) status = 0;
1269 asl->out_list[i].fd = -1;
1270 } else {
1271 status = 0;
1272 }
1273
1274 free(str);
1275 }
1276 }
1277 if (release_sendmsg) {
1278 asl_msg_release(release_sendmsg);
1279 }
1280
1281 return status;
1282 }
1283
1284 static ASL_STATUS
1285 _asl_send_message(asl_object_t obj, uint32_t eval, asl_msg_t *msg, const char *mstr)
1286 {
1287 uint32_t len, level, lmask, outstatus, objtype;
1288 const char *sstr, *fstr;
1289 struct timeval tval = {0, 0};
1290 int status;
1291 int use_global_lock = 0;
1292 kern_return_t kstatus;
1293 asl_msg_t *sendmsg;
1294 asl_msg_t *qd_msg = NULL;
1295 asl_client_t *asl = NULL;
1296 static dispatch_once_t noquota_once;
1297 __block int log_quota_msg = 0;
1298
1299 if ((eval & EVAL_ASL) == 0) return ASL_STATUS_OK;
1300
1301 if (obj == NULL)
1302 {
1303 asl = _asl_open_default();
1304 if (asl == NULL) return ASL_STATUS_FAILED;
1305 use_global_lock = 1;
1306 objtype = ASL_TYPE_CLIENT;
1307 }
1308 else
1309 {
1310 objtype = asl_get_type(obj);
1311 if (objtype == ASL_TYPE_CLIENT) asl = (asl_client_t *)obj;
1312 }
1313
1314 level = eval & EVAL_LEVEL_MASK;
1315 if (level > 7) level = 7;
1316 lmask = ASL_FILTER_MASK(level);
1317
1318 if ((objtype == ASL_TYPE_CLIENT) && (asl->aslfile != NULL)) use_global_lock = 1;
1319
1320 status = gettimeofday(&tval, NULL);
1321 if (status != 0)
1322 {
1323 time_t tick = time(NULL);
1324 tval.tv_sec = tick;
1325 tval.tv_usec = 0;
1326 }
1327
1328 sstr = NULL;
1329 status = asl_msg_lookup(msg, ASL_KEY_SENDER, &sstr, NULL);
1330 if (status != 0) sstr = NULL;
1331
1332 fstr = NULL;
1333 status = asl_msg_lookup(msg, ASL_KEY_FACILITY, &fstr, NULL);
1334 if (status != 0) fstr = NULL;
1335
1336 sendmsg = asl_base_msg(asl, level, &tval, sstr, fstr, mstr);
1337 if (sendmsg == NULL) return ASL_STATUS_FAILED;
1338
1339 /* Set "ASLOption store" if tunneling */
1340 if (eval & EVAL_TUNNEL) _asl_set_option(sendmsg, ASL_OPT_STORE);
1341
1342 outstatus = -1;
1343
1344 if (use_global_lock != 0) pthread_mutex_lock(&_asl_global.lock);
1345
1346 sendmsg = asl_msg_merge(sendmsg, msg);
1347
1348 if (objtype != ASL_TYPE_CLIENT)
1349 {
1350 asl_append(obj, (asl_object_t)sendmsg);
1351 asl_msg_release(sendmsg);
1352 if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
1353 return ASL_STATUS_OK;
1354 }
1355
1356 /*
1357 * If there is an aslfile this is a stand-alone file client.
1358 * Just save to the file.
1359 */
1360 if (asl->aslfile != NULL)
1361 {
1362 outstatus = ASL_STATUS_FAILED;
1363
1364 if (sendmsg != NULL)
1365 {
1366 outstatus = asl_file_save(asl->aslfile, sendmsg, &(asl->aslfileid));
1367 asl->aslfileid++;
1368 }
1369
1370 asl_msg_release(sendmsg);
1371
1372 if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
1373 return outstatus;
1374 }
1375
1376 _asl_global_init();
1377 outstatus = 0;
1378
1379 /*
1380 * ASL message quota
1381 * Quotas are disabled if:
1382 * - a remote control filter is in place (EVAL_TUNNEL)
1383 * - Environment variable ASL_QUOTA_DISABLED == 1
1384 * - /etc/asl/.noquota existed at the time that the process started
1385 *
1386 * We just check /etc/asl/.noquota once, since it would be
1387 * expensive to stat() for every log message.
1388 *
1389 * We only check the Environment variable once, since getenv() is
1390 * not thread safe. If someone is changing the environment,
1391 * this can crash.
1392 */
1393
1394 dispatch_once(&noquota_once, ^{
1395 struct stat sb;
1396 memset(&sb, 0, sizeof(struct stat));
1397
1398 int save_errno = errno;
1399
1400 if (stat(NOQUOTA_FILE_PATH, &sb) == 0)
1401 {
1402 _asl_global.quota = UINT32_MAX;
1403 }
1404 else
1405 {
1406 const char *qtest = getenv(NOQUOTA_ENV);
1407 if ((qtest != NULL) && (!strcmp(qtest, "1")))
1408 {
1409 _asl_global.quota = UINT32_MAX;
1410 log_quota_msg = 1;
1411 }
1412 }
1413
1414 /* reset errno since we want stat() to fail silently */
1415 errno = save_errno;
1416 });
1417
1418 if (log_quota_msg != 0)
1419 {
1420 qd_msg = asl_base_msg(asl, QUOTA_LEVEL, &tval, sstr, fstr, QUOTA_DISABLED_MSG);
1421 asl_msg_set_key_val(qd_msg, ASL_KEY_OPTION, ASL_OPT_STORE);
1422 }
1423
1424 if (((eval & EVAL_TUNNEL) == 0) && (_asl_global.quota != UINT32_MAX))
1425 {
1426 time_t last_send = _asl_global.last_send;
1427 time_t last_oq = _asl_global.last_oq_msg;
1428 uint32_t qcurr = _asl_global.quota;
1429 time_t delta;
1430 uint32_t qinc, qnew;
1431
1432 qnew = qcurr;
1433
1434 /* add QUOTA_MPS to quota for each second we've been idle */
1435 if (tval.tv_sec > last_send)
1436 {
1437 delta = tval.tv_sec - last_send;
1438
1439 qinc = QUOTA_MPH;
1440 if (delta < (QUOTA_MPH / QUOTA_MPS)) qinc = delta * QUOTA_MPS;
1441
1442 qnew = MIN(QUOTA_MPH, qcurr + qinc);
1443 OSAtomicCompareAndSwapLongBarrier(last_send, tval.tv_sec, (long *)&_asl_global.last_send);
1444 }
1445
1446 if (qnew == 0)
1447 {
1448 if ((tval.tv_sec - last_oq) > QUOTA_MSG_INTERVAL)
1449 {
1450 eval |= EVAL_QUOTA;
1451 OSAtomicCompareAndSwapLongBarrier(last_oq, tval.tv_sec, (long *)&_asl_global.last_oq_msg);
1452 }
1453 else
1454 {
1455 eval &= ~EVAL_SEND_ASL;
1456 }
1457 }
1458 else
1459 {
1460 OSAtomicCompareAndSwap32Barrier(qcurr, qnew - 1, (int32_t *)&_asl_global.quota);
1461 }
1462 }
1463
1464 if ((_asl_global.server_port != MACH_PORT_NULL) && (eval & EVAL_SEND_ASL))
1465 {
1466 asl_string_t *send_str;
1467 const char *str;
1468 size_t vmsize;
1469
1470 if (eval & EVAL_QUOTA)
1471 {
1472 asl_msg_set_key_val(sendmsg, ASL_KEY_LEVEL, QUOTA_LEVEL_STR);
1473 asl_msg_set_key_val(sendmsg, ASL_KEY_MSG, QUOTA_MSG);
1474 }
1475
1476 if (qd_msg != NULL)
1477 {
1478 send_str = asl_msg_to_string_raw(ASL_STRING_MIG, qd_msg, "raw");
1479 len = asl_string_length(send_str);
1480 vmsize = asl_string_allocated_size(send_str);
1481 str = asl_string_release_return_bytes(send_str);
1482 if (len != 0)
1483 {
1484 kstatus = _asl_server_message(_asl_global.server_port, (caddr_t)str, len);
1485 if (kstatus != KERN_SUCCESS)
1486 {
1487 vm_deallocate(mach_task_self(), (vm_address_t)str, vmsize);
1488 }
1489 }
1490 else if (vmsize != 0)
1491 {
1492 vm_deallocate(mach_task_self(), (vm_address_t)str, vmsize);
1493 }
1494 asl_msg_release(qd_msg);
1495 }
1496
1497 send_str = asl_msg_to_string_raw(ASL_STRING_MIG, sendmsg, "raw");
1498 len = asl_string_length(send_str);
1499
1500 if (len > LIBASL_MAX_MSG_SIZE)
1501 {
1502 char tmp[256];
1503
1504 snprintf(tmp, sizeof(tmp), SIZE_LIMIT_MSG, len, LIBASL_MAX_MSG_SIZE);
1505 asl_msg_t *limitmsg = asl_base_msg(asl, ASL_LEVEL_CRIT, &tval, sstr, fstr, tmp);
1506
1507 asl_string_release(send_str);
1508 len = 0;
1509
1510 if (limitmsg != NULL)
1511 {
1512 /* Set "ASLOption store" if tunneling */
1513 if (eval & EVAL_TUNNEL) _asl_set_option(limitmsg, ASL_OPT_STORE);
1514
1515 send_str = asl_msg_to_string_raw(ASL_STRING_MIG, limitmsg, "raw");
1516 len = asl_string_length(send_str);
1517 asl_msg_release(limitmsg);
1518 }
1519 }
1520
1521 vmsize = asl_string_allocated_size(send_str);
1522 str = asl_string_release_return_bytes(send_str);
1523
1524 if (len != 0)
1525 {
1526 /* send a mach message to syslogd */
1527 kstatus = _asl_server_message(_asl_global.server_port, (caddr_t)str, len);
1528 if (kstatus != KERN_SUCCESS)
1529 {
1530 vm_deallocate(mach_task_self(), (vm_address_t)str, vmsize);
1531 outstatus = -1;
1532 }
1533 }
1534 else if (vmsize >0) vm_deallocate(mach_task_self(), (vm_address_t)str, vmsize);
1535 }
1536
1537 if ((sendmsg != NULL) && (asl->out_count > 0))
1538 {
1539 status = _asl_send_message_text(asl, sendmsg, obj, eval, msg, mstr, false);
1540 }
1541
1542 asl_msg_release(sendmsg);
1543
1544 if (use_global_lock != 0) pthread_mutex_unlock(&_asl_global.lock);
1545
1546 return outstatus;
1547 }
1548
1549 static void
1550 os_log_with_args_wrapper(void *addr, int level, const char *format, ...)
1551 {
1552 va_list ap;
1553 if (level < ASL_LEVEL_EMERG) level = ASL_LEVEL_EMERG;
1554 if (level > ASL_LEVEL_DEBUG) level = ASL_LEVEL_DEBUG;
1555 os_log_type_t type = shim_asl_to_log_type[level];
1556
1557 va_start(ap, format);
1558 os_log_with_args(OS_LOG_DEFAULT, type, format, ap, addr);
1559 va_end(ap);
1560 }
1561
1562 /*
1563 * asl_send: send a message
1564 * This routine may be used instead of asl_log() or asl_vlog() if asl_set()
1565 * has been used to set all of a message's attributes.
1566 * eval: hints about what to do with the message
1567 * msg: an asl message
1568 * returns 0 for success, non-zero for failure
1569 */
1570 __private_extern__ ASL_STATUS
1571 asl_client_internal_send(asl_object_t obj, asl_object_t msg, void *addr)
1572 {
1573 int status = ASL_STATUS_OK;
1574 uint32_t eval = _asl_evaluate_send(obj, msg, -1);
1575
1576 const char *message = asl_msg_get_val_for_key((asl_msg_t *)msg, ASL_KEY_MSG);
1577
1578 if ((eval & EVAL_SEND_TRACE) && message && message[0] && os_log_shim_enabled(addr))
1579 {
1580 int level = ASL_LEVEL_DEBUG;
1581 const char *lval = asl_msg_get_val_for_key((asl_msg_t *)msg, ASL_KEY_LEVEL);
1582 if (lval != NULL) level = atoi(lval);
1583
1584 /*
1585 * If the return address and the format string are from different
1586 * binaries, os_log_with_args will not record the return address.
1587 * Work around this by passing a dynamic format string.
1588 */
1589 char dynamic_format[] = "%s";
1590 os_log_with_args_wrapper(addr, level, dynamic_format, message);
1591
1592 if (eval & EVAL_TEXT_FILE)
1593 {
1594 status = _asl_send_message_text(NULL, NULL, obj, eval, (asl_msg_t *)msg, NULL, true);
1595 }
1596 }
1597 else if (eval & EVAL_ASL)
1598 {
1599 status = _asl_send_message(obj, eval, (asl_msg_t *)msg, NULL);
1600 }
1601
1602 return status;
1603 }
1604
1605 #pragma mark -
1606 #pragma mark auxiliary files and URLs
1607
1608 static ASL_STATUS
1609 _asl_aux_save_context(asl_aux_context_t *ctx)
1610 {
1611 if (ctx == NULL) return ASL_STATUS_FAILED;
1612
1613 pthread_mutex_lock(&_asl_global.lock);
1614
1615 _asl_global.aux_ctx = (asl_aux_context_t **)reallocf(_asl_global.aux_ctx, (_asl_global.aux_count + 1) * sizeof(asl_aux_context_t *));
1616 if (_asl_global.aux_ctx == NULL)
1617 {
1618 _asl_global.aux_count = 0;
1619 pthread_mutex_unlock(&_asl_global.lock);
1620 return ASL_STATUS_FAILED;
1621 }
1622
1623 _asl_global.aux_ctx[_asl_global.aux_count++] = ctx;
1624
1625 pthread_mutex_unlock(&_asl_global.lock);
1626
1627 return ASL_STATUS_OK;
1628 }
1629
1630 /*
1631 * Creates an auxiliary file that may be used to save arbitrary data. The ASL message msg
1632 * will be saved at the time that the auxiliary file is created. The message will include
1633 * any keys and values found in msg, and it will include the title and Uniform Type
1634 * Identifier specified. Output parameter out_fd will contain the file descriptor of the
1635 * new auxiliary file.
1636 */
1637 static ASL_STATUS
1638 _asl_auxiliary(asl_msg_t *msg, const char *title, const char *uti, const char *url, int *out_fd)
1639 {
1640 asl_msg_t *aux;
1641 asl_string_t *send_str;
1642 const char *str;
1643 fileport_t fileport;
1644 kern_return_t kstatus;
1645 size_t len, vmsize;
1646 uint32_t newurllen, where;
1647 int status, fd, fdpair[2];
1648 caddr_t newurl;
1649 dispatch_queue_t pipe_q;
1650 dispatch_io_t pipe_channel;
1651 dispatch_semaphore_t sem;
1652
1653 aux = asl_msg_new(ASL_TYPE_MSG);
1654
1655 if (url != NULL) asl_msg_set_key_val(aux, ASL_KEY_AUX_URL, url);
1656 if (title != NULL) asl_msg_set_key_val(aux, ASL_KEY_AUX_TITLE, title);
1657 if (uti == NULL) asl_msg_set_key_val(aux, ASL_KEY_AUX_UTI, "public.data");
1658 else asl_msg_set_key_val(aux, ASL_KEY_AUX_UTI, uti);
1659
1660 aux = asl_msg_merge(aux, msg);
1661
1662 /* if (out_fd == NULL), this is from asl_log_auxiliary_location */
1663 if (out_fd == NULL)
1664 {
1665 uint32_t eval = _asl_evaluate_send(NULL, (asl_object_t)aux, -1);
1666 status = _asl_send_message(NULL, eval, aux, NULL);
1667 asl_msg_release(aux);
1668 return status;
1669 }
1670
1671 where = asl_store_location();
1672 if (where == ASL_STORE_LOCATION_MEMORY)
1673 {
1674 /* create a pipe */
1675 asl_aux_context_t *ctx = (asl_aux_context_t *)calloc(1, sizeof(asl_aux_context_t));
1676 if (ctx == NULL) return ASL_STATUS_FAILED;
1677
1678 status = pipe(fdpair);
1679 if (status < 0)
1680 {
1681 free(ctx);
1682 return ASL_STATUS_FAILED;
1683 }
1684
1685 /* give read end to dispatch_io_read */
1686 fd = fdpair[0];
1687 sem = dispatch_semaphore_create(0);
1688 ctx->sem = sem;
1689 ctx->fd = fdpair[1];
1690
1691 status = _asl_aux_save_context(ctx);
1692 if (status != ASL_STATUS_OK)
1693 {
1694 close(fdpair[0]);
1695 close(fdpair[1]);
1696 dispatch_release(sem);
1697 free(ctx);
1698 return ASL_STATUS_FAILED;
1699 }
1700
1701 pipe_q = dispatch_queue_create("ASL_AUX_PIPE_Q", NULL);
1702 pipe_channel = dispatch_io_create(DISPATCH_IO_STREAM, fd, pipe_q, ^(int err){
1703 close(fd);
1704 });
1705
1706 *out_fd = fdpair[1];
1707
1708 dispatch_io_set_low_water(pipe_channel, SIZE_MAX);
1709
1710 dispatch_io_read(pipe_channel, 0, SIZE_MAX, pipe_q, ^(bool done, dispatch_data_t pipedata, int err){
1711 if (err == 0)
1712 {
1713 size_t len = dispatch_data_get_size(pipedata);
1714 if (len > 0)
1715 {
1716 const char *bytes = NULL;
1717 char *encoded;
1718 uint32_t eval;
1719
1720 dispatch_data_t md = dispatch_data_create_map(pipedata, (const void **)&bytes, &len);
1721 encoded = asl_core_encode_buffer(bytes, len);
1722 asl_msg_set_key_val(aux, ASL_KEY_AUX_DATA, encoded);
1723 free(encoded);
1724 eval = _asl_evaluate_send(NULL, (asl_object_t)aux, -1);
1725 _asl_send_message(NULL, eval, aux, NULL);
1726 asl_msg_release(aux);
1727 dispatch_release(md);
1728 }
1729 }
1730
1731 if (done)
1732 {
1733 dispatch_semaphore_signal(sem);
1734 dispatch_release(pipe_channel);
1735 dispatch_release(pipe_q);
1736 }
1737 });
1738
1739 return ASL_STATUS_OK;
1740 }
1741
1742 _asl_global_init();
1743 if (_asl_global.server_port == MACH_PORT_NULL) return ASL_STATUS_FAILED;
1744
1745 send_str = asl_msg_to_string_raw(ASL_STRING_MIG, aux, "raw");
1746 len = asl_string_length(send_str);
1747 vmsize = asl_string_allocated_size(send_str);
1748 str = asl_string_release_return_bytes(send_str);
1749
1750 if (len == 0)
1751 {
1752 asl_msg_release(aux);
1753 vm_deallocate(mach_task_self(), (vm_address_t)str, vmsize);
1754 return ASL_STATUS_FAILED;
1755 }
1756
1757 status = 0;
1758 fileport = MACH_PORT_NULL;
1759 status = KERN_SUCCESS;
1760
1761 kstatus = _asl_server_create_aux_link(_asl_global.server_port, (caddr_t)str, len, &fileport, &newurl, &newurllen, &status);
1762 if (kstatus != KERN_SUCCESS)
1763 {
1764 vm_deallocate(mach_task_self(), (vm_address_t)str, vmsize);
1765 asl_msg_release(aux);
1766 return ASL_STATUS_FAILED;
1767 }
1768
1769 if (status != 0)
1770 {
1771 asl_msg_release(aux);
1772 return status;
1773 }
1774
1775 if (newurl != NULL)
1776 {
1777 asl_msg_set_key_val(aux, ASL_KEY_AUX_URL, newurl);
1778 vm_deallocate(mach_task_self(), (vm_address_t)newurl, newurllen);
1779 }
1780
1781 if (fileport == MACH_PORT_NULL)
1782 {
1783 asl_msg_release(aux);
1784 return ASL_STATUS_FAILED;
1785 }
1786
1787 fd = fileport_makefd(fileport);
1788 mach_port_deallocate(mach_task_self(), fileport);
1789 if (fd < 0)
1790 {
1791 asl_msg_release(aux);
1792 status = -1;
1793 }
1794 else
1795 {
1796 asl_aux_context_t *ctx = (asl_aux_context_t *)calloc(1, sizeof(asl_aux_context_t));
1797 if (ctx == NULL)
1798 {
1799 status = -1;
1800 }
1801 else
1802 {
1803 *out_fd = fd;
1804
1805 ctx->fd = fd;
1806 ctx->msg = aux;
1807
1808 status = _asl_aux_save_context(ctx);
1809 }
1810 }
1811
1812 return status;
1813 }
1814
1815 int
1816 asl_create_auxiliary_file(asl_object_t msg, const char *title, const char *uti, int *out_fd)
1817 {
1818 if (out_fd == NULL) return -1;
1819
1820 ASL_STATUS status = _asl_auxiliary((asl_msg_t *)msg, title, uti, NULL, out_fd);
1821 return (status == ASL_STATUS_OK) ? 0 : -1;
1822 }
1823
1824 int
1825 asl_log_auxiliary_location(asl_object_t msg, const char *title, const char *uti, const char *url)
1826 {
1827 ASL_STATUS status = _asl_auxiliary((asl_msg_t *)msg, title, uti, url, NULL);
1828 return (status == ASL_STATUS_OK) ? 0 : -1;
1829 }
1830
1831 /*
1832 * Close an auxiliary file.
1833 * Sends the cached auxiliary message to syslogd.
1834 * Returns 0 on success, -1 on error.
1835 */
1836 int
1837 asl_close_auxiliary_file(int fd)
1838 {
1839 int i, j, status;
1840 asl_msg_t *aux_msg;
1841 dispatch_semaphore_t aux_sem = NULL;
1842
1843 pthread_mutex_lock(&(_asl_global.lock));
1844
1845 aux_msg = NULL;
1846 status = -1;
1847
1848 for (i = 0; i < _asl_global.aux_count; i++)
1849 {
1850 if (_asl_global.aux_ctx[i]->fd == fd)
1851 {
1852 status = 0;
1853
1854 aux_msg = _asl_global.aux_ctx[i]->msg;
1855 aux_sem = _asl_global.aux_ctx[i]->sem;
1856
1857 free(_asl_global.aux_ctx[i]);
1858
1859 for (j = i + 1; j < _asl_global.aux_count; i++, j++)
1860 {
1861 _asl_global.aux_ctx[i] = _asl_global.aux_ctx[j];
1862 }
1863
1864 _asl_global.aux_count--;
1865
1866 if (_asl_global.aux_count == 0)
1867 {
1868 free(_asl_global.aux_ctx);
1869 _asl_global.aux_ctx = NULL;
1870 }
1871 else
1872 {
1873 _asl_global.aux_ctx = (asl_aux_context_t **)reallocf(_asl_global.aux_ctx, _asl_global.aux_count * sizeof(asl_aux_context_t *));
1874 if (_asl_global.aux_ctx == NULL)
1875 {
1876 _asl_global.aux_count = 0;
1877 status = -1;
1878 }
1879 }
1880
1881 break;
1882 }
1883 }
1884
1885 pthread_mutex_unlock(&(_asl_global.lock));
1886
1887 close(fd);
1888
1889 if (aux_msg != NULL)
1890 {
1891 uint32_t eval = _asl_evaluate_send(NULL, (asl_object_t)aux_msg, -1);
1892 if (_asl_send_message(NULL, eval, aux_msg, NULL) != ASL_STATUS_OK) status = -1;
1893 asl_msg_release(aux_msg);
1894 }
1895
1896 if (aux_sem != NULL)
1897 {
1898 dispatch_semaphore_wait(aux_sem, DISPATCH_TIME_FOREVER);
1899 dispatch_release(aux_sem);
1900 }
1901
1902 return status;
1903 }
1904
1905 #pragma mark -
1906
1907 asl_msg_t *
1908 _asl_server_control_query(void)
1909 {
1910 asl_msg_list_t *list = NULL;
1911 char *res;
1912 uint32_t len, reslen, status;
1913 uint64_t cmax, qmin;
1914 kern_return_t kstatus;
1915 caddr_t vmstr;
1916 asl_msg_t *m = NULL;
1917 static const char ctlstr[] = "1\nQ [= ASLOption control]\n";
1918
1919 _asl_global_init();
1920 if (_asl_global.server_port == MACH_PORT_NULL) return NULL;
1921
1922 len = strlen(ctlstr) + 1;
1923
1924 qmin = 0;
1925 cmax = 0;
1926 res = NULL;
1927 reslen = 0;
1928
1929 kstatus = vm_allocate(mach_task_self(), (vm_address_t *)&vmstr, len, VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_ASL));
1930 if (kstatus != KERN_SUCCESS) return NULL;
1931
1932 memmove(vmstr, ctlstr, len);
1933
1934 status = 0;
1935 kstatus = _asl_server_query_2(_asl_global.server_port, vmstr, len, qmin, FETCH_BATCH, 0, (caddr_t *)&res, &reslen, &cmax, (int *)&status);
1936 if (kstatus != KERN_SUCCESS) return NULL;
1937
1938 list = asl_msg_list_from_string(res);
1939 vm_deallocate(mach_task_self(), (vm_address_t)res, reslen);
1940
1941 if (list == NULL) return NULL;
1942 if (list->count > 0) m = asl_msg_retain(list->msg[0]);
1943 asl_msg_list_release(list);
1944 return m;
1945 }
1946
1947 /*
1948 * Returns ASL_STORE_LOCATION_FILE or ASL_STORE_LOCATION_MEMORY
1949 */
1950 int
1951 asl_store_location()
1952 {
1953 kern_return_t kstatus;
1954 char *res;
1955 uint32_t reslen, status;
1956 uint64_t cmax;
1957
1958 _asl_global_init();
1959 if (_asl_global.server_port == MACH_PORT_NULL) return ASL_STORE_LOCATION_FILE;
1960
1961 res = NULL;
1962 reslen = 0;
1963 cmax = 0;
1964 status = ASL_STATUS_OK;
1965
1966 kstatus = _asl_server_query_2(_asl_global.server_port, NULL, 0, 0, -1, 0, (caddr_t *)&res, &reslen, &cmax, (int *)&status);
1967 if (kstatus != KERN_SUCCESS) return ASL_STORE_LOCATION_FILE;
1968
1969 /* res should never be returned, but just to be certain we don't leak VM ... */
1970 if (res != NULL) vm_deallocate(mach_task_self(), (vm_address_t)res, reslen);
1971
1972 if (status == ASL_STATUS_OK) return ASL_STORE_LOCATION_MEMORY;
1973 return ASL_STORE_LOCATION_FILE;
1974 }
1975
1976 asl_object_t
1977 asl_open_path(const char *path, uint32_t opts)
1978 {
1979 struct stat sb;
1980 asl_file_t *fout = NULL;
1981 asl_store_t *sout = NULL;
1982
1983 if (opts == 0) opts = ASL_OPT_OPEN_READ;
1984
1985 if (opts & ASL_OPT_OPEN_READ)
1986 {
1987 if (path == NULL)
1988 {
1989 if (asl_store_open_read(ASL_PLACE_DATABASE_DEFAULT, &sout) != ASL_STATUS_OK) return NULL;
1990 return (asl_object_t)sout;
1991 }
1992
1993 memset(&sb, 0, sizeof(struct stat));
1994 if (stat(path, &sb) < 0) return NULL;
1995
1996 if (sb.st_mode & S_IFREG)
1997 {
1998 if (asl_file_open_read(path, &fout) != ASL_STATUS_OK) return NULL;
1999 return (asl_object_t)fout;
2000 }
2001 else if (sb.st_mode & S_IFDIR)
2002 {
2003 if (asl_store_open_read(path, &sout) != ASL_STATUS_OK) return NULL;
2004 return (asl_object_t)sout;
2005 }
2006
2007 return NULL;
2008 }
2009 else if (opts & ASL_OPT_OPEN_WRITE)
2010 {
2011 if (path == NULL) return NULL;
2012
2013 memset(&sb, 0, sizeof(struct stat));
2014 if (stat(path, &sb) < 0)
2015 {
2016 if (errno != ENOENT) return NULL;
2017
2018 if (opts & ASL_OPT_CREATE_STORE)
2019 {
2020 if (asl_store_open_write(path, &sout) != ASL_STATUS_OK) return NULL;
2021 return (asl_object_t)sout;
2022 }
2023 else
2024 {
2025 if (asl_file_open_write(path, 0644, geteuid(), getegid(), &fout) != ASL_STATUS_OK) return NULL;
2026 return (asl_object_t)fout;
2027 }
2028 }
2029 else if (sb.st_mode & S_IFREG)
2030 {
2031 if (asl_file_open_write(path, 0644, geteuid(), getegid(), &fout) != ASL_STATUS_OK) return NULL;
2032 return (asl_object_t)fout;
2033 }
2034 else if (sb.st_mode & S_IFDIR)
2035 {
2036 if (asl_store_open_write(path, &sout) != ASL_STATUS_OK) return NULL;
2037 return (asl_object_t)sout;
2038 }
2039 }
2040
2041 return NULL;
2042 }