/* $Id: main.c 5078 2007-12-09 16:56:36Z morris $ */ /* * Copyright (c) 2004-2007 Axel Andersson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include #include #include #include #include #include #include #include "clients.h" #include "main.h" #include "servers.h" #include "settings.h" #include "tracker.h" #include "version.h" static void wt_cleanup(void); static void wt_usage(void); static void wt_version(void); static void wt_write_pid(void); static void wt_delete_pid(void); static void wt_delete_status(void); static void wt_signals_init(void); static void wt_block_signals(void); static int wt_wait_signals(void); static void wt_signal_thread(wi_runtime_instance_t *); static void wt_signal_crash(int); static wi_boolean_t wt_daemonize = true; wi_boolean_t wt_running = true; wi_address_family_t wt_address_family = WI_ADDRESS_NULL; wi_lock_t *wt_status_lock; wi_date_t *wt_start_date; wi_uinteger_t wt_current_servers, wt_total_clients; wi_uinteger_t wt_current_users; wi_uinteger_t wt_current_files; wi_file_offset_t wt_current_size; int main(int argc, const char **argv) { wi_array_t *arguments; wi_pool_t *pool; wi_string_t *string; const char **xargv; int ch, facility; wi_boolean_t no_chroot, test_config, daemonize; /* init libwired */ wi_initialize(); wi_load(argc, argv); pool = wi_pool_init(wi_pool_alloc()); wi_log_startup = true; wi_log_syslog = true; wi_log_syslog_facility = LOG_DAEMON; /* init core systems */ wt_version_init(); wt_status_lock = wi_lock_init(wi_lock_alloc()); wt_start_date = wi_date_init(wi_date_alloc()); /* set defaults */ wi_root_path = wi_string_init_with_cstring(wi_string_alloc(), WT_ROOT); wi_settings_config_path = wi_string_init_with_cstring(wi_string_alloc(), WT_CONFIG_PATH); no_chroot = false; test_config = false; daemonize = true; /* init reexec argument list */ arguments = wi_array_init(wi_array_alloc()); /* parse command line switches */ while((ch = getopt(argc, (char * const *) argv, "46Dd:f:hi:L:ls:tuVvX")) != -1) { switch(ch) { case '4': wt_address_family = WI_ADDRESS_IPV4; break; case '6': wt_address_family = WI_ADDRESS_IPV6; break; case 'D': wt_daemonize = false; wi_log_stderr = true; break; case 'd': wi_release(wi_root_path); wi_root_path = wi_string_init_with_cstring(wi_string_alloc(), optarg); break; case 'f': wi_release(wi_settings_config_path); wi_settings_config_path = wi_string_init_with_cstring(wi_string_alloc(), optarg); break; case 'i': wi_log_limit = wi_string_uint32(wi_string_with_cstring(optarg)); break; case 'L': wi_log_syslog = false; wi_log_file = true; wi_release(wi_log_path); wi_log_path = wi_string_init_with_cstring(wi_string_alloc(), optarg); break; case 'l': wi_log_level++; break; case 's': string = wi_string_with_cstring(optarg); facility = wi_log_syslog_facility_with_name(string); if(facility < 0) { wi_log_err(WI_STR("Could not find syslog facility \"%@\": %m"), string); } wi_log_syslog_facility = facility; break; case 't': test_config = true; break; case 'u': no_chroot = true; break; case 'V': case 'v': wt_version(); break; case 'X': daemonize = false; break; case '?': case 'h': default: wt_usage(); break; } wi_array_add_data(arguments, wi_string_with_format(WI_STR("-%c"), ch)); if(optarg) wi_array_add_data(arguments, wi_string_with_cstring(optarg)); } /* detach */ if(daemonize) { wi_array_add_data(arguments, WI_STR("-X")); wi_array_insert_data_at_index(arguments, wi_string_with_cstring(argv[0]), 0); switch(fork()) { case -1: wi_log_err(WI_STR("Could not fork: %m")); break; case 0: xargv = wi_array_argv(arguments); if(execv(argv[0], (char * const *) xargv) < 0) wi_log_err(WI_STR("Could not execute %s: %m"), argv[0]); break; default: _exit(0); break; } } wi_release(arguments); /* open log */ wi_log_open(); /* init subsystems */ wt_ssl_init(); wt_clients_init(); wt_servers_init(); /* read the config file */ wt_settings_chroot = !no_chroot; wt_settings_init(); if(!wt_settings_read_config()) exit(1); /* change root directory */ if(!no_chroot) { if(!wi_change_root()) wi_log_err(WI_STR("Could not change root to %@: %m"), wi_root_path); } /* apply settings */ wt_settings_apply_settings(); if(test_config) { printf("Config OK\n"); exit(0); } /* dump command line */ wi_log_info(WI_STR("Started as %@ %@"), wi_process_path(wi_process()), wi_array_components_joined_by_string(wi_process_arguments(wi_process()), WI_STR(" "))); /* init tracker */ wi_log_info(WI_STR("Starting Wired Tracker version %@"), wt_version_string); wt_tracker_init(); /* switch user/group */ wi_switch_user(wt_settings.user, wt_settings.group); /* create tracker threads after privilege drop */ wt_signals_init(); wt_block_signals(); wt_servers_schedule(); wt_tracker_create_threads(); wt_write_pid(); wt_write_status(true); wi_log_startup = false; /* clean up pool after startup */ wi_pool_drain(pool); /* enter the signal handling thread in the main thread */ wt_signal_thread(NULL); /* dropped out */ wt_cleanup(); wi_log_close(); wi_release(pool); return 0; } static void wt_cleanup(void) { wt_delete_pid(); wt_delete_status(); } static void wt_usage(void) { fprintf(stderr, "Usage: trackerd [-Dllhtuv] [-d path] [-f file] [-i lines] [-L file] [-s facility]\n\ \n\ Options:\n\ -4 listen on IPv4 addresses only\n\ -6 listen on IPv6 addresses only\n\ -D do not daemonize\n\ -d path set the server root path\n\ -f file set the config file to load\n\ -h display this message\n\ -i lines set limit on number of lines for -L\n\ -L set alternate file for log output\n\ -l increase log level (can be used twice)\n\ -s facility set the syslog(3) facility\n\ -t run syntax test on config\n\ -u do not chroot(2) to root path\n\ -v display version information\n\ \n\ By Axel Andersson <%s>\n", WT_BUGREPORT); exit(2); } static void wt_version(void) { fprintf(stderr, "Wired Tracker %s, protocol %s, %s\n", wi_string_cstring(wt_version_string), wi_string_cstring(wt_protocol_version_string), SSLeay_version(SSLEAY_VERSION)); exit(2); } #pragma mark - static void wt_write_pid(void) { wi_string_t *string; if(wt_settings.pid) { string = wi_string_with_format(WI_STR("%d\n"), getpid()); if(!wi_string_write_to_file(string, wt_settings.pid)) wi_log_warn(WI_STR("Could not write to %@: %m"), wt_settings.pid); } } static void wt_delete_pid(void) { if(wt_settings.pid) { if(!wi_file_delete(wt_settings.pid)) wi_log_warn(WI_STR("Could not delete %@: %m"), wt_settings.pid); } } void wt_write_status(wi_boolean_t force) { static wi_time_interval_t update; wi_string_t *string; wi_time_interval_t interval; interval = wi_time_interval(); if(!force && interval - update < 1.0) return; update = interval; wi_process_set_name(wi_process(), wi_string_with_format(WI_STR("%u %@"), wt_current_servers, wt_current_servers == 1 ? WI_STR("server") : WI_STR("servers"))); if(wt_settings.status) { string = wi_string_with_format(WI_STR("%.0f %u %u %u %u %llu\n"), wi_date_time_interval(wt_start_date), wt_current_servers, wt_total_clients, wt_current_users, wt_current_files, wt_current_size); if(!wi_string_write_to_file(string, wt_settings.status)) wi_log_warn(WI_STR("Could not write to %@: %m"), wt_settings.status); } } static void wt_delete_status(void) { if(wt_settings.status) { if(!wi_file_delete(wt_settings.status)) wi_log_warn(WI_STR("Could not delete %@: %m"), wt_settings.status); } } #pragma mark - static void wt_signals_init(void) { signal(SIGPIPE, SIG_IGN); signal(SIGILL, wt_signal_crash); signal(SIGABRT, wt_signal_crash); signal(SIGFPE, wt_signal_crash); signal(SIGBUS, wt_signal_crash); signal(SIGSEGV, wt_signal_crash); } static void wt_block_signals(void) { wi_thread_block_signals(SIGHUP, SIGINT, SIGTERM, SIGQUIT, 0); } static int wt_wait_signals(void) { return wi_thread_wait_for_signals(SIGHUP, SIGINT, SIGTERM, SIGQUIT, 0); } static void wt_signal_thread(wi_runtime_instance_t *arg) { wi_pool_t *pool; unsigned int i = 0; int signal; pool = wi_pool_init(wi_pool_alloc()); while(wt_running) { signal = wt_wait_signals(); switch(signal) { case SIGHUP: wi_log_info(WI_STR("Signal HUP received, reloading configuration")); wt_settings_read_config(); wt_settings_apply_settings(); break; case SIGINT: wi_log_info(WI_STR("Signal INT received, quitting")); wt_running = false; break; case SIGQUIT: wi_log_info(WI_STR("Signal QUIT received, quitting")); wt_running = false; break; case SIGTERM: wi_log_info(WI_STR("Signal TERM received, quitting")); wt_running = false; break; } if(++i % 10 == 0) wi_pool_drain(pool); } wi_release(pool); } static void wt_signal_crash(int sigraised) { wt_cleanup(); sleep(360); if(signal(sigraised, SIG_DFL) != SIG_ERR) raise(sigraised); }