]> git.saurik.com Git - apple/syslog.git/blob - syslogd.tproj/asl_action.c
64715c8e3b3981779124da1ba1490a2fa5ae49d2
[apple/syslog.git] / syslogd.tproj / asl_action.c
1 /*
2 * Copyright (c) 2004-2011 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 <sys/types.h>
25 #include <sys/stat.h>
26 #include <sys/socket.h>
27 #include <sys/un.h>
28 #include <sys/uio.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <fcntl.h>
34 #include <errno.h>
35 #include <netdb.h>
36 #include <notify.h>
37 #include <pthread.h>
38 #include <sys/acl.h>
39 #include <membership.h>
40 #include "daemon.h"
41 #include <dispatch/private.h>
42
43 #define _PATH_WALL "/usr/bin/wall"
44 #define _PATH_ASL_CONF "/etc/asl.conf"
45 #define MY_ID "asl_action"
46
47 #define MAX_FAILURES 5
48
49 #define ACTION_NONE 0
50 #define ACTION_IGNORE 1
51 #define ACTION_NOTIFY 2
52 #define ACTION_BROADCAST 3
53 #define ACTION_ACCESS 4
54 #define ACTION_ASL_STORE 5 /* Save in main ASL Database */
55 #define ACTION_ASL_FILE 6 /* Save in an ASL format data file */
56 #define ACTION_ASL_DIR 7 /* Save in an ASL directory */
57 #define ACTION_FILE 8
58 #define ACTION_FORWARD 9
59
60 #define forever for(;;)
61
62 #define ACT_FLAG_HAS_LOGGED 0x80000000
63 #define ACT_FLAG_CLEAR_LOGGED 0x7fffffff
64
65 #define ACT_STORE_FLAG_STAY_OPEN 0x00000001
66 #define ACT_STORE_FLAG_CONTINUE 0x00000002
67
68 #define ACT_FILE_FLAG_DUP_SUPRESS 0x00000001
69 #define ACT_FILE_FLAG_ROTATE 0x00000002
70
71 static dispatch_queue_t asl_action_queue;
72 static time_t last_file_day;
73
74 typedef struct action_rule_s
75 {
76 asl_msg_t *query;
77 int action;
78 char *options;
79 void *data;
80 struct action_rule_s *next;
81 } action_rule_t;
82
83 struct store_data
84 {
85 asl_file_t *store;
86 FILE *storedata;
87 char *dir;
88 char *path;
89 mode_t mode;
90 uid_t uid;
91 gid_t gid;
92 uint64_t next_id;
93 uint32_t fails;
94 uint32_t flags;
95 uint32_t refcount;
96 uint32_t p_year;
97 uint32_t p_month;
98 uint32_t p_day;
99 };
100
101 struct file_data
102 {
103 int fd;
104 char *path;
105 char *fmt;
106 const char *tfmt;
107 mode_t mode;
108 uid_t *uid;
109 uint32_t nuid;
110 gid_t *gid;
111 uint32_t ngid;
112 size_t max_size;
113 uint32_t fails;
114 uint32_t flags;
115 uint32_t refcount;
116 time_t stamp;
117 uint32_t last_hash;
118 uint32_t last_count;
119 time_t last_time;
120 dispatch_source_t dup_timer;
121 char *last_msg;
122 };
123
124 static action_rule_t *asl_action_rule = NULL;
125 static action_rule_t *asl_datastore_rule = NULL;
126
127 static int _parse_config_file(const char *);
128 extern void db_save_message(aslmsg m);
129
130 /* forward */
131 int _act_file_open(struct file_data *fdata);
132 static void _act_file_init(action_rule_t *r);
133 static void _act_store_init(action_rule_t *r);
134
135 static char *
136 _next_word(char **s)
137 {
138 char *a, *p, *e, *out;
139 int quote, len;
140
141 if (s == NULL) return NULL;
142 if (*s == NULL) return NULL;
143
144 quote = 0;
145
146 p = *s;
147 a = p;
148 e = p;
149
150 while (*p != '\0')
151 {
152 if (*p == '\\')
153 {
154 p++;
155 e = p;
156
157 if (*p == '\0')
158 {
159 p--;
160 break;
161 }
162
163 p++;
164 e = p;
165 continue;
166 }
167
168 if (*p == '"')
169 {
170 if (quote == 0) quote = 1;
171 else quote = 0;
172 }
173
174 if (((*p == ' ') || (*p == '\t')) && (quote == 0))
175 {
176 e = p + 1;
177 break;
178 }
179
180 p++;
181 e = p;
182 }
183
184 *s = e;
185
186 len = p - a;
187 if (len == 0) return NULL;
188
189 out = malloc(len + 1);
190 if (out == NULL) return NULL;
191
192 memcpy(out, a, len);
193 out[len] = '\0';
194 return out;
195 }
196
197 /*
198 * Config File format:
199 * Set parameter rule - initializes a parameter.
200 * = param args...
201 * Query rule - if a message matches the query, then the action is invoked.
202 * The rule may be identified by either "?" or "Q".
203 * ? [k v] [k v] ... action args...
204 * Q [k v] [k v] ... action args...
205 * Universal match rule - the action is invoked for all messages
206 * * action args...
207 */
208
209 /* Skip over query */
210 static char *
211 _find_action(char *s)
212 {
213 char *p;
214
215 p = s;
216 if (p == NULL) return NULL;
217 if ((*p != 'Q') && (*p != '?') && (*p != '*')) return NULL;
218
219 p++;
220
221 forever
222 {
223 /* Find next [ */
224 while ((*p == ' ') || (*p == '\t')) p++;
225
226 if (*p == '\0') return NULL;
227 if (*p != '[') return p;
228
229 /* skip to closing ] */
230 while (*p != ']')
231 {
232 p++;
233 if (*p == '\\')
234 {
235 p++;
236 if (*p == ']') p++;
237 }
238 }
239
240 if (*p == ']') p++;
241 }
242
243 return NULL;
244 }
245
246 static int
247 _parse_query_action(char *s)
248 {
249 char *act, *p;
250 action_rule_t *out, *rule;
251
252 act = _find_action(s);
253 if (act == NULL) return -1;
254
255 out = (action_rule_t *)calloc(1, sizeof(action_rule_t));
256 if (out == NULL) return -1;
257
258 p = strchr(act, ' ');
259 if (p != NULL) *p = '\0';
260
261 if (!strcasecmp(act, "ignore")) out->action = ACTION_IGNORE;
262 else if (!strcasecmp(act, "notify")) out->action = ACTION_NOTIFY;
263 else if (!strcasecmp(act, "broadcast")) out->action = ACTION_BROADCAST;
264 else if (!strcasecmp(act, "access")) out->action = ACTION_ACCESS;
265 else if (!strcasecmp(act, "store")) out->action = ACTION_ASL_STORE;
266 else if (!strcasecmp(act, "save")) out->action = ACTION_ASL_STORE;
267 else if (!strcasecmp(act, "store_file")) out->action = ACTION_ASL_FILE;
268 else if (!strcasecmp(act, "store_directory")) out->action = ACTION_ASL_DIR;
269 else if (!strcasecmp(act, "store_dir")) out->action = ACTION_ASL_DIR;
270 else if (!strcasecmp(act, "file")) out->action = ACTION_FILE;
271 else if (!strcasecmp(act, "forward")) out->action = ACTION_FORWARD;
272
273 if (p != NULL)
274 {
275 out->options = strdup(p+1);
276
277 if (out->options == NULL)
278 {
279 free(out);
280 return -1;
281 }
282 }
283
284 p = act - 1;
285
286 *p = '\0';
287
288 if (s[0] == '*') out->query = asl_msg_new(ASL_TYPE_QUERY);
289 else
290 {
291 s[0] = 'Q';
292 out->query = asl_msg_from_string(s);
293 }
294
295 if (out->query == NULL)
296 {
297 asldebug("out->query is NULL (ERROR)\n");
298 free(out->options);
299 free(out);
300 return -1;
301 }
302
303 /* store /some/path means save to a file */
304 if ((out->action == ACTION_ASL_STORE) && (out->options != NULL)) out->action = ACTION_ASL_FILE;
305
306 if (out->action == ACTION_FILE) _act_file_init(out);
307 else if ((out->action == ACTION_ASL_FILE) || (out->action == ACTION_ASL_DIR)) _act_store_init(out);
308
309 if (out->action == ACTION_ASL_STORE)
310 {
311 asldebug("action = ACTION_ASL_STORE\n");
312 if (asl_datastore_rule == NULL) asl_datastore_rule = out;
313 else
314 {
315 for (rule = asl_datastore_rule; rule->next != NULL; rule = rule->next);
316 rule->next = out;
317 }
318 }
319 else
320 {
321 asldebug("action = %d options = %s\n", out->action, out->options);
322 if (asl_action_rule == NULL) asl_action_rule = out;
323 else
324 {
325 for (rule = asl_action_rule; rule->next != NULL; rule = rule->next);
326 rule->next = out;
327 }
328 }
329
330 return 0;
331 }
332
333 static int
334 _parse_line(char *s)
335 {
336 char *str;
337 int status;
338
339 if (s == NULL) return -1;
340 while ((*s == ' ') || (*s == '\t')) s++;
341
342 /* First non-whitespace char is the rule type */
343 switch (*s)
344 {
345 case '\0':
346 case '#':
347 {
348 /* Blank Line or Comment */
349 return 0;
350 }
351 case 'Q':
352 case '?':
353 case '*':
354 {
355 /* Query-match action */
356 status = _parse_query_action(s);
357 break;
358 }
359 case '=':
360 {
361 /* Set parameter */
362 status = control_set_param(s);
363 break;
364 }
365 default:
366 {
367 status = -1;
368 break;
369 }
370 }
371
372 if (status != 0)
373 {
374 str = NULL;
375 asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [%s Ignoring unrecognized entry in %s: %s] [%s 0] [%s 0] [Facility syslog]",
376 ASL_KEY_SENDER,
377 ASL_KEY_LEVEL, ASL_LEVEL_ERR,
378 ASL_KEY_PID, getpid(),
379 ASL_KEY_MSG, _PATH_ASL_CONF, s,
380 ASL_KEY_UID, ASL_KEY_GID);
381
382 internal_log_message(str);
383 free(str);
384 }
385
386 return status;
387 }
388
389 static void
390 _act_notify(action_rule_t *r)
391 {
392 if (r == NULL) return;
393 if (r->options == NULL) return;
394
395 notify_post(r->options);
396 }
397
398 static void
399 _act_broadcast(action_rule_t *r, aslmsg msg)
400 {
401 #ifndef CONFIG_IPHONE
402 FILE *pw;
403 const char *val;
404
405 if (r == NULL) return;
406 if (msg == NULL) return;
407
408 val = r->options;
409 if (val == NULL) val = asl_get(msg, ASL_KEY_MSG);
410 if (val == NULL) return;
411
412 pw = popen(_PATH_WALL, "w");
413 if (pw < 0)
414 {
415 asldebug("%s: error sending wall message: %s\n", MY_ID, strerror(errno));
416 return;
417 }
418
419 fprintf(pw, "%s", val);
420 pclose(pw);
421 #endif
422 }
423
424 static void
425 _act_access_control(action_rule_t *r, aslmsg msg)
426 {
427 int32_t ruid, rgid;
428 char *p;
429
430 ruid = atoi(r->options);
431 rgid = -1;
432 p = strchr(r->options, ' ');
433 if (p == NULL) p = strchr(r->options, '\t');
434 if (p != NULL)
435 {
436 *p = '\0';
437 p++;
438 rgid = atoi(p);
439 }
440
441 if (ruid != -1) asl_set(msg, ASL_KEY_READ_UID, r->options);
442 if (p != NULL)
443 {
444 if (rgid != -1) asl_set(msg, ASL_KEY_READ_GID, p);
445 p--;
446 *p = ' ';
447 }
448 }
449
450 static uint32_t
451 _act_store_file_setup(struct store_data *sd)
452 {
453 uint32_t status;
454
455 if (sd == NULL) return ASL_STATUS_INVALID_STORE;
456 if (sd->store == NULL) return ASL_STATUS_INVALID_STORE;
457 if (sd->store->store == NULL) return ASL_STATUS_INVALID_STORE;
458
459 status = asl_file_read_set_position(sd->store, ASL_FILE_POSITION_LAST);
460 if (status != ASL_STATUS_OK) return status;
461
462 sd->next_id = sd->store->cursor_xid + 1;
463 if (fseek(sd->store->store, 0, SEEK_END) != 0) return ASL_STATUS_ACCESS_DENIED;
464
465 return ASL_STATUS_OK;
466 }
467
468 static uint32_t
469 _act_store_dir_setup(struct store_data *sd, time_t tick)
470 {
471 struct tm ctm;
472 char *path;
473 struct stat sb;
474 uint64_t xid;
475 int status;
476 mode_t mask;
477
478 if (sd == NULL) return ASL_STATUS_INVALID_STORE;
479 if (sd->dir == NULL) return ASL_STATUS_INVALID_STORE;
480
481 /* get / set message id from StoreData file */
482 xid = 0;
483
484 if (sd->storedata == NULL)
485 {
486 memset(&sb, 0, sizeof(struct stat));
487 status = stat(sd->dir, &sb);
488 if (status == 0)
489 {
490 /* must be a directory */
491 if (!S_ISDIR(sb.st_mode)) return ASL_STATUS_INVALID_STORE;
492 }
493 else if (errno == ENOENT)
494 {
495 /* doesn't exist - create it */
496 mask = umask(0);
497 status = mkdir(sd->dir, sd->mode);
498 umask(mask);
499
500 if (status != 0) return ASL_STATUS_WRITE_FAILED;
501 if (chown(sd->dir, sd->uid, sd->gid) != 0) return ASL_STATUS_WRITE_FAILED;
502 }
503 else
504 {
505 /* Unexpected stat error */
506 return ASL_STATUS_FAILED;
507 }
508
509 path = NULL;
510 asprintf(&path, "%s/%s", sd->dir, FILE_ASL_STORE_DATA);
511 if (path == NULL) return ASL_STATUS_NO_MEMORY;
512
513 memset(&sb, 0, sizeof(struct stat));
514 status = stat(path, &sb);
515 if (status == 0)
516 {
517 /* StoreData exists: open and read last xid */
518 sd->storedata = fopen(path, "r+");
519 if (sd->storedata == NULL)
520 {
521 free(path);
522 return ASL_STATUS_FAILED;
523 }
524
525 if (fread(&xid, sizeof(uint64_t), 1, sd->storedata) != 1)
526 {
527 free(path);
528 fclose(sd->storedata);
529 sd->storedata = NULL;
530 return ASL_STATUS_READ_FAILED;
531 }
532 }
533 else if (errno == ENOENT)
534 {
535 /* StoreData does not exist: create it */
536 sd->storedata = fopen(path, "w");
537 if (sd->storedata == NULL)
538 {
539 free(path);
540 return ASL_STATUS_FAILED;
541 }
542
543 if (chown(path, sd->uid, sd->gid) != 0)
544 {
545 free(path);
546 return ASL_STATUS_WRITE_FAILED;
547 }
548 }
549 else
550 {
551 /* Unexpected stat error */
552 free(path);
553 return ASL_STATUS_FAILED;
554 }
555
556 free(path);
557 }
558 else
559 {
560 rewind(sd->storedata);
561 if (fread(&xid, sizeof(uint64_t), 1, sd->storedata) != 1)
562 {
563 fclose(sd->storedata);
564 sd->storedata = NULL;
565 return ASL_STATUS_READ_FAILED;
566 }
567 }
568
569 xid = asl_core_ntohq(xid);
570 xid++;
571 sd->next_id = xid;
572
573 xid = asl_core_htonq(xid);
574 rewind(sd->storedata);
575 status = fwrite(&xid, sizeof(uint64_t), 1, sd->storedata);
576 if (status != 1)
577 {
578 fclose(sd->storedata);
579 sd->storedata = NULL;
580 return ASL_STATUS_WRITE_FAILED;
581 }
582
583 if ((sd->flags & ACT_STORE_FLAG_STAY_OPEN) == 0)
584 {
585 fclose(sd->storedata);
586 sd->storedata = NULL;
587 }
588
589 memset(&ctm, 0, sizeof(struct tm));
590
591 if (localtime_r((const time_t *)&tick, &ctm) == NULL) return ASL_STATUS_FAILED;
592 if ((sd->p_year == ctm.tm_year) && (sd->p_month == ctm.tm_mon) && (sd->p_day == ctm.tm_mday) && (sd->path != NULL)) return ASL_STATUS_OK;
593
594 if (sd->store != NULL) asl_file_close(sd->store);
595 sd->store = NULL;
596
597 sd->p_year = 0;
598 sd->p_month = 0;
599 sd->p_day = 0;
600
601 free(sd->path);
602 sd->path = NULL;
603
604 asprintf(&(sd->path), "%s/%d.%02d.%02d.asl", sd->dir, ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday);
605 if (sd->path == NULL) return ASL_STATUS_NO_MEMORY;
606
607 sd->p_year = ctm.tm_year;
608 sd->p_month = ctm.tm_mon;
609 sd->p_day = ctm.tm_mday;
610
611 return ASL_STATUS_OK;
612 }
613
614 static void
615 _act_store_init(action_rule_t *r)
616 {
617 struct store_data *sd, *xd;
618 char *str, *opts, *p, *path;
619 action_rule_t *x;
620
621 /* check if the store data is already set up */
622 if (r->data != NULL) return;
623
624 opts = r->options;
625 path = _next_word(&opts);
626
627 if ((path == NULL) || (path[0] != '/'))
628 {
629 str = NULL;
630 asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Invalid path for \"%s\" action: %s]",
631 ASL_KEY_SENDER,
632 ASL_KEY_LEVEL, ASL_LEVEL_ERR,
633 ASL_KEY_PID, getpid(),
634 ASL_KEY_MSG,
635 (r->action == ACTION_ASL_FILE) ? "store" : "store_directory",
636 (path == NULL) ? "no path specified" : path);
637
638 internal_log_message(str);
639 free(str);
640 r->action = ACTION_NONE;
641 free(path);
642 return;
643 }
644
645 /* check if a previous rule has set up this path (ACTION_ASL_FILE) or dir (ACTION_ASL_DIR) */
646 for (x = asl_action_rule; x != NULL; x = x->next)
647 {
648 if ((x->action == r->action) && (x->data != NULL))
649 {
650 xd = (struct store_data *)x->data;
651 p = xd->path;
652 if (r->action == ACTION_ASL_DIR) p = xd->dir;
653
654 if ((p != NULL) && (!strcmp(path, p)))
655 {
656 free(path);
657 xd->refcount++;
658 r->data = x->data;
659 return;
660 }
661 }
662 }
663
664 /* set up store data */
665 sd = (struct store_data *)calloc(1, sizeof(struct store_data));
666 if (sd == NULL) return;
667
668 sd->refcount = 1;
669 sd->mode = 0755;
670 sd->next_id = 0;
671 sd->uid = 0;
672 sd->gid = 0;
673 sd->flags = 0;
674
675 if (r->action == ACTION_ASL_DIR) sd->dir = path;
676 else sd->path = path;
677
678 while (NULL != (p = _next_word(&opts)))
679 {
680 if (!strcmp(p, "stayopen"))
681 {
682 sd->flags |= ACT_STORE_FLAG_STAY_OPEN;
683 }
684 else if (!strcmp(p, "continue"))
685 {
686 sd->flags |= ACT_STORE_FLAG_CONTINUE;
687 }
688 else if (!strncmp(p, "mode=", 5)) sd->mode = strtol(p+5, NULL, 0);
689 else if (!strncmp(p, "uid=", 4)) sd->uid = atoi(p+4);
690 else if (!strncmp(p, "gid=", 4)) sd->gid = atoi(p+4);
691
692 free(p);
693 p = NULL;
694 }
695
696 r->data = sd;
697 }
698
699 /*
700 * Save a message to an ASL format file (ACTION_ASL_FILE)
701 * or to an ASL directory (ACTION_ASL_DIR).
702 */
703 static void
704 _act_store(action_rule_t *r, aslmsg msg)
705 {
706 struct store_data *sd;
707 asl_file_t *s;
708 uint32_t status;
709 uint64_t mid;
710 mode_t mask;
711 char *str, *opts;
712 const char *val;
713 time_t tick;
714
715 s = NULL;
716
717 if (r->data == NULL) return;
718
719 sd = (struct store_data *)r->data;
720
721 if (sd->flags & ACT_FLAG_HAS_LOGGED) return;
722 sd->flags |= ACT_FLAG_HAS_LOGGED;
723
724 if (r->action == ACTION_ASL_DIR)
725 {
726 val = asl_get(msg, ASL_KEY_TIME);
727 if (val == NULL) return;
728
729 tick = atol(val);
730 status = _act_store_dir_setup(sd, tick);
731 if (status != ASL_STATUS_OK)
732 {
733 asldebug("_act_store_dir_setup %s failed: %s\n", sd->path, asl_core_error(status));
734
735 sd->fails++;
736
737 /* disable further activity after multiple failures */
738 if (sd->fails > MAX_FAILURES)
739 {
740 char *str = NULL;
741 asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Disabling writes to path %s following %u failures (%s)]",
742 ASL_KEY_SENDER,
743 ASL_KEY_LEVEL, ASL_LEVEL_ERR,
744 ASL_KEY_PID, getpid(),
745 ASL_KEY_MSG, sd->path, sd->fails, asl_core_error(status));
746
747 internal_log_message(str);
748 free(str);
749
750 asl_file_close(sd->store);
751 sd->store = NULL;
752 r->action = ACTION_NONE;
753 return;
754 }
755 }
756 else
757 {
758 sd->fails = 0;
759 }
760 }
761
762 if (sd->store == NULL)
763 {
764 s = NULL;
765
766 mask = umask(0);
767 status = asl_file_open_write(sd->path, (sd->mode & 0666), sd->uid, sd->gid, &s);
768 umask(mask);
769
770 if ((status != ASL_STATUS_OK) || (s == NULL))
771 {
772 asldebug("asl_file_open_write %s failed: %s\n", sd->path, asl_core_error(status));
773
774 sd->fails++;
775
776 /* disable further activity after multiple failures */
777 if (sd->fails > MAX_FAILURES)
778 {
779 char *str = NULL;
780 asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Disabling writes to path %s following %u failures (%s)]",
781 ASL_KEY_SENDER,
782 ASL_KEY_LEVEL, ASL_LEVEL_ERR,
783 ASL_KEY_PID, getpid(),
784 ASL_KEY_MSG, sd->path, sd->fails, asl_core_error(status));
785
786 internal_log_message(str);
787 free(str);
788
789 asl_file_close(sd->store);
790 sd->store = NULL;
791 r->action = ACTION_NONE;
792 return;
793 }
794 }
795 else if (status == ASL_STATUS_OK)
796 {
797 sd->fails = 0;
798 }
799
800 sd->store = s;
801 }
802
803 if (r->action != ACTION_ASL_DIR)
804 {
805 status = _act_store_file_setup(sd);
806 if (status != ASL_STATUS_OK)
807 {
808 asldebug("_act_store_file_setup %s failed: %s\n", sd->path, asl_core_error(status));
809
810 sd->fails++;
811
812 /* disable further activity after multiple failures */
813 if (sd->fails > MAX_FAILURES)
814 {
815 char *str = NULL;
816 asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Disabling writes to path %s following %u failures (%s)]",
817 ASL_KEY_SENDER,
818 ASL_KEY_LEVEL, ASL_LEVEL_ERR,
819 ASL_KEY_PID, getpid(),
820 ASL_KEY_MSG, sd->path, sd->fails, asl_core_error(status));
821
822 internal_log_message(str);
823 free(str);
824
825 asl_file_close(sd->store);
826 sd->store = NULL;
827 r->action = ACTION_NONE;
828 return;
829 }
830 }
831 else
832 {
833 sd->fails = 0;
834 }
835 }
836
837 mid = sd->next_id;
838
839 status = asl_file_save(sd->store, msg, &mid);
840 if (status != ASL_STATUS_OK)
841 {
842 asldebug("asl_file_save %s failed: %s\n", sd->path, asl_core_error(status));
843
844 sd->fails++;
845
846 /* disable further activity after multiple failures */
847 if (sd->fails > MAX_FAILURES)
848 {
849 char *str = NULL;
850 asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Disabling writes to path %s following %u failures (%s)]",
851 ASL_KEY_SENDER,
852 ASL_KEY_LEVEL, ASL_LEVEL_ERR,
853 ASL_KEY_PID, getpid(),
854 ASL_KEY_MSG, sd->path, sd->fails, asl_core_error(status));
855
856 internal_log_message(str);
857 free(str);
858
859 asl_file_close(sd->store);
860 sd->store = NULL;
861 r->action = ACTION_NONE;
862 return;
863 }
864 }
865 else
866 {
867 sd->fails = 0;
868 }
869
870 if ((sd->flags & ACT_STORE_FLAG_STAY_OPEN) == 0)
871 {
872 asl_file_close(sd->store);
873 sd->store = NULL;
874 }
875
876 if ((sd->flags & ACT_STORE_FLAG_CONTINUE) == 0)
877 {
878 opts = (char *)asl_get(msg, ASL_KEY_OPTION);
879 if (opts == NULL)
880 {
881 asl_set(msg, ASL_KEY_OPTION, ASL_OPT_IGNORE);
882 }
883 else
884 {
885 str = NULL;
886 asprintf(&str, "%s %s", ASL_OPT_IGNORE, opts);
887 if (str != NULL)
888 {
889 asl_set(msg, ASL_KEY_OPTION, str);
890 free(str);
891 }
892 }
893 }
894 }
895
896 static int
897 _act_file_send_repeat_msg(struct file_data *fdata)
898 {
899 char vt[32], *msg;
900 int len, status, closeit;
901 time_t now = time(NULL);
902
903 if (fdata == NULL) return -1;
904
905 free(fdata->last_msg);
906 fdata->last_msg = NULL;
907
908 if (fdata->last_count == 0) return 0;
909
910 /* stop the timer */
911 dispatch_suspend(fdata->dup_timer);
912
913 memset(vt, 0, sizeof(vt));
914 ctime_r(&now, vt);
915 vt[19] = '\0';
916
917 msg = NULL;
918 asprintf(&msg, "%s --- last message repeated %u time%s ---\n", vt + 4, fdata->last_count, (fdata->last_count == 1) ? "" : "s");
919 fdata->last_count = 0;
920 if (msg == NULL) return -1;
921
922 closeit = 0;
923 if (fdata->fd < 0)
924 {
925 closeit = 1;
926 fdata->fd = _act_file_open(fdata);
927 if (fdata->fd < 0)
928 {
929 asldebug("%s: error opening for repeat message (%s): %s\n", MY_ID, fdata->path, strerror(errno));
930 return -1;
931 }
932 }
933
934 len = strlen(msg);
935 status = write(fdata->fd, msg, len);
936 free(msg);
937 if (closeit != 0)
938 {
939 close(fdata->fd);
940 fdata->fd = -1;
941 }
942
943 if ((status < 0) || (status < len))
944 {
945 asldebug("%s: error writing repeat message (%s): %s\n", MY_ID, fdata->path, strerror(errno));
946 return -1;
947 }
948
949 return 0;
950 }
951
952 /*
953 * N.B. This is basic file rotation support.
954 * More rotation options will be added in the future, along
955 * with support in aslmanager for compression and deletion.
956 */
957 static void
958 _act_file_rotate_file_data(struct file_data *fdata, time_t now)
959 {
960 char str[MAXPATHLEN];
961 size_t len;
962 int width;
963
964 if (now == 0) now = time(NULL);
965
966 /* flush duplicates if pending */
967 _act_file_send_repeat_msg(fdata);
968
969 /* sleep to prevent a sub-second rotation */
970 while (now == fdata->stamp)
971 {
972 sleep(1);
973 now = time(NULL);
974 }
975
976 len = strlen(fdata->path);
977 width = len - 4;
978 if ((len > 4) && (!strcasecmp(fdata->path + width, ".log")))
979 {
980 /* ".log" rename: abc.log -> abc.timestamp.log */
981 snprintf(str, sizeof(str), "%.*s.%lu.log", width, fdata->path, fdata->stamp);
982 }
983 else
984 {
985 snprintf(str, sizeof(str), "%s.%lu", fdata->path, fdata->stamp);
986 }
987
988 rename(fdata->path, str);
989
990 fdata->stamp = now;
991 }
992
993 int
994 _act_file_open(struct file_data *fdata)
995 {
996 acl_t acl;
997 uuid_t uuid;
998 acl_entry_t entry;
999 acl_permset_t perms;
1000 int status;
1001 int fd = -1;
1002 mode_t mask;
1003 struct stat sb;
1004 uint32_t i;
1005
1006 memset(&sb, 0, sizeof(struct stat));
1007 status = stat(fdata->path, &sb);
1008 if (status == 0)
1009 {
1010 /* must be a regular file */
1011 if (!S_ISREG(sb.st_mode)) return -1;
1012
1013 /* use st_birthtimespec if stamp is zero */
1014 if (fdata->stamp == 0) fdata->stamp = sb.st_birthtimespec.tv_sec;
1015
1016 /* rotate if over size limit */
1017 if ((fdata->max_size > 0) && (sb.st_size > fdata->max_size))
1018 {
1019 _act_file_rotate_file_data(fdata, 0);
1020 }
1021 else
1022 {
1023 /* open existing file */
1024 fd = open(fdata->path, O_RDWR | O_APPEND | O_EXCL, 0);
1025 return fd;
1026 }
1027 }
1028 else if (errno != ENOENT)
1029 {
1030 return -1;
1031 }
1032
1033 #if TARGET_OS_EMBEDDED
1034 return open(fdata->path, O_RDWR | O_CREAT | O_EXCL, (fdata->mode & 0666));
1035 #else
1036
1037 acl = acl_init(1);
1038
1039 for (i = 0; i < fdata->ngid; i++)
1040 {
1041 status = mbr_gid_to_uuid(fdata->gid[i], uuid);
1042 if (status != 0)
1043 {
1044 char *str = NULL;
1045 asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Unknown GID %d for \"file\" action: %s]",
1046 ASL_KEY_SENDER,
1047 ASL_KEY_LEVEL, ASL_LEVEL_ERR,
1048 ASL_KEY_PID, getpid(),
1049 ASL_KEY_MSG, fdata->gid[i], fdata->path);
1050
1051 internal_log_message(str);
1052 free(str);
1053 continue;
1054 }
1055
1056 status = acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY);
1057 if (status != 0) goto asl_file_create_return;
1058
1059 status = acl_set_tag_type(entry, ACL_EXTENDED_ALLOW);
1060 if (status != 0) goto asl_file_create_return;
1061
1062 status = acl_set_qualifier(entry, &uuid);
1063 if (status != 0) goto asl_file_create_return;
1064
1065 status = acl_get_permset(entry, &perms);
1066 if (status != 0) goto asl_file_create_return;
1067
1068 status = acl_add_perm(perms, ACL_READ_DATA);
1069 if (status != 0) goto asl_file_create_return;
1070 }
1071
1072 for (i = 0; i < fdata->nuid; i++)
1073 {
1074 status = mbr_uid_to_uuid(fdata->uid[i], uuid);
1075 if (status != 0)
1076 {
1077 char *str = NULL;
1078 asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Unknown UID %d for \"file\" action: %s]",
1079 ASL_KEY_SENDER,
1080 ASL_KEY_LEVEL, ASL_LEVEL_ERR,
1081 ASL_KEY_PID, getpid(),
1082 ASL_KEY_MSG, fdata->uid[i], fdata->path);
1083
1084 internal_log_message(str);
1085 free(str);
1086 continue;
1087 }
1088
1089 status = acl_create_entry_np(&acl, &entry, ACL_FIRST_ENTRY);
1090 if (status != 0) goto asl_file_create_return;
1091
1092 status = acl_set_tag_type(entry, ACL_EXTENDED_ALLOW);
1093 if (status != 0) goto asl_file_create_return;
1094
1095 status = acl_set_qualifier(entry, &uuid);
1096 if (status != 0) goto asl_file_create_return;
1097
1098 status = acl_get_permset(entry, &perms);
1099 if (status != 0) goto asl_file_create_return;
1100
1101 status = acl_add_perm(perms, ACL_READ_DATA);
1102 if (status != 0) goto asl_file_create_return;
1103 }
1104
1105 mask = umask(0);
1106 fd = open(fdata->path, O_RDWR | O_CREAT | O_EXCL, (fdata->mode & 0666));
1107 umask(mask);
1108 if (fd < 0) goto asl_file_create_return;
1109
1110 errno = 0;
1111 status = acl_set_fd(fd, acl);
1112
1113 if (status != 0)
1114 {
1115 close(fd);
1116 fd = -1;
1117 unlink(fdata->path);
1118 }
1119
1120 asl_file_create_return:
1121
1122 acl_free(acl);
1123 return fd;
1124 #endif
1125 }
1126
1127 static void
1128 _act_file_rotate(const char *path)
1129 {
1130 action_rule_t *r;
1131 struct file_data *fdata;
1132 time_t now = time(NULL);
1133
1134 for (r = asl_action_rule; r != NULL; r = r->next)
1135 {
1136 if (r->action == ACTION_FILE)
1137 {
1138 fdata = (struct file_data *)r->data;
1139 if (fdata->flags & ACT_FILE_FLAG_ROTATE)
1140 {
1141 if ((path == NULL) || ((fdata->path != NULL) && !strcmp(fdata->path, path)))
1142 {
1143 _act_file_rotate_file_data(fdata, now);
1144 }
1145 }
1146 }
1147 }
1148 }
1149
1150 static char *
1151 _act_file_format_string(char *s)
1152 {
1153 char *fmt;
1154 size_t i, len, n;
1155
1156 if (s == NULL) return NULL;
1157
1158 len = strlen(s);
1159 n = 0;
1160 for (i = 0; i < len; i++) if (s[i] == '\\') n++;
1161
1162 fmt = malloc(1 + len - n);
1163 if (fmt == NULL) return NULL;
1164
1165 for (i = 0, n = 0; i < len; i++) if (s[i] != '\\') fmt[n++] = s[i];
1166 fmt[n] = '\0';
1167 return fmt;
1168 }
1169
1170 static size_t
1171 _act_file_max_size(char *s)
1172 {
1173 size_t len, n, max;
1174 char x;
1175
1176 if (s == NULL) return 0;
1177
1178 len = strlen(s);
1179 if (len == 0) return 0;
1180
1181 n = 1;
1182 x = s[len - 1];
1183 if (x > 90) x -= 32;
1184 if (x == 'K') n = 1ll << 10;
1185 else if (x == 'M') n = 1ll << 20;
1186 else if (x == 'G') n = 1ll << 30;
1187 else if (x == 'T') n = 1ll << 40;
1188
1189 max = atoll(s) * n;
1190 return max;
1191 }
1192
1193 static void
1194 _act_file_add_uid(struct file_data *fdata, char *s)
1195 {
1196 if (fdata == NULL) return;
1197 if (s == NULL) return;
1198
1199 fdata->uid = reallocf(fdata->uid, (fdata->nuid + 1) * sizeof(uid_t));
1200 if (fdata->uid == NULL)
1201 {
1202 fdata->nuid = 0;
1203 return;
1204 }
1205
1206 fdata->uid[fdata->nuid++] = atoi(s);
1207 }
1208
1209 static void
1210 _act_file_add_gid(struct file_data *fdata, char *s)
1211 {
1212 if (fdata == NULL) return;
1213 if (s == NULL) return;
1214
1215 fdata->gid = reallocf(fdata->gid, (fdata->ngid + 1) * sizeof(gid_t));
1216 if (fdata->gid == NULL)
1217 {
1218 fdata->ngid = 0;
1219 return;
1220 }
1221
1222 fdata->gid[fdata->ngid++] = atoi(s);
1223 }
1224
1225 static void
1226 _act_file_init(action_rule_t *r)
1227 {
1228 struct file_data *fdata, *xdata;
1229 char *str, *opts, *p, *path;
1230 action_rule_t *x;
1231
1232 /* check if the file data is already set up */
1233 if (r->data != NULL) return;
1234
1235 /* requires at least a path */
1236 if (r->options == NULL) return;
1237 opts = r->options;
1238 path = _next_word(&opts);
1239
1240 if ((path == NULL) || (path[0] != '/'))
1241 {
1242 str = NULL;
1243 asprintf(&str, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Invalid path for \"file\" action: %s]",
1244 ASL_KEY_SENDER,
1245 ASL_KEY_LEVEL, ASL_LEVEL_ERR,
1246 ASL_KEY_PID, getpid(),
1247 ASL_KEY_MSG, (path == NULL) ? "no path specified" : path);
1248
1249 internal_log_message(str);
1250 free(str);
1251 free(path);
1252 r->action = ACTION_NONE;
1253 return;
1254 }
1255
1256 /* check if a previous rule has set up this path */
1257 for (x = asl_action_rule; x != NULL; x = x->next)
1258 {
1259 if ((x->action == ACTION_FILE) && (x->data != NULL))
1260 {
1261 xdata = (struct file_data *)x->data;
1262 if ((xdata->path != NULL) && (!strcmp(path, xdata->path)))
1263 {
1264 free(path);
1265 xdata->refcount++;
1266 r->data = x->data;
1267 return;
1268 }
1269 }
1270 }
1271
1272 /* set up file data */
1273 fdata = (struct file_data *)calloc(1, sizeof(struct file_data));
1274 if (fdata == NULL) return;
1275
1276 fdata->refcount = 1;
1277 fdata->path = path;
1278
1279 /*
1280 * options:
1281 * mode= set file creation mode
1282 * uid= user added to read ACL
1283 * gid= group added to read ACL
1284 * format= format string (also fmt=)
1285 * no_dup_supress no duplicate supression
1286 *
1287 * rotate automatic daily rotation
1288 * this is basic rotation - more support is TBD
1289 */
1290 fdata->mode = 0644;
1291 fdata->flags = ACT_FILE_FLAG_DUP_SUPRESS;
1292
1293 while (NULL != (p = _next_word(&opts)))
1294 {
1295 if (!strncmp(p, "mode=", 5)) fdata->mode = strtol(p+5, NULL, 0);
1296 else if (!strncmp(p, "uid=", 4)) _act_file_add_uid(fdata, p+4);
1297 else if (!strncmp(p, "gid=", 4)) _act_file_add_gid(fdata, p+4);
1298 else if (!strncmp(p, "fmt=", 4)) fdata->fmt = _act_file_format_string(p+4);
1299 else if (!strncmp(p, "format=", 7)) fdata->fmt = _act_file_format_string(p+7);
1300 else if (!strncmp(p, "no_dup_supress", 14)) fdata->flags &= ~ACT_FILE_FLAG_DUP_SUPRESS;
1301 else if (!strncmp(p, "rotate", 6)) fdata->flags |= ACT_FILE_FLAG_ROTATE;
1302 else if (!strncmp(p, "max_size=", 9)) fdata->max_size = _act_file_max_size(p+9);
1303
1304 free(p);
1305 p = NULL;
1306 }
1307
1308 if (fdata->fmt == NULL) fdata->fmt = strdup("std");
1309
1310 /* duplicate compression is only possible for std and bsd formats */
1311 if (strcmp(fdata->fmt, "std") && strcmp(fdata->fmt, "bsd")) fdata->flags &= ~ACT_FILE_FLAG_DUP_SUPRESS;
1312
1313 /* set time format for raw output */
1314 if (!strcmp(fdata->fmt, "raw")) fdata->tfmt = "sec";
1315
1316 r->data = fdata;
1317 }
1318
1319 static void
1320 _act_file(action_rule_t *r, aslmsg msg)
1321 {
1322 struct file_data *fdata;
1323 int is_dup;
1324 uint32_t len, msg_hash = 0;
1325 char *str;
1326 time_t now, today;
1327 struct tm ctm;
1328
1329 if (r->data == NULL) return;
1330
1331 fdata = (struct file_data *)r->data;
1332
1333 now = time(NULL);
1334 today = now;
1335
1336 memset(&ctm, 0, sizeof(struct tm));
1337 if (localtime_r((const time_t *)&now, &ctm) != NULL)
1338 {
1339 ctm.tm_sec = 0;
1340 ctm.tm_min = 0;
1341 ctm.tm_hour = 0;
1342 today = mktime(&ctm);
1343 }
1344
1345 /* check for rotation */
1346 if ((last_file_day != 0) && (last_file_day != today))
1347 {
1348 _act_file_rotate(NULL);
1349 }
1350
1351 last_file_day = today;
1352
1353
1354 /*
1355 * asl.conf may contain multuple rules for the same file, eg:
1356 * ? [= Facility zippy] /var/log/abc.log
1357 * ? [= Color purple] /var/log/abc.log
1358 *
1359 * To prevent duplicates we set a flag bit when a message is logged
1360 * to this file, and bail out if it has already been logged.
1361 * Note that asl_out_message clears the flag bit in all file_data
1362 * structures before processing each message.
1363 */
1364 if (fdata->flags & ACT_FLAG_HAS_LOGGED) return;
1365 fdata->flags |= ACT_FLAG_HAS_LOGGED;
1366
1367 is_dup = 0;
1368
1369 str = asl_format_message((asl_msg_t *)msg, fdata->fmt, fdata->tfmt, ASL_ENCODE_SAFE, &len);
1370
1371 if (fdata->flags & ACT_FILE_FLAG_DUP_SUPRESS)
1372 {
1373 if (fdata->dup_timer == NULL)
1374 {
1375 /* create a timer to flush dups on this file */
1376 fdata->dup_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, asl_action_queue);
1377 dispatch_source_set_event_handler(fdata->dup_timer, ^{ _act_file_send_repeat_msg((struct file_data *)r->data); });
1378 }
1379
1380 if ((global.bsd_max_dup_time > 0) && (str != NULL) && (fdata->last_msg != NULL))
1381 {
1382 msg_hash = asl_core_string_hash(str + 16, len - 16);
1383 if ((fdata->last_hash == msg_hash) && (!strcmp(fdata->last_msg, str + 16)))
1384 {
1385 if ((now - fdata->last_time) < global.bsd_max_dup_time) is_dup = 1;
1386 }
1387 }
1388 }
1389
1390 if (is_dup == 1)
1391 {
1392 if (fdata->last_count == 0)
1393 {
1394 /* start the timer */
1395 dispatch_source_set_timer(fdata->dup_timer, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * global.bsd_max_dup_time), DISPATCH_TIME_FOREVER, 0);
1396 dispatch_resume(fdata->dup_timer);
1397 }
1398
1399 fdata->last_count++;
1400 }
1401 else
1402 {
1403 fdata->fd = _act_file_open(fdata);
1404 if (fdata->fd < 0)
1405 {
1406 asldebug("_act_file_open %s failed: %s\n", fdata->path, strerror(errno));
1407
1408 fdata->fails++;
1409
1410 /* disable further activity after multiple failures */
1411 if (fdata->fails > MAX_FAILURES)
1412 {
1413 char *tmp = NULL;
1414 asprintf(&tmp, "[%s syslogd] [%s %u] [%s %u] [Facility syslog] [%s Disabling writes to path %s following %u failures (%s)]",
1415 ASL_KEY_SENDER,
1416 ASL_KEY_LEVEL, ASL_LEVEL_ERR,
1417 ASL_KEY_PID, getpid(),
1418 ASL_KEY_MSG, fdata->path, fdata->fails, strerror(errno));
1419
1420 internal_log_message(tmp);
1421 free(tmp);
1422
1423 r->action = ACTION_NONE;
1424 free(str);
1425 return;
1426 }
1427 }
1428 else
1429 {
1430 fdata->fails = 0;
1431 }
1432
1433 /*
1434 * The current message is not a duplicate. If fdata->last_count > 0
1435 * we need to write a "last message repeated N times" log entry.
1436 * _act_file_send_repeat_msg will free last_msg and do nothing if
1437 * last_count == 0, but we test and free here to avoid a function call.
1438 */
1439 if (fdata->last_count > 0)
1440 {
1441 _act_file_send_repeat_msg(fdata);
1442 }
1443 else
1444 {
1445 free(fdata->last_msg);
1446 fdata->last_msg = NULL;
1447 }
1448
1449 if (str != NULL) fdata->last_msg = strdup(str + 16);
1450
1451 fdata->last_hash = msg_hash;
1452 fdata->last_count = 0;
1453 fdata->last_time = now;
1454
1455 if ((str != NULL) && (len > 1)) write(fdata->fd, str, len - 1);
1456 close(fdata->fd);
1457 fdata->fd = -1;
1458 }
1459
1460 free(str);
1461 }
1462
1463 static void
1464 _act_forward(action_rule_t *r, aslmsg msg)
1465 {
1466 /* To do: <rdar://problem/6130747> Add a "forward" action to asl.conf */
1467 }
1468
1469 static void
1470 _send_to_asl_store(aslmsg msg)
1471 {
1472 int log_me;
1473 action_rule_t *r;
1474
1475 /* ASLOption "store" forces a message to be saved */
1476 log_me = asl_check_option(msg, ASL_OPT_STORE);
1477 if (log_me == 1)
1478 {
1479 db_save_message(msg);
1480 return;
1481 }
1482
1483 /* if there are no rules, save the message */
1484 if (asl_datastore_rule == NULL)
1485 {
1486 db_save_message(msg);
1487 return;
1488 }
1489
1490 for (r = asl_datastore_rule; r != NULL; r = r->next)
1491 {
1492 if (asl_msg_cmp(r->query, (asl_msg_t *)msg) == 1)
1493 {
1494 /* if any rule matches, save the message (once!) */
1495 db_save_message(msg);
1496 return;
1497 }
1498 }
1499 }
1500
1501 static void
1502 _asl_action_message(aslmsg msg)
1503 {
1504 action_rule_t *r;
1505
1506 if (msg == NULL) return;
1507
1508 /* reset flag bit used for file duplicate avoidance */
1509 for (r = asl_action_rule; r != NULL; r = r->next)
1510 {
1511 if ((r->action == ACTION_FILE) && (r->data != NULL))
1512 {
1513 ((struct file_data *)(r->data))->flags &= ACT_FLAG_CLEAR_LOGGED;
1514 }
1515 else if (((r->action == ACTION_ASL_DIR) || (r->action == ACTION_ASL_FILE)) && (r->data != NULL))
1516 {
1517 ((struct store_data *)(r->data))->flags &= ACT_FLAG_CLEAR_LOGGED;
1518 }
1519 }
1520
1521 for (r = asl_action_rule; r != NULL; r = r->next)
1522 {
1523 if (asl_msg_cmp(r->query, (asl_msg_t *)msg) == 1)
1524 {
1525 if ((r->action == ACTION_ASL_FILE) || (r->action == ACTION_ASL_DIR))
1526 {
1527 _act_store(r, msg);
1528 if (asl_check_option(msg, ASL_OPT_IGNORE) != 0) return;
1529 }
1530
1531 if (r->action == ACTION_NONE) continue;
1532 else if (r->action == ACTION_IGNORE) return;
1533 else if (r->action == ACTION_ACCESS) _act_access_control(r, msg);
1534 else if (r->action == ACTION_NOTIFY) _act_notify(r);
1535 else if (r->action == ACTION_BROADCAST) _act_broadcast(r, msg);
1536 else if (r->action == ACTION_FILE) _act_file(r, msg);
1537 else if (r->action == ACTION_FORWARD) _act_forward(r, msg);
1538 }
1539 }
1540
1541 if (asl_check_option(msg, ASL_OPT_IGNORE) != 0) return;
1542
1543 _send_to_asl_store(msg);
1544 }
1545
1546 void
1547 asl_out_message(aslmsg msg)
1548 {
1549 dispatch_flush_continuation_cache();
1550
1551 asl_msg_retain((asl_msg_t *)msg);
1552
1553 dispatch_async(asl_action_queue, ^{
1554 _asl_action_message(msg);
1555 asl_msg_release((asl_msg_t *)msg);
1556 });
1557 }
1558
1559 static int
1560 _parse_config_file(const char *name)
1561 {
1562 FILE *cf;
1563 char *line;
1564
1565 cf = fopen(name, "r");
1566 if (cf == NULL) return 1;
1567
1568 while (NULL != (line = get_line_from_file(cf)))
1569 {
1570 _parse_line(line);
1571 free(line);
1572 }
1573
1574 fclose(cf);
1575
1576 return 0;
1577 }
1578
1579 int
1580 asl_action_init(void)
1581 {
1582 static dispatch_once_t once;
1583
1584 asldebug("%s: init\n", MY_ID);
1585 _parse_config_file(_PATH_ASL_CONF);
1586
1587 dispatch_once(&once, ^{
1588 asl_action_queue = dispatch_queue_create("ASL Action Queue", NULL);
1589 });
1590
1591 return 0;
1592 }
1593
1594 int
1595 _asl_action_close_internal(void)
1596 {
1597 action_rule_t *r, *n;
1598 struct store_data *sd;
1599 struct file_data *fdata;
1600 n = NULL;
1601 for (r = asl_action_rule; r != NULL; r = n)
1602 {
1603 n = r->next;
1604 if (r->data != NULL)
1605 {
1606 if (((r->action == ACTION_ASL_FILE) || (r->action == ACTION_ASL_DIR) || (r->action == ACTION_NONE)))
1607 {
1608 sd = (struct store_data *)r->data;
1609 if (sd->refcount > 0) sd->refcount--;
1610 if (sd->refcount == 0)
1611 {
1612 if (sd->store != NULL) asl_file_close(sd->store);
1613 if (sd->storedata != NULL) fclose(sd->storedata);
1614
1615 free(sd->dir);
1616 free(sd->path);
1617 free(sd);
1618 }
1619 }
1620
1621 if (r->action == ACTION_FILE)
1622 {
1623 fdata = (struct file_data *)r->data;
1624 if (fdata->refcount > 0) fdata->refcount--;
1625 if (fdata->refcount == 0)
1626 {
1627 _act_file_send_repeat_msg(fdata);
1628
1629 if (fdata->dup_timer != NULL)
1630 {
1631 dispatch_source_cancel(fdata->dup_timer);
1632 dispatch_resume(fdata->dup_timer);
1633 dispatch_release(fdata->dup_timer);
1634 }
1635
1636 free(fdata->path);
1637 free(fdata->fmt);
1638 free(fdata->uid);
1639 free(fdata->gid);
1640 free(fdata->last_msg);
1641 free(fdata);
1642 }
1643 }
1644 }
1645
1646 if (r->query != NULL) asl_msg_release(r->query);
1647 free(r->options);
1648
1649 free(r);
1650 }
1651
1652 asl_action_rule = NULL;
1653
1654 n = NULL;
1655 for (r = asl_datastore_rule; r != NULL; r = n)
1656 {
1657 n = r->next;
1658
1659 if (r->query != NULL) asl_msg_release(r->query);
1660 free(r->options);
1661
1662 free(r);
1663 }
1664
1665 asl_datastore_rule = NULL;
1666
1667 return 0;
1668 }
1669
1670 int
1671 asl_action_close(void)
1672 {
1673 dispatch_async(asl_action_queue, ^{
1674 _asl_action_close_internal();
1675 });
1676
1677 return 0;
1678 }
1679
1680 int
1681 asl_action_reset(void)
1682 {
1683 dispatch_async(asl_action_queue, ^{
1684 _asl_action_close_internal();
1685 asl_action_init();
1686 });
1687
1688 return 0;
1689 }
1690
1691 int
1692 asl_action_file_rotate(const char *path)
1693 {
1694 /*
1695 * The caller may want to know when the rotation has been completed,
1696 * so this is synchronous. Also ensures the string stays intact while we work.
1697 */
1698 dispatch_sync(asl_action_queue, ^{
1699 _act_file_rotate(path);
1700 });
1701
1702 return 0;
1703 }
1704