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