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