2 * Copyright (c) 2004-2008 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
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>
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
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
57 #define NOTIFY_DELAY 1
59 #define NETWORK_CHANGE_NOTIFICATION "com.apple.system.config.network_change"
61 #define streq(A,B) (strcmp(A,B)==0)
62 #define forever for(;;)
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;
70 extern int __notify_78945668_info__
;
71 extern int _malloc_no_asl_log
;
73 static TAILQ_HEAD(ml
, module_list
) Moduleq
;
76 struct global_s global
;
82 static int activate_asl_in
= 1;
84 int asl_action_init();
85 int asl_action_reset();
86 int asl_action_close();
87 static int activate_asl_action
= 1;
92 static int activate_klog_in
= 1;
97 static int activate_bsd_in
= 1;
102 static int activate_bsd_out
= 1;
107 static int activate_remote
= 0;
112 static int activate_udp_in
= 1;
114 extern void database_server();
115 extern void db_worker();
116 extern void launchd_drain();
117 extern void bsd_flush_duplicates(time_t now
);
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
129 struct module_list
*tmp
;
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.
138 if (activate_asl_action
!= 0)
140 tmp
= calloc(1, sizeof(struct module_list
));
141 if (tmp
== NULL
) return 1;
143 tmp
->name
= strdup("asl_action");
144 if (tmp
->name
== 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
);
157 if (activate_asl_in
!= 0)
159 tmp
= calloc(1, sizeof(struct module_list
));
160 if (tmp
== NULL
) return 1;
162 tmp
->name
= strdup("asl_in");
163 if (tmp
->name
== 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
);
176 if (activate_klog_in
!= 0)
178 tmp
= calloc(1, sizeof(struct module_list
));
179 if (tmp
== NULL
) return 1;
181 tmp
->name
= strdup("klog_in");
182 if (tmp
->name
== 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
);
195 if (activate_bsd_in
!= 0)
197 tmp
= calloc(1, sizeof(struct module_list
));
198 if (tmp
== NULL
) return 1;
200 tmp
->name
= strdup("bsd_in");
201 if (tmp
->name
== 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
);
214 if (activate_bsd_out
!= 0)
216 tmp
= calloc(1, sizeof(struct module_list
));
217 if (tmp
== NULL
) return 1;
219 tmp
->name
= strdup("bsd_out");
220 if (tmp
->name
== 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
);
233 if (activate_remote
!= 0)
235 tmp
= calloc(1, sizeof(struct module_list
));
236 if (tmp
== NULL
) return 1;
238 tmp
->name
= strdup("remote");
239 if (tmp
->name
== NULL
)
246 tmp
->init
= remote_init
;
247 tmp
->reset
= remote_reset
;
248 tmp
->close
= remote_close
;
249 TAILQ_INSERT_TAIL(&Moduleq
, tmp
, entries
);
252 if (activate_udp_in
!= 0)
254 tmp
= calloc(1, sizeof(struct module_list
));
255 if (tmp
== NULL
) return 1;
257 tmp
->name
= strdup("udp_in");
258 if (tmp
->name
== 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
);
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.
280 load_modules(const char *mp
)
284 struct module_list
*tmp
;
286 char *modulepath
= NULL
;
289 if (d
== NULL
) return -1;
291 while (NULL
!= (de
= readdir(d
)))
293 if (de
->d_name
[0] == '.') continue;
295 /* Must have ".so" in the name" */
296 if (!strstr(de
->d_name
, ".so")) continue;
298 asprintf(&modulepath
, "%s/%s", mp
, de
->d_name
);
299 if (!modulepath
) continue;
301 c
= dlopen(modulepath
, RTLD_LOCAL
);
308 tmp
= calloc(1, sizeof(struct module_list
));
316 bn
= basename(modulepath
);
317 tmp
->name
= strdup(bn
);
318 if (tmp
->name
== NULL
)
325 TAILQ_INSERT_TAIL(&Moduleq
, tmp
, entries
);
327 tmp
->init
= dlsym(tmp
->module, "aslmod_init");
328 tmp
->reset
= dlsym(tmp
->module, "aslmod_reset");
329 tmp
->close
= dlsym(tmp
->module, "aslmod_close");
344 fp
= fopen(_PATH_PIDFILE
, "w");
347 fprintf(fp
, "%d\n", getpid());
357 for (i
= getdtablesize() - 1; i
>= 0; i
--) close(i
);
359 open("/dev/null", O_RDWR
, 0);
367 signal(SIGINT
, SIG_IGN
);
368 signal(SIGPIPE
, SIG_IGN
);
381 struct module_list
*mod
;
383 for (mod
= Moduleq
.tqh_first
; mod
!= NULL
; mod
= mod
->entries
.tqe_next
)
385 if (mod
->reset
!= NULL
) mod
->reset();
390 * perform timed activities and set next run-loop timeout
393 timed_events(struct timeval
**run
)
395 uint64_t now
, delta
, t
;
396 static struct timeval next
;
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.
418 if (__notify_78945668_info__
< 0)
420 if (notify_post("com.apple.system.syslogd") == NOTIFY_STATUS_OK
) __notify_78945668_info__
= 0;
421 else next
.tv_sec
= 1;
427 * Despite Albert Einstein's assurance, time has gone backward.
428 * Reset "last" times to current time.
435 * Tickle bsd_out module to flush duplicates.
437 if (global
.bsd_flush_time
> 0)
439 bsd_flush_duplicates(now
);
440 if (global
.bsd_flush_time
> 0)
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
;
450 if (global
.asl_store_ping_time
> 0)
453 if (global
.asl_store_ping_time
> 0)
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
;
465 delta
= now
- mark_last
;
466 if (delta
>= mark_time
)
474 t
= mark_time
- delta
;
477 if (next
.tv_sec
== 0) next
.tv_sec
= t
;
478 else if (t
< next
.tv_sec
) next
.tv_sec
= t
;
482 * set output timeout parameter if runloop needs to have a timer
484 if (next
.tv_sec
> 0) *run
= &next
;
492 launch_data_t tmp
, pdict
;
493 kern_return_t status
;
495 tmp
= launch_data_new_string(LAUNCH_KEY_CHECKIN
);
496 global
.launch_dict
= launch_msg(tmp
);
497 launch_data_free(tmp
);
499 if (global
.launch_dict
== NULL
)
501 fprintf(stderr
, "%d launchd checkin failed\n", getpid());
505 tmp
= launch_data_dict_lookup(global
.launch_dict
, LAUNCH_JOBKEY_MACHSERVICES
);
508 fprintf(stderr
, "%d launchd lookup of LAUNCH_JOBKEY_MACHSERVICES failed\n", getpid());
512 pdict
= launch_data_dict_lookup(tmp
, SERVICE_NAME
);
515 fprintf(stderr
, "%d launchd lookup of SERVICE_NAME failed\n", getpid());
519 global
.server_port
= launch_data_get_machport(pdict
);
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
);
526 main(int argc
, const char *argv
[])
528 struct module_list
*mod
;
529 fd_set rd
, wr
, ex
, kern
;
530 int32_t fd
, i
, max
, status
, daemonize
;
532 struct timeval
*runloop_timer
, zto
;
539 memset(&global
, 0, sizeof(struct global_s
));
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
;
551 global
.dbtype
= DB_TYPE_FILE
;
552 global
.db_file_max
= 25600000;
553 global
.asl_store_ping_time
= 150;
556 #ifdef CONFIG_APPLETV
557 global
.dbtype
= DB_TYPE_FILE
;
558 global
.db_file_max
= 10240000;
562 global
.dbtype
= DB_TYPE_MINI
;
564 activate_bsd_out
= 0;
567 mp
= _PATH_MODULE_LIB
;
569 __notify_78945668_info__
= -1;
574 /* prevent malloc from calling ASL on error */
575 _malloc_no_asl_log
= 1;
577 /* first pass sets up default configurations */
578 for (i
= 1; i
< argc
; i
++)
580 if (streq(argv
[i
], "-config"))
582 if (((i
+ 1) < argc
) && (argv
[i
+1][0] != '-'))
585 if (streq(argv
[i
], "mac"))
587 global
.dbtype
= DB_TYPE_FILE
;
588 global
.db_file_max
= 25600000;
590 else if (streq(argv
[i
], "appletv"))
592 global
.dbtype
= DB_TYPE_FILE
;
593 global
.db_file_max
= 10240000;
595 else if (streq(argv
[i
], "iphone"))
597 global
.dbtype
= DB_TYPE_MINI
;
604 for (i
= 1; i
< argc
; i
++)
606 if (streq(argv
[i
], "-d"))
609 if (((i
+1) < argc
) && (argv
[i
+1][0] != '-')) global
.debug_file
= argv
[++i
];
610 memset(tstr
, 0, sizeof(tstr
));
614 asldebug("%s syslogd[%d]: Start\n", tstr
, getpid());
616 else if (streq(argv
[i
], "-db"))
618 if (((i
+ 1) < argc
) && (argv
[i
+1][0] != '-'))
621 if (streq(argv
[i
], "file"))
623 global
.dbtype
|= DB_TYPE_FILE
;
624 if (((i
+ 1) < argc
) && (argv
[i
+1][0] != '-')) global
.db_file_max
= atol(argv
[++i
]);
626 else if (streq(argv
[i
], "memory"))
628 global
.dbtype
|= DB_TYPE_MEMORY
;
629 if (((i
+ 1) < argc
) && (argv
[i
+1][0] != '-')) global
.db_memory_max
= atol(argv
[++i
]);
631 else if (streq(argv
[i
], "mini"))
633 global
.dbtype
|= DB_TYPE_MINI
;
634 if (((i
+ 1) < argc
) && (argv
[i
+1][0] != '-')) global
.db_mini_max
= atol(argv
[++i
]);
638 else if (streq(argv
[i
], "-D"))
642 else if (streq(argv
[i
], "-m"))
644 if ((i
+ 1) < argc
) mark_time
= 60 * atoll(argv
[++i
]);
646 else if (streq(argv
[i
], "-utmp_ttl"))
648 if ((i
+ 1) < argc
) global
.utmp_ttl
= atol(argv
[++i
]);
650 else if (streq(argv
[i
], "-fs_ttl"))
652 if ((i
+ 1) < argc
) global
.fs_ttl
= atol(argv
[++i
]);
654 else if (streq(argv
[i
], "-l"))
656 if ((i
+ 1) < argc
) mp
= argv
[++i
];
658 else if (streq(argv
[i
], "-c"))
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
]));
666 else if (streq(argv
[i
], "-dup_delay"))
668 if ((i
+ 1) < argc
) global
.bsd_max_dup_time
= atoll(argv
[++i
]);
670 else if (streq(argv
[i
], "-asl_in"))
672 if ((i
+ 1) < argc
) activate_asl_in
= atoi(argv
[++i
]);
674 else if (streq(argv
[i
], "-asl_action"))
676 if ((i
+ 1) < argc
) activate_asl_action
= atoi(argv
[++i
]);
678 else if (streq(argv
[i
], "-klog_in"))
680 if ((i
+ 1) < argc
) activate_klog_in
= atoi(argv
[++i
]);
682 else if (streq(argv
[i
], "-bsd_in"))
684 if ((i
+ 1) < argc
) activate_bsd_in
= atoi(argv
[++i
]);
686 else if (streq(argv
[i
], "-bsd_out"))
688 if ((i
+ 1) < argc
) activate_bsd_out
= atoi(argv
[++i
]);
690 else if (streq(argv
[i
], "-remote"))
692 if ((i
+ 1) < argc
) activate_remote
= atoi(argv
[++i
]);
694 else if (streq(argv
[i
], "-udp_in"))
696 if ((i
+ 1) < argc
) activate_udp_in
= atoi(argv
[++i
]);
700 if (global
.dbtype
== 0) global
.dbtype
= DB_TYPE_FILE
;
702 TAILQ_INIT(&Moduleq
);
707 if (global
.debug
== 0)
711 if (fork() != 0) exit(0);
722 signal(SIGHUP
, catch_sighup
);
725 notify_register_signal(NETWORK_CHANGE_NOTIFICATION
, SIGHUP
, &nctoken
);
727 for (mod
= Moduleq
.tqh_first
; mod
!= NULL
; mod
= mod
->entries
.tqe_next
)
730 if (fd
< 0) continue;
734 * Start database server thread
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
);
742 * Start database worker thread
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
);
754 * drain /dev/klog first
758 max
= global
.kfd
+ 1;
759 while (select(max
, &kern
, NULL
, NULL
, &zto
) > 0)
761 aslevent_handleevent(&kern
, &wr
, &ex
);
766 * Start launchd drain thread
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
);
773 runloop_timer
= NULL
;
774 timed_events(&runloop_timer
);
778 max
= aslevent_fdsets(&rd
, &wr
, &ex
) + 1;
780 status
= select(max
, &rd
, &wr
, &ex
, runloop_timer
);
781 if ((global
.kfd
>= 0) && FD_ISSET(global
.kfd
, &rd
))
783 /* drain /dev/klog */
784 max
= global
.kfd
+ 1;
786 while (select(max
, &kern
, NULL
, NULL
, &zto
) > 0)
788 aslevent_handleevent(&kern
, &wr
, &ex
);
798 if (status
!= 0) aslevent_handleevent(&rd
, &wr
, &ex
);
800 timed_events(&runloop_timer
);