// Copyright (c) Hercules Dev Team, licensed under GNU GPL. // See the LICENSE file // Portions Copyright (c) Athena Dev Teams #include "../common/mmo.h" #include "../common/showmsg.h" #include "../common/malloc.h" #include "core.h" #include "../common/console.h" #ifndef MINICORE #include "../common/db.h" #include "../common/socket.h" #include "../common/timer.h" #include "../common/thread.h" #include "../common/mempool.h" #include "../common/sql.h" #include "../config/core.h" #include "../common/strlib.h" #endif #include #include #include #include #ifndef _WIN32 #include #else #include "../common/winapi.h" // Console close event handling #endif /// Called when a terminate signal is received. void (*shutdown_callback)(void) = NULL; int runflag = CORE_ST_RUN; int arg_c = 0; char **arg_v = NULL; char *SERVER_NAME = NULL; char SERVER_TYPE = ATHENA_SERVER_NONE; #ifndef MINICORE // minimalist Core // Added by Gabuzomeu // // This is an implementation of signal() using sigaction() for portability. // (sigaction() is POSIX; signal() is not.) Taken from Stevens' _Advanced // Programming in the UNIX Environment_. // #ifdef WIN32 // windows don't have SIGPIPE #define SIGPIPE SIGINT #endif #ifndef POSIX #define compat_signal(signo, func) signal(signo, func) #else sigfunc *compat_signal(int signo, sigfunc *func) { struct sigaction sact, oact; sact.sa_handler = func; sigemptyset(&sact.sa_mask); sact.sa_flags = 0; #ifdef SA_INTERRUPT sact.sa_flags |= SA_INTERRUPT; /* SunOS */ #endif if (sigaction(signo, &sact, &oact) < 0) return (SIG_ERR); return (oact.sa_handler); } #endif /*====================================== * CORE : Console events for Windows *--------------------------------------*/ #ifdef _WIN32 static BOOL WINAPI console_handler(DWORD c_event) { switch(c_event) { case CTRL_CLOSE_EVENT: case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: if( shutdown_callback != NULL ) shutdown_callback(); else runflag = CORE_ST_STOP;// auto-shutdown break; default: return FALSE; } return TRUE; } static void cevents_init() { if (SetConsoleCtrlHandler(console_handler,TRUE)==FALSE) ShowWarning ("Unable to install the console handler!\n"); } #endif /*====================================== * CORE : Signal Sub Function *--------------------------------------*/ static void sig_proc(int sn) { static int is_called = 0; switch (sn) { case SIGINT: case SIGTERM: if (++is_called > 3) exit(EXIT_SUCCESS); if( shutdown_callback != NULL ) shutdown_callback(); else runflag = CORE_ST_STOP;// auto-shutdown break; case SIGSEGV: case SIGFPE: do_abort(); // Pass the signal to the system's default handler compat_signal(sn, SIG_DFL); raise(sn); break; #ifndef _WIN32 case SIGXFSZ: // ignore and allow it to set errno to EFBIG ShowWarning ("Max file size reached!\n"); //run_flag = 0; // should we quit? break; case SIGPIPE: //ShowInfo ("Broken pipe found... closing socket\n"); // set to eof in socket.c break; // does nothing here #endif } } void signals_init (void) { compat_signal(SIGTERM, sig_proc); compat_signal(SIGINT, sig_proc); #ifndef _DEBUG // need unhandled exceptions to debug on Windows compat_signal(SIGSEGV, sig_proc); compat_signal(SIGFPE, sig_proc); #endif #ifndef _WIN32 compat_signal(SIGILL, SIG_DFL); compat_signal(SIGXFSZ, sig_proc); compat_signal(SIGPIPE, sig_proc); compat_signal(SIGBUS, SIG_DFL); compat_signal(SIGTRAP, SIG_DFL); #endif } #endif #ifdef SVNVERSION const char *get_svn_revision(void) { return EXPAND_AND_QUOTE(SVNVERSION); } #else// not SVNVERSION const char* get_svn_revision(void) { static char svn_version_buffer[16] = ""; FILE *fp; if( svn_version_buffer[0] != '\0' ) return svn_version_buffer; // subversion 1.7 uses a sqlite3 database // FIXME this is hackish at best... // - ignores database file structure // - assumes the data in NODES.dav_cache column ends with "!svn/ver//)" // - since it's a cache column, the data might not even exist if( (fp = fopen(".svn"PATHSEP_STR"wc.db", "rb")) != NULL || (fp = fopen(".."PATHSEP_STR".svn"PATHSEP_STR"wc.db", "rb")) != NULL ) { #ifndef SVNNODEPATH //not sure how to handle branches, so i'll leave this overridable define until a better solution comes up #define SVNNODEPATH trunk #endif const char* prefix = "!svn/ver/"; const char* postfix = "/"EXPAND_AND_QUOTE(SVNNODEPATH)")"; // there should exist only 1 entry like this size_t prefix_len = strlen(prefix); size_t postfix_len = strlen(postfix); size_t i,j,len; char* buffer; // read file to buffer fseek(fp, 0, SEEK_END); len = ftell(fp); buffer = (char*)aMalloc(len + 1); fseek(fp, 0, SEEK_SET); len = fread(buffer, 1, len, fp); buffer[len] = '\0'; fclose(fp); // parse buffer for( i = prefix_len + 1; i + postfix_len <= len; ++i ) { if( buffer[i] != postfix[0] || memcmp(buffer + i, postfix, postfix_len) != 0 ) continue; // postfix missmatch for( j = i; j > 0; --j ) {// skip digits if( !ISDIGIT(buffer[j - 1]) ) break; } if( memcmp(buffer + j - prefix_len, prefix, prefix_len) != 0 ) continue; // prefix missmatch // done snprintf(svn_version_buffer, sizeof(svn_version_buffer), "%d", atoi(buffer + j)); break; } aFree(buffer); if( svn_version_buffer[0] != '\0' ) return svn_version_buffer; } // subversion 1.6 and older? if ((fp = fopen(".svn/entries", "r")) != NULL) { char line[1024]; int rev; // Check the version if (fgets(line, sizeof(line), fp)) { if(!ISDIGIT(line[0])) { // XML File format while (fgets(line,sizeof(line),fp)) if (strstr(line,"revision=")) break; if (sscanf(line," %*[^\"]\"%d%*[^\n]", &rev) == 1) { snprintf(svn_version_buffer, sizeof(svn_version_buffer), "%d", rev); } } else { // Bin File format if ( fgets(line, sizeof(line), fp) == NULL ) { printf("Can't get bin name\n"); } // Get the name if ( fgets(line, sizeof(line), fp) == NULL ) { printf("Can't get entries kind\n"); } // Get the entries kind if(fgets(line, sizeof(line), fp)) { // Get the rev numver snprintf(svn_version_buffer, sizeof(svn_version_buffer), "%d", atoi(line)); } } } fclose(fp); if( svn_version_buffer[0] != '\0' ) return svn_version_buffer; } // fallback svn_version_buffer[0] = HERC_UNKNOWN_VER; return svn_version_buffer; } #endif /* whats our origin */ #define GIT_ORIGIN "refs/remotes/origin/master" /* Grabs the hash from the last time the user updated his working copy (last pull) */ const char *get_git_hash (void) { static char HerculesGitHash[41] = "";//Sha(40) + 1 FILE *fp; if( HerculesGitHash[0] != '\0' ) return HerculesGitHash; if ( (fp = fopen (".git/"GIT_ORIGIN, "r")) != NULL) { char line[64]; char *rev = malloc (sizeof (char) * 50); if (fgets (line, sizeof (line), fp) && sscanf (line, "%50s", rev)) snprintf (HerculesGitHash, sizeof (HerculesGitHash), "%s", rev); free (rev); fclose (fp); } else { HerculesGitHash[0] = HERC_UNKNOWN_VER; } if (! (*HerculesGitHash)) { HerculesGitHash[0] = HERC_UNKNOWN_VER; } return HerculesGitHash; } // Warning if executed as superuser (root) void usercheck(void) { #ifndef _WIN32 if (geteuid() == 0) { ShowWarning ("You are running Hercules with root privileges, it is not necessary.\n"); } #endif } /*====================================== * CORE : MAINROUTINE *--------------------------------------*/ int main (int argc, char **argv) { {// initialize program arguments char *p1 = SERVER_NAME = argv[0]; char *p2 = p1; while ((p1 = strchr(p2, '/')) != NULL || (p1 = strchr(p2, '\\')) != NULL) { SERVER_NAME = ++p1; p2 = p1; } arg_c = argc; arg_v = argv; } console_defaults(); malloc_init();// needed for Show* in display_title() [FlavioJS] console->display_title(); #ifdef MINICORE // minimalist Core usercheck(); do_init(argc,argv); do_final(); #else// not MINICORE set_server_type(); usercheck(); Sql_Init(); rathread_init(); mempool_init(); db_init(); signals_init(); #ifdef _WIN32 cevents_init(); #endif timer_init(); console->init(); socket_init(); do_init(argc,argv); {// Main runtime cycle int next; while (runflag != CORE_ST_STOP) { next = do_timer(gettick_nocache()); do_sockets(next); } } console->final(); do_final(); timer_final(); socket_final(); db_final(); mempool_final(); rathread_final(); #endif malloc_final(); return 0; }