]> git.saurik.com Git - apple/syslog.git/blob - aslmanager.tproj/aslmanager.c
syslog-97.1.tar.gz
[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 <asl.h>
35 #include <asl_private.h>
36 #include <asl_core.h>
37 #include <asl_file.h>
38 #include <asl_store.h>
39
40 #define SECONDS_PER_DAY 86400
41 #define DEFAULT_MAX_SIZE 150000000
42 #define DEFAULT_TTL 7
43
44 #define IndexNull (uint32_t)-1
45 #define _PATH_ASL_CONF "/etc/asl.conf"
46
47 /* global */
48 static char *archive = NULL;
49 static char *store_dir = PATH_ASL_STORE;
50 static time_t ttl;
51 static size_t max_size;
52 static mode_t archive_mode = 0400;
53 static int debug;
54
55 typedef struct name_list_s
56 {
57 char *name;
58 size_t size;
59 struct name_list_s *next;
60 } name_list_t;
61
62 name_list_t *
63 add_to_list(name_list_t *l, const char *name, size_t size)
64 {
65 name_list_t *e, *x;
66
67 if (name == NULL) return l;
68
69 e = (name_list_t *)calloc(1, sizeof(name_list_t));
70 if (e == NULL) return NULL;
71
72 e->name = strdup(name);
73 if (e->name == NULL)
74 {
75 free(e);
76 return NULL;
77 }
78
79 e->size = size;
80
81 /* list is sorted by name (i.e. primarily by timestamp) */
82 if (l == NULL) return e;
83
84 if (strcmp(e->name, l->name) <= 0)
85 {
86 e->next = l;
87 return e;
88 }
89
90 for (x = l; (x->next != NULL) && (strcmp(e->name, x->next->name) > 0) ; x = x->next);
91
92 e->next = x->next;
93 x->next = e;
94 return l;
95 }
96
97 void
98 free_list(name_list_t *l)
99 {
100 name_list_t *e;
101
102 while (l != NULL)
103 {
104 e = l;
105 l = l->next;
106 free(e->name);
107 free(e);
108 }
109
110 free(l);
111 }
112
113 uint32_t
114 do_copy(const char *infile, const char *outfile, mode_t mode)
115 {
116 asl_search_result_t *res;
117 asl_file_t *f;
118 uint32_t status, i;
119 uint64_t mid;
120
121 if (infile == NULL) return ASL_STATUS_INVALID_ARG;
122 if (outfile == NULL) return ASL_STATUS_INVALID_ARG;
123
124 f = NULL;
125 status = asl_file_open_read(infile, &f);
126 if (status != ASL_STATUS_OK) return status;
127
128 res = NULL;
129 mid = 0;
130
131 status = asl_file_match(f, NULL, &res, &mid, 0, 0, 1);
132 asl_file_close(f);
133
134 if (status != ASL_STATUS_OK) return status;
135 if (res->count == 0)
136 {
137 aslresponse_free(res);
138 return ASL_STATUS_OK;
139 }
140
141 f = NULL;
142 status = asl_file_open_write(outfile, mode, -1, -1, &f);
143 if (status != ASL_STATUS_OK) return status;
144 if (f == ASL_STATUS_OK) return ASL_STATUS_FAILED;
145
146 f->flags = ASL_FILE_FLAG_UNLIMITED_CACHE | ASL_FILE_FLAG_PRESERVE_MSG_ID;
147
148 for (i = 0; i < res->count; i++)
149 {
150 mid = 0;
151 status = asl_file_save(f, res->msg[i], &mid);
152 if (status != ASL_STATUS_OK) break;
153 }
154
155 asl_file_close(f);
156 return status;
157 }
158
159 static char **
160 _insertString(char *s, char **l, uint32_t x)
161 {
162 int i, len;
163
164 if (s == NULL) return l;
165 if (l == NULL)
166 {
167 l = (char **)malloc(2 * sizeof(char *));
168 if (l == NULL) return NULL;
169
170 l[0] = strdup(s);
171 if (l[0] == NULL)
172 {
173 free(l);
174 return NULL;
175 }
176
177 l[1] = NULL;
178 return l;
179 }
180
181 for (i = 0; l[i] != NULL; i++);
182 len = i + 1; /* count the NULL on the end of the list too! */
183
184 l = (char **)reallocf(l, (len + 1) * sizeof(char *));
185 if (l == NULL) return NULL;
186
187 if ((x >= (len - 1)) || (x == IndexNull))
188 {
189 l[len - 1] = strdup(s);
190 if (l[len - 1] == NULL)
191 {
192 free(l);
193 return NULL;
194 }
195
196 l[len] = NULL;
197 return l;
198 }
199
200 for (i = len; i > x; i--) l[i] = l[i - 1];
201 l[x] = strdup(s);
202 if (l[x] == NULL) return NULL;
203
204 return l;
205 }
206
207 char **
208 explode(const char *s, const char *delim)
209 {
210 char **l = NULL;
211 const char *p;
212 char *t, quote;
213 int i, n;
214
215 if (s == NULL) return NULL;
216
217 quote = '\0';
218
219 p = s;
220 while (p[0] != '\0')
221 {
222 /* scan forward */
223 for (i = 0; p[i] != '\0'; i++)
224 {
225 if (quote == '\0')
226 {
227 /* not inside a quoted string: check for delimiters and quotes */
228 if (strchr(delim, p[i]) != NULL) break;
229 else if (p[i] == '\'') quote = p[i];
230 else if (p[i] == '"') quote = p[i];
231 }
232 else
233 {
234 /* inside a quoted string - look for matching quote */
235 if (p[i] == quote) quote = '\0';
236 }
237 }
238
239 n = i;
240 t = malloc(n + 1);
241 if (t == NULL) return NULL;
242
243 for (i = 0; i < n; i++) t[i] = p[i];
244 t[n] = '\0';
245 l = _insertString(t, l, IndexNull);
246 free(t);
247 t = NULL;
248 if (p[i] == '\0') return l;
249 if (p[i + 1] == '\0') l = _insertString("", l, IndexNull);
250 p = p + i + 1;
251 }
252
253 return l;
254 }
255
256 void
257 freeList(char **l)
258 {
259 int i;
260
261 if (l == NULL) return;
262 for (i = 0; l[i] != NULL; i++) free(l[i]);
263 free(l);
264 }
265
266 /*
267 * Used to sed config parameters.
268 * Line format "= name value"
269 */
270 static void
271 _parse_set_param(char *s)
272 {
273 char **l;
274 uint32_t count;
275
276 if (s == NULL) return;
277 if (s[0] == '\0') return;
278
279 /* skip '=' and whitespace */
280 s++;
281 while ((*s == ' ') || (*s == '\t')) s++;
282
283 l = explode(s, " \t");
284 if (l == NULL) return;
285
286 for (count = 0; l[count] != NULL; count++);
287
288 /* name is required */
289 if (count == 0)
290 {
291 freeList(l);
292 return;
293 }
294
295 /* value is required */
296 if (count == 1)
297 {
298 freeList(l);
299 return;
300 }
301
302 if (!strcasecmp(l[0], "aslmanager_debug"))
303 {
304 /* = debug {0|1} */
305 debug = atoi(l[1]);
306 }
307 else if (!strcasecmp(l[0], "store_ttl"))
308 {
309 /* = store_ttl days */
310 ttl = SECONDS_PER_DAY * (time_t)atoll(l[1]);
311 }
312 else if (!strcasecmp(l[0], "max_store_size"))
313 {
314 /* = max_file_size bytes */
315 max_size = atoi(l[1]);
316 }
317 else if (!strcasecmp(l[0], "archive"))
318 {
319 /* = archive {0|1} path */
320 if (!strcmp(l[1], "1"))
321 {
322 if (l[2] == NULL) archive = PATH_ASL_ARCHIVE;
323 else archive = strdup(l[2]); /* never freed */
324 }
325 else archive = NULL;
326 }
327 else if (!strcasecmp(l[0], "store_path"))
328 {
329 /* = archive path */
330 store_dir = strdup(l[1]); /* never freed */
331 }
332 else if (!strcasecmp(l[0], "archive_mode"))
333 {
334 archive_mode = strtol(l[1], NULL, 0);
335 if ((archive_mode == 0) && (errno == EINVAL)) archive_mode = 0400;
336 }
337
338 freeList(l);
339 }
340
341 static void
342 _parse_line(char *s)
343 {
344 if (s == NULL) return;
345 while ((*s == ' ') || (*s == '\t')) s++;
346
347 /*
348 * First non-whitespace char is the rule type.
349 * aslmanager only checks "=" (set parameter) rules.
350 */
351 if (*s == '=') _parse_set_param(s);
352 }
353
354 char *
355 get_line_from_file(FILE *f)
356 {
357 char *s, *out;
358 size_t len;
359
360 out = fgetln(f, &len);
361 if (out == NULL) return NULL;
362 if (len == 0) return NULL;
363
364 s = malloc(len + 1);
365 if (s == NULL) return NULL;
366
367 memcpy(s, out, len);
368
369 s[len - 1] = '\0';
370 return s;
371 }
372
373 static int
374 _parse_config_file(const char *name)
375 {
376 FILE *cf;
377 char *line;
378
379 cf = fopen(name, "r");
380 if (cf == NULL) return 1;
381
382 while (NULL != (line = get_line_from_file(cf)))
383 {
384 _parse_line(line);
385 free(line);
386 }
387
388 fclose(cf);
389
390 return 0;
391 }
392
393 int
394 main(int argc, const char *argv[])
395 {
396 int i, today_ymd_stringlen, expire_ymd_stringlen;
397 time_t now, ymd_expire;
398 struct tm ctm;
399 char today_ymd_string[32], expire_ymd_string[32], *str;
400 DIR *dp;
401 struct dirent *dent;
402 name_list_t *ymd_list, *bb_list, *e;
403 uint32_t status;
404 size_t file_size, store_size;
405 struct stat sb;
406
407 ymd_list = NULL;
408 bb_list = NULL;
409
410 ttl = DEFAULT_TTL * SECONDS_PER_DAY;
411 max_size = DEFAULT_MAX_SIZE;
412 store_size = 0;
413 debug = 0;
414
415 for (i = 1; i < argc; i++)
416 {
417 if (!strcmp(argv[i], "-a"))
418 {
419 if (((i + 1) < argc) && (argv[i + 1][0] != '-')) archive = (char *)argv[++i];
420 else archive = PATH_ASL_ARCHIVE;
421 }
422 else if (!strcmp(argv[i], "-s"))
423 {
424 if (((i + 1) < argc) && (argv[i + 1][0] != '-')) store_dir = (char *)argv[++i];
425 }
426 else if (!strcmp(argv[i], "-ttl"))
427 {
428 if (((i + 1) < argc) && (argv[i + 1][0] != '-')) ttl = atoi(argv[++i]) * SECONDS_PER_DAY;
429 }
430 else if (!strcmp(argv[i], "-size"))
431 {
432 if (((i + 1) < argc) && (argv[i + 1][0] != '-')) max_size = atoi(argv[++i]);
433 }
434 else if (!strcmp(argv[i], "-d"))
435 {
436 debug = 1;
437 }
438 }
439
440 _parse_config_file(_PATH_ASL_CONF);
441
442 if (debug == 1) printf("aslmanager starting\n");
443
444 /* check archive */
445 if (archive != NULL)
446 {
447 memset(&sb, 0, sizeof(struct stat));
448 if (stat(archive, &sb) == 0)
449 {
450 /* must be a directory */
451 if ((sb.st_mode & S_IFDIR) == 0)
452 {
453 fprintf(stderr, "aslmanager error: archive %s is not a directory", archive);
454 return -1;
455 }
456 }
457 else
458 {
459 if (errno == ENOENT)
460 {
461 /* archive doesn't exist - create it */
462 if (mkdir(archive, 0755) != 0)
463 {
464 fprintf(stderr, "aslmanager error: can't create archive %s: %s\n", archive, strerror(errno));
465 return -1;
466 }
467 }
468 else
469 {
470 /* stat failed for some other reason */
471 fprintf(stderr, "aslmanager error: can't stat archive %s: %s\n", archive, strerror(errno));
472 return -1;
473 }
474 }
475 }
476
477 chdir(store_dir);
478
479 /* determine current time */
480 now = time(NULL);
481
482 /* ttl 0 means files never expire */
483 ymd_expire = 0;
484 if (ttl > 0) ymd_expire = now - ttl;
485
486 /* construct today's date as YYYY.MM.DD */
487 memset(&ctm, 0, sizeof(struct tm));
488 if (localtime_r((const time_t *)&now, &ctm) == NULL) return -1;
489
490 snprintf(today_ymd_string, sizeof(today_ymd_string), "%d.%02d.%02d.", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday);
491 today_ymd_stringlen = strlen(today_ymd_string);
492
493 /* construct regular file expiry date as YYYY.MM.DD */
494 memset(&ctm, 0, sizeof(struct tm));
495 if (localtime_r((const time_t *)&ymd_expire, &ctm) == NULL) return -1;
496
497 snprintf(expire_ymd_string, sizeof(expire_ymd_string), "%d.%02d.%02d.", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday);
498 expire_ymd_stringlen = strlen(expire_ymd_string);
499
500 if (debug == 1) printf("Expiry Date %s\n", expire_ymd_string);
501
502 dp = opendir(store_dir);
503 if (dp == NULL) return -1;
504
505 /* gather a list of YMD files and BB files */
506 while ((dent = readdir(dp)) != NULL)
507 {
508 memset(&sb, 0, sizeof(struct stat));
509 file_size = 0;
510 if (stat(dent->d_name, &sb) == 0) file_size = sb.st_size;
511
512 if ((dent->d_name[0] >= '0') && (dent->d_name[0] <= '9'))
513 {
514 ymd_list = add_to_list(ymd_list, dent->d_name, file_size);
515 store_size += file_size;
516 }
517 else if (!strncmp(dent->d_name, "BB.", 3) && (dent->d_name[3] >= '0') && (dent->d_name[3] <= '9'))
518 {
519 bb_list = add_to_list(bb_list, dent->d_name, file_size);
520 store_size += file_size;
521 }
522 else if ((!strcmp(dent->d_name, ".")) || (!strcmp(dent->d_name, "..")))
523 {}
524 else if ((!strcmp(dent->d_name, "StoreData")) || (!strcmp(dent->d_name, "SweepStore")))
525 {}
526 else
527 {
528 fprintf(stderr, "aslmanager: unexpected file %s in ASL data store\n", dent->d_name);
529 }
530 }
531
532 closedir(dp);
533
534 if (debug == 1)
535 {
536 printf("Data Store Size = %lu\n", store_size);
537 printf("Data Store YMD Files\n");
538 for (e = ymd_list; e != NULL; e = e->next) printf(" %s %lu\n", e->name, e->size);
539 printf("Data Store BB Files\n");
540 for (e = bb_list; e != NULL; e = e->next) printf(" %s %lu\n", e->name, e->size);
541 }
542
543 /* Delete/achive expired YMD files */
544 if (debug == 1) printf("Start YMD Scan\n");
545
546 e = ymd_list;
547 while (e != NULL)
548 {
549 /* stop when a file name/date is after the expire date */
550 if (strncmp(e->name, expire_ymd_string, expire_ymd_stringlen) > 0) break;
551
552 if (archive != NULL)
553 {
554 str = NULL;
555 asprintf(&str, "%s/%s", archive, e->name);
556 if (str == NULL) return -1;
557
558 if (debug == 1) printf(" copy %s ---> %s\n", e->name, str);
559 status = do_copy(e->name, str, archive_mode);
560 free(str);
561 }
562
563 if (debug == 1) printf(" unlink %s\n", e->name);
564 unlink(e->name);
565
566 store_size -= e->size;
567 e->size = 0;
568
569 e = e->next;
570 }
571
572 if (debug == 1) printf("Finished YMD Scan\n");
573
574 /* Delete/achive expired BB files */
575 if (debug == 1) printf("Start BB Scan\n");
576
577 e = bb_list;
578 while (e != NULL)
579 {
580 /* stop when a file name/date is after the expire date */
581 if (strncmp(e->name + 3, today_ymd_string, today_ymd_stringlen) > 0) break;
582
583 if (archive != NULL)
584 {
585 str = NULL;
586 asprintf(&str, "%s/%s", archive, e->name);
587 if (str == NULL) return -1;
588
589 /* syslog -x [str] -f [e->name] */
590 if (debug == 1) printf(" copy %s ---> %s\n", e->name, str);
591 status = do_copy(e->name, str, archive_mode);
592 free(str);
593 }
594
595 if (debug == 1) printf(" unlink %s\n", e->name);
596 unlink(e->name);
597
598 store_size -= e->size;
599 e->size = 0;
600
601 e = e->next;
602 }
603
604 if (debug == 1) printf("Finished BB Scan\n");
605
606 /* if data store is over max_size, delete/archive more YMD files */
607 if ((debug == 1) && (store_size > max_size)) printf("Additional YMD Scan\n");
608
609 e = ymd_list;
610 while ((e != NULL) && (store_size > max_size))
611 {
612 if (e->size != 0)
613 {
614 /* stop when we get to today's files */
615 if (strncmp(e->name, today_ymd_string, today_ymd_stringlen) == 0) break;
616
617 if (archive != NULL)
618 {
619 str = NULL;
620 asprintf(&str, "%s/%s", archive, e->name);
621 if (str == NULL) return -1;
622
623 /* syslog -x [str] -f [e->name] */
624 if (debug == 1) printf(" copy %s ---> %s\n", e->name, str);
625 status = do_copy(e->name, str, archive_mode);
626 free(str);
627 }
628
629 if (debug == 1) printf(" unlink %s\n", e->name);
630 unlink(e->name);
631
632 store_size -= e->size;
633 e->size = 0;
634 }
635
636 e = e->next;
637 }
638
639 /* if data store is over max_size, delete/archive more BB files */
640 if ((debug == 1) && (store_size > max_size)) printf("Additional BB Scan\n");
641
642 e = bb_list;
643 while ((e != NULL) && (store_size > max_size))
644 {
645 if (e->size != 0)
646 {
647 if (archive != NULL)
648 {
649 str = NULL;
650 asprintf(&str, "%s/%s", archive, e->name);
651 if (str == NULL) return -1;
652
653 /* syslog -x [str] -f [e->name] */
654 if (debug == 1) printf(" copy %s ---> %s\n", e->name, str);
655 status = do_copy(e->name, str, archive_mode);
656 free(str);
657 }
658
659 if (debug == 1) printf(" unlink %s\n", e->name);
660 unlink(e->name);
661
662 store_size -= e->size;
663 e->size = 0;
664 }
665
666 e = e->next;
667 }
668
669 free_list(ymd_list);
670 free_list(bb_list);
671
672 if (debug == 1)
673 {
674 printf("Data Store Size = %lu\n", store_size);
675 printf("aslmanager finished\n");
676 }
677
678 return 0;
679 }
680