]>
Commit | Line | Data |
---|---|---|
b5d655f7 | 1 | /* |
1f2f436a | 2 | * Copyright (c) 2007-2010 Apple Inc. All rights reserved. |
b5d655f7 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
1f2f436a A |
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. | |
b5d655f7 A |
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, | |
1f2f436a A |
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. | |
b5d655f7 A |
20 | * |
21 | * @APPLE_LICENSE_HEADER_END@ | |
22 | */ | |
23 | ||
24 | #include <stdlib.h> | |
25 | #include <unistd.h> | |
26 | #include <string.h> | |
27 | #include <errno.h> | |
28 | #include <dirent.h> | |
29 | #include <sys/types.h> | |
30 | #include <sys/stat.h> | |
31 | #include <fcntl.h> | |
32 | #include <asl.h> | |
33 | #include <asl_private.h> | |
34 | #include <asl_core.h> | |
35 | #include <asl_store.h> | |
36 | #include <notify.h> | |
37 | ||
38 | extern time_t asl_parse_time(const char *str); | |
39 | extern uint64_t asl_file_cursor(asl_file_t *s); | |
40 | extern uint32_t asl_file_match_start(asl_file_t *s, uint64_t start_id, int32_t direction); | |
41 | extern uint32_t asl_file_match_next(asl_file_t *s, aslresponse query, asl_msg_t **msg, uint64_t *last_id, int32_t direction, int32_t ruid, int32_t rgid); | |
1f2f436a | 42 | extern int asl_file_create(const char *path, uid_t uid, gid_t gid, mode_t mode); |
b5d655f7 A |
43 | |
44 | #define SECONDS_PER_DAY 86400 | |
45 | ||
46 | /* | |
47 | * The ASL Store is organized as a set of files in a common directory. | |
48 | * Files are prefixed by the date (YYYY.MM.DD) of their contents. | |
b5d655f7 A |
49 | * |
50 | * Messages with no access controls are saved in YYYY.MM.DD.asl | |
34e8f829 A |
51 | * Messages with access limited to UID uuu are saved in YYYY.MM.DD.Uuuu.asl |
52 | * Messages with access limited to GID ggg are saved in YYYY.MM.DD.Gggg.asl | |
53 | * Messages with access limited to UID uuu and GID ggg are saved in YYYY.MM.DD.Uuuu.Gggg.asl | |
54 | * | |
55 | * Messages that have a value for ASLExpireTime are saved in BB.YYYY.MM.DD.asl | |
56 | * where the timestamp is the "Best Before" date of the file. Access controls | |
57 | * are implemented as above with Uuuu and Gggg in the file name. Note that the | |
58 | * Best Before files are for the last day of the month, so a single file contains | |
59 | * messages that expire in that month. | |
b5d655f7 A |
60 | * |
61 | * An external tool runs daily and deletes "old" files. | |
62 | */ | |
63 | ||
34e8f829 A |
64 | static time_t |
65 | _asl_start_today() | |
66 | { | |
67 | time_t now; | |
68 | struct tm ctm; | |
69 | ||
70 | memset(&ctm, 0, sizeof(struct tm)); | |
71 | now = time(NULL); | |
72 | ||
73 | if (localtime_r((const time_t *)&now, &ctm) == NULL) return 0; | |
74 | ||
75 | ctm.tm_sec = 0; | |
76 | ctm.tm_min = 0; | |
77 | ctm.tm_hour = 0; | |
78 | ||
79 | return mktime(&ctm); | |
80 | } | |
81 | ||
b5d655f7 A |
82 | /* |
83 | * The base directory contains a data file which stores | |
84 | * the last record ID. | |
85 | * | |
86 | * | MAX_ID (uint64_t) | | |
87 | * | |
88 | */ | |
89 | uint32_t | |
90 | asl_store_open_write(const char *basedir, asl_store_t **s) | |
91 | { | |
92 | asl_store_t *out; | |
b5d655f7 | 93 | struct stat sb; |
34e8f829 A |
94 | uint32_t i, flags; |
95 | char *path; | |
b5d655f7 A |
96 | FILE *sd; |
97 | uint64_t last_id; | |
34e8f829 | 98 | time_t start; |
b5d655f7 A |
99 | |
100 | if (s == NULL) return ASL_STATUS_INVALID_ARG; | |
101 | ||
34e8f829 A |
102 | start = _asl_start_today(); |
103 | if (start == 0) return ASL_STATUS_FAILED; | |
104 | ||
b5d655f7 A |
105 | if (basedir == NULL) basedir = PATH_ASL_STORE; |
106 | ||
107 | memset(&sb, 0, sizeof(struct stat)); | |
108 | if (stat(basedir, &sb) != 0) return ASL_STATUS_INVALID_STORE; | |
1f2f436a | 109 | if (!S_ISDIR(sb.st_mode)) return ASL_STATUS_INVALID_STORE; |
b5d655f7 A |
110 | |
111 | path = NULL; | |
112 | asprintf(&path, "%s/%s", basedir, FILE_ASL_STORE_DATA); | |
113 | if (path == NULL) return ASL_STATUS_NO_MEMORY; | |
114 | ||
115 | sd = NULL; | |
116 | ||
117 | memset(&sb, 0, sizeof(struct stat)); | |
118 | if (stat(path, &sb) != 0) | |
119 | { | |
120 | if (errno != ENOENT) | |
121 | { | |
122 | free(path); | |
123 | return ASL_STATUS_FAILED; | |
124 | } | |
125 | ||
126 | sd = fopen(path, "w+"); | |
127 | free(path); | |
128 | ||
129 | if (sd == NULL) return ASL_STATUS_FAILED; | |
130 | ||
131 | last_id = 0; | |
132 | ||
34e8f829 A |
133 | /* Create new StoreData file (8 bytes ID + 4 bytes flags) */ |
134 | ||
b5d655f7 A |
135 | if (fwrite(&last_id, sizeof(uint64_t), 1, sd) != 1) |
136 | { | |
137 | fclose(sd); | |
138 | return ASL_STATUS_WRITE_FAILED; | |
139 | } | |
34e8f829 A |
140 | |
141 | flags = 0; | |
142 | if (fwrite(&flags, sizeof(uint32_t), 1, sd) != 1) | |
143 | { | |
144 | fclose(sd); | |
145 | return ASL_STATUS_WRITE_FAILED; | |
146 | } | |
1f2f436a A |
147 | |
148 | /* flush data */ | |
149 | fflush(sd); | |
b5d655f7 A |
150 | } |
151 | else | |
152 | { | |
153 | sd = fopen(path, "r+"); | |
154 | free(path); | |
155 | ||
156 | if (sd == NULL) return ASL_STATUS_FAILED; | |
157 | if (fread(&last_id, sizeof(uint64_t), 1, sd) != 1) | |
158 | { | |
159 | fclose(sd); | |
160 | return ASL_STATUS_READ_FAILED; | |
161 | } | |
162 | ||
163 | last_id = asl_core_ntohq(last_id); | |
164 | } | |
165 | ||
b5d655f7 A |
166 | out = (asl_store_t *)calloc(1, sizeof(asl_store_t)); |
167 | if (out == NULL) | |
168 | { | |
169 | fclose(sd); | |
b5d655f7 A |
170 | return ASL_STATUS_NO_MEMORY; |
171 | } | |
172 | ||
173 | if (basedir == NULL) out->base_dir = strdup(PATH_ASL_STORE); | |
174 | else out->base_dir = strdup(basedir); | |
175 | ||
176 | if (out->base_dir == NULL) | |
177 | { | |
178 | fclose(sd); | |
b5d655f7 A |
179 | free(out); |
180 | return ASL_STATUS_NO_MEMORY; | |
181 | } | |
182 | ||
34e8f829 | 183 | out->start_today = start; |
b5d655f7 | 184 | out->start_tomorrow = out->start_today + SECONDS_PER_DAY; |
b5d655f7 A |
185 | out->storedata = sd; |
186 | out->next_id = last_id + 1; | |
187 | ||
188 | for (i = 0; i < FILE_CACHE_SIZE; i++) | |
189 | { | |
190 | memset(&out->file_cache[i], 0, sizeof(asl_cached_file_t)); | |
191 | out->file_cache[i].u = -1; | |
192 | out->file_cache[i].g = -1; | |
193 | } | |
194 | ||
195 | *s = out; | |
196 | return ASL_STATUS_OK; | |
197 | } | |
198 | ||
199 | uint32_t | |
200 | asl_store_statistics(asl_store_t *s, aslmsg *msg) | |
201 | { | |
202 | aslmsg out; | |
b5d655f7 A |
203 | |
204 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
205 | if (msg == NULL) return ASL_STATUS_INVALID_ARG; | |
206 | ||
1f2f436a | 207 | out = asl_new(ASL_TYPE_MSG); |
b5d655f7 A |
208 | if (out == NULL) return ASL_STATUS_NO_MEMORY; |
209 | ||
34e8f829 | 210 | /* does nothing for now */ |
b5d655f7 A |
211 | |
212 | *msg = out; | |
213 | return ASL_STATUS_OK; | |
214 | } | |
215 | ||
216 | uint32_t | |
217 | asl_store_open_read(const char *basedir, asl_store_t **s) | |
218 | { | |
219 | asl_store_t *out; | |
220 | struct stat sb; | |
221 | ||
222 | if (s == NULL) return ASL_STATUS_INVALID_ARG; | |
223 | ||
224 | if (basedir == NULL) basedir = PATH_ASL_STORE; | |
225 | ||
226 | memset(&sb, 0, sizeof(struct stat)); | |
227 | if (stat(basedir, &sb) != 0) return ASL_STATUS_INVALID_STORE; | |
1f2f436a | 228 | if (!S_ISDIR(sb.st_mode)) return ASL_STATUS_INVALID_STORE; |
b5d655f7 A |
229 | |
230 | out = (asl_store_t *)calloc(1, sizeof(asl_store_t)); | |
231 | if (out == NULL) return ASL_STATUS_NO_MEMORY; | |
232 | ||
233 | if (basedir == NULL) out->base_dir = strdup(PATH_ASL_STORE); | |
234 | else out->base_dir = strdup(basedir); | |
235 | ||
236 | if (out->base_dir == NULL) | |
237 | { | |
238 | free(out); | |
239 | return ASL_STATUS_NO_MEMORY; | |
240 | } | |
241 | ||
242 | *s = out; | |
243 | return ASL_STATUS_OK; | |
244 | } | |
245 | ||
246 | uint32_t | |
247 | asl_store_max_file_size(asl_store_t *s, size_t max) | |
248 | { | |
249 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
250 | ||
251 | s->max_file_size = max; | |
252 | return ASL_STATUS_OK; | |
253 | } | |
254 | ||
1f2f436a | 255 | __private_extern__ void |
b5d655f7 A |
256 | asl_store_file_closeall(asl_store_t *s) |
257 | { | |
258 | uint32_t i; | |
259 | ||
260 | if (s == NULL) return; | |
261 | ||
262 | for (i = 0; i < FILE_CACHE_SIZE; i++) | |
263 | { | |
264 | if (s->file_cache[i].f != NULL) asl_file_close(s->file_cache[i].f); | |
265 | s->file_cache[i].f = NULL; | |
266 | if (s->file_cache[i].path != NULL) free(s->file_cache[i].path); | |
267 | s->file_cache[i].path = NULL; | |
268 | s->file_cache[i].u = -1; | |
269 | s->file_cache[i].g = -1; | |
34e8f829 | 270 | s->file_cache[i].bb = 0; |
b5d655f7 A |
271 | s->file_cache[i].ts = 0; |
272 | } | |
273 | } | |
274 | ||
275 | uint32_t | |
276 | asl_store_close(asl_store_t *s) | |
277 | { | |
278 | if (s == NULL) return ASL_STATUS_OK; | |
279 | ||
280 | if (s->base_dir != NULL) free(s->base_dir); | |
281 | s->base_dir = NULL; | |
b5d655f7 A |
282 | asl_store_file_closeall(s); |
283 | if (s->storedata != NULL) fclose(s->storedata); | |
284 | ||
285 | free(s); | |
286 | ||
287 | return ASL_STATUS_OK; | |
288 | } | |
289 | ||
290 | uint32_t | |
291 | asl_store_signal_sweep(asl_store_t *s) | |
292 | { | |
293 | char *str; | |
294 | int semfd; | |
34e8f829 A |
295 | uint64_t xid; |
296 | uint32_t status; | |
b5d655f7 A |
297 | |
298 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
299 | ||
300 | asprintf(&str, "%s/%s", s->base_dir, FILE_ASL_STORE_SWEEP_SEMAPHORE); | |
301 | if (str == NULL) return ASL_STATUS_NO_MEMORY; | |
302 | ||
303 | semfd = open(str, O_WRONLY | O_CREAT | O_NONBLOCK, 0644); | |
304 | free(str); | |
305 | ||
306 | if (semfd < 0) return ASL_STATUS_WRITE_FAILED; | |
307 | ||
34e8f829 A |
308 | status = ASL_STATUS_OK; |
309 | ||
310 | /* write the current message ID in the SweepStore file */ | |
311 | xid = asl_core_htonq(s->next_id - 1); | |
312 | if (write(semfd, &xid, sizeof(uint64_t)) != sizeof(uint64_t)) status = ASL_STATUS_WRITE_FAILED; | |
313 | ||
b5d655f7 | 314 | close(semfd); |
34e8f829 | 315 | return status; |
b5d655f7 A |
316 | } |
317 | ||
318 | /* | |
319 | * Sweep the file cache. | |
320 | * Close any files that have not been used in the last FILE_CACHE_TTL seconds. | |
321 | * Returns least recently used or unused cache slot. | |
322 | */ | |
323 | static uint32_t | |
34e8f829 | 324 | asl_store_file_cache_lru(asl_store_t *s, time_t now, uint32_t ignorex) |
b5d655f7 A |
325 | { |
326 | time_t min; | |
327 | uint32_t i, x; | |
328 | ||
329 | if (s == NULL) return 0; | |
330 | ||
331 | x = 0; | |
332 | min = now - FILE_CACHE_TTL; | |
34e8f829 | 333 | |
b5d655f7 A |
334 | for (i = 0; i < FILE_CACHE_SIZE; i++) |
335 | { | |
34e8f829 | 336 | if ((i != ignorex) && (s->file_cache[i].ts < min)) |
b5d655f7 A |
337 | { |
338 | asl_file_close(s->file_cache[i].f); | |
339 | s->file_cache[i].f = NULL; | |
340 | if (s->file_cache[i].path != NULL) free(s->file_cache[i].path); | |
341 | s->file_cache[i].path = NULL; | |
342 | s->file_cache[i].u = -1; | |
343 | s->file_cache[i].g = -1; | |
34e8f829 A |
344 | s->file_cache[i].bb = 0; |
345 | s->file_cache[i].ts = 0; | |
b5d655f7 | 346 | } |
34e8f829 | 347 | |
b5d655f7 A |
348 | if (s->file_cache[i].ts < s->file_cache[x].ts) x = i; |
349 | } | |
350 | ||
351 | return x; | |
352 | } | |
353 | ||
354 | uint32_t | |
355 | asl_store_sweep_file_cache(asl_store_t *s) | |
356 | { | |
357 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
358 | ||
34e8f829 | 359 | asl_store_file_cache_lru(s, time(NULL), FILE_CACHE_SIZE); |
b5d655f7 A |
360 | return ASL_STATUS_OK; |
361 | } | |
362 | ||
1f2f436a A |
363 | static char * |
364 | asl_store_make_ug_path(const char *dir, const char *base, const char *ext, uid_t ruid, gid_t rgid, uid_t *u, gid_t *g, mode_t *m) | |
365 | { | |
366 | char *path = NULL; | |
367 | ||
368 | *u = 0; | |
369 | *g = 0; | |
370 | *m = 0644; | |
371 | ||
372 | if (ruid == -1) | |
373 | { | |
374 | if (rgid == -1) | |
375 | { | |
376 | if (ext == NULL) asprintf(&path, "%s/%s", dir, base); | |
377 | else asprintf(&path, "%s/%s.%s", dir, base, ext); | |
378 | } | |
379 | else | |
380 | { | |
381 | *g = rgid; | |
382 | *m = 0600; | |
383 | if (ext == NULL) asprintf(&path, "%s/%s.G%d", dir, base, *g); | |
384 | else asprintf(&path, "%s/%s.G%d.%s", dir, base, *g, ext); | |
385 | } | |
386 | } | |
387 | else | |
388 | { | |
389 | *u = ruid; | |
390 | if (rgid == -1) | |
391 | { | |
392 | *m = 0600; | |
393 | if (ext == NULL) asprintf(&path, "%s/%s.U%d", dir, base, *u); | |
394 | else asprintf(&path, "%s/%s.U%d.%s", dir, base, *u, ext); | |
395 | } | |
396 | else | |
397 | { | |
398 | *g = rgid; | |
399 | *m = 0600; | |
400 | if (ext == NULL) asprintf(&path, "%s/%s.U%d.G%d", dir, base, *u, *g); | |
401 | else asprintf(&path, "%s/%s.U%d.G%u.%s", dir, base, *u, *g, ext); | |
402 | } | |
403 | } | |
404 | ||
405 | return path; | |
406 | } | |
407 | ||
b5d655f7 | 408 | static uint32_t |
34e8f829 | 409 | asl_store_file_open_write(asl_store_t *s, char *tstring, int32_t ruid, int32_t rgid, time_t bb, asl_file_t **f, time_t now, uint32_t check_cache) |
b5d655f7 A |
410 | { |
411 | char *path; | |
412 | mode_t m; | |
1f2f436a A |
413 | int32_t i, x; |
414 | uid_t u; | |
415 | gid_t g; | |
b5d655f7 A |
416 | uint32_t status; |
417 | asl_file_t *out; | |
418 | ||
419 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
420 | ||
421 | /* see if the file is already open and in the cache */ | |
422 | for (i = 0; i < FILE_CACHE_SIZE; i++) | |
423 | { | |
34e8f829 | 424 | if ((s->file_cache[i].u == ruid) && (s->file_cache[i].g == rgid) && (s->file_cache[i].bb == bb) && (s->file_cache[i].f != NULL)) |
b5d655f7 A |
425 | { |
426 | s->file_cache[i].ts = now; | |
427 | *f = s->file_cache[i].f; | |
34e8f829 | 428 | if (check_cache == 1) asl_store_file_cache_lru(s, now, i); |
b5d655f7 A |
429 | return ASL_STATUS_OK; |
430 | } | |
431 | } | |
432 | ||
b5d655f7 A |
433 | u = 0; |
434 | g = 0; | |
435 | m = 0644; | |
1f2f436a | 436 | path = asl_store_make_ug_path(s->base_dir, tstring, "asl", (uid_t)ruid, (gid_t)rgid, &u, &g, &m); |
b5d655f7 A |
437 | if (path == NULL) return ASL_STATUS_NO_MEMORY; |
438 | ||
439 | out = NULL; | |
440 | status = asl_file_open_write(path, m, u, g, &out); | |
441 | if (status != ASL_STATUS_OK) | |
442 | { | |
443 | free(path); | |
444 | return status; | |
445 | } | |
446 | ||
34e8f829 | 447 | x = asl_store_file_cache_lru(s, now, FILE_CACHE_SIZE); |
b5d655f7 A |
448 | if (s->file_cache[x].f != NULL) asl_file_close(s->file_cache[x].f); |
449 | if (s->file_cache[x].path != NULL) free(s->file_cache[x].path); | |
450 | ||
451 | s->file_cache[x].f = out; | |
452 | s->file_cache[x].path = path; | |
453 | s->file_cache[x].u = ruid; | |
454 | s->file_cache[x].g = rgid; | |
34e8f829 | 455 | s->file_cache[x].bb = bb; |
b5d655f7 A |
456 | s->file_cache[x].ts = time(NULL); |
457 | ||
458 | *f = out; | |
459 | ||
460 | return ASL_STATUS_OK; | |
461 | } | |
462 | ||
1f2f436a | 463 | __private_extern__ char * |
b5d655f7 A |
464 | asl_store_file_path(asl_store_t *s, asl_file_t *f) |
465 | { | |
466 | uint32_t i; | |
467 | ||
468 | if (s == NULL) return NULL; | |
469 | ||
470 | for (i = 0; i < FILE_CACHE_SIZE; i++) | |
471 | { | |
472 | if (s->file_cache[i].f == f) | |
473 | { | |
474 | if (s->file_cache[i].path == NULL) return NULL; | |
475 | return strdup(s->file_cache[i].path); | |
476 | } | |
477 | } | |
478 | ||
479 | return NULL; | |
480 | } | |
481 | ||
1f2f436a | 482 | __private_extern__ void |
b5d655f7 A |
483 | asl_store_file_close(asl_store_t *s, asl_file_t *f) |
484 | { | |
485 | uint32_t i; | |
486 | ||
487 | if (s == NULL) return; | |
34e8f829 | 488 | if (f == NULL) return; |
b5d655f7 A |
489 | |
490 | for (i = 0; i < FILE_CACHE_SIZE; i++) | |
491 | { | |
492 | if (s->file_cache[i].f == f) | |
493 | { | |
494 | asl_file_close(s->file_cache[i].f); | |
495 | s->file_cache[i].f = NULL; | |
496 | if (s->file_cache[i].path != NULL) free(s->file_cache[i].path); | |
497 | s->file_cache[i].path = NULL; | |
498 | s->file_cache[i].u = -1; | |
499 | s->file_cache[i].g = -1; | |
34e8f829 | 500 | s->file_cache[i].bb = 0; |
b5d655f7 A |
501 | s->file_cache[i].ts = 0; |
502 | return; | |
503 | } | |
504 | } | |
505 | } | |
506 | ||
507 | uint32_t | |
508 | asl_store_save(asl_store_t *s, aslmsg msg) | |
509 | { | |
510 | struct tm ctm; | |
34e8f829 A |
511 | time_t msg_time, now, bb; |
512 | char *path, *tmp_path, *tstring, *scratch; | |
b5d655f7 A |
513 | const char *val; |
514 | uid_t ruid; | |
515 | gid_t rgid; | |
516 | asl_file_t *f; | |
34e8f829 | 517 | uint32_t status, check_cache, signal_sweep, len; |
b5d655f7 A |
518 | uint64_t xid, ftime; |
519 | size_t fsize; | |
520 | ||
521 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
522 | if (msg == NULL) return ASL_STATUS_INVALID_ARG; | |
523 | ||
524 | now = time(NULL); | |
525 | ||
34e8f829 A |
526 | check_cache = 0; |
527 | if ((s->last_write + FILE_CACHE_TTL) <= now) check_cache = 1; | |
528 | ||
529 | signal_sweep = 0; | |
530 | ||
531 | msg_time = 0; | |
b5d655f7 | 532 | val = asl_get(msg, ASL_KEY_TIME); |
34e8f829 A |
533 | if (val == NULL) msg_time = now; |
534 | else msg_time = asl_parse_time(val); | |
b5d655f7 | 535 | |
34e8f829 | 536 | if (msg_time >= s->start_tomorrow) |
b5d655f7 A |
537 | { |
538 | if (now >= s->start_tomorrow) | |
539 | { | |
540 | /* new day begins */ | |
34e8f829 A |
541 | check_cache = 0; |
542 | signal_sweep = 1; | |
543 | asl_store_file_closeall(s); | |
544 | ||
545 | /* | |
546 | * _asl_start_today should never fail, but if it does, | |
547 | * just push forward one day. That will probably be correct, and if | |
548 | * it isn't, the next message that gets saved will push it ahead again | |
549 | * until we get to the right date. | |
550 | */ | |
551 | s->start_today = _asl_start_today(); | |
552 | if (s->start_today == 0) s->start_today = s->start_tomorrow; | |
553 | ||
554 | s->start_tomorrow = s->start_today + SECONDS_PER_DAY; | |
b5d655f7 A |
555 | } |
556 | } | |
557 | ||
558 | val = asl_get(msg, ASL_KEY_READ_UID); | |
559 | ruid = -1; | |
560 | if (val != NULL) ruid = atoi(val); | |
561 | ||
562 | val = asl_get(msg, ASL_KEY_READ_GID); | |
563 | rgid = -1; | |
564 | if (val != NULL) rgid = atoi(val); | |
565 | ||
34e8f829 A |
566 | bb = 0; |
567 | val = asl_get(msg, ASL_KEY_EXPIRE_TIME); | |
568 | if (val != NULL) | |
569 | { | |
570 | bb = 1; | |
571 | msg_time = asl_parse_time(val); | |
572 | } | |
573 | ||
b5d655f7 A |
574 | if (fseeko(s->storedata, 0, SEEK_SET) != 0) return ASL_STATUS_WRITE_FAILED; |
575 | ||
576 | xid = asl_core_htonq(s->next_id); | |
577 | if (fwrite(&xid, sizeof(uint64_t), 1, s->storedata) != 1) return ASL_STATUS_WRITE_FAILED; | |
578 | ||
1f2f436a A |
579 | /* flush data */ |
580 | fflush(s->storedata); | |
581 | ||
b5d655f7 A |
582 | xid = s->next_id; |
583 | s->next_id++; | |
584 | ||
b5d655f7 | 585 | s->last_write = now; |
34e8f829 A |
586 | |
587 | if (localtime_r((const time_t *)&msg_time, &ctm) == NULL) return ASL_STATUS_FAILED; | |
588 | ||
589 | tstring = NULL; | |
590 | if (bb == 1) | |
b5d655f7 | 591 | { |
34e8f829 | 592 | /* |
1f2f436a | 593 | * This supports 12 monthly "Best Before" buckets. |
34e8f829 A |
594 | * We advance the actual expiry time to day zero of the following month. |
595 | * mktime() is clever enough to know that you actually mean the last day | |
596 | * of the previous month. What we get back from localtime is the last | |
597 | * day of the month in which the message expires, which we use in the name. | |
598 | */ | |
599 | ctm.tm_sec = 0; | |
600 | ctm.tm_min = 0; | |
601 | ctm.tm_hour = 0; | |
602 | ctm.tm_mday = 0; | |
603 | ctm.tm_mon += 1; | |
604 | ||
605 | bb = mktime(&ctm); | |
606 | ||
607 | if (localtime_r((const time_t *)&bb, &ctm) == NULL) return ASL_STATUS_FAILED; | |
608 | asprintf(&tstring, "BB.%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); | |
609 | } | |
610 | else | |
611 | { | |
612 | asprintf(&tstring, "%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); | |
b5d655f7 A |
613 | } |
614 | ||
34e8f829 | 615 | if (tstring == NULL) return ASL_STATUS_NO_MEMORY; |
b5d655f7 | 616 | |
34e8f829 A |
617 | status = asl_store_file_open_write(s, tstring, ruid, rgid, bb, &f, now, check_cache); |
618 | free(tstring); | |
619 | tstring = NULL; | |
b5d655f7 A |
620 | |
621 | if (status != ASL_STATUS_OK) return status; | |
622 | ||
623 | status = asl_file_save(f, msg, &xid); | |
624 | if (status != ASL_STATUS_OK) return status; | |
625 | ||
626 | fsize = asl_file_size(f); | |
627 | ftime = asl_file_ctime(f); | |
628 | ||
34e8f829 | 629 | /* if file is larger than max_file_size, rename it and touch semaphore file in the store */ |
b5d655f7 A |
630 | if ((s->max_file_size != 0) && (fsize > s->max_file_size)) |
631 | { | |
34e8f829 | 632 | signal_sweep = 1; |
b5d655f7 A |
633 | status = ASL_STATUS_OK; |
634 | ||
635 | path = asl_store_file_path(s, f); | |
b5d655f7 A |
636 | |
637 | asl_store_file_close(s, f); | |
638 | ||
639 | if (path != NULL) | |
640 | { | |
34e8f829 A |
641 | tmp_path = NULL; |
642 | ||
643 | len = strlen(path); | |
644 | if ((len >= 4) && (!strcmp(path + len - 4, ".asl"))) | |
645 | { | |
646 | /* rename xxxxxxx.asl to xxxxxxx.timestamp.asl */ | |
647 | scratch = strdup(path); | |
648 | if (scratch != NULL) | |
649 | { | |
650 | scratch[len - 4] = '\0'; | |
651 | asprintf(&tmp_path, "%s.%llu.asl", scratch, ftime); | |
652 | free(scratch); | |
653 | ||
654 | } | |
655 | } | |
656 | else | |
657 | { | |
658 | /* append timestamp */ | |
659 | asprintf(&tmp_path, "%s.%llu", path, ftime); | |
660 | } | |
661 | ||
662 | if (tmp_path == NULL) | |
b5d655f7 A |
663 | { |
664 | status = ASL_STATUS_NO_MEMORY; | |
665 | } | |
666 | else | |
667 | { | |
34e8f829 A |
668 | if (rename(path, tmp_path) != 0) status = ASL_STATUS_FAILED; |
669 | free(tmp_path); | |
b5d655f7 A |
670 | } |
671 | ||
672 | free(path); | |
673 | } | |
b5d655f7 A |
674 | } |
675 | ||
34e8f829 A |
676 | if (signal_sweep != 0) asl_store_signal_sweep(s); |
677 | ||
b5d655f7 A |
678 | return status; |
679 | } | |
680 | ||
1f2f436a A |
681 | static uint32_t |
682 | asl_store_mkdir(asl_store_t *s, const char *dir, mode_t m) | |
683 | { | |
684 | char *tstring = NULL; | |
685 | int status; | |
686 | struct stat sb; | |
687 | ||
688 | asprintf(&tstring, "%s/%s", s->base_dir, dir); | |
689 | if (tstring == NULL) return ASL_STATUS_NO_MEMORY; | |
690 | ||
691 | memset(&sb, 0, sizeof(struct stat)); | |
692 | status = stat(tstring, &sb); | |
693 | ||
694 | if (status == 0) | |
695 | { | |
696 | /* must be a directory */ | |
697 | if (!S_ISDIR(sb.st_mode)) | |
698 | { | |
699 | free(tstring); | |
700 | return ASL_STATUS_INVALID_STORE; | |
701 | } | |
702 | } | |
703 | else | |
704 | { | |
705 | if (errno == ENOENT) | |
706 | { | |
707 | /* doesn't exist - create it */ | |
708 | if (mkdir(tstring, m) != 0) | |
709 | { | |
710 | free(tstring); | |
711 | return ASL_STATUS_WRITE_FAILED; | |
712 | } | |
713 | } | |
714 | else | |
715 | { | |
716 | /* stat failed for some other reason */ | |
717 | free(tstring); | |
718 | return ASL_STATUS_FAILED; | |
719 | } | |
720 | } | |
721 | ||
722 | free(tstring); | |
723 | return ASL_STATUS_OK; | |
724 | } | |
725 | ||
726 | uint32_t | |
727 | asl_store_open_aux(asl_store_t *s, aslmsg msg, int *out_fd, char **url) | |
728 | { | |
729 | struct tm ctm; | |
730 | time_t msg_time, bb; | |
731 | char *path, *dir, *tstring; | |
732 | const char *val; | |
733 | uid_t ruid, u; | |
734 | gid_t rgid, g; | |
735 | mode_t m; | |
736 | uint32_t status; | |
737 | uint64_t fid; | |
738 | int fd; | |
739 | ||
740 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
741 | if (msg == NULL) return ASL_STATUS_INVALID_ARG; | |
742 | if (out_fd == NULL) return ASL_STATUS_INVALID_ARG; | |
743 | if (url == NULL) return ASL_STATUS_INVALID_ARG; | |
744 | ||
745 | msg_time = time(NULL); | |
746 | ||
747 | val = asl_get(msg, ASL_KEY_READ_UID); | |
748 | ruid = -1; | |
749 | if (val != NULL) ruid = atoi(val); | |
750 | ||
751 | val = asl_get(msg, ASL_KEY_READ_GID); | |
752 | rgid = -1; | |
753 | if (val != NULL) rgid = atoi(val); | |
754 | ||
755 | bb = 0; | |
756 | val = asl_get(msg, ASL_KEY_EXPIRE_TIME); | |
757 | if (val != NULL) | |
758 | { | |
759 | bb = 1; | |
760 | msg_time = asl_parse_time(val); | |
761 | } | |
762 | ||
763 | if (localtime_r((const time_t *)&msg_time, &ctm) == NULL) return ASL_STATUS_FAILED; | |
764 | ||
765 | dir = NULL; | |
766 | if (bb == 1) | |
767 | { | |
768 | /* | |
769 | * This supports 12 monthly "Best Before" buckets. | |
770 | * We advance the actual expiry time to day zero of the following month. | |
771 | * mktime() is clever enough to know that you actually mean the last day | |
772 | * of the previous month. What we get back from localtime is the last | |
773 | * day of the month in which the message expires, which we use in the name. | |
774 | */ | |
775 | ctm.tm_sec = 0; | |
776 | ctm.tm_min = 0; | |
777 | ctm.tm_hour = 0; | |
778 | ctm.tm_mday = 0; | |
779 | ctm.tm_mon += 1; | |
780 | ||
781 | bb = mktime(&ctm); | |
782 | ||
783 | if (localtime_r((const time_t *)&bb, &ctm) == NULL) return ASL_STATUS_FAILED; | |
784 | asprintf(&dir, "BB.AUX.%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); | |
785 | } | |
786 | else | |
787 | { | |
788 | asprintf(&dir, "AUX.%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); | |
789 | } | |
790 | ||
791 | if (dir == NULL) return ASL_STATUS_NO_MEMORY; | |
792 | ||
793 | status = asl_store_mkdir(s, dir, 0755); | |
794 | if (status != ASL_STATUS_OK) | |
795 | { | |
796 | free(dir); | |
797 | return status; | |
798 | } | |
799 | ||
800 | fid = s->next_id; | |
801 | s->next_id++; | |
802 | tstring = NULL; | |
803 | ||
804 | asprintf(&tstring, "%s/%llu", dir, fid); | |
805 | free(dir); | |
806 | if (tstring == NULL) return ASL_STATUS_NO_MEMORY; | |
807 | ||
808 | u = 0; | |
809 | g = 0; | |
810 | m = 0644; | |
811 | path = asl_store_make_ug_path(s->base_dir, tstring, NULL, ruid, rgid, &u, &g, &m); | |
812 | free(tstring); | |
813 | if (path == NULL) return ASL_STATUS_NO_MEMORY; | |
814 | ||
815 | fd = asl_file_create(path, u, g, m); | |
816 | if (fd < 0) | |
817 | { | |
818 | free(path); | |
819 | *out_fd = -1; | |
820 | return ASL_STATUS_WRITE_FAILED; | |
821 | } | |
822 | ||
823 | /* URL is file://<path> */ | |
824 | *url = NULL; | |
825 | asprintf(url, "file://%s", path); | |
826 | free(path); | |
827 | ||
828 | *out_fd = fd; | |
829 | ||
830 | return status; | |
831 | } | |
832 | ||
b5d655f7 A |
833 | uint32_t |
834 | asl_store_match_timeout(asl_store_t *s, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction, uint32_t usec) | |
835 | { | |
836 | DIR *dp; | |
837 | struct dirent *dent; | |
838 | uint32_t status; | |
839 | asl_file_t *f; | |
840 | char *path; | |
841 | asl_file_list_t *files; | |
842 | ||
843 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
844 | if (res == NULL) return ASL_STATUS_INVALID_ARG; | |
845 | ||
846 | files = NULL; | |
847 | ||
848 | /* | |
849 | * Open all readable files | |
850 | */ | |
851 | dp = opendir(s->base_dir); | |
852 | if (dp == NULL) return ASL_STATUS_READ_FAILED; | |
853 | ||
854 | while ((dent = readdir(dp)) != NULL) | |
855 | { | |
856 | if (dent->d_name[0] == '.') continue; | |
857 | ||
858 | path = NULL; | |
859 | asprintf(&path, "%s/%s", s->base_dir, dent->d_name); | |
860 | ||
861 | /* NB asl_file_open_read will fail if path is NULL, if the file is not an ASL store file, or if it isn't readable */ | |
862 | status = asl_file_open_read(path, &f); | |
863 | if (path != NULL) free(path); | |
864 | if ((status != ASL_STATUS_OK) || (f == NULL)) continue; | |
865 | ||
866 | files = asl_file_list_add(files, f); | |
867 | } | |
868 | ||
869 | closedir(dp); | |
870 | ||
871 | status = asl_file_list_match_timeout(files, query, res, last_id, start_id, count, direction, usec); | |
872 | asl_file_list_close(files); | |
873 | return status; | |
874 | } | |
875 | ||
876 | uint32_t | |
877 | asl_store_match(asl_store_t *s, aslresponse query, aslresponse *res, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction) | |
878 | { | |
879 | return asl_store_match_timeout(s, query, res, last_id, start_id, count, direction, 0); | |
880 | } | |
881 | ||
882 | uint32_t | |
883 | asl_store_match_start(asl_store_t *s, uint64_t start_id, int32_t direction) | |
884 | { | |
885 | DIR *dp; | |
886 | struct dirent *dent; | |
887 | uint32_t status; | |
888 | asl_file_t *f; | |
889 | char *path; | |
890 | asl_file_list_t *files; | |
891 | ||
892 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
893 | ||
894 | if (s->work != NULL) asl_file_list_match_end(s->work); | |
895 | s->work = NULL; | |
896 | ||
897 | files = NULL; | |
898 | ||
899 | /* | |
900 | * Open all readable files | |
901 | */ | |
902 | dp = opendir(s->base_dir); | |
903 | if (dp == NULL) return ASL_STATUS_READ_FAILED; | |
904 | ||
905 | while ((dent = readdir(dp)) != NULL) | |
906 | { | |
907 | if (dent->d_name[0] == '.') continue; | |
908 | ||
909 | path = NULL; | |
910 | asprintf(&path, "%s/%s", s->base_dir, dent->d_name); | |
911 | ||
912 | /* NB asl_file_open_read will fail if path is NULL, if the file is not an ASL store file, or if it isn't readable */ | |
913 | status = asl_file_open_read(path, &f); | |
914 | if (path != NULL) free(path); | |
915 | if ((status != ASL_STATUS_OK) || (f == NULL)) continue; | |
916 | ||
917 | files = asl_file_list_add(files, f); | |
918 | } | |
919 | ||
920 | closedir(dp); | |
921 | ||
922 | s->work = asl_file_list_match_start(files, start_id, direction); | |
923 | if (s->work == NULL) return ASL_STATUS_FAILED; | |
924 | ||
925 | return ASL_STATUS_OK; | |
926 | } | |
927 | ||
928 | uint32_t | |
929 | asl_store_match_next(asl_store_t *s, aslresponse query, aslresponse *res, uint32_t count) | |
930 | { | |
931 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
932 | if (s->work == NULL) return ASL_STATUS_OK; | |
933 | ||
934 | return asl_file_list_match_next(s->work, query, res, count); | |
935 | } |