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