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