porting.cpp 15 KB

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