From: pk910 Date: Wed, 26 Sep 2012 14:26:24 +0000 (+0200) Subject: Merge remote-tracking branch 'origin/development' X-Git-Url: http://git.pk910.de/?p=NeonServV5.git;a=commitdiff_plain;h=73e82db5a85f3679af9dc0880aba813c6d299372;hp=67dfed5461abb34f0f048640af78d47be7ba0c74 Merge remote-tracking branch 'origin/development' --- diff --git a/Makefile.am b/Makefile.am index df5f180..fdfb7f1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -211,6 +211,7 @@ neonserv_SOURCES = src/version.c \ src/modules.c \ src/module_commands.c \ src/ModuleFunctions.c \ + src/statistics.c \ src/memoryDebug.c neonserv_LDADD = $(MYSQL_LIBS) $(SYSTEM_LIBS) diff --git a/src/ChanNode.c b/src/ChanNode.c index cf3c1db..41bbd45 100644 --- a/src/ChanNode.c +++ b/src/ChanNode.c @@ -21,6 +21,7 @@ #include "modcmd.h" #include "ModeNode.h" #include "IRCEvents.h" +#include "tools.h" static struct ChanNode **chanList; diff --git a/src/ClientSocket.c b/src/ClientSocket.c index 3be7037..eb11d88 100644 --- a/src/ClientSocket.c +++ b/src/ClientSocket.c @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - +#include "main.h" #include "ClientSocket.h" #include "IRCParser.h" #include "UserNode.h" @@ -222,6 +222,8 @@ static IOHANDLER_CALLBACK(socket_callback) { #ifdef HAVE_THREADS unsigned int tid; #endif + if(process_state.running == 0) + return; //just ignore the event (shutdown sequence) switch(event->type) { case IOEVENT_CONNECTED: client->flags |= SOCKET_FLAG_CONNECTED; @@ -285,13 +287,19 @@ struct ClientSocket* getBots(int flags, struct ClientSocket* last_bot) { return NULL; } -void free_sockets() { +void free_sockets(int close_only) { if(!sockets) return; struct ClientSocket *client, *next; for (client = sockets->data; client; client = next) { next = client->next; - destroy_socket(client); + if(close_only) { + if((client->flags & SOCKET_FLAG_CONNECTED)) + iohandler_printf(client->iofd, "QUIT :[NeonServ %s.%d] shutdown requested.\n", NEONSERV_VERSION, patchlevel); + } else + destroy_socket(client); + } + if(!close_only) { + free(sockets); + sockets = NULL; } - free(sockets); - sockets = NULL; } diff --git a/src/ClientSocket.h b/src/ClientSocket.h index ccc6bd7..9e73fd1 100644 --- a/src/ClientSocket.h +++ b/src/ClientSocket.h @@ -92,6 +92,6 @@ int clientsocket_parseorder_top(unsigned int tid); /* MODULAR ACCESSIBLE */ void putsock(struct ClientSocket *client, const char *text, ...) PRINTF_LIKE(2, 3); /* MODULAR ACCESSIBLE */ struct ClientSocket* getBots(int flags, struct ClientSocket* last_bot); void init_sockets(); -void free_sockets(); +void free_sockets(int close_only); #endif #endif diff --git a/src/ConfigParser.c b/src/ConfigParser.c index 5d32afc..050d6b4 100644 --- a/src/ConfigParser.c +++ b/src/ConfigParser.c @@ -16,6 +16,7 @@ */ #include "ConfigParser.h" +#include "tools.h" #define ENTRYTYPE_BLOCK 1 #define ENTRYTYPE_STRING 2 diff --git a/src/DBHelper.c b/src/DBHelper.c index d5d8d0b..69bdf44 100644 --- a/src/DBHelper.c +++ b/src/DBHelper.c @@ -226,8 +226,8 @@ int renameAccount(char *oldauth, char *newauth) { printf_mysql_query("SELECT `chanuser_id`, `chanuser_access`, `chanuser_flags` FROM `chanusers` WHERE `chanuser_uid` = '%d'", userid); res2 = mysql_use(); if((row2 = mysql_fetch_row(res2)) != NULL) { - if(atoi(row[0]) > atoi(row2[0])) { - printf_mysql_query("UPDATE `chanusers` SET `chanuser_access` = '%s' WHERE `chanuser_id` = '%s'", row[0], row2[0]); + if(atoi(row[1]) > atoi(row2[1])) { + printf_mysql_query("UPDATE `chanusers` SET `chanuser_access` = '%s' WHERE `chanuser_id` = '%s'", row[1], row2[0]); } printf_mysql_query("DELETE FROM `chanusers` WHERE `chanuser_id` = '%s'", row[0]); } else @@ -241,11 +241,10 @@ int renameAccount(char *oldauth, char *newauth) { printf_mysql_query("UPDATE `owner_history` SET `owner_history_from_uid` = '%d' WHERE `owner_history_from_uid` = '%d'", userid, newuid); printf_mysql_query("UPDATE `owner_history` SET `owner_history_from_uid` = '%d' WHERE `owner_history_from_uid` = '%d'", userid, newuid); printf_mysql_query("UPDATE `noinvite` SET `uid` = '%d' WHERE `uid` = '%d'", userid, newuid); - printf_mysql_query("DELETE FROM `users` WHERE `chanuser_id` = '%d'", newuid); - } else { - //simply rename the account - printf_mysql_query("UPDATE `users` SET `user_user` = '%s' WHERE `user_id` = '%d'", escape_string(newauth), userid); + printf_mysql_query("DELETE FROM `users` WHERE `user_id` = '%d'", newuid); } + //simply rename the account + printf_mysql_query("UPDATE `users` SET `user_user` = '%s' WHERE `user_id` = '%d'", escape_string(newauth), userid); char *alertchan = get_string_field("General.CheckAuths.alertchan"); if(alertchan) { struct ChanNode *alertchan_chan = getChanByName(alertchan); diff --git a/src/IOHandler.c b/src/IOHandler.c index 81d8d09..db9a599 100644 --- a/src/IOHandler.c +++ b/src/IOHandler.c @@ -706,11 +706,15 @@ void iohandler_events(struct IODescriptor *iofd, int readable, int writeable) { } void iohandler_poll() { + struct timeval timeout; + timeout.tv_sec = IO_MAX_TIMEOUT; + timeout.tv_usec = 0; + iohandler_poll_timeout(timeout); +} + +void iohandler_poll_timeout(struct timeval timeout) { if(engine) { IOSYNCHRONIZE(io_poll_sync); //quite senceless multithread support... better support will follow - struct timeval timeout; - timeout.tv_sec = IO_MAX_TIMEOUT; - timeout.tv_usec = 0; engine->loop(&timeout); IODESYNCHRONIZE(io_poll_sync); } diff --git a/src/IOHandler.h b/src/IOHandler.h index 23c411c..ea7b443 100644 --- a/src/IOHandler.h +++ b/src/IOHandler.h @@ -118,5 +118,6 @@ void iohandler_update(struct IODescriptor *iofd); void iohandler_set_timeout(struct IODescriptor *iofd, struct timeval *timeout); void iohandler_poll(); +void iohandler_poll_timeout(struct timeval timeout); #endif diff --git a/src/IRCParser.c b/src/IRCParser.c index ff244a0..bcb9f02 100644 --- a/src/IRCParser.c +++ b/src/IRCParser.c @@ -30,6 +30,7 @@ #include "bots.h" #include "timeq.h" #include "ConfigParser.h" +#include "statistics.h" struct irc_cmd *irc_commands = NULL; //static struct UserNode *registering_users = NULL; @@ -645,8 +646,7 @@ static IRC_CMD(raw_privmsg) { if(argv[0][0] == '#') { //Channel message struct ChanNode *chan = getChanByName(argv[0]); if(chan && client == get_first_prefered_bot_in_channel(chan)) { - if(statistics_enabled) - statistics_privmsg++; + statistics_privmsg++; if(argv[1][0] == '\001') { char *cmd = &argv[1][1]; char *text = strstr(cmd, " "); diff --git a/src/IRCQueue.c b/src/IRCQueue.c index 1c49972..296850d 100644 --- a/src/IRCQueue.c +++ b/src/IRCQueue.c @@ -17,6 +17,7 @@ #include "IRCQueue.h" #include "ClientSocket.h" #include "IOHandler.h" +#include "tools.h" #define MAXPENALTY 8 /* 4 messages */ diff --git a/src/QServer.c b/src/QServer.c index 23cc2cc..ef382c9 100644 --- a/src/QServer.c +++ b/src/QServer.c @@ -25,6 +25,7 @@ #include "ConfigParser.h" #include "bots.h" #include "IOHandler.h" +#include "tools.h" #ifdef WIN32 typedef uint32_t socklen_t; diff --git a/src/bots.c b/src/bots.c index 693901d..076c0cb 100644 --- a/src/bots.c +++ b/src/bots.c @@ -26,6 +26,7 @@ #include "modcmd.h" #include "DBHelper.h" #include "IRCEvents.h" +#include "tools.h" struct cmd_bot_alias { int botid; diff --git a/src/lang.c b/src/lang.c index e07735e..22b0c77 100644 --- a/src/lang.c +++ b/src/lang.c @@ -18,6 +18,7 @@ #include "UserNode.h" #include "DBHelper.h" #include "mysqlConn.h" +#include "tools.h" #define DEFAULT_LANG_TAG "EN" #define DEFAULT_LANG_NAME "English" diff --git a/src/main.c b/src/main.c index 5ab1efd..23903d9 100644 --- a/src/main.c +++ b/src/main.c @@ -15,6 +15,10 @@ * along with this program. If not, see . */ +#define DEFAULT_PID_FILE "neonserv.pid" +#define DEFAULT_CONF_FILE "neonserv.conf" +#define DEFAULT_LOG_FILE "neonserv.log" + #include "main.h" #include "signal.h" #include "ClientSocket.h" @@ -41,138 +45,49 @@ #include "module_commands.h" #include "ModuleFunctions.h" #include "IOHandler.h" +#include "statistics.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,91 +95,74 @@ 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); + putlog(LOGLEVEL_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)); + putlog(LOGLEVEL_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); - } - 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); +} + +static int reload_configuration() { + if(!loadConfig(process_state.config)) + return 1; + 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_lang(); init_parser(); init_UserNode(); @@ -280,105 +178,236 @@ 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() { + 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 + + //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(log_sync); + 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 +445,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,72 +485,18 @@ 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 -} +/* LOG BACKEND */ void write_log(int loglevel, const char *line, int len) { SYNCHRONIZE(log_sync); - if(!daemonized && (print_loglevel & loglevel)) { + if(!process_state.daemonized && (process_state.loglevel & loglevel)) { printf("%s", line); - } else if(!daemonized && loglevel == LOGLEVEL_ERROR) { + } else if(!process_state.daemonized && loglevel == LOGLEVEL_ERROR) { fprintf(stderr, "%s", line); } if(get_int_field("log.loglevel") & loglevel) { if(!log_fptr) { - log_fptr = fopen(LOG_FILE, "a"); + log_fptr = fopen(DEFAULT_LOG_FILE, "a"); if(!log_fptr) goto write_log_end; } time_t rawtime; @@ -550,6 +525,8 @@ void putlog(int loglevel, const char *text, ...) { write_log(loglevel, logBuf, pos); } +/* INSTALLATION SCRIPT */ + static void check_firstrun() { MYSQL_RES *res; MYSQL_ROW row; diff --git a/src/main.h b/src/main.h index 26f4319..85e4fd8 100644 --- a/src/main.h +++ b/src/main.h @@ -18,11 +18,28 @@ #define _main_h #include "overall.h" +struct ProcessState { + time_t start_time; + int running : 1; + int restart : 1; + int run_as_daemon : 1; + int daemonized : 1; + int loglevel : 8; + int loaded_config : 1; + int running_threads : 8; + + int argc; + char **argv; + + char config[MAXLEN]; + char pidfile[MAXLEN]; +}; + #ifndef DND_FUNCTIONS -extern time_t start_time; -extern int statistics_enabled; + +extern struct ProcessState process_state; + #ifdef HAVE_THREADS -extern int running_threads; extern pthread_mutex_t cache_sync; extern pthread_mutex_t whohandler_sync, whohandler_mass_sync; @@ -32,19 +49,10 @@ extern pthread_mutex_t whohandler_sync, whohandler_mass_sync; /* MODULAR ACCESSIBLE */ time_t getStartTime(); /* MODULAR ACCESSIBLE */ int getRunningThreads(); -void exit_daemon(); - -/* MODULAR ACCESSIBLE */ int stricmp (const char *s1, const char *s2); -/* MODULAR ACCESSIBLE */ int stricmplen (const char *s1, const char *s2, int len); - -/* MODULAR ACCESSIBLE */ void restart_process(); -/* MODULAR ACCESSIBLE */ void cleanup(); -/* MODULAR ACCESSIBLE */ void restart_bot(int do_hard_restart); +/* MODULAR ACCESSIBLE */ void restart_bot(int crash); /* MODULAR ACCESSIBLE */ void stop_bot(); /* MODULAR ACCESSIBLE */ void reload_config(); -int statistics_update(); - /* MODULAR ACCESSIBLE */ void putlog(int loglevel, const char *text, ...) PRINTF_LIKE(2, 3); #endif #endif \ No newline at end of file diff --git a/src/memoryDebug.c b/src/memoryDebug.c index 1f52ece..c425592 100644 --- a/src/memoryDebug.c +++ b/src/memoryDebug.c @@ -17,6 +17,7 @@ #include "main.h" #include "memoryDebug.h" #include "memoryInfo.h" +#include "tools.h" #define FILE_NAME_LENGTH 256 #define OUTPUT_FILE "leak_info.txt" diff --git a/src/modcmd.c b/src/modcmd.c index e19d261..0c1e92e 100644 --- a/src/modcmd.c +++ b/src/modcmd.c @@ -27,6 +27,7 @@ #include "mysqlConn.h" #include "DBHelper.h" #include "EventLogger.h" +#include "tools.h" struct trigger_callback { int botid; @@ -237,8 +238,7 @@ static void handle_command(struct ClientSocket *client, struct UserNode *user, s if(cbind == NULL) break; } else if(cbind->flags & CMDFLAG_SUB_LINKER) cbind = modcmd_sub_linker_command(client, textclient, user, cbind, bind_index, &args); - if(statistics_enabled) - statistics_commands++; + statistics_commands++; total_triggered++; cbind->triggered++; if((BIND_FLAGS(cbind) & CMDFLAG_FUNCMD)) { diff --git a/src/modules.c b/src/modules.c index 0de4505..7310c88 100644 --- a/src/modules.c +++ b/src/modules.c @@ -19,7 +19,10 @@ #include #endif -/* 000-011 */ #include "main.h" +/* 000-001 */ #include "main.h" +/* 002-004 */ #include "tools.h" +/* 005-006 */ /* deprecated */ +/* 007-011 */ /* main.h */ /* 012 */ #include "BanNode.h" /* 013-019 */ #include "bots.h" /* 020-025 */ #include "ChanNode.h" @@ -40,7 +43,7 @@ /* 126-136 */ #include "ModeNode.h" /* 137-142 */ #include "mysqlConn.h" /* 143-149 */ #include "timeq.h" -/* 150-169 */ #include "tools.h" +/* 150-169 */ /* tools.h */ /* 170-180 */ #include "UserNode.h" /* 181-183 */ #include "WHOHandler.h" /* 184-188 */ #include "version.h" @@ -56,11 +59,11 @@ void *global_functions[] = { /* 000 */ (Function) getStartTime, /* 001 */ (Function) getRunningThreads, -/* 002 */ (Function) exit_daemon, +/* 002 */ (Function) getCurrentSecondsOfDay, /* 003 */ (Function) stricmp, /* 004 */ (Function) stricmplen, -/* 005 */ (Function) restart_process, -/* 006 */ (Function) cleanup, +/* 005 */ (Function) NULL, /* deprecated */ +/* 006 */ (Function) NULL, /* deprecated */ /* 007 */ (Function) restart_bot, /* 008 */ (Function) stop_bot, /* 009 */ (Function) reload_config, diff --git a/src/modules/NeonServ.mod/cmd_neonserv_invitemeall.c b/src/modules/NeonServ.mod/cmd_neonserv_invitemeall.c index 9f5b56d..c7d3340 100644 --- a/src/modules/NeonServ.mod/cmd_neonserv_invitemeall.c +++ b/src/modules/NeonServ.mod/cmd_neonserv_invitemeall.c @@ -31,7 +31,7 @@ CMD_BIND(neonserv_cmd_invitemeall) { struct ClientSocket *bot; while((chanuserrow = mysql_fetch_row(res)) != NULL) { int userflags = atoi(chanuserrow[1]); - if(!(userflags & DB_CHANUSER_AUTOINVITE)) continue; + if(!(argc && !stricmp(argv[0], "all")) && !(userflags & DB_CHANUSER_AUTOINVITE)) continue; if(!(chan = getChanByName(chanuserrow[2]))) continue; //no bot is in the channel if((bchanuser = getChanUser(client->user, chan)) && (bchanuser->flags & CHANUSERFLAG_OPPED)) bot = client; diff --git a/src/modules/global.mod/cmd_global_restart.c b/src/modules/global.mod/cmd_global_restart.c index 64ef5a5..80f4403 100644 --- a/src/modules/global.mod/cmd_global_restart.c +++ b/src/modules/global.mod/cmd_global_restart.c @@ -22,7 +22,5 @@ */ CMD_BIND(global_cmd_restart) { - int hard_restart = 1; - if(argc > 0 && !stricmp(argv[0], "soft")) hard_restart = 0; - restart_bot(hard_restart); + restart_bot(0); } diff --git a/src/modules/module.h b/src/modules/module.h index 071c148..25a7676 100644 --- a/src/modules/module.h +++ b/src/modules/module.h @@ -27,11 +27,11 @@ extern int module_id; /**** global function list ****/ /* 000 */ #define getStartTime ((time_t (*)(void))global[0]) /* 001 */ #define getRunningThreads ((int (*)(void))global[1]) -/* 002 */ #define exit_daemon ((int (*)(void))global[2]) +/* 002 */ #define getCurrentSecondsOfDay ((int (*)(void))global[2]) /* 003 */ #define stricmp ((int (*)(const char *, const char *))global[3]) /* 004 */ #define stricmplen ((int (*)(const char *, const char *, int))global[4]) -/* 005 */ #define restart_process ((void (*)(void))global[5]) -/* 006 */ #define cleanup ((void (*)(void))global[6]) +/* 005 */ /* deprecated */ +/* 006 */ /* deprecated */ /* 007 */ #define restart_bot ((void (*)(int))global[7]) /* 008 */ #define stop_bot ((void (*)(void))global[8]) /* 009 */ #define reload_config ((void (*)(void))global[9]) diff --git a/src/mysqlConn.c b/src/mysqlConn.c index 79c82e9..4673c7d 100644 --- a/src/mysqlConn.c +++ b/src/mysqlConn.c @@ -16,6 +16,8 @@ */ #include "mysqlConn.h" +#include "ConfigParser.h" +#include "tools.h" #define DATABASE_VERSION "20" static void show_mysql_error(); @@ -96,14 +98,39 @@ void mysql_free() { mysql_conn->escaped_strings = NULL; } -void init_mysql(char *host, int port, char *user, char *pass, char *base) { - THREAD_MUTEX_INIT(synchronized); - mysql_host = strdup(host); - mysql_serverport = port; - mysql_user = strdup(user); - mysql_pass = strdup(pass); - mysql_base = strdup(base); +int reload_mysql() { + char *new_mysql_host = get_string_field("MySQL.host"); + char *new_mysql_user = get_string_field("MySQL.user"); + char *new_mysql_pass = get_string_field("MySQL.pass"); + char *new_mysql_base = get_string_field("MySQL.base"); + if(!(new_mysql_host && new_mysql_user && new_mysql_pass && new_mysql_base)) + return 0; + + //replace login data + if(mysql_host) + free(mysql_host); + mysql_host = strdup(new_mysql_host); + + if(mysql_user) + free(mysql_user); + mysql_user = strdup(new_mysql_user); + + if(mysql_pass) + free(mysql_pass); + mysql_pass = strdup(new_mysql_pass); + if(mysql_base) + free(mysql_base); + mysql_base = strdup(new_mysql_base); + + mysql_serverport = get_int_field("MySQL.port"); + if(!mysql_serverport) + mysql_serverport = 3306; + return 1; +} + +void init_mysql() { + THREAD_MUTEX_INIT(synchronized); MYSQL *mysql_conn = get_mysql_conn(); diff --git a/src/mysqlConn.h b/src/mysqlConn.h index 660e2cb..e85832f 100644 --- a/src/mysqlConn.h +++ b/src/mysqlConn.h @@ -30,7 +30,8 @@ #ifndef DND_FUNCTIONS /* MODULAR ACCESSIBLE */ MYSQL_RES *mysql_use(); /* MODULAR ACCESSIBLE */ void mysql_free(); -void init_mysql(char *host, int port, char *user, char *pass, char *base); +int reload_mysql(); +void init_mysql(); void free_mysql(); /* MODULAR ACCESSIBLE */ void printf_mysql_query(const char *text, ...) PRINTF_LIKE(1, 2); /* MODULAR ACCESSIBLE */ void printf_long_mysql_query(int len, const char *text, ...) PRINTF_LIKE(2, 3); diff --git a/src/overall.h b/src/overall.h index caf062d..26ff10e 100644 --- a/src/overall.h +++ b/src/overall.h @@ -120,10 +120,6 @@ #include "memoryDebug.h" #endif -#define PID_FILE "neonserv.pid" -#define CONF_FILE "neonserv.conf" -#define LOG_FILE "neonserv.log" - #define SOCKET_SELECT_TIME 1 #define SOCKET_RECONNECT_TIME 20 diff --git a/src/signal.c b/src/signal.c index 24e399d..bc44446 100644 --- a/src/signal.c +++ b/src/signal.c @@ -92,15 +92,12 @@ static void sigcrash(int signum) { } } putlog(LOGLEVEL_INFO, "hard shutdown...\n"); - exit_daemon(); usleep(2000000); //hard restart - restart_process(); + restart_bot(1); exit(0); } static void sigexit(int signum) { - cleanup(); - exit_daemon(); - exit(0); + stop_bot(); } diff --git a/src/statistics.c b/src/statistics.c new file mode 100644 index 0000000..5d102ca --- /dev/null +++ b/src/statistics.c @@ -0,0 +1,78 @@ +/* statistics.c - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "statistics.h" +#include "timeq.h" +#include "ConfigParser.h" +#include "ClientSocket.h" +#include "bots.h" +#include "UserNode.h" +#include "ChanNode.h" +#include "ChanUser.h" +#include "IRCParser.h" +#include "modcmd.h" + +int statistics_requested_lusers = 0; + +static 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_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_privmsg = 0; + statistics_commands = 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; +} + +void init_statistics() { + int update_minutes = get_int_field("statistics.frequency"); + if(!update_minutes) update_minutes = 2; + timeq_add(update_minutes * 60 + 10, 0, main_statistics, NULL); +} diff --git a/src/statistics.h b/src/statistics.h new file mode 100644 index 0000000..79f7f4d --- /dev/null +++ b/src/statistics.h @@ -0,0 +1,25 @@ +/* statistics.h - NeonServ v5.6 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef _statistics_h +#define _statistics_h + +#include "main.h" + +int statistics_update(); +void init_statistics(); + +#endif diff --git a/src/timeq.c b/src/timeq.c index 0136c51..c95068d 100644 --- a/src/timeq.c +++ b/src/timeq.c @@ -17,6 +17,7 @@ #include "timeq.h" #include "IOHandler.h" +#include "tools.h" static struct timeq_entry *timeq_events; #ifdef HAVE_THREADS diff --git a/src/tools.c b/src/tools.c index daedcd0..5447232 100644 --- a/src/tools.c +++ b/src/tools.c @@ -375,6 +375,16 @@ int strToTime(struct UserNode *user, char *str) { return total_time; } +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; +} + struct ModeBuffer* initModeBuffer(struct ClientSocket *client, struct ChanNode *chan) { struct ModeBuffer *modeBuf = malloc(sizeof(*modeBuf)); if(!modeBuf) { @@ -576,6 +586,28 @@ unsigned long crc32(const char *text) { return (crc^0xFFFFFFFF); } +int stricmp (const char *s1, const char *s2) { + return stricmplen(s1, s2, -1); +} + +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))) { + if (*s1 == '\0') + break; + i++; + s1++; + s2++; + if(len != -1 && i == len) break; + } + return c1 - c2; +} + void init_tools() { register_default_language_table(msgtab); crc32_init(); diff --git a/src/tools.h b/src/tools.h index 320cb79..bab14cb 100644 --- a/src/tools.h +++ b/src/tools.h @@ -75,6 +75,7 @@ struct ModeBuffer { /* MODULAR ACCESSIBLE */ char* timeToStr(struct UserNode *user, int seconds, int items, char *buf); /* MODULAR ACCESSIBLE */ int strToTime(struct UserNode *user, char *str); +/* MODULAR ACCESSIBLE */ int getCurrentSecondsOfDay(); /* MODULAR ACCESSIBLE */ struct ModeBuffer* initModeBuffer(struct ClientSocket *client, struct ChanNode *chan); /* MODULAR ACCESSIBLE */ void modeBufferSet(struct ModeBuffer *modeBuf, int add, char mode, char *param); @@ -91,6 +92,9 @@ struct ModeBuffer { /* MODULAR ACCESSIBLE */ unsigned long crc32(const char *text); +/* MODULAR ACCESSIBLE */ int stricmp (const char *s1, const char *s2); +/* MODULAR ACCESSIBLE */ int stricmplen (const char *s1, const char *s2, int len); + void init_tools(); #endif