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