#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <mach/mach_time.h>
#include <servers/bootstrap.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/fcntl.h>
#include <sys/queue.h>
#include <sys/time.h>
#include <pthread.h>
#include <dirent.h>
#include <dlfcn.h>
#include <libgen.h>
#include <notify.h>
#include "daemon.h"
#define SERVICE_NAME "com.apple.system.logger"
#define SERVER_STATUS_ERROR -1
#define SERVER_STATUS_INACTIVE 0
#define SERVER_STATUS_ACTIVE 1
#define SERVER_STATUS_ON_DEMAND 2
#define DEFAULT_MARK_SEC 0
#define DEFAULT_UTMP_TTL_SEC 31622400
#define DEFAULT_FS_TTL_SEC 31622400
#define DEFAULT_BSD_MAX_DUP_SEC 30
#define DEFAULT_MPS_LIMIT 500
#define BILLION 1000000000
#define NOTIFY_DELAY 1
#define NETWORK_CHANGE_NOTIFICATION "com.apple.system.config.network_change"
#define streq(A,B) (strcmp(A,B)==0)
#define forever for(;;)
static uint64_t time_start = 0;
static uint64_t mark_last = 0;
static uint64_t ping_last = 0;
static uint64_t time_last = 0;
extern int __notify_78945668_info__;
extern int _malloc_no_asl_log;
static TAILQ_HEAD(ml, module_list) Moduleq;
struct global_s global;
int asl_in_init();
int asl_in_reset();
int asl_in_close();
static int activate_asl_in = 1;
int asl_action_init();
int asl_action_reset();
int asl_action_close();
static int activate_asl_action = 1;
int klog_in_init();
int klog_in_reset();
int klog_in_close();
static int activate_klog_in = 1;
int bsd_in_init();
int bsd_in_reset();
int bsd_in_close();
static int activate_bsd_in = 1;
int bsd_out_init();
int bsd_out_reset();
int bsd_out_close();
static int activate_bsd_out = 1;
int remote_init();
int remote_reset();
int remote_close();
static int activate_remote = 0;
int udp_in_init();
int udp_in_reset();
int udp_in_close();
static int activate_udp_in = 1;
extern void database_server();
extern void output_worker();
extern void launchd_drain();
extern void bsd_flush_duplicates(time_t now);
extern void bsd_close_idle_files(time_t now);
static int
static_modules()
{
struct module_list *tmp;
if (activate_asl_action != 0)
{
tmp = calloc(1, sizeof(struct module_list));
if (tmp == NULL) return 1;
tmp->name = strdup("asl_action");
if (tmp->name == NULL)
{
free(tmp);
return 1;
}
tmp->module = NULL;
tmp->init = asl_action_init;
tmp->reset = asl_action_reset;
tmp->close = asl_action_close;
TAILQ_INSERT_TAIL(&Moduleq, tmp, entries);
}
if (activate_asl_in != 0)
{
tmp = calloc(1, sizeof(struct module_list));
if (tmp == NULL) return 1;
tmp->name = strdup("asl_in");
if (tmp->name == NULL)
{
free(tmp);
return 1;
}
tmp->module = NULL;
tmp->init = asl_in_init;
tmp->reset = asl_in_reset;
tmp->close = asl_in_close;
TAILQ_INSERT_TAIL(&Moduleq, tmp, entries);
}
if (activate_klog_in != 0)
{
tmp = calloc(1, sizeof(struct module_list));
if (tmp == NULL) return 1;
tmp->name = strdup("klog_in");
if (tmp->name == NULL)
{
free(tmp);
return 1;
}
tmp->module = NULL;
tmp->init = klog_in_init;
tmp->reset = klog_in_reset;
tmp->close = klog_in_close;
TAILQ_INSERT_TAIL(&Moduleq, tmp, entries);
}
if (activate_bsd_in != 0)
{
tmp = calloc(1, sizeof(struct module_list));
if (tmp == NULL) return 1;
tmp->name = strdup("bsd_in");
if (tmp->name == NULL)
{
free(tmp);
return 1;
}
tmp->module = NULL;
tmp->init = bsd_in_init;
tmp->reset = bsd_in_reset;
tmp->close = bsd_in_close;
TAILQ_INSERT_TAIL(&Moduleq, tmp, entries);
}
if (activate_bsd_out != 0)
{
tmp = calloc(1, sizeof(struct module_list));
if (tmp == NULL) return 1;
tmp->name = strdup("bsd_out");
if (tmp->name == NULL)
{
free(tmp);
return 1;
}
tmp->module = NULL;
tmp->init = bsd_out_init;
tmp->reset = bsd_out_reset;
tmp->close = bsd_out_close;
TAILQ_INSERT_TAIL(&Moduleq, tmp, entries);
}
if (activate_remote != 0)
{
tmp = calloc(1, sizeof(struct module_list));
if (tmp == NULL) return 1;
tmp->name = strdup("remote");
if (tmp->name == NULL)
{
free(tmp);
return 1;
}
tmp->module = NULL;
tmp->init = remote_init;
tmp->reset = remote_reset;
tmp->close = remote_close;
TAILQ_INSERT_TAIL(&Moduleq, tmp, entries);
}
if (activate_udp_in != 0)
{
tmp = calloc(1, sizeof(struct module_list));
if (tmp == NULL) return 1;
tmp->name = strdup("udp_in");
if (tmp->name == NULL)
{
free(tmp);
return 1;
}
tmp->module = NULL;
tmp->init = udp_in_init;
tmp->reset = udp_in_reset;
tmp->close = udp_in_close;
TAILQ_INSERT_TAIL(&Moduleq, tmp, entries);
}
return 0;
}
static int
load_modules(const char *mp)
{
DIR *d;
struct dirent *de;
struct module_list *tmp;
void *c, *bn;
char *modulepath = NULL;
d = opendir(mp);
if (d == NULL) return -1;
while (NULL != (de = readdir(d)))
{
if (de->d_name[0] == '.') continue;
if (!strstr(de->d_name, ".so")) continue;
asprintf(&modulepath, "%s/%s", mp, de->d_name);
if (!modulepath) continue;
c = dlopen(modulepath, RTLD_LOCAL);
if (c == NULL)
{
free(modulepath);
continue;
}
tmp = calloc(1, sizeof(struct module_list));
if (tmp == NULL)
{
free(modulepath);
dlclose(c);
continue;
}
bn = basename(modulepath);
tmp->name = strdup(bn);
if (tmp->name == NULL)
{
free(tmp);
return 1;
}
tmp->module = c;
TAILQ_INSERT_TAIL(&Moduleq, tmp, entries);
tmp->init = dlsym(tmp->module, "aslmod_init");
tmp->reset = dlsym(tmp->module, "aslmod_reset");
tmp->close = dlsym(tmp->module, "aslmod_close");
free(modulepath);
}
closedir(d);
return 0;
}
static void
writepid(void)
{
FILE *fp;
fp = fopen(_PATH_PIDFILE, "w");
if (fp != NULL)
{
fprintf(fp, "%d\n", getpid());
fclose(fp);
}
}
static void
closeall(void)
{
int i;
for (i = getdtablesize() - 1; i >= 0; i--) close(i);
open("/dev/null", O_RDWR, 0);
dup(0);
dup(0);
}
static void
detach(void)
{
signal(SIGINT, SIG_IGN);
signal(SIGPIPE, SIG_IGN);
setsid();
}
static void
catch_sighup(int x)
{
global.reset = RESET_CONFIG;
}
static void
catch_siginfo(int x)
{
global.reset = RESET_NETWORK;
}
static void
send_reset(void)
{
struct module_list *mod;
for (mod = Moduleq.tqh_first; mod != NULL; mod = mod->entries.tqe_next)
{
if (mod->reset != NULL) mod->reset();
}
}
static void
timed_events(struct timeval **run)
{
time_t now, delta, t;
static struct timeval next;
now = time(NULL);
*run = NULL;
next.tv_sec = 0;
next.tv_usec = 0;
if (time_start == 0)
{
time_start = now;
time_last = now;
mark_last = now;
ping_last = now;
}
if (__notify_78945668_info__ < 0)
{
if (notify_post("com.apple.system.syslogd") == NOTIFY_STATUS_OK) __notify_78945668_info__ = 0;
else next.tv_sec = 1;
}
if (time_last > now)
{
time_last = now;
mark_last = now;
ping_last = now;
}
if (global.bsd_flush_time > 0)
{
bsd_flush_duplicates(now);
bsd_close_idle_files(now);
if (global.bsd_flush_time > 0)
{
if (next.tv_sec == 0) next.tv_sec = global.bsd_flush_time;
else if (global.bsd_flush_time < next.tv_sec) next.tv_sec = global.bsd_flush_time;
}
}
if (global.asl_store_ping_time > 0)
{
delta = now - ping_last;
if (delta >= global.asl_store_ping_time)
{
db_ping_store();
bsd_close_idle_files(now);
ping_last = now;
t = global.asl_store_ping_time;
}
else
{
t = global.asl_store_ping_time - delta;
}
if (next.tv_sec == 0) next.tv_sec = t;
else if (t < next.tv_sec) next.tv_sec = t;
}
if (global.mark_time > 0)
{
delta = now - mark_last;
if (delta >= global.mark_time)
{
asl_mark();
mark_last = now;
t = global.mark_time;
}
else
{
t = global.mark_time - delta;
}
if (next.tv_sec == 0) next.tv_sec = t;
else if (t < next.tv_sec) next.tv_sec = t;
}
if (next.tv_sec > 0) *run = &next;
time_last = now;
}
void
init_config()
{
launch_data_t tmp, pdict;
kern_return_t status;
tmp = launch_data_new_string(LAUNCH_KEY_CHECKIN);
global.launch_dict = launch_msg(tmp);
launch_data_free(tmp);
if (global.launch_dict == NULL)
{
fprintf(stderr, "%d launchd checkin failed\n", getpid());
exit(1);
}
tmp = launch_data_dict_lookup(global.launch_dict, LAUNCH_JOBKEY_MACHSERVICES);
if (tmp == NULL)
{
fprintf(stderr, "%d launchd lookup of LAUNCH_JOBKEY_MACHSERVICES failed\n", getpid());
exit(1);
}
pdict = launch_data_dict_lookup(tmp, SERVICE_NAME);
if (pdict == NULL)
{
fprintf(stderr, "%d launchd lookup of SERVICE_NAME failed\n", getpid());
exit(1);
}
global.server_port = launch_data_get_machport(pdict);
status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &(global.self_port));
if (status != KERN_SUCCESS)
{
fprintf(stderr, "mach_port_allocate self_port failed: %d", status);
exit(1);
}
status = mach_port_insert_right(mach_task_self(), global.self_port, global.self_port, MACH_MSG_TYPE_MAKE_SEND);
if (status != KERN_SUCCESS)
{
fprintf(stderr, "Can't make send right for self_port: %d\n", status);
exit(1);
}
status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &(global.dead_session_port));
if (status != KERN_SUCCESS)
{
fprintf(stderr, "mach_port_allocate dead_session_port failed: %d", status);
exit(1);
}
status = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &(global.listen_set));
if (status != KERN_SUCCESS)
{
fprintf(stderr, "mach_port_allocate listen_set failed: %d", status);
exit(1);
}
status = mach_port_move_member(mach_task_self(), global.server_port, global.listen_set);
if (status != KERN_SUCCESS)
{
fprintf(stderr, "mach_port_move_member server_port failed: %d", status);
exit(1);
}
status = mach_port_move_member(mach_task_self(), global.self_port, global.listen_set);
if (status != KERN_SUCCESS)
{
fprintf(stderr, "mach_port_move_member self_port failed: %d", status);
exit(1);
}
status = mach_port_move_member(mach_task_self(), global.dead_session_port, global.listen_set);
if (status != KERN_SUCCESS)
{
fprintf(stderr, "mach_port_move_member dead_session_port failed (%u)", status);
exit(1);
}
}
void
config_debug(int enable, const char *path)
{
OSSpinLockLock(&global.lock);
global.debug = enable;
if (global.debug_file != NULL) free(global.debug_file);
global.debug_file = strdup(path);
OSSpinLockUnlock(&global.lock);
}
void
config_data_store(int type, uint32_t file_max, uint32_t memory_max, uint32_t mini_max)
{
pthread_mutex_lock(global.db_lock);
if (global.dbtype & DB_TYPE_FILE)
{
asl_store_close(global.file_db);
global.file_db = NULL;
}
if (global.dbtype & DB_TYPE_MEMORY)
{
asl_memory_close(global.memory_db);
global.memory_db = NULL;
}
if (global.dbtype & DB_TYPE_MINI)
{
asl_mini_memory_close(global.mini_db);
global.mini_db = NULL;
}
global.dbtype = type;
global.db_file_max = file_max;
global.db_memory_max = memory_max;
global.db_mini_max = mini_max;
pthread_mutex_unlock(global.db_lock);
}
int
main(int argc, const char *argv[])
{
struct module_list *mod;
fd_set rd, wr, ex, kern;
int32_t fd, i, max, status, daemonize;
const char *mp;
struct timeval *runloop_timer, zto;
pthread_attr_t attr;
pthread_t t;
int network_change_token;
char tstr[32];
time_t now;
memset(&global, 0, sizeof(struct global_s));
global.db_lock = (pthread_mutex_t *)calloc(1, sizeof(pthread_mutex_t));
pthread_mutex_init(global.db_lock, NULL);
global.work_queue_lock = (pthread_mutex_t *)calloc(1, sizeof(pthread_mutex_t));
pthread_mutex_init(global.work_queue_lock, NULL);
pthread_cond_init(&global.work_queue_cond, NULL);
global.work_queue = (asl_search_result_t *)calloc(1, sizeof(asl_search_result_t));
global.asl_log_filter = ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG);
global.db_file_max = 16384000;
global.db_memory_max = 8192;
global.db_mini_max = 256;
global.bsd_max_dup_time = DEFAULT_BSD_MAX_DUP_SEC;
global.utmp_ttl = DEFAULT_UTMP_TTL_SEC;
global.fs_ttl = DEFAULT_FS_TTL_SEC;
global.mps_limit = DEFAULT_MPS_LIMIT;
global.kfd = -1;
#ifdef CONFIG_MAC
global.dbtype = DB_TYPE_FILE;
global.db_file_max = 25600000;
global.asl_store_ping_time = 150;
#endif
#ifdef CONFIG_APPLETV
global.dbtype = DB_TYPE_FILE;
global.db_file_max = 10240000;
global.asl_store_ping_time = 150;
#endif
#ifdef CONFIG_IPHONE
global.dbtype = DB_TYPE_MINI;
activate_remote = 1;
activate_bsd_out = 0;
#endif
mp = _PATH_MODULE_LIB;
daemonize = 0;
__notify_78945668_info__ = 0xf0000000;
zto.tv_sec = 0;
zto.tv_usec = 0;
_malloc_no_asl_log = 1;
for (i = 1; i < argc; i++)
{
if (streq(argv[i], "-config"))
{
if (((i + 1) < argc) && (argv[i+1][0] != '-'))
{
i++;
if (streq(argv[i], "mac"))
{
global.dbtype = DB_TYPE_FILE;
global.db_file_max = 25600000;
}
else if (streq(argv[i], "appletv"))
{
global.dbtype = DB_TYPE_FILE;
global.db_file_max = 10240000;
}
else if (streq(argv[i], "iphone"))
{
global.dbtype = DB_TYPE_MINI;
activate_remote = 1;
}
}
}
}
for (i = 1; i < argc; i++)
{
if (streq(argv[i], "-d"))
{
global.debug = 1;
if (((i+1) < argc) && (argv[i+1][0] != '-')) global.debug_file = strdup(argv[++i]);
memset(tstr, 0, sizeof(tstr));
now = time(NULL);
ctime_r(&now, tstr);
tstr[19] = '\0';
asldebug("%s syslogd[%d]: Start\n", tstr, getpid());
}
else if (streq(argv[i], "-db"))
{
if (((i + 1) < argc) && (argv[i+1][0] != '-'))
{
i++;
if (streq(argv[i], "file"))
{
global.dbtype |= DB_TYPE_FILE;
if (((i + 1) < argc) && (argv[i+1][0] != '-')) global.db_file_max = atol(argv[++i]);
}
else if (streq(argv[i], "memory"))
{
global.dbtype |= DB_TYPE_MEMORY;
if (((i + 1) < argc) && (argv[i+1][0] != '-')) global.db_memory_max = atol(argv[++i]);
}
else if (streq(argv[i], "mini"))
{
global.dbtype |= DB_TYPE_MINI;
if (((i + 1) < argc) && (argv[i+1][0] != '-')) global.db_mini_max = atol(argv[++i]);
}
}
}
else if (streq(argv[i], "-D"))
{
daemonize = 1;
}
else if (streq(argv[i], "-m"))
{
if ((i + 1) < argc) global.mark_time = 60 * atoll(argv[++i]);
}
else if (streq(argv[i], "-utmp_ttl"))
{
if ((i + 1) < argc) global.utmp_ttl = atol(argv[++i]);
}
else if (streq(argv[i], "-fs_ttl"))
{
if ((i + 1) < argc) global.fs_ttl = atol(argv[++i]);
}
else if (streq(argv[i], "-mps_limit"))
{
if ((i + 1) < argc) global.mps_limit = atol(argv[++i]);
}
else if (streq(argv[i], "-l"))
{
if ((i + 1) < argc) mp = argv[++i];
}
else if (streq(argv[i], "-c"))
{
if ((i + 1) < argc)
{
i++;
if ((argv[i][0] >= '0') && (argv[i][0] <= '7') && (argv[i][1] == '\0')) global.asl_log_filter = ASL_FILTER_MASK_UPTO(atoi(argv[i]));
}
}
else if (streq(argv[i], "-dup_delay"))
{
if ((i + 1) < argc) global.bsd_max_dup_time = atoll(argv[++i]);
}
else if (streq(argv[i], "-asl_in"))
{
if ((i + 1) < argc) activate_asl_in = atoi(argv[++i]);
}
else if (streq(argv[i], "-asl_action"))
{
if ((i + 1) < argc) activate_asl_action = atoi(argv[++i]);
}
else if (streq(argv[i], "-klog_in"))
{
if ((i + 1) < argc) activate_klog_in = atoi(argv[++i]);
}
else if (streq(argv[i], "-bsd_in"))
{
if ((i + 1) < argc) activate_bsd_in = atoi(argv[++i]);
}
else if (streq(argv[i], "-bsd_out"))
{
if ((i + 1) < argc) activate_bsd_out = atoi(argv[++i]);
}
else if (streq(argv[i], "-remote"))
{
if ((i + 1) < argc) activate_remote = atoi(argv[++i]);
}
else if (streq(argv[i], "-udp_in"))
{
if ((i + 1) < argc) activate_udp_in = atoi(argv[++i]);
}
}
if (global.dbtype == 0)
{
global.dbtype = DB_TYPE_FILE;
global.db_file_max = 25600000;
global.asl_store_ping_time = 150;
}
TAILQ_INIT(&Moduleq);
static_modules();
load_modules(mp);
aslevent_init();
if (global.debug == 0)
{
if (daemonize != 0)
{
if (fork() != 0) exit(0);
detach();
closeall();
}
writepid();
}
init_config();
signal(SIGHUP, catch_sighup);
signal(SIGINFO, catch_siginfo);
network_change_token = -1;
if (activate_udp_in != 0) notify_register_signal(NETWORK_CHANGE_NOTIFICATION, SIGINFO, &network_change_token);
for (mod = Moduleq.tqh_first; mod != NULL; mod = mod->entries.tqe_next)
{
fd = mod->init();
if (fd < 0) continue;
}
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&t, &attr, (void *(*)(void *))database_server, NULL);
pthread_attr_destroy(&attr);
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&t, &attr, (void *(*)(void *))output_worker, NULL);
pthread_attr_destroy(&attr);
FD_ZERO(&rd);
FD_ZERO(&wr);
FD_ZERO(&ex);
if (global.kfd >= 0)
{
FD_ZERO(&kern);
FD_SET(global.kfd, &kern);
max = global.kfd + 1;
while (select(max, &kern, NULL, NULL, &zto) > 0)
{
aslevent_handleevent(&kern, &wr, &ex);
}
}
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&t, &attr, (void *(*)(void *))launchd_drain, NULL);
pthread_attr_destroy(&attr);
runloop_timer = NULL;
timed_events(&runloop_timer);
forever
{
max = aslevent_fdsets(&rd, &wr, &ex) + 1;
status = select(max, &rd, &wr, &ex, runloop_timer);
if ((global.kfd >= 0) && FD_ISSET(global.kfd, &rd))
{
FD_ZERO(&kern);
FD_SET(global.kfd, &kern);
max = global.kfd + 1;
while (select(max, &kern, NULL, NULL, &zto) > 0)
{
aslevent_handleevent(&kern, &wr, &ex);
}
}
if (global.reset != RESET_NONE)
{
send_reset();
global.reset = RESET_NONE;
}
if (status != 0) aslevent_handleevent(&rd, &wr, &ex);
timed_events(&runloop_timer);
}
}