2 * Copyright (c) 2015 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
36 #include <sys/types.h>
37 #include <libkern/OSAtomic.h>
38 #include <crt_externs.h>
40 #include <asl_private.h>
43 #include <asl_client.h>
45 #define PUBLIC_OPT_MASK 0x000000ff
47 /* private asl_file SPI */
48 __private_extern__ ASL_STATUS
asl_file_open_write_fd(int descriptor
, asl_file_t
**s
);
51 __private_extern__ ASL_STATUS
asl_client_internal_send(asl_object_t client
, asl_object_t msg
, void *addr
);
54 #pragma mark asl_client_t
57 _asl_client_free_internal(asl_client_t
*client
)
61 if (client
== NULL
) return;
63 if (client
->kvdict
!= NULL
) asl_msg_release(client
->kvdict
);
64 client
->kvdict
= NULL
;
66 if (client
->aslfile
!= NULL
) asl_file_close(client
->aslfile
);
67 client
->aslfile
= NULL
;
69 for (i
= 0; i
< client
->out_count
; i
++)
71 free(client
->out_list
[i
].mfmt
);
72 free(client
->out_list
[i
].tfmt
);
75 free(client
->out_list
);
76 client
->out_list
= NULL
;
82 asl_client_open(const char *ident
, const char *facility
, uint32_t opts
)
84 asl_client_t
*client
= (asl_client_t
*)calloc(1, sizeof(asl_client_t
));
91 client
->asl_type
= ASL_TYPE_CLIENT
;
94 client
->kvdict
= asl_msg_new(ASL_TYPE_MSG
);
95 if (client
->kvdict
== NULL
)
97 asl_client_release(client
);
102 client
->options
= opts
& PUBLIC_OPT_MASK
;
104 client
->pid
= getpid();
105 client
->uid
= getuid();
106 client
->gid
= getgid();
110 asl_msg_set_key_val(client
->kvdict
, ASL_KEY_SENDER
, ident
);
114 char *name
= *(*_NSGetArgv());
117 char *x
= strrchr(name
, '/');
120 asl_msg_set_key_val(client
->kvdict
, ASL_KEY_SENDER
, x
);
124 if (facility
!= NULL
)
126 asl_msg_set_key_val(client
->kvdict
, ASL_KEY_FACILITY
, facility
);
128 else if (client
->uid
== 0)
130 asl_msg_set_key_val(client
->kvdict
, ASL_KEY_FACILITY
, asl_syslog_faciliy_num_to_name(LOG_DAEMON
));
134 asl_msg_set_key_val(client
->kvdict
, ASL_KEY_FACILITY
, asl_syslog_faciliy_num_to_name(LOG_USER
));
137 client
->filter
= ASL_FILTER_MASK_UPTO(ASL_LEVEL_NOTICE
);
139 client
->filter
|= EVAL_ACTIVE
;
140 if (!(opts
& ASL_OPT_SHIM_NO_ASL
)) client
->filter
|= EVAL_SEND_ASL
;
141 if (!(opts
& ASL_OPT_SHIM_NO_TRACE
)) client
->filter
|= EVAL_SEND_TRACE
;
143 if (client
->options
& ASL_OPT_STDERR
)
145 /* only add stderr if it is valid */
146 if (fcntl(STDERR_FILENO
, F_GETFD
) >= 0)
148 asl_client_add_output_file(client
, fileno(stderr
), ASL_MSG_FMT_STD
, ASL_TIME_FMT_LCL
, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG
), ASL_ENCODE_SAFE
);
152 /* stderr has been closed, ignore ASL_OPT_STDERR flag */
153 client
->options
&= ~ASL_OPT_STDERR
;
161 asl_client_open_from_file(int descriptor
, const char *ident
, const char *facility
)
164 asl_client_t
*client
= asl_client_open(ident
, facility
, 0);
165 if (client
== NULL
) return NULL
;
167 client
->filter
= ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG
);
168 client
->filter
|= (EVAL_ACTIVE
| EVAL_SEND_ASL
);
170 status
= asl_file_open_write_fd(descriptor
, &(client
->aslfile
));
171 if (status
!= ASL_STATUS_OK
)
173 _asl_client_free_internal(client
);
177 client
->aslfileid
= 1;
183 asl_client_retain(asl_client_t
*client
)
185 if (client
== NULL
) return NULL
;
186 asl_retain((asl_object_t
)client
);
191 asl_client_release(asl_client_t
*client
)
193 if (client
== NULL
) return;
194 asl_release((asl_object_t
)client
);
198 #pragma mark database access
201 asl_client_send(asl_client_t
*client
, asl_msg_t
*msg
)
203 return asl_client_internal_send((asl_object_t
)client
, (asl_object_t
)msg
, __builtin_return_address(0));
206 static asl_msg_list_t
*
207 _do_server_match(asl_msg_list_t
*qlist
, size_t *last
, size_t start
, size_t count
, uint32_t duration
, int dir
)
209 char *str
, *res
= NULL
;
210 uint32_t len
, reslen
, status
;
211 uint64_t last64
, start64
, count64
;
212 kern_return_t kstatus
;
215 mach_port_t asl_server_port
= asl_core_get_service_port(0);
217 if (asl_server_port
== MACH_PORT_NULL
) return NULL
;
222 asprintf(&str
, "0\n");
227 str
= asl_msg_list_to_string(qlist
, &len
);
230 if (str
== NULL
) return NULL
;
232 kstatus
= vm_allocate(mach_task_self(), (vm_address_t
*)&vmstr
, len
, VM_FLAGS_ANYWHERE
| VM_MAKE_TAG(VM_MEMORY_ASL
));
233 if (kstatus
!= KERN_SUCCESS
) return NULL
;
235 memmove(vmstr
, str
, len
);
242 kstatus
= _asl_server_match(asl_server_port
, vmstr
, len
, start64
, count64
, duration
, dir
, (caddr_t
*)&res
, &reslen
, &last64
, (int *)&status
);
245 out
= asl_msg_list_from_string(res
);
246 vm_deallocate(mach_task_self(), (vm_address_t
)res
, reslen
);
251 static asl_msg_list_t
*
252 _do_server_search(asl_msg_t
*q
)
255 char *qstr
, *str
, *res
= NULL
;
256 uint32_t len
, reslen
= 0, status
= ASL_STATUS_OK
;
258 kern_return_t kstatus
;
260 mach_port_t asl_server_port
= asl_core_get_service_port(0);
262 if (asl_server_port
== MACH_PORT_NULL
) return NULL
;
265 qstr
= asl_msg_to_string(q
, &len
);
270 asprintf(&str
, "0\n");
275 asprintf(&str
, "1\n%s\n", qstr
);
280 if (str
== NULL
) return NULL
;
282 kstatus
= vm_allocate(mach_task_self(), (vm_address_t
*)&vmstr
, len
, VM_FLAGS_ANYWHERE
| VM_MAKE_TAG(VM_MEMORY_ASL
));
283 if (kstatus
!= KERN_SUCCESS
) return NULL
;
285 memmove(vmstr
, str
, len
);
288 kstatus
= _asl_server_query_2(asl_server_port
, vmstr
, len
, 0, 0, 0, (caddr_t
*)&res
, &reslen
, &cmax
, (int *)&status
);
289 if (kstatus
!= KERN_SUCCESS
) return NULL
;
291 out
= asl_msg_list_from_string(res
);
292 vm_deallocate(mach_task_self(), (vm_address_t
)res
, reslen
);
297 static asl_msg_list_t
*
298 _do_store_match(asl_msg_list_t
*qlist
, size_t *last
, size_t start
, size_t count
, uint32_t duration
, int32_t direction
)
302 uint64_t l64
= 0, s64
;
303 asl_store_t
*store
= NULL
;
306 char *str
= asl_msg_list_to_string(qlist
, &len
);
309 status
= asl_store_open_read(NULL
, &store
);
310 if (status
!= 0) return NULL
;
311 if (store
== NULL
) return NULL
;
314 out
= asl_store_match(store
, qlist
, &l64
, s64
, count
, duration
, direction
);
317 asl_store_close(store
);
322 static asl_msg_list_t
*
323 _do_store_search(asl_msg_t
*query
)
325 asl_msg_list_t
*out
, *qlist
= NULL
;
328 uint64_t last
= 0, start
= 0;
329 asl_store_t
*store
= NULL
;
330 const char *val
= NULL
;
332 /* check for "ASLMessageId >[=] n" and set start_id */
333 status
= asl_msg_lookup(query
, ASL_KEY_MSG_ID
, &val
, &op
);
334 if ((status
== 0) && (val
!= NULL
) && (op
& ASL_QUERY_OP_GREATER
))
336 if (op
& ASL_QUERY_OP_EQUAL
) start
= atoll(val
);
337 else start
= atoll(val
) + 1;
340 status
= asl_store_open_read(NULL
, &store
);
341 if (status
!= 0) return NULL
;
342 if (store
== NULL
) return NULL
;
346 qlist
= asl_msg_list_new();
347 asl_msg_list_append(qlist
, query
);
350 out
= asl_store_match(store
, qlist
, &last
, start
, 0, 0, 1);
351 asl_store_close(store
);
353 asl_msg_list_release(qlist
);
358 asl_client_match(asl_client_t
*client
, asl_msg_list_t
*qlist
, size_t *last
, size_t start
, size_t count
, uint32_t duration
, int32_t direction
)
360 if (asl_store_location() == ASL_STORE_LOCATION_FILE
) return _do_store_match(qlist
, last
, start
, count
, duration
, direction
);
361 return _do_server_match(qlist
, last
, start
, count
, duration
, direction
);
365 asl_client_search(asl_client_t
*client
, asl_msg_t
*query
)
367 if (asl_store_location() == ASL_STORE_LOCATION_FILE
) return _do_store_search(query
);
368 return _do_server_search(query
);
373 #pragma mark output control
376 * Returns last filter value, or -1 on error.
377 * Note that this allows ASL_FILTER_MASK_TUNNEL (0x100) to be set.
378 * That is SPI that's used by some clients.
381 asl_client_set_filter(asl_client_t
*client
, int filter
)
383 if (client
== NULL
) return -1;
385 uint32_t allbits
= client
->filter
;
386 int last
= allbits
& (~EVAL_ACTION_MASK
);
387 client
->filter
= (allbits
& EVAL_ACTION_MASK
) | (filter
& (~EVAL_ACTION_MASK
));
393 asl_client_set_control(asl_client_t
*client
, uint32_t filter
)
395 if (client
== NULL
) return UINT32_MAX
;
397 uint32_t last
= client
->filter
;
398 client
->filter
= filter
;
403 asl_client_get_control(asl_client_t
*client
)
405 if (client
== NULL
) return UINT32_MAX
;
406 return client
->filter
;
410 asl_client_add_output_file(asl_client_t
*client
, int descriptor
, const char *mfmt
, const char *tfmt
, int filter
, int text_encoding
)
414 if (client
== NULL
) return ASL_STATUS_FAILED
;
416 for (i
= 0; i
< client
->out_count
; i
++)
418 if (client
->out_list
[i
].fd
== descriptor
)
420 /* update message format, time format, filter, and text encoding */
421 free(client
->out_list
[i
].mfmt
);
422 client
->out_list
[i
].mfmt
= NULL
;
423 if (mfmt
!= NULL
) client
->out_list
[i
].mfmt
= strdup(mfmt
);
425 free(client
->out_list
[i
].tfmt
);
426 client
->out_list
[i
].tfmt
= NULL
;
427 if (tfmt
!= NULL
) client
->out_list
[i
].tfmt
= strdup(tfmt
);
429 client
->out_list
[i
].encoding
= text_encoding
;
430 client
->out_list
[i
].filter
= filter
;
432 return ASL_STATUS_OK
;
436 if (client
->out_count
== 0) client
->out_list
= NULL
;
437 client
->out_list
= (asl_out_file_t
*)reallocf(client
->out_list
, (1 + client
->out_count
) * sizeof(asl_out_file_t
));
439 if (client
->out_list
== NULL
) return ASL_STATUS_FAILED
;
441 client
->out_list
[client
->out_count
].fd
= descriptor
;
442 client
->out_list
[client
->out_count
].encoding
= text_encoding
;
443 client
->out_list
[client
->out_count
].filter
= filter
;
444 client
->out_list
[client
->out_count
].mfmt
= NULL
;
445 if (mfmt
!= NULL
) client
->out_list
[client
->out_count
].mfmt
= strdup(mfmt
);
446 client
->out_list
[client
->out_count
].tfmt
= NULL
;
447 if (tfmt
!= NULL
) client
->out_list
[client
->out_count
].tfmt
= strdup(tfmt
);
451 return ASL_STATUS_OK
;
454 /* returns last filter value, or -1 on error */
456 asl_client_set_output_file_filter(asl_client_t
*client
, int descriptor
, int filter
)
461 if (client
== NULL
) return -1;
463 for (i
= 0; i
< client
->out_count
; i
++)
465 if (client
->out_list
[i
].fd
== descriptor
)
468 last
= client
->out_list
[i
].filter
;
469 client
->out_list
[i
].filter
= filter
;
478 asl_client_remove_output_file(asl_client_t
*client
, int descriptor
)
483 if (client
== NULL
) return ASL_STATUS_INVALID_ARG
;
485 if (client
->out_count
== 0) return ASL_STATUS_OK
;
488 for (i
= 0; i
< client
->out_count
; i
++)
490 if (client
->out_list
[i
].fd
== descriptor
)
497 if (x
== -1) return ASL_STATUS_OK
;
499 free(client
->out_list
[x
].mfmt
);
500 free(client
->out_list
[x
].tfmt
);
502 for (i
= x
+ 1; i
< client
->out_count
; i
++, x
++)
504 client
->out_list
[x
] = client
->out_list
[i
];
509 if (client
->out_count
== 0)
511 free(client
->out_list
);
512 client
->out_list
= NULL
;
516 client
->out_list
= (asl_out_file_t
*)reallocf(client
->out_list
, client
->out_count
* sizeof(asl_out_file_t
));
518 if (client
->out_list
== NULL
)
520 client
->out_count
= 0;
521 return ASL_STATUS_FAILED
;
525 return ASL_STATUS_OK
;
529 #pragma mark dictionary access
532 asl_client_kvdict(asl_client_t
*client
)
534 if (client
== NULL
) return NULL
;
535 return client
->kvdict
;
539 #pragma mark asl_object support
542 _jump_dealloc(asl_object_private_t
*obj
)
544 _asl_client_free_internal((asl_client_t
*)obj
);
548 _jump_append(asl_object_private_t
*obj
, asl_object_private_t
*newobj
, void *addr
)
550 int type
= asl_get_type((asl_object_t
)newobj
);
552 if (type
== ASL_TYPE_LIST
)
555 asl_msg_list_reset_iteration((asl_msg_list_t
*)newobj
, 0);
556 while (NULL
!= (msg
= asl_msg_list_next((asl_msg_list_t
*)newobj
)))
558 if (asl_client_internal_send((asl_object_t
)obj
, (asl_object_t
)msg
, addr
) != ASL_STATUS_OK
) return;
561 else if ((type
== ASL_TYPE_MSG
) || (type
== ASL_TYPE_QUERY
))
563 asl_client_internal_send((asl_object_t
)obj
, (asl_object_t
)newobj
, addr
);
567 static asl_object_private_t
*
568 _jump_search(asl_object_private_t
*obj
, asl_object_private_t
*query
)
570 int type
= asl_get_type((asl_object_t
)query
);
571 if ((query
!= NULL
) && (type
!= ASL_TYPE_MSG
) && (type
!= ASL_TYPE_QUERY
)) return NULL
;
573 return (asl_object_private_t
*)asl_client_search((asl_client_t
*)obj
, (asl_msg_t
*)query
);
576 static asl_object_private_t
*
577 _jump_match(asl_object_private_t
*obj
, asl_object_private_t
*qlist
, size_t *last
, size_t start
, size_t count
, uint32_t duration
, int32_t dir
)
579 asl_msg_list_t
*out
= NULL
;
580 int type
= asl_get_type((asl_object_t
)qlist
);
582 if ((qlist
!= NULL
) && (type
!= ASL_TYPE_LIST
)) return NULL
;
584 out
= asl_client_match((asl_client_t
*)obj
, (asl_msg_list_t
*)qlist
, last
, start
, count
, duration
, dir
);
585 return (asl_object_private_t
*)out
;
588 __private_extern__
const asl_jump_table_t
*
589 asl_client_jump_table()
591 static const asl_jump_table_t jump
=
594 .dealloc
= &_jump_dealloc
,
595 .set_key_val_op
= NULL
,
597 .get_val_op_for_key
= NULL
,
598 .get_key_val_op_at_index
= NULL
,
602 .get_object_at_index
= NULL
,
603 .set_iteration_index
= NULL
,
604 .remove_object_at_index
= NULL
,
605 .append
= &_jump_append
,
607 .search
= &_jump_search
,
608 .match
= &_jump_match