porting.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714
  1. /*
  2. Minetest
  3. Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU Lesser General Public License as published by
  6. the Free Software Foundation; either version 2.1 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public License along
  13. with this program; if not, write to the Free Software Foundation, Inc.,
  14. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  15. */
  16. /*
  17. Random portability stuff
  18. See comments in porting.h
  19. */
  20. #include "porting.h"
  21. #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
  22. #include <sys/types.h>
  23. #include <sys/sysctl.h>
  24. #elif defined(_WIN32)
  25. #include <windows.h>
  26. #include <wincrypt.h>
  27. #include <algorithm>
  28. #include <shlwapi.h>
  29. #endif
  30. #if !defined(_WIN32)
  31. #include <unistd.h>
  32. #include <sys/utsname.h>
  33. #endif
  34. #if defined(__hpux)
  35. #define _PSTAT64
  36. #include <sys/pstat.h>
  37. #endif
  38. #include "config.h"
  39. #include "debug.h"
  40. #include "filesys.h"
  41. #include "log.h"
  42. #include "util/string.h"
  43. #include "settings.h"
  44. #include <list>
  45. #include <cstdarg>
  46. #include <cstdio>
  47. namespace porting
  48. {
  49. /*
  50. Signal handler (grabs Ctrl-C on POSIX systems)
  51. */
  52. bool g_killed = false;
  53. bool *signal_handler_killstatus()
  54. {
  55. return &g_killed;
  56. }
  57. #if !defined(_WIN32) // POSIX
  58. #include <signal.h>
  59. void signal_handler(int sig)
  60. {
  61. if (!g_killed) {
  62. if (sig == SIGINT) {
  63. dstream << "INFO: signal_handler(): "
  64. << "Ctrl-C pressed, shutting down." << std::endl;
  65. } else if (sig == SIGTERM) {
  66. dstream << "INFO: signal_handler(): "
  67. << "got SIGTERM, shutting down." << std::endl;
  68. }
  69. // Comment out for less clutter when testing scripts
  70. /*dstream << "INFO: sigint_handler(): "
  71. << "Printing debug stacks" << std::endl;
  72. debug_stacks_print();*/
  73. g_killed = true;
  74. } else {
  75. (void)signal(sig, SIG_DFL);
  76. }
  77. }
  78. void signal_handler_init(void)
  79. {
  80. (void)signal(SIGINT, signal_handler);
  81. (void)signal(SIGTERM, signal_handler);
  82. }
  83. #else // _WIN32
  84. #include <signal.h>
  85. BOOL WINAPI event_handler(DWORD sig)
  86. {
  87. switch (sig) {
  88. case CTRL_C_EVENT:
  89. case CTRL_CLOSE_EVENT:
  90. case CTRL_LOGOFF_EVENT:
  91. case CTRL_SHUTDOWN_EVENT:
  92. if (!g_killed) {
  93. dstream << "INFO: event_handler(): "
  94. << "Ctrl+C, Close Event, Logoff Event or Shutdown Event,"
  95. " shutting down." << std::endl;
  96. g_killed = true;
  97. } else {
  98. (void)signal(SIGINT, SIG_DFL);
  99. }
  100. break;
  101. case CTRL_BREAK_EVENT:
  102. break;
  103. }
  104. return TRUE;
  105. }
  106. void signal_handler_init(void)
  107. {
  108. SetConsoleCtrlHandler((PHANDLER_ROUTINE)event_handler, TRUE);
  109. }
  110. #endif
  111. /*
  112. Path mangler
  113. */
  114. // Default to RUN_IN_PLACE style relative paths
  115. std::string path_share = "..";
  116. std::string path_user = "..";
  117. std::string path_locale = path_share + DIR_DELIM + "locale";
  118. std::string path_cache = path_user + DIR_DELIM + "cache";
  119. std::string getDataPath(const char *subpath)
  120. {
  121. return path_share + DIR_DELIM + subpath;
  122. }
  123. void pathRemoveFile(char *path, char delim)
  124. {
  125. // Remove filename and path delimiter
  126. int i;
  127. for(i = strlen(path)-1; i>=0; i--)
  128. {
  129. if(path[i] == delim)
  130. break;
  131. }
  132. path[i] = 0;
  133. }
  134. bool detectMSVCBuildDir(const std::string &path)
  135. {
  136. const char *ends[] = {
  137. "bin\\Release",
  138. "bin\\MinSizeRel",
  139. "bin\\RelWithDebInfo",
  140. "bin\\Debug",
  141. "bin\\Build",
  142. NULL
  143. };
  144. return (!removeStringEnd(path, ends).empty());
  145. }
  146. std::string get_sysinfo()
  147. {
  148. #ifdef _WIN32
  149. std::ostringstream oss;
  150. LPSTR filePath = new char[MAX_PATH];
  151. UINT blockSize;
  152. VS_FIXEDFILEINFO *fixedFileInfo;
  153. GetSystemDirectoryA(filePath, MAX_PATH);
  154. PathAppendA(filePath, "kernel32.dll");
  155. DWORD dwVersionSize = GetFileVersionInfoSizeA(filePath, NULL);
  156. LPBYTE lpVersionInfo = new BYTE[dwVersionSize];
  157. GetFileVersionInfoA(filePath, 0, dwVersionSize, lpVersionInfo);
  158. VerQueryValueA(lpVersionInfo, "\\", (LPVOID *)&fixedFileInfo, &blockSize);
  159. oss << "Windows/"
  160. << HIWORD(fixedFileInfo->dwProductVersionMS) << '.' // Major
  161. << LOWORD(fixedFileInfo->dwProductVersionMS) << '.' // Minor
  162. << HIWORD(fixedFileInfo->dwProductVersionLS) << ' '; // Build
  163. #ifdef _WIN64
  164. oss << "x86_64";
  165. #else
  166. BOOL is64 = FALSE;
  167. if (IsWow64Process(GetCurrentProcess(), &is64) && is64)
  168. oss << "x86_64"; // 32-bit app on 64-bit OS
  169. else
  170. oss << "x86";
  171. #endif
  172. delete[] lpVersionInfo;
  173. delete[] filePath;
  174. return oss.str();
  175. #else
  176. struct utsname osinfo;
  177. uname(&osinfo);
  178. return std::string(osinfo.sysname) + "/"
  179. + osinfo.release + " " + osinfo.machine;
  180. #endif
  181. }
  182. bool getCurrentWorkingDir(char *buf, size_t len)
  183. {
  184. #ifdef _WIN32
  185. DWORD ret = GetCurrentDirectory(len, buf);
  186. return (ret != 0) && (ret <= len);
  187. #else
  188. return getcwd(buf, len);
  189. #endif
  190. }
  191. bool getExecPathFromProcfs(char *buf, size_t buflen)
  192. {
  193. #ifndef _WIN32
  194. buflen--;
  195. ssize_t len;
  196. if ((len = readlink("/proc/self/exe", buf, buflen)) == -1 &&
  197. (len = readlink("/proc/curproc/file", buf, buflen)) == -1 &&
  198. (len = readlink("/proc/curproc/exe", buf, buflen)) == -1)
  199. return false;
  200. buf[len] = '\0';
  201. return true;
  202. #else
  203. return false;
  204. #endif
  205. }
  206. //// Windows
  207. #if defined(_WIN32)
  208. bool getCurrentExecPath(char *buf, size_t len)
  209. {
  210. DWORD written = GetModuleFileNameA(NULL, buf, len);
  211. if (written == 0 || written == len)
  212. return false;
  213. return true;
  214. }
  215. //// Linux
  216. #elif defined(__linux__)
  217. bool getCurrentExecPath(char *buf, size_t len)
  218. {
  219. if (!getExecPathFromProcfs(buf, len))
  220. return false;
  221. return true;
  222. }
  223. //// Mac OS X, Darwin
  224. #elif defined(__APPLE__)
  225. bool getCurrentExecPath(char *buf, size_t len)
  226. {
  227. uint32_t lenb = (uint32_t)len;
  228. if (_NSGetExecutablePath(buf, &lenb) == -1)
  229. return false;
  230. return true;
  231. }
  232. //// FreeBSD, NetBSD, DragonFlyBSD
  233. #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
  234. bool getCurrentExecPath(char *buf, size_t len)
  235. {
  236. // Try getting path from procfs first, since valgrind
  237. // doesn't work with the latter
  238. if (getExecPathFromProcfs(buf, len))
  239. return true;
  240. int mib[4];
  241. mib[0] = CTL_KERN;
  242. mib[1] = KERN_PROC;
  243. mib[2] = KERN_PROC_PATHNAME;
  244. mib[3] = -1;
  245. if (sysctl(mib, 4, buf, &len, NULL, 0) == -1)
  246. return false;
  247. return true;
  248. }
  249. //// Solaris
  250. #elif defined(__sun) || defined(sun)
  251. bool getCurrentExecPath(char *buf, size_t len)
  252. {
  253. const char *exec = getexecname();
  254. if (exec == NULL)
  255. return false;
  256. if (strlcpy(buf, exec, len) >= len)
  257. return false;
  258. return true;
  259. }
  260. // HP-UX
  261. #elif defined(__hpux)
  262. bool getCurrentExecPath(char *buf, size_t len)
  263. {
  264. struct pst_status psts;
  265. if (pstat_getproc(&psts, sizeof(psts), 0, getpid()) == -1)
  266. return false;
  267. if (pstat_getpathname(buf, len, &psts.pst_fid_text) == -1)
  268. return false;
  269. return true;
  270. }
  271. #else
  272. bool getCurrentExecPath(char *buf, size_t len)
  273. {
  274. return false;
  275. }
  276. #endif
  277. //// Non-Windows
  278. #if !defined(_WIN32)
  279. const char *getHomeOrFail()
  280. {
  281. const char *home = getenv("HOME");
  282. // In rare cases the HOME environment variable may be unset
  283. FATAL_ERROR_IF(!home,
  284. "Required environment variable HOME is not set");
  285. return home;
  286. }
  287. #endif
  288. //// Windows
  289. #if defined(_WIN32)
  290. bool setSystemPaths()
  291. {
  292. char buf[BUFSIZ];
  293. // Find path of executable and set path_share relative to it
  294. FATAL_ERROR_IF(!getCurrentExecPath(buf, sizeof(buf)),
  295. "Failed to get current executable path");
  296. pathRemoveFile(buf, '\\');
  297. std::string exepath(buf);
  298. // Use ".\bin\.."
  299. path_share = exepath + "\\..";
  300. if (detectMSVCBuildDir(exepath)) {
  301. // The msvc build dir schould normaly not be present if properly installed,
  302. // but its usefull for debugging.
  303. path_share += DIR_DELIM "..";
  304. }
  305. // Use "C:\Users\<user>\AppData\Roaming\<PROJECT_NAME_C>"
  306. DWORD len = GetEnvironmentVariable("APPDATA", buf, sizeof(buf));
  307. FATAL_ERROR_IF(len == 0 || len > sizeof(buf), "Failed to get APPDATA");
  308. path_user = std::string(buf) + DIR_DELIM + PROJECT_NAME_C;
  309. return true;
  310. }
  311. //// Linux
  312. #elif defined(__linux__)
  313. bool setSystemPaths()
  314. {
  315. char buf[BUFSIZ];
  316. if (!getCurrentExecPath(buf, sizeof(buf))) {
  317. #ifdef __ANDROID__
  318. errorstream << "Unable to read bindir "<< std::endl;
  319. #else
  320. FATAL_ERROR("Unable to read bindir");
  321. #endif
  322. return false;
  323. }
  324. pathRemoveFile(buf, '/');
  325. std::string bindir(buf);
  326. // Find share directory from these.
  327. // It is identified by containing the subdirectory "builtin".
  328. std::list<std::string> trylist;
  329. std::string static_sharedir = STATIC_SHAREDIR;
  330. if (!static_sharedir.empty() && static_sharedir != ".")
  331. trylist.push_back(static_sharedir);
  332. trylist.push_back(bindir + DIR_DELIM ".." DIR_DELIM "share"
  333. DIR_DELIM + PROJECT_NAME);
  334. trylist.push_back(bindir + DIR_DELIM "..");
  335. #ifdef __ANDROID__
  336. trylist.push_back(path_user);
  337. #endif
  338. for (std::list<std::string>::const_iterator
  339. i = trylist.begin(); i != trylist.end(); ++i) {
  340. const std::string &trypath = *i;
  341. if (!fs::PathExists(trypath) ||
  342. !fs::PathExists(trypath + DIR_DELIM + "builtin")) {
  343. warningstream << "system-wide share not found at \""
  344. << trypath << "\""<< std::endl;
  345. continue;
  346. }
  347. // Warn if was not the first alternative
  348. if (i != trylist.begin()) {
  349. warningstream << "system-wide share found at \""
  350. << trypath << "\"" << std::endl;
  351. }
  352. path_share = trypath;
  353. break;
  354. }
  355. #ifndef __ANDROID__
  356. path_user = std::string(getHomeOrFail()) + DIR_DELIM "."
  357. + PROJECT_NAME;
  358. #endif
  359. return true;
  360. }
  361. //// Mac OS X
  362. #elif defined(__APPLE__)
  363. bool setSystemPaths()
  364. {
  365. CFBundleRef main_bundle = CFBundleGetMainBundle();
  366. CFURLRef resources_url = CFBundleCopyResourcesDirectoryURL(main_bundle);
  367. char path[PATH_MAX];
  368. if (CFURLGetFileSystemRepresentation(resources_url,
  369. TRUE, (UInt8 *)path, PATH_MAX)) {
  370. path_share = std::string(path);
  371. } else {
  372. warningstream << "Could not determine bundle resource path" << std::endl;
  373. }
  374. CFRelease(resources_url);
  375. path_user = std::string(getHomeOrFail())
  376. + "/Library/Application Support/"
  377. + PROJECT_NAME;
  378. return true;
  379. }
  380. #else
  381. bool setSystemPaths()
  382. {
  383. path_share = STATIC_SHAREDIR;
  384. path_user = std::string(getHomeOrFail()) + DIR_DELIM "."
  385. + lowercase(PROJECT_NAME);
  386. return true;
  387. }
  388. #endif
  389. void migrateCachePath()
  390. {
  391. const std::string local_cache_path = path_user + DIR_DELIM + "cache";
  392. // Delete tmp folder if it exists (it only ever contained
  393. // a temporary ogg file, which is no longer used).
  394. if (fs::PathExists(local_cache_path + DIR_DELIM + "tmp"))
  395. fs::RecursiveDelete(local_cache_path + DIR_DELIM + "tmp");
  396. // Bail if migration impossible
  397. if (path_cache == local_cache_path || !fs::PathExists(local_cache_path)
  398. || fs::PathExists(path_cache)) {
  399. return;
  400. }
  401. if (!fs::Rename(local_cache_path, path_cache)) {
  402. errorstream << "Failed to migrate local cache path "
  403. "to system path!" << std::endl;
  404. }
  405. }
  406. void initializePaths()
  407. {
  408. #if RUN_IN_PLACE
  409. char buf[BUFSIZ];
  410. infostream << "Using relative paths (RUN_IN_PLACE)" << std::endl;
  411. bool success =
  412. getCurrentExecPath(buf, sizeof(buf)) ||
  413. getExecPathFromProcfs(buf, sizeof(buf));
  414. if (success) {
  415. pathRemoveFile(buf, DIR_DELIM_CHAR);
  416. std::string execpath(buf);
  417. path_share = execpath + DIR_DELIM "..";
  418. path_user = execpath + DIR_DELIM "..";
  419. if (detectMSVCBuildDir(execpath)) {
  420. path_share += DIR_DELIM "..";
  421. path_user += DIR_DELIM "..";
  422. }
  423. } else {
  424. errorstream << "Failed to get paths by executable location, "
  425. "trying cwd" << std::endl;
  426. if (!getCurrentWorkingDir(buf, sizeof(buf)))
  427. FATAL_ERROR("Ran out of methods to get paths");
  428. size_t cwdlen = strlen(buf);
  429. if (cwdlen >= 1 && buf[cwdlen - 1] == DIR_DELIM_CHAR) {
  430. cwdlen--;
  431. buf[cwdlen] = '\0';
  432. }
  433. if (cwdlen >= 4 && !strcmp(buf + cwdlen - 4, DIR_DELIM "bin"))
  434. pathRemoveFile(buf, DIR_DELIM_CHAR);
  435. std::string execpath(buf);
  436. path_share = execpath;
  437. path_user = execpath;
  438. }
  439. path_cache = path_user + DIR_DELIM + "cache";
  440. #else
  441. infostream << "Using system-wide paths (NOT RUN_IN_PLACE)" << std::endl;
  442. if (!setSystemPaths())
  443. errorstream << "Failed to get one or more system-wide path" << std::endl;
  444. # ifdef _WIN32
  445. path_cache = path_user + DIR_DELIM + "cache";
  446. # else
  447. // Initialize path_cache
  448. // First try $XDG_CACHE_HOME/PROJECT_NAME
  449. const char *cache_dir = getenv("XDG_CACHE_HOME");
  450. const char *home_dir = getenv("HOME");
  451. if (cache_dir) {
  452. path_cache = std::string(cache_dir) + DIR_DELIM + PROJECT_NAME;
  453. } else if (home_dir) {
  454. // Then try $HOME/.cache/PROJECT_NAME
  455. path_cache = std::string(home_dir) + DIR_DELIM + ".cache"
  456. + DIR_DELIM + PROJECT_NAME;
  457. } else {
  458. // If neither works, use $PATH_USER/cache
  459. path_cache = path_user + DIR_DELIM + "cache";
  460. }
  461. // Migrate cache folder to new location if possible
  462. migrateCachePath();
  463. # endif // _WIN32
  464. #endif // RUN_IN_PLACE
  465. infostream << "Detected share path: " << path_share << std::endl;
  466. infostream << "Detected user path: " << path_user << std::endl;
  467. infostream << "Detected cache path: " << path_cache << std::endl;
  468. #if USE_GETTEXT
  469. bool found_localedir = false;
  470. # ifdef STATIC_LOCALEDIR
  471. if (STATIC_LOCALEDIR[0] && fs::PathExists(STATIC_LOCALEDIR)) {
  472. found_localedir = true;
  473. path_locale = STATIC_LOCALEDIR;
  474. infostream << "Using locale directory " << STATIC_LOCALEDIR << std::endl;
  475. } else {
  476. path_locale = getDataPath("locale");
  477. if (fs::PathExists(path_locale)) {
  478. found_localedir = true;
  479. infostream << "Using in-place locale directory " << path_locale
  480. << " even though a static one was provided "
  481. << "(RUN_IN_PLACE or CUSTOM_LOCALEDIR)." << std::endl;
  482. }
  483. }
  484. # else
  485. path_locale = getDataPath("locale");
  486. if (fs::PathExists(path_locale)) {
  487. found_localedir = true;
  488. }
  489. # endif
  490. if (!found_localedir) {
  491. warningstream << "Couldn't find a locale directory!" << std::endl;
  492. }
  493. #endif // USE_GETTEXT
  494. }
  495. ////
  496. //// OS-specific Secure Random
  497. ////
  498. #ifdef WIN32
  499. bool secure_rand_fill_buf(void *buf, size_t len)
  500. {
  501. HCRYPTPROV wctx;
  502. if (!CryptAcquireContext(&wctx, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
  503. return false;
  504. CryptGenRandom(wctx, len, (BYTE *)buf);
  505. CryptReleaseContext(wctx, 0);
  506. return true;
  507. }
  508. #else
  509. bool secure_rand_fill_buf(void *buf, size_t len)
  510. {
  511. // N.B. This function checks *only* for /dev/urandom, because on most
  512. // common OSes it is non-blocking, whereas /dev/random is blocking, and it
  513. // is exceptionally uncommon for there to be a situation where /dev/random
  514. // exists but /dev/urandom does not. This guesswork is necessary since
  515. // random devices are not covered by any POSIX standard...
  516. FILE *fp = fopen("/dev/urandom", "rb");
  517. if (!fp)
  518. return false;
  519. bool success = fread(buf, len, 1, fp) == 1;
  520. fclose(fp);
  521. return success;
  522. }
  523. #endif
  524. void attachOrCreateConsole()
  525. {
  526. #ifdef _WIN32
  527. static bool consoleAllocated = false;
  528. const bool redirected = (_fileno(stdout) == -2 || _fileno(stdout) == -1); // If output is redirected to e.g a file
  529. if (!consoleAllocated && redirected && (AttachConsole(ATTACH_PARENT_PROCESS) || AllocConsole())) {
  530. freopen("CONOUT$", "w", stdout);
  531. freopen("CONOUT$", "w", stderr);
  532. consoleAllocated = true;
  533. }
  534. #endif
  535. }
  536. int mt_snprintf(char *buf, const size_t buf_size, const char *fmt, ...)
  537. {
  538. // https://msdn.microsoft.com/en-us/library/bt7tawza.aspx
  539. // Many of the MSVC / Windows printf-style functions do not support positional
  540. // arguments (eg. "%1$s"). We just forward the call to vsnprintf for sane
  541. // platforms, but defer to _vsprintf_p on MSVC / Windows.
  542. // https://github.com/FFmpeg/FFmpeg/blob/5ae9fa13f5ac640bec113120d540f70971aa635d/compat/msvcrt/snprintf.c#L46
  543. // _vsprintf_p has to be shimmed with _vscprintf_p on -1 (for an example see
  544. // above FFmpeg link).
  545. va_list args;
  546. va_start(args, fmt);
  547. #ifndef _MSC_VER
  548. int c = vsnprintf(buf, buf_size, fmt, args);
  549. #else // _MSC_VER
  550. int c = _vsprintf_p(buf, buf_size, fmt, args);
  551. if (c == -1)
  552. c = _vscprintf_p(fmt, args);
  553. #endif // _MSC_VER
  554. va_end(args);
  555. return c;
  556. }
  557. // Load performance counter frequency only once at startup
  558. #ifdef _WIN32
  559. inline double get_perf_freq()
  560. {
  561. LARGE_INTEGER freq;
  562. QueryPerformanceFrequency(&freq);
  563. return freq.QuadPart;
  564. }
  565. double perf_freq = get_perf_freq();
  566. #endif
  567. } //namespace porting