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