]>
Commit | Line | Data |
---|---|---|
81582353 A |
1 | /* |
2 | * Copyright (c) 2007-2011 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 <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 | ||
81582353 A |
38 | extern uint64_t asl_file_cursor(asl_file_t *s); |
39 | extern uint32_t asl_file_match_start(asl_file_t *s, uint64_t start_id, int32_t direction); | |
f3df4c03 | 40 | extern uint32_t asl_file_match_next(asl_file_t *s, asl_msg_list_t *qlist, asl_msg_t **msg, uint64_t *last_id, int32_t direction, int32_t ruid, int32_t rgid); |
81582353 A |
41 | extern int asl_file_create(const char *path, uid_t uid, gid_t gid, mode_t mode); |
42 | ||
43 | #define SECONDS_PER_DAY 86400 | |
44 | ||
45 | /* | |
46 | * The ASL Store is organized as a set of files in a common directory. | |
47 | * Files are prefixed by the date (YYYY.MM.DD) of their contents. | |
48 | * | |
49 | * Messages with no access controls are saved in YYYY.MM.DD.asl | |
50 | * Messages with access limited to UID uuu are saved in YYYY.MM.DD.Uuuu.asl | |
51 | * Messages with access limited to GID ggg are saved in YYYY.MM.DD.Gggg.asl | |
52 | * Messages with access limited to UID uuu and GID ggg are saved in YYYY.MM.DD.Uuuu.Gggg.asl | |
53 | * | |
54 | * Messages that have a value for ASLExpireTime are saved in BB.YYYY.MM.DD.asl | |
55 | * where the timestamp is the "Best Before" date of the file. Access controls | |
56 | * are implemented as above with Uuuu and Gggg in the file name. Note that the | |
57 | * Best Before files are for the last day of the month, so a single file contains | |
58 | * messages that expire in that month. | |
59 | * | |
60 | * An external tool runs daily and deletes "old" files. | |
61 | */ | |
62 | ||
63 | static time_t | |
64 | _asl_start_today() | |
65 | { | |
66 | time_t now; | |
67 | struct tm ctm; | |
68 | ||
69 | memset(&ctm, 0, sizeof(struct tm)); | |
70 | now = time(NULL); | |
71 | ||
72 | if (localtime_r((const time_t *)&now, &ctm) == NULL) return 0; | |
73 | ||
74 | ctm.tm_sec = 0; | |
75 | ctm.tm_min = 0; | |
76 | ctm.tm_hour = 0; | |
77 | ||
78 | return mktime(&ctm); | |
79 | } | |
80 | ||
81 | /* | |
82 | * The base directory contains a data file which stores | |
83 | * the last record ID. | |
84 | * | |
85 | * | MAX_ID (uint64_t) | | |
86 | * | |
87 | */ | |
f3df4c03 | 88 | ASL_STATUS |
81582353 A |
89 | asl_store_open_write(const char *basedir, asl_store_t **s) |
90 | { | |
91 | asl_store_t *out; | |
92 | struct stat sb; | |
93 | uint32_t i, flags; | |
5222c21d | 94 | char path[MAXPATHLEN]; |
81582353 A |
95 | FILE *sd; |
96 | uint64_t last_id; | |
97 | time_t start; | |
98 | ||
99 | if (s == NULL) return ASL_STATUS_INVALID_ARG; | |
100 | ||
101 | start = _asl_start_today(); | |
102 | if (start == 0) return ASL_STATUS_FAILED; | |
103 | ||
104 | if (basedir == NULL) basedir = PATH_ASL_STORE; | |
105 | ||
106 | memset(&sb, 0, sizeof(struct stat)); | |
5222c21d A |
107 | if (stat(basedir, &sb) != 0) |
108 | { | |
109 | if (errno != ENOENT) return ASL_STATUS_INVALID_STORE; | |
110 | if (mkdir(basedir, 0755) != 0) return ASL_STATUS_WRITE_FAILED; | |
111 | } | |
112 | else | |
113 | { | |
114 | if (!S_ISDIR(sb.st_mode)) return ASL_STATUS_INVALID_STORE; | |
115 | } | |
116 | ||
117 | snprintf(path, sizeof(path), "%s/%s", basedir, FILE_ASL_STORE_DATA); | |
81582353 A |
118 | |
119 | sd = NULL; | |
120 | ||
121 | memset(&sb, 0, sizeof(struct stat)); | |
122 | if (stat(path, &sb) != 0) | |
123 | { | |
5222c21d | 124 | if (errno != ENOENT) return ASL_STATUS_FAILED; |
81582353 A |
125 | |
126 | sd = fopen(path, "w+"); | |
81582353 A |
127 | |
128 | if (sd == NULL) return ASL_STATUS_FAILED; | |
129 | ||
130 | last_id = 0; | |
131 | ||
132 | /* Create new StoreData file (8 bytes ID + 4 bytes flags) */ | |
133 | ||
134 | if (fwrite(&last_id, sizeof(uint64_t), 1, sd) != 1) | |
135 | { | |
136 | fclose(sd); | |
137 | return ASL_STATUS_WRITE_FAILED; | |
138 | } | |
139 | ||
140 | flags = 0; | |
141 | if (fwrite(&flags, sizeof(uint32_t), 1, sd) != 1) | |
142 | { | |
143 | fclose(sd); | |
144 | return ASL_STATUS_WRITE_FAILED; | |
145 | } | |
146 | ||
147 | /* flush data */ | |
148 | fflush(sd); | |
149 | } | |
150 | else | |
151 | { | |
152 | sd = fopen(path, "r+"); | |
81582353 A |
153 | |
154 | if (sd == NULL) return ASL_STATUS_FAILED; | |
155 | if (fread(&last_id, sizeof(uint64_t), 1, sd) != 1) | |
156 | { | |
157 | fclose(sd); | |
158 | return ASL_STATUS_READ_FAILED; | |
159 | } | |
160 | ||
161 | last_id = asl_core_ntohq(last_id); | |
162 | } | |
163 | ||
164 | out = (asl_store_t *)calloc(1, sizeof(asl_store_t)); | |
165 | if (out == NULL) | |
166 | { | |
167 | fclose(sd); | |
168 | return ASL_STATUS_NO_MEMORY; | |
169 | } | |
170 | ||
f3df4c03 A |
171 | out->asl_type = ASL_TYPE_STORE; |
172 | out->refcount = 1; | |
173 | ||
81582353 A |
174 | if (basedir == NULL) out->base_dir = strdup(PATH_ASL_STORE); |
175 | else out->base_dir = strdup(basedir); | |
176 | ||
177 | if (out->base_dir == NULL) | |
178 | { | |
179 | fclose(sd); | |
180 | free(out); | |
181 | return ASL_STATUS_NO_MEMORY; | |
182 | } | |
183 | ||
184 | out->start_today = start; | |
185 | out->start_tomorrow = out->start_today + SECONDS_PER_DAY; | |
186 | out->storedata = sd; | |
187 | out->next_id = last_id + 1; | |
188 | ||
189 | for (i = 0; i < FILE_CACHE_SIZE; i++) | |
190 | { | |
191 | memset(&out->file_cache[i], 0, sizeof(asl_cached_file_t)); | |
192 | out->file_cache[i].u = -1; | |
193 | out->file_cache[i].g = -1; | |
194 | } | |
195 | ||
196 | *s = out; | |
197 | return ASL_STATUS_OK; | |
198 | } | |
199 | ||
200 | uint32_t | |
f3df4c03 | 201 | asl_store_set_flags(asl_store_t *s, uint32_t flags) |
81582353 | 202 | { |
f3df4c03 A |
203 | if (s == NULL) return 0; |
204 | uint32_t oldflags = s->flags; | |
205 | s->flags = flags; | |
206 | return oldflags; | |
207 | } | |
208 | ||
209 | ASL_STATUS | |
210 | asl_store_statistics(asl_store_t *s, asl_msg_t **msg) | |
211 | { | |
212 | asl_msg_t *out; | |
81582353 A |
213 | |
214 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
215 | if (msg == NULL) return ASL_STATUS_INVALID_ARG; | |
216 | ||
f3df4c03 | 217 | out = asl_msg_new(ASL_TYPE_MSG); |
81582353 A |
218 | if (out == NULL) return ASL_STATUS_NO_MEMORY; |
219 | ||
220 | /* does nothing for now */ | |
221 | ||
222 | *msg = out; | |
223 | return ASL_STATUS_OK; | |
224 | } | |
225 | ||
226 | uint32_t | |
227 | asl_store_open_read(const char *basedir, asl_store_t **s) | |
228 | { | |
229 | asl_store_t *out; | |
230 | struct stat sb; | |
231 | ||
232 | if (s == NULL) return ASL_STATUS_INVALID_ARG; | |
233 | ||
234 | if (basedir == NULL) basedir = PATH_ASL_STORE; | |
235 | ||
236 | memset(&sb, 0, sizeof(struct stat)); | |
237 | if (stat(basedir, &sb) != 0) return ASL_STATUS_INVALID_STORE; | |
238 | if (!S_ISDIR(sb.st_mode)) return ASL_STATUS_INVALID_STORE; | |
239 | ||
240 | out = (asl_store_t *)calloc(1, sizeof(asl_store_t)); | |
241 | if (out == NULL) return ASL_STATUS_NO_MEMORY; | |
242 | ||
f3df4c03 A |
243 | out->asl_type = ASL_TYPE_STORE; |
244 | out->refcount = 1; | |
245 | ||
81582353 A |
246 | if (basedir == NULL) out->base_dir = strdup(PATH_ASL_STORE); |
247 | else out->base_dir = strdup(basedir); | |
248 | ||
249 | if (out->base_dir == NULL) | |
250 | { | |
251 | free(out); | |
252 | return ASL_STATUS_NO_MEMORY; | |
253 | } | |
254 | ||
255 | *s = out; | |
256 | return ASL_STATUS_OK; | |
257 | } | |
258 | ||
259 | uint32_t | |
260 | asl_store_max_file_size(asl_store_t *s, size_t max) | |
261 | { | |
262 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
263 | ||
264 | s->max_file_size = max; | |
265 | return ASL_STATUS_OK; | |
266 | } | |
267 | ||
268 | __private_extern__ void | |
269 | asl_store_file_closeall(asl_store_t *s) | |
270 | { | |
271 | uint32_t i; | |
272 | ||
273 | if (s == NULL) return; | |
274 | ||
275 | for (i = 0; i < FILE_CACHE_SIZE; i++) | |
276 | { | |
277 | if (s->file_cache[i].f != NULL) asl_file_close(s->file_cache[i].f); | |
278 | s->file_cache[i].f = NULL; | |
279 | if (s->file_cache[i].path != NULL) free(s->file_cache[i].path); | |
280 | s->file_cache[i].path = NULL; | |
281 | s->file_cache[i].u = -1; | |
282 | s->file_cache[i].g = -1; | |
283 | s->file_cache[i].bb = 0; | |
284 | s->file_cache[i].ts = 0; | |
285 | } | |
286 | } | |
287 | ||
f3df4c03 A |
288 | asl_store_t * |
289 | asl_store_retain(asl_store_t *s) | |
290 | { | |
291 | if (s == NULL) return NULL; | |
292 | asl_retain((asl_object_t)s); | |
293 | return s; | |
294 | } | |
295 | ||
296 | void | |
297 | asl_store_release(asl_store_t *s) | |
298 | { | |
299 | if (s == NULL) return; | |
300 | asl_release((asl_object_t)s); | |
301 | } | |
302 | ||
303 | ASL_STATUS | |
81582353 A |
304 | asl_store_close(asl_store_t *s) |
305 | { | |
306 | if (s == NULL) return ASL_STATUS_OK; | |
f3df4c03 A |
307 | asl_release((asl_object_t)s); |
308 | return ASL_STATUS_OK; | |
309 | } | |
310 | ||
311 | static void | |
312 | _asl_store_free_internal(asl_store_t *s) | |
313 | { | |
314 | if (s == NULL) return; | |
81582353 A |
315 | |
316 | if (s->base_dir != NULL) free(s->base_dir); | |
317 | s->base_dir = NULL; | |
318 | asl_store_file_closeall(s); | |
319 | if (s->storedata != NULL) fclose(s->storedata); | |
320 | ||
321 | free(s); | |
81582353 A |
322 | } |
323 | ||
324 | /* | |
325 | * Sweep the file cache. | |
326 | * Close any files that have not been used in the last FILE_CACHE_TTL seconds. | |
327 | * Returns least recently used or unused cache slot. | |
328 | */ | |
329 | static uint32_t | |
330 | asl_store_file_cache_lru(asl_store_t *s, time_t now, uint32_t ignorex) | |
331 | { | |
332 | time_t min; | |
333 | uint32_t i, x; | |
334 | ||
335 | if (s == NULL) return 0; | |
336 | ||
337 | x = 0; | |
338 | min = now - FILE_CACHE_TTL; | |
339 | ||
340 | for (i = 0; i < FILE_CACHE_SIZE; i++) | |
341 | { | |
342 | if ((i != ignorex) && (s->file_cache[i].ts < min)) | |
343 | { | |
344 | asl_file_close(s->file_cache[i].f); | |
345 | s->file_cache[i].f = NULL; | |
346 | if (s->file_cache[i].path != NULL) free(s->file_cache[i].path); | |
347 | s->file_cache[i].path = NULL; | |
348 | s->file_cache[i].u = -1; | |
349 | s->file_cache[i].g = -1; | |
350 | s->file_cache[i].bb = 0; | |
351 | s->file_cache[i].ts = 0; | |
352 | } | |
353 | ||
354 | if (s->file_cache[i].ts < s->file_cache[x].ts) x = i; | |
355 | } | |
356 | ||
357 | return x; | |
358 | } | |
359 | ||
f3df4c03 | 360 | ASL_STATUS |
81582353 A |
361 | asl_store_sweep_file_cache(asl_store_t *s) |
362 | { | |
363 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
364 | ||
365 | asl_store_file_cache_lru(s, time(NULL), FILE_CACHE_SIZE); | |
366 | return ASL_STATUS_OK; | |
367 | } | |
368 | ||
369 | static char * | |
370 | 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) | |
371 | { | |
5222c21d | 372 | char *path = NULL; |
81582353 A |
373 | |
374 | *u = 0; | |
375 | *g = 0; | |
376 | *m = 0644; | |
377 | ||
378 | if (ruid == -1) | |
379 | { | |
380 | if (rgid == -1) | |
381 | { | |
382 | if (ext == NULL) asprintf(&path, "%s/%s", dir, base); | |
383 | else asprintf(&path, "%s/%s.%s", dir, base, ext); | |
384 | } | |
385 | else | |
386 | { | |
387 | *g = rgid; | |
388 | *m = 0600; | |
389 | if (ext == NULL) asprintf(&path, "%s/%s.G%d", dir, base, *g); | |
390 | else asprintf(&path, "%s/%s.G%d.%s", dir, base, *g, ext); | |
391 | } | |
392 | } | |
393 | else | |
394 | { | |
395 | *u = ruid; | |
396 | if (rgid == -1) | |
397 | { | |
398 | *m = 0600; | |
399 | if (ext == NULL) asprintf(&path, "%s/%s.U%d", dir, base, *u); | |
400 | else asprintf(&path, "%s/%s.U%d.%s", dir, base, *u, ext); | |
401 | } | |
402 | else | |
403 | { | |
404 | *g = rgid; | |
405 | *m = 0600; | |
406 | if (ext == NULL) asprintf(&path, "%s/%s.U%d.G%d", dir, base, *u, *g); | |
407 | else asprintf(&path, "%s/%s.U%d.G%u.%s", dir, base, *u, *g, ext); | |
408 | } | |
409 | } | |
410 | ||
411 | return path; | |
412 | } | |
413 | ||
f3df4c03 | 414 | static ASL_STATUS |
81582353 A |
415 | 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) |
416 | { | |
417 | char *path; | |
418 | mode_t m; | |
419 | int32_t i, x; | |
420 | uid_t u; | |
421 | gid_t g; | |
422 | uint32_t status; | |
423 | asl_file_t *out; | |
424 | ||
425 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
426 | ||
427 | /* see if the file is already open and in the cache */ | |
428 | for (i = 0; i < FILE_CACHE_SIZE; i++) | |
429 | { | |
430 | 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)) | |
431 | { | |
432 | s->file_cache[i].ts = now; | |
433 | *f = s->file_cache[i].f; | |
434 | if (check_cache == 1) asl_store_file_cache_lru(s, now, i); | |
435 | return ASL_STATUS_OK; | |
436 | } | |
437 | } | |
438 | ||
439 | u = 0; | |
440 | g = 0; | |
441 | m = 0644; | |
442 | path = asl_store_make_ug_path(s->base_dir, tstring, "asl", (uid_t)ruid, (gid_t)rgid, &u, &g, &m); | |
443 | if (path == NULL) return ASL_STATUS_NO_MEMORY; | |
444 | ||
445 | out = NULL; | |
446 | status = asl_file_open_write(path, m, u, g, &out); | |
447 | if (status != ASL_STATUS_OK) | |
448 | { | |
449 | free(path); | |
450 | return status; | |
451 | } | |
452 | ||
453 | x = asl_store_file_cache_lru(s, now, FILE_CACHE_SIZE); | |
454 | if (s->file_cache[x].f != NULL) asl_file_close(s->file_cache[x].f); | |
455 | if (s->file_cache[x].path != NULL) free(s->file_cache[x].path); | |
456 | ||
457 | s->file_cache[x].f = out; | |
458 | s->file_cache[x].path = path; | |
459 | s->file_cache[x].u = ruid; | |
460 | s->file_cache[x].g = rgid; | |
461 | s->file_cache[x].bb = bb; | |
462 | s->file_cache[x].ts = time(NULL); | |
463 | ||
464 | *f = out; | |
465 | ||
466 | return ASL_STATUS_OK; | |
467 | } | |
468 | ||
469 | __private_extern__ char * | |
470 | asl_store_file_path(asl_store_t *s, asl_file_t *f) | |
471 | { | |
472 | uint32_t i; | |
473 | ||
474 | if (s == NULL) return NULL; | |
475 | ||
476 | for (i = 0; i < FILE_CACHE_SIZE; i++) | |
477 | { | |
478 | if (s->file_cache[i].f == f) | |
479 | { | |
480 | if (s->file_cache[i].path == NULL) return NULL; | |
481 | return strdup(s->file_cache[i].path); | |
482 | } | |
483 | } | |
484 | ||
485 | return NULL; | |
486 | } | |
487 | ||
488 | __private_extern__ void | |
489 | asl_store_file_close(asl_store_t *s, asl_file_t *f) | |
490 | { | |
491 | uint32_t i; | |
492 | ||
493 | if (s == NULL) return; | |
494 | if (f == NULL) return; | |
495 | ||
496 | for (i = 0; i < FILE_CACHE_SIZE; i++) | |
497 | { | |
498 | if (s->file_cache[i].f == f) | |
499 | { | |
500 | asl_file_close(s->file_cache[i].f); | |
501 | s->file_cache[i].f = NULL; | |
502 | if (s->file_cache[i].path != NULL) free(s->file_cache[i].path); | |
503 | s->file_cache[i].path = NULL; | |
504 | s->file_cache[i].u = -1; | |
505 | s->file_cache[i].g = -1; | |
506 | s->file_cache[i].bb = 0; | |
507 | s->file_cache[i].ts = 0; | |
508 | return; | |
509 | } | |
510 | } | |
511 | } | |
512 | ||
f3df4c03 A |
513 | ASL_STATUS |
514 | asl_store_save(asl_store_t *s, asl_msg_t *msg) | |
81582353 A |
515 | { |
516 | struct tm ctm; | |
517 | time_t msg_time, now, bb; | |
5222c21d A |
518 | char *path; |
519 | char tstring[128], tmp_path[MAXPATHLEN]; | |
81582353 A |
520 | const char *val; |
521 | uid_t ruid; | |
522 | gid_t rgid; | |
523 | asl_file_t *f; | |
524 | uint32_t status, check_cache, trigger_aslmanager, len; | |
525 | uint64_t xid, ftime; | |
526 | size_t fsize; | |
527 | ||
528 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
529 | if (msg == NULL) return ASL_STATUS_INVALID_ARG; | |
530 | ||
531 | now = time(NULL); | |
532 | ||
533 | check_cache = 0; | |
534 | if ((s->last_write + FILE_CACHE_TTL) <= now) check_cache = 1; | |
535 | ||
536 | trigger_aslmanager = 0; | |
537 | ||
538 | msg_time = 0; | |
f3df4c03 A |
539 | val = NULL; |
540 | ||
541 | if (asl_msg_lookup(msg, ASL_KEY_TIME, &val, NULL) != 0) msg_time = now; | |
542 | else if (val == NULL) msg_time = now; | |
543 | else msg_time = asl_core_parse_time(val, NULL); | |
81582353 A |
544 | |
545 | if (msg_time >= s->start_tomorrow) | |
546 | { | |
547 | if (now >= s->start_tomorrow) | |
548 | { | |
549 | /* new day begins */ | |
550 | check_cache = 0; | |
551 | asl_store_file_closeall(s); | |
552 | ||
553 | /* | |
554 | * _asl_start_today should never fail, but if it does, | |
555 | * just push forward one day. That will probably be correct, and if | |
556 | * it isn't, the next message that gets saved will push it ahead again | |
557 | * until we get to the right date. | |
558 | */ | |
559 | s->start_today = _asl_start_today(); | |
560 | if (s->start_today == 0) s->start_today = s->start_tomorrow; | |
561 | ||
562 | s->start_tomorrow = s->start_today + SECONDS_PER_DAY; | |
563 | } | |
564 | } | |
565 | ||
81582353 | 566 | ruid = -1; |
81582353 | 567 | rgid = -1; |
f3df4c03 A |
568 | if ((s->flags & ASL_STORE_FLAG_NO_ACLS) == 0) |
569 | { | |
570 | val = NULL; | |
571 | if ((asl_msg_lookup(msg, ASL_KEY_READ_UID, &val, NULL) == 0) && (val != NULL)) ruid = atoi(val); | |
572 | ||
573 | val = NULL; | |
574 | if ((asl_msg_lookup(msg, ASL_KEY_READ_GID, &val, NULL) == 0) && (val != NULL)) rgid = atoi(val); | |
575 | } | |
81582353 A |
576 | |
577 | bb = 0; | |
f3df4c03 | 578 | if ((s->flags & ASL_STORE_FLAG_NO_TTL) == 0) |
81582353 | 579 | { |
f3df4c03 A |
580 | val = NULL; |
581 | if ((asl_msg_lookup(msg, ASL_KEY_EXPIRE_TIME, &val, NULL) == 0) && (val != NULL)) | |
582 | { | |
583 | bb = 1; | |
584 | msg_time = asl_core_parse_time(val, NULL); | |
585 | } | |
81582353 A |
586 | } |
587 | ||
588 | if (fseeko(s->storedata, 0, SEEK_SET) != 0) return ASL_STATUS_WRITE_FAILED; | |
589 | ||
590 | xid = asl_core_htonq(s->next_id); | |
591 | if (fwrite(&xid, sizeof(uint64_t), 1, s->storedata) != 1) return ASL_STATUS_WRITE_FAILED; | |
592 | ||
593 | /* flush data */ | |
594 | fflush(s->storedata); | |
595 | ||
596 | xid = s->next_id; | |
597 | s->next_id++; | |
598 | ||
599 | s->last_write = now; | |
600 | ||
601 | if (localtime_r((const time_t *)&msg_time, &ctm) == NULL) return ASL_STATUS_FAILED; | |
602 | ||
81582353 A |
603 | if (bb == 1) |
604 | { | |
605 | /* | |
606 | * This supports 12 monthly "Best Before" buckets. | |
607 | * We advance the actual expiry time to day zero of the following month. | |
608 | * mktime() is clever enough to know that you actually mean the last day | |
609 | * of the previous month. What we get back from localtime is the last | |
610 | * day of the month in which the message expires, which we use in the name. | |
611 | */ | |
612 | ctm.tm_sec = 0; | |
613 | ctm.tm_min = 0; | |
614 | ctm.tm_hour = 0; | |
615 | ctm.tm_mday = 0; | |
616 | ctm.tm_mon += 1; | |
617 | ||
618 | bb = mktime(&ctm); | |
619 | ||
620 | if (localtime_r((const time_t *)&bb, &ctm) == NULL) return ASL_STATUS_FAILED; | |
5222c21d | 621 | snprintf(tstring, sizeof(tstring), "BB.%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); |
81582353 A |
622 | } |
623 | else | |
624 | { | |
5222c21d | 625 | snprintf(tstring, sizeof(tstring), "%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); |
81582353 A |
626 | } |
627 | ||
81582353 | 628 | status = asl_store_file_open_write(s, tstring, ruid, rgid, bb, &f, now, check_cache); |
81582353 A |
629 | if (status != ASL_STATUS_OK) return status; |
630 | ||
631 | status = asl_file_save(f, msg, &xid); | |
632 | if (status != ASL_STATUS_OK) return status; | |
633 | ||
634 | fsize = asl_file_size(f); | |
635 | ftime = asl_file_ctime(f); | |
636 | ||
637 | /* if file is larger than max_file_size, rename it and trigger aslmanager */ | |
638 | if ((s->max_file_size != 0) && (fsize > s->max_file_size)) | |
639 | { | |
640 | trigger_aslmanager = 1; | |
641 | status = ASL_STATUS_OK; | |
642 | ||
643 | path = asl_store_file_path(s, f); | |
644 | ||
645 | asl_store_file_close(s, f); | |
646 | ||
647 | if (path != NULL) | |
648 | { | |
81582353 A |
649 | len = strlen(path); |
650 | if ((len >= 4) && (!strcmp(path + len - 4, ".asl"))) | |
651 | { | |
652 | /* rename xxxxxxx.asl to xxxxxxx.timestamp.asl */ | |
5222c21d A |
653 | char scratch[MAXPATHLEN]; |
654 | snprintf(scratch, sizeof(scratch), "%s", path); | |
655 | scratch[len - 4] = '\0'; | |
656 | snprintf(tmp_path, sizeof(tmp_path), "%s.%llu.asl", scratch, ftime); | |
81582353 A |
657 | } |
658 | else | |
659 | { | |
660 | /* append timestamp */ | |
5222c21d | 661 | snprintf(tmp_path, sizeof(tmp_path), "%s.%llu", path, ftime); |
81582353 A |
662 | } |
663 | ||
5222c21d | 664 | if (rename(path, tmp_path) != 0) status = ASL_STATUS_FAILED; |
81582353 A |
665 | |
666 | free(path); | |
667 | } | |
668 | } | |
669 | ||
670 | if (trigger_aslmanager != 0) asl_trigger_aslmanager(); | |
671 | ||
672 | return status; | |
673 | } | |
674 | ||
f3df4c03 | 675 | static ASL_STATUS |
81582353 A |
676 | asl_store_mkdir(asl_store_t *s, const char *dir, mode_t m) |
677 | { | |
5222c21d | 678 | char tstring[MAXPATHLEN]; |
81582353 A |
679 | int status; |
680 | struct stat sb; | |
681 | ||
5222c21d | 682 | snprintf(tstring, sizeof(tstring), "%s/%s", s->base_dir, dir); |
81582353 A |
683 | |
684 | memset(&sb, 0, sizeof(struct stat)); | |
685 | status = stat(tstring, &sb); | |
686 | ||
687 | if (status == 0) | |
688 | { | |
689 | /* must be a directory */ | |
5222c21d | 690 | if (!S_ISDIR(sb.st_mode)) return ASL_STATUS_INVALID_STORE; |
81582353 A |
691 | } |
692 | else | |
693 | { | |
694 | if (errno == ENOENT) | |
695 | { | |
696 | /* doesn't exist - create it */ | |
5222c21d | 697 | if (mkdir(tstring, m) != 0) return ASL_STATUS_WRITE_FAILED; |
81582353 A |
698 | } |
699 | else | |
700 | { | |
701 | /* stat failed for some other reason */ | |
81582353 A |
702 | return ASL_STATUS_FAILED; |
703 | } | |
704 | } | |
705 | ||
81582353 A |
706 | return ASL_STATUS_OK; |
707 | } | |
708 | ||
f3df4c03 A |
709 | ASL_STATUS |
710 | asl_store_open_aux(asl_store_t *s, asl_msg_t *msg, int *out_fd, char **url) | |
81582353 A |
711 | { |
712 | struct tm ctm; | |
713 | time_t msg_time, bb; | |
5222c21d A |
714 | char *path; |
715 | char tstring[128], dir[128]; | |
81582353 A |
716 | const char *val; |
717 | uid_t ruid, u; | |
718 | gid_t rgid, g; | |
719 | mode_t m; | |
720 | uint32_t status; | |
721 | uint64_t fid; | |
722 | int fd; | |
723 | ||
724 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
725 | if (msg == NULL) return ASL_STATUS_INVALID_ARG; | |
726 | if (out_fd == NULL) return ASL_STATUS_INVALID_ARG; | |
727 | if (url == NULL) return ASL_STATUS_INVALID_ARG; | |
728 | ||
729 | msg_time = time(NULL); | |
730 | ||
81582353 | 731 | ruid = -1; |
81582353 | 732 | rgid = -1; |
f3df4c03 A |
733 | if ((s->flags & ASL_STORE_FLAG_NO_ACLS) == 0) |
734 | { | |
735 | val = NULL; | |
736 | if ((asl_msg_lookup(msg, ASL_KEY_READ_UID, &val, NULL) == 0) && (val != NULL)) ruid = atoi(val); | |
737 | ||
738 | val = NULL; | |
739 | if ((asl_msg_lookup(msg, ASL_KEY_READ_GID, &val, NULL) == 0) && (val != NULL)) rgid = atoi(val); | |
740 | } | |
81582353 A |
741 | |
742 | bb = 0; | |
f3df4c03 | 743 | if ((s->flags & ASL_STORE_FLAG_NO_TTL) == 0) |
81582353 | 744 | { |
f3df4c03 A |
745 | val = NULL; |
746 | if ((asl_msg_lookup(msg, ASL_KEY_EXPIRE_TIME, &val, NULL) == 0) && (val != NULL)) | |
747 | { | |
748 | bb = 1; | |
749 | msg_time = asl_core_parse_time(val, NULL); | |
750 | } | |
81582353 A |
751 | } |
752 | ||
753 | if (localtime_r((const time_t *)&msg_time, &ctm) == NULL) return ASL_STATUS_FAILED; | |
754 | ||
81582353 A |
755 | if (bb == 1) |
756 | { | |
757 | /* | |
758 | * This supports 12 monthly "Best Before" buckets. | |
759 | * We advance the actual expiry time to day zero of the following month. | |
760 | * mktime() is clever enough to know that you actually mean the last day | |
761 | * of the previous month. What we get back from localtime is the last | |
762 | * day of the month in which the message expires, which we use in the name. | |
763 | */ | |
764 | ctm.tm_sec = 0; | |
765 | ctm.tm_min = 0; | |
766 | ctm.tm_hour = 0; | |
767 | ctm.tm_mday = 0; | |
768 | ctm.tm_mon += 1; | |
769 | ||
770 | bb = mktime(&ctm); | |
771 | ||
772 | if (localtime_r((const time_t *)&bb, &ctm) == NULL) return ASL_STATUS_FAILED; | |
5222c21d | 773 | snprintf(dir, sizeof(dir), "BB.AUX.%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); |
81582353 A |
774 | } |
775 | else | |
776 | { | |
5222c21d | 777 | snprintf(dir, sizeof(dir), "AUX.%d.%02d.%02d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday); |
81582353 A |
778 | } |
779 | ||
81582353 | 780 | status = asl_store_mkdir(s, dir, 0755); |
5222c21d | 781 | if (status != ASL_STATUS_OK) return status; |
81582353 A |
782 | |
783 | fid = s->next_id; | |
784 | s->next_id++; | |
81582353 | 785 | |
5222c21d | 786 | snprintf(tstring, sizeof(tstring), "%s/%llu", dir, fid); |
81582353 A |
787 | |
788 | u = 0; | |
789 | g = 0; | |
790 | m = 0644; | |
791 | path = asl_store_make_ug_path(s->base_dir, tstring, NULL, ruid, rgid, &u, &g, &m); | |
81582353 A |
792 | if (path == NULL) return ASL_STATUS_NO_MEMORY; |
793 | ||
794 | fd = asl_file_create(path, u, g, m); | |
795 | if (fd < 0) | |
796 | { | |
797 | free(path); | |
798 | *out_fd = -1; | |
799 | return ASL_STATUS_WRITE_FAILED; | |
800 | } | |
801 | ||
802 | /* URL is file://<path> */ | |
803 | *url = NULL; | |
804 | asprintf(url, "file://%s", path); | |
805 | free(path); | |
806 | ||
807 | *out_fd = fd; | |
808 | ||
809 | return status; | |
810 | } | |
811 | ||
f3df4c03 A |
812 | asl_msg_list_t * |
813 | asl_store_match(asl_store_t *s, asl_msg_list_t *qlist, uint64_t *last_id, uint64_t start_id, uint32_t count, uint32_t duration, int32_t direction) | |
81582353 A |
814 | { |
815 | DIR *dp; | |
816 | struct dirent *dent; | |
817 | uint32_t status; | |
818 | asl_file_t *f; | |
5222c21d | 819 | char path[MAXPATHLEN]; |
81582353 | 820 | asl_file_list_t *files; |
f3df4c03 | 821 | asl_msg_list_t *res; |
81582353 | 822 | |
f3df4c03 | 823 | if (s == NULL) return NULL; |
81582353 A |
824 | |
825 | files = NULL; | |
826 | ||
827 | /* | |
828 | * Open all readable files | |
829 | */ | |
830 | dp = opendir(s->base_dir); | |
f3df4c03 | 831 | if (dp == NULL) return NULL; |
81582353 A |
832 | |
833 | while ((dent = readdir(dp)) != NULL) | |
834 | { | |
835 | if (dent->d_name[0] == '.') continue; | |
836 | ||
5222c21d | 837 | snprintf(path, sizeof(path), "%s/%s", s->base_dir, dent->d_name); |
81582353 A |
838 | |
839 | /* 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 */ | |
840 | status = asl_file_open_read(path, &f); | |
81582353 A |
841 | if ((status != ASL_STATUS_OK) || (f == NULL)) continue; |
842 | ||
843 | files = asl_file_list_add(files, f); | |
844 | } | |
845 | ||
846 | closedir(dp); | |
847 | ||
f3df4c03 | 848 | res = asl_file_list_match(files, qlist, last_id, start_id, count, duration, direction); |
81582353 | 849 | asl_file_list_close(files); |
f3df4c03 | 850 | return res; |
81582353 A |
851 | } |
852 | ||
f3df4c03 A |
853 | /* |
854 | * PRIVATE FOR DEV TOOLS SUPPORT | |
855 | * DO NOT USE THIS INTERFACE OTHERWISE | |
856 | * | |
857 | * This is only called by a client that compiled with a 10.9 SDK, but is running | |
858 | * with an new 10.10 libasl. | |
859 | * | |
860 | * Only searches the ASL database, so the store (first parameter) is ignored. | |
861 | * | |
862 | * The query and result are old-style message lists. | |
863 | * | |
864 | */ | |
865 | typedef struct | |
866 | { | |
867 | uint32_t count; | |
868 | uint32_t curr; | |
869 | void **msg; | |
870 | } asl_msg_list_v1_t; | |
871 | ||
872 | ASL_STATUS | |
873 | asl_store_match_timeout(void *ignored, void *query_v1, void **result_v1, uint64_t *last_id, uint64_t start_id, uint32_t count, int32_t direction, uint32_t usec) | |
81582353 | 874 | { |
f3df4c03 A |
875 | asl_store_t *asldb = NULL; |
876 | asl_msg_list_v1_t *listv1; | |
877 | asl_msg_list_t *qlist = NULL; | |
878 | uint32_t status, n; | |
879 | ||
880 | if (result_v1 == NULL) return ASL_STATUS_FAILED; | |
881 | *result_v1 = NULL; | |
882 | ||
883 | status = asl_store_open_read(NULL, &asldb); | |
884 | if (status != ASL_STATUS_OK) return status; | |
885 | ||
886 | /* convert query_v1 into an asl_msg_list_t */ | |
887 | listv1 = (asl_msg_list_v1_t *)query_v1; | |
888 | if (listv1 != NULL) | |
889 | { | |
890 | if (listv1->count > 0) qlist = (asl_msg_list_t *)asl_new(ASL_TYPE_LIST); | |
891 | ||
892 | for (listv1->curr = 0; listv1->curr < listv1->count; listv1->curr++) | |
893 | { | |
894 | asl_append((asl_object_t)qlist, (asl_object_t)listv1->msg[listv1->curr]); | |
895 | } | |
896 | } | |
897 | ||
898 | asl_msg_list_t *result = asl_store_match(asldb, qlist, last_id, start_id, count, usec, direction); | |
899 | asl_release((asl_object_t)asldb); | |
900 | asl_release((asl_object_t)qlist); | |
901 | ||
902 | if (result == NULL) return ASL_STATUS_OK; | |
903 | ||
904 | n = asl_count((asl_object_t)result); | |
905 | if (n == 0) | |
906 | { | |
907 | asl_release((asl_object_t)result); | |
908 | return ASL_STATUS_OK; | |
909 | } | |
910 | ||
911 | listv1 = (asl_msg_list_v1_t *)calloc(1, sizeof(asl_msg_list_v1_t)); | |
912 | if (listv1 == NULL) | |
913 | { | |
914 | asl_release((asl_object_t)result); | |
915 | return ASL_STATUS_NO_MEMORY; | |
916 | } | |
917 | ||
918 | listv1->count = n; | |
919 | listv1->msg = (void **)calloc(listv1->count, sizeof(void *)); | |
920 | if (listv1 == NULL) | |
921 | { | |
922 | free(listv1); | |
923 | asl_release((asl_object_t)result); | |
924 | return ASL_STATUS_NO_MEMORY; | |
925 | } | |
926 | ||
927 | for (listv1->curr = 0; listv1->curr < listv1->count; listv1->curr++) | |
928 | { | |
929 | listv1->msg[listv1->curr] = asl_retain(asl_next((asl_object_t)result)); | |
930 | } | |
931 | ||
932 | listv1->curr = 0; | |
933 | *result_v1 = listv1; | |
934 | ||
935 | asl_release((asl_object_t)result); | |
936 | return ASL_STATUS_OK; | |
81582353 A |
937 | } |
938 | ||
f3df4c03 | 939 | ASL_STATUS |
81582353 A |
940 | asl_store_match_start(asl_store_t *s, uint64_t start_id, int32_t direction) |
941 | { | |
942 | DIR *dp; | |
943 | struct dirent *dent; | |
944 | uint32_t status; | |
945 | asl_file_t *f; | |
5222c21d | 946 | char path[MAXPATHLEN]; |
81582353 A |
947 | asl_file_list_t *files; |
948 | ||
949 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
950 | ||
951 | if (s->work != NULL) asl_file_list_match_end(s->work); | |
952 | s->work = NULL; | |
953 | ||
954 | files = NULL; | |
955 | ||
956 | /* | |
957 | * Open all readable files | |
958 | */ | |
959 | dp = opendir(s->base_dir); | |
960 | if (dp == NULL) return ASL_STATUS_READ_FAILED; | |
961 | ||
962 | while ((dent = readdir(dp)) != NULL) | |
963 | { | |
964 | if (dent->d_name[0] == '.') continue; | |
965 | ||
5222c21d | 966 | snprintf(path, sizeof(path), "%s/%s", s->base_dir, dent->d_name); |
81582353 | 967 | |
f3df4c03 A |
968 | /* |
969 | * NB asl_file_open_read will fail if path is NULL, | |
970 | * if it is not an ASL store file, or if it isn't readable. | |
971 | * We expect that. | |
972 | */ | |
81582353 | 973 | status = asl_file_open_read(path, &f); |
81582353 A |
974 | if ((status != ASL_STATUS_OK) || (f == NULL)) continue; |
975 | ||
976 | files = asl_file_list_add(files, f); | |
977 | } | |
978 | ||
979 | closedir(dp); | |
980 | ||
981 | s->work = asl_file_list_match_start(files, start_id, direction); | |
982 | if (s->work == NULL) return ASL_STATUS_FAILED; | |
983 | ||
984 | return ASL_STATUS_OK; | |
985 | } | |
986 | ||
f3df4c03 A |
987 | ASL_STATUS |
988 | asl_store_match_next(asl_store_t *s, asl_msg_list_t *qlist, asl_msg_list_t **res, uint32_t count) | |
81582353 A |
989 | { |
990 | if (s == NULL) return ASL_STATUS_INVALID_STORE; | |
991 | if (s->work == NULL) return ASL_STATUS_OK; | |
992 | ||
f3df4c03 | 993 | return asl_file_list_match_next(s->work, qlist, res, count); |
81582353 A |
994 | } |
995 | ||
f3df4c03 A |
996 | #pragma mark - |
997 | #pragma mark asl_object support | |
81582353 | 998 | |
f3df4c03 A |
999 | static void |
1000 | _jump_dealloc(asl_object_private_t *obj) | |
1001 | { | |
1002 | _asl_store_free_internal((asl_store_t *)obj); | |
1003 | } | |
81582353 | 1004 | |
f3df4c03 A |
1005 | static asl_object_private_t * |
1006 | _jump_next(asl_object_private_t *obj) | |
1007 | { | |
1008 | asl_store_t *s = (asl_store_t *)obj; | |
1009 | asl_msg_list_t *list; | |
1010 | asl_msg_t *out = NULL; | |
1011 | uint64_t last = 0; | |
81582353 | 1012 | |
f3df4c03 A |
1013 | if (s == NULL) return NULL; |
1014 | if (s->curr == SIZE_MAX) return NULL; | |
1015 | ||
1016 | s->curr++; | |
1017 | list = asl_store_match(s, NULL, &last, s->curr, 1, 0, 1); | |
1018 | if (list == NULL) | |
1019 | { | |
1020 | s->curr = SIZE_MAX; | |
1021 | return NULL; | |
1022 | } | |
1023 | ||
1024 | s->curr = last; | |
1025 | out = asl_msg_list_get_index(list, 0); | |
1026 | asl_msg_list_release(list); | |
1027 | ||
1028 | return (asl_object_private_t *)out; | |
81582353 A |
1029 | } |
1030 | ||
f3df4c03 A |
1031 | static asl_object_private_t * |
1032 | _jump_prev(asl_object_private_t *obj) | |
1033 | { | |
1034 | asl_store_t *s = (asl_store_t *)obj; | |
1035 | asl_msg_list_t *list; | |
1036 | asl_msg_t *out = NULL; | |
1037 | uint64_t last = 0; | |
81582353 | 1038 | |
f3df4c03 A |
1039 | if (s == NULL) return NULL; |
1040 | if (s->curr == 0) return NULL; | |
81582353 | 1041 | |
f3df4c03 A |
1042 | s->curr--; |
1043 | if (s->curr == 0) return NULL; | |
81582353 | 1044 | |
f3df4c03 A |
1045 | list = asl_store_match(s, NULL, &last, s->curr, 1, 0, -1); |
1046 | if (list == NULL) | |
1047 | { | |
1048 | s->curr = 0; | |
1049 | return NULL; | |
1050 | } | |
1051 | ||
1052 | s->curr = last; | |
1053 | out = asl_msg_list_get_index(list, 0); | |
1054 | asl_msg_list_release(list); | |
1055 | ||
1056 | return (asl_object_private_t *)out; | |
1057 | } | |
1058 | ||
1059 | static void | |
1060 | _jump_set_iteration_index(asl_object_private_t *obj, size_t n) | |
1061 | { | |
1062 | asl_store_t *s = (asl_store_t *)obj; | |
1063 | if (s == NULL) return; | |
1064 | ||
1065 | s->curr = n; | |
1066 | } | |
1067 | ||
1068 | static void | |
af7d442c | 1069 | _jump_append(asl_object_private_t *obj, asl_object_private_t *newobj, void *addr) |
f3df4c03 A |
1070 | { |
1071 | asl_store_t *s = (asl_store_t *)obj; | |
1072 | int type = asl_get_type((asl_object_t)newobj); | |
1073 | if (s == NULL) return; | |
1074 | if (s->flags & ASL_FILE_FLAG_READ) return; | |
1075 | ||
1076 | if (type == ASL_TYPE_LIST) | |
1077 | { | |
1078 | asl_msg_t *msg; | |
1079 | asl_msg_list_reset_iteration((asl_msg_list_t *)newobj, 0); | |
1080 | while (NULL != (msg = asl_msg_list_next((asl_msg_list_t *)newobj))) | |
1081 | { | |
1082 | if (asl_store_save(s, msg) != ASL_STATUS_OK) return; | |
1083 | } | |
1084 | } | |
1085 | else if ((type == ASL_TYPE_MSG) || (type == ASL_TYPE_QUERY)) | |
1086 | { | |
1087 | asl_store_save(s, (asl_msg_t *)newobj); | |
1088 | } | |
1089 | } | |
1090 | ||
1091 | static asl_object_private_t * | |
1092 | _jump_search(asl_object_private_t *obj, asl_object_private_t *query) | |
1093 | { | |
1094 | asl_store_t *s = (asl_store_t *)obj; | |
1095 | int type = asl_get_type((asl_object_t)query); | |
1096 | asl_msg_list_t *out = NULL; | |
1097 | asl_msg_list_t *ql = NULL; | |
1098 | uint64_t last; | |
f3df4c03 A |
1099 | |
1100 | if (query == NULL) | |
1101 | { | |
1102 | out = asl_store_match(s, NULL, &last, 0, 0, 0, 1); | |
1103 | } | |
1104 | else if (type == ASL_TYPE_LIST) | |
1105 | { | |
1106 | out = asl_store_match(s, (asl_msg_list_t *)query, &last, 0, 0, 0, 1); | |
1107 | } | |
1108 | else if ((type == ASL_TYPE_MSG) || (type == ASL_TYPE_QUERY)) | |
1109 | { | |
1110 | ql = asl_msg_list_new(); | |
1111 | asl_msg_list_append(ql, query); | |
1112 | ||
1113 | out = asl_store_match(s, ql, &last, 0, 0, 0, 1); | |
1114 | asl_msg_list_release(ql); | |
1115 | } | |
1116 | ||
f3df4c03 A |
1117 | return (asl_object_private_t *)out; |
1118 | } | |
1119 | ||
1120 | static asl_object_private_t * | |
1121 | _jump_match(asl_object_private_t *obj, asl_object_private_t *qlist, size_t *last, size_t start, size_t count, uint32_t duration, int32_t dir) | |
1122 | { | |
1123 | uint64_t x; | |
1124 | asl_msg_list_t *out; | |
1125 | ||
1126 | out = asl_store_match((asl_store_t *)obj, (asl_msg_list_t *)qlist, &x, start, count, duration, dir); | |
1127 | *last = x; | |
1128 | return (asl_object_private_t *)out; | |
1129 | } | |
1130 | ||
1131 | __private_extern__ const asl_jump_table_t * | |
1132 | asl_store_jump_table() | |
1133 | { | |
1134 | static const asl_jump_table_t jump = | |
1135 | { | |
1136 | .alloc = NULL, | |
1137 | .dealloc = &_jump_dealloc, | |
1138 | .set_key_val_op = NULL, | |
1139 | .unset_key = NULL, | |
1140 | .get_val_op_for_key = NULL, | |
1141 | .get_key_val_op_at_index = NULL, | |
1142 | .count = NULL, | |
1143 | .next = &_jump_next, | |
1144 | .prev = &_jump_prev, | |
1145 | .get_object_at_index = NULL, | |
1146 | .set_iteration_index = &_jump_set_iteration_index, | |
1147 | .remove_object_at_index = NULL, | |
1148 | .append = &_jump_append, | |
1149 | .prepend = NULL, | |
1150 | .search = &_jump_search, | |
1151 | .match = &_jump_match | |
1152 | }; | |
1153 | ||
1154 | return &jump; | |
81582353 | 1155 | } |