X-Git-Url: http://git.pk910.de/?p=NeonServV5.git;a=blobdiff_plain;f=src%2Fmain.c;h=fb16300da97ac5697e13adfcc59b85811a8468d3;hp=5ab1efd0bd5f93ccce05657783bcdda7f8722484;hb=HEAD;hpb=fc61be208ca6dbf2fd915591c8dc7e5ef5779891 diff --git a/src/main.c b/src/main.c index 5ab1efd..fb16300 100644 --- a/src/main.c +++ b/src/main.c @@ -15,6 +15,9 @@ * along with this program. If not, see . */ +#define DEFAULT_PID_FILE "neonserv.pid" +#define DEFAULT_CONF_FILE "neonserv.conf" + #include "main.h" #include "signal.h" #include "ClientSocket.h" @@ -41,138 +44,47 @@ #include "module_commands.h" #include "ModuleFunctions.h" #include "IOHandler.h" +#include "statistics.h" +#include "log.h" + +struct ProcessState process_state; -time_t start_time; -static int running, hard_restart; -static int statistics_requested_lusers = 0; -int statistics_enabled; -TIMEQ_CALLBACK(main_statistics); -TIMEQ_CALLBACK(main_checkauths); -static int daemonized = 0; -static int print_loglevel = 0; -static FILE *log_fptr = NULL; -static int process_argc; -static char **process_argv; #ifdef HAVE_THREADS -int running_threads; pthread_mutex_t cache_sync; pthread_mutex_t whohandler_sync, whohandler_mass_sync; -static pthread_mutex_t log_sync; +static pthread_t *current_threads = NULL; #endif +static void *main_tread(void *empty); +static TIMEQ_CALLBACK(clear_cache); +static TIMEQ_CALLBACK(main_checkauths); static void check_firstrun(); -void cleanup() { - stop_modules(); - free_sockets(); - qserver_free(); - free_parser(); - free_UserNode(); - free_ChanNode(); - free_bind(); - free_modcmd(); - free_whoqueue(); - free_mysql(); - free_handleinfohandler(); - free_lang(); -} - -static int load_mysql_config() { - char *mysql_host, *mysql_user, *mysql_pass, *mysql_base; - int mysql_serverport; - - mysql_host = get_string_field("MySQL.host"); - if(!mysql_host) { - perror("invalid neonserv.conf: missing MySQL.host"); - return 0; - } - mysql_serverport = get_int_field("MySQL.port"); - if(!mysql_serverport) - mysql_serverport = 3306; - mysql_user = get_string_field("MySQL.user"); - if(!mysql_user) { - perror("invalid neonserv.conf: missing MySQL.user"); - return 0; - } - mysql_pass = get_string_field("MySQL.pass"); - if(!mysql_pass) { - perror("invalid neonserv.conf: missing MySQL.pass"); - return 0; - } - mysql_base = get_string_field("MySQL.base"); - if(!mysql_base) { - perror("invalid neonserv.conf: missing MySQL base"); - return 0; - } - init_mysql(mysql_host, mysql_serverport, mysql_user, mysql_pass, mysql_base); - return 1; -} - -static TIMEQ_CALLBACK(clear_cache) { - timeq_add(CLEAR_CACHE_INTERVAL, 0, clear_cache, NULL); - clearTempUsers(); - destroyEvents(); - mysql_free(); -} - -void *thread_main(void *arg) { - while(running) { - iohandler_poll(); - } - return NULL; -} - -#ifdef HAVE_THREADS -pthread_t *current_threads = NULL; -int getCurrentThreadID() { - if(!current_threads) return 0; - int i; - unsigned int my_tid = (unsigned int) pthread_self_tid(); - for(i = 0; i < running_threads; i++) { - #ifdef WIN32 - if((unsigned int) current_threads[i].p == my_tid) - #else - if((unsigned int) current_threads[i] == my_tid) - #endif - return i+1; - } - return 0; -} - -#endif - -void exit_daemon() { - running = 0; - if(daemonized) { - remove(PID_FILE); - } - if(log_fptr) { - fclose(log_fptr); - log_fptr = NULL; - } -} - -int main(int argc, char *argv[]) { - int run_as_daemon = 1; +static void main_parse_arguments() { int c; - process_argv = argv; - process_argc = argc; - printf("NeonServ v%s\n\n", NEONSERV_VERSION); struct option options[] = { {"show", 1, 0, 's'}, {"foreground", 0, 0, 'f'}, + {"config", 1, 0, 'c'}, + {"pid", 1, 0, 'p'}, {"help", 0, 0, 'h'}, {"version", 0, 0, 'v'}, {0, 0, 0, 0} }; - while ((c = getopt_long(argc, argv, "s:fvh", options, NULL)) != -1) { + while ((c = getopt_long(process_state.argc, process_state.argv, "s:fvh", options, NULL)) != -1) { switch (c) { + case 'c': + strncpy(process_state.config, optarg, MAXLEN-1); + break; + case 'p': + strncpy(process_state.pidfile, optarg, MAXLEN-1); + break; case 's': - print_loglevel = atoi(optarg); + process_state.loglevel = atoi(optarg); break; case 'f': - run_as_daemon = 0; + process_state.run_as_daemon = 0; break; case 'v': printf("Version: %s.%d (%s)\n", NEONSERV_VERSION, patchlevel, (strcmp(revision, "") ? revision : "-")); @@ -180,97 +92,84 @@ int main(int argc, char *argv[]) { exit(0); break; case 'h': - printf("Usage: ./neonserv [-s loglevel] [-f] [-h|-v]\n"); - printf(" -s, --show show log lines matching loglevel in stdout.\n"); + printf("Usage: ./neonserv [-c neonserv.conf] [-p neonserv.pid] [-s loglevel] [-f] [-h|-v]\n"); + printf(" -c, --config use this configuration file.\n"); printf(" -f, --foreground run NeonServ in the foreground.\n"); printf(" -h, --help prints this usage message.\n"); + printf(" -p, --pid use this pid file.\n"); + printf(" -s, --show show log lines matching loglevel in stdout.\n"); printf(" -v, --version prints this program's version.\n"); exit(0); break; } } +} + +static void main_daemon_exit() { + remove(process_state.pidfile); +} + +static void main_daemonize() { #ifndef WIN32 - if(geteuid() == 0 || getuid() == 0) { - fprintf(stderr, "NeonServ may not be run with super user privileges.\n"); + /* Attempt to fork into the background if daemon mode is on. */ + pid_t pid = fork(); + if (pid < 0) { + fprintf(stderr, "Unable to fork: %s\n", strerror(errno)); + } else if (pid > 0) { + printf("Forking into the background (pid: %d)...\n", pid); + printf_log("main", LOG_INFO, "Forking into the background (pid: %d)...\n", pid); exit(0); } - #endif - #ifdef ENABLE_MEMORY_DEBUG - initMemoryDebug(); - #endif - if(!loadConfig(CONF_FILE)) { - fprintf(stderr, "Unable to load " CONF_FILE "\n"); - exit(0); + setsid(); + process_state.daemonized = 1; + atexit(main_daemon_exit); + FILE *pidfile = fopen(process_state.pidfile, "w"); + if (pidfile == NULL) { + fprintf(stderr, "Unable to create PID file: %s\n", strerror(errno)); + printf_log("main", LOG_ERROR, "Unable to create PID file: %s\n", strerror(errno)); + } else { + fprintf(pidfile, "%i\n", (int)getpid()); + fclose(pidfile); } - init_bind(); - event_reload(1); - #if HAVE_THREADS - THREAD_MUTEX_INIT(log_sync); + FILE *retn; + fclose(stdin); retn = fopen("/dev/null", "r"); + fclose(stdout); retn = fopen("/dev/null", "w"); + fclose(stderr); retn = fopen("/dev/null", "w"); #endif - if(!load_mysql_config()) { - fprintf(stderr, "Unable to connect to MySQL\n"); - exit(0); - } - check_firstrun(); - char **modulelist = get_all_fieldnames("modules"); - if(!modulelist || !modulelist[0]) { - fprintf(stderr, "no modules loaded... Please update your configuration!\n"); - exit(0); +} + +static int reload_configuration() { + printf_log("main", LOG_DEBUG, "reloading configuration file: %s", process_state.config); + if(!loadConfig(process_state.config)) { + printf_log("main", LOG_ERROR, "could not reload configuration file: %s", process_state.config); + return 1; } - free(modulelist); - if (run_as_daemon) { - #ifndef WIN32 - /* Attempt to fork into the background if daemon mode is on. */ - pid_t pid = fork(); - if (pid < 0) { - fprintf(stderr, "Unable to fork: %s\n", strerror(errno)); - } else if (pid > 0) { - printf("Forking into the background (pid: %d)...\n", pid); - putlog(LOGLEVEL_INFO, "Forking into the background (pid: %d)...\n", pid); - exit(0); - } - setsid(); - daemonized = 1; - atexit(exit_daemon); - FILE *pidfile = fopen(PID_FILE, "w"); - if (pidfile == NULL) { - fprintf(stderr, "Unable to create PID file: %s\n", strerror(errno)); - putlog(LOGLEVEL_ERROR, "Unable to create PID file: %s\n", strerror(errno)); - } else { - fprintf(pidfile, "%i\n", (int)getpid()); - fclose(pidfile); + if(process_state.loaded_config) { + if(!reload_mysql()) + return 2; + char **modulelist = get_all_fieldnames("modules"); + if(!modulelist || !modulelist[0]) { + free(modulelist); + return 3; } - FILE *retn; - fclose(stdin); retn = fopen("/dev/null", "r"); - fclose(stdout); retn = fopen("/dev/null", "w"); - fclose(stderr); retn = fopen("/dev/null", "w"); - #endif + free(modulelist); + event_reload(0); } - -main: - signal(SIGABRT, sighandler); - signal(SIGFPE, sighandler); - signal(SIGILL, sighandler); - signal(SIGINT, sighandler); - signal(SIGSEGV, sighandler); - signal(SIGTERM, sighandler); - - start_time = time(0); - - statistics_enabled = get_int_field("statistics.enable"); - - #ifdef HAVE_THREADS - THREAD_MUTEX_INIT(cache_sync); - THREAD_MUTEX_INIT(whohandler_sync); - THREAD_MUTEX_INIT(whohandler_mass_sync); - #endif - + process_state.loaded_config = 1; + return 0; +} + + +/* INITIALISATION OF SUBSYSTEMS */ +void initialize_subsystems() { + init_bind(); + init_log(); + printf_log("main", LOG_INFO, "starting up NeonServ %s subsystems...", NEONSERV_VERSION); init_lang(); init_parser(); init_UserNode(); init_ChanNode(); init_ModeNode(); - init_bind(); init_modcmd(); register_module_commands(); init_handleinfohandler(); @@ -280,105 +179,241 @@ main: init_bots(); init_DBHelper(); qserver_init(); - load_languages(); - int update_minutes = get_int_field("statistics.frequency"); - if(!update_minutes) update_minutes = 2; - timeq_add(update_minutes * 60 + 10, 0, main_statistics, NULL); - - timeq_add(90, 0, main_checkauths, NULL); - - timeq_add(CLEAR_CACHE_INTERVAL, 0, clear_cache, NULL); - + init_statistics(); +} + +void shutdown_subsystems() { + printf_log("main", LOG_INFO, "stopping NeonServ subsystems..."); + free_sockets(1); + //wait 50ms (run iohandler) + { + struct timeval timeout, ctime1, ctime2; + gettimeofday(&ctime1, NULL); + ctime1.tv_usec += 50000; + if(ctime1.tv_usec > 1000000) { + ctime1.tv_usec -= 1000000; + ctime1.tv_sec++; + } + do { + timeout.tv_sec = 0; + timeout.tv_usec = 10000; + iohandler_poll_timeout(timeout); + gettimeofday(&ctime2, NULL); + } while(timeval_is_bigger(ctime1, ctime2)); + } + stop_modules(); + free_sockets(0); + qserver_free(); + free_parser(); + free_UserNode(); + free_ChanNode(); + free_bind(); + free_modcmd(); + free_whoqueue(); + free_mysql(); + free_handleinfohandler(); + free_lang(); +} + +/* THREAD CONTROL */ +#ifdef HAVE_THREADS +int getCurrentThreadID() { + if(!current_threads) return 0; + int i; + unsigned int my_tid = (unsigned int) pthread_self_tid(); + for(i = 0; i < process_state.running_threads; i++) { + #ifdef WIN32 + if((unsigned int) current_threads[i].p == my_tid) + #else + if((unsigned int) current_threads[i] == my_tid) + #endif + return i+1; + } + return 0; +} +#endif + +int getRunningThreads() { + #ifdef HAVE_THREADS + return process_state.running_threads; + #else + return 1; + #endif +} + +static void main_start_threads() { int worker_threads = get_int_field("General.worker_threads"); if(!worker_threads) worker_threads = 1; - running = 1; #ifdef HAVE_THREADS int tid_id = 0; - current_threads = calloc(worker_threads, sizeof(*current_threads)); - for(tid_id = 0; tid_id < worker_threads; tid_id++) { - running_threads++; - pthread_create(¤t_threads[tid_id], NULL, thread_main, NULL); + { + current_threads = calloc(worker_threads, sizeof(*current_threads)); + for(tid_id = 0; tid_id < worker_threads; tid_id++) { + process_state.running_threads++; + pthread_create(¤t_threads[tid_id], NULL, main_tread, NULL); + } } #endif - thread_main(NULL); + main_tread(NULL); #ifdef HAVE_THREADS - for(tid_id = 0; tid_id < worker_threads; tid_id++) { - pthread_join(current_threads[tid_id], NULL); + { + for(tid_id = 0; tid_id < worker_threads; tid_id++) { + pthread_join(current_threads[tid_id], NULL); + } + process_state.running_threads = 0; } - running_threads = 0; #endif - cleanup(); - if(hard_restart) { - restart_process(); +} + +/* MAIN FUNCTION(S) */ + +static void *main_tread(void *empty) { + while(process_state.running) { + iohandler_poll(); } - goto main; + return NULL; } -void restart_process() { +static void main_restart_process() { /* Append a NULL to the end of argv[]. */ - char **restart_argv = (char **)alloca((process_argc + 1) * sizeof(char *)); - memcpy(restart_argv, process_argv, process_argc * sizeof(char *)); - restart_argv[process_argc] = NULL; + char **restart_argv = (char **)alloca((process_state.argc + 1) * sizeof(char *)); + memcpy(restart_argv, process_state.argv, process_state.argc * sizeof(char *)); + restart_argv[process_state.argc] = NULL; #ifdef WIN32 - execv(process_argv[0], (const char * const*)restart_argv); + execv(process_state.argv[0], (const char * const*)restart_argv); #else - execv(process_argv[0], restart_argv); + execv(process_state.argv[0], restart_argv); #endif } -int stricmp (const char *s1, const char *s2) -{ - if (s1 == NULL) return s2 == NULL ? 0 : -(*s2); - if (s2 == NULL) return *s1; - char c1, c2; - while ((c1 = tolower (*s1)) == (c2 = tolower (*s2))) - { - if (*s1 == '\0') break; - ++s1; ++s2; - } - return c1 - c2; +int main(int argc, char *argv[]) { + memset(&process_state, 0, sizeof(process_state)); + printf("NeonServ v%s\n\n", NEONSERV_VERSION); + + process_state.argv = argv; + process_state.argc = argc; + process_state.run_as_daemon = 1; + strcpy(process_state.config, DEFAULT_CONF_FILE); + strcpy(process_state.pidfile, DEFAULT_PID_FILE); + + //parse argv + main_parse_arguments(); + + //initialize memory debugger BEFORE allocating memory + #ifdef ENABLE_MEMORY_DEBUG + initMemoryDebug(); + #endif + + //initialize mutex debugger BEFORE using any mutexes + #ifdef ENABLE_MUTEX_DEBUG + initMutexDebug(); + #endif + + //deny root startup + #ifndef WIN32 + if(geteuid() == 0 || getuid() == 0) { + fprintf(stderr, "NeonServ may not be run with super user privileges.\n"); + exit(0); + } + #endif + + //load configuration + int errid; + if((errid = reload_configuration())) { + fprintf(stderr, "Unable to load configuration file `%s`. (errid: %d)\n", process_state.config, errid); + exit(0); + } + + //check mysql configuration + if(!reload_mysql()) { + fprintf(stderr, "Unable to load MySQL configuration.\n"); + exit(0); + } + + //check module configuration + char **modulelist = get_all_fieldnames("modules"); + if(!modulelist || !modulelist[0]) { + fprintf(stderr, "Unable to load Module configuration.\n"); + exit(0); + } + free(modulelist); + + #if HAVE_THREADS + THREAD_MUTEX_INIT(cache_sync); + THREAD_MUTEX_INIT(whohandler_sync); + THREAD_MUTEX_INIT(whohandler_mass_sync); + #endif + + //connect to mysql and check if it's the frst bot startup + init_mysql(); + check_firstrun(); + + //deamonize if wanted + if(process_state.run_as_daemon) + main_daemonize(); + + //set signal handlers + signal(SIGABRT, sighandler); + signal(SIGFPE, sighandler); + signal(SIGILL, sighandler); + signal(SIGINT, sighandler); + signal(SIGSEGV, sighandler); + signal(SIGTERM, sighandler); + + //set start time and initialize other code parts + process_state.running = 1; + process_state.start_time = time(0); + initialize_subsystems(); + + //start timers + timeq_add(CLEAR_CACHE_INTERVAL, 0, clear_cache, NULL); + timeq_add(90, 0, main_checkauths, NULL); + + //start worker threads + main_start_threads(); //BLOCKING + + //shutdown sequence... + shutdown_subsystems(); + if(process_state.restart) + main_restart_process(); //terminates the current process on success + + //eop (end of program :P) + //trust me, thats the end! + exit(0); } -int stricmplen (const char *s1, const char *s2, int len) -{ - if (s1 == NULL) return s2 == NULL ? 0 : -(*s2); - if (s2 == NULL) return *s1; - char c1, c2; - int i = 0; - while ((c1 = tolower (*s1)) == (c2 = tolower (*s2))) - { - i++; - if (*s1 == '\0') break; - ++s1; ++s2; - if(i == len) break; - } - return c1 - c2; +/* BOT INFORMATION */ +time_t getStartTime() { + return process_state.start_time; } -void restart_bot(int do_hard_restart) { - hard_restart = do_hard_restart; - running = 0; +/* BOT CONTROL */ +void restart_bot(int crash) { + if(crash) { + main_daemon_exit(); + main_restart_process(); + } else { + process_state.restart = 1; + process_state.running = 0; + } } void stop_bot() { - cleanup(); - exit(0); + process_state.running = 0; } void reload_config() { - loadConfig(CONF_FILE); - event_reload(0); + reload_configuration(); } -static int getCurrentSecondsOfDay() { - time_t now = time(0); - struct tm *timeofday = localtime(&now); - int seconds = 0; - seconds += timeofday->tm_hour * 3600; - seconds += timeofday->tm_min * 60; - seconds += timeofday->tm_sec; - return seconds; +/* TIMER FUNCTIONS */ + +static TIMEQ_CALLBACK(clear_cache) { + timeq_add(CLEAR_CACHE_INTERVAL, 0, clear_cache, NULL); + clearTempUsers(); + destroyEvents(); + mysql_free(); } static AUTHLOOKUP_CALLBACK(main_checkauths_callback) { @@ -416,7 +451,7 @@ static AUTHLOOKUP_CALLBACK(main_checkauths_callback) { } } -TIMEQ_CALLBACK(main_checkauths) { +static TIMEQ_CALLBACK(main_checkauths) { int next_call = 600; if(get_int_field("General.CheckAuths.enabled")) { int check_start_time = get_int_field("General.CheckAuths.start_time") * 3600; @@ -456,99 +491,7 @@ TIMEQ_CALLBACK(main_checkauths) { timeq_add(next_call, 0, main_checkauths, NULL); } -TIMEQ_CALLBACK(main_statistics) { - int update_minutes = get_int_field("statistics.frequency"); - if(!update_minutes) update_minutes = 2; - timeq_add(update_minutes * 60, 0, main_statistics, NULL); - if(get_int_field("statistics.enable")) { - statistics_enabled = 1; - statistics_requested_lusers = 1; - if(get_int_field("statistics.include_lusers")) { - struct ClientSocket *bot, *lusersbot = NULL; - for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { - if(bot->flags & SOCKET_FLAG_PREFERRED) - lusersbot = bot; - } - bot = lusersbot; - if(bot == NULL) bot = getBots(SOCKET_FLAG_READY, NULL); - putsock(bot, "LUSERS"); - } else { - statistics_update(); - } - } else - statistics_enabled = 0; -} - -int statistics_update() { - if(get_int_field("statistics.enable") && statistics_requested_lusers && get_string_field("statistics.execute")) { - statistics_requested_lusers = 0; - char command[MAXLEN]; - /* parameters: - - visible users - - visible chanusers - - visible channels - - privmsg per minute - - commands per minute - - network users - - network channels - */ - sprintf(command, "%s %d %d %d %d %d %d %d", get_string_field("statistics.execute"), getUserCount(), getChanUserCount(), getChannelCount(), statistics_privmsg, statistics_commands, statistics_network_users, statistics_network_channels); - statistics_privmsg = 0; - statistics_commands = 0; - return system(command); - } - return -1; -} - -time_t getStartTime() { - return start_time; -} - -int getRunningThreads() { - #ifdef HAVE_THREADS - return running_threads; - #else - return 1; - #endif -} - -void write_log(int loglevel, const char *line, int len) { - SYNCHRONIZE(log_sync); - if(!daemonized && (print_loglevel & loglevel)) { - printf("%s", line); - } else if(!daemonized && loglevel == LOGLEVEL_ERROR) { - fprintf(stderr, "%s", line); - } - if(get_int_field("log.loglevel") & loglevel) { - if(!log_fptr) { - log_fptr = fopen(LOG_FILE, "a"); - if(!log_fptr) goto write_log_end; - } - time_t rawtime; - struct tm *timeinfo; - time(&rawtime); - timeinfo = localtime(&rawtime); - char timestr[20]; - int timepos = strftime(timestr, 20, "%x %X ", timeinfo); - fwrite(timestr, 1, timepos, log_fptr); - fwrite(line, 1, len, log_fptr); - } - write_log_end: - DESYNCHRONIZE(log_sync); -} - -void putlog(int loglevel, const char *text, ...) { - va_list arg_list; - char logBuf[MAXLOGLEN]; - int pos; - logBuf[0] = '\0'; - va_start(arg_list, text); - pos = vsnprintf(logBuf, MAXLOGLEN - 1, text, arg_list); - va_end(arg_list); - if (pos < 0 || pos > (MAXLOGLEN - 1)) pos = MAXLOGLEN - 1; - logBuf[pos] = '\0'; - write_log(loglevel, logBuf, pos); -} +/* INSTALLATION SCRIPT */ static void check_firstrun() { MYSQL_RES *res;