os_unix.cpp 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247
  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. logf_error("%s%s%s\n", gray, backtrace->format(strlen(indent)).utf8().get_data(), reset);
  1065. }
  1066. }
  1067. UnixTerminalLogger::~UnixTerminalLogger() {}
  1068. OS_Unix::OS_Unix() {
  1069. #if !defined(__GLIBC__) && !defined(WEB_ENABLED)
  1070. _load_iconv();
  1071. #endif
  1072. Vector<Logger *> loggers;
  1073. loggers.push_back(memnew(UnixTerminalLogger));
  1074. _set_logger(memnew(CompositeLogger(loggers)));
  1075. }
  1076. #endif