]> git.saurik.com Git - apple/syslog.git/blame - aslcommon/asl_common.c
syslog-267.tar.gz
[apple/syslog.git] / aslcommon / asl_common.c
CommitLineData
81582353
A
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>
f3df4c03 41#include <asl.h>
81582353
A
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
f3df4c03
A
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
81582353
A
55#if !TARGET_IPHONE_SIMULATOR
56#define _PATH_ASL_CONF_LOCAL_DIR "/usr/local/etc/asl"
57#endif
58
59static 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 ",
f3df4c03
A
70 "set ",
71 "unset ",
81582353
A
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
81582353
A
83#define forever for(;;)
84#define KEYMATCH(S,K) ((strncasecmp(S, K, strlen(K)) == 0))
85
f3df4c03
A
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
81582353
A
92asl_msg_t *
93xpc_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
187asl_msg_t *
188configuration_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 */
197static 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
228static 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
278char **
279explode(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
327void
328free_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
337char *
338get_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
357char *
358next_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
f3df4c03
A
446static 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 */
474static 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
81582353 581int
f3df4c03 582asl_out_mkpath(asl_out_module_t *mlist, asl_out_rule_t *r)
81582353
A
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 {
f3df4c03
A
605 if (S_ISDIR(sb.st_mode)) return 0;
606 return -1;
81582353 607 }
f3df4c03
A
608
609 if (errno == ENOENT)
81582353 610 {
f3df4c03
A
611 uint32_t dirflag = r->dst->flags & MODULE_FLAG_NONSTD_DIR;
612 status = _asl_common_make_dir_path(mlist, dirflag, tmp);
613 return status;
81582353
A
614 }
615
f3df4c03 616 return -1;
81582353
A
617}
618
619void
620asl_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
681void
682asl_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
f3df4c03 687 if (dst->flags & (MODULE_FLAG_BASESTAMP | MODULE_FLAG_TYPE_ASL_DIR))
81582353
A
688 {
689 char tstamp[32];
f3df4c03
A
690 const char *name = dst->path;
691
692 if (dst->flags & MODULE_FLAG_TYPE_ASL_DIR) name = dst->fname;
81582353
A
693
694 if (dst->stamp == 0) dst->stamp = time(NULL);
695 asl_make_timestamp(dst->stamp, dst->flags, tstamp, sizeof(tstamp));
f3df4c03 696 snprintf(buf, len, "%s.%s", name, tstamp);
81582353
A
697 }
698 else
699 {
700 snprintf(buf, len, "%s", dst->path);
701 }
702}
703
704int
f3df4c03 705asl_check_option(asl_msg_t *msg, const char *opt)
81582353
A
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
f3df4c03 716 p = asl_msg_get_val_for_key(msg, ASL_KEY_OPTION);
81582353
A
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
736void
737asl_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
755asl_out_dst_data_t *
756asl_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 */
764int
765asl_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 */
f3df4c03 805 if ((dst->gid[i] == fgid) && (dst->mode & 00040)) continue;
81582353
A
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 */
f3df4c03 838 if ((dst->uid[i] == fuid) && (dst->mode & 00400)) continue;
81582353
A
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
870asl_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 */
879int
f3df4c03 880asl_out_dst_file_create_open(asl_out_dst_data_t *dst, char **pathp)
81582353
A
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));
f3df4c03
A
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);
81582353
A
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
f3df4c03 919 fd = open(outpath, O_RDWR | O_CREAT | O_EXCL, (dst->mode & 00666));
81582353
A
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
930void
931asl_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
961asl_out_module_t *
962asl_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 */
982static 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 */
1042static 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
1126static 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
1152static 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
1179static 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
1207size_t
1208asl_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
1229static 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
1242static 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;
f3df4c03 1250 uint32_t i, flags = 0;
81582353
A
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);
f3df4c03 1308 path_parts = NULL;
81582353 1309
f3df4c03 1310 if ((did_sub == 1) && (has_dotdot == 0))
81582353
A
1311 {
1312 /* substitution might have added a ".." so check the new path */
1313 free(path);
f3df4c03 1314 path = asl_string_release_return_bytes(processed_path);
81582353
A
1315 processed_path = asl_string_new(ASL_ENCODE_NONE);
1316 path_parts = explode(path, "/");
1317 recursion_limit--;
1318 }
1319 }
1320
1321 free(path);
f3df4c03
A
1322 free_string_list(path_parts);
1323 path_parts = NULL;
81582353
A
1324
1325 if ((has_dotdot != 0) || (recursion_limit == 0))
1326 {
f3df4c03 1327 asl_string_release(processed_path);
81582353
A
1328 return NULL;
1329 }
1330
f3df4c03 1331 path = asl_string_release_return_bytes(processed_path);
81582353
A
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
f3df4c03
A
1348 flags |= MODULE_FLAG_NONSTD_DIR;
1349
81582353
A
1350 if (path[0] != '/')
1351 {
1352 char *t = path;
1353 const char *log_root = "/var/log";
1354
1355#if TARGET_IPHONE_SIMULATOR
f3df4c03
A
1356 log_root = getenv("SIMULATOR_LOG_ROOT");
1357 if (log_root == NULL) log_root = "/tmp/log";
81582353
A
1358#endif
1359
f3df4c03
A
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 }
81582353
A
1368
1369 free(t);
f3df4c03
A
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;
81582353
A
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;
f3df4c03
A
1395 dst->ttl[LEVEL_ALL] = DEFAULT_TTL;
1396 dst->flags = flags | MODULE_FLAG_COALESCE;
81582353
A
1397
1398 while (NULL != (p = next_word_from_string(&opts)))
1399 {
1400 if (KEYMATCH(p, "mode=")) dst->mode = strtol(p+5, NULL, 0);
81582353
A
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;
f3df4c03
A
1417 else if (KEYMATCH(p, "truncate")) dst->flags |= MODULE_FLAG_TRUNCATE;
1418 else if (KEYMATCH(p, "dir")) dst->flags |= MODULE_FLAG_TYPE_ASL_DIR;
81582353
A
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 }
f3df4c03
A
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 }
81582353
A
1481
1482 free(p);
1483 p = NULL;
1484 }
1485
1486#if TARGET_OS_EMBEDDED
1487 /* check for crashreporter files */
f3df4c03 1488 if ((KEYMATCH(dst->path, _PATH_CRASHREPORTER)) || (KEYMATCH(dst->path, _PATH_CRASHREPORTER_MOBILE)))
81582353
A
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
f3df4c03
A
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
81582353
A
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 */
f3df4c03 1507 if (!strcmp(dst->fmt, "std") || !strcmp(dst->fmt, "bsd") || !strcmp(dst->fmt, "msg")) dst->flags |= MODULE_FLAG_STD_BSD_MSG;
81582353
A
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
f3df4c03
A
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
81582353
A
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
1535static 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;
f3df4c03
A
1570 else if (!strcasecmp(act, "set")) out->action = ACTION_SET_KEY;
1571 else if (!strcasecmp(act, "unset")) out->action = ACTION_UNSET_KEY;
81582353
A
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 */
f3df4c03
A
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 }
81582353
A
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
f3df4c03
A
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
81582353
A
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 {
81582353
A
1654 out->dst->flags |= MODULE_FLAG_TYPE_ASL;
1655 }
1656
1657 if (out->action == ACTION_ASL_DIR)
1658 {
f3df4c03 1659 /* coalesce is meaningless for ASL directories */
81582353 1660 out->dst->flags &= ~MODULE_FLAG_COALESCE;
f3df4c03
A
1661
1662 /* no compression at this point */
1663 out->dst->flags &= ~MODULE_FLAG_COMPRESS;
1664
81582353 1665 out->dst->flags |= MODULE_FLAG_TYPE_ASL_DIR;
f3df4c03
A
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;
81582353
A
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
1693asl_out_rule_t *
1694asl_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 {
f3df4c03 1708 _asl_out_module_parse_dst(m, s + 1, 010000);
81582353
A
1709 }
1710
1711 return NULL;
1712}
1713
1714asl_out_module_t *
1715asl_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
1735static 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
1751static 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
1816asl_out_module_t *
1817asl_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 */
1872char *
1873asl_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 */
1900void
1901asl_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;
f3df4c03 1905 uint32_t i, ttlnset;
81582353
A
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 }
f3df4c03 1940 if (o->flags & MODULE_FLAG_SOFT_WRITE)
81582353 1941 {
f3df4c03 1942 fprintf(f, "%csoft", c);
81582353
A
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 }
81582353
A
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 }
f3df4c03 1995 if (o->flags & MODULE_FLAG_NONSTD_DIR)
81582353 1996 {
f3df4c03 1997 fprintf(f, "%cnon-std_dir", c);
81582353
A
1998 c = ' ';
1999 }
f3df4c03 2000 if (o->flags & MODULE_FLAG_EXTERNAL)
81582353 2001 {
f3df4c03
A
2002 fprintf(f, "%cexternal", c);
2003 c = ' ';
2004 }
2005 if (o->flags & MODULE_FLAG_CRASHLOG)
2006 {
2007 fprintf(f, "%ccrashlog", c);
81582353
A
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 }
81582353
A
2020 fprintf(f, ")");
2021 }
2022 fprintf(f, "\n");
2023
f3df4c03
A
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
81582353
A
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
2050void
2051asl_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 */
2072bool
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 */
f3df4c03
A
2163int
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
81582353
A
2261asl_out_file_list_t *
2262asl_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;
f3df4c03 2270 int pstyle, fstyle;
81582353
A
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
81582353
A
2291 seq = IndexNull;
2292 ftime = 0;
2293
f3df4c03
A
2294 pstyle = _parse_stamp_style(stamp, flags, &seq, &ftime);
2295 free(stamp);
81582353 2296
f3df4c03 2297 if (pstyle == STAMP_STYLE_INVALID) continue;
81582353 2298
f3df4c03
A
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;
81582353 2304
f3df4c03
A
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 */
81582353 2311
f3df4c03
A
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;
81582353 2316
f3df4c03 2317 if (!check) continue;
81582353
A
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;
f3df4c03 2335 if (pstyle == STAMP_STYLE_SEQ)
81582353
A
2336 {
2337 x->ftime = sb.st_birthtimespec.tv_sec;
2338 if (x->ftime == 0) x->ftime = sb.st_mtimespec.tv_sec;
2339 }
2340 }
2341
f3df4c03 2342 if (pstyle == STAMP_STYLE_SEQ)
81582353
A
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 */
2417asl_out_file_list_t *
2418asl_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.
f3df4c03 2429 * We check for its existence, and that it is non-zero in size.
81582353
A
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 {
f3df4c03 2439 if (S_ISREG(sb.st_mode) && (sb.st_size != 0))
81582353
A
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 */
2497asl_out_file_list_t *
2498asl_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}
f3df4c03
A
2521
2522static int
2523asl_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
2575int
2576asl_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}