1/*
2 * Copyright (c) 2007-2009 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 <stdio.h>
25#include <dirent.h>
26#include <string.h>
27#include <stdlib.h>
28#include <unistd.h>
29#include <stdint.h>
30#include <errno.h>
31#include <time.h>
32#include <sys/time.h>
33#include <sys/stat.h>
34#include <sys/param.h>
35#include <servers/bootstrap.h>
36#include <bootstrap_priv.h>
37#include <mach/mach.h>
38#include <copyfile.h>
39#include <fcntl.h>
40#include <zlib.h>
41#include <xpc/xpc.h>
42#include <xpc/private.h>
43#include <os/assumes.h>
44#include <vproc_priv.h>
45#include <asl.h>
46#include <asl_private.h>
47#include <asl_core.h>
48#include <asl_file.h>
49#include <asl_store.h>
50#include "asl_common.h"
51
52#define DEFAULT_MAX_SIZE 150000000
53#define IOBUFSIZE 4096
54
55#define DO_ASLDB	0x00000001
56#define DO_MODULE	0x00000002
57#define DO_CHECKPT	0x00000004
58
59#define DEBUG_FLAG_MASK  0xfffffff0
60#define DEBUG_LEVEL_MASK 0x0000000f
61#define DEBUG_STDERR     0x00000010
62#define DEBUG_ASL        0x00000020
63
64#define AUX_URL_MINE "file:///var/log/asl/"
65#define AUX_URL_MINE_LEN 20
66
67/* length of "file://" */
68#define AUX_URL_PATH_OFFSET 7
69
70extern kern_return_t _asl_server_query
71(
72 mach_port_t server,
73 caddr_t request,
74 mach_msg_type_number_t requestCnt,
75 uint64_t startid,
76 int count,
77 int flags,
78 caddr_t *reply,
79 mach_msg_type_number_t *replyCnt,
80 uint64_t *lastid,
81 int *status,
82 security_token_t *token
83);
84
85/* global */
86static time_t module_ttl;
87static uint32_t debug;
88static int dryrun;
89static int asl_aux_fd = -1;
90static aslclient aslc;
91static mach_port_t asl_server_port;
92static xpc_connection_t listener;
93static dispatch_queue_t serverq;
94
95typedef struct name_list_s
96{
97	char *name;
98	size_t size;
99	struct name_list_s *next;
100} name_list_t;
101
102static const char *
103keep_str(uint8_t mask)
104{
105	static char str[9];
106	uint32_t x = 0;
107
108	memset(str, 0, sizeof(str));
109	if (mask & 0x01) str[x++] = '0';
110	if (mask & 0x02) str[x++] = '1';
111	if (mask & 0x04) str[x++] = '2';
112	if (mask & 0x08) str[x++] = '3';
113	if (mask & 0x10) str[x++] = '4';
114	if (mask & 0x20) str[x++] = '5';
115	if (mask & 0x40) str[x++] = '6';
116	if (mask & 0x80) str[x++] = '7';
117	if (x == 0) str[x++] = '-';
118	return str;
119}
120
121void
122set_debug(int flag, const char *str)
123{
124	int level, x;
125
126	if (str == NULL) x = ASL_LEVEL_ERR;
127	else if (((str[0] == 'L') || (str[0] == 'l')) && ((str[1] >= '0') && (str[1] <= '7')) && (str[2] == '\0')) x = atoi(str+1);
128	else if ((str[0] >= '0') && (str[0] <= '7') && (str[1] == '\0')) x = ASL_LEVEL_CRIT + atoi(str);
129	else x = ASL_LEVEL_ERR;
130
131	if (x <= 0) x = 0;
132	else if (x > 7) x = 7;
133
134	level = debug & DEBUG_LEVEL_MASK;
135	if (x > level) level = x;
136
137	debug = debug & DEBUG_FLAG_MASK;
138	debug |= flag;
139	debug |= level;
140}
141
142void
143debug_log(int level, const char *str, ...)
144{
145	va_list v;
146
147	if ((debug & DEBUG_STDERR) && (level <= (debug & DEBUG_LEVEL_MASK)))
148	{
149		va_start(v, str);
150		vfprintf(stderr, str, v);
151		va_end(v);
152	}
153
154	if (debug & DEBUG_ASL)
155	{
156		char *line = NULL;
157
158		if (aslc == NULL)
159		{
160			aslc = asl_open("aslmanager", "syslog", 0);
161			asl_msg_t *msg = asl_msg_new(ASL_TYPE_MSG);
162
163			asl_msg_set_key_val(msg, ASL_KEY_MSG, "Status Report");
164			asl_msg_set_key_val(msg, ASL_KEY_LEVEL, ASL_STRING_NOTICE);
165			asl_create_auxiliary_file((asl_object_t)msg, "Status Report", "public.text", &asl_aux_fd);
166			asl_msg_release(msg);
167		}
168
169		va_start(v, str);
170		vasprintf(&line, str, v);
171		va_end(v);
172
173		if (line != NULL) write(asl_aux_fd, line, strlen(line));
174		free(line);
175	}
176}
177
178__attribute__((noreturn)) static void
179xpc_server_exit(int status)
180{
181	xpc_connection_cancel(listener);
182	xpc_release(listener);
183	dispatch_release(serverq);
184	exit(status);
185}
186
187name_list_t *
188add_to_name_list(name_list_t *l, const char *name, size_t size)
189{
190	name_list_t *e, *x;
191
192	if (name == NULL) return l;
193
194	e = (name_list_t *)calloc(1, sizeof(name_list_t));
195	if (e == NULL) return NULL;
196
197	e->name = strdup(name);
198	if (e->name == NULL)
199	{
200		free(e);
201		return NULL;
202	}
203
204	e->size = size;
205
206	/* list is sorted by name (i.e. primarily by timestamp) */
207	if (l == NULL) return e;
208
209	if (strcmp(e->name, l->name) <= 0)
210	{
211		e->next = l;
212		return e;
213	}
214
215	for (x = l; (x->next != NULL) && (strcmp(e->name, x->next->name) > 0) ; x = x->next);
216
217	e->next = x->next;
218	x->next = e;
219	return l;
220}
221
222void
223free_name_list(name_list_t *l)
224{
225	name_list_t *e;
226
227	while (l != NULL)
228	{
229		e = l;
230		l = l->next;
231		free(e->name);
232		free(e);
233	}
234
235	free(l);
236}
237/*
238 * Copy ASL files by reading and writing each record.
239 */
240uint32_t
241copy_asl_file(const char *src, const char *dst, mode_t mode)
242{
243	asl_msg_list_t *res;
244	asl_file_t *f;
245	uint32_t status, i;
246	uint64_t mid;
247	size_t rcount;
248
249	if (src == NULL) return ASL_STATUS_INVALID_ARG;
250	if (dst == NULL) return ASL_STATUS_INVALID_ARG;
251
252	f = NULL;
253	status = asl_file_open_read(src, &f);
254	if (status != ASL_STATUS_OK) return status;
255
256	res = NULL;
257	mid = 0;
258
259	res = asl_file_match(f, NULL, &mid, 0, 0, 0, 1);
260	asl_file_close(f);
261
262	if (res == NULL) return ASL_STATUS_OK;
263	rcount = asl_msg_list_count(res);
264	if (rcount == 0)
265	{
266		asl_msg_list_release(res);
267		return ASL_STATUS_OK;
268	}
269
270	f = NULL;
271	status = asl_file_open_write(dst, mode, -1, -1, &f);
272	if (status != ASL_STATUS_OK) return status;
273	if (f == ASL_STATUS_OK) return ASL_STATUS_FAILED;
274
275	f->flags = ASL_FILE_FLAG_PRESERVE_MSG_ID;
276
277	for (i = 0; i < rcount; i++)
278	{
279		mid = 0;
280		status = asl_file_save(f, asl_msg_list_get_index(res, i), &mid);
281		if (status != ASL_STATUS_OK) break;
282	}
283
284	asl_file_close(f);
285	return status;
286}
287
288int
289copy_compress_file(asl_out_dst_data_t *asldst, const char *src, const char *dst)
290{
291	int in, out;
292	size_t n;
293	gzFile gz;
294	char buf[IOBUFSIZE];
295
296	in = open(src, O_RDONLY, 0);
297	if (in < 0) return -1;
298
299	out = open(dst, O_WRONLY | O_CREAT, asldst->mode);
300	if (out >= 0) out = asl_out_dst_set_access(out, asldst);
301	if (out < 0)
302	{
303		close(in);
304		return -1;
305	}
306
307	gz = gzdopen(out, "w");
308	if (gz == NULL)
309	{
310		close(in);
311		close(out);
312		return -1;
313	}
314
315	do {
316		n = read(in, buf, sizeof(buf));
317		if (n > 0) gzwrite(gz, buf, n);
318	} while (n == IOBUFSIZE);
319
320	gzclose(gz);
321	close(in);
322	close(out);
323
324	return 0;
325}
326
327void
328filesystem_rename(const char *src, const char *dst)
329{
330	int status = 0;
331
332	debug_log(ASL_LEVEL_NOTICE, "  rename %s ---> %s\n", src, dst);
333	if (dryrun == 1) return;
334
335	status = rename(src, dst);
336	if (status != 0) debug_log(ASL_LEVEL_ERR, "  FAILED status %d errno %d [%s] rename %s ---> %s\n", status, errno, strerror(errno), src, dst);
337}
338
339void
340filesystem_unlink(const char *path)
341{
342	int status = 0;
343
344	debug_log(ASL_LEVEL_NOTICE, "  remove %s\n", path);
345	if (dryrun == 1) return;
346
347	status = unlink(path);
348	if (status != 0) debug_log(ASL_LEVEL_ERR, "  FAILED status %d errno %d [%s] unlink %s\n", status, errno, strerror(errno), path);
349}
350
351void
352filesystem_truncate(const char *path)
353{
354	int status = 0;
355
356	debug_log(ASL_LEVEL_NOTICE, "  truncate %s\n", path);
357	if (dryrun == 1) return;
358
359	status = truncate(path, 0);
360	if (status != 0) debug_log(ASL_LEVEL_ERR, "  FAILED status %d errno %d [%s] unlink %s\n", status, errno, strerror(errno), path);
361}
362
363void
364filesystem_rmdir(const char *path)
365{
366	int status = 0;
367
368	debug_log(ASL_LEVEL_NOTICE, "  remove directory %s\n", path);
369	if (dryrun == 1) return;
370
371	status = rmdir(path);
372	if (status != 0) debug_log(ASL_LEVEL_ERR, "  FAILED status %d errno %d [%s] rmdir %s\n", status, errno, strerror(errno), path);
373}
374
375int32_t
376filesystem_copy(asl_out_dst_data_t *asldst, const char *src, const char *dst, uint32_t flags)
377{
378	char *dot;
379
380	if ((src == NULL) || (dst == NULL)) return 0;
381
382	dot = strrchr(src, '.');
383	if ((dot != NULL) && (!strcmp(dot, ".gz"))) flags &= ~MODULE_FLAG_COMPRESS;
384
385	if (((flags & MODULE_FLAG_COMPRESS) == 0) && (!strcmp(src, dst))) return 0;
386
387	if (flags & MODULE_FLAG_TYPE_ASL) debug_log(ASL_LEVEL_NOTICE, "  copy asl %s ---> %s\n", src, dst);
388	else if (flags & MODULE_FLAG_COMPRESS) debug_log(ASL_LEVEL_NOTICE, "  copy compress %s ---> %s.gz\n", src, dst);
389	else debug_log(ASL_LEVEL_NOTICE, "  copy %s ---> %s\n", src, dst);
390
391	if (dryrun == 1) return 0;
392
393	if (flags & MODULE_FLAG_TYPE_ASL)
394	{
395		uint32_t status = copy_asl_file(src, dst, asldst->mode);
396		if (status != 0)
397		{
398			debug_log(ASL_LEVEL_ERR, "  FAILED status %u [%s] asl copy %s ---> %s\n", status, asl_core_error(status), src, dst);
399			return 0;
400		}
401	}
402	else if (flags & MODULE_FLAG_COMPRESS)
403	{
404		char gzdst[MAXPATHLEN];
405
406		snprintf(gzdst, sizeof(gzdst), "%s.gz", dst);
407
408		int status = copy_compress_file(asldst, src, gzdst);
409		if (status != 0)
410		{
411			debug_log(ASL_LEVEL_ERR, "  FAILED status %d errno %d [%s] copy & compress %s ---> %s\n", status, errno, strerror(errno), src, dst);
412			return 0;
413		}
414	}
415	else
416	{
417		int status = copyfile(src, dst, NULL, COPYFILE_ALL | COPYFILE_RECURSIVE);
418		if (status != 0)
419		{
420			debug_log(ASL_LEVEL_ERR, "  FAILED status %d errno %d [%s] copy %s ---> %s\n", status, errno, strerror(errno), src, dst);
421			return 0;
422		}
423	}
424
425	return 1;
426}
427
428int
429remove_directory(const char *path)
430{
431	DIR *dp;
432	struct dirent *dent;
433	char *str;
434
435	dp = opendir(path);
436	if (dp == NULL) return 0;
437
438	while ((dent = readdir(dp)) != NULL)
439	{
440		if ((!strcmp(dent->d_name, ".")) || (!strcmp(dent->d_name, ".."))) continue;
441		asprintf(&str, "%s/%s", path, dent->d_name);
442		if (str != NULL)
443		{
444			filesystem_unlink(str);
445			free(str);
446			str = NULL;
447		}
448	}
449
450	closedir(dp);
451	filesystem_rmdir(path);
452
453	return 0;
454}
455
456/*
457 * Determine the age (in whole days) of a YMD file from its name.
458 * Also determines UID and GID from ".Unnn.Gnnn" part of file name.
459 */
460uint32_t
461ymd_file_age(const char *name, time_t now, uid_t *u, gid_t *g)
462{
463	struct tm ftime;
464	time_t created;
465	uint32_t days;
466	const char *p;
467
468	if (name == NULL) return 0;
469
470	if (now == 0) now = time(NULL);
471
472	memset(&ftime, 0, sizeof(struct tm));
473	ftime.tm_hour = 24;
474
475	/* name is YYYY.MM.DD.<...> */
476
477	if ((name[0] < '0') || (name[0] > '9')) return 0;
478	ftime.tm_year = 1000 * (name[0] - '0');
479
480	if ((name[1] < '0') || (name[1] > '9')) return 0;
481	ftime.tm_year += 100 * (name[1] - '0');
482
483	if ((name[2] < '0') || (name[2] > '9')) return 0;
484	ftime.tm_year += 10 * (name[2] - '0');
485
486	if ((name[3] < '0') || (name[3] > '9')) return 0;
487	ftime.tm_year += name[3] - '0';
488	ftime.tm_year -= 1900;
489
490	if (name[4] != '.') return 0;
491
492	if ((name[5] < '0') || (name[5] > '9')) return 0;
493	ftime.tm_mon = 10 * (name[5] - '0');
494
495	if ((name[6] < '0') || (name[6] > '9')) return 0;
496	ftime.tm_mon += name[6] - '0';
497	ftime.tm_mon -= 1;
498
499	if (name[7] != '.') return 0;
500
501	if ((name[8] < '0') || (name[8] > '9')) return 0;
502	ftime.tm_mday = 10 * (name[8] - '0');
503
504	if ((name[9] < '0') || (name[9] > '9')) return 0;
505	ftime.tm_mday += name[9] - '0';
506
507	if (name[10] != '.') return 0;
508
509	created = mktime(&ftime);
510	if (created > now) return 0;
511
512	days = (now - created) / 86400;
513
514	if (u != NULL)
515	{
516		*u = -1;
517		p = strchr(name+10, 'U');
518		if (p != NULL) *u = atoi(p+1);
519	}
520
521	if (g != NULL)
522	{
523		*g = -1;
524		p = strchr(name+10, 'G');
525		if (p != NULL) *g = atoi(p+1);
526	}
527
528	return days;
529}
530
531void
532aux_url_callback(const char *url)
533{
534	if (url == NULL) return;
535	if (!strncmp(url, AUX_URL_MINE, AUX_URL_MINE_LEN)) filesystem_unlink(url + AUX_URL_PATH_OFFSET);
536}
537
538uint32_t
539ymd_file_filter(const char *name, const char *path, uint32_t keep_mask, mode_t ymd_mode, uid_t ymd_uid, gid_t ymd_gid)
540{
541	asl_file_t *f = NULL;
542	uint8_t km = keep_mask;
543	uint32_t status, len, dstcount = 0;
544	char src[MAXPATHLEN];
545	char dst[MAXPATHLEN];
546
547	if (snprintf(src, MAXPATHLEN, "%s/%s", path, name) >= MAXPATHLEN) return ASL_STATUS_FAILED;
548	if (snprintf(dst, MAXPATHLEN, "%s/%s", path, name) >= MAXPATHLEN) return ASL_STATUS_FAILED;
549	len = strlen(src) - 3;
550	snprintf(dst + len, 4, "tmp");
551
552	//TODO: check if src file is already filtered
553	debug_log(ASL_LEVEL_NOTICE, "  filter %s %s ---> %s\n", src, keep_str(km), dst);
554
555	status = ASL_STATUS_OK;
556
557	if (dryrun == 0)
558	{
559		status = asl_file_open_read(name, &f);
560		if (status != ASL_STATUS_OK) return status;
561
562		status = asl_file_filter_level(f, dst, keep_mask, ymd_mode, ymd_uid, ymd_gid, &dstcount, aux_url_callback);
563		asl_file_close(f);
564	}
565
566	filesystem_unlink(src);
567	if ((status != ASL_STATUS_OK) || (dstcount == 0)) filesystem_unlink(dst);
568	else filesystem_rename(dst, src);
569
570	return status;
571}
572
573/*
574 * Used to set config parameters.
575 * Line format "= name value"
576 */
577static void
578_aslmanager_set_param(asl_out_dst_data_t *dst, char *s)
579{
580	char **l;
581	uint32_t count;
582
583	if (s == NULL) return;
584	if (s[0] == '\0') return;
585
586	/* skip '=' and whitespace */
587	if (*s == '=') s++;
588	while ((*s == ' ') || (*s == '\t')) s++;
589
590	l = explode(s, " \t");
591	if (l == NULL) return;
592
593	for (count = 0; l[count] != NULL; count++);
594
595	/* name is required */
596	if (count == 0)
597	{
598		free_string_list(l);
599		return;
600	}
601
602	/* value is required */
603	if (count == 1)
604	{
605		free_string_list(l);
606		return;
607	}
608
609	if (!strcasecmp(l[0], "aslmanager_debug"))
610	{
611		/* = debug level */
612		set_debug(DEBUG_ASL, l[1]);
613	}
614	else if (!strcasecmp(l[0], "store_ttl"))
615	{
616		/* = store_ttl days */
617		dst->ttl[LEVEL_ALL] = (time_t)atoll(l[1]);
618	}
619	else if (!strcasecmp(l[0], "module_ttl"))
620	{
621		/* = module_ttl days */
622		module_ttl = (time_t)atoll(l[1]);
623	}
624	else if (!strcasecmp(l[0], "max_store_size"))
625	{
626		/* = max_file_size bytes */
627		dst->all_max = atoi(l[1]);
628	}
629	else if (!strcasecmp(l[0], "archive"))
630	{
631		free(dst->rotate_dir);
632		dst->rotate_dir = NULL;
633
634		/* = archive {0|1} path */
635		if (!strcmp(l[1], "1"))
636		{
637			if (l[2] == NULL) dst->rotate_dir = strdup(PATH_ASL_ARCHIVE);
638			else dst->rotate_dir = strdup(l[2]);
639		}
640	}
641	else if (!strcasecmp(l[0], "store_path"))
642	{
643		/* = archive path */
644		free(dst->path);
645		dst->path = strdup(l[1]);
646	}
647	else if (!strcasecmp(l[0], "archive_mode"))
648	{
649		dst->mode = strtol(l[1], NULL, 0);
650		if ((dst->mode == 0) && (errno == EINVAL)) dst->mode = 0400;
651	}
652
653	free_string_list(l);
654}
655
656size_t
657directory_size(const char *path)
658{
659	DIR *dp;
660	struct dirent *dent;
661	struct stat sb;
662	size_t size;
663	char *str;
664
665	dp = opendir(path);
666	if (dp == NULL) return 0;
667
668	size = 0;
669	while ((dent = readdir(dp)) != NULL)
670	{
671		if ((!strcmp(dent->d_name, ".")) || (!strcmp(dent->d_name, ".."))) continue;
672
673		memset(&sb, 0, sizeof(struct stat));
674		str = NULL;
675		asprintf(&str, "%s/%s", path, dent->d_name);
676
677		if ((str != NULL) && (stat(str, &sb) == 0) && S_ISREG(sb.st_mode))
678		{
679			size += sb.st_size;
680			free(str);
681		}
682	}
683
684	closedir(dp);
685	return size;
686}
687
688static int
689process_asl_data_store(asl_out_dst_data_t *dst)
690{
691	int32_t today_ymd_stringlen, expire_ymd_stringlen;
692	time_t now, ttl, ymd_expire;
693	struct tm ctm;
694	char today_ymd_string[32], expire_ymd_string[32], *str;
695	DIR *dp;
696	struct dirent *dent;
697	name_list_t *ymd_list, *bb_list, *aux_list, *bb_aux_list, *e;
698	size_t file_size, store_size;
699	struct stat sb;
700
701	ymd_list = NULL;
702	bb_list = NULL;
703	aux_list = NULL;
704	bb_aux_list = NULL;
705	store_size = 0;
706
707	if (dst == NULL) return 0;
708	if (dst->path == NULL) return 0;
709
710	debug_log(ASL_LEVEL_NOTICE, "----------------------------------------\n");
711	debug_log(ASL_LEVEL_NOTICE, "Processing data store %s\n", dst->path);
712
713	if (dst->rotate_dir != NULL)
714	{
715		/* check archive */
716		memset(&sb, 0, sizeof(struct stat));
717		if (stat(dst->rotate_dir, &sb) == 0)
718		{
719			/* must be a directory */
720			if (!S_ISDIR(sb.st_mode))
721			{
722				debug_log(ASL_LEVEL_ERR, "aslmanager error: archive %s is not a directory", dst->rotate_dir);
723				return -1;
724			}
725		}
726		else
727		{
728			if (errno == ENOENT)
729			{
730				/* archive doesn't exist - create it */
731				if (mkdir(dst->rotate_dir, 0755) != 0)
732				{
733					debug_log(ASL_LEVEL_ERR, "aslmanager error: can't create archive %s: %s\n", dst->rotate_dir, strerror(errno));
734					return -1;
735				}
736			}
737			else
738			{
739				/* stat failed for some other reason */
740				debug_log(ASL_LEVEL_ERR, "aslmanager error: can't stat archive %s: %s\n", dst->rotate_dir, strerror(errno));
741				return -1;
742			}
743		}
744	}
745
746	chdir(dst->path);
747
748	/* determine current time */
749	now = time(NULL);
750
751	/* ttl 0 means files never expire */
752	ymd_expire = 0;
753	ttl = dst->ttl[LEVEL_ALL] * SECONDS_PER_DAY;
754
755	if ((ttl > 0) && (ttl <= now)) ymd_expire = now - ttl;
756
757	/* construct today's date as YYYY.MM.DD */
758	memset(&ctm, 0, sizeof(struct tm));
759	if (localtime_r((const time_t *)&now, &ctm) == NULL) return -1;
760
761	snprintf(today_ymd_string, sizeof(today_ymd_string), "%d.%02d.%02d.", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday);
762	today_ymd_stringlen = strlen(today_ymd_string);
763
764	/* construct regular file expiry date as YYYY.MM.DD */
765	memset(&ctm, 0, sizeof(struct tm));
766	if (localtime_r((const time_t *)&ymd_expire, &ctm) == NULL) return -1;
767
768	snprintf(expire_ymd_string, sizeof(expire_ymd_string), "%d.%02d.%02d.", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday);
769	expire_ymd_stringlen = strlen(expire_ymd_string);
770
771	debug_log(ASL_LEVEL_NOTICE, "Expiry Date %s\n", expire_ymd_string);
772
773	dp = opendir(dst->path);
774	if (dp == NULL) return -1;
775
776	/* gather a list of YMD files, AUX dirs, BB.AUX dirs, and BB files */
777	while ((dent = readdir(dp)) != NULL)
778	{
779		memset(&sb, 0, sizeof(struct stat));
780		file_size = 0;
781		if (stat(dent->d_name, &sb) == 0) file_size = sb.st_size;
782
783		if ((dent->d_name[0] >= '0') && (dent->d_name[0] <= '9'))
784		{
785			ymd_list = add_to_name_list(ymd_list, dent->d_name, file_size);
786			store_size += file_size;
787		}
788		else if (!strncmp(dent->d_name, "AUX.", 4) && (dent->d_name[4] >= '0') && (dent->d_name[4] <= '9') && S_ISDIR(sb.st_mode))
789		{
790			file_size = directory_size(dent->d_name);
791			aux_list = add_to_name_list(aux_list, dent->d_name, file_size);
792			store_size += file_size;
793		}
794		else if (!strncmp(dent->d_name, "BB.AUX.", 7) && (dent->d_name[7] >= '0') && (dent->d_name[7] <= '9') && S_ISDIR(sb.st_mode))
795		{
796			file_size = directory_size(dent->d_name);
797			bb_aux_list = add_to_name_list(bb_aux_list, dent->d_name, file_size);
798			store_size += file_size;
799		}
800		else if (!strncmp(dent->d_name, "BB.", 3) && (dent->d_name[3] >= '0') && (dent->d_name[3] <= '9'))
801		{
802			bb_list = add_to_name_list(bb_list, dent->d_name, file_size);
803			store_size += file_size;
804		}
805		else if ((!strcmp(dent->d_name, ".")) || (!strcmp(dent->d_name, "..")))
806		{}
807		else if ((!strcmp(dent->d_name, "StoreData")) || (!strcmp(dent->d_name, "SweepStore")))
808		{}
809		else
810		{
811			debug_log(ASL_LEVEL_ERR, "aslmanager: unexpected file %s in ASL data store\n", dent->d_name);
812		}
813	}
814
815	closedir(dp);
816
817	debug_log(ASL_LEVEL_NOTICE, "Data Store Size = %lu\n", store_size);
818	debug_log(ASL_LEVEL_NOTICE, "Data Store YMD Files\n");
819	for (e = ymd_list; e != NULL; e = e->next) debug_log(ASL_LEVEL_NOTICE, "	%s   %lu\n", e->name, e->size);
820	debug_log(ASL_LEVEL_NOTICE, "Data Store AUX Directories\n");
821	for (e = aux_list; e != NULL; e = e->next) debug_log(ASL_LEVEL_NOTICE, "	%s   %lu\n", e->name, e->size);
822	debug_log(ASL_LEVEL_NOTICE, "Data Store BB.AUX Directories\n");
823	for (e = bb_aux_list; e != NULL; e = e->next) debug_log(ASL_LEVEL_NOTICE, "	%s   %lu\n", e->name, e->size);
824	debug_log(ASL_LEVEL_NOTICE, "Data Store BB Files\n");
825	for (e = bb_list; e != NULL; e = e->next) debug_log(ASL_LEVEL_NOTICE, "	%s   %lu\n", e->name, e->size);
826
827	/* Delete/achive expired YMD files */
828	debug_log(ASL_LEVEL_NOTICE, "Start YMD File Scan\n");
829
830	e = ymd_list;
831	while (e != NULL)
832	{
833		if (strncmp(e->name, expire_ymd_string, expire_ymd_stringlen) <= 0)
834		{
835			/* file has expired, archive it if required, then unlink it */
836			if (dst->rotate_dir != NULL)
837			{
838				str = NULL;
839				asprintf(&str, "%s/%s", dst->rotate_dir, e->name);
840				if (str == NULL) return -1;
841
842				filesystem_copy(dst, e->name, str, 0);
843				free(str);
844			}
845
846			filesystem_unlink(e->name);
847			store_size -= e->size;
848			e->size = 0;
849		}
850		else
851		{
852			/* check if there are any per-level TTLs and filter the file if required */
853			uint32_t i, bit, keep_mask;
854			uid_t ymd_uid = -1;
855			gid_t ymd_gid = -1;
856			mode_t ymd_mode = 0600;
857			uint32_t age = ymd_file_age(e->name, now, &ymd_uid, &ymd_gid);
858
859			if (age > 0)
860			{
861				keep_mask = 0x000000ff;
862				bit = 1;
863				for (i = 0; i <= 7; i++)
864				{
865					if ((dst->ttl[i] > 0) && (age >= dst->ttl[i])) keep_mask &= ~bit;
866					bit *= 2;
867				}
868
869				memset(&sb, 0, sizeof(struct stat));
870				if (stat(e->name, &sb) == 0) ymd_mode = sb.st_mode & 0777;
871
872				if (keep_mask != 0x000000ff) ymd_file_filter(e->name, dst->path, keep_mask, ymd_mode, ymd_uid, ymd_gid);
873			}
874		}
875
876		e = e->next;
877	}
878
879	debug_log(ASL_LEVEL_NOTICE, "Finished YMD File Scan\n");
880
881	/* Delete/achive expired YMD AUX directories */
882	debug_log(ASL_LEVEL_NOTICE, "Start AUX Directory Scan\n");
883
884	e = aux_list;
885	while (e != NULL)
886	{
887		/* stop when a file name/date is after the expire date */
888		if (strncmp(e->name + 4, expire_ymd_string, expire_ymd_stringlen) > 0) break;
889
890		if (dst->rotate_dir != NULL)
891		{
892			str = NULL;
893			asprintf(&str, "%s/%s", dst->rotate_dir, e->name);
894			if (str == NULL) return -1;
895
896			filesystem_copy(dst, e->name, str, 0);
897			free(str);
898		}
899
900		remove_directory(e->name);
901		store_size -= e->size;
902		e->size = 0;
903
904		e = e->next;
905	}
906
907	debug_log(ASL_LEVEL_NOTICE, "Finished AUX Directory Scan\n");
908
909	/* Delete/achive expired BB.AUX directories */
910	debug_log(ASL_LEVEL_NOTICE, "Start BB.AUX Directory Scan\n");
911
912	e = bb_aux_list;
913	while (e != NULL)
914	{
915		/* stop when a file name/date is after the expire date */
916		if (strncmp(e->name + 7, today_ymd_string, today_ymd_stringlen) > 0) break;
917
918		if (dst->rotate_dir != NULL)
919		{
920			str = NULL;
921			asprintf(&str, "%s/%s", dst->rotate_dir, e->name);
922			if (str == NULL) return -1;
923
924			filesystem_copy(dst, e->name, str, 0);
925			free(str);
926		}
927
928		remove_directory(e->name);
929		store_size -= e->size;
930		e->size = 0;
931
932		e = e->next;
933	}
934
935	debug_log(ASL_LEVEL_NOTICE, "Finished BB.AUX Directory Scan\n");
936
937	/* Delete/achive expired BB files */
938	debug_log(ASL_LEVEL_NOTICE, "Start BB Scan\n");
939
940	e = bb_list;
941	while (e != NULL)
942	{
943		/* stop when a file name/date is after the expire date */
944		if (strncmp(e->name + 3, today_ymd_string, today_ymd_stringlen) > 0) break;
945
946		if (dst->rotate_dir != NULL)
947		{
948			str = NULL;
949			asprintf(&str, "%s/%s", dst->rotate_dir, e->name);
950			if (str == NULL) return -1;
951
952			/* syslog -x [str] -f [e->name] */
953			filesystem_copy(dst, e->name, str, 0);
954			free(str);
955		}
956
957		filesystem_unlink(e->name);
958		store_size -= e->size;
959		e->size = 0;
960
961		e = e->next;
962	}
963
964	debug_log(ASL_LEVEL_NOTICE, "Finished BB Scan\n");
965
966	if (dst->all_max > 0)
967	{
968		/* if data store is over max_size, delete/archive more YMD files */
969		if (store_size > dst->all_max) debug_log(ASL_LEVEL_NOTICE, "Additional YMD Scan\n");
970
971		e = ymd_list;
972		while ((e != NULL) && (store_size > dst->all_max))
973		{
974			if (e->size != 0)
975			{
976				if (strncmp(e->name, today_ymd_string, today_ymd_stringlen) == 0)
977				{
978					/* do not touch active file YYYY.MM.DD.asl */
979					if (strcmp(e->name + today_ymd_stringlen, "asl") == 0)
980					{
981						e = e->next;
982						continue;
983					}
984				}
985
986				if (dst->rotate_dir != NULL)
987				{
988					str = NULL;
989					asprintf(&str, "%s/%s", dst->rotate_dir, e->name);
990					if (str == NULL) return -1;
991
992					/* syslog -x [str] -f [e->name] */
993					filesystem_copy(dst, e->name, str, 0);
994					free(str);
995				}
996
997				filesystem_unlink(e->name);
998				store_size -= e->size;
999				e->size = 0;
1000			}
1001
1002			e = e->next;
1003		}
1004
1005		/* if data store is over dst->all_max, delete/archive more BB files */
1006		if (store_size > dst->all_max) debug_log(ASL_LEVEL_NOTICE, "Additional BB Scan\n");
1007
1008		e = bb_list;
1009		while ((e != NULL) && (store_size > dst->all_max))
1010		{
1011			if (e->size != 0)
1012			{
1013				if (dst->rotate_dir != NULL)
1014				{
1015					str = NULL;
1016					asprintf(&str, "%s/%s", dst->rotate_dir, e->name);
1017					if (str == NULL) return -1;
1018
1019					/* syslog -x [str] -f [e->name] */
1020					filesystem_copy(dst, e->name, str, 0);
1021					free(str);
1022				}
1023
1024				filesystem_unlink(e->name);
1025				store_size -= e->size;
1026				e->size = 0;
1027			}
1028
1029			e = e->next;
1030		}
1031	}
1032
1033	free_name_list(ymd_list);
1034	free_name_list(bb_list);
1035	free_name_list(aux_list);
1036	free_name_list(bb_aux_list);
1037
1038	debug_log(ASL_LEVEL_NOTICE, "Data Store Size = %lu\n", store_size);
1039
1040	return 0;
1041}
1042
1043/* move sequenced source files to dst dir, renaming as we go */
1044static int
1045module_copy_rename(asl_out_dst_data_t *dst)
1046{
1047	asl_out_file_list_t *src_list, *dst_list, *f, *dst_last;
1048	char *base, *dst_dir;
1049	char fpathsrc[MAXPATHLEN], fpathdst[MAXPATHLEN];
1050	uint32_t src_count, dst_count;
1051	int32_t x, moved;
1052
1053	if (dst == NULL) return -1;
1054	if (dst->path == NULL) return -1;
1055
1056	base = strrchr(dst->path, '/');
1057	if (base == NULL) return -1;
1058
1059	src_list = asl_list_src_files(dst);
1060	if (src_list == 0)
1061	{
1062		debug_log(ASL_LEVEL_INFO, "    no src files\n");
1063		return 0;
1064	}
1065
1066	debug_log(ASL_LEVEL_INFO, "    src files\n");
1067
1068	src_count = 0;
1069	for (f = src_list; f != NULL; f = f->next)
1070	{
1071		debug_log(ASL_LEVEL_INFO, "      %s\n", f->name);
1072		src_count++;
1073	}
1074
1075	dst_list = asl_list_dst_files(dst);
1076
1077	*base = '\0';
1078	base++;
1079
1080	dst_dir = dst->rotate_dir;
1081	if (dst_dir == NULL) dst_dir = dst->path;
1082
1083	dst_count = 0;
1084	dst_last = dst_list;
1085
1086	if (dst_list == NULL) debug_log(ASL_LEVEL_INFO, "    no dst files\n");
1087	else debug_log(ASL_LEVEL_INFO, "    dst files\n");
1088
1089	for (f = dst_list; f != NULL; f = f->next)
1090	{
1091		debug_log(ASL_LEVEL_INFO, "      %s\n", f->name);
1092		dst_last = f;
1093		dst_count++;
1094	}
1095
1096	if (dst->flags & MODULE_FLAG_STYLE_SEQ)
1097	{
1098		for (f = dst_last; f != NULL; f = f->prev)
1099		{
1100			int is_gz = 0;
1101			char *dot = strrchr(f->name, '.');
1102			if ((dot != NULL) && (!strcmp(dot, ".gz"))) is_gz = 1;
1103
1104			snprintf(fpathsrc, sizeof(fpathsrc), "%s/%s", dst_dir, f->name);
1105			snprintf(fpathdst, sizeof(fpathdst), "%s/%s.%d%s", dst_dir, base, f->seq+src_count, (is_gz == 1) ? ".gz" : "");
1106			filesystem_rename(fpathsrc, fpathdst);
1107		}
1108
1109		for (f = src_list, x = 0; f != NULL; f = f->next, x++)
1110		{
1111			snprintf(fpathsrc, sizeof(fpathsrc), "%s/%s", dst->path, f->name);
1112			snprintf(fpathdst, sizeof(fpathdst), "%s/%s.%d", dst_dir, base, x);
1113			moved = filesystem_copy(dst, fpathsrc, fpathdst, dst->flags);
1114			if (moved != 0)
1115			{
1116				if (dst->flags & MODULE_FLAG_TRUNCATE) filesystem_truncate(fpathsrc);
1117				else filesystem_unlink(fpathsrc);
1118			}
1119		}
1120	}
1121	else
1122	{
1123		for (f = src_list; f != NULL; f = f->next)
1124		{
1125			/* final / active base stamped file looks like a checkpointed file - ignore it */
1126			if ((dst->flags & MODULE_FLAG_BASESTAMP) && (f->next == NULL)) break;
1127
1128			snprintf(fpathsrc, sizeof(fpathsrc), "%s/%s", dst->path, f->name);
1129
1130			/* MODULE_FLAG_EXTERNAL files are not decorated with a timestamp */
1131			if (dst->flags & MODULE_FLAG_EXTERNAL)
1132			{
1133				char tstamp[32];
1134
1135				asl_make_timestamp(f->ftime, dst->flags, tstamp, sizeof(tstamp));
1136				snprintf(fpathdst, sizeof(fpathdst), "%s/%s.%s", dst_dir, base, tstamp);
1137			}
1138			else
1139			{
1140				snprintf(fpathdst, sizeof(fpathdst), "%s/%s", dst_dir, f->name);
1141			}
1142
1143			moved = filesystem_copy(dst, fpathsrc, fpathdst, dst->flags);
1144			if (moved != 0)
1145			{
1146				if (dst->flags & MODULE_FLAG_TRUNCATE) filesystem_truncate(fpathsrc);
1147				else filesystem_unlink(fpathsrc);
1148			}
1149		}
1150	}
1151
1152	asl_out_file_list_free(src_list);
1153	asl_out_file_list_free(dst_list);
1154
1155	if (base != NULL) *--base = '/';
1156
1157	return 0;
1158}
1159
1160/* delete expired files */
1161static int
1162module_expire(asl_out_dst_data_t *dst)
1163{
1164	asl_out_file_list_t *dst_list, *f;
1165	char *base, *dst_dir, fpath[MAXPATHLEN];
1166	time_t now, ttl, cutoff;
1167
1168	if (dst == NULL) return -1;
1169	if (dst->path == NULL) return -1;
1170	if (dst->ttl[LEVEL_ALL] == 0) return 0;
1171
1172	ttl = 0;
1173	if (module_ttl > 0) ttl = module_ttl;
1174	else ttl = dst->ttl[LEVEL_ALL];
1175
1176	ttl *= SECONDS_PER_DAY;
1177
1178	now = time(NULL);
1179	if (ttl > now) return 0;
1180
1181	cutoff = now - ttl;
1182
1183	base = strrchr(dst->path, '/');
1184	if (base == NULL) return -1;
1185
1186	dst_list = asl_list_dst_files(dst);
1187
1188	*base = '\0';
1189
1190	dst_dir = dst->rotate_dir;
1191	if (dst_dir == NULL) dst_dir = dst->path;
1192
1193	if (dst_list == NULL)
1194	{
1195		debug_log(ASL_LEVEL_INFO, "    no dst files\n");
1196	}
1197	else
1198	{
1199		debug_log(ASL_LEVEL_INFO, "    dst files\n");
1200		for (f = dst_list; f != NULL; f = f->next) debug_log(ASL_LEVEL_INFO, "      %s\n", f->name);
1201	}
1202
1203	for (f = dst_list; f != NULL; f = f->next)
1204	{
1205		if (f->ftime <= cutoff)
1206		{
1207			snprintf(fpath, sizeof(fpath), "%s/%s", dst_dir, f->name);
1208			filesystem_unlink(fpath);
1209		}
1210	}
1211
1212	asl_out_file_list_free(dst_list);
1213
1214	if (base != NULL) *base = '/';
1215
1216	return 0;
1217}
1218
1219/* check all_max size and delete files (oldest first) to stay within size limit */
1220static int
1221module_check_size(asl_out_dst_data_t *dst)
1222{
1223	asl_out_file_list_t *dst_list, *f, *dst_end;
1224	char *base, *dst_dir, fpath[MAXPATHLEN];
1225	size_t total;
1226
1227	if (dst == NULL) return -1;
1228	if (dst->path == NULL) return -1;
1229
1230	if (dst->all_max == 0) return 0;
1231
1232	dst_list = asl_list_dst_files(dst);
1233	if (dst_list == NULL)
1234	{
1235		debug_log(ASL_LEVEL_INFO, "    no dst files\n");
1236		return 0;
1237	}
1238
1239	base = NULL;
1240	dst_dir = dst->rotate_dir;
1241	if (dst_dir == NULL)
1242	{
1243		dst_dir = dst->path;
1244		base = strrchr(dst->path, '/');
1245		if (base == NULL)
1246		{
1247			asl_out_file_list_free(dst_list);
1248			return -1;
1249		}
1250
1251		*base = '\0';
1252	}
1253
1254	debug_log(ASL_LEVEL_INFO, "    dst files\n");
1255	dst_end = dst_list;
1256	for (f = dst_list; f != NULL; f = f->next)
1257	{
1258		dst_end = f;
1259		debug_log(ASL_LEVEL_INFO, "      %s size %lu\n", f->name, f->size);
1260	}
1261
1262	total = 0;
1263	for (f = dst_list; f != NULL; f = f->next) total += f->size;
1264
1265	for (f = dst_end; (total > dst->all_max) && (f != NULL); f = f->prev)
1266	{
1267		snprintf(fpath, sizeof(fpath), "%s/%s", dst_dir, f->name);
1268		filesystem_unlink(fpath);
1269		total -= f->size;
1270	}
1271
1272	asl_out_file_list_free(dst_list);
1273
1274	if (base != NULL) *base = '/';
1275
1276	return 0;
1277}
1278
1279
1280static int
1281process_module(asl_out_module_t *mod)
1282{
1283	asl_out_rule_t *r;
1284
1285	if (mod == NULL) return -1;
1286
1287	debug_log(ASL_LEVEL_NOTICE, "----------------------------------------\n");
1288	debug_log(ASL_LEVEL_NOTICE, "Processing module %s\n", (mod->name == NULL) ? "asl.conf" : mod->name);
1289
1290	for (r = mod->ruleset; r != NULL; r = r->next)
1291	{
1292		if (r->action == ACTION_OUT_DEST)
1293		{
1294			if (r->dst == NULL)
1295			{
1296				debug_log(ASL_LEVEL_NOTICE, "NULL dst data for output rule - skipped\n");
1297			}
1298			else if (r->dst->flags & MODULE_FLAG_ROTATE)
1299			{
1300				debug_log(ASL_LEVEL_NOTICE, "Checking file %s\n", r->dst->path);
1301				debug_log(ASL_LEVEL_NOTICE, "- Rename, move to destination directory, and compress as required\n");
1302
1303				module_copy_rename(r->dst);
1304
1305				if (r->dst->ttl[LEVEL_ALL] > 0)
1306				{
1307					debug_log(ASL_LEVEL_NOTICE, "- Check for expired files - TTL = %d days\n", r->dst->ttl[LEVEL_ALL]);
1308					module_expire(r->dst);
1309				}
1310
1311				if (r->dst->all_max > 0)
1312				{
1313					debug_log(ASL_LEVEL_NOTICE, "- Check total storage used - MAX = %lu\n", r->dst->all_max);
1314					module_check_size(r->dst);
1315				}
1316			}
1317			else if ((r->dst->flags & MODULE_FLAG_TYPE_ASL_DIR) && (r->dst->ttl[LEVEL_ALL] > 0))
1318			{
1319				process_asl_data_store(r->dst);
1320			}
1321		}
1322	}
1323
1324	debug_log(ASL_LEVEL_NOTICE, "Finished processing module %s\n", (mod->name == NULL) ? "asl.conf" : mod->name);
1325	return 0;
1326}
1327
1328asl_msg_list_t *
1329control_query(asl_msg_t *a)
1330{
1331	asl_msg_list_t *out;
1332	char *qstr, *str, *res;
1333	uint32_t len, reslen, status;
1334	uint64_t cmax, qmin;
1335	kern_return_t kstatus;
1336	caddr_t vmstr;
1337	security_token_t sec;
1338
1339	if (asl_server_port == MACH_PORT_NULL)
1340	{
1341		bootstrap_look_up2(bootstrap_port, ASL_SERVICE_NAME, &asl_server_port, 0, BOOTSTRAP_PRIVILEGED_SERVER);
1342		if (asl_server_port == MACH_PORT_NULL) return NULL;
1343	}
1344
1345	qstr = asl_msg_to_string((asl_msg_t *)a, &len);
1346
1347	str = NULL;
1348	if (qstr == NULL)
1349	{
1350		asprintf(&str, "1\nQ [= ASLOption control]\n");
1351	}
1352	else
1353	{
1354		asprintf(&str, "1\n%s [= ASLOption control]\n", qstr);
1355		free(qstr);
1356	}
1357
1358	if (str == NULL) return NULL;
1359
1360	/* length includes trailing nul */
1361	len = strlen(str) + 1;
1362	out = NULL;
1363	qmin = 0;
1364	cmax = 0;
1365	sec.val[0] = -1;
1366	sec.val[1] = -1;
1367
1368	res = NULL;
1369	reslen = 0;
1370	status = ASL_STATUS_OK;
1371
1372	kstatus = vm_allocate(mach_task_self(), (vm_address_t *)&vmstr, len, TRUE);
1373	if (kstatus != KERN_SUCCESS) return NULL;
1374
1375	memmove(vmstr, str, len);
1376	free(str);
1377
1378	status = 0;
1379	kstatus = _asl_server_query(asl_server_port, vmstr, len, qmin, 1, 0, (caddr_t *)&res, &reslen, &cmax, (int *)&status, &sec);
1380	if (kstatus != KERN_SUCCESS) return NULL;
1381
1382	if (res == NULL) return NULL;
1383
1384	out = asl_msg_list_from_string(res);
1385	vm_deallocate(mach_task_self(), (vm_address_t)res, reslen);
1386
1387	return out;
1388}
1389
1390int
1391checkpoint(const char *name)
1392{
1393	/* send checkpoint message to syslogd */
1394	debug_log(ASL_LEVEL_NOTICE, "Checkpoint module %s\n", (name == NULL) ? "*" : name);
1395	if (dryrun != 0) return 0;
1396
1397	asl_msg_t *qmsg = asl_msg_new(ASL_TYPE_QUERY);
1398	char *tmp = NULL;
1399	asl_msg_list_t *res;
1400
1401	asprintf(&tmp, "%s checkpoint", (name == NULL) ? "*" : name);
1402	asl_msg_set_key_val_op(qmsg, "action", tmp, ASL_QUERY_OP_EQUAL);
1403	free(tmp);
1404
1405	res = control_query(qmsg);
1406
1407	asl_msg_list_release(res);
1408	return 0;
1409}
1410
1411int
1412cli_main(int argc, char *argv[])
1413{
1414	int i, work;
1415	asl_out_module_t *mod, *m;
1416	asl_out_rule_t *r;
1417	asl_out_dst_data_t store, *asl_store_dst = NULL;
1418	const char *mname = NULL;
1419
1420	if (geteuid() != 0)
1421	{
1422		if (argc == 0) debug = DEBUG_ASL;
1423		else debug = DEBUG_STDERR;
1424
1425		debug_log(ASL_LEVEL_ERR, "aslmanager must be run by root\n");
1426		exit(1);
1427	}
1428
1429	module_ttl = DEFAULT_TTL;
1430
1431	/* cobble up a dst_data with defaults and parameter settings */
1432	memset(&store, 0, sizeof(store));
1433	store.ttl[LEVEL_ALL] = DEFAULT_TTL;
1434	store.all_max = DEFAULT_MAX_SIZE;
1435
1436	for (i = 1; i < argc; i++)
1437	{
1438		if (!strcmp(argv[i], "-s"))
1439		{
1440			if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
1441			{
1442				store.path = strdup(argv[++i]);
1443				asl_store_dst = &store;
1444			}
1445		}
1446	}
1447
1448	/* get parameters from asl.conf */
1449	mod = asl_out_module_init();
1450
1451	if (mod != NULL)
1452	{
1453		for (r = mod->ruleset; r != NULL; r = r->next)
1454		{
1455			if ((asl_store_dst == NULL) && (r->action == ACTION_OUT_DEST) && (!strcmp(r->dst->path, PATH_ASL_STORE)))
1456				asl_store_dst = r->dst;
1457		}
1458
1459		for (r = mod->ruleset; r != NULL; r = r->next)
1460		{
1461			if (r->action == ACTION_SET_PARAM)
1462			{
1463				if (r->query == NULL) _aslmanager_set_param(asl_store_dst, r->options);
1464			}
1465		}
1466	}
1467
1468	work = DO_ASLDB | DO_MODULE;
1469
1470	for (i = 1; i < argc; i++)
1471	{
1472		if (!strcmp(argv[i], "-a"))
1473		{
1474			if (((i + 1) < argc) && (argv[i + 1][0] != '-')) asl_store_dst->rotate_dir = strdup(argv[++i]);
1475			else asl_store_dst->rotate_dir = strdup(PATH_ASL_ARCHIVE);
1476			asl_store_dst->mode = 0400;
1477		}
1478		else if (!strcmp(argv[i], "-store_ttl"))
1479		{
1480			if (((i + 1) < argc) && (argv[i + 1][0] != '-')) asl_store_dst->ttl[LEVEL_ALL] = atoi(argv[++i]);
1481		}
1482		else if (!strcmp(argv[i], "-module_ttl"))
1483		{
1484			if (((i + 1) < argc) && (argv[i + 1][0] != '-')) module_ttl = atoi(argv[++i]);
1485		}
1486		else if (!strcmp(argv[i], "-ttl"))
1487		{
1488			if (((i + 1) < argc) && (argv[i + 1][0] != '-')) module_ttl = asl_store_dst->ttl[LEVEL_ALL] = atoi(argv[++i]);
1489		}
1490		else if (!strcmp(argv[i], "-size"))
1491		{
1492			if (((i + 1) < argc) && (argv[i + 1][0] != '-')) asl_store_dst->all_max = asl_str_to_size(argv[++i]);
1493		}
1494		else if (!strcmp(argv[i], "-checkpoint"))
1495		{
1496			work |= DO_CHECKPT;
1497		}
1498		else if (!strcmp(argv[i], "-module"))
1499		{
1500			work &= ~DO_ASLDB;
1501
1502			/* optional name follows -module */
1503			if ((i +1) < argc)
1504			{
1505				if (argv[i + 1][0] != '-') mname = argv[++i];
1506			}
1507		}
1508		else if (!strcmp(argv[i], "-asldb"))
1509		{
1510			work = DO_ASLDB;
1511		}
1512		else if (!strcmp(argv[i], "-d"))
1513		{
1514			if (((i + i) < argc) && (argv[i+1][0] != '-')) set_debug(DEBUG_STDERR, argv[++i]);
1515			else set_debug(DEBUG_STDERR, NULL);
1516		}
1517		else if (!strcmp(argv[i], "-dd"))
1518		{
1519			dryrun = 1;
1520
1521			if (((i + i) < argc) && (argv[i+1][0] != '-')) set_debug(DEBUG_STDERR, argv[++i]);
1522			else set_debug(DEBUG_STDERR, NULL);
1523		}
1524	}
1525
1526	if (asl_store_dst->path == NULL) asl_store_dst->path = strdup(PATH_ASL_STORE);
1527
1528	debug_log(ASL_LEVEL_ERR, "aslmanager starting%s\n", (dryrun == 1) ? " dryrun" : "");
1529
1530	if (work & DO_ASLDB) process_asl_data_store(asl_store_dst);
1531
1532	if (work & DO_MODULE)
1533	{
1534		if (work & DO_CHECKPT) checkpoint(mname);
1535
1536		if (mod != NULL)
1537		{
1538			for (m = mod; m != NULL; m = m->next)
1539			{
1540				if ((mname == NULL) || ((m->name != NULL) && (!strcmp(m->name, mname))))
1541				{
1542					process_module(m);
1543				}
1544			}
1545		}
1546	}
1547
1548	asl_out_module_free(mod);
1549
1550	debug_log(ASL_LEVEL_NOTICE, "----------------------------------------\n");
1551	debug_log(ASL_LEVEL_ERR, "aslmanager finished%s\n", (dryrun == 1) ? " dryrun" : "");
1552	if (asl_aux_fd >= 0) asl_close_auxiliary_file(asl_aux_fd);
1553
1554	return 0;
1555}
1556
1557static void
1558accept_connection(xpc_connection_t peer)
1559{
1560	xpc_connection_set_event_handler(peer, ^(xpc_object_t request) {
1561		if (xpc_get_type(request) == XPC_TYPE_DICTIONARY)
1562		{
1563			uid_t uid = xpc_connection_get_euid(peer);
1564
1565			/* send a reply immediately */
1566			xpc_object_t reply = xpc_dictionary_create_reply(request);
1567			xpc_connection_send_message(peer, reply);
1568			xpc_release(reply);
1569
1570			/*
1571			 * Some day, we may use the dictionary to pass parameters
1572			 * to aslmanager, but for now, we ignore the input.
1573			 */
1574			if (uid == 0) cli_main(0, NULL);
1575		}
1576		else if (xpc_get_type(request) == XPC_TYPE_ERROR)
1577		{
1578			/* disconnect */
1579		}
1580
1581		dispatch_async(serverq, ^__attribute__((noreturn)) { xpc_server_exit(0); });
1582	});
1583
1584	xpc_connection_resume(peer);
1585}
1586
1587int
1588main(int argc, char *argv[])
1589{
1590	int64_t is_managed = 0;
1591
1592	vproc_swap_integer(NULL, VPROC_GSK_IS_MANAGED, NULL, &is_managed);
1593
1594	if (is_managed == 0) return cli_main(argc, argv);
1595
1596	/* XPC server */
1597	serverq = dispatch_queue_create("aslmanager", NULL);
1598	xpc_track_activity();
1599
1600	/* Handle incoming messages. */
1601	listener = xpc_connection_create_mach_service("com.apple.aslmanager", serverq, XPC_CONNECTION_MACH_SERVICE_LISTENER);
1602	xpc_connection_set_event_handler(listener, ^(xpc_object_t peer) {
1603		if (xpc_get_type(peer) == XPC_TYPE_CONNECTION) accept_connection(peer);
1604	});
1605	xpc_connection_resume(listener);
1606
1607	dispatch_main();
1608}
1609