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