]> git.saurik.com Git - apple/syslog.git/blame_incremental - aslcommon/asl_common.c
syslog-217.1.4.tar.gz
[apple/syslog.git] / aslcommon / asl_common.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 2012 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 <assert.h>
25#include <stdlib.h>
26#include <stdint.h>
27#include <stdbool.h>
28#include <string.h>
29#include <dirent.h>
30#include <errno.h>
31#include <time.h>
32#include <fcntl.h>
33#include <unistd.h>
34#include <sys/param.h>
35#include <sys/stat.h>
36#include <sys/acl.h>
37#include <membership.h>
38#include <xpc/xpc.h>
39#include <TargetConditionals.h>
40#include <configuration_profile.h>
41#include <asl_core.h>
42#include <asl_msg.h>
43#include "asl_common.h"
44
45#define _PATH_ASL_CONF "/etc/asl.conf"
46#define _PATH_ASL_CONF_DIR "/etc/asl"
47
48#if !TARGET_IPHONE_SIMULATOR
49#define _PATH_ASL_CONF_LOCAL_DIR "/usr/local/etc/asl"
50#endif
51
52static const char *asl_out_action_name[] =
53{
54 "none ",
55 "set ",
56 "output ",
57 "ignore ",
58 "skip ",
59 "claim ",
60 "notify ",
61 "broadcast ",
62 "access ",
63 "store ",
64 "asl_file ",
65 "asl_dir ",
66 "file ",
67 "forward ",
68 "control ",
69 "set (file) ",
70 "set (plist) ",
71 "set (profile)"
72};
73
74static time_t start_today;
75
76extern asl_msg_t *asl_msg_from_string(const char *buf);
77
78#define forever for(;;)
79#define KEYMATCH(S,K) ((strncasecmp(S, K, strlen(K)) == 0))
80
81asl_msg_t *
82xpc_object_to_asl_msg(xpc_object_t xobj)
83{
84 __block asl_msg_t *out;
85
86 if (xobj == NULL) return NULL;
87 if (xpc_get_type(xobj) != XPC_TYPE_DICTIONARY) return NULL;
88
89 out = asl_msg_new(ASL_TYPE_MSG);
90 xpc_dictionary_apply(xobj, ^bool(const char *key, xpc_object_t xval) {
91 char tmp[64];
92
93 if (xpc_get_type(xval) == XPC_TYPE_NULL)
94 {
95 asl_msg_set_key_val_op(out, key, NULL, 0);
96 }
97 else if (xpc_get_type(xval) == XPC_TYPE_BOOL)
98 {
99 if (xpc_bool_get_value(xval)) asl_msg_set_key_val_op(out, key, "1", 0);
100 else asl_msg_set_key_val_op(out, key, "0", 0);
101 }
102 else if (xpc_get_type(xval) == XPC_TYPE_INT64)
103 {
104 snprintf(tmp, sizeof(tmp), "%lld", xpc_int64_get_value(xval));
105 asl_msg_set_key_val_op(out, key, tmp, 0);
106 }
107 else if (xpc_get_type(xval) == XPC_TYPE_UINT64)
108 {
109 snprintf(tmp, sizeof(tmp), "%llu", xpc_uint64_get_value(xval));
110 asl_msg_set_key_val_op(out, key, tmp, 0);
111 }
112 else if (xpc_get_type(xval) == XPC_TYPE_DOUBLE)
113 {
114 snprintf(tmp, sizeof(tmp), "%f", xpc_double_get_value(xval));
115 asl_msg_set_key_val_op(out, key, tmp, 0);
116 }
117 else if (xpc_get_type(xval) == XPC_TYPE_DATE)
118 {
119 snprintf(tmp, sizeof(tmp), "%lld", xpc_date_get_value(xval));
120 asl_msg_set_key_val_op(out, key, tmp, 0);
121 }
122 else if (xpc_get_type(xval) == XPC_TYPE_DATA)
123 {
124 size_t len = xpc_data_get_length(xval);
125 char *encoded = asl_core_encode_buffer(xpc_data_get_bytes_ptr(xval), len);
126 asl_msg_set_key_val_op(out, key, encoded, 0);
127 free(encoded);
128 }
129 else if (xpc_get_type(xval) == XPC_TYPE_STRING)
130 {
131 asl_msg_set_key_val_op(out, key, xpc_string_get_string_ptr(xval), 0);
132 }
133 else if (xpc_get_type(xval) == XPC_TYPE_UUID)
134 {
135 uuid_string_t us;
136 uuid_unparse(xpc_uuid_get_bytes(xval), us);
137 asl_msg_set_key_val_op(out, key, us, 0);
138 }
139 else if (xpc_get_type(xval) == XPC_TYPE_FD)
140 {
141 /* XPC_TYPE_FD is not supported */
142 asl_msg_set_key_val_op(out, key, "{XPC_TYPE_FD}", 0);
143 }
144 else if (xpc_get_type(xval) == XPC_TYPE_SHMEM)
145 {
146 /* XPC_TYPE_SHMEM is not supported */
147 asl_msg_set_key_val_op(out, key, "{XPC_TYPE_SHMEM}", 0);
148 }
149 else if (xpc_get_type(xval) == XPC_TYPE_ARRAY)
150 {
151 /* XPC_TYPE_ARRAY is not supported */
152 asl_msg_set_key_val_op(out, key, "{XPC_TYPE_ARRAY}", 0);
153 }
154 else if (xpc_get_type(xval) == XPC_TYPE_DICTIONARY)
155 {
156 /* XPC_TYPE_DICTIONARY is not supported */
157 asl_msg_set_key_val_op(out, key, "{XPC_TYPE_DICTIONARY}", 0);
158 }
159 else if (xpc_get_type(xval) == XPC_TYPE_ERROR)
160 {
161 /* XPC_TYPE_ERROR is not supported */
162 asl_msg_set_key_val_op(out, key, "{XPC_TYPE_ERROR}", 0);
163 }
164 else
165 {
166 /* UNKNOWN TYPE */
167 asl_msg_set_key_val_op(out, key, "{XPC_TYPE_???}", 0);
168 }
169
170 return true;
171 });
172
173 return out;
174}
175
176asl_msg_t *
177configuration_profile_to_asl_msg(const char *ident)
178{
179 xpc_object_t xobj = configuration_profile_copy_property_list(ident);
180 asl_msg_t *out = xpc_object_to_asl_msg(xobj);
181 if (xobj != NULL) xpc_release(xobj);
182 return out;
183}
184
185/* strdup + skip leading and trailing whitespace */
186static char *
187_strdup_clean(const char *s)
188{
189 char *out;
190 const char *first, *last;
191 size_t len;
192
193 if (s == NULL) return NULL;
194
195 first = s;
196 while ((*first == ' ') || (*first == '\t')) first++;
197 len = strlen(first);
198 if (len == 0) return NULL;
199
200 last = first + len - 1;
201 while ((len > 0) && ((*last == ' ') || (*last == '\t')))
202 {
203 last--;
204 len--;
205 }
206
207 if (len == 0) return NULL;
208
209 out = malloc(len + 1);
210 if (out == NULL) return NULL;
211
212 memcpy(out, first, len);
213 out[len] = '\0';
214 return out;
215}
216
217static char **
218_insert_string(char *s, char **l, uint32_t x)
219{
220 int i, len;
221
222 if (s == NULL) return l;
223 if (l == NULL)
224 {
225 l = (char **)malloc(2 * sizeof(char *));
226 if (l == NULL) return NULL;
227
228 l[0] = strdup(s);
229 if (l[0] == NULL)
230 {
231 free(l);
232 return NULL;
233 }
234
235 l[1] = NULL;
236 return l;
237 }
238
239 for (i = 0; l[i] != NULL; i++);
240
241 /* len includes the NULL at the end of the list */
242 len = i + 1;
243
244 l = (char **)reallocf(l, (len + 1) * sizeof(char *));
245 if (l == NULL) return NULL;
246
247 if ((x >= (len - 1)) || (x == IndexNull))
248 {
249 l[len - 1] = strdup(s);
250 if (l[len - 1] == NULL)
251 {
252 free(l);
253 return NULL;
254 }
255
256 l[len] = NULL;
257 return l;
258 }
259
260 for (i = len; i > x; i--) l[i] = l[i - 1];
261 l[x] = strdup(s);
262 if (l[x] == NULL) return NULL;
263
264 return l;
265}
266
267char **
268explode(const char *s, const char *delim)
269{
270 char **l = NULL;
271 const char *p;
272 char *t, quote;
273 int i, n;
274
275 if (s == NULL) return NULL;
276
277 quote = '\0';
278
279 p = s;
280 while (p[0] != '\0')
281 {
282 /* scan forward */
283 for (i = 0; p[i] != '\0'; i++)
284 {
285 if (quote == '\0')
286 {
287 /* not inside a quoted string: check for delimiters and quotes */
288 if (strchr(delim, p[i]) != NULL) break;
289 else if (p[i] == '\'') quote = p[i];
290 else if (p[i] == '"') quote = p[i];
291 }
292 else
293 {
294 /* inside a quoted string - look for matching quote */
295 if (p[i] == quote) quote = '\0';
296 }
297 }
298
299 n = i;
300 t = malloc(n + 1);
301 if (t == NULL) return NULL;
302
303 for (i = 0; i < n; i++) t[i] = p[i];
304 t[n] = '\0';
305 l = _insert_string(t, l, IndexNull);
306 free(t);
307 t = NULL;
308 if (p[i] == '\0') return l;
309 if (p[i + 1] == '\0') l = _insert_string("", l, IndexNull);
310 p = p + i + 1;
311 }
312
313 return l;
314}
315
316void
317free_string_list(char **l)
318{
319 int i;
320
321 if (l == NULL) return;
322 for (i = 0; l[i] != NULL; i++) free(l[i]);
323 free(l);
324}
325
326char *
327get_line_from_file(FILE *f)
328{
329 char *s, *out;
330 size_t len;
331
332 out = fgetln(f, &len);
333 if (out == NULL) return NULL;
334 if (len == 0) return NULL;
335
336 s = malloc(len + 1);
337 if (s == NULL) return NULL;
338
339 memcpy(s, out, len);
340
341 if (s[len - 1] != '\n') len++;
342 s[len - 1] = '\0';
343 return s;
344}
345
346char *
347next_word_from_string(char **s)
348{
349 char *a, *p, *e, *out, s0;
350 int quote1, quote2, len;
351
352 if (s == NULL) return NULL;
353 if (*s == NULL) return NULL;
354
355 s0 = **s;
356
357 quote1 = 0;
358 quote2 = 0;
359
360 p = *s;
361
362 /* allow whole word to be contained in quotes */
363 if (*p == '\'')
364 {
365 quote1 = 1;
366 p++;
367 }
368
369 if (*p == '"')
370 {
371 quote2 = 1;
372 p++;
373 }
374
375 a = p;
376 e = p;
377
378 while (*p != '\0')
379 {
380 if (*p == '\\')
381 {
382 p++;
383 e = p;
384
385 if (*p == '\0')
386 {
387 p--;
388 break;
389 }
390
391 p++;
392 e = p;
393 continue;
394 }
395
396 if (*p == '\'')
397 {
398 if (quote1 == 0) quote1 = 1;
399 else quote1 = 0;
400 }
401
402 if (*p == '"')
403 {
404 if (quote2 == 0) quote2 = 1;
405 else quote2 = 0;
406 }
407
408 if (((*p == ' ') || (*p == '\t')) && (quote1 == 0) && (quote2 == 0))
409 {
410 e = p + 1;
411 break;
412 }
413
414 p++;
415 e = p;
416 }
417
418 *s = e;
419
420 len = p - a;
421
422 /* check for quoted string */
423 if (((s0 == '\'') || (s0 == '"')) && (s0 == a[len-1])) len--;
424
425 if (len == 0) return NULL;
426
427 out = malloc(len + 1);
428 if (out == NULL) return NULL;
429
430 memcpy(out, a, len);
431 out[len] = '\0';
432 return out;
433}
434
435int
436asl_out_mkpath(asl_out_rule_t *r)
437{
438 char tmp[MAXPATHLEN], *p;
439 struct stat sb;
440 int status;
441
442 if (r == NULL) return -1;
443 if (r->dst == NULL) return -1;
444 if (r->dst->path == NULL) return -1;
445
446 snprintf(tmp, sizeof(tmp), "%s", r->dst->path);
447
448 if (r->action != ACTION_ASL_DIR)
449 {
450 p = strrchr(tmp, '/');
451 if (p == NULL) return -1;
452 *p = '\0';
453 }
454
455 memset(&sb, 0, sizeof(struct stat));
456 status = stat(tmp, &sb);
457 if (status == 0)
458 {
459 if (!S_ISDIR(sb.st_mode)) return -1;
460 }
461 else if (errno == ENOENT)
462 {
463 status = mkpath_np(tmp, 0755);
464 }
465
466 return status;
467}
468
469void
470asl_make_timestamp(time_t stamp, uint32_t flags, char *buf, size_t len)
471{
472 struct tm t;
473 uint32_t h, m, s;
474
475 if (buf == NULL) return;
476
477 if (flags & MODULE_FLAG_STYLE_UTC)
478 {
479 memset(&t, 0, sizeof(t));
480 gmtime_r(&stamp, &t);
481 snprintf(buf, len, "%d-%02d-%02dT%02d:%02d:%02dZ", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
482 }
483 else if (flags & MODULE_FLAG_STYLE_UTC_B)
484 {
485 memset(&t, 0, sizeof(t));
486 gmtime_r(&stamp, &t);
487 snprintf(buf, len, "%d%02d%02dT%02d%02d%02dZ", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
488 }
489 else if (flags & MODULE_FLAG_STYLE_LCL)
490 {
491 bool neg = false;
492 memset(&t, 0, sizeof(t));
493 localtime_r(&stamp, &t);
494
495 if ((neg = (t.tm_gmtoff < 0))) t.tm_gmtoff *= -1;
496
497 s = t.tm_gmtoff;
498 h = s / 3600;
499 s %= 3600;
500 m = s / 60;
501 s %= 60;
502
503 if (s > 0) snprintf(buf, len, "%d-%02d-%02dT%02d:%02d:%02d%c%u:%02u:%02u", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, neg ? '-' : '+', h, m, s);
504 else if (m > 0) snprintf(buf, len, "%d-%02d-%02dT%02d:%02d:%02d%c%u:%02u", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, neg ? '-' : '+', h, m);
505 else snprintf(buf, len, "%d-%02d-%02dT%02d:%02d:%02d%c%u", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, neg ? '-' : '+', h);
506 }
507 else if (flags & MODULE_FLAG_STYLE_LCL_B)
508 {
509 bool neg = false;
510 memset(&t, 0, sizeof(t));
511 localtime_r(&stamp, &t);
512
513 if ((neg = (t.tm_gmtoff < 0))) t.tm_gmtoff *= -1;
514
515 s = t.tm_gmtoff;
516 h = s / 3600;
517 s %= 3600;
518 m = s / 60;
519 s %= 60;
520
521 if (s > 0) snprintf(buf, len, "%d%02d%02dT%02d%02d%02d%c%02u%02u%02u", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, neg ? '-' : '+', h, m, s);
522 else if (m > 0) snprintf(buf, len, "%d%02d%02dT%02d%02d%02d%c%02u%02u", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, neg ? '-' : '+', h, m);
523 else snprintf(buf, len, "%d%02d%02dT%02d%02d%02d%c%02u", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, neg ? '-' : '+', h);
524 }
525 else
526 {
527 snprintf(buf, len, "%c%lu", STYLE_SEC_PREFIX_CHAR, stamp);
528 }
529}
530
531void
532asl_make_dst_filename(asl_out_dst_data_t *dst, char *buf, size_t len)
533{
534 if (dst == NULL) return;
535 if (buf == NULL) return;
536
537 if (dst->flags & MODULE_FLAG_BASESTAMP)
538 {
539 char tstamp[32];
540
541 if (dst->stamp == 0) dst->stamp = time(NULL);
542 asl_make_timestamp(dst->stamp, dst->flags, tstamp, sizeof(tstamp));
543 snprintf(buf, len, "%s.%s", dst->path, tstamp);
544 }
545 else
546 {
547 snprintf(buf, len, "%s", dst->path);
548 }
549}
550
551int
552asl_out_dst_checkpoint(asl_out_dst_data_t *dst, uint32_t force)
553{
554 char newpath[MAXPATHLEN];
555 time_t now;
556
557 now = time(NULL);
558
559 /* clock went backwards - force a reset */
560 if (now < start_today) start_today = 0;
561
562 /* check start_today and reset if required */
563 if (now >= (start_today + SECONDS_PER_DAY))
564 {
565 /* use localtime / mktime since start_today might be zero */
566 struct tm t;
567
568 start_today = now;
569
570 localtime_r(&start_today, &t);
571
572 t.tm_sec = 0;
573 t.tm_min = 0;
574 t.tm_hour = 0;
575
576 start_today = mktime(&t);
577 }
578
579 /* sleep to prevent a sub-second rotation */
580 while (now == dst->stamp)
581 {
582 sleep(1);
583 now = time(NULL);
584 }
585
586 if ((dst->stamp == 0) || (dst->size == 0))
587 {
588 struct stat sb;
589
590 memset(&sb, 0, sizeof(struct stat));
591
592 if (stat(dst->path, &sb) < 0)
593 {
594 if (errno == ENOENT) return 0;
595 return -1;
596 }
597
598 if (dst->stamp == 0) dst->stamp = sb.st_birthtimespec.tv_sec;
599 if (dst->stamp == 0) dst->stamp = sb.st_mtimespec.tv_sec;
600 dst->size = sb.st_size;
601 }
602
603 if (force == CHECKPOINT_TEST)
604 {
605 if ((dst->file_max > 0) && (dst->size >= dst->file_max)) force |= CHECKPOINT_SIZE;
606 if (dst->stamp < start_today) force |= CHECKPOINT_TIME;
607
608 if (force == CHECKPOINT_TEST) return 0;
609 }
610
611 if (dst->flags & MODULE_FLAG_TYPE_ASL_DIR)
612 {
613 if (force & CHECKPOINT_SIZE)
614 {
615 snprintf(newpath, sizeof(newpath), "%s.%c%lu", dst->fname, STYLE_SEC_PREFIX_CHAR, dst->stamp);
616 rename(dst->fname, newpath);
617 }
618 else
619 {
620 return 0;
621 }
622 }
623
624 if ((dst->flags & MODULE_FLAG_BASESTAMP) == 0)
625 {
626 char tstamp[32];
627
628 asl_make_timestamp(dst->stamp, dst->flags, tstamp, sizeof(tstamp));
629 snprintf(newpath, sizeof(newpath), "%s.%s", dst->path, tstamp);
630 rename(dst->path, newpath);
631 }
632
633 dst->stamp = 0;
634 dst->size = 0;
635
636 return 1;
637}
638
639int
640asl_check_option(aslmsg msg, const char *opt)
641{
642 const char *p;
643 uint32_t len;
644
645 if (msg == NULL) return 0;
646 if (opt == NULL) return 0;
647
648 len = strlen(opt);
649 if (len == 0) return 0;
650
651 p = asl_get(msg, ASL_KEY_OPTION);
652 if (p == NULL) return 0;
653
654 while (*p != '\0')
655 {
656 while ((*p == ' ') || (*p == '\t') || (*p == ',')) p++;
657 if (*p == '\0') return 0;
658
659 if (strncasecmp(p, opt, len) == 0)
660 {
661 p += len;
662 if ((*p == ' ') || (*p == '\t') || (*p == ',') || (*p == '\0')) return 1;
663 }
664
665 while ((*p != ' ') && (*p != '\t') && (*p != ',') && (*p != '\0')) p++;
666 }
667
668 return 0;
669}
670
671void
672asl_out_dst_data_release(asl_out_dst_data_t *dst)
673{
674 if (dst == NULL) return;
675
676 if (dst->refcount > 0) dst->refcount--;
677 if (dst->refcount > 0) return;
678
679 free(dst->path);
680 free(dst->fname);
681 free(dst->rotate_dir);
682 free(dst->fmt);
683#if !TARGET_IPHONE_SIMULATOR
684 free(dst->uid);
685 free(dst->gid);
686#endif
687 free(dst);
688}
689
690asl_out_dst_data_t *
691asl_out_dst_data_retain(asl_out_dst_data_t *dst)
692{
693 if (dst == NULL) return NULL;
694 dst->refcount++;
695 return dst;
696}
697
698/* set owner, group, mode, and acls for a file */
699int
700asl_out_dst_set_access(int fd, asl_out_dst_data_t *dst)
701{
702#if !TARGET_IPHONE_SIMULATOR
703 uid_t fuid = 0;
704 gid_t fgid = 80;
705#if !TARGET_OS_EMBEDDED
706 int status;
707 acl_t acl;
708 uuid_t uuid;
709 acl_entry_t entry;
710 acl_permset_t perms;
711 uint32_t i;
712#endif
713#endif
714
715 if (dst == NULL) return -1;
716 if (fd < 0) return -1;
717
718#if TARGET_IPHONE_SIMULATOR
719 return fd;
720#else
721
722 if (dst->nuid > 0) fuid = dst->uid[0];
723 if (dst->ngid > 0) fgid = dst->gid[0];
724
725 fchown(fd, fuid, fgid);
726
727#if TARGET_OS_EMBEDDED
728 return fd;
729#else
730 acl = acl_init(1);
731
732 for (i = 0; i < dst->ngid; i++)
733 {
734 if (dst->gid[i] == -2) continue;
735
736 /*
737 * Don't bother setting group access if this is
738 * file's group and the file is group-readable.
739 */
740 if ((dst->gid[i] == fgid) && (dst->mode & 0040)) continue;
741
742 status = mbr_gid_to_uuid(dst->gid[i], uuid);
743 if (status != 0)
744 {
745 dst->gid[i] = -2;
746 continue;
747 }
748
749 status = acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY);
750 if (status != 0) goto asl_file_create_return;
751
752 status = acl_set_tag_type(entry, ACL_EXTENDED_ALLOW);
753 if (status != 0) goto asl_file_create_return;
754
755 status = acl_set_qualifier(entry, &uuid);
756 if (status != 0) goto asl_file_create_return;
757
758 status = acl_get_permset(entry, &perms);
759 if (status != 0) goto asl_file_create_return;
760
761 status = acl_add_perm(perms, ACL_READ_DATA);
762 if (status != 0) goto asl_file_create_return;
763 }
764
765 for (i = 0; i < dst->nuid; i++)
766 {
767 if (dst->uid[i] == -2) continue;
768
769 /*
770 * Don't bother setting user access if this is
771 * file's owner and the file is owner-readable.
772 */
773 if ((dst->uid[i] == fuid) && (dst->mode & 0400)) continue;
774
775 status = mbr_uid_to_uuid(dst->uid[i], uuid);
776 if (status != 0)
777 {
778 dst->uid[i] = -2;
779 continue;
780 }
781
782 status = acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY);
783 if (status != 0) goto asl_file_create_return;
784
785 status = acl_set_tag_type(entry, ACL_EXTENDED_ALLOW);
786 if (status != 0) goto asl_file_create_return;
787
788 status = acl_set_qualifier(entry, &uuid);
789 if (status != 0) goto asl_file_create_return;
790
791 status = acl_get_permset(entry, &perms);
792 if (status != 0) goto asl_file_create_return;
793
794 status = acl_add_perm(perms, ACL_READ_DATA);
795 if (status != 0) goto asl_file_create_return;
796 }
797
798 status = acl_set_fd(fd, acl);
799 if (status != 0)
800 {
801 close(fd);
802 fd = -1;
803 }
804
805asl_file_create_return:
806
807 acl_free(acl);
808 return fd;
809#endif /* !TARGET_OS_EMBEDDED */
810#endif /* !TARGET_IPHONE_SIMULATOR */
811}
812
813/* create a file with acls */
814int
815asl_out_dst_file_create_open(asl_out_dst_data_t *dst)
816{
817 int fd, status;
818 struct stat sb;
819 char outpath[MAXPATHLEN];
820
821 if (dst == NULL) return -1;
822 if (dst->path == NULL) return -1;
823
824 asl_make_dst_filename(dst, outpath, sizeof(outpath));
825
826 memset(&sb, 0, sizeof(struct stat));
827 status = stat(outpath, &sb);
828 if (status == 0)
829 {
830 /* must be a regular file */
831 if (!S_ISREG(sb.st_mode)) return -1;
832
833 /* file exists */
834 fd = open(outpath, O_RDWR | O_APPEND | O_EXCL, 0);
835
836 if (dst->stamp == 0) dst->stamp = sb.st_birthtimespec.tv_sec;
837 if (dst->stamp == 0) dst->stamp = sb.st_mtimespec.tv_sec;
838 dst->size = sb.st_size;
839
840 return fd;
841 }
842 else if (errno != ENOENT)
843 {
844 /* stat error other than non-existant file */
845 return -1;
846 }
847
848 fd = open(outpath, O_RDWR | O_CREAT | O_EXCL, (dst->mode & 0666));
849 if (fd < 0) return -1;
850
851 dst->stamp = time(NULL);
852
853 fd = asl_out_dst_set_access(fd, dst);
854 if (fd < 0) unlink(outpath);
855
856 return fd;
857}
858
859void
860asl_out_module_free(asl_out_module_t *m)
861{
862 asl_out_rule_t *r, *n;
863 asl_out_module_t *x;
864
865 while (m != NULL)
866 {
867 x = m->next;
868
869 /* free name */
870 free(m->name);
871
872 /* free ruleset */
873 r = m->ruleset;
874 while (r != NULL)
875 {
876 n = r->next;
877 if (r->dst != NULL) asl_out_dst_data_release(r->dst);
878
879 if (r->query != NULL) asl_msg_release(r->query);
880 free(r->options);
881 free(r);
882 r = n;
883 }
884
885 free(m);
886 m = x;
887 }
888}
889
890asl_out_module_t *
891asl_out_module_new(const char *name)
892{
893 asl_out_module_t *out = (asl_out_module_t *)calloc(1, sizeof(asl_out_module_t));
894
895 if (out == NULL) return NULL;
896 if (name == NULL) return NULL;
897
898 out->name = strdup(name);
899 if (out->name == NULL)
900 {
901 free(out);
902 return NULL;
903 }
904
905 out->flags = MODULE_FLAG_ENABLED;
906
907 return out;
908}
909
910/* Skip over query */
911static char *
912_asl_out_module_find_action(char *s)
913{
914 char *p;
915
916 p = s;
917 if (p == NULL) return NULL;
918
919 /* Skip command character (?, Q, *, or =) */
920 p++;
921
922 forever
923 {
924 /* Find next [ */
925 while ((*p == ' ') || (*p == '\t')) p++;
926
927 if (*p == '\0') return NULL;
928 if (*p != '[') return p;
929
930 /* skip to closing ] */
931 while (*p != ']')
932 {
933 p++;
934 if (*p == '\\')
935 {
936 p++;
937 if (*p == ']') p++;
938 }
939 }
940
941 if (*p == ']') p++;
942 }
943
944 /* skip whitespace */
945 while ((*p == ' ') || (*p == '\t')) p++;
946
947 return NULL;
948}
949
950/*
951 * Parse parameter setting line
952 *
953 * = param options
954 * evaluated once when module is initialized
955 *
956 * = [query] param options
957 * evaluated for each message, param set if message matches query
958 *
959 * = param [File path]
960 * evaluated once when module is initialized
961 * evaluated when change notification received for path
962 *
963 * = param [Plist path] ...
964 * evaluated once when module is initialized
965 * evaluated when change notification received for path
966 *
967 * = param [Profile name] ...
968 * evaluated once when module is initialized
969 * evaluated when change notification received for profile
970 */
971static asl_out_rule_t *
972_asl_out_module_parse_set_param(asl_out_module_t *m, char *s)
973{
974 char *act, *p, *q;
975 asl_out_rule_t *out, *rule;
976
977 if (m == NULL) return NULL;
978
979 out = (asl_out_rule_t *)calloc(1, sizeof(asl_out_rule_t));
980 if (out == NULL) return NULL;
981
982 q = s + 1;
983 while ((*q == ' ') || (*q == '\'')) q++;
984 out->action = ACTION_SET_PARAM;
985
986 if (*q == '[')
987 {
988 /* = [query] param options */
989 act = _asl_out_module_find_action(s);
990 if (act == NULL)
991 {
992 free(out);
993 return NULL;
994 }
995
996 out->options = _strdup_clean(act);
997
998 p = act - 1;
999 if (*p == ']') p = act;
1000 *p = '\0';
1001
1002 *s = 'Q';
1003 out->query = asl_msg_from_string(s);
1004 if (out->query == NULL)
1005 {
1006 free(out->options);
1007 free(out);
1008 return NULL;
1009 }
1010 }
1011 else
1012 {
1013 /* = param ... */
1014 p = strchr(s, '[');
1015 if (p == NULL)
1016 {
1017 /* = param options */
1018 out->options = _strdup_clean(q);
1019 }
1020 else
1021 {
1022 /* = param [query] */
1023 if ((!strncmp(p, "[File ", 6)) || (!strncmp(p, "[File\t", 6))) out->action = ACTION_SET_FILE;
1024 else if ((!strncmp(p, "[Plist ", 7)) || (!strncmp(p, "[Plist\t", 7))) out->action = ACTION_SET_PLIST;
1025 else if ((!strncmp(p, "[Profile ", 9)) || (!strncmp(p, "[Profile\t", 9))) out->action = ACTION_SET_PROF;
1026
1027 p--;
1028 *p = '\0';
1029 out->options = _strdup_clean(q);
1030
1031 *p = ' ';
1032 p--;
1033 *p = 'Q';
1034 out->query = asl_msg_from_string(p);
1035 if (out->query == NULL)
1036 {
1037 free(out->options);
1038 free(out);
1039 return NULL;
1040 }
1041 }
1042 }
1043
1044 if (m->ruleset == NULL) m->ruleset = out;
1045 else
1046 {
1047 for (rule = m->ruleset; rule->next != NULL; rule = rule->next);
1048 rule->next = out;
1049 }
1050
1051 return out;
1052}
1053
1054#if !TARGET_IPHONE_SIMULATOR
1055static void
1056_dst_add_uid(asl_out_dst_data_t *dst, char *s)
1057{
1058 int i;
1059 uid_t uid;
1060
1061 if (dst == NULL) return;
1062 if (s == NULL) return;
1063
1064 uid = atoi(s);
1065
1066 for (i = 0 ; i < dst->nuid; i++)
1067 {
1068 if (dst->uid[i] == uid) return;
1069 }
1070
1071 dst->uid = reallocf(dst->uid, (dst->nuid + 1) * sizeof(uid_t));
1072 if (dst->uid == NULL)
1073 {
1074 dst->nuid = 0;
1075 return;
1076 }
1077
1078 dst->uid[dst->nuid++] = uid;
1079}
1080
1081static void
1082_dst_add_gid(asl_out_dst_data_t *dst, char *s)
1083{
1084 int i;
1085 gid_t gid;
1086
1087 if (dst == NULL) return;
1088 if (s == NULL) return;
1089
1090 gid = atoi(s);
1091
1092 for (i = 0 ; i < dst->ngid; i++)
1093 {
1094 if (dst->gid[i] == gid) return;
1095 }
1096
1097 dst->gid = reallocf(dst->gid, (dst->ngid + 1) * sizeof(gid_t));
1098 if (dst->gid == NULL)
1099 {
1100 dst->ngid = 0;
1101 return;
1102 }
1103
1104 dst->gid[dst->ngid++] = gid;
1105}
1106#endif /* !TARGET_IPHONE_SIMULATOR */
1107
1108static char *
1109_dst_format_string(char *s)
1110{
1111 char *fmt;
1112 size_t i, len, n;
1113
1114 if (s == NULL) return NULL;
1115
1116 len = strlen(s);
1117
1118 /* format string can be enclosed by quotes */
1119 if ((len >= 2) && ((s[0] == '\'') || (s[0] == '"')) && (s[len-1] == s[0]))
1120 {
1121 s++;
1122 len -= 2;
1123 }
1124
1125 n = 0;
1126 for (i = 0; i < len; i++) if (s[i] == '\\') n++;
1127
1128 fmt = malloc(1 + len - n);
1129 if (fmt == NULL) return NULL;
1130
1131 for (i = 0, n = 0; i < len; i++) if (s[i] != '\\') fmt[n++] = s[i];
1132 fmt[n] = '\0';
1133 return fmt;
1134}
1135
1136size_t
1137asl_str_to_size(char *s)
1138{
1139 size_t len, n, max;
1140 char x;
1141
1142 if (s == NULL) return 0;
1143
1144 len = strlen(s);
1145 if (len == 0) return 0;
1146
1147 n = 1;
1148 x = s[len - 1];
1149 if (x > 90) x -= 32;
1150 if (x == 'K') n = 1ll << 10;
1151 else if (x == 'M') n = 1ll << 20;
1152 else if (x == 'G') n = 1ll << 30;
1153
1154 max = atoll(s) * n;
1155 return max;
1156}
1157
1158static bool
1159_dst_path_match(const char *newpath, const char *existingpath)
1160{
1161 if (newpath == NULL) return (existingpath == NULL);
1162 if (existingpath == NULL) return false;
1163 if (newpath[0] == '/') return (strcmp(newpath, existingpath) == 0);
1164
1165 const char *trailing = strrchr(existingpath, '/');
1166 if (trailing == NULL) return (strcmp(newpath, existingpath) == 0);
1167 trailing++;
1168 return (strcmp(newpath, trailing) == 0);
1169}
1170
1171static asl_out_dst_data_t *
1172_asl_out_module_parse_dst(asl_out_module_t *m, char *s, mode_t def_mode)
1173{
1174 asl_out_rule_t *out, *rule;
1175 asl_out_dst_data_t *dst;
1176 char *p, *opts, *path;
1177 char **path_parts;
1178 int has_dotdot, recursion_limit;
1179
1180 if (m == NULL) return NULL;
1181 if (s == NULL) return NULL;
1182
1183 /* skip whitespace */
1184 while ((*s == ' ') || (*s == '\t')) s++;
1185
1186 opts = s;
1187 path = next_word_from_string(&opts);
1188 if (path == NULL) return NULL;
1189
1190 /*
1191 * Check path for ".." component (not permitted).
1192 * Also substitute environment variables.
1193 */
1194 has_dotdot = 0;
1195 path_parts = explode(path, "/");
1196 asl_string_t *processed_path = asl_string_new(ASL_ENCODE_NONE);
1197 recursion_limit = 5;
1198
1199 while ((recursion_limit > 0) && (path_parts != NULL) && (processed_path != NULL))
1200 {
1201 uint32_t i;
1202 int did_sub = 0;
1203
1204 for (i = 0; path_parts[i] != NULL; i++)
1205 {
1206 if (!strncmp(path_parts[i], "$ENV(", 5))
1207 {
1208 char *p = strchr(path_parts[i], ')');
1209 if (p != NULL) *p = '\0';
1210 char *env_val = getenv(path_parts[i] + 5);
1211 if (env_val != NULL)
1212 {
1213 did_sub = 1;
1214
1215 if (env_val[0] != '/') asl_string_append_char_no_encoding(processed_path, '/');
1216 asl_string_append_no_encoding(processed_path, env_val);
1217 }
1218 }
1219 else
1220 {
1221 if (i == 0)
1222 {
1223 if (path_parts[0][0] != '\0') asl_string_append_no_encoding(processed_path, path_parts[i]);
1224 }
1225 else
1226 {
1227 asl_string_append_char_no_encoding(processed_path, '/');
1228 asl_string_append_no_encoding(processed_path, path_parts[i]);
1229 }
1230 }
1231
1232 if ((has_dotdot == 0) && (!strcmp(path_parts[i], ".."))) has_dotdot = 1;
1233 }
1234
1235 free_string_list(path_parts);
1236
1237 if ((did_sub == 0) || (has_dotdot == 1))
1238 {
1239 path_parts = NULL;
1240 }
1241 else
1242 {
1243 /* substitution might have added a ".." so check the new path */
1244 free(path);
1245 path = asl_string_free_return_bytes(processed_path);
1246 processed_path = asl_string_new(ASL_ENCODE_NONE);
1247 path_parts = explode(path, "/");
1248 recursion_limit--;
1249 }
1250 }
1251
1252 free(path);
1253
1254 if ((has_dotdot != 0) || (recursion_limit == 0))
1255 {
1256 asl_string_free(processed_path);
1257 return NULL;
1258 }
1259
1260 path = asl_string_free_return_bytes(processed_path);
1261
1262 /* check if there's already a dst for this path */
1263 for (rule = m->ruleset; rule != NULL; rule = rule->next)
1264 {
1265 if (rule->action != ACTION_OUT_DEST) continue;
1266
1267 dst = rule->dst;
1268 if (dst == NULL) continue;
1269
1270 if (_dst_path_match(path, dst->path))
1271 {
1272 free(path);
1273 return dst;
1274 }
1275 }
1276
1277 if (path[0] != '/')
1278 {
1279 char *t = path;
1280 const char *log_root = "/var/log";
1281
1282#if TARGET_IPHONE_SIMULATOR
1283 log_root = getenv("IPHONE_SIMULATOR_LOG_ROOT");
1284 assert(log_root);
1285#endif
1286
1287 if (!strcmp(m->name, ASL_MODULE_NAME)) asprintf(&path, "%s/%s", log_root, t);
1288 else asprintf(&path, "%s/module/%s/%s", log_root, m->name, t);
1289
1290 free(t);
1291 }
1292
1293 out = (asl_out_rule_t *)calloc(1, sizeof(asl_out_rule_t));
1294 dst = (asl_out_dst_data_t *)calloc(1, sizeof(asl_out_dst_data_t));
1295 if ((out == NULL) || (dst == NULL))
1296 {
1297 free(path);
1298 free(out);
1299 free(dst);
1300 return NULL;
1301 }
1302
1303 dst->refcount = 1;
1304 dst->path = path;
1305 dst->mode = def_mode;
1306 dst->ttl = DEFAULT_TTL;
1307 dst->flags = MODULE_FLAG_COALESCE;
1308
1309 while (NULL != (p = next_word_from_string(&opts)))
1310 {
1311 if (KEYMATCH(p, "mode=")) dst->mode = strtol(p+5, NULL, 0);
1312 else if (KEYMATCH(p, "ttl=")) dst->ttl = strtol(p+4, NULL, 0);
1313#if !TARGET_IPHONE_SIMULATOR
1314 else if (KEYMATCH(p, "uid=")) _dst_add_uid(dst, p+4);
1315 else if (KEYMATCH(p, "gid=")) _dst_add_gid(dst, p+4);
1316#endif
1317 else if (KEYMATCH(p, "fmt=")) dst->fmt = _dst_format_string(p+4);
1318 else if (KEYMATCH(p, "format=")) dst->fmt = _dst_format_string(p+7);
1319 else if (KEYMATCH(p, "dest=")) dst->rotate_dir = _strdup_clean(p+5);
1320 else if (KEYMATCH(p, "dst=")) dst->rotate_dir = _strdup_clean(p+4);
1321 else if (KEYMATCH(p, "coalesce="))
1322 {
1323 if (KEYMATCH(p+9, "0")) dst->flags &= ~MODULE_FLAG_COALESCE;
1324 else if (KEYMATCH(p+9, "off")) dst->flags &= ~MODULE_FLAG_COALESCE;
1325 else if (KEYMATCH(p+9, "false")) dst->flags &= ~MODULE_FLAG_COALESCE;
1326 }
1327 else if (KEYMATCH(p, "compress")) dst->flags |= MODULE_FLAG_COMPRESS;
1328 else if (KEYMATCH(p, "extern")) dst->flags |= MODULE_FLAG_EXTERNAL;
1329 else if (KEYMATCH(p, "soft")) dst->flags |= MODULE_FLAG_SOFT_WRITE;
1330 else if (KEYMATCH(p, "file_max=")) dst->file_max = asl_str_to_size(p+9);
1331 else if (KEYMATCH(p, "all_max=")) dst->all_max = asl_str_to_size(p+8);
1332 else if (KEYMATCH(p, "style=") || KEYMATCH(p, "rotate="))
1333 {
1334 const char *x = p + 6;
1335
1336 if (KEYMATCH(p, "rotate=")) x++;
1337
1338 dst->flags |= MODULE_FLAG_ROTATE;
1339
1340 if (KEYMATCH(x, "sec") || KEYMATCH(x, "seconds"))
1341 {
1342 dst->flags |= MODULE_FLAG_STYLE_SEC;
1343 }
1344 else if (KEYMATCH(x, "utc") || KEYMATCH(x, "date") || KEYMATCH(x, "zulu"))
1345 {
1346 const char *dash = strchr(x, '-');
1347 if ((dash != NULL) && (*(dash + 1) == 'b')) dst->flags |= MODULE_FLAG_STYLE_UTC_B;
1348 else dst->flags |= MODULE_FLAG_STYLE_UTC;
1349 }
1350 else if (KEYMATCH(x, "local") || KEYMATCH(x, "lcl"))
1351 {
1352 const char *dash = strchr(x, '-');
1353 if ((dash != NULL) && (*(dash + 1) == 'b')) dst->flags |= MODULE_FLAG_STYLE_LCL_B;
1354 else dst->flags |= MODULE_FLAG_STYLE_LCL;
1355 }
1356 else if (KEYMATCH(x, "#") || KEYMATCH(x, "seq") || KEYMATCH(x, "sequence"))
1357 {
1358 dst->flags |= MODULE_FLAG_STYLE_SEQ;
1359 }
1360 else
1361 {
1362 dst->flags |= MODULE_FLAG_STYLE_SEC;
1363 }
1364 }
1365 else if (KEYMATCH(p, "rotate")) dst->flags |= MODULE_FLAG_ROTATE;
1366 else if (KEYMATCH(p, "crashlog"))
1367 {
1368 /* crashlog implies rotation */
1369 dst->flags |= MODULE_FLAG_ROTATE;
1370 dst->flags |= MODULE_FLAG_CRASHLOG;
1371 dst->flags |= MODULE_FLAG_BASESTAMP;
1372 dst->flags &= ~MODULE_FLAG_COALESCE;
1373 }
1374 else if (KEYMATCH(p, "basestamp"))
1375 {
1376 dst->flags |= MODULE_FLAG_BASESTAMP;
1377 }
1378
1379 free(p);
1380 p = NULL;
1381 }
1382
1383#if TARGET_OS_EMBEDDED
1384 /* check for crashreporter files */
1385 if (KEYMATCH(dst->path, _PATH_CRASHREPORTER))
1386 {
1387 dst->flags |= MODULE_FLAG_ROTATE;
1388 dst->flags |= MODULE_FLAG_CRASHLOG;
1389 dst->flags |= MODULE_FLAG_BASESTAMP;
1390 dst->flags &= ~MODULE_FLAG_COALESCE;
1391 }
1392#endif
1393
1394 /* default text file format is "std" */
1395 if (dst->fmt == NULL) dst->fmt = strdup("std");
1396
1397 /* duplicate compression is only possible for std and bsd formats */
1398 if (strcmp(dst->fmt, "std") && strcmp(dst->fmt, "bsd")) dst->flags &= ~MODULE_FLAG_COALESCE;
1399
1400 /* note if format is one of std, bsd, or msg */
1401 if ((!strcmp(dst->fmt, "std")) || (!strcmp(dst->fmt, "bsd")) || (!strcmp(dst->fmt, "msg"))) dst->flags |= MODULE_FLAG_STD_BSD_MSG;
1402
1403 /* MODULE_FLAG_STYLE_SEQ can not be used with MODULE_FLAG_BASESTAMP */
1404 if ((dst->flags & MODULE_FLAG_BASESTAMP) && (dst->flags & MODULE_FLAG_STYLE_SEQ))
1405 {
1406 dst->flags &= ~MODULE_FLAG_STYLE_SEQ;
1407 dst->flags |= MODULE_FLAG_STYLE_SEC;
1408 }
1409
1410 /* set time format for raw output */
1411 if (!strcmp(dst->fmt, "raw")) dst->tfmt = "sec";
1412
1413 out->action = ACTION_OUT_DEST;
1414 out->dst = dst;
1415
1416 /* dst rules go first */
1417 out->next = m->ruleset;
1418 m->ruleset = out;
1419
1420 return dst;
1421}
1422
1423static asl_out_rule_t *
1424_asl_out_module_parse_query_action(asl_out_module_t *m, char *s)
1425{
1426 char *act, *p;
1427 asl_out_rule_t *out, *rule;
1428
1429 if (m == NULL) return NULL;
1430
1431 out = (asl_out_rule_t *)calloc(1, sizeof(asl_out_rule_t));
1432 if (out == NULL) return NULL;
1433
1434 act = _asl_out_module_find_action(s);
1435 if (act == NULL) return NULL;
1436
1437 /* find whitespace delimiter */
1438 p = strchr(act, ' ');
1439 if (p == NULL) p = strchr(act, '\t');
1440 if (p != NULL) *p = '\0';
1441
1442 if (!strcasecmp(act, "ignore")) out->action = ACTION_IGNORE;
1443 else if (!strcasecmp(act, "skip")) out->action = ACTION_SKIP;
1444 else if (!strcasecmp(act, "claim")) out->action = ACTION_CLAIM;
1445 else if (!strcasecmp(act, "notify")) out->action = ACTION_NOTIFY;
1446 else if (!strcasecmp(act, "file")) out->action = ACTION_FILE;
1447 else if (!strcasecmp(act, "asl_file")) out->action = ACTION_ASL_FILE;
1448 else if (!strcasecmp(act, "directory")) out->action = ACTION_ASL_DIR;
1449 else if (!strcasecmp(act, "dir")) out->action = ACTION_ASL_DIR;
1450 else if (!strcasecmp(act, "asl_directory")) out->action = ACTION_ASL_DIR;
1451 else if (!strcasecmp(act, "asl_dir")) out->action = ACTION_ASL_DIR;
1452 else if (!strcasecmp(act, "store_dir")) out->action = ACTION_ASL_DIR;
1453 else if (!strcasecmp(act, "store_directory")) out->action = ACTION_ASL_DIR;
1454 else if (!strcasecmp(act, "control")) out->action = ACTION_CONTROL;
1455 else if (!strcasecmp(act, "save")) out->action = ACTION_ASL_STORE;
1456 else if (!strcasecmp(act, "store")) out->action = ACTION_ASL_STORE;
1457 else if (!strcasecmp(act, "access")) out->action = ACTION_ACCESS;
1458 else if (!strcmp(m->name, ASL_MODULE_NAME))
1459 {
1460 /* actions only allowed in com.apple.asl */
1461 if (!strcasecmp(act, "broadcast")) out->action = ACTION_BROADCAST;
1462 else if (!strcasecmp(act, "forward")) out->action = ACTION_FORWARD;
1463 }
1464
1465 if (out->action == ACTION_NONE)
1466 {
1467 free(out);
1468 return NULL;
1469 }
1470
1471 /* options follow delimited (now zero) */
1472 if (p != NULL)
1473 {
1474 /* skip whitespace */
1475 while ((*p == ' ') || (*p == '\t')) p++;
1476
1477 out->options = _strdup_clean(p+1);
1478
1479 if (out->options == NULL)
1480 {
1481 free(out);
1482 return NULL;
1483 }
1484 }
1485
1486 p = act - 1;
1487
1488 *p = '\0';
1489
1490 if (*s== '*')
1491 {
1492 out->query = asl_msg_new(ASL_TYPE_QUERY);
1493 }
1494 else
1495 {
1496 *s = 'Q';
1497 out->query = asl_msg_from_string(s);
1498 }
1499
1500 if (out->query == NULL)
1501 {
1502 free(out->options);
1503 free(out);
1504 return NULL;
1505 }
1506
1507 /* store /some/path means save to an asl file */
1508 if ((out->action == ACTION_ASL_STORE) && (out->options != NULL)) out->action = ACTION_ASL_FILE;
1509
1510 if ((out->action == ACTION_FILE) || (out->action == ACTION_ASL_FILE) || (out->action == ACTION_ASL_DIR))
1511 {
1512 mode_t def_mode = 0644;
1513 if (out->action == ACTION_ASL_DIR) def_mode = 0755;
1514
1515 out->dst = asl_out_dst_data_retain(_asl_out_module_parse_dst(m, out->options, def_mode));
1516 if (out->dst == NULL)
1517 {
1518 out->action = ACTION_NONE;
1519 return out;
1520 }
1521
1522 if ((out->action == ACTION_FILE) && (out->dst != NULL) && (out->dst->fmt != NULL) && (!strcasecmp(out->dst->fmt, "asl")))
1523 {
1524 out->action = ACTION_ASL_FILE;
1525 }
1526
1527 if ((out->action == ACTION_ASL_FILE) && (out->dst != NULL))
1528 {
1529 /* remove meaningless flags */
1530 out->dst->flags &= ~MODULE_FLAG_COALESCE;
1531 out->dst->flags &= ~MODULE_FLAG_STD_BSD_MSG;
1532 out->dst->flags |= MODULE_FLAG_TYPE_ASL;
1533 }
1534
1535 if (out->action == ACTION_ASL_DIR)
1536 {
1537 /* remove meaningless flags */
1538 out->dst->flags &= ~MODULE_FLAG_ROTATE;
1539 out->dst->flags &= ~MODULE_FLAG_COALESCE;
1540 out->dst->flags &= ~MODULE_FLAG_STD_BSD_MSG;
1541 out->dst->flags |= MODULE_FLAG_TYPE_ASL_DIR;
1542 }
1543
1544 /* only ACTION_FILE and ACTION_ASL_FILE may rotate */
1545 if ((out->action != ACTION_FILE) && (out->action != ACTION_ASL_FILE))
1546 {
1547 out->dst->flags &= ~MODULE_FLAG_ROTATE;
1548 }
1549
1550#if !TARGET_IPHONE_SIMULATOR
1551 if (out->dst->nuid == 0) _dst_add_uid(out->dst, "0");
1552 if (out->dst->ngid == 0) _dst_add_gid(out->dst, "80");
1553#endif
1554 }
1555
1556 if (m->ruleset == NULL) m->ruleset = out;
1557 else
1558 {
1559 for (rule = m->ruleset; rule->next != NULL; rule = rule->next);
1560 rule->next = out;
1561 }
1562
1563 return out;
1564}
1565
1566asl_out_rule_t *
1567asl_out_module_parse_line(asl_out_module_t *m, char *s)
1568{
1569 while ((*s == ' ') || (*s == '\t')) s++;
1570
1571 if ((*s == 'Q') || (*s == '?') || (*s == '*'))
1572 {
1573 return _asl_out_module_parse_query_action(m, s);
1574 }
1575 else if (*s == '=')
1576 {
1577 return _asl_out_module_parse_set_param(m, s);
1578 }
1579 else if (*s == '>')
1580 {
1581 _asl_out_module_parse_dst(m, s + 1, 0644);
1582 }
1583
1584 return NULL;
1585}
1586
1587asl_out_module_t *
1588asl_out_module_init_from_file(const char *name, FILE *f)
1589{
1590 asl_out_module_t *out;
1591 char *line;
1592
1593 if (f == NULL) return NULL;
1594
1595 out = asl_out_module_new(name);
1596 if (out == NULL) return NULL;
1597
1598 /* read and parse config file */
1599 while (NULL != (line = get_line_from_file(f)))
1600 {
1601 asl_out_module_parse_line(out, line);
1602 free(line);
1603 }
1604
1605 return out;
1606}
1607
1608static asl_out_module_t *
1609_asl_out_module_find(asl_out_module_t *list, const char *name)
1610{
1611 asl_out_module_t *x;
1612
1613 if (list == NULL) return NULL;
1614 if (name == NULL) return NULL;
1615
1616 for (x = list; x != NULL; x = x->next)
1617 {
1618 if ((x->name != NULL) && (!strcmp(x->name, name))) return x;
1619 }
1620
1621 return NULL;
1622}
1623
1624static void
1625_asl_out_module_read_and_merge_dir(asl_out_module_t **list, const char *path, uint32_t flags)
1626{
1627 DIR *d;
1628 struct dirent *ent;
1629 FILE *f;
1630 asl_out_module_t *last, *x;
1631
1632 if (list == NULL) return;
1633 if (path == NULL) return;
1634
1635 last = *list;
1636 if (last != NULL)
1637 {
1638 while (last->next != NULL) last = last->next;
1639 }
1640
1641 d = opendir(path);
1642 if (d != NULL)
1643 {
1644 while (NULL != (ent = readdir(d)))
1645 {
1646 if ((ent->d_name != NULL) && (ent->d_name[0] != '.'))
1647 {
1648 /* merge: skip this file if we already have a module with this name */
1649 if (_asl_out_module_find(*list, ent->d_name) != NULL) continue;
1650
1651 char tmp[MAXPATHLEN];
1652 snprintf(tmp, sizeof(tmp), "%s/%s", path, ent->d_name);
1653 f = fopen(tmp, "r");
1654 if (f != NULL)
1655 {
1656 x = asl_out_module_init_from_file(ent->d_name, f);
1657 fclose(f);
1658
1659 if (x != NULL)
1660 {
1661 x->flags |= flags;
1662
1663 if (!strcmp(ent->d_name, ASL_MODULE_NAME))
1664 {
1665 /* com.apple.asl goes at the head of the list */
1666 x->next = *list;
1667 *list = x;
1668 if (last == NULL) last = *list;
1669 }
1670 else if (*list == NULL)
1671 {
1672 *list = x;
1673 last = *list;
1674 }
1675 else
1676 {
1677 last->next = x;
1678 last = x;
1679 }
1680 }
1681 }
1682 }
1683 }
1684
1685 closedir(d);
1686 }
1687}
1688
1689asl_out_module_t *
1690asl_out_module_init(void)
1691{
1692 asl_out_module_t *out = NULL;
1693
1694#if TARGET_IPHONE_SIMULATOR
1695 char *sim_root_path, *sim_resources_path;
1696 char *asl_conf, *asl_conf_dir, *asl_conf_local_dir;
1697
1698 sim_root_path = getenv("IPHONE_SIMULATOR_ROOT");
1699 assert(sim_root_path);
1700
1701 sim_resources_path = getenv("IPHONE_SHARED_RESOURCES_DIRECTORY");
1702 assert(sim_resources_path);
1703
1704 asprintf(&asl_conf, "%s%s", sim_root_path, _PATH_ASL_CONF);
1705 asprintf(&asl_conf_dir, "%s%s", sim_root_path, _PATH_ASL_CONF_DIR);
1706 asprintf(&asl_conf_local_dir, "%s%s", sim_resources_path, _PATH_ASL_CONF_DIR);
1707
1708 _asl_out_module_read_and_merge_dir(&out, asl_conf_local_dir, MODULE_FLAG_LOCAL);
1709 free(asl_conf_local_dir);
1710
1711 _asl_out_module_read_and_merge_dir(&out, asl_conf_dir, 0);
1712 free(asl_conf_dir);
1713#else
1714 _asl_out_module_read_and_merge_dir(&out, _PATH_ASL_CONF_LOCAL_DIR, MODULE_FLAG_LOCAL);
1715 _asl_out_module_read_and_merge_dir(&out, _PATH_ASL_CONF_DIR, 0);
1716#endif
1717
1718 if (_asl_out_module_find(out, ASL_MODULE_NAME) == NULL)
1719 {
1720 /* system just has old-style /etc/asl.conf */
1721#if TARGET_IPHONE_SIMULATOR
1722 FILE *f = fopen(asl_conf, "r");
1723 free(asl_conf);
1724#else
1725 FILE *f = fopen(_PATH_ASL_CONF, "r");
1726#endif
1727 if (f != NULL)
1728 {
1729 asl_out_module_t *x = asl_out_module_init_from_file(ASL_MODULE_NAME, f);
1730 fclose(f);
1731 if (x != NULL)
1732 {
1733 x->next = out;
1734 out = x;
1735 }
1736 }
1737 }
1738
1739 return out;
1740}
1741
1742/*
1743 * Print rule
1744 */
1745char *
1746asl_out_module_rule_to_string(asl_out_rule_t *r)
1747{
1748 uint32_t len;
1749 char *str, *out;
1750
1751 if (r == NULL)
1752 {
1753 asprintf(&out, "NULL rule");
1754 return out;
1755 }
1756
1757 str = asl_msg_to_string(r->query, &len);
1758
1759 asprintf(&out, " %s%s%s%s%s",
1760 asl_out_action_name[r->action],
1761 (r->query == NULL) ? "" : " ",
1762 (r->query == NULL) ? "" : str,
1763 (r->options == NULL) ? "" : " ",
1764 (r->options == NULL) ? "" : r->options);
1765
1766 free(str);
1767 return out;
1768}
1769
1770/*
1771 * Print module
1772 */
1773void
1774asl_out_module_print(FILE *f, asl_out_module_t *m)
1775{
1776 asl_out_rule_t *r, *n;
1777 asl_out_dst_data_t *o;
1778 uint32_t i;
1779
1780 n = NULL;
1781 for (r = m->ruleset; r != NULL; r = n)
1782 {
1783 uint32_t len;
1784 char *str = asl_msg_to_string(r->query, &len);
1785
1786 fprintf(f, " %s", asl_out_action_name[r->action]);
1787 if (r->query != NULL) fprintf(f, " %s", str);
1788 if (r->options != NULL) fprintf(f, " %s", r->options);
1789 if (r->action == ACTION_OUT_DEST)
1790 {
1791 o = r->dst;
1792 if (o == NULL)
1793 {
1794 fprintf(f, " data: NULL");
1795 }
1796 else
1797 {
1798 fprintf(f, "%s\n", o->path);
1799 fprintf(f, " rules: %u\n", o->refcount - 1);
1800 fprintf(f, " dest: %s\n", (o->rotate_dir == NULL) ? "(none)" : o->rotate_dir);
1801 fprintf(f, " format: %s\n", (o->fmt == NULL) ? "std" : o->fmt);
1802 fprintf(f, " time_format: %s\n", (o->tfmt == NULL) ? "lcl" : o->tfmt);
1803 fprintf(f, " flags: 0x%08x", o->flags);
1804 if (o->flags != 0)
1805 {
1806 char c = '(';
1807 fprintf(f, " ");
1808 if (o->flags & MODULE_FLAG_ENABLED)
1809 {
1810 fprintf(f, "%cenabled", c);
1811 c = ' ';
1812 }
1813 if (o->flags & MODULE_FLAG_LOCAL)
1814 {
1815 fprintf(f, "%clocal", c);
1816 c = ' ';
1817 }
1818 if (o->flags & MODULE_FLAG_ROTATE)
1819 {
1820 fprintf(f, "%crotate", c);
1821 c = ' ';
1822 }
1823 if (o->flags & MODULE_FLAG_COALESCE)
1824 {
1825 fprintf(f, "%ccoalesce", c);
1826 c = ' ';
1827 }
1828 if (o->flags & MODULE_FLAG_COMPRESS)
1829 {
1830 fprintf(f, "%ccompress", c);
1831 c = ' ';
1832 }
1833 if (o->flags & MODULE_FLAG_EXTERNAL)
1834 {
1835 fprintf(f, "%cexternal", c);
1836 c = ' ';
1837 }
1838 if (o->flags & MODULE_FLAG_STYLE_SEC)
1839 {
1840 fprintf(f, "%cseconds", c);
1841 c = ' ';
1842 }
1843 if (o->flags & MODULE_FLAG_STYLE_SEQ)
1844 {
1845 fprintf(f, "%csequence", c);
1846 c = ' ';
1847 }
1848 if (o->flags & MODULE_FLAG_STYLE_UTC)
1849 {
1850 fprintf(f, "%cutc", c);
1851 c = ' ';
1852 }
1853 if (o->flags & MODULE_FLAG_STYLE_UTC_B)
1854 {
1855 fprintf(f, "%cutc-basic", c);
1856 c = ' ';
1857 }
1858 if (o->flags & MODULE_FLAG_STYLE_LCL)
1859 {
1860 fprintf(f, "%clocal", c);
1861 c = ' ';
1862 }
1863 if (o->flags & MODULE_FLAG_STYLE_LCL_B)
1864 {
1865 fprintf(f, "%clocal-basic", c);
1866 c = ' ';
1867 }
1868 if (o->flags & MODULE_FLAG_BASESTAMP)
1869 {
1870 fprintf(f, "%cbasestamp", c);
1871 c = ' ';
1872 }
1873 if (o->flags & MODULE_FLAG_CRASHLOG)
1874 {
1875 fprintf(f, "%ccrashlog", c);
1876 c = ' ';
1877 }
1878 if (o->flags & MODULE_FLAG_SOFT_WRITE)
1879 {
1880 fprintf(f, "%csoft", c);
1881 c = ' ';
1882 }
1883 if (o->flags & MODULE_FLAG_TYPE_ASL)
1884 {
1885 fprintf(f, "%casl_file", c);
1886 c = ' ';
1887 }
1888 if (o->flags & MODULE_FLAG_TYPE_ASL_DIR)
1889 {
1890 fprintf(f, "%casl_directory", c);
1891 c = ' ';
1892 }
1893 if (o->flags & MODULE_FLAG_STD_BSD_MSG)
1894 {
1895 fprintf(f, "%cstd/bsd/msg", c);
1896 c = ' ';
1897 }
1898 fprintf(f, ")");
1899 }
1900 fprintf(f, "\n");
1901
1902 fprintf(f, " ttl: %u\n", o->ttl);
1903 fprintf(f, " mode: 0%o\n", o->mode);
1904 fprintf(f, " file_max: %lu\n", o->file_max);
1905 fprintf(f, " all_max: %lu\n", o->all_max);
1906#if !TARGET_IPHONE_SIMULATOR
1907 fprintf(f, " uid:");
1908 for (i = 0; i < o->nuid; i++) fprintf(f, " %d", o->uid[i]);
1909 fprintf(f, "\n");
1910 fprintf(f, " gid:");
1911 for (i = 0; i < o->ngid; i++) fprintf(f, " %d", o->gid[i]);
1912#endif
1913 }
1914 }
1915
1916 fprintf(f, "\n");
1917 n = r->next;
1918
1919 free(str);
1920 }
1921}
1922
1923void
1924asl_out_file_list_free(asl_out_file_list_t *l)
1925{
1926 asl_out_file_list_t *n;
1927
1928 if (l == NULL) return;
1929
1930 while (l != NULL)
1931 {
1932 free(l->name);
1933 n = l->next;
1934 free(l);
1935 l = n;
1936 }
1937}
1938
1939/*
1940 * Checks input name for the form base[.stamp][.gz]
1941 * name == base is allowed if src is true.
1942 * base.gz is not allowed.
1943 * Output parameter stamp must be freed by caller.
1944 */
1945bool
1946_check_file_name(const char *name, const char *base, bool src, char **stamp)
1947{
1948 size_t baselen, nparts;
1949 const char *p, *q, *part[2];
1950 bool isgz = false;
1951
1952 if (name == NULL) return false;
1953 if (base == NULL) return false;
1954
1955 baselen = strlen(base);
1956 if (baselen == 0) return false;
1957
1958 if (stamp != NULL) *stamp = NULL;
1959
1960 if (strncmp(name, base, baselen)) return false;
1961
1962 p = name + baselen;
1963
1964 /* name == base not allowed (it's the "active" file) */
1965 if (*p == '\0') return false;
1966
1967 /* name must be base.something */
1968 if (*p != '.') return false;
1969
1970 /* maximum of 2 parts (stamp and gz) */
1971 nparts = 0;
1972 for (q = p; *q != '\0'; q++)
1973 {
1974 if (*q == '.')
1975 {
1976 if (nparts == 2) return false;
1977 part[nparts++] = q + 1;
1978 }
1979 }
1980
1981 if (nparts == 0) return false;
1982
1983 isgz = strcmp(part[nparts - 1], "gz") == 0;
1984
1985 /* no compressed files in src */
1986 if (src && isgz) return false;
1987
1988 /* expecting base.stamp or base.stamp.gz */
1989
1990 if (nparts == 1)
1991 {
1992 /* compressed files must have a stamp (base.gz is not allowed) */
1993 if (isgz) return false;
1994
1995 /* got base.stamp */
1996 if (stamp != NULL) *stamp = strdup(part[0]);
1997 return true;
1998 }
1999
2000 /* expecting base.stamp.gz */
2001 if (!isgz) return false;
2002
2003 /* got base.stamp.gz */
2004 if (stamp != NULL)
2005 {
2006 *stamp = strdup(part[0]);
2007 char *x = strchr(*stamp, '.');
2008 if (x != NULL) *x = '\0';
2009 }
2010
2011 return true;
2012}
2013
2014/*
2015 * Find files in a directory (dir) that all have a common prefix (base).
2016 * Bits in flags further control the search.
2017 *
2018 * MODULE_FLAG_STYLE_SEQ means a numeric sequence number is expected, although not required.
2019 * E.g. foo.log foo.log.0
2020 *
2021 * MODULE_FLAG_STYLE_SEC also means a numeric sequence number is required following an 'T' character.
2022 * The numeric value is the file's timestamp in seconds. E.g foo.log.T1335200452
2023 *
2024 * MODULE_FLAG_STYLE_UTC requires a date/time component as the file's timestamp.
2025 * E.g. foo.2012-04-06T15:30:00Z
2026 *
2027 * MODULE_FLAG_STYLE_UTC_B requires a date/time component as the file's timestamp.
2028 * E.g. foo.20120406T153000Z
2029 *
2030 * MODULE_FLAG_STYLE_LCL requires a date/time component as the file's timestamp.
2031 * E.g. foo.2012-04-06T15:30:00-7
2032 *
2033 * MODULE_FLAG_STYLE_LCL_B requires a date/time component as the file's timestamp.
2034 * E.g. foo.20120406T153000-07
2035 */
2036asl_out_file_list_t *
2037asl_list_log_files(const char *dir, const char *base, bool src, uint32_t flags)
2038{
2039 DIR *d;
2040 struct dirent *ent;
2041 char path[MAXPATHLEN];
2042 uint32_t seq;
2043 time_t ftime;
2044 struct stat sb;
2045 int n;
2046 asl_out_file_list_t *out, *x, *y;
2047
2048 if (dir == NULL) return NULL;
2049 if (base == NULL) return NULL;
2050
2051 out = NULL;
2052
2053 d = opendir(dir);
2054 if (d == NULL) return NULL;
2055
2056 while (NULL != (ent = readdir(d)))
2057 {
2058 char *stamp = NULL;
2059 bool check;
2060
2061 if (ent->d_name == NULL) continue;
2062
2063 check = _check_file_name(ent->d_name, base, src, &stamp);
2064 if (!check) continue;
2065
2066 /* exclude base from dst list */
2067
2068 seq = IndexNull;
2069 ftime = 0;
2070
2071 if (stamp == NULL)
2072 {
2073 }
2074 else if (flags & MODULE_FLAG_STYLE_SEQ)
2075 {
2076 seq = atoi(stamp);
2077 if ((seq == 0) && strcmp(stamp, "0"))
2078 {
2079 free(stamp);
2080 continue;
2081 }
2082 }
2083 else if (flags & MODULE_FLAG_STYLE_SEC)
2084 {
2085 ftime = atoi(stamp + 1);
2086 }
2087 else if ((flags & MODULE_FLAG_STYLE_UTC) || (flags & MODULE_FLAG_STYLE_UTC_B) || (flags & MODULE_FLAG_STYLE_LCL) || (flags & MODULE_FLAG_STYLE_LCL_B))
2088 {
2089 struct tm t;
2090 char zone;
2091 uint32_t h, m, s;
2092 long utc_offset = 0;
2093
2094 memset(&t, 0, sizeof(t));
2095 h = m = s = 0;
2096
2097 n = 0;
2098 if ((flags & MODULE_FLAG_STYLE_UTC) || (flags & MODULE_FLAG_STYLE_LCL))
2099 {
2100 n = sscanf(stamp, "%d-%d-%dT%d:%d:%d%c%u:%u:%u", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec, &zone, &h, &m, &s);
2101 }
2102 else
2103 {
2104 n = sscanf(stamp, "%4d%2d%2dT%2d%2d%2d%c%2u%2u%2u", &t.tm_year, &t.tm_mon, &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec, &zone, &h, &m, &s);
2105 }
2106
2107 if (n < 6)
2108 {
2109 continue;
2110 }
2111 else if (n == 6)
2112 {
2113 zone = 'J';
2114 }
2115 else if ((zone == '-') || (zone == '+'))
2116 {
2117 if (n >= 8) utc_offset += (3600 * h);
2118 if (n >= 9) utc_offset += (60 * m);
2119 if (n == 10) utc_offset += s;
2120 if (zone == '-') utc_offset *= -1;
2121 }
2122 else if ((zone >= 'A') && (zone <= 'Z'))
2123 {
2124 if (zone < 'J') utc_offset = 3600 * ((zone - 'A') + 1);
2125 else if ((zone >= 'K') && (zone <= 'M')) utc_offset = 3600 * (zone - 'A');
2126 else if (zone <= 'Y') utc_offset = -3600 * ((zone - 'N') + 1);
2127 }
2128 else if ((zone >= 'a') && (zone <= 'z'))
2129 {
2130 if (zone < 'j') utc_offset = 3600 * ((zone - 'a') + 1);
2131 else if ((zone >= 'k') && (zone <= 'm')) utc_offset = 3600 * (zone - 'a');
2132 else if (zone <= 'y') utc_offset = -3600 * ((zone - 'n') + 1);
2133 }
2134 else
2135 {
2136 free(stamp);
2137 continue;
2138 }
2139
2140 t.tm_year -= 1900;
2141 t.tm_mon -= 1;
2142 t.tm_sec += utc_offset;
2143 t.tm_isdst = -1;
2144
2145 if ((zone == 'J') || (zone == 'j')) ftime = mktime(&t);
2146 else ftime = timegm(&t);
2147 }
2148
2149 free(stamp);
2150
2151 x = (asl_out_file_list_t *)calloc(1, sizeof(asl_out_file_list_t));
2152 if (x == NULL)
2153 {
2154 asl_out_file_list_free(out);
2155 return NULL;
2156 }
2157
2158 x->name = strdup(ent->d_name);
2159 x->ftime = ftime;
2160 x->seq = seq;
2161
2162 memset(&sb, 0, sizeof(sb));
2163 snprintf(path, sizeof(path), "%s/%s", dir, ent->d_name);
2164 if (stat(path, &sb) == 0)
2165 {
2166 x->size = sb.st_size;
2167 if (flags & MODULE_FLAG_STYLE_SEQ)
2168 {
2169 x->ftime = sb.st_birthtimespec.tv_sec;
2170 if (x->ftime == 0) x->ftime = sb.st_mtimespec.tv_sec;
2171 }
2172 }
2173
2174 if (flags & MODULE_FLAG_STYLE_SEQ)
2175 {
2176 if (out == NULL)
2177 {
2178 out = x;
2179 }
2180 else if ((x->seq == IndexNull) || ((x->seq < out->seq) && (out->seq != IndexNull)))
2181 {
2182 x->next = out;
2183 out->prev = x;
2184 out = x;
2185 }
2186 else
2187 {
2188 for (y = out; y != NULL; y = y->next)
2189 {
2190 if (y->next == NULL)
2191 {
2192 y->next = x;
2193 x->prev = y;
2194 break;
2195 }
2196 else if ((x->seq < y->next->seq) && (y->next->seq != IndexNull))
2197 {
2198 x->next = y->next;
2199 y->next = x;
2200 x->prev = y;
2201 x->next->prev = x;
2202 break;
2203 }
2204 }
2205 }
2206 }
2207 else
2208 {
2209 if (out == NULL)
2210 {
2211 out = x;
2212 }
2213 else if (x->ftime < out->ftime)
2214 {
2215 x->next = out;
2216 out->prev = x;
2217 out = x;
2218 }
2219 else
2220 {
2221 for (y = out; y != NULL; y = y->next)
2222 {
2223 if (y->next == NULL)
2224 {
2225 y->next = x;
2226 x->prev = y;
2227 break;
2228 }
2229 else if (x->ftime < y->next->ftime)
2230 {
2231 x->next = y->next;
2232 y->next = x;
2233 x->prev = y;
2234 x->next->prev = x;
2235 break;
2236 }
2237 }
2238 }
2239 }
2240 }
2241
2242 closedir(d);
2243 return out;
2244}
2245
2246/*
2247 * List the source files for an output asl_out_dst_data_t
2248 */
2249asl_out_file_list_t *
2250asl_list_src_files(asl_out_dst_data_t *dst)
2251{
2252 char *base;
2253 uint32_t flags = MODULE_FLAG_STYLE_SEC;
2254 asl_out_file_list_t *out;
2255
2256 if (dst == NULL) return NULL;
2257 if (dst->path == NULL) return NULL;
2258
2259 /*
2260 * MODULE_FLAG_EXTERNAL means some process other than syslogd writes the file.
2261 * We simply check for its existence.
2262 */
2263 if (dst->flags & MODULE_FLAG_EXTERNAL)
2264 {
2265 struct stat sb;
2266
2267 memset(&sb, 0, sizeof(struct stat));
2268
2269 if (stat(dst->path, &sb) == 0)
2270 {
2271 if (S_ISREG(sb.st_mode))
2272 {
2273 out = (asl_out_file_list_t *)calloc(1, sizeof(asl_out_file_list_t));
2274 if (out != NULL)
2275 {
2276 char *p = strrchr(dst->path, '/');
2277 if (p == NULL) p = dst->path;
2278 else p++;
2279 out->name = strdup(p);
2280 out->ftime = sb.st_birthtimespec.tv_sec;
2281 if (out->ftime == 0) out->ftime = sb.st_mtimespec.tv_sec;
2282 return out;
2283 }
2284 }
2285 }
2286
2287 return NULL;
2288 }
2289
2290 /*
2291 * Checkpoint / source format may be one of:
2292 * MODULE_FLAG_STYLE_SEC (foo.T12345678.log),
2293 * MODULE_FLAG_STYLE_UTC (foo.20120-06-24T12:34:56Z.log)
2294 * MODULE_FLAG_STYLE_UTC_B (foo.201200624T123456Z.log)
2295 * MODULE_FLAG_STYLE_LCL (foo.20120-06-24T12:34:56-7.log)
2296 * MODULE_FLAG_STYLE_LCL_B (foo.201200624T123456-07.log)
2297 *
2298 * MODULE_FLAG_STYLE_SEC format is used for sequenced (MODULE_FLAG_STYLE_SEQ) files.
2299 * aslmanager converts the file names.
2300 */
2301
2302 if (dst->flags & MODULE_FLAG_STYLE_UTC) flags = MODULE_FLAG_STYLE_UTC;
2303 else if (dst->flags & MODULE_FLAG_STYLE_UTC_B) flags = MODULE_FLAG_STYLE_UTC_B;
2304 else if (dst->flags & MODULE_FLAG_STYLE_LCL) flags = MODULE_FLAG_STYLE_LCL;
2305 else if (dst->flags & MODULE_FLAG_STYLE_LCL_B) flags = MODULE_FLAG_STYLE_LCL_B;
2306
2307 if ((dst->rotate_dir == NULL) && ((dst->flags & MODULE_FLAG_STYLE_SEQ) == 0) && ((dst->flags & MODULE_FLAG_COMPRESS) == 0))
2308 {
2309 /* files do not move to a dest dir, get renamed, or get compressed - nothing to do */
2310 return NULL;
2311 }
2312
2313 base = strrchr(dst->path, '/');
2314 if (base == NULL) return NULL;
2315
2316 *base = '\0';
2317 base++;
2318
2319 out = asl_list_log_files(dst->path, base, true, flags);
2320
2321 if (base != NULL) *--base = '/';
2322
2323 return out;
2324}
2325
2326/*
2327 * List the destination files for an output asl_out_dst_data_t
2328 */
2329asl_out_file_list_t *
2330asl_list_dst_files(asl_out_dst_data_t *dst)
2331{
2332 char *base, *dst_dir;
2333 asl_out_file_list_t *out;
2334
2335 if (dst == NULL) return NULL;
2336 if (dst->path == NULL) return NULL;
2337
2338 base = strrchr(dst->path, '/');
2339 if (base == NULL) return NULL;
2340
2341 *base = '\0';
2342 base++;
2343
2344 dst_dir = dst->rotate_dir;
2345 if (dst_dir == NULL) dst_dir = dst->path;
2346
2347 out = asl_list_log_files(dst_dir, base, false, dst->flags);
2348
2349 if (base != NULL) *--base = '/';
2350
2351 return out;
2352}