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