]> git.saurik.com Git - apple/syslog.git/blame - aslcommon/asl_common.c
syslog-322.tar.gz
[apple/syslog.git] / aslcommon / asl_common.c
CommitLineData
81582353
A
1/*
2 * Copyright (c) 2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5222c21d 5 *
81582353
A
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.
5222c21d 12 *
81582353
A
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.
5222c21d 20 *
81582353
A
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
81582353
A
24#include <stdlib.h>
25#include <stdint.h>
26#include <stdbool.h>
27#include <string.h>
28#include <dirent.h>
29#include <errno.h>
30#include <time.h>
31#include <fcntl.h>
32#include <unistd.h>
33#include <sys/param.h>
34#include <sys/stat.h>
35#include <sys/acl.h>
36#include <membership.h>
37#include <xpc/xpc.h>
38#include <TargetConditionals.h>
39#include <configuration_profile.h>
f3df4c03 40#include <asl.h>
81582353
A
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
f3df4c03
A
48#define PATH_VAR_LOG "/var/log/"
49#define PATH_VAR_LOG_LEN 9
50
51#define PATH_LIBRARY_LOGS "/Library/Logs/"
52#define PATH_LIBRARY_LOGS_LEN 14
53
81582353
A
54#if !TARGET_IPHONE_SIMULATOR
55#define _PATH_ASL_CONF_LOCAL_DIR "/usr/local/etc/asl"
56#endif
57
5222c21d
A
58//#define DEBUG_LIST_FILES 1
59
81582353
A
60static const char *asl_out_action_name[] =
61{
62 "none ",
63 "set ",
64 "output ",
65 "ignore ",
66 "skip ",
67 "claim ",
68 "notify ",
69 "broadcast ",
70 "access ",
f3df4c03
A
71 "set ",
72 "unset ",
81582353
A
73 "store ",
74 "asl_file ",
75 "asl_dir ",
76 "file ",
77 "forward ",
78 "control ",
79 "set (file) ",
80 "set (plist) ",
81 "set (profile)"
82};
83
81582353
A
84#define forever for(;;)
85#define KEYMATCH(S,K) ((strncasecmp(S, K, strlen(K)) == 0))
86
87asl_msg_t *
88xpc_object_to_asl_msg(xpc_object_t xobj)
89{
90 __block asl_msg_t *out;
91
92 if (xobj == NULL) return NULL;
93 if (xpc_get_type(xobj) != XPC_TYPE_DICTIONARY) return NULL;
94
95 out = asl_msg_new(ASL_TYPE_MSG);
96 xpc_dictionary_apply(xobj, ^bool(const char *key, xpc_object_t xval) {
97 char tmp[64];
98
99 if (xpc_get_type(xval) == XPC_TYPE_NULL)
100 {
101 asl_msg_set_key_val_op(out, key, NULL, 0);
102 }
103 else if (xpc_get_type(xval) == XPC_TYPE_BOOL)
104 {
105 if (xpc_bool_get_value(xval)) asl_msg_set_key_val_op(out, key, "1", 0);
106 else asl_msg_set_key_val_op(out, key, "0", 0);
107 }
108 else if (xpc_get_type(xval) == XPC_TYPE_INT64)
109 {
110 snprintf(tmp, sizeof(tmp), "%lld", xpc_int64_get_value(xval));
111 asl_msg_set_key_val_op(out, key, tmp, 0);
112 }
113 else if (xpc_get_type(xval) == XPC_TYPE_UINT64)
114 {
115 snprintf(tmp, sizeof(tmp), "%llu", xpc_uint64_get_value(xval));
116 asl_msg_set_key_val_op(out, key, tmp, 0);
117 }
118 else if (xpc_get_type(xval) == XPC_TYPE_DOUBLE)
119 {
120 snprintf(tmp, sizeof(tmp), "%f", xpc_double_get_value(xval));
121 asl_msg_set_key_val_op(out, key, tmp, 0);
122 }
123 else if (xpc_get_type(xval) == XPC_TYPE_DATE)
124 {
125 snprintf(tmp, sizeof(tmp), "%lld", xpc_date_get_value(xval));
126 asl_msg_set_key_val_op(out, key, tmp, 0);
127 }
128 else if (xpc_get_type(xval) == XPC_TYPE_DATA)
129 {
130 size_t len = xpc_data_get_length(xval);
131 char *encoded = asl_core_encode_buffer(xpc_data_get_bytes_ptr(xval), len);
132 asl_msg_set_key_val_op(out, key, encoded, 0);
133 free(encoded);
134 }
135 else if (xpc_get_type(xval) == XPC_TYPE_STRING)
136 {
137 asl_msg_set_key_val_op(out, key, xpc_string_get_string_ptr(xval), 0);
138 }
139 else if (xpc_get_type(xval) == XPC_TYPE_UUID)
140 {
141 uuid_string_t us;
142 uuid_unparse(xpc_uuid_get_bytes(xval), us);
143 asl_msg_set_key_val_op(out, key, us, 0);
144 }
145 else if (xpc_get_type(xval) == XPC_TYPE_FD)
146 {
147 /* XPC_TYPE_FD is not supported */
148 asl_msg_set_key_val_op(out, key, "{XPC_TYPE_FD}", 0);
149 }
150 else if (xpc_get_type(xval) == XPC_TYPE_SHMEM)
151 {
152 /* XPC_TYPE_SHMEM is not supported */
153 asl_msg_set_key_val_op(out, key, "{XPC_TYPE_SHMEM}", 0);
154 }
155 else if (xpc_get_type(xval) == XPC_TYPE_ARRAY)
156 {
157 /* XPC_TYPE_ARRAY is not supported */
158 asl_msg_set_key_val_op(out, key, "{XPC_TYPE_ARRAY}", 0);
159 }
160 else if (xpc_get_type(xval) == XPC_TYPE_DICTIONARY)
161 {
162 /* XPC_TYPE_DICTIONARY is not supported */
163 asl_msg_set_key_val_op(out, key, "{XPC_TYPE_DICTIONARY}", 0);
164 }
165 else if (xpc_get_type(xval) == XPC_TYPE_ERROR)
166 {
167 /* XPC_TYPE_ERROR is not supported */
168 asl_msg_set_key_val_op(out, key, "{XPC_TYPE_ERROR}", 0);
169 }
170 else
171 {
172 /* UNKNOWN TYPE */
173 asl_msg_set_key_val_op(out, key, "{XPC_TYPE_???}", 0);
174 }
175
176 return true;
177 });
178
179 return out;
180}
181
182asl_msg_t *
183configuration_profile_to_asl_msg(const char *ident)
184{
185 xpc_object_t xobj = configuration_profile_copy_property_list(ident);
186 asl_msg_t *out = xpc_object_to_asl_msg(xobj);
187 if (xobj != NULL) xpc_release(xobj);
188 return out;
189}
190
191/* strdup + skip leading and trailing whitespace */
192static char *
193_strdup_clean(const char *s)
194{
195 char *out;
196 const char *first, *last;
197 size_t len;
198
199 if (s == NULL) return NULL;
200
201 first = s;
202 while ((*first == ' ') || (*first == '\t')) first++;
203 len = strlen(first);
204 if (len == 0) return NULL;
205
206 last = first + len - 1;
207 while ((len > 0) && ((*last == ' ') || (*last == '\t')))
208 {
209 last--;
210 len--;
211 }
212
213 if (len == 0) return NULL;
214
215 out = malloc(len + 1);
216 if (out == NULL) return NULL;
217
218 memcpy(out, first, len);
219 out[len] = '\0';
220 return out;
221}
222
223static char **
224_insert_string(char *s, char **l, uint32_t x)
225{
226 int i, len;
227
228 if (s == NULL) return l;
5222c21d 229 if (l == NULL)
81582353
A
230 {
231 l = (char **)malloc(2 * sizeof(char *));
232 if (l == NULL) return NULL;
233
234 l[0] = strdup(s);
235 if (l[0] == NULL)
236 {
237 free(l);
238 return NULL;
239 }
240
241 l[1] = NULL;
242 return l;
243 }
244
245 for (i = 0; l[i] != NULL; i++);
246
5222c21d 247 /* len includes the NULL at the end of the list */
81582353
A
248 len = i + 1;
249
250 l = (char **)reallocf(l, (len + 1) * sizeof(char *));
251 if (l == NULL) return NULL;
252
253 if ((x >= (len - 1)) || (x == IndexNull))
254 {
255 l[len - 1] = strdup(s);
256 if (l[len - 1] == NULL)
257 {
258 free(l);
259 return NULL;
260 }
261
262 l[len] = NULL;
263 return l;
264 }
265
266 for (i = len; i > x; i--) l[i] = l[i - 1];
267 l[x] = strdup(s);
268 if (l[x] == NULL) return NULL;
269
270 return l;
271}
272
273char **
274explode(const char *s, const char *delim)
275{
276 char **l = NULL;
277 const char *p;
278 char *t, quote;
279 int i, n;
280
281 if (s == NULL) return NULL;
282
283 quote = '\0';
284
285 p = s;
286 while (p[0] != '\0')
287 {
288 /* scan forward */
289 for (i = 0; p[i] != '\0'; i++)
290 {
291 if (quote == '\0')
292 {
293 /* not inside a quoted string: check for delimiters and quotes */
294 if (strchr(delim, p[i]) != NULL) break;
295 else if (p[i] == '\'') quote = p[i];
296 else if (p[i] == '"') quote = p[i];
297 }
298 else
299 {
300 /* inside a quoted string - look for matching quote */
301 if (p[i] == quote) quote = '\0';
302 }
303 }
304
305 n = i;
306 t = malloc(n + 1);
307 if (t == NULL) return NULL;
308
309 for (i = 0; i < n; i++) t[i] = p[i];
310 t[n] = '\0';
311 l = _insert_string(t, l, IndexNull);
312 free(t);
313 t = NULL;
314 if (p[i] == '\0') return l;
315 if (p[i + 1] == '\0') l = _insert_string("", l, IndexNull);
316 p = p + i + 1;
317 }
318
319 return l;
320}
321
322void
323free_string_list(char **l)
324{
325 int i;
326
327 if (l == NULL) return;
328 for (i = 0; l[i] != NULL; i++) free(l[i]);
329 free(l);
330}
331
332char *
333get_line_from_file(FILE *f)
334{
335 char *s, *out;
336 size_t len;
337
338 out = fgetln(f, &len);
339 if (out == NULL) return NULL;
340 if (len == 0) return NULL;
341
342 s = malloc(len + 1);
343 if (s == NULL) return NULL;
344
345 memcpy(s, out, len);
346
347 if (s[len - 1] != '\n') len++;
348 s[len - 1] = '\0';
349 return s;
350}
351
352char *
353next_word_from_string(char **s)
354{
355 char *a, *p, *e, *out, s0;
356 int quote1, quote2, len;
357
358 if (s == NULL) return NULL;
359 if (*s == NULL) return NULL;
360
361 s0 = **s;
362
363 quote1 = 0;
364 quote2 = 0;
365
366 p = *s;
367
368 /* allow whole word to be contained in quotes */
369 if (*p == '\'')
370 {
371 quote1 = 1;
372 p++;
373 }
374
375 if (*p == '"')
376 {
377 quote2 = 1;
378 p++;
379 }
380
381 a = p;
382 e = p;
383
384 while (*p != '\0')
385 {
386 if (*p == '\\')
387 {
388 p++;
389 e = p;
390
391 if (*p == '\0')
392 {
393 p--;
394 break;
395 }
396
397 p++;
398 e = p;
399 continue;
400 }
401
402 if (*p == '\'')
403 {
404 if (quote1 == 0) quote1 = 1;
405 else quote1 = 0;
406 }
407
408 if (*p == '"')
409 {
410 if (quote2 == 0) quote2 = 1;
411 else quote2 = 0;
412 }
413
414 if (((*p == ' ') || (*p == '\t')) && (quote1 == 0) && (quote2 == 0))
415 {
416 e = p + 1;
417 break;
418 }
419
420 p++;
421 e = p;
422 }
423
424 *s = e;
425
426 len = p - a;
427
428 /* check for quoted string */
429 if (((s0 == '\'') || (s0 == '"')) && (s0 == a[len-1])) len--;
430
431 if (len == 0) return NULL;
432
433 out = malloc(len + 1);
434 if (out == NULL) return NULL;
435
436 memcpy(out, a, len);
437 out[len] = '\0';
438 return out;
439}
440
5222c21d
A
441asl_out_dst_data_t *
442asl_out_dest_for_path(asl_out_module_t *m, const char *path)
f3df4c03
A
443{
444 if (m == NULL) return NULL;
445 if (path == NULL) return NULL;
446
447 while (m != NULL)
448 {
449 asl_out_rule_t *r = m->ruleset;
450 while (r != NULL)
451 {
5222c21d 452 if ((r->action == ACTION_OUT_DEST) && (r->dst != NULL) && (r->dst->path != NULL) && (streq(r->dst->path, path))) return r->dst;
f3df4c03
A
453 r = r->next;
454 }
455
456 m = m->next;
457 }
458
459 return NULL;
460}
461
462/*
463 * Create a directory path.
464 *
465 * mlist provides owner, group, and access mode, which are required for "non-standard"
466 * directories. Directories for standard paths (/var/log or /Library/Logs) default
467 * to root/admin/0755 if there is no mlist rule for them.
468 */
469static int
470_asl_common_make_dir_path(asl_out_module_t *mlist, uint32_t flags, const char *path)
471{
472 int i;
473 char **path_parts;
474 asl_string_t *processed_path;
475 mode_t mode;
476
477 if (path == NULL) return 0;
478
479 path_parts = explode(path, "/");
480 if (path_parts == NULL) return 0;
481
482 processed_path = asl_string_new(ASL_ENCODE_NONE);
483
484 i = 0;
485 if (path[0] == '/') i = 1;
486
487 for (; path_parts[i] != NULL; i++)
488 {
489 struct stat sb;
490 int status;
491 mode_t mask;
492 asl_out_dst_data_t *dst;
493 char *tmp;
494
495 asl_string_append_char_no_encoding(processed_path, '/');
496 asl_string_append_no_encoding(processed_path, path_parts[i]);
497 tmp = asl_string_bytes(processed_path);
498
499 memset(&sb, 0, sizeof(struct stat));
500 status = lstat(tmp, &sb);
501 if ((status == 0) && S_ISLNK(sb.st_mode))
502 {
503 char real[MAXPATHLEN];
504 if (realpath(tmp, real) == NULL)
505 {
506 asl_string_release(processed_path);
507 free_string_list(path_parts);
508 return -1;
509 }
510
511 memset(&sb, 0, sizeof(struct stat));
512 status = stat(real, &sb);
513 }
514
515 if (status == 0)
516 {
517 if (!S_ISDIR(sb.st_mode))
518 {
519 /* path component is not a directory! */
520 asl_string_release(processed_path);
521 free_string_list(path_parts);
522 return -1;
523 }
524
525 /* exists and is a directory or a link to a directory */
526 continue;
527 }
528 else if (errno != ENOENT)
529 {
530 /* unexpected status from stat() */
531 asl_string_release(processed_path);
532 free_string_list(path_parts);
533 return -1;
534 }
535
5222c21d 536 dst = asl_out_dest_for_path(mlist, tmp);
f3df4c03
A
537 if ((dst == NULL) && (flags & MODULE_FLAG_NONSTD_DIR))
538 {
539 /* no rule to create a non-standard path component! */
540 asl_string_release(processed_path);
541 free_string_list(path_parts);
542 return -1;
543 }
544
545 mode = 0755;
546 if (dst != NULL)
547 {
548 mode = dst->mode;
549 if (mode == 010000) mode = 0755;
550 }
551
552 mask = umask(0);
553 status = mkdir(tmp, mode);
554 umask(mask);
555
556#if !TARGET_IPHONE_SIMULATOR
557 uid_t u = 0;
558 gid_t g = 80;
559
560 if (dst != NULL)
561 {
562 if (dst->nuid > 0) u = dst->uid[0];
563 if (dst->ngid > 0) g = dst->gid[0];
564 }
565
566 chown(tmp, u, g);
567#endif
568 }
569
570 asl_string_release(processed_path);
571 free_string_list(path_parts);
572
573 return 0;
574}
575
81582353 576int
f3df4c03 577asl_out_mkpath(asl_out_module_t *mlist, asl_out_rule_t *r)
81582353
A
578{
579 char tmp[MAXPATHLEN], *p;
580 struct stat sb;
581 int status;
582
583 if (r == NULL) return -1;
584 if (r->dst == NULL) return -1;
585 if (r->dst->path == NULL) return -1;
586
587 snprintf(tmp, sizeof(tmp), "%s", r->dst->path);
588
589 if (r->action != ACTION_ASL_DIR)
590 {
591 p = strrchr(tmp, '/');
592 if (p == NULL) return -1;
593 *p = '\0';
594 }
595
596 memset(&sb, 0, sizeof(struct stat));
597 status = stat(tmp, &sb);
598 if (status == 0)
599 {
f3df4c03
A
600 if (S_ISDIR(sb.st_mode)) return 0;
601 return -1;
81582353 602 }
f3df4c03
A
603
604 if (errno == ENOENT)
81582353 605 {
f3df4c03
A
606 uint32_t dirflag = r->dst->flags & MODULE_FLAG_NONSTD_DIR;
607 status = _asl_common_make_dir_path(mlist, dirflag, tmp);
608 return status;
81582353
A
609 }
610
f3df4c03 611 return -1;
81582353
A
612}
613
5222c21d
A
614int
615asl_make_database_dir(const char *dir, char **out)
616{
617 const char *asldir, *path;
618 char *str = NULL;
619 struct stat sb;
620 int status;
621 mode_t mask;
622
623 if (out != NULL) *out = NULL;
624
625 asldir = asl_filesystem_path(ASL_PLACE_DATABASE);
626 if (asldir == NULL) return -1;
627
628 if (dir == NULL)
629 {
630 /* create the database directory itself */
631 path = asldir;
632 }
633 else
634 {
635 if (strchr(dir, '/') != NULL) return -1;
636
637 asprintf(&str, "%s/%s", asldir, dir);
638 if (str == NULL) return -1;
639 path = str;
640 }
641
642 memset(&sb, 0, sizeof(struct stat));
643
644 status = stat(path, &sb);
645 if (status == 0)
646 {
647 if (S_ISDIR(sb.st_mode))
648 {
649 if (out == NULL) free(str);
650 else *out = str;
651 return 0;
652 }
653
654 free(str);
655 return -1;
656 }
657
658 if (errno != ENOENT)
659 {
660 free(str);
661 return -1;
662 }
663
664 mask = umask(0);
665 status = mkdir(path, 0755);
666 umask(mask);
667
668 if (status == 0)
669 {
670 if (out == NULL) free(str);
671 else *out = str;
672 }
673 else free(str);
674
675 return status;
676}
677
81582353
A
678void
679asl_make_timestamp(time_t stamp, uint32_t flags, char *buf, size_t len)
680{
681 struct tm t;
682 uint32_t h, m, s;
683
684 if (buf == NULL) return;
685
5222c21d 686 if (flags & MODULE_NAME_STYLE_STAMP_UTC)
81582353
A
687 {
688 memset(&t, 0, sizeof(t));
689 gmtime_r(&stamp, &t);
690 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);
691 }
5222c21d 692 else if (flags & MODULE_NAME_STYLE_STAMP_UTC_B)
81582353
A
693 {
694 memset(&t, 0, sizeof(t));
695 gmtime_r(&stamp, &t);
696 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);
697 }
5222c21d 698 else if (flags & MODULE_NAME_STYLE_STAMP_LCL)
81582353
A
699 {
700 bool neg = false;
701 memset(&t, 0, sizeof(t));
702 localtime_r(&stamp, &t);
703
704 if ((neg = (t.tm_gmtoff < 0))) t.tm_gmtoff *= -1;
705
706 s = t.tm_gmtoff;
707 h = s / 3600;
708 s %= 3600;
709 m = s / 60;
710 s %= 60;
711
712 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);
713 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);
714 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);
715 }
5222c21d 716 else if (flags & MODULE_NAME_STYLE_STAMP_LCL_B)
81582353
A
717 {
718 bool neg = false;
719 memset(&t, 0, sizeof(t));
720 localtime_r(&stamp, &t);
721
722 if ((neg = (t.tm_gmtoff < 0))) t.tm_gmtoff *= -1;
723
724 s = t.tm_gmtoff;
725 h = s / 3600;
726 s %= 3600;
727 m = s / 60;
728 s %= 60;
729
730 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);
731 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);
732 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);
733 }
734 else
735 {
5222c21d 736 snprintf(buf, len, "%c%llu", STYLE_SEC_PREFIX_CHAR, (unsigned long long)stamp);
81582353
A
737 }
738}
739
740void
5222c21d 741asl_dst_make_current_name(asl_out_dst_data_t *dst, uint32_t xflags, char *buf, size_t len)
81582353 742{
5222c21d
A
743 char tstamp[32];
744
81582353
A
745 if (dst == NULL) return;
746 if (buf == NULL) return;
747
5222c21d 748 xflags |= dst->flags;
f3df4c03 749
5222c21d
A
750 if (dst->timestamp == 0) dst->timestamp = time(NULL);
751 asl_make_timestamp(dst->timestamp, dst->style_flags, tstamp, sizeof(tstamp));
81582353 752
5222c21d
A
753 if (xflags & MODULE_FLAG_TYPE_ASL_DIR)
754 {
755 snprintf(buf, len, "%s.%s", dst->current_name, tstamp);
756 }
757 else if (xflags & MODULE_FLAG_BASESTAMP)
758 {
759 if ((dst->dir != NULL) && (dst->style_flags & MODULE_NAME_STYLE_FORMAT_BSE))
760 {
761 snprintf(buf, len, "%s/%s.%s.%s", dst->dir, dst->base, tstamp, dst->ext);
762 }
763 else
764 {
765 snprintf(buf, len, "%s.%s", dst->path, tstamp);
766 }
81582353
A
767 }
768 else
769 {
770 snprintf(buf, len, "%s", dst->path);
771 }
772}
773
774int
f3df4c03 775asl_check_option(asl_msg_t *msg, const char *opt)
81582353
A
776{
777 const char *p;
778 uint32_t len;
779
780 if (msg == NULL) return 0;
781 if (opt == NULL) return 0;
782
783 len = strlen(opt);
784 if (len == 0) return 0;
785
f3df4c03 786 p = asl_msg_get_val_for_key(msg, ASL_KEY_OPTION);
81582353
A
787 if (p == NULL) return 0;
788
789 while (*p != '\0')
790 {
791 while ((*p == ' ') || (*p == '\t') || (*p == ',')) p++;
792 if (*p == '\0') return 0;
793
794 if (strncasecmp(p, opt, len) == 0)
795 {
796 p += len;
797 if ((*p == ' ') || (*p == '\t') || (*p == ',') || (*p == '\0')) return 1;
798 }
799
800 while ((*p != ' ') && (*p != '\t') && (*p != ',') && (*p != '\0')) p++;
801 }
802
803 return 0;
804}
805
806void
807asl_out_dst_data_release(asl_out_dst_data_t *dst)
808{
809 if (dst == NULL) return;
810
811 if (dst->refcount > 0) dst->refcount--;
812 if (dst->refcount > 0) return;
813
5222c21d 814 free(dst->dir);
81582353 815 free(dst->path);
5222c21d
A
816 free(dst->current_name);
817 free(dst->base);
818 free(dst->ext);
81582353
A
819 free(dst->rotate_dir);
820 free(dst->fmt);
821#if !TARGET_IPHONE_SIMULATOR
822 free(dst->uid);
823 free(dst->gid);
824#endif
825 free(dst);
826}
827
828asl_out_dst_data_t *
829asl_out_dst_data_retain(asl_out_dst_data_t *dst)
830{
831 if (dst == NULL) return NULL;
832 dst->refcount++;
833 return dst;
834}
835
836/* set owner, group, mode, and acls for a file */
837int
838asl_out_dst_set_access(int fd, asl_out_dst_data_t *dst)
839{
840#if !TARGET_IPHONE_SIMULATOR
841 uid_t fuid = 0;
842 gid_t fgid = 80;
843#if !TARGET_OS_EMBEDDED
844 int status;
845 acl_t acl;
846 uuid_t uuid;
847 acl_entry_t entry;
848 acl_permset_t perms;
849 uint32_t i;
850#endif
851#endif
852
853 if (dst == NULL) return -1;
854 if (fd < 0) return -1;
855
856#if TARGET_IPHONE_SIMULATOR
857 return fd;
858#else
859
860 if (dst->nuid > 0) fuid = dst->uid[0];
861 if (dst->ngid > 0) fgid = dst->gid[0];
862
863 fchown(fd, fuid, fgid);
864
865#if TARGET_OS_EMBEDDED
866 return fd;
867#else
868 acl = acl_init(1);
869
870 for (i = 0; i < dst->ngid; i++)
871 {
872 if (dst->gid[i] == -2) continue;
873
874 /*
875 * Don't bother setting group access if this is
876 * file's group and the file is group-readable.
877 */
f3df4c03 878 if ((dst->gid[i] == fgid) && (dst->mode & 00040)) continue;
81582353
A
879
880 status = mbr_gid_to_uuid(dst->gid[i], uuid);
881 if (status != 0)
882 {
883 dst->gid[i] = -2;
884 continue;
885 }
886
887 status = acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY);
888 if (status != 0) goto asl_file_create_return;
889
890 status = acl_set_tag_type(entry, ACL_EXTENDED_ALLOW);
891 if (status != 0) goto asl_file_create_return;
892
893 status = acl_set_qualifier(entry, &uuid);
894 if (status != 0) goto asl_file_create_return;
895
896 status = acl_get_permset(entry, &perms);
897 if (status != 0) goto asl_file_create_return;
898
899 status = acl_add_perm(perms, ACL_READ_DATA);
900 if (status != 0) goto asl_file_create_return;
901 }
902
903 for (i = 0; i < dst->nuid; i++)
904 {
905 if (dst->uid[i] == -2) continue;
906
907 /*
908 * Don't bother setting user access if this is
909 * file's owner and the file is owner-readable.
910 */
f3df4c03 911 if ((dst->uid[i] == fuid) && (dst->mode & 00400)) continue;
81582353
A
912
913 status = mbr_uid_to_uuid(dst->uid[i], uuid);
914 if (status != 0)
915 {
916 dst->uid[i] = -2;
917 continue;
918 }
919
920 status = acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY);
921 if (status != 0) goto asl_file_create_return;
922
923 status = acl_set_tag_type(entry, ACL_EXTENDED_ALLOW);
924 if (status != 0) goto asl_file_create_return;
925
926 status = acl_set_qualifier(entry, &uuid);
927 if (status != 0) goto asl_file_create_return;
928
929 status = acl_get_permset(entry, &perms);
930 if (status != 0) goto asl_file_create_return;
931
932 status = acl_add_perm(perms, ACL_READ_DATA);
933 if (status != 0) goto asl_file_create_return;
934 }
935
936 status = acl_set_fd(fd, acl);
937 if (status != 0)
938 {
939 close(fd);
940 fd = -1;
941 }
942
943asl_file_create_return:
944
945 acl_free(acl);
946 return fd;
947#endif /* !TARGET_OS_EMBEDDED */
948#endif /* !TARGET_IPHONE_SIMULATOR */
949}
950
951/* create a file with acls */
952int
f3df4c03 953asl_out_dst_file_create_open(asl_out_dst_data_t *dst, char **pathp)
81582353
A
954{
955 int fd, status;
956 struct stat sb;
957 char outpath[MAXPATHLEN];
958
959 if (dst == NULL) return -1;
960 if (dst->path == NULL) return -1;
961
5222c21d
A
962 asl_dst_make_current_name(dst, 0, outpath, sizeof(outpath));
963 free(dst->current_name);
f3df4c03 964
5222c21d
A
965 dst->current_name = strdup(outpath);
966 if (dst->current_name == NULL) return -1;
f3df4c03
A
967
968 if (pathp != NULL) *pathp = strdup(outpath);
81582353
A
969
970 memset(&sb, 0, sizeof(struct stat));
971 status = stat(outpath, &sb);
972 if (status == 0)
973 {
974 /* must be a regular file */
975 if (!S_ISREG(sb.st_mode)) return -1;
976
977 /* file exists */
978 fd = open(outpath, O_RDWR | O_APPEND | O_EXCL, 0);
979
5222c21d
A
980 if (dst->timestamp == 0) dst->timestamp = sb.st_birthtimespec.tv_sec;
981 if (dst->timestamp == 0) dst->timestamp = sb.st_mtimespec.tv_sec;
81582353
A
982 dst->size = sb.st_size;
983
5222c21d 984 if ((dst->flags & MODULE_FLAG_BASESTAMP) && (dst->flags & MODULE_FLAG_SYMLINK)) symlink(outpath, dst->path);
81582353
A
985 return fd;
986 }
987 else if (errno != ENOENT)
988 {
989 /* stat error other than non-existant file */
990 return -1;
991 }
992
f3df4c03 993 fd = open(outpath, O_RDWR | O_CREAT | O_EXCL, (dst->mode & 00666));
81582353
A
994 if (fd < 0) return -1;
995
5222c21d 996 dst->timestamp = time(NULL);
81582353
A
997
998 fd = asl_out_dst_set_access(fd, dst);
999 if (fd < 0) unlink(outpath);
1000
5222c21d
A
1001 if ((dst->flags & MODULE_FLAG_BASESTAMP) && (dst->flags & MODULE_FLAG_SYMLINK))
1002 {
1003 /* remove old symlink, make a new link to the "current" file */
1004 unlink(dst->path);
1005 symlink(outpath, dst->path);
1006 }
1007
81582353
A
1008 return fd;
1009}
1010
1011void
1012asl_out_module_free(asl_out_module_t *m)
1013{
1014 asl_out_rule_t *r, *n;
1015 asl_out_module_t *x;
1016
1017 while (m != NULL)
1018 {
1019 x = m->next;
1020
1021 /* free name */
1022 free(m->name);
1023
1024 /* free ruleset */
1025 r = m->ruleset;
1026 while (r != NULL)
1027 {
1028 n = r->next;
1029 if (r->dst != NULL) asl_out_dst_data_release(r->dst);
1030
1031 if (r->query != NULL) asl_msg_release(r->query);
1032 free(r->options);
1033 free(r);
1034 r = n;
1035 }
1036
1037 free(m);
1038 m = x;
1039 }
1040}
1041
1042asl_out_module_t *
1043asl_out_module_new(const char *name)
1044{
1045 asl_out_module_t *out = (asl_out_module_t *)calloc(1, sizeof(asl_out_module_t));
1046
1047 if (out == NULL) return NULL;
1048 if (name == NULL) return NULL;
1049
1050 out->name = strdup(name);
1051 if (out->name == NULL)
1052 {
1053 free(out);
1054 return NULL;
1055 }
1056
1057 out->flags = MODULE_FLAG_ENABLED;
1058
1059 return out;
1060}
1061
1062/* Skip over query */
1063static char *
1064_asl_out_module_find_action(char *s)
1065{
1066 char *p;
1067
1068 p = s;
1069 if (p == NULL) return NULL;
1070
1071 /* Skip command character (?, Q, *, or =) */
1072 p++;
1073
1074 forever
1075 {
1076 /* Find next [ */
1077 while ((*p == ' ') || (*p == '\t')) p++;
1078
1079 if (*p == '\0') return NULL;
1080 if (*p != '[') return p;
1081
1082 /* skip to closing ] */
1083 while (*p != ']')
1084 {
1085 p++;
1086 if (*p == '\\')
1087 {
1088 p++;
1089 if (*p == ']') p++;
1090 }
1091 }
1092
1093 if (*p == ']') p++;
1094 }
1095
1096 /* skip whitespace */
1097 while ((*p == ' ') || (*p == '\t')) p++;
1098
1099 return NULL;
1100}
1101
1102/*
1103 * Parse parameter setting line
1104 *
1105 * = param options
1106 * evaluated once when module is initialized
1107 *
1108 * = [query] param options
1109 * evaluated for each message, param set if message matches query
1110 *
1111 * = param [File path]
1112 * evaluated once when module is initialized
1113 * evaluated when change notification received for path
1114 *
1115 * = param [Plist path] ...
1116 * evaluated once when module is initialized
1117 * evaluated when change notification received for path
1118 *
1119 * = param [Profile name] ...
1120 * evaluated once when module is initialized
1121 * evaluated when change notification received for profile
1122 */
1123static asl_out_rule_t *
1124_asl_out_module_parse_set_param(asl_out_module_t *m, char *s)
1125{
1126 char *act, *p, *q;
1127 asl_out_rule_t *out, *rule;
1128
1129 if (m == NULL) return NULL;
1130
1131 out = (asl_out_rule_t *)calloc(1, sizeof(asl_out_rule_t));
1132 if (out == NULL) return NULL;
1133
1134 q = s + 1;
1135 while ((*q == ' ') || (*q == '\'')) q++;
1136 out->action = ACTION_SET_PARAM;
1137
1138 if (*q == '[')
1139 {
1140 /* = [query] param options */
1141 act = _asl_out_module_find_action(s);
1142 if (act == NULL)
1143 {
1144 free(out);
1145 return NULL;
1146 }
1147
1148 out->options = _strdup_clean(act);
1149
1150 p = act - 1;
1151 if (*p == ']') p = act;
1152 *p = '\0';
1153
1154 *s = 'Q';
1155 out->query = asl_msg_from_string(s);
1156 if (out->query == NULL)
1157 {
1158 free(out->options);
1159 free(out);
1160 return NULL;
1161 }
1162 }
1163 else
1164 {
1165 /* = param ... */
1166 p = strchr(s, '[');
1167 if (p == NULL)
1168 {
1169 /* = param options */
1170 out->options = _strdup_clean(q);
1171 }
1172 else
1173 {
1174 /* = param [query] */
5222c21d
A
1175 if (streq_len(p, "[File ", 6) || streq_len(p, "[File\t", 6)) out->action = ACTION_SET_FILE;
1176 else if (streq_len(p, "[Plist ", 7) || streq_len(p, "[Plist\t", 7)) out->action = ACTION_SET_PLIST;
1177 else if (streq_len(p, "[Profile ", 9) || streq_len(p, "[Profile\t", 9)) out->action = ACTION_SET_PROF;
81582353
A
1178
1179 p--;
1180 *p = '\0';
1181 out->options = _strdup_clean(q);
1182
1183 *p = ' ';
1184 p--;
1185 *p = 'Q';
1186 out->query = asl_msg_from_string(p);
1187 if (out->query == NULL)
1188 {
1189 free(out->options);
1190 free(out);
1191 return NULL;
1192 }
1193 }
1194 }
1195
1196 if (m->ruleset == NULL) m->ruleset = out;
1197 else
1198 {
1199 for (rule = m->ruleset; rule->next != NULL; rule = rule->next);
1200 rule->next = out;
1201 }
1202
1203 return out;
1204}
1205
1206#if !TARGET_IPHONE_SIMULATOR
1207static void
1208_dst_add_uid(asl_out_dst_data_t *dst, char *s)
1209{
1210 int i;
1211 uid_t uid;
1212
1213 if (dst == NULL) return;
1214 if (s == NULL) return;
1215
1216 uid = atoi(s);
1217
1218 for (i = 0 ; i < dst->nuid; i++)
1219 {
1220 if (dst->uid[i] == uid) return;
1221 }
1222
1223 dst->uid = reallocf(dst->uid, (dst->nuid + 1) * sizeof(uid_t));
1224 if (dst->uid == NULL)
1225 {
1226 dst->nuid = 0;
1227 return;
1228 }
1229
1230 dst->uid[dst->nuid++] = uid;
1231}
1232
1233static void
1234_dst_add_gid(asl_out_dst_data_t *dst, char *s)
1235{
1236 int i;
1237 gid_t gid;
1238
1239 if (dst == NULL) return;
1240 if (s == NULL) return;
1241
1242 gid = atoi(s);
1243
1244 for (i = 0 ; i < dst->ngid; i++)
1245 {
1246 if (dst->gid[i] == gid) return;
1247 }
1248
1249 dst->gid = reallocf(dst->gid, (dst->ngid + 1) * sizeof(gid_t));
1250 if (dst->gid == NULL)
1251 {
1252 dst->ngid = 0;
1253 return;
1254 }
1255
1256 dst->gid[dst->ngid++] = gid;
1257}
1258#endif /* !TARGET_IPHONE_SIMULATOR */
1259
1260static char *
1261_dst_format_string(char *s)
1262{
1263 char *fmt;
1264 size_t i, len, n;
1265
1266 if (s == NULL) return NULL;
1267
1268 len = strlen(s);
1269
1270 /* format string can be enclosed by quotes */
1271 if ((len >= 2) && ((s[0] == '\'') || (s[0] == '"')) && (s[len-1] == s[0]))
1272 {
1273 s++;
1274 len -= 2;
1275 }
1276
1277 n = 0;
1278 for (i = 0; i < len; i++) if (s[i] == '\\') n++;
1279
1280 fmt = malloc(1 + len - n);
1281 if (fmt == NULL) return NULL;
1282
1283 for (i = 0, n = 0; i < len; i++) if (s[i] != '\\') fmt[n++] = s[i];
1284 fmt[n] = '\0';
1285 return fmt;
1286}
1287
81582353
A
1288static bool
1289_dst_path_match(const char *newpath, const char *existingpath)
1290{
1291 if (newpath == NULL) return (existingpath == NULL);
1292 if (existingpath == NULL) return false;
1293 if (newpath[0] == '/') return (strcmp(newpath, existingpath) == 0);
1294
1295 const char *trailing = strrchr(existingpath, '/');
1296 if (trailing == NULL) return (strcmp(newpath, existingpath) == 0);
1297 trailing++;
1298 return (strcmp(newpath, trailing) == 0);
1299}
1300
5222c21d
A
1301static uint32_t
1302_parse_stamp_string(const char *in)
1303{
1304 char buf[16];
1305 uint32_t x;
1306
1307 if (in == NULL) return 0;
1308
1309 for (x = 0; (((in[x] >= 'a') && (in[x] <= 'z')) || (in[x] == '-')) && (x < 11); x++) buf[x] = in[x];
1310 buf[x] = '\0';
1311
1312 if (streq(buf, "sec") || streq(buf, "seconds")) return MODULE_NAME_STYLE_STAMP_SEC;
1313 if (streq(buf, "zulu") || streq(buf, "utc")) return MODULE_NAME_STYLE_STAMP_UTC;
1314 if (streq(buf, "utc-b") || streq(buf, "utc-basic")) return MODULE_NAME_STYLE_STAMP_UTC_B;
1315 if (streq(buf, "local") || streq(buf, "lcl")) return MODULE_NAME_STYLE_STAMP_LCL;
1316 if (streq(buf, "local-b") || streq(buf, "lcl-b") || streq(buf, "local-basic") || streq(buf, "lcl-basic")) return MODULE_NAME_STYLE_STAMP_LCL_B;
1317 if (streq(buf, "#") || streq(buf, "seq") || streq(buf, "sequence"))return MODULE_NAME_STYLE_STAMP_SEQ;
1318
1319 return 0;
1320}
1321
1322/*
1323 * Parse a file-rotation naming style.
1324 *
1325 * Legacy: sec / seconds, utc / date / zulu [-b], local / lcl [-b], # / seq / sequence
1326 * We scan the whole line and match to one of these.
1327 *
1328 * New scheme: 2 or 3 components: base and style, or base, style, and extension.
1329 * these define a name format. base is the file name without a leading directory path
1330 * and with no extension (e.g. "foo"). style is one of the styles above. extension is
1331 * the file name extension (e.g. "log", "txt", etc).
1332 *
1333 * Examples:
1334 * foo.utc.log
1335 * foo.log.lcl
1336 * foo.seq
1337 *
1338 * The leading base name may be ommitted (E.G. ".lcl.log", ".log.seq")
1339 * If the leading base name AND extension are omitted, it is taken from the path. E.G. ".lcl", ".seq"
1340 *
1341 * If we get input without a stamp spec, we default to "sec".
1342 */
1343static int
1344_parse_dst_style(asl_out_dst_data_t *dst, const char *in)
1345{
1346 const char *p, *q;
1347 size_t len;
1348
1349 if ((dst == NULL) || (in == NULL)) return -1;
1350
1351 /* check for base. or just . for shorthand */
1352 p = NULL;
1353 if (in[0] == '.')
1354 {
1355 p = in + 1;
1356 }
1357 else
1358 {
1359 if (dst->base == NULL) return -1;
1360
1361 len = strlen(dst->base);
1362 if (streq_len(in, dst->base, len) && (in[len] == '.')) p = in + len + 1;
1363 }
1364
1365 if (p == NULL)
1366 {
1367 /* input does not start with '.' or base, so this is legacy style */
1368 dst->style_flags = _parse_stamp_string(in);
1369 if (dst->style_flags == 0) return -1;
1370
1371 if (dst->ext == NULL) dst->style_flags |= MODULE_NAME_STYLE_FORMAT_BS;
1372 else dst->style_flags |= MODULE_NAME_STYLE_FORMAT_BES;
1373
1374 return 0;
1375 }
1376
1377 /* look for another dot in the name */
1378 for (q = p; (*q != '.') && (*q != ' ') && (*q != '\t') && (*q != '\0'); q++);
1379 if (*q != '.') q = NULL;
1380
1381 if (q == NULL)
1382 {
1383 /* we require a stamp spec, so we are expecting base.stamp */
1384 dst->style_flags = _parse_stamp_string(p);
1385
1386 if (dst->style_flags == 0) return -1;
1387
1388 /*
1389 * We got a valid stamp style ("base.stamp").
1390 * Note that we might have skipped the extention if the file name was "foo.log".
1391 * That's OK - syslogd writes "foo.log", but the rotated files are e.g. foo.20141018T1745Z.
1392 */
1393 dst->style_flags |= MODULE_NAME_STYLE_FORMAT_BS;
1394 return 0;
1395 }
1396
1397 /* set q to the char past the dot */
1398 q++;
1399
1400 /* either base.stamp.ext or base.ext.stamp */
1401 if (dst->ext == NULL) return -1;
1402
1403 len = strlen(dst->ext);
1404 if (streq_len(p, dst->ext, len) && (p[len] == '.'))
1405 {
1406 /* got base.ext.stamp */
1407 dst->style_flags = _parse_stamp_string(q);
1408 if (dst->style_flags == 0) return -1;
1409
1410 dst->style_flags |= MODULE_NAME_STYLE_FORMAT_BES;
1411 return 0;
1412 }
1413
1414 /* must be base.stamp.ext */
1415 if (strneq_len(q, dst->ext, len)) return -1;
1416
1417 dst->style_flags = _parse_stamp_string(p);
1418 if (dst->style_flags == 0) return -1;
1419
1420 dst->style_flags |= MODULE_NAME_STYLE_FORMAT_BSE;
1421 return 0;
1422}
1423
81582353
A
1424static asl_out_dst_data_t *
1425_asl_out_module_parse_dst(asl_out_module_t *m, char *s, mode_t def_mode)
1426{
1427 asl_out_rule_t *out, *rule;
1428 asl_out_dst_data_t *dst;
5222c21d 1429 char *p, *dot, *opts, *path;
81582353
A
1430 char **path_parts;
1431 int has_dotdot, recursion_limit;
f3df4c03 1432 uint32_t i, flags = 0;
81582353
A
1433
1434 if (m == NULL) return NULL;
1435 if (s == NULL) return NULL;
1436
1437 /* skip whitespace */
1438 while ((*s == ' ') || (*s == '\t')) s++;
1439
1440 opts = s;
1441 path = next_word_from_string(&opts);
1442 if (path == NULL) return NULL;
1443
1444 /*
1445 * Check path for ".." component (not permitted).
1446 * Also substitute environment variables.
1447 */
1448 has_dotdot = 0;
1449 path_parts = explode(path, "/");
1450 asl_string_t *processed_path = asl_string_new(ASL_ENCODE_NONE);
1451 recursion_limit = 5;
1452
1453 while ((recursion_limit > 0) && (path_parts != NULL) && (processed_path != NULL))
1454 {
1455 uint32_t i;
1456 int did_sub = 0;
1457
1458 for (i = 0; path_parts[i] != NULL; i++)
1459 {
5222c21d 1460 if (streq_len(path_parts[i], "$ENV(", 5))
81582353
A
1461 {
1462 char *p = strchr(path_parts[i], ')');
1463 if (p != NULL) *p = '\0';
1464 char *env_val = getenv(path_parts[i] + 5);
1465 if (env_val != NULL)
1466 {
1467 did_sub = 1;
1468
1469 if (env_val[0] != '/') asl_string_append_char_no_encoding(processed_path, '/');
1470 asl_string_append_no_encoding(processed_path, env_val);
1471 }
1472 }
1473 else
1474 {
1475 if (i == 0)
1476 {
1477 if (path_parts[0][0] != '\0') asl_string_append_no_encoding(processed_path, path_parts[i]);
1478 }
1479 else
1480 {
1481 asl_string_append_char_no_encoding(processed_path, '/');
1482 asl_string_append_no_encoding(processed_path, path_parts[i]);
1483 }
1484 }
1485
5222c21d 1486 if ((has_dotdot == 0) && streq(path_parts[i], "..")) has_dotdot = 1;
81582353
A
1487 }
1488
1489 free_string_list(path_parts);
f3df4c03 1490 path_parts = NULL;
81582353 1491
f3df4c03 1492 if ((did_sub == 1) && (has_dotdot == 0))
81582353
A
1493 {
1494 /* substitution might have added a ".." so check the new path */
1495 free(path);
f3df4c03 1496 path = asl_string_release_return_bytes(processed_path);
81582353
A
1497 processed_path = asl_string_new(ASL_ENCODE_NONE);
1498 path_parts = explode(path, "/");
1499 recursion_limit--;
1500 }
1501 }
1502
1503 free(path);
f3df4c03
A
1504 free_string_list(path_parts);
1505 path_parts = NULL;
81582353
A
1506
1507 if ((has_dotdot != 0) || (recursion_limit == 0))
1508 {
f3df4c03 1509 asl_string_release(processed_path);
81582353
A
1510 return NULL;
1511 }
1512
f3df4c03 1513 path = asl_string_release_return_bytes(processed_path);
81582353
A
1514
1515 /* check if there's already a dst for this path */
1516 for (rule = m->ruleset; rule != NULL; rule = rule->next)
1517 {
1518 if (rule->action != ACTION_OUT_DEST) continue;
1519
1520 dst = rule->dst;
1521 if (dst == NULL) continue;
1522
1523 if (_dst_path_match(path, dst->path))
1524 {
1525 free(path);
1526 return dst;
1527 }
1528 }
1529
f3df4c03
A
1530 flags |= MODULE_FLAG_NONSTD_DIR;
1531
81582353
A
1532 if (path[0] != '/')
1533 {
1534 char *t = path;
1535 const char *log_root = "/var/log";
1536
1537#if TARGET_IPHONE_SIMULATOR
f3df4c03
A
1538 log_root = getenv("SIMULATOR_LOG_ROOT");
1539 if (log_root == NULL) log_root = "/tmp/log";
81582353
A
1540#endif
1541
5222c21d 1542 if (streq(m->name, ASL_MODULE_NAME))
f3df4c03
A
1543 {
1544 asprintf(&path, "%s/%s", log_root, t);
1545 }
1546 else
1547 {
1548 asprintf(&path, "%s/module/%s/%s", log_root, m->name, t);
1549 }
81582353
A
1550
1551 free(t);
f3df4c03
A
1552 flags &= ~MODULE_FLAG_NONSTD_DIR;
1553 }
1554 else
1555 {
1556 /*
1557 * Standard log directories get marked so that syslogd
1558 * will create them without explicit rules.
1559 */
5222c21d
A
1560 if (streq_len(path, PATH_VAR_LOG, PATH_VAR_LOG_LEN)) flags &= ~MODULE_FLAG_NONSTD_DIR;
1561 else if (streq_len(path, PATH_LIBRARY_LOGS, PATH_LIBRARY_LOGS_LEN)) flags &= ~MODULE_FLAG_NONSTD_DIR;
81582353
A
1562 }
1563
1564 out = (asl_out_rule_t *)calloc(1, sizeof(asl_out_rule_t));
1565 dst = (asl_out_dst_data_t *)calloc(1, sizeof(asl_out_dst_data_t));
1566 if ((out == NULL) || (dst == NULL))
1567 {
1568 free(path);
1569 free(out);
1570 free(dst);
1571 return NULL;
1572 }
1573
1574 dst->refcount = 1;
1575 dst->path = path;
5222c21d
A
1576
1577 p = strrchr(dst->path, '/');
1578 if (p != NULL)
1579 {
1580 *p = '\0';
1581 dst->dir = strdup(dst->path);
1582 *p = '/';
1583 }
1584
81582353 1585 dst->mode = def_mode;
f3df4c03
A
1586 dst->ttl[LEVEL_ALL] = DEFAULT_TTL;
1587 dst->flags = flags | MODULE_FLAG_COALESCE;
81582353 1588
5222c21d
A
1589 /*
1590 * Break out base and extension (if present) from path.
1591 * Note this only supports a '.' as a separator.
1592 */
1593 p = strrchr(path, '/');
1594 if (p == NULL) p = path;
1595 else p++;
1596
1597 dot = strrchr(path, '.');
1598 if (dot != NULL)
1599 {
1600 *dot = '\0';
1601 dst->ext = strdup(dot + 1);
1602 }
1603
1604 dst->base = strdup(p);
1605 if (dot != NULL) *dot = '.';
1606
81582353
A
1607 while (NULL != (p = next_word_from_string(&opts)))
1608 {
1609 if (KEYMATCH(p, "mode=")) dst->mode = strtol(p+5, NULL, 0);
81582353
A
1610#if !TARGET_IPHONE_SIMULATOR
1611 else if (KEYMATCH(p, "uid=")) _dst_add_uid(dst, p+4);
1612 else if (KEYMATCH(p, "gid=")) _dst_add_gid(dst, p+4);
1613#endif
1614 else if (KEYMATCH(p, "fmt=")) dst->fmt = _dst_format_string(p+4);
1615 else if (KEYMATCH(p, "format=")) dst->fmt = _dst_format_string(p+7);
1616 else if (KEYMATCH(p, "dest=")) dst->rotate_dir = _strdup_clean(p+5);
1617 else if (KEYMATCH(p, "dst=")) dst->rotate_dir = _strdup_clean(p+4);
1618 else if (KEYMATCH(p, "coalesce="))
1619 {
1620 if (KEYMATCH(p+9, "0")) dst->flags &= ~MODULE_FLAG_COALESCE;
1621 else if (KEYMATCH(p+9, "off")) dst->flags &= ~MODULE_FLAG_COALESCE;
1622 else if (KEYMATCH(p+9, "false")) dst->flags &= ~MODULE_FLAG_COALESCE;
1623 }
1624 else if (KEYMATCH(p, "compress")) dst->flags |= MODULE_FLAG_COMPRESS;
5222c21d 1625 else if (KEYMATCH(p, "activity")) dst->flags |= MODULE_FLAG_ACTIVITY;
81582353 1626 else if (KEYMATCH(p, "extern")) dst->flags |= MODULE_FLAG_EXTERNAL;
f3df4c03
A
1627 else if (KEYMATCH(p, "truncate")) dst->flags |= MODULE_FLAG_TRUNCATE;
1628 else if (KEYMATCH(p, "dir")) dst->flags |= MODULE_FLAG_TYPE_ASL_DIR;
81582353 1629 else if (KEYMATCH(p, "soft")) dst->flags |= MODULE_FLAG_SOFT_WRITE;
5222c21d
A
1630 else if (KEYMATCH(p, "file_max=")) dst->file_max = asl_core_str_to_size(p+9);
1631 else if (KEYMATCH(p, "all_max=")) dst->all_max = asl_core_str_to_size(p+8);
81582353
A
1632 else if (KEYMATCH(p, "style=") || KEYMATCH(p, "rotate="))
1633 {
1634 const char *x = p + 6;
1635
5222c21d
A
1636 if (*p == 'r') x++;
1637 if (_parse_dst_style(dst, x) == 0) dst->flags |= MODULE_FLAG_ROTATE;
1638 }
1639 else if (KEYMATCH(p, "rotate"))
1640 {
1641 if (dst->ext == NULL) dst->style_flags = MODULE_NAME_STYLE_FORMAT_BS | MODULE_NAME_STYLE_STAMP_SEC;
1642 else dst->style_flags = MODULE_NAME_STYLE_FORMAT_BES | MODULE_NAME_STYLE_STAMP_SEC;
81582353
A
1643
1644 dst->flags |= MODULE_FLAG_ROTATE;
81582353 1645 }
81582353
A
1646 else if (KEYMATCH(p, "crashlog"))
1647 {
1648 /* crashlog implies rotation */
1649 dst->flags |= MODULE_FLAG_ROTATE;
1650 dst->flags |= MODULE_FLAG_CRASHLOG;
1651 dst->flags |= MODULE_FLAG_BASESTAMP;
1652 dst->flags &= ~MODULE_FLAG_COALESCE;
1653 }
1654 else if (KEYMATCH(p, "basestamp"))
1655 {
1656 dst->flags |= MODULE_FLAG_BASESTAMP;
1657 }
5222c21d
A
1658 else if (KEYMATCH(p, "link") || KEYMATCH(p, "symlink"))
1659 {
1660 dst->flags |= MODULE_FLAG_SYMLINK;
1661 }
f3df4c03
A
1662 else if (KEYMATCH(p, "ttl"))
1663 {
1664 char *q = p + 3;
1665 if (*q == '=')
1666 {
5222c21d 1667 dst->ttl[LEVEL_ALL] = asl_core_str_to_time(p+4, SECONDS_PER_DAY);
f3df4c03
A
1668 }
1669 else if ((*q >= '0') && (*q <= '7') && (*(q+1) == '='))
1670 {
1671 uint32_t x = *q - '0';
5222c21d 1672 dst->ttl[x] = asl_core_str_to_time(p+5, SECONDS_PER_DAY);
f3df4c03
A
1673 }
1674 }
81582353
A
1675
1676 free(p);
1677 p = NULL;
1678 }
1679
1680#if TARGET_OS_EMBEDDED
1681 /* check for crashreporter files */
5222c21d 1682 if ((KEYMATCH(dst->path, _PATH_CRASHREPORTER)) || (KEYMATCH(dst->path, _PATH_CRASHREPORTER_MOBILE_1)) || (KEYMATCH(dst->path, _PATH_CRASHREPORTER_MOBILE_2)))
81582353
A
1683 {
1684 dst->flags |= MODULE_FLAG_ROTATE;
1685 dst->flags |= MODULE_FLAG_CRASHLOG;
1686 dst->flags |= MODULE_FLAG_BASESTAMP;
1687 dst->flags &= ~MODULE_FLAG_COALESCE;
1688 }
1689#endif
1690
f3df4c03
A
1691 /* ttl[LEVEL_ALL] must be max of all level-specific ttls */
1692 for (i = 0; i <= 7; i++) if (dst->ttl[i] > dst->ttl[LEVEL_ALL]) dst->ttl[LEVEL_ALL] = dst->ttl[i];
1693
81582353
A
1694 /* default text file format is "std" */
1695 if (dst->fmt == NULL) dst->fmt = strdup("std");
1696
1697 /* duplicate compression is only possible for std and bsd formats */
1698 if (strcmp(dst->fmt, "std") && strcmp(dst->fmt, "bsd")) dst->flags &= ~MODULE_FLAG_COALESCE;
1699
1700 /* note if format is one of std, bsd, or msg */
5222c21d 1701 if (streq(dst->fmt, "std") || streq(dst->fmt, "bsd") || streq(dst->fmt, "msg")) dst->flags |= MODULE_FLAG_STD_BSD_MSG;
81582353 1702
5222c21d
A
1703 /* MODULE_NAME_STYLE_STAMP_SEQ can not be used with MODULE_FLAG_BASESTAMP */
1704 if ((dst->flags & MODULE_FLAG_BASESTAMP) && (dst->flags & MODULE_NAME_STYLE_STAMP_SEQ))
81582353 1705 {
5222c21d
A
1706 dst->flags &= ~MODULE_NAME_STYLE_STAMP_SEQ;
1707 dst->flags |= MODULE_NAME_STYLE_STAMP_SEC;
81582353
A
1708 }
1709
1710 /* set time format for raw output */
5222c21d 1711 if (streq(dst->fmt, "raw")) dst->tfmt = "sec";
81582353 1712
f3df4c03 1713 /* check for ASL_PLACE_DATABASE_DEFAULT */
5222c21d 1714 if (streq(dst->path, ASL_PLACE_DATABASE_DEFAULT))
f3df4c03
A
1715 {
1716 dst->flags = MODULE_FLAG_TYPE_ASL_DIR;
1717 }
1718
5222c21d
A
1719 /* set file_max to all_max if it is zero */
1720 if (dst->file_max == 0) dst->file_max = dst->all_max;
1721
81582353
A
1722 out->action = ACTION_OUT_DEST;
1723 out->dst = dst;
1724
1725 /* dst rules go first */
1726 out->next = m->ruleset;
1727 m->ruleset = out;
1728
1729 return dst;
1730}
1731
1732static asl_out_rule_t *
1733_asl_out_module_parse_query_action(asl_out_module_t *m, char *s)
1734{
1735 char *act, *p;
1736 asl_out_rule_t *out, *rule;
1737
1738 if (m == NULL) return NULL;
1739
1740 out = (asl_out_rule_t *)calloc(1, sizeof(asl_out_rule_t));
1741 if (out == NULL) return NULL;
1742
1743 act = _asl_out_module_find_action(s);
1744 if (act == NULL) return NULL;
1745
1746 /* find whitespace delimiter */
1747 p = strchr(act, ' ');
1748 if (p == NULL) p = strchr(act, '\t');
1749 if (p != NULL) *p = '\0';
1750
5222c21d
A
1751 if (strcaseeq(act, "ignore")) out->action = ACTION_IGNORE;
1752 else if (strcaseeq(act, "skip")) out->action = ACTION_SKIP;
1753 else if (strcaseeq(act, "claim")) out->action = ACTION_CLAIM;
1754 else if (strcaseeq(act, "notify")) out->action = ACTION_NOTIFY;
1755 else if (strcaseeq(act, "file")) out->action = ACTION_FILE;
1756 else if (strcaseeq(act, "asl_file")) out->action = ACTION_ASL_FILE;
1757 else if (strcaseeq(act, "directory")) out->action = ACTION_ASL_DIR;
1758 else if (strcaseeq(act, "dir")) out->action = ACTION_ASL_DIR;
1759 else if (strcaseeq(act, "asl_directory")) out->action = ACTION_ASL_DIR;
1760 else if (strcaseeq(act, "asl_dir")) out->action = ACTION_ASL_DIR;
1761 else if (strcaseeq(act, "store_dir")) out->action = ACTION_ASL_DIR;
1762 else if (strcaseeq(act, "store_directory")) out->action = ACTION_ASL_DIR;
1763 else if (strcaseeq(act, "control")) out->action = ACTION_CONTROL;
1764 else if (strcaseeq(act, "save")) out->action = ACTION_ASL_STORE;
1765 else if (strcaseeq(act, "store")) out->action = ACTION_ASL_STORE;
1766 else if (strcaseeq(act, "access")) out->action = ACTION_ACCESS;
1767 else if (strcaseeq(act, "set")) out->action = ACTION_SET_KEY;
1768 else if (strcaseeq(act, "unset")) out->action = ACTION_UNSET_KEY;
1769 else if (streq(m->name, ASL_MODULE_NAME))
81582353
A
1770 {
1771 /* actions only allowed in com.apple.asl */
5222c21d
A
1772 if (strcaseeq(act, "broadcast")) out->action = ACTION_BROADCAST;
1773 else if (strcaseeq(act, "forward")) out->action = ACTION_FORWARD;
81582353
A
1774 }
1775
1776 if (out->action == ACTION_NONE)
1777 {
1778 free(out);
1779 return NULL;
1780 }
1781
1782 /* options follow delimited (now zero) */
1783 if (p != NULL)
1784 {
1785 /* skip whitespace */
1786 while ((*p == ' ') || (*p == '\t')) p++;
1787
1788 out->options = _strdup_clean(p+1);
1789
1790 if (out->options == NULL)
1791 {
1792 free(out);
1793 return NULL;
1794 }
1795 }
1796
1797 p = act - 1;
1798
1799 *p = '\0';
1800
1801 if (*s== '*')
1802 {
1803 out->query = asl_msg_new(ASL_TYPE_QUERY);
1804 }
1805 else
1806 {
1807 *s = 'Q';
1808 out->query = asl_msg_from_string(s);
1809 }
1810
1811 if (out->query == NULL)
1812 {
1813 free(out->options);
1814 free(out);
1815 return NULL;
1816 }
1817
1818 /* store /some/path means save to an asl file */
f3df4c03
A
1819 if (out->action == ACTION_ASL_STORE)
1820 {
1821 if (out->options == NULL) out->dst = asl_out_dst_data_retain(_asl_out_module_parse_dst(m, ASL_PLACE_DATABASE_DEFAULT, 0755));
5222c21d 1822 else if (streq_len(out->options, ASL_PLACE_DATABASE_DEFAULT, strlen(ASL_PLACE_DATABASE_DEFAULT))) out->dst = asl_out_dst_data_retain(_asl_out_module_parse_dst(m, out->options, 0755));
f3df4c03
A
1823 else if (out->options != NULL) out->action = ACTION_ASL_FILE;
1824 }
81582353
A
1825
1826 if ((out->action == ACTION_FILE) || (out->action == ACTION_ASL_FILE) || (out->action == ACTION_ASL_DIR))
1827 {
1828 mode_t def_mode = 0644;
1829 if (out->action == ACTION_ASL_DIR) def_mode = 0755;
1830
1831 out->dst = asl_out_dst_data_retain(_asl_out_module_parse_dst(m, out->options, def_mode));
1832 if (out->dst == NULL)
1833 {
1834 out->action = ACTION_NONE;
1835 return out;
1836 }
1837
f3df4c03
A
1838 /*
1839 * dst might have been set up by a previous ACTION_OUT_DEST ('>') rule with no mode.
1840 * If so, mode would be 010000. Set it now, since we know whether it is a file or dir.
1841 */
1842 if (out->dst->mode == 010000) out->dst->mode = def_mode;
1843
5222c21d 1844 if ((out->action == ACTION_FILE) && (out->dst != NULL) && (out->dst->fmt != NULL) && (strcaseeq(out->dst->fmt, "asl")))
81582353
A
1845 {
1846 out->action = ACTION_ASL_FILE;
1847 }
1848
1849 if ((out->action == ACTION_ASL_FILE) && (out->dst != NULL))
1850 {
81582353
A
1851 out->dst->flags |= MODULE_FLAG_TYPE_ASL;
1852 }
1853
1854 if (out->action == ACTION_ASL_DIR)
1855 {
f3df4c03 1856 /* coalesce is meaningless for ASL directories */
81582353 1857 out->dst->flags &= ~MODULE_FLAG_COALESCE;
f3df4c03 1858
81582353 1859 out->dst->flags |= MODULE_FLAG_TYPE_ASL_DIR;
f3df4c03
A
1860
1861 /* set style bits for basestamp asl_dirs */
5222c21d 1862 if (((out->dst->style_flags & MODULE_NAME_STYLE_STAMP_MASK) == 0) && (out->dst->flags & MODULE_FLAG_BASESTAMP)) out->dst->style_flags |= MODULE_NAME_STYLE_STAMP_LCL_B;
81582353
A
1863 }
1864
1865 /* only ACTION_FILE and ACTION_ASL_FILE may rotate */
1866 if ((out->action != ACTION_FILE) && (out->action != ACTION_ASL_FILE))
1867 {
1868 out->dst->flags &= ~MODULE_FLAG_ROTATE;
1869 }
1870
1871#if !TARGET_IPHONE_SIMULATOR
1872 if (out->dst->nuid == 0) _dst_add_uid(out->dst, "0");
1873 if (out->dst->ngid == 0) _dst_add_gid(out->dst, "80");
1874#endif
1875 }
1876
1877 if (m->ruleset == NULL) m->ruleset = out;
1878 else
1879 {
1880 for (rule = m->ruleset; rule->next != NULL; rule = rule->next);
1881 rule->next = out;
1882 }
1883
1884 return out;
1885}
1886
1887asl_out_rule_t *
1888asl_out_module_parse_line(asl_out_module_t *m, char *s)
1889{
1890 while ((*s == ' ') || (*s == '\t')) s++;
1891
1892 if ((*s == 'Q') || (*s == '?') || (*s == '*'))
1893 {
1894 return _asl_out_module_parse_query_action(m, s);
1895 }
1896 else if (*s == '=')
1897 {
1898 return _asl_out_module_parse_set_param(m, s);
1899 }
5222c21d 1900 else if (*s == '>')
81582353 1901 {
f3df4c03 1902 _asl_out_module_parse_dst(m, s + 1, 010000);
81582353
A
1903 }
1904
1905 return NULL;
1906}
1907
1908asl_out_module_t *
1909asl_out_module_init_from_file(const char *name, FILE *f)
1910{
1911 asl_out_module_t *out;
1912 char *line;
1913
1914 if (f == NULL) return NULL;
1915
1916 out = asl_out_module_new(name);
1917 if (out == NULL) return NULL;
1918
1919 /* read and parse config file */
1920 while (NULL != (line = get_line_from_file(f)))
1921 {
1922 asl_out_module_parse_line(out, line);
1923 free(line);
1924 }
1925
1926 return out;
1927}
1928
1929static asl_out_module_t *
1930_asl_out_module_find(asl_out_module_t *list, const char *name)
1931{
1932 asl_out_module_t *x;
1933
1934 if (list == NULL) return NULL;
1935 if (name == NULL) return NULL;
1936
1937 for (x = list; x != NULL; x = x->next)
1938 {
5222c21d 1939 if ((x->name != NULL) && (streq(x->name, name))) return x;
81582353
A
1940 }
1941
1942 return NULL;
1943}
1944
1945static void
1946_asl_out_module_read_and_merge_dir(asl_out_module_t **list, const char *path, uint32_t flags)
1947{
1948 DIR *d;
1949 struct dirent *ent;
1950 FILE *f;
1951 asl_out_module_t *last, *x;
1952
1953 if (list == NULL) return;
1954 if (path == NULL) return;
1955
1956 last = *list;
1957 if (last != NULL)
1958 {
1959 while (last->next != NULL) last = last->next;
1960 }
1961
1962 d = opendir(path);
1963 if (d != NULL)
1964 {
1965 while (NULL != (ent = readdir(d)))
1966 {
5222c21d 1967 if (ent->d_name[0] != '.')
81582353
A
1968 {
1969 /* merge: skip this file if we already have a module with this name */
1970 if (_asl_out_module_find(*list, ent->d_name) != NULL) continue;
1971
1972 char tmp[MAXPATHLEN];
1973 snprintf(tmp, sizeof(tmp), "%s/%s", path, ent->d_name);
1974 f = fopen(tmp, "r");
1975 if (f != NULL)
1976 {
1977 x = asl_out_module_init_from_file(ent->d_name, f);
1978 fclose(f);
1979
1980 if (x != NULL)
1981 {
1982 x->flags |= flags;
1983
5222c21d 1984 if (streq(ent->d_name, ASL_MODULE_NAME))
81582353
A
1985 {
1986 /* com.apple.asl goes at the head of the list */
1987 x->next = *list;
1988 *list = x;
1989 if (last == NULL) last = *list;
1990 }
1991 else if (*list == NULL)
1992 {
1993 *list = x;
1994 last = *list;
1995 }
1996 else
1997 {
1998 last->next = x;
1999 last = x;
2000 }
2001 }
2002 }
2003 }
2004 }
2005
2006 closedir(d);
2007 }
2008}
2009
2010asl_out_module_t *
2011asl_out_module_init(void)
2012{
2013 asl_out_module_t *out = NULL;
2014
2015#if TARGET_IPHONE_SIMULATOR
2016 char *sim_root_path, *sim_resources_path;
2017 char *asl_conf, *asl_conf_dir, *asl_conf_local_dir;
2018
2019 sim_root_path = getenv("IPHONE_SIMULATOR_ROOT");
5222c21d 2020 if (sim_root_path == NULL) return NULL;
81582353
A
2021
2022 sim_resources_path = getenv("IPHONE_SHARED_RESOURCES_DIRECTORY");
5222c21d 2023 if (sim_resources_path == NULL) return NULL;
81582353
A
2024
2025 asprintf(&asl_conf, "%s%s", sim_root_path, _PATH_ASL_CONF);
2026 asprintf(&asl_conf_dir, "%s%s", sim_root_path, _PATH_ASL_CONF_DIR);
2027 asprintf(&asl_conf_local_dir, "%s%s", sim_resources_path, _PATH_ASL_CONF_DIR);
2028
2029 _asl_out_module_read_and_merge_dir(&out, asl_conf_local_dir, MODULE_FLAG_LOCAL);
2030 free(asl_conf_local_dir);
2031
2032 _asl_out_module_read_and_merge_dir(&out, asl_conf_dir, 0);
2033 free(asl_conf_dir);
2034#else
2035 _asl_out_module_read_and_merge_dir(&out, _PATH_ASL_CONF_LOCAL_DIR, MODULE_FLAG_LOCAL);
2036 _asl_out_module_read_and_merge_dir(&out, _PATH_ASL_CONF_DIR, 0);
2037#endif
2038
2039 if (_asl_out_module_find(out, ASL_MODULE_NAME) == NULL)
2040 {
2041 /* system just has old-style /etc/asl.conf */
2042#if TARGET_IPHONE_SIMULATOR
2043 FILE *f = fopen(asl_conf, "r");
2044 free(asl_conf);
2045#else
2046 FILE *f = fopen(_PATH_ASL_CONF, "r");
2047#endif
2048 if (f != NULL)
2049 {
2050 asl_out_module_t *x = asl_out_module_init_from_file(ASL_MODULE_NAME, f);
2051 fclose(f);
2052 if (x != NULL)
2053 {
2054 x->next = out;
2055 out = x;
2056 }
2057 }
2058 }
2059
2060 return out;
2061}
2062
2063/*
2064 * Print rule
2065 */
2066char *
2067asl_out_module_rule_to_string(asl_out_rule_t *r)
2068{
2069 uint32_t len;
2070 char *str, *out;
2071
2072 if (r == NULL)
2073 {
2074 asprintf(&out, "NULL rule");
2075 return out;
2076 }
2077
2078 str = asl_msg_to_string(r->query, &len);
2079
2080 asprintf(&out, " %s%s%s%s%s",
2081 asl_out_action_name[r->action],
5222c21d 2082 (r->query == NULL) ? "" : " ",
81582353 2083 (r->query == NULL) ? "" : str,
5222c21d 2084 (r->options == NULL) ? "" : " ",
81582353
A
2085 (r->options == NULL) ? "" : r->options);
2086
2087 free(str);
2088 return out;
2089}
2090
5222c21d
A
2091static const char *
2092_stamp_style_name(uint32_t flags)
2093{
2094 if (flags & MODULE_NAME_STYLE_STAMP_SEC) return "<seconds>";
2095 if (flags & MODULE_NAME_STYLE_STAMP_SEQ) return "<sequence>";
2096 if (flags & MODULE_NAME_STYLE_STAMP_UTC) return "<utc>";
2097 if (flags & MODULE_NAME_STYLE_STAMP_UTC_B) return "<utc-basic>";
2098 if (flags & MODULE_NAME_STYLE_STAMP_LCL) return "<local>";
2099 if (flags & MODULE_NAME_STYLE_STAMP_LCL_B) return "<local-basic>";
2100 return "<unknown>";
2101}
2102
81582353
A
2103/*
2104 * Print module
2105 */
2106void
2107asl_out_module_print(FILE *f, asl_out_module_t *m)
2108{
2109 asl_out_rule_t *r, *n;
2110 asl_out_dst_data_t *o;
f3df4c03 2111 uint32_t i, ttlnset;
5222c21d 2112 char tstr[150];
81582353
A
2113
2114 n = NULL;
2115 for (r = m->ruleset; r != NULL; r = n)
2116 {
2117 uint32_t len;
2118 char *str = asl_msg_to_string(r->query, &len);
2119
2120 fprintf(f, " %s", asl_out_action_name[r->action]);
2121 if (r->query != NULL) fprintf(f, " %s", str);
2122 if (r->options != NULL) fprintf(f, " %s", r->options);
2123 if (r->action == ACTION_OUT_DEST)
2124 {
2125 o = r->dst;
2126 if (o == NULL)
2127 {
2128 fprintf(f, " data: NULL");
2129 }
2130 else
2131 {
2132 fprintf(f, "%s\n", o->path);
2133 fprintf(f, " rules: %u\n", o->refcount - 1);
2134 fprintf(f, " dest: %s\n", (o->rotate_dir == NULL) ? "(none)" : o->rotate_dir);
2135 fprintf(f, " format: %s\n", (o->fmt == NULL) ? "std" : o->fmt);
2136 fprintf(f, " time_format: %s\n", (o->tfmt == NULL) ? "lcl" : o->tfmt);
2137 fprintf(f, " flags: 0x%08x", o->flags);
2138 if (o->flags != 0)
2139 {
2140 char c = '(';
2141 fprintf(f, " ");
2142 if (o->flags & MODULE_FLAG_ENABLED)
2143 {
2144 fprintf(f, "%cenabled", c);
2145 c = ' ';
2146 }
f3df4c03 2147 if (o->flags & MODULE_FLAG_SOFT_WRITE)
81582353 2148 {
f3df4c03 2149 fprintf(f, "%csoft", c);
81582353
A
2150 c = ' ';
2151 }
2152 if (o->flags & MODULE_FLAG_ROTATE)
2153 {
2154 fprintf(f, "%crotate", c);
2155 c = ' ';
2156 }
2157 if (o->flags & MODULE_FLAG_COALESCE)
2158 {
2159 fprintf(f, "%ccoalesce", c);
2160 c = ' ';
2161 }
2162 if (o->flags & MODULE_FLAG_COMPRESS)
2163 {
2164 fprintf(f, "%ccompress", c);
2165 c = ' ';
2166 }
5222c21d 2167 if (o->flags & MODULE_FLAG_BASESTAMP)
81582353 2168 {
5222c21d 2169 fprintf(f, "%cbasestamp", c);
81582353
A
2170 c = ' ';
2171 }
5222c21d 2172 if (o->flags & MODULE_FLAG_SYMLINK)
81582353 2173 {
5222c21d 2174 fprintf(f, "%csymlink", c);
81582353
A
2175 c = ' ';
2176 }
f3df4c03 2177 if (o->flags & MODULE_FLAG_NONSTD_DIR)
81582353 2178 {
f3df4c03 2179 fprintf(f, "%cnon-std_dir", c);
81582353
A
2180 c = ' ';
2181 }
f3df4c03 2182 if (o->flags & MODULE_FLAG_EXTERNAL)
81582353 2183 {
f3df4c03
A
2184 fprintf(f, "%cexternal", c);
2185 c = ' ';
2186 }
5222c21d
A
2187 if (o->flags & MODULE_FLAG_ACTIVITY)
2188 {
2189 fprintf(f, "%cactivity", c);
2190 c = ' ';
2191 }
f3df4c03
A
2192 if (o->flags & MODULE_FLAG_CRASHLOG)
2193 {
2194 fprintf(f, "%ccrashlog", c);
81582353
A
2195 c = ' ';
2196 }
2197 if (o->flags & MODULE_FLAG_TYPE_ASL)
2198 {
2199 fprintf(f, "%casl_file", c);
2200 c = ' ';
2201 }
2202 if (o->flags & MODULE_FLAG_TYPE_ASL_DIR)
2203 {
2204 fprintf(f, "%casl_directory", c);
2205 c = ' ';
2206 }
81582353
A
2207 fprintf(f, ")");
2208 }
2209 fprintf(f, "\n");
2210
5222c21d
A
2211 if (o->flags & MODULE_FLAG_ROTATE)
2212 {
2213 fprintf(f, " rotatation style: ");
2214 if (o->style_flags & MODULE_NAME_STYLE_FORMAT_BS)
2215 {
2216 fprintf(f, "[base=%s].%s\n", o->base, _stamp_style_name(o->style_flags));
2217 }
2218 else if (o->style_flags & MODULE_NAME_STYLE_FORMAT_BES)
2219 {
2220 fprintf(f, "[base=%s].[ext=%s].%s\n", o->base, o->ext, _stamp_style_name(o->style_flags));
2221 }
2222 else if (o->style_flags & MODULE_NAME_STYLE_FORMAT_BSE)
2223 {
2224 fprintf(f, "[base=%s].%s.[ext=%s]\n", o->base, _stamp_style_name(o->style_flags), o->ext);
2225 }
2226 else
2227 {
2228 fprintf(f, "0x%08x\n", o->style_flags);
2229 }
2230 }
2231
2232 asl_core_time_to_str(o->ttl[LEVEL_ALL], tstr, sizeof(tstr));
2233 fprintf(f, " ttl: %s\n", tstr);
2234
f3df4c03
A
2235 ttlnset = 0;
2236 for (i = 0; (i <= 7) & (ttlnset == 0); i++) if (o->ttl[i] != 0) ttlnset = 1;
5222c21d
A
2237 if (ttlnset != 0)
2238 {
2239 for (i = 0; i <= 7; i++)
2240 {
2241 time_t x = o->ttl[i];
2242 if (x == 0) x = o->ttl[LEVEL_ALL];
2243 asl_core_time_to_str(x, tstr, sizeof(tstr));
2244
2245 fprintf(f, " [%d %s]", i, tstr);
2246 }
2247
2248 fprintf(f, "\n");
2249 }
f3df4c03 2250
81582353
A
2251 fprintf(f, " mode: 0%o\n", o->mode);
2252 fprintf(f, " file_max: %lu\n", o->file_max);
2253 fprintf(f, " all_max: %lu\n", o->all_max);
2254#if !TARGET_IPHONE_SIMULATOR
2255 fprintf(f, " uid:");
2256 for (i = 0; i < o->nuid; i++) fprintf(f, " %d", o->uid[i]);
2257 fprintf(f, "\n");
2258 fprintf(f, " gid:");
2259 for (i = 0; i < o->ngid; i++) fprintf(f, " %d", o->gid[i]);
2260#endif
2261 }
2262 }
2263
2264 fprintf(f, "\n");
2265 n = r->next;
2266
2267 free(str);
2268 }
2269}
2270
2271void
2272asl_out_file_list_free(asl_out_file_list_t *l)
2273{
2274 asl_out_file_list_t *n;
2275
2276 if (l == NULL) return;
2277
2278 while (l != NULL)
2279 {
2280 free(l->name);
2281 n = l->next;
2282 free(l);
2283 l = n;
2284 }
2285}
2286
2287/*
5222c21d
A
2288 * Checks input name for one of the forms:
2289 * MODULE_NAME_STYLE_FORMAT_BS base (src only) or base.stamp[.gz]
2290 * MODULE_NAME_STYLE_FORMAT_BES base.ext (src only) or base.ext.stamp[.gz]
2291 * MODULE_NAME_STYLE_FORMAT_BSE base.ext (src only) or base.stamp.ext[.gz]
2292 *
2293 * name == base[.ext] is allowed if src is true.
81582353
A
2294 * base.gz is not allowed.
2295 * Output parameter stamp must be freed by caller.
2296 */
2297bool
5222c21d 2298_check_file_name(const char *name, const char *base, const char *ext, uint32_t flags, bool src, char **stamp)
81582353 2299{
5222c21d
A
2300 size_t baselen, extlen;
2301 const char *p, *z;
81582353
A
2302 bool isgz = false;
2303
5222c21d
A
2304#ifdef DEBUG_LIST_FILES
2305 fprintf(stderr, "_check_file_name name=%s base=%s ext=%s flags=0x%08x %s\n", name, base, (ext == NULL) ? "(NULL)" : ext, flags, src ? "src" : "dst");
2306#endif
2307
81582353
A
2308 if (name == NULL) return false;
2309 if (base == NULL) return false;
2310
2311 baselen = strlen(base);
2312 if (baselen == 0) return false;
2313
5222c21d
A
2314 extlen = 0;
2315 if (ext != NULL) extlen = strlen(ext);
2316
81582353
A
2317 if (stamp != NULL) *stamp = NULL;
2318
5222c21d
A
2319 if (strneq_len(name, base, baselen))
2320 {
2321#ifdef DEBUG_LIST_FILES
2322 fprintf(stderr, " base match failed [%u]\n", __LINE__);
2323#endif
2324 return false;
2325 }
81582353 2326
5222c21d
A
2327 z = strrchr(name, '.');
2328 if ((z != NULL) && streq(z, ".gz")) isgz = true;
81582353 2329
5222c21d
A
2330#ifdef DEBUG_LIST_FILES
2331 fprintf(stderr, "z=%s isgz=%s\n", (z == NULL) ? "NULL" : z, isgz ? "true" : "false");
2332#endif
81582353 2333
5222c21d 2334 p = name + baselen;
81582353 2335
5222c21d 2336 if (flags & MODULE_NAME_STYLE_FORMAT_BS)
81582353 2337 {
5222c21d
A
2338#ifdef DEBUG_LIST_FILES
2339 fprintf(stderr, " MODULE_NAME_STYLE_FORMAT_BS\n");
2340#endif
2341 if (*p == '\0')
81582353 2342 {
5222c21d
A
2343 /* name == base OK if src is true */
2344#ifdef DEBUG_LIST_FILES
2345 fprintf(stderr, " name == base %s [%u]\n", src ? "OK" : "failed", __LINE__);
2346#endif
2347 return src;
81582353 2348 }
81582353 2349
5222c21d
A
2350 /* expecting p == .stamp[.gz] */
2351 if (*p != '.')
2352 {
2353#ifdef DEBUG_LIST_FILES
2354 fprintf(stderr, " expecting dot - failed [%u]\n", __LINE__);
2355#endif
2356 return false;
2357 }
81582353 2358
5222c21d 2359 p++;
81582353 2360
5222c21d
A
2361 if (p == z)
2362 {
2363 /* base.gz is not allowed */
2364#ifdef DEBUG_LIST_FILES
2365 fprintf(stderr, " got base.gz - failed [%u]\n", __LINE__);
2366#endif
2367 return false;
2368 }
81582353 2369
5222c21d
A
2370 /* got base.stamp[.gz] */
2371 if (stamp != NULL)
2372 {
2373 *stamp = strdup(p);
2374 char *x = strchr(*stamp, '.');
2375 if (x != NULL) *x = '\0';
2376 }
81582353 2377
5222c21d
A
2378#ifdef DEBUG_LIST_FILES
2379 fprintf(stderr, " got base.stamp%s - OK\n", isgz ? ".gz" : "");
2380#endif
2381 return true;
2382 }
2383 else if (flags & MODULE_NAME_STYLE_FORMAT_BES)
81582353 2384 {
5222c21d
A
2385#ifdef DEBUG_LIST_FILES
2386 fprintf(stderr, " MODULE_NAME_STYLE_FORMAT_BES\n");
2387#endif
2388 if (*p != '.')
2389 {
2390#ifdef DEBUG_LIST_FILES
2391 fprintf(stderr, " expecting dot - failed [%u]\n", __LINE__);
2392#endif
2393 return false;
2394 }
2395
2396 p++;
2397
2398 if (strneq_len(p, ext, extlen))
2399 {
2400#ifdef DEBUG_LIST_FILES
2401 fprintf(stderr, " ext match failed [%u]\n", __LINE__);
2402#endif
2403 return false;
2404 }
81582353 2405
5222c21d
A
2406 /* expecting p == .ext[.stamp][.gz] */
2407 p += extlen;
2408
2409 if (*p == '\0')
2410 {
2411 /* name == base.ext OK if src is true */
2412#ifdef DEBUG_LIST_FILES
2413 fprintf(stderr, " name == base.ext %s [%u]\n", src ? "OK" : "failed", __LINE__);
2414#endif
2415 return src;
2416 }
2417
2418 /* expecting p == .stamp[.gz] */
2419 if (*p != '.')
2420 {
2421#ifdef DEBUG_LIST_FILES
2422 fprintf(stderr, " expecting dot - failed [%u]\n", __LINE__);
2423#endif
2424 return false;
2425 }
2426
2427 p++;
2428
2429 if (p == z)
2430 {
2431 /* base.ext.gz is not allowed */
2432#ifdef DEBUG_LIST_FILES
2433 fprintf(stderr, " got base.ext.gz - failed [%u]\n", __LINE__);
2434#endif
2435 return false;
2436 }
2437
2438 /* got base.ext.stamp[.gz] */
2439 if (stamp != NULL)
2440 {
2441 *stamp = strdup(p);
2442 char *x = strchr(*stamp, '.');
2443 if (x != NULL) *x = '\0';
2444 }
2445
2446#ifdef DEBUG_LIST_FILES
2447 fprintf(stderr, " got base.ext.stamp%s - OK\n", isgz ? ".gz" : "");
2448#endif
81582353
A
2449 return true;
2450 }
5222c21d
A
2451 else if (flags & MODULE_NAME_STYLE_FORMAT_BSE)
2452 {
2453#ifdef DEBUG_LIST_FILES
2454 fprintf(stderr, " MODULE_NAME_STYLE_FORMAT_BSE name=%s base=%s ext=%s flags=0x%08x %s\n", name, base, (ext == NULL) ? "(NULL)" : ext, flags, src ? "src" : "dst");
2455#endif
81582353 2456
5222c21d
A
2457 if (*p != '.')
2458 {
2459#ifdef DEBUG_LIST_FILES
2460 fprintf(stderr, " expecting dot - failed [%u]\n", __LINE__);
2461#endif
2462 return false;
2463 }
81582353 2464
5222c21d
A
2465 p++;
2466
2467 if (streq_len(p, ext, extlen))
2468 {
2469 p += extlen;
2470 if (*p == '\0')
2471 {
2472 /* name == base.ext OK if src is true */
2473#ifdef DEBUG_LIST_FILES
2474 fprintf(stderr, " name == base.ext %s [%u]\n", src ? "OK" : "failed", __LINE__);
2475#endif
2476 return src;
2477 }
2478 }
2479
2480 /* expecting p == stamp.ext[.gz] */
2481
2482 if (isgz)
2483 {
2484 if (strneq_len(z - extlen, ext, extlen))
2485 {
2486#ifdef DEBUG_LIST_FILES
2487 fprintf(stderr, " ext match (%s) isgz failed [%u]\n", z-extlen, __LINE__);
2488#endif
2489 return false;
2490 }
2491 }
2492 else
2493 {
2494 if (strneq_len(z + 1, ext, extlen))
2495 {
2496#ifdef DEBUG_LIST_FILES
2497 fprintf(stderr, " ext match (%s) failed [%u]\n", z, __LINE__);
2498#endif
2499 return false;
2500 }
2501 }
2502
2503 /* got base.stamp.ext[.gz] */
2504 if (stamp != NULL)
2505 {
2506 *stamp = strdup(p);
2507 char *x = strchr(*stamp, '.');
2508 if (x != NULL) *x = '\0';
2509 }
2510
2511#ifdef DEBUG_LIST_FILES
2512 fprintf(stderr, " got base.stamp.ext%s - OK\n", isgz ? ".gz" : "");
2513#endif
2514 return true;
81582353
A
2515 }
2516
5222c21d
A
2517#ifdef DEBUG_LIST_FILES
2518 fprintf(stderr, " unknown format - failed\n");
2519#endif
2520
2521 return false;
81582353
A
2522}
2523
2524/*
2525 * Find files in a directory (dir) that all have a common prefix (base).
2526 * Bits in flags further control the search.
2527 *
5222c21d 2528 * MODULE_NAME_STYLE_STAMP_SEQ means a numeric sequence number is expected, although not required.
81582353
A
2529 * E.g. foo.log foo.log.0
2530 *
5222c21d 2531 * MODULE_NAME_STYLE_STAMP_SEC also means a numeric sequence number is required following an 'T' character.
81582353
A
2532 * The numeric value is the file's timestamp in seconds. E.g foo.log.T1335200452
2533 *
5222c21d 2534 * MODULE_NAME_STYLE_STAMP_UTC requires a date/time component as the file's timestamp.
81582353
A
2535 * E.g. foo.2012-04-06T15:30:00Z
2536 *
5222c21d 2537 * MODULE_NAME_STYLE_STAMP_UTC_B requires a date/time component as the file's timestamp.
81582353
A
2538 * E.g. foo.20120406T153000Z
2539 *
5222c21d 2540 * MODULE_NAME_STYLE_STAMP_LCL requires a date/time component as the file's timestamp.
81582353
A
2541 * E.g. foo.2012-04-06T15:30:00-7
2542 *
5222c21d 2543 * MODULE_NAME_STYLE_STAMP_LCL_B requires a date/time component as the file's timestamp.
81582353
A
2544 * E.g. foo.20120406T153000-07
2545 */
f3df4c03
A
2546int
2547_parse_stamp_style(char *stamp, uint32_t flags, uint32_t *sp, time_t *tp)
2548{
2549 int i, n;
2550 bool digits;
2551 struct tm t;
2552 char zone;
2553 uint32_t h, m, s;
2554 long utc_offset = 0;
2555 time_t ftime = 0;
2556
2557 /* check for NULL (no stamp) */
2558 if (stamp == NULL) return STAMP_STYLE_NULL;
2559
5222c21d 2560 /* check for MODULE_NAME_STYLE_STAMP_SEC (foo.T12345678) */
f3df4c03
A
2561 if (stamp[0] == 'T')
2562 {
2563 n = atoi(stamp + 1);
2564 if ((n == 0) && strcmp(stamp + 1, "0")) return STAMP_STYLE_INVALID;
2565 if (tp != NULL) *tp = (time_t)n;
2566
2567 return STAMP_STYLE_SEC;
2568 }
2569
5222c21d 2570 /* check for MODULE_NAME_STYLE_STAMP_SEQ (foo.0 or foo.2.gz) */
f3df4c03
A
2571 digits = true;
2572 for (i = 0; digits && (stamp[i] != '\0'); i++) digits = (stamp[i] >= '0') && (stamp[i] <= '9');
2573
5222c21d 2574 if (!digits && (streq(stamp + i, ".gz"))) digits = true;
f3df4c03
A
2575
2576 if (digits)
2577 {
2578 n = atoi(stamp);
2579 if (sp != NULL) *sp = (uint32_t)n;
2580 return STAMP_STYLE_SEQ;
2581 }
2582
5222c21d 2583 /* check for MODULE_NAME_STYLE_STAMP_UTC, UTC_B, LCL, or LCL_B */
f3df4c03
A
2584 memset(&t, 0, sizeof(t));
2585 h = m = s = 0;
2586
2587 n = 0;
5222c21d 2588 if ((flags & MODULE_NAME_STYLE_STAMP_UTC) || (flags & MODULE_NAME_STYLE_STAMP_LCL))
f3df4c03
A
2589 {
2590 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);
2591 }
5222c21d 2592 else if ((flags & MODULE_NAME_STYLE_STAMP_UTC_B) || (flags & MODULE_NAME_STYLE_STAMP_LCL_B))
f3df4c03
A
2593 {
2594 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);
2595 }
2596 else
2597 {
5222c21d
A
2598#ifdef DEBUG_LIST_FILES
2599 fprintf(stderr, "_parse_stamp_style fail %u\n", __LINE__);
2600#endif
f3df4c03
A
2601 return STAMP_STYLE_INVALID;
2602 }
2603
5222c21d
A
2604 if (n < 6)
2605 {
2606#ifdef DEBUG_LIST_FILES
2607 fprintf(stderr, "_parse_stamp_style fail %u\n", __LINE__);
2608#endif
2609 return STAMP_STYLE_INVALID;
2610 }
f3df4c03
A
2611
2612 if (n == 6)
2613 {
2614 zone = 'J';
2615 }
2616 else if ((zone == '-') || (zone == '+'))
2617 {
2618 if (n >= 8) utc_offset += (3600 * h);
2619 if (n >= 9) utc_offset += (60 * m);
2620 if (n == 10) utc_offset += s;
5222c21d 2621 if (zone == '+') utc_offset *= -1;
f3df4c03
A
2622 }
2623 else if ((zone >= 'A') && (zone <= 'Z'))
2624 {
2625 if (zone < 'J') utc_offset = 3600 * ((zone - 'A') + 1);
2626 else if ((zone >= 'K') && (zone <= 'M')) utc_offset = 3600 * (zone - 'A');
2627 else if (zone <= 'Y') utc_offset = -3600 * ((zone - 'N') + 1);
2628 }
2629 else if ((zone >= 'a') && (zone <= 'z'))
2630 {
2631 if (zone < 'j') utc_offset = 3600 * ((zone - 'a') + 1);
2632 else if ((zone >= 'k') && (zone <= 'm')) utc_offset = 3600 * (zone - 'a');
2633 else if (zone <= 'y') utc_offset = -3600 * ((zone - 'n') + 1);
2634 }
2635 else
2636 {
5222c21d
A
2637#ifdef DEBUG_LIST_FILES
2638 fprintf(stderr, "_parse_stamp_style fail %u\n", __LINE__);
2639#endif
f3df4c03
A
2640 return STAMP_STYLE_INVALID;
2641 }
2642
2643 t.tm_year -= 1900;
2644 t.tm_mon -= 1;
2645 t.tm_sec += utc_offset;
2646 t.tm_isdst = -1;
2647
2648 if ((zone == 'J') || (zone == 'j')) ftime = mktime(&t);
2649 else ftime = timegm(&t);
2650
2651 if (tp != NULL) *tp = ftime;
2652
5222c21d 2653 return STAMP_STYLE_ISO8601;
f3df4c03
A
2654}
2655
81582353 2656asl_out_file_list_t *
5222c21d 2657asl_list_log_files(const char *dir, const char *base, const char *ext, uint32_t flags, bool src)
81582353
A
2658{
2659 DIR *d;
2660 struct dirent *ent;
2661 char path[MAXPATHLEN];
2662 uint32_t seq;
2663 time_t ftime;
2664 struct stat sb;
f3df4c03 2665 int pstyle, fstyle;
81582353
A
2666 asl_out_file_list_t *out, *x, *y;
2667
5222c21d
A
2668#ifdef DEBUG_LIST_FILES
2669 fprintf(stderr, "asl_list_log_files dir=%s base=%s ext=%s flags=0x%08x %s\n", dir, base, (ext == NULL) ? "(NULL)" : ext, flags, src ? "src" : "dst");
2670#endif
2671
81582353
A
2672 if (dir == NULL) return NULL;
2673 if (base == NULL) return NULL;
2674
2675 out = NULL;
2676
2677 d = opendir(dir);
2678 if (d == NULL) return NULL;
2679
2680 while (NULL != (ent = readdir(d)))
2681 {
2682 char *stamp = NULL;
2683 bool check;
5222c21d 2684 bool stat_ok = false;
81582353 2685
5222c21d 2686 check = _check_file_name(ent->d_name, base, ext, flags, src, &stamp);
81582353
A
2687 if (!check) continue;
2688
81582353
A
2689 seq = IndexNull;
2690 ftime = 0;
2691
f3df4c03
A
2692 pstyle = _parse_stamp_style(stamp, flags, &seq, &ftime);
2693 free(stamp);
81582353 2694
f3df4c03 2695 if (pstyle == STAMP_STYLE_INVALID) continue;
81582353 2696
5222c21d
A
2697 snprintf(path, sizeof(path), "%s/%s", dir, ent->d_name);
2698 memset(&sb, 0, sizeof(sb));
2699 if (lstat(path, &sb) == 0)
2700 {
2701 /* ignore symlinks (created for basestamp / symlink files) */
2702 stat_ok = true;
2703 if ((sb.st_mode & S_IFMT) == S_IFLNK) continue;
2704 }
2705
f3df4c03 2706 fstyle = STAMP_STYLE_NULL;
5222c21d
A
2707 if (flags & MODULE_NAME_STYLE_STAMP_SEC) fstyle = STAMP_STYLE_SEC;
2708 else if (flags & MODULE_NAME_STYLE_STAMP_SEQ) fstyle = STAMP_STYLE_SEQ;
2709 else if ((flags & MODULE_NAME_STYLE_STAMP_UTC) || (flags & MODULE_NAME_STYLE_STAMP_LCL)) fstyle = STAMP_STYLE_ISO8601;
2710 else if ((flags & MODULE_NAME_STYLE_STAMP_UTC_B) || (flags & MODULE_NAME_STYLE_STAMP_LCL_B)) fstyle = STAMP_STYLE_ISO8601;
2711
2712#ifdef DEBUG_LIST_FILES
2713 fprintf(stderr, "%s %s %u fstyle %u pstyle %u\n", __func__, path, __LINE__, fstyle, pstyle);
2714#endif
81582353 2715
f3df4c03
A
2716 /*
2717 * accept the file if:
2718 * style is STAMP_STYLE_NULL (no timestamp)
2719 * src is true and style is STAMP_STYLE_SEC
2720 * actual style matches the style implied by the input flags
2721 */
81582353 2722
f3df4c03
A
2723 check = false;
2724 if (pstyle == STAMP_STYLE_NULL) check = true;
2725 if ((pstyle == STAMP_STYLE_SEC) && src) check = true;
2726 if (pstyle == fstyle) check = true;
81582353 2727
5222c21d
A
2728 if (!check)
2729 {
2730#ifdef DEBUG_LIST_FILES
2731 fprintf(stderr, "%s reject %s at line %u\n", __func__, path, __LINE__);
2732#endif
2733 continue;
2734 }
2735
81582353
A
2736 x = (asl_out_file_list_t *)calloc(1, sizeof(asl_out_file_list_t));
2737 if (x == NULL)
2738 {
2739 asl_out_file_list_free(out);
2740 return NULL;
2741 }
2742
2743 x->name = strdup(ent->d_name);
5222c21d 2744 x->stamp = pstyle;
81582353
A
2745 x->ftime = ftime;
2746 x->seq = seq;
2747
5222c21d 2748 if (stat_ok)
81582353
A
2749 {
2750 x->size = sb.st_size;
f3df4c03 2751 if (pstyle == STAMP_STYLE_SEQ)
81582353
A
2752 {
2753 x->ftime = sb.st_birthtimespec.tv_sec;
2754 if (x->ftime == 0) x->ftime = sb.st_mtimespec.tv_sec;
2755 }
2756 }
2757
f3df4c03 2758 if (pstyle == STAMP_STYLE_SEQ)
81582353 2759 {
5222c21d
A
2760#ifdef DEBUG_LIST_FILES
2761 fprintf(stderr, "asl_list_log_files SEQ %s %u %ld\n", path, x->seq, x->ftime);
2762#endif
81582353
A
2763 if (out == NULL)
2764 {
2765 out = x;
2766 }
5222c21d 2767 else if ((x->seq == IndexNull) || ((x->seq > out->seq) && (out->seq != IndexNull)))
81582353
A
2768 {
2769 x->next = out;
2770 out->prev = x;
2771 out = x;
2772 }
2773 else
2774 {
2775 for (y = out; y != NULL; y = y->next)
2776 {
2777 if (y->next == NULL)
2778 {
2779 y->next = x;
2780 x->prev = y;
2781 break;
2782 }
5222c21d 2783 else if ((x->seq > y->next->seq) && (y->next->seq != IndexNull))
81582353
A
2784 {
2785 x->next = y->next;
2786 y->next = x;
2787 x->prev = y;
2788 x->next->prev = x;
2789 break;
2790 }
2791 }
2792 }
2793 }
2794 else
2795 {
5222c21d
A
2796#ifdef DEBUG_LIST_FILES
2797 fprintf(stderr, "asl_list_log_files TIME %s %ld\n", path, x->ftime);
2798#endif
81582353
A
2799 if (out == NULL)
2800 {
2801 out = x;
2802 }
2803 else if (x->ftime < out->ftime)
2804 {
2805 x->next = out;
2806 out->prev = x;
2807 out = x;
2808 }
2809 else
2810 {
2811 for (y = out; y != NULL; y = y->next)
2812 {
2813 if (y->next == NULL)
2814 {
2815 y->next = x;
2816 x->prev = y;
2817 break;
2818 }
2819 else if (x->ftime < y->next->ftime)
2820 {
2821 x->next = y->next;
2822 y->next = x;
2823 x->prev = y;
2824 x->next->prev = x;
2825 break;
2826 }
2827 }
2828 }
2829 }
2830 }
2831
2832 closedir(d);
2833 return out;
2834}
2835
2836/*
2837 * List the source files for an output asl_out_dst_data_t
2838 */
2839asl_out_file_list_t *
2840asl_list_src_files(asl_out_dst_data_t *dst)
2841{
5222c21d
A
2842 uint32_t flags;
2843#ifdef DEBUG_LIST_FILES
2844 fprintf(stderr, "asl_list_src_files\n");
2845#endif
81582353
A
2846
2847 if (dst == NULL) return NULL;
2848 if (dst->path == NULL) return NULL;
5222c21d 2849 if (dst->base == NULL) return NULL;
81582353
A
2850
2851 /*
2852 * MODULE_FLAG_EXTERNAL means some process other than syslogd writes the file.
f3df4c03 2853 * We check for its existence, and that it is non-zero in size.
81582353
A
2854 */
2855 if (dst->flags & MODULE_FLAG_EXTERNAL)
2856 {
2857 struct stat sb;
2858
2859 memset(&sb, 0, sizeof(struct stat));
2860
2861 if (stat(dst->path, &sb) == 0)
2862 {
f3df4c03 2863 if (S_ISREG(sb.st_mode) && (sb.st_size != 0))
81582353 2864 {
5222c21d 2865 asl_out_file_list_t *out = (asl_out_file_list_t *)calloc(1, sizeof(asl_out_file_list_t));
81582353
A
2866 if (out != NULL)
2867 {
2868 char *p = strrchr(dst->path, '/');
2869 if (p == NULL) p = dst->path;
2870 else p++;
2871 out->name = strdup(p);
2872 out->ftime = sb.st_birthtimespec.tv_sec;
2873 if (out->ftime == 0) out->ftime = sb.st_mtimespec.tv_sec;
2874 return out;
2875 }
2876 }
2877 }
2878
2879 return NULL;
2880 }
2881
5222c21d 2882 if ((dst->rotate_dir == NULL) && (dst->flags & MODULE_FLAG_BASESTAMP) && ((dst->flags & MODULE_FLAG_COMPRESS) == 0))
81582353
A
2883 {
2884 /* files do not move to a dest dir, get renamed, or get compressed - nothing to do */
2885 return NULL;
2886 }
2887
5222c21d
A
2888 /*
2889 * MODULE_NAME_STYLE_STAMP_SEC format is used for sequenced (MODULE_NAME_STYLE_STAMP_SEQ) files.
2890 * aslmanager converts the file names.
2891 */
2892 flags = dst->style_flags;
2893 if (flags & MODULE_NAME_STYLE_STAMP_SEQ)
2894 {
2895 flags &= ~MODULE_NAME_STYLE_STAMP_SEQ;
2896 flags |= MODULE_NAME_STYLE_STAMP_SEC;
2897 }
81582353 2898
5222c21d 2899 return asl_list_log_files(dst->dir, dst->base, dst->ext, flags, true);
81582353
A
2900}
2901
2902/*
2903 * List the destination files for an output asl_out_dst_data_t
2904 */
2905asl_out_file_list_t *
2906asl_list_dst_files(asl_out_dst_data_t *dst)
2907{
5222c21d
A
2908 char *dst_dir;
2909#ifdef DEBUG_LIST_FILES
2910 fprintf(stderr, "asl_list_dst_files\n");
2911#endif
81582353
A
2912
2913 if (dst == NULL) return NULL;
2914 if (dst->path == NULL) return NULL;
5222c21d 2915 if (dst->base == NULL) return NULL;
81582353
A
2916
2917 dst_dir = dst->rotate_dir;
5222c21d 2918 if (dst_dir == NULL) dst_dir = dst->dir;
81582353 2919
5222c21d 2920 return asl_list_log_files(dst_dir, dst->base, dst->ext, dst->style_flags, false);
81582353 2921}
f3df4c03
A
2922
2923static int
2924asl_secure_open_dir(const char *path)
2925{
2926 int fd, i;
2927 char **path_parts;
2928
2929 if (path == NULL) return -1;
2930 if (path[0] != '/') return -1;
2931
2932 path_parts = explode(path + 1, "/");
2933 if (path_parts == NULL) return 0;
2934
2935 fd = open("/", O_RDONLY | O_NOFOLLOW, 0);
2936 if (fd < 0)
2937 {
2938 free_string_list(path_parts);
2939 return -1;
2940 }
2941
2942 for (i = 0; path_parts[i] != NULL; i++)
2943 {
2944 int fd_next, status;
2945 struct stat sb;
2946
2947 fd_next = openat(fd, path_parts[i], O_RDONLY | O_NOFOLLOW, 0);
2948 close(fd);
2949 fd = fd_next;
2950 if (fd < 0)
2951 {
2952 free_string_list(path_parts);
2953 return -1;
2954 }
2955
2956 memset(&sb, 0, sizeof(sb));
2957
2958 status = fstat(fd, &sb);
2959 if (status < 0)
2960 {
2961 free_string_list(path_parts);
2962 return -1;
2963 }
2964
2965 if (!S_ISDIR(sb.st_mode))
2966 {
2967 free_string_list(path_parts);
2968 return -1;
2969 }
2970 }
2971
2972 free_string_list(path_parts);
2973 return fd;
2974}
2975
2976int
2977asl_secure_chown_chmod_dir(const char *path, uid_t uid, gid_t gid, mode_t mode)
2978{
2979 int fd, status;
5222c21d 2980
f3df4c03
A
2981 fd = asl_secure_open_dir(path);
2982 if (fd < 0) return fd;
2983
2984 status = fchown(fd, uid, gid);
2985 if (status < 0)
2986 {
2987 close(fd);
2988 return -1;
2989 }
2990
2991 if (mode >= 0) status = fchmod(fd, mode);
2992 close(fd);
2993
2994 if (status < 0) return -1;
2995 return 0;
2996}