xref: /NextBSD/usr.bin/aslutil/syslog.c (revision 33da5adc555b3bc29986eeadca03829e4ad06b1e)
1 /*
2  * Copyright (c) 2007-2011 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 <TargetConditionals.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <ctype.h>
29 #include <time.h>
30 #include <string.h>
31 #include <errno.h>
32 #include <dirent.h>
33 #include <fcntl.h>
34 #include <sys/socket.h>
35 #include <sys/sysctl.h>
36 #include <sys/user.h>
37 #include <netinet/in.h>
38 #include <arpa/inet.h>
39 #include <mach/mach.h>
40 #include <servers/bootstrap.h>
41 #include <bootstrap_priv.h>
42 #include <netdb.h>
43 #include <notify.h>
44 #include <asl.h>
45 #include <asl_msg.h>
46 #include <asl_msg_list.h>
47 #include <asl_private.h>
48 #include "asl_ipc.h"
49 #include <asl_core.h>
50 #include <asl_store.h>
51 #include <asl_file.h>
52 #include <asl_client.h>
53 #include "asl_common.h"
54 
55 #define MOD_CASE_FOLD 'C'
56 #define MOD_REGEX     'R'
57 #define MOD_SUBSTRING 'S'
58 #define MOD_PREFIX    'A'
59 #define MOD_SUFFIX    'Z'
60 #define MOD_NUMERIC   'N'
61 
62 #define OP_EQ "eq"
63 #define OP_NE "ne"
64 #define OP_GT "gt"
65 #define OP_GE "ge"
66 #define OP_LT "lt"
67 #define OP_LE "le"
68 
69 #define ASL_QUERY_OP_NOT	0x1000
70 
71 #define QUERY_FLAG_SEARCH_REVERSE 0x00000001
72 
73 #define FACILITY_CONSOLE "com.apple.console"
74 
75 #define SEARCH_EOF -1
76 #define SEARCH_NULL 0
77 #define SEARCH_MATCH 1
78 
79 #define PROC_NOT_FOUND -1
80 #define PROC_NOT_UNIQUE -2
81 
82 #define RC_MASTER -1
83 
84 #define CHUNK 64
85 #define forever for(;;)
86 
87 #define SEND_FORMAT_LEGACY 0
88 #define SEND_FORMAT_ASL 1
89 
90 #define FORMAT_RAW		0x00000100
91 #define FORMAT_LEGACY	0x00000200
92 #define FORMAT_STD		0x00000400
93 #define FORMAT_XML		0x00000800
94 #define COMPRESS_DUPS   0x00010000
95 
96 #define EXPORT			0x00000100
97 
98 #define ASL_FILTER_MASK_PACEWNID 0xff
99 #define ASL_FILTER_MASK_PACEWNI  0x7f
100 #define ASL_FILTER_MASK_PACEWN   0x3f
101 #define ASL_FILTER_MASK_PACEW    0x1f
102 #define ASL_FILTER_MASK_PACE     0x0f
103 #define ASL_FILTER_MASK_PAC      0x07
104 
105 #define FETCH_BATCH	1024
106 #define MAX_RANDOM 8192
107 
108 #define DB_SELECT_ASL     0
109 #define DB_SELECT_STORE   1
110 #define DB_SELECT_FILES   2
111 #define DB_SELECT_SYSLOGD 3
112 #define DB_SELECT_LEGACY  4
113 
114 /* STD and BSD format messages start with 'DAY MMM DD HH:MM:SS ' timestamp */
115 #define STD_BSD_DATE_LEN 20
116 
117 /* Max message size for direct watch */
118 #define MAX_DIRECT_SIZE 16384
119 
120 /* Buffer for direct watch data */
121 #define DIRECT_BUF_SIZE 1024
122 
123 static asl_file_list_t *db_files = NULL;
124 static asl_store_t *store = NULL;
125 static asl_file_t *legacy = NULL;
126 static asl_file_t *export = NULL;
127 static const char *sort_key = NULL;
128 static const char *sort_key_2 = NULL;
129 static int sort_numeric = 0;
130 static char *last_printmsg_str = NULL;
131 static int last_printmsg_count = 0;
132 static const char *tfmt = NULL;
133 
134 #if TARGET_OS_EMBEDDED
135 static uint32_t dbselect = DB_SELECT_SYSLOGD;
136 #else
137 static uint32_t dbselect = DB_SELECT_ASL;
138 #endif
139 
140 /* notify SPI */
141 uint32_t notify_register_plain(const char *name, int *out_token);
142 
143 //extern asl_msg_t *asl_msg_from_string(const char *buf);
144 //extern char *asl_list_to_string(asl_msg_list_t *list, uint32_t *outlen);
145 //extern asl_msg_list_t *asl_list_from_string(const char *buf);
146 //extern int asl_msg_cmp(asl_msg_t *a, asl_msg_t *b);
147 asl_msg_t *_asl_server_control_query(void);
148 extern time_t asl_parse_time(const char *in);
149 /* END PRIVATE API */
150 
151 static mach_port_t asl_server_port = MACH_PORT_NULL;
152 
153 static const char *myname = "syslog";
154 
155 /* forward */
156 asl_msg_list_t *syslogd_query(asl_msg_list_t *q, uint64_t start, int count, int dir, uint64_t *last);
157 static void printmsg(FILE *f, asl_msg_t *msg, char *fmt, int pflags);
158 
159 void
usage()160 usage()
161 {
162 	fprintf(stderr, "usage:\n");
163 	fprintf(stderr, "%s -s [-r host] [-l level] message...\n", myname);
164 	fprintf(stderr, "   send a message\n");
165 	fprintf(stderr, "\n");
166 	fprintf(stderr, "%s -s [-r host] -k key val [key val]...\n", myname);
167 	fprintf(stderr, "   send a message with the given keys and values\n");
168 	fprintf(stderr, "\n");
169 	fprintf(stderr, "%s -c process [filter]\n", myname);
170 	fprintf(stderr, "   get (set if filter is specified) syslog filter for process (pid or name)\n");
171 	fprintf(stderr, "   level may be any combination of the characters \"p a c e w n i d\"\n");
172 	fprintf(stderr, "   p = Emergency (\"Panic\")\n");
173 	fprintf(stderr, "   a = Alert\n");
174 	fprintf(stderr, "   c = Critical\n");
175 	fprintf(stderr, "   e = Error\n");
176 	fprintf(stderr, "   w = Warning\n");
177 	fprintf(stderr, "   n = Notice\n");
178 	fprintf(stderr, "   i = Info\n");
179 	fprintf(stderr, "   d = Debug\n");
180 	fprintf(stderr, "   a minus sign preceding a single letter means \"up to\" that level\n");
181 	fprintf(stderr, "\n");
182 	fprintf(stderr, "%s -config [params...]\n", myname);
183 	fprintf(stderr, "   without params, fetch and print syslogd parameters and statistics\n");
184 	fprintf(stderr, "   otherwise, set or reset syslogd configuration parameters\n");
185 	fprintf(stderr, "\n");
186 	fprintf(stderr, "%s -module [name [action]]\n", myname);
187 	fprintf(stderr, "   with no name, prints configuration for all ASL output modules\n");
188 	fprintf(stderr, "   with name and no action, prints configuration for named ASL output module\n");
189 	fprintf(stderr, "   supported actions - module name required, use '*' (with single quotes) for all modules:\n");
190 	fprintf(stderr, "       enable [01]          enables (or disables with 0) named module\n");
191 	fprintf(stderr, "                            does not apply to com.apple.asl when '*' is used\n");
192 	fprintf(stderr, "       checkpoint [file]    checkpoints all files or specified file for named module\n");
193 	fprintf(stderr, "\n");
194 	fprintf(stderr, "%s [-f file...] [-d path...] [-x file] [-w [N]] [-F format] [-nocompress] [-u] [-sort key1 [key2]] [-nsort key1 [key2]] [-k key [[op] val]]... [-o -k key [[op] val]] ...]...\n", myname);
195 	fprintf(stderr, "   -f     read named file[s], rather than standard log message store.\n");
196 	fprintf(stderr, "   -d     read all file in named directory path, rather than standard log message store.\n");
197 	fprintf(stderr, "   -x     export to named ASL format file, rather than printing\n");
198 	fprintf(stderr, "   -w     watch data store (^C to quit)\n");
199 	fprintf(stderr, "          prints the last N matching lines (default 10) before waiting\n");
200 	fprintf(stderr, "          \"-w all\" prints all matching lines before waiting\n");
201 	fprintf(stderr, "          \"-w boot\" prints all matching lines since last system boot before waiting\n");
202 	fprintf(stderr, "   -F     output format may be \"std\", \"raw\", \"bsd\", or \"xml\"\n");
203 	fprintf(stderr, "          format may also be a string containing variables of the form\n");
204 	fprintf(stderr, "          $Key or $(Key) - use the latter for non-whitespace delimited variables\n");
205 	fprintf(stderr, "   -T     timestamp format may be \"sec\" (seconds), \"utc\" (UTC), or \"local\" (local timezone)\n");
206 	fprintf(stderr, "   -E     text encoding may be \"vis\", \"safe\", or \"none\"\n");
207 	fprintf(stderr, "   -nodc  no duplicate message compression\n");
208 	fprintf(stderr, "   -u     print timestamps using UTC (equivalent to \"-T utc\")\n");
209 	fprintf(stderr, "   -sort  sort messages using value for specified key1 (secondary sort by key2 if provided)\n");
210 	fprintf(stderr, "   -nsort numeric sort messages using value for specified key1 (secondary sort by key2 if provided)\n");
211 	fprintf(stderr, "   -k     key/value match\n");
212 	fprintf(stderr, "          if no operator or value is given, checks for the existence of the key\n");
213 	fprintf(stderr, "          if no operator is given, default is \"%s\"\n", OP_EQ);
214 	fprintf(stderr, "   -B     only process log messages since last system boot\n");
215 	fprintf(stderr, "   -C     alias for \"-k Facility com.apple.console\"\n");
216 	fprintf(stderr, "   -o     begins a new query\n");
217 	fprintf(stderr, "          queries are \'OR\'ed together\n");
218 	fprintf(stderr, "operators are zero or more modifiers followed by a comparison\n");
219 	fprintf(stderr, "   %s   equal\n", OP_EQ);
220 	fprintf(stderr, "   %s   not equal\n", OP_NE);
221 	fprintf(stderr, "   %s   greater than\n", OP_GT);
222 	fprintf(stderr, "   %s   greater or equal\n", OP_GE);
223 	fprintf(stderr, "   %s   less than\n", OP_LT);
224 	fprintf(stderr, "   %s   less or equal\n", OP_LE);
225 	fprintf(stderr, "optional modifiers for operators\n");
226 	fprintf(stderr, "   %c    case-fold\n", MOD_CASE_FOLD);
227 	fprintf(stderr, "   %c    regular expression\n", MOD_REGEX);
228 	fprintf(stderr, "   %c    substring\n", MOD_SUBSTRING);
229 	fprintf(stderr, "   %c    prefix\n", MOD_PREFIX);
230 	fprintf(stderr, "   %c    suffix\n", MOD_SUFFIX);
231 	fprintf(stderr, "   %c    numeric comparison\n", MOD_NUMERIC);
232 }
233 
234 const char *
notify_status_string(int status)235 notify_status_string(int status)
236 {
237 	if (status == NOTIFY_STATUS_OK) return "OK";
238 	if (status == NOTIFY_STATUS_INVALID_NAME) return "Process not registered";
239 	if (status == NOTIFY_STATUS_NOT_AUTHORIZED) return "Not authorized";
240 	return "Operation failed";
241 }
242 
243 const char *
asl_level_string(int level)244 asl_level_string(int level)
245 {
246 	if (level == ASL_LEVEL_EMERG) return ASL_STRING_EMERG;
247 	if (level == ASL_LEVEL_ALERT) return ASL_STRING_ALERT;
248 	if (level == ASL_LEVEL_CRIT) return ASL_STRING_CRIT;
249 	if (level == ASL_LEVEL_ERR) return ASL_STRING_ERR;
250 	if (level == ASL_LEVEL_WARNING) return ASL_STRING_WARNING;
251 	if (level == ASL_LEVEL_NOTICE) return ASL_STRING_NOTICE;
252 	if (level == ASL_LEVEL_INFO) return ASL_STRING_INFO;
253 	if (level == ASL_LEVEL_DEBUG) return ASL_STRING_DEBUG;
254 	return "Unknown";
255 }
256 
257 int
module_control(int argc,char * argv[])258 module_control(int argc, char *argv[])
259 {
260 	const char *val = NULL;
261 	uint64_t last;
262 	char *str;
263 
264 	asl_msg_t *ctl = _asl_server_control_query();
265 	if (ctl == NULL)
266 	{
267 		fprintf(stderr, "can't get status information from syslogd\n");
268 		return -1;
269 	}
270 
271 	argc -= 2;
272 	argv += 2;
273 
274 	if (argc < 2)
275 	{
276 		int first = 1;
277 
278 		/* print config */
279 		asl_out_module_t *m = asl_out_module_init();
280 		asl_out_module_t *x = m;
281 
282 		while (x != NULL)
283 		{
284 			if ((argc == 0) || (!strcmp(argv[0], x->name)))
285 			{
286 				asl_msg_lookup(ctl, x->name, &val, NULL);
287 
288 				if (first == 0) printf("\n");
289 				first = 0;
290 
291 				if (x->name == NULL) printf("ASL out module has no name\n");
292 				else printf("ASL out module: %s %s[current status: %s]\n", x->name, (x->flags & MODULE_FLAG_LOCAL) ? "local " : "",  (val == NULL) ? "unknown" : val );
293 
294 				asl_out_module_print(stdout, x);
295 			}
296 
297 			x = x->next;
298 		}
299 
300 		asl_msg_release(ctl);
301 		asl_out_module_free(m);
302 		return 0;
303 	}
304 
305 	/* name enable [val] */
306 	/* name disable [val] */
307 	if ((!strcmp(argv[1], "enable")) || (!strcmp(argv[1], "disable")))
308 	{
309 		int want = -1;
310 		int status = -1;
311 		asl_msg_t *cm;
312 		asl_client_t *ac;
313 
314 		if (!strcmp(argv[1], "enable"))
315 		{
316 			if (argc < 3) want = 1;
317 			else if (!strcmp(argv[2], "1")) want = 1;
318 			else if (!strcmp(argv[2], "0")) want = 0;
319 			else
320 			{
321 				printf("invalid value %s for %s %s - expecting 0 or 1\n", argv[2], argv[0], argv[1]);
322 				exit(-1);
323 			}
324 		}
325 		else
326 		{
327 			if (argc < 3) want = 0;
328 			else if (!strcmp(argv[2], "1")) want = 0;
329 			else if (!strcmp(argv[2], "0")) want = 1;
330 			else
331 			{
332 				printf("invalid value %s for %s %s - expecting 0 or 1\n", argv[2], argv[0], argv[1]);
333 				exit(-1);
334 			}
335 		}
336 
337 		asl_msg_lookup(ctl, argv[0], &val, NULL);
338 		if (val != NULL)
339 		{
340 			if (!strcmp(val, "enabled")) status = 1;
341 			else status = 0;
342 		}
343 
344 		asl_msg_release(ctl);
345 
346 		if (want < 0)
347 		{
348 			printf("internal error: want = -1\n");
349 			exit(-1);
350 		}
351 
352 		if (want == status)
353 		{
354 			printf("module %s is already %s\n", argv[0], val);
355 			return 0;
356 		}
357 
358 		cm = asl_msg_new(ASL_TYPE_MSG);
359 		asprintf(&str, "@ %s enable %d", argv[0], want);
360 
361 		if ((cm == NULL) || (str == NULL))
362 		{
363 			fprintf(stderr, "can't allocate memory - exiting\n");
364 			exit(-1);
365 		}
366 
367 		ac = asl_client_open(NULL, NULL, 0);
368 		asl_client_set_filter(ac, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG));
369 		asl_msg_set_key_val(cm, ASL_KEY_LEVEL, "7");
370 		asl_msg_set_key_val(cm, ASL_KEY_OPTION, "control");
371 		asl_msg_set_key_val(cm, ASL_KEY_MSG, str);
372 		asl_client_send(ac, cm);
373 
374 		asl_client_release(ac);
375 		asl_msg_release(cm);
376 		free(str);
377 		return 0;
378 	}
379 
380 	asl_msg_release(ctl);
381 
382 	/* name checkpoint [file] */
383 	if (!strcmp(argv[1], "checkpoint"))
384 	{
385 		asl_msg_list_t *q = asl_msg_list_new();
386 		asl_msg_t *qm = asl_msg_new(ASL_TYPE_QUERY);
387 
388 		if ((q == NULL) || (qm == NULL))
389 		{
390 			fprintf(stderr, "can't allocate memory - exiting\n");
391 			exit(-1);
392 		}
393 
394 		asl_msg_list_append(q, qm);
395 		asl_msg_release(qm);
396 
397 		asl_msg_set_key_val_op(qm, ASL_KEY_OPTION, "control", ASL_QUERY_OP_EQUAL);
398 		asprintf(&str, "%s checkpoint%s%s", argv[0], (argc > 2) ? " " : "", (argc > 2) ? argv[2] : "");
399 		asl_msg_set_key_val_op(qm, "action", str, ASL_QUERY_OP_EQUAL);
400 
401 		asl_msg_list_t *res = syslogd_query((asl_msg_list_t *)q, 0, 0, 1, &last);
402 		free(q);
403 		asl_msg_list_release(res);
404 		return 0;
405 	}
406 
407 	printf("unknown module control: %s\n", argv[1]);
408 	exit(-1);
409 }
410 
411 int
procinfo(char * pname,int * pid,int * uid)412 procinfo(char *pname, int *pid, int *uid)
413 {
414 	int mib[4];
415 	int i, status, nprocs;
416 	size_t miblen, size;
417 	struct kinfo_proc *procs, *newprocs;
418 
419 	size = 0;
420 	procs = NULL;
421 
422 	mib[0] = CTL_KERN;
423 	mib[1] = KERN_PROC;
424 	mib[2] = KERN_PROC_ALL;
425 	mib[3] = 0;
426 	miblen = 3;
427 
428 	status = sysctl(mib, miblen, NULL, &size, NULL, 0);
429 	do
430 	{
431 		size += size / 10;
432 		newprocs = reallocf(procs, size);
433 		if (newprocs == NULL)
434 		{
435 			if (procs != NULL) free(procs);
436 			return PROC_NOT_FOUND;
437 		}
438 
439 		procs = newprocs;
440 		status = sysctl(mib, miblen, procs, &size, NULL, 0);
441 	} while ((status == -1) && (errno == ENOMEM));
442 
443 	if (status == -1)
444 	{
445 		if (procs != NULL) free(procs);
446 		return PROC_NOT_FOUND;
447 	}
448 
449 	if (size % sizeof(struct kinfo_proc) != 0)
450 	{
451 		if (procs != NULL) free(procs);
452 		return PROC_NOT_FOUND;
453 	}
454 
455 	if (procs == NULL) return PROC_NOT_FOUND;
456 
457 	nprocs = size / sizeof(struct kinfo_proc);
458 
459 	if (pname == NULL)
460 	{
461 		/* Search for a pid */
462 		for (i = 0; i < nprocs; i++)
463 		{
464 			if (*pid == procs[i].ki_pid)
465 			{
466 				*uid = procs[i].ki_uid;
467 				return 0;
468 			}
469 		}
470 
471 		return PROC_NOT_FOUND;
472 	}
473 
474 	*pid = PROC_NOT_FOUND;
475 
476 	for (i = 0; i < nprocs; i++)
477 	{
478 		if (!strcmp(procs[i].ki_comm, pname))
479 		{
480 			if (*pid != PROC_NOT_FOUND)
481 			{
482 				free(procs);
483 				return PROC_NOT_UNIQUE;
484 			}
485 
486 			*pid = procs[i].ki_pid;
487 			*uid = procs[i].ki_uid;
488 		}
489 	}
490 
491 	free(procs);
492 	if (*pid == PROC_NOT_FOUND) return PROC_NOT_FOUND;
493 
494 	return 0;
495 }
496 
497 int
rcontrol_get_string(const char * name,int * val)498 rcontrol_get_string(const char *name, int *val)
499 {
500 	int t, status;
501 	uint64_t x;
502 
503 	status = notify_register_plain(name, &t);
504 	if (status != NOTIFY_STATUS_OK) return status;
505 
506 	x = 0;
507 	status = notify_get_state(t, &x);
508 	notify_cancel(t);
509 
510 	*val = x;
511 
512 	return status;
513 }
514 
515 int
rcontrol_set_string(const char * name,int filter)516 rcontrol_set_string(const char *name, int filter)
517 {
518 	int t, status;
519 	uint64_t x;
520 
521 	status = notify_register_plain(name, &t);
522 	if (status != NOTIFY_STATUS_OK) return status;
523 
524 	x = filter;
525 	status = notify_set_state(t, x);
526 	notify_post(NOTIFY_RC);
527 	notify_cancel(t);
528 	return status;
529 }
530 
531 int
asl_string_to_filter(char * s)532 asl_string_to_filter(char *s)
533 {
534 	int f, i;
535 
536 	if (s == NULL) return 0;
537 	if (s[0] == '\0') return 0;
538 
539 	if ((s[0] >= '0') && (s[0] <= '9')) return ASL_FILTER_MASK(atoi(s));
540 
541 	if (s[0] == '-')
542 	{
543 		if ((s[1] == 'P') || (s[1] == 'p')) i = ASL_LEVEL_EMERG;
544 		else if ((s[1] == 'A') || (s[1] == 'a')) i = ASL_LEVEL_ALERT;
545 		else if ((s[1] == 'C') || (s[1] == 'c')) i = ASL_LEVEL_CRIT;
546 		else if ((s[1] == 'E') || (s[1] == 'e')) i = ASL_LEVEL_ERR;
547 		else if ((s[1] == 'X') || (s[1] == 'x')) i = ASL_LEVEL_ERR;
548 		else if ((s[1] == 'W') || (s[1] == 'w')) i = ASL_LEVEL_WARNING;
549 		else if ((s[1] == 'N') || (s[1] == 'n')) i = ASL_LEVEL_NOTICE;
550 		else if ((s[1] == 'I') || (s[1] == 'i')) i = ASL_LEVEL_INFO;
551 		else if ((s[1] == 'D') || (s[1] == 'd')) i = ASL_LEVEL_DEBUG;
552 		else i = atoi(s + 1);
553 		f = ASL_FILTER_MASK_UPTO(i);
554 		return f;
555 	}
556 
557 	f = 0;
558 	for (i = 0; s[i] != '\0'; i++)
559 	{
560 		if ((s[i] == 'P') || (s[i] == 'p')) f |= ASL_FILTER_MASK_EMERG;
561 		else if ((s[i] == 'A') || (s[i] == 'a')) f |= ASL_FILTER_MASK_ALERT;
562 		else if ((s[i] == 'C') || (s[i] == 'c')) f |= ASL_FILTER_MASK_CRIT;
563 		else if ((s[i] == 'E') || (s[i] == 'e')) f |= ASL_FILTER_MASK_ERR;
564 		else if ((s[i] == 'X') || (s[i] == 'x')) f |= ASL_FILTER_MASK_ERR;
565 		else if ((s[i] == 'W') || (s[i] == 'w')) f |= ASL_FILTER_MASK_WARNING;
566 		else if ((s[i] == 'N') || (s[i] == 'n')) f |= ASL_FILTER_MASK_NOTICE;
567 		else if ((s[i] == 'I') || (s[i] == 'i')) f |= ASL_FILTER_MASK_INFO;
568 		else if ((s[i] == 'D') || (s[i] == 'd')) f |= ASL_FILTER_MASK_DEBUG;
569 	}
570 
571 	return f;
572 }
573 
574 char *
asl_filter_string(int f)575 asl_filter_string(int f)
576 {
577 	static char str[1024];
578 	int i;
579 
580 	memset(str, 0, sizeof(str));
581 	i = 0;
582 
583 	if ((f == ASL_FILTER_MASK_PACEWNID) != 0)
584 	{
585 		strcat(str, "Emergency - Debug");
586 		return str;
587 	}
588 
589 	if ((f == ASL_FILTER_MASK_PACEWNI) != 0)
590 	{
591 		strcat(str, "Emergency - Info");
592 		return str;
593 	}
594 
595 	if ((f == ASL_FILTER_MASK_PACEWN) != 0)
596 	{
597 		strcat(str, "Emergency - Notice");
598 		return str;
599 	}
600 
601 	if ((f == ASL_FILTER_MASK_PACEW) != 0)
602 	{
603 		strcat(str, "Emergency - Warning");
604 		return str;
605 	}
606 
607 	if ((f == ASL_FILTER_MASK_PACE) != 0)
608 	{
609 		strcat(str, "Emergency - Error");
610 		return str;
611 	}
612 
613 	if ((f == ASL_FILTER_MASK_PAC) != 0)
614 	{
615 		strcat(str, "Emergency - Critical");
616 		return str;
617 	}
618 
619 	if ((f & ASL_FILTER_MASK_EMERG) != 0)
620 	{
621 		strcat(str, "Emergency");
622 		i++;
623 	}
624 
625 	if ((f & ASL_FILTER_MASK_ALERT) != 0)
626 	{
627 		if (i > 0) strcat(str, ", ");
628 		strcat(str, "Alert");
629 		i++;
630 	}
631 
632 	if ((f & ASL_FILTER_MASK_CRIT) != 0)
633 	{
634 		if (i > 0) strcat(str, ", ");
635 		strcat(str, "Critical");
636 		i++;
637 	}
638 
639 	if ((f & ASL_FILTER_MASK_ERR) != 0)
640 	{
641 		if (i > 0) strcat(str, ", ");
642 		strcat(str, "Error");
643 		i++;
644 	}
645 
646 	if ((f & ASL_FILTER_MASK_WARNING) != 0)
647 	{
648 		if (i > 0) strcat(str, ", ");
649 		strcat(str, "Warning");
650 		i++;
651 	}
652 
653 	if ((f & ASL_FILTER_MASK_NOTICE) != 0)
654 	{
655 		if (i > 0) strcat(str, ", ");
656 		strcat(str, "Notice");
657 		i++;
658 	}
659 
660 	if ((f & ASL_FILTER_MASK_INFO) != 0)
661 	{
662 		if (i > 0) strcat(str, ", ");
663 		strcat(str, "Info");
664 		i++;
665 	}
666 
667 	if ((f & ASL_FILTER_MASK_DEBUG) != 0)
668 	{
669 		if (i > 0) strcat(str, ", ");
670 		strcat(str, "Debug");
671 		i++;
672 	}
673 
674 	if (i == 0) sprintf(str, "Off");
675 
676 	return str;
677 }
678 
679 const char *
rcontrol_name(pid_t pid,uid_t uid)680 rcontrol_name(pid_t pid, uid_t uid)
681 {
682 	static char str[1024];
683 
684 	if (pid == RC_MASTER) return NOTIFY_SYSTEM_MASTER;
685 
686 	memset(str, 0, sizeof(str));
687 	if (uid == 0) snprintf(str, sizeof(str) - 1, "%s.%d", NOTIFY_PREFIX_SYSTEM, pid);
688 	else snprintf(str, sizeof(str) - 1, "user.uid.%d.syslog.%d", uid, pid);
689 	return str;
690 }
691 
692 int
rcontrol_get(pid_t pid,uid_t uid)693 rcontrol_get(pid_t pid, uid_t uid)
694 {
695 	int filter, status;
696 
697 	filter = 0;
698 
699 	if (pid < 0)
700 	{
701 		status = rcontrol_get_string(rcontrol_name(pid, uid), &filter);
702 		if (status == NOTIFY_STATUS_OK)
703 		{
704 			printf("Master filter mask: %s\n", asl_filter_string(filter));
705 			return 0;
706 		}
707 
708 		printf("Unable to determine master filter mask\n");
709 		return -1;
710 	}
711 
712 	status = rcontrol_get_string(rcontrol_name(pid, uid), &filter);
713 	if (status == NOTIFY_STATUS_OK)
714 	{
715 		printf("Process %d syslog filter mask: %s\n", pid, asl_filter_string(filter));
716 		return 0;
717 	}
718 
719 	printf("Unable to determine syslog filter mask for pid %d\n", pid);
720 	return -1;
721 }
722 
723 int
rcontrol_set(pid_t pid,uid_t uid,int filter)724 rcontrol_set(pid_t pid, uid_t uid, int filter)
725 {
726 	int status;
727 	const char *rcname;
728 
729 	rcname = rcontrol_name(pid, uid);
730 
731 	if (pid < 0)
732 	{
733 		status = rcontrol_set_string(rcname, filter);
734 
735 		if (status == NOTIFY_STATUS_OK)
736 		{
737 			if (pid == RC_MASTER) status = notify_post(NOTIFY_SYSTEM_MASTER);
738 			return 0;
739 		}
740 
741 		printf("Unable to set master syslog filter mask: %s\n", notify_status_string(status));
742 		return -1;
743 	}
744 
745 	status = rcontrol_set_string(rcname, filter);
746 	if (status == NOTIFY_STATUS_OK)
747 	{
748 		status = notify_post(rcname);
749 		return 0;
750 	}
751 
752 	printf("Unable to set syslog filter mask for pid %d: %s\n", pid, notify_status_string(status));
753 	return -1;
754 }
755 
756 int
rsend(asl_msg_t * msg,char * rhost)757 rsend(asl_msg_t *msg, char *rhost)
758 {
759 	char *str, *out;
760 	uint32_t len, level;
761 	char *timestr;
762 	const char *val;
763 	time_t tick;
764 	int s;
765 	struct sockaddr_in dst;
766 	struct hostent *h;
767 	char myname[MAXHOSTNAMELEN + 1];
768 
769 	if (msg == NULL) return 0;
770 
771 	h = gethostbyname(rhost);
772 	if (h == NULL) return -1;
773 
774 	s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
775 	if (s <= 0) return -1;
776 
777 	memset(&dst, 0, sizeof(struct sockaddr_in));
778 	memcpy(&(dst.sin_addr.s_addr), h->h_addr_list[0], 4);
779 	dst.sin_family = AF_INET;
780 	dst.sin_port = 514;
781 	dst.sin_len = sizeof(struct sockaddr_in);
782 
783 	level = ASL_LEVEL_DEBUG;
784 
785 	val = asl_msg_get_val_for_key(msg, ASL_KEY_LEVEL);
786 	if (val != NULL) level = atoi(val);
787 
788 
789 	tick = time(NULL);
790 	timestr = NULL;
791 	asprintf(&timestr, "%lu", tick);
792 	if (timestr != NULL)
793 	{
794 		asl_msg_set_key_val(msg, ASL_KEY_TIME, timestr);
795 		free(timestr);
796 	}
797 
798 	if (gethostname(myname, MAXHOSTNAMELEN) == 0) asl_msg_set_key_val(msg, ASL_KEY_HOST, myname);
799 
800 	len = 0;
801 	str = asl_msg_to_string((asl_msg_t *)msg, &len);
802 	if (str == NULL) return -1;
803 
804 	asprintf(&out, "%10u %s\n", len+1, str);
805 	free(str);
806 	if (out == NULL) return -1;
807 
808 	sendto(s, out, len+12, 0, (const struct sockaddr *)&dst, sizeof(struct sockaddr_in));
809 
810 	free(out);
811 	close(s);
812 	return 0;
813 }
814 
815 int
rlegacy(char * msg,int level,char * rhost)816 rlegacy(char *msg, int level, char *rhost)
817 {
818 	char *out;
819 	uint32_t len;
820 	time_t tick;
821 	char *ltime;
822 	int s;
823 	struct sockaddr_in dst;
824 	struct hostent *h;
825 	char myname[MAXHOSTNAMELEN + 1];
826 
827 	if (msg == NULL) return 0;
828 
829 	h = gethostbyname(rhost);
830 	if (h == NULL) return -1;
831 
832 	s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
833 	if (s <= 0) return -1;
834 
835 	memset(&dst, 0, sizeof(struct sockaddr_in));
836 	memcpy(&(dst.sin_addr.s_addr), h->h_addr_list[0], 4);
837 	dst.sin_family = AF_INET;
838 	dst.sin_port = 514;
839 	dst.sin_len = sizeof(struct sockaddr_in);
840 
841 	tick = time(NULL);
842 	ltime = ctime(&tick);
843 	ltime[19] = '\0';
844 
845 	gethostname(myname, MAXHOSTNAMELEN);
846 
847 	asprintf(&out, "<%d>%s %s syslog[%d]: %s", level, ltime+4, myname, getpid(), msg);
848 	len = strlen(out);
849 	sendto(s, out, len, 0, (const struct sockaddr *)&dst, sizeof(struct sockaddr_in));
850 
851 	free(out);
852 	close(s);
853 	return 0;
854 }
855 
856 static int
_isanumber(char * s)857 _isanumber(char *s)
858 {
859 	int i;
860 
861 	if (s == NULL) return 0;
862 
863 	i = 0;
864 	if ((s[0] == '-') || (s[0] == '+')) i = 1;
865 
866 	if (s[i] == '\0') return 0;
867 
868 	for (; s[i] != '\0'; i++)
869 	{
870 		if (!isdigit(s[i])) return 0;
871 	}
872 
873 	return 1;
874 }
875 
876 int
asl_string_to_level(const char * s)877 asl_string_to_level(const char *s)
878 {
879 	if (s == NULL) return -1;
880 
881 	if ((s[0] >= '0') && (s[0] <= '7') && (s[1] == '\0')) return atoi(s);
882 
883 	if (!strncasecmp(s, "em", 2)) return ASL_LEVEL_EMERG;
884 	else if (!strncasecmp(s, "p",  1)) return ASL_LEVEL_EMERG;
885 	else if (!strncasecmp(s, "a",  1)) return ASL_LEVEL_ALERT;
886 	else if (!strncasecmp(s, "c",  1)) return ASL_LEVEL_CRIT;
887 	else if (!strncasecmp(s, "er", 2)) return ASL_LEVEL_ERR;
888 	else if (!strncasecmp(s, "x",  1)) return ASL_LEVEL_ERR;
889 	else if (!strncasecmp(s, "w",  1)) return ASL_LEVEL_WARNING;
890 	else if (!strncasecmp(s, "n",  1)) return ASL_LEVEL_NOTICE;
891 	else if (!strncasecmp(s, "i",  1)) return ASL_LEVEL_INFO;
892 	else if (!strncasecmp(s, "d",  1)) return ASL_LEVEL_DEBUG;
893 
894 	return -1;
895 }
896 
897 const char *
asl_string_to_char_level(const char * s)898 asl_string_to_char_level(const char *s)
899 {
900 	if (s == NULL) return NULL;
901 
902 	if ((s[0] >= '0') && (s[0] <= '7') && (s[1] == '\0')) return s;
903 
904 	if (!strncasecmp(s, "em", 2)) return "0";
905 	else if (!strncasecmp(s, "p",  1)) return "0";
906 	else if (!strncasecmp(s, "a",  1)) return "1";
907 	else if (!strncasecmp(s, "c",  1)) return "2";
908 	else if (!strncasecmp(s, "er", 2)) return "3";
909 	else if (!strncasecmp(s, "x",  1)) return "3";
910 	else if (!strncasecmp(s, "w",  1)) return "4";
911 	else if (!strncasecmp(s, "n",  1)) return "5";
912 	else if (!strncasecmp(s, "i",  1)) return "6";
913 	else if (!strncasecmp(s, "d",  1)) return "7";
914 
915 	return NULL;
916 }
917 
918 int
syslog_remote_control(int argc,char * argv[])919 syslog_remote_control(int argc, char *argv[])
920 {
921 	int pid, uid, status, mask;
922 
923 	if ((argc < 3) || (argc > 4))
924 	{
925 		fprintf(stderr, "usage:\n");
926 		fprintf(stderr, "%s -c process [mask]\n", myname);
927 		fprintf(stderr, "   get (set if mask is specified) syslog filter mask for process (pid or name)\n");
928 		fprintf(stderr, "   process may be pid or process name\n");
929 		fprintf(stderr, "   use \"-c 0\" to get master syslog filter mask\n");
930 		fprintf(stderr, "   use \"-c 0 off\" to disable master syslog filter mask\n");
931 		fprintf(stderr, "\n");
932 		return -1;
933 	}
934 
935 	pid = RC_MASTER;
936 	uid = -2;
937 
938 	status = PROC_NOT_FOUND;
939 
940 	if ((!strcmp(argv[2], "syslogd")) || (!strcmp(argv[2], "syslog")))
941 	{
942 		fprintf(stderr, "%s: does not have a filter mask\n", argv[2]);
943 		return -1;
944 	}
945 	else if (_isanumber(argv[2]) != 0)
946 	{
947 		pid = atoi(argv[2]);
948 		status = procinfo(NULL, &pid, &uid);
949 	}
950 	else
951 	{
952 		status = procinfo(argv[2], &pid, &uid);
953 	}
954 
955 	if (status == PROC_NOT_FOUND)
956 	{
957 		fprintf(stderr, "%s: process not found\n", argv[2]);
958 		return -1;
959 	}
960 
961 	if (status == PROC_NOT_UNIQUE)
962 	{
963 		fprintf(stderr, "%s: multiple processes found\n", argv[2]);
964 		fprintf(stderr, "use pid to identify a process uniquely\n");
965 		return -1;
966 	}
967 
968 	if (pid == 0) pid = RC_MASTER;
969 
970 	if (argc == 4)
971 	{
972 		if ((pid == RC_MASTER) && (!strcasecmp(argv[3], "off"))) mask = 0;
973 		else
974 		{
975 			mask = asl_string_to_filter(argv[3]);
976 			if (mask < 0)
977 			{
978 				printf("unknown syslog mask: %s\n", argv[3]);
979 				return -1;
980 			}
981 		}
982 
983 		rcontrol_set(pid, uid, mask);
984 	}
985 	else
986 	{
987 		rcontrol_get(pid, uid);
988 	}
989 
990 	return 0;
991 }
992 
993 int
syslog_send(int argc,char * argv[])994 syslog_send(int argc, char *argv[])
995 {
996 	int i, start, kv, len, rfmt, rlevel;
997 	asl_client_t *asl;
998 	asl_msg_t *m;
999 	char tmp[64], *str, *rhost;
1000 
1001 	kv = 0;
1002 	rhost = NULL;
1003 	rfmt = SEND_FORMAT_LEGACY;
1004 	start = 1;
1005 	rlevel = 7;
1006 
1007 	for (i = 1; i < argc; i++)
1008 	{
1009 		if (!strcmp(argv[i], "-s")) start = i+1;
1010 		else if (!strcmp(argv[i], "-k"))
1011 		{
1012 			kv = 1;
1013 			rfmt = SEND_FORMAT_ASL;
1014 		}
1015 		else if (!strcmp(argv[i], "-r"))
1016 		{
1017 			rhost = argv[++i];
1018 			start = i+1;
1019 		}
1020 		else if (!strcmp(argv[i], "-l"))
1021 		{
1022 			rlevel = asl_string_to_level(argv[++i]);
1023 			if (rlevel < 0)
1024 			{
1025 				fprintf(stderr, "Unknown level: %s\n", argv[i]);
1026 				return(-1);
1027 			}
1028 			start = i+1;
1029 		}
1030 	}
1031 
1032 	asl = asl_client_open(myname, "syslog", 0);
1033 	asl_client_set_filter(asl, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG));
1034 
1035 	m = asl_msg_new(ASL_TYPE_MSG);
1036 	asl_msg_set_key_val(m, ASL_KEY_SENDER, myname);
1037 
1038 	sprintf(tmp, "%d", rlevel);
1039 	asl_msg_set_key_val(m, ASL_KEY_LEVEL, tmp);
1040 
1041 	str = NULL;
1042 
1043 	if (kv == 0)
1044 	{
1045 		len = 0;
1046 		for (i = start; i < argc; i++) len += (strlen(argv[i]) + 1);
1047 		str = calloc(len + 1, 1);
1048 		if (str == NULL) return -1;
1049 
1050 		for (i = start; i < argc; i++)
1051 		{
1052 			strcat(str, argv[i]);
1053 			if ((i+1) < argc) strcat(str, " ");
1054 		}
1055 		asl_msg_set_key_val(m, ASL_KEY_MSG, str);
1056 	}
1057 	else
1058 	{
1059 		for (i = start + 1; i < argc; i += 2)
1060 		{
1061 			if (!strcmp(argv[i], "-k")) i++;
1062 			asl_msg_set_key_val(m, argv[i], argv[i + 1]);
1063 			if (!strcmp(argv[i], ASL_KEY_LEVEL)) rlevel = atoi(argv[i + 1]);
1064 		}
1065 	}
1066 
1067 	if (rhost == NULL)
1068 	{
1069 		asl_client_send(asl, m);
1070 	}
1071 	else if (rfmt == SEND_FORMAT_ASL)
1072 	{
1073 		rsend(m, rhost);
1074 	}
1075 	else if ((rfmt == SEND_FORMAT_LEGACY) && (str != NULL))
1076 	{
1077 		rlegacy(str, rlevel, rhost);
1078 	}
1079 
1080 	asl_msg_release(m);
1081 
1082 	if (str != NULL) free(str);
1083 
1084 	asl_client_release(asl);
1085 
1086 	return 0;
1087 }
1088 
1089 int
syslog_config(int argc,char * argv[])1090 syslog_config(int argc, char *argv[])
1091 {
1092 	int i;
1093 	uint32_t x;
1094 	uid_t uid;
1095 	asl_client_t *asl;
1096 	asl_msg_t *m;
1097 	asl_string_t *str;
1098 	const char *key, *val;
1099 
1100 	if (argc == 2)
1101 	{
1102 		asl_msg_t *ctl = _asl_server_control_query();
1103 		if (ctl == NULL)
1104 		{
1105 			fprintf(stderr, "can't get status information from syslogd\n");
1106 			return -1;
1107 		}
1108 
1109 		for (x = asl_msg_fetch(ctl, 0, &key, &val, NULL); x != IndexNull; x = asl_msg_fetch(ctl, x, &key, &val, NULL))
1110 		{
1111 			printf("%s %s\n", key, val);
1112 		}
1113 
1114 		asl_msg_release(ctl);
1115 		return 0;
1116 	}
1117 
1118 	uid = geteuid();
1119 	if (uid != 0)
1120 	{
1121 		fprintf(stderr, "syslogd parameters may only be set by the superuser\n");
1122 		return -1;
1123 	}
1124 
1125 	str = asl_string_new(0);
1126 	asl_string_append(str, "= ");
1127 
1128 	for (i = 2; i < argc; i++)
1129 	{
1130 		asl_string_append(str, argv[i]);
1131 		if ((i + 1) < argc) asl_string_append(str, " ");
1132 	}
1133 
1134 	asl = asl_client_open(myname, "syslog", 0);
1135 
1136 	m = asl_msg_new(ASL_TYPE_MSG);
1137 	asl_msg_set_key_val(m, ASL_KEY_LEVEL, ASL_STRING_NOTICE);
1138 	asl_msg_set_key_val(m, ASL_KEY_OPTION, ASL_OPT_CONTROL);
1139 	asl_msg_set_key_val(m, ASL_KEY_SENDER, myname);
1140 	asl_msg_set_key_val(m, ASL_KEY_MSG, asl_string_bytes(str));
1141 
1142 	asl_client_send(asl, m);
1143 
1144 	asl_string_release(str);
1145 	asl_msg_release(m);
1146 	asl_client_release(asl);
1147 
1148 	return 0;
1149 }
1150 
1151 int
syslog_control(int argc,char * argv[])1152 syslog_control(int argc, char *argv[])
1153 {
1154 	int i;
1155 	uid_t uid;
1156 	asl_client_t *asl;
1157 	asl_msg_t *m;
1158 	asl_string_t *str;
1159 
1160 	uid = geteuid();
1161 	if (uid != 0)
1162 	{
1163 		fprintf(stderr, "syslog control limited to use by superuser\n");
1164 		return -1;
1165 	}
1166 
1167 	str = asl_string_new(0);
1168 	asl_string_append(str, "@ ");
1169 
1170 	for (i = 2; i < argc; i++)
1171 	{
1172 		asl_string_append(str, argv[i]);
1173 		if ((i + 1) < argc) asl_string_append(str, " ");
1174 	}
1175 
1176 	asl = asl_client_open(myname, "syslog", 0);
1177 
1178 	m = asl_msg_new(ASL_TYPE_MSG);
1179 	asl_msg_set_key_val(m, ASL_KEY_LEVEL, ASL_STRING_NOTICE);
1180 	asl_msg_set_key_val(m, ASL_KEY_OPTION, ASL_OPT_CONTROL);
1181 	asl_msg_set_key_val(m, ASL_KEY_SENDER, myname);
1182 	asl_msg_set_key_val(m, ASL_KEY_MSG, asl_string_bytes(str));
1183 
1184 	asl_client_send(asl, m);
1185 
1186 	asl_string_release(str);
1187 	asl_msg_release(m);
1188 	asl_client_release(asl);
1189 
1190 	return 0;
1191 }
1192 
1193 static void
print_xml_header(FILE * f)1194 print_xml_header(FILE *f)
1195 {
1196 	if (f == NULL) return;
1197 
1198 	fprintf(f, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
1199 	fprintf(f, "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n");
1200 	fprintf(f, "<plist version=\"1.0\">\n");
1201 	fprintf(f, "<array>\n");
1202 }
1203 
1204 static void
print_xml_trailer(FILE * f)1205 print_xml_trailer(FILE *f)
1206 {
1207 	if (f == NULL) return;
1208 
1209 	fprintf(f, "</array>\n");
1210 	fprintf(f, "</plist>\n");
1211 }
1212 
1213 static void
printmsg(FILE * f,asl_msg_t * msg,char * fmt,int pflags)1214 printmsg(FILE *f, asl_msg_t *msg, char *fmt, int pflags)
1215 {
1216 	char *str;
1217 	const char *mf;
1218 	uint32_t encode, len, status;
1219 	uint64_t xid;
1220 
1221 	if (f == NULL)
1222 	{
1223 		if (export != NULL)
1224 		{
1225 			xid = 0;
1226 			status = asl_file_save(export, msg, &xid);
1227 			if (status != ASL_STATUS_OK)
1228 			{
1229 				fprintf(stderr, "export file write failed: %s\n", asl_core_error(status));
1230 				asl_file_close(export);
1231 				export = NULL;
1232 			}
1233 		}
1234 
1235 		return;
1236 	}
1237 
1238 	encode = pflags & 0x0000000f;
1239 
1240 	mf = ASL_MSG_FMT_RAW;
1241 	if (fmt != NULL) mf = (const char *)fmt;
1242 	else if (pflags & FORMAT_STD) mf = ASL_MSG_FMT_STD;
1243 	else if (pflags & FORMAT_LEGACY) mf = ASL_MSG_FMT_BSD;
1244 	else if (pflags & FORMAT_XML) mf = ASL_MSG_FMT_XML;
1245 
1246 	len = 0;
1247 	str = asl_format_message((asl_msg_t *)msg, mf, tfmt, encode, &len);
1248 	if (str == NULL) return;
1249 
1250 	if (pflags & COMPRESS_DUPS)
1251 	{
1252 		if (last_printmsg_str != NULL)
1253 		{
1254 			if (!strcmp(str + STD_BSD_DATE_LEN, last_printmsg_str + STD_BSD_DATE_LEN))
1255 			{
1256 				last_printmsg_count++;
1257 				free(str);
1258 			}
1259 			else
1260 			{
1261 				if (last_printmsg_count > 0)
1262 				{
1263 					fprintf(f, "--- last message repeated %d time%s ---\n", last_printmsg_count, (last_printmsg_count == 1) ? "" : "s");
1264 				}
1265 
1266 				free(last_printmsg_str);
1267 				last_printmsg_str = str;
1268 				last_printmsg_count = 0;
1269 
1270 				fprintf(f, "%s", str);
1271 			}
1272 		}
1273 		else
1274 		{
1275 			last_printmsg_str = str;
1276 			last_printmsg_count = 0;
1277 
1278 			fprintf(f, "%s", str);
1279 		}
1280 	}
1281 	else
1282 	{
1283 		fprintf(f, "%s", str);
1284 		free(str);
1285 	}
1286 }
1287 
1288 asl_msg_list_t *
store_query(asl_msg_list_t * q,uint64_t start,int count,int dir,uint64_t * last)1289 store_query(asl_msg_list_t *q, uint64_t start, int count, int dir, uint64_t *last)
1290 {
1291 	if (store == NULL)
1292 	{
1293 		uint32_t status = asl_store_open_read(NULL, &store);
1294 		if (status != 0) return NULL;
1295 	}
1296 
1297 	return asl_store_match(store, q, last, start, count, 0, dir);
1298 }
1299 
1300 asl_msg_list_t *
file_query(asl_msg_list_t * q,uint64_t start,int count,int dir,uint64_t * last)1301 file_query(asl_msg_list_t *q, uint64_t start, int count, int dir, uint64_t *last)
1302 {
1303 	return asl_file_list_match(db_files, q, last, start, count, 0, dir);;
1304 }
1305 
1306 asl_msg_list_t *
legacy_query(asl_msg_list_t * q,uint64_t start,int count,int dir,uint64_t * last)1307 legacy_query(asl_msg_list_t *q, uint64_t start, int count, int dir, uint64_t *last)
1308 {
1309 	return asl_file_match(legacy, q, last, start, count, 0, dir);
1310 }
1311 
1312 asl_msg_list_t *
syslogd_query(asl_msg_list_t * q,uint64_t start,int count,int dir,uint64_t * last)1313 syslogd_query(asl_msg_list_t *q, uint64_t start, int count, int dir, uint64_t *last)
1314 {
1315 	char *str, *res;
1316 	caddr_t vmstr;
1317 	uint32_t len, reslen, status;
1318 	int flags;
1319 	kern_return_t kstatus;
1320 	asl_msg_list_t *l;
1321 
1322 	if (asl_server_port == MACH_PORT_NULL)
1323 	{
1324 		kstatus = bootstrap_look_up2(bootstrap_port, ASL_SERVICE_NAME, &asl_server_port, 0, BOOTSTRAP_PRIVILEGED_SERVER);
1325 		if (kstatus != KERN_SUCCESS)
1326 		{
1327 			fprintf(stderr, "query failed: can't contact syslogd\n");
1328 			return NULL;
1329 		}
1330 	}
1331 
1332 	len = 0;
1333 	str = asl_msg_list_to_string(q, &len);
1334 
1335 	kstatus = vm_allocate(mach_task_self(), (vm_address_t *)&vmstr, len, TRUE);
1336 	if (kstatus != KERN_SUCCESS)
1337 	{
1338 		free(str);
1339 		return NULL;
1340 	}
1341 
1342 	memmove(vmstr, str, len);
1343 	free(str);
1344 
1345 	res = NULL;
1346 	reslen = 0;
1347 	status = 0;
1348 	flags = 0;
1349 	if (dir < 0) flags = QUERY_FLAG_SEARCH_REVERSE;
1350 
1351 	kstatus = _asl_server_query_2(asl_server_port, (caddr_t)vmstr, len, start, count, flags, (caddr_t *)&res, &reslen, last, (int *)&status);
1352 
1353 	if (res == NULL) return NULL;
1354 	l = asl_msg_list_from_string(res);
1355 	vm_deallocate(mach_task_self(), (vm_address_t)res, reslen);
1356 	return l;
1357 }
1358 
1359 void
filter_and_print(asl_msg_t * msg,asl_msg_list_t * ql,FILE * f,char * pfmt,int pflags)1360 filter_and_print(asl_msg_t *msg, asl_msg_list_t *ql, FILE *f, char *pfmt, int pflags)
1361 {
1362 	int i, do_match, did_match;
1363 
1364 	if (msg == NULL) return;
1365 
1366 	do_match = 1;
1367 	if (ql == NULL) do_match = 0;
1368 	else if (ql->count == 0) do_match = 0;
1369 
1370 	did_match = 1;
1371 
1372 	if (do_match != 0)
1373 	{
1374 		did_match = 0;
1375 
1376 		for (i = 0; (i < ql->count) && (did_match == 0); i++)
1377 		{
1378 			did_match = asl_msg_cmp(ql->msg[i], (asl_msg_t *)msg);
1379 		}
1380 	}
1381 
1382 	if (did_match != 0) printmsg(f, msg, pfmt, pflags);
1383 }
1384 
1385 #if TARGET_OS_EMBEDDED
1386 void
syslogd_direct_watch(FILE * f,char * pfmt,int pflags,asl_msg_list_t * ql)1387 syslogd_direct_watch(FILE *f, char *pfmt, int pflags, asl_msg_list_t *ql)
1388 {
1389 	struct sockaddr_in address;
1390 	int i, bytes, sock, stream, status;
1391 	uint32_t n, inlen;
1392 	uint16_t port;
1393 	socklen_t addresslength;
1394 	char *str, buf[DIRECT_BUF_SIZE];
1395 	asl_msg_t *msg;
1396 
1397 	if (asl_server_port == MACH_PORT_NULL)
1398 	{
1399 		status = bootstrap_look_up(bootstrap_port, ASL_SERVICE_NAME, &asl_server_port);
1400 		if (status != KERN_SUCCESS)
1401 		{
1402 			fprintf(stderr, "query failed: can't contact syslogd\n");
1403 			exit(1);
1404 		}
1405 	}
1406 
1407 	addresslength = sizeof(address);
1408 	sock = socket(AF_INET, SOCK_STREAM, 0);
1409 	port = (arc4random() % (IPPORT_HILASTAUTO - IPPORT_HIFIRSTAUTO)) + IPPORT_HIFIRSTAUTO;
1410 
1411 	memset(&address, 0, addresslength);
1412 	address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
1413 	address.sin_family = AF_INET;
1414 	address.sin_port = htons(port);
1415 
1416 	status = bind(sock, (struct sockaddr *)&address, sizeof(address));
1417 
1418 	for (i = 0; (i < MAX_RANDOM) && (status < 0); i++)
1419 	{
1420 		port = (arc4random() % (IPPORT_HILASTAUTO - IPPORT_HIFIRSTAUTO)) + IPPORT_HIFIRSTAUTO;
1421 		address.sin_port = htons(port);
1422 
1423 		status = bind(sock, (struct sockaddr *)&address, sizeof(address));
1424 	}
1425 
1426 	if (status < 0)
1427 	{
1428 		fprintf(stderr, "query failed: can't find a port to connect to syslogd\n");
1429 		exit(1);
1430 	}
1431 
1432 	bytes = 0;
1433 
1434 	if (listen(sock, 1) == -1)
1435 	{
1436 		perror("listen");
1437 		exit(1);
1438 	}
1439 
1440 	i = htons(port);
1441 	_asl_server_register_direct_watch(asl_server_port, i);
1442 
1443 	stream = accept(sock, (struct sockaddr*)&address, &addresslength);
1444 	if (stream == -1)
1445 	{
1446 		perror("accept");
1447 		exit(1);
1448 	}
1449 
1450 	forever
1451 	{
1452 		inlen = 0;
1453 		errno = 0;
1454 		bytes = recvfrom(stream, &n, sizeof(n), 0, NULL, NULL);
1455 		if (bytes <= 0)
1456 		{
1457 			fprintf(stderr, "\nrecvfrom (message length) returned %d (errno %d) - exiting\n", bytes, errno);
1458 			break;
1459 		}
1460 		else inlen = ntohl(n);
1461 
1462 		if (inlen == 0) continue;
1463 
1464 		str = NULL;
1465 		if (inlen <= DIRECT_BUF_SIZE)
1466 		{
1467 			str = buf;
1468 		}
1469 		else
1470 		{
1471 			str = calloc(1, inlen + 1);
1472 			if (str == NULL)
1473 			{
1474 				fprintf(stderr, "\ncan't allocate memory - exiting\n");
1475 				close(stream);
1476 				close(sock);
1477 				exit(1);
1478 			}
1479 		}
1480 
1481 		n = 0;
1482 		while (n < inlen)
1483 		{
1484 			errno = 0;
1485 			bytes = recvfrom(stream, str + n, inlen - n, 0, NULL, NULL);
1486 			if (bytes <= 0)
1487 			{
1488 				fprintf(stderr, "\nrecvfrom (message body) returned %d (errno %d) at length %d of %d - exiting\n", bytes, errno, n, inlen);
1489 				break;
1490 			}
1491 			else n += bytes;
1492 		}
1493 
1494 		if (n < inlen)
1495 		{
1496 			fprintf(stderr, "\ntruncated message: expected %d bytes received %d bytes\n", inlen, n);
1497 			close(stream);
1498 			close(sock);
1499 			exit(1);
1500 		}
1501 
1502 		msg = asl_msg_from_string(str);
1503 		if (str != buf) free(str);
1504 		filter_and_print(msg, ql, f, pfmt, pflags);
1505 		asl_msg_release(msg);
1506 	}
1507 
1508 	close(stream);
1509 	close(sock);
1510 
1511 	address.sin_addr.s_addr = 0;
1512 }
1513 #endif
1514 
1515 int
sort_compare_key(asl_msg_t * a,asl_msg_t * b,const char * key)1516 sort_compare_key(asl_msg_t *a, asl_msg_t *b, const char *key)
1517 {
1518 	const char *va, *vb;
1519 	uint64_t na, nb;
1520 
1521 	if (key == NULL) return 0;
1522 
1523 	va = asl_msg_get_val_for_key(a, key);
1524 	vb = asl_msg_get_val_for_key(b, key);
1525 
1526 	if (va == NULL) return -1;
1527 	if (vb == NULL) return 1;
1528 
1529 	if (sort_numeric == 1)
1530 	{
1531 		na = atoll(va);
1532 		nb = atoll(vb);
1533 		if (na < nb) return -1;
1534 		if (na > nb) return 1;
1535 		return 0;
1536 	}
1537 
1538 	return strcmp(va, vb);
1539 }
1540 
1541 int
sort_compare(const void * ap,const void * bp)1542 sort_compare(const void *ap, const void *bp)
1543 {
1544 	int cmp;
1545 	asl_msg_t *a, *b;
1546 
1547 	if (sort_key == NULL) return 0;
1548 
1549 	a = (asl_msg_t *)ap;
1550 	b = (asl_msg_t *)bp;
1551 
1552 	cmp = sort_compare_key(a, b, sort_key);
1553 	if ((cmp == 0) && (sort_key_2 != NULL)) cmp = sort_compare_key(a, b, sort_key_2);
1554 
1555 	return cmp;
1556 }
1557 
1558 void
search_once(FILE * f,char * pfmt,int pflags,asl_msg_list_t * ql,uint64_t qmin,uint64_t * cmax,uint32_t count,uint32_t batch,int dir,uint32_t tail)1559 search_once(FILE *f, char *pfmt, int pflags, asl_msg_list_t *ql, uint64_t qmin, uint64_t *cmax, uint32_t count, uint32_t batch, int dir, uint32_t tail)
1560 {
1561 	asl_msg_list_t *res;
1562 	int i, more, total;
1563 
1564 	if (pflags & FORMAT_XML) print_xml_header(f);
1565 
1566 	res = NULL;
1567 	more = 1;
1568 	total = 0;
1569 
1570 	while (more == 1)
1571 	{
1572 		if (batch == 0) more = 0;
1573 
1574 		if ((dbselect == DB_SELECT_ASL) || (dbselect == DB_SELECT_STORE)) res = store_query(ql, qmin, batch, dir, cmax);
1575 		else if (dbselect == DB_SELECT_FILES) res = file_query(ql, qmin, batch, dir, cmax);
1576 		else if (dbselect == DB_SELECT_SYSLOGD) res = syslogd_query(ql, qmin, batch, dir, cmax);
1577 		else if (dbselect == DB_SELECT_LEGACY) res = legacy_query(ql, qmin, batch, dir, cmax);
1578 
1579 		if ((dir >= 0) && (*cmax > qmin)) qmin = *cmax + 1;
1580 		else if ((dir < 0) && (*cmax < qmin)) qmin = *cmax - 1;
1581 
1582 		if (res == NULL)
1583 		{
1584 			more = 0;
1585 		}
1586 		else
1587 		{
1588 			if ((batch > 0) && (res->count < batch)) more = 0;
1589 			total += res->count;
1590 			if ((count > 0) && (total >= count)) more = 0;
1591 
1592 			i = 0;
1593 			if (tail != 0)
1594 			{
1595 				i = res->count - tail;
1596 				tail = 0;
1597 				if (i < 0) i = 0;
1598 			}
1599 
1600 			if (sort_key != NULL)
1601 			{
1602 				qsort(res->msg, res->count, sizeof(asl_msg_t *), sort_compare);
1603 			}
1604 
1605 			if ((f != NULL) || (export != NULL))
1606 			{
1607 				for (; i < res->count; i++) printmsg(f, res->msg[i], pfmt, pflags);
1608 			}
1609 
1610 			asl_msg_list_release(res);
1611 		}
1612 	}
1613 
1614 	if ((pflags & COMPRESS_DUPS) && (last_printmsg_count > 0))
1615 	{
1616 		fprintf(f, "--- last message repeated %d time%s ---\n", last_printmsg_count, (last_printmsg_count == 1) ? "" : "s");
1617 		free(last_printmsg_str);
1618 		last_printmsg_str = NULL;
1619 		last_printmsg_count = 0;
1620 	}
1621 
1622 	if (pflags & FORMAT_XML) print_xml_trailer(f);
1623 }
1624 
1625 uint32_t
optype(char * o)1626 optype(char *o)
1627 {
1628 	uint32_t op, i;
1629 
1630 	op = ASL_QUERY_OP_NULL;
1631 
1632 	if (o == NULL) return op;
1633 
1634 	for (i = 0; o[i] != '\0'; i++)
1635 	{
1636 		if (o[i] == MOD_CASE_FOLD) op |= ASL_QUERY_OP_CASEFOLD;
1637 		else if (o[i] == MOD_REGEX) op |= ASL_QUERY_OP_REGEX;
1638 		else if (o[i] == MOD_NUMERIC) op |= ASL_QUERY_OP_NUMERIC;
1639 		else if (o[i] == MOD_SUBSTRING) op |= ASL_QUERY_OP_SUBSTRING;
1640 		else if (o[i] == MOD_PREFIX) op |= ASL_QUERY_OP_PREFIX;
1641 		else if (o[i] == MOD_SUFFIX) op |= ASL_QUERY_OP_SUFFIX;
1642 
1643 		else if (!strncasecmp(o+i, OP_EQ, sizeof(OP_EQ)))
1644 		{
1645 			op |= ASL_QUERY_OP_EQUAL;
1646 			i += (sizeof(OP_EQ) - 2);
1647 		}
1648 		else if (!strncasecmp(o+i, OP_NE, sizeof(OP_NE)))
1649 		{
1650 			op |= ASL_QUERY_OP_NOT_EQUAL;
1651 			i += (sizeof(OP_NE) - 2);
1652 		}
1653 		else if (!strncasecmp(o+i, OP_GT, sizeof(OP_GT)))
1654 		{
1655 			op |= ASL_QUERY_OP_GREATER;
1656 			i += (sizeof(OP_GT) - 2);
1657 		}
1658 		else if (!strncasecmp(o+i, OP_GE, sizeof(OP_GE)))
1659 		{
1660 			op |= ASL_QUERY_OP_GREATER_EQUAL;
1661 			i += (sizeof(OP_GE) - 2);
1662 		}
1663 		else if (!strncasecmp(o+i, OP_LT, sizeof(OP_LT)))
1664 		{
1665 			op |= ASL_QUERY_OP_LESS;
1666 			i += (sizeof(OP_LT) - 2);
1667 		}
1668 		else if (!strncasecmp(o+i, OP_LE, sizeof(OP_LE)))
1669 		{
1670 			op |= ASL_QUERY_OP_LESS_EQUAL;
1671 			i += (sizeof(OP_LE) - 2);
1672 		}
1673 		else
1674 		{
1675 			fprintf(stderr, "invalid option: %s\n", o);
1676 			return 0;
1677 		}
1678 	}
1679 
1680 	/* sanity check */
1681 	if (op & ASL_QUERY_OP_NUMERIC)
1682 	{
1683 		if (op & ASL_QUERY_OP_CASEFOLD)
1684 		{
1685 			fprintf(stderr, "warning: case fold modifier has no effect with numeric comparisons\n");
1686 			op &= ~ASL_QUERY_OP_CASEFOLD;
1687 		}
1688 
1689 		if (op & ASL_QUERY_OP_REGEX)
1690 		{
1691 			fprintf(stderr, "warning: regex modifier has no effect with numeric comparisons\n");
1692 			op &= ~ASL_QUERY_OP_REGEX;
1693 		}
1694 
1695 		if (op & ASL_QUERY_OP_SUBSTRING)
1696 		{
1697 			fprintf(stderr, "warning: substring modifier has no effect with numeric comparisons\n");
1698 			op &= ~ASL_QUERY_OP_SUBSTRING;
1699 		}
1700 
1701 		if (op & ASL_QUERY_OP_PREFIX)
1702 		{
1703 			fprintf(stderr, "warning: prefix modifier has no effect with numeric comparisons\n");
1704 			op &= ~ASL_QUERY_OP_PREFIX;
1705 		}
1706 
1707 		if (op & ASL_QUERY_OP_SUFFIX)
1708 		{
1709 			fprintf(stderr, "warning: suffix modifier has no effect with numeric comparisons\n");
1710 			op &= ~ASL_QUERY_OP_SUFFIX;
1711 		}
1712 	}
1713 
1714 	if (op & ASL_QUERY_OP_REGEX)
1715 	{
1716 		if (op & ASL_QUERY_OP_SUBSTRING)
1717 		{
1718 			fprintf(stderr, "warning: substring modifier has no effect with regular expression comparisons\n");
1719 			op &= ~ASL_QUERY_OP_SUBSTRING;
1720 		}
1721 
1722 		if (op & ASL_QUERY_OP_PREFIX)
1723 		{
1724 			fprintf(stderr, "warning: prefix modifier has no effect with regular expression comparisons\n");
1725 			op &= ~ASL_QUERY_OP_PREFIX;
1726 		}
1727 
1728 		if (op & ASL_QUERY_OP_SUFFIX)
1729 		{
1730 			fprintf(stderr, "warning: suffix modifier has no effect with regular expression comparisons\n");
1731 			op &= ~ASL_QUERY_OP_SUFFIX;
1732 		}
1733 	}
1734 
1735 	return op;
1736 }
1737 
1738 int
add_op(asl_msg_t * q,char * key,char * op,char * val,uint32_t flags)1739 add_op(asl_msg_t *q, char *key, char *op, char *val, uint32_t flags)
1740 {
1741 	uint32_t o;
1742 	const char *qval;
1743 
1744 	if (key == NULL) return -1;
1745 	if (q == NULL) return -1;
1746 
1747 	qval = NULL;
1748 	if (strcmp(key, ASL_KEY_TIME) == 0)
1749 	{
1750 		qval = (const char *)val;
1751 	}
1752 	else if ((strcmp(key, ASL_KEY_LEVEL) == 0) && (_isanumber(val) == 0))
1753 	{
1754 		/* Convert level strings to numeric values */
1755 		qval = asl_string_to_char_level(val);
1756 		if (qval == NULL)
1757 		{
1758 			fprintf(stderr, "invalid value for \"Level\"key: %s\n", val);
1759 			return -1;
1760 		}
1761 	}
1762 
1763 	o = ASL_QUERY_OP_NULL;
1764 	if (val == NULL) o = ASL_QUERY_OP_TRUE;
1765 
1766 	if (op != NULL)
1767 	{
1768 		o = optype(op);
1769 		if (o == ASL_QUERY_OP_NULL) return -1;
1770 		if (val == NULL)
1771 		{
1772 			fprintf(stderr, "no value supplied for operator %s %s\n", key, op);
1773 			return -1;
1774 		}
1775 
1776 		if ((qval == NULL) && (o & ASL_QUERY_OP_NUMERIC) && (_isanumber(val) == 0))
1777 		{
1778 			fprintf(stderr, "non-numeric value supplied for numeric operator %s %s %s\n", key, op, val);
1779 			return -1;
1780 		}
1781 	}
1782 
1783 	o |= flags;
1784 	if (qval != NULL) asl_msg_set_key_val_op(q, key, qval, o);
1785 	else asl_msg_set_key_val_op(q, key, val, o);
1786 
1787 	return 0;
1788 }
1789 
1790 static uint32_t
add_db_file(const char * name)1791 add_db_file(const char *name)
1792 {
1793 	asl_file_t *s;
1794 	uint32_t status;
1795 
1796 	if (dbselect == DB_SELECT_LEGACY)
1797 	{
1798 		fprintf(stderr, "syslog can only read one legacy format database\n");
1799 		fprintf(stderr, "can't combine legacy and non-legacy databases in a single search\n");
1800 		exit(1);
1801 	}
1802 
1803 	/* shouldn't happen */
1804 	if (name == NULL) return DB_SELECT_ASL;
1805 
1806 	s = NULL;
1807 	status = asl_file_open_read(name, &s);
1808 	if (status != ASL_STATUS_OK)
1809 	{
1810 		fprintf(stderr, "data store file %s open failed: %s \n", name, asl_core_error(status));
1811 		exit(1);
1812 	}
1813 
1814 	if (s == NULL)
1815 	{
1816 		fprintf(stderr, "data store file %s open failed\n", name);
1817 		exit(1);
1818 	}
1819 
1820 	if (s->flags & ASL_FILE_FLAG_LEGACY_STORE)
1821 	{
1822 		if (db_files != NULL)
1823 		{
1824 			fprintf(stderr, "syslog can only read a single legacy format database\n");
1825 			fprintf(stderr, "can't combine legacy and non-legacy databases in a single search\n");
1826 			exit(1);
1827 		}
1828 
1829 		legacy = s;
1830 		return DB_SELECT_LEGACY;
1831 	}
1832 
1833 	db_files = asl_file_list_add(db_files, s);
1834 	return DB_SELECT_FILES;
1835 }
1836 
1837 static uint32_t
add_db_dir(const char * name)1838 add_db_dir(const char *name)
1839 {
1840 	DIR *dp;
1841 	struct dirent *dent;
1842 	uint32_t status;
1843 	asl_file_t *s;
1844 	char *path;
1845 
1846 	/*
1847 	 * Try opening as a data store
1848 	 */
1849 	status = asl_store_open_read(name, &store);
1850 	if (status == 0)
1851 	{
1852 		if (name == NULL) return DB_SELECT_ASL;
1853 		if (!strcmp(name, PATH_ASL_STORE)) return DB_SELECT_ASL;
1854 		return DB_SELECT_STORE;
1855 	}
1856 
1857 	/*
1858 	 * Open all readable files
1859 	 */
1860 	dp = opendir(name);
1861 	if (dp == NULL)
1862 	{
1863 		fprintf(stderr, "%s: %s\n", name, strerror(errno));
1864 		exit(1);
1865 	}
1866 
1867 	while ((dent = readdir(dp)) != NULL)
1868 	{
1869 		if (dent->d_name[0] == '.') continue;
1870 
1871 		path = NULL;
1872 		asprintf(&path, "%s/%s", name, dent->d_name);
1873 
1874 		/*
1875 		 * asl_file_open_read will fail if path is NULL,
1876 		 * if the file is not an ASL store file,
1877 		 * or if it isn't readable.
1878 		 */
1879 		s = NULL;
1880 		status = asl_file_open_read(path, &s);
1881 		if (path != NULL) free(path);
1882 		if ((status != ASL_STATUS_OK) || (s == NULL)) continue;
1883 
1884 		db_files = asl_file_list_add(db_files, s);
1885 	}
1886 
1887 	closedir(dp);
1888 
1889 	return DB_SELECT_FILES;
1890 }
1891 
1892 int
main(int argc,char * argv[])1893 main(int argc, char *argv[])
1894 {
1895 	FILE *outfile;
1896 	int i, j, n, watch, status, pflags, iamroot, user_tflag, export_preserve_id, saw_dash_d, since_boot;
1897 	int notify_file, notify_token;
1898 	asl_msg_list_t *qlist;
1899 	asl_msg_t *cq;
1900 	char *pfmt;
1901 	const char *exportname;
1902 	uint32_t flags, tail_count, batch, encode;
1903 	uint64_t qmin, cmax;
1904 
1905 	watch = 0;
1906 	iamroot = 0;
1907 	user_tflag = 0;
1908 	pfmt = NULL;
1909 	flags = 0;
1910 	tail_count = 0;
1911 	batch = FETCH_BATCH;
1912 	pflags = FORMAT_STD | COMPRESS_DUPS;
1913 	encode = ASL_ENCODE_SAFE;
1914 	cq = NULL;
1915 	exportname = NULL;
1916 	export_preserve_id = 0;
1917 	saw_dash_d = 0;
1918 	since_boot = 0;
1919 
1920 	i = asl_store_location();
1921 	if (i == ASL_STORE_LOCATION_MEMORY) dbselect = DB_SELECT_SYSLOGD;
1922 
1923 	if (getuid() == 0) iamroot = 1;
1924 
1925 	for (i = 1; i < argc; i++)
1926 	{
1927 		if ((!strcmp(argv[i], "-help")) || (!strcmp(argv[i], "--help")))
1928 		{
1929 			usage();
1930 			exit(0);
1931 		}
1932 
1933 		if ((!strcmp(argv[i], "-time")) || (!strcmp(argv[i], "--time")))
1934 		{
1935 			qmin = time(NULL);
1936 			printf("%llu\n", qmin);
1937 			exit(0);
1938 		}
1939 
1940 		if ((!strcmp(argv[i], "-config")) || (!strcmp(argv[i], "--config")))
1941 		{
1942 			syslog_config(argc, argv);
1943 			exit(0);
1944 		}
1945 
1946 		if ((!strcmp(argv[i], "-control")) || (!strcmp(argv[i], "--control")))
1947 		{
1948 			syslog_control(argc, argv);
1949 			exit(0);
1950 		}
1951 
1952 		if ((!strcmp(argv[i], "-module")) || (!strcmp(argv[i], "--module")))
1953 		{
1954 			module_control(argc, argv);
1955 			exit(0);
1956 		}
1957 
1958 		if (!strcmp(argv[i], "-s"))
1959 		{
1960 			syslog_send(argc, argv);
1961 			exit(0);
1962 		}
1963 
1964 		if (!strcmp(argv[i], "-c"))
1965 		{
1966 			syslog_remote_control(argc, argv);
1967 			exit(0);
1968 		}
1969 	}
1970 
1971 	qlist = asl_msg_list_new();
1972 	if (qlist == NULL) exit(1);
1973 
1974 	cq = NULL;
1975 
1976 	for (i = 1; i < argc; i++)
1977 	{
1978 		if (!strcmp(argv[i], "-f"))
1979 		{
1980 			if ((i + 1) < argc)
1981 			{
1982 				for (j = i + 1; j < argc; j++)
1983 				{
1984 					if (!strcmp(argv[j], "-"))
1985 					{
1986 						dbselect = DB_SELECT_SYSLOGD;
1987 						i++;
1988 						break;
1989 					}
1990 					else if (argv[j][0] == '-')
1991 					{
1992 						break;
1993 					}
1994 					else
1995 					{
1996 						dbselect = add_db_file(argv[j]);
1997 						i++;
1998 					}
1999 				}
2000 			}
2001 		}
2002 		else if (!strcmp(argv[i], "-d"))
2003 		{
2004 			saw_dash_d = i + 1;
2005 
2006 			if (saw_dash_d < argc)
2007 			{
2008 				for (j = saw_dash_d; j < argc; j++)
2009 				{
2010 					if (!strcmp(argv[j], "store"))
2011 					{
2012 						dbselect = add_db_dir(PATH_ASL_STORE);
2013 						i++;
2014 					}
2015 					else if (!strcmp(argv[j], "archive"))
2016 					{
2017 						dbselect = add_db_dir(PATH_ASL_ARCHIVE);
2018 						i++;
2019 					}
2020 					else if (argv[j][0] == '-')
2021 					{
2022 						break;
2023 					}
2024 					else
2025 					{
2026 						dbselect = add_db_dir(argv[j]);
2027 						i++;
2028 					}
2029 				}
2030 			}
2031 			else
2032 			{
2033 				fprintf(stderr, "missing directory name following -d flag\n");
2034 				return -1;
2035 			}
2036 		}
2037 		else if (!strcmp(argv[i], "-b"))
2038 		{
2039 			batch = atoi(argv[++i]);
2040 		}
2041 		else if (!strcmp(argv[i], "-B"))
2042 		{
2043 			since_boot = 1;
2044 		}
2045 		else if (!strcmp(argv[i], "-w"))
2046 		{
2047 			watch = 1;
2048 			tail_count = 10;
2049 			if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
2050 			{
2051 				i++;
2052 				if (!strcmp(argv[i], "all"))
2053 				{
2054 					tail_count = (uint32_t)-1;
2055 				}
2056 				else if (!strcmp(argv[i], "boot"))
2057 				{
2058 					since_boot = 1;
2059 				}
2060 				else
2061 				{
2062 					tail_count = atoi(argv[i]);
2063 				}
2064 			}
2065 		}
2066 		else if (!strcmp(argv[i], "-sort"))
2067 		{
2068 			if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
2069 			{
2070 				i++;
2071 				sort_key = argv[i];
2072 
2073 				if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
2074 				{
2075 					i++;
2076 					sort_key_2 = argv[i];
2077 				}
2078 			}
2079 			else
2080 			{
2081 				sort_key = ASL_KEY_MSG_ID;
2082 			}
2083 
2084 			batch = 0;
2085 		}
2086 		else if (!strcmp(argv[i], "-nsort"))
2087 		{
2088 			if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
2089 			{
2090 				i++;
2091 				sort_key = argv[i];
2092 
2093 				if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
2094 				{
2095 					i++;
2096 					sort_key_2 = argv[i];
2097 				}
2098 			}
2099 			else
2100 			{
2101 				sort_key = ASL_KEY_MSG_ID;
2102 			}
2103 
2104 			sort_numeric = 1;
2105 			batch = 0;
2106 		}
2107 		else if (!strcmp(argv[i], "-u"))
2108 		{
2109 			tfmt = "Z";
2110 			user_tflag = 1;
2111 		}
2112 		else if ((!strcmp(argv[i], "-x")) || (!strcmp(argv[i], "-X")))
2113 		{
2114 			if ((i + 1) >= argc)
2115 			{
2116 				asl_msg_list_release(qlist);
2117 				usage();
2118 				exit(1);
2119 			}
2120 
2121 			if (!strcmp(argv[i], "-x")) export_preserve_id = 1;
2122 
2123 			exportname = argv[++i];
2124 		}
2125 		else if (!strcmp(argv[i], "-E"))
2126 		{
2127 			if ((i + 1) >= argc)
2128 			{
2129 				asl_msg_list_release(qlist);
2130 				usage();
2131 				exit(1);
2132 			}
2133 
2134 			i++;
2135 
2136 			if (!strcmp(argv[i], "vis")) encode = ASL_ENCODE_ASL;
2137 			else if (!strcmp(argv[i], "safe")) encode = ASL_ENCODE_SAFE;
2138 			else if (!strcmp(argv[i], "xml")) encode = ASL_ENCODE_XML;
2139 			else if (!strcmp(argv[i], "none")) encode = ASL_ENCODE_NONE;
2140 			else if ((argv[i][0] >= '0') && (argv[i][0] <= '9') && (argv[i][1] == '\0')) encode = atoi(argv[i]);
2141 		}
2142 		else if (!strcmp(argv[i], "-F"))
2143 		{
2144 			if ((i + 1) >= argc)
2145 			{
2146 				asl_msg_list_release(qlist);
2147 				usage();
2148 				exit(1);
2149 			}
2150 
2151 			i++;
2152 
2153 			if (!strcmp(argv[i], "raw"))
2154 			{
2155 				pflags = FORMAT_RAW;
2156 				if (user_tflag == 0) tfmt = "sec";
2157 			}
2158 			else if (!strcmp(argv[i], "std"))
2159 			{
2160 				pflags = FORMAT_STD | COMPRESS_DUPS;
2161 			}
2162 			else if (!strcmp(argv[i], "bsd"))
2163 			{
2164 				pflags = FORMAT_LEGACY | COMPRESS_DUPS;
2165 			}
2166 			else if (!strcmp(argv[i], "xml"))
2167 			{
2168 				pflags = FORMAT_XML;
2169 				encode = ASL_ENCODE_XML;
2170 			}
2171 			else
2172 			{
2173 				pflags = 0;
2174 				pfmt = argv[i];
2175 			}
2176 		}
2177 		else if (!strcmp(argv[i], "-T"))
2178 		{
2179 			if ((i + 1) >= argc)
2180 			{
2181 				asl_msg_list_release(qlist);
2182 				usage();
2183 				exit(1);
2184 			}
2185 
2186 			i++;
2187 			tfmt = argv[i];
2188 			user_tflag = 1;
2189 		}
2190 		else if (!strcmp(argv[i], "-nodc"))
2191 		{
2192 			pflags = pflags & ~COMPRESS_DUPS;
2193 		}
2194 		else if (!strcmp(argv[i], "-o"))
2195 		{
2196 			flags = 0;
2197 
2198 			if (cq != NULL)
2199 			{
2200 				asl_msg_list_append(qlist, cq);
2201 				asl_msg_release(cq);
2202 				cq = NULL;
2203 			}
2204 		}
2205 		else if (!strcmp(argv[i], "-n"))
2206 		{
2207 			flags = ASL_QUERY_OP_NOT;
2208 		}
2209 		else if (!strcmp(argv[i], "-C"))
2210 		{
2211 			if (cq != NULL)
2212 			{
2213 				asl_msg_list_append(qlist, cq);
2214 				asl_msg_release(cq);
2215 				cq = NULL;
2216 			}
2217 
2218 			flags = 0;
2219 			cq = asl_msg_new(ASL_TYPE_QUERY);
2220 			status = add_op(cq, ASL_KEY_FACILITY, OP_EQ, FACILITY_CONSOLE, flags);
2221 			asl_msg_list_append(qlist, cq);
2222 			asl_msg_release(cq);
2223 			cq = NULL;
2224 
2225 			if (status != 0)
2226 			{
2227 				asl_msg_list_release(qlist);
2228 				exit(1);
2229 			}
2230 		}
2231 		else if (!strcmp(argv[i], "-k"))
2232 		{
2233 			i++;
2234 			for (n = i; n < argc; n++)
2235 			{
2236 				if (!strcmp(argv[n], "-o")) break;
2237 				if (!strcmp(argv[n], "-n")) break;
2238 				if (!strcmp(argv[n], "-k")) break;
2239 				if ((n - i) > 2)
2240 				{
2241 					fprintf(stderr, "invalid sequence: -k");
2242 					for (j = i; j <= n; j++) fprintf(stderr, " %s", argv[j]);
2243 					fprintf(stderr, "\n");
2244 					usage();
2245 					exit(1);
2246 				}
2247 			}
2248 
2249 			n -= i;
2250 			if (n == 0)
2251 			{
2252 				i--;
2253 				continue;
2254 			}
2255 
2256 			if (cq == NULL) cq = asl_msg_new(ASL_TYPE_QUERY);
2257 
2258 			status = 0;
2259 			if (n == 1) status = add_op(cq, argv[i], NULL, NULL, flags);
2260 			else if (n == 2) status = add_op(cq, argv[i], OP_EQ, argv[i+1], flags);
2261 			else status = add_op(cq, argv[i], argv[i+1], argv[i+2], flags);
2262 
2263 			flags = 0;
2264 			if (status != 0)
2265 			{
2266 				asl_msg_list_release(qlist);
2267 				exit(1);
2268 			}
2269 
2270 			i += (n - 1);
2271 		}
2272 		else
2273 		{
2274 			fprintf(stderr, "syslog: unknown option \"%s\"\n", argv[i]);
2275 			fprintf(stderr, "run \"syslog -help\" for usage\n");
2276 			exit(1);
2277 		}
2278 	}
2279 
2280 	if (cq != NULL)
2281 	{
2282 		asl_msg_list_append(qlist, cq);
2283 		asl_msg_release(cq);
2284 		cq = NULL;
2285 	}
2286 
2287 	pflags |= encode;
2288 
2289 	outfile = stdout;
2290 
2291 	/*
2292 	 * Catch and report some cases where watch (-w) doesn't work
2293 	 */
2294 	if (watch == 1)
2295 	{
2296 		if (sort_key != NULL)
2297 		{
2298 			fprintf(stderr, "Warning: -w flag has no effect with -sort flag\n");
2299 			watch = 0;
2300 		}
2301 
2302 		if (dbselect == DB_SELECT_FILES)
2303 		{
2304 			if (saw_dash_d == 0)
2305 			{
2306 				fprintf(stderr, "Warning: -w flag not supported for a set of one or more files\n");
2307 			}
2308 			else
2309 			{
2310 				fprintf(stderr, "Warning: directory \"%s\" is not an ASL data store\n", argv[saw_dash_d]);
2311 				fprintf(stderr, "         -w flag not supported for a set of one or more files\n");
2312 			}
2313 
2314 			watch = 0;
2315 		}
2316 	}
2317 
2318 	if (exportname != NULL)
2319 	{
2320 		if (watch == 1)
2321 		{
2322 			fprintf(stderr, "Warning: -w flag has no effect with -x export flag\n");
2323 			watch = 0;
2324 		}
2325 
2326 		status = asl_file_open_write(exportname, 0644, -1, -1, &export);
2327 		if (status != ASL_STATUS_OK)
2328 		{
2329 			asl_msg_list_release(qlist);
2330 			fprintf(stderr, "export file open failed: %s\n", asl_core_error(status));
2331 			exit(1);
2332 		}
2333 
2334 		/*
2335 		 * allow the string cache to be unlimited to maximize string dup compression
2336 		 * preserve message IDs
2337 		 */
2338 		export->flags = ASL_FILE_FLAG_UNLIMITED_CACHE;
2339 		if (export_preserve_id != 0) export->flags |= ASL_FILE_FLAG_PRESERVE_MSG_ID;
2340 
2341 		outfile = NULL;
2342 		pflags = EXPORT;
2343 	}
2344 
2345 	qmin = 0;
2346 	cmax = 0;
2347 	notify_file = -1;
2348 	notify_token = -1;
2349 
2350 	/* set starting point */
2351 	if (since_boot == 1)
2352 	{
2353 		/* search back for last "BOOT_TIME (ut_type == 2) record */
2354 		asl_msg_list_t *bt;
2355 		asl_msg_t *bq;
2356 
2357 		bt = asl_msg_list_new();
2358 		if (bt == NULL)
2359 		{
2360 			fprintf(stderr, "\ncan't allocate memory - exiting\n");
2361 			exit(1);
2362 		}
2363 
2364 		bq = asl_msg_new(ASL_TYPE_QUERY);
2365 		if (bq == NULL)
2366 		{
2367 			fprintf(stderr, "\ncan't allocate memory - exiting\n");
2368 			exit(1);
2369 		}
2370 
2371 		asl_msg_list_append(bt, bq);
2372 		asl_msg_release(bq);
2373 
2374 		asl_msg_set_key_val_op(bq, "ut_type", "2", ASL_QUERY_OP_EQUAL);
2375 
2376 		search_once(NULL, NULL, 0, (asl_msg_list_t *)bt, -1, &qmin, 1, 1, -1, 0);
2377 		asl_msg_list_release(bt);
2378 
2379 		if (qmin > 0) qmin--;
2380 		tail_count = 0;
2381 	}
2382 	else if (watch == 1)
2383 	{
2384 		/* go back tail_count records from last record */
2385 		qmin = -1;
2386 		search_once(NULL, NULL, 0, qlist, qmin, &cmax, 1, 1, -1, 0);
2387 
2388 		if (cmax >= tail_count) qmin = cmax - tail_count;
2389 		else qmin = 0;
2390 
2391 		tail_count = 0;
2392 	}
2393 
2394 	if ((watch == 1) && (dbselect == DB_SELECT_ASL))
2395 	{
2396 		status = notify_register_file_descriptor("com.apple.system.logger.message", &notify_file, 0, &notify_token);
2397 		if (status != NOTIFY_STATUS_OK) notify_token = -1;
2398 	}
2399 
2400 	/* output should be line buffered */
2401 	if (outfile != NULL) setlinebuf(outfile);
2402 
2403 	search_once(outfile, pfmt, pflags, qlist, qmin + 1, &cmax, 0, 0, 1, tail_count);
2404 
2405 	if (watch == 1)
2406 	{
2407 		if (dbselect == DB_SELECT_SYSLOGD)
2408 		{
2409 #if TARGET_OS_EMBEDDED
2410 			syslogd_direct_watch(outfile, pfmt, pflags, qlist);
2411 #else
2412 			fprintf(stderr, "Warning: -w flag cannot be used when querying syslogd directly\n");
2413 			exit(1);
2414 #endif
2415 		}
2416 		else if (notify_token == -1)
2417 		{
2418 			forever
2419 			{
2420 				usleep(500000);
2421 				if (cmax > qmin) qmin = cmax;
2422 				search_once(outfile, pfmt, pflags, qlist, qmin + 1, &cmax, 0, batch, 1, 0);
2423 			}
2424 		}
2425 		else
2426 		{
2427 			while (read(notify_file, &i, 4) == 4)
2428 			{
2429 				if (cmax > qmin) qmin = cmax;
2430 				search_once(outfile, pfmt, pflags, qlist, qmin + 1, &cmax, 0, batch, 1, 0);
2431 			}
2432 		}
2433 	}
2434 
2435 	if (db_files != NULL) asl_file_list_close(db_files);
2436 	if (store != NULL) asl_store_release(store);
2437 	if (export != NULL) asl_file_release(export);
2438 
2439 	asl_msg_list_release(qlist);
2440 
2441 	exit(0);
2442 }
2443