1 /*************************************************************************/
2 /* os_unix.cpp */
3 /*************************************************************************/
4 /* This file is part of: */
5 /* GODOT ENGINE */
6 /* https://godotengine.org */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
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
32 #ifdef UNIX_ENABLED
33
34 #include "core/os/thread_dummy.h"
35 #include "memory_pool_static_malloc.h"
36 #include "mutex_posix.h"
37 #include "os/memory_pool_dynamic_static.h"
38 #include "semaphore_posix.h"
39 #include "thread_posix.h"
40
41 //#include "core/io/file_access_buffered_fa.h"
42 #include "dir_access_unix.h"
43 #include "file_access_unix.h"
44 #include "packet_peer_udp_posix.h"
45 #include "stream_peer_tcp_posix.h"
46 #include "tcp_server_posix.h"
47
48 #ifdef __APPLE__
49 #include <mach-o/dyld.h>
50 #endif
51
52 #if defined(__FreeBSD__) || defined(__OpenBSD__)
53 #include <sys/param.h>
54 #include <sys/sysctl.h>
55 #endif
56 #include "globals.h"
57 #include <assert.h>
58 #include <errno.h>
59 #include <poll.h>
60 #include <signal.h>
61 #include <stdarg.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <sys/time.h>
65 #include <sys/wait.h>
66
67 extern bool _print_error_enabled;
68
print_error(const char * p_function,const char * p_file,int p_line,const char * p_code,const char * p_rationale,ErrorType p_type)69 void OS_Unix::print_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, ErrorType p_type) {
70
71 if (!_print_error_enabled)
72 return;
73
74 const char *err_details;
75 if (p_rationale && p_rationale[0])
76 err_details = p_rationale;
77 else
78 err_details = p_code;
79
80 switch (p_type) {
81 case ERR_ERROR:
82 print("\E[1;31mERROR: %s: \E[0m\E[1m%s\n", p_function, err_details);
83 print("\E[0;31m At: %s:%i.\E[0m\n", p_file, p_line);
84 break;
85 case ERR_WARNING:
86 print("\E[1;33mWARNING: %s: \E[0m\E[1m%s\n", p_function, err_details);
87 print("\E[0;33m At: %s:%i.\E[0m\n", p_file, p_line);
88 break;
89 case ERR_SCRIPT:
90 print("\E[1;35mSCRIPT ERROR: %s: \E[0m\E[1m%s\n", p_function, err_details);
91 print("\E[0;35m At: %s:%i.\E[0m\n", p_file, p_line);
92 break;
93 }
94 }
95
debug_break()96 void OS_Unix::debug_break() {
97
98 assert(false);
99 };
100
get_audio_driver_count() const101 int OS_Unix::get_audio_driver_count() const {
102
103 return 1;
104 }
get_audio_driver_name(int p_driver) const105 const char *OS_Unix::get_audio_driver_name(int p_driver) const {
106
107 return "dummy";
108 }
109
unix_initialize_audio(int p_audio_driver)110 int OS_Unix::unix_initialize_audio(int p_audio_driver) {
111
112 return 0;
113 }
114
115 static MemoryPoolStaticMalloc *mempool_static = NULL;
116 static MemoryPoolDynamicStatic *mempool_dynamic = NULL;
117
118 // Very simple signal handler to reap processes where ::execute was called with
119 // !p_blocking
handle_sigchld(int sig)120 void handle_sigchld(int sig) {
121 int saved_errno = errno;
122 while (waitpid((pid_t)(-1), 0, WNOHANG) > 0) {
123 }
124 errno = saved_errno;
125 }
126
initialize_core()127 void OS_Unix::initialize_core() {
128
129 #ifdef NO_PTHREADS
130 ThreadDummy::make_default();
131 SemaphoreDummy::make_default();
132 MutexDummy::make_default();
133 #else
134 ThreadPosix::make_default();
135 SemaphorePosix::make_default();
136 MutexPosix::make_default();
137 #endif
138 FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_RESOURCES);
139 FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_USERDATA);
140 FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_FILESYSTEM);
141 //FileAccessBufferedFA<FileAccessUnix>::make_default();
142 DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_RESOURCES);
143 DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_USERDATA);
144 DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_FILESYSTEM);
145
146 #ifndef NO_NETWORK
147 TCPServerPosix::make_default();
148 StreamPeerTCPPosix::make_default();
149 PacketPeerUDPPosix::make_default();
150 IP_Unix::make_default();
151 #endif
152 mempool_static = new MemoryPoolStaticMalloc;
153 mempool_dynamic = memnew(MemoryPoolDynamicStatic);
154
155 ticks_start = 0;
156 ticks_start = get_ticks_usec();
157
158 struct sigaction sa;
159 sa.sa_handler = &handle_sigchld;
160 sigemptyset(&sa.sa_mask);
161 sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
162 if (sigaction(SIGCHLD, &sa, 0) == -1) {
163 perror("ERROR sigaction() failed:");
164 }
165 }
166
finalize_core()167 void OS_Unix::finalize_core() {
168
169 if (mempool_dynamic)
170 memdelete(mempool_dynamic);
171 delete mempool_static;
172 }
173
vprint(const char * p_format,va_list p_list,bool p_stder)174 void OS_Unix::vprint(const char *p_format, va_list p_list, bool p_stder) {
175
176 if (p_stder) {
177
178 vfprintf(stderr, p_format, p_list);
179 fflush(stderr);
180 } else {
181
182 vprintf(p_format, p_list);
183 fflush(stdout);
184 }
185 }
186
print(const char * p_format,...)187 void OS_Unix::print(const char *p_format, ...) {
188
189 va_list argp;
190 va_start(argp, p_format);
191 vprintf(p_format, argp);
192 va_end(argp);
193 }
alert(const String & p_alert,const String & p_title)194 void OS_Unix::alert(const String &p_alert, const String &p_title) {
195
196 fprintf(stderr, "ERROR: %s\n", p_alert.utf8().get_data());
197 }
198
has_data(FILE * p_fd,int timeout_usec=0)199 static int has_data(FILE *p_fd, int timeout_usec = 0) {
200
201 fd_set readset;
202 int fd = fileno(p_fd);
203 FD_ZERO(&readset);
204 FD_SET(fd, &readset);
205 timeval time;
206 time.tv_sec = 0;
207 time.tv_usec = timeout_usec;
208 int res = 0; //select(fd + 1, &readset, NULL, NULL, &time);
209 return res > 0;
210 };
211
get_stdin_string(bool p_block)212 String OS_Unix::get_stdin_string(bool p_block) {
213
214 String ret;
215 if (p_block) {
216 char buff[1024];
217 ret = stdin_buf + fgets(buff, 1024, stdin);
218 stdin_buf = "";
219 return ret;
220 };
221
222 while (has_data(stdin)) {
223
224 char ch;
225 read(fileno(stdin), &ch, 1);
226 if (ch == '\n') {
227 ret = stdin_buf;
228 stdin_buf = "";
229 return ret;
230 } else {
231 char str[2] = { ch, 0 };
232 stdin_buf += str;
233 };
234 };
235
236 return "";
237 }
238
get_name()239 String OS_Unix::get_name() {
240
241 return "Unix";
242 }
243
get_unix_time() const244 uint64_t OS_Unix::get_unix_time() const {
245
246 return time(NULL);
247 };
248
get_system_time_secs() const249 uint64_t OS_Unix::get_system_time_secs() const {
250 struct timeval tv_now;
251 gettimeofday(&tv_now, NULL);
252 //localtime(&tv_now.tv_usec);
253 //localtime((const long *)&tv_now.tv_usec);
254 return uint64_t(tv_now.tv_sec);
255 }
256
get_date(bool utc) const257 OS::Date OS_Unix::get_date(bool utc) const {
258
259 time_t t = time(NULL);
260 struct tm *lt;
261 if (utc)
262 lt = gmtime(&t);
263 else
264 lt = localtime(&t);
265 Date ret;
266 ret.year = 1900 + lt->tm_year;
267 // Index starting at 1 to match OS_Unix::get_date
268 // and Windows SYSTEMTIME and tm_mon follows the typical structure
269 // of 0-11, noted here: http://www.cplusplus.com/reference/ctime/tm/
270 ret.month = (Month)(lt->tm_mon + 1);
271 ret.day = lt->tm_mday;
272 ret.weekday = (Weekday)lt->tm_wday;
273 ret.dst = lt->tm_isdst;
274
275 return ret;
276 }
277
get_time(bool utc) const278 OS::Time OS_Unix::get_time(bool utc) const {
279 time_t t = time(NULL);
280 struct tm *lt;
281 if (utc)
282 lt = gmtime(&t);
283 else
284 lt = localtime(&t);
285 Time ret;
286 ret.hour = lt->tm_hour;
287 ret.min = lt->tm_min;
288 ret.sec = lt->tm_sec;
289 get_time_zone_info();
290 return ret;
291 }
292
get_time_zone_info() const293 OS::TimeZoneInfo OS_Unix::get_time_zone_info() const {
294 time_t t = time(NULL);
295 struct tm *lt = localtime(&t);
296 char name[16];
297 strftime(name, 16, "%Z", lt);
298 name[15] = 0;
299 TimeZoneInfo ret;
300 ret.name = name;
301
302 char bias_buf[16];
303 strftime(bias_buf, 16, "%z", lt);
304 int bias;
305 bias_buf[15] = 0;
306 sscanf(bias_buf, "%d", &bias);
307
308 // convert from ISO 8601 (1 minute=1, 1 hour=100) to minutes
309 int hour = (int)bias / 100;
310 int minutes = bias % 100;
311 if (bias < 0)
312 ret.bias = hour * 60 - minutes;
313 else
314 ret.bias = hour * 60 + minutes;
315
316 return ret;
317 }
318
delay_usec(uint32_t p_usec) const319 void OS_Unix::delay_usec(uint32_t p_usec) const {
320
321 usleep(p_usec);
322 }
get_ticks_usec() const323 uint64_t OS_Unix::get_ticks_usec() const {
324
325 struct timeval tv_now;
326 gettimeofday(&tv_now, NULL);
327
328 uint64_t longtime = (uint64_t)tv_now.tv_usec + (uint64_t)tv_now.tv_sec * 1000000L;
329 longtime -= ticks_start;
330
331 return longtime;
332 }
333
execute(const String & p_path,const List<String> & p_arguments,bool p_blocking,ProcessID * r_child_id,String * r_pipe,int * r_exitcode,bool read_stderr)334 Error OS_Unix::execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id, String *r_pipe, int *r_exitcode, bool read_stderr) {
335
336 if (p_blocking && r_pipe) {
337
338 String argss;
339 argss = "\"" + p_path + "\"";
340
341 for (int i = 0; i < p_arguments.size(); i++) {
342
343 argss += String(" \"") + p_arguments[i] + "\"";
344 }
345
346 if (read_stderr) {
347 argss += " 2>&1"; // Read stderr too
348 } else {
349 argss += " 2>/dev/null"; //silence stderr
350 }
351 FILE *f = popen(argss.utf8().get_data(), "r");
352
353 ERR_FAIL_COND_V(!f, ERR_CANT_OPEN);
354
355 char buf[65535];
356 while (fgets(buf, 65535, f)) {
357
358 (*r_pipe) += buf;
359 }
360
361 int rv = pclose(f);
362 if (r_exitcode)
363 *r_exitcode = rv;
364
365 return OK;
366 }
367
368 pid_t pid = fork();
369 ERR_FAIL_COND_V(pid < 0, ERR_CANT_FORK);
370
371 if (pid == 0) {
372 // is child
373 Vector<CharString> cs;
374 cs.push_back(p_path.utf8());
375 for (int i = 0; i < p_arguments.size(); i++)
376 cs.push_back(p_arguments[i].utf8());
377
378 Vector<char *> args;
379 for (int i = 0; i < cs.size(); i++)
380 args.push_back((char *)cs[i].get_data()); // shitty C cast
381 args.push_back(0);
382
383 execv(p_path.utf8().get_data(), &args[0]);
384 // still alive? something failed..
385 fprintf(stderr, "**ERROR** OS_Unix::execute - Could not create child process while executing: %s\n", p_path.utf8().get_data());
386 abort();
387 }
388
389 if (p_blocking) {
390
391 int status;
392 pid_t rpid = waitpid(pid, &status, 0);
393 if (r_exitcode)
394 *r_exitcode = WEXITSTATUS(status);
395 } else {
396
397 if (r_child_id)
398 *r_child_id = pid;
399 }
400
401 return OK;
402 }
403
kill(const ProcessID & p_pid)404 Error OS_Unix::kill(const ProcessID &p_pid) {
405
406 int ret = ::kill(p_pid, SIGKILL);
407 if (!ret) {
408 //avoid zombie process
409 int st;
410 ::waitpid(p_pid, &st, 0);
411 }
412 return ret ? ERR_INVALID_PARAMETER : OK;
413 }
414
get_process_ID() const415 int OS_Unix::get_process_ID() const {
416
417 return getpid();
418 };
419
has_environment(const String & p_var) const420 bool OS_Unix::has_environment(const String &p_var) const {
421
422 return getenv(p_var.utf8().get_data()) != NULL;
423 }
424
get_locale() const425 String OS_Unix::get_locale() const {
426
427 if (!has_environment("LANG"))
428 return "en";
429
430 String locale = get_environment("LANG");
431 int tp = locale.find(".");
432 if (tp != -1)
433 locale = locale.substr(0, tp);
434 return locale;
435 }
436
set_cwd(const String & p_cwd)437 Error OS_Unix::set_cwd(const String &p_cwd) {
438
439 if (chdir(p_cwd.utf8().get_data()) != 0)
440 return ERR_CANT_OPEN;
441
442 return OK;
443 }
444
get_environment(const String & p_var) const445 String OS_Unix::get_environment(const String &p_var) const {
446
447 if (getenv(p_var.utf8().get_data()))
448 return getenv(p_var.utf8().get_data());
449 return "";
450 }
451
get_processor_count() const452 int OS_Unix::get_processor_count() const {
453
454 return sysconf(_SC_NPROCESSORS_CONF);
455 }
456
get_data_dir() const457 String OS_Unix::get_data_dir() const {
458
459 String an = get_safe_application_name();
460 if (an != "") {
461
462 if (has_environment("HOME")) {
463
464 bool use_godot = Globals::get_singleton()->get("application/use_shared_user_dir");
465 if (use_godot)
466 return get_environment("HOME") + "/.godot/app_userdata/" + an;
467 else
468 return get_environment("HOME") + "/." + an;
469 }
470 }
471
472 return Globals::get_singleton()->get_resource_path();
473 }
474
get_installed_templates_path() const475 String OS_Unix::get_installed_templates_path() const {
476 String p = get_global_settings_path();
477 if (p != "")
478 return p + "/templates/";
479 else
480 return "";
481 }
482
get_executable_path() const483 String OS_Unix::get_executable_path() const {
484
485 #ifdef __linux__
486 //fix for running from a symlink
487 char buf[256];
488 memset(buf, 0, 256);
489 readlink("/proc/self/exe", buf, sizeof(buf));
490 String b;
491 b.parse_utf8(buf);
492 if (b == "") {
493 WARN_PRINT("Couldn't get executable path from /proc/self/exe, using argv[0]");
494 return OS::get_executable_path();
495 }
496 return b;
497 #elif defined(__OpenBSD__)
498 char resolved_path[MAXPATHLEN];
499
500 realpath(OS::get_executable_path().utf8().get_data(), resolved_path);
501
502 return String(resolved_path);
503
504 #elif defined(__FreeBSD__)
505 int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
506 char buf[MAXPATHLEN];
507 size_t len = sizeof(buf);
508 if (sysctl(mib, 4, buf, &len, NULL, 0) != 0) {
509 WARN_PRINT("Couldn't get executable path from sysctl");
510 return OS::get_executable_path();
511 }
512 String b;
513 b.parse_utf8(buf);
514 return b;
515 #elif defined(__APPLE__)
516 char temp_path[1];
517 uint32_t buff_size = 1;
518 _NSGetExecutablePath(temp_path, &buff_size);
519
520 char *resolved_path = new char[buff_size + 1];
521
522 if (_NSGetExecutablePath(resolved_path, &buff_size) == 1)
523 WARN_PRINT("MAXPATHLEN is too small");
524
525 String path(resolved_path);
526 delete[] resolved_path;
527
528 return path;
529 #else
530 ERR_PRINT("Warning, don't know how to obtain executable path on this OS! Please override this function properly.");
531 return OS::get_executable_path();
532 #endif
533 }
534
535 #endif
536