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