core.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. // Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
  2. // For more information, see LICENCE in the main folder
  3. #include "core.hpp"
  4. #include <config/core.hpp>
  5. #ifndef MINICORE
  6. #include "database.hpp"
  7. #include "ers.hpp"
  8. #include "socket.hpp"
  9. #include "timer.hpp"
  10. #include "sql.hpp"
  11. #endif
  12. #include <stdlib.h>
  13. #include <signal.h>
  14. #ifndef _WIN32
  15. #include <unistd.h>
  16. #else
  17. #include "winapi.hpp" // Console close event handling
  18. #include <direct.h> // _chdir
  19. #endif
  20. #include "cbasetypes.hpp"
  21. #include "malloc.hpp"
  22. #include "mmo.hpp"
  23. #include "showmsg.hpp"
  24. #include "strlib.hpp"
  25. #ifndef DEPRECATED_COMPILER_SUPPORT
  26. #if defined( _MSC_VER ) && _MSC_VER < 1910
  27. #error "Visual Studio versions older than Visual Studio 2017 are not officially supported anymore"
  28. #elif defined( __clang__ ) && __clang_major__ < 6
  29. #error "clang versions older than clang 6.0 are not officially supported anymore"
  30. #elif !defined( __clang__ ) && defined( __GNUC__ ) && __GNUC__ < 5
  31. #error "GCC versions older than GCC 5 are not officially supported anymore"
  32. #endif
  33. #endif
  34. using namespace rathena::server_core;
  35. Core* global_core = nullptr;
  36. #if defined(BUILDBOT)
  37. int buildbotflag = 0;
  38. #endif
  39. char db_path[12] = "db"; /// relative path for db from server
  40. char conf_path[12] = "conf"; /// relative path for conf from server
  41. char *SERVER_NAME = NULL;
  42. #ifndef MINICORE // minimalist Core
  43. // Added by Gabuzomeu
  44. //
  45. // This is an implementation of signal() using sigaction() for portability.
  46. // (sigaction() is POSIX; signal() is not.) Taken from Stevens' _Advanced
  47. // Programming in the UNIX Environment_.
  48. //
  49. #ifdef WIN32 // windows don't have SIGPIPE
  50. #define SIGPIPE SIGINT
  51. #endif
  52. #ifndef POSIX
  53. #define compat_signal(signo, func) signal(signo, func)
  54. #else
  55. sigfunc *compat_signal(int signo, sigfunc *func) {
  56. struct sigaction sact, oact;
  57. sact.sa_handler = func;
  58. sigemptyset(&sact.sa_mask);
  59. sact.sa_flags = 0;
  60. #ifdef SA_INTERRUPT
  61. sact.sa_flags |= SA_INTERRUPT; /* SunOS */
  62. #endif
  63. if (sigaction(signo, &sact, &oact) < 0)
  64. return (SIG_ERR);
  65. return (oact.sa_handler);
  66. }
  67. #endif
  68. /*======================================
  69. * CORE : Console events for Windows
  70. *--------------------------------------*/
  71. #ifdef _WIN32
  72. static BOOL WINAPI console_handler(DWORD c_event) {
  73. switch(c_event) {
  74. case CTRL_CLOSE_EVENT:
  75. case CTRL_LOGOFF_EVENT:
  76. case CTRL_SHUTDOWN_EVENT:
  77. if( global_core != nullptr ){
  78. global_core->signal_shutdown();
  79. }
  80. break;
  81. default:
  82. return FALSE;
  83. }
  84. return TRUE;
  85. }
  86. static void cevents_init() {
  87. if (SetConsoleCtrlHandler(console_handler,TRUE)==FALSE)
  88. ShowWarning ("Unable to install the console handler!\n");
  89. }
  90. #endif
  91. /*======================================
  92. * CORE : Signal Sub Function
  93. *--------------------------------------*/
  94. static void sig_proc(int sn) {
  95. static int is_called = 0;
  96. switch (sn) {
  97. case SIGINT:
  98. case SIGTERM:
  99. if (++is_called > 3)
  100. exit(EXIT_SUCCESS);
  101. if( global_core != nullptr ){
  102. global_core->signal_shutdown();
  103. }
  104. break;
  105. case SIGSEGV:
  106. case SIGFPE:
  107. if( global_core != nullptr ){
  108. global_core->signal_crash();
  109. }
  110. // Pass the signal to the system's default handler
  111. compat_signal(sn, SIG_DFL);
  112. raise(sn);
  113. break;
  114. #ifndef _WIN32
  115. case SIGXFSZ:
  116. // ignore and allow it to set errno to EFBIG
  117. ShowWarning ("Max file size reached!\n");
  118. //run_flag = 0; // should we quit?
  119. break;
  120. case SIGPIPE:
  121. //ShowInfo ("Broken pipe found... closing socket\n"); // set to eof in socket.cpp
  122. break; // does nothing here
  123. #endif
  124. }
  125. }
  126. void signals_init (void) {
  127. compat_signal(SIGTERM, sig_proc);
  128. compat_signal(SIGINT, sig_proc);
  129. #ifndef _DEBUG // need unhandled exceptions to debug on Windows
  130. compat_signal(SIGSEGV, sig_proc);
  131. compat_signal(SIGFPE, sig_proc);
  132. #endif
  133. #ifndef _WIN32
  134. compat_signal(SIGILL, SIG_DFL);
  135. compat_signal(SIGXFSZ, sig_proc);
  136. compat_signal(SIGPIPE, sig_proc);
  137. compat_signal(SIGBUS, SIG_DFL);
  138. compat_signal(SIGTRAP, SIG_DFL);
  139. #endif
  140. }
  141. #endif
  142. const char* get_svn_revision(void) {
  143. #ifdef SVNVERSION
  144. return EXPAND_AND_QUOTE(SVNVERSION);
  145. #else// not SVNVERSION
  146. static char svn_version_buffer[16] = "";
  147. FILE *fp;
  148. if( svn_version_buffer[0] != '\0' )
  149. return svn_version_buffer;
  150. // subversion 1.7 uses a sqlite3 database
  151. // FIXME this is hackish at best...
  152. // - ignores database file structure
  153. // - assumes the data in NODES.dav_cache column ends with "!svn/ver/<revision>/<path>)"
  154. // - since it's a cache column, the data might not even exist
  155. if( (fp = fopen(".svn" PATHSEP_STR "wc.db", "rb")) != NULL || (fp = fopen(".." PATHSEP_STR ".svn" PATHSEP_STR "wc.db", "rb")) != NULL )
  156. {
  157. #ifndef SVNNODEPATH
  158. //not sure how to handle branches, so i'll leave this overridable define until a better solution comes up
  159. #define SVNNODEPATH trunk
  160. #endif
  161. const char* prefix = "!svn/ver/";
  162. const char* postfix = "/" EXPAND_AND_QUOTE(SVNNODEPATH) ")"; // there should exist only 1 entry like this
  163. size_t prefix_len = strlen(prefix);
  164. size_t postfix_len = strlen(postfix);
  165. size_t i,j,len;
  166. char* buffer;
  167. // read file to buffer
  168. fseek(fp, 0, SEEK_END);
  169. len = ftell(fp);
  170. buffer = (char*)aMalloc(len + 1);
  171. fseek(fp, 0, SEEK_SET);
  172. len = fread(buffer, 1, len, fp);
  173. buffer[len] = '\0';
  174. fclose(fp);
  175. // parse buffer
  176. for( i = prefix_len + 1; i + postfix_len <= len; ++i ) {
  177. if( buffer[i] != postfix[0] || memcmp(buffer + i, postfix, postfix_len) != 0 )
  178. continue; // postfix missmatch
  179. for( j = i; j > 0; --j ) {// skip digits
  180. if( !ISDIGIT(buffer[j - 1]) )
  181. break;
  182. }
  183. if( memcmp(buffer + j - prefix_len, prefix, prefix_len) != 0 )
  184. continue; // prefix missmatch
  185. // done
  186. snprintf(svn_version_buffer, sizeof(svn_version_buffer), "%d", atoi(buffer + j));
  187. break;
  188. }
  189. aFree(buffer);
  190. if( svn_version_buffer[0] != '\0' )
  191. return svn_version_buffer;
  192. }
  193. // subversion 1.6 and older?
  194. if ((fp = fopen(".svn/entries", "r")) != NULL)
  195. {
  196. char line[1024];
  197. int rev;
  198. // Check the version
  199. if (fgets(line, sizeof(line), fp))
  200. {
  201. if(!ISDIGIT(line[0]))
  202. {
  203. // XML File format
  204. while (fgets(line,sizeof(line),fp))
  205. if (strstr(line,"revision=")) break;
  206. if (sscanf(line," %*[^\"]\"%11d%*[^\n]", &rev) == 1) {
  207. snprintf(svn_version_buffer, sizeof(svn_version_buffer), "%d", rev);
  208. }
  209. }
  210. else
  211. {
  212. // Bin File format
  213. if ( fgets(line, sizeof(line), fp) == NULL ) { printf("Can't get bin name\n"); } // Get the name
  214. if ( fgets(line, sizeof(line), fp) == NULL ) { printf("Can't get entries kind\n"); } // Get the entries kind
  215. if(fgets(line, sizeof(line), fp)) // Get the rev numver
  216. {
  217. snprintf(svn_version_buffer, sizeof(svn_version_buffer), "%d", atoi(line));
  218. }
  219. }
  220. }
  221. fclose(fp);
  222. if( svn_version_buffer[0] != '\0' )
  223. return svn_version_buffer;
  224. }
  225. // fallback
  226. svn_version_buffer[0] = UNKNOWN_VERSION;
  227. return svn_version_buffer;
  228. #endif
  229. }
  230. // Grabs the hash from the last time the user updated their working copy (last pull)
  231. const char *get_git_hash (void) {
  232. static char GitHash[41] = ""; //Sha(40) + 1
  233. FILE *fp;
  234. if( GitHash[0] != '\0' )
  235. return GitHash;
  236. if( (fp = fopen(".git/refs/remotes/origin/master", "r")) != NULL || // Already pulled once
  237. (fp = fopen(".git/refs/heads/master", "r")) != NULL ) { // Cloned only
  238. char line[64];
  239. char *rev = (char*)malloc(sizeof(char) * 50);
  240. if( fgets(line, sizeof(line), fp) && sscanf(line, "%40s", rev) )
  241. snprintf(GitHash, sizeof(GitHash), "%s", rev);
  242. free(rev);
  243. fclose(fp);
  244. } else {
  245. GitHash[0] = UNKNOWN_VERSION;
  246. }
  247. if ( !(*GitHash) ) {
  248. GitHash[0] = UNKNOWN_VERSION;
  249. }
  250. return GitHash;
  251. }
  252. /*======================================
  253. * CORE : Display title
  254. * ASCII By CalciumKid 1/12/2011
  255. *--------------------------------------*/
  256. static void display_title(void) {
  257. const char* svn = get_svn_revision();
  258. const char* git = get_git_hash();
  259. ShowMessage("\n");
  260. ShowMessage("" CL_PASS " " CL_BOLD " " CL_PASS"" CL_CLL "" CL_NORMAL "\n");
  261. ShowMessage("" CL_PASS " " CL_BT_WHITE " rAthena Development Team presents " CL_PASS "" CL_CLL "" CL_NORMAL "\n");
  262. ShowMessage("" CL_PASS " " CL_BOLD " ___ __ __ " CL_PASS "" CL_CLL "" CL_NORMAL "\n");
  263. ShowMessage("" CL_PASS " " CL_BOLD " _____/ | / /_/ /_ ___ ____ ____ _ " CL_PASS "" CL_CLL "" CL_NORMAL "\n");
  264. ShowMessage("" CL_PASS " " CL_BOLD " / ___/ /| |/ __/ __ \\/ _ \\/ __ \\/ __ `/ " CL_PASS "" CL_CLL "" CL_NORMAL "\n");
  265. ShowMessage("" CL_PASS " " CL_BOLD " / / / ___ / /_/ / / / __/ / / / /_/ / " CL_PASS "" CL_CLL "" CL_NORMAL "\n");
  266. ShowMessage("" CL_PASS " " CL_BOLD " /_/ /_/ |_\\__/_/ /_/\\___/_/ /_/\\__,_/ " CL_PASS "" CL_CLL "" CL_NORMAL "\n");
  267. ShowMessage("" CL_PASS " " CL_BOLD " " CL_PASS "" CL_CLL "" CL_NORMAL "\n");
  268. ShowMessage("" CL_PASS " " CL_GREEN " http://rathena.org/board/ " CL_PASS "" CL_CLL "" CL_NORMAL "\n");
  269. ShowMessage("" CL_PASS " " CL_BOLD " " CL_PASS "" CL_CLL "" CL_NORMAL "\n");
  270. if( svn[0] != UNKNOWN_VERSION )
  271. ShowInfo("SVN Revision: '" CL_WHITE "%s" CL_RESET "'\n", svn);
  272. else if( git[0] != UNKNOWN_VERSION )
  273. ShowInfo("Git Hash: '" CL_WHITE "%s" CL_RESET "'\n", git);
  274. }
  275. // Warning if executed as superuser (root)
  276. void usercheck(void)
  277. {
  278. #if !defined(BUILDBOT)
  279. #ifdef _WIN32
  280. if (IsCurrentUserLocalAdministrator()) {
  281. ShowWarning("You are running rAthena with admin privileges, it is not necessary.\n");
  282. }
  283. #else
  284. if (geteuid() == 0) {
  285. ShowWarning ("You are running rAthena with root privileges, it is not necessary.\n");
  286. }
  287. #endif
  288. #endif
  289. }
  290. int Core::start( int argc, char **argv ){
  291. if( this->get_status() != e_core_status::NOT_STARTED) {
  292. ShowFatalError( "Core was already started and cannot be started again!\n" );
  293. return EXIT_FAILURE;
  294. }
  295. this->set_status( e_core_status::CORE_INITIALIZING );
  296. {// initialize program arguments
  297. char *p1;
  298. if((p1 = strrchr(argv[0], '/')) != NULL || (p1 = strrchr(argv[0], '\\')) != NULL ){
  299. char *pwd = NULL; //path working directory
  300. int n=0;
  301. SERVER_NAME = ++p1;
  302. n = p1-argv[0]; //calc dir name len
  303. pwd = safestrncpy((char*)malloc(n + 1), argv[0], n);
  304. if(chdir(pwd) != 0)
  305. ShowError("Couldn't change working directory to %s for %s, runtime will probably fail",pwd,SERVER_NAME);
  306. free(pwd);
  307. }else{
  308. // On Windows the .bat files have the executeable names as parameters without any path seperator [Lemongrass]
  309. SERVER_NAME = argv[0];
  310. }
  311. }
  312. malloc_init();// needed for Show* in display_title() [FlavioJS]
  313. display_title();
  314. usercheck();
  315. #ifndef MINICORE
  316. Sql_Init();
  317. db_init();
  318. signals_init();
  319. do_init_database();
  320. #ifdef _WIN32
  321. cevents_init();
  322. #endif
  323. timer_init();
  324. socket_init();
  325. #endif
  326. this->set_status( e_core_status::CORE_INITIALIZED );
  327. this->set_status( e_core_status::SERVER_INITIALIZING );
  328. if( !this->initialize( argc, argv ) ){
  329. return EXIT_FAILURE;
  330. }
  331. // If initialization did not trigger shutdown
  332. if( this->m_status != e_core_status::STOPPING ){
  333. this->set_status( e_core_status::SERVER_INITIALIZED );
  334. this->set_status( e_core_status::RUNNING );
  335. #ifndef MINICORE
  336. if( !this->m_run_once ){
  337. // Main runtime cycle
  338. while( this->get_status() == e_core_status::RUNNING ){
  339. t_tick next = do_timer( gettick_nocache() );
  340. this->handle_main( next );
  341. }
  342. }
  343. #endif
  344. this->set_status( e_core_status::STOPPING );
  345. }
  346. this->set_status( e_core_status::SERVER_FINALIZING );
  347. this->finalize();
  348. this->set_status( e_core_status::SERVER_FINALIZED );
  349. this->set_status( e_core_status::CORE_FINALIZING );
  350. #ifndef MINICORE
  351. timer_final();
  352. socket_final();
  353. db_final();
  354. ers_final();
  355. #endif
  356. malloc_final();
  357. this->set_status( e_core_status::CORE_FINALIZED );
  358. #if defined(BUILDBOT)
  359. if( buildbotflag ){
  360. exit(EXIT_FAILURE);
  361. }
  362. #endif
  363. this->set_status( e_core_status::STOPPED );
  364. return EXIT_SUCCESS;
  365. }
  366. bool Core::initialize( int argc, char* argv[] ){
  367. // Do nothing
  368. return true;
  369. }
  370. void Core::handle_main( t_tick next ){
  371. #ifndef MINICORE
  372. // By default we handle all socket packets
  373. do_sockets( next );
  374. #endif
  375. }
  376. void Core::handle_crash(){
  377. // Do nothing
  378. }
  379. void Core::handle_shutdown(){
  380. // Do nothing
  381. }
  382. void Core::finalize(){
  383. // Do nothing
  384. }
  385. void Core::set_status( e_core_status status ){
  386. this->m_status = status;
  387. }
  388. e_core_status Core::get_status(){
  389. return this->m_status;
  390. }
  391. e_core_type Core::get_type(){
  392. return this->m_type;
  393. }
  394. bool Core::is_running(){
  395. return this->get_status() == e_core_status::RUNNING;
  396. }
  397. void Core::set_run_once( bool run_once ){
  398. this->m_run_once = run_once;
  399. }
  400. void Core::signal_crash(){
  401. this->set_status( e_core_status::STOPPING );
  402. if( this->m_crashed ){
  403. ShowFatalError( "Received another crash signal, while trying to handle the last crash!\n" );
  404. }else{
  405. ShowFatalError( "Received a crash signal, trying to handle it as good as possible!\n" );
  406. this->m_crashed = true;
  407. this->handle_crash();
  408. }
  409. // Now stop the process
  410. exit( EXIT_FAILURE );
  411. }
  412. void Core::signal_shutdown(){
  413. this->set_status( e_core_status::STOPPING );
  414. this->handle_shutdown();
  415. }