]>
Commit | Line | Data |
---|---|---|
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 | |
65 | static dispatch_queue_t asl_action_queue; | |
81582353 A |
66 | static dispatch_source_t checkpoint_timer; |
67 | static time_t sweep_time = 0; | |
b16a592a | 68 | |
81582353 A |
69 | #if TARGET_OS_EMBEDDED |
70 | static dispatch_queue_t crashlog_queue; | |
71 | static dispatch_source_t crashlog_sentinel_src; | |
72 | static int crashlog_sentinel_fd = -1; | |
73 | static time_t crashmover_state = 0; | |
74 | static int crashmover_token = -1; | |
75 | #endif | |
76 | ||
77 | typedef struct store_data | |
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 | 89 | typedef 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 | 100 | typedef struct set_param_data |
c4fdb7d1 | 101 | { |
81582353 A |
102 | int token; |
103 | } asl_action_set_param_data_t; | |
c4fdb7d1 | 104 | |
81582353 A |
105 | static int action_asl_store_count; |
106 | static bool store_has_logged; | |
c4fdb7d1 | 107 | |
81582353 | 108 | extern void db_save_message(aslmsg m); |
b16a592a | 109 | |
81582353 A |
110 | /* forward */ |
111 | static int _act_file_checkpoint_all(uint32_t force); | |
112 | static void _asl_action_post_process_rule(asl_out_module_t *m, asl_out_rule_t *r); | |
113 | static void _asl_action_close_idle_files(time_t idle_time); | |
b16a592a | 114 | |
81582353 A |
115 | static 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 | 166 | static 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 | 178 | static 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 | ||
211 | static 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 |
246 | static void | |
247 | _crashlog_sentinel_init(void) | |
248 | { | |
249 | char path[MAXPATHLEN]; | |
250 | ||
251 | if (crashlog_sentinel_src != NULL) return; | |
252 | ||
253 | snprintf(path, sizeof(path), "%s/com.apple.asl.%ld", _PATH_CRASHREPORTER, time(NULL)); | |
254 | ||
255 | crashlog_sentinel_fd = open(path, O_WRONLY | O_CREAT); | |
256 | if (crashlog_sentinel_fd < 0) | |
257 | { | |
258 | char *str = NULL; | |
259 | asprintf(&str, "[Sender syslogd] [Level 3] [PID %u] [Facility syslog] [Message Sentinel %s create/open failed (%s)]", global.pid, path, strerror(errno)); | |
260 | internal_log_message(str); | |
261 | free(str); | |
262 | return; | |
263 | } | |
264 | ||
265 | close(crashlog_sentinel_fd); | |
266 | ||
267 | crashlog_sentinel_fd = open(path, O_EVTONLY, 0); | |
268 | if (crashlog_sentinel_fd < 0) | |
269 | { | |
270 | char *str = NULL; | |
271 | asprintf(&str, "[Sender syslogd] [Level 3] [PID %u] [Facility syslog] [Message Sentinel %s event/open failed (%s)]", global.pid, path, strerror(errno)); | |
272 | internal_log_message(str); | |
273 | free(str); | |
274 | return; | |
275 | } | |
276 | ||
277 | crashlog_sentinel_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, (uintptr_t)crashlog_sentinel_fd, DISPATCH_VNODE_DELETE, asl_action_queue); | |
278 | if (crashlog_sentinel_src == NULL) | |
279 | { | |
280 | char *str = NULL; | |
281 | asprintf(&str, "[Sender syslogd] [Level 3] [PID %u] [Facility syslog] [Message Sentinel %s dispatch_source_create failed]", global.pid, path); | |
282 | internal_log_message(str); | |
283 | free(str); | |
284 | close(crashlog_sentinel_fd); | |
285 | crashlog_sentinel_fd = -1; | |
286 | return; | |
287 | } | |
288 | ||
289 | dispatch_source_set_event_handler(crashlog_sentinel_src, ^{ | |
290 | if (crashmover_state != 0) | |
291 | { | |
292 | asldebug("CrashMover inactive / sentinel deleted: resuming crashlog queue\n"); | |
293 | dispatch_resume(crashlog_queue); | |
294 | crashmover_state = 0; | |
295 | } | |
296 | ||
297 | if (crashlog_sentinel_src != NULL) | |
298 | { | |
299 | dispatch_source_cancel(crashlog_sentinel_src); | |
300 | dispatch_release(crashlog_sentinel_src); | |
301 | } | |
302 | ||
303 | crashlog_sentinel_src = NULL; | |
304 | ||
305 | close(crashlog_sentinel_fd); | |
306 | crashlog_sentinel_fd = -1; | |
307 | _crashlog_sentinel_init(); | |
308 | }); | |
309 | ||
310 | dispatch_resume(crashlog_sentinel_src); | |
311 | asldebug("Created CrashLog Sentinel: %s\n", path); | |
312 | } | |
313 | ||
314 | static void | |
315 | _crashlog_queue_check(void) | |
316 | { | |
317 | /* | |
318 | * Check whether the crashlog queue has been suspended for too long. | |
319 | * We allow the crashlog quque to be suspended for 60 seconds. | |
320 | * After that, we start logging again. This prevents syslogd from | |
321 | * filling memory due to a suspended queue. CrashMover really shoud | |
322 | * take no more than a second or two to finish. | |
323 | */ | |
324 | if (crashmover_state == 0) return; | |
325 | if ((time(NULL) - crashmover_state) <= 60) return; | |
326 | ||
327 | asldebug("CrashMover timeout: resuming crashlog queue\n"); | |
328 | dispatch_resume(crashlog_queue); | |
329 | crashmover_state = 0; | |
330 | ||
331 | /* | |
332 | * crashlog_sentinel_src should never be NULL, but if | |
333 | * _crashlog_sentinel_init failed for some strange reason, | |
334 | * it will be NULL here. | |
335 | */ | |
336 | if (crashlog_sentinel_src != NULL) | |
337 | { | |
338 | dispatch_source_cancel(crashlog_sentinel_src); | |
339 | dispatch_release(crashlog_sentinel_src); | |
340 | } | |
341 | ||
342 | crashlog_sentinel_src = NULL; | |
343 | ||
344 | close(crashlog_sentinel_fd); | |
345 | crashlog_sentinel_fd = -1; | |
346 | _crashlog_sentinel_init(); | |
347 | } | |
348 | #endif | |
349 | ||
350 | static void | |
351 | _act_dst_close(asl_out_rule_t *r) | |
352 | { | |
353 | if (r == NULL) return; | |
354 | if (r->dst == NULL) return; | |
355 | if (r->dst->private == NULL) return; | |
356 | ||
357 | if ((r->action == ACTION_ASL_DIR) || (r->action == ACTION_ASL_FILE)) | |
358 | { | |
359 | asl_action_store_data_t *sdata = (asl_action_store_data_t *)r->dst->private; | |
360 | if (sdata->store != NULL) asl_file_close(sdata->store); | |
361 | sdata->store = NULL; | |
362 | ||
363 | if (sdata->storedata != NULL) fclose(sdata->storedata); | |
364 | sdata->storedata = NULL; | |
365 | ||
366 | if (sdata->monitor != NULL) | |
367 | { | |
368 | dispatch_source_cancel(sdata->monitor); | |
369 | dispatch_release(sdata->monitor); | |
370 | sdata->monitor = NULL; | |
371 | } | |
372 | } | |
373 | else if (r->action == ACTION_FILE) | |
374 | { | |
375 | asl_action_file_data_t *fdata = (asl_action_file_data_t *)r->dst->private; | |
376 | if (fdata->fd >= 0) close(fdata->fd); | |
377 | fdata->fd = -1; | |
378 | ||
379 | if (fdata->monitor != NULL) | |
380 | { | |
381 | dispatch_source_cancel(fdata->monitor); | |
382 | dispatch_release(fdata->monitor); | |
383 | fdata->monitor = NULL; | |
384 | } | |
385 | } | |
386 | } | |
387 | ||
c4fdb7d1 | 388 | static 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 | 455 | static 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 | ||
630 | static 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 |
644 | static 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 |
669 | static void |
670 | _asl_action_set_param_data_free(asl_action_set_param_data_t *spdata) | |
671 | { | |
672 | if (spdata != NULL) notify_cancel(spdata->token); | |
673 | free(spdata); | |
674 | } | |
c4fdb7d1 | 675 | |
81582353 A |
676 | static void |
677 | _asl_action_save_failed(const char *where, asl_out_module_t *m, asl_out_rule_t *r, uint32_t status) | |
678 | { | |
679 | if (r->dst->flags & MODULE_FLAG_SOFT_WRITE) return; | |
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 |
700 | static 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 |
745 | static int |
746 | _act_store_dir(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg) | |
747 | { | |
748 | asl_action_store_data_t *sdata; | |
749 | uint32_t status; | |
750 | uint64_t mid; | |
751 | const char *val; | |
752 | time_t tick; | |
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 |
792 | static void |
793 | _act_store_final(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg) | |
794 | { | |
795 | if (r->action == ACTION_ASL_DIR) _act_store_dir(m, r, msg); | |
796 | else _act_store_file(m, r, msg); | |
797 | } | |
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 | */ | |
803 | static void | |
804 | _act_store(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg) | |
805 | { | |
806 | if (r == NULL) return; | |
807 | if (msg == NULL) return; | |
808 | if (m == NULL) return; | |
809 | if ((m->flags & MODULE_FLAG_ENABLED) == 0) return; | |
810 | if (r->dst == NULL) return; | |
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 | 831 | static 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 |
878 | static 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 |
916 | static 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) */ |
945 | static 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 |
966 | static 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 |
1010 | static 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 | ||
1026 | static 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 | ||
1152 | static 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 | ||
1182 | static void | |
1183 | _act_forward(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg) | |
1184 | { | |
1185 | /* To do: <rdar://problem/6130747> Add a "forward" action to asl.conf */ | |
1186 | } | |
1187 | ||
1188 | static void | |
1189 | _act_control(asl_out_module_t *m, asl_out_rule_t *r, aslmsg msg) | |
1190 | { | |
1191 | const char *p; | |
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 |
1213 | static void |
1214 | _send_to_asl_store(aslmsg msg) | |
1215 | { | |
1216 | if ((global.asl_out_module != NULL) && ((global.asl_out_module->flags & MODULE_FLAG_ENABLED) == 0)) return; | |
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 |
1224 | static 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 |
1278 | void |
1279 | asl_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 |
1329 | static char * |
1330 | _asl_action_profile_test(asl_out_module_t *m, asl_out_rule_t *r) | |
1331 | { | |
1332 | const char *ident; | |
1333 | asl_msg_t *profile; | |
1334 | bool eval; | |
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 |
1352 | static 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 | ||
1376 | static void | |
1377 | _asl_action_handle_file_change_notification(int t) | |
1378 | { | |
1379 | asl_out_module_t *m; | |
1380 | asl_out_rule_t *r; | |
1381 | ||
1382 | for (m = global.asl_out_module; m != NULL; m = m->next) | |
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 | 1421 | static 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(¬ify_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 |
1608 | static 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 | ||
1678 | int | |
1679 | asl_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 | */ | |
1707 | static 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 |
1757 | static int |
1758 | _asl_action_close_internal(void) | |
1759 | { | |
1760 | #if TARGET_OS_EMBEDDED | |
1761 | dispatch_source_cancel(crashlog_sentinel_src); | |
1762 | dispatch_release(crashlog_sentinel_src); | |
1763 | crashlog_sentinel_src = NULL; | |
1764 | close(crashlog_sentinel_fd); | |
1765 | if (crashmover_state != 0) | |
1766 | { | |
1767 | dispatch_resume(crashlog_queue); | |
1768 | crashmover_state = 0; | |
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 |
1782 | static void |
1783 | _asl_action_close_idle_files(time_t idle_time) | |
1784 | { | |
1785 | asl_out_module_t *m; | |
1786 | time_t now = time(NULL); | |
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 | |
1822 | int | |
1823 | asl_action_close(void) | |
1824 | { | |
1825 | dispatch_async(asl_action_queue, ^{ | |
1826 | _asl_action_close_internal(); | |
1827 | }); | |
1828 | ||
1829 | return 0; | |
1830 | } | |
1831 | ||
1832 | int | |
1833 | asl_action_reset(void) | |
1834 | { | |
1835 | dispatch_async(asl_action_queue, ^{ | |
1836 | _asl_action_close_internal(); | |
1837 | asl_action_init(); | |
1838 | }); | |
1839 | ||
1840 | return 0; | |
1841 | } | |
1842 | ||
81582353 A |
1843 | asl_out_module_t * |
1844 | _asl_action_module_with_name(const char *name) | |
1845 | { | |
1846 | asl_out_module_t *m; | |
1847 | ||
1848 | if (global.asl_out_module == NULL) return NULL; | |
1849 | if (name == NULL) return global.asl_out_module; | |
1850 | ||
1851 | for (m = global.asl_out_module; m != NULL; m = m->next) | |
1852 | { | |
1853 | if ((m->name != NULL) && (!strcmp(m->name, name))) return m; | |
1854 | } | |
1855 | ||
1856 | return NULL; | |
1857 | } | |
1858 | ||
1859 | /* | |
1860 | * called from control_message | |
1861 | * Used to control modules dynamically. | |
1862 | * Line format "@ module param [value ...]" | |
1863 | * | |
1864 | * Note this is synchronous on asl_action queue. | |
1865 | */ | |
db78b1bd | 1866 | int |
81582353 | 1867 | asl_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 |
1986 | int |
1987 | asl_action_file_checkpoint(const char *module, const char *path) | |
1988 | { | |
1989 | /* Note this is synchronous on asl_action queue */ | |
1990 | dispatch_sync(asl_action_queue, ^{ | |
1991 | asl_out_module_t *m = _asl_action_module_with_name(module); | |
1992 | _act_file_checkpoint(m, path, CHECKPOINT_FORCE); | |
1993 | }); | |
1994 | ||
1995 | return 0; | |
1996 | } |