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