os_unix.cpp 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249
  1. /**************************************************************************/
  2. /* os_unix.cpp */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /**************************************************************************/
  30. #include "os_unix.h"
  31. #ifdef UNIX_ENABLED
  32. #include "core/config/project_settings.h"
  33. #include "core/debugger/engine_debugger.h"
  34. #include "core/debugger/script_debugger.h"
  35. #include "drivers/unix/dir_access_unix.h"
  36. #include "drivers/unix/file_access_unix.h"
  37. #include "drivers/unix/file_access_unix_pipe.h"
  38. #include "drivers/unix/net_socket_unix.h"
  39. #include "drivers/unix/thread_posix.h"
  40. #include "servers/rendering_server.h"
  41. #if defined(__APPLE__)
  42. #include <mach-o/dyld.h>
  43. #include <mach/host_info.h>
  44. #include <mach/mach_host.h>
  45. #include <mach/mach_time.h>
  46. #include <sys/sysctl.h>
  47. #endif
  48. #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
  49. #include <sys/param.h>
  50. #include <sys/sysctl.h>
  51. #endif
  52. #if defined(__FreeBSD__)
  53. #include <kvm.h>
  54. #endif
  55. #if defined(__OpenBSD__)
  56. #include <sys/swap.h>
  57. #include <uvm/uvmexp.h>
  58. #endif
  59. #if defined(__NetBSD__)
  60. #include <uvm/uvm_extern.h>
  61. #endif
  62. #include <dlfcn.h>
  63. #include <poll.h>
  64. #include <sys/resource.h>
  65. #include <sys/stat.h>
  66. #include <sys/time.h>
  67. #include <sys/wait.h>
  68. #include <unistd.h>
  69. #include <cerrno>
  70. #include <csignal>
  71. #include <cstdarg>
  72. #include <cstdio>
  73. #include <cstdlib>
  74. #include <ctime>
  75. #ifndef RTLD_DEEPBIND
  76. #define RTLD_DEEPBIND 0
  77. #endif
  78. #ifndef SANITIZERS_ENABLED
  79. #define GODOT_DLOPEN_MODE RTLD_NOW | RTLD_DEEPBIND
  80. #else
  81. #define GODOT_DLOPEN_MODE RTLD_NOW
  82. #endif
  83. #if defined(MACOS_ENABLED) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 28)
  84. // Random location for getentropy. Fitting.
  85. #include <sys/random.h>
  86. #define UNIX_GET_ENTROPY
  87. #elif defined(__FreeBSD__) || defined(__OpenBSD__) || (defined(__GLIBC_MINOR__) && (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 26))
  88. // In <unistd.h>.
  89. // One day... (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 700)
  90. // https://publications.opengroup.org/standards/unix/c211
  91. #define UNIX_GET_ENTROPY
  92. #endif
  93. #if !defined(UNIX_GET_ENTROPY) && !defined(NO_URANDOM)
  94. #include <fcntl.h>
  95. #endif
  96. /// Clock Setup function (used by get_ticks_usec)
  97. static uint64_t _clock_start = 0;
  98. #if defined(__APPLE__)
  99. static double _clock_scale = 0;
  100. static void _setup_clock() {
  101. mach_timebase_info_data_t info;
  102. kern_return_t ret = mach_timebase_info(&info);
  103. ERR_FAIL_COND_MSG(ret != 0, "OS CLOCK IS NOT WORKING!");
  104. _clock_scale = ((double)info.numer / (double)info.denom) / 1000.0;
  105. _clock_start = mach_absolute_time() * _clock_scale;
  106. }
  107. #else
  108. #if defined(CLOCK_MONOTONIC_RAW) && !defined(WEB_ENABLED) // This is a better clock on Linux.
  109. #define GODOT_CLOCK CLOCK_MONOTONIC_RAW
  110. #else
  111. #define GODOT_CLOCK CLOCK_MONOTONIC
  112. #endif
  113. static void _setup_clock() {
  114. struct timespec tv_now = { 0, 0 };
  115. ERR_FAIL_COND_MSG(clock_gettime(GODOT_CLOCK, &tv_now) != 0, "OS CLOCK IS NOT WORKING!");
  116. _clock_start = ((uint64_t)tv_now.tv_nsec / 1000L) + (uint64_t)tv_now.tv_sec * 1000000L;
  117. }
  118. #endif
  119. static void handle_interrupt(int sig) {
  120. if (!EngineDebugger::is_active()) {
  121. return;
  122. }
  123. EngineDebugger::get_script_debugger()->set_depth(-1);
  124. EngineDebugger::get_script_debugger()->set_lines_left(1);
  125. }
  126. void OS_Unix::initialize_debugging() {
  127. if (EngineDebugger::is_active()) {
  128. struct sigaction action;
  129. memset(&action, 0, sizeof(action));
  130. action.sa_handler = handle_interrupt;
  131. sigaction(SIGINT, &action, nullptr);
  132. }
  133. }
  134. int OS_Unix::unix_initialize_audio(int p_audio_driver) {
  135. return 0;
  136. }
  137. void OS_Unix::initialize_core() {
  138. #ifdef THREADS_ENABLED
  139. init_thread_posix();
  140. #endif
  141. FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_RESOURCES);
  142. FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_USERDATA);
  143. FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_FILESYSTEM);
  144. FileAccess::make_default<FileAccessUnixPipe>(FileAccess::ACCESS_PIPE);
  145. DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_RESOURCES);
  146. DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_USERDATA);
  147. DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_FILESYSTEM);
  148. #ifndef UNIX_SOCKET_UNAVAILABLE
  149. NetSocketUnix::make_default();
  150. IPUnix::make_default();
  151. #endif
  152. process_map = memnew((HashMap<ProcessID, ProcessInfo>));
  153. _setup_clock();
  154. }
  155. void OS_Unix::finalize_core() {
  156. memdelete(process_map);
  157. #ifndef UNIX_SOCKET_UNAVAILABLE
  158. NetSocketUnix::cleanup();
  159. #endif
  160. }
  161. Vector<String> OS_Unix::get_video_adapter_driver_info() const {
  162. return Vector<String>();
  163. }
  164. String OS_Unix::get_stdin_string(int64_t p_buffer_size) {
  165. Vector<uint8_t> data;
  166. data.resize(p_buffer_size);
  167. if (fgets((char *)data.ptrw(), data.size(), stdin)) {
  168. return String::utf8((char *)data.ptr()).replace("\r\n", "\n").rstrip("\n");
  169. }
  170. return String();
  171. }
  172. PackedByteArray OS_Unix::get_stdin_buffer(int64_t p_buffer_size) {
  173. Vector<uint8_t> data;
  174. data.resize(p_buffer_size);
  175. size_t sz = fread((void *)data.ptrw(), 1, data.size(), stdin);
  176. if (sz > 0) {
  177. data.resize(sz);
  178. return data;
  179. }
  180. return PackedByteArray();
  181. }
  182. OS_Unix::StdHandleType OS_Unix::get_stdin_type() const {
  183. int h = fileno(stdin);
  184. if (h == -1) {
  185. return STD_HANDLE_INVALID;
  186. }
  187. if (isatty(h)) {
  188. return STD_HANDLE_CONSOLE;
  189. }
  190. struct stat statbuf;
  191. if (fstat(h, &statbuf) < 0) {
  192. return STD_HANDLE_UNKNOWN;
  193. }
  194. if (S_ISFIFO(statbuf.st_mode)) {
  195. return STD_HANDLE_PIPE;
  196. } else if (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)) {
  197. return STD_HANDLE_FILE;
  198. }
  199. return STD_HANDLE_UNKNOWN;
  200. }
  201. OS_Unix::StdHandleType OS_Unix::get_stdout_type() const {
  202. int h = fileno(stdout);
  203. if (h == -1) {
  204. return STD_HANDLE_INVALID;
  205. }
  206. if (isatty(h)) {
  207. return STD_HANDLE_CONSOLE;
  208. }
  209. struct stat statbuf;
  210. if (fstat(h, &statbuf) < 0) {
  211. return STD_HANDLE_UNKNOWN;
  212. }
  213. if (S_ISFIFO(statbuf.st_mode)) {
  214. return STD_HANDLE_PIPE;
  215. } else if (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)) {
  216. return STD_HANDLE_FILE;
  217. }
  218. return STD_HANDLE_UNKNOWN;
  219. }
  220. OS_Unix::StdHandleType OS_Unix::get_stderr_type() const {
  221. int h = fileno(stderr);
  222. if (h == -1) {
  223. return STD_HANDLE_INVALID;
  224. }
  225. if (isatty(h)) {
  226. return STD_HANDLE_CONSOLE;
  227. }
  228. struct stat statbuf;
  229. if (fstat(h, &statbuf) < 0) {
  230. return STD_HANDLE_UNKNOWN;
  231. }
  232. if (S_ISFIFO(statbuf.st_mode)) {
  233. return STD_HANDLE_PIPE;
  234. } else if (S_ISREG(statbuf.st_mode) || S_ISLNK(statbuf.st_mode)) {
  235. return STD_HANDLE_FILE;
  236. }
  237. return STD_HANDLE_UNKNOWN;
  238. }
  239. Error OS_Unix::get_entropy(uint8_t *r_buffer, int p_bytes) {
  240. #if defined(UNIX_GET_ENTROPY)
  241. int left = p_bytes;
  242. int ofs = 0;
  243. do {
  244. int chunk = MIN(left, 256);
  245. ERR_FAIL_COND_V(getentropy(r_buffer + ofs, chunk), FAILED);
  246. left -= chunk;
  247. ofs += chunk;
  248. } while (left > 0);
  249. // Define this yourself if you don't want to fall back to /dev/urandom.
  250. #elif !defined(NO_URANDOM)
  251. int r = open("/dev/urandom", O_RDONLY);
  252. ERR_FAIL_COND_V(r < 0, FAILED);
  253. int left = p_bytes;
  254. do {
  255. ssize_t ret = read(r, r_buffer, p_bytes);
  256. ERR_FAIL_COND_V(ret <= 0, FAILED);
  257. left -= ret;
  258. } while (left > 0);
  259. #else
  260. return ERR_UNAVAILABLE;
  261. #endif
  262. return OK;
  263. }
  264. String OS_Unix::get_name() const {
  265. return "Unix";
  266. }
  267. String OS_Unix::get_distribution_name() const {
  268. return "";
  269. }
  270. String OS_Unix::get_version() const {
  271. return "";
  272. }
  273. String OS_Unix::get_temp_path() const {
  274. return "/tmp";
  275. }
  276. double OS_Unix::get_unix_time() const {
  277. struct timeval tv_now;
  278. gettimeofday(&tv_now, nullptr);
  279. return (double)tv_now.tv_sec + double(tv_now.tv_usec) / 1000000;
  280. }
  281. OS::DateTime OS_Unix::get_datetime(bool p_utc) const {
  282. time_t t = time(nullptr);
  283. struct tm lt;
  284. if (p_utc) {
  285. gmtime_r(&t, &lt);
  286. } else {
  287. localtime_r(&t, &lt);
  288. }
  289. DateTime ret;
  290. ret.year = 1900 + lt.tm_year;
  291. // Index starting at 1 to match OS_Unix::get_date
  292. // and Windows SYSTEMTIME and tm_mon follows the typical structure
  293. // of 0-11, noted here: http://www.cplusplus.com/reference/ctime/tm/
  294. ret.month = (Month)(lt.tm_mon + 1);
  295. ret.day = lt.tm_mday;
  296. ret.weekday = (Weekday)lt.tm_wday;
  297. ret.hour = lt.tm_hour;
  298. ret.minute = lt.tm_min;
  299. ret.second = lt.tm_sec;
  300. ret.dst = lt.tm_isdst;
  301. return ret;
  302. }
  303. OS::TimeZoneInfo OS_Unix::get_time_zone_info() const {
  304. time_t t = time(nullptr);
  305. struct tm lt;
  306. localtime_r(&t, &lt);
  307. char name[16];
  308. strftime(name, 16, "%Z", &lt);
  309. name[15] = 0;
  310. TimeZoneInfo ret;
  311. ret.name = name;
  312. char bias_buf[16];
  313. strftime(bias_buf, 16, "%z", &lt);
  314. int bias;
  315. bias_buf[15] = 0;
  316. sscanf(bias_buf, "%d", &bias);
  317. // convert from ISO 8601 (1 minute=1, 1 hour=100) to minutes
  318. int hour = (int)bias / 100;
  319. int minutes = bias % 100;
  320. if (bias < 0) {
  321. ret.bias = hour * 60 - minutes;
  322. } else {
  323. ret.bias = hour * 60 + minutes;
  324. }
  325. return ret;
  326. }
  327. void OS_Unix::delay_usec(uint32_t p_usec) const {
  328. struct timespec requested = { static_cast<time_t>(p_usec / 1000000), (static_cast<long>(p_usec) % 1000000) * 1000 };
  329. struct timespec remaining;
  330. while (nanosleep(&requested, &remaining) == -1 && errno == EINTR) {
  331. requested.tv_sec = remaining.tv_sec;
  332. requested.tv_nsec = remaining.tv_nsec;
  333. }
  334. }
  335. uint64_t OS_Unix::get_ticks_usec() const {
  336. #if defined(__APPLE__)
  337. uint64_t longtime = mach_absolute_time() * _clock_scale;
  338. #else
  339. // Unchecked return. Static analyzers might complain.
  340. // If _setup_clock() succeeded, we assume clock_gettime() works.
  341. struct timespec tv_now = { 0, 0 };
  342. clock_gettime(GODOT_CLOCK, &tv_now);
  343. uint64_t longtime = ((uint64_t)tv_now.tv_nsec / 1000L) + (uint64_t)tv_now.tv_sec * 1000000L;
  344. #endif
  345. longtime -= _clock_start;
  346. return longtime;
  347. }
  348. Dictionary OS_Unix::get_memory_info() const {
  349. Dictionary meminfo;
  350. meminfo["physical"] = -1;
  351. meminfo["free"] = -1;
  352. meminfo["available"] = -1;
  353. meminfo["stack"] = -1;
  354. #if defined(__APPLE__)
  355. int pagesize = 0;
  356. size_t len = sizeof(pagesize);
  357. if (sysctlbyname("vm.pagesize", &pagesize, &len, nullptr, 0) < 0) {
  358. ERR_PRINT(vformat("Could not get vm.pagesize, error code: %d - %s", errno, strerror(errno)));
  359. }
  360. int64_t phy_mem = 0;
  361. len = sizeof(phy_mem);
  362. if (sysctlbyname("hw.memsize", &phy_mem, &len, nullptr, 0) < 0) {
  363. ERR_PRINT(vformat("Could not get hw.memsize, error code: %d - %s", errno, strerror(errno)));
  364. }
  365. mach_msg_type_number_t count = HOST_VM_INFO64_COUNT;
  366. vm_statistics64_data_t vmstat;
  367. if (host_statistics64(mach_host_self(), HOST_VM_INFO64, (host_info64_t)&vmstat, &count) != KERN_SUCCESS) {
  368. ERR_PRINT("Could not get host vm statistics.");
  369. }
  370. struct xsw_usage swap_used;
  371. len = sizeof(swap_used);
  372. if (sysctlbyname("vm.swapusage", &swap_used, &len, nullptr, 0) < 0) {
  373. ERR_PRINT(vformat("Could not get vm.swapusage, error code: %d - %s", errno, strerror(errno)));
  374. }
  375. if (phy_mem != 0) {
  376. meminfo["physical"] = phy_mem;
  377. }
  378. if (vmstat.free_count * (int64_t)pagesize != 0) {
  379. meminfo["free"] = vmstat.free_count * (int64_t)pagesize;
  380. }
  381. if (swap_used.xsu_avail + vmstat.free_count * (int64_t)pagesize != 0) {
  382. meminfo["available"] = swap_used.xsu_avail + vmstat.free_count * (int64_t)pagesize;
  383. }
  384. #elif defined(__FreeBSD__)
  385. int pagesize = 0;
  386. size_t len = sizeof(pagesize);
  387. if (sysctlbyname("vm.stats.vm.v_page_size", &pagesize, &len, nullptr, 0) < 0) {
  388. ERR_PRINT(vformat("Could not get vm.stats.vm.v_page_size, error code: %d - %s", errno, strerror(errno)));
  389. }
  390. uint64_t mtotal = 0;
  391. len = sizeof(mtotal);
  392. if (sysctlbyname("vm.stats.vm.v_page_count", &mtotal, &len, nullptr, 0) < 0) {
  393. ERR_PRINT(vformat("Could not get vm.stats.vm.v_page_count, error code: %d - %s", errno, strerror(errno)));
  394. }
  395. uint64_t mfree = 0;
  396. len = sizeof(mfree);
  397. if (sysctlbyname("vm.stats.vm.v_free_count", &mfree, &len, nullptr, 0) < 0) {
  398. ERR_PRINT(vformat("Could not get vm.stats.vm.v_free_count, error code: %d - %s", errno, strerror(errno)));
  399. }
  400. uint64_t stotal = 0;
  401. uint64_t sused = 0;
  402. char errmsg[_POSIX2_LINE_MAX] = {};
  403. kvm_t *kd = kvm_openfiles(nullptr, "/dev/null", nullptr, 0, errmsg);
  404. if (kd == nullptr) {
  405. ERR_PRINT(vformat("kvm_openfiles failed, error: %s", errmsg));
  406. } else {
  407. struct kvm_swap swap_info[32];
  408. int count = kvm_getswapinfo(kd, swap_info, 32, 0);
  409. for (int i = 0; i < count; i++) {
  410. stotal += swap_info[i].ksw_total;
  411. sused += swap_info[i].ksw_used;
  412. }
  413. kvm_close(kd);
  414. }
  415. if (mtotal * pagesize != 0) {
  416. meminfo["physical"] = mtotal * pagesize;
  417. }
  418. if (mfree * pagesize != 0) {
  419. meminfo["free"] = mfree * pagesize;
  420. }
  421. if ((mfree + stotal - sused) * pagesize != 0) {
  422. meminfo["available"] = (mfree + stotal - sused) * pagesize;
  423. }
  424. #elif defined(__OpenBSD__)
  425. int pagesize = sysconf(_SC_PAGESIZE);
  426. const int mib[] = { CTL_VM, VM_UVMEXP };
  427. uvmexp uvmexp_info;
  428. size_t len = sizeof(uvmexp_info);
  429. if (sysctl(mib, 2, &uvmexp_info, &len, nullptr, 0) < 0) {
  430. ERR_PRINT(vformat("Could not get CTL_VM, VM_UVMEXP, error code: %d - %s", errno, strerror(errno)));
  431. }
  432. uint64_t stotal = 0;
  433. uint64_t sused = 0;
  434. int count = swapctl(SWAP_NSWAP, 0, 0);
  435. if (count > 0) {
  436. swapent swap_info[count];
  437. count = swapctl(SWAP_STATS, swap_info, count);
  438. for (int i = 0; i < count; i++) {
  439. if (swap_info[i].se_flags & SWF_ENABLE) {
  440. sused += swap_info[i].se_inuse;
  441. stotal += swap_info[i].se_nblks;
  442. }
  443. }
  444. }
  445. if (uvmexp_info.npages * pagesize != 0) {
  446. meminfo["physical"] = uvmexp_info.npages * pagesize;
  447. }
  448. if (uvmexp_info.free * pagesize != 0) {
  449. meminfo["free"] = uvmexp_info.free * pagesize;
  450. }
  451. if ((uvmexp_info.free * pagesize) + (stotal - sused) * DEV_BSIZE != 0) {
  452. meminfo["available"] = (uvmexp_info.free * pagesize) + (stotal - sused) * DEV_BSIZE;
  453. }
  454. #elif defined(__NetBSD__)
  455. int pagesize = sysconf(_SC_PAGESIZE);
  456. const int mib[] = { CTL_VM, VM_UVMEXP2 };
  457. uvmexp_sysctl uvmexp_info;
  458. size_t len = sizeof(uvmexp_info);
  459. if (sysctl(mib, 2, &uvmexp_info, &len, nullptr, 0) < 0) {
  460. ERR_PRINT(vformat("Could not get CTL_VM, VM_UVMEXP2, error code: %d - %s", errno, strerror(errno)));
  461. }
  462. if (uvmexp_info.npages * pagesize != 0) {
  463. meminfo["physical"] = uvmexp_info.npages * pagesize;
  464. }
  465. if (uvmexp_info.free * pagesize != 0) {
  466. meminfo["free"] = uvmexp_info.free * pagesize;
  467. }
  468. if ((uvmexp_info.free + uvmexp_info.swpages - uvmexp_info.swpginuse) * pagesize != 0) {
  469. meminfo["available"] = (uvmexp_info.free + uvmexp_info.swpages - uvmexp_info.swpginuse) * pagesize;
  470. }
  471. #else
  472. Error err;
  473. Ref<FileAccess> f = FileAccess::open("/proc/meminfo", FileAccess::READ, &err);
  474. uint64_t mtotal = 0;
  475. uint64_t mfree = 0;
  476. uint64_t sfree = 0;
  477. while (f.is_valid() && !f->eof_reached()) {
  478. String s = f->get_line().strip_edges();
  479. if (s.begins_with("MemTotal:")) {
  480. Vector<String> stok = s.replace("MemTotal:", "").strip_edges().split(" ");
  481. if (stok.size() == 2) {
  482. mtotal = stok[0].to_int() * 1024;
  483. }
  484. }
  485. if (s.begins_with("MemFree:")) {
  486. Vector<String> stok = s.replace("MemFree:", "").strip_edges().split(" ");
  487. if (stok.size() == 2) {
  488. mfree = stok[0].to_int() * 1024;
  489. }
  490. }
  491. if (s.begins_with("SwapFree:")) {
  492. Vector<String> stok = s.replace("SwapFree:", "").strip_edges().split(" ");
  493. if (stok.size() == 2) {
  494. sfree = stok[0].to_int() * 1024;
  495. }
  496. }
  497. }
  498. if (mtotal != 0) {
  499. meminfo["physical"] = mtotal;
  500. }
  501. if (mfree != 0) {
  502. meminfo["free"] = mfree;
  503. }
  504. if (mfree + sfree != 0) {
  505. meminfo["available"] = mfree + sfree;
  506. }
  507. #endif
  508. rlimit stackinfo = {};
  509. getrlimit(RLIMIT_STACK, &stackinfo);
  510. if (stackinfo.rlim_cur != 0) {
  511. meminfo["stack"] = (int64_t)stackinfo.rlim_cur;
  512. }
  513. return meminfo;
  514. }
  515. #if !defined(__GLIBC__) && !defined(WEB_ENABLED)
  516. void OS_Unix::_load_iconv() {
  517. #if defined(MACOS_ENABLED) || defined(IOS_ENABLED)
  518. String iconv_lib_aliases[] = { "/usr/lib/libiconv.2.dylib" };
  519. String iconv_func_aliases[] = { "iconv" };
  520. String charset_lib_aliases[] = { "/usr/lib/libcharset.1.dylib" };
  521. #else
  522. String iconv_lib_aliases[] = { "", "libiconv.2.so", "libiconv.so" };
  523. String iconv_func_aliases[] = { "libiconv", "iconv", "bsd_iconv", "rpl_iconv" };
  524. String charset_lib_aliases[] = { "", "libcharset.1.so", "libcharset.so" };
  525. #endif
  526. for (size_t i = 0; i < sizeof(iconv_lib_aliases) / sizeof(iconv_lib_aliases[0]); i++) {
  527. void *iconv_lib = iconv_lib_aliases[i].is_empty() ? RTLD_NEXT : dlopen(iconv_lib_aliases[i].utf8().get_data(), RTLD_NOW);
  528. if (iconv_lib) {
  529. for (size_t j = 0; j < sizeof(iconv_func_aliases) / sizeof(iconv_func_aliases[0]); j++) {
  530. gd_iconv_open = (PIConvOpen)dlsym(iconv_lib, (iconv_func_aliases[j] + "_open").utf8().get_data());
  531. gd_iconv = (PIConv)dlsym(iconv_lib, (iconv_func_aliases[j]).utf8().get_data());
  532. gd_iconv_close = (PIConvClose)dlsym(iconv_lib, (iconv_func_aliases[j] + "_close").utf8().get_data());
  533. if (gd_iconv_open && gd_iconv && gd_iconv_close) {
  534. break;
  535. }
  536. }
  537. if (gd_iconv_open && gd_iconv && gd_iconv_close) {
  538. break;
  539. }
  540. if (!iconv_lib_aliases[i].is_empty()) {
  541. dlclose(iconv_lib);
  542. }
  543. }
  544. }
  545. for (size_t i = 0; i < sizeof(charset_lib_aliases) / sizeof(charset_lib_aliases[0]); i++) {
  546. void *cs_lib = charset_lib_aliases[i].is_empty() ? RTLD_NEXT : dlopen(charset_lib_aliases[i].utf8().get_data(), RTLD_NOW);
  547. if (cs_lib) {
  548. gd_locale_charset = (PIConvLocaleCharset)dlsym(cs_lib, "locale_charset");
  549. if (gd_locale_charset) {
  550. break;
  551. }
  552. if (!charset_lib_aliases[i].is_empty()) {
  553. dlclose(cs_lib);
  554. }
  555. }
  556. }
  557. _iconv_ok = gd_iconv_open && gd_iconv && gd_iconv_close && gd_locale_charset;
  558. }
  559. #endif
  560. String OS_Unix::multibyte_to_string(const String &p_encoding, const PackedByteArray &p_array) const {
  561. ERR_FAIL_COND_V_MSG(!_iconv_ok, String(), "Conversion failed: Unable to load libiconv");
  562. LocalVector<char> chars;
  563. #if defined(__GLIBC__) || defined(WEB_ENABLED)
  564. gd_iconv_t ctx = gd_iconv_open("UTF-8", p_encoding.is_empty() ? nl_langinfo(CODESET) : p_encoding.utf8().get_data());
  565. #else
  566. gd_iconv_t ctx = gd_iconv_open("UTF-8", p_encoding.is_empty() ? gd_locale_charset() : p_encoding.utf8().get_data());
  567. #endif
  568. ERR_FAIL_COND_V_MSG(ctx == (gd_iconv_t)(-1), String(), "Conversion failed: Unknown encoding");
  569. char *in_ptr = (char *)p_array.ptr();
  570. size_t in_size = p_array.size();
  571. chars.resize(in_size);
  572. char *out_ptr = (char *)chars.ptr();
  573. size_t out_size = chars.size();
  574. while (gd_iconv(ctx, &in_ptr, &in_size, &out_ptr, &out_size) == (size_t)-1) {
  575. if (errno != E2BIG) {
  576. gd_iconv_close(ctx);
  577. ERR_FAIL_V_MSG(String(), vformat("Conversion failed: %d - %s", errno, strerror(errno)));
  578. }
  579. int64_t rate = (chars.size()) / (p_array.size() - in_size);
  580. size_t oldpos = chars.size() - out_size;
  581. chars.resize(chars.size() + in_size * rate);
  582. out_ptr = (char *)chars.ptr() + oldpos;
  583. out_size = chars.size() - oldpos;
  584. }
  585. chars.resize(chars.size() - out_size);
  586. gd_iconv_close(ctx);
  587. return String::utf8((const char *)chars.ptr(), chars.size());
  588. }
  589. PackedByteArray OS_Unix::string_to_multibyte(const String &p_encoding, const String &p_string) const {
  590. ERR_FAIL_COND_V_MSG(!_iconv_ok, PackedByteArray(), "Conversion failed: Unable to load libiconv");
  591. CharString charstr = p_string.utf8();
  592. PackedByteArray ret;
  593. #if defined(__GLIBC__) || defined(WEB_ENABLED)
  594. gd_iconv_t ctx = gd_iconv_open(p_encoding.is_empty() ? nl_langinfo(CODESET) : p_encoding.utf8().get_data(), "UTF-8");
  595. #else
  596. gd_iconv_t ctx = gd_iconv_open(p_encoding.is_empty() ? gd_locale_charset() : p_encoding.utf8().get_data(), "UTF-8");
  597. #endif
  598. ERR_FAIL_COND_V_MSG(ctx == (gd_iconv_t)(-1), PackedByteArray(), "Conversion failed: Unknown encoding");
  599. char *in_ptr = (char *)charstr.ptr();
  600. size_t in_size = charstr.size();
  601. ret.resize(in_size);
  602. char *out_ptr = (char *)ret.ptrw();
  603. size_t out_size = ret.size();
  604. while (gd_iconv(ctx, &in_ptr, &in_size, &out_ptr, &out_size) == (size_t)-1) {
  605. if (errno != E2BIG) {
  606. gd_iconv_close(ctx);
  607. ERR_FAIL_V_MSG(PackedByteArray(), vformat("Conversion failed: %d - %s", errno, strerror(errno)));
  608. }
  609. int64_t rate = (ret.size()) / (charstr.size() - in_size);
  610. size_t oldpos = ret.size() - out_size;
  611. ret.resize(ret.size() + in_size * rate);
  612. out_ptr = (char *)ret.ptrw() + oldpos;
  613. out_size = ret.size() - oldpos;
  614. }
  615. ret.resize(ret.size() - out_size);
  616. gd_iconv_close(ctx);
  617. return ret;
  618. }
  619. Dictionary OS_Unix::execute_with_pipe(const String &p_path, const List<String> &p_arguments, bool p_blocking) {
  620. #define CLEAN_PIPES \
  621. if (pipe_in[0] >= 0) { \
  622. ::close(pipe_in[0]); \
  623. } \
  624. if (pipe_in[1] >= 0) { \
  625. ::close(pipe_in[1]); \
  626. } \
  627. if (pipe_out[0] >= 0) { \
  628. ::close(pipe_out[0]); \
  629. } \
  630. if (pipe_out[1] >= 0) { \
  631. ::close(pipe_out[1]); \
  632. } \
  633. if (pipe_err[0] >= 0) { \
  634. ::close(pipe_err[0]); \
  635. } \
  636. if (pipe_err[1] >= 0) { \
  637. ::close(pipe_err[1]); \
  638. }
  639. Dictionary ret;
  640. #ifdef __EMSCRIPTEN__
  641. // Don't compile this code at all to avoid undefined references.
  642. // Actual virtual call goes to OS_Web.
  643. ERR_FAIL_V(ret);
  644. #else
  645. // Create pipes.
  646. int pipe_in[2] = { -1, -1 };
  647. int pipe_out[2] = { -1, -1 };
  648. int pipe_err[2] = { -1, -1 };
  649. ERR_FAIL_COND_V(pipe(pipe_in) != 0, ret);
  650. if (pipe(pipe_out) != 0) {
  651. CLEAN_PIPES
  652. ERR_FAIL_V(ret);
  653. }
  654. if (pipe(pipe_err) != 0) {
  655. CLEAN_PIPES
  656. ERR_FAIL_V(ret);
  657. }
  658. // Create process.
  659. pid_t pid = fork();
  660. if (pid < 0) {
  661. CLEAN_PIPES
  662. ERR_FAIL_V(ret);
  663. }
  664. if (pid == 0) {
  665. // The child process.
  666. Vector<CharString> cs;
  667. cs.push_back(p_path.utf8());
  668. for (const String &arg : p_arguments) {
  669. cs.push_back(arg.utf8());
  670. }
  671. Vector<char *> args;
  672. for (int i = 0; i < cs.size(); i++) {
  673. args.push_back((char *)cs[i].get_data());
  674. }
  675. args.push_back(0);
  676. ::close(STDIN_FILENO);
  677. ::dup2(pipe_in[0], STDIN_FILENO);
  678. ::close(STDOUT_FILENO);
  679. ::dup2(pipe_out[1], STDOUT_FILENO);
  680. ::close(STDERR_FILENO);
  681. ::dup2(pipe_err[1], STDERR_FILENO);
  682. CLEAN_PIPES
  683. execvp(p_path.utf8().get_data(), &args[0]);
  684. // The execvp() function only returns if an error occurs.
  685. ERR_PRINT("Could not create child process: " + p_path);
  686. raise(SIGKILL);
  687. }
  688. ::close(pipe_in[0]);
  689. ::close(pipe_out[1]);
  690. ::close(pipe_err[1]);
  691. Ref<FileAccessUnixPipe> main_pipe;
  692. main_pipe.instantiate();
  693. main_pipe->open_existing(pipe_out[0], pipe_in[1], p_blocking);
  694. Ref<FileAccessUnixPipe> err_pipe;
  695. err_pipe.instantiate();
  696. err_pipe->open_existing(pipe_err[0], 0, p_blocking);
  697. ProcessInfo pi;
  698. process_map_mutex.lock();
  699. process_map->insert(pid, pi);
  700. process_map_mutex.unlock();
  701. ret["stdio"] = main_pipe;
  702. ret["stderr"] = err_pipe;
  703. ret["pid"] = pid;
  704. #undef CLEAN_PIPES
  705. return ret;
  706. #endif
  707. }
  708. int OS_Unix::_wait_for_pid_completion(const pid_t p_pid, int *r_status, int p_options) {
  709. while (true) {
  710. if (waitpid(p_pid, r_status, p_options) != -1) {
  711. // Thread exited normally.
  712. return 0;
  713. }
  714. const int error = errno;
  715. if (error == EINTR) {
  716. // We're in a debugger, should call waitpid again.
  717. // See https://stackoverflow.com/a/45472920/730797.
  718. continue;
  719. }
  720. return error;
  721. }
  722. }
  723. bool OS_Unix::_check_pid_is_running(const pid_t p_pid, int *r_status) const {
  724. const ProcessInfo *pi = process_map->getptr(p_pid);
  725. if (pi && !pi->is_running) {
  726. // Can return cached value.
  727. if (r_status) {
  728. *r_status = pi->exit_code;
  729. }
  730. return false;
  731. }
  732. int status = 0;
  733. const int result = _wait_for_pid_completion(p_pid, &status, WNOHANG);
  734. if (result == 0) {
  735. // Thread is still running.
  736. return true;
  737. }
  738. ERR_FAIL_COND_V_MSG(result == -1, false, vformat("Thread %d exited with errno: %d", (int)p_pid, errno));
  739. // Thread exited normally.
  740. status = WIFEXITED(status) ? WEXITSTATUS(status) : status;
  741. if (pi) {
  742. pi->is_running = false;
  743. pi->exit_code = status;
  744. }
  745. if (r_status) {
  746. *r_status = status;
  747. }
  748. return false;
  749. }
  750. Error OS_Unix::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex, bool p_open_console) {
  751. #ifdef __EMSCRIPTEN__
  752. // Don't compile this code at all to avoid undefined references.
  753. // Actual virtual call goes to OS_Web.
  754. ERR_FAIL_V(ERR_BUG);
  755. #else
  756. if (r_pipe) {
  757. String command = "\"" + p_path + "\"";
  758. for (const String &arg : p_arguments) {
  759. command += String(" \"") + arg + "\"";
  760. }
  761. if (read_stderr) {
  762. command += " 2>&1"; // Include stderr
  763. } else {
  764. command += " 2>/dev/null"; // Silence stderr
  765. }
  766. FILE *f = popen(command.utf8().get_data(), "r");
  767. ERR_FAIL_NULL_V_MSG(f, ERR_CANT_OPEN, "Cannot create pipe from command: " + command + ".");
  768. char buf[65535];
  769. while (fgets(buf, 65535, f)) {
  770. if (p_pipe_mutex) {
  771. p_pipe_mutex->lock();
  772. }
  773. String pipe_out;
  774. if (pipe_out.append_utf8(buf) == OK) {
  775. (*r_pipe) += pipe_out;
  776. } else {
  777. (*r_pipe) += String(buf); // If not valid UTF-8 try decode as Latin-1
  778. }
  779. if (p_pipe_mutex) {
  780. p_pipe_mutex->unlock();
  781. }
  782. }
  783. int rv = pclose(f);
  784. if (r_exitcode) {
  785. *r_exitcode = WEXITSTATUS(rv);
  786. }
  787. return OK;
  788. }
  789. pid_t pid = fork();
  790. ERR_FAIL_COND_V(pid < 0, ERR_CANT_FORK);
  791. if (pid == 0) {
  792. // The child process
  793. Vector<CharString> cs;
  794. cs.push_back(p_path.utf8());
  795. for (const String &arg : p_arguments) {
  796. cs.push_back(arg.utf8());
  797. }
  798. Vector<char *> args;
  799. for (int i = 0; i < cs.size(); i++) {
  800. args.push_back((char *)cs[i].get_data());
  801. }
  802. args.push_back(0);
  803. execvp(p_path.utf8().get_data(), &args[0]);
  804. // The execvp() function only returns if an error occurs.
  805. ERR_PRINT("Could not create child process: " + p_path);
  806. raise(SIGKILL);
  807. }
  808. int status = 0;
  809. const int result = _wait_for_pid_completion(pid, &status, 0);
  810. if (r_exitcode) {
  811. *r_exitcode = WIFEXITED(status) ? WEXITSTATUS(status) : status;
  812. }
  813. return result ? FAILED : OK;
  814. #endif
  815. }
  816. Error OS_Unix::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id, bool p_open_console) {
  817. #ifdef __EMSCRIPTEN__
  818. // Don't compile this code at all to avoid undefined references.
  819. // Actual virtual call goes to OS_Web.
  820. ERR_FAIL_V(ERR_BUG);
  821. #else
  822. pid_t pid = fork();
  823. ERR_FAIL_COND_V(pid < 0, ERR_CANT_FORK);
  824. if (pid == 0) {
  825. // The new process
  826. // Create a new session-ID so parent won't wait for it.
  827. // This ensures the process won't go zombie at the end.
  828. setsid();
  829. Vector<CharString> cs;
  830. cs.push_back(p_path.utf8());
  831. for (const String &arg : p_arguments) {
  832. cs.push_back(arg.utf8());
  833. }
  834. Vector<char *> args;
  835. for (int i = 0; i < cs.size(); i++) {
  836. args.push_back((char *)cs[i].get_data());
  837. }
  838. args.push_back(0);
  839. execvp(p_path.utf8().get_data(), &args[0]);
  840. // The execvp() function only returns if an error occurs.
  841. ERR_PRINT("Could not create child process: " + p_path);
  842. raise(SIGKILL);
  843. }
  844. ProcessInfo pi;
  845. process_map_mutex.lock();
  846. process_map->insert(pid, pi);
  847. process_map_mutex.unlock();
  848. if (r_child_id) {
  849. *r_child_id = pid;
  850. }
  851. return OK;
  852. #endif
  853. }
  854. Error OS_Unix::kill(const ProcessID &p_pid) {
  855. int ret = ::kill(p_pid, SIGKILL);
  856. if (!ret) {
  857. //avoid zombie process
  858. int st;
  859. _wait_for_pid_completion(p_pid, &st, 0);
  860. }
  861. return ret ? ERR_INVALID_PARAMETER : OK;
  862. }
  863. int OS_Unix::get_process_id() const {
  864. return getpid();
  865. }
  866. bool OS_Unix::is_process_running(const ProcessID &p_pid) const {
  867. MutexLock lock(process_map_mutex);
  868. return _check_pid_is_running(p_pid, nullptr);
  869. }
  870. int OS_Unix::get_process_exit_code(const ProcessID &p_pid) const {
  871. MutexLock lock(process_map_mutex);
  872. int exit_code = 0;
  873. if (_check_pid_is_running(p_pid, &exit_code)) {
  874. // Thread is still running
  875. return -1;
  876. }
  877. return exit_code;
  878. }
  879. String OS_Unix::get_locale() const {
  880. if (!has_environment("LANG")) {
  881. return "en";
  882. }
  883. String locale = get_environment("LANG");
  884. int tp = locale.find_char('.');
  885. if (tp != -1) {
  886. locale = locale.substr(0, tp);
  887. }
  888. return locale;
  889. }
  890. Error OS_Unix::open_dynamic_library(const String &p_path, void *&p_library_handle, GDExtensionData *p_data) {
  891. String path = p_path;
  892. if (FileAccess::exists(path) && path.is_relative_path()) {
  893. // dlopen expects a slash, in this case a leading ./ for it to be interpreted as a relative path,
  894. // otherwise it will end up searching various system directories for the lib instead and finally failing.
  895. path = "./" + path;
  896. }
  897. if (!FileAccess::exists(path)) {
  898. // This code exists so GDExtension can load .so files from within the executable path.
  899. path = get_executable_path().get_base_dir().path_join(p_path.get_file());
  900. }
  901. if (!FileAccess::exists(path)) {
  902. // This code exists so GDExtension can load .so files from a standard unix location.
  903. path = get_executable_path().get_base_dir().path_join("../lib").path_join(p_path.get_file());
  904. }
  905. ERR_FAIL_COND_V(!FileAccess::exists(path), ERR_FILE_NOT_FOUND);
  906. p_library_handle = dlopen(path.utf8().get_data(), GODOT_DLOPEN_MODE);
  907. ERR_FAIL_NULL_V_MSG(p_library_handle, ERR_CANT_OPEN, vformat("Can't open dynamic library: %s. Error: %s.", p_path, dlerror()));
  908. if (p_data != nullptr && p_data->r_resolved_path != nullptr) {
  909. *p_data->r_resolved_path = path;
  910. }
  911. return OK;
  912. }
  913. Error OS_Unix::close_dynamic_library(void *p_library_handle) {
  914. if (dlclose(p_library_handle)) {
  915. return FAILED;
  916. }
  917. return OK;
  918. }
  919. Error OS_Unix::get_dynamic_library_symbol_handle(void *p_library_handle, const String &p_name, void *&p_symbol_handle, bool p_optional) {
  920. const char *error;
  921. dlerror(); // Clear existing errors
  922. p_symbol_handle = dlsym(p_library_handle, p_name.utf8().get_data());
  923. error = dlerror();
  924. if (error != nullptr) {
  925. ERR_FAIL_COND_V_MSG(!p_optional, ERR_CANT_RESOLVE, "Can't resolve symbol " + p_name + ". Error: " + error + ".");
  926. return ERR_CANT_RESOLVE;
  927. }
  928. return OK;
  929. }
  930. Error OS_Unix::set_cwd(const String &p_cwd) {
  931. if (chdir(p_cwd.utf8().get_data()) != 0) {
  932. return ERR_CANT_OPEN;
  933. }
  934. return OK;
  935. }
  936. bool OS_Unix::has_environment(const String &p_var) const {
  937. return getenv(p_var.utf8().get_data()) != nullptr;
  938. }
  939. String OS_Unix::get_environment(const String &p_var) const {
  940. const char *val = getenv(p_var.utf8().get_data());
  941. if (val == nullptr) { // Not set; return empty string
  942. return "";
  943. }
  944. String s;
  945. if (s.append_utf8(val) == OK) {
  946. return s;
  947. }
  948. return String(val); // Not valid UTF-8, so return as-is
  949. }
  950. void OS_Unix::set_environment(const String &p_var, const String &p_value) const {
  951. ERR_FAIL_COND_MSG(p_var.is_empty() || p_var.contains_char('='), vformat("Invalid environment variable name '%s', cannot be empty or include '='.", p_var));
  952. int err = setenv(p_var.utf8().get_data(), p_value.utf8().get_data(), /* overwrite: */ 1);
  953. ERR_FAIL_COND_MSG(err != 0, vformat("Failed setting environment variable '%s', the system is out of memory.", p_var));
  954. }
  955. void OS_Unix::unset_environment(const String &p_var) const {
  956. ERR_FAIL_COND_MSG(p_var.is_empty() || p_var.contains_char('='), vformat("Invalid environment variable name '%s', cannot be empty or include '='.", p_var));
  957. unsetenv(p_var.utf8().get_data());
  958. }
  959. String OS_Unix::get_user_data_dir(const String &p_user_dir) const {
  960. return get_data_path().path_join(p_user_dir);
  961. }
  962. String OS_Unix::get_executable_path() const {
  963. #ifdef __linux__
  964. //fix for running from a symlink
  965. char buf[256];
  966. memset(buf, 0, 256);
  967. ssize_t len = readlink("/proc/self/exe", buf, sizeof(buf));
  968. String b;
  969. if (len > 0) {
  970. b.append_utf8(buf, len);
  971. }
  972. if (b.is_empty()) {
  973. WARN_PRINT("Couldn't get executable path from /proc/self/exe, using argv[0]");
  974. return OS::get_executable_path();
  975. }
  976. return b;
  977. #elif defined(__OpenBSD__)
  978. char resolved_path[MAXPATHLEN];
  979. realpath(OS::get_executable_path().utf8().get_data(), resolved_path);
  980. return String(resolved_path);
  981. #elif defined(__NetBSD__)
  982. int mib[4] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME };
  983. char buf[MAXPATHLEN];
  984. size_t len = sizeof(buf);
  985. if (sysctl(mib, 4, buf, &len, nullptr, 0) != 0) {
  986. WARN_PRINT("Couldn't get executable path from sysctl");
  987. return OS::get_executable_path();
  988. }
  989. // NetBSD does not always return a normalized path. For example if argv[0] is "./a.out" then executable path is "/home/netbsd/./a.out". Normalize with realpath:
  990. char resolved_path[MAXPATHLEN];
  991. realpath(buf, resolved_path);
  992. return String(resolved_path);
  993. #elif defined(__FreeBSD__)
  994. int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
  995. char buf[MAXPATHLEN];
  996. size_t len = sizeof(buf);
  997. if (sysctl(mib, 4, buf, &len, nullptr, 0) != 0) {
  998. WARN_PRINT("Couldn't get executable path from sysctl");
  999. return OS::get_executable_path();
  1000. }
  1001. return String::utf8(buf);
  1002. #elif defined(__APPLE__)
  1003. char temp_path[1];
  1004. uint32_t buff_size = 1;
  1005. _NSGetExecutablePath(temp_path, &buff_size);
  1006. char *resolved_path = new char[buff_size + 1];
  1007. if (_NSGetExecutablePath(resolved_path, &buff_size) == 1) {
  1008. WARN_PRINT("MAXPATHLEN is too small");
  1009. }
  1010. String path = String::utf8(resolved_path);
  1011. delete[] resolved_path;
  1012. return path;
  1013. #else
  1014. ERR_PRINT("Warning, don't know how to obtain executable path on this OS! Please override this function properly.");
  1015. return OS::get_executable_path();
  1016. #endif
  1017. }
  1018. void UnixTerminalLogger::log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type, const Vector<Ref<ScriptBacktrace>> &p_script_backtraces) {
  1019. if (!should_log(true)) {
  1020. return;
  1021. }
  1022. const char *err_details;
  1023. if (p_rationale && p_rationale[0]) {
  1024. err_details = p_rationale;
  1025. } else {
  1026. err_details = p_code;
  1027. }
  1028. // Disable color codes if stdout is not a TTY.
  1029. // This prevents Godot from writing ANSI escape codes when redirecting
  1030. // stdout and stderr to a file.
  1031. const bool tty = isatty(fileno(stdout));
  1032. const char *gray = tty ? "\E[0;90m" : "";
  1033. const char *red = tty ? "\E[0;91m" : "";
  1034. const char *red_bold = tty ? "\E[1;31m" : "";
  1035. const char *yellow = tty ? "\E[0;93m" : "";
  1036. const char *yellow_bold = tty ? "\E[1;33m" : "";
  1037. const char *magenta = tty ? "\E[0;95m" : "";
  1038. const char *magenta_bold = tty ? "\E[1;35m" : "";
  1039. const char *cyan = tty ? "\E[0;96m" : "";
  1040. const char *cyan_bold = tty ? "\E[1;36m" : "";
  1041. const char *reset = tty ? "\E[0m" : "";
  1042. const char *indent = "";
  1043. switch (p_type) {
  1044. case ERR_WARNING:
  1045. indent = " ";
  1046. logf_error("%sWARNING:%s %s\n", yellow_bold, yellow, err_details);
  1047. break;
  1048. case ERR_SCRIPT:
  1049. indent = " ";
  1050. logf_error("%sSCRIPT ERROR:%s %s\n", magenta_bold, magenta, err_details);
  1051. break;
  1052. case ERR_SHADER:
  1053. indent = " ";
  1054. logf_error("%sSHADER ERROR:%s %s\n", cyan_bold, cyan, err_details);
  1055. break;
  1056. case ERR_ERROR:
  1057. default:
  1058. indent = " ";
  1059. logf_error("%sERROR:%s %s\n", red_bold, red, err_details);
  1060. break;
  1061. }
  1062. logf_error("%s%sat: %s (%s:%i)%s\n", gray, indent, p_function, p_file, p_line, reset);
  1063. for (const Ref<ScriptBacktrace> &backtrace : p_script_backtraces) {
  1064. if (!backtrace->is_empty()) {
  1065. logf_error("%s%s%s\n", gray, backtrace->format(strlen(indent)).utf8().get_data(), reset);
  1066. }
  1067. }
  1068. }
  1069. UnixTerminalLogger::~UnixTerminalLogger() {}
  1070. OS_Unix::OS_Unix() {
  1071. #if !defined(__GLIBC__) && !defined(WEB_ENABLED)
  1072. _load_iconv();
  1073. #endif
  1074. Vector<Logger *> loggers;
  1075. loggers.push_back(memnew(UnixTerminalLogger));
  1076. _set_logger(memnew(CompositeLogger(loggers)));
  1077. }
  1078. #endif