]> git.saurik.com Git - apple/syslog.git/blob - aslcommon/asl_common.c
syslog-377.40.1.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 <asl.h>
41 #include <asl_core.h>
42 #include <asl_msg.h>
43 #include "asl_common.h"
44 #include <pwd.h>
45
46 #define _PATH_ASL_CONF "/etc/asl.conf"
47 #define _PATH_ASL_CONF_DIR "/etc/asl"
48
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
55 #if !TARGET_OS_SIMULATOR
56 #define _PATH_ASL_CONF_LOCAL_DIR "/usr/local/etc/asl"
57 #endif
58
59 //#define DEBUG_LIST_FILES 1
60
61 static 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 ",
72 "set ",
73 "unset ",
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
85 #define forever for(;;)
86 #define KEYMATCH(S,K) ((strncasecmp(S, K, strlen(K)) == 0))
87
88 asl_msg_t *
89 xpc_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
183 asl_msg_t *
184 configuration_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 */
193 static 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
224 static char **
225 _insert_string(char *s, char **l, uint32_t x)
226 {
227 int i, len;
228
229 if (s == NULL) return l;
230 if (l == NULL)
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
248 /* len includes the NULL at the end of the list */
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
274 char **
275 explode(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
323 void
324 free_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
333 char *
334 get_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
353 char *
354 next_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
442 asl_out_dst_data_t *
443 asl_out_dest_for_path(asl_out_module_t *m, const char *path)
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 {
453 if ((r->action == ACTION_OUT_DEST) && (r->dst != NULL) && (r->dst->path != NULL) && (streq(r->dst->path, path))) return r->dst;
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 */
470 static 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
537 dst = asl_out_dest_for_path(mlist, tmp);
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
557 #if !TARGET_OS_SIMULATOR
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
577 int
578 asl_out_mkpath(asl_out_module_t *mlist, asl_out_rule_t *r)
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 {
601 if (S_ISDIR(sb.st_mode)) return 0;
602 return -1;
603 }
604
605 if (errno == ENOENT)
606 {
607 uint32_t dirflag = r->dst->flags & MODULE_FLAG_NONSTD_DIR;
608 status = _asl_common_make_dir_path(mlist, dirflag, tmp);
609 return status;
610 }
611
612 return -1;
613 }
614
615 int
616 asl_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
679 void
680 asl_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
687 if (flags & MODULE_NAME_STYLE_STAMP_UTC)
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 }
693 else if (flags & MODULE_NAME_STYLE_STAMP_UTC_B)
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 }
699 else if (flags & MODULE_NAME_STYLE_STAMP_LCL)
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 }
717 else if (flags & MODULE_NAME_STYLE_STAMP_LCL_B)
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 {
737 snprintf(buf, len, "%c%llu", STYLE_SEC_PREFIX_CHAR, (unsigned long long)stamp);
738 }
739 }
740
741 void
742 asl_dst_make_current_name(asl_out_dst_data_t *dst, uint32_t xflags, char *buf, size_t len)
743 {
744 char tstamp[32];
745
746 if (dst == NULL) return;
747 if (buf == NULL) return;
748
749 xflags |= dst->flags;
750
751 if (dst->timestamp == 0) dst->timestamp = time(NULL);
752 asl_make_timestamp(dst->timestamp, dst->style_flags, tstamp, sizeof(tstamp));
753
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 }
768 }
769 else
770 {
771 snprintf(buf, len, "%s", dst->path);
772 }
773 }
774
775 int
776 asl_check_option(asl_msg_t *msg, const char *opt)
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
787 p = asl_msg_get_val_for_key(msg, ASL_KEY_OPTION);
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
807 void
808 asl_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
815 free(dst->dir);
816 free(dst->path);
817 free(dst->current_name);
818 free(dst->base);
819 free(dst->ext);
820 free(dst->rotate_dir);
821 free(dst->fmt);
822 #if !TARGET_OS_SIMULATOR
823 free(dst->uid);
824 free(dst->gid);
825 #endif
826 free(dst);
827 }
828
829 asl_out_dst_data_t *
830 asl_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 */
838 int
839 asl_out_dst_set_access(int fd, asl_out_dst_data_t *dst)
840 {
841 #if !TARGET_OS_SIMULATOR
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
857 #if TARGET_OS_SIMULATOR
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 */
879 if ((dst->gid[i] == fgid) && (dst->mode & 00040)) continue;
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 */
912 if ((dst->uid[i] == fuid) && (dst->mode & 00400)) continue;
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
944 asl_file_create_return:
945
946 acl_free(acl);
947 return fd;
948 #endif /* !TARGET_OS_EMBEDDED */
949 #endif /* !TARGET_OS_SIMULATOR */
950 }
951
952 /* create a file with acls */
953 int
954 asl_out_dst_file_create_open(asl_out_dst_data_t *dst, char **pathp)
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
963 asl_dst_make_current_name(dst, 0, outpath, sizeof(outpath));
964 free(dst->current_name);
965
966 dst->current_name = strdup(outpath);
967 if (dst->current_name == NULL) return -1;
968
969 if (pathp != NULL) *pathp = strdup(outpath);
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
981 if (dst->timestamp == 0) dst->timestamp = sb.st_birthtimespec.tv_sec;
982 if (dst->timestamp == 0) dst->timestamp = sb.st_mtimespec.tv_sec;
983 dst->size = sb.st_size;
984
985 if ((dst->flags & MODULE_FLAG_BASESTAMP) && (dst->flags & MODULE_FLAG_SYMLINK)) symlink(outpath, dst->path);
986 return fd;
987 }
988 else if (errno != ENOENT)
989 {
990 /* stat error other than non-existant file */
991 return -1;
992 }
993
994 fd = open(outpath, O_RDWR | O_CREAT | O_EXCL, (dst->mode & 00666));
995 if (fd < 0) return -1;
996
997 dst->timestamp = time(NULL);
998
999 fd = asl_out_dst_set_access(fd, dst);
1000 if (fd < 0) unlink(outpath);
1001
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
1009 return fd;
1010 }
1011
1012 void
1013 asl_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
1043 asl_out_module_t *
1044 asl_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 */
1064 static 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 */
1124 static 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] */
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;
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
1207 #if !TARGET_OS_SIMULATOR
1208 static 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
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
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
1245 static 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
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
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 }
1281 #endif /* !TARGET_OS_SIMULATOR */
1282
1283 static 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
1311 static 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
1324 static 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 */
1366 static 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
1447 static 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;
1452 char *p, *dot, *opts, *path;
1453 char **path_parts;
1454 int has_dotdot, recursion_limit;
1455 uint32_t i, flags = 0;
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 {
1483 if (streq_len(path_parts[i], "$ENV(", 5))
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
1509 if ((has_dotdot == 0) && streq(path_parts[i], "..")) has_dotdot = 1;
1510 }
1511
1512 free_string_list(path_parts);
1513 path_parts = NULL;
1514
1515 if ((did_sub == 1) && (has_dotdot == 0))
1516 {
1517 /* substitution might have added a ".." so check the new path */
1518 free(path);
1519 path = asl_string_release_return_bytes(processed_path);
1520 processed_path = asl_string_new(ASL_ENCODE_NONE);
1521 path_parts = explode(path, "/");
1522 recursion_limit--;
1523 }
1524 }
1525
1526 free(path);
1527 free_string_list(path_parts);
1528 path_parts = NULL;
1529
1530 if ((has_dotdot != 0) || (recursion_limit == 0))
1531 {
1532 asl_string_release(processed_path);
1533 return NULL;
1534 }
1535
1536 path = asl_string_release_return_bytes(processed_path);
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
1553 flags |= MODULE_FLAG_NONSTD_DIR;
1554
1555 if (path[0] != '/')
1556 {
1557 char *t = path;
1558 const char *log_root = "/var/log";
1559
1560 #if TARGET_OS_SIMULATOR
1561 log_root = getenv("SIMULATOR_LOG_ROOT");
1562 if (log_root == NULL) log_root = "/tmp/log";
1563 #endif
1564
1565 if (streq(m->name, ASL_MODULE_NAME))
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 }
1573
1574 free(t);
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 */
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;
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;
1599
1600 p = strrchr(dst->path, '/');
1601 if (p != NULL)
1602 {
1603 *p = '\0';
1604 dst->dir = strdup(dst->path);
1605 *p = '/';
1606 }
1607
1608 dst->mode = def_mode;
1609 dst->ttl[LEVEL_ALL] = DEFAULT_TTL;
1610 dst->flags = flags | MODULE_FLAG_COALESCE;
1611
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
1630 while (NULL != (p = next_word_from_string(&opts)))
1631 {
1632 if (KEYMATCH(p, "mode=")) dst->mode = strtol(p+5, NULL, 0);
1633 #if !TARGET_OS_SIMULATOR
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;
1649 else if (KEYMATCH(p, "truncate")) dst->flags |= MODULE_FLAG_TRUNCATE;
1650 else if (KEYMATCH(p, "dir")) dst->flags |= MODULE_FLAG_TYPE_ASL_DIR;
1651 else if (KEYMATCH(p, "soft")) dst->flags |= MODULE_FLAG_SOFT_WRITE;
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);
1654 else if (KEYMATCH(p, "style=") || KEYMATCH(p, "rotate="))
1655 {
1656 const char *x = p + 6;
1657
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;
1665
1666 dst->flags |= MODULE_FLAG_ROTATE;
1667 }
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 }
1680 else if (KEYMATCH(p, "link") || KEYMATCH(p, "symlink"))
1681 {
1682 dst->flags |= MODULE_FLAG_SYMLINK;
1683 }
1684 else if (KEYMATCH(p, "ttl"))
1685 {
1686 char *q = p + 3;
1687 if (*q == '=')
1688 {
1689 dst->ttl[LEVEL_ALL] = asl_core_str_to_time(p+4, SECONDS_PER_DAY);
1690 }
1691 else if ((*q >= '0') && (*q <= '7') && (*(q+1) == '='))
1692 {
1693 uint32_t x = *q - '0';
1694 dst->ttl[x] = asl_core_str_to_time(p+5, SECONDS_PER_DAY);
1695 }
1696 }
1697 else if (KEYMATCH(p, "size_only"))
1698 {
1699 dst->flags |= MODULE_FLAG_SIZE_ONLY;
1700 }
1701
1702 free(p);
1703 p = NULL;
1704 }
1705
1706 #if TARGET_OS_EMBEDDED
1707 /* check for crashreporter files */
1708 if ((KEYMATCH(dst->path, _PATH_CRASHREPORTER)) || (KEYMATCH(dst->path, _PATH_CRASHREPORTER_MOBILE_1)) || (KEYMATCH(dst->path, _PATH_CRASHREPORTER_MOBILE_2)))
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
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
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 */
1727 if (streq(dst->fmt, "std") || streq(dst->fmt, "bsd") || streq(dst->fmt, "msg")) dst->flags |= MODULE_FLAG_STD_BSD_MSG;
1728
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))
1731 {
1732 dst->flags &= ~MODULE_NAME_STYLE_STAMP_SEQ;
1733 dst->flags |= MODULE_NAME_STYLE_STAMP_SEC;
1734 }
1735
1736 /* set time format for raw output */
1737 if (streq(dst->fmt, "raw")) dst->tfmt = "sec";
1738
1739 /* check for ASL_PLACE_DATABASE_DEFAULT */
1740 if (streq(dst->path, ASL_PLACE_DATABASE_DEFAULT))
1741 {
1742 dst->flags = MODULE_FLAG_TYPE_ASL_DIR;
1743 }
1744
1745 /* set file_max to all_max if it is zero */
1746 if (dst->file_max == 0) dst->file_max = dst->all_max;
1747
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
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
1770 static 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
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))
1808 {
1809 /* actions only allowed in com.apple.asl */
1810 if (strcaseeq(act, "broadcast")) out->action = ACTION_BROADCAST;
1811 else if (strcaseeq(act, "forward")) out->action = ACTION_FORWARD;
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 */
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));
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));
1861 else if (out->options != NULL) out->action = ACTION_ASL_FILE;
1862 }
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
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
1882 if ((out->action == ACTION_FILE) && (out->dst != NULL) && (out->dst->fmt != NULL) && (strcaseeq(out->dst->fmt, "asl")))
1883 {
1884 out->action = ACTION_ASL_FILE;
1885 }
1886
1887 if ((out->action == ACTION_ASL_FILE) && (out->dst != NULL))
1888 {
1889 out->dst->flags |= MODULE_FLAG_TYPE_ASL;
1890 }
1891
1892 if (out->action == ACTION_ASL_DIR)
1893 {
1894 /* coalesce is meaningless for ASL directories */
1895 out->dst->flags &= ~MODULE_FLAG_COALESCE;
1896
1897 out->dst->flags |= MODULE_FLAG_TYPE_ASL_DIR;
1898
1899 /* set style bits for basestamp asl_dirs */
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;
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
1909 #if !TARGET_OS_SIMULATOR
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
1925 asl_out_rule_t *
1926 asl_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 }
1938 else if (*s == '>')
1939 {
1940 _asl_out_module_parse_dst(m, s + 1, 010000);
1941 }
1942
1943 return NULL;
1944 }
1945
1946 asl_out_module_t *
1947 asl_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
1967 static 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 {
1977 if ((x->name != NULL) && (streq(x->name, name))) return x;
1978 }
1979
1980 return NULL;
1981 }
1982
1983 static 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 {
2005 if (ent->d_name[0] != '.')
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
2022 if (streq(ent->d_name, ASL_MODULE_NAME))
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
2048 asl_out_module_t *
2049 asl_out_module_init(void)
2050 {
2051 asl_out_module_t *out = NULL;
2052
2053 #if TARGET_OS_SIMULATOR
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");
2058 if (sim_root_path == NULL) return NULL;
2059
2060 sim_resources_path = getenv("IPHONE_SHARED_RESOURCES_DIRECTORY");
2061 if (sim_resources_path == NULL) return NULL;
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 */
2080 #if TARGET_OS_SIMULATOR
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 */
2104 char *
2105 asl_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],
2120 (r->query == NULL) ? "" : " ",
2121 (r->query == NULL) ? "" : str,
2122 (r->options == NULL) ? "" : " ",
2123 (r->options == NULL) ? "" : r->options);
2124
2125 free(str);
2126 return out;
2127 }
2128
2129 static 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
2141 /*
2142 * Print module
2143 */
2144 void
2145 asl_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;
2149 uint32_t i, ttlnset;
2150 char tstr[150];
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 }
2185 if (o->flags & MODULE_FLAG_SOFT_WRITE)
2186 {
2187 fprintf(f, "%csoft", c);
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 }
2205 if (o->flags & MODULE_FLAG_BASESTAMP)
2206 {
2207 fprintf(f, "%cbasestamp", c);
2208 c = ' ';
2209 }
2210 if (o->flags & MODULE_FLAG_SYMLINK)
2211 {
2212 fprintf(f, "%csymlink", c);
2213 c = ' ';
2214 }
2215 if (o->flags & MODULE_FLAG_NONSTD_DIR)
2216 {
2217 fprintf(f, "%cnon-std_dir", c);
2218 c = ' ';
2219 }
2220 if (o->flags & MODULE_FLAG_EXTERNAL)
2221 {
2222 fprintf(f, "%cexternal", c);
2223 c = ' ';
2224 }
2225 if (o->flags & MODULE_FLAG_CRASHLOG)
2226 {
2227 fprintf(f, "%ccrashlog", c);
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 }
2240 if (o->flags & MODULE_FLAG_SIZE_ONLY)
2241 {
2242 fprintf(f, "%csize_only", c);
2243 c = ' ';
2244 }
2245 fprintf(f, ")");
2246 }
2247 fprintf(f, "\n");
2248
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
2273 ttlnset = 0;
2274 for (i = 0; (i <= 7) & (ttlnset == 0); i++) if (o->ttl[i] != 0) ttlnset = 1;
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 }
2288
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);
2292 #if !TARGET_OS_SIMULATOR
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
2309 void
2310 asl_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 /*
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.
2332 * base.gz is not allowed.
2333 * Output parameter stamp must be freed by caller.
2334 */
2335 bool
2336 _check_file_name(const char *name, const char *base, const char *ext, uint32_t flags, bool src, char **stamp)
2337 {
2338 size_t baselen, extlen;
2339 const char *p, *z;
2340 bool isgz = false;
2341
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
2346 if (name == NULL) return false;
2347 if (base == NULL) return false;
2348
2349 baselen = strlen(base);
2350 if (baselen == 0) return false;
2351
2352 extlen = 0;
2353 if (ext != NULL) extlen = strlen(ext);
2354
2355 if (stamp != NULL) *stamp = NULL;
2356
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 }
2364
2365 z = strrchr(name, '.');
2366 if ((z != NULL) && streq(z, ".gz")) isgz = true;
2367
2368 #ifdef DEBUG_LIST_FILES
2369 fprintf(stderr, "z=%s isgz=%s\n", (z == NULL) ? "NULL" : z, isgz ? "true" : "false");
2370 #endif
2371
2372 p = name + baselen;
2373
2374 if (flags & MODULE_NAME_STYLE_FORMAT_BS)
2375 {
2376 #ifdef DEBUG_LIST_FILES
2377 fprintf(stderr, " MODULE_NAME_STYLE_FORMAT_BS\n");
2378 #endif
2379 if (*p == '\0')
2380 {
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;
2386 }
2387
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 }
2396
2397 p++;
2398
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 }
2407
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 }
2415
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)
2422 {
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 }
2443
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
2487 return true;
2488 }
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
2494
2495 if (*p != '.')
2496 {
2497 #ifdef DEBUG_LIST_FILES
2498 fprintf(stderr, " expecting dot - failed [%u]\n", __LINE__);
2499 #endif
2500 return false;
2501 }
2502
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;
2553 }
2554
2555 #ifdef DEBUG_LIST_FILES
2556 fprintf(stderr, " unknown format - failed\n");
2557 #endif
2558
2559 return false;
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 *
2566 * MODULE_NAME_STYLE_STAMP_SEQ means a numeric sequence number is expected, although not required.
2567 * E.g. foo.log foo.log.0
2568 *
2569 * MODULE_NAME_STYLE_STAMP_SEC also means a numeric sequence number is required following an 'T' character.
2570 * The numeric value is the file's timestamp in seconds. E.g foo.log.T1335200452
2571 *
2572 * MODULE_NAME_STYLE_STAMP_UTC requires a date/time component as the file's timestamp.
2573 * E.g. foo.2012-04-06T15:30:00Z
2574 *
2575 * MODULE_NAME_STYLE_STAMP_UTC_B requires a date/time component as the file's timestamp.
2576 * E.g. foo.20120406T153000Z
2577 *
2578 * MODULE_NAME_STYLE_STAMP_LCL requires a date/time component as the file's timestamp.
2579 * E.g. foo.2012-04-06T15:30:00-7
2580 *
2581 * MODULE_NAME_STYLE_STAMP_LCL_B requires a date/time component as the file's timestamp.
2582 * E.g. foo.20120406T153000-07
2583 */
2584 int
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
2598 /* check for MODULE_NAME_STYLE_STAMP_SEC (foo.T12345678) */
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
2608 /* check for MODULE_NAME_STYLE_STAMP_SEQ (foo.0 or foo.2.gz) */
2609 digits = true;
2610 for (i = 0; digits && (stamp[i] != '\0'); i++) digits = (stamp[i] >= '0') && (stamp[i] <= '9');
2611
2612 if (!digits && (streq(stamp + i, ".gz"))) digits = true;
2613
2614 if (digits)
2615 {
2616 n = atoi(stamp);
2617 if (sp != NULL) *sp = (uint32_t)n;
2618 return STAMP_STYLE_SEQ;
2619 }
2620
2621 /* check for MODULE_NAME_STYLE_STAMP_UTC, UTC_B, LCL, or LCL_B */
2622 memset(&t, 0, sizeof(t));
2623 h = m = s = 0;
2624
2625 n = 0;
2626 if ((flags & MODULE_NAME_STYLE_STAMP_UTC) || (flags & MODULE_NAME_STYLE_STAMP_LCL))
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 }
2630 else if ((flags & MODULE_NAME_STYLE_STAMP_UTC_B) || (flags & MODULE_NAME_STYLE_STAMP_LCL_B))
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 {
2636 #ifdef DEBUG_LIST_FILES
2637 fprintf(stderr, "_parse_stamp_style fail %u\n", __LINE__);
2638 #endif
2639 return STAMP_STYLE_INVALID;
2640 }
2641
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 }
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;
2659 if (zone == '+') utc_offset *= -1;
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 {
2675 #ifdef DEBUG_LIST_FILES
2676 fprintf(stderr, "_parse_stamp_style fail %u\n", __LINE__);
2677 #endif
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
2691 return STAMP_STYLE_ISO8601;
2692 }
2693
2694 asl_out_file_list_t *
2695 asl_list_log_files(const char *dir, const char *base, const char *ext, uint32_t flags, bool src)
2696 {
2697 DIR *d;
2698 struct dirent *ent;
2699 char path[MAXPATHLEN];
2700 uint32_t seq;
2701 time_t ftime;
2702 struct stat sb;
2703 int pstyle, fstyle;
2704 asl_out_file_list_t *out, *x, *y;
2705
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
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;
2722 bool stat_ok = false;
2723
2724 check = _check_file_name(ent->d_name, base, ext, flags, src, &stamp);
2725 if (!check) continue;
2726
2727 seq = IndexNull;
2728 ftime = 0;
2729
2730 pstyle = _parse_stamp_style(stamp, flags, &seq, &ftime);
2731 free(stamp);
2732
2733 if (pstyle == STAMP_STYLE_INVALID) continue;
2734
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
2744 fstyle = STAMP_STYLE_NULL;
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
2753
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 */
2760
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;
2765
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
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);
2782 x->stamp = pstyle;
2783 x->ftime = ftime;
2784 x->seq = seq;
2785
2786 if (stat_ok)
2787 {
2788 x->size = sb.st_size;
2789 if (pstyle == STAMP_STYLE_SEQ)
2790 {
2791 x->ftime = sb.st_birthtimespec.tv_sec;
2792 if (x->ftime == 0) x->ftime = sb.st_mtimespec.tv_sec;
2793 }
2794 }
2795
2796 if (pstyle == STAMP_STYLE_SEQ)
2797 {
2798 #ifdef DEBUG_LIST_FILES
2799 fprintf(stderr, "asl_list_log_files SEQ %s %u %ld\n", path, x->seq, x->ftime);
2800 #endif
2801 if (out == NULL)
2802 {
2803 out = x;
2804 }
2805 else if ((x->seq == IndexNull) || ((x->seq > out->seq) && (out->seq != IndexNull)))
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 }
2821 else if ((x->seq > y->next->seq) && (y->next->seq != IndexNull))
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 {
2834 #ifdef DEBUG_LIST_FILES
2835 fprintf(stderr, "asl_list_log_files TIME %s %ld\n", path, x->ftime);
2836 #endif
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 */
2877 asl_out_file_list_t *
2878 asl_list_src_files(asl_out_dst_data_t *dst)
2879 {
2880 uint32_t flags;
2881 #ifdef DEBUG_LIST_FILES
2882 fprintf(stderr, "asl_list_src_files\n");
2883 #endif
2884
2885 if (dst == NULL) return NULL;
2886 if (dst->path == NULL) return NULL;
2887 if (dst->base == NULL) return NULL;
2888
2889 /*
2890 * MODULE_FLAG_EXTERNAL means some process other than syslogd writes the file.
2891 * We check for its existence, and that it is non-zero in size.
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 {
2901 if (S_ISREG(sb.st_mode) && (sb.st_size != 0))
2902 {
2903 asl_out_file_list_t *out = (asl_out_file_list_t *)calloc(1, sizeof(asl_out_file_list_t));
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
2920 if ((dst->rotate_dir == NULL) && (dst->flags & MODULE_FLAG_BASESTAMP) && ((dst->flags & MODULE_FLAG_COMPRESS) == 0))
2921 {
2922 /* files do not move to a dest dir, get renamed, or get compressed - nothing to do */
2923 return NULL;
2924 }
2925
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 }
2936
2937 return asl_list_log_files(dst->dir, dst->base, dst->ext, flags, true);
2938 }
2939
2940 /*
2941 * List the destination files for an output asl_out_dst_data_t
2942 */
2943 asl_out_file_list_t *
2944 asl_list_dst_files(asl_out_dst_data_t *dst)
2945 {
2946 char *dst_dir;
2947 #ifdef DEBUG_LIST_FILES
2948 fprintf(stderr, "asl_list_dst_files\n");
2949 #endif
2950
2951 if (dst == NULL) return NULL;
2952 if (dst->path == NULL) return NULL;
2953 if (dst->base == NULL) return NULL;
2954
2955 dst_dir = dst->rotate_dir;
2956 if (dst_dir == NULL) dst_dir = dst->dir;
2957
2958 return asl_list_log_files(dst_dir, dst->base, dst->ext, dst->style_flags, false);
2959 }
2960
2961 static int
2962 asl_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
3014 int
3015 asl_secure_chown_chmod_dir(const char *path, uid_t uid, gid_t gid, mode_t mode)
3016 {
3017 int fd, status;
3018
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 }