]> git.saurik.com Git - apple/syslog.git/blame - libsystem_asl.tproj/src/asl_client.c
syslog-385.tar.gz
[apple/syslog.git] / libsystem_asl.tproj / src / asl_client.c
CommitLineData
f3df4c03 1/*
5222c21d 2 * Copyright (c) 2015 Apple Inc. All rights reserved.
f3df4c03
A
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 */
af7d442c 51__private_extern__ ASL_STATUS asl_client_internal_send(asl_object_t client, asl_object_t msg, void *addr);
f3df4c03
A
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
5222c21d
A
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;
142
f3df4c03
A
143 if (client->options & ASL_OPT_STDERR)
144 {
145 /* only add stderr if it is valid */
146 if (fcntl(STDERR_FILENO, F_GETFD) >= 0)
147 {
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);
149 }
150 else
151 {
152 /* stderr has been closed, ignore ASL_OPT_STDERR flag */
153 client->options &= ~ASL_OPT_STDERR;
154 }
155 }
156
157 return client;
158}
159
160asl_client_t *
161asl_client_open_from_file(int descriptor, const char *ident, const char *facility)
162{
163 uint32_t status;
164 asl_client_t *client = asl_client_open(ident, facility, 0);
165 if (client == NULL) return NULL;
166
167 client->filter = ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG);
5222c21d 168 client->filter |= (EVAL_ACTIVE | EVAL_SEND_ASL);
f3df4c03
A
169
170 status = asl_file_open_write_fd(descriptor, &(client->aslfile));
171 if (status != ASL_STATUS_OK)
172 {
173 _asl_client_free_internal(client);
174 return NULL;
175 }
176
177 client->aslfileid = 1;
178
179 return client;
180}
181
182asl_client_t *
183asl_client_retain(asl_client_t *client)
184{
185 if (client == NULL) return NULL;
186 asl_retain((asl_object_t)client);
187 return client;
188}
189
190void
191asl_client_release(asl_client_t *client)
192{
193 if (client == NULL) return;
194 asl_release((asl_object_t)client);
195}
196
197#pragma mark -
198#pragma mark database access
199
200ASL_STATUS
201asl_client_send(asl_client_t *client, asl_msg_t *msg)
202{
af7d442c 203 return asl_client_internal_send((asl_object_t)client, (asl_object_t)msg, __builtin_return_address(0));
f3df4c03
A
204}
205
206static 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)
208{
209 char *str, *res = NULL;
210 uint32_t len, reslen, status;
211 uint64_t last64, start64, count64;
212 kern_return_t kstatus;
213 asl_msg_list_t *out;
214 caddr_t vmstr;
215 mach_port_t asl_server_port = asl_core_get_service_port(0);
216
217 if (asl_server_port == MACH_PORT_NULL) return NULL;
218
219 str = NULL;
220 if (qlist == NULL)
221 {
222 asprintf(&str, "0\n");
223 len = 3;
224 }
225 else
226 {
227 str = asl_msg_list_to_string(qlist, &len);
228 }
229
230 if (str == NULL) return NULL;
231
5222c21d 232 kstatus = vm_allocate(mach_task_self(), (vm_address_t *)&vmstr, len, VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_ASL));
f3df4c03
A
233 if (kstatus != KERN_SUCCESS) return NULL;
234
235 memmove(vmstr, str, len);
236 free(str);
237
238 last64 = 0;
239 start64 = start;
240 count64 = count;
241
242 kstatus = _asl_server_match(asl_server_port, vmstr, len, start64, count64, duration, dir, (caddr_t *)&res, &reslen, &last64, (int *)&status);
86a8bcf5 243 if (kstatus != KERN_SUCCESS) return NULL;
f3df4c03 244
86a8bcf5 245 *last = last64;
f3df4c03
A
246 out = asl_msg_list_from_string(res);
247 vm_deallocate(mach_task_self(), (vm_address_t)res, reslen);
248
249 return out;
250}
251
252static asl_msg_list_t *
253_do_server_search(asl_msg_t *q)
254{
255 asl_msg_list_t *out;
256 char *qstr, *str, *res = NULL;
257 uint32_t len, reslen = 0, status = ASL_STATUS_OK;
258 uint64_t cmax = 0;
259 kern_return_t kstatus;
260 caddr_t vmstr;
261 mach_port_t asl_server_port = asl_core_get_service_port(0);
262
263 if (asl_server_port == MACH_PORT_NULL) return NULL;
264
265 len = 0;
266 qstr = asl_msg_to_string(q, &len);
267
268 str = NULL;
269 if (qstr == NULL)
270 {
271 asprintf(&str, "0\n");
272 len = 3;
273 }
274 else
275 {
276 asprintf(&str, "1\n%s\n", qstr);
277 len += 3;
278 free(qstr);
279 }
280
281 if (str == NULL) return NULL;
282
5222c21d 283 kstatus = vm_allocate(mach_task_self(), (vm_address_t *)&vmstr, len, VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_ASL));
f3df4c03
A
284 if (kstatus != KERN_SUCCESS) return NULL;
285
286 memmove(vmstr, str, len);
287 free(str);
288
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;
291
292 out = asl_msg_list_from_string(res);
293 vm_deallocate(mach_task_self(), (vm_address_t)res, reslen);
294
295 return out;
296}
297
298static 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)
300{
301 asl_msg_list_t *out;
302 uint32_t status;
303 uint64_t l64 = 0, s64;
304 asl_store_t *store = NULL;
305
306 uint32_t len;
307 char *str = asl_msg_list_to_string(qlist, &len);
308 free(str);
309
310 status = asl_store_open_read(NULL, &store);
311 if (status != 0) return NULL;
312 if (store == NULL) return NULL;
313
314 s64 = start;
315 out = asl_store_match(store, qlist, &l64, s64, count, duration, direction);
316 *last = l64;
317
318 asl_store_close(store);
319
320 return out;
321}
322
323static asl_msg_list_t *
324_do_store_search(asl_msg_t *query)
325{
326 asl_msg_list_t *out, *qlist = NULL;
327 uint32_t status;
328 uint16_t op;
329 uint64_t last = 0, start = 0;
330 asl_store_t *store = NULL;
331 const char *val = NULL;
332
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))
336 {
337 if (op & ASL_QUERY_OP_EQUAL) start = atoll(val);
338 else start = atoll(val) + 1;
339 }
340
341 status = asl_store_open_read(NULL, &store);
342 if (status != 0) return NULL;
343 if (store == NULL) return NULL;
344
345 if (query != NULL)
346 {
347 qlist = asl_msg_list_new();
348 asl_msg_list_append(qlist, query);
349 }
350
351 out = asl_store_match(store, qlist, &last, start, 0, 0, 1);
352 asl_store_close(store);
353
354 asl_msg_list_release(qlist);
355 return out;
356}
357
358asl_msg_list_t *
359asl_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{
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);
363}
364
365asl_msg_list_t *
366asl_client_search(asl_client_t *client, asl_msg_t *query)
367{
368 if (asl_store_location() == ASL_STORE_LOCATION_FILE) return _do_store_search(query);
369 return _do_server_search(query);
370}
371
372
373#pragma mark -
374#pragma mark output control
375
5222c21d
A
376/*
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.
380 */
f3df4c03
A
381int
382asl_client_set_filter(asl_client_t *client, int filter)
383{
f3df4c03 384 if (client == NULL) return -1;
5222c21d
A
385
386 uint32_t allbits = client->filter;
387 int last = allbits & (~EVAL_ACTION_MASK);
388 client->filter = (allbits & EVAL_ACTION_MASK) | (filter & (~EVAL_ACTION_MASK));
389 return last;
390}
391
392/* SPI */
393uint32_t
394asl_client_set_control(asl_client_t *client, uint32_t filter)
395{
396 if (client == NULL) return UINT32_MAX;
397
398 uint32_t last = client->filter;
f3df4c03
A
399 client->filter = filter;
400 return last;
401}
402
5222c21d
A
403uint32_t
404asl_client_get_control(asl_client_t *client)
405{
406 if (client == NULL) return UINT32_MAX;
407 return client->filter;
408}
409
f3df4c03
A
410ASL_STATUS
411asl_client_add_output_file(asl_client_t *client, int descriptor, const char *mfmt, const char *tfmt, int filter, int text_encoding)
412{
413 uint32_t i;
414
415 if (client == NULL) return ASL_STATUS_FAILED;
416
417 for (i = 0; i < client->out_count; i++)
418 {
419 if (client->out_list[i].fd == descriptor)
420 {
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);
425
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);
429
430 client->out_list[i].encoding = text_encoding;
431 client->out_list[i].filter = filter;
432
433 return ASL_STATUS_OK;
434 }
435 }
436
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));
439
440 if (client->out_list == NULL) return ASL_STATUS_FAILED;
441
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;
5222c21d 445 client->out_list[client->out_count].mfmt = NULL;
f3df4c03 446 if (mfmt != NULL) client->out_list[client->out_count].mfmt = strdup(mfmt);
5222c21d 447 client->out_list[client->out_count].tfmt = NULL;
f3df4c03
A
448 if (tfmt != NULL) client->out_list[client->out_count].tfmt = strdup(tfmt);
449
450 client->out_count++;
451
452 return ASL_STATUS_OK;
453}
454
455/* returns last filter value, or -1 on error */
456int
457asl_client_set_output_file_filter(asl_client_t *client, int descriptor, int filter)
458{
459 uint32_t i;
460 int last = 0;
461
462 if (client == NULL) return -1;
463
464 for (i = 0; i < client->out_count; i++)
465 {
466 if (client->out_list[i].fd == descriptor)
467 {
468 /* update filter */
469 last = client->out_list[i].filter;
470 client->out_list[i].filter = filter;
471 break;
472 }
473 }
474
475 return last;
476}
477
478ASL_STATUS
479asl_client_remove_output_file(asl_client_t *client, int descriptor)
480{
481 uint32_t i;
482 int x;
483
484 if (client == NULL) return ASL_STATUS_INVALID_ARG;
485
486 if (client->out_count == 0) return ASL_STATUS_OK;
487
488 x = -1;
489 for (i = 0; i < client->out_count; i++)
490 {
491 if (client->out_list[i].fd == descriptor)
492 {
493 x = i;
494 break;
495 }
496 }
497
498 if (x == -1) return ASL_STATUS_OK;
499
500 free(client->out_list[x].mfmt);
501 free(client->out_list[x].tfmt);
502
503 for (i = x + 1; i < client->out_count; i++, x++)
504 {
505 client->out_list[x] = client->out_list[i];
506 }
507
508 client->out_count--;
509
510 if (client->out_count == 0)
511 {
512 free(client->out_list);
513 client->out_list = NULL;
514 }
515 else
516 {
517 client->out_list = (asl_out_file_t *)reallocf(client->out_list, client->out_count * sizeof(asl_out_file_t));
518
519 if (client->out_list == NULL)
520 {
521 client->out_count = 0;
522 return ASL_STATUS_FAILED;
523 }
524 }
525
526 return ASL_STATUS_OK;
527}
528
529#pragma mark -
530#pragma mark dictionary access
531
532asl_msg_t *
533asl_client_kvdict(asl_client_t *client)
534{
535 if (client == NULL) return NULL;
536 return client->kvdict;
537}
538
539#pragma mark -
540#pragma mark asl_object support
541
542static void
543_jump_dealloc(asl_object_private_t *obj)
544{
545 _asl_client_free_internal((asl_client_t *)obj);
546}
547
548static void
af7d442c 549_jump_append(asl_object_private_t *obj, asl_object_private_t *newobj, void *addr)
f3df4c03
A
550{
551 int type = asl_get_type((asl_object_t)newobj);
552
553 if (type == ASL_TYPE_LIST)
554 {
555 asl_msg_t *msg;
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)))
558 {
af7d442c 559 if (asl_client_internal_send((asl_object_t)obj, (asl_object_t)msg, addr) != ASL_STATUS_OK) return;
f3df4c03
A
560 }
561 }
562 else if ((type == ASL_TYPE_MSG) || (type == ASL_TYPE_QUERY))
563 {
af7d442c 564 asl_client_internal_send((asl_object_t)obj, (asl_object_t)newobj, addr);
f3df4c03
A
565 }
566}
567
568static asl_object_private_t *
569_jump_search(asl_object_private_t *obj, asl_object_private_t *query)
570{
571 int type = asl_get_type((asl_object_t)query);
572 if ((query != NULL) && (type != ASL_TYPE_MSG) && (type != ASL_TYPE_QUERY)) return NULL;
573
574 return (asl_object_private_t *)asl_client_search((asl_client_t *)obj, (asl_msg_t *)query);
575}
576
577static 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)
579{
580 asl_msg_list_t *out = NULL;
581 int type = asl_get_type((asl_object_t)qlist);
582
583 if ((qlist != NULL) && (type != ASL_TYPE_LIST)) return NULL;
584
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;
587}
588
589__private_extern__ const asl_jump_table_t *
590asl_client_jump_table()
591{
592 static const asl_jump_table_t jump =
593 {
594 .alloc = NULL,
595 .dealloc = &_jump_dealloc,
596 .set_key_val_op = NULL,
597 .unset_key = NULL,
598 .get_val_op_for_key = NULL,
599 .get_key_val_op_at_index = NULL,
600 .count = NULL,
601 .next = NULL,
602 .prev = NULL,
603 .get_object_at_index = NULL,
604 .set_iteration_index = NULL,
605 .remove_object_at_index = NULL,
606 .append = &_jump_append,
607 .prepend = NULL,
608 .search = &_jump_search,
609 .match = &_jump_match
610 };
611
612 return &jump;
613}
614