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
);
243 if (kstatus
!= KERN_SUCCESS
) return NULL
;
246 out
= asl_msg_list_from_string(res
);
247 vm_deallocate(mach_task_self(), (vm_address_t
)res
, reslen
);
252 static asl_msg_list_t
*
253 _do_server_search(asl_msg_t
*q
)
256 char *qstr
, *str
, *res
= NULL
;
257 uint32_t len
, reslen
= 0, status
= ASL_STATUS_OK
;
259 kern_return_t kstatus
;
261 mach_port_t asl_server_port
= asl_core_get_service_port(0);
263 if (asl_server_port
== MACH_PORT_NULL
) return NULL
;
266 qstr
= asl_msg_to_string(q
, &len
);
271 asprintf(&str
, "0\n");
276 asprintf(&str
, "1\n%s\n", qstr
);
281 if (str
== NULL
) return NULL
;
283 kstatus
= vm_allocate(mach_task_self(), (vm_address_t
*)&vmstr
, len
, VM_FLAGS_ANYWHERE
| VM_MAKE_TAG(VM_MEMORY_ASL
));
284 if (kstatus
!= KERN_SUCCESS
) return NULL
;
286 memmove(vmstr
, str
, len
);
289 kstatus
= _asl_server_query_2(asl_server_port
, vmstr
, len
, 0, 0, 0, (caddr_t
*)&res
, &reslen
, &cmax
, (int *)&status
);
290 if (kstatus
!= KERN_SUCCESS
) return NULL
;
292 out
= asl_msg_list_from_string(res
);
293 vm_deallocate(mach_task_self(), (vm_address_t
)res
, reslen
);
298 static asl_msg_list_t
*
299 _do_store_match(asl_msg_list_t
*qlist
, size_t *last
, size_t start
, size_t count
, uint32_t duration
, int32_t direction
)
303 uint64_t l64
= 0, s64
;
304 asl_store_t
*store
= NULL
;
307 char *str
= asl_msg_list_to_string(qlist
, &len
);
310 status
= asl_store_open_read(NULL
, &store
);
311 if (status
!= 0) return NULL
;
312 if (store
== NULL
) return NULL
;
315 out
= asl_store_match(store
, qlist
, &l64
, s64
, count
, duration
, direction
);
318 asl_store_close(store
);
323 static asl_msg_list_t
*
324 _do_store_search(asl_msg_t
*query
)
326 asl_msg_list_t
*out
, *qlist
= NULL
;
329 uint64_t last
= 0, start
= 0;
330 asl_store_t
*store
= NULL
;
331 const char *val
= NULL
;
333 /* check for "ASLMessageId >[=] n" and set start_id */
334 status
= asl_msg_lookup(query
, ASL_KEY_MSG_ID
, &val
, &op
);
335 if ((status
== 0) && (val
!= NULL
) && (op
& ASL_QUERY_OP_GREATER
))
337 if (op
& ASL_QUERY_OP_EQUAL
) start
= atoll(val
);
338 else start
= atoll(val
) + 1;
341 status
= asl_store_open_read(NULL
, &store
);
342 if (status
!= 0) return NULL
;
343 if (store
== NULL
) return NULL
;
347 qlist
= asl_msg_list_new();
348 asl_msg_list_append(qlist
, query
);
351 out
= asl_store_match(store
, qlist
, &last
, start
, 0, 0, 1);
352 asl_store_close(store
);
354 asl_msg_list_release(qlist
);
359 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
)
361 if (asl_store_location() == ASL_STORE_LOCATION_FILE
) return _do_store_match(qlist
, last
, start
, count
, duration
, direction
);
362 return _do_server_match(qlist
, last
, start
, count
, duration
, direction
);
366 asl_client_search(asl_client_t
*client
, asl_msg_t
*query
)
368 if (asl_store_location() == ASL_STORE_LOCATION_FILE
) return _do_store_search(query
);
369 return _do_server_search(query
);
374 #pragma mark output control
377 * Returns last filter value, or -1 on error.
378 * Note that this allows ASL_FILTER_MASK_TUNNEL (0x100) to be set.
379 * That is SPI that's used by some clients.
382 asl_client_set_filter(asl_client_t
*client
, int filter
)
384 if (client
== NULL
) return -1;
386 uint32_t allbits
= client
->filter
;
387 int last
= allbits
& (~EVAL_ACTION_MASK
);
388 client
->filter
= (allbits
& EVAL_ACTION_MASK
) | (filter
& (~EVAL_ACTION_MASK
));
394 asl_client_set_control(asl_client_t
*client
, uint32_t filter
)
396 if (client
== NULL
) return UINT32_MAX
;
398 uint32_t last
= client
->filter
;
399 client
->filter
= filter
;
404 asl_client_get_control(asl_client_t
*client
)
406 if (client
== NULL
) return UINT32_MAX
;
407 return client
->filter
;
411 asl_client_add_output_file(asl_client_t
*client
, int descriptor
, const char *mfmt
, const char *tfmt
, int filter
, int text_encoding
)
415 if (client
== NULL
) return ASL_STATUS_FAILED
;
417 for (i
= 0; i
< client
->out_count
; i
++)
419 if (client
->out_list
[i
].fd
== descriptor
)
421 /* update message format, time format, filter, and text encoding */
422 free(client
->out_list
[i
].mfmt
);
423 client
->out_list
[i
].mfmt
= NULL
;
424 if (mfmt
!= NULL
) client
->out_list
[i
].mfmt
= strdup(mfmt
);
426 free(client
->out_list
[i
].tfmt
);
427 client
->out_list
[i
].tfmt
= NULL
;
428 if (tfmt
!= NULL
) client
->out_list
[i
].tfmt
= strdup(tfmt
);
430 client
->out_list
[i
].encoding
= text_encoding
;
431 client
->out_list
[i
].filter
= filter
;
433 return ASL_STATUS_OK
;
437 if (client
->out_count
== 0) client
->out_list
= NULL
;
438 client
->out_list
= (asl_out_file_t
*)reallocf(client
->out_list
, (1 + client
->out_count
) * sizeof(asl_out_file_t
));
440 if (client
->out_list
== NULL
) return ASL_STATUS_FAILED
;
442 client
->out_list
[client
->out_count
].fd
= descriptor
;
443 client
->out_list
[client
->out_count
].encoding
= text_encoding
;
444 client
->out_list
[client
->out_count
].filter
= filter
;
445 client
->out_list
[client
->out_count
].mfmt
= NULL
;
446 if (mfmt
!= NULL
) client
->out_list
[client
->out_count
].mfmt
= strdup(mfmt
);
447 client
->out_list
[client
->out_count
].tfmt
= NULL
;
448 if (tfmt
!= NULL
) client
->out_list
[client
->out_count
].tfmt
= strdup(tfmt
);
452 return ASL_STATUS_OK
;
455 /* returns last filter value, or -1 on error */
457 asl_client_set_output_file_filter(asl_client_t
*client
, int descriptor
, int filter
)
462 if (client
== NULL
) return -1;
464 for (i
= 0; i
< client
->out_count
; i
++)
466 if (client
->out_list
[i
].fd
== descriptor
)
469 last
= client
->out_list
[i
].filter
;
470 client
->out_list
[i
].filter
= filter
;
479 asl_client_remove_output_file(asl_client_t
*client
, int descriptor
)
484 if (client
== NULL
) return ASL_STATUS_INVALID_ARG
;
486 if (client
->out_count
== 0) return ASL_STATUS_OK
;
489 for (i
= 0; i
< client
->out_count
; i
++)
491 if (client
->out_list
[i
].fd
== descriptor
)
498 if (x
== -1) return ASL_STATUS_OK
;
500 free(client
->out_list
[x
].mfmt
);
501 free(client
->out_list
[x
].tfmt
);
503 for (i
= x
+ 1; i
< client
->out_count
; i
++, x
++)
505 client
->out_list
[x
] = client
->out_list
[i
];
510 if (client
->out_count
== 0)
512 free(client
->out_list
);
513 client
->out_list
= NULL
;
517 client
->out_list
= (asl_out_file_t
*)reallocf(client
->out_list
, client
->out_count
* sizeof(asl_out_file_t
));
519 if (client
->out_list
== NULL
)
521 client
->out_count
= 0;
522 return ASL_STATUS_FAILED
;
526 return ASL_STATUS_OK
;
530 #pragma mark dictionary access
533 asl_client_kvdict(asl_client_t
*client
)
535 if (client
== NULL
) return NULL
;
536 return client
->kvdict
;
540 #pragma mark asl_object support
543 _jump_dealloc(asl_object_private_t
*obj
)
545 _asl_client_free_internal((asl_client_t
*)obj
);
549 _jump_append(asl_object_private_t
*obj
, asl_object_private_t
*newobj
, void *addr
)
551 int type
= asl_get_type((asl_object_t
)newobj
);
553 if (type
== ASL_TYPE_LIST
)
556 asl_msg_list_reset_iteration((asl_msg_list_t
*)newobj
, 0);
557 while (NULL
!= (msg
= asl_msg_list_next((asl_msg_list_t
*)newobj
)))
559 if (asl_client_internal_send((asl_object_t
)obj
, (asl_object_t
)msg
, addr
) != ASL_STATUS_OK
) return;
562 else if ((type
== ASL_TYPE_MSG
) || (type
== ASL_TYPE_QUERY
))
564 asl_client_internal_send((asl_object_t
)obj
, (asl_object_t
)newobj
, addr
);
568 static asl_object_private_t
*
569 _jump_search(asl_object_private_t
*obj
, asl_object_private_t
*query
)
571 int type
= asl_get_type((asl_object_t
)query
);
572 if ((query
!= NULL
) && (type
!= ASL_TYPE_MSG
) && (type
!= ASL_TYPE_QUERY
)) return NULL
;
574 return (asl_object_private_t
*)asl_client_search((asl_client_t
*)obj
, (asl_msg_t
*)query
);
577 static asl_object_private_t
*
578 _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
)
580 asl_msg_list_t
*out
= NULL
;
581 int type
= asl_get_type((asl_object_t
)qlist
);
583 if ((qlist
!= NULL
) && (type
!= ASL_TYPE_LIST
)) return NULL
;
585 out
= asl_client_match((asl_client_t
*)obj
, (asl_msg_list_t
*)qlist
, last
, start
, count
, duration
, dir
);
586 return (asl_object_private_t
*)out
;
589 __private_extern__
const asl_jump_table_t
*
590 asl_client_jump_table()
592 static const asl_jump_table_t jump
=
595 .dealloc
= &_jump_dealloc
,
596 .set_key_val_op
= NULL
,
598 .get_val_op_for_key
= NULL
,
599 .get_key_val_op_at_index
= NULL
,
603 .get_object_at_index
= NULL
,
604 .set_iteration_index
= NULL
,
605 .remove_object_at_index
= NULL
,
606 .append
= &_jump_append
,
608 .search
= &_jump_search
,
609 .match
= &_jump_match