]> git.saurik.com Git - apple/syslog.git/blob - syslogd.tproj/asl_action.c
syslog-385.tar.gz
[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 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/socket.h>
28 #include <sys/un.h>
29 #include <sys/uio.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <fcntl.h>
35 #include <errno.h>
36 #include <netdb.h>
37 #include <notify.h>
38 #include <pthread.h>
39 #include <sys/acl.h>
40 #include <dirent.h>
41 #include <time.h>
42 #include <membership.h>
43 #include <configuration_profile.h>
44 #include "daemon.h"
45 #include <xpc/private.h>
46
47 #define _PATH_WALL "/usr/bin/wall"
48
49 #define MY_ID "asl_action"
50
51 #define MAX_FAILURES 5
52
53 #define ACTION_STATUS_ERROR -1
54 #define ACTION_STATUS_OK 0
55
56 #define IDLE_CLOSE 300
57
58 #define PRIVATE_FLAG_NO_CLOSE 0x00000001 /* File or Store is closed by a cancellation handler */
59
60 #define DST_CLOSE_CHECKPOINT 0
61 #define DST_CLOSE_DELETED 1
62 #define DST_CLOSE_ERROR 2
63 #define DST_CLOSE_IDLE 3
64 #define DST_CLOSE_SHUTDOWN 4
65 static const char *why_str[] =
66 {
67 "checkpoint",
68 "deleted",
69 "error",
70 "idle",
71 "shutdown"
72 };
73
74 #define forever for(;;)
75
76 static dispatch_queue_t asl_action_queue;
77 static dispatch_source_t checkpoint_timer;
78 static time_t sweep_time = 0;
79
80 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
81 #ifndef CRASH_MOVER_SERVICE
82 #define CRASH_MOVER_SERVICE "com.apple.crash_mover"
83 #endif
84 static dispatch_queue_t crashlog_queue;
85 static time_t crashmover_state = 0;
86 static int crashmover_token = -1;
87 #endif
88
89 typedef struct asl_file_data
90 {
91 uint64_t next_id;
92 asl_file_t *aslfile;
93 time_t last_time;
94 uint32_t flags;
95 uint32_t pending;
96 dispatch_source_t monitor;
97 } asl_action_asl_file_data_t;
98
99 typedef struct asl_store_data
100 {
101 FILE *storedata;
102 asl_file_t *aslfile;
103 uint64_t next_id;
104 time_t last_time;
105 uint32_t flags;
106 uint32_t pending;
107 uint32_t p_year;
108 uint32_t p_month;
109 uint32_t p_day;
110 dispatch_source_t storedata_monitor;
111 dispatch_source_t aslfile_monitor;
112 } asl_action_asl_store_data_t;
113
114 typedef struct file_data
115 {
116 int fd;
117 uint32_t flags;
118 time_t last_time;
119 uint32_t last_count;
120 uint32_t last_hash;
121 uint32_t pending;
122 char *last_msg;
123 dispatch_source_t dup_timer;
124 dispatch_source_t monitor;
125 } asl_action_file_data_t;
126
127 typedef struct set_param_data
128 {
129 int token;
130 } asl_action_set_param_data_t;
131
132 static int action_asl_store_count;
133 static bool store_has_logged;
134
135 extern void db_save_message(asl_msg_t *m);
136
137 /* forward */
138 static int _act_file_checkpoint_all(uint32_t force);
139 static void _asl_action_post_process_rule(asl_out_module_t *m, asl_out_rule_t *r);
140 static void _asl_action_close_idle_files(time_t idle_time);
141 static void _act_dst_close(asl_out_rule_t *r, int why);
142
143 static void
144 _act_out_set_param(asl_out_module_t *m, char *x, bool eval)
145 {
146 char *s = x;
147 char **l;
148 uint32_t count, intval;
149
150 l = explode(s, " \t");
151 if (l == NULL) return;
152
153 for (count = 0; l[count] != NULL; count++);
154 if (count == 0)
155 {
156 free_string_list(l);
157 return;
158 }
159
160 if (!strcasecmp(l[0], "enable"))
161 {
162 /* = enable [1|0] */
163 if (count < 2) intval = 1;
164 else intval = atoi(l[1]);
165
166 if (!eval) intval = (intval == 0) ? 1 : 0;
167
168 if (intval == 0) m->flags &= ~MODULE_FLAG_ENABLED;
169 else m->flags|= MODULE_FLAG_ENABLED;
170 free_string_list(l);
171 return;
172 }
173 else if (!strcasecmp(l[0], "disable"))
174 {
175 /* = disable [1|0] */
176 if (count < 2) intval = 1;
177 else intval = atoi(l[1]);
178
179 if (!eval) intval = (intval == 0) ? 1 : 0;
180
181 if (intval != 0) m->flags &= ~MODULE_FLAG_ENABLED;
182 else m->flags|= MODULE_FLAG_ENABLED;
183 free_string_list(l);
184 return;
185 }
186
187 free_string_list(l);
188
189 if (!strcmp(m->name, ASL_MODULE_NAME))
190 {
191 /* Other parameters may be set by com.apple.asl module */
192 control_set_param(x, eval);
193 }
194 }
195
196 static void
197 _act_notify(asl_out_module_t *m, asl_out_rule_t *r)
198 {
199 if (m == NULL) return;
200 if ((m->flags & MODULE_FLAG_ENABLED) == 0) return;
201
202 if (r == NULL) return;
203 if (r->options == NULL) return;
204
205 notify_post(r->options);
206 }
207
208 static void
209 _act_broadcast(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
210 {
211 #if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
212 FILE *pw;
213 const char *val;
214
215 if (m == NULL) return;
216 if ((m->flags & MODULE_FLAG_ENABLED) == 0) return;
217
218 if (m->name == NULL) return;
219 if (r == NULL) return;
220 if (msg == NULL) return;
221
222 /* only base module (asl.conf) may broadcast */
223 if (strcmp(m->name, ASL_MODULE_NAME)) return;
224
225 val = r->options;
226 if (val == NULL) val = asl_msg_get_val_for_key(msg, ASL_KEY_MSG);
227 if (val == NULL) return;
228
229 pw = popen(_PATH_WALL, "w");
230 if (pw == NULL)
231 {
232 asldebug("%s: error sending wall message: %s\n", MY_ID, strerror(errno));
233 return;
234 }
235
236 fprintf(pw, "%s", val);
237 pclose(pw);
238 #endif
239 }
240
241 static void
242 _act_set_key(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
243 {
244 /* Placeholder */
245 }
246
247 static void
248 _act_unset_key(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
249 {
250 /* Placeholder */
251 }
252
253 static void
254 _act_access_control(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
255 {
256 int32_t ruid, rgid;
257 char *p;
258
259 if (m == NULL) return;
260 if (m->name == NULL) return;
261 if (r == NULL) return;
262 if (msg == NULL) return;
263
264 /* only base module (asl.conf) may set access controls */
265 if (strcmp(m->name, ASL_MODULE_NAME)) return;
266
267 ruid = atoi(r->options);
268 rgid = -1;
269 p = strchr(r->options, ' ');
270 if (p == NULL) p = strchr(r->options, '\t');
271 if (p != NULL)
272 {
273 *p = '\0';
274 p++;
275 rgid = atoi(p);
276 }
277
278 if (ruid != -1) asl_msg_set_key_val(msg, ASL_KEY_READ_UID, r->options);
279 if (p != NULL)
280 {
281 if (rgid != -1) asl_msg_set_key_val(msg, ASL_KEY_READ_GID, p);
282 p--;
283 *p = ' ';
284 }
285 }
286
287 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
288 static void
289 _crashlog_queue_check(void)
290 {
291 /*
292 * Check whether the crashlog queue has been suspended for too long.
293 * We allow the crashlog quque to be suspended for 60 seconds.
294 * After that, we start logging again. This prevents syslogd from
295 * filling memory due to a suspended queue. CrashMover really shoud
296 * take no more than a second or two to finish.
297 */
298 if (crashmover_state == 0) return;
299 if ((time(NULL) - crashmover_state) <= 60) return;
300
301 asldebug("CrashMover timeout: resuming crashlog queue\n");
302 dispatch_resume(crashlog_queue);
303 crashmover_state = 0;
304 }
305 #endif
306
307 /*
308 * cover routine for asl_out_dst_file_create_open()
309 */
310 static int
311 _act_file_create_open(asl_out_dst_data_t *dst)
312 {
313 return asl_out_dst_file_create_open(dst, NULL);
314 }
315
316 /*
317 * Checks and creates (if required) a directory for an ASL data store.
318 */
319 static int
320 _asl_dir_create(asl_out_rule_t *r)
321 {
322 struct stat sb;
323 int status;
324
325 memset(&sb, 0, sizeof(struct stat));
326 status = stat(r->dst->path, &sb);
327 if (status == 0)
328 {
329 /* Store path must be a directory */
330 if (!S_ISDIR(sb.st_mode))
331 {
332 asldebug("_asl_dir_create: expected a directory at path %s\n", r->dst->path);
333 return -1;
334 }
335 }
336 else if (errno == ENOENT)
337 {
338 /* Directory doesn't exists - try to create it */
339 status = asl_out_mkpath(global.asl_out_module, r);
340 if (status != 0)
341 {
342 asldebug("_asl_dir_create: asl_out_mkpath failed: %s\n", r->dst->path);
343 return -1;
344 }
345 }
346 else
347 {
348 /* Unexpected stat error */
349 asldebug("_asl_dir_create: stat error %s\n", strerror(errno));
350 return -1;
351 }
352
353 return 0;
354 }
355
356 /*
357 * Close an ASL Directory StoreData file
358 * - cancel the dispatch source for the file
359 */
360 static void
361 _asl_dir_storedata_close(asl_out_rule_t *r)
362 {
363 asl_action_asl_store_data_t *as_data = (asl_action_asl_store_data_t *)r->dst->private;
364 if (as_data->storedata == NULL) return;
365
366 if (as_data->storedata_monitor == NULL)
367 {
368 /*
369 * This should never happen, but _asl_dir_storedata_open allows
370 * dispatch_source_create to fail silently. If there is no dispatch
371 * source, we just close the file.
372 */
373 asldebug("close ASL storedata fd %d\n", fileno(as_data->storedata));
374 fclose(as_data->storedata);
375 }
376 else
377 {
378 /*
379 * The storedata_monitor cancel handler will close the file.
380 */
381 dispatch_source_cancel(as_data->storedata_monitor);
382 dispatch_release(as_data->storedata_monitor);
383
384 }
385
386 asldebug("_asl_dir_storedata_close %p\n", as_data->storedata);
387 as_data->storedata = NULL;
388 }
389
390 /*
391 * Open an ASL Directory StoreData file
392 * - check directory existance and create it if necessary
393 * - check for StoreData file and create it (with given xid) if necessary.
394 * - create a dispatch source to watch for StoreData file deletion
395 */
396 static int
397 _asl_dir_storedata_open(asl_out_rule_t *r, uint64_t xid)
398 {
399 struct stat sb;
400 int status;
401 char dstpath[MAXPATHLEN];
402
403 asl_action_asl_store_data_t *as_data = (asl_action_asl_store_data_t *)r->dst->private;
404 if (as_data->storedata != NULL) return 0;
405
406 status = _asl_dir_create(r);
407 if (status != 0)
408 {
409 asldebug("_asl_dir_storedata_open: No directory at path %s\n", r->dst->path);
410 return -1;
411 }
412
413 /* StoreData file is not open */
414 snprintf(dstpath, sizeof(dstpath), "%s/%s", r->dst->path, FILE_ASL_STORE_DATA);
415
416 memset(&sb, 0, sizeof(struct stat));
417 status = stat(dstpath, &sb);
418 if (status == 0)
419 {
420 /* StoreData file exists */
421 as_data->storedata = fopen(dstpath, "r+");
422 if (as_data->storedata == NULL)
423 {
424 asldebug("_asl_dir_storedata_open: fopen existing %s: %s\n", dstpath, strerror(errno));
425 return -1;
426 }
427 }
428 else if (errno == ENOENT)
429 {
430 /*
431 * StoreData file does not exist.
432 * Create a new StoreData with a given xid.
433 */
434 //TODO: This should return a failure if there are any ASL files.
435 /* that would require reading the directory, thus a performance hit */
436 as_data->storedata = fopen(dstpath, "w+");
437 if (as_data->storedata == NULL)
438 {
439 asldebug("_asl_dir_storedata_open: fopen new %s: %s\n", dstpath, strerror(errno));
440 return -1;
441 }
442
443 uint64_t xout = asl_core_htonq(xid);
444 status = fwrite(&xout, sizeof(uint64_t), 1, as_data->storedata);
445 if (status != 1)
446 {
447 asldebug("_asl_dir_storedata_open: storedata write failed %d %s\n", errno, strerror(errno));
448 return -1;
449 }
450
451 #if !TARGET_OS_SIMULATOR
452 if (chown(dstpath, r->dst->uid[0], r->dst->gid[0]) != 0)
453 {
454 asldebug("_asl_dir_storedata_open: chown %d %d new %s: %s\n", dstpath, r->dst->uid[0], r->dst->gid[0], strerror(errno));
455 return -1;
456 }
457 #endif
458 }
459 else
460 {
461 /* Unexpected stat error */
462 asldebug("_asl_dir_storedata_open: stat error %s\n", strerror(errno));
463 return -1;
464 }
465
466 /* create storedata_monitor */
467 int fd = fileno(as_data->storedata);
468 FILE *sdfp = as_data->storedata;
469
470 as_data->storedata_monitor = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, DISPATCH_VNODE_DELETE, asl_action_queue);
471 if (as_data->storedata_monitor != NULL)
472 {
473 dispatch_source_set_event_handler(as_data->storedata_monitor, ^{
474 _act_dst_close(r, DST_CLOSE_DELETED);
475 });
476
477 dispatch_source_set_cancel_handler(as_data->storedata_monitor, ^{
478 asldebug("cancel/close ASL storedata fd %d\n", fd);
479 fclose(sdfp);
480 });
481
482 dispatch_resume(as_data->storedata_monitor);
483 }
484
485 asldebug("_asl_dir_storedata_open ASL storedata %s fd %d\n", dstpath, fd);
486 return 0;
487 }
488
489 /*
490 * Close an ASL Directory ASL file
491 * - cancel the dispatch source for the file
492 * - clears file name and timestamp in asl_action_asl_store_data_t structue
493 */
494 static void
495 _asl_dir_today_close(asl_out_rule_t *r)
496 {
497 asl_action_asl_store_data_t *as_data = (asl_action_asl_store_data_t *)r->dst->private;
498 if (as_data->aslfile == NULL) return;
499
500 if (as_data->pending != 0)
501 {
502 char *str = NULL;
503 asprintf(&str, "[Sender syslogd] [Level 4] [PID %u] [Facility syslog] [Message ASL Store %s was closed with %d pending messages]", global.pid, r->dst->current_name, as_data->pending);
504 internal_log_message(str);
505 free(str);
506 }
507
508 if (as_data->aslfile_monitor == NULL)
509 {
510 /*
511 * This should never happen, but _asl_dir_today_open allows
512 * dispatch_source_create to fail silently. If there is no dispatch
513 * source, we just close the file.
514 */
515 asldebug("close ASL fd %d\n", fileno(as_data->aslfile->store));
516 asl_file_close(as_data->aslfile);
517 }
518 else
519 {
520 /*
521 * The aslfile_monitor cancel handler will close the file.
522 */
523 dispatch_source_cancel(as_data->aslfile_monitor);
524 dispatch_release(as_data->aslfile_monitor);
525 as_data->aslfile_monitor = NULL;
526 }
527
528 as_data->p_year = 0;
529 as_data->p_month = 0;
530 as_data->p_day = 0;
531
532 free(r->dst->current_name);
533 r->dst->current_name = NULL;
534
535 as_data->aslfile = NULL;
536 }
537
538 /*
539 * Check file size.
540 */
541 static int
542 _act_checkpoint(asl_out_rule_t *r, uint32_t force)
543 {
544 char tmpcurrent_name[MAXPATHLEN], *fn;
545 bool size_only = false;
546
547 if (r == NULL) return 0;
548 if (r->dst == NULL) return 0;
549
550 fn = r->dst->current_name;
551 if (fn == NULL)
552 {
553 if (r->dst->path == NULL) return 0;
554 asl_dst_make_current_name(r->dst, 0, tmpcurrent_name, sizeof(tmpcurrent_name));
555 fn = tmpcurrent_name;
556 }
557
558 if ((force == CHECKPOINT_TEST) || (r->dst->flags & MODULE_FLAG_SIZE_ONLY))
559 {
560 size_only = true;
561 }
562
563 if (size_only && (r->dst->file_max == 0)) return 0;
564
565 if ((r->dst->size == 0) || (r->dst->timestamp == 0))
566 {
567 struct stat sb;
568
569 memset(&sb, 0, sizeof(struct stat));
570
571 if (stat(fn, &sb) < 0)
572 {
573 if (errno == ENOENT) return 0;
574 return -1;
575 }
576
577 if (r->dst->timestamp == 0) r->dst->timestamp = sb.st_birthtimespec.tv_sec;
578 if (r->dst->timestamp == 0) r->dst->timestamp = sb.st_mtimespec.tv_sec;
579 r->dst->size = sb.st_size;
580 }
581
582 if (size_only && (r->dst->size < r->dst->file_max)) return 0;
583
584 if (r->dst->flags & MODULE_FLAG_BASESTAMP)
585 {
586 _act_dst_close(r, DST_CLOSE_CHECKPOINT);
587 }
588 else
589 {
590 char srcpath[MAXPATHLEN];
591 char dstpath[MAXPATHLEN];
592
593 snprintf(srcpath, sizeof(srcpath), "%s", fn);
594
595 r->dst->timestamp = time(NULL);
596 asl_dst_make_current_name(r->dst, MODULE_FLAG_BASESTAMP, dstpath, sizeof(dstpath));
597
598 _act_dst_close(r, DST_CLOSE_CHECKPOINT);
599 if (strneq(srcpath, dstpath))
600 {
601 rename(srcpath, dstpath);
602 asldebug("CHECKPOINT RENAME %s %s\n", srcpath, dstpath);
603 }
604 }
605
606 r->dst->timestamp = 0;
607 r->dst->size = 0;
608 return 1;
609 }
610
611 /*
612 * Open today's ASL file in an ASL directory
613 * - Checks date and closes a currently open file if it has the wrong date
614 * - Opens today's file
615 */
616 static int
617 _asl_dir_today_open(asl_out_rule_t *r, const time_t *tick)
618 {
619 int status;
620 mode_t mask;
621 struct tm ctm;
622 time_t now;
623
624 if (r == NULL) return -1;
625 if (r->dst == NULL) return -1;
626
627 status = _asl_dir_create(r);
628 if (status != 0)
629 {
630 asldebug("_asl_dir_today_open: No directory at path %s\n", r->dst->path);
631 return -1;
632 }
633
634 asl_action_asl_store_data_t *as_data = (asl_action_asl_store_data_t *)r->dst->private;
635
636 memset(&ctm, 0, sizeof(struct tm));
637 if (tick == NULL)
638 {
639 now = time(NULL);
640 tick = (const time_t *)&now;
641 }
642
643 if (localtime_r(tick, &ctm) == NULL)
644 {
645 asldebug("_asl_dir_today_open: localtime_r error %s\n", strerror(errno));
646 return -1;
647 }
648
649 /* checks file_max and closes if required */
650 status = _act_checkpoint(r, CHECKPOINT_TEST);
651 if (status == 1) asl_trigger_aslmanager();
652
653 if (as_data->aslfile != NULL)
654 {
655 /* Check the date */
656 if ((as_data->p_year == ctm.tm_year) && (as_data->p_month == ctm.tm_mon) && (as_data->p_day == ctm.tm_mday)) return 0;
657
658 /* Wrong date, close the current file */
659 _asl_dir_today_close(r);
660 }
661
662 /* Open data file */
663
664 if (r->dst->flags & MODULE_FLAG_BASESTAMP)
665 {
666 char tstamp[32];
667
668 if (tick == NULL)
669 {
670 now = time(NULL);
671 tick = (const time_t *)&now;
672 }
673
674 asl_make_timestamp(now, r->dst->style_flags, tstamp, sizeof(tstamp));
675 asprintf(&(r->dst->current_name), "%s/%s.asl", r->dst->path, tstamp);
676 }
677 else
678 {
679 asprintf(&(r->dst->current_name), "%s/%d.%02d.%02d.asl", r->dst->path, ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday);
680 }
681
682
683 if (r->dst->current_name == NULL)
684 {
685 asldebug("_asl_dir_today_open: asprintf error %s\n", strerror(errno));
686 return -1;
687 }
688
689 #if TARGET_OS_SIMULATOR
690 uid_t uid = -1;
691 gid_t gid = -1;
692 #else
693 uid_t uid = r->dst->uid[0];
694 gid_t gid = r->dst->gid[0];
695 #endif
696
697 mask = umask(0);
698 status = asl_file_open_write(r->dst->current_name, (r->dst->mode & 00666), uid, gid, &(as_data->aslfile));
699 umask(mask);
700
701 if (status != ASL_STATUS_OK)
702 {
703 asldebug("_asl_dir_today_open: asl_file_open_write %s error %s\n", r->dst->current_name, asl_core_error(status));
704 free(r->dst->current_name);
705 r->dst->current_name = NULL;
706 return -1;
707 }
708
709 if (fseek(as_data->aslfile->store, 0, SEEK_END) != 0)
710 {
711 asldebug("_asl_dir_today_open: fseek %s error %s\n", r->dst->current_name, strerror(errno));
712 free(r->dst->current_name);
713 r->dst->current_name = NULL;
714 return -1;
715 }
716
717 as_data->p_year = ctm.tm_year;
718 as_data->p_month = ctm.tm_mon;
719 as_data->p_day = ctm.tm_mday;
720
721 /* create aslfile_monitor */
722 int fd = fileno(as_data->aslfile->store);
723 asl_file_t *aslf = as_data->aslfile;
724
725 as_data->aslfile_monitor = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, DISPATCH_VNODE_DELETE, asl_action_queue);
726 if (as_data->aslfile_monitor != NULL)
727 {
728 dispatch_source_set_event_handler(as_data->aslfile_monitor, ^{
729 _act_dst_close(r, DST_CLOSE_DELETED);
730 });
731
732 dispatch_source_set_cancel_handler(as_data->aslfile_monitor, ^{
733 asldebug("cancel/close ASL file fd %d\n", fd);
734 asl_file_close(aslf);
735 });
736
737 dispatch_resume(as_data->aslfile_monitor);
738 }
739
740 asldebug("_asl_dir_today_open ASL file %s fd %d\n", r->dst->current_name, fd);
741
742 return 0;
743 }
744
745 static void
746 _asl_file_close(asl_out_rule_t *r)
747 {
748 if (r == NULL) return;
749 if (r->dst == NULL) return;
750
751 asl_action_asl_file_data_t *af_data = (asl_action_asl_file_data_t *)r->dst->private;
752 if (af_data->aslfile == NULL) return;
753
754 if (af_data->pending != 0)
755 {
756 char *str = NULL;
757 asprintf(&str, "[Sender syslogd] [Level 4] [PID %u] [Facility syslog] [Message ASL File %s was closed with %d pending messages]", global.pid, r->dst->current_name, af_data->pending);
758 internal_log_message(str);
759 free(str);
760 }
761
762 if (af_data->monitor == NULL)
763 {
764 /*
765 * This should never happen, but _asl_file_open allows
766 * dispatch_source_create to fail silently. If there is no dispatch
767 * source, we just close the file.
768 */
769 asldebug("close ASL fd %d\n", fileno(af_data->aslfile->store));
770 asl_file_close(af_data->aslfile);
771 }
772 else
773 {
774 /*
775 * The monitor cancel handler will close the file.
776 */
777 dispatch_source_cancel(af_data->monitor);
778 dispatch_release(af_data->monitor);
779 af_data->monitor = NULL;
780 }
781
782 af_data->aslfile = NULL;
783 }
784
785 static int
786 _asl_file_open(asl_out_rule_t *r)
787 {
788 int fd, status;
789
790 if (r == NULL) return -1;
791 if (r->dst == NULL) return -1;
792
793 asl_action_asl_file_data_t *af_data = (asl_action_asl_file_data_t *)r->dst->private;
794 if (af_data->aslfile != NULL) return 0;
795
796 /* create path if necessary */
797 status = asl_out_mkpath(global.asl_out_module, r);
798 if (status != 0)
799 {
800 asldebug("_asl_file_open: asl_out_mkpath %s failed\n", r->dst->path);
801 return -1;
802 }
803
804 fd = _act_file_create_open(r->dst);
805 if (fd < 0)
806 {
807 asldebug("_asl_file_open: _act_file_create_open %s failed %d %s\n", r->dst->current_name, errno, strerror(errno));
808 return -1;
809 }
810
811 close(fd);
812
813 if (r->dst->current_name == NULL) return -1;
814
815 status = asl_file_open_write(r->dst->current_name, 0, -1, -1, &(af_data->aslfile));
816 if (status != ASL_STATUS_OK)
817 {
818 asldebug("_asl_file_open: asl_file_open_write %s failed %d %s\n", r->dst->current_name, errno, strerror(errno));
819 return -1;
820 }
821
822 /* create monitor */
823 fd = fileno(af_data->aslfile->store);
824 asl_file_t *aslf = af_data->aslfile;
825
826 af_data->monitor = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, DISPATCH_VNODE_DELETE, asl_action_queue);
827 if (af_data->monitor != NULL)
828 {
829 dispatch_source_set_event_handler(af_data->monitor, ^{
830 _act_dst_close(r, DST_CLOSE_DELETED);
831 });
832
833 dispatch_source_set_cancel_handler(af_data->monitor, ^{
834 asldebug("cancel/close ASL file fd %d\n", fd);
835 asl_file_close(aslf);
836 });
837
838 dispatch_resume(af_data->monitor);
839 }
840
841 asldebug("_asl_file_open ASL file %s fd %d\n", r->dst->current_name, fd);
842 return 0;
843 }
844
845 static void
846 _text_file_close(asl_out_rule_t *r)
847 {
848 asl_action_file_data_t *f_data = (asl_action_file_data_t *)r->dst->private;
849 if (f_data->fd < 0) return;
850
851 if (f_data->pending != 0)
852 {
853 char *str = NULL;
854 asprintf(&str, "[Sender syslogd] [Level 4] [PID %u] [Facility syslog] [Message File %s was closed with %d pending messages]", global.pid, r->dst->current_name, f_data->pending);
855 internal_log_message(str);
856 free(str);
857 }
858
859 if (f_data->monitor == NULL)
860 {
861 /*
862 * This should never happen, but _text_file_open allows
863 * dispatch_source_create to fail silently. If there is no dispatch
864 * source, we just close the file.
865 */
866 asldebug("close fd %d\n", f_data->fd);
867 close(f_data->fd);
868 }
869 else
870 {
871 /*
872 * The monitor cancel handler will close the file.
873 */
874 dispatch_source_cancel(f_data->monitor);
875 dispatch_release(f_data->monitor);
876 f_data->monitor = NULL;
877 }
878
879 f_data->fd = -1;
880 }
881
882 static int
883 _text_file_open(asl_out_rule_t *r)
884 {
885 asl_action_file_data_t *f_data = (asl_action_file_data_t *)r->dst->private;
886
887 if (f_data->fd < 0)
888 {
889 f_data->fd = _act_file_create_open(r->dst);
890 if (f_data->fd < 0)
891 {
892 /*
893 * lazy path creation: create path and retry
894 * _act_file_create_open does not create the path
895 * so we do it here.
896 */
897 int status = asl_out_mkpath(global.asl_out_module, r);
898 if (status != 0) return -1;
899
900 f_data->fd = _act_file_create_open(r->dst);
901 }
902
903 if (f_data->fd < 0) return -1;
904
905 f_data->monitor = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, f_data->fd, DISPATCH_VNODE_DELETE, asl_action_queue);
906 if (f_data->monitor != NULL)
907 {
908 int ffd = f_data->fd;
909
910 dispatch_source_set_event_handler(f_data->monitor, ^{
911 asldebug("dispatch_source DISPATCH_VNODE_DELETE fd %d\n", ffd);
912 _act_dst_close(r, DST_CLOSE_DELETED);
913 });
914
915 dispatch_source_set_cancel_handler(f_data->monitor, ^{
916 asldebug("cancel/close file fd %d\n", ffd);
917 close(ffd);
918 });
919
920 dispatch_resume(f_data->monitor);
921 }
922 }
923
924 return 0;
925 }
926
927 static int
928 _act_dst_open(asl_out_rule_t *r, const time_t *tick, uint64_t xid)
929 {
930 if (r == NULL) return -1;
931 if (r->dst == NULL) return -1;
932 if (r->dst->private == NULL) return -1;
933
934 if (r->action == ACTION_ASL_DIR)
935 {
936 if (_asl_dir_today_open(r, tick) != 0)
937 {
938 asldebug("_act_dst_open:_asl_dir_today_open %s FAILED!\n", r->dst->path);
939 return -1;
940 }
941
942 if (_asl_dir_storedata_open(r, xid) != 0)
943 {
944 asldebug("_act_dst_open:_asl_dir_storedata_open %s FAILED! Closing today file\n", r->dst->path);
945 _asl_dir_today_close(r);
946 return -1;
947 }
948
949 return 0;
950 }
951
952 if (r->action == ACTION_ASL_FILE)
953 {
954 return _asl_file_open(r);
955 }
956
957 if (r->action == ACTION_FILE)
958 {
959 return _text_file_open(r);
960 }
961
962 return -1;
963 }
964
965 static void
966 _act_dst_close(asl_out_rule_t *r, int why)
967 {
968 if (r == NULL) return;
969 if (r->dst == NULL) return;
970 if (r->dst->private == NULL) return;
971
972 if (r->action == ACTION_ASL_DIR)
973 {
974 asldebug("_act_dst_close: %s ASL DIR %s\n", why_str[why], r->dst->path);
975 if (why != DST_CLOSE_CHECKPOINT) _asl_dir_storedata_close(r);
976 _asl_dir_today_close(r);
977 }
978 else if (r->action == ACTION_ASL_FILE)
979 {
980 asldebug("_act_dst_close: %s ASL FILE %s\n", why_str[why], (r->dst->current_name == NULL) ? r->dst->path : r->dst->current_name);
981 _asl_file_close(r);
982 }
983 else if (r->action == ACTION_FILE)
984 {
985 asldebug("_act_dst_close: %s FILE %s\n", why_str[why], (r->dst->current_name == NULL) ? r->dst->path : r->dst->current_name);
986 _text_file_close(r);
987 }
988 }
989
990 static uint32_t
991 _act_store_file_setup(asl_out_module_t *m, asl_out_rule_t *r)
992 {
993 uint32_t status;
994 asl_action_asl_file_data_t *af_data;
995
996 if (r == NULL) return ASL_STATUS_INVALID_STORE;
997 if (r->dst == NULL) return ASL_STATUS_INVALID_STORE;
998 if (r->dst->private == NULL) return ASL_STATUS_INVALID_STORE;
999
1000 af_data = (asl_action_asl_file_data_t *)r->dst->private;
1001 if (af_data->aslfile != NULL)
1002 {
1003 af_data->next_id++;
1004 return ASL_STATUS_OK;
1005 }
1006
1007 if (_act_dst_open(r, NULL, 0) != 0) return ASL_STATUS_WRITE_FAILED;
1008
1009 status = asl_file_read_set_position(af_data->aslfile, ASL_FILE_POSITION_LAST);
1010 if (status != ASL_STATUS_OK)
1011 {
1012 asldebug("_act_store_file_setup: asl_file_read_set_position failed %d %s\n", status, asl_core_error(status));
1013 _act_dst_close(r, DST_CLOSE_ERROR);
1014 return status;
1015 }
1016
1017 af_data->next_id = af_data->aslfile->cursor_xid + 1;
1018 if (fseek(af_data->aslfile->store, 0, SEEK_END) != 0)
1019 {
1020 asldebug("_act_store_file_setup: fseek failed %d %s\n", errno, strerror(errno));
1021 _act_dst_close(r, DST_CLOSE_ERROR);
1022 return ASL_STATUS_WRITE_FAILED;
1023 }
1024
1025 return ASL_STATUS_OK;
1026 }
1027
1028 /*
1029 * _act_store_dir_setup
1030 *
1031 * Opens StoreData file and today's file
1032 * Reads ASL Message ID from StoreData file
1033 * Writes ASL Message ID + 1 to StoreData file
1034 */
1035 static uint32_t
1036 _act_store_dir_setup(asl_out_module_t *m, asl_out_rule_t *r, time_t tick)
1037 {
1038 uint64_t xid;
1039 int status;
1040 asl_action_asl_store_data_t *as_data;
1041
1042 if (r == NULL) return ASL_STATUS_INVALID_STORE;
1043 if (r->dst == NULL) return ASL_STATUS_INVALID_STORE;
1044 if (r->dst->private == NULL) return ASL_STATUS_INVALID_STORE;
1045 if (r->dst->path == NULL) return ASL_STATUS_INVALID_STORE;
1046
1047 as_data = (asl_action_asl_store_data_t *)r->dst->private;
1048
1049 if (_act_dst_open(r, NULL, as_data->next_id) != 0)
1050 {
1051 asldebug("_act_store_dir_setup: _act_dst_open %s failed\n", r->dst->path);
1052 return ASL_STATUS_WRITE_FAILED;
1053 }
1054
1055 /* get / set message id from StoreData file */
1056 xid = 0;
1057 rewind(as_data->storedata);
1058 if (fread(&xid, sizeof(uint64_t), 1, as_data->storedata) != 1)
1059 {
1060 asldebug("_act_store_dir_setup: storedata read failed %d %s\n", errno, strerror(errno));
1061 _act_dst_close(r, DST_CLOSE_ERROR);
1062 return ASL_STATUS_READ_FAILED;
1063 }
1064
1065 xid = asl_core_ntohq(xid);
1066 xid++;
1067 as_data->next_id = xid;
1068
1069 xid = asl_core_htonq(xid);
1070 rewind(as_data->storedata);
1071 status = fwrite(&xid, sizeof(uint64_t), 1, as_data->storedata);
1072 if (status != 1)
1073 {
1074 asldebug("_act_store_dir_setup: storedata write failed %d %s\n", errno, strerror(errno));
1075 _act_dst_close(r, DST_CLOSE_ERROR);
1076 return ASL_STATUS_WRITE_FAILED;
1077 }
1078
1079 fflush(as_data->storedata);
1080
1081 if (fseek(as_data->aslfile->store, 0, SEEK_END) != 0)
1082 {
1083 asldebug("_act_store_dir_setup: aslfile fseek failed %d %s\n", errno, strerror(errno));
1084 _act_dst_close(r, DST_CLOSE_ERROR);
1085 return ASL_STATUS_FAILED;
1086 }
1087
1088 return ASL_STATUS_OK;
1089 }
1090
1091 static void
1092 _asl_action_asl_store_data_free(asl_action_asl_store_data_t *as_data)
1093 {
1094 if (as_data == NULL) return;
1095 free(as_data);
1096 }
1097
1098 static void
1099 _asl_action_asl_file_data_free(asl_action_asl_file_data_t *af_data)
1100 {
1101 if (af_data == NULL) return;
1102 free(af_data);
1103 }
1104
1105 static void
1106 _asl_action_file_data_free(asl_action_file_data_t *f_data)
1107 {
1108 if (f_data == NULL) return;
1109
1110 if (f_data->dup_timer != NULL)
1111 {
1112 if (f_data->last_count == 0)
1113 {
1114 /*
1115 * The timer exists, but last_count is zero, so the timer is suspended.
1116 * Sources must not be released in when suspended.
1117 * So we resume it so that we can release it.
1118 */
1119 dispatch_resume(f_data->dup_timer);
1120 }
1121
1122 dispatch_release(f_data->dup_timer);
1123 }
1124
1125 free(f_data->last_msg);
1126 free(f_data);
1127 }
1128
1129 static void
1130 _asl_action_set_param_data_free(asl_action_set_param_data_t *spdata)
1131 {
1132 if (spdata != NULL) notify_cancel(spdata->token);
1133 free(spdata);
1134 }
1135
1136 static void
1137 _asl_action_save_failed(const char *where, asl_out_module_t *m, asl_out_rule_t *r, uint32_t status)
1138 {
1139 if (r->dst->flags & MODULE_FLAG_SOFT_WRITE) return;
1140
1141 r->dst->fails++;
1142 asldebug("%s: %s save to %s failed: %s\n", where, m->name, r->dst->path, asl_core_error(status));
1143
1144 /* disable further activity after multiple failures */
1145 if (r->dst->fails > MAX_FAILURES)
1146 {
1147 char *str = NULL;
1148 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));
1149 internal_log_message(str);
1150 free(str);
1151
1152 if (r->action == ACTION_ASL_DIR) _asl_action_asl_store_data_free((asl_action_asl_store_data_t *)r->dst->private);
1153 else if (r->action == ACTION_ASL_FILE) _asl_action_asl_file_data_free((asl_action_asl_file_data_t *)r->dst->private);
1154 else if (r->action == ACTION_FILE) _asl_action_file_data_free((asl_action_file_data_t *)r->dst->private);
1155
1156 r->dst->private = NULL;
1157 r->action = ACTION_NONE;
1158 }
1159 }
1160
1161 /*
1162 * Save a message in an ASL file.
1163 */
1164 static int
1165 _act_store_file(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
1166 {
1167 asl_action_asl_file_data_t *af_data;
1168 uint32_t status;
1169 uint64_t mid;
1170
1171 if (r == NULL) return ACTION_STATUS_ERROR;
1172 if (r->dst == NULL) return ACTION_STATUS_ERROR;
1173 if (r->dst->private == NULL) return ACTION_STATUS_ERROR;
1174
1175 af_data = (asl_action_asl_file_data_t *)r->dst->private;
1176 if (af_data->pending > 0) af_data->pending--;
1177
1178 status = _act_store_file_setup(m, r);
1179 if (status == ASL_STATUS_OK)
1180 {
1181 af_data->last_time = time(NULL);
1182
1183 r->dst->fails = 0;
1184 mid = af_data->next_id;
1185
1186 /* save message to file and update dst size */
1187 status = asl_file_save(af_data->aslfile, msg, &mid);
1188 if (status == ASL_STATUS_OK)
1189 {
1190 r->dst->size = af_data->aslfile->file_size;
1191
1192 if (_act_checkpoint(r, CHECKPOINT_TEST) == 1) asl_trigger_aslmanager();
1193 }
1194 }
1195
1196 if (status != ASL_STATUS_OK)
1197 {
1198 _asl_action_save_failed("_act_store_file", m, r, status);
1199 return ACTION_STATUS_ERROR;
1200 }
1201
1202 return ACTION_STATUS_OK;
1203 }
1204
1205 /*
1206 * Save a message in an ASL data store.
1207 */
1208 static int
1209 _act_store_dir(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
1210 {
1211 asl_action_asl_store_data_t *as_data;
1212 uint32_t status;
1213 uint64_t mid;
1214 const char *val;
1215 time_t tick;
1216
1217 if (r == NULL) return ACTION_STATUS_ERROR;
1218 if (r->dst == NULL) return ACTION_STATUS_ERROR;
1219 if (r->dst->private == NULL) return ACTION_STATUS_ERROR;
1220
1221 as_data = (asl_action_asl_store_data_t *)r->dst->private;
1222 if (as_data->pending > 0) as_data->pending--;
1223
1224 val = asl_msg_get_val_for_key(msg, ASL_KEY_TIME);
1225 if (val == NULL) return ACTION_STATUS_ERROR;
1226
1227 tick = atol(val);
1228
1229 status = _act_store_dir_setup(m, r, tick);
1230 if (status == ASL_STATUS_OK)
1231 {
1232 as_data->last_time = time(NULL);
1233
1234 r->dst->fails = 0;
1235 mid = as_data->next_id;
1236 status = asl_file_save(as_data->aslfile, msg, &mid);
1237 if (status == ASL_STATUS_OK) r->dst->size = as_data->aslfile->file_size;
1238 else asldebug("_act_store_dir asl_file_save FAILED %s\n", asl_core_error(status));
1239 //TODO: checkpoint here?
1240 /*
1241 * Currently, the checkpoint is in _asl_dir_today_open().
1242 * Moving it here would be in keeping with the way that
1243 * _act_store_file() and _act_file_final() do checkpoints.
1244 */
1245 }
1246
1247 if (status != ASL_STATUS_OK)
1248 {
1249 _asl_action_save_failed("_act_store_dir", m, r, status);
1250 return ACTION_STATUS_ERROR;
1251 }
1252
1253 return ACTION_STATUS_OK;
1254 }
1255
1256 static void
1257 _act_store_final(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
1258 {
1259 if (r->action == ACTION_ASL_DIR) _act_store_dir(m, r, msg);
1260 else _act_store_file(m, r, msg);
1261 }
1262
1263 /*
1264 * Save a message to an ASL format file (ACTION_ASL_FILE)
1265 * or to an ASL directory (ACTION_ASL_DIR).
1266 */
1267 static void
1268 _act_store(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
1269 {
1270 if (r == NULL) return;
1271 if (r->dst == NULL) return;
1272 if (msg == NULL) return;
1273 if (m == NULL) return;
1274 if ((m->flags & MODULE_FLAG_ENABLED) == 0) return;
1275
1276 if (r->dst->flags & MODULE_FLAG_HAS_LOGGED) return;
1277
1278 r->dst->flags |= MODULE_FLAG_HAS_LOGGED;
1279 if (r->action == ACTION_ASL_DIR)
1280 {
1281 asl_action_asl_store_data_t *as_data = (asl_action_asl_store_data_t *)r->dst->private;
1282 if (as_data != NULL) as_data->pending++;
1283 }
1284 else if (r->action == ACTION_ASL_FILE)
1285 {
1286 asl_action_asl_file_data_t *af_data = (asl_action_asl_file_data_t *)r->dst->private;
1287 if (af_data != NULL) af_data->pending++;
1288 }
1289
1290 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
1291 if (r->dst->flags & MODULE_FLAG_CRASHLOG)
1292 {
1293 _crashlog_queue_check();
1294 asl_msg_retain(msg);
1295 dispatch_async(crashlog_queue, ^{
1296 dispatch_async(asl_action_queue, ^{
1297 _act_store_final(m, r, msg);
1298 asl_msg_release(msg);
1299 });
1300 });
1301 return;
1302 }
1303 #endif
1304
1305 _act_store_final(m, r, msg);
1306 }
1307
1308 static int
1309 _send_repeat_msg(asl_out_rule_t *r)
1310 {
1311 asl_action_file_data_t *f_data;
1312 char vt[32], *msg;
1313 int len, status;
1314 time_t now = time(NULL);
1315
1316 if (r == NULL) return -1;
1317 if (r->dst == NULL) return -1;
1318 if (r->dst->private == NULL) return -1;
1319
1320 f_data = (asl_action_file_data_t *)r->dst->private;
1321
1322 free(f_data->last_msg);
1323 f_data->last_msg = NULL;
1324
1325 if (f_data->last_count == 0) return 0;
1326
1327 /* stop the timer */
1328 dispatch_suspend(f_data->dup_timer);
1329
1330 memset(vt, 0, sizeof(vt));
1331 ctime_r(&now, vt);
1332 vt[19] = '\0';
1333
1334 msg = NULL;
1335 asprintf(&msg, "%s --- last message repeated %u time%s ---\n", vt + 4, f_data->last_count, (f_data->last_count == 1) ? "" : "s");
1336 f_data->last_count = 0;
1337 f_data->last_time = now;
1338 if (msg == NULL) return -1;
1339
1340 if (f_data->fd < 0) f_data->fd = _act_file_create_open(r->dst);
1341
1342 len = strlen(msg);
1343 status = write(f_data->fd, msg, len);
1344 free(msg);
1345
1346 if ((status < 0) || (status < len))
1347 {
1348 asldebug("%s: error writing repeat message (%s): %s\n", MY_ID, r->dst->current_name, strerror(errno));
1349 return -1;
1350 }
1351
1352 return 0;
1353 }
1354
1355 static void
1356 _start_cycling()
1357 {
1358 struct timespec midnight;
1359 struct tm t;
1360 time_t x;
1361
1362 x = time(NULL);
1363
1364 if (checkpoint_timer != NULL) return;
1365
1366 localtime_r(&x, &t);
1367
1368 t.tm_sec = 0;
1369 t.tm_min = 0;
1370 t.tm_hour = 0;
1371 t.tm_mday++;
1372
1373 x = mktime(&t);
1374 midnight.tv_sec = x;
1375 midnight.tv_nsec = 0;
1376
1377 checkpoint_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, asl_action_queue);
1378 dispatch_source_set_timer(checkpoint_timer, dispatch_walltime(&midnight, 0), NSEC_PER_SEC * SEC_PER_DAY, 0);
1379 dispatch_source_set_event_handler(checkpoint_timer, ^{ _act_file_checkpoint_all(CHECKPOINT_FORCE); });
1380 dispatch_resume(checkpoint_timer);
1381 }
1382
1383 /* check if a module path (mpath) matches a user path (upath) */
1384 static bool
1385 _act_file_equal(const char *mpath, const char *upath)
1386 {
1387 const char *slash;
1388
1389 /* NULL upath means user wants to match all files */
1390 if (upath == NULL) return true;
1391
1392 if (mpath == NULL) return false;
1393
1394 /* check for exact match */
1395 if (!strcmp(mpath, upath)) return true;
1396
1397 /* upath may be the last component of mpath */
1398 slash = strrchr(mpath, '/');
1399 if (slash == NULL) return false;
1400
1401 if (!strcmp(slash + 1, upath)) return true;
1402 return false;
1403 }
1404
1405 static int
1406 _act_file_checkpoint(asl_out_module_t *m, const char *path, uint32_t force)
1407 {
1408 asl_out_rule_t *r;
1409 int did_checkpoint = 0;
1410
1411 if (m == NULL) return 0;
1412
1413 for (r = m->ruleset; r != NULL; r = r->next)
1414 {
1415 if ((r->action == ACTION_FILE) || (r->action == ACTION_ASL_FILE))
1416 {
1417 if (r->dst->flags & MODULE_FLAG_ROTATE)
1418 {
1419 if (_act_file_equal(r->dst->path, path))
1420 {
1421 if (_act_checkpoint(r, force) > 0)
1422 {
1423 did_checkpoint = 1;
1424 _act_dst_close(r, DST_CLOSE_CHECKPOINT);
1425 }
1426 }
1427 }
1428 }
1429 }
1430
1431 return did_checkpoint;
1432 }
1433
1434 static int
1435 _act_file_checkpoint_all(uint32_t force)
1436 {
1437 asl_out_module_t *m;
1438 int did_checkpoint = 0;
1439
1440 for (m = global.asl_out_module; m != NULL; m = m->next)
1441 {
1442 if (_act_file_checkpoint(m, NULL, force) > 0) did_checkpoint = 1;
1443 }
1444
1445 asl_trigger_aslmanager();
1446
1447 return did_checkpoint;
1448 }
1449
1450 /*
1451 * Save a message in a plain text file.
1452 */
1453 static void
1454 _act_file_final(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
1455 {
1456 asl_action_file_data_t *f_data;
1457 int is_dup;
1458 uint32_t len, msg_hash = 0;
1459 char *str;
1460 time_t now;
1461
1462 if (r == NULL) return;
1463 if (r->dst == NULL) return;
1464 if (r->dst->private == NULL) return;
1465
1466 f_data = (asl_action_file_data_t *)r->dst->private;
1467 if (f_data->pending > 0) f_data->pending--;
1468
1469 /*
1470 * If print format is std, bsd, or msg, then skip messages with
1471 * no ASL_KEY_MSG, or without a value for it.
1472 */
1473 if (r->dst->flags & MODULE_FLAG_STD_BSD_MSG)
1474 {
1475 const char *msgval = NULL;
1476 if (asl_msg_lookup(msg, ASL_KEY_MSG, &msgval, NULL) != 0) return;
1477 if (msgval == NULL) return;
1478 }
1479
1480 now = time(NULL);
1481
1482 is_dup = 0;
1483
1484 str = asl_format_message(msg, r->dst->fmt, r->dst->tfmt, ASL_ENCODE_SAFE, &len);
1485
1486 if (r->dst->flags & MODULE_FLAG_COALESCE)
1487 {
1488 if (f_data->dup_timer == NULL)
1489 {
1490 /* create a timer to flush dups on this file */
1491 f_data->dup_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, asl_action_queue);
1492 dispatch_source_set_event_handler(f_data->dup_timer, ^{ _send_repeat_msg(r); });
1493 }
1494
1495 if ((global.bsd_max_dup_time > 0) && (str != NULL) && (f_data->last_msg != NULL))
1496 {
1497 msg_hash = asl_core_string_hash(str + 16, len - 16);
1498 if ((f_data->last_hash == msg_hash) && (!strcmp(f_data->last_msg, str + 16)))
1499 {
1500 if ((now - f_data->last_time) < global.bsd_max_dup_time) is_dup = 1;
1501 }
1502 }
1503 }
1504
1505 if (is_dup == 1)
1506 {
1507 if (f_data->last_count == 0)
1508 {
1509 /* start the timer */
1510 dispatch_source_set_timer(f_data->dup_timer, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * global.bsd_max_dup_time), DISPATCH_TIME_FOREVER, 0);
1511 dispatch_resume(f_data->dup_timer);
1512 }
1513
1514 f_data->last_count++;
1515 }
1516 else
1517 {
1518 if (_act_dst_open(r, NULL, 0) != 0)
1519 {
1520 _asl_action_save_failed("_act_file", m, r, ASL_STATUS_FAILED);
1521 free(str);
1522 return;
1523 }
1524 else
1525 {
1526 r->dst->fails = 0;
1527 }
1528
1529 /*
1530 * The current message is not a duplicate. If f_data->last_count > 0
1531 * we need to write a "last message repeated N times" log entry.
1532 * _send_repeat_msg will free last_msg and do nothing if
1533 * last_count == 0, but we test and free here to avoid a function call.
1534 */
1535 if (f_data->last_count > 0)
1536 {
1537 _send_repeat_msg(r);
1538 }
1539 else
1540 {
1541 free(f_data->last_msg);
1542 f_data->last_msg = NULL;
1543 }
1544
1545 if (str != NULL) f_data->last_msg = strdup(str + 16);
1546
1547 f_data->last_hash = msg_hash;
1548 f_data->last_count = 0;
1549 f_data->last_time = now;
1550
1551 if ((str != NULL) && (len > 1))
1552 {
1553 /* write line to file and update dst size */
1554 size_t bytes = write(f_data->fd, str, len - 1);
1555 if (bytes > 0) r->dst->size += bytes;
1556
1557 if (_act_checkpoint(r, CHECKPOINT_TEST) == 1) asl_trigger_aslmanager();
1558 }
1559 }
1560
1561 free(str);
1562 }
1563
1564 static void
1565 _act_file(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
1566 {
1567 asl_action_file_data_t *f_data;
1568
1569 if (r == NULL) return;
1570 if (msg == NULL) return;
1571 if (m == NULL) return;
1572 if ((m->flags & MODULE_FLAG_ENABLED) == 0) return;
1573 if (r->dst == NULL) return;
1574 if (r->dst->private == NULL) return;
1575
1576 if (r->dst->flags & MODULE_FLAG_HAS_LOGGED) return;
1577
1578 r->dst->flags |= MODULE_FLAG_HAS_LOGGED;
1579 f_data = (asl_action_file_data_t *)r->dst->private;
1580 if (f_data != NULL) f_data->pending++;
1581
1582 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
1583 if (r->dst->flags & MODULE_FLAG_CRASHLOG)
1584 {
1585 _crashlog_queue_check();
1586 asl_msg_retain(msg);
1587 dispatch_async(crashlog_queue, ^{
1588 dispatch_async(asl_action_queue, ^{
1589 _act_file_final(m, r, msg);
1590 asl_msg_release(msg);
1591 });
1592 });
1593 return;
1594 }
1595 #endif
1596
1597 _act_file_final(m, r, msg);
1598 }
1599
1600 static void
1601 _act_forward(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
1602 {
1603 /* To do: <rdar://problem/6130747> Add a "forward" action to asl.conf */
1604 }
1605
1606 static void
1607 _act_control(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
1608 {
1609 const char *p;
1610
1611 if (m == NULL) return;
1612 if (r == NULL) return;
1613
1614 p = asl_msg_get_val_for_key(msg, ASL_KEY_MODULE);
1615
1616 if (r->options == NULL) return;
1617
1618 if (!strcmp(r->options, "enable"))
1619 {
1620 m->flags |= MODULE_FLAG_ENABLED;
1621 }
1622 else if (!strcmp(r->options, "disable"))
1623 {
1624 m->flags &= ~MODULE_FLAG_ENABLED;
1625 }
1626 else if ((!strcmp(r->options, "checkpoint")) || (!strcmp(r->options, "rotate")))
1627 {
1628 _act_file_checkpoint(m, NULL, CHECKPOINT_FORCE);
1629 }
1630 }
1631
1632 static void
1633 _send_to_asl_store(asl_msg_t *msg)
1634 {
1635 if ((global.asl_out_module != NULL) && ((global.asl_out_module->flags & MODULE_FLAG_ENABLED) == 0)) return;
1636
1637 if (store_has_logged) return;
1638 store_has_logged = true;
1639
1640 db_save_message(msg);
1641 }
1642
1643 static int
1644 _asl_out_process_message(asl_out_module_t *m, asl_msg_t *msg)
1645 {
1646 asl_out_rule_t *r;
1647
1648 if (m == NULL) return 1;
1649 if (msg == NULL) return 1;
1650
1651 /* reset flag bit used for duplicate avoidance */
1652 for (r = m->ruleset; r != NULL; r = r->next)
1653 {
1654 if ((r->action == ACTION_FILE) || (r->action == ACTION_ASL_DIR) || (r->action == ACTION_ASL_FILE))
1655 {
1656 if (r->dst != NULL) r->dst->flags &= MODULE_FLAG_CLEAR_LOGGED;
1657 }
1658 }
1659
1660 for (r = m->ruleset; r != NULL; r = r->next)
1661 {
1662 if (r->query == NULL) continue;
1663
1664 /* ACTION_SET_FILE, ACTION_SET_PLIST, and ACTION_SET_PROF are handled independently */
1665 if ((r->action == ACTION_SET_FILE) || (r->action == ACTION_SET_PLIST) || (r->action == ACTION_SET_PROF)) continue;
1666
1667 /*
1668 * ACTION_CLAIM during processing is a filter. It will only be here if the option "only"
1669 * was supplied. In this case we test the message against the query. If it does not
1670 * match, we skip the message.
1671 */
1672 if (r->action == ACTION_CLAIM)
1673 {
1674 if ((asl_msg_cmp(r->query, msg) != 1)) return 0;
1675 }
1676
1677 if ((asl_msg_cmp(r->query, msg) == 1))
1678 {
1679 if (r->action == ACTION_NONE) continue;
1680 else if (r->action == ACTION_IGNORE) return 1;
1681 else if (r->action == ACTION_SKIP) return 0;
1682 else if (r->action == ACTION_ASL_STORE) _send_to_asl_store(msg);
1683 else if (r->action == ACTION_ACCESS) _act_access_control(m, r, msg);
1684 else if (r->action == ACTION_SET_KEY) _act_set_key(m, r, msg);
1685 else if (r->action == ACTION_UNSET_KEY) _act_unset_key(m, r, msg);
1686 else if (r->action == ACTION_NOTIFY) _act_notify(m, r);
1687 else if (r->action == ACTION_BROADCAST) _act_broadcast(m, r, msg);
1688 else if (r->action == ACTION_FORWARD) _act_forward(m, r, msg);
1689 else if (r->action == ACTION_CONTROL) _act_control(m, r, msg);
1690 else if (r->action == ACTION_SET_PARAM) _act_out_set_param(m, r->options, true);
1691 else if ((r->action == ACTION_ASL_FILE) || (r->action == ACTION_ASL_DIR)) _act_store(m, r, msg);
1692 else if (r->action == ACTION_FILE) _act_file(m, r, msg);
1693 }
1694 }
1695
1696 return 0;
1697 }
1698
1699 void
1700 asl_out_message(asl_msg_t *msg, int64_t msize)
1701 {
1702 OSAtomicIncrement32(&global.asl_queue_count);
1703 asl_msg_retain(msg);
1704
1705 dispatch_async(asl_action_queue, ^{
1706 int ignore = 0;
1707 const char *p;
1708 time_t now = time(NULL);
1709 asl_out_module_t *m = global.asl_out_module;
1710
1711 store_has_logged = false;
1712
1713 p = asl_msg_get_val_for_key(msg, ASL_KEY_MODULE);
1714 if (p == NULL)
1715 {
1716 if ((action_asl_store_count == 0) || (asl_check_option(msg, ASL_OPT_STORE) == 1)) _send_to_asl_store(msg);
1717
1718 ignore = _asl_out_process_message(m, msg);
1719 if (ignore == 0)
1720 {
1721 if (m != NULL) m = m->next;
1722 while (m != NULL)
1723 {
1724 _asl_out_process_message(m, msg);
1725 m = m->next;
1726 }
1727 }
1728 }
1729 else
1730 {
1731 if (m != NULL) m = m->next;
1732 while (m != NULL)
1733 {
1734 if (!strcmp(p, m->name)) _asl_out_process_message(m, msg);
1735 m = m->next;
1736 }
1737 }
1738
1739 p = asl_msg_get_val_for_key(msg, ASL_KEY_FINAL_NOTIFICATION);
1740 if (p != NULL) asl_msg_set_key_val(msg, ASL_KEY_FREE_NOTE, p);
1741
1742 /* chain to the next output module (done this way to make queue size accounting easier */
1743 #if !TARGET_OS_SIMULATOR
1744 if (global.bsd_out_enabled) bsd_out_message(msg, msize);
1745 else OSAtomicAdd64(-1ll * msize, &global.memory_size);
1746 #else
1747 OSAtomicAdd64(-1ll * msize, &global.memory_size);
1748 #endif
1749
1750 asl_msg_release(msg);
1751 OSAtomicDecrement32(&global.asl_queue_count);
1752
1753 if ((now - sweep_time) >= IDLE_CLOSE)
1754 {
1755 _asl_action_close_idle_files(IDLE_CLOSE);
1756 sweep_time = now;
1757 }
1758 });
1759 }
1760
1761 static char *
1762 _asl_action_profile_test(asl_out_module_t *m, asl_out_rule_t *r)
1763 {
1764 const char *ident;
1765 asl_msg_t *profile;
1766 bool eval;
1767
1768 /* ident is first message key */
1769 asl_msg_fetch(r->query, 0, &ident, NULL, NULL);
1770 if (ident == NULL)
1771 {
1772 r->action = ACTION_NONE;
1773 return NULL;
1774 }
1775
1776 profile = configuration_profile_to_asl_msg(ident);
1777 eval = (asl_msg_cmp(r->query, profile) == 1);
1778 _act_out_set_param(m, r->options, eval);
1779 asl_msg_release(profile);
1780
1781 return strdup(ident);
1782 }
1783
1784 static const char *
1785 _asl_action_file_test(asl_out_module_t *m, asl_out_rule_t *r)
1786 {
1787 const char *path;
1788 struct stat sb;
1789 int status;
1790 bool eval;
1791
1792 /* path is first message key */
1793 asl_msg_fetch(r->query, 0, &path, NULL, NULL);
1794 if (path == NULL)
1795 {
1796 r->action = ACTION_NONE;
1797 return NULL;
1798 }
1799
1800 memset(&sb, 0, sizeof(struct stat));
1801 status = stat(path, &sb);
1802 eval = (status == 0);
1803 _act_out_set_param(m, r->options, eval);
1804
1805 return path;
1806 }
1807
1808 static void
1809 _asl_action_handle_file_change_notification(int t)
1810 {
1811 asl_out_module_t *m;
1812 asl_out_rule_t *r;
1813
1814 for (m = global.asl_out_module; m != NULL; m = m->next)
1815 {
1816 for (r = m->ruleset; r != NULL; r = r->next)
1817 {
1818 if (r->action == ACTION_SET_FILE)
1819 {
1820 asl_action_set_param_data_t *spdata = (asl_action_set_param_data_t *)r->private;
1821 if ((spdata != NULL) && (spdata->token == t))
1822 {
1823 _asl_action_file_test(m, r);
1824 return;
1825 }
1826 }
1827 else if (r->action == ACTION_SET_PLIST)
1828 {
1829 asl_action_set_param_data_t *spdata = (asl_action_set_param_data_t *)r->private;
1830 if ((spdata != NULL) && (spdata->token == t))
1831 {
1832 char *str = _asl_action_profile_test(m, r);
1833 free(str);
1834 return;
1835 }
1836 }
1837 else if (r->action == ACTION_SET_PROF)
1838 {
1839 asl_action_set_param_data_t *spdata = (asl_action_set_param_data_t *)r->private;
1840 if ((spdata != NULL) && (spdata->token == t))
1841 {
1842 char *str = _asl_action_profile_test(m, r);
1843 free(str);
1844 return;
1845 }
1846 }
1847 }
1848 }
1849
1850 asl_out_module_free(m);
1851 }
1852
1853 static void
1854 _asl_action_post_process_rule(asl_out_module_t *m, asl_out_rule_t *r)
1855 {
1856 if ((m == NULL) || (r == NULL)) return;
1857
1858 if (m != global.asl_out_module)
1859 {
1860 /* check if any previous module has used this destination */
1861 asl_out_module_t *n;
1862 bool search = true;
1863
1864 if ((r->dst != NULL) && (r->dst->path != NULL))
1865 {
1866 for (n = global.asl_out_module; search && (n != NULL) && (n != m); n = n->next)
1867 {
1868 asl_out_rule_t *s;
1869 for (s = n->ruleset; search && (s != NULL); s = s->next)
1870 {
1871 if (s->action == ACTION_OUT_DEST)
1872 {
1873 if ((s->dst != NULL) && (s->dst->path != NULL) && (!strcmp(r->dst->path, s->dst->path)))
1874 {
1875 /* rule r of module m is using previously used dst of rule s of module n */
1876 asl_out_dst_data_release(r->dst);
1877 r->dst = NULL;
1878
1879 if (r->action == ACTION_OUT_DEST)
1880 {
1881 char *str = NULL;
1882 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);
1883 internal_log_message(str);
1884 free(str);
1885 }
1886 else
1887 {
1888 r->dst = asl_out_dst_data_retain(s->dst);
1889 }
1890
1891 search = false;
1892 }
1893 }
1894 }
1895 }
1896 }
1897 }
1898
1899 if (r->action == ACTION_SET_PARAM)
1900 {
1901 if (r->query == NULL) _act_out_set_param(m, r->options, true);
1902 }
1903 else if (r->action == ACTION_CLAIM)
1904 {
1905 /* becomes ACTION_SKIP in com.apple.asl config */
1906 if (m != global.asl_out_module)
1907 {
1908 asl_out_rule_t *rule = (asl_out_rule_t *)calloc(1, sizeof(asl_out_rule_t));
1909 if (rule != NULL)
1910 {
1911 char *str = NULL;
1912 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);
1913 internal_log_message(str);
1914 free(str);
1915
1916 rule->query = asl_msg_copy(r->query);
1917 rule->action = ACTION_SKIP;
1918 rule->next = global.asl_out_module->ruleset;
1919 global.asl_out_module->ruleset = rule;
1920 }
1921
1922 /*
1923 * After adding ACTION_SKIP to com.apple.asl module, the claim becomes a no-op in this module
1924 * UNLESS the claim includes the option "only". In that case, the claim becomes a filter:
1925 * any messages that DO NOT match the claim are skipped by this module.
1926 */
1927 if (r->options == NULL) r->action = ACTION_NONE;
1928 else if (strcmp(r->options, "only") != 0) r->action = ACTION_NONE;
1929 }
1930 }
1931 else if (r->action == ACTION_ASL_STORE)
1932 {
1933 action_asl_store_count++;
1934 }
1935 else if (r->action == ACTION_ASL_DIR)
1936 {
1937 if (r->dst->private == NULL) r->dst->private = (asl_action_asl_store_data_t *)calloc(1, sizeof(asl_action_asl_store_data_t));
1938 }
1939 else if (r->action == ACTION_ASL_FILE)
1940 {
1941 if (r->dst->private == NULL)r->dst->private = (asl_action_asl_file_data_t *)calloc(1, sizeof(asl_action_asl_file_data_t));
1942 }
1943 else if (r->action == ACTION_FILE)
1944 {
1945 if (r->dst->private == NULL) r->dst->private = (asl_action_file_data_t *)calloc(1, sizeof(asl_action_file_data_t));
1946 if (r->dst->private != NULL) ((asl_action_file_data_t *)(r->dst->private))->fd = -1;
1947 }
1948 else if (r->action == ACTION_SET_PLIST)
1949 {
1950 char *ident =_asl_action_profile_test(m, r);
1951 char *notify_key = configuration_profile_create_notification_key(ident);
1952 free(ident);
1953
1954 if (notify_key != NULL)
1955 {
1956 int status, token;
1957 asl_action_set_param_data_t *spdata;
1958
1959 status = notify_register_dispatch(notify_key, &token, asl_action_queue, ^(int t){
1960 _asl_action_handle_file_change_notification(t);
1961 });
1962
1963 free(notify_key);
1964
1965 spdata = (asl_action_set_param_data_t *)calloc(1, sizeof(asl_action_set_param_data_t));
1966 if (spdata == NULL)
1967 {
1968 notify_cancel(token);
1969 }
1970 else
1971 {
1972 spdata->token = token;
1973 r->private = spdata;
1974 }
1975 }
1976 }
1977 else if (r->action == ACTION_SET_PROF)
1978 {
1979 char *ident =_asl_action_profile_test(m, r);
1980 char *notify_key = configuration_profile_create_notification_key(ident);
1981 free(ident);
1982
1983 if (notify_key != NULL)
1984 {
1985 int status, token;
1986 asl_action_set_param_data_t *spdata;
1987
1988 status = notify_register_dispatch(notify_key, &token, asl_action_queue, ^(int t){
1989 _asl_action_handle_file_change_notification(t);
1990 });
1991
1992 free(notify_key);
1993
1994 spdata = (asl_action_set_param_data_t *)calloc(1, sizeof(asl_action_set_param_data_t));
1995 if (spdata == NULL)
1996 {
1997 notify_cancel(token);
1998 }
1999 else
2000 {
2001 spdata->token = token;
2002 r->private = spdata;
2003 }
2004 }
2005 }
2006 else if (r->action == ACTION_SET_FILE)
2007 {
2008 char *notify_key;
2009 const char *path =_asl_action_file_test(m, r);
2010
2011 if (path != NULL)
2012 {
2013 asprintf(&notify_key, "%s%s", NOTIFY_PATH_SERVICE, path);
2014 if (notify_key != NULL)
2015 {
2016 int status, token;
2017 asl_action_set_param_data_t *spdata;
2018
2019 status = notify_register_dispatch(notify_key, &token, asl_action_queue, ^(int t){
2020 _asl_action_handle_file_change_notification(t);
2021 });
2022
2023 free(notify_key);
2024
2025 spdata = (asl_action_set_param_data_t *)calloc(1, sizeof(asl_action_set_param_data_t));
2026 if (spdata == NULL)
2027 {
2028 notify_cancel(token);
2029 }
2030 else
2031 {
2032 spdata->token = token;
2033 r->private = spdata;
2034 }
2035 }
2036 }
2037 }
2038 }
2039
2040 static void
2041 _asl_action_configure()
2042 {
2043 asl_out_rule_t *r;
2044 asl_out_module_t *m;
2045 uint32_t flags = 0;
2046
2047 if (global.asl_out_module == NULL) global.asl_out_module = asl_out_module_init();
2048 if (global.asl_out_module == NULL) return;
2049
2050 asldebug("%s: init\n", MY_ID);
2051
2052 action_asl_store_count = 0;
2053
2054 for (m = global.asl_out_module; m != NULL; m = m->next)
2055 {
2056 for (r = m->ruleset; r != NULL; r = r->next)
2057 {
2058 _asl_action_post_process_rule(m, r);
2059 if (r->dst != NULL) flags |= (r->dst->flags & (MODULE_FLAG_ROTATE | MODULE_FLAG_CRASHLOG));
2060 }
2061 }
2062
2063 if (global.debug != 0)
2064 {
2065 FILE *dfp;
2066 if (global.debug_file == NULL) dfp = fopen(_PATH_SYSLOGD_LOG, "a");
2067 else dfp = fopen(global.debug_file, "a");
2068 if (dfp != NULL)
2069 {
2070 for (m = global.asl_out_module; m != NULL; m = m->next)
2071 {
2072 fprintf(dfp, "module: %s%s\n", (m->name == NULL) ? "<unknown>" : m->name, (m->flags & MODULE_FLAG_LOCAL) ? " (local)" : "");
2073 asl_out_module_print(dfp, m);
2074 fprintf(dfp, "\n");
2075 }
2076 fclose(dfp);
2077 }
2078 }
2079
2080 sweep_time = time(NULL);
2081
2082 if (flags & MODULE_FLAG_ROTATE)
2083 {
2084 _act_file_checkpoint_all(CHECKPOINT_TEST);
2085 if (checkpoint_timer == NULL) _start_cycling();
2086 }
2087 }
2088
2089 int
2090 asl_action_init(void)
2091 {
2092 static dispatch_once_t once;
2093
2094 dispatch_once(&once, ^{
2095 asl_action_queue = dispatch_queue_create("ASL Action Queue", NULL);
2096 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
2097 crashlog_queue = dispatch_queue_create("iOS CrashLog Queue", NULL);
2098 notify_register_dispatch(CRASH_MOVER_SERVICE, &crashmover_token, asl_action_queue, ^(int unused) {
2099 uint64_t cmstate = 0;
2100 uint64_t oldstate = (crashmover_state == 0) ? 0llu : 1llu;
2101
2102 uint32_t status = notify_get_state(crashmover_token, &cmstate);
2103 if (status == 0)
2104 {
2105 if (cmstate != oldstate)
2106 {
2107 crashmover_state = 0;
2108 if (cmstate == 1) crashmover_state = time(NULL);
2109
2110 if (crashmover_state == 0)
2111 {
2112 asldebug("CrashMover finished\n");
2113 dispatch_resume(crashlog_queue);
2114 }
2115 else
2116 {
2117 asldebug("CrashMover active: suspending crashlog queue and closing files\n");
2118 dispatch_suspend(crashlog_queue);
2119 _asl_action_close_idle_files(0);
2120 }
2121 }
2122 }
2123 });
2124 #endif
2125 });
2126
2127 _asl_action_configure();
2128
2129 return 0;
2130 }
2131
2132 /*
2133 * Close outputs and free modules.
2134 */
2135 static void
2136 _asl_action_free_modules(asl_out_module_t *m)
2137 {
2138 asl_out_rule_t *r;
2139 asl_out_module_t *x;
2140
2141 /*
2142 * asl_common frees a list of modules with asl_out_module_free.
2143 * This loop frees the private data attached some modules.
2144 */
2145 for (x = m; x != NULL; x = x->next)
2146 {
2147 for (r = x->ruleset; r != NULL; r = r->next)
2148 {
2149 if (r->action == ACTION_ASL_DIR)
2150 {
2151 _act_dst_close(r, DST_CLOSE_SHUTDOWN);
2152 if (r->dst != NULL)
2153 {
2154 _asl_action_asl_store_data_free((asl_action_asl_store_data_t *)r->dst->private);
2155 r->dst->private = NULL;
2156 }
2157 }
2158 else if (r->action == ACTION_ASL_FILE)
2159 {
2160 _act_dst_close(r, DST_CLOSE_SHUTDOWN);
2161 if (r->dst != NULL)
2162 {
2163 _asl_action_asl_file_data_free((asl_action_asl_file_data_t *)r->dst->private);
2164 r->dst->private = NULL;
2165 }
2166 }
2167 else if (r->action == ACTION_FILE)
2168 {
2169 _act_dst_close(r, DST_CLOSE_SHUTDOWN);
2170 if (r->dst != NULL)
2171 {
2172 asl_action_file_data_t *f_data = (asl_action_file_data_t *)r->dst->private;
2173 if (f_data != NULL)
2174 {
2175 /* flush repeat message if necessary */
2176 if (f_data->last_count > 0) _send_repeat_msg(r);
2177 _asl_action_file_data_free(f_data);
2178 r->dst->private = NULL;
2179 }
2180 }
2181 }
2182 else if (r->action == ACTION_SET_PLIST)
2183 {
2184 _asl_action_set_param_data_free((asl_action_set_param_data_t *)r->private);
2185 }
2186 else if (r->action == ACTION_SET_PROF)
2187 {
2188 _asl_action_set_param_data_free((asl_action_set_param_data_t *)r->private);
2189 }
2190 else if (r->action == ACTION_SET_FILE)
2191 {
2192 _asl_action_set_param_data_free((asl_action_set_param_data_t *)r->private);
2193 }
2194 }
2195 }
2196
2197 asl_out_module_free(m);
2198 }
2199
2200 static int
2201 _asl_action_close_internal(void)
2202 {
2203 #if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
2204 if (crashmover_state != 0)
2205 {
2206 dispatch_resume(crashlog_queue);
2207 crashmover_state = 0;
2208 }
2209
2210 /* wait for the crashlog_queue to flush before _asl_action_free_modules() */
2211 dispatch_sync(crashlog_queue, ^{ int x = 0; if (x == 1) x = 2; });
2212 #endif
2213
2214 _asl_action_free_modules(global.asl_out_module);
2215 global.asl_out_module = NULL;
2216 sweep_time = time(NULL);
2217
2218 return 0;
2219 }
2220
2221 static void
2222 _asl_action_close_idle_files(time_t idle_time)
2223 {
2224 asl_out_module_t *m;
2225 time_t now = time(NULL);
2226
2227 for (m = global.asl_out_module; m != NULL; m = m->next)
2228 {
2229 asl_out_rule_t *r;
2230
2231 for (r = m->ruleset; r != NULL; r = r->next)
2232 {
2233 if (idle_time == 0)
2234 {
2235 if ((r->dst != NULL) && (r->dst->flags & MODULE_FLAG_CRASHLOG))
2236 {
2237 _act_dst_close(r, DST_CLOSE_IDLE);
2238 //TODO: can r->action even be ACTION_ASL_DIR?
2239 /* if not, we can avoid the extra check here */
2240 if (r->action != ACTION_ASL_DIR) _act_checkpoint(r, CHECKPOINT_FORCE);
2241 }
2242 }
2243 else if (r->action == ACTION_ASL_DIR)
2244 {
2245 if (r->dst != NULL)
2246 {
2247 asl_action_asl_store_data_t *as_data = (asl_action_asl_store_data_t *)r->dst->private;
2248 if ((as_data != NULL) && (as_data->aslfile != NULL) && (as_data->pending == 0) && ((now - as_data->last_time) >= idle_time)) _act_dst_close(r, DST_CLOSE_IDLE);
2249 }
2250 }
2251 else if (r->action == ACTION_ASL_FILE)
2252 {
2253 if (r->dst != NULL)
2254 {
2255 asl_action_asl_file_data_t *af_data = (asl_action_asl_file_data_t *)r->dst->private;
2256 if ((af_data != NULL) && (af_data->aslfile != NULL) && (af_data->pending == 0) && ((now - af_data->last_time) >= idle_time)) _act_dst_close(r, DST_CLOSE_IDLE);
2257 }
2258 }
2259 else if (r->action == ACTION_FILE)
2260 {
2261 if (r->dst != NULL)
2262 {
2263 asl_action_file_data_t *f_data = (asl_action_file_data_t *)r->dst->private;
2264 if ((f_data != NULL) && (f_data->fd >= 0) && (f_data->pending == 0) && ((now - f_data->last_time) >= idle_time)) _act_dst_close(r, DST_CLOSE_IDLE);
2265 }
2266 }
2267 }
2268 }
2269 }
2270
2271 int
2272 asl_action_close(void)
2273 {
2274 dispatch_async(asl_action_queue, ^{
2275 _asl_action_close_internal();
2276 });
2277
2278 return 0;
2279 }
2280
2281 int
2282 asl_action_reset(void)
2283 {
2284 dispatch_async(asl_action_queue, ^{
2285 _asl_action_close_internal();
2286 asl_action_init();
2287 });
2288
2289 return 0;
2290 }
2291
2292 asl_out_module_t *
2293 _asl_action_module_with_name(const char *name)
2294 {
2295 asl_out_module_t *m;
2296
2297 if (global.asl_out_module == NULL) return NULL;
2298 if (name == NULL) return global.asl_out_module;
2299
2300 for (m = global.asl_out_module; m != NULL; m = m->next)
2301 {
2302 if ((m->name != NULL) && (!strcmp(m->name, name))) return m;
2303 }
2304
2305 return NULL;
2306 }
2307
2308 /*
2309 * called from control_message
2310 * Used to control modules dynamically.
2311 * Line format "@ module param [value ...]"
2312 *
2313 * Note this is synchronous on asl_action queue.
2314 */
2315 int
2316 asl_action_control_set_param(const char *s)
2317 {
2318 __block char **l;
2319 uint32_t count = 0;
2320
2321 if (s == NULL) return -1;
2322 if (s[0] == '\0') return 0;
2323
2324 /* skip '@' and whitespace */
2325 if (*s == '@') s++;
2326 while ((*s == ' ') || (*s == '\t')) s++;
2327
2328 l = explode(s, " \t");
2329 if (l != NULL) for (count = 0; l[count] != NULL; count++);
2330
2331 /* at least 2 parameters (l[0] = module, l[1] = param) required */
2332 if (count < 2)
2333 {
2334 free_string_list(l);
2335 return -1;
2336 }
2337
2338 if (global.asl_out_module == NULL)
2339 {
2340 asldebug("asl_action_control_set_param: no modules loaded\n");
2341 free_string_list(l);
2342 return -1;
2343 }
2344
2345 /* create / modify a module */
2346 if ((!strcasecmp(l[1], "define")) && (strcmp(l[0], "*")))
2347 {
2348 char *str = strdup(s);
2349 if (str == NULL)
2350 {
2351 asldebug("asl_action_control_set_param: memory allocation failed\n");
2352 free_string_list(l);
2353 return -1;
2354 }
2355
2356 dispatch_sync(asl_action_queue, ^{
2357 asl_out_module_t *m;
2358 asl_out_rule_t *r;
2359 char *p = str;
2360
2361 /* skip name, whitespace, "define" */
2362 while ((*p != ' ') && (*p != '\t')) p++;
2363 while ((*p == ' ') || (*p == '\t')) p++;
2364 while ((*p != ' ') && (*p != '\t')) p++;
2365
2366 m = _asl_action_module_with_name(l[0]);
2367 if (m == NULL)
2368 {
2369 asl_out_module_t *x;
2370
2371 m = asl_out_module_new(l[0]);
2372 for (x = global.asl_out_module; x->next != NULL; x = x->next);
2373 x->next = m;
2374 }
2375
2376 r = asl_out_module_parse_line(m, p);
2377 if (r != NULL)
2378 {
2379 _asl_action_post_process_rule(m, r);
2380 if ((r->dst != NULL) && (r->dst->flags & MODULE_FLAG_ROTATE))
2381 {
2382 _act_file_checkpoint_all(CHECKPOINT_TEST);
2383 if (checkpoint_timer == NULL) _start_cycling();
2384 }
2385 }
2386 });
2387
2388 free(str);
2389 free_string_list(l);
2390 return 0;
2391 }
2392
2393 dispatch_sync(asl_action_queue, ^{
2394 uint32_t intval;
2395 int do_all = 0;
2396 asl_out_module_t *m;
2397
2398 if (!strcmp(l[0], "*"))
2399 {
2400 do_all = 1;
2401 m = _asl_action_module_with_name(NULL);
2402 }
2403 else
2404 {
2405 m = _asl_action_module_with_name(l[0]);
2406 }
2407
2408 while (m != NULL)
2409 {
2410 if (!strcasecmp(l[1], "enable"))
2411 {
2412 intval = 1;
2413
2414 /* don't do enable for ASL_MODULE_NAME if input name is "*" */
2415 if ((do_all == 0) || (strcmp(m->name, ASL_MODULE_NAME)))
2416 {
2417 /* @ module enable {0|1} */
2418 if (count > 2) intval = atoi(l[2]);
2419
2420 if (intval == 0) m->flags &= ~MODULE_FLAG_ENABLED;
2421 else m->flags |= MODULE_FLAG_ENABLED;
2422 }
2423 }
2424 else if (!strcasecmp(l[1], "checkpoint"))
2425 {
2426 /* @ module checkpoint [file] */
2427 if (count > 2) _act_file_checkpoint(m, l[2], CHECKPOINT_FORCE);
2428 else _act_file_checkpoint(m, NULL, CHECKPOINT_FORCE);
2429 }
2430
2431 if (do_all == 1) m = m->next;
2432 else m = NULL;
2433 }
2434
2435 });
2436
2437 free_string_list(l);
2438 return 0;
2439 }
2440
2441 void
2442 asl_action_out_module_query(asl_msg_t *q, asl_msg_t *m, bool all)
2443 {
2444 dispatch_sync(asl_action_queue, ^{
2445 asl_out_module_t *om;
2446 const char *val;
2447 for (om = global.asl_out_module; om != NULL; om = om->next)
2448 {
2449 if (all || (0 == asl_msg_lookup(q, om->name, NULL, NULL)))
2450 {
2451 val = om->flags & MODULE_FLAG_ENABLED ? "enabled" : "disabled";
2452 if (om->name == NULL) asl_msg_set_key_val(m, "asl.conf", val);
2453 else asl_msg_set_key_val(m, om->name, val);
2454 }
2455 }
2456 });
2457 }