]> git.saurik.com Git - apple/syslog.git/blob - aslmanager.tproj/aslmanager.c
ace82324328c03e027e6fee76c743d1e1886b44f
[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 char *path = NULL;
143 bool quiet = false;
144 bool cache_delete = false;
145 bool cache_delete_query = false;
146
147 #if !TARGET_OS_SIMULATOR
148 if (geteuid() != 0)
149 {
150 if (argc == 0) debug = DEBUG_ASL;
151 else debug = DEBUG_STDERR;
152
153 debug_log(ASL_LEVEL_ERR, "aslmanager must be run by root\n");
154 exit(1);
155 }
156 #endif
157
158 module_ttl = DEFAULT_TTL;
159
160 /* cobble up a dst_data with defaults and parameter settings */
161 memset(&store, 0, sizeof(store));
162 store.ttl[LEVEL_ALL] = DEFAULT_TTL;
163 store.all_max = DEFAULT_MAX_SIZE;
164
165 memset(&opts, 0, sizeof(opts));
166 opts.ttl[LEVEL_ALL] = DEFAULT_TTL;
167 opts.all_max = DEFAULT_MAX_SIZE;
168
169 for (i = 1; i < argc; i++)
170 {
171 if (!strcmp(argv[i], "-q"))
172 {
173 quiet = true;
174 }
175 else if (!strcmp(argv[i], "-dd"))
176 {
177 quiet = true;
178 }
179 else if (!strcmp(argv[i], "-s"))
180 {
181 if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
182 {
183 store.path = strdup(argv[++i]);
184 asl_store_dst = &store;
185 }
186 }
187 }
188
189 if (!quiet)
190 {
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 }
208
209 /* get parameters from asl.conf */
210 mod = asl_out_module_init();
211
212 if (mod != NULL)
213 {
214 for (r = mod->ruleset; (r != NULL) && (asl_store_dst == NULL); r = r->next)
215 {
216 if ((r->dst != NULL) && (r->action == ACTION_OUT_DEST) && (!strcmp(r->dst->path, PATH_ASL_STORE)))
217 asl_store_dst = r->dst;
218 }
219
220 for (r = mod->ruleset; r != NULL; r = r->next)
221 {
222 if (r->action == ACTION_SET_PARAM)
223 {
224 if (r->query == NULL) _aslmanager_set_param(asl_store_dst, r->options);
225 }
226 }
227 }
228
229 work = DO_ASLDB | DO_MODULE;
230
231 for (i = 1; i < argc; i++)
232 {
233 if (!strcmp(argv[i], "-a"))
234 {
235 if (asl_store_dst == NULL) asl_store_dst = &store;
236
237 if (((i + 1) < argc) && (argv[i + 1][0] != '-')) asl_store_dst->rotate_dir = strdup(argv[++i]);
238 else asl_store_dst->rotate_dir = strdup(PATH_ASL_ARCHIVE);
239 asl_store_dst->mode = 0400;
240 }
241 else if (!strcmp(argv[i], "-store_ttl"))
242 {
243 if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
244 {
245 if (asl_store_dst == NULL) asl_store_dst = &store;
246 asl_store_dst->ttl[LEVEL_ALL] = asl_core_str_to_time(argv[++i], SECONDS_PER_DAY);
247 }
248 }
249 else if (!strcmp(argv[i], "-module_ttl"))
250 {
251 if (((i + 1) < argc) && (argv[i + 1][0] != '-')) module_ttl = asl_core_str_to_time(argv[++i], SECONDS_PER_DAY);
252 }
253 else if (!strcmp(argv[i], "-ttl"))
254 {
255 if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
256 {
257 opts.ttl[LEVEL_ALL] = asl_core_str_to_time(argv[++i], SECONDS_PER_DAY);
258
259 if (asl_store_dst == NULL) asl_store_dst = &store;
260 asl_store_dst->ttl[LEVEL_ALL] = opts.ttl[LEVEL_ALL];
261
262 module_ttl = opts.ttl[LEVEL_ALL];
263 }
264 }
265 else if (!strcmp(argv[i], "-size"))
266 {
267 if (((i + 1) < argc) && (argv[i + 1][0] != '-'))
268 {
269 opts.all_max = asl_core_str_to_size(argv[++i]);
270
271 if (asl_store_dst == NULL) asl_store_dst = &store;
272 asl_store_dst->all_max = opts.all_max;
273 }
274 }
275 else if (!strcmp(argv[i], "-checkpoint"))
276 {
277 work |= DO_CHECKPT;
278 }
279 else if (!strcmp(argv[i], "-cache_delete"))
280 {
281 cache_delete = true;
282 if (((i + 1) < argc) && (argv[i + 1][0] == 'q')) cache_delete_query = true;
283 }
284 else if (!strcmp(argv[i], "-module"))
285 {
286 work &= ~DO_ASLDB;
287
288 /* optional name follows -module */
289 if ((i +1) < argc)
290 {
291 if (argv[i + 1][0] != '-') mname = argv[++i];
292 }
293 }
294 else if (!strcmp(argv[i], "-asldb"))
295 {
296 work = DO_ASLDB;
297 }
298 else if (!strcmp(argv[i], "-d"))
299 {
300 if (((i + i) < argc) && (argv[i+1][0] != '-')) set_debug(DEBUG_STDERR, argv[++i]);
301 else set_debug(DEBUG_STDERR, NULL);
302 }
303 else if (!strcmp(argv[i], "-dd"))
304 {
305 dryrun = true;
306
307 if (((i + i) < argc) && (argv[i+1][0] != '-')) set_debug(DEBUG_STDERR, argv[++i]);
308 else set_debug(DEBUG_STDERR, "l7");
309 }
310 }
311
312 if (asl_store_dst->path == NULL) asl_store_dst->path = strdup(PATH_ASL_STORE);
313
314 debug_log(ASL_LEVEL_ERR, "aslmanager starting%s\n", dryrun ? " dryrun" : "");
315
316 if (cache_delete)
317 {
318 size_t curr_size = 0;
319
320 if (cache_delete_task(true, &curr_size) != 0)
321 {
322 debug_log(ASL_LEVEL_NOTICE, "cache_delete_process failed - can't determine current size\n");
323 }
324 else
325 {
326 debug_log(ASL_LEVEL_NOTICE, "cache delete current size = %lu\n", curr_size);
327
328 if (!cache_delete_query)
329 {
330 size_t new_size = curr_size - opts.all_max;
331
332 if (cache_delete_task(false, &new_size) != 0)
333 {
334 debug_log(ASL_LEVEL_NOTICE, "cache_delete_process failed - delete failed\n");
335 }
336 else
337 {
338 debug_log(ASL_LEVEL_NOTICE, "cache delete new size = %lu\n", new_size);
339 }
340 }
341 }
342
343 asl_out_module_free(mod);
344
345 debug_log(ASL_LEVEL_NOTICE, "----------------------------------------\n");
346 debug_log(ASL_LEVEL_ERR, "aslmanager finished%s\n", dryrun ? " dryrun" : "");
347 debug_close();
348
349 return 0;
350 }
351
352 if (work & DO_ASLDB) process_asl_data_store(asl_store_dst, &opts);
353
354 if (work & DO_MODULE)
355 {
356 if (work & DO_CHECKPT) checkpoint(mname);
357
358 if (mod != NULL)
359 {
360 for (m = mod; m != NULL; m = m->next)
361 {
362 if (mname == NULL)
363 {
364 process_module(m, NULL);
365 }
366 else if ((m->name != NULL) && (!strcmp(m->name, mname)))
367 {
368 process_module(m, &opts);
369 }
370 }
371 }
372 }
373
374 asl_out_module_free(mod);
375
376 debug_log(ASL_LEVEL_NOTICE, "----------------------------------------\n");
377 debug_log(ASL_LEVEL_ERR, "aslmanager finished%s\n", dryrun ? " dryrun" : "");
378 debug_close();
379
380 return 0;
381 }
382
383 /* dispatched on server_queue, dispatches to work_queue */
384 void
385 main_task(void)
386 {
387 /* if main task is already running or queued, do nothing */
388 if (main_task_enqueued) return;
389
390 main_task_enqueued = true;
391 xpc_transaction_begin();
392
393 if (initial_main_task)
394 {
395 initial_main_task = false;
396 dispatch_time_t delay = dispatch_walltime(NULL, MAIN_TASK_INITIAL_DELAY * NSEC_PER_SEC);
397
398 dispatch_after(delay, work_queue, ^{
399 cli_main(0, NULL);
400 main_task_enqueued = false;
401 xpc_transaction_end();
402 });
403 }
404 else
405 {
406 dispatch_async(work_queue, ^{
407 cli_main(0, NULL);
408 main_task_enqueued = false;
409 xpc_transaction_end();
410 });
411 }
412 }
413
414 static void
415 accept_connection(xpc_connection_t peer)
416 {
417 xpc_connection_set_event_handler(peer, ^(xpc_object_t request) {
418 if (xpc_get_type(request) == XPC_TYPE_DICTIONARY)
419 {
420 uid_t uid = xpc_connection_get_euid(peer);
421
422 /* send a reply immediately */
423 xpc_object_t reply = xpc_dictionary_create_reply(request);
424 xpc_connection_send_message(peer, reply);
425 xpc_release(reply);
426
427 /*
428 * Some day, we may use the dictionary to pass parameters
429 * to aslmanager, but for now, we ignore the input.
430 */
431
432 if (uid == geteuid())
433 {
434 main_task();
435 }
436 }
437 else if (xpc_get_type(request) == XPC_TYPE_ERROR)
438 {
439 /* disconnect */
440 }
441 });
442
443 xpc_connection_resume(peer);
444 }
445
446 int
447 main(int argc, char *argv[])
448 {
449 int64_t is_managed = 0;
450
451 vproc_swap_integer(NULL, VPROC_GSK_IS_MANAGED, NULL, &is_managed);
452
453 if (is_managed == 0) return cli_main(argc, argv);
454
455 /* Set I/O policy */
456 setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_PROCESS, IOPOL_THROTTLE);
457
458 /* XPC server */
459 server_queue = dispatch_queue_create("aslmanager", NULL);
460
461 work_queue = dispatch_queue_create("work queue", NULL);
462
463 /* Exit on SIGTERM */
464 signal(SIGTERM, SIG_IGN);
465 sig_term_src = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, (uintptr_t)SIGTERM, 0, dispatch_get_main_queue());
466 dispatch_source_set_event_handler(sig_term_src, ^{
467 debug_log(ASL_LEVEL_NOTICE, "SIGTERM exit\n");
468 exit(0);
469 });
470
471 dispatch_resume(sig_term_src);
472
473 /* Handle incoming messages. */
474 listener = xpc_connection_create_mach_service("com.apple.aslmanager", server_queue, XPC_CONNECTION_MACH_SERVICE_LISTENER);
475 xpc_connection_set_event_handler(listener, ^(xpc_object_t peer) {
476 if (xpc_get_type(peer) == XPC_TYPE_CONNECTION) accept_connection(peer);
477 });
478 xpc_connection_resume(listener);
479
480 cache_delete_register();
481
482 dispatch_main();
483 }