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
); 
  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
); 
 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
) 
 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
) != 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
); 
 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