]>
Commit | Line | Data |
---|---|---|
f3df4c03 A |
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 | ||
56 | static 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 | ||
81 | asl_client_t * | |
82 | asl_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 | ||
156 | asl_client_t * | |
157 | asl_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 | ||
177 | asl_client_t * | |
178 | asl_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 | ||
185 | void | |
186 | asl_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 | ||
195 | ASL_STATUS | |
196 | asl_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 | ||
201 | static 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 | ||
246 | static 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 | ||
292 | static 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 | ||
317 | static 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 | ||
352 | asl_msg_list_t * | |
353 | 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) | |
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 | ||
359 | asl_msg_list_t * | |
360 | asl_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 */ | |
371 | int | |
372 | asl_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 | ||
382 | ASL_STATUS | |
383 | asl_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 */ | |
426 | int | |
427 | asl_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 | ||
448 | ASL_STATUS | |
449 | asl_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 | ||
502 | asl_msg_t * | |
503 | asl_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 | ||
512 | static void | |
513 | _jump_dealloc(asl_object_private_t *obj) | |
514 | { | |
515 | _asl_client_free_internal((asl_client_t *)obj); | |
516 | } | |
517 | ||
518 | static 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 | ||
538 | static 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 | ||
547 | static 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 * | |
560 | asl_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 |