]> git.saurik.com Git - apple/libc.git/blame - gen/asl_store.c
Libc-763.13.tar.gz
[apple/libc.git] / gen / asl_store.c
CommitLineData
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
38extern time_t asl_parse_time(const char *str);
39extern uint64_t asl_file_cursor(asl_file_t *s);
40extern uint32_t asl_file_match_start(asl_file_t *s, uint64_t start_id, int32_t direction);
41extern 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 42extern 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
64static 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 */
89uint32_t
90asl_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
199uint32_t
200asl_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
216uint32_t
217asl_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
246uint32_t
247asl_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
256asl_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
275uint32_t
276asl_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
290uint32_t
291asl_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 */
323static uint32_t
34e8f829 324asl_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
354uint32_t
355asl_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
363static char *
364asl_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 408static uint32_t
34e8f829 409asl_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
464asl_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
483asl_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
507uint32_t
508asl_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
681static uint32_t
682asl_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
726uint32_t
727asl_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
833uint32_t
834asl_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
876uint32_t
877asl_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
882uint32_t
883asl_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
928uint32_t
929asl_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}