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