]> git.saurik.com Git - apple/syslog.git/blob - aslmanager.tproj/aslmanager.c
syslog-356.200.4.tar.gz
[apple/syslog.git] / aslmanager.tproj / aslmanager.c
1 /*
2 * Copyright (c) 2007-2015 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <asl.h>
25 #include <asl_msg.h>
26 #include <asl_msg_list.h>
27 #include <asl_store.h>
28 #include <errno.h>
29 #include <vproc_priv.h>
30
31 #include "asl_common.h"
32 #include "daemon.h"
33 #include "cache_delete.h"
34
35 /* global */
36 bool dryrun;
37 uint32_t debug;
38 FILE *debugfp;
39 dispatch_queue_t work_queue;
40
41 static dispatch_queue_t server_queue;
42 static time_t module_ttl;
43 static xpc_connection_t listener;
44 static bool main_task_enqueued;
45 static bool initial_main_task = true;
46 static dispatch_source_t sig_term_src;
47
48 /* wait 5 minutes to run main task after being invoked by XPC */
49 #define MAIN_TASK_INITIAL_DELAY 300
50
51 /*
52 * Used to set config parameters.
53 * Line format "= name value"
54 */
55 static void
56 _aslmanager_set_param(asl_out_dst_data_t *dst, char *s)
57 {
58 char **l;
59 uint32_t count;
60
61 if (s == NULL) return;
62 if (s[0] == '\0') return;
63
64 /* skip '=' and whitespace */
65 if (*s == '=') s++;
66 while ((*s == ' ') || (*s == '\t')) s++;
67
68 l = explode(s, " \t");
69 if (l == NULL) return;
70
71 for (count = 0; l[count] != NULL; count++);
72
73 /* name is required */
74 if (count == 0)
75 {
76 free_string_list(l);
77 return;
78 }
79
80 /* value is required */
81 if (count == 1)
82 {
83 free_string_list(l);
84 return;
85 }
86
87 if (!strcasecmp(l[0], "aslmanager_debug"))
88 {
89 /* = debug level */
90 set_debug(DEBUG_ASL, l[1]);
91 }
92 else if (!strcasecmp(l[0], "store_ttl"))
93 {
94 /* = store_ttl days */
95 dst->ttl[LEVEL_ALL] = asl_core_str_to_time(l[1], SECONDS_PER_DAY);
96 }
97 else if (!strcasecmp(l[0], "module_ttl"))
98 {
99 /* = module_ttl days */
100 module_ttl = asl_core_str_to_time(l[1], SECONDS_PER_DAY);
101 }
102 else if (!strcasecmp(l[0], "max_store_size"))
103 {
104 /* = max_file_size bytes */
105 dst->all_max = asl_core_str_to_size(l[1]);
106 }
107 else if (!strcasecmp(l[0], "archive"))
108 {
109 free(dst->rotate_dir);
110 dst->rotate_dir = NULL;
111
112 /* = archive {0|1} path */
113 if (!strcmp(l[1], "1"))
114 {
115 if (l[2] == NULL) dst->rotate_dir = strdup(PATH_ASL_ARCHIVE);
116 else dst->rotate_dir = strdup(l[2]);
117 }
118 }
119 else if (!strcasecmp(l[0], "store_path"))
120 {
121 /* = archive path */
122 free(dst->path);
123 dst->path = strdup(l[1]);
124 }
125 else if (!strcasecmp(l[0], "archive_mode"))
126 {
127 dst->mode = strtol(l[1], NULL, 0);
128 if ((dst->mode == 0) && (errno == EINVAL)) dst->mode = 0400;
129 }
130
131 free_string_list(l);
132 }
133
134 int
135 cli_main(int argc, char *argv[])
136 {
137 int i, work;
138 asl_out_module_t *mod, *m;
139 asl_out_rule_t *r;
140 asl_out_dst_data_t store, opts, *asl_store_dst = NULL;
141 const char *mname = NULL;
142 bool quiet = false;
143 bool cache_delete = false;
144 bool cache_delete_query = false;
145
146 #if !TARGET_OS_SIMULATOR
147 if (geteuid() != 0)
148 {
149 if (argc == 0) debug = DEBUG_ASL;
150 else debug = DEBUG_STDERR;
151
152 debug_log(ASL_LEVEL_ERR, "aslmanager must be run by root\n");
153 exit(1);
154 }
155 #endif
156
157 module_ttl = DEFAULT_TTL;
158
159 /* cobble up a dst_data with defaults and parameter settings */
160 memset(&store, 0, sizeof(store));
161 store.ttl[LEVEL_ALL] = DEFAULT_TTL;
162 store.all_max = DEFAULT_MAX_SIZE;
163
164 memset(&opts, 0, sizeof(opts));
165 opts.ttl[LEVEL_ALL] = DEFAULT_TTL;
166 opts.all_max = DEFAULT_MAX_SIZE;
167
168 for (i = 1; i < argc; i++)
169 {
170 if (!strcmp(argv[i], "-q"))
171 {
172 quiet = true;
173 }
174 else if (!strcmp(argv[i], "-dd"))
175 {
176 quiet = true;
177 }
178 else if (!strcmp(argv[i], "-s"))
179 {
180 if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
181 {
182 store.path = strdup(argv[++i]);
183 asl_store_dst = &store;
184 }
185 }
186 }
187
188 if (!quiet)
189 {
190 char *path = NULL;
191 int status = asl_make_database_dir(NULL, NULL);
192 if (status == 0) status = asl_make_database_dir(ASL_INTERNAL_LOGS_DIR, &path);
193 if (status == 0)
194 {
195 char tstamp[32], *str = NULL;
196
197 asl_make_timestamp(time(NULL), MODULE_NAME_STYLE_STAMP_LCL_B, tstamp, sizeof(tstamp));
198 asprintf(&str, "%s/aslmanager.%s", path, tstamp);
199
200 if (str != NULL)
201 {
202 if (status == 0) debugfp = fopen(str, "w");
203 if (debugfp != NULL) debug |= DEBUG_FILE;
204 free(str);
205 }
206 }
207 free(path);
208 }
209
210 /* get parameters from asl.conf */
211 mod = asl_out_module_init();
212
213 if (mod != NULL)
214 {
215 for (r = mod->ruleset; (r != NULL) && (asl_store_dst == NULL); r = r->next)
216 {
217 if ((r->dst != NULL) && (r->action == ACTION_OUT_DEST) && (!strcmp(r->dst->path, PATH_ASL_STORE)))
218 asl_store_dst = r->dst;
219 }
220
221 for (r = mod->ruleset; r != NULL; r = r->next)
222 {
223 if (r->action == ACTION_SET_PARAM)
224 {
225 if (r->query == NULL) _aslmanager_set_param(asl_store_dst, r->options);
226 }
227 }
228 }
229
230 work = DO_ASLDB | DO_MODULE;
231
232 for (i = 1; i < argc; i++)
233 {
234 if (!strcmp(argv[i], "-a"))
235 {
236 if (asl_store_dst == NULL) asl_store_dst = &store;
237
238 if (((i + 1) < argc) && (argv[i + 1][0] != '-')) asl_store_dst->rotate_dir = strdup(argv[++i]);
239 else asl_store_dst->rotate_dir = strdup(PATH_ASL_ARCHIVE);
240 asl_store_dst->mode = 0400;
241 }
242 else if (!strcmp(argv[i], "-store_ttl"))
243 {
244 if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
245 {
246 if (asl_store_dst == NULL) asl_store_dst = &store;
247 asl_store_dst->ttl[LEVEL_ALL] = asl_core_str_to_time(argv[++i], SECONDS_PER_DAY);
248 }
249 }
250 else if (!strcmp(argv[i], "-module_ttl"))
251 {
252 if (((i + 1) < argc) && (argv[i + 1][0] != '-')) module_ttl = asl_core_str_to_time(argv[++i], SECONDS_PER_DAY);
253 }
254 else if (!strcmp(argv[i], "-ttl"))
255 {
256 if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
257 {
258 opts.ttl[LEVEL_ALL] = asl_core_str_to_time(argv[++i], SECONDS_PER_DAY);
259
260 if (asl_store_dst == NULL) asl_store_dst = &store;
261 asl_store_dst->ttl[LEVEL_ALL] = opts.ttl[LEVEL_ALL];
262
263 module_ttl = opts.ttl[LEVEL_ALL];
264 }
265 }
266 else if (!strcmp(argv[i], "-size"))
267 {
268 if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
269 {
270 opts.all_max = asl_core_str_to_size(argv[++i]);
271
272 if (asl_store_dst == NULL) asl_store_dst = &store;
273 asl_store_dst->all_max = opts.all_max;
274 }
275 }
276 else if (!strcmp(argv[i], "-checkpoint"))
277 {
278 work |= DO_CHECKPT;
279 }
280 else if (!strcmp(argv[i], "-cache_delete"))
281 {
282 cache_delete = true;
283 if (((i + 1) < argc) && (argv[i + 1][0] == 'q')) cache_delete_query = true;
284 }
285 else if (!strcmp(argv[i], "-module"))
286 {
287 work &= ~DO_ASLDB;
288
289 /* optional name follows -module */
290 if ((i +1) < argc)
291 {
292 if (argv[i + 1][0] != '-') mname = argv[++i];
293 }
294 }
295 else if (!strcmp(argv[i], "-asldb"))
296 {
297 work = DO_ASLDB;
298 }
299 else if (!strcmp(argv[i], "-d"))
300 {
301 if (((i + i) < argc) && (argv[i+1][0] != '-')) set_debug(DEBUG_STDERR, argv[++i]);
302 else set_debug(DEBUG_STDERR, NULL);
303 }
304 else if (!strcmp(argv[i], "-dd"))
305 {
306 dryrun = true;
307
308 if (((i + i) < argc) && (argv[i+1][0] != '-')) set_debug(DEBUG_STDERR, argv[++i]);
309 else set_debug(DEBUG_STDERR, "l7");
310 }
311 }
312
313 if (asl_store_dst != NULL && asl_store_dst->path == NULL) asl_store_dst->path = strdup(PATH_ASL_STORE);
314
315 debug_log(ASL_LEVEL_ERR, "aslmanager starting%s\n", dryrun ? " dryrun" : "");
316
317 if (cache_delete)
318 {
319 size_t curr_size = 0;
320
321 if (cache_delete_task(true, &curr_size) != 0)
322 {
323 debug_log(ASL_LEVEL_NOTICE, "cache_delete_process failed - can't determine current size\n");
324 }
325 else
326 {
327 debug_log(ASL_LEVEL_NOTICE, "cache delete current size = %lu\n", curr_size);
328
329 if (!cache_delete_query)
330 {
331 size_t new_size = curr_size - opts.all_max;
332
333 if (cache_delete_task(false, &new_size) != 0)
334 {
335 debug_log(ASL_LEVEL_NOTICE, "cache_delete_process failed - delete failed\n");
336 }
337 else
338 {
339 debug_log(ASL_LEVEL_NOTICE, "cache delete new size = %lu\n", new_size);
340 }
341 }
342 }
343
344 asl_out_module_free(mod);
345
346 debug_log(ASL_LEVEL_NOTICE, "----------------------------------------\n");
347 debug_log(ASL_LEVEL_ERR, "aslmanager finished%s\n", dryrun ? " dryrun" : "");
348 debug_close();
349
350 return 0;
351 }
352
353 if (work & DO_ASLDB) process_asl_data_store(asl_store_dst, &opts);
354
355 if (work & DO_MODULE)
356 {
357 if (work & DO_CHECKPT) checkpoint(mname);
358
359 if (mod != NULL)
360 {
361 for (m = mod; m != NULL; m = m->next)
362 {
363 if (mname == NULL)
364 {
365 process_module(m, NULL);
366 }
367 else if ((m->name != NULL) && (!strcmp(m->name, mname)))
368 {
369 process_module(m, &opts);
370 }
371 }
372 }
373 }
374
375 asl_out_module_free(mod);
376
377 debug_log(ASL_LEVEL_NOTICE, "----------------------------------------\n");
378 debug_log(ASL_LEVEL_ERR, "aslmanager finished%s\n", dryrun ? " dryrun" : "");
379 debug_close();
380
381 return 0;
382 }
383
384 /* dispatched on server_queue, dispatches to work_queue */
385 void
386 main_task(void)
387 {
388 /* if main task is already running or queued, do nothing */
389 if (main_task_enqueued) return;
390
391 main_task_enqueued = true;
392 xpc_transaction_begin();
393
394 if (initial_main_task)
395 {
396 initial_main_task = false;
397 dispatch_time_t delay = dispatch_walltime(NULL, MAIN_TASK_INITIAL_DELAY * NSEC_PER_SEC);
398
399 dispatch_after(delay, work_queue, ^{
400 cli_main(0, NULL);
401 main_task_enqueued = false;
402 xpc_transaction_end();
403 });
404 }
405 else
406 {
407 dispatch_async(work_queue, ^{
408 cli_main(0, NULL);
409 main_task_enqueued = false;
410 xpc_transaction_end();
411 });
412 }
413 }
414
415 static void
416 accept_connection(xpc_connection_t peer)
417 {
418 xpc_connection_set_event_handler(peer, ^(xpc_object_t request) {
419 if (xpc_get_type(request) == XPC_TYPE_DICTIONARY)
420 {
421 uid_t uid = xpc_connection_get_euid(peer);
422
423 /* send a reply immediately */
424 xpc_object_t reply = xpc_dictionary_create_reply(request);
425 xpc_connection_send_message(peer, reply);
426 xpc_release(reply);
427
428 /*
429 * Some day, we may use the dictionary to pass parameters
430 * to aslmanager, but for now, we ignore the input.
431 */
432
433 if (uid == geteuid())
434 {
435 main_task();
436 }
437 }
438 else if (xpc_get_type(request) == XPC_TYPE_ERROR)
439 {
440 /* disconnect */
441 }
442 });
443
444 xpc_connection_resume(peer);
445 }
446
447 int
448 main(int argc, char *argv[])
449 {
450 int64_t is_managed = 0;
451
452 vproc_swap_integer(NULL, VPROC_GSK_IS_MANAGED, NULL, &is_managed);
453
454 if (is_managed == 0) return cli_main(argc, argv);
455
456 /* Set I/O policy */
457 setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_THROTTLE);
458
459 /* XPC server */
460 server_queue = dispatch_queue_create("aslmanager", NULL);
461
462 work_queue = dispatch_queue_create("work queue", NULL);
463
464 /* Exit on SIGTERM */
465 signal(SIGTERM, SIG_IGN);
466 sig_term_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t)SIGTERM, 0, dispatch_get_main_queue());
467 dispatch_source_set_event_handler(sig_term_src, ^{
468 debug_log(ASL_LEVEL_NOTICE, "SIGTERM exit\n");
469 exit(0);
470 });
471
472 dispatch_resume(sig_term_src);
473
474 /* Handle incoming messages. */
475 listener = xpc_connection_create_mach_service("com.apple.aslmanager", server_queue, XPC_CONNECTION_MACH_SERVICE_LISTENER);
476 xpc_connection_set_event_handler(listener, ^(xpc_object_t peer) {
477 if (xpc_get_type(peer) == XPC_TYPE_CONNECTION) accept_connection(peer);
478 });
479 xpc_connection_resume(listener);
480
481 cache_delete_register();
482
483 dispatch_main();
484 }