porting.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891
  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__) || defined(__OpenBSD__)
  22. #include <sys/types.h>
  23. #include <sys/sysctl.h>
  24. extern char **environ;
  25. #elif defined(_WIN32)
  26. #include <windows.h>
  27. #include <wincrypt.h>
  28. #include <algorithm>
  29. #include <shlwapi.h>
  30. #include <shellapi.h>
  31. #include <mmsystem.h>
  32. #endif
  33. #if !defined(_WIN32)
  34. #include <unistd.h>
  35. #include <sys/utsname.h>
  36. #if !defined(__ANDROID__)
  37. #include <spawn.h>
  38. #endif
  39. #endif
  40. #if defined(__hpux)
  41. #define _PSTAT64
  42. #include <sys/pstat.h>
  43. #endif
  44. #if defined(__ANDROID__)
  45. #include "porting_android.h"
  46. #endif
  47. #if defined(__APPLE__)
  48. #include <mach-o/dyld.h>
  49. #include <CoreFoundation/CoreFoundation.h>
  50. // For _NSGetEnviron()
  51. // Related: https://gitlab.haskell.org/ghc/ghc/issues/2458
  52. #include <crt_externs.h>
  53. #endif
  54. #if defined(__HAIKU__)
  55. #include <FindDirectory.h>
  56. #endif
  57. #include "config.h"
  58. #include "debug.h"
  59. #include "filesys.h"
  60. #include "log.h"
  61. #include "util/string.h"
  62. #include <vector>
  63. #include <cstdarg>
  64. #include <cstdio>
  65. #include <signal.h>
  66. #if !defined(SERVER) && defined(_WIN32)
  67. // On Windows export some driver-specific variables to encourage Minetest to be
  68. // executed on the discrete GPU in case of systems with two. Portability is fun.
  69. extern "C" {
  70. __declspec(dllexport) DWORD NvOptimusEnablement = 1;
  71. __declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 1;
  72. }
  73. #endif
  74. namespace porting
  75. {
  76. /*
  77. Signal handler (grabs Ctrl-C on POSIX systems)
  78. */
  79. static bool g_killed = false;
  80. bool *signal_handler_killstatus()
  81. {
  82. return &g_killed;
  83. }
  84. #if !defined(_WIN32) // POSIX
  85. static void signal_handler(int sig)
  86. {
  87. if (!g_killed) {
  88. if (sig == SIGINT) {
  89. dstream << "INFO: signal_handler(): "
  90. << "Ctrl-C pressed, shutting down." << std::endl;
  91. } else if (sig == SIGTERM) {
  92. dstream << "INFO: signal_handler(): "
  93. << "got SIGTERM, shutting down." << std::endl;
  94. }
  95. // Comment out for less clutter when testing scripts
  96. /*dstream << "INFO: sigint_handler(): "
  97. << "Printing debug stacks" << std::endl;
  98. debug_stacks_print();*/
  99. g_killed = true;
  100. } else {
  101. (void)signal(sig, SIG_DFL);
  102. }
  103. }
  104. void signal_handler_init(void)
  105. {
  106. (void)signal(SIGINT, signal_handler);
  107. (void)signal(SIGTERM, signal_handler);
  108. }
  109. #else // _WIN32
  110. static BOOL WINAPI event_handler(DWORD sig)
  111. {
  112. switch (sig) {
  113. case CTRL_C_EVENT:
  114. case CTRL_CLOSE_EVENT:
  115. case CTRL_LOGOFF_EVENT:
  116. case CTRL_SHUTDOWN_EVENT:
  117. if (!g_killed) {
  118. dstream << "INFO: event_handler(): "
  119. << "Ctrl+C, Close Event, Logoff Event or Shutdown Event,"
  120. " shutting down." << std::endl;
  121. g_killed = true;
  122. } else {
  123. (void)signal(SIGINT, SIG_DFL);
  124. }
  125. break;
  126. case CTRL_BREAK_EVENT:
  127. break;
  128. }
  129. return TRUE;
  130. }
  131. void signal_handler_init(void)
  132. {
  133. SetConsoleCtrlHandler((PHANDLER_ROUTINE)event_handler, TRUE);
  134. }
  135. #endif
  136. /*
  137. Path mangler
  138. */
  139. // Nobody should be reading these before initializePaths() is called
  140. std::string path_share = "UNINITIALIZED";
  141. std::string path_user = "UNINITIALIZED";
  142. std::string path_locale = "UNINITIALIZED";
  143. std::string path_cache = "UNINITIALIZED";
  144. std::string getDataPath(const char *subpath)
  145. {
  146. return path_share + DIR_DELIM + subpath;
  147. }
  148. [[maybe_unused]] static void pathRemoveFile(char *path, char delim)
  149. {
  150. // Remove filename and path delimiter
  151. int i;
  152. for(i = strlen(path)-1; i>=0; i--)
  153. {
  154. if(path[i] == delim)
  155. break;
  156. }
  157. path[i] = 0;
  158. }
  159. bool detectMSVCBuildDir(const std::string &path)
  160. {
  161. const char *ends[] = {
  162. "bin\\Release",
  163. "bin\\MinSizeRel",
  164. "bin\\RelWithDebInfo",
  165. "bin\\Debug",
  166. "bin\\Build",
  167. NULL
  168. };
  169. return (!removeStringEnd(path, ends).empty());
  170. }
  171. std::string get_sysinfo()
  172. {
  173. #ifdef _WIN32
  174. std::ostringstream oss;
  175. LPSTR filePath = new char[MAX_PATH];
  176. UINT blockSize;
  177. VS_FIXEDFILEINFO *fixedFileInfo;
  178. GetSystemDirectoryA(filePath, MAX_PATH);
  179. PathAppendA(filePath, "kernel32.dll");
  180. DWORD dwVersionSize = GetFileVersionInfoSizeA(filePath, NULL);
  181. LPBYTE lpVersionInfo = new BYTE[dwVersionSize];
  182. GetFileVersionInfoA(filePath, 0, dwVersionSize, lpVersionInfo);
  183. VerQueryValueA(lpVersionInfo, "\\", (LPVOID *)&fixedFileInfo, &blockSize);
  184. oss << "Windows/"
  185. << HIWORD(fixedFileInfo->dwProductVersionMS) << '.' // Major
  186. << LOWORD(fixedFileInfo->dwProductVersionMS) << '.' // Minor
  187. << HIWORD(fixedFileInfo->dwProductVersionLS) << ' '; // Build
  188. SYSTEM_INFO info;
  189. GetNativeSystemInfo(&info);
  190. switch (info.wProcessorArchitecture) {
  191. case PROCESSOR_ARCHITECTURE_AMD64:
  192. oss << "x86_64";
  193. break;
  194. case PROCESSOR_ARCHITECTURE_ARM:
  195. oss << "arm";
  196. break;
  197. case PROCESSOR_ARCHITECTURE_ARM64:
  198. oss << "arm64";
  199. break;
  200. case PROCESSOR_ARCHITECTURE_INTEL:
  201. oss << "x86";
  202. break;
  203. default:
  204. oss << "unknown";
  205. break;
  206. }
  207. delete[] lpVersionInfo;
  208. delete[] filePath;
  209. return oss.str();
  210. #else
  211. struct utsname osinfo;
  212. uname(&osinfo);
  213. return std::string(osinfo.sysname) + "/"
  214. + osinfo.release + " " + osinfo.machine;
  215. #endif
  216. }
  217. bool getCurrentWorkingDir(char *buf, size_t len)
  218. {
  219. #ifdef _WIN32
  220. DWORD ret = GetCurrentDirectory(len, buf);
  221. return (ret != 0) && (ret <= len);
  222. #else
  223. return getcwd(buf, len);
  224. #endif
  225. }
  226. static bool getExecPathFromProcfs(char *buf, size_t buflen)
  227. {
  228. #if defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
  229. buflen--;
  230. ssize_t len;
  231. if ((len = readlink("/proc/self/exe", buf, buflen)) == -1 &&
  232. (len = readlink("/proc/curproc/file", buf, buflen)) == -1 &&
  233. (len = readlink("/proc/curproc/exe", buf, buflen)) == -1)
  234. return false;
  235. buf[len] = '\0';
  236. return true;
  237. #else
  238. return false;
  239. #endif
  240. }
  241. //// Windows
  242. #if defined(_WIN32)
  243. bool getCurrentExecPath(char *buf, size_t len)
  244. {
  245. DWORD written = GetModuleFileNameA(NULL, buf, len);
  246. if (written == 0 || written == len)
  247. return false;
  248. return true;
  249. }
  250. //// Linux
  251. #elif defined(__linux__)
  252. bool getCurrentExecPath(char *buf, size_t len)
  253. {
  254. if (!getExecPathFromProcfs(buf, len))
  255. return false;
  256. return true;
  257. }
  258. //// Mac OS X, Darwin
  259. #elif defined(__APPLE__)
  260. bool getCurrentExecPath(char *buf, size_t len)
  261. {
  262. uint32_t lenb = (uint32_t)len;
  263. if (_NSGetExecutablePath(buf, &lenb) == -1)
  264. return false;
  265. return true;
  266. }
  267. //// FreeBSD, NetBSD, DragonFlyBSD
  268. #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
  269. bool getCurrentExecPath(char *buf, size_t len)
  270. {
  271. // Try getting path from procfs first, since valgrind
  272. // doesn't work with the latter
  273. if (getExecPathFromProcfs(buf, len))
  274. return true;
  275. int mib[4];
  276. mib[0] = CTL_KERN;
  277. mib[1] = KERN_PROC;
  278. mib[2] = KERN_PROC_PATHNAME;
  279. mib[3] = -1;
  280. if (sysctl(mib, 4, buf, &len, NULL, 0) == -1)
  281. return false;
  282. return true;
  283. }
  284. #elif defined(__HAIKU__)
  285. bool getCurrentExecPath(char *buf, size_t len)
  286. {
  287. return find_path(B_APP_IMAGE_SYMBOL, B_FIND_PATH_IMAGE_PATH, NULL, buf, len) == B_OK;
  288. }
  289. //// Solaris
  290. #elif defined(__sun) || defined(sun)
  291. bool getCurrentExecPath(char *buf, size_t len)
  292. {
  293. const char *exec = getexecname();
  294. if (exec == NULL)
  295. return false;
  296. if (strlcpy(buf, exec, len) >= len)
  297. return false;
  298. return true;
  299. }
  300. // HP-UX
  301. #elif defined(__hpux)
  302. bool getCurrentExecPath(char *buf, size_t len)
  303. {
  304. struct pst_status psts;
  305. if (pstat_getproc(&psts, sizeof(psts), 0, getpid()) == -1)
  306. return false;
  307. if (pstat_getpathname(buf, len, &psts.pst_fid_text) == -1)
  308. return false;
  309. return true;
  310. }
  311. #else
  312. bool getCurrentExecPath(char *buf, size_t len)
  313. {
  314. return false;
  315. }
  316. #endif
  317. [[maybe_unused]] static inline const char *getHomeOrFail()
  318. {
  319. const char *home = getenv("HOME");
  320. // In rare cases the HOME environment variable may be unset
  321. FATAL_ERROR_IF(!home,
  322. "Required environment variable HOME is not set");
  323. return home;
  324. }
  325. //// Windows
  326. #if defined(_WIN32)
  327. bool setSystemPaths()
  328. {
  329. char buf[BUFSIZ];
  330. // Find path of executable and set path_share relative to it
  331. FATAL_ERROR_IF(!getCurrentExecPath(buf, sizeof(buf)),
  332. "Failed to get current executable path");
  333. pathRemoveFile(buf, '\\');
  334. std::string exepath(buf);
  335. // Use ".\bin\.."
  336. path_share = exepath + "\\..";
  337. if (detectMSVCBuildDir(exepath)) {
  338. // The msvc build dir schould normaly not be present if properly installed,
  339. // but its useful for debugging.
  340. path_share += DIR_DELIM "..";
  341. }
  342. // Use %MINETEST_USER_PATH%
  343. DWORD len = GetEnvironmentVariable("MINETEST_USER_PATH", buf, sizeof(buf));
  344. FATAL_ERROR_IF(len > sizeof(buf), "Failed to get MINETEST_USER_PATH (too large for buffer)");
  345. if (len == 0) {
  346. // Use "C:\Users\<user>\AppData\Roaming\<PROJECT_NAME_C>"
  347. len = GetEnvironmentVariable("APPDATA", buf, sizeof(buf));
  348. FATAL_ERROR_IF(len == 0 || len > sizeof(buf), "Failed to get APPDATA");
  349. path_user = std::string(buf) + DIR_DELIM + PROJECT_NAME_C;
  350. } else {
  351. path_user = std::string(buf);
  352. }
  353. return true;
  354. }
  355. //// Android
  356. #elif defined(__ANDROID__)
  357. extern bool setSystemPaths(); // defined in porting_android.cpp
  358. //// Linux
  359. #elif defined(__linux__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
  360. bool setSystemPaths()
  361. {
  362. char buf[BUFSIZ];
  363. if (!getCurrentExecPath(buf, sizeof(buf))) {
  364. FATAL_ERROR("Unable to read bindir");
  365. return false;
  366. }
  367. pathRemoveFile(buf, '/');
  368. std::string bindir(buf);
  369. // Find share directory from these.
  370. // It is identified by containing the subdirectory "builtin".
  371. std::vector<std::string> trylist;
  372. std::string static_sharedir = STATIC_SHAREDIR;
  373. if (!static_sharedir.empty() && static_sharedir != ".")
  374. trylist.push_back(static_sharedir);
  375. trylist.push_back(bindir + DIR_DELIM ".." DIR_DELIM "share"
  376. DIR_DELIM + PROJECT_NAME);
  377. trylist.push_back(bindir + DIR_DELIM "..");
  378. for (auto i = trylist.begin(); i != trylist.end(); ++i) {
  379. const std::string &trypath = *i;
  380. if (!fs::PathExists(trypath) ||
  381. !fs::PathExists(trypath + DIR_DELIM + "builtin")) {
  382. warningstream << "system-wide share not found at \""
  383. << trypath << "\""<< std::endl;
  384. continue;
  385. }
  386. // Warn if was not the first alternative
  387. if (i != trylist.begin()) {
  388. warningstream << "system-wide share found at \""
  389. << trypath << "\"" << std::endl;
  390. }
  391. path_share = trypath;
  392. break;
  393. }
  394. const char *const minetest_user_path = getenv("MINETEST_USER_PATH");
  395. if (minetest_user_path && minetest_user_path[0] != '\0') {
  396. path_user = std::string(minetest_user_path);
  397. } else {
  398. path_user = std::string(getHomeOrFail()) + DIR_DELIM "."
  399. + PROJECT_NAME;
  400. }
  401. return true;
  402. }
  403. //// Mac OS X
  404. #elif defined(__APPLE__)
  405. bool setSystemPaths()
  406. {
  407. CFBundleRef main_bundle = CFBundleGetMainBundle();
  408. CFURLRef resources_url = CFBundleCopyResourcesDirectoryURL(main_bundle);
  409. char path[PATH_MAX];
  410. if (CFURLGetFileSystemRepresentation(resources_url,
  411. TRUE, (UInt8 *)path, PATH_MAX)) {
  412. path_share = std::string(path);
  413. } else {
  414. warningstream << "Could not determine bundle resource path" << std::endl;
  415. }
  416. CFRelease(resources_url);
  417. const char *const minetest_user_path = getenv("MINETEST_USER_PATH");
  418. if (minetest_user_path && minetest_user_path[0] != '\0') {
  419. path_user = std::string(minetest_user_path);
  420. } else {
  421. path_user = std::string(getHomeOrFail())
  422. + "/Library/Application Support/"
  423. + PROJECT_NAME;
  424. }
  425. return true;
  426. }
  427. #else
  428. bool setSystemPaths()
  429. {
  430. path_share = STATIC_SHAREDIR;
  431. const char *const minetest_user_path = getenv("MINETEST_USER_PATH");
  432. if (minetest_user_path && minetest_user_path[0] != '\0') {
  433. path_user = std::string(minetest_user_path);
  434. } else {
  435. path_user = std::string(getHomeOrFail()) + DIR_DELIM "."
  436. + lowercase(PROJECT_NAME);
  437. }
  438. return true;
  439. }
  440. #endif
  441. // Move cache folder from path_user to system cache location if possible.
  442. [[maybe_unused]] static void migrateCachePath()
  443. {
  444. const std::string local_cache_path = path_user + DIR_DELIM + "cache";
  445. // Delete tmp folder if it exists (it only ever contained
  446. // a temporary ogg file, which is no longer used).
  447. if (fs::PathExists(local_cache_path + DIR_DELIM + "tmp"))
  448. fs::RecursiveDelete(local_cache_path + DIR_DELIM + "tmp");
  449. // Bail if migration impossible
  450. if (path_cache == local_cache_path || !fs::PathExists(local_cache_path)
  451. || fs::PathExists(path_cache)) {
  452. return;
  453. }
  454. if (!fs::Rename(local_cache_path, path_cache)) {
  455. errorstream << "Failed to migrate local cache path "
  456. "to system path!" << std::endl;
  457. }
  458. }
  459. // Create tag in cache folder according to <https://bford.info/cachedir/> spec
  460. static void createCacheDirTag()
  461. {
  462. const auto path = path_cache + DIR_DELIM + "CACHEDIR.TAG";
  463. if (fs::PathExists(path))
  464. return;
  465. fs::CreateAllDirs(path_cache);
  466. std::ofstream ofs(path, std::ios::out | std::ios::binary);
  467. if (!ofs.good())
  468. return;
  469. ofs << "Signature: 8a477f597d28d172789f06886806bc55\n"
  470. "# This file is a cache directory tag automatically created by "
  471. PROJECT_NAME_C ".\n"
  472. "# For information about cache directory tags, see: "
  473. "https://bford.info/cachedir/\n";
  474. }
  475. void initializePaths()
  476. {
  477. #if RUN_IN_PLACE
  478. infostream << "Using relative paths (RUN_IN_PLACE)" << std::endl;
  479. char buf[BUFSIZ];
  480. bool success =
  481. getCurrentExecPath(buf, sizeof(buf)) ||
  482. getExecPathFromProcfs(buf, sizeof(buf));
  483. if (success) {
  484. pathRemoveFile(buf, DIR_DELIM_CHAR);
  485. std::string execpath(buf);
  486. path_share = execpath + DIR_DELIM "..";
  487. path_user = execpath + DIR_DELIM "..";
  488. if (detectMSVCBuildDir(execpath)) {
  489. path_share += DIR_DELIM "..";
  490. path_user += DIR_DELIM "..";
  491. }
  492. } else {
  493. errorstream << "Failed to get paths by executable location, "
  494. "trying cwd" << std::endl;
  495. if (!getCurrentWorkingDir(buf, sizeof(buf)))
  496. FATAL_ERROR("Ran out of methods to get paths");
  497. size_t cwdlen = strlen(buf);
  498. if (cwdlen >= 1 && buf[cwdlen - 1] == DIR_DELIM_CHAR) {
  499. cwdlen--;
  500. buf[cwdlen] = '\0';
  501. }
  502. if (cwdlen >= 4 && !strcmp(buf + cwdlen - 4, DIR_DELIM "bin"))
  503. pathRemoveFile(buf, DIR_DELIM_CHAR);
  504. std::string execpath(buf);
  505. path_share = execpath;
  506. path_user = execpath;
  507. }
  508. path_cache = path_user + DIR_DELIM + "cache";
  509. #else
  510. infostream << "Using system-wide paths (NOT RUN_IN_PLACE)" << std::endl;
  511. if (!setSystemPaths())
  512. errorstream << "Failed to get one or more system-wide path" << std::endl;
  513. # ifdef __ANDROID__
  514. sanity_check(!path_cache.empty());
  515. # elif defined(_WIN32)
  516. path_cache = path_user + DIR_DELIM + "cache";
  517. # else
  518. // First try $XDG_CACHE_HOME/PROJECT_NAME
  519. const char *cache_dir = getenv("XDG_CACHE_HOME");
  520. const char *home_dir = getenv("HOME");
  521. if (cache_dir && cache_dir[0] != '\0') {
  522. path_cache = std::string(cache_dir) + DIR_DELIM + PROJECT_NAME;
  523. } else if (home_dir) {
  524. // Then try $HOME/.cache/PROJECT_NAME
  525. path_cache = std::string(home_dir) + DIR_DELIM + ".cache"
  526. + DIR_DELIM + PROJECT_NAME;
  527. } else {
  528. // If neither works, use $PATH_USER/cache
  529. path_cache = path_user + DIR_DELIM + "cache";
  530. }
  531. # endif // _WIN32
  532. // Migrate cache folder to new location if possible
  533. migrateCachePath();
  534. #endif // RUN_IN_PLACE
  535. infostream << "Detected share path: " << path_share << std::endl;
  536. infostream << "Detected user path: " << path_user << std::endl;
  537. infostream << "Detected cache path: " << path_cache << std::endl;
  538. createCacheDirTag();
  539. #if USE_GETTEXT
  540. bool found_localedir = false;
  541. # ifdef STATIC_LOCALEDIR
  542. /* STATIC_LOCALEDIR may be a generalized path such as /usr/share/locale that
  543. * doesn't necessarily contain our locale files, so check data path first. */
  544. path_locale = getDataPath("locale");
  545. if (fs::PathExists(path_locale)) {
  546. found_localedir = true;
  547. infostream << "Using in-place locale directory " << path_locale
  548. << " even though a static one was provided." << std::endl;
  549. } else if (STATIC_LOCALEDIR[0] && fs::PathExists(STATIC_LOCALEDIR)) {
  550. found_localedir = true;
  551. path_locale = STATIC_LOCALEDIR;
  552. infostream << "Using static locale directory " << STATIC_LOCALEDIR
  553. << std::endl;
  554. }
  555. # else
  556. path_locale = getDataPath("locale");
  557. if (fs::PathExists(path_locale)) {
  558. found_localedir = true;
  559. }
  560. # endif
  561. if (!found_localedir) {
  562. warningstream << "Couldn't find a locale directory!" << std::endl;
  563. }
  564. #endif // USE_GETTEXT
  565. }
  566. ////
  567. //// OS-specific Secure Random
  568. ////
  569. #ifdef WIN32
  570. bool secure_rand_fill_buf(void *buf, size_t len)
  571. {
  572. HCRYPTPROV wctx;
  573. if (!CryptAcquireContext(&wctx, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
  574. return false;
  575. CryptGenRandom(wctx, len, (BYTE *)buf);
  576. CryptReleaseContext(wctx, 0);
  577. return true;
  578. }
  579. #else
  580. bool secure_rand_fill_buf(void *buf, size_t len)
  581. {
  582. // N.B. This function checks *only* for /dev/urandom, because on most
  583. // common OSes it is non-blocking, whereas /dev/random is blocking, and it
  584. // is exceptionally uncommon for there to be a situation where /dev/random
  585. // exists but /dev/urandom does not. This guesswork is necessary since
  586. // random devices are not covered by any POSIX standard...
  587. FILE *fp = fopen("/dev/urandom", "rb");
  588. if (!fp)
  589. return false;
  590. bool success = fread(buf, len, 1, fp) == 1;
  591. fclose(fp);
  592. return success;
  593. }
  594. #endif
  595. #ifndef __ANDROID__
  596. void osSpecificInit()
  597. {
  598. #ifdef _WIN32
  599. // hardening options
  600. HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
  601. SetSearchPathMode(BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE |
  602. BASE_SEARCH_PATH_PERMANENT);
  603. SetProcessDEPPolicy(PROCESS_DEP_ENABLE);
  604. #endif
  605. }
  606. #endif
  607. void attachOrCreateConsole()
  608. {
  609. #ifdef _WIN32
  610. static bool once = false;
  611. const bool redirected = _fileno(stdout) >= 0; // If output is redirected to e.g a file
  612. if (!once && !redirected) {
  613. if (AttachConsole(ATTACH_PARENT_PROCESS) || AllocConsole()) {
  614. freopen("CONOUT$", "w", stdout);
  615. freopen("CONOUT$", "w", stderr);
  616. }
  617. once = true;
  618. }
  619. #endif
  620. }
  621. #ifdef _WIN32
  622. std::string QuoteArgv(const std::string &arg)
  623. {
  624. // Quoting rules on Windows are batshit insane, can differ between applications
  625. // and there isn't even a stdlib function to deal with it.
  626. // Ref: https://learn.microsoft.com/archive/blogs/twistylittlepassagesallalike/everyone-quotes-command-line-arguments-the-wrong-way
  627. if (!arg.empty() && arg.find_first_of(" \t\n\v\"") == std::string::npos)
  628. return arg;
  629. std::string ret;
  630. ret.reserve(arg.size()+2);
  631. ret.push_back('"');
  632. for (auto it = arg.begin(); it != arg.end(); ++it) {
  633. u32 back = 0;
  634. while (it != arg.end() && *it == '\\')
  635. ++back, ++it;
  636. if (it == arg.end()) {
  637. ret.append(2 * back, '\\');
  638. break;
  639. } else if (*it == '"') {
  640. ret.append(2 * back + 1, '\\');
  641. } else {
  642. ret.append(back, '\\');
  643. }
  644. ret.push_back(*it);
  645. }
  646. ret.push_back('"');
  647. return ret;
  648. }
  649. #endif
  650. int mt_snprintf(char *buf, const size_t buf_size, const char *fmt, ...)
  651. {
  652. // https://msdn.microsoft.com/en-us/library/bt7tawza.aspx
  653. // Many of the MSVC / Windows printf-style functions do not support positional
  654. // arguments (eg. "%1$s"). We just forward the call to vsnprintf for sane
  655. // platforms, but defer to _vsprintf_p on MSVC / Windows.
  656. // https://github.com/FFmpeg/FFmpeg/blob/5ae9fa13f5ac640bec113120d540f70971aa635d/compat/msvcrt/snprintf.c#L46
  657. // _vsprintf_p has to be shimmed with _vscprintf_p on -1 (for an example see
  658. // above FFmpeg link).
  659. va_list args;
  660. va_start(args, fmt);
  661. #ifndef _MSC_VER
  662. int c = vsnprintf(buf, buf_size, fmt, args);
  663. #else // _MSC_VER
  664. int c = _vsprintf_p(buf, buf_size, fmt, args);
  665. if (c == -1)
  666. c = _vscprintf_p(fmt, args);
  667. #endif // _MSC_VER
  668. va_end(args);
  669. return c;
  670. }
  671. #ifdef __ANDROID__
  672. // defined in porting_android.cpp
  673. extern void openURIAndroid(const char *url);
  674. #endif
  675. static bool open_uri(const std::string &uri)
  676. {
  677. if (uri.find_first_of("\r\n") != std::string::npos) {
  678. errorstream << "Unable to open URI as it is invalid, contains new line: " << uri << std::endl;
  679. return false;
  680. }
  681. #if defined(_WIN32)
  682. return (intptr_t)ShellExecuteA(NULL, NULL, uri.c_str(), NULL, NULL, SW_SHOWNORMAL) > 32;
  683. #elif defined(__ANDROID__)
  684. openURIAndroid(uri.c_str());
  685. return true;
  686. #elif defined(__APPLE__)
  687. const char *argv[] = {"open", uri.c_str(), NULL};
  688. return posix_spawnp(NULL, "open", NULL, NULL, (char**)argv,
  689. (*_NSGetEnviron())) == 0;
  690. #else
  691. const char *argv[] = {"xdg-open", uri.c_str(), NULL};
  692. return posix_spawnp(NULL, "xdg-open", NULL, NULL, (char**)argv, environ) == 0;
  693. #endif
  694. }
  695. bool open_url(const std::string &url)
  696. {
  697. if (!str_starts_with(url, "http://") && !str_starts_with(url, "https://")) {
  698. errorstream << "Unable to open browser as URL is missing schema: " << url << std::endl;
  699. return false;
  700. }
  701. return open_uri(url);
  702. }
  703. bool open_directory(const std::string &path)
  704. {
  705. if (!fs::IsDir(path)) {
  706. errorstream << "Unable to open directory as it does not exist: " << path << std::endl;
  707. return false;
  708. }
  709. return open_uri(path);
  710. }
  711. // Load performance counter frequency only once at startup
  712. #ifdef _WIN32
  713. inline double get_perf_freq()
  714. {
  715. // Also use this opportunity to enable high-res timers
  716. timeBeginPeriod(1);
  717. LARGE_INTEGER freq;
  718. QueryPerformanceFrequency(&freq);
  719. return freq.QuadPart;
  720. }
  721. double perf_freq = get_perf_freq();
  722. #endif
  723. } //namespace porting