1/* 2 * Copyright (c) 2013 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 <fcntl.h> 34#include <time.h> 35#include <sys/time.h> 36#include <sys/types.h> 37#include <libkern/OSAtomic.h> 38#include <crt_externs.h> 39#include <asl.h> 40#include <asl_private.h> 41#include <asl_ipc.h> 42#include <asl_core.h> 43#include <asl_client.h> 44 45#define PUBLIC_OPT_MASK 0x000000ff 46 47/* private asl_file SPI */ 48__private_extern__ ASL_STATUS asl_file_open_write_fd(int descriptor, asl_file_t **s); 49 50/* private asl SPI */ 51__private_extern__ ASL_STATUS asl_client_internal_send(asl_object_t client, asl_object_t msg); 52 53#pragma mark - 54#pragma mark asl_client_t 55 56static void 57_asl_client_free_internal(asl_client_t *client) 58{ 59 uint32_t i; 60 61 if (client == NULL) return; 62 63 if (client->kvdict != NULL) asl_msg_release(client->kvdict); 64 client->kvdict = NULL; 65 66 if (client->aslfile != NULL) asl_file_close(client->aslfile); 67 client->aslfile = NULL; 68 69 for (i = 0; i < client->out_count; i++) 70 { 71 free(client->out_list[i].mfmt); 72 free(client->out_list[i].tfmt); 73 } 74 75 free(client->out_list); 76 client->out_list = NULL; 77 78 free(client); 79} 80 81asl_client_t * 82asl_client_open(const char *ident, const char *facility, uint32_t opts) 83{ 84 asl_client_t *client = (asl_client_t *)calloc(1, sizeof(asl_client_t)); 85 if (client == NULL) 86 { 87 errno = ENOMEM; 88 return NULL; 89 } 90 91 client->asl_type = ASL_TYPE_CLIENT; 92 client->refcount = 1; 93 94 client->kvdict = asl_msg_new(ASL_TYPE_MSG); 95 if (client->kvdict == NULL) 96 { 97 asl_client_release(client); 98 errno = ENOMEM; 99 return NULL; 100 } 101 102 client->options = opts & PUBLIC_OPT_MASK; 103 104 client->pid = getpid(); 105 client->uid = getuid(); 106 client->gid = getgid(); 107 108 if (ident != NULL) 109 { 110 asl_msg_set_key_val(client->kvdict, ASL_KEY_SENDER, ident); 111 } 112 else 113 { 114 char *name = *(*_NSGetArgv()); 115 if (name != NULL) 116 { 117 char *x = strrchr(name, '/'); 118 if (x != NULL) x++; 119 else x = name; 120 asl_msg_set_key_val(client->kvdict, ASL_KEY_SENDER, x); 121 } 122 } 123 124 if (facility != NULL) 125 { 126 asl_msg_set_key_val(client->kvdict, ASL_KEY_FACILITY, facility); 127 } 128 else if (client->uid == 0) 129 { 130 asl_msg_set_key_val(client->kvdict, ASL_KEY_FACILITY, asl_syslog_faciliy_num_to_name(LOG_DAEMON)); 131 } 132 else 133 { 134 asl_msg_set_key_val(client->kvdict, ASL_KEY_FACILITY, asl_syslog_faciliy_num_to_name(LOG_USER)); 135 } 136 137 client->filter = ASL_FILTER_MASK_UPTO(ASL_LEVEL_NOTICE); 138 139 if (client->options & ASL_OPT_STDERR) 140 { 141 /* only add stderr if it is valid */ 142 if (fcntl(STDERR_FILENO, F_GETFD) >= 0) 143 { 144 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); 145 } 146 else 147 { 148 /* stderr has been closed, ignore ASL_OPT_STDERR flag */ 149 client->options &= ~ASL_OPT_STDERR; 150 } 151 } 152 153 return client; 154} 155 156asl_client_t * 157asl_client_open_from_file(int descriptor, const char *ident, const char *facility) 158{ 159 uint32_t status; 160 asl_client_t *client = asl_client_open(ident, facility, 0); 161 if (client == NULL) return NULL; 162 163 client->filter = ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG); 164 165 status = asl_file_open_write_fd(descriptor, &(client->aslfile)); 166 if (status != ASL_STATUS_OK) 167 { 168 _asl_client_free_internal(client); 169 return NULL; 170 } 171 172 client->aslfileid = 1; 173 174 return client; 175} 176 177asl_client_t * 178asl_client_retain(asl_client_t *client) 179{ 180 if (client == NULL) return NULL; 181 asl_retain((asl_object_t)client); 182 return client; 183} 184 185void 186asl_client_release(asl_client_t *client) 187{ 188 if (client == NULL) return; 189 asl_release((asl_object_t)client); 190} 191 192#pragma mark - 193#pragma mark database access 194 195ASL_STATUS 196asl_client_send(asl_client_t *client, asl_msg_t *msg) 197{ 198 return asl_client_internal_send((asl_object_t)client, (asl_object_t)msg); 199} 200 201static asl_msg_list_t * 202_do_server_match(asl_msg_list_t *qlist, size_t *last, size_t start, size_t count, uint32_t duration, int dir) 203{ 204 char *str, *res = NULL; 205 uint32_t len, reslen, status; 206 uint64_t last64, start64, count64; 207 kern_return_t kstatus; 208 asl_msg_list_t *out; 209 caddr_t vmstr; 210 mach_port_t asl_server_port = asl_core_get_service_port(0); 211 212 if (asl_server_port == MACH_PORT_NULL) return NULL; 213 214 str = NULL; 215 if (qlist == NULL) 216 { 217 asprintf(&str, "0\n"); 218 len = 3; 219 } 220 else 221 { 222 str = asl_msg_list_to_string(qlist, &len); 223 } 224 225 if (str == NULL) return NULL; 226 227 kstatus = vm_allocate(mach_task_self(), (vm_address_t *)&vmstr, len, TRUE); 228 if (kstatus != KERN_SUCCESS) return NULL; 229 230 memmove(vmstr, str, len); 231 free(str); 232 233 last64 = 0; 234 start64 = start; 235 count64 = count; 236 237 kstatus = _asl_server_match(asl_server_port, vmstr, len, start64, count64, duration, dir, (caddr_t *)&res, &reslen, &last64, (int *)&status); 238 *last = last64; 239 240 out = asl_msg_list_from_string(res); 241 vm_deallocate(mach_task_self(), (vm_address_t)res, reslen); 242 243 return out; 244} 245 246static asl_msg_list_t * 247_do_server_search(asl_msg_t *q) 248{ 249 asl_msg_list_t *out; 250 char *qstr, *str, *res = NULL; 251 uint32_t len, reslen = 0, status = ASL_STATUS_OK; 252 uint64_t cmax = 0; 253 kern_return_t kstatus; 254 caddr_t vmstr; 255 mach_port_t asl_server_port = asl_core_get_service_port(0); 256 257 if (asl_server_port == MACH_PORT_NULL) return NULL; 258 259 len = 0; 260 qstr = asl_msg_to_string(q, &len); 261 262 str = NULL; 263 if (qstr == NULL) 264 { 265 asprintf(&str, "0\n"); 266 len = 3; 267 } 268 else 269 { 270 asprintf(&str, "1\n%s\n", qstr); 271 len += 3; 272 free(qstr); 273 } 274 275 if (str == NULL) return NULL; 276 277 kstatus = vm_allocate(mach_task_self(), (vm_address_t *)&vmstr, len, TRUE); 278 if (kstatus != KERN_SUCCESS) return NULL; 279 280 memmove(vmstr, str, len); 281 free(str); 282 283 kstatus = _asl_server_query_2(asl_server_port, vmstr, len, 0, 0, 0, (caddr_t *)&res, &reslen, &cmax, (int *)&status); 284 if (kstatus != KERN_SUCCESS) return NULL; 285 286 out = asl_msg_list_from_string(res); 287 vm_deallocate(mach_task_self(), (vm_address_t)res, reslen); 288 289 return out; 290} 291 292static asl_msg_list_t * 293_do_store_match(asl_msg_list_t *qlist, size_t *last, size_t start, size_t count, uint32_t duration, int32_t direction) 294{ 295 asl_msg_list_t *out; 296 uint32_t status; 297 uint64_t l64 = 0, s64; 298 asl_store_t *store = NULL; 299 300 uint32_t len; 301 char *str = asl_msg_list_to_string(qlist, &len); 302 free(str); 303 304 status = asl_store_open_read(NULL, &store); 305 if (status != 0) return NULL; 306 if (store == NULL) return NULL; 307 308 s64 = start; 309 out = asl_store_match(store, qlist, &l64, s64, count, duration, direction); 310 *last = l64; 311 312 asl_store_close(store); 313 314 return out; 315} 316 317static asl_msg_list_t * 318_do_store_search(asl_msg_t *query) 319{ 320 asl_msg_list_t *out, *qlist = NULL; 321 uint32_t status; 322 uint16_t op; 323 uint64_t last = 0, start = 0; 324 asl_store_t *store = NULL; 325 const char *val = NULL; 326 327 /* check for "ASLMessageId >[=] n" and set start_id */ 328 status = asl_msg_lookup(query, ASL_KEY_MSG_ID, &val, &op); 329 if ((status == 0) && (val != NULL) && (op & ASL_QUERY_OP_GREATER)) 330 { 331 if (op & ASL_QUERY_OP_EQUAL) start = atoll(val); 332 else start = atoll(val) + 1; 333 } 334 335 status = asl_store_open_read(NULL, &store); 336 if (status != 0) return NULL; 337 if (store == NULL) return NULL; 338 339 if (query != NULL) 340 { 341 qlist = asl_msg_list_new(); 342 asl_msg_list_append(qlist, query); 343 } 344 345 out = asl_store_match(store, qlist, &last, start, 0, 0, 1); 346 asl_store_close(store); 347 348 asl_msg_list_release(qlist); 349 return out; 350} 351 352asl_msg_list_t * 353asl_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) 354{ 355 if (asl_store_location() == ASL_STORE_LOCATION_FILE) return _do_store_match(qlist, last, start, count, duration, direction); 356 return _do_server_match(qlist, last, start, count, duration, direction); 357} 358 359asl_msg_list_t * 360asl_client_search(asl_client_t *client, asl_msg_t *query) 361{ 362 if (asl_store_location() == ASL_STORE_LOCATION_FILE) return _do_store_search(query); 363 return _do_server_search(query); 364} 365 366 367#pragma mark - 368#pragma mark output control 369 370/* returns last filter value, or -1 on error */ 371int 372asl_client_set_filter(asl_client_t *client, int filter) 373{ 374 int last; 375 376 if (client == NULL) return -1; 377 last = client->filter; 378 client->filter = filter; 379 return last; 380} 381 382ASL_STATUS 383asl_client_add_output_file(asl_client_t *client, int descriptor, const char *mfmt, const char *tfmt, int filter, int text_encoding) 384{ 385 uint32_t i; 386 387 if (client == NULL) return ASL_STATUS_FAILED; 388 389 for (i = 0; i < client->out_count; i++) 390 { 391 if (client->out_list[i].fd == descriptor) 392 { 393 /* update message format, time format, filter, and text encoding */ 394 free(client->out_list[i].mfmt); 395 client->out_list[i].mfmt = NULL; 396 if (mfmt != NULL) client->out_list[i].mfmt = strdup(mfmt); 397 398 free(client->out_list[i].tfmt); 399 client->out_list[i].tfmt = NULL; 400 if (tfmt != NULL) client->out_list[i].tfmt = strdup(tfmt); 401 402 client->out_list[i].encoding = text_encoding; 403 client->out_list[i].filter = filter; 404 405 return ASL_STATUS_OK; 406 } 407 } 408 409 if (client->out_count == 0) client->out_list = NULL; 410 client->out_list = (asl_out_file_t *)reallocf(client->out_list, (1 + client->out_count) * sizeof(asl_out_file_t)); 411 412 if (client->out_list == NULL) return ASL_STATUS_FAILED; 413 414 client->out_list[client->out_count].fd = descriptor; 415 client->out_list[client->out_count].encoding = text_encoding; 416 client->out_list[client->out_count].filter = filter; 417 if (mfmt != NULL) client->out_list[client->out_count].mfmt = strdup(mfmt); 418 if (tfmt != NULL) client->out_list[client->out_count].tfmt = strdup(tfmt); 419 420 client->out_count++; 421 422 return ASL_STATUS_OK; 423} 424 425/* returns last filter value, or -1 on error */ 426int 427asl_client_set_output_file_filter(asl_client_t *client, int descriptor, int filter) 428{ 429 uint32_t i; 430 int last = 0; 431 432 if (client == NULL) return -1; 433 434 for (i = 0; i < client->out_count; i++) 435 { 436 if (client->out_list[i].fd == descriptor) 437 { 438 /* update filter */ 439 last = client->out_list[i].filter; 440 client->out_list[i].filter = filter; 441 break; 442 } 443 } 444 445 return last; 446} 447 448ASL_STATUS 449asl_client_remove_output_file(asl_client_t *client, int descriptor) 450{ 451 uint32_t i; 452 int x; 453 454 if (client == NULL) return ASL_STATUS_INVALID_ARG; 455 456 if (client->out_count == 0) return ASL_STATUS_OK; 457 458 x = -1; 459 for (i = 0; i < client->out_count; i++) 460 { 461 if (client->out_list[i].fd == descriptor) 462 { 463 x = i; 464 break; 465 } 466 } 467 468 if (x == -1) return ASL_STATUS_OK; 469 470 free(client->out_list[x].mfmt); 471 free(client->out_list[x].tfmt); 472 473 for (i = x + 1; i < client->out_count; i++, x++) 474 { 475 client->out_list[x] = client->out_list[i]; 476 } 477 478 client->out_count--; 479 480 if (client->out_count == 0) 481 { 482 free(client->out_list); 483 client->out_list = NULL; 484 } 485 else 486 { 487 client->out_list = (asl_out_file_t *)reallocf(client->out_list, client->out_count * sizeof(asl_out_file_t)); 488 489 if (client->out_list == NULL) 490 { 491 client->out_count = 0; 492 return ASL_STATUS_FAILED; 493 } 494 } 495 496 return ASL_STATUS_OK; 497} 498 499#pragma mark - 500#pragma mark dictionary access 501 502asl_msg_t * 503asl_client_kvdict(asl_client_t *client) 504{ 505 if (client == NULL) return NULL; 506 return client->kvdict; 507} 508 509#pragma mark - 510#pragma mark asl_object support 511 512static void 513_jump_dealloc(asl_object_private_t *obj) 514{ 515 _asl_client_free_internal((asl_client_t *)obj); 516} 517 518static void 519_jump_append(asl_object_private_t *obj, asl_object_private_t *newobj) 520{ 521 int type = asl_get_type((asl_object_t)newobj); 522 523 if (type == ASL_TYPE_LIST) 524 { 525 asl_msg_t *msg; 526 asl_msg_list_reset_iteration((asl_msg_list_t *)newobj, 0); 527 while (NULL != (msg = asl_msg_list_next((asl_msg_list_t *)newobj))) 528 { 529 if (asl_client_internal_send((asl_object_t)obj, (asl_object_t)msg) != ASL_STATUS_OK) return; 530 } 531 } 532 else if ((type == ASL_TYPE_MSG) || (type == ASL_TYPE_QUERY)) 533 { 534 asl_client_internal_send((asl_object_t)obj, (asl_object_t)newobj); 535 } 536} 537 538static asl_object_private_t * 539_jump_search(asl_object_private_t *obj, asl_object_private_t *query) 540{ 541 int type = asl_get_type((asl_object_t)query); 542 if ((query != NULL) && (type != ASL_TYPE_MSG) && (type != ASL_TYPE_QUERY)) return NULL; 543 544 return (asl_object_private_t *)asl_client_search((asl_client_t *)obj, (asl_msg_t *)query); 545} 546 547static asl_object_private_t * 548_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) 549{ 550 asl_msg_list_t *out = NULL; 551 int type = asl_get_type((asl_object_t)qlist); 552 553 if ((qlist != NULL) && (type != ASL_TYPE_LIST)) return NULL; 554 555 out = asl_client_match((asl_client_t *)obj, (asl_msg_list_t *)qlist, last, start, count, duration, dir); 556 return (asl_object_private_t *)out; 557} 558 559__private_extern__ const asl_jump_table_t * 560asl_client_jump_table() 561{ 562 static const asl_jump_table_t jump = 563 { 564 .alloc = NULL, 565 .dealloc = &_jump_dealloc, 566 .set_key_val_op = NULL, 567 .unset_key = NULL, 568 .get_val_op_for_key = NULL, 569 .get_key_val_op_at_index = NULL, 570 .count = NULL, 571 .next = NULL, 572 .prev = NULL, 573 .get_object_at_index = NULL, 574 .set_iteration_index = NULL, 575 .remove_object_at_index = NULL, 576 .append = &_jump_append, 577 .prepend = NULL, 578 .search = &_jump_search, 579 .match = &_jump_match 580 }; 581 582 return &jump; 583} 584 585