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_EMBEDDED
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
_act_out_set_param(asl_out_module_t * m,char * x,bool eval)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
_act_notify(asl_out_module_t * m,asl_out_rule_t * r)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 // XXX notify_post(r->options);
206 }
207
208 static void
_act_broadcast(asl_out_module_t * m,asl_out_rule_t * r,asl_msg_t * msg)209 _act_broadcast(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
210 {
211 #if !TARGET_OS_EMBEDDED
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
_act_set_key(asl_out_module_t * m,asl_out_rule_t * r,asl_msg_t * msg)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
_act_unset_key(asl_out_module_t * m,asl_out_rule_t * r,asl_msg_t * msg)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
_act_access_control(asl_out_module_t * m,asl_out_rule_t * r,asl_msg_t * msg)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_EMBEDDED
288 static void
_crashlog_queue_check(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
_act_file_create_open(asl_out_dst_data_t * dst)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
_asl_dir_create(asl_out_rule_t * r)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
_asl_dir_storedata_close(asl_out_rule_t * r)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
_asl_dir_storedata_open(asl_out_rule_t * r,uint64_t xid)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_IPHONE_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
_asl_dir_today_close(asl_out_rule_t * r)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->fname, 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->fname);
533 r->dst->fname = NULL;
534
535 as_data->aslfile = NULL;
536 }
537
538 /*
539 * Check file size.
540 */
541 static int
_act_checkpoint(asl_out_rule_t * r,uint32_t force)542 _act_checkpoint(asl_out_rule_t *r, uint32_t force)
543 {
544 char tmpfname[MAXPATHLEN], *fn;
545
546 if (r == NULL) return 0;
547 if (r->dst == NULL) return 0;
548
549 fn = r->dst->fname;
550 if (fn == NULL)
551 {
552 if (r->dst->path == NULL) return 0;
553 asl_make_dst_filename(r->dst, tmpfname, sizeof(tmpfname));
554 fn = tmpfname;
555 }
556
557 if ((force == CHECKPOINT_TEST) && (r->dst->file_max == 0)) return 0;
558
559 if ((r->dst->size == 0) || (r->dst->stamp == 0))
560 {
561 struct stat sb;
562
563 memset(&sb, 0, sizeof(struct stat));
564
565 if (stat(fn, &sb) < 0)
566 {
567 if (errno == ENOENT) return 0;
568 return -1;
569 }
570
571 if (r->dst->stamp == 0) r->dst->stamp = sb.st_birthtimespec.tv_sec;
572 if (r->dst->stamp == 0) r->dst->stamp = sb.st_mtimespec.tv_sec;
573 r->dst->size = sb.st_size;
574 }
575
576 if ((force == CHECKPOINT_TEST) && (r->dst->size < r->dst->file_max)) return 0;
577
578 if (r->dst->flags & MODULE_FLAG_BASESTAMP)
579 {
580 _act_dst_close(r, DST_CLOSE_CHECKPOINT);
581 }
582 else
583 {
584 char srcpath[MAXPATHLEN];
585 char dstpath[MAXPATHLEN];
586 char tstamp[32];
587
588 if (r->dst->stamp == 0) r->dst->stamp = time(NULL);
589 asl_make_timestamp(r->dst->stamp, r->dst->flags, tstamp, sizeof(tstamp));
590
591 snprintf(srcpath, sizeof(srcpath), "%s", fn);
592 snprintf(dstpath, sizeof(dstpath), "%s.%s", fn, tstamp);
593
594 _act_dst_close(r, DST_CLOSE_CHECKPOINT);
595 rename(srcpath, dstpath);
596 }
597
598 r->dst->stamp = 0;
599 r->dst->size = 0;
600 return 1;
601 }
602
603 /*
604 * Open today's ASL file in an ASL directory
605 * - Checks date and closes a currently open file if it has the wrong date
606 * - Opens today's file
607 */
608 static int
_asl_dir_today_open(asl_out_rule_t * r,const time_t * tick)609 _asl_dir_today_open(asl_out_rule_t *r, const time_t *tick)
610 {
611 int status;
612 mode_t mask;
613 struct tm ctm;
614 time_t now;
615
616 if (r == NULL) return -1;
617 if (r->dst == NULL) return -1;
618
619 status = _asl_dir_create(r);
620 if (status != 0)
621 {
622 asldebug("_asl_dir_today_open: No directory at path %s\n", r->dst->path);
623 return -1;
624 }
625
626 asl_action_asl_store_data_t *as_data = (asl_action_asl_store_data_t *)r->dst->private;
627
628 memset(&ctm, 0, sizeof(struct tm));
629 if (tick == NULL)
630 {
631 now = time(NULL);
632 tick = (const time_t *)&now;
633 }
634
635 if (localtime_r(tick, &ctm) == NULL)
636 {
637 asldebug("_asl_dir_today_open: localtime_r error %s\n", strerror(errno));
638 return -1;
639 }
640
641 /* checks file_max and closes if required */
642 status = _act_checkpoint(r, CHECKPOINT_TEST);
643 if (status == 1) trigger_aslmanager();
644
645 if (as_data->aslfile != NULL)
646 {
647 /* Check the date */
648 if ((as_data->p_year == ctm.tm_year) && (as_data->p_month == ctm.tm_mon) && (as_data->p_day == ctm.tm_mday)) return 0;
649
650 /* Wrong date, close the current file */
651 _asl_dir_today_close(r);
652 }
653
654 /* Open data file */
655
656 if (r->dst->flags & MODULE_FLAG_BASESTAMP)
657 {
658 char tstamp[32];
659
660 if (tick == NULL)
661 {
662 now = time(NULL);
663 tick = (const time_t *)&now;
664 }
665
666 asl_make_timestamp(now, r->dst->flags, tstamp, sizeof(tstamp));
667 asprintf(&(r->dst->fname), "%s/%s.asl", r->dst->path, tstamp);
668 }
669 else
670 {
671 asprintf(&(r->dst->fname), "%s/%d.%02d.%02d.asl", r->dst->path, ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday);
672 }
673
674
675 if (r->dst->fname == NULL)
676 {
677 asldebug("_asl_dir_today_open: asprintf error %s\n", strerror(errno));
678 return -1;
679 }
680
681 #if TARGET_IPHONE_SIMULATOR
682 uid_t uid = -1;
683 gid_t gid = -1;
684 #else
685 uid_t uid = r->dst->uid[0];
686 gid_t gid = r->dst->gid[0];
687 #endif
688
689 mask = umask(0);
690 status = asl_file_open_write(r->dst->fname, (r->dst->mode & 00666), uid, gid, &(as_data->aslfile));
691 umask(mask);
692
693 if (status != ASL_STATUS_OK)
694 {
695 asldebug("_asl_dir_today_open: asl_file_open_write %s error %s\n", r->dst->fname, asl_core_error(status));
696 free(r->dst->fname);
697 r->dst->fname = NULL;
698 return -1;
699 }
700
701 if (fseek(as_data->aslfile->store, 0, SEEK_END) != 0)
702 {
703 asldebug("_asl_dir_today_open: fseek %s error %s\n", r->dst->fname, strerror(errno));
704 free(r->dst->fname);
705 r->dst->fname = NULL;
706 return -1;
707 }
708
709 as_data->p_year = ctm.tm_year;
710 as_data->p_month = ctm.tm_mon;
711 as_data->p_day = ctm.tm_mday;
712
713 /* create aslfile_monitor */
714 int fd = fileno(as_data->aslfile->store);
715 asl_file_t *aslf = as_data->aslfile;
716
717 as_data->aslfile_monitor = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, DISPATCH_VNODE_DELETE, asl_action_queue);
718 if (as_data->aslfile_monitor != NULL)
719 {
720 dispatch_source_set_event_handler(as_data->aslfile_monitor, ^{
721 _act_dst_close(r, DST_CLOSE_DELETED);
722 });
723
724 dispatch_source_set_cancel_handler(as_data->aslfile_monitor, ^{
725 asldebug("cancel/close ASL file fd %d\n", fd);
726 asl_file_close(aslf);
727 });
728
729 dispatch_resume(as_data->aslfile_monitor);
730 }
731
732 asldebug("_asl_dir_today_open ASL file %s fd %d\n", r->dst->fname, fd);
733
734 return 0;
735 }
736
737 static void
_asl_file_close(asl_out_rule_t * r)738 _asl_file_close(asl_out_rule_t *r)
739 {
740 if (r == NULL) return;
741 if (r->dst == NULL) return;
742
743 asl_action_asl_file_data_t *af_data = (asl_action_asl_file_data_t *)r->dst->private;
744 if (af_data->aslfile == NULL) return;
745
746 if (af_data->pending != 0)
747 {
748 char *str = NULL;
749 asprintf(&str, "[Sender syslogd] [Level 4] [PID %u] [Facility syslog] [Message ASL File %s was closed with %d pending messages]", global.pid, r->dst->fname, af_data->pending);
750 internal_log_message(str);
751 free(str);
752 }
753
754 if (af_data->monitor == NULL)
755 {
756 /*
757 * This should never happen, but _asl_file_open allows
758 * dispatch_source_create to fail silently. If there is no dispatch
759 * source, we just close the file.
760 */
761 asldebug("close ASL fd %d\n", fileno(af_data->aslfile->store));
762 asl_file_close(af_data->aslfile);
763 }
764 else
765 {
766 /*
767 * The monitor cancel handler will close the file.
768 */
769 dispatch_source_cancel(af_data->monitor);
770 dispatch_release(af_data->monitor);
771 af_data->monitor = NULL;
772 }
773
774 af_data->aslfile = NULL;
775 }
776
777 static int
_asl_file_open(asl_out_rule_t * r)778 _asl_file_open(asl_out_rule_t *r)
779 {
780 int fd, status;
781
782 if (r == NULL) return -1;
783 if (r->dst == NULL) return -1;
784
785 asl_action_asl_file_data_t *af_data = (asl_action_asl_file_data_t *)r->dst->private;
786 if (af_data->aslfile != NULL) return 0;
787
788 /* create path if necessary */
789 status = asl_out_mkpath(global.asl_out_module, r);
790 if (status != 0)
791 {
792 asldebug("_asl_file_open: asl_out_mkpath %s failed\n", r->dst->path);
793 return -1;
794 }
795
796 fd = _act_file_create_open(r->dst);
797 if (fd < 0)
798 {
799 asldebug("_asl_file_open: _act_file_create_open %s failed %d %s\n", r->dst->fname, errno, strerror(errno));
800 return -1;
801 }
802
803 close(fd);
804
805 if (r->dst->fname == NULL) return -1;
806
807 status = asl_file_open_write(r->dst->fname, 0, -1, -1, &(af_data->aslfile));
808 if (status != ASL_STATUS_OK)
809 {
810 asldebug("_asl_file_open: asl_file_open_write %s failed %d %s\n", r->dst->fname, errno, strerror(errno));
811 return -1;
812 }
813
814 /* create monitor */
815 fd = fileno(af_data->aslfile->store);
816 asl_file_t *aslf = af_data->aslfile;
817
818 af_data->monitor = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, DISPATCH_VNODE_DELETE, asl_action_queue);
819 if (af_data->monitor != NULL)
820 {
821 dispatch_source_set_event_handler(af_data->monitor, ^{
822 _act_dst_close(r, DST_CLOSE_DELETED);
823 });
824
825 dispatch_source_set_cancel_handler(af_data->monitor, ^{
826 asldebug("cancel/close ASL file fd %d\n", fd);
827 asl_file_close(aslf);
828 });
829
830 dispatch_resume(af_data->monitor);
831 }
832
833 asldebug("_asl_file_open ASL file %s fd %d\n", r->dst->fname, fd);
834 return 0;
835 }
836
837 static void
_text_file_close(asl_out_rule_t * r)838 _text_file_close(asl_out_rule_t *r)
839 {
840 asl_action_file_data_t *f_data = (asl_action_file_data_t *)r->dst->private;
841 if (f_data->fd < 0) return;
842
843 if (f_data->pending != 0)
844 {
845 char *str = NULL;
846 asprintf(&str, "[Sender syslogd] [Level 4] [PID %u] [Facility syslog] [Message File %s was closed with %d pending messages]", global.pid, r->dst->fname, f_data->pending);
847 internal_log_message(str);
848 free(str);
849 }
850
851 if (f_data->monitor == NULL)
852 {
853 /*
854 * This should never happen, but _text_file_open allows
855 * dispatch_source_create to fail silently. If there is no dispatch
856 * source, we just close the file.
857 */
858 asldebug("close fd %d\n", f_data->fd);
859 close(f_data->fd);
860 }
861 else
862 {
863 /*
864 * The monitor cancel handler will close the file.
865 */
866 dispatch_source_cancel(f_data->monitor);
867 dispatch_release(f_data->monitor);
868 f_data->monitor = NULL;
869 }
870
871 f_data->fd = -1;
872 }
873
874 static int
_text_file_open(asl_out_rule_t * r)875 _text_file_open(asl_out_rule_t *r)
876 {
877 asl_action_file_data_t *f_data = (asl_action_file_data_t *)r->dst->private;
878
879 if (f_data->fd < 0)
880 {
881 f_data->fd = _act_file_create_open(r->dst);
882 if (f_data->fd < 0)
883 {
884 /*
885 * lazy path creation: create path and retry
886 * _act_file_create_open does not create the path
887 * so we do it here.
888 */
889 int status = asl_out_mkpath(global.asl_out_module, r);
890 if (status != 0) return -1;
891
892 f_data->fd = _act_file_create_open(r->dst);
893 }
894
895 if (f_data->fd < 0) return -1;
896
897 f_data->monitor = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, f_data->fd, DISPATCH_VNODE_DELETE, asl_action_queue);
898 if (f_data->monitor != NULL)
899 {
900 int ffd = f_data->fd;
901
902 dispatch_source_set_event_handler(f_data->monitor, ^{
903 asldebug("dispatch_source DISPATCH_VNODE_DELETE fd %d\n", ffd);
904 _act_dst_close(r, DST_CLOSE_DELETED);
905 });
906
907 dispatch_source_set_cancel_handler(f_data->monitor, ^{
908 asldebug("cancel/close file fd %d\n", ffd);
909 close(ffd);
910 });
911
912 dispatch_resume(f_data->monitor);
913 }
914 }
915
916 return 0;
917 }
918
919 static int
_act_dst_open(asl_out_rule_t * r,const time_t * tick,uint64_t xid)920 _act_dst_open(asl_out_rule_t *r, const time_t *tick, uint64_t xid)
921 {
922 if (r == NULL) return -1;
923 if (r->dst == NULL) return -1;
924 if (r->dst->private == NULL) return -1;
925
926 if (r->action == ACTION_ASL_DIR)
927 {
928 if (_asl_dir_today_open(r, tick) != 0)
929 {
930 asldebug("_act_dst_open:_asl_dir_today_open %s FAILED!\n", r->dst->path);
931 return -1;
932 }
933
934 if (_asl_dir_storedata_open(r, xid) != 0)
935 {
936 asldebug("_act_dst_open:_asl_dir_storedata_open %s FAILED! Closing today file\n", r->dst->path);
937 _asl_dir_today_close(r);
938 return -1;
939 }
940
941 return 0;
942 }
943
944 if (r->action == ACTION_ASL_FILE)
945 {
946 return _asl_file_open(r);
947 }
948
949 if (r->action == ACTION_FILE)
950 {
951 return _text_file_open(r);
952 }
953
954 return -1;
955 }
956
957 static void
_act_dst_close(asl_out_rule_t * r,int why)958 _act_dst_close(asl_out_rule_t *r, int why)
959 {
960 if (r == NULL) return;
961 if (r->dst == NULL) return;
962 if (r->dst->private == NULL) return;
963
964 if (r->action == ACTION_ASL_DIR)
965 {
966 asldebug("_act_dst_close: %s ASL DIR %s\n", why_str[why], r->dst->path);
967 if (why != DST_CLOSE_CHECKPOINT) _asl_dir_storedata_close(r);
968 _asl_dir_today_close(r);
969 }
970 else if (r->action == ACTION_ASL_FILE)
971 {
972 asldebug("_act_dst_close: %s ASL FILE %s\n", why_str[why], (r->dst->fname == NULL) ? r->dst->path : r->dst->fname);
973 _asl_file_close(r);
974 }
975 else if (r->action == ACTION_FILE)
976 {
977 asldebug("_act_dst_close: %s FILE %s\n", why_str[why], (r->dst->fname == NULL) ? r->dst->path : r->dst->fname);
978 _text_file_close(r);
979 }
980 }
981
982 static uint32_t
_act_store_file_setup(asl_out_module_t * m,asl_out_rule_t * r)983 _act_store_file_setup(asl_out_module_t *m, asl_out_rule_t *r)
984 {
985 uint32_t status;
986 asl_action_asl_file_data_t *af_data;
987
988 if (r == NULL) return ASL_STATUS_INVALID_STORE;
989 if (r->dst == NULL) return ASL_STATUS_INVALID_STORE;
990 if (r->dst->private == NULL) return ASL_STATUS_INVALID_STORE;
991
992 af_data = (asl_action_asl_file_data_t *)r->dst->private;
993 if (af_data->aslfile != NULL)
994 {
995 af_data->next_id++;
996 return ASL_STATUS_OK;
997 }
998
999 if (_act_dst_open(r, NULL, 0) != 0) return ASL_STATUS_WRITE_FAILED;
1000
1001 status = asl_file_read_set_position(af_data->aslfile, ASL_FILE_POSITION_LAST);
1002 if (status != ASL_STATUS_OK)
1003 {
1004 asldebug("_act_store_file_setup: asl_file_read_set_position failed %d %s\n", status, asl_core_error(status));
1005 _act_dst_close(r, DST_CLOSE_ERROR);
1006 return status;
1007 }
1008
1009 af_data->next_id = af_data->aslfile->cursor_xid + 1;
1010 if (fseek(af_data->aslfile->store, 0, SEEK_END) != 0)
1011 {
1012 asldebug("_act_store_file_setup: fseek failed %d %s\n", errno, strerror(errno));
1013 _act_dst_close(r, DST_CLOSE_ERROR);
1014 return ASL_STATUS_WRITE_FAILED;
1015 }
1016
1017 return ASL_STATUS_OK;
1018 }
1019
1020 /*
1021 * _act_store_dir_setup
1022 *
1023 * Opens StoreData file and today's file
1024 * Reads ASL Message ID from StoreData file
1025 * Writes ASL Message ID + 1 to StoreData file
1026 */
1027 static uint32_t
_act_store_dir_setup(asl_out_module_t * m,asl_out_rule_t * r,time_t tick)1028 _act_store_dir_setup(asl_out_module_t *m, asl_out_rule_t *r, time_t tick)
1029 {
1030 uint64_t xid;
1031 int status;
1032 asl_action_asl_store_data_t *as_data;
1033
1034 if (r == NULL) return ASL_STATUS_INVALID_STORE;
1035 if (r->dst == NULL) return ASL_STATUS_INVALID_STORE;
1036 if (r->dst->private == NULL) return ASL_STATUS_INVALID_STORE;
1037 if (r->dst->path == NULL) return ASL_STATUS_INVALID_STORE;
1038
1039 as_data = (asl_action_asl_store_data_t *)r->dst->private;
1040
1041 if (_act_dst_open(r, NULL, as_data->next_id) != 0)
1042 {
1043 asldebug("_act_store_dir_setup: _act_dst_open %s failed\n", r->dst->path);
1044 return ASL_STATUS_WRITE_FAILED;
1045 }
1046
1047 /* get / set message id from StoreData file */
1048 xid = 0;
1049 rewind(as_data->storedata);
1050 if (fread(&xid, sizeof(uint64_t), 1, as_data->storedata) != 1)
1051 {
1052 asldebug("_act_store_dir_setup: storedata read failed %d %s\n", errno, strerror(errno));
1053 _act_dst_close(r, DST_CLOSE_ERROR);
1054 return ASL_STATUS_READ_FAILED;
1055 }
1056
1057 xid = asl_core_ntohq(xid);
1058 xid++;
1059 as_data->next_id = xid;
1060
1061 xid = asl_core_htonq(xid);
1062 rewind(as_data->storedata);
1063 status = fwrite(&xid, sizeof(uint64_t), 1, as_data->storedata);
1064 if (status != 1)
1065 {
1066 asldebug("_act_store_dir_setup: storedata write failed %d %s\n", errno, strerror(errno));
1067 _act_dst_close(r, DST_CLOSE_ERROR);
1068 return ASL_STATUS_WRITE_FAILED;
1069 }
1070
1071 fflush(as_data->storedata);
1072
1073 if (fseek(as_data->aslfile->store, 0, SEEK_END) != 0)
1074 {
1075 asldebug("_act_store_dir_setup: aslfile fseek failed %d %s\n", errno, strerror(errno));
1076 _act_dst_close(r, DST_CLOSE_ERROR);
1077 return ASL_STATUS_FAILED;
1078 }
1079
1080 return ASL_STATUS_OK;
1081 }
1082
1083 static void
_asl_action_asl_store_data_free(asl_action_asl_store_data_t * as_data)1084 _asl_action_asl_store_data_free(asl_action_asl_store_data_t *as_data)
1085 {
1086 if (as_data == NULL) return;
1087 free(as_data);
1088 }
1089
1090 static void
_asl_action_asl_file_data_free(asl_action_asl_file_data_t * af_data)1091 _asl_action_asl_file_data_free(asl_action_asl_file_data_t *af_data)
1092 {
1093 if (af_data == NULL) return;
1094 free(af_data);
1095 }
1096
1097 static void
_asl_action_file_data_free(asl_action_file_data_t * f_data)1098 _asl_action_file_data_free(asl_action_file_data_t *f_data)
1099 {
1100 if (f_data == NULL) return;
1101
1102 if (f_data->dup_timer != NULL)
1103 {
1104 if (f_data->last_count == 0)
1105 {
1106 /*
1107 * The timer exists, but last_count is zero, so the timer is suspended.
1108 * Sources must not be released in when suspended.
1109 * So we resume it so that we can release it.
1110 */
1111 dispatch_resume(f_data->dup_timer);
1112 }
1113
1114 dispatch_release(f_data->dup_timer);
1115 }
1116
1117 free(f_data->last_msg);
1118 free(f_data);
1119 }
1120
1121 static void
_asl_action_set_param_data_free(asl_action_set_param_data_t * spdata)1122 _asl_action_set_param_data_free(asl_action_set_param_data_t *spdata)
1123 {
1124 // XXX if (spdata != NULL) notify_cancel(spdata->token);
1125 free(spdata);
1126 }
1127
1128 static void
_asl_action_save_failed(const char * where,asl_out_module_t * m,asl_out_rule_t * r,uint32_t status)1129 _asl_action_save_failed(const char *where, asl_out_module_t *m, asl_out_rule_t *r, uint32_t status)
1130 {
1131 if (r->dst->flags & MODULE_FLAG_SOFT_WRITE) return;
1132
1133 r->dst->fails++;
1134 asldebug("%s: %s save to %s failed: %s\n", where, m->name, r->dst->path, asl_core_error(status));
1135
1136 /* disable further activity after multiple failures */
1137 if (r->dst->fails > MAX_FAILURES)
1138 {
1139 char *str = NULL;
1140 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));
1141 internal_log_message(str);
1142 free(str);
1143
1144 if (r->action == ACTION_ASL_DIR) _asl_action_asl_store_data_free((asl_action_asl_store_data_t *)r->dst->private);
1145 else if (r->action == ACTION_ASL_FILE) _asl_action_asl_file_data_free((asl_action_asl_file_data_t *)r->dst->private);
1146 else if (r->action == ACTION_FILE) _asl_action_file_data_free((asl_action_file_data_t *)r->dst->private);
1147
1148 r->dst->private = NULL;
1149 r->action = ACTION_NONE;
1150 }
1151 }
1152
1153 /*
1154 * Save a message in an ASL file.
1155 */
1156 static int
_act_store_file(asl_out_module_t * m,asl_out_rule_t * r,asl_msg_t * msg)1157 _act_store_file(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
1158 {
1159 asl_action_asl_file_data_t *af_data;
1160 uint32_t status;
1161 uint64_t mid;
1162
1163 if (r == NULL) return ACTION_STATUS_ERROR;
1164 if (r->dst == NULL) return ACTION_STATUS_ERROR;
1165 if (r->dst->private == NULL) return ACTION_STATUS_ERROR;
1166
1167 af_data = (asl_action_asl_file_data_t *)r->dst->private;
1168 if (af_data->pending > 0) af_data->pending--;
1169
1170 status = _act_store_file_setup(m, r);
1171 if (status == ASL_STATUS_OK)
1172 {
1173 af_data->last_time = time(NULL);
1174
1175 r->dst->fails = 0;
1176 mid = af_data->next_id;
1177
1178 /* save message to file and update dst size */
1179 status = asl_file_save(af_data->aslfile, msg, &mid);
1180 if (status == ASL_STATUS_OK)
1181 {
1182 r->dst->size = af_data->aslfile->file_size;
1183
1184 if (_act_checkpoint(r, CHECKPOINT_TEST) == 1) trigger_aslmanager();
1185 }
1186 }
1187
1188 if (status != ASL_STATUS_OK)
1189 {
1190 _asl_action_save_failed("_act_store_file", m, r, status);
1191 return ACTION_STATUS_ERROR;
1192 }
1193
1194 return ACTION_STATUS_OK;
1195 }
1196
1197 /*
1198 * Save a message in an ASL data store.
1199 */
1200 static int
_act_store_dir(asl_out_module_t * m,asl_out_rule_t * r,asl_msg_t * msg)1201 _act_store_dir(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
1202 {
1203 asl_action_asl_store_data_t *as_data;
1204 uint32_t status;
1205 uint64_t mid;
1206 const char *val;
1207 time_t tick;
1208
1209 if (r == NULL) return ACTION_STATUS_ERROR;
1210 if (r->dst == NULL) return ACTION_STATUS_ERROR;
1211 if (r->dst->private == NULL) return ACTION_STATUS_ERROR;
1212
1213 as_data = (asl_action_asl_store_data_t *)r->dst->private;
1214 if (as_data->pending > 0) as_data->pending--;
1215
1216 val = asl_msg_get_val_for_key(msg, ASL_KEY_TIME);
1217 if (val == NULL) return ACTION_STATUS_ERROR;
1218
1219 tick = atol(val);
1220
1221 status = _act_store_dir_setup(m, r, tick);
1222 if (status == ASL_STATUS_OK)
1223 {
1224 as_data->last_time = time(NULL);
1225
1226 r->dst->fails = 0;
1227 mid = as_data->next_id;
1228 status = asl_file_save(as_data->aslfile, msg, &mid);
1229 if (status == ASL_STATUS_OK) r->dst->size = as_data->aslfile->file_size;
1230 else asldebug("_act_store_dir asl_file_save FAILED %s\n", asl_core_error(status));
1231 //TODO: checkpoint here?
1232 /*
1233 * Currently, the checkpoint is in _asl_dir_today_open().
1234 * Moving it here would be in keeping with the way that
1235 * _act_store_file() and _act_file_final() do checkpoints.
1236 */
1237 }
1238
1239 if (status != ASL_STATUS_OK)
1240 {
1241 _asl_action_save_failed("_act_store_dir", m, r, status);
1242 return ACTION_STATUS_ERROR;
1243 }
1244
1245 return ACTION_STATUS_OK;
1246 }
1247
1248 static void
_act_store_final(asl_out_module_t * m,asl_out_rule_t * r,asl_msg_t * msg)1249 _act_store_final(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
1250 {
1251 if (r->action == ACTION_ASL_DIR) _act_store_dir(m, r, msg);
1252 else _act_store_file(m, r, msg);
1253 }
1254
1255 /*
1256 * Save a message to an ASL format file (ACTION_ASL_FILE)
1257 * or to an ASL directory (ACTION_ASL_DIR).
1258 */
1259 static void
_act_store(asl_out_module_t * m,asl_out_rule_t * r,asl_msg_t * msg)1260 _act_store(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
1261 {
1262 if (r == NULL) return;
1263 if (r->dst == NULL) return;
1264 if (msg == NULL) return;
1265 if (m == NULL) return;
1266 if ((m->flags & MODULE_FLAG_ENABLED) == 0) return;
1267
1268 if (r->dst->flags & MODULE_FLAG_HAS_LOGGED) return;
1269
1270 r->dst->flags |= MODULE_FLAG_HAS_LOGGED;
1271 if (r->action == ACTION_ASL_DIR)
1272 {
1273 asl_action_asl_store_data_t *as_data = (asl_action_asl_store_data_t *)r->dst->private;
1274 if (as_data != NULL) as_data->pending++;
1275 }
1276 else if (r->action == ACTION_ASL_FILE)
1277 {
1278 asl_action_asl_file_data_t *af_data = (asl_action_asl_file_data_t *)r->dst->private;
1279 if (af_data != NULL) af_data->pending++;
1280 }
1281
1282 #if TARGET_OS_EMBEDDED
1283 if (r->dst->flags & MODULE_FLAG_CRASHLOG)
1284 {
1285 _crashlog_queue_check();
1286 asl_msg_retain(msg);
1287 dispatch_async(crashlog_queue, ^{
1288 dispatch_async(asl_action_queue, ^{
1289 _act_store_final(m, r, msg);
1290 asl_msg_release(msg);
1291 });
1292 });
1293 return;
1294 }
1295 #endif
1296
1297 _act_store_final(m, r, msg);
1298 }
1299
1300 static int
_send_repeat_msg(asl_out_rule_t * r)1301 _send_repeat_msg(asl_out_rule_t *r)
1302 {
1303 asl_action_file_data_t *f_data;
1304 char vt[32], *msg;
1305 int len, status;
1306 time_t now = time(NULL);
1307
1308 if (r == NULL) return -1;
1309 if (r->dst == NULL) return -1;
1310 if (r->dst->private == NULL) return -1;
1311
1312 f_data = (asl_action_file_data_t *)r->dst->private;
1313
1314 free(f_data->last_msg);
1315 f_data->last_msg = NULL;
1316
1317 if (f_data->last_count == 0) return 0;
1318
1319 /* stop the timer */
1320 dispatch_suspend(f_data->dup_timer);
1321
1322 memset(vt, 0, sizeof(vt));
1323 ctime_r(&now, vt);
1324 vt[19] = '\0';
1325
1326 msg = NULL;
1327 asprintf(&msg, "%s --- last message repeated %u time%s ---\n", vt + 4, f_data->last_count, (f_data->last_count == 1) ? "" : "s");
1328 f_data->last_count = 0;
1329 f_data->last_time = now;
1330 if (msg == NULL) return -1;
1331
1332 if (f_data->fd < 0) f_data->fd = _act_file_create_open(r->dst);
1333
1334 len = strlen(msg);
1335 status = write(f_data->fd, msg, len);
1336 free(msg);
1337
1338 if ((status < 0) || (status < len))
1339 {
1340 asldebug("%s: error writing repeat message (%s): %s\n", MY_ID, r->dst->fname, strerror(errno));
1341 return -1;
1342 }
1343
1344 return 0;
1345 }
1346
1347 static void
_start_cycling()1348 _start_cycling()
1349 {
1350 struct timespec midnight;
1351 struct tm t;
1352 time_t x;
1353
1354 x = time(NULL);
1355
1356 if (checkpoint_timer != NULL) return;
1357
1358 localtime_r(&x, &t);
1359
1360 t.tm_sec = 0;
1361 t.tm_min = 0;
1362 t.tm_hour = 0;
1363 t.tm_mday++;
1364
1365 x = mktime(&t);
1366 midnight.tv_sec = x;
1367 midnight.tv_nsec = 0;
1368
1369 checkpoint_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, asl_action_queue);
1370 dispatch_source_set_timer(checkpoint_timer, dispatch_walltime(&midnight, 0), NSEC_PER_SEC * SEC_PER_DAY, 0);
1371 dispatch_source_set_event_handler(checkpoint_timer, ^{ _act_file_checkpoint_all(CHECKPOINT_FORCE); });
1372 dispatch_resume(checkpoint_timer);
1373 }
1374
1375 /* check if a module path (mpath) matches a user path (upath) */
1376 static bool
_act_file_equal(const char * mpath,const char * upath)1377 _act_file_equal(const char *mpath, const char *upath)
1378 {
1379 const char *slash;
1380
1381 /* NULL upath means user wants to match all files */
1382 if (upath == NULL) return true;
1383
1384 if (mpath == NULL) return false;
1385
1386 /* check for exact match */
1387 if (!strcmp(mpath, upath)) return true;
1388
1389 /* upath may be the last component of mpath */
1390 slash = strrchr(mpath, '/');
1391 if (slash == NULL) return false;
1392
1393 if (!strcmp(slash + 1, upath)) return true;
1394 return false;
1395 }
1396
1397 static int
_act_file_checkpoint(asl_out_module_t * m,const char * path,uint32_t force)1398 _act_file_checkpoint(asl_out_module_t *m, const char *path, uint32_t force)
1399 {
1400 asl_out_rule_t *r;
1401 int did_checkpoint = 0;
1402
1403 if (m == NULL) return 0;
1404
1405
1406 for (r = m->ruleset; r != NULL; r = r->next)
1407 {
1408 if ((r->action == ACTION_FILE) || (r->action == ACTION_ASL_FILE))
1409 {
1410 if (r->dst->flags & MODULE_FLAG_ROTATE)
1411 {
1412 if (_act_file_equal(r->dst->path, path))
1413 {
1414 if (force & CHECKPOINT_CRASH)
1415 {
1416 if (r->dst->flags & MODULE_FLAG_CRASHLOG)
1417 {
1418 if (_act_checkpoint(r, CHECKPOINT_FORCE) > 0)
1419 {
1420 did_checkpoint = 1;
1421 _act_dst_close(r, DST_CLOSE_CHECKPOINT);
1422 }
1423 }
1424 }
1425 else
1426 {
1427 if (_act_checkpoint(r, force) > 0)
1428 {
1429 did_checkpoint = 1;
1430 _act_dst_close(r, DST_CLOSE_CHECKPOINT);
1431 }
1432 }
1433 }
1434 }
1435 }
1436 }
1437
1438 return did_checkpoint;
1439 }
1440
1441 static int
_act_file_checkpoint_all(uint32_t force)1442 _act_file_checkpoint_all(uint32_t force)
1443 {
1444 asl_out_module_t *m;
1445 int did_checkpoint = 0;
1446
1447 for (m = global.asl_out_module; m != NULL; m = m->next)
1448 {
1449 if (_act_file_checkpoint(m, NULL, force) > 0) did_checkpoint = 1;
1450 }
1451
1452 trigger_aslmanager();
1453
1454 return did_checkpoint;
1455 }
1456
1457 /*
1458 * Save a message in a plain text file.
1459 */
1460 static void
_act_file_final(asl_out_module_t * m,asl_out_rule_t * r,asl_msg_t * msg)1461 _act_file_final(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
1462 {
1463 asl_action_file_data_t *f_data;
1464 int is_dup;
1465 uint32_t len, msg_hash = 0;
1466 char *str;
1467 time_t now;
1468
1469 if (r->dst->private == NULL) return;
1470
1471 f_data = (asl_action_file_data_t *)r->dst->private;
1472 if (f_data->pending > 0) f_data->pending--;
1473
1474 /*
1475 * If print format is std, bsd, or msg, then skip messages with
1476 * no ASL_KEY_MSG, or without a value for it.
1477 */
1478 if (r->dst->flags & MODULE_FLAG_STD_BSD_MSG)
1479 {
1480 const char *msgval = NULL;
1481 if (asl_msg_lookup(msg, ASL_KEY_MSG, &msgval, NULL) != 0) return;
1482 if (msgval == NULL) return;
1483 }
1484
1485 now = time(NULL);
1486
1487 is_dup = 0;
1488
1489 str = asl_format_message(msg, r->dst->fmt, r->dst->tfmt, ASL_ENCODE_SAFE, &len);
1490
1491 if (r->dst->flags & MODULE_FLAG_COALESCE)
1492 {
1493 if (f_data->dup_timer == NULL)
1494 {
1495 /* create a timer to flush dups on this file */
1496 f_data->dup_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, asl_action_queue);
1497 dispatch_source_set_event_handler(f_data->dup_timer, ^{ _send_repeat_msg(r); });
1498 }
1499
1500 if ((global.bsd_max_dup_time > 0) && (str != NULL) && (f_data->last_msg != NULL))
1501 {
1502 msg_hash = asl_core_string_hash(str + 16, len - 16);
1503 if ((f_data->last_hash == msg_hash) && (!strcmp(f_data->last_msg, str + 16)))
1504 {
1505 if ((now - f_data->last_time) < global.bsd_max_dup_time) is_dup = 1;
1506 }
1507 }
1508 }
1509
1510 if (is_dup == 1)
1511 {
1512 if (f_data->last_count == 0)
1513 {
1514 /* start the timer */
1515 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);
1516 dispatch_resume(f_data->dup_timer);
1517 }
1518
1519 f_data->last_count++;
1520 }
1521 else
1522 {
1523 if (_act_dst_open(r, NULL, 0) != 0)
1524 {
1525 _asl_action_save_failed("_act_file", m, r, ASL_STATUS_FAILED);
1526 free(str);
1527 return;
1528 }
1529 else
1530 {
1531 r->dst->fails = 0;
1532 }
1533
1534 /*
1535 * The current message is not a duplicate. If f_data->last_count > 0
1536 * we need to write a "last message repeated N times" log entry.
1537 * _send_repeat_msg will free last_msg and do nothing if
1538 * last_count == 0, but we test and free here to avoid a function call.
1539 */
1540 if (f_data->last_count > 0)
1541 {
1542 _send_repeat_msg(r);
1543 }
1544 else
1545 {
1546 free(f_data->last_msg);
1547 f_data->last_msg = NULL;
1548 }
1549
1550 if (str != NULL) f_data->last_msg = strdup(str + 16);
1551
1552 f_data->last_hash = msg_hash;
1553 f_data->last_count = 0;
1554 f_data->last_time = now;
1555
1556 if ((str != NULL) && (len > 1))
1557 {
1558 /* write line to file and update dst size */
1559 size_t bytes = write(f_data->fd, str, len - 1);
1560 if (bytes > 0) r->dst->size += bytes;
1561
1562 if (_act_checkpoint(r, CHECKPOINT_TEST) == 1) trigger_aslmanager();
1563 }
1564 }
1565
1566 free(str);
1567 }
1568
1569 static void
_act_file(asl_out_module_t * m,asl_out_rule_t * r,asl_msg_t * msg)1570 _act_file(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
1571 {
1572 asl_action_file_data_t *f_data;
1573
1574 if (r == NULL) return;
1575 if (msg == NULL) return;
1576 if (m == NULL) return;
1577 if ((m->flags & MODULE_FLAG_ENABLED) == 0) return;
1578 if (r->dst == NULL) return;
1579 if (r->dst->private == NULL) return;
1580
1581 if (r->dst->flags & MODULE_FLAG_HAS_LOGGED) return;
1582
1583 r->dst->flags |= MODULE_FLAG_HAS_LOGGED;
1584 f_data = (asl_action_file_data_t *)r->dst->private;
1585 if (f_data != NULL) f_data->pending++;
1586
1587 #if TARGET_OS_EMBEDDED
1588 if (r->dst->flags & MODULE_FLAG_CRASHLOG)
1589 {
1590 _crashlog_queue_check();
1591 asl_msg_retain(msg);
1592 dispatch_async(crashlog_queue, ^{
1593 dispatch_async(asl_action_queue, ^{
1594 _act_file_final(m, r, msg);
1595 asl_msg_release(msg);
1596 });
1597 });
1598 return;
1599 }
1600 #endif
1601
1602 _act_file_final(m, r, msg);
1603 }
1604
1605 static void
_act_forward(asl_out_module_t * m,asl_out_rule_t * r,asl_msg_t * msg)1606 _act_forward(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
1607 {
1608 /* To do: <rdar://problem/6130747> Add a "forward" action to asl.conf */
1609 }
1610
1611 static void
_act_control(asl_out_module_t * m,asl_out_rule_t * r,asl_msg_t * msg)1612 _act_control(asl_out_module_t *m, asl_out_rule_t *r, asl_msg_t *msg)
1613 {
1614 const char *p;
1615
1616 if (m == NULL) return;
1617 if (r == NULL) return;
1618
1619 p = asl_msg_get_val_for_key(msg, ASL_KEY_MODULE);
1620
1621 if (r->options == NULL) return;
1622
1623 if (!strcmp(r->options, "enable"))
1624 {
1625 m->flags |= MODULE_FLAG_ENABLED;
1626 }
1627 else if (!strcmp(r->options, "disable"))
1628 {
1629 m->flags &= ~MODULE_FLAG_ENABLED;
1630 }
1631 else if ((!strcmp(r->options, "checkpoint")) || (!strcmp(r->options, "rotate")))
1632 {
1633 _act_file_checkpoint(m, NULL, CHECKPOINT_FORCE);
1634 }
1635 }
1636
1637 static void
_send_to_asl_store(asl_msg_t * msg)1638 _send_to_asl_store(asl_msg_t *msg)
1639 {
1640 if ((global.asl_out_module != NULL) && ((global.asl_out_module->flags & MODULE_FLAG_ENABLED) == 0)) return;
1641
1642 if (store_has_logged) return;
1643 store_has_logged = true;
1644
1645 db_save_message(msg);
1646 }
1647
1648 static int
_asl_out_process_message(asl_out_module_t * m,asl_msg_t * msg)1649 _asl_out_process_message(asl_out_module_t *m, asl_msg_t *msg)
1650 {
1651 asl_out_rule_t *r;
1652
1653 if (m == NULL) return 1;
1654 if (msg == NULL) return 1;
1655
1656 /* reset flag bit used for duplicate avoidance */
1657 for (r = m->ruleset; r != NULL; r = r->next)
1658 {
1659 if ((r->action == ACTION_FILE) || (r->action == ACTION_ASL_DIR) || (r->action == ACTION_ASL_FILE))
1660 {
1661 if (r->dst != NULL) r->dst->flags &= MODULE_FLAG_CLEAR_LOGGED;
1662 }
1663 }
1664
1665 for (r = m->ruleset; r != NULL; r = r->next)
1666 {
1667 if (r->query == NULL) continue;
1668
1669 /* ACTION_SET_FILE, ACTION_SET_PLIST, and ACTION_SET_PROF are handled independently */
1670 if ((r->action == ACTION_SET_FILE) || (r->action == ACTION_SET_PLIST) || (r->action == ACTION_SET_PROF)) continue;
1671
1672 /*
1673 * ACTION_CLAIM during processing is a filter. It will only be here if the option "only"
1674 * was supplied. In this case we test the message against the query. If it does not
1675 * match, we skip the message.
1676 */
1677 if (r->action == ACTION_CLAIM)
1678 {
1679 if ((asl_msg_cmp(r->query, msg) != 1)) return 0;
1680 }
1681
1682 if ((asl_msg_cmp(r->query, msg) == 1))
1683 {
1684 if (r->action == ACTION_NONE) continue;
1685 else if (r->action == ACTION_IGNORE) return 1;
1686 else if (r->action == ACTION_SKIP) return 0;
1687 else if (r->action == ACTION_ASL_STORE) _send_to_asl_store(msg);
1688 else if (r->action == ACTION_ACCESS) _act_access_control(m, r, msg);
1689 else if (r->action == ACTION_SET_KEY) _act_set_key(m, r, msg);
1690 else if (r->action == ACTION_UNSET_KEY) _act_unset_key(m, r, msg);
1691 else if (r->action == ACTION_NOTIFY) _act_notify(m, r);
1692 else if (r->action == ACTION_BROADCAST) _act_broadcast(m, r, msg);
1693 else if (r->action == ACTION_FORWARD) _act_forward(m, r, msg);
1694 else if (r->action == ACTION_CONTROL) _act_control(m, r, msg);
1695 else if (r->action == ACTION_SET_PARAM) _act_out_set_param(m, r->options, true);
1696 else if ((r->action == ACTION_ASL_FILE) || (r->action == ACTION_ASL_DIR)) _act_store(m, r, msg);
1697 else if (r->action == ACTION_FILE) _act_file(m, r, msg);
1698 }
1699 }
1700
1701 return 0;
1702 }
1703
1704 void
asl_out_message(asl_msg_t * msg)1705 asl_out_message(asl_msg_t *msg)
1706 {
1707 OSAtomicIncrement32(&global.asl_queue_count);
1708 asl_msg_retain(msg);
1709
1710 dispatch_async(asl_action_queue, ^{
1711 int ignore = 0;
1712 const char *p;
1713 time_t now = time(NULL);
1714 asl_out_module_t *m = global.asl_out_module;
1715
1716 store_has_logged = false;
1717
1718 p = asl_msg_get_val_for_key(msg, ASL_KEY_MODULE);
1719 if (p == NULL)
1720 {
1721 if ((action_asl_store_count == 0) || (asl_check_option(msg, ASL_OPT_STORE) == 1)) _send_to_asl_store(msg);
1722
1723 ignore = _asl_out_process_message(m, msg);
1724 if (ignore == 0)
1725 {
1726 if (m != NULL) m = m->next;
1727 while (m != NULL)
1728 {
1729 _asl_out_process_message(m, msg);
1730 m = m->next;
1731 }
1732 }
1733 }
1734 else
1735 {
1736 if (m != NULL) m = m->next;
1737 while (m != NULL)
1738 {
1739 if (!strcmp(p, m->name)) _asl_out_process_message(m, msg);
1740 m = m->next;
1741 }
1742 }
1743
1744 p = asl_msg_get_val_for_key(msg, ASL_KEY_FINAL_NOTIFICATION);
1745 if (p != NULL) asl_msg_set_key_val(msg, ASL_KEY_FREE_NOTE, p);
1746
1747 asl_msg_release(msg);
1748 OSAtomicDecrement32(&global.asl_queue_count);
1749
1750 if ((now - sweep_time) >= IDLE_CLOSE)
1751 {
1752 _asl_action_close_idle_files(IDLE_CLOSE);
1753 sweep_time = now;
1754 }
1755 });
1756 }
1757
1758 static char *
_asl_action_profile_test(asl_out_module_t * m,asl_out_rule_t * r)1759 _asl_action_profile_test(asl_out_module_t *m, asl_out_rule_t *r)
1760 {
1761 const char *ident;
1762 asl_msg_t *profile;
1763 bool eval;
1764
1765 /* ident is first message key */
1766 asl_msg_fetch(r->query, 0, &ident, NULL, NULL);
1767 if (ident == NULL)
1768 {
1769 r->action = ACTION_NONE;
1770 return NULL;
1771 }
1772
1773 #if 0
1774 profile = configuration_profile_to_asl_msg(ident);
1775 #endif
1776 eval = (asl_msg_cmp(r->query, profile) == 1);
1777 _act_out_set_param(m, r->options, eval);
1778 asl_msg_release(profile);
1779
1780 return strdup(ident);
1781 }
1782
1783 static const char *
_asl_action_file_test(asl_out_module_t * m,asl_out_rule_t * r)1784 _asl_action_file_test(asl_out_module_t *m, asl_out_rule_t *r)
1785 {
1786 const char *path;
1787 struct stat sb;
1788 int status;
1789 bool eval;
1790
1791 /* path is first message key */
1792 asl_msg_fetch(r->query, 0, &path, NULL, NULL);
1793 if (path == NULL)
1794 {
1795 r->action = ACTION_NONE;
1796 return NULL;
1797 }
1798
1799 memset(&sb, 0, sizeof(struct stat));
1800 status = stat(path, &sb);
1801 eval = (status == 0);
1802 _act_out_set_param(m, r->options, eval);
1803
1804 return path;
1805 }
1806
1807 static void
_asl_action_handle_file_change_notification(int t)1808 _asl_action_handle_file_change_notification(int t)
1809 {
1810 asl_out_module_t *m;
1811 asl_out_rule_t *r;
1812
1813 for (m = global.asl_out_module; m != NULL; m = m->next)
1814 {
1815 for (r = m->ruleset; r != NULL; r = r->next)
1816 {
1817 if (r->action == ACTION_SET_FILE)
1818 {
1819 asl_action_set_param_data_t *spdata = (asl_action_set_param_data_t *)r->private;
1820 if ((spdata != NULL) && (spdata->token == t))
1821 {
1822 _asl_action_file_test(m, r);
1823 return;
1824 }
1825 }
1826 else if (r->action == ACTION_SET_PLIST)
1827 {
1828 asl_action_set_param_data_t *spdata = (asl_action_set_param_data_t *)r->private;
1829 if ((spdata != NULL) && (spdata->token == t))
1830 {
1831 char *str = _asl_action_profile_test(m, r);
1832 free(str);
1833 return;
1834 }
1835 }
1836 else if (r->action == ACTION_SET_PROF)
1837 {
1838 asl_action_set_param_data_t *spdata = (asl_action_set_param_data_t *)r->private;
1839 if ((spdata != NULL) && (spdata->token == t))
1840 {
1841 char *str = _asl_action_profile_test(m, r);
1842 free(str);
1843 return;
1844 }
1845 }
1846 }
1847 }
1848
1849 asl_out_module_free(m);
1850 }
1851
1852 static void
_asl_action_post_process_rule(asl_out_module_t * m,asl_out_rule_t * r)1853 _asl_action_post_process_rule(asl_out_module_t *m, asl_out_rule_t *r)
1854 {
1855 if ((m == NULL) || (r == NULL)) return;
1856
1857 if (m != global.asl_out_module)
1858 {
1859 /* check if any previous module has used this destination */
1860 asl_out_module_t *n;
1861 bool search = true;
1862
1863 if ((r->dst != NULL) && (r->dst->path != NULL))
1864 {
1865 for (n = global.asl_out_module; search && (n != NULL) && (n != m); n = n->next)
1866 {
1867 asl_out_rule_t *s;
1868 for (s = n->ruleset; search && (s != NULL); s = s->next)
1869 {
1870 if (s->action == ACTION_OUT_DEST)
1871 {
1872 if ((s->dst != NULL) && (s->dst->path != NULL) && (!strcmp(r->dst->path, s->dst->path)))
1873 {
1874 /* rule r of module m is using previously used dst of rule s of module n */
1875 asl_out_dst_data_release(r->dst);
1876 r->dst = NULL;
1877
1878 if (r->action == ACTION_OUT_DEST)
1879 {
1880 char *str = NULL;
1881 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);
1882 internal_log_message(str);
1883 free(str);
1884 }
1885 else
1886 {
1887 r->dst = asl_out_dst_data_retain(s->dst);
1888 }
1889
1890 search = false;
1891 }
1892 }
1893 }
1894 }
1895 }
1896 }
1897
1898 if (r->action == ACTION_SET_PARAM)
1899 {
1900 if (r->query == NULL) _act_out_set_param(m, r->options, true);
1901 }
1902 else if (r->action == ACTION_CLAIM)
1903 {
1904 /* becomes ACTION_SKIP in com.apple.asl config */
1905 if (m != global.asl_out_module)
1906 {
1907 asl_out_rule_t *rule = (asl_out_rule_t *)calloc(1, sizeof(asl_out_rule_t));
1908 if (rule != NULL)
1909 {
1910 char *str = NULL;
1911 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);
1912 internal_log_message(str);
1913 free(str);
1914
1915 rule->query = asl_msg_copy(r->query);
1916 rule->action = ACTION_SKIP;
1917 rule->next = global.asl_out_module->ruleset;
1918 global.asl_out_module->ruleset = rule;
1919 }
1920
1921 /*
1922 * After adding ACTION_SKIP to com.apple.asl module, the claim becomes a no-op in this module
1923 * UNLESS the claim includes the option "only". In that case, the claim becomes a filter:
1924 * any messages that DO NOT match the claim are skipped by this module.
1925 */
1926 if (r->options == NULL) r->action = ACTION_NONE;
1927 else if (strcmp(r->options, "only") != 0) r->action = ACTION_NONE;
1928 }
1929 }
1930 else if (r->action == ACTION_ASL_STORE)
1931 {
1932 action_asl_store_count++;
1933 }
1934 else if (r->action == ACTION_ASL_DIR)
1935 {
1936 if (r->dst->private == NULL) r->dst->private = (asl_action_asl_store_data_t *)calloc(1, sizeof(asl_action_asl_store_data_t));
1937 }
1938 else if (r->action == ACTION_ASL_FILE)
1939 {
1940 if (r->dst->private == NULL)r->dst->private = (asl_action_asl_file_data_t *)calloc(1, sizeof(asl_action_asl_file_data_t));
1941 }
1942 else if (r->action == ACTION_FILE)
1943 {
1944 if (r->dst->private == NULL) r->dst->private = (asl_action_file_data_t *)calloc(1, sizeof(asl_action_file_data_t));
1945 if (r->dst->private != NULL) ((asl_action_file_data_t *)(r->dst->private))->fd = -1;
1946 }
1947 else if (r->action == ACTION_SET_PLIST)
1948 {
1949 char *ident =_asl_action_profile_test(m, r);
1950 char *notify_key = NULL; // XXX configuration_profile_create_notification_key(ident);
1951 free(ident);
1952
1953 if (notify_key != NULL)
1954 {
1955 int status, token;
1956 asl_action_set_param_data_t *spdata;
1957
1958 status = notify_register_dispatch(notify_key, &token, asl_action_queue, ^(int t){
1959 _asl_action_handle_file_change_notification(t);
1960 });
1961
1962 free(notify_key);
1963
1964 spdata = (asl_action_set_param_data_t *)calloc(1, sizeof(asl_action_set_param_data_t));
1965 if (spdata == NULL)
1966 {
1967 // XXX notify_cancel(token);
1968 }
1969 else
1970 {
1971 spdata->token = token;
1972 r->private = spdata;
1973 }
1974 }
1975 }
1976 else if (r->action == ACTION_SET_PROF)
1977 {
1978 char *ident =_asl_action_profile_test(m, r);
1979 char *notify_key = NULL; // XXX configuration_profile_create_notification_key(ident);
1980 free(ident);
1981
1982 if (notify_key != NULL)
1983 {
1984 int status, token;
1985 asl_action_set_param_data_t *spdata;
1986
1987 // status = notify_register_dispatch(notify_key, &token, asl_action_queue, ^(int t){
1988 // _asl_action_handle_file_change_notification(t);
1989 // XXX });
1990
1991 free(notify_key);
1992
1993 spdata = (asl_action_set_param_data_t *)calloc(1, sizeof(asl_action_set_param_data_t));
1994 if (spdata == NULL)
1995 {
1996 // XXX notify_cancel(token);
1997 }
1998 else
1999 {
2000 spdata->token = token;
2001 r->private = spdata;
2002 }
2003 }
2004 }
2005 else if (r->action == ACTION_SET_FILE)
2006 {
2007 char *notify_key;
2008 const char *path =_asl_action_file_test(m, r);
2009
2010 if (path != NULL)
2011 {
2012 asprintf(¬ify_key, "%s%s", NOTIFY_PATH_SERVICE, path);
2013 if (notify_key != NULL)
2014 {
2015 int status, token;
2016 asl_action_set_param_data_t *spdata;
2017
2018 // XXX status = notify_register_dispatch(notify_key, &token, asl_action_queue, ^(int t){
2019 // _asl_action_handle_file_change_notification(t);
2020 // });
2021
2022 free(notify_key);
2023
2024 spdata = (asl_action_set_param_data_t *)calloc(1, sizeof(asl_action_set_param_data_t));
2025 if (spdata == NULL)
2026 {
2027 // XXX notify_cancel(token);
2028 }
2029 else
2030 {
2031 spdata->token = token;
2032 r->private = spdata;
2033 }
2034 }
2035 }
2036 }
2037 }
2038
2039 static void
_asl_action_configure()2040 _asl_action_configure()
2041 {
2042 asl_out_rule_t *r;
2043 asl_out_module_t *m;
2044 uint32_t flags = 0;
2045
2046 if (global.asl_out_module == NULL) global.asl_out_module = asl_out_module_init();
2047 if (global.asl_out_module == NULL) return;
2048
2049 asldebug("%s: init\n", MY_ID);
2050
2051 action_asl_store_count = 0;
2052
2053 for (m = global.asl_out_module; m != NULL; m = m->next)
2054 {
2055 for (r = m->ruleset; r != NULL; r = r->next)
2056 {
2057 _asl_action_post_process_rule(m, r);
2058 if (r->dst != NULL) flags |= (r->dst->flags & (MODULE_FLAG_ROTATE | MODULE_FLAG_CRASHLOG));
2059 }
2060 }
2061
2062 if (global.debug != 0)
2063 {
2064 FILE *dfp;
2065 if (global.debug_file == NULL) dfp = fopen(_PATH_SYSLOGD_LOG, "a");
2066 else dfp = fopen(global.debug_file, "a");
2067 if (dfp != NULL)
2068 {
2069 for (m = global.asl_out_module; m != NULL; m = m->next)
2070 {
2071 fprintf(dfp, "module: %s%s\n", (m->name == NULL) ? "<unknown>" : m->name, (m->flags & MODULE_FLAG_LOCAL) ? " (local)" : "");
2072 asl_out_module_print(dfp, m);
2073 fprintf(dfp, "\n");
2074 }
2075 fclose(dfp);
2076 }
2077 }
2078
2079 sweep_time = time(NULL);
2080
2081 if (flags & MODULE_FLAG_ROTATE)
2082 {
2083 _act_file_checkpoint_all(CHECKPOINT_TEST);
2084 if (checkpoint_timer == NULL) _start_cycling();
2085 }
2086 }
2087
2088 int
asl_action_init(void)2089 asl_action_init(void)
2090 {
2091 static dispatch_once_t once;
2092
2093 dispatch_once(&once, ^{
2094 asl_action_queue = dispatch_queue_create("ASL Action Queue", NULL);
2095 #if TARGET_OS_EMBEDDED
2096 crashlog_queue = dispatch_queue_create("iOS CrashLog Queue", NULL);
2097 notify_register_dispatch(CRASH_MOVER_SERVICE, &crashmover_token, asl_action_queue, ^(int unused) {
2098 uint64_t cmstate = 0;
2099 uint64_t oldstate = (crashmover_state == 0) ? 0llu : 1llu;
2100
2101 uint32_t status = 1; // XXX notify_get_state(crashmover_token, &cmstate);
2102 if (status == 0)
2103 {
2104 if (cmstate != oldstate)
2105 {
2106 crashmover_state = 0;
2107 if (cmstate == 1) crashmover_state = time(NULL);
2108
2109 if (crashmover_state == 0)
2110 {
2111 asldebug("CrashMover finished\n");
2112 dispatch_resume(crashlog_queue);
2113 }
2114 else
2115 {
2116 asldebug("CrashMover active: suspending crashlog queue and closing files\n");
2117 dispatch_suspend(crashlog_queue);
2118 _asl_action_close_idle_files(0);
2119 }
2120 }
2121 }
2122 });
2123 #endif
2124 });
2125
2126 _asl_action_configure();
2127
2128 return 0;
2129 }
2130
2131 /*
2132 * Close outputs and free modules.
2133 */
2134 static void
_asl_action_free_modules(asl_out_module_t * m)2135 _asl_action_free_modules(asl_out_module_t *m)
2136 {
2137 asl_out_rule_t *r;
2138 asl_out_module_t *x;
2139
2140 /*
2141 * asl_common frees a list of modules with asl_out_module_free.
2142 * This loop frees the private data attached some modules.
2143 */
2144 for (x = m; x != NULL; x = x->next)
2145 {
2146 for (r = x->ruleset; r != NULL; r = r->next)
2147 {
2148 if (r->action == ACTION_ASL_DIR)
2149 {
2150 _act_dst_close(r, DST_CLOSE_SHUTDOWN);
2151 if (r->dst != NULL)
2152 {
2153 _asl_action_asl_store_data_free((asl_action_asl_store_data_t *)r->dst->private);
2154 r->dst->private = NULL;
2155 }
2156 }
2157 else if (r->action == ACTION_ASL_FILE)
2158 {
2159 _act_dst_close(r, DST_CLOSE_SHUTDOWN);
2160 if (r->dst != NULL)
2161 {
2162 _asl_action_asl_file_data_free((asl_action_asl_file_data_t *)r->dst->private);
2163 r->dst->private = NULL;
2164 }
2165 }
2166 else if (r->action == ACTION_FILE)
2167 {
2168 _act_dst_close(r, DST_CLOSE_SHUTDOWN);
2169 if (r->dst != NULL)
2170 {
2171 asl_action_file_data_t *f_data = (asl_action_file_data_t *)r->dst->private;
2172 if (f_data != NULL)
2173 {
2174 /* flush repeat message if necessary */
2175 if (f_data->last_count > 0) _send_repeat_msg(r);
2176 _asl_action_file_data_free(f_data);
2177 r->dst->private = NULL;
2178 }
2179 }
2180 }
2181 else if (r->action == ACTION_SET_PLIST)
2182 {
2183 _asl_action_set_param_data_free((asl_action_set_param_data_t *)r->private);
2184 }
2185 else if (r->action == ACTION_SET_PROF)
2186 {
2187 _asl_action_set_param_data_free((asl_action_set_param_data_t *)r->private);
2188 }
2189 else if (r->action == ACTION_SET_FILE)
2190 {
2191 _asl_action_set_param_data_free((asl_action_set_param_data_t *)r->private);
2192 }
2193 }
2194 }
2195
2196 asl_out_module_free(m);
2197 }
2198
2199 static int
_asl_action_close_internal(void)2200 _asl_action_close_internal(void)
2201 {
2202 #if TARGET_OS_EMBEDDED
2203 if (crashmover_state != 0)
2204 {
2205 dispatch_resume(crashlog_queue);
2206 crashmover_state = 0;
2207 }
2208
2209 /* wait for the crashlog_queue to flush before _asl_action_free_modules() */
2210 dispatch_sync(crashlog_queue, ^{ int x = 0; if (x == 1) x = 2; });
2211 #endif
2212
2213 _asl_action_free_modules(global.asl_out_module);
2214 global.asl_out_module = NULL;
2215 sweep_time = time(NULL);
2216
2217 return 0;
2218 }
2219
2220 static void
_asl_action_close_idle_files(time_t idle_time)2221 _asl_action_close_idle_files(time_t idle_time)
2222 {
2223 asl_out_module_t *m;
2224 time_t now = time(NULL);
2225
2226 for (m = global.asl_out_module; m != NULL; m = m->next)
2227 {
2228 asl_out_rule_t *r;
2229
2230 for (r = m->ruleset; r != NULL; r = r->next)
2231 {
2232 if (idle_time == 0)
2233 {
2234 if ((r->dst != NULL) && (r->dst->flags & MODULE_FLAG_CRASHLOG))
2235 {
2236 _act_dst_close(r, DST_CLOSE_IDLE);
2237 //TODO: can r->action even be ACTION_ASL_DIR?
2238 /* if not, we can avoid the extra check here */
2239 if (r->action != ACTION_ASL_DIR) _act_checkpoint(r, CHECKPOINT_FORCE);
2240 }
2241 }
2242 else if (r->action == ACTION_ASL_DIR)
2243 {
2244 if (r->dst != NULL)
2245 {
2246 asl_action_asl_store_data_t *as_data = (asl_action_asl_store_data_t *)r->dst->private;
2247 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);
2248 }
2249 }
2250 else if (r->action == ACTION_ASL_FILE)
2251 {
2252 if (r->dst != NULL)
2253 {
2254 asl_action_asl_file_data_t *af_data = (asl_action_asl_file_data_t *)r->dst->private;
2255 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);
2256 }
2257 }
2258 else if (r->action == ACTION_FILE)
2259 {
2260 if (r->dst != NULL)
2261 {
2262 asl_action_file_data_t *f_data = (asl_action_file_data_t *)r->dst->private;
2263 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);
2264 }
2265 }
2266 }
2267 }
2268 }
2269
2270 int
asl_action_close(void)2271 asl_action_close(void)
2272 {
2273 dispatch_async(asl_action_queue, ^{
2274 _asl_action_close_internal();
2275 });
2276
2277 return 0;
2278 }
2279
2280 int
asl_action_reset(void)2281 asl_action_reset(void)
2282 {
2283 dispatch_async(asl_action_queue, ^{
2284 _asl_action_close_internal();
2285 asl_action_init();
2286 });
2287
2288 return 0;
2289 }
2290
2291 asl_out_module_t *
_asl_action_module_with_name(const char * name)2292 _asl_action_module_with_name(const char *name)
2293 {
2294 asl_out_module_t *m;
2295
2296 if (global.asl_out_module == NULL) return NULL;
2297 if (name == NULL) return global.asl_out_module;
2298
2299 for (m = global.asl_out_module; m != NULL; m = m->next)
2300 {
2301 if ((m->name != NULL) && (!strcmp(m->name, name))) return m;
2302 }
2303
2304 return NULL;
2305 }
2306
2307 /*
2308 * called from control_message
2309 * Used to control modules dynamically.
2310 * Line format "@ module param [value ...]"
2311 *
2312 * Note this is synchronous on asl_action queue.
2313 */
2314 int
asl_action_control_set_param(const char * s)2315 asl_action_control_set_param(const char *s)
2316 {
2317 __block char **l;
2318 __block char *p;
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 p = strdup(s);
2349 if (p == 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
2360 /* skip name, whitespace, "define" */
2361 while ((*p != ' ') && (*p != '\t')) p++;
2362 while ((*p == ' ') || (*p == '\t')) p++;
2363 while ((*p != ' ') && (*p != '\t')) p++;
2364
2365 m = _asl_action_module_with_name(l[0]);
2366 if (m == NULL)
2367 {
2368 asl_out_module_t *x;
2369
2370 m = asl_out_module_new(l[0]);
2371 for (x = global.asl_out_module; x->next != NULL; x = x->next);
2372 x->next = m;
2373 }
2374
2375 r = asl_out_module_parse_line(m, p);
2376 if (r != NULL)
2377 {
2378 _asl_action_post_process_rule(m, r);
2379 if ((r->dst != NULL) && (r->dst->flags & MODULE_FLAG_ROTATE))
2380 {
2381 _act_file_checkpoint_all(CHECKPOINT_TEST);
2382 if (checkpoint_timer == NULL) _start_cycling();
2383 }
2384 }
2385 });
2386
2387 free(p);
2388 free_string_list(l);
2389 return 0;
2390 }
2391
2392 dispatch_sync(asl_action_queue, ^{
2393 uint32_t intval;
2394 int do_all = 0;
2395 asl_out_module_t *m;
2396
2397 if (!strcmp(l[0], "*"))
2398 {
2399 do_all = 1;
2400 m = _asl_action_module_with_name(NULL);
2401 }
2402 else
2403 {
2404 m = _asl_action_module_with_name(l[0]);
2405 }
2406
2407 while (m != NULL)
2408 {
2409 if (!strcasecmp(l[1], "enable"))
2410 {
2411 intval = 1;
2412
2413 /* don't do enable for ASL_MODULE_NAME if input name is "*" */
2414 if ((do_all == 0) || (strcmp(m->name, ASL_MODULE_NAME)))
2415 {
2416 /* @ module enable {0|1} */
2417 if (count > 2) intval = atoi(l[2]);
2418
2419 if (intval == 0) m->flags &= ~MODULE_FLAG_ENABLED;
2420 else m->flags |= MODULE_FLAG_ENABLED;
2421 }
2422 }
2423 else if (!strcasecmp(l[1], "checkpoint"))
2424 {
2425 /* @ module checkpoint [file] */
2426 if (count > 2) _act_file_checkpoint(m, l[2], CHECKPOINT_FORCE);
2427 else _act_file_checkpoint(m, NULL, CHECKPOINT_FORCE);
2428 }
2429
2430 if (do_all == 1) m = m->next;
2431 else m = NULL;
2432 }
2433
2434 });
2435
2436 free_string_list(l);
2437 return 0;
2438 }
2439
2440 int
asl_action_file_checkpoint(const char * module,const char * path)2441 asl_action_file_checkpoint(const char *module, const char *path)
2442 {
2443 /* Note this is synchronous on asl_action queue */
2444 dispatch_sync(asl_action_queue, ^{
2445 asl_out_module_t *m = _asl_action_module_with_name(module);
2446 _act_file_checkpoint(m, path, CHECKPOINT_FORCE);
2447 });
2448
2449 return 0;
2450 }
2451