]> git.saurik.com Git - apple/syslog.git/blob - syslogd.tproj/asl_action.c
fd4d38108adff2f45560c3e1c9a2c74d29a142d5
[apple/syslog.git] / syslogd.tproj / asl_action.c
1 /*
2 * Copyright (c) 2004-2013 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 <TargetConditionals.h>
25
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <sys/socket.h>
29 #include <sys/un.h>
30 #include <sys/uio.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <errno.h>
37 #include <netdb.h>
38 #include <notify.h>
39 #include <pthread.h>
40 #include <sys/acl.h>
41 #include <dirent.h>
42 #include <time.h>
43 #include <membership.h>
44 #include <configuration_profile.h>
45 #include "daemon.h"
46 #include <xpc/private.h>
47
48 #define _PATH_WALL "/usr/bin/wall"
49 #define NOTIFY_PATH_SERVICE "com.apple.system.notify.service.path:0x87:"
50
51 #define MY_ID "asl_action"
52
53 /* XXX add to asl.h */
54 #define ASL_KEY_MODULE "ASLModule"
55
56 #define MAX_FAILURES 5
57
58 #define ACTION_STATUS_ERROR -1
59 #define ACTION_STATUS_OK 0
60
61 #define IDLE_CLOSE 300
62
63 #define forever for(;;)
64
65 static dispatch_queue_t asl_action_queue;
66 static dispatch_source_t checkpoint_timer;
67 static time_t sweep_time = 0;
68
69 #if TARGET_OS_EMBEDDED
70 static dispatch_queue_t crashlog_queue;
71 static dispatch_source_t crashlog_sentinel_src;
72 static int crashlog_sentinel_fd = -1;
73 static time_t crashmover_state = 0;
74 static int crashmover_token = -1;
75 #endif
76
77 typedef struct store_data
78 {
79 asl_file_t *store;
80 FILE *storedata;
81 uint64_t next_id;
82 time_t last_time;
83 uint32_t p_year;
84 uint32_t p_month;
85 uint32_t p_day;
86 dispatch_source_t monitor;
87 } asl_action_store_data_t;
88
89 typedef struct file_data
90 {
91 int fd;
92 uint32_t last_hash;
93 uint32_t last_count;
94 time_t last_time;
95 char *last_msg;
96 dispatch_source_t dup_timer;
97 dispatch_source_t monitor;
98 } asl_action_file_data_t;
99
100 typedef struct set_param_data
101 {
102 int token;
103 } asl_action_set_param_data_t;
104
105 static int action_asl_store_count;
106 static bool store_has_logged;
107
108 extern void db_save_message(aslmsg m);
109
110 /* forward */
111 static int _act_file_checkpoint_all(uint32_t force);
112 static void _asl_action_post_process_rule(asl_out_module_t *m, asl_out_rule_t *r);
113 static void _asl_action_close_idle_files(time_t idle_time);
114
115 static void
116 _act_out_set_param(asl_out_module_t *m, char *x, bool eval)
117 {
118 char *s = x;
119 char **l;
120 uint32_t count, intval;
121
122 l = explode(s, " \t");
123 if (l == NULL) return;
124
125 for (count = 0; l[count] != NULL; count++);
126 if (count == 0)
127 {
128 free_string_list(l);
129 return;
130 }
131
132 if (!strcasecmp(l[0], "enable"))
133 {
134 /* = enable [1|0] */
135 if (count < 2) intval = 1;
136 else intval = atoi(l[1]);
137
138 if (!eval) intval = (intval == 0) ? 1 : 0;
139
140 if (intval == 0) m->flags &= ~MODULE_FLAG_ENABLED;
141 else m->flags|= MODULE_FLAG_ENABLED;
142 return;
143 }
144 else if (!strcasecmp(l[0], "disable"))
145 {
146 /* = disable [1|0] */
147 if (count < 2) intval = 1;
148 else intval = atoi(l[1]);
149
150 if (!eval) intval = (intval == 0) ? 1 : 0;
151
152 if (intval != 0) m->flags &= ~MODULE_FLAG_ENABLED;
153 else m->flags|= MODULE_FLAG_ENABLED;
154 return;
155 }
156
157 free_string_list(l);
158
159 if (!strcmp(m->name, ASL_MODULE_NAME))
160 {
161 /* Other parameters may be set by com.apple.asl module */
162 control_set_param(x, eval);
163 }
164 }
165
166 static void
167 _act_notify(asl_out_module_t *m, asl_out_rule_t *r)
168 {
169 if (m == NULL) return;
170 if ((m->flags & MODULE_FLAG_ENABLED) == 0) return;
171
172 if (r == NULL) return;
173 if (r->options == NULL) return;
174
175 notify_post(r->options);
176 }
177
178 static void
179 _act_broadcast(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
180 {
181 #if !TARGET_OS_EMBEDDED
182 FILE *pw;
183 const char *val;
184
185 if (m == NULL) return;
186 if ((m->flags & MODULE_FLAG_ENABLED) == 0) return;
187
188 if (m->name == NULL) return;
189 if (r == NULL) return;
190 if (msg == NULL) return;
191
192 /* only base module (asl.conf) may broadcast */
193 if (strcmp(m->name, ASL_MODULE_NAME)) return;
194
195 val = r->options;
196 if (val == NULL) val = asl_get(msg, ASL_KEY_MSG);
197 if (val == NULL) return;
198
199 pw = popen(_PATH_WALL, "w");
200 if (pw == NULL)
201 {
202 asldebug("%s: error sending wall message: %s\n", MY_ID, strerror(errno));
203 return;
204 }
205
206 fprintf(pw, "%s", val);
207 pclose(pw);
208 #endif
209 }
210
211 static void
212 _act_access_control(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
213 {
214 int32_t ruid, rgid;
215 char *p;
216
217 if (m == NULL) return;
218 if (m->name == NULL) return;
219 if (r == NULL) return;
220 if (msg == NULL) return;
221
222 /* only base module (asl.conf) may set access controls */
223 if (strcmp(m->name, ASL_MODULE_NAME)) return;
224
225 ruid = atoi(r->options);
226 rgid = -1;
227 p = strchr(r->options, ' ');
228 if (p == NULL) p = strchr(r->options, '\t');
229 if (p != NULL)
230 {
231 *p = '\0';
232 p++;
233 rgid = atoi(p);
234 }
235
236 if (ruid != -1) asl_set(msg, ASL_KEY_READ_UID, r->options);
237 if (p != NULL)
238 {
239 if (rgid != -1) asl_set(msg, ASL_KEY_READ_GID, p);
240 p--;
241 *p = ' ';
242 }
243 }
244
245 #if TARGET_OS_EMBEDDED
246 static void
247 _crashlog_sentinel_init(void)
248 {
249 char path[MAXPATHLEN];
250
251 if (crashlog_sentinel_src != NULL) return;
252
253 snprintf(path, sizeof(path), "%s/com.apple.asl.%ld", _PATH_CRASHREPORTER, time(NULL));
254
255 crashlog_sentinel_fd = open(path, O_WRONLY | O_CREAT);
256 if (crashlog_sentinel_fd < 0)
257 {
258 char *str = NULL;
259 asprintf(&str, "[Sender syslogd] [Level 3] [PID %u] [Facility syslog] [Message Sentinel %s create/open failed (%s)]", global.pid, path, strerror(errno));
260 internal_log_message(str);
261 free(str);
262 return;
263 }
264
265 close(crashlog_sentinel_fd);
266
267 crashlog_sentinel_fd = open(path, O_EVTONLY, 0);
268 if (crashlog_sentinel_fd < 0)
269 {
270 char *str = NULL;
271 asprintf(&str, "[Sender syslogd] [Level 3] [PID %u] [Facility syslog] [Message Sentinel %s event/open failed (%s)]", global.pid, path, strerror(errno));
272 internal_log_message(str);
273 free(str);
274 return;
275 }
276
277 crashlog_sentinel_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, (uintptr_t)crashlog_sentinel_fd, DISPATCH_VNODE_DELETE, asl_action_queue);
278 if (crashlog_sentinel_src == NULL)
279 {
280 char *str = NULL;
281 asprintf(&str, "[Sender syslogd] [Level 3] [PID %u] [Facility syslog] [Message Sentinel %s dispatch_source_create failed]", global.pid, path);
282 internal_log_message(str);
283 free(str);
284 close(crashlog_sentinel_fd);
285 crashlog_sentinel_fd = -1;
286 return;
287 }
288
289 dispatch_source_set_event_handler(crashlog_sentinel_src, ^{
290 if (crashmover_state != 0)
291 {
292 asldebug("CrashMover inactive / sentinel deleted: resuming crashlog queue\n");
293 dispatch_resume(crashlog_queue);
294 crashmover_state = 0;
295 }
296
297 if (crashlog_sentinel_src != NULL)
298 {
299 dispatch_source_cancel(crashlog_sentinel_src);
300 dispatch_release(crashlog_sentinel_src);
301 }
302
303 crashlog_sentinel_src = NULL;
304
305 close(crashlog_sentinel_fd);
306 crashlog_sentinel_fd = -1;
307 _crashlog_sentinel_init();
308 });
309
310 dispatch_resume(crashlog_sentinel_src);
311 asldebug("Created CrashLog Sentinel: %s\n", path);
312 }
313
314 static void
315 _crashlog_queue_check(void)
316 {
317 /*
318 * Check whether the crashlog queue has been suspended for too long.
319 * We allow the crashlog quque to be suspended for 60 seconds.
320 * After that, we start logging again. This prevents syslogd from
321 * filling memory due to a suspended queue. CrashMover really shoud
322 * take no more than a second or two to finish.
323 */
324 if (crashmover_state == 0) return;
325 if ((time(NULL) - crashmover_state) <= 60) return;
326
327 asldebug("CrashMover timeout: resuming crashlog queue\n");
328 dispatch_resume(crashlog_queue);
329 crashmover_state = 0;
330
331 /*
332 * crashlog_sentinel_src should never be NULL, but if
333 * _crashlog_sentinel_init failed for some strange reason,
334 * it will be NULL here.
335 */
336 if (crashlog_sentinel_src != NULL)
337 {
338 dispatch_source_cancel(crashlog_sentinel_src);
339 dispatch_release(crashlog_sentinel_src);
340 }
341
342 crashlog_sentinel_src = NULL;
343
344 close(crashlog_sentinel_fd);
345 crashlog_sentinel_fd = -1;
346 _crashlog_sentinel_init();
347 }
348 #endif
349
350 static void
351 _act_dst_close(asl_out_rule_t *r)
352 {
353 if (r == NULL) return;
354 if (r->dst == NULL) return;
355 if (r->dst->private == NULL) return;
356
357 if ((r->action == ACTION_ASL_DIR) || (r->action == ACTION_ASL_FILE))
358 {
359 asl_action_store_data_t *sdata = (asl_action_store_data_t *)r->dst->private;
360 if (sdata->store != NULL) asl_file_close(sdata->store);
361 sdata->store = NULL;
362
363 if (sdata->storedata != NULL) fclose(sdata->storedata);
364 sdata->storedata = NULL;
365
366 if (sdata->monitor != NULL)
367 {
368 dispatch_source_cancel(sdata->monitor);
369 dispatch_release(sdata->monitor);
370 sdata->monitor = NULL;
371 }
372 }
373 else if (r->action == ACTION_FILE)
374 {
375 asl_action_file_data_t *fdata = (asl_action_file_data_t *)r->dst->private;
376 if (fdata->fd >= 0) close(fdata->fd);
377 fdata->fd = -1;
378
379 if (fdata->monitor != NULL)
380 {
381 dispatch_source_cancel(fdata->monitor);
382 dispatch_release(fdata->monitor);
383 fdata->monitor = NULL;
384 }
385 }
386 }
387
388 static uint32_t
389 _act_store_file_setup(asl_out_module_t *m, asl_out_rule_t *r)
390 {
391 uint32_t status;
392 asl_action_store_data_t *sdata;
393 char dstpath[MAXPATHLEN];
394
395 if (r == NULL) return ASL_STATUS_INVALID_STORE;
396 if (r->dst == NULL) return ASL_STATUS_INVALID_STORE;
397 if (r->dst->private == NULL) return ASL_STATUS_INVALID_STORE;
398
399 sdata = (asl_action_store_data_t *)r->dst->private;
400 if (sdata->store == NULL)
401 {
402 /* create path if necessary */
403 asl_out_mkpath(r);
404
405 int fd = asl_out_dst_file_create_open(r->dst);
406 if (fd < 0)
407 {
408 asldebug("_act_store_file_setup: asl_out_dst_file_create_open failed %d %s\n", errno, strerror(errno));
409 return ASL_STATUS_WRITE_FAILED;
410 }
411 close(fd);
412
413 asl_make_dst_filename(r->dst, dstpath, sizeof(dstpath));
414 status = asl_file_open_write(dstpath, 0, -1, -1, &(sdata->store));
415 if (status != ASL_STATUS_OK) return status;
416
417 sdata->monitor = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fileno(sdata->store->store), DISPATCH_VNODE_DELETE, asl_action_queue);
418 if (sdata->monitor != NULL)
419 {
420 dispatch_source_set_event_handler(sdata->monitor, ^{ _act_dst_close(r); });
421 dispatch_resume(sdata->monitor);
422 }
423
424 status = asl_file_read_set_position(sdata->store, ASL_FILE_POSITION_LAST);
425 if (status != ASL_STATUS_OK)
426 {
427 asldebug("_act_store_file_setup: asl_file_read_set_position failed %d %s\n", status, asl_core_error(status));
428 return status;
429 }
430
431 sdata->next_id = sdata->store->cursor_xid + 1;
432 if (fseek(sdata->store->store, 0, SEEK_END) != 0)
433 {
434 asldebug("_act_store_file_setup: fseek failed %d %s\n", errno, strerror(errno));
435 return ASL_STATUS_WRITE_FAILED;
436 }
437 }
438 else
439 {
440 sdata->next_id++;
441 }
442
443 return ASL_STATUS_OK;
444 }
445
446 /*
447 * _act_store_dir_setup
448 *
449 * Creates store directory if it does not exist
450 * Creates StoreData file if it does not exist
451 * Reads ASL Message ID from StoreData file
452 * Writes ASL Message ID + 1 to StoreData file
453 * Opens current day file (e.g. "/foo/bar/2012.04.06.asl")
454 */
455 static uint32_t
456 _act_store_dir_setup(asl_out_module_t *m, asl_out_rule_t *r, time_t tick)
457 {
458 struct tm ctm;
459 char *path;
460 struct stat sb;
461 uint64_t xid;
462 int status;
463 mode_t mask;
464 asl_action_store_data_t *sdata;
465
466 if (r == NULL) return ASL_STATUS_INVALID_STORE;
467 if (r->dst == NULL) return ASL_STATUS_INVALID_STORE;
468 if (r->dst->private == NULL) return ASL_STATUS_INVALID_STORE;
469 if (r->dst->path == NULL) return ASL_STATUS_INVALID_STORE;
470
471 sdata = (asl_action_store_data_t *)r->dst->private;
472
473 /* get / set message id from StoreData file */
474 xid = 0;
475
476 if (sdata->storedata == NULL)
477 {
478 memset(&sb, 0, sizeof(struct stat));
479 status = stat(r->dst->path, &sb);
480 if (status == 0)
481 {
482 /* must be a directory */
483 if (!S_ISDIR(sb.st_mode)) return ASL_STATUS_INVALID_STORE;
484 }
485 else if (errno == ENOENT)
486 {
487 /* doesn't exist - create it */
488 mask = umask(S_IWGRP | S_IWOTH);
489 status = mkpath_np(r->dst->path, 0755);
490 if (status == 0) status = chmod(r->dst->path, r->dst->mode);
491 umask(mask);
492
493 if (status != 0) return ASL_STATUS_WRITE_FAILED;
494 #if !TARGET_IPHONE_SIMULATOR
495 if (chown(r->dst->path, r->dst->uid[0], r->dst->gid[0]) != 0) return ASL_STATUS_WRITE_FAILED;
496 #endif
497 }
498 else
499 {
500 /* Unexpected stat error */
501 return ASL_STATUS_FAILED;
502 }
503
504 path = NULL;
505 asprintf(&path, "%s/%s", r->dst->path, FILE_ASL_STORE_DATA);
506 if (path == NULL) return ASL_STATUS_NO_MEMORY;
507
508 memset(&sb, 0, sizeof(struct stat));
509 status = stat(path, &sb);
510 if (status == 0)
511 {
512 /* StoreData exists: open and read last xid */
513 sdata->storedata = fopen(path, "r+");
514 if (sdata->storedata == NULL)
515 {
516 free(path);
517 return ASL_STATUS_FAILED;
518 }
519
520 if (fread(&xid, sizeof(uint64_t), 1, sdata->storedata) != 1)
521 {
522 free(path);
523 fclose(sdata->storedata);
524 sdata->storedata = NULL;
525 return ASL_STATUS_READ_FAILED;
526 }
527 }
528 else if (errno == ENOENT)
529 {
530 /* StoreData does not exist: create it */
531 sdata->storedata = fopen(path, "w");
532 if (sdata->storedata == NULL)
533 {
534 free(path);
535 return ASL_STATUS_FAILED;
536 }
537
538 #if !TARGET_IPHONE_SIMULATOR
539 if (chown(path, r->dst->uid[0], r->dst->gid[0]) != 0)
540 {
541 free(path);
542 return ASL_STATUS_WRITE_FAILED;
543 }
544 #endif
545 }
546 else
547 {
548 /* Unexpected stat error */
549 free(path);
550 return ASL_STATUS_FAILED;
551 }
552
553 sdata->monitor = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fileno(sdata->storedata), DISPATCH_VNODE_DELETE, asl_action_queue);
554 if (sdata->monitor != NULL)
555 {
556 dispatch_source_set_event_handler(sdata->monitor, ^{ _act_dst_close(r); });
557 dispatch_resume(sdata->monitor);
558 }
559
560 free(path);
561 }
562 else
563 {
564 rewind(sdata->storedata);
565 if (fread(&xid, sizeof(uint64_t), 1, sdata->storedata) != 1)
566 {
567 fclose(sdata->storedata);
568 sdata->storedata = NULL;
569 return ASL_STATUS_READ_FAILED;
570 }
571 }
572
573 xid = asl_core_ntohq(xid);
574 xid++;
575 sdata->next_id = xid;
576
577 xid = asl_core_htonq(xid);
578 rewind(sdata->storedata);
579 status = fwrite(&xid, sizeof(uint64_t), 1, sdata->storedata);
580 if (status != 1)
581 {
582 fclose(sdata->storedata);
583 sdata->storedata = NULL;
584 return ASL_STATUS_WRITE_FAILED;
585 }
586
587 memset(&ctm, 0, sizeof(struct tm));
588
589 if (localtime_r((const time_t *)&tick, &ctm) == NULL) return ASL_STATUS_FAILED;
590 if ((sdata->store != NULL) && (sdata->p_year == ctm.tm_year) && (sdata->p_month == ctm.tm_mon) && (sdata->p_day == ctm.tm_mday))
591 {
592 return ASL_STATUS_OK;
593 }
594
595 if (sdata->store != NULL) asl_file_close(sdata->store);
596 sdata->store = NULL;
597 free(r->dst->fname);
598 r->dst->fname = NULL;
599
600 r->dst->stamp = tick;
601
602 sdata->p_year = ctm.tm_year;
603 sdata->p_month = ctm.tm_mon;
604 sdata->p_day = ctm.tm_mday;
605
606 asprintf(&(r->dst->fname), "%s/%d.%02d.%02d.asl", r->dst->path, ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday);
607 if (r->dst->fname == NULL) return ASL_STATUS_NO_MEMORY;
608 mask = umask(0);
609
610 status = ASL_STATUS_OK;
611 if (sdata->store == NULL) {
612 #if TARGET_IPHONE_SIMULATOR
613 uid_t uid = -1;
614 gid_t gid = -1;
615 #else
616 uid_t uid = r->dst->uid[0];
617 gid_t gid = r->dst->gid[0];
618 #endif
619 status = asl_file_open_write(r->dst->fname, (r->dst->mode & 0666), uid, gid, &(sdata->store));
620 }
621 umask(mask);
622
623 if (status != ASL_STATUS_OK) return status;
624
625 if (fseek(sdata->store->store, 0, SEEK_END) != 0) return ASL_STATUS_FAILED;
626
627 return ASL_STATUS_OK;
628 }
629
630 static void
631 _asl_action_store_data_free(asl_action_store_data_t *sdata)
632 {
633 if (sdata == NULL) return;
634
635 if (sdata->store != NULL) asl_file_close(sdata->store);
636 sdata->store = NULL;
637
638 if (sdata->storedata != NULL) fclose(sdata->storedata);
639 sdata->storedata = NULL;
640
641 free(sdata);
642 }
643
644 static void
645 _asl_action_file_data_free(asl_action_file_data_t *fdata)
646 {
647 if (fdata == NULL) return;
648
649 if (fdata->dup_timer != NULL)
650 {
651 if (fdata->last_count == 0)
652 {
653 /*
654 * The timer exists, but last_count is zero, so the timer is suspended.
655 * Sources must not be released in when suspended.
656 * So we resume it so that we can release it.
657 */
658 dispatch_resume(fdata->dup_timer);
659 }
660
661 dispatch_release(fdata->dup_timer);
662 }
663
664 free(fdata->last_msg);
665 if (fdata->fd >= 0) close(fdata->fd);
666 free(fdata);
667 }
668
669 static void
670 _asl_action_set_param_data_free(asl_action_set_param_data_t *spdata)
671 {
672 if (spdata != NULL) notify_cancel(spdata->token);
673 free(spdata);
674 }
675
676 static void
677 _asl_action_save_failed(const char *where, asl_out_module_t *m, asl_out_rule_t *r, uint32_t status)
678 {
679 if (r->dst->flags & MODULE_FLAG_SOFT_WRITE) return;
680
681 r->dst->fails++;
682 asldebug("%s: %s save to %s failed: %s\n", where, m->name, r->dst->path, asl_core_error(status));
683
684 /* disable further activity after multiple failures */
685 if (r->dst->fails > MAX_FAILURES)
686 {
687 char *str = NULL;
688 asprintf(&str, "[Sender syslogd] [Level 3] [PID %u] [Facility syslog] [Message Disabling module %s writes to %s following %u failures (%s)]", global.pid, m->name, r->dst->path, r->dst->fails, asl_core_error(status));
689 internal_log_message(str);
690 free(str);
691
692 if (r->action == ACTION_FILE) _asl_action_file_data_free((asl_action_file_data_t *)r->dst->private);
693 else _asl_action_store_data_free((asl_action_store_data_t *)r->dst->private);
694
695 r->dst->private = NULL;
696 r->action = ACTION_NONE;
697 }
698 }
699
700 static int
701 _act_store_file(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
702 {
703 asl_action_store_data_t *sdata;
704 uint32_t status;
705 uint64_t mid;
706
707 if (r == NULL) return ACTION_STATUS_ERROR;
708 if (r->dst == NULL) return ACTION_STATUS_ERROR;
709 if (r->dst->private == NULL) return ACTION_STATUS_ERROR;
710
711 sdata = (asl_action_store_data_t *)r->dst->private;
712
713 /* check dst for file_max & etc */
714 if (r->dst->flags & MODULE_FLAG_ROTATE)
715 {
716 if (asl_out_dst_checkpoint(r->dst, CHECKPOINT_TEST) != 0)
717 {
718 _act_dst_close(r);
719 asl_trigger_aslmanager();
720 }
721 }
722
723 status = _act_store_file_setup(m, r);
724 if (status == ASL_STATUS_OK)
725 {
726 sdata->last_time = time(NULL);
727
728 r->dst->fails = 0;
729 mid = sdata->next_id;
730
731 /* save message to file and update dst size */
732 status = asl_file_save(sdata->store, msg, &mid);
733 if (status == ASL_STATUS_OK) r->dst->size = sdata->store->file_size;
734 }
735
736 if (status != ASL_STATUS_OK)
737 {
738 _asl_action_save_failed("_act_store_file", m, r, status);
739 return ACTION_STATUS_ERROR;
740 }
741
742 return ACTION_STATUS_OK;
743 }
744
745 static int
746 _act_store_dir(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
747 {
748 asl_action_store_data_t *sdata;
749 uint32_t status;
750 uint64_t mid;
751 const char *val;
752 time_t tick;
753
754 if (r == NULL) return ACTION_STATUS_ERROR;
755 if (r->dst == NULL) return ACTION_STATUS_ERROR;
756 if (r->dst->private == NULL) return ACTION_STATUS_ERROR;
757
758 sdata = (asl_action_store_data_t *)r->dst->private;
759
760 val = asl_get(msg, ASL_KEY_TIME);
761 if (val == NULL) return ACTION_STATUS_ERROR;
762
763 /* check dst for file_max & etc */
764 if (asl_out_dst_checkpoint(r->dst, CHECKPOINT_TEST) != 0)
765 {
766 _act_dst_close(r);
767 asl_trigger_aslmanager();
768 }
769
770 tick = atol(val);
771
772 status = _act_store_dir_setup(m, r, tick);
773 if (status == ASL_STATUS_OK)
774 {
775 sdata->last_time = time(NULL);
776
777 r->dst->fails = 0;
778 mid = sdata->next_id;
779 status = asl_file_save(sdata->store, msg, &mid);
780 if (status == ASL_STATUS_OK) r->dst->size = sdata->store->file_size;
781 }
782
783 if (status != ASL_STATUS_OK)
784 {
785 _asl_action_save_failed("_act_store_dir", m, r, status);
786 return ACTION_STATUS_ERROR;
787 }
788
789 return ACTION_STATUS_OK;
790 }
791
792 static void
793 _act_store_final(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
794 {
795 if (r->action == ACTION_ASL_DIR) _act_store_dir(m, r, msg);
796 else _act_store_file(m, r, msg);
797 }
798
799 /*
800 * Save a message to an ASL format file (ACTION_ASL_FILE)
801 * or to an ASL directory (ACTION_ASL_DIR).
802 */
803 static void
804 _act_store(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
805 {
806 if (r == NULL) return;
807 if (msg == NULL) return;
808 if (m == NULL) return;
809 if ((m->flags & MODULE_FLAG_ENABLED) == 0) return;
810 if (r->dst == NULL) return;
811
812 if (r->dst->flags & MODULE_FLAG_HAS_LOGGED) return;
813 r->dst->flags |= MODULE_FLAG_HAS_LOGGED;
814
815 #if TARGET_OS_EMBEDDED
816 if (r->dst->flags & MODULE_FLAG_CRASHLOG)
817 {
818 _crashlog_queue_check();
819 asl_msg_retain((asl_msg_t *)msg);
820 dispatch_async(crashlog_queue, ^{
821 _act_store_final(m, r, msg);
822 asl_msg_release((asl_msg_t *)msg);
823 });
824 return;
825 }
826 #endif
827
828 _act_store_final(m, r, msg);
829 }
830
831 static int
832 _send_repeat_msg(asl_out_rule_t *r)
833 {
834 asl_action_file_data_t *fdata;
835 char vt[32], *msg;
836 int len, status;
837 time_t now = time(NULL);
838
839 if (r == NULL) return -1;
840 if (r->dst == NULL) return -1;
841 if (r->dst->private == NULL) return -1;
842
843 fdata = (asl_action_file_data_t *)r->dst->private;
844
845 free(fdata->last_msg);
846 fdata->last_msg = NULL;
847
848 if (fdata->last_count == 0) return 0;
849
850 /* stop the timer */
851 dispatch_suspend(fdata->dup_timer);
852
853 memset(vt, 0, sizeof(vt));
854 ctime_r(&now, vt);
855 vt[19] = '\0';
856
857 msg = NULL;
858 asprintf(&msg, "%s --- last message repeated %u time%s ---\n", vt + 4, fdata->last_count, (fdata->last_count == 1) ? "" : "s");
859 fdata->last_count = 0;
860 fdata->last_time = now;
861 if (msg == NULL) return -1;
862
863 if (fdata->fd < 0) fdata->fd = asl_out_dst_file_create_open(r->dst);
864
865 len = strlen(msg);
866 status = write(fdata->fd, msg, len);
867 free(msg);
868
869 if ((status < 0) || (status < len))
870 {
871 asldebug("%s: error writing repeat message (%s): %s\n", MY_ID, r->dst->path, strerror(errno));
872 return -1;
873 }
874
875 return 0;
876 }
877
878 static int
879 _act_file_open(asl_out_module_t *m, asl_out_rule_t *r)
880 {
881 asl_action_file_data_t *fdata;
882
883 if (r == NULL) return -1;
884 if (r->dst == NULL) return -1;
885 if (r->dst->private == NULL) return -1;
886
887 fdata = (asl_action_file_data_t *)r->dst->private;
888 if (fdata->fd < 0)
889 {
890 fdata->fd = asl_out_dst_file_create_open(r->dst);
891 if (fdata->fd < 0)
892 {
893 /*
894 * lazy path creation: create path and retry
895 * asl_out_dst_file_create_open doesn not create the path
896 * so we do it here.
897 */
898 asl_out_mkpath(r);
899 fdata->fd = asl_out_dst_file_create_open(r->dst);
900 }
901
902 if (fdata->fd >= 0)
903 {
904 fdata->monitor = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fdata->fd, DISPATCH_VNODE_DELETE, asl_action_queue);
905 if (fdata->monitor != NULL)
906 {
907 dispatch_source_set_event_handler(fdata->monitor, ^{ _act_dst_close(r); });
908 dispatch_resume(fdata->monitor);
909 }
910 }
911 }
912
913 return fdata->fd;
914 }
915
916 static void
917 _start_cycling()
918 {
919 struct timespec midnight;
920 struct tm t;
921 time_t x;
922
923 x = time(NULL);
924
925 if (checkpoint_timer != NULL) return;
926
927 localtime_r(&x, &t);
928
929 t.tm_sec = 0;
930 t.tm_min = 0;
931 t.tm_hour = 0;
932 t.tm_mday++;
933
934 x = mktime(&t);
935 midnight.tv_sec = x;
936 midnight.tv_nsec = 0;
937
938 checkpoint_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, asl_action_queue);
939 dispatch_source_set_timer(checkpoint_timer, dispatch_walltime(&midnight, 0), NSEC_PER_SEC * SEC_PER_DAY, 0);
940 dispatch_source_set_event_handler(checkpoint_timer, ^{ _act_file_checkpoint_all(CHECKPOINT_FORCE); });
941 dispatch_resume(checkpoint_timer);
942 }
943
944 /* check if a module path (mpath) matches a user path (upath) */
945 static bool
946 _act_file_equal(const char *mpath, const char *upath)
947 {
948 const char *slash;
949
950 /* NULL upath means user wants to match all files */
951 if (upath == NULL) return true;
952
953 if (mpath == NULL) return false;
954
955 /* check for exact match */
956 if (!strcmp(mpath, upath)) return true;
957
958 /* upath may be the last component of mpath */
959 slash = strrchr(mpath, '/');
960 if (slash == NULL) return false;
961
962 if (!strcmp(slash + 1, upath)) return true;
963 return false;
964 }
965
966 static int
967 _act_file_checkpoint(asl_out_module_t *m, const char *path, uint32_t force)
968 {
969 asl_out_rule_t *r;
970 int did_checkpoint = 0;
971
972 if (m == NULL) return 0;
973
974
975 for (r = m->ruleset; r != NULL; r = r->next)
976 {
977 if ((r->action == ACTION_FILE) || (r->action == ACTION_ASL_FILE))
978 {
979 if (r->dst->flags & MODULE_FLAG_ROTATE)
980 {
981 if (_act_file_equal(r->dst->path, path))
982 {
983 if (force & CHECKPOINT_CRASH)
984 {
985 if (r->dst->flags & MODULE_FLAG_CRASHLOG)
986 {
987 if (asl_out_dst_checkpoint(r->dst, CHECKPOINT_FORCE) > 0)
988 {
989 did_checkpoint = 1;
990 _act_dst_close(r);
991 }
992 }
993 }
994 else
995 {
996 if (asl_out_dst_checkpoint(r->dst, force) > 0)
997 {
998 did_checkpoint = 1;
999 _act_dst_close(r);
1000 }
1001 }
1002 }
1003 }
1004 }
1005 }
1006
1007 return did_checkpoint;
1008 }
1009
1010 static int
1011 _act_file_checkpoint_all(uint32_t force)
1012 {
1013 asl_out_module_t *m;
1014 int did_checkpoint = 0;
1015
1016 for (m = global.asl_out_module; m != NULL; m = m->next)
1017 {
1018 if (_act_file_checkpoint(m, NULL, force) > 0) did_checkpoint = 1;
1019 }
1020
1021 asl_trigger_aslmanager();
1022
1023 return did_checkpoint;
1024 }
1025
1026 static void
1027 _act_file_final(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
1028 {
1029 asl_action_file_data_t *fdata;
1030 int is_dup;
1031 uint32_t len, msg_hash = 0;
1032 char *str;
1033 time_t now;
1034
1035 /*
1036 * If print format is std, bsd, or msg, then skip messages with
1037 * no ASL_KEY_MSG, or without a value for it.
1038 */
1039 if (r->dst->flags & MODULE_FLAG_STD_BSD_MSG)
1040 {
1041 const char *msgval = NULL;
1042 if (asl_msg_lookup((asl_msg_t *)msg, ASL_KEY_MSG, &msgval, NULL) != 0) return;
1043 if (msgval == NULL) return;
1044 }
1045
1046 fdata = (asl_action_file_data_t *)r->dst->private;
1047
1048 now = time(NULL);
1049
1050 is_dup = 0;
1051
1052 str = asl_format_message((asl_msg_t *)msg, r->dst->fmt, r->dst->tfmt, ASL_ENCODE_SAFE, &len);
1053
1054 if (r->dst->flags & MODULE_FLAG_COALESCE)
1055 {
1056 if (fdata->dup_timer == NULL)
1057 {
1058 /* create a timer to flush dups on this file */
1059 fdata->dup_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, asl_action_queue);
1060 dispatch_source_set_event_handler(fdata->dup_timer, ^{ _send_repeat_msg(r); });
1061 }
1062
1063 if ((global.bsd_max_dup_time > 0) && (str != NULL) && (fdata->last_msg != NULL))
1064 {
1065 msg_hash = asl_core_string_hash(str + 16, len - 16);
1066 if ((fdata->last_hash == msg_hash) && (!strcmp(fdata->last_msg, str + 16)))
1067 {
1068 if ((now - fdata->last_time) < global.bsd_max_dup_time) is_dup = 1;
1069 }
1070 }
1071 }
1072
1073 if (is_dup == 1)
1074 {
1075 if (fdata->last_count == 0)
1076 {
1077 /* start the timer */
1078 dispatch_source_set_timer(fdata->dup_timer, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * global.bsd_max_dup_time), DISPATCH_TIME_FOREVER, 0);
1079 dispatch_resume(fdata->dup_timer);
1080 }
1081
1082 fdata->last_count++;
1083 }
1084 else
1085 {
1086 if (_act_file_open(m, r) < 0)
1087 {
1088 _asl_action_save_failed("_act_file", m, r, ASL_STATUS_FAILED);
1089 free(str);
1090 return;
1091 }
1092 else
1093 {
1094 r->dst->fails = 0;
1095 }
1096
1097 /*
1098 * The current message is not a duplicate. If fdata->last_count > 0
1099 * we need to write a "last message repeated N times" log entry.
1100 * _send_repeat_msg will free last_msg and do nothing if
1101 * last_count == 0, but we test and free here to avoid a function call.
1102 */
1103 if (fdata->last_count > 0)
1104 {
1105 _send_repeat_msg(r);
1106 }
1107 else
1108 {
1109 free(fdata->last_msg);
1110 fdata->last_msg = NULL;
1111 }
1112
1113 /* check dst for file_max & etc */
1114 if (r->dst->flags & MODULE_FLAG_ROTATE)
1115 {
1116 int ckpt = asl_out_dst_checkpoint(r->dst, CHECKPOINT_TEST);
1117 if (ckpt != 0)
1118 {
1119 _act_dst_close(r);
1120 asl_trigger_aslmanager();
1121
1122 if (_act_file_open(m, r) < 0)
1123 {
1124 _asl_action_save_failed("_act_file", m, r, ASL_STATUS_FAILED);
1125 free(str);
1126 return;
1127 }
1128 else
1129 {
1130 r->dst->fails = 0;
1131 }
1132 }
1133 }
1134
1135 if (str != NULL) fdata->last_msg = strdup(str + 16);
1136
1137 fdata->last_hash = msg_hash;
1138 fdata->last_count = 0;
1139 fdata->last_time = now;
1140
1141 if ((str != NULL) && (len > 1))
1142 {
1143 /* write line to file and update dst size */
1144 size_t bytes = write(fdata->fd, str, len - 1);
1145 if (bytes > 0) r->dst->size += bytes;
1146 }
1147 }
1148
1149 free(str);
1150 }
1151
1152 static void
1153 _act_file(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
1154 {
1155 if (r == NULL) return;
1156 if (msg == NULL) return;
1157 if (m == NULL) return;
1158 if ((m->flags & MODULE_FLAG_ENABLED) == 0) return;
1159 if (r->dst == NULL) return;
1160 if (r->dst->private == NULL) return;
1161
1162 if (r->dst->flags & MODULE_FLAG_HAS_LOGGED) return;
1163
1164 r->dst->flags |= MODULE_FLAG_HAS_LOGGED;
1165
1166 #if TARGET_OS_EMBEDDED
1167 if (r->dst->flags & MODULE_FLAG_CRASHLOG)
1168 {
1169 _crashlog_queue_check();
1170 asl_msg_retain((asl_msg_t *)msg);
1171 dispatch_async(crashlog_queue, ^{
1172 _act_file_final(m, r, msg);
1173 asl_msg_release((asl_msg_t *)msg);
1174 });
1175 return;
1176 }
1177 #endif
1178
1179 _act_file_final(m, r, msg);
1180 }
1181
1182 static void
1183 _act_forward(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
1184 {
1185 /* To do: <rdar://problem/6130747> Add a "forward" action to asl.conf */
1186 }
1187
1188 static void
1189 _act_control(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg)
1190 {
1191 const char *p;
1192
1193 if (m == NULL) return;
1194 if (r == NULL) return;
1195 p = asl_get(msg, ASL_KEY_MODULE);
1196
1197 if (r->options == NULL) return;
1198
1199 if (!strcmp(r->options, "enable"))
1200 {
1201 m->flags |= MODULE_FLAG_ENABLED;
1202 }
1203 else if (!strcmp(r->options, "disable"))
1204 {
1205 m->flags &= ~MODULE_FLAG_ENABLED;
1206 }
1207 else if ((!strcmp(r->options, "checkpoint")) || (!strcmp(r->options, "rotate")))
1208 {
1209 _act_file_checkpoint(m, NULL, CHECKPOINT_FORCE);
1210 }
1211 }
1212
1213 static void
1214 _send_to_asl_store(aslmsg msg)
1215 {
1216 if ((global.asl_out_module != NULL) && ((global.asl_out_module->flags & MODULE_FLAG_ENABLED) == 0)) return;
1217
1218 if (store_has_logged) return;
1219 store_has_logged = true;
1220
1221 db_save_message(msg);
1222 }
1223
1224 static int
1225 _asl_out_process_message(asl_out_module_t *m, aslmsg msg)
1226 {
1227 asl_out_rule_t *r;
1228
1229 if (m == NULL) return 1;
1230 if (msg == NULL) return 1;
1231
1232 /* reset flag bit used for duplicate avoidance */
1233 for (r = m->ruleset; r != NULL; r = r->next)
1234 {
1235 if ((r->action == ACTION_FILE) || (r->action == ACTION_ASL_DIR) || (r->action == ACTION_ASL_FILE))
1236 {
1237 if (r->dst != NULL) r->dst->flags &= MODULE_FLAG_CLEAR_LOGGED;
1238 }
1239 }
1240
1241 for (r = m->ruleset; r != NULL; r = r->next)
1242 {
1243 if (r->query == NULL) continue;
1244
1245 /* ACTION_SET_FILE, ACTION_SET_PLIST, and ACTION_SET_PROF are handled independently */
1246 if ((r->action == ACTION_SET_FILE) || (r->action == ACTION_SET_PLIST) || (r->action == ACTION_SET_PROF)) continue;
1247
1248 /*
1249 * ACTION_CLAIM during processing is a filter. It will only be here if the option "only"
1250 * was supplied. In this case we test the message against the query. If it does not
1251 * match, we skip the message.
1252 */
1253 if (r->action == ACTION_CLAIM)
1254 {
1255 if ((asl_msg_cmp(r->query, (asl_msg_t *)msg) != 1)) return 0;
1256 }
1257
1258 if ((asl_msg_cmp(r->query, (asl_msg_t *)msg) == 1))
1259 {
1260 if (r->action == ACTION_NONE) continue;
1261 else if (r->action == ACTION_IGNORE) return 1;
1262 else if (r->action == ACTION_SKIP) return 0;
1263 else if (r->action == ACTION_ASL_STORE) _send_to_asl_store(msg);
1264 else if (r->action == ACTION_ACCESS) _act_access_control(m, r, msg);
1265 else if (r->action == ACTION_NOTIFY) _act_notify(m, r);
1266 else if (r->action == ACTION_BROADCAST) _act_broadcast(m, r, msg);
1267 else if (r->action == ACTION_FORWARD) _act_forward(m, r, msg);
1268 else if (r->action == ACTION_CONTROL) _act_control(m, r, msg);
1269 else if (r->action == ACTION_SET_PARAM) _act_out_set_param(m, r->options, true);
1270 else if ((r->action == ACTION_ASL_FILE) || (r->action == ACTION_ASL_DIR)) _act_store(m, r, msg);
1271 else if (r->action == ACTION_FILE) _act_file(m, r, msg);
1272 }
1273 }
1274
1275 return 0;
1276 }
1277
1278 void
1279 asl_out_message(aslmsg msg)
1280 {
1281 OSAtomicIncrement32(&global.asl_queue_count);
1282 asl_msg_retain((asl_msg_t *)msg);
1283
1284 dispatch_async(asl_action_queue, ^{
1285 int ignore = 0;
1286 const char *p;
1287 time_t now = time(NULL);
1288 asl_out_module_t *m = global.asl_out_module;
1289
1290 store_has_logged = false;
1291
1292 p = asl_get(msg, ASL_KEY_MODULE);
1293 if (p == NULL)
1294 {
1295 if ((action_asl_store_count == 0) || (asl_check_option(msg, ASL_OPT_STORE) == 1)) _send_to_asl_store(msg);
1296
1297 ignore = _asl_out_process_message(m, msg);
1298 if (ignore == 0)
1299 {
1300 if (m != NULL) m = m->next;
1301 while (m != NULL)
1302 {
1303 _asl_out_process_message(m, msg);
1304 m = m->next;
1305 }
1306 }
1307 }
1308 else
1309 {
1310 if (m != NULL) m = m->next;
1311 while (m != NULL)
1312 {
1313 if (!strcmp(p, m->name)) _asl_out_process_message(m, msg);
1314 m = m->next;
1315 }
1316 }
1317
1318 asl_msg_release((asl_msg_t *)msg);
1319 OSAtomicDecrement32(&global.asl_queue_count);
1320
1321 if ((now - sweep_time) >= IDLE_CLOSE)
1322 {
1323 _asl_action_close_idle_files(IDLE_CLOSE);
1324 sweep_time = now;
1325 }
1326 });
1327 }
1328
1329 static char *
1330 _asl_action_profile_test(asl_out_module_t *m, asl_out_rule_t *r)
1331 {
1332 const char *ident;
1333 asl_msg_t *profile;
1334 bool eval;
1335
1336 /* ident is first message key */
1337 asl_msg_fetch((asl_msg_t *)r->query, 0, &ident, NULL, NULL);
1338 if (ident == NULL)
1339 {
1340 r->action = ACTION_NONE;
1341 return NULL;
1342 }
1343
1344 profile = configuration_profile_to_asl_msg(ident);
1345 eval = (asl_msg_cmp(r->query, profile) == 1);
1346 _act_out_set_param(m, r->options, eval);
1347 asl_msg_release(profile);
1348
1349 return strdup(ident);
1350 }
1351
1352 static const char *
1353 _asl_action_file_test(asl_out_module_t *m, asl_out_rule_t *r)
1354 {
1355 const char *path;
1356 struct stat sb;
1357 int status;
1358 bool eval;
1359
1360 /* path is first message key */
1361 asl_msg_fetch((asl_msg_t *)r->query, 0, &path, NULL, NULL);
1362 if (path == NULL)
1363 {
1364 r->action = ACTION_NONE;
1365 return NULL;
1366 }
1367
1368 memset(&sb, 0, sizeof(struct stat));
1369 status = stat(path, &sb);
1370 eval = (status == 0);
1371 _act_out_set_param(m, r->options, eval);
1372
1373 return path;
1374 }
1375
1376 static void
1377 _asl_action_handle_file_change_notification(int t)
1378 {
1379 asl_out_module_t *m;
1380 asl_out_rule_t *r;
1381
1382 for (m = global.asl_out_module; m != NULL; m = m->next)
1383 {
1384 for (r = m->ruleset; r != NULL; r = r->next)
1385 {
1386 if (r->action == ACTION_SET_FILE)
1387 {
1388 asl_action_set_param_data_t *spdata = (asl_action_set_param_data_t *)r->private;
1389 if ((spdata != NULL) && (spdata->token == t))
1390 {
1391 _asl_action_file_test(m, r);
1392 return;
1393 }
1394 }
1395 else if (r->action == ACTION_SET_PLIST)
1396 {
1397 asl_action_set_param_data_t *spdata = (asl_action_set_param_data_t *)r->private;
1398 if ((spdata != NULL) && (spdata->token == t))
1399 {
1400 char *str = _asl_action_profile_test(m, r);
1401 free(str);
1402 return;
1403 }
1404 }
1405 else if (r->action == ACTION_SET_PROF)
1406 {
1407 asl_action_set_param_data_t *spdata = (asl_action_set_param_data_t *)r->private;
1408 if ((spdata != NULL) && (spdata->token == t))
1409 {
1410 char *str = _asl_action_profile_test(m, r);
1411 free(str);
1412 return;
1413 }
1414 }
1415 }
1416 }
1417
1418 asl_out_module_free(m);
1419 }
1420
1421 static void
1422 _asl_action_post_process_rule(asl_out_module_t *m, asl_out_rule_t *r)
1423 {
1424 if ((m == NULL) || (r == NULL)) return;
1425
1426 if (m != global.asl_out_module)
1427 {
1428 /* check if any previous module has used this destination */
1429 asl_out_module_t *n;
1430 bool search = true;
1431
1432 if ((r->dst != NULL) && (r->dst->path != NULL))
1433 {
1434 for (n = global.asl_out_module; search && (n != NULL) && (n != m); n = n->next)
1435 {
1436 asl_out_rule_t *s;
1437 for (s = n->ruleset; search && (s != NULL); s = s->next)
1438 {
1439 if (s->action == ACTION_OUT_DEST)
1440 {
1441 if ((s->dst != NULL) && (s->dst->path != NULL) && (!strcmp(r->dst->path, s->dst->path)))
1442 {
1443 /* rule r of module m is using previously used dst of rule s of module n */
1444 asl_out_dst_data_release(r->dst);
1445 r->dst = NULL;
1446
1447 if (r->action == ACTION_OUT_DEST)
1448 {
1449 char *str = NULL;
1450 asprintf(&str, "[Sender syslogd] [Level 5] [PID %u] [Message Configuration Notice:\nASL Module \"%s\" sharing output destination \"%s\" with ASL Module \"%s\".\nOutput parameters from ASL Module \"%s\" override any specified in ASL Module \"%s\".] [UID 0] [GID 0] [Facility syslog]", global.pid, m->name, s->dst->path, n->name, n->name, m->name);
1451 internal_log_message(str);
1452 free(str);
1453 }
1454 else
1455 {
1456 r->dst = asl_out_dst_data_retain(s->dst);
1457 }
1458
1459 search = false;
1460 }
1461 }
1462 }
1463 }
1464 }
1465 }
1466
1467 if (r->action == ACTION_SET_PARAM)
1468 {
1469 if (r->query == NULL) _act_out_set_param(m, r->options, true);
1470 }
1471 else if (r->action == ACTION_CLAIM)
1472 {
1473 /* becomes ACTION_SKIP in com.apple.asl config */
1474 if (m != global.asl_out_module)
1475 {
1476 asl_out_rule_t *rule = (asl_out_rule_t *)calloc(1, sizeof(asl_out_rule_t));
1477 if (rule != NULL)
1478 {
1479 char *str = NULL;
1480 asprintf(&str, "[Sender syslogd] [Level 5] [PID %u] [Message Configuration Notice:\nASL Module \"%s\" claims selected messages.\nThose messages may not appear in standard system log files or in the ASL database.] [UID 0] [GID 0] [Facility syslog]", global.pid, m->name);
1481 internal_log_message(str);
1482 free(str);
1483
1484 rule->query = asl_msg_copy(r->query);
1485 rule->action = ACTION_SKIP;
1486 rule->next = global.asl_out_module->ruleset;
1487 global.asl_out_module->ruleset = rule;
1488 }
1489
1490 /*
1491 * After adding ACTION_SKIP to com.apple.asl module, the claim becomes a no-op in this module
1492 * UNLESS the claim includes the option "only". In that case, the claim becomes a filter:
1493 * any messages that DO NOT match the claim are skipped by this module.
1494 */
1495 if (r->options == NULL) r->action = ACTION_NONE;
1496 else if (strcmp(r->options, "only") != 0) r->action = ACTION_NONE;
1497 }
1498 }
1499 else if (r->action == ACTION_ASL_STORE)
1500 {
1501 action_asl_store_count++;
1502 }
1503 else if (r->action == ACTION_ASL_DIR)
1504 {
1505 if (r->dst->private == NULL) r->dst->private = (asl_action_store_data_t *)calloc(1, sizeof(asl_action_store_data_t));
1506 }
1507 else if (r->action == ACTION_ASL_FILE)
1508 {
1509 if (r->dst->private == NULL)r->dst->private = (asl_action_store_data_t *)calloc(1, sizeof(asl_action_store_data_t));
1510 }
1511 else if (r->action == ACTION_FILE)
1512 {
1513 if (r->dst->private == NULL) r->dst->private = (asl_action_file_data_t *)calloc(1, sizeof(asl_action_file_data_t));
1514 if (r->dst->private != NULL) ((asl_action_file_data_t *)(r->dst->private))->fd = -1;
1515 }
1516 else if (r->action == ACTION_SET_PLIST)
1517 {
1518 char *ident =_asl_action_profile_test(m, r);
1519 char *notify_key = configuration_profile_create_notification_key(ident);
1520 free(ident);
1521
1522 if (notify_key != NULL)
1523 {
1524 int status, token;
1525 asl_action_set_param_data_t *spdata;
1526
1527 status = notify_register_dispatch(notify_key, &token, asl_action_queue, ^(int t){
1528 _asl_action_handle_file_change_notification(t);
1529 });
1530
1531 free(notify_key);
1532
1533 spdata = (asl_action_set_param_data_t *)calloc(1, sizeof(asl_action_set_param_data_t));
1534 if (spdata == NULL)
1535 {
1536 notify_cancel(token);
1537 }
1538 else
1539 {
1540 spdata->token = token;
1541 r->private = spdata;
1542 }
1543 }
1544 }
1545 else if (r->action == ACTION_SET_PROF)
1546 {
1547 char *ident =_asl_action_profile_test(m, r);
1548 char *notify_key = configuration_profile_create_notification_key(ident);
1549 free(ident);
1550
1551 if (notify_key != NULL)
1552 {
1553 int status, token;
1554 asl_action_set_param_data_t *spdata;
1555
1556 status = notify_register_dispatch(notify_key, &token, asl_action_queue, ^(int t){
1557 _asl_action_handle_file_change_notification(t);
1558 });
1559
1560 free(notify_key);
1561
1562 spdata = (asl_action_set_param_data_t *)calloc(1, sizeof(asl_action_set_param_data_t));
1563 if (spdata == NULL)
1564 {
1565 notify_cancel(token);
1566 }
1567 else
1568 {
1569 spdata->token = token;
1570 r->private = spdata;
1571 }
1572 }
1573 }
1574 else if (r->action == ACTION_SET_FILE)
1575 {
1576 char *notify_key;
1577 const char *path =_asl_action_file_test(m, r);
1578
1579 if (path != NULL)
1580 {
1581 asprintf(&notify_key, "%s%s", NOTIFY_PATH_SERVICE, path);
1582 if (notify_key != NULL)
1583 {
1584 int status, token;
1585 asl_action_set_param_data_t *spdata;
1586
1587 status = notify_register_dispatch(notify_key, &token, asl_action_queue, ^(int t){
1588 _asl_action_handle_file_change_notification(t);
1589 });
1590
1591 free(notify_key);
1592
1593 spdata = (asl_action_set_param_data_t *)calloc(1, sizeof(asl_action_set_param_data_t));
1594 if (spdata == NULL)
1595 {
1596 notify_cancel(token);
1597 }
1598 else
1599 {
1600 spdata->token = token;
1601 r->private = spdata;
1602 }
1603 }
1604 }
1605 }
1606 }
1607
1608 static void
1609 _asl_action_configure()
1610 {
1611 asl_out_rule_t *r;
1612 asl_out_module_t *m;
1613 uint32_t flags = 0;
1614
1615 if (global.asl_out_module == NULL) global.asl_out_module = asl_out_module_init();
1616 if (global.asl_out_module == NULL) return;
1617
1618 if (global.debug != 0)
1619 {
1620 FILE *dfp;
1621 if (global.debug_file == NULL) dfp = fopen(_PATH_SYSLOGD_LOG, "a");
1622 else dfp = fopen(global.debug_file, "a");
1623 if (dfp != NULL)
1624 {
1625 for (m = global.asl_out_module; m != NULL; m = m->next)
1626 {
1627 fprintf(dfp, "module: %s%s\n", (m->name == NULL) ? "<unknown>" : m->name, (m->flags & MODULE_FLAG_LOCAL) ? " (local)" : "");
1628 asl_out_module_print(dfp, m);
1629 fprintf(dfp, "\n");
1630 }
1631 fclose(dfp);
1632 }
1633 }
1634
1635 asldebug("%s: init\n", MY_ID);
1636
1637 action_asl_store_count = 0;
1638
1639 for (m = global.asl_out_module; m != NULL; m = m->next)
1640 {
1641 for (r = m->ruleset; r != NULL; r = r->next)
1642 {
1643 _asl_action_post_process_rule(m, r);
1644 if (r->dst != NULL) flags |= (r->dst->flags & (MODULE_FLAG_ROTATE | MODULE_FLAG_CRASHLOG));
1645 }
1646 }
1647
1648 if (global.debug != 0)
1649 {
1650 FILE *dfp;
1651 if (global.debug_file == NULL) dfp = fopen(_PATH_SYSLOGD_LOG, "a");
1652 else dfp = fopen(global.debug_file, "a");
1653 if (dfp != NULL)
1654 {
1655 for (m = global.asl_out_module; m != NULL; m = m->next)
1656 {
1657 fprintf(dfp, "module: %s%s\n", (m->name == NULL) ? "<unknown>" : m->name, (m->flags & MODULE_FLAG_LOCAL) ? " (local)" : "");
1658 asl_out_module_print(dfp, m);
1659 fprintf(dfp, "\n");
1660 }
1661 fclose(dfp);
1662 }
1663 }
1664
1665 sweep_time = time(NULL);
1666
1667 if (flags & MODULE_FLAG_ROTATE)
1668 {
1669 _act_file_checkpoint_all(CHECKPOINT_TEST);
1670 if (checkpoint_timer == NULL) _start_cycling();
1671 }
1672
1673 #if TARGET_OS_EMBEDDED
1674 if (flags & MODULE_FLAG_CRASHLOG) _crashlog_sentinel_init();
1675 #endif
1676 }
1677
1678 int
1679 asl_action_init(void)
1680 {
1681 static dispatch_once_t once;
1682
1683 dispatch_once(&once, ^{
1684 asl_action_queue = dispatch_queue_create("ASL Action Queue", NULL);
1685 #if TARGET_OS_EMBEDDED
1686 crashlog_queue = dispatch_queue_create("iOS CrashLog Queue", NULL);
1687 notify_register_dispatch(CRASH_MOVER_WILL_START_NOTIFICATION, &crashmover_token, asl_action_queue, ^(int unused){
1688 if (crashmover_state == 0)
1689 {
1690 asldebug("CrashMover active: suspending crashlog queue and closing files\n");
1691 crashmover_state = time(NULL);
1692 dispatch_suspend(crashlog_queue);
1693 _asl_action_close_idle_files(0);
1694 }
1695 });
1696 #endif
1697 });
1698
1699 _asl_action_configure();
1700
1701 return 0;
1702 }
1703
1704 /*
1705 * Free a module.
1706 */
1707 static void
1708 _asl_action_free_modules(asl_out_module_t *m)
1709 {
1710 asl_out_rule_t *r;
1711 asl_out_module_t *x;
1712
1713 /*
1714 * asl_common frees a list of modules with asl_out_module_free.
1715 * This loop frees the private data attached some modules.
1716 */
1717 for (x = m; x != NULL; x = x->next)
1718 {
1719 for (r = x->ruleset; r != NULL; r = r->next)
1720 {
1721 if ((r->action == ACTION_ASL_FILE) || (r->action == ACTION_ASL_DIR))
1722 {
1723 if (r->dst != NULL)
1724 {
1725 _asl_action_store_data_free((asl_action_store_data_t *)r->dst->private);
1726 r->dst->private = NULL;
1727 }
1728 }
1729 else if (r->action == ACTION_FILE)
1730 {
1731 if (r->dst != NULL)
1732 {
1733 asl_action_file_data_t *fdata = (asl_action_file_data_t *)r->dst->private;
1734 if (fdata != NULL)
1735 {
1736 /* flush repeat message if necessary */
1737 if (fdata->last_count > 0) _send_repeat_msg(r);
1738 _asl_action_file_data_free(fdata);
1739 r->dst->private = NULL;
1740 }
1741 }
1742 }
1743 else if (r->action == ACTION_SET_PLIST)
1744 {
1745 _asl_action_set_param_data_free((asl_action_set_param_data_t *)r->private);
1746 }
1747 else if (r->action == ACTION_SET_PROF)
1748 {
1749 _asl_action_set_param_data_free((asl_action_set_param_data_t *)r->private);
1750 }
1751 }
1752 }
1753
1754 asl_out_module_free(m);
1755 }
1756
1757 static int
1758 _asl_action_close_internal(void)
1759 {
1760 #if TARGET_OS_EMBEDDED
1761 dispatch_source_cancel(crashlog_sentinel_src);
1762 dispatch_release(crashlog_sentinel_src);
1763 crashlog_sentinel_src = NULL;
1764 close(crashlog_sentinel_fd);
1765 if (crashmover_state != 0)
1766 {
1767 dispatch_resume(crashlog_queue);
1768 crashmover_state = 0;
1769 }
1770
1771 /* wait for the crashlog_queue to flush before _asl_action_free_modules() */
1772 dispatch_sync(crashlog_queue, ^{ crashlog_sentinel_fd = -1; });
1773 #endif
1774
1775 _asl_action_free_modules(global.asl_out_module);
1776 global.asl_out_module = NULL;
1777 sweep_time = time(NULL);
1778
1779 return 0;
1780 }
1781
1782 static void
1783 _asl_action_close_idle_files(time_t idle_time)
1784 {
1785 asl_out_module_t *m;
1786 time_t now = time(NULL);
1787
1788 for (m = global.asl_out_module; m != NULL; m = m->next)
1789 {
1790 asl_out_rule_t *r;
1791
1792 for (r = m->ruleset; r != NULL; r = r->next)
1793 {
1794 if (idle_time == 0)
1795 {
1796 if ((r->dst != NULL) && (r->dst->flags & MODULE_FLAG_CRASHLOG))
1797 {
1798 _act_dst_close(r);
1799 if (r->action != ACTION_ASL_DIR) asl_out_dst_checkpoint(r->dst, CHECKPOINT_FORCE);
1800 }
1801 }
1802 else if ((r->action == ACTION_ASL_DIR) || (r->action == ACTION_ASL_FILE))
1803 {
1804 if (r->dst != NULL)
1805 {
1806 asl_action_store_data_t *sdata = (asl_action_store_data_t *)r->dst->private;
1807 if ((sdata != NULL) && (sdata->store != NULL) && ((now - sdata->last_time) >= idle_time)) _act_dst_close(r);
1808 }
1809 }
1810 else if (r->action == ACTION_FILE)
1811 {
1812 if (r->dst != NULL)
1813 {
1814 asl_action_file_data_t *fdata = (asl_action_file_data_t *)r->dst->private;
1815 if ((fdata != NULL) && (fdata->fd >= 0) && ((now - fdata->last_time) >= idle_time)) _act_dst_close(r);
1816 }
1817 }
1818 }
1819 }
1820 }
1821
1822 int
1823 asl_action_close(void)
1824 {
1825 dispatch_async(asl_action_queue, ^{
1826 _asl_action_close_internal();
1827 });
1828
1829 return 0;
1830 }
1831
1832 int
1833 asl_action_reset(void)
1834 {
1835 dispatch_async(asl_action_queue, ^{
1836 _asl_action_close_internal();
1837 asl_action_init();
1838 });
1839
1840 return 0;
1841 }
1842
1843 asl_out_module_t *
1844 _asl_action_module_with_name(const char *name)
1845 {
1846 asl_out_module_t *m;
1847
1848 if (global.asl_out_module == NULL) return NULL;
1849 if (name == NULL) return global.asl_out_module;
1850
1851 for (m = global.asl_out_module; m != NULL; m = m->next)
1852 {
1853 if ((m->name != NULL) && (!strcmp(m->name, name))) return m;
1854 }
1855
1856 return NULL;
1857 }
1858
1859 /*
1860 * called from control_message
1861 * Used to control modules dynamically.
1862 * Line format "@ module param [value ...]"
1863 *
1864 * Note this is synchronous on asl_action queue.
1865 */
1866 int
1867 asl_action_control_set_param(const char *s)
1868 {
1869 __block char **l;
1870 __block char *p;
1871 uint32_t count = 0;
1872
1873 if (s == NULL) return -1;
1874 if (s[0] == '\0') return 0;
1875
1876 /* skip '@' and whitespace */
1877 if (*s == '@') s++;
1878 while ((*s == ' ') || (*s == '\t')) s++;
1879
1880 l = explode(s, " \t");
1881 if (l != NULL) for (count = 0; l[count] != NULL; count++);
1882
1883 /* at least 2 parameters (l[0] = module, l[1] = param) required */
1884 if (count < 2) return -1;
1885
1886 if (global.asl_out_module == NULL)
1887 {
1888 asldebug("asl_action_control_set_param: no modules loaded\n");
1889 return -1;
1890 }
1891
1892 /* create / modify a module */
1893 if ((!strcasecmp(l[1], "define")) && (strcmp(l[0], "*")))
1894 {
1895 p = strdup(s);
1896 if (p == NULL)
1897 {
1898 asldebug("asl_action_control_set_param: memory allocation failed\n");
1899 return -1;
1900 }
1901
1902 dispatch_sync(asl_action_queue, ^{
1903 asl_out_module_t *m;
1904 asl_out_rule_t *r;
1905
1906 /* skip name, whitespace, "define" */
1907 while ((*p != ' ') && (*p != '\t')) p++;
1908 while ((*p == ' ') || (*p == '\t')) p++;
1909 while ((*p != ' ') && (*p != '\t')) p++;
1910
1911 m = _asl_action_module_with_name(l[0]);
1912 if (m == NULL)
1913 {
1914 asl_out_module_t *x;
1915
1916 m = asl_out_module_new(l[0]);
1917 for (x = global.asl_out_module; x->next != NULL; x = x->next);
1918 x->next = m;
1919 }
1920
1921 r = asl_out_module_parse_line(m, p);
1922 if (r != NULL)
1923 {
1924 _asl_action_post_process_rule(m, r);
1925 if ((r->dst != NULL) && (r->dst->flags & MODULE_FLAG_ROTATE))
1926 {
1927 _act_file_checkpoint_all(CHECKPOINT_TEST);
1928 if (checkpoint_timer == NULL) _start_cycling();
1929 }
1930 }
1931 });
1932
1933 free(p);
1934 free_string_list(l);
1935 return 0;
1936 }
1937
1938 dispatch_sync(asl_action_queue, ^{
1939 uint32_t intval;
1940 int do_all = 0;
1941 asl_out_module_t *m;
1942
1943 if (!strcmp(l[0], "*"))
1944 {
1945 do_all = 1;
1946 m = _asl_action_module_with_name(NULL);
1947 }
1948 else
1949 {
1950 m = _asl_action_module_with_name(l[0]);
1951 }
1952
1953 while (m != NULL)
1954 {
1955 if (!strcasecmp(l[1], "enable"))
1956 {
1957 intval = 1;
1958
1959 /* don't do enable for ASL_MODULE_NAME if input name is "*" */
1960 if ((do_all == 0) || (strcmp(m->name, ASL_MODULE_NAME)))
1961 {
1962 /* @ module enable {0|1} */
1963 if (count > 2) intval = atoi(l[2]);
1964
1965 if (intval == 0) m->flags &= ~MODULE_FLAG_ENABLED;
1966 else m->flags |= MODULE_FLAG_ENABLED;
1967 }
1968 }
1969 else if (!strcasecmp(l[1], "checkpoint"))
1970 {
1971 /* @ module checkpoint [file] */
1972 if (count > 2) _act_file_checkpoint(m, l[2], CHECKPOINT_FORCE);
1973 else _act_file_checkpoint(m, NULL, CHECKPOINT_FORCE);
1974 }
1975
1976 if (do_all == 1) m = m->next;
1977 else m = NULL;
1978 }
1979
1980 });
1981
1982 free_string_list(l);
1983 return 0;
1984 }
1985
1986 int
1987 asl_action_file_checkpoint(const char *module, const char *path)
1988 {
1989 /* Note this is synchronous on asl_action queue */
1990 dispatch_sync(asl_action_queue, ^{
1991 asl_out_module_t *m = _asl_action_module_with_name(module);
1992 _act_file_checkpoint(m, path, CHECKPOINT_FORCE);
1993 });
1994
1995 return 0;
1996 }