2 * Copyright (c) 2004-2009 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 DEFAULT_MPS_LIMIT 500
56 #define BILLION 1000000000
58 #define NOTIFY_DELAY 1
60 #define NETWORK_CHANGE_NOTIFICATION "com.apple.system.config.network_change"
62 #define streq(A,B) (strcmp(A,B)==0)
63 #define forever for(;;)
65 static uint64_t time_start
= 0;
66 static uint64_t mark_last
= 0;
67 static uint64_t ping_last
= 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 output_worker();
116 extern void launchd_drain();
117 extern void bsd_flush_duplicates(time_t now
);
118 extern void bsd_close_idle_files(time_t now
);
121 * Module approach: only one type of module. This module may implement
122 * the set of functions necessary for any of the functions (input, output,
123 * etc.) Prior to using the modules, dlsym() is consulted to see what it
130 struct module_list
*tmp
;
133 * The order of these initializations is important.
134 * When messages are sent to output modules, they are
135 * sent in the same order as these initializations.
136 * asl_action may add modify messages, for example to
137 * add access controls, so it must come first.
139 if (activate_asl_action
!= 0)
141 tmp
= calloc(1, sizeof(struct module_list
));
142 if (tmp
== NULL
) return 1;
144 tmp
->name
= strdup("asl_action");
145 if (tmp
->name
== NULL
)
152 tmp
->init
= asl_action_init
;
153 tmp
->reset
= asl_action_reset
;
154 tmp
->close
= asl_action_close
;
155 TAILQ_INSERT_TAIL(&Moduleq
, tmp
, entries
);
158 if (activate_asl_in
!= 0)
160 tmp
= calloc(1, sizeof(struct module_list
));
161 if (tmp
== NULL
) return 1;
163 tmp
->name
= strdup("asl_in");
164 if (tmp
->name
== NULL
)
171 tmp
->init
= asl_in_init
;
172 tmp
->reset
= asl_in_reset
;
173 tmp
->close
= asl_in_close
;
174 TAILQ_INSERT_TAIL(&Moduleq
, tmp
, entries
);
177 if (activate_klog_in
!= 0)
179 tmp
= calloc(1, sizeof(struct module_list
));
180 if (tmp
== NULL
) return 1;
182 tmp
->name
= strdup("klog_in");
183 if (tmp
->name
== NULL
)
190 tmp
->init
= klog_in_init
;
191 tmp
->reset
= klog_in_reset
;
192 tmp
->close
= klog_in_close
;
193 TAILQ_INSERT_TAIL(&Moduleq
, tmp
, entries
);
196 if (activate_bsd_in
!= 0)
198 tmp
= calloc(1, sizeof(struct module_list
));
199 if (tmp
== NULL
) return 1;
201 tmp
->name
= strdup("bsd_in");
202 if (tmp
->name
== NULL
)
209 tmp
->init
= bsd_in_init
;
210 tmp
->reset
= bsd_in_reset
;
211 tmp
->close
= bsd_in_close
;
212 TAILQ_INSERT_TAIL(&Moduleq
, tmp
, entries
);
215 if (activate_bsd_out
!= 0)
217 tmp
= calloc(1, sizeof(struct module_list
));
218 if (tmp
== NULL
) return 1;
220 tmp
->name
= strdup("bsd_out");
221 if (tmp
->name
== NULL
)
228 tmp
->init
= bsd_out_init
;
229 tmp
->reset
= bsd_out_reset
;
230 tmp
->close
= bsd_out_close
;
231 TAILQ_INSERT_TAIL(&Moduleq
, tmp
, entries
);
234 if (activate_remote
!= 0)
236 tmp
= calloc(1, sizeof(struct module_list
));
237 if (tmp
== NULL
) return 1;
239 tmp
->name
= strdup("remote");
240 if (tmp
->name
== NULL
)
247 tmp
->init
= remote_init
;
248 tmp
->reset
= remote_reset
;
249 tmp
->close
= remote_close
;
250 TAILQ_INSERT_TAIL(&Moduleq
, tmp
, entries
);
253 if (activate_udp_in
!= 0)
255 tmp
= calloc(1, sizeof(struct module_list
));
256 if (tmp
== NULL
) return 1;
258 tmp
->name
= strdup("udp_in");
259 if (tmp
->name
== NULL
)
266 tmp
->init
= udp_in_init
;
267 tmp
->reset
= udp_in_reset
;
268 tmp
->close
= udp_in_close
;
269 TAILQ_INSERT_TAIL(&Moduleq
, tmp
, entries
);
276 * Loads all the modules. This DOES NOT call the modules initializer
277 * functions. It simply scans the modules directory looking for modules
278 * and loads them. This does not mean the module will be used.
281 load_modules(const char *mp
)
285 struct module_list
*tmp
;
287 char *modulepath
= NULL
;
290 if (d
== NULL
) return -1;
292 while (NULL
!= (de
= readdir(d
)))
294 if (de
->d_name
[0] == '.') continue;
296 /* Must have ".so" in the name" */
297 if (!strstr(de
->d_name
, ".so")) continue;
299 asprintf(&modulepath
, "%s/%s", mp
, de
->d_name
);
300 if (!modulepath
) continue;
302 c
= dlopen(modulepath
, RTLD_LOCAL
);
309 tmp
= calloc(1, sizeof(struct module_list
));
317 bn
= basename(modulepath
);
318 tmp
->name
= strdup(bn
);
319 if (tmp
->name
== NULL
)
326 TAILQ_INSERT_TAIL(&Moduleq
, tmp
, entries
);
328 tmp
->init
= dlsym(tmp
->module, "aslmod_init");
329 tmp
->reset
= dlsym(tmp
->module, "aslmod_reset");
330 tmp
->close
= dlsym(tmp
->module, "aslmod_close");
345 fp
= fopen(_PATH_PIDFILE
, "w");
348 fprintf(fp
, "%d\n", getpid());
358 for (i
= getdtablesize() - 1; i
>= 0; i
--) close(i
);
360 open("/dev/null", O_RDWR
, 0);
368 signal(SIGINT
, SIG_IGN
);
369 signal(SIGPIPE
, SIG_IGN
);
376 global
.reset
= RESET_CONFIG
;
382 global
.reset
= RESET_NETWORK
;
388 struct module_list
*mod
;
390 for (mod
= Moduleq
.tqh_first
; mod
!= NULL
; mod
= mod
->entries
.tqe_next
)
392 if (mod
->reset
!= NULL
) mod
->reset();
397 * perform timed activities and set next run-loop timeout
400 timed_events(struct timeval
**run
)
402 time_t now
, delta
, t
;
403 static struct timeval next
;
421 * At startup, we try sending a notification once a second.
422 * Once it succeeds, we set the Libc global __notify_78945668_info__ to 0
423 * which lets Libc's localtime calculations use notifyd to invalidate
424 * cached timezones. This prevents a deadlock in localtime.
426 if (__notify_78945668_info__
< 0)
428 if (notify_post("com.apple.system.syslogd") == NOTIFY_STATUS_OK
) __notify_78945668_info__
= 0;
429 else next
.tv_sec
= 1;
435 * Despite Albert Einstein's assurance, time has gone backward.
436 * Reset "last" times to current time.
444 * Tickle bsd_out module to flush duplicates.
446 if (global
.bsd_flush_time
> 0)
448 bsd_flush_duplicates(now
);
449 bsd_close_idle_files(now
);
450 if (global
.bsd_flush_time
> 0)
452 if (next
.tv_sec
== 0) next
.tv_sec
= global
.bsd_flush_time
;
453 else if (global
.bsd_flush_time
< next
.tv_sec
) next
.tv_sec
= global
.bsd_flush_time
;
458 * Tickle asl_store to sweep file cache
460 if (global
.asl_store_ping_time
> 0)
462 delta
= now
- ping_last
;
463 if (delta
>= global
.asl_store_ping_time
)
466 bsd_close_idle_files(now
);
468 t
= global
.asl_store_ping_time
;
472 t
= global
.asl_store_ping_time
- delta
;
475 if (next
.tv_sec
== 0) next
.tv_sec
= t
;
476 else if (t
< next
.tv_sec
) next
.tv_sec
= t
;
482 if (global
.mark_time
> 0)
484 delta
= now
- mark_last
;
485 if (delta
>= global
.mark_time
)
489 t
= global
.mark_time
;
493 t
= global
.mark_time
- delta
;
496 if (next
.tv_sec
== 0) next
.tv_sec
= t
;
497 else if (t
< next
.tv_sec
) next
.tv_sec
= t
;
501 * set output timeout parameter if runloop needs to have a timer
503 if (next
.tv_sec
> 0) *run
= &next
;
511 launch_data_t tmp
, pdict
;
512 kern_return_t status
;
514 tmp
= launch_data_new_string(LAUNCH_KEY_CHECKIN
);
515 global
.launch_dict
= launch_msg(tmp
);
516 launch_data_free(tmp
);
518 if (global
.launch_dict
== NULL
)
520 fprintf(stderr
, "%d launchd checkin failed\n", getpid());
524 tmp
= launch_data_dict_lookup(global
.launch_dict
, LAUNCH_JOBKEY_MACHSERVICES
);
527 fprintf(stderr
, "%d launchd lookup of LAUNCH_JOBKEY_MACHSERVICES failed\n", getpid());
531 pdict
= launch_data_dict_lookup(tmp
, SERVICE_NAME
);
534 fprintf(stderr
, "%d launchd lookup of SERVICE_NAME failed\n", getpid());
538 global
.server_port
= launch_data_get_machport(pdict
);
540 /* port for receiving internal messages */
541 status
= mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE
, &(global
.self_port
));
542 if (status
!= KERN_SUCCESS
)
544 fprintf(stderr
, "mach_port_allocate self_port failed: %d", status
);
548 status
= mach_port_insert_right(mach_task_self(), global
.self_port
, global
.self_port
, MACH_MSG_TYPE_MAKE_SEND
);
549 if (status
!= KERN_SUCCESS
)
551 fprintf(stderr
, "Can't make send right for self_port: %d\n", status
);
555 /* port for receiving MACH_NOTIFY_DEAD_NAME notifications */
556 status
= mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE
, &(global
.dead_session_port
));
557 if (status
!= KERN_SUCCESS
)
559 fprintf(stderr
, "mach_port_allocate dead_session_port failed: %d", status
);
563 status
= mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET
, &(global
.listen_set
));
564 if (status
!= KERN_SUCCESS
)
566 fprintf(stderr
, "mach_port_allocate listen_set failed: %d", status
);
570 status
= mach_port_move_member(mach_task_self(), global
.server_port
, global
.listen_set
);
571 if (status
!= KERN_SUCCESS
)
573 fprintf(stderr
, "mach_port_move_member server_port failed: %d", status
);
577 status
= mach_port_move_member(mach_task_self(), global
.self_port
, global
.listen_set
);
578 if (status
!= KERN_SUCCESS
)
580 fprintf(stderr
, "mach_port_move_member self_port failed: %d", status
);
584 status
= mach_port_move_member(mach_task_self(), global
.dead_session_port
, global
.listen_set
);
585 if (status
!= KERN_SUCCESS
)
587 fprintf(stderr
, "mach_port_move_member dead_session_port failed (%u)", status
);
593 config_debug(int enable
, const char *path
)
595 OSSpinLockLock(&global
.lock
);
597 global
.debug
= enable
;
598 if (global
.debug_file
!= NULL
) free(global
.debug_file
);
599 global
.debug_file
= strdup(path
);
601 OSSpinLockUnlock(&global
.lock
);
605 config_data_store(int type
, uint32_t file_max
, uint32_t memory_max
, uint32_t mini_max
)
607 pthread_mutex_lock(global
.db_lock
);
609 if (global
.dbtype
& DB_TYPE_FILE
)
611 asl_store_close(global
.file_db
);
612 global
.file_db
= NULL
;
615 if (global
.dbtype
& DB_TYPE_MEMORY
)
617 asl_memory_close(global
.memory_db
);
618 global
.memory_db
= NULL
;
621 if (global
.dbtype
& DB_TYPE_MINI
)
623 asl_mini_memory_close(global
.mini_db
);
624 global
.mini_db
= NULL
;
627 global
.dbtype
= type
;
628 global
.db_file_max
= file_max
;
629 global
.db_memory_max
= memory_max
;
630 global
.db_mini_max
= mini_max
;
632 pthread_mutex_unlock(global
.db_lock
);
636 main(int argc
, const char *argv
[])
638 struct module_list
*mod
;
639 fd_set rd
, wr
, ex
, kern
;
640 int32_t fd
, i
, max
, status
, daemonize
;
642 struct timeval
*runloop_timer
, zto
;
645 int network_change_token
;
649 memset(&global
, 0, sizeof(struct global_s
));
651 global
.db_lock
= (pthread_mutex_t
*)calloc(1, sizeof(pthread_mutex_t
));
652 pthread_mutex_init(global
.db_lock
, NULL
);
654 global
.work_queue_lock
= (pthread_mutex_t
*)calloc(1, sizeof(pthread_mutex_t
));
655 pthread_mutex_init(global
.work_queue_lock
, NULL
);
657 pthread_cond_init(&global
.work_queue_cond
, NULL
);
659 global
.work_queue
= (asl_search_result_t
*)calloc(1, sizeof(asl_search_result_t
));
661 global
.asl_log_filter
= ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG
);
662 global
.db_file_max
= 16384000;
663 global
.db_memory_max
= 8192;
664 global
.db_mini_max
= 256;
665 global
.bsd_max_dup_time
= DEFAULT_BSD_MAX_DUP_SEC
;
666 global
.utmp_ttl
= DEFAULT_UTMP_TTL_SEC
;
667 global
.fs_ttl
= DEFAULT_FS_TTL_SEC
;
668 global
.mps_limit
= DEFAULT_MPS_LIMIT
;
672 global
.dbtype
= DB_TYPE_FILE
;
673 global
.db_file_max
= 25600000;
674 global
.asl_store_ping_time
= 150;
677 #ifdef CONFIG_APPLETV
678 global
.dbtype
= DB_TYPE_FILE
;
679 global
.db_file_max
= 10240000;
680 global
.asl_store_ping_time
= 150;
684 global
.dbtype
= DB_TYPE_MINI
;
686 activate_bsd_out
= 0;
689 mp
= _PATH_MODULE_LIB
;
691 __notify_78945668_info__
= 0xf0000000;
695 /* prevent malloc from calling ASL on error */
696 _malloc_no_asl_log
= 1;
698 /* first pass sets up default configurations */
699 for (i
= 1; i
< argc
; i
++)
701 if (streq(argv
[i
], "-config"))
703 if (((i
+ 1) < argc
) && (argv
[i
+1][0] != '-'))
706 if (streq(argv
[i
], "mac"))
708 global
.dbtype
= DB_TYPE_FILE
;
709 global
.db_file_max
= 25600000;
711 else if (streq(argv
[i
], "appletv"))
713 global
.dbtype
= DB_TYPE_FILE
;
714 global
.db_file_max
= 10240000;
716 else if (streq(argv
[i
], "iphone"))
718 global
.dbtype
= DB_TYPE_MINI
;
725 for (i
= 1; i
< argc
; i
++)
727 if (streq(argv
[i
], "-d"))
730 if (((i
+1) < argc
) && (argv
[i
+1][0] != '-')) global
.debug_file
= strdup(argv
[++i
]);
731 memset(tstr
, 0, sizeof(tstr
));
735 asldebug("%s syslogd[%d]: Start\n", tstr
, getpid());
737 else if (streq(argv
[i
], "-db"))
739 if (((i
+ 1) < argc
) && (argv
[i
+1][0] != '-'))
742 if (streq(argv
[i
], "file"))
744 global
.dbtype
|= DB_TYPE_FILE
;
745 if (((i
+ 1) < argc
) && (argv
[i
+1][0] != '-')) global
.db_file_max
= atol(argv
[++i
]);
747 else if (streq(argv
[i
], "memory"))
749 global
.dbtype
|= DB_TYPE_MEMORY
;
750 if (((i
+ 1) < argc
) && (argv
[i
+1][0] != '-')) global
.db_memory_max
= atol(argv
[++i
]);
752 else if (streq(argv
[i
], "mini"))
754 global
.dbtype
|= DB_TYPE_MINI
;
755 if (((i
+ 1) < argc
) && (argv
[i
+1][0] != '-')) global
.db_mini_max
= atol(argv
[++i
]);
759 else if (streq(argv
[i
], "-D"))
763 else if (streq(argv
[i
], "-m"))
765 if ((i
+ 1) < argc
) global
.mark_time
= 60 * atoll(argv
[++i
]);
767 else if (streq(argv
[i
], "-utmp_ttl"))
769 if ((i
+ 1) < argc
) global
.utmp_ttl
= atol(argv
[++i
]);
771 else if (streq(argv
[i
], "-fs_ttl"))
773 if ((i
+ 1) < argc
) global
.fs_ttl
= atol(argv
[++i
]);
775 else if (streq(argv
[i
], "-mps_limit"))
777 if ((i
+ 1) < argc
) global
.mps_limit
= atol(argv
[++i
]);
779 else if (streq(argv
[i
], "-l"))
781 if ((i
+ 1) < argc
) mp
= argv
[++i
];
783 else if (streq(argv
[i
], "-c"))
788 if ((argv
[i
][0] >= '0') && (argv
[i
][0] <= '7') && (argv
[i
][1] == '\0')) global
.asl_log_filter
= ASL_FILTER_MASK_UPTO(atoi(argv
[i
]));
791 else if (streq(argv
[i
], "-dup_delay"))
793 if ((i
+ 1) < argc
) global
.bsd_max_dup_time
= atoll(argv
[++i
]);
795 else if (streq(argv
[i
], "-asl_in"))
797 if ((i
+ 1) < argc
) activate_asl_in
= atoi(argv
[++i
]);
799 else if (streq(argv
[i
], "-asl_action"))
801 if ((i
+ 1) < argc
) activate_asl_action
= atoi(argv
[++i
]);
803 else if (streq(argv
[i
], "-klog_in"))
805 if ((i
+ 1) < argc
) activate_klog_in
= atoi(argv
[++i
]);
807 else if (streq(argv
[i
], "-bsd_in"))
809 if ((i
+ 1) < argc
) activate_bsd_in
= atoi(argv
[++i
]);
811 else if (streq(argv
[i
], "-bsd_out"))
813 if ((i
+ 1) < argc
) activate_bsd_out
= atoi(argv
[++i
]);
815 else if (streq(argv
[i
], "-remote"))
817 if ((i
+ 1) < argc
) activate_remote
= atoi(argv
[++i
]);
819 else if (streq(argv
[i
], "-udp_in"))
821 if ((i
+ 1) < argc
) activate_udp_in
= atoi(argv
[++i
]);
825 if (global
.dbtype
== 0)
827 global
.dbtype
= DB_TYPE_FILE
;
828 global
.db_file_max
= 25600000;
829 global
.asl_store_ping_time
= 150;
832 TAILQ_INIT(&Moduleq
);
837 if (global
.debug
== 0)
841 if (fork() != 0) exit(0);
852 signal(SIGHUP
, catch_sighup
);
853 signal(SIGINFO
, catch_siginfo
);
855 /* register for network change notifications if the udp_in module is active */
856 network_change_token
= -1;
857 if (activate_udp_in
!= 0) notify_register_signal(NETWORK_CHANGE_NOTIFICATION
, SIGINFO
, &network_change_token
);
859 for (mod
= Moduleq
.tqh_first
; mod
!= NULL
; mod
= mod
->entries
.tqe_next
)
862 if (fd
< 0) continue;
866 * Start database server thread
868 pthread_attr_init(&attr
);
869 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
870 pthread_create(&t
, &attr
, (void *(*)(void *))database_server
, NULL
);
871 pthread_attr_destroy(&attr
);
874 * Start output worker thread
876 pthread_attr_init(&attr
);
877 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
878 pthread_create(&t
, &attr
, (void *(*)(void *))output_worker
, NULL
);
879 pthread_attr_destroy(&attr
);
886 * drain /dev/klog first
891 FD_SET(global
.kfd
, &kern
);
892 max
= global
.kfd
+ 1;
893 while (select(max
, &kern
, NULL
, NULL
, &zto
) > 0)
895 aslevent_handleevent(&kern
, &wr
, &ex
);
900 * Start launchd drain thread
902 pthread_attr_init(&attr
);
903 pthread_attr_setdetachstate(&attr
, PTHREAD_CREATE_DETACHED
);
904 pthread_create(&t
, &attr
, (void *(*)(void *))launchd_drain
, NULL
);
905 pthread_attr_destroy(&attr
);
907 runloop_timer
= NULL
;
908 timed_events(&runloop_timer
);
912 max
= aslevent_fdsets(&rd
, &wr
, &ex
) + 1;
914 status
= select(max
, &rd
, &wr
, &ex
, runloop_timer
);
915 if ((global
.kfd
>= 0) && FD_ISSET(global
.kfd
, &rd
))
917 /* drain /dev/klog */
919 FD_SET(global
.kfd
, &kern
);
920 max
= global
.kfd
+ 1;
922 while (select(max
, &kern
, NULL
, NULL
, &zto
) > 0)
924 aslevent_handleevent(&kern
, &wr
, &ex
);
928 if (global
.reset
!= RESET_NONE
)
931 global
.reset
= RESET_NONE
;
934 if (status
!= 0) aslevent_handleevent(&rd
, &wr
, &ex
);
936 timed_events(&runloop_timer
);