]> git.saurik.com Git - apple/syslog.git/blob - aslmanager.tproj/aslmanager.c
f5b8a6f309bbb62f76808e122a0984f230e57978
[apple/syslog.git] / aslmanager.tproj / aslmanager.c
1 /*
2 * Copyright (c) 2007-2008 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 51200000
42 #define DEFAULT_TTL 2
43
44 #define LONGTTL_TMP_FILE "LongTTL.new"
45
46 typedef struct name_list_s
47 {
48 char *name;
49 size_t size;
50 struct name_list_s *next;
51 } name_list_t;
52
53 void
54 mgr_exit(const char *store, int status)
55 {
56 char *s;
57
58 if (store == NULL) exit(status);
59
60 s = NULL;
61 asprintf(&s, "%s/%s", store, FILE_ASL_STORE_SWEEP_SEMAPHORE);
62 if (s != NULL)
63 {
64 unlink(s);
65 free(s);
66 }
67 else exit(1);
68
69 exit(status);
70 }
71
72 name_list_t *
73 add_to_list(name_list_t *l, const char *name, size_t size)
74 {
75 name_list_t *e, *x;
76
77 if (name == NULL) return l;
78
79 e = (name_list_t *)calloc(1, sizeof(name_list_t));
80 if (e == NULL) return NULL;
81
82 e->name = strdup(name);
83 if (e->name == NULL)
84 {
85 free(e);
86 return NULL;
87 }
88
89 e->size = size;
90
91 /* list is sorted by name (i.e. primarily by timestamp) */
92 if (l == NULL) return e;
93
94 if (strcmp(e->name, l->name) <= 0)
95 {
96 e->next = l;
97 return e;
98 }
99
100 for (x = l; (x->next != NULL) && (strcmp(e->name, x->next->name) > 0) ; x = x->next);
101
102 e->next = x->next;
103 x->next = e;
104 return l;
105 }
106
107 void
108 free_list(name_list_t *l)
109 {
110 name_list_t *e;
111
112 while (l != NULL)
113 {
114 e = l;
115 l = l->next;
116 free(e->name);
117 free(e);
118 }
119
120 free(l);
121 }
122
123 uint32_t
124 do_match(const char *infile, const char *outfile, int do_ttl, time_t expire_time)
125 {
126 asl_search_result_t q, *query, *res;
127 asl_msg_t *m, *qm[1];
128 asl_file_t *in, *out;
129 uint32_t status, i;
130 char str[64];
131 uint64_t mid;
132
133 if (infile == NULL) return ASL_STATUS_INVALID_ARG;
134 if (outfile == NULL) return ASL_STATUS_INVALID_ARG;
135
136 in = NULL;
137 status = asl_file_open_read(infile, &in);
138 if (status != ASL_STATUS_OK) return status;
139
140 query = NULL;
141 q.count = 1;
142 q.curr = 0;
143 q.msg = qm;
144 qm[0] = NULL;
145 m = NULL;
146
147 if (do_ttl == 1)
148 {
149 query = &q;
150 m = asl_new(ASL_TYPE_QUERY);
151 if (m == NULL)
152 {
153 asl_file_close(in);
154 return ASL_STATUS_NO_MEMORY;
155 }
156
157 qm[0] = m;
158
159 if (expire_time != 0)
160 {
161 snprintf(str, sizeof(str), "%llu", (long long unsigned int)expire_time);
162 if (asl_set_query(m, ASL_KEY_EXPIRE_TIME, str, ASL_QUERY_OP_NUMERIC | ASL_QUERY_OP_GREATER_EQUAL) != 0)
163 {
164 asl_file_close(in);
165 asl_free(m);
166 return ASL_STATUS_NO_MEMORY;
167 }
168 }
169 else
170 {
171 if (asl_set_query(m, ASL_KEY_EXPIRE_TIME, NULL, ASL_QUERY_OP_TRUE) != 0)
172 {
173 asl_file_close(in);
174 asl_free(m);
175 return ASL_STATUS_NO_MEMORY;
176 }
177 }
178 }
179
180 res = NULL;
181 mid = 0;
182 status = asl_file_match(in, query, &res, &mid, 0, 0, 1);
183 if (m != NULL) asl_free(m);
184 asl_file_close(in);
185
186 if (status != ASL_STATUS_OK) return status;
187
188 /*
189 * N.B. "ASL_STATUS_NOT_FOUND" is never returned by asl_file_match.
190 * We use it here to signal the caller that no records were found by the match.
191 */
192 if (res == NULL) return ASL_STATUS_NOT_FOUND;
193 if (res->count == 0)
194 {
195 aslresponse_free(res);
196 return ASL_STATUS_NOT_FOUND;
197 }
198
199 out = NULL;
200 status = asl_file_open_write(outfile, 0644, -1, -1, &out);
201 if (status != ASL_STATUS_OK) return status;
202
203 out->flags = ASL_FILE_FLAG_UNLIMITED_CACHE | ASL_FILE_FLAG_PRESERVE_MSG_ID;
204
205 for (i = 0; i < res->count; i++)
206 {
207 mid = 0;
208 status = asl_file_save(out, res->msg[i], &mid);
209 if (status != ASL_STATUS_OK) break;
210 }
211
212 asl_file_close(out);
213 return status;
214 }
215
216 int
217 main(int argc, const char *argv[])
218 {
219 int i, bbstrlen, debug;
220 const char *archive, *store_dir;
221 time_t now, best_before, ttl;
222 struct tm ctm;
223 char bbstr[32], *str, *p;
224 DIR *dp;
225 struct dirent *dent;
226 name_list_t *list, *e;
227 uint32_t status;
228 size_t file_size, store_size, max_size;
229 struct stat sb;
230
231 list = NULL;
232
233 archive = NULL;
234 store_dir = PATH_ASL_STORE;
235 ttl = DEFAULT_TTL * SECONDS_PER_DAY;
236 max_size = DEFAULT_MAX_SIZE;
237 store_size = 0;
238 debug = 0;
239
240 for (i = 1; i < argc; i++)
241 {
242 if (!strcmp(argv[i], "-a"))
243 {
244 if (((i + 1) < argc) && (argv[i + 1][0] != '-')) archive = argv[++i];
245 else archive = PATH_ASL_ARCHIVE;
246 }
247 else if (!strcmp(argv[i], "-s"))
248 {
249 if (((i + 1) < argc) && (argv[i + 1][0] != '-')) store_dir = argv[++i];
250 }
251 else if (!strcmp(argv[i], "-ttl"))
252 {
253 if (((i + 1) < argc) && (argv[i + 1][0] != '-')) ttl = atoi(argv[++i]) * SECONDS_PER_DAY;
254 }
255 else if (!strcmp(argv[i], "-size"))
256 {
257 if (((i + 1) < argc) && (argv[i + 1][0] != '-')) max_size = atoi(argv[++i]);
258 }
259 else if (!strcmp(argv[i], "-d"))
260 {
261 debug = 1;
262 }
263 }
264
265 /* check archive */
266 if (archive != NULL)
267 {
268 memset(&sb, 0, sizeof(struct stat));
269 if (stat(archive, &sb) == 0)
270 {
271 /* must be a directory */
272 if ((sb.st_mode & S_IFDIR) == 0)
273 {
274 fprintf(stderr, "aslmanager error: archive %s is not a directory", archive);
275 return -1;
276 }
277 }
278 else
279 {
280 if (errno == ENOENT)
281 {
282 /* archive doesn't exist - create it */
283 if (mkdir(archive, 0755) != 0)
284 {
285 fprintf(stderr, "aslmanager error: can't create archive %s: %s\n", archive, strerror(errno));
286 return -1;
287 }
288 }
289 else
290 {
291 /* stat failed for some other reason */
292 fprintf(stderr, "aslmanager error: can't stat archive %s: %s\n", archive, strerror(errno));
293 return -1;
294 }
295 }
296 }
297
298 chdir(store_dir);
299
300 /* determine current time and time TTL ago */
301 now = time(NULL);
302 best_before = 0;
303 if (ttl > 0) best_before = now - ttl;
304
305 /* construct best before date as YYYY.MM.DD */
306 memset(&ctm, 0, sizeof(struct tm));
307 if (localtime_r((const time_t *)&best_before, &ctm) == NULL) mgr_exit(store_dir, 1);
308
309 snprintf(bbstr, sizeof(bbstr), "%d.%02d.%02d.", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday);
310 bbstrlen = strlen(bbstr);
311
312 if (debug == 1) printf("Best Before Date %s\n", bbstr);
313
314 dp = opendir(store_dir);
315 if (dp == NULL) mgr_exit(store_dir, 1);
316
317 /* gather a list of files for dates before the best before date */
318
319 while ((dent = readdir(dp)) != NULL)
320 {
321 if ((dent->d_name[0] < '0') || (dent->d_name[0] > '9')) continue;
322
323 memset(&sb, 0, sizeof(struct stat));
324 file_size = 0;
325 if (stat(dent->d_name, &sb) == 0) file_size = sb.st_size;
326 store_size += file_size;
327
328 list = add_to_list(list, dent->d_name, file_size);
329 }
330
331 closedir(dp);
332
333 if (debug == 1)
334 {
335 printf("\nData Store Size = %lu\n", store_size);
336 printf("\nData Store Files\n");
337 for (e = list; e != NULL; e = e->next) printf(" %s %lu\n", e->name, e->size);
338 }
339
340 /* copy messages in each expired file with ASLExpireTime values to LongTTL files */
341 if (debug == 1) printf("\nStart Scan\n");
342
343 e = list;
344 while (e != NULL)
345 {
346 if ((store_size <= max_size) && (strncmp(e->name, bbstr, bbstrlen) >= 0)) break;
347
348 /* find '.' after year */
349 p = strchr(e->name, '.');
350 if (p == NULL) continue;
351
352 /* find '.' after month */
353 p++;
354 p = strchr(p, '.');
355 if (p == NULL) continue;
356
357 /* find '.' after day */
358 p++;
359 p = strchr(p, '.');
360 if (p == NULL) continue;
361
362 str = NULL;
363 asprintf(&str, "LongTTL%s", p);
364 if (str == NULL) mgr_exit(store_dir, 1);
365
366 /* syslog -x [str] -db [e->name] -k ASLExpireTime */
367 if (debug == 1) printf(" scan %s ---> %s\n", e->name, str);
368 else status = do_match(e->name, str, 1, 0);
369
370 free(str);
371 str = NULL;
372
373 if (archive != NULL)
374 {
375 str = NULL;
376 asprintf(&str, "%s/%s", archive, e->name);
377 if (str == NULL) mgr_exit(store_dir, 1);
378
379 /* syslog -x [str] -db [e->name] */
380 if (debug == 1) printf(" copy %s ---> %s\n", e->name, str);
381 else status = do_match(e->name, str, 0, 0);
382 free(str);
383 }
384
385 if (debug == 1) printf(" unlink %s\n", e->name);
386 else unlink(e->name);
387
388 store_size -= e->size;
389 e->size = 0;
390
391 e = e->next;
392 }
393
394 if (debug == 1)
395 {
396 printf("Finished Scan\n");
397 printf("\nData Store Size = %lu\n", store_size);
398 }
399
400 free_list(list);
401 list = NULL;
402
403 dp = opendir(PATH_ASL_STORE);
404 if (dp == NULL) mgr_exit(store_dir, 1);
405
406 /* gather a list of LongTTL files */
407
408 while ((dent = readdir(dp)) != NULL)
409 {
410 if (!strcmp(dent->d_name, LONGTTL_TMP_FILE)) unlink(LONGTTL_TMP_FILE);
411 else if (!strncmp(dent->d_name, "LongTTL.", 8)) list = add_to_list(list, dent->d_name, 0);
412 }
413
414 closedir(dp);
415
416 if (debug == 1)
417 {
418 printf("\nData Store LongTTL Files\n");
419 for (e = list; e != NULL; e = e->next) printf(" %s\n", e->name);
420 }
421
422 if (debug == 1) printf("\nScan for expired messages\n");
423
424 e = list;
425 while (e != NULL)
426 {
427 /* syslog -x LongTTL.new -db [e->name] -k ASLExpireTime Nge [now] */
428 if (debug == 1)
429 {
430 printf(" %s\n", e->name);
431 }
432 else
433 {
434 status = do_match(e->name, LONGTTL_TMP_FILE, 1, now);
435 unlink(e->name);
436 if (status == ASL_STATUS_OK) rename(LONGTTL_TMP_FILE, e->name);
437 }
438
439 e = e->next;
440 }
441
442 if (debug == 1) printf("Finished scan for expired messages\n");
443
444 free_list(list);
445 list = NULL;
446
447 mgr_exit(store_dir, 0);
448 /* UNREACHED */
449 return 0;
450 }
451