]> git.saurik.com Git - apple/syslog.git/blob - aslcommon/asl_common.c
syslog-323.50.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_IPHONE_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_IPHONE_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_IPHONE_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_IPHONE_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_IPHONE_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_IPHONE_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_IPHONE_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_IPHONE_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_IPHONE_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_IPHONE_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, "activity")) dst->flags |= MODULE_FLAG_ACTIVITY;
1649 else if (KEYMATCH(p, "extern")) dst->flags |= MODULE_FLAG_EXTERNAL;
1650 else if (KEYMATCH(p, "truncate")) dst->flags |= MODULE_FLAG_TRUNCATE;
1651 else if (KEYMATCH(p, "dir")) dst->flags |= MODULE_FLAG_TYPE_ASL_DIR;
1652 else if (KEYMATCH(p, "soft")) dst->flags |= MODULE_FLAG_SOFT_WRITE;
1653 else if (KEYMATCH(p, "file_max=")) dst->file_max = asl_core_str_to_size(p+9);
1654 else if (KEYMATCH(p, "all_max=")) dst->all_max = asl_core_str_to_size(p+8);
1655 else if (KEYMATCH(p, "style=") || KEYMATCH(p, "rotate="))
1656 {
1657 const char *x = p + 6;
1658
1659 if (*p == 'r') x++;
1660 if (_parse_dst_style(dst, x) == 0) dst->flags |= MODULE_FLAG_ROTATE;
1661 }
1662 else if (KEYMATCH(p, "rotate"))
1663 {
1664 if (dst->ext == NULL) dst->style_flags = MODULE_NAME_STYLE_FORMAT_BS | MODULE_NAME_STYLE_STAMP_SEC;
1665 else dst->style_flags = MODULE_NAME_STYLE_FORMAT_BES | MODULE_NAME_STYLE_STAMP_SEC;
1666
1667 dst->flags |= MODULE_FLAG_ROTATE;
1668 }
1669 else if (KEYMATCH(p, "crashlog"))
1670 {
1671 /* crashlog implies rotation */
1672 dst->flags |= MODULE_FLAG_ROTATE;
1673 dst->flags |= MODULE_FLAG_CRASHLOG;
1674 dst->flags |= MODULE_FLAG_BASESTAMP;
1675 dst->flags &= ~MODULE_FLAG_COALESCE;
1676 }
1677 else if (KEYMATCH(p, "basestamp"))
1678 {
1679 dst->flags |= MODULE_FLAG_BASESTAMP;
1680 }
1681 else if (KEYMATCH(p, "link") || KEYMATCH(p, "symlink"))
1682 {
1683 dst->flags |= MODULE_FLAG_SYMLINK;
1684 }
1685 else if (KEYMATCH(p, "ttl"))
1686 {
1687 char *q = p + 3;
1688 if (*q == '=')
1689 {
1690 dst->ttl[LEVEL_ALL] = asl_core_str_to_time(p+4, SECONDS_PER_DAY);
1691 }
1692 else if ((*q >= '0') && (*q <= '7') && (*(q+1) == '='))
1693 {
1694 uint32_t x = *q - '0';
1695 dst->ttl[x] = asl_core_str_to_time(p+5, SECONDS_PER_DAY);
1696 }
1697 }
1698
1699 free(p);
1700 p = NULL;
1701 }
1702
1703 #if TARGET_OS_EMBEDDED
1704 /* check for crashreporter files */
1705 if ((KEYMATCH(dst->path, _PATH_CRASHREPORTER)) || (KEYMATCH(dst->path, _PATH_CRASHREPORTER_MOBILE_1)) || (KEYMATCH(dst->path, _PATH_CRASHREPORTER_MOBILE_2)))
1706 {
1707 dst->flags |= MODULE_FLAG_ROTATE;
1708 dst->flags |= MODULE_FLAG_CRASHLOG;
1709 dst->flags |= MODULE_FLAG_BASESTAMP;
1710 dst->flags &= ~MODULE_FLAG_COALESCE;
1711 }
1712 #endif
1713
1714 /* ttl[LEVEL_ALL] must be max of all level-specific ttls */
1715 for (i = 0; i <= 7; i++) if (dst->ttl[i] > dst->ttl[LEVEL_ALL]) dst->ttl[LEVEL_ALL] = dst->ttl[i];
1716
1717 /* default text file format is "std" */
1718 if (dst->fmt == NULL) dst->fmt = strdup("std");
1719
1720 /* duplicate compression is only possible for std and bsd formats */
1721 if (strcmp(dst->fmt, "std") && strcmp(dst->fmt, "bsd")) dst->flags &= ~MODULE_FLAG_COALESCE;
1722
1723 /* note if format is one of std, bsd, or msg */
1724 if (streq(dst->fmt, "std") || streq(dst->fmt, "bsd") || streq(dst->fmt, "msg")) dst->flags |= MODULE_FLAG_STD_BSD_MSG;
1725
1726 /* MODULE_NAME_STYLE_STAMP_SEQ can not be used with MODULE_FLAG_BASESTAMP */
1727 if ((dst->flags & MODULE_FLAG_BASESTAMP) && (dst->flags & MODULE_NAME_STYLE_STAMP_SEQ))
1728 {
1729 dst->flags &= ~MODULE_NAME_STYLE_STAMP_SEQ;
1730 dst->flags |= MODULE_NAME_STYLE_STAMP_SEC;
1731 }
1732
1733 /* set time format for raw output */
1734 if (streq(dst->fmt, "raw")) dst->tfmt = "sec";
1735
1736 /* check for ASL_PLACE_DATABASE_DEFAULT */
1737 if (streq(dst->path, ASL_PLACE_DATABASE_DEFAULT))
1738 {
1739 dst->flags = MODULE_FLAG_TYPE_ASL_DIR;
1740 }
1741
1742 /* set file_max to all_max if it is zero */
1743 if (dst->file_max == 0) dst->file_max = dst->all_max;
1744
1745 out->action = ACTION_OUT_DEST;
1746 out->dst = dst;
1747
1748 /* dst rules go first */
1749 out->next = m->ruleset;
1750 m->ruleset = out;
1751
1752 return dst;
1753 }
1754
1755 static asl_out_rule_t *
1756 _asl_out_module_parse_query_action(asl_out_module_t *m, char *s)
1757 {
1758 char *act, *p;
1759 asl_out_rule_t *out, *rule;
1760
1761 if (m == NULL) return NULL;
1762
1763 out = (asl_out_rule_t *)calloc(1, sizeof(asl_out_rule_t));
1764 if (out == NULL) return NULL;
1765
1766 act = _asl_out_module_find_action(s);
1767 if (act == NULL) return NULL;
1768
1769 /* find whitespace delimiter */
1770 p = strchr(act, ' ');
1771 if (p == NULL) p = strchr(act, '\t');
1772 if (p != NULL) *p = '\0';
1773
1774 if (strcaseeq(act, "ignore")) out->action = ACTION_IGNORE;
1775 else if (strcaseeq(act, "skip")) out->action = ACTION_SKIP;
1776 else if (strcaseeq(act, "claim")) out->action = ACTION_CLAIM;
1777 else if (strcaseeq(act, "notify")) out->action = ACTION_NOTIFY;
1778 else if (strcaseeq(act, "file")) out->action = ACTION_FILE;
1779 else if (strcaseeq(act, "asl_file")) out->action = ACTION_ASL_FILE;
1780 else if (strcaseeq(act, "directory")) out->action = ACTION_ASL_DIR;
1781 else if (strcaseeq(act, "dir")) out->action = ACTION_ASL_DIR;
1782 else if (strcaseeq(act, "asl_directory")) out->action = ACTION_ASL_DIR;
1783 else if (strcaseeq(act, "asl_dir")) out->action = ACTION_ASL_DIR;
1784 else if (strcaseeq(act, "store_dir")) out->action = ACTION_ASL_DIR;
1785 else if (strcaseeq(act, "store_directory")) out->action = ACTION_ASL_DIR;
1786 else if (strcaseeq(act, "control")) out->action = ACTION_CONTROL;
1787 else if (strcaseeq(act, "save")) out->action = ACTION_ASL_STORE;
1788 else if (strcaseeq(act, "store")) out->action = ACTION_ASL_STORE;
1789 else if (strcaseeq(act, "access")) out->action = ACTION_ACCESS;
1790 else if (strcaseeq(act, "set")) out->action = ACTION_SET_KEY;
1791 else if (strcaseeq(act, "unset")) out->action = ACTION_UNSET_KEY;
1792 else if (streq(m->name, ASL_MODULE_NAME))
1793 {
1794 /* actions only allowed in com.apple.asl */
1795 if (strcaseeq(act, "broadcast")) out->action = ACTION_BROADCAST;
1796 else if (strcaseeq(act, "forward")) out->action = ACTION_FORWARD;
1797 }
1798
1799 if (out->action == ACTION_NONE)
1800 {
1801 free(out);
1802 return NULL;
1803 }
1804
1805 /* options follow delimited (now zero) */
1806 if (p != NULL)
1807 {
1808 /* skip whitespace */
1809 while ((*p == ' ') || (*p == '\t')) p++;
1810
1811 out->options = _strdup_clean(p+1);
1812
1813 if (out->options == NULL)
1814 {
1815 free(out);
1816 return NULL;
1817 }
1818 }
1819
1820 p = act - 1;
1821
1822 *p = '\0';
1823
1824 if (*s== '*')
1825 {
1826 out->query = asl_msg_new(ASL_TYPE_QUERY);
1827 }
1828 else
1829 {
1830 *s = 'Q';
1831 out->query = asl_msg_from_string(s);
1832 }
1833
1834 if (out->query == NULL)
1835 {
1836 free(out->options);
1837 free(out);
1838 return NULL;
1839 }
1840
1841 /* store /some/path means save to an asl file */
1842 if (out->action == ACTION_ASL_STORE)
1843 {
1844 if (out->options == NULL) out->dst = asl_out_dst_data_retain(_asl_out_module_parse_dst(m, ASL_PLACE_DATABASE_DEFAULT, 0755));
1845 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));
1846 else if (out->options != NULL) out->action = ACTION_ASL_FILE;
1847 }
1848
1849 if ((out->action == ACTION_FILE) || (out->action == ACTION_ASL_FILE) || (out->action == ACTION_ASL_DIR))
1850 {
1851 mode_t def_mode = 0644;
1852 if (out->action == ACTION_ASL_DIR) def_mode = 0755;
1853
1854 out->dst = asl_out_dst_data_retain(_asl_out_module_parse_dst(m, out->options, def_mode));
1855 if (out->dst == NULL)
1856 {
1857 out->action = ACTION_NONE;
1858 return out;
1859 }
1860
1861 /*
1862 * dst might have been set up by a previous ACTION_OUT_DEST ('>') rule with no mode.
1863 * If so, mode would be 010000. Set it now, since we know whether it is a file or dir.
1864 */
1865 if (out->dst->mode == 010000) out->dst->mode = def_mode;
1866
1867 if ((out->action == ACTION_FILE) && (out->dst != NULL) && (out->dst->fmt != NULL) && (strcaseeq(out->dst->fmt, "asl")))
1868 {
1869 out->action = ACTION_ASL_FILE;
1870 }
1871
1872 if ((out->action == ACTION_ASL_FILE) && (out->dst != NULL))
1873 {
1874 out->dst->flags |= MODULE_FLAG_TYPE_ASL;
1875 }
1876
1877 if (out->action == ACTION_ASL_DIR)
1878 {
1879 /* coalesce is meaningless for ASL directories */
1880 out->dst->flags &= ~MODULE_FLAG_COALESCE;
1881
1882 out->dst->flags |= MODULE_FLAG_TYPE_ASL_DIR;
1883
1884 /* set style bits for basestamp asl_dirs */
1885 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;
1886 }
1887
1888 /* only ACTION_FILE and ACTION_ASL_FILE may rotate */
1889 if ((out->action != ACTION_FILE) && (out->action != ACTION_ASL_FILE))
1890 {
1891 out->dst->flags &= ~MODULE_FLAG_ROTATE;
1892 }
1893
1894 #if !TARGET_IPHONE_SIMULATOR
1895 if (out->dst->nuid == 0) _dst_add_uid(out->dst, "0");
1896 if (out->dst->ngid == 0) _dst_add_gid(out->dst, "80");
1897 #endif
1898 }
1899
1900 if (m->ruleset == NULL) m->ruleset = out;
1901 else
1902 {
1903 for (rule = m->ruleset; rule->next != NULL; rule = rule->next);
1904 rule->next = out;
1905 }
1906
1907 return out;
1908 }
1909
1910 asl_out_rule_t *
1911 asl_out_module_parse_line(asl_out_module_t *m, char *s)
1912 {
1913 while ((*s == ' ') || (*s == '\t')) s++;
1914
1915 if ((*s == 'Q') || (*s == '?') || (*s == '*'))
1916 {
1917 return _asl_out_module_parse_query_action(m, s);
1918 }
1919 else if (*s == '=')
1920 {
1921 return _asl_out_module_parse_set_param(m, s);
1922 }
1923 else if (*s == '>')
1924 {
1925 _asl_out_module_parse_dst(m, s + 1, 010000);
1926 }
1927
1928 return NULL;
1929 }
1930
1931 asl_out_module_t *
1932 asl_out_module_init_from_file(const char *name, FILE *f)
1933 {
1934 asl_out_module_t *out;
1935 char *line;
1936
1937 if (f == NULL) return NULL;
1938
1939 out = asl_out_module_new(name);
1940 if (out == NULL) return NULL;
1941
1942 /* read and parse config file */
1943 while (NULL != (line = get_line_from_file(f)))
1944 {
1945 asl_out_module_parse_line(out, line);
1946 free(line);
1947 }
1948
1949 return out;
1950 }
1951
1952 static asl_out_module_t *
1953 _asl_out_module_find(asl_out_module_t *list, const char *name)
1954 {
1955 asl_out_module_t *x;
1956
1957 if (list == NULL) return NULL;
1958 if (name == NULL) return NULL;
1959
1960 for (x = list; x != NULL; x = x->next)
1961 {
1962 if ((x->name != NULL) && (streq(x->name, name))) return x;
1963 }
1964
1965 return NULL;
1966 }
1967
1968 static void
1969 _asl_out_module_read_and_merge_dir(asl_out_module_t **list, const char *path, uint32_t flags)
1970 {
1971 DIR *d;
1972 struct dirent *ent;
1973 FILE *f;
1974 asl_out_module_t *last, *x;
1975
1976 if (list == NULL) return;
1977 if (path == NULL) return;
1978
1979 last = *list;
1980 if (last != NULL)
1981 {
1982 while (last->next != NULL) last = last->next;
1983 }
1984
1985 d = opendir(path);
1986 if (d != NULL)
1987 {
1988 while (NULL != (ent = readdir(d)))
1989 {
1990 if (ent->d_name[0] != '.')
1991 {
1992 /* merge: skip this file if we already have a module with this name */
1993 if (_asl_out_module_find(*list, ent->d_name) != NULL) continue;
1994
1995 char tmp[MAXPATHLEN];
1996 snprintf(tmp, sizeof(tmp), "%s/%s", path, ent->d_name);
1997 f = fopen(tmp, "r");
1998 if (f != NULL)
1999 {
2000 x = asl_out_module_init_from_file(ent->d_name, f);
2001 fclose(f);
2002
2003 if (x != NULL)
2004 {
2005 x->flags |= flags;
2006
2007 if (streq(ent->d_name, ASL_MODULE_NAME))
2008 {
2009 /* com.apple.asl goes at the head of the list */
2010 x->next = *list;
2011 *list = x;
2012 if (last == NULL) last = *list;
2013 }
2014 else if (*list == NULL)
2015 {
2016 *list = x;
2017 last = *list;
2018 }
2019 else
2020 {
2021 last->next = x;
2022 last = x;
2023 }
2024 }
2025 }
2026 }
2027 }
2028
2029 closedir(d);
2030 }
2031 }
2032
2033 asl_out_module_t *
2034 asl_out_module_init(void)
2035 {
2036 asl_out_module_t *out = NULL;
2037
2038 #if TARGET_IPHONE_SIMULATOR
2039 char *sim_root_path, *sim_resources_path;
2040 char *asl_conf, *asl_conf_dir, *asl_conf_local_dir;
2041
2042 sim_root_path = getenv("IPHONE_SIMULATOR_ROOT");
2043 if (sim_root_path == NULL) return NULL;
2044
2045 sim_resources_path = getenv("IPHONE_SHARED_RESOURCES_DIRECTORY");
2046 if (sim_resources_path == NULL) return NULL;
2047
2048 asprintf(&asl_conf, "%s%s", sim_root_path, _PATH_ASL_CONF);
2049 asprintf(&asl_conf_dir, "%s%s", sim_root_path, _PATH_ASL_CONF_DIR);
2050 asprintf(&asl_conf_local_dir, "%s%s", sim_resources_path, _PATH_ASL_CONF_DIR);
2051
2052 _asl_out_module_read_and_merge_dir(&out, asl_conf_local_dir, MODULE_FLAG_LOCAL);
2053 free(asl_conf_local_dir);
2054
2055 _asl_out_module_read_and_merge_dir(&out, asl_conf_dir, 0);
2056 free(asl_conf_dir);
2057 #else
2058 _asl_out_module_read_and_merge_dir(&out, _PATH_ASL_CONF_LOCAL_DIR, MODULE_FLAG_LOCAL);
2059 _asl_out_module_read_and_merge_dir(&out, _PATH_ASL_CONF_DIR, 0);
2060 #endif
2061
2062 if (_asl_out_module_find(out, ASL_MODULE_NAME) == NULL)
2063 {
2064 /* system just has old-style /etc/asl.conf */
2065 #if TARGET_IPHONE_SIMULATOR
2066 FILE *f = fopen(asl_conf, "r");
2067 free(asl_conf);
2068 #else
2069 FILE *f = fopen(_PATH_ASL_CONF, "r");
2070 #endif
2071 if (f != NULL)
2072 {
2073 asl_out_module_t *x = asl_out_module_init_from_file(ASL_MODULE_NAME, f);
2074 fclose(f);
2075 if (x != NULL)
2076 {
2077 x->next = out;
2078 out = x;
2079 }
2080 }
2081 }
2082
2083 return out;
2084 }
2085
2086 /*
2087 * Print rule
2088 */
2089 char *
2090 asl_out_module_rule_to_string(asl_out_rule_t *r)
2091 {
2092 uint32_t len;
2093 char *str, *out;
2094
2095 if (r == NULL)
2096 {
2097 asprintf(&out, "NULL rule");
2098 return out;
2099 }
2100
2101 str = asl_msg_to_string(r->query, &len);
2102
2103 asprintf(&out, " %s%s%s%s%s",
2104 asl_out_action_name[r->action],
2105 (r->query == NULL) ? "" : " ",
2106 (r->query == NULL) ? "" : str,
2107 (r->options == NULL) ? "" : " ",
2108 (r->options == NULL) ? "" : r->options);
2109
2110 free(str);
2111 return out;
2112 }
2113
2114 static const char *
2115 _stamp_style_name(uint32_t flags)
2116 {
2117 if (flags & MODULE_NAME_STYLE_STAMP_SEC) return "<seconds>";
2118 if (flags & MODULE_NAME_STYLE_STAMP_SEQ) return "<sequence>";
2119 if (flags & MODULE_NAME_STYLE_STAMP_UTC) return "<utc>";
2120 if (flags & MODULE_NAME_STYLE_STAMP_UTC_B) return "<utc-basic>";
2121 if (flags & MODULE_NAME_STYLE_STAMP_LCL) return "<local>";
2122 if (flags & MODULE_NAME_STYLE_STAMP_LCL_B) return "<local-basic>";
2123 return "<unknown>";
2124 }
2125
2126 /*
2127 * Print module
2128 */
2129 void
2130 asl_out_module_print(FILE *f, asl_out_module_t *m)
2131 {
2132 asl_out_rule_t *r, *n;
2133 asl_out_dst_data_t *o;
2134 uint32_t i, ttlnset;
2135 char tstr[150];
2136
2137 n = NULL;
2138 for (r = m->ruleset; r != NULL; r = n)
2139 {
2140 uint32_t len;
2141 char *str = asl_msg_to_string(r->query, &len);
2142
2143 fprintf(f, " %s", asl_out_action_name[r->action]);
2144 if (r->query != NULL) fprintf(f, " %s", str);
2145 if (r->options != NULL) fprintf(f, " %s", r->options);
2146 if (r->action == ACTION_OUT_DEST)
2147 {
2148 o = r->dst;
2149 if (o == NULL)
2150 {
2151 fprintf(f, " data: NULL");
2152 }
2153 else
2154 {
2155 fprintf(f, "%s\n", o->path);
2156 fprintf(f, " rules: %u\n", o->refcount - 1);
2157 fprintf(f, " dest: %s\n", (o->rotate_dir == NULL) ? "(none)" : o->rotate_dir);
2158 fprintf(f, " format: %s\n", (o->fmt == NULL) ? "std" : o->fmt);
2159 fprintf(f, " time_format: %s\n", (o->tfmt == NULL) ? "lcl" : o->tfmt);
2160 fprintf(f, " flags: 0x%08x", o->flags);
2161 if (o->flags != 0)
2162 {
2163 char c = '(';
2164 fprintf(f, " ");
2165 if (o->flags & MODULE_FLAG_ENABLED)
2166 {
2167 fprintf(f, "%cenabled", c);
2168 c = ' ';
2169 }
2170 if (o->flags & MODULE_FLAG_SOFT_WRITE)
2171 {
2172 fprintf(f, "%csoft", c);
2173 c = ' ';
2174 }
2175 if (o->flags & MODULE_FLAG_ROTATE)
2176 {
2177 fprintf(f, "%crotate", c);
2178 c = ' ';
2179 }
2180 if (o->flags & MODULE_FLAG_COALESCE)
2181 {
2182 fprintf(f, "%ccoalesce", c);
2183 c = ' ';
2184 }
2185 if (o->flags & MODULE_FLAG_COMPRESS)
2186 {
2187 fprintf(f, "%ccompress", c);
2188 c = ' ';
2189 }
2190 if (o->flags & MODULE_FLAG_BASESTAMP)
2191 {
2192 fprintf(f, "%cbasestamp", c);
2193 c = ' ';
2194 }
2195 if (o->flags & MODULE_FLAG_SYMLINK)
2196 {
2197 fprintf(f, "%csymlink", c);
2198 c = ' ';
2199 }
2200 if (o->flags & MODULE_FLAG_NONSTD_DIR)
2201 {
2202 fprintf(f, "%cnon-std_dir", c);
2203 c = ' ';
2204 }
2205 if (o->flags & MODULE_FLAG_EXTERNAL)
2206 {
2207 fprintf(f, "%cexternal", c);
2208 c = ' ';
2209 }
2210 if (o->flags & MODULE_FLAG_ACTIVITY)
2211 {
2212 fprintf(f, "%cactivity", c);
2213 c = ' ';
2214 }
2215 if (o->flags & MODULE_FLAG_CRASHLOG)
2216 {
2217 fprintf(f, "%ccrashlog", c);
2218 c = ' ';
2219 }
2220 if (o->flags & MODULE_FLAG_TYPE_ASL)
2221 {
2222 fprintf(f, "%casl_file", c);
2223 c = ' ';
2224 }
2225 if (o->flags & MODULE_FLAG_TYPE_ASL_DIR)
2226 {
2227 fprintf(f, "%casl_directory", c);
2228 c = ' ';
2229 }
2230 fprintf(f, ")");
2231 }
2232 fprintf(f, "\n");
2233
2234 if (o->flags & MODULE_FLAG_ROTATE)
2235 {
2236 fprintf(f, " rotatation style: ");
2237 if (o->style_flags & MODULE_NAME_STYLE_FORMAT_BS)
2238 {
2239 fprintf(f, "[base=%s].%s\n", o->base, _stamp_style_name(o->style_flags));
2240 }
2241 else if (o->style_flags & MODULE_NAME_STYLE_FORMAT_BES)
2242 {
2243 fprintf(f, "[base=%s].[ext=%s].%s\n", o->base, o->ext, _stamp_style_name(o->style_flags));
2244 }
2245 else if (o->style_flags & MODULE_NAME_STYLE_FORMAT_BSE)
2246 {
2247 fprintf(f, "[base=%s].%s.[ext=%s]\n", o->base, _stamp_style_name(o->style_flags), o->ext);
2248 }
2249 else
2250 {
2251 fprintf(f, "0x%08x\n", o->style_flags);
2252 }
2253 }
2254
2255 asl_core_time_to_str(o->ttl[LEVEL_ALL], tstr, sizeof(tstr));
2256 fprintf(f, " ttl: %s\n", tstr);
2257
2258 ttlnset = 0;
2259 for (i = 0; (i <= 7) & (ttlnset == 0); i++) if (o->ttl[i] != 0) ttlnset = 1;
2260 if (ttlnset != 0)
2261 {
2262 for (i = 0; i <= 7; i++)
2263 {
2264 time_t x = o->ttl[i];
2265 if (x == 0) x = o->ttl[LEVEL_ALL];
2266 asl_core_time_to_str(x, tstr, sizeof(tstr));
2267
2268 fprintf(f, " [%d %s]", i, tstr);
2269 }
2270
2271 fprintf(f, "\n");
2272 }
2273
2274 fprintf(f, " mode: 0%o\n", o->mode);
2275 fprintf(f, " file_max: %lu\n", o->file_max);
2276 fprintf(f, " all_max: %lu\n", o->all_max);
2277 #if !TARGET_IPHONE_SIMULATOR
2278 fprintf(f, " uid:");
2279 for (i = 0; i < o->nuid; i++) fprintf(f, " %d", o->uid[i]);
2280 fprintf(f, "\n");
2281 fprintf(f, " gid:");
2282 for (i = 0; i < o->ngid; i++) fprintf(f, " %d", o->gid[i]);
2283 #endif
2284 }
2285 }
2286
2287 fprintf(f, "\n");
2288 n = r->next;
2289
2290 free(str);
2291 }
2292 }
2293
2294 void
2295 asl_out_file_list_free(asl_out_file_list_t *l)
2296 {
2297 asl_out_file_list_t *n;
2298
2299 if (l == NULL) return;
2300
2301 while (l != NULL)
2302 {
2303 free(l->name);
2304 n = l->next;
2305 free(l);
2306 l = n;
2307 }
2308 }
2309
2310 /*
2311 * Checks input name for one of the forms:
2312 * MODULE_NAME_STYLE_FORMAT_BS base (src only) or base.stamp[.gz]
2313 * MODULE_NAME_STYLE_FORMAT_BES base.ext (src only) or base.ext.stamp[.gz]
2314 * MODULE_NAME_STYLE_FORMAT_BSE base.ext (src only) or base.stamp.ext[.gz]
2315 *
2316 * name == base[.ext] is allowed if src is true.
2317 * base.gz is not allowed.
2318 * Output parameter stamp must be freed by caller.
2319 */
2320 bool
2321 _check_file_name(const char *name, const char *base, const char *ext, uint32_t flags, bool src, char **stamp)
2322 {
2323 size_t baselen, extlen;
2324 const char *p, *z;
2325 bool isgz = false;
2326
2327 #ifdef DEBUG_LIST_FILES
2328 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");
2329 #endif
2330
2331 if (name == NULL) return false;
2332 if (base == NULL) return false;
2333
2334 baselen = strlen(base);
2335 if (baselen == 0) return false;
2336
2337 extlen = 0;
2338 if (ext != NULL) extlen = strlen(ext);
2339
2340 if (stamp != NULL) *stamp = NULL;
2341
2342 if (strneq_len(name, base, baselen))
2343 {
2344 #ifdef DEBUG_LIST_FILES
2345 fprintf(stderr, " base match failed [%u]\n", __LINE__);
2346 #endif
2347 return false;
2348 }
2349
2350 z = strrchr(name, '.');
2351 if ((z != NULL) && streq(z, ".gz")) isgz = true;
2352
2353 #ifdef DEBUG_LIST_FILES
2354 fprintf(stderr, "z=%s isgz=%s\n", (z == NULL) ? "NULL" : z, isgz ? "true" : "false");
2355 #endif
2356
2357 p = name + baselen;
2358
2359 if (flags & MODULE_NAME_STYLE_FORMAT_BS)
2360 {
2361 #ifdef DEBUG_LIST_FILES
2362 fprintf(stderr, " MODULE_NAME_STYLE_FORMAT_BS\n");
2363 #endif
2364 if (*p == '\0')
2365 {
2366 /* name == base OK if src is true */
2367 #ifdef DEBUG_LIST_FILES
2368 fprintf(stderr, " name == base %s [%u]\n", src ? "OK" : "failed", __LINE__);
2369 #endif
2370 return src;
2371 }
2372
2373 /* expecting p == .stamp[.gz] */
2374 if (*p != '.')
2375 {
2376 #ifdef DEBUG_LIST_FILES
2377 fprintf(stderr, " expecting dot - failed [%u]\n", __LINE__);
2378 #endif
2379 return false;
2380 }
2381
2382 p++;
2383
2384 if (p == z)
2385 {
2386 /* base.gz is not allowed */
2387 #ifdef DEBUG_LIST_FILES
2388 fprintf(stderr, " got base.gz - failed [%u]\n", __LINE__);
2389 #endif
2390 return false;
2391 }
2392
2393 /* got base.stamp[.gz] */
2394 if (stamp != NULL)
2395 {
2396 *stamp = strdup(p);
2397 char *x = strchr(*stamp, '.');
2398 if (x != NULL) *x = '\0';
2399 }
2400
2401 #ifdef DEBUG_LIST_FILES
2402 fprintf(stderr, " got base.stamp%s - OK\n", isgz ? ".gz" : "");
2403 #endif
2404 return true;
2405 }
2406 else if (flags & MODULE_NAME_STYLE_FORMAT_BES)
2407 {
2408 #ifdef DEBUG_LIST_FILES
2409 fprintf(stderr, " MODULE_NAME_STYLE_FORMAT_BES\n");
2410 #endif
2411 if (*p != '.')
2412 {
2413 #ifdef DEBUG_LIST_FILES
2414 fprintf(stderr, " expecting dot - failed [%u]\n", __LINE__);
2415 #endif
2416 return false;
2417 }
2418
2419 p++;
2420
2421 if (strneq_len(p, ext, extlen))
2422 {
2423 #ifdef DEBUG_LIST_FILES
2424 fprintf(stderr, " ext match failed [%u]\n", __LINE__);
2425 #endif
2426 return false;
2427 }
2428
2429 /* expecting p == .ext[.stamp][.gz] */
2430 p += extlen;
2431
2432 if (*p == '\0')
2433 {
2434 /* name == base.ext OK if src is true */
2435 #ifdef DEBUG_LIST_FILES
2436 fprintf(stderr, " name == base.ext %s [%u]\n", src ? "OK" : "failed", __LINE__);
2437 #endif
2438 return src;
2439 }
2440
2441 /* expecting p == .stamp[.gz] */
2442 if (*p != '.')
2443 {
2444 #ifdef DEBUG_LIST_FILES
2445 fprintf(stderr, " expecting dot - failed [%u]\n", __LINE__);
2446 #endif
2447 return false;
2448 }
2449
2450 p++;
2451
2452 if (p == z)
2453 {
2454 /* base.ext.gz is not allowed */
2455 #ifdef DEBUG_LIST_FILES
2456 fprintf(stderr, " got base.ext.gz - failed [%u]\n", __LINE__);
2457 #endif
2458 return false;
2459 }
2460
2461 /* got base.ext.stamp[.gz] */
2462 if (stamp != NULL)
2463 {
2464 *stamp = strdup(p);
2465 char *x = strchr(*stamp, '.');
2466 if (x != NULL) *x = '\0';
2467 }
2468
2469 #ifdef DEBUG_LIST_FILES
2470 fprintf(stderr, " got base.ext.stamp%s - OK\n", isgz ? ".gz" : "");
2471 #endif
2472 return true;
2473 }
2474 else if (flags & MODULE_NAME_STYLE_FORMAT_BSE)
2475 {
2476 #ifdef DEBUG_LIST_FILES
2477 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");
2478 #endif
2479
2480 if (*p != '.')
2481 {
2482 #ifdef DEBUG_LIST_FILES
2483 fprintf(stderr, " expecting dot - failed [%u]\n", __LINE__);
2484 #endif
2485 return false;
2486 }
2487
2488 p++;
2489
2490 if (streq_len(p, ext, extlen))
2491 {
2492 p += extlen;
2493 if (*p == '\0')
2494 {
2495 /* name == base.ext OK if src is true */
2496 #ifdef DEBUG_LIST_FILES
2497 fprintf(stderr, " name == base.ext %s [%u]\n", src ? "OK" : "failed", __LINE__);
2498 #endif
2499 return src;
2500 }
2501 }
2502
2503 /* expecting p == stamp.ext[.gz] */
2504
2505 if (isgz)
2506 {
2507 if (strneq_len(z - extlen, ext, extlen))
2508 {
2509 #ifdef DEBUG_LIST_FILES
2510 fprintf(stderr, " ext match (%s) isgz failed [%u]\n", z-extlen, __LINE__);
2511 #endif
2512 return false;
2513 }
2514 }
2515 else
2516 {
2517 if (strneq_len(z + 1, ext, extlen))
2518 {
2519 #ifdef DEBUG_LIST_FILES
2520 fprintf(stderr, " ext match (%s) failed [%u]\n", z, __LINE__);
2521 #endif
2522 return false;
2523 }
2524 }
2525
2526 /* got base.stamp.ext[.gz] */
2527 if (stamp != NULL)
2528 {
2529 *stamp = strdup(p);
2530 char *x = strchr(*stamp, '.');
2531 if (x != NULL) *x = '\0';
2532 }
2533
2534 #ifdef DEBUG_LIST_FILES
2535 fprintf(stderr, " got base.stamp.ext%s - OK\n", isgz ? ".gz" : "");
2536 #endif
2537 return true;
2538 }
2539
2540 #ifdef DEBUG_LIST_FILES
2541 fprintf(stderr, " unknown format - failed\n");
2542 #endif
2543
2544 return false;
2545 }
2546
2547 /*
2548 * Find files in a directory (dir) that all have a common prefix (base).
2549 * Bits in flags further control the search.
2550 *
2551 * MODULE_NAME_STYLE_STAMP_SEQ means a numeric sequence number is expected, although not required.
2552 * E.g. foo.log foo.log.0
2553 *
2554 * MODULE_NAME_STYLE_STAMP_SEC also means a numeric sequence number is required following an 'T' character.
2555 * The numeric value is the file's timestamp in seconds. E.g foo.log.T1335200452
2556 *
2557 * MODULE_NAME_STYLE_STAMP_UTC requires a date/time component as the file's timestamp.
2558 * E.g. foo.2012-04-06T15:30:00Z
2559 *
2560 * MODULE_NAME_STYLE_STAMP_UTC_B requires a date/time component as the file's timestamp.
2561 * E.g. foo.20120406T153000Z
2562 *
2563 * MODULE_NAME_STYLE_STAMP_LCL requires a date/time component as the file's timestamp.
2564 * E.g. foo.2012-04-06T15:30:00-7
2565 *
2566 * MODULE_NAME_STYLE_STAMP_LCL_B requires a date/time component as the file's timestamp.
2567 * E.g. foo.20120406T153000-07
2568 */
2569 int
2570 _parse_stamp_style(char *stamp, uint32_t flags, uint32_t *sp, time_t *tp)
2571 {
2572 int i, n;
2573 bool digits;
2574 struct tm t;
2575 char zone;
2576 uint32_t h, m, s;
2577 long utc_offset = 0;
2578 time_t ftime = 0;
2579
2580 /* check for NULL (no stamp) */
2581 if (stamp == NULL) return STAMP_STYLE_NULL;
2582
2583 /* check for MODULE_NAME_STYLE_STAMP_SEC (foo.T12345678) */
2584 if (stamp[0] == 'T')
2585 {
2586 n = atoi(stamp + 1);
2587 if ((n == 0) && strcmp(stamp + 1, "0")) return STAMP_STYLE_INVALID;
2588 if (tp != NULL) *tp = (time_t)n;
2589
2590 return STAMP_STYLE_SEC;
2591 }
2592
2593 /* check for MODULE_NAME_STYLE_STAMP_SEQ (foo.0 or foo.2.gz) */
2594 digits = true;
2595 for (i = 0; digits && (stamp[i] != '\0'); i++) digits = (stamp[i] >= '0') && (stamp[i] <= '9');
2596
2597 if (!digits && (streq(stamp + i, ".gz"))) digits = true;
2598
2599 if (digits)
2600 {
2601 n = atoi(stamp);
2602 if (sp != NULL) *sp = (uint32_t)n;
2603 return STAMP_STYLE_SEQ;
2604 }
2605
2606 /* check for MODULE_NAME_STYLE_STAMP_UTC, UTC_B, LCL, or LCL_B */
2607 memset(&t, 0, sizeof(t));
2608 h = m = s = 0;
2609
2610 n = 0;
2611 if ((flags & MODULE_NAME_STYLE_STAMP_UTC) || (flags & MODULE_NAME_STYLE_STAMP_LCL))
2612 {
2613 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);
2614 }
2615 else if ((flags & MODULE_NAME_STYLE_STAMP_UTC_B) || (flags & MODULE_NAME_STYLE_STAMP_LCL_B))
2616 {
2617 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);
2618 }
2619 else
2620 {
2621 #ifdef DEBUG_LIST_FILES
2622 fprintf(stderr, "_parse_stamp_style fail %u\n", __LINE__);
2623 #endif
2624 return STAMP_STYLE_INVALID;
2625 }
2626
2627 if (n < 6)
2628 {
2629 #ifdef DEBUG_LIST_FILES
2630 fprintf(stderr, "_parse_stamp_style fail %u\n", __LINE__);
2631 #endif
2632 return STAMP_STYLE_INVALID;
2633 }
2634
2635 if (n == 6)
2636 {
2637 zone = 'J';
2638 }
2639 else if ((zone == '-') || (zone == '+'))
2640 {
2641 if (n >= 8) utc_offset += (3600 * h);
2642 if (n >= 9) utc_offset += (60 * m);
2643 if (n == 10) utc_offset += s;
2644 if (zone == '+') utc_offset *= -1;
2645 }
2646 else if ((zone >= 'A') && (zone <= 'Z'))
2647 {
2648 if (zone < 'J') utc_offset = 3600 * ((zone - 'A') + 1);
2649 else if ((zone >= 'K') && (zone <= 'M')) utc_offset = 3600 * (zone - 'A');
2650 else if (zone <= 'Y') utc_offset = -3600 * ((zone - 'N') + 1);
2651 }
2652 else if ((zone >= 'a') && (zone <= 'z'))
2653 {
2654 if (zone < 'j') utc_offset = 3600 * ((zone - 'a') + 1);
2655 else if ((zone >= 'k') && (zone <= 'm')) utc_offset = 3600 * (zone - 'a');
2656 else if (zone <= 'y') utc_offset = -3600 * ((zone - 'n') + 1);
2657 }
2658 else
2659 {
2660 #ifdef DEBUG_LIST_FILES
2661 fprintf(stderr, "_parse_stamp_style fail %u\n", __LINE__);
2662 #endif
2663 return STAMP_STYLE_INVALID;
2664 }
2665
2666 t.tm_year -= 1900;
2667 t.tm_mon -= 1;
2668 t.tm_sec += utc_offset;
2669 t.tm_isdst = -1;
2670
2671 if ((zone == 'J') || (zone == 'j')) ftime = mktime(&t);
2672 else ftime = timegm(&t);
2673
2674 if (tp != NULL) *tp = ftime;
2675
2676 return STAMP_STYLE_ISO8601;
2677 }
2678
2679 asl_out_file_list_t *
2680 asl_list_log_files(const char *dir, const char *base, const char *ext, uint32_t flags, bool src)
2681 {
2682 DIR *d;
2683 struct dirent *ent;
2684 char path[MAXPATHLEN];
2685 uint32_t seq;
2686 time_t ftime;
2687 struct stat sb;
2688 int pstyle, fstyle;
2689 asl_out_file_list_t *out, *x, *y;
2690
2691 #ifdef DEBUG_LIST_FILES
2692 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");
2693 #endif
2694
2695 if (dir == NULL) return NULL;
2696 if (base == NULL) return NULL;
2697
2698 out = NULL;
2699
2700 d = opendir(dir);
2701 if (d == NULL) return NULL;
2702
2703 while (NULL != (ent = readdir(d)))
2704 {
2705 char *stamp = NULL;
2706 bool check;
2707 bool stat_ok = false;
2708
2709 check = _check_file_name(ent->d_name, base, ext, flags, src, &stamp);
2710 if (!check) continue;
2711
2712 seq = IndexNull;
2713 ftime = 0;
2714
2715 pstyle = _parse_stamp_style(stamp, flags, &seq, &ftime);
2716 free(stamp);
2717
2718 if (pstyle == STAMP_STYLE_INVALID) continue;
2719
2720 snprintf(path, sizeof(path), "%s/%s", dir, ent->d_name);
2721 memset(&sb, 0, sizeof(sb));
2722 if (lstat(path, &sb) == 0)
2723 {
2724 /* ignore symlinks (created for basestamp / symlink files) */
2725 stat_ok = true;
2726 if ((sb.st_mode & S_IFMT) == S_IFLNK) continue;
2727 }
2728
2729 fstyle = STAMP_STYLE_NULL;
2730 if (flags & MODULE_NAME_STYLE_STAMP_SEC) fstyle = STAMP_STYLE_SEC;
2731 else if (flags & MODULE_NAME_STYLE_STAMP_SEQ) fstyle = STAMP_STYLE_SEQ;
2732 else if ((flags & MODULE_NAME_STYLE_STAMP_UTC) || (flags & MODULE_NAME_STYLE_STAMP_LCL)) fstyle = STAMP_STYLE_ISO8601;
2733 else if ((flags & MODULE_NAME_STYLE_STAMP_UTC_B) || (flags & MODULE_NAME_STYLE_STAMP_LCL_B)) fstyle = STAMP_STYLE_ISO8601;
2734
2735 #ifdef DEBUG_LIST_FILES
2736 fprintf(stderr, "%s %s %u fstyle %u pstyle %u\n", __func__, path, __LINE__, fstyle, pstyle);
2737 #endif
2738
2739 /*
2740 * accept the file if:
2741 * style is STAMP_STYLE_NULL (no timestamp)
2742 * src is true and style is STAMP_STYLE_SEC
2743 * actual style matches the style implied by the input flags
2744 */
2745
2746 check = false;
2747 if (pstyle == STAMP_STYLE_NULL) check = true;
2748 if ((pstyle == STAMP_STYLE_SEC) && src) check = true;
2749 if (pstyle == fstyle) check = true;
2750
2751 if (!check)
2752 {
2753 #ifdef DEBUG_LIST_FILES
2754 fprintf(stderr, "%s reject %s at line %u\n", __func__, path, __LINE__);
2755 #endif
2756 continue;
2757 }
2758
2759 x = (asl_out_file_list_t *)calloc(1, sizeof(asl_out_file_list_t));
2760 if (x == NULL)
2761 {
2762 asl_out_file_list_free(out);
2763 return NULL;
2764 }
2765
2766 x->name = strdup(ent->d_name);
2767 x->stamp = pstyle;
2768 x->ftime = ftime;
2769 x->seq = seq;
2770
2771 if (stat_ok)
2772 {
2773 x->size = sb.st_size;
2774 if (pstyle == STAMP_STYLE_SEQ)
2775 {
2776 x->ftime = sb.st_birthtimespec.tv_sec;
2777 if (x->ftime == 0) x->ftime = sb.st_mtimespec.tv_sec;
2778 }
2779 }
2780
2781 if (pstyle == STAMP_STYLE_SEQ)
2782 {
2783 #ifdef DEBUG_LIST_FILES
2784 fprintf(stderr, "asl_list_log_files SEQ %s %u %ld\n", path, x->seq, x->ftime);
2785 #endif
2786 if (out == NULL)
2787 {
2788 out = x;
2789 }
2790 else if ((x->seq == IndexNull) || ((x->seq > out->seq) && (out->seq != IndexNull)))
2791 {
2792 x->next = out;
2793 out->prev = x;
2794 out = x;
2795 }
2796 else
2797 {
2798 for (y = out; y != NULL; y = y->next)
2799 {
2800 if (y->next == NULL)
2801 {
2802 y->next = x;
2803 x->prev = y;
2804 break;
2805 }
2806 else if ((x->seq > y->next->seq) && (y->next->seq != IndexNull))
2807 {
2808 x->next = y->next;
2809 y->next = x;
2810 x->prev = y;
2811 x->next->prev = x;
2812 break;
2813 }
2814 }
2815 }
2816 }
2817 else
2818 {
2819 #ifdef DEBUG_LIST_FILES
2820 fprintf(stderr, "asl_list_log_files TIME %s %ld\n", path, x->ftime);
2821 #endif
2822 if (out == NULL)
2823 {
2824 out = x;
2825 }
2826 else if (x->ftime < out->ftime)
2827 {
2828 x->next = out;
2829 out->prev = x;
2830 out = x;
2831 }
2832 else
2833 {
2834 for (y = out; y != NULL; y = y->next)
2835 {
2836 if (y->next == NULL)
2837 {
2838 y->next = x;
2839 x->prev = y;
2840 break;
2841 }
2842 else if (x->ftime < y->next->ftime)
2843 {
2844 x->next = y->next;
2845 y->next = x;
2846 x->prev = y;
2847 x->next->prev = x;
2848 break;
2849 }
2850 }
2851 }
2852 }
2853 }
2854
2855 closedir(d);
2856 return out;
2857 }
2858
2859 /*
2860 * List the source files for an output asl_out_dst_data_t
2861 */
2862 asl_out_file_list_t *
2863 asl_list_src_files(asl_out_dst_data_t *dst)
2864 {
2865 uint32_t flags;
2866 #ifdef DEBUG_LIST_FILES
2867 fprintf(stderr, "asl_list_src_files\n");
2868 #endif
2869
2870 if (dst == NULL) return NULL;
2871 if (dst->path == NULL) return NULL;
2872 if (dst->base == NULL) return NULL;
2873
2874 /*
2875 * MODULE_FLAG_EXTERNAL means some process other than syslogd writes the file.
2876 * We check for its existence, and that it is non-zero in size.
2877 */
2878 if (dst->flags & MODULE_FLAG_EXTERNAL)
2879 {
2880 struct stat sb;
2881
2882 memset(&sb, 0, sizeof(struct stat));
2883
2884 if (stat(dst->path, &sb) == 0)
2885 {
2886 if (S_ISREG(sb.st_mode) && (sb.st_size != 0))
2887 {
2888 asl_out_file_list_t *out = (asl_out_file_list_t *)calloc(1, sizeof(asl_out_file_list_t));
2889 if (out != NULL)
2890 {
2891 char *p = strrchr(dst->path, '/');
2892 if (p == NULL) p = dst->path;
2893 else p++;
2894 out->name = strdup(p);
2895 out->ftime = sb.st_birthtimespec.tv_sec;
2896 if (out->ftime == 0) out->ftime = sb.st_mtimespec.tv_sec;
2897 return out;
2898 }
2899 }
2900 }
2901
2902 return NULL;
2903 }
2904
2905 if ((dst->rotate_dir == NULL) && (dst->flags & MODULE_FLAG_BASESTAMP) && ((dst->flags & MODULE_FLAG_COMPRESS) == 0))
2906 {
2907 /* files do not move to a dest dir, get renamed, or get compressed - nothing to do */
2908 return NULL;
2909 }
2910
2911 /*
2912 * MODULE_NAME_STYLE_STAMP_SEC format is used for sequenced (MODULE_NAME_STYLE_STAMP_SEQ) files.
2913 * aslmanager converts the file names.
2914 */
2915 flags = dst->style_flags;
2916 if (flags & MODULE_NAME_STYLE_STAMP_SEQ)
2917 {
2918 flags &= ~MODULE_NAME_STYLE_STAMP_SEQ;
2919 flags |= MODULE_NAME_STYLE_STAMP_SEC;
2920 }
2921
2922 return asl_list_log_files(dst->dir, dst->base, dst->ext, flags, true);
2923 }
2924
2925 /*
2926 * List the destination files for an output asl_out_dst_data_t
2927 */
2928 asl_out_file_list_t *
2929 asl_list_dst_files(asl_out_dst_data_t *dst)
2930 {
2931 char *dst_dir;
2932 #ifdef DEBUG_LIST_FILES
2933 fprintf(stderr, "asl_list_dst_files\n");
2934 #endif
2935
2936 if (dst == NULL) return NULL;
2937 if (dst->path == NULL) return NULL;
2938 if (dst->base == NULL) return NULL;
2939
2940 dst_dir = dst->rotate_dir;
2941 if (dst_dir == NULL) dst_dir = dst->dir;
2942
2943 return asl_list_log_files(dst_dir, dst->base, dst->ext, dst->style_flags, false);
2944 }
2945
2946 static int
2947 asl_secure_open_dir(const char *path)
2948 {
2949 int fd, i;
2950 char **path_parts;
2951
2952 if (path == NULL) return -1;
2953 if (path[0] != '/') return -1;
2954
2955 path_parts = explode(path + 1, "/");
2956 if (path_parts == NULL) return 0;
2957
2958 fd = open("/", O_RDONLY | O_NOFOLLOW, 0);
2959 if (fd < 0)
2960 {
2961 free_string_list(path_parts);
2962 return -1;
2963 }
2964
2965 for (i = 0; path_parts[i] != NULL; i++)
2966 {
2967 int fd_next, status;
2968 struct stat sb;
2969
2970 fd_next = openat(fd, path_parts[i], O_RDONLY | O_NOFOLLOW, 0);
2971 close(fd);
2972 fd = fd_next;
2973 if (fd < 0)
2974 {
2975 free_string_list(path_parts);
2976 return -1;
2977 }
2978
2979 memset(&sb, 0, sizeof(sb));
2980
2981 status = fstat(fd, &sb);
2982 if (status < 0)
2983 {
2984 free_string_list(path_parts);
2985 return -1;
2986 }
2987
2988 if (!S_ISDIR(sb.st_mode))
2989 {
2990 free_string_list(path_parts);
2991 return -1;
2992 }
2993 }
2994
2995 free_string_list(path_parts);
2996 return fd;
2997 }
2998
2999 int
3000 asl_secure_chown_chmod_dir(const char *path, uid_t uid, gid_t gid, mode_t mode)
3001 {
3002 int fd, status;
3003
3004 fd = asl_secure_open_dir(path);
3005 if (fd < 0) return fd;
3006
3007 status = fchown(fd, uid, gid);
3008 if (status < 0)
3009 {
3010 close(fd);
3011 return -1;
3012 }
3013
3014 if (mode >= 0) status = fchmod(fd, mode);
3015 close(fd);
3016
3017 if (status < 0) return -1;
3018 return 0;
3019 }