]> git.saurik.com Git - apple/syslog.git/blob - aslcommon/asl_common.c
syslog-385.tar.gz
[apple/syslog.git] / aslcommon / asl_common.c
1 /*
2 * Copyright (c) 2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <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>
40 #include <os/variant_private.h>
41 #include <asl.h>
42 #include <asl_core.h>
43 #include <asl_msg.h>
44 #include "asl_common.h"
45 #include <pwd.h>
46
47 #define _PATH_ASL_CONF "/etc/asl.conf"
48 #define _PATH_ASL_CONF_DIR "/etc/asl"
49
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
56 #if !TARGET_OS_SIMULATOR
57 #define _PATH_ASL_CONF_LOCAL_DIR "/usr/local/etc/asl"
58 #endif
59
60 //#define DEBUG_LIST_FILES 1
61
62 static 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 ",
73 "set ",
74 "unset ",
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
86 #define forever for(;;)
87 #define KEYMATCH(S,K) ((strncasecmp(S, K, strlen(K)) == 0))
88
89 asl_msg_t *
90 xpc_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
184 asl_msg_t *
185 configuration_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 */
194 static 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
225 static char **
226 _insert_string(char *s, char **l, uint32_t x)
227 {
228 int i, len;
229
230 if (s == NULL) return l;
231 if (l == NULL)
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
249 /* len includes the NULL at the end of the list */
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
275 char **
276 explode(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
324 void
325 free_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
334 char *
335 get_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
354 char *
355 next_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
443 asl_out_dst_data_t *
444 asl_out_dest_for_path(asl_out_module_t *m, const char *path)
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 {
454 if ((r->action == ACTION_OUT_DEST) && (r->dst != NULL) && (r->dst->path != NULL) && (streq(r->dst->path, path))) return r->dst;
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 */
471 static 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
538 dst = asl_out_dest_for_path(mlist, tmp);
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
558 #if !TARGET_OS_SIMULATOR
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
578 int
579 asl_out_mkpath(asl_out_module_t *mlist, asl_out_rule_t *r)
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 {
602 if (S_ISDIR(sb.st_mode)) return 0;
603 return -1;
604 }
605
606 if (errno == ENOENT)
607 {
608 uint32_t dirflag = r->dst->flags & MODULE_FLAG_NONSTD_DIR;
609 status = _asl_common_make_dir_path(mlist, dirflag, tmp);
610 return status;
611 }
612
613 return -1;
614 }
615
616 int
617 asl_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
680 void
681 asl_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
688 if (flags & MODULE_NAME_STYLE_STAMP_UTC)
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 }
694 else if (flags & MODULE_NAME_STYLE_STAMP_UTC_B)
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 }
700 else if (flags & MODULE_NAME_STYLE_STAMP_LCL)
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 }
718 else if (flags & MODULE_NAME_STYLE_STAMP_LCL_B)
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 {
738 snprintf(buf, len, "%c%llu", STYLE_SEC_PREFIX_CHAR, (unsigned long long)stamp);
739 }
740 }
741
742 void
743 asl_dst_make_current_name(asl_out_dst_data_t *dst, uint32_t xflags, char *buf, size_t len)
744 {
745 char tstamp[32];
746
747 if (dst == NULL) return;
748 if (buf == NULL) return;
749
750 xflags |= dst->flags;
751
752 if (dst->timestamp == 0) dst->timestamp = time(NULL);
753 asl_make_timestamp(dst->timestamp, dst->style_flags, tstamp, sizeof(tstamp));
754
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 }
769 }
770 else
771 {
772 snprintf(buf, len, "%s", dst->path);
773 }
774 }
775
776 int
777 asl_check_option(asl_msg_t *msg, const char *opt)
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
788 p = asl_msg_get_val_for_key(msg, ASL_KEY_OPTION);
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
808 void
809 asl_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
816 free(dst->dir);
817 free(dst->path);
818 free(dst->current_name);
819 free(dst->base);
820 free(dst->ext);
821 free(dst->rotate_dir);
822 free(dst->fmt);
823 #if !TARGET_OS_SIMULATOR
824 free(dst->uid);
825 free(dst->gid);
826 #endif
827 free(dst);
828 }
829
830 asl_out_dst_data_t *
831 asl_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 */
839 int
840 asl_out_dst_set_access(int fd, asl_out_dst_data_t *dst)
841 {
842 #if !TARGET_OS_SIMULATOR
843 uid_t fuid = 0;
844 gid_t fgid = 80;
845 #if !TARGET_OS_IPHONE
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
858 #if TARGET_OS_SIMULATOR
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
867 #if TARGET_OS_IPHONE
868 return fd;
869 #else
870 if (os_variant_is_basesystem("com.apple.syslog")) {
871 return fd;
872 }
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 */
883 if ((dst->gid[i] == fgid) && (dst->mode & 00040)) continue;
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 */
916 if ((dst->uid[i] == fuid) && (dst->mode & 00400)) continue;
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
948 asl_file_create_return:
949
950 acl_free(acl);
951 return fd;
952 #endif /* !TARGET_OS_IPHONE */
953 #endif /* !TARGET_OS_SIMULATOR */
954 }
955
956 /* create a file with acls */
957 int
958 asl_out_dst_file_create_open(asl_out_dst_data_t *dst, char **pathp)
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
967 asl_dst_make_current_name(dst, 0, outpath, sizeof(outpath));
968 free(dst->current_name);
969
970 dst->current_name = strdup(outpath);
971 if (dst->current_name == NULL) return -1;
972
973 if (pathp != NULL) *pathp = strdup(outpath);
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
985 if (dst->timestamp == 0) dst->timestamp = sb.st_birthtimespec.tv_sec;
986 if (dst->timestamp == 0) dst->timestamp = sb.st_mtimespec.tv_sec;
987 dst->size = sb.st_size;
988
989 if ((dst->flags & MODULE_FLAG_BASESTAMP) && (dst->flags & MODULE_FLAG_SYMLINK)) symlink(outpath, dst->path);
990 return fd;
991 }
992 else if (errno != ENOENT)
993 {
994 /* stat error other than non-existant file */
995 return -1;
996 }
997
998 fd = open(outpath, O_RDWR | O_CREAT | O_EXCL, (dst->mode & 00666));
999 if (fd < 0) return -1;
1000
1001 dst->timestamp = time(NULL);
1002
1003 fd = asl_out_dst_set_access(fd, dst);
1004 if (fd < 0) unlink(outpath);
1005
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
1013 return fd;
1014 }
1015
1016 void
1017 asl_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
1047 asl_out_module_t *
1048 asl_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 */
1068 static 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 */
1128 static 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] */
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;
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
1211 #if !TARGET_OS_SIMULATOR
1212 static 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
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
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
1249 static 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
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
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 }
1285 #endif /* !TARGET_OS_SIMULATOR */
1286
1287 static 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
1315 static 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
1328 static 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 */
1370 static 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
1451 static 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;
1456 char *p, *dot, *opts, *path;
1457 char **path_parts;
1458 int has_dotdot, recursion_limit;
1459 uint32_t i, flags = 0;
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 {
1487 if (streq_len(path_parts[i], "$ENV(", 5))
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
1513 if ((has_dotdot == 0) && streq(path_parts[i], "..")) has_dotdot = 1;
1514 }
1515
1516 free_string_list(path_parts);
1517 path_parts = NULL;
1518
1519 if ((did_sub == 1) && (has_dotdot == 0))
1520 {
1521 /* substitution might have added a ".." so check the new path */
1522 free(path);
1523 path = asl_string_release_return_bytes(processed_path);
1524 processed_path = asl_string_new(ASL_ENCODE_NONE);
1525 path_parts = explode(path, "/");
1526 recursion_limit--;
1527 }
1528 }
1529
1530 free(path);
1531 free_string_list(path_parts);
1532 path_parts = NULL;
1533
1534 if ((has_dotdot != 0) || (recursion_limit == 0))
1535 {
1536 asl_string_release(processed_path);
1537 return NULL;
1538 }
1539
1540 path = asl_string_release_return_bytes(processed_path);
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
1557 flags |= MODULE_FLAG_NONSTD_DIR;
1558
1559 if (path[0] != '/')
1560 {
1561 char *t = path;
1562 const char *log_root = "/var/log";
1563
1564 #if TARGET_OS_SIMULATOR
1565 log_root = getenv("SIMULATOR_LOG_ROOT");
1566 if (log_root == NULL) log_root = "/tmp/log";
1567 #endif
1568
1569 if (streq(m->name, ASL_MODULE_NAME))
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 }
1577
1578 free(t);
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 */
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;
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;
1603
1604 p = strrchr(dst->path, '/');
1605 if (p != NULL)
1606 {
1607 *p = '\0';
1608 dst->dir = strdup(dst->path);
1609 *p = '/';
1610 }
1611
1612 dst->mode = def_mode;
1613 dst->ttl[LEVEL_ALL] = DEFAULT_TTL;
1614 dst->flags = flags | MODULE_FLAG_COALESCE;
1615
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
1634 while (NULL != (p = next_word_from_string(&opts)))
1635 {
1636 if (KEYMATCH(p, "mode=")) dst->mode = strtol(p+5, NULL, 0);
1637 #if !TARGET_OS_SIMULATOR
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;
1653 else if (KEYMATCH(p, "truncate")) dst->flags |= MODULE_FLAG_TRUNCATE;
1654 else if (KEYMATCH(p, "dir")) dst->flags |= MODULE_FLAG_TYPE_ASL_DIR;
1655 else if (KEYMATCH(p, "soft")) dst->flags |= MODULE_FLAG_SOFT_WRITE;
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);
1658 else if (KEYMATCH(p, "style=") || KEYMATCH(p, "rotate="))
1659 {
1660 const char *x = p + 6;
1661
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;
1669
1670 dst->flags |= MODULE_FLAG_ROTATE;
1671 }
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 }
1684 else if (KEYMATCH(p, "link") || KEYMATCH(p, "symlink"))
1685 {
1686 dst->flags |= MODULE_FLAG_SYMLINK;
1687 }
1688 else if (KEYMATCH(p, "ttl"))
1689 {
1690 char *q = p + 3;
1691 if (*q == '=')
1692 {
1693 dst->ttl[LEVEL_ALL] = asl_core_str_to_time(p+4, SECONDS_PER_DAY);
1694 }
1695 else if ((*q >= '0') && (*q <= '7') && (*(q+1) == '='))
1696 {
1697 uint32_t x = *q - '0';
1698 dst->ttl[x] = asl_core_str_to_time(p+5, SECONDS_PER_DAY);
1699 }
1700 }
1701 else if (KEYMATCH(p, "size_only"))
1702 {
1703 dst->flags |= MODULE_FLAG_SIZE_ONLY;
1704 }
1705
1706 free(p);
1707 p = NULL;
1708 }
1709
1710 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
1711 /* check for crashreporter files */
1712 if ((KEYMATCH(dst->path, _PATH_CRASHREPORTER)) || (KEYMATCH(dst->path, _PATH_CRASHREPORTER_MOBILE_1)) || (KEYMATCH(dst->path, _PATH_CRASHREPORTER_MOBILE_2)))
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
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
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 */
1731 if (streq(dst->fmt, "std") || streq(dst->fmt, "bsd") || streq(dst->fmt, "msg")) dst->flags |= MODULE_FLAG_STD_BSD_MSG;
1732
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))
1735 {
1736 dst->flags &= ~MODULE_NAME_STYLE_STAMP_SEQ;
1737 dst->flags |= MODULE_NAME_STYLE_STAMP_SEC;
1738 }
1739
1740 /* set time format for raw output */
1741 if (streq(dst->fmt, "raw")) dst->tfmt = "sec";
1742
1743 /* check for ASL_PLACE_DATABASE_DEFAULT */
1744 if (streq(dst->path, ASL_PLACE_DATABASE_DEFAULT))
1745 {
1746 dst->flags = MODULE_FLAG_TYPE_ASL_DIR;
1747 }
1748
1749 /* set file_max to all_max if it is zero */
1750 if (dst->file_max == 0) dst->file_max = dst->all_max;
1751
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
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
1774 static 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
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))
1812 {
1813 /* actions only allowed in com.apple.asl */
1814 if (strcaseeq(act, "broadcast")) out->action = ACTION_BROADCAST;
1815 else if (strcaseeq(act, "forward")) out->action = ACTION_FORWARD;
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 */
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));
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));
1865 else if (out->options != NULL) out->action = ACTION_ASL_FILE;
1866 }
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
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
1886 if ((out->action == ACTION_FILE) && (out->dst != NULL) && (out->dst->fmt != NULL) && (strcaseeq(out->dst->fmt, "asl")))
1887 {
1888 out->action = ACTION_ASL_FILE;
1889 }
1890
1891 if ((out->action == ACTION_ASL_FILE) && (out->dst != NULL))
1892 {
1893 out->dst->flags |= MODULE_FLAG_TYPE_ASL;
1894 }
1895
1896 if (out->action == ACTION_ASL_DIR)
1897 {
1898 /* coalesce is meaningless for ASL directories */
1899 out->dst->flags &= ~MODULE_FLAG_COALESCE;
1900
1901 out->dst->flags |= MODULE_FLAG_TYPE_ASL_DIR;
1902
1903 /* set style bits for basestamp asl_dirs */
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;
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
1913 #if !TARGET_OS_SIMULATOR
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
1929 asl_out_rule_t *
1930 asl_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 }
1942 else if (*s == '>')
1943 {
1944 _asl_out_module_parse_dst(m, s + 1, 010000);
1945 }
1946
1947 return NULL;
1948 }
1949
1950 asl_out_module_t *
1951 asl_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
1971 static 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 {
1981 if ((x->name != NULL) && (streq(x->name, name))) return x;
1982 }
1983
1984 return NULL;
1985 }
1986
1987 static 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 {
2009 if (ent->d_name[0] != '.')
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
2026 if (streq(ent->d_name, ASL_MODULE_NAME))
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
2052 asl_out_module_t *
2053 asl_out_module_init(void)
2054 {
2055 asl_out_module_t *out = NULL;
2056
2057 #if TARGET_OS_SIMULATOR
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");
2062 if (sim_root_path == NULL) return NULL;
2063
2064 sim_resources_path = getenv("IPHONE_SHARED_RESOURCES_DIRECTORY");
2065 if (sim_resources_path == NULL) return NULL;
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 */
2084 #if TARGET_OS_SIMULATOR
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 */
2108 char *
2109 asl_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],
2124 (r->query == NULL) ? "" : " ",
2125 (r->query == NULL) ? "" : str,
2126 (r->options == NULL) ? "" : " ",
2127 (r->options == NULL) ? "" : r->options);
2128
2129 free(str);
2130 return out;
2131 }
2132
2133 static 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
2145 /*
2146 * Print module
2147 */
2148 void
2149 asl_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;
2153 uint32_t i, ttlnset;
2154 char tstr[150];
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 }
2189 if (o->flags & MODULE_FLAG_SOFT_WRITE)
2190 {
2191 fprintf(f, "%csoft", c);
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 }
2209 if (o->flags & MODULE_FLAG_BASESTAMP)
2210 {
2211 fprintf(f, "%cbasestamp", c);
2212 c = ' ';
2213 }
2214 if (o->flags & MODULE_FLAG_SYMLINK)
2215 {
2216 fprintf(f, "%csymlink", c);
2217 c = ' ';
2218 }
2219 if (o->flags & MODULE_FLAG_NONSTD_DIR)
2220 {
2221 fprintf(f, "%cnon-std_dir", c);
2222 c = ' ';
2223 }
2224 if (o->flags & MODULE_FLAG_EXTERNAL)
2225 {
2226 fprintf(f, "%cexternal", c);
2227 c = ' ';
2228 }
2229 if (o->flags & MODULE_FLAG_CRASHLOG)
2230 {
2231 fprintf(f, "%ccrashlog", c);
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 }
2244 if (o->flags & MODULE_FLAG_SIZE_ONLY)
2245 {
2246 fprintf(f, "%csize_only", c);
2247 c = ' ';
2248 }
2249 fprintf(f, ")");
2250 }
2251 fprintf(f, "\n");
2252
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
2277 ttlnset = 0;
2278 for (i = 0; (i <= 7) & (ttlnset == 0); i++) if (o->ttl[i] != 0) ttlnset = 1;
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 }
2292
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);
2296 #if !TARGET_OS_SIMULATOR
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
2313 void
2314 asl_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 /*
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.
2336 * base.gz is not allowed.
2337 * Output parameter stamp must be freed by caller.
2338 */
2339 bool
2340 _check_file_name(const char *name, const char *base, const char *ext, uint32_t flags, bool src, char **stamp)
2341 {
2342 size_t baselen, extlen;
2343 const char *p, *z;
2344 bool isgz = false;
2345
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
2350 if (name == NULL) return false;
2351 if (base == NULL) return false;
2352
2353 baselen = strlen(base);
2354 if (baselen == 0) return false;
2355
2356 extlen = 0;
2357 if (ext != NULL) extlen = strlen(ext);
2358
2359 if (stamp != NULL) *stamp = NULL;
2360
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 }
2368
2369 z = strrchr(name, '.');
2370 if ((z != NULL) && streq(z, ".gz")) isgz = true;
2371
2372 #ifdef DEBUG_LIST_FILES
2373 fprintf(stderr, "z=%s isgz=%s\n", (z == NULL) ? "NULL" : z, isgz ? "true" : "false");
2374 #endif
2375
2376 p = name + baselen;
2377
2378 if (flags & MODULE_NAME_STYLE_FORMAT_BS)
2379 {
2380 #ifdef DEBUG_LIST_FILES
2381 fprintf(stderr, " MODULE_NAME_STYLE_FORMAT_BS\n");
2382 #endif
2383 if (*p == '\0')
2384 {
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;
2390 }
2391
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 }
2400
2401 p++;
2402
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 }
2411
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 }
2419
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)
2426 {
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 }
2447
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
2491 return true;
2492 }
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
2498
2499 if (*p != '.')
2500 {
2501 #ifdef DEBUG_LIST_FILES
2502 fprintf(stderr, " expecting dot - failed [%u]\n", __LINE__);
2503 #endif
2504 return false;
2505 }
2506
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;
2557 }
2558
2559 #ifdef DEBUG_LIST_FILES
2560 fprintf(stderr, " unknown format - failed\n");
2561 #endif
2562
2563 return false;
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 *
2570 * MODULE_NAME_STYLE_STAMP_SEQ means a numeric sequence number is expected, although not required.
2571 * E.g. foo.log foo.log.0
2572 *
2573 * MODULE_NAME_STYLE_STAMP_SEC also means a numeric sequence number is required following an 'T' character.
2574 * The numeric value is the file's timestamp in seconds. E.g foo.log.T1335200452
2575 *
2576 * MODULE_NAME_STYLE_STAMP_UTC requires a date/time component as the file's timestamp.
2577 * E.g. foo.2012-04-06T15:30:00Z
2578 *
2579 * MODULE_NAME_STYLE_STAMP_UTC_B requires a date/time component as the file's timestamp.
2580 * E.g. foo.20120406T153000Z
2581 *
2582 * MODULE_NAME_STYLE_STAMP_LCL requires a date/time component as the file's timestamp.
2583 * E.g. foo.2012-04-06T15:30:00-7
2584 *
2585 * MODULE_NAME_STYLE_STAMP_LCL_B requires a date/time component as the file's timestamp.
2586 * E.g. foo.20120406T153000-07
2587 */
2588 int
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
2602 /* check for MODULE_NAME_STYLE_STAMP_SEC (foo.T12345678) */
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
2612 /* check for MODULE_NAME_STYLE_STAMP_SEQ (foo.0 or foo.2.gz) */
2613 digits = true;
2614 for (i = 0; digits && (stamp[i] != '\0'); i++) digits = (stamp[i] >= '0') && (stamp[i] <= '9');
2615
2616 if (!digits && (streq(stamp + i, ".gz"))) digits = true;
2617
2618 if (digits)
2619 {
2620 n = atoi(stamp);
2621 if (sp != NULL) *sp = (uint32_t)n;
2622 return STAMP_STYLE_SEQ;
2623 }
2624
2625 /* check for MODULE_NAME_STYLE_STAMP_UTC, UTC_B, LCL, or LCL_B */
2626 memset(&t, 0, sizeof(t));
2627 h = m = s = 0;
2628
2629 n = 0;
2630 if ((flags & MODULE_NAME_STYLE_STAMP_UTC) || (flags & MODULE_NAME_STYLE_STAMP_LCL))
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 }
2634 else if ((flags & MODULE_NAME_STYLE_STAMP_UTC_B) || (flags & MODULE_NAME_STYLE_STAMP_LCL_B))
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 {
2640 #ifdef DEBUG_LIST_FILES
2641 fprintf(stderr, "_parse_stamp_style fail %u\n", __LINE__);
2642 #endif
2643 return STAMP_STYLE_INVALID;
2644 }
2645
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 }
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;
2663 if (zone == '+') utc_offset *= -1;
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 {
2679 #ifdef DEBUG_LIST_FILES
2680 fprintf(stderr, "_parse_stamp_style fail %u\n", __LINE__);
2681 #endif
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
2695 return STAMP_STYLE_ISO8601;
2696 }
2697
2698 asl_out_file_list_t *
2699 asl_list_log_files(const char *dir, const char *base, const char *ext, uint32_t flags, bool src)
2700 {
2701 DIR *d;
2702 struct dirent *ent;
2703 char path[MAXPATHLEN];
2704 uint32_t seq;
2705 time_t ftime;
2706 struct stat sb;
2707 int pstyle, fstyle;
2708 asl_out_file_list_t *out, *x, *y;
2709
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
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;
2726 bool stat_ok = false;
2727
2728 check = _check_file_name(ent->d_name, base, ext, flags, src, &stamp);
2729 if (!check) continue;
2730
2731 seq = IndexNull;
2732 ftime = 0;
2733
2734 pstyle = _parse_stamp_style(stamp, flags, &seq, &ftime);
2735 free(stamp);
2736
2737 if (pstyle == STAMP_STYLE_INVALID) continue;
2738
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
2748 fstyle = STAMP_STYLE_NULL;
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
2757
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 */
2764
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;
2769
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
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);
2786 x->stamp = pstyle;
2787 x->ftime = ftime;
2788 x->seq = seq;
2789
2790 if (stat_ok)
2791 {
2792 x->size = sb.st_size;
2793 if (pstyle == STAMP_STYLE_SEQ)
2794 {
2795 x->ftime = sb.st_birthtimespec.tv_sec;
2796 if (x->ftime == 0) x->ftime = sb.st_mtimespec.tv_sec;
2797 }
2798 }
2799
2800 if (pstyle == STAMP_STYLE_SEQ)
2801 {
2802 #ifdef DEBUG_LIST_FILES
2803 fprintf(stderr, "asl_list_log_files SEQ %s %u %ld\n", path, x->seq, x->ftime);
2804 #endif
2805 if (out == NULL)
2806 {
2807 out = x;
2808 }
2809 else if ((x->seq == IndexNull) || ((x->seq > out->seq) && (out->seq != IndexNull)))
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 }
2825 else if ((x->seq > y->next->seq) && (y->next->seq != IndexNull))
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 {
2838 #ifdef DEBUG_LIST_FILES
2839 fprintf(stderr, "asl_list_log_files TIME %s %ld\n", path, x->ftime);
2840 #endif
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 */
2881 asl_out_file_list_t *
2882 asl_list_src_files(asl_out_dst_data_t *dst)
2883 {
2884 uint32_t flags;
2885 #ifdef DEBUG_LIST_FILES
2886 fprintf(stderr, "asl_list_src_files\n");
2887 #endif
2888
2889 if (dst == NULL) return NULL;
2890 if (dst->path == NULL) return NULL;
2891 if (dst->base == NULL) return NULL;
2892
2893 /*
2894 * MODULE_FLAG_EXTERNAL means some process other than syslogd writes the file.
2895 * We check for its existence, and that it is non-zero in size.
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 {
2905 if (S_ISREG(sb.st_mode) && (sb.st_size != 0))
2906 {
2907 asl_out_file_list_t *out = (asl_out_file_list_t *)calloc(1, sizeof(asl_out_file_list_t));
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
2924 if ((dst->rotate_dir == NULL) && (dst->flags & MODULE_FLAG_BASESTAMP) && ((dst->flags & MODULE_FLAG_COMPRESS) == 0))
2925 {
2926 /* files do not move to a dest dir, get renamed, or get compressed - nothing to do */
2927 return NULL;
2928 }
2929
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 }
2940
2941 return asl_list_log_files(dst->dir, dst->base, dst->ext, flags, true);
2942 }
2943
2944 /*
2945 * List the destination files for an output asl_out_dst_data_t
2946 */
2947 asl_out_file_list_t *
2948 asl_list_dst_files(asl_out_dst_data_t *dst)
2949 {
2950 char *dst_dir;
2951 #ifdef DEBUG_LIST_FILES
2952 fprintf(stderr, "asl_list_dst_files\n");
2953 #endif
2954
2955 if (dst == NULL) return NULL;
2956 if (dst->path == NULL) return NULL;
2957 if (dst->base == NULL) return NULL;
2958
2959 dst_dir = dst->rotate_dir;
2960 if (dst_dir == NULL) dst_dir = dst->dir;
2961
2962 return asl_list_log_files(dst_dir, dst->base, dst->ext, dst->style_flags, false);
2963 }
2964
2965 static int
2966 asl_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
3018 int
3019 asl_secure_chown_chmod_dir(const char *path, uid_t uid, gid_t gid, mode_t mode)
3020 {
3021 int fd, status;
3022
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 }