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