]> git.saurik.com Git - apple/syslog.git/blob - syslogd.tproj/syslogd.c
syslog-69.0.4.tar.gz
[apple/syslog.git] / syslogd.tproj / syslogd.c
1 /*
2 * Copyright (c) 2004-2008 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 <stdio.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <signal.h>
29 #include <mach/mach.h>
30 #include <mach/mach_error.h>
31 #include <mach/mach_time.h>
32 #include <servers/bootstrap.h>
33 #include <sys/types.h>
34 #include <sys/socket.h>
35 #include <sys/fcntl.h>
36 #include <sys/queue.h>
37 #include <sys/time.h>
38 #include <pthread.h>
39 #include <dirent.h>
40 #include <dlfcn.h>
41 #include <libgen.h>
42 #include <notify.h>
43 #include "daemon.h"
44
45 #define SERVICE_NAME "com.apple.system.logger"
46 #define SERVER_STATUS_ERROR -1
47 #define SERVER_STATUS_INACTIVE 0
48 #define SERVER_STATUS_ACTIVE 1
49 #define SERVER_STATUS_ON_DEMAND 2
50
51 #define DEFAULT_MARK_SEC 0
52 #define DEFAULT_UTMP_TTL_SEC 31622400
53 #define DEFAULT_FS_TTL_SEC 31622400
54 #define DEFAULT_BSD_MAX_DUP_SEC 30
55 #define BILLION 1000000000
56
57 #define NOTIFY_DELAY 1
58
59 #define NETWORK_CHANGE_NOTIFICATION "com.apple.system.config.network_change"
60
61 #define streq(A,B) (strcmp(A,B)==0)
62 #define forever for(;;)
63
64 static int reset = 0;
65 static uint64_t time_start = 0;
66 static uint64_t mark_last = 0;
67 static uint64_t mark_time = 0;
68 static uint64_t time_last = 0;
69
70 extern int __notify_78945668_info__;
71 extern int _malloc_no_asl_log;
72
73 static TAILQ_HEAD(ml, module_list) Moduleq;
74
75 /* global */
76 struct global_s global;
77
78 /* Static Modules */
79 int asl_in_init();
80 int asl_in_reset();
81 int asl_in_close();
82 static int activate_asl_in = 1;
83
84 int asl_action_init();
85 int asl_action_reset();
86 int asl_action_close();
87 static int activate_asl_action = 1;
88
89 int klog_in_init();
90 int klog_in_reset();
91 int klog_in_close();
92 static int activate_klog_in = 1;
93
94 int bsd_in_init();
95 int bsd_in_reset();
96 int bsd_in_close();
97 static int activate_bsd_in = 1;
98
99 int bsd_out_init();
100 int bsd_out_reset();
101 int bsd_out_close();
102 static int activate_bsd_out = 1;
103
104 int remote_init();
105 int remote_reset();
106 int remote_close();
107 static int activate_remote = 0;
108
109 int udp_in_init();
110 int udp_in_reset();
111 int udp_in_close();
112 static int activate_udp_in = 1;
113
114 extern void database_server();
115 extern void db_worker();
116 extern void launchd_drain();
117 extern void bsd_flush_duplicates(time_t now);
118
119 /*
120 * Module approach: only one type of module. This module may implement
121 * the set of functions necessary for any of the functions (input, output,
122 * etc.) Prior to using the modules, dlsym() is consulted to see what it
123 * implements.
124 */
125
126 static int
127 static_modules()
128 {
129 struct module_list *tmp;
130
131 /*
132 * The order of these initializations is important.
133 * When messages are sent to output modules, they are
134 * sent in the same order as these initializations.
135 * asl_action may add modify messages, for example to
136 * add access controls, so it must come first.
137 */
138 if (activate_asl_action != 0)
139 {
140 tmp = calloc(1, sizeof(struct module_list));
141 if (tmp == NULL) return 1;
142
143 tmp->name = strdup("asl_action");
144 if (tmp->name == NULL)
145 {
146 free(tmp);
147 return 1;
148 }
149
150 tmp->module = NULL;
151 tmp->init = asl_action_init;
152 tmp->reset = asl_action_reset;
153 tmp->close = asl_action_close;
154 TAILQ_INSERT_TAIL(&Moduleq, tmp, entries);
155 }
156
157 if (activate_asl_in != 0)
158 {
159 tmp = calloc(1, sizeof(struct module_list));
160 if (tmp == NULL) return 1;
161
162 tmp->name = strdup("asl_in");
163 if (tmp->name == NULL)
164 {
165 free(tmp);
166 return 1;
167 }
168
169 tmp->module = NULL;
170 tmp->init = asl_in_init;
171 tmp->reset = asl_in_reset;
172 tmp->close = asl_in_close;
173 TAILQ_INSERT_TAIL(&Moduleq, tmp, entries);
174 }
175
176 if (activate_klog_in != 0)
177 {
178 tmp = calloc(1, sizeof(struct module_list));
179 if (tmp == NULL) return 1;
180
181 tmp->name = strdup("klog_in");
182 if (tmp->name == NULL)
183 {
184 free(tmp);
185 return 1;
186 }
187
188 tmp->module = NULL;
189 tmp->init = klog_in_init;
190 tmp->reset = klog_in_reset;
191 tmp->close = klog_in_close;
192 TAILQ_INSERT_TAIL(&Moduleq, tmp, entries);
193 }
194
195 if (activate_bsd_in != 0)
196 {
197 tmp = calloc(1, sizeof(struct module_list));
198 if (tmp == NULL) return 1;
199
200 tmp->name = strdup("bsd_in");
201 if (tmp->name == NULL)
202 {
203 free(tmp);
204 return 1;
205 }
206
207 tmp->module = NULL;
208 tmp->init = bsd_in_init;
209 tmp->reset = bsd_in_reset;
210 tmp->close = bsd_in_close;
211 TAILQ_INSERT_TAIL(&Moduleq, tmp, entries);
212 }
213
214 if (activate_bsd_out != 0)
215 {
216 tmp = calloc(1, sizeof(struct module_list));
217 if (tmp == NULL) return 1;
218
219 tmp->name = strdup("bsd_out");
220 if (tmp->name == NULL)
221 {
222 free(tmp);
223 return 1;
224 }
225
226 tmp->module = NULL;
227 tmp->init = bsd_out_init;
228 tmp->reset = bsd_out_reset;
229 tmp->close = bsd_out_close;
230 TAILQ_INSERT_TAIL(&Moduleq, tmp, entries);
231 }
232
233 if (activate_remote != 0)
234 {
235 tmp = calloc(1, sizeof(struct module_list));
236 if (tmp == NULL) return 1;
237
238 tmp->name = strdup("remote");
239 if (tmp->name == NULL)
240 {
241 free(tmp);
242 return 1;
243 }
244
245 tmp->module = NULL;
246 tmp->init = remote_init;
247 tmp->reset = remote_reset;
248 tmp->close = remote_close;
249 TAILQ_INSERT_TAIL(&Moduleq, tmp, entries);
250 }
251
252 if (activate_udp_in != 0)
253 {
254 tmp = calloc(1, sizeof(struct module_list));
255 if (tmp == NULL) return 1;
256
257 tmp->name = strdup("udp_in");
258 if (tmp->name == NULL)
259 {
260 free(tmp);
261 return 1;
262 }
263
264 tmp->module = NULL;
265 tmp->init = udp_in_init;
266 tmp->reset = udp_in_reset;
267 tmp->close = udp_in_close;
268 TAILQ_INSERT_TAIL(&Moduleq, tmp, entries);
269 }
270
271 return 0;
272 }
273
274 /*
275 * Loads all the modules. This DOES NOT call the modules initializer
276 * functions. It simply scans the modules directory looking for modules
277 * and loads them. This does not mean the module will be used.
278 */
279 static int
280 load_modules(const char *mp)
281 {
282 DIR *d;
283 struct dirent *de;
284 struct module_list *tmp;
285 void *c, *bn;
286 char *modulepath = NULL;
287
288 d = opendir(mp);
289 if (d == NULL) return -1;
290
291 while (NULL != (de = readdir(d)))
292 {
293 if (de->d_name[0] == '.') continue;
294
295 /* Must have ".so" in the name" */
296 if (!strstr(de->d_name, ".so")) continue;
297
298 asprintf(&modulepath, "%s/%s", mp, de->d_name);
299 if (!modulepath) continue;
300
301 c = dlopen(modulepath, RTLD_LOCAL);
302 if (c == NULL)
303 {
304 free(modulepath);
305 continue;
306 }
307
308 tmp = calloc(1, sizeof(struct module_list));
309 if (tmp == NULL)
310 {
311 free(modulepath);
312 dlclose(c);
313 continue;
314 }
315
316 bn = basename(modulepath);
317 tmp->name = strdup(bn);
318 if (tmp->name == NULL)
319 {
320 free(tmp);
321 return 1;
322 }
323
324 tmp->module = c;
325 TAILQ_INSERT_TAIL(&Moduleq, tmp, entries);
326
327 tmp->init = dlsym(tmp->module, "aslmod_init");
328 tmp->reset = dlsym(tmp->module, "aslmod_reset");
329 tmp->close = dlsym(tmp->module, "aslmod_close");
330
331 free(modulepath);
332 }
333
334 closedir(d);
335
336 return 0;
337 }
338
339 static void
340 writepid(void)
341 {
342 FILE *fp;
343
344 fp = fopen(_PATH_PIDFILE, "w");
345 if (fp != NULL)
346 {
347 fprintf(fp, "%d\n", getpid());
348 fclose(fp);
349 }
350 }
351
352 static void
353 closeall(void)
354 {
355 int i;
356
357 for (i = getdtablesize() - 1; i >= 0; i--) close(i);
358
359 open("/dev/null", O_RDWR, 0);
360 dup(0);
361 dup(0);
362 }
363
364 static void
365 detach(void)
366 {
367 signal(SIGINT, SIG_IGN);
368 signal(SIGPIPE, SIG_IGN);
369 setsid();
370 }
371
372 static void
373 catch_sighup(int x)
374 {
375 reset = 1;
376 }
377
378 static void
379 send_reset(void)
380 {
381 struct module_list *mod;
382
383 for (mod = Moduleq.tqh_first; mod != NULL; mod = mod->entries.tqe_next)
384 {
385 if (mod->reset != NULL) mod->reset();
386 }
387 }
388
389 /*
390 * perform timed activities and set next run-loop timeout
391 */
392 static void
393 timed_events(struct timeval **run)
394 {
395 uint64_t now, delta, t;
396 static struct timeval next;
397
398 now = time(NULL);
399
400 *run = NULL;
401 next.tv_sec = 0;
402 next.tv_usec = 0;
403
404 if (time_start == 0)
405 {
406 /* startup */
407 time_start = now;
408 time_last = now;
409 mark_last = now;
410 }
411
412 /*
413 * At startup, we try sending a notification once a second.
414 * Once it succeeds, we set the Libc global __notify_78945668_info__ to 0
415 * which lets Libc's localtime calculations use notifyd to invalidate
416 * cached timezones. This prevents a deadlock in localtime.
417 */
418 if (__notify_78945668_info__ < 0)
419 {
420 if (notify_post("com.apple.system.syslogd") == NOTIFY_STATUS_OK) __notify_78945668_info__ = 0;
421 else next.tv_sec = 1;
422 }
423
424 if (time_last > now)
425 {
426 /*
427 * Despite Albert Einstein's assurance, time has gone backward.
428 * Reset "last" times to current time.
429 */
430 time_last = now;
431 mark_last = now;
432 }
433
434 /*
435 * Tickle bsd_out module to flush duplicates.
436 */
437 if (global.bsd_flush_time > 0)
438 {
439 bsd_flush_duplicates(now);
440 if (global.bsd_flush_time > 0)
441 {
442 if (next.tv_sec == 0) next.tv_sec = global.bsd_flush_time;
443 else if (global.bsd_flush_time < next.tv_sec) next.tv_sec = global.bsd_flush_time;
444 }
445 }
446
447 /*
448 * Tickle asl_store
449 */
450 if (global.asl_store_ping_time > 0)
451 {
452 db_ping_store(now);
453 if (global.asl_store_ping_time > 0)
454 {
455 if (next.tv_sec == 0) next.tv_sec = global.asl_store_ping_time;
456 else if (global.asl_store_ping_time < next.tv_sec) next.tv_sec = global.asl_store_ping_time;
457 }
458 }
459
460 /*
461 * Send MARK
462 */
463 if (mark_time > 0)
464 {
465 delta = now - mark_last;
466 if (delta >= mark_time)
467 {
468 asl_mark();
469 mark_last = now;
470 t = mark_time;
471 }
472 else
473 {
474 t = mark_time - delta;
475 }
476
477 if (next.tv_sec == 0) next.tv_sec = t;
478 else if (t < next.tv_sec) next.tv_sec = t;
479 }
480
481 /*
482 * set output timeout parameter if runloop needs to have a timer
483 */
484 if (next.tv_sec > 0) *run = &next;
485
486 time_last = now;
487 }
488
489 void
490 init_config()
491 {
492 launch_data_t tmp, pdict;
493 kern_return_t status;
494
495 tmp = launch_data_new_string(LAUNCH_KEY_CHECKIN);
496 global.launch_dict = launch_msg(tmp);
497 launch_data_free(tmp);
498
499 if (global.launch_dict == NULL)
500 {
501 fprintf(stderr, "%d launchd checkin failed\n", getpid());
502 exit(1);
503 }
504
505 tmp = launch_data_dict_lookup(global.launch_dict, LAUNCH_JOBKEY_MACHSERVICES);
506 if (tmp == NULL)
507 {
508 fprintf(stderr, "%d launchd lookup of LAUNCH_JOBKEY_MACHSERVICES failed\n", getpid());
509 exit(1);
510 }
511
512 pdict = launch_data_dict_lookup(tmp, SERVICE_NAME);
513 if (pdict == NULL)
514 {
515 fprintf(stderr, "%d launchd lookup of SERVICE_NAME failed\n", getpid());
516 exit(1);
517 }
518
519 global.server_port = launch_data_get_machport(pdict);
520
521 status = mach_port_insert_right(mach_task_self(), global.server_port, global.server_port, MACH_MSG_TYPE_MAKE_SEND);
522 if (status != KERN_SUCCESS) fprintf(stderr, "Warning! Can't make send right for server_port: %x\n", status);
523 }
524
525 int
526 main(int argc, const char *argv[])
527 {
528 struct module_list *mod;
529 fd_set rd, wr, ex, kern;
530 int32_t fd, i, max, status, daemonize;
531 const char *mp;
532 struct timeval *runloop_timer, zto;
533 pthread_attr_t attr;
534 pthread_t t;
535 int nctoken;
536 char tstr[32];
537 time_t now;
538
539 memset(&global, 0, sizeof(struct global_s));
540
541 global.asl_log_filter = ASL_FILTER_MASK_UPTO(ASL_LEVEL_NOTICE);
542 global.db_file_max = 16384000;
543 global.db_memory_max = 8192;
544 global.db_mini_max = 256;
545 global.bsd_max_dup_time = DEFAULT_BSD_MAX_DUP_SEC;
546 global.utmp_ttl = DEFAULT_UTMP_TTL_SEC;
547 global.fs_ttl = DEFAULT_FS_TTL_SEC;
548 global.kfd = -1;
549
550 #ifdef CONFIG_MAC
551 global.dbtype = DB_TYPE_FILE;
552 global.db_file_max = 25600000;
553 global.asl_store_ping_time = 150;
554 #endif
555
556 #ifdef CONFIG_APPLETV
557 global.dbtype = DB_TYPE_FILE;
558 global.db_file_max = 10240000;
559 #endif
560
561 #ifdef CONFIG_IPHONE
562 global.dbtype = DB_TYPE_MINI;
563 activate_remote = 1;
564 activate_bsd_out = 0;
565 #endif
566
567 mp = _PATH_MODULE_LIB;
568 daemonize = 0;
569 __notify_78945668_info__ = -1;
570 zto.tv_sec = 0;
571 zto.tv_usec = 0;
572 FD_ZERO(&kern);
573
574 /* prevent malloc from calling ASL on error */
575 _malloc_no_asl_log = 1;
576
577 /* first pass sets up default configurations */
578 for (i = 1; i < argc; i++)
579 {
580 if (streq(argv[i], "-config"))
581 {
582 if (((i + 1) < argc) && (argv[i+1][0] != '-'))
583 {
584 i++;
585 if (streq(argv[i], "mac"))
586 {
587 global.dbtype = DB_TYPE_FILE;
588 global.db_file_max = 25600000;
589 }
590 else if (streq(argv[i], "appletv"))
591 {
592 global.dbtype = DB_TYPE_FILE;
593 global.db_file_max = 10240000;
594 }
595 else if (streq(argv[i], "iphone"))
596 {
597 global.dbtype = DB_TYPE_MINI;
598 activate_remote = 1;
599 }
600 }
601 }
602 }
603
604 for (i = 1; i < argc; i++)
605 {
606 if (streq(argv[i], "-d"))
607 {
608 global.debug = 1;
609 if (((i+1) < argc) && (argv[i+1][0] != '-')) global.debug_file = argv[++i];
610 memset(tstr, 0, sizeof(tstr));
611 now = time(NULL);
612 ctime_r(&now, tstr);
613 tstr[19] = '\0';
614 asldebug("%s syslogd[%d]: Start\n", tstr, getpid());
615 }
616 else if (streq(argv[i], "-db"))
617 {
618 if (((i + 1) < argc) && (argv[i+1][0] != '-'))
619 {
620 i++;
621 if (streq(argv[i], "file"))
622 {
623 global.dbtype |= DB_TYPE_FILE;
624 if (((i + 1) < argc) && (argv[i+1][0] != '-')) global.db_file_max = atol(argv[++i]);
625 }
626 else if (streq(argv[i], "memory"))
627 {
628 global.dbtype |= DB_TYPE_MEMORY;
629 if (((i + 1) < argc) && (argv[i+1][0] != '-')) global.db_memory_max = atol(argv[++i]);
630 }
631 else if (streq(argv[i], "mini"))
632 {
633 global.dbtype |= DB_TYPE_MINI;
634 if (((i + 1) < argc) && (argv[i+1][0] != '-')) global.db_mini_max = atol(argv[++i]);
635 }
636 }
637 }
638 else if (streq(argv[i], "-D"))
639 {
640 daemonize = 1;
641 }
642 else if (streq(argv[i], "-m"))
643 {
644 if ((i + 1) < argc) mark_time = 60 * atoll(argv[++i]);
645 }
646 else if (streq(argv[i], "-utmp_ttl"))
647 {
648 if ((i + 1) < argc) global.utmp_ttl = atol(argv[++i]);
649 }
650 else if (streq(argv[i], "-fs_ttl"))
651 {
652 if ((i + 1) < argc) global.fs_ttl = atol(argv[++i]);
653 }
654 else if (streq(argv[i], "-l"))
655 {
656 if ((i + 1) < argc) mp = argv[++i];
657 }
658 else if (streq(argv[i], "-c"))
659 {
660 if ((i + 1) < argc)
661 {
662 i++;
663 if ((argv[i][0] >= '0') && (argv[i][0] <= '7') && (argv[i][1] == '\0')) global.asl_log_filter = ASL_FILTER_MASK_UPTO(atoi(argv[i]));
664 }
665 }
666 else if (streq(argv[i], "-dup_delay"))
667 {
668 if ((i + 1) < argc) global.bsd_max_dup_time = atoll(argv[++i]);
669 }
670 else if (streq(argv[i], "-asl_in"))
671 {
672 if ((i + 1) < argc) activate_asl_in = atoi(argv[++i]);
673 }
674 else if (streq(argv[i], "-asl_action"))
675 {
676 if ((i + 1) < argc) activate_asl_action = atoi(argv[++i]);
677 }
678 else if (streq(argv[i], "-klog_in"))
679 {
680 if ((i + 1) < argc) activate_klog_in = atoi(argv[++i]);
681 }
682 else if (streq(argv[i], "-bsd_in"))
683 {
684 if ((i + 1) < argc) activate_bsd_in = atoi(argv[++i]);
685 }
686 else if (streq(argv[i], "-bsd_out"))
687 {
688 if ((i + 1) < argc) activate_bsd_out = atoi(argv[++i]);
689 }
690 else if (streq(argv[i], "-remote"))
691 {
692 if ((i + 1) < argc) activate_remote = atoi(argv[++i]);
693 }
694 else if (streq(argv[i], "-udp_in"))
695 {
696 if ((i + 1) < argc) activate_udp_in = atoi(argv[++i]);
697 }
698 }
699
700 if (global.dbtype == 0) global.dbtype = DB_TYPE_FILE;
701
702 TAILQ_INIT(&Moduleq);
703 static_modules();
704 load_modules(mp);
705 aslevent_init();
706
707 if (global.debug == 0)
708 {
709 if (daemonize != 0)
710 {
711 if (fork() != 0) exit(0);
712
713 detach();
714 closeall();
715 }
716
717 writepid();
718 }
719
720 init_config();
721
722 signal(SIGHUP, catch_sighup);
723
724 nctoken = -1;
725 notify_register_signal(NETWORK_CHANGE_NOTIFICATION, SIGHUP, &nctoken);
726
727 for (mod = Moduleq.tqh_first; mod != NULL; mod = mod->entries.tqe_next)
728 {
729 fd = mod->init();
730 if (fd < 0) continue;
731 }
732
733 /*
734 * Start database server thread
735 */
736 pthread_attr_init(&attr);
737 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
738 pthread_create(&t, &attr, (void *(*)(void *))database_server, NULL);
739 pthread_attr_destroy(&attr);
740
741 /*
742 * Start database worker thread
743 */
744 pthread_attr_init(&attr);
745 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
746 pthread_create(&t, &attr, (void *(*)(void *))db_worker, NULL);
747 pthread_attr_destroy(&attr);
748
749 FD_ZERO(&rd);
750 FD_ZERO(&wr);
751 FD_ZERO(&ex);
752
753 /*
754 * drain /dev/klog first
755 */
756 if (global.kfd >= 0)
757 {
758 max = global.kfd + 1;
759 while (select(max, &kern, NULL, NULL, &zto) > 0)
760 {
761 aslevent_handleevent(&kern, &wr, &ex);
762 }
763 }
764
765 /*
766 * Start launchd drain thread
767 */
768 pthread_attr_init(&attr);
769 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
770 pthread_create(&t, &attr, (void *(*)(void *))launchd_drain, NULL);
771 pthread_attr_destroy(&attr);
772
773 runloop_timer = NULL;
774 timed_events(&runloop_timer);
775
776 forever
777 {
778 max = aslevent_fdsets(&rd, &wr, &ex) + 1;
779
780 status = select(max, &rd, &wr, &ex, runloop_timer);
781 if ((global.kfd >= 0) && FD_ISSET(global.kfd, &rd))
782 {
783 /* drain /dev/klog */
784 max = global.kfd + 1;
785
786 while (select(max, &kern, NULL, NULL, &zto) > 0)
787 {
788 aslevent_handleevent(&kern, &wr, &ex);
789 }
790 }
791
792 if (reset != 0)
793 {
794 send_reset();
795 reset = 0;
796 }
797
798 if (status != 0) aslevent_handleevent(&rd, &wr, &ex);
799
800 timed_events(&runloop_timer);
801 }
802 }