]>
Commit | Line | Data |
---|---|---|
b5d655f7 | 1 | /* |
34e8f829 | 2 | * Copyright (c) 2007-2009 Apple Inc. All rights reserved. |
b5d655f7 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * "Portions Copyright (c) 2007 Apple Inc. All Rights | |
7 | * Reserved. This file contains Original Code and/or Modifications of | |
8 | * Original Code as defined in and that are subject to the Apple Public | |
9 | * Source License Version 1.0 (the 'License'). You may not use this file | |
10 | * except in compliance with the License. Please obtain a copy of the | |
11 | * License at http://www.apple.com/publicsource and read it before using | |
12 | * this file. | |
13 | * | |
14 | * The Original Code and all software distributed under the License are | |
15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the | |
19 | * License for the specific language governing rights and limitations | |
20 | * under the License." | |
21 | * | |
22 | * @APPLE_LICENSE_HEADER_END@ | |
23 | */ | |
24 | ||
25 | #include <stdlib.h> | |
26 | #include <unistd.h> | |
27 | #include <string.h> | |
28 | #include <errno.h> | |
29 | #include <dirent.h> | |
30 | #include <sys/types.h> | |
31 | #include <sys/stat.h> | |
32 | #include <fcntl.h> | |
33 | #include <asl.h> | |
34 | #include <asl_private.h> | |
35 | #include <asl_core.h> | |
36 | #include <asl_store.h> | |
37 | #include <notify.h> | |
38 | ||
39 | extern time_t asl_parse_time(const char *str); | |
40 | extern uint64_t asl_file_cursor(asl_file_t *s); | |
41 | extern uint32_t asl_file_match_start(asl_file_t *s, uint64_t start_id, int32_t direction); | |
42 | 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); | |
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; | |
109 | if ((sb.st_mode & S_IFDIR) == 0) return ASL_STATUS_INVALID_STORE; | |
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 | } | |
b5d655f7 A |
147 | } |
148 | else | |
149 | { | |
150 | sd = fopen(path, "r+"); | |
151 | free(path); | |
152 | ||
153 | if (sd == NULL) return ASL_STATUS_FAILED; | |
154 | if (fread(&last_id, sizeof(uint64_t), 1, sd) != 1) | |
155 | { | |
156 | fclose(sd); | |
157 | return ASL_STATUS_READ_FAILED; | |
158 | } | |
159 | ||
160 | last_id = asl_core_ntohq(last_id); | |
161 | } | |
162 | ||
b5d655f7 A |
163 | out = (asl_store_t *)calloc(1, sizeof(asl_store_t)); |
164 | if (out == NULL) | |
165 | { | |
166 | fclose(sd); | |
b5d655f7 A |
167 | return ASL_STATUS_NO_MEMORY; |
168 | } | |
169 | ||
170 | if (basedir == NULL) out->base_dir = strdup(PATH_ASL_STORE); | |
171 | else out->base_dir = strdup(basedir); | |
172 | ||
173 | if (out->base_dir == NULL) | |
174 | { | |
175 | fclose(sd); | |
b5d655f7 A |
176 | free(out); |
177 | return ASL_STATUS_NO_MEMORY; | |
178 | } | |
179 | ||
34e8f829 | 180 | out->start_today = start; |
b5d655f7 | 181 | out->start_tomorrow = out->start_today + SECONDS_PER_DAY; |
b5d655f7 A |
182 | out->storedata = sd; |
183 | out->next_id = last_id + 1; | |
184 | ||
185 | for (i = 0; i < FILE_CACHE_SIZE; i++) | |
186 | { | |
187 | memset(&out->file_cache[i], 0, sizeof(asl_cached_file_t)); | |
188 | out->file_cache[i].u = -1; | |
189 | out->file_cache[i].g = -1; | |
190 | } | |
191 | ||
192 | *s = out; | |
193 | return ASL_STATUS_OK; | |
194 | } | |
195 | ||
196 | uint32_t | |
197 | asl_store_statistics(asl_store_t *s, aslmsg *msg) | |
198 | { | |
199 | aslmsg out; | |
b5d655f7 A |
200 | |
201 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
202 | if (msg == NULL) return ASL_STATUS_INVALID_ARG; | |
203 | ||
204 | out = (aslmsg)calloc(1, sizeof(asl_msg_t)); | |
205 | if (out == NULL) return ASL_STATUS_NO_MEMORY; | |
206 | ||
34e8f829 | 207 | /* does nothing for now */ |
b5d655f7 A |
208 | |
209 | *msg = out; | |
210 | return ASL_STATUS_OK; | |
211 | } | |
212 | ||
213 | uint32_t | |
214 | asl_store_open_read(const char *basedir, asl_store_t **s) | |
215 | { | |
216 | asl_store_t *out; | |
217 | struct stat sb; | |
218 | ||
219 | if (s == NULL) return ASL_STATUS_INVALID_ARG; | |
220 | ||
221 | if (basedir == NULL) basedir = PATH_ASL_STORE; | |
222 | ||
223 | memset(&sb, 0, sizeof(struct stat)); | |
224 | if (stat(basedir, &sb) != 0) return ASL_STATUS_INVALID_STORE; | |
225 | if ((sb.st_mode & S_IFDIR) == 0) return ASL_STATUS_INVALID_STORE; | |
226 | ||
227 | out = (asl_store_t *)calloc(1, sizeof(asl_store_t)); | |
228 | if (out == NULL) return ASL_STATUS_NO_MEMORY; | |
229 | ||
230 | if (basedir == NULL) out->base_dir = strdup(PATH_ASL_STORE); | |
231 | else out->base_dir = strdup(basedir); | |
232 | ||
233 | if (out->base_dir == NULL) | |
234 | { | |
235 | free(out); | |
236 | return ASL_STATUS_NO_MEMORY; | |
237 | } | |
238 | ||
239 | *s = out; | |
240 | return ASL_STATUS_OK; | |
241 | } | |
242 | ||
243 | uint32_t | |
244 | asl_store_max_file_size(asl_store_t *s, size_t max) | |
245 | { | |
246 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
247 | ||
248 | s->max_file_size = max; | |
249 | return ASL_STATUS_OK; | |
250 | } | |
251 | ||
252 | void | |
253 | asl_store_file_closeall(asl_store_t *s) | |
254 | { | |
255 | uint32_t i; | |
256 | ||
257 | if (s == NULL) return; | |
258 | ||
259 | for (i = 0; i < FILE_CACHE_SIZE; i++) | |
260 | { | |
261 | if (s->file_cache[i].f != NULL) asl_file_close(s->file_cache[i].f); | |
262 | s->file_cache[i].f = NULL; | |
263 | if (s->file_cache[i].path != NULL) free(s->file_cache[i].path); | |
264 | s->file_cache[i].path = NULL; | |
265 | s->file_cache[i].u = -1; | |
266 | s->file_cache[i].g = -1; | |
34e8f829 | 267 | s->file_cache[i].bb = 0; |
b5d655f7 A |
268 | s->file_cache[i].ts = 0; |
269 | } | |
270 | } | |
271 | ||
272 | uint32_t | |
273 | asl_store_close(asl_store_t *s) | |
274 | { | |
275 | if (s == NULL) return ASL_STATUS_OK; | |
276 | ||
277 | if (s->base_dir != NULL) free(s->base_dir); | |
278 | s->base_dir = NULL; | |
b5d655f7 A |
279 | asl_store_file_closeall(s); |
280 | if (s->storedata != NULL) fclose(s->storedata); | |
281 | ||
282 | free(s); | |
283 | ||
284 | return ASL_STATUS_OK; | |
285 | } | |
286 | ||
287 | uint32_t | |
288 | asl_store_signal_sweep(asl_store_t *s) | |
289 | { | |
290 | char *str; | |
291 | int semfd; | |
34e8f829 A |
292 | uint64_t xid; |
293 | uint32_t status; | |
b5d655f7 A |
294 | |
295 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
296 | ||
297 | asprintf(&str, "%s/%s", s->base_dir, FILE_ASL_STORE_SWEEP_SEMAPHORE); | |
298 | if (str == NULL) return ASL_STATUS_NO_MEMORY; | |
299 | ||
300 | semfd = open(str, O_WRONLY | O_CREAT | O_NONBLOCK, 0644); | |
301 | free(str); | |
302 | ||
303 | if (semfd < 0) return ASL_STATUS_WRITE_FAILED; | |
304 | ||
34e8f829 A |
305 | status = ASL_STATUS_OK; |
306 | ||
307 | /* write the current message ID in the SweepStore file */ | |
308 | xid = asl_core_htonq(s->next_id - 1); | |
309 | if (write(semfd, &xid, sizeof(uint64_t)) != sizeof(uint64_t)) status = ASL_STATUS_WRITE_FAILED; | |
310 | ||
b5d655f7 | 311 | close(semfd); |
34e8f829 | 312 | return status; |
b5d655f7 A |
313 | } |
314 | ||
315 | /* | |
316 | * Sweep the file cache. | |
317 | * Close any files that have not been used in the last FILE_CACHE_TTL seconds. | |
318 | * Returns least recently used or unused cache slot. | |
319 | */ | |
320 | static uint32_t | |
34e8f829 | 321 | asl_store_file_cache_lru(asl_store_t *s, time_t now, uint32_t ignorex) |
b5d655f7 A |
322 | { |
323 | time_t min; | |
324 | uint32_t i, x; | |
325 | ||
326 | if (s == NULL) return 0; | |
327 | ||
328 | x = 0; | |
329 | min = now - FILE_CACHE_TTL; | |
34e8f829 | 330 | |
b5d655f7 A |
331 | for (i = 0; i < FILE_CACHE_SIZE; i++) |
332 | { | |
34e8f829 | 333 | if ((i != ignorex) && (s->file_cache[i].ts < min)) |
b5d655f7 A |
334 | { |
335 | asl_file_close(s->file_cache[i].f); | |
336 | s->file_cache[i].f = NULL; | |
337 | if (s->file_cache[i].path != NULL) free(s->file_cache[i].path); | |
338 | s->file_cache[i].path = NULL; | |
339 | s->file_cache[i].u = -1; | |
340 | s->file_cache[i].g = -1; | |
34e8f829 A |
341 | s->file_cache[i].bb = 0; |
342 | s->file_cache[i].ts = 0; | |
b5d655f7 | 343 | } |
34e8f829 | 344 | |
b5d655f7 A |
345 | if (s->file_cache[i].ts < s->file_cache[x].ts) x = i; |
346 | } | |
347 | ||
348 | return x; | |
349 | } | |
350 | ||
351 | uint32_t | |
352 | asl_store_sweep_file_cache(asl_store_t *s) | |
353 | { | |
354 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
355 | ||
34e8f829 | 356 | asl_store_file_cache_lru(s, time(NULL), FILE_CACHE_SIZE); |
b5d655f7 A |
357 | return ASL_STATUS_OK; |
358 | } | |
359 | ||
360 | static uint32_t | |
34e8f829 | 361 | 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 |
362 | { |
363 | char *path; | |
364 | mode_t m; | |
365 | int32_t i, x, u, g; | |
366 | uint32_t status; | |
367 | asl_file_t *out; | |
368 | ||
369 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
370 | ||
371 | /* see if the file is already open and in the cache */ | |
372 | for (i = 0; i < FILE_CACHE_SIZE; i++) | |
373 | { | |
34e8f829 | 374 | 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 |
375 | { |
376 | s->file_cache[i].ts = now; | |
377 | *f = s->file_cache[i].f; | |
34e8f829 | 378 | if (check_cache == 1) asl_store_file_cache_lru(s, now, i); |
b5d655f7 A |
379 | return ASL_STATUS_OK; |
380 | } | |
381 | } | |
382 | ||
383 | path = NULL; | |
384 | u = 0; | |
385 | g = 0; | |
386 | m = 0644; | |
387 | ||
388 | if (ruid == -1) | |
389 | { | |
390 | if (rgid == -1) | |
391 | { | |
34e8f829 | 392 | asprintf(&path, "%s/%s.asl", s->base_dir, tstring); |
b5d655f7 A |
393 | } |
394 | else | |
395 | { | |
396 | g = rgid; | |
397 | m = 0640; | |
34e8f829 | 398 | asprintf(&path, "%s/%s.G%d.asl", s->base_dir, tstring, g); |
b5d655f7 A |
399 | } |
400 | } | |
401 | else | |
402 | { | |
403 | u = ruid; | |
404 | if (rgid == -1) | |
405 | { | |
406 | m = 0600; | |
34e8f829 | 407 | asprintf(&path, "%s/%s.U%d.asl", s->base_dir, tstring, u); |
b5d655f7 A |
408 | } |
409 | else | |
410 | { | |
411 | g = rgid; | |
412 | m = 0640; | |
34e8f829 | 413 | asprintf(&path, "%s/%s.U%d.G%u.asl", s->base_dir, tstring, u, g); |
b5d655f7 A |
414 | } |
415 | } | |
416 | ||
417 | if (path == NULL) return ASL_STATUS_NO_MEMORY; | |
418 | ||
419 | out = NULL; | |
420 | status = asl_file_open_write(path, m, u, g, &out); | |
421 | if (status != ASL_STATUS_OK) | |
422 | { | |
423 | free(path); | |
424 | return status; | |
425 | } | |
426 | ||
34e8f829 | 427 | x = asl_store_file_cache_lru(s, now, FILE_CACHE_SIZE); |
b5d655f7 A |
428 | if (s->file_cache[x].f != NULL) asl_file_close(s->file_cache[x].f); |
429 | if (s->file_cache[x].path != NULL) free(s->file_cache[x].path); | |
430 | ||
431 | s->file_cache[x].f = out; | |
432 | s->file_cache[x].path = path; | |
433 | s->file_cache[x].u = ruid; | |
434 | s->file_cache[x].g = rgid; | |
34e8f829 | 435 | s->file_cache[x].bb = bb; |
b5d655f7 A |
436 | s->file_cache[x].ts = time(NULL); |
437 | ||
438 | *f = out; | |
439 | ||
440 | return ASL_STATUS_OK; | |
441 | } | |
442 | ||
443 | char * | |
444 | asl_store_file_path(asl_store_t *s, asl_file_t *f) | |
445 | { | |
446 | uint32_t i; | |
447 | ||
448 | if (s == NULL) return NULL; | |
449 | ||
450 | for (i = 0; i < FILE_CACHE_SIZE; i++) | |
451 | { | |
452 | if (s->file_cache[i].f == f) | |
453 | { | |
454 | if (s->file_cache[i].path == NULL) return NULL; | |
455 | return strdup(s->file_cache[i].path); | |
456 | } | |
457 | } | |
458 | ||
459 | return NULL; | |
460 | } | |
461 | ||
462 | void | |
463 | asl_store_file_close(asl_store_t *s, asl_file_t *f) | |
464 | { | |
465 | uint32_t i; | |
466 | ||
467 | if (s == NULL) return; | |
34e8f829 | 468 | if (f == NULL) return; |
b5d655f7 A |
469 | |
470 | for (i = 0; i < FILE_CACHE_SIZE; i++) | |
471 | { | |
472 | if (s->file_cache[i].f == f) | |
473 | { | |
474 | asl_file_close(s->file_cache[i].f); | |
475 | s->file_cache[i].f = NULL; | |
476 | if (s->file_cache[i].path != NULL) free(s->file_cache[i].path); | |
477 | s->file_cache[i].path = NULL; | |
478 | s->file_cache[i].u = -1; | |
479 | s->file_cache[i].g = -1; | |
34e8f829 | 480 | s->file_cache[i].bb = 0; |
b5d655f7 A |
481 | s->file_cache[i].ts = 0; |
482 | return; | |
483 | } | |
484 | } | |
485 | } | |
486 | ||
487 | uint32_t | |
488 | asl_store_save(asl_store_t *s, aslmsg msg) | |
489 | { | |
490 | struct tm ctm; | |
34e8f829 A |
491 | time_t msg_time, now, bb; |
492 | char *path, *tmp_path, *tstring, *scratch; | |
b5d655f7 A |
493 | const char *val; |
494 | uid_t ruid; | |
495 | gid_t rgid; | |
496 | asl_file_t *f; | |
34e8f829 | 497 | uint32_t status, check_cache, signal_sweep, len; |
b5d655f7 A |
498 | uint64_t xid, ftime; |
499 | size_t fsize; | |
500 | ||
501 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
502 | if (msg == NULL) return ASL_STATUS_INVALID_ARG; | |
503 | ||
504 | now = time(NULL); | |
505 | ||
34e8f829 A |
506 | check_cache = 0; |
507 | if ((s->last_write + FILE_CACHE_TTL) <= now) check_cache = 1; | |
508 | ||
509 | signal_sweep = 0; | |
510 | ||
511 | msg_time = 0; | |
b5d655f7 | 512 | val = asl_get(msg, ASL_KEY_TIME); |
34e8f829 A |
513 | if (val == NULL) msg_time = now; |
514 | else msg_time = asl_parse_time(val); | |
b5d655f7 | 515 | |
34e8f829 | 516 | if (msg_time >= s->start_tomorrow) |
b5d655f7 A |
517 | { |
518 | if (now >= s->start_tomorrow) | |
519 | { | |
520 | /* new day begins */ | |
34e8f829 A |
521 | check_cache = 0; |
522 | signal_sweep = 1; | |
523 | asl_store_file_closeall(s); | |
524 | ||
525 | /* | |
526 | * _asl_start_today should never fail, but if it does, | |
527 | * just push forward one day. That will probably be correct, and if | |
528 | * it isn't, the next message that gets saved will push it ahead again | |
529 | * until we get to the right date. | |
530 | */ | |
531 | s->start_today = _asl_start_today(); | |
532 | if (s->start_today == 0) s->start_today = s->start_tomorrow; | |
533 | ||
534 | s->start_tomorrow = s->start_today + SECONDS_PER_DAY; | |
b5d655f7 A |
535 | } |
536 | } | |
537 | ||
538 | val = asl_get(msg, ASL_KEY_READ_UID); | |
539 | ruid = -1; | |
540 | if (val != NULL) ruid = atoi(val); | |
541 | ||
542 | val = asl_get(msg, ASL_KEY_READ_GID); | |
543 | rgid = -1; | |
544 | if (val != NULL) rgid = atoi(val); | |
545 | ||
34e8f829 A |
546 | bb = 0; |
547 | val = asl_get(msg, ASL_KEY_EXPIRE_TIME); | |
548 | if (val != NULL) | |
549 | { | |
550 | bb = 1; | |
551 | msg_time = asl_parse_time(val); | |
552 | } | |
553 | ||
b5d655f7 A |
554 | if (fseeko(s->storedata, 0, SEEK_SET) != 0) return ASL_STATUS_WRITE_FAILED; |
555 | ||
556 | xid = asl_core_htonq(s->next_id); | |
557 | if (fwrite(&xid, sizeof(uint64_t), 1, s->storedata) != 1) return ASL_STATUS_WRITE_FAILED; | |
558 | ||
559 | xid = s->next_id; | |
560 | s->next_id++; | |
561 | ||
b5d655f7 | 562 | s->last_write = now; |
34e8f829 A |
563 | |
564 | if (localtime_r((const time_t *)&msg_time, &ctm) == NULL) return ASL_STATUS_FAILED; | |
565 | ||
566 | tstring = NULL; | |
567 | if (bb == 1) | |
b5d655f7 | 568 | { |
34e8f829 A |
569 | /* |
570 | * This supports 12 monthy "Best Before" buckets. | |
571 | * We advance the actual expiry time to day zero of the following month. | |
572 | * mktime() is clever enough to know that you actually mean the last day | |
573 | * of the previous month. What we get back from localtime is the last | |
574 | * day of the month in which the message expires, which we use in the name. | |
575 | */ | |
576 | ctm.tm_sec = 0; | |
577 | ctm.tm_min = 0; | |
578 | ctm.tm_hour = 0; | |
579 | ctm.tm_mday = 0; | |
580 | ctm.tm_mon += 1; | |
581 | ||
582 | bb = mktime(&ctm); | |
583 | ||
584 | if (localtime_r((const time_t *)&bb, &ctm) == NULL) return ASL_STATUS_FAILED; | |
585 | asprintf(&tstring, "BB.%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); | |
586 | } | |
587 | else | |
588 | { | |
589 | asprintf(&tstring, "%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); | |
b5d655f7 A |
590 | } |
591 | ||
34e8f829 | 592 | if (tstring == NULL) return ASL_STATUS_NO_MEMORY; |
b5d655f7 | 593 | |
34e8f829 A |
594 | status = asl_store_file_open_write(s, tstring, ruid, rgid, bb, &f, now, check_cache); |
595 | free(tstring); | |
596 | tstring = NULL; | |
b5d655f7 A |
597 | |
598 | if (status != ASL_STATUS_OK) return status; | |
599 | ||
600 | status = asl_file_save(f, msg, &xid); | |
601 | if (status != ASL_STATUS_OK) return status; | |
602 | ||
603 | fsize = asl_file_size(f); | |
604 | ftime = asl_file_ctime(f); | |
605 | ||
34e8f829 | 606 | /* if file is larger than max_file_size, rename it and touch semaphore file in the store */ |
b5d655f7 A |
607 | if ((s->max_file_size != 0) && (fsize > s->max_file_size)) |
608 | { | |
34e8f829 | 609 | signal_sweep = 1; |
b5d655f7 A |
610 | status = ASL_STATUS_OK; |
611 | ||
612 | path = asl_store_file_path(s, f); | |
b5d655f7 A |
613 | |
614 | asl_store_file_close(s, f); | |
615 | ||
616 | if (path != NULL) | |
617 | { | |
34e8f829 A |
618 | tmp_path = NULL; |
619 | ||
620 | len = strlen(path); | |
621 | if ((len >= 4) && (!strcmp(path + len - 4, ".asl"))) | |
622 | { | |
623 | /* rename xxxxxxx.asl to xxxxxxx.timestamp.asl */ | |
624 | scratch = strdup(path); | |
625 | if (scratch != NULL) | |
626 | { | |
627 | scratch[len - 4] = '\0'; | |
628 | asprintf(&tmp_path, "%s.%llu.asl", scratch, ftime); | |
629 | free(scratch); | |
630 | ||
631 | } | |
632 | } | |
633 | else | |
634 | { | |
635 | /* append timestamp */ | |
636 | asprintf(&tmp_path, "%s.%llu", path, ftime); | |
637 | } | |
638 | ||
639 | if (tmp_path == NULL) | |
b5d655f7 A |
640 | { |
641 | status = ASL_STATUS_NO_MEMORY; | |
642 | } | |
643 | else | |
644 | { | |
34e8f829 A |
645 | if (rename(path, tmp_path) != 0) status = ASL_STATUS_FAILED; |
646 | free(tmp_path); | |
b5d655f7 A |
647 | } |
648 | ||
649 | free(path); | |
650 | } | |
b5d655f7 A |
651 | } |
652 | ||
34e8f829 A |
653 | if (signal_sweep != 0) asl_store_signal_sweep(s); |
654 | ||
b5d655f7 A |
655 | return status; |
656 | } | |
657 | ||
658 | uint32_t | |
659 | 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) | |
660 | { | |
661 | DIR *dp; | |
662 | struct dirent *dent; | |
663 | uint32_t status; | |
664 | asl_file_t *f; | |
665 | char *path; | |
666 | asl_file_list_t *files; | |
667 | ||
668 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
669 | if (res == NULL) return ASL_STATUS_INVALID_ARG; | |
670 | ||
671 | files = NULL; | |
672 | ||
673 | /* | |
674 | * Open all readable files | |
675 | */ | |
676 | dp = opendir(s->base_dir); | |
677 | if (dp == NULL) return ASL_STATUS_READ_FAILED; | |
678 | ||
679 | while ((dent = readdir(dp)) != NULL) | |
680 | { | |
681 | if (dent->d_name[0] == '.') continue; | |
682 | ||
683 | path = NULL; | |
684 | asprintf(&path, "%s/%s", s->base_dir, dent->d_name); | |
685 | ||
686 | /* 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 */ | |
687 | status = asl_file_open_read(path, &f); | |
688 | if (path != NULL) free(path); | |
689 | if ((status != ASL_STATUS_OK) || (f == NULL)) continue; | |
690 | ||
691 | files = asl_file_list_add(files, f); | |
692 | } | |
693 | ||
694 | closedir(dp); | |
695 | ||
696 | status = asl_file_list_match_timeout(files, query, res, last_id, start_id, count, direction, usec); | |
697 | asl_file_list_close(files); | |
698 | return status; | |
699 | } | |
700 | ||
701 | uint32_t | |
702 | 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) | |
703 | { | |
704 | return asl_store_match_timeout(s, query, res, last_id, start_id, count, direction, 0); | |
705 | } | |
706 | ||
707 | uint32_t | |
708 | asl_store_match_start(asl_store_t *s, uint64_t start_id, int32_t direction) | |
709 | { | |
710 | DIR *dp; | |
711 | struct dirent *dent; | |
712 | uint32_t status; | |
713 | asl_file_t *f; | |
714 | char *path; | |
715 | asl_file_list_t *files; | |
716 | ||
717 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
718 | ||
719 | if (s->work != NULL) asl_file_list_match_end(s->work); | |
720 | s->work = NULL; | |
721 | ||
722 | files = NULL; | |
723 | ||
724 | /* | |
725 | * Open all readable files | |
726 | */ | |
727 | dp = opendir(s->base_dir); | |
728 | if (dp == NULL) return ASL_STATUS_READ_FAILED; | |
729 | ||
730 | while ((dent = readdir(dp)) != NULL) | |
731 | { | |
732 | if (dent->d_name[0] == '.') continue; | |
733 | ||
734 | path = NULL; | |
735 | asprintf(&path, "%s/%s", s->base_dir, dent->d_name); | |
736 | ||
737 | /* 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 */ | |
738 | status = asl_file_open_read(path, &f); | |
739 | if (path != NULL) free(path); | |
740 | if ((status != ASL_STATUS_OK) || (f == NULL)) continue; | |
741 | ||
742 | files = asl_file_list_add(files, f); | |
743 | } | |
744 | ||
745 | closedir(dp); | |
746 | ||
747 | s->work = asl_file_list_match_start(files, start_id, direction); | |
748 | if (s->work == NULL) return ASL_STATUS_FAILED; | |
749 | ||
750 | return ASL_STATUS_OK; | |
751 | } | |
752 | ||
753 | uint32_t | |
754 | asl_store_match_next(asl_store_t *s, aslresponse query, aslresponse *res, uint32_t count) | |
755 | { | |
756 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
757 | if (s->work == NULL) return ASL_STATUS_OK; | |
758 | ||
759 | return asl_file_list_match_next(s->work, query, res, count); | |
760 | } |