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