]> git.saurik.com Git - apple/syslog.git/blob - aslmanager.tproj/aslmanager.c
02eb7e0d439edea0f1b7cf627f417ffb6870c089
[apple/syslog.git] / aslmanager.tproj / aslmanager.c
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
70 extern 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 */
86 static time_t module_ttl;
87 static uint32_t debug;
88 static int dryrun;
89 static int asl_aux_fd = -1;
90 static aslclient aslc;
91 static mach_port_t asl_server_port;
92 static xpc_connection_t listener;
93 static dispatch_queue_t serverq;
94
95 typedef struct name_list_s
96 {
97 char *name;
98 size_t size;
99 struct name_list_s *next;
100 } name_list_t;
101
102 static const char *
103 keep_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
121 void
122 set_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
142 void
143 debug_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
179 xpc_server_exit(int status)
180 {
181 xpc_connection_cancel(listener);
182 xpc_release(listener);
183 dispatch_release(serverq);
184 exit(status);
185 }
186
187 name_list_t *
188 add_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
222 void
223 free_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 */
240 uint32_t
241 copy_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
288 int
289 copy_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
327 void
328 filesystem_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
339 void
340 filesystem_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
351 void
352 filesystem_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
363 void
364 filesystem_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
375 int32_t
376 filesystem_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
428 int
429 remove_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 */
460 uint32_t
461 ymd_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
531 void
532 aux_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
538 uint32_t
539 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)
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 */
577 static 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
656 size_t
657 directory_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
688 static int
689 process_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 */
1044 static int
1045 module_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 */
1161 static int
1162 module_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 */
1220 static int
1221 module_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
1280 static int
1281 process_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
1328 asl_msg_list_t *
1329 control_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
1390 int
1391 checkpoint(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
1411 int
1412 cli_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
1557 static void
1558 accept_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
1587 int
1588 main(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 }