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