1 /*
2 porting.cpp
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 */
5 
6 /*
7 This file is part of Freeminer.
8 
9 Freeminer is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 Freeminer  is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with Freeminer.  If not, see <http://www.gnu.org/licenses/>.
21 */
22 
23 /*
24 	Random portability stuff
25 
26 	See comments in porting.h
27 */
28 
29 #include "porting.h"
30 
31 #if defined(__FreeBSD__) || defined(__DragonFly__)
32 	#include <sys/types.h>
33 	#include <sys/sysctl.h>
34 #elif defined(_WIN32)
35 	#include <algorithm>
36 #endif
37 #if !defined(_WIN32)
38 	#include <unistd.h>
39 	#include <sys/utsname.h>
40 #endif
41 
42 #include "config.h"
43 #include "debug.h"
44 #include "filesys.h"
45 #include "log.h"
46 #include "util/string.h"
47 #include "settings.h"
48 #include <list>
49 
50 namespace porting
51 {
52 
53 /*
54 	Signal handler (grabs Ctrl-C on POSIX systems)
55 */
56 
57 bool g_killed = false;
58 
signal_handler_killstatus(void)59 bool * signal_handler_killstatus(void)
60 {
61 	return &g_killed;
62 }
63 
64 std::atomic_bool g_sighup, g_siginfo;
65 
66 #if !defined(_WIN32) // POSIX
67 	#include <signal.h>
68 
sigint_handler(int sig)69 void sigint_handler(int sig)
70 {
71 	switch(sig) {
72 #if defined(SIGINFO)
73 		case SIGINFO:
74 			g_siginfo = true;
75 		break;
76 #endif
77 		case SIGHUP:
78 			g_sighup = true;
79 		break;
80 		case SIGINT:
81 		case SIGTERM:
82 
83 	if(g_killed == false)
84 	{
85 		g_killed = true;
86 
87 		dstream<<"INFO: sigint_handler(): "
88 				<<"Ctrl-C pressed, shutting down."<<std::endl;
89 
90 		// Comment out for less clutter when testing scripts
91 		/*dstream<<DTIME<<"INFO: sigint_handler(): "
92 				<<"Printing debug stacks"<<std::endl;
93 		debug_stacks_print();*/
94 	}
95 		break;
96 
97 		default:
98 		(void)signal(sig, SIG_DFL);
99 	}
100 
101 }
102 
signal_handler_init(void)103 void signal_handler_init(void)
104 {
105 	g_sighup = false;
106 	g_siginfo = false;
107 
108 	signal(SIGINT, sigint_handler);
109 	signal(SIGTERM, sigint_handler);
110 	signal(SIGHUP, sigint_handler);
111 #if defined(SIGINFO)
112 	signal(SIGINFO, sigint_handler);
113 #endif
114 }
115 
116 #else // _WIN32
117 	#include <signal.h>
118 
event_handler(DWORD sig)119 	BOOL WINAPI event_handler(DWORD sig)
120 	{
121 		switch(sig)
122 		{
123 		case CTRL_C_EVENT:
124 		case CTRL_CLOSE_EVENT:
125 		case CTRL_LOGOFF_EVENT:
126 		case CTRL_SHUTDOWN_EVENT:
127 
128 			if(g_killed == false)
129 			{
130 				dstream<<DTIME<<"INFO: event_handler(): "
131 						<<"Ctrl+C, Close Event, Logoff Event or Shutdown Event, shutting down."<<std::endl;
132 				// Comment out for less clutter when testing scripts
133 				/*dstream<<DTIME<<"INFO: event_handler(): "
134 						<<"Printing debug stacks"<<std::endl;
135 				debug_stacks_print();*/
136 
137 				g_killed = true;
138 			}
139 			else
140 			{
141 				(void)signal(SIGINT, SIG_DFL);
142 			}
143 
144 			break;
145 		case CTRL_BREAK_EVENT:
146 			break;
147 		}
148 
149 		return TRUE;
150 	}
151 
signal_handler_init(void)152 void signal_handler_init(void)
153 {
154 	SetConsoleCtrlHandler( (PHANDLER_ROUTINE)event_handler,TRUE);
155 }
156 
157 #endif
158 
159 
160 /*
161 	Multithreading support
162 */
getNumberOfProcessors()163 int getNumberOfProcessors() {
164 #if defined(_SC_NPROCESSORS_CONF)
165 	return sysconf(_SC_NPROCESSORS_CONF);
166 
167 #elif defined(_SC_NPROCESSORS_ONLN)
168 
169 	return sysconf(_SC_NPROCESSORS_ONLN);
170 
171 #elif defined(__FreeBSD__) || defined(__DragonFly__) || defined(__APPLE__)
172 
173 	unsigned int len, count;
174 	len = sizeof(count);
175 	return sysctlbyname("hw.ncpu", &count, &len, NULL, 0);
176 
177 #elif defined(_GNU_SOURCE)
178 
179 	return get_nprocs_conf();
180 
181 #elif defined(_WIN32)
182 
183 	SYSTEM_INFO sysinfo;
184 	GetSystemInfo(&sysinfo);
185 	return sysinfo.dwNumberOfProcessors;
186 
187 #elif defined(PTW32_VERSION) || defined(__hpux)
188 
189 	return pthread_num_processors_np();
190 
191 #else
192 
193 	return 1;
194 
195 #endif
196 }
197 
198 
199 #ifndef __ANDROID__
threadBindToProcessor(threadid_t tid,int pnumber)200 bool threadBindToProcessor(threadid_t tid, int pnumber) {
201 #if defined(_WIN32)
202 
203 	HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, 0, tid);
204 	if (!hThread)
205 		return false;
206 
207 	bool success = SetThreadAffinityMask(hThread, 1 << pnumber) != 0;
208 
209 	CloseHandle(hThread);
210 	return success;
211 
212 #elif (defined(__FreeBSD__) && (__FreeBSD_version >= 702106)) \
213 	|| defined(__linux) || defined(linux)
214 
215 	cpu_set_t cpuset;
216 
217 	CPU_ZERO(&cpuset);
218 	CPU_SET(pnumber, &cpuset);
219 	return pthread_setaffinity_np(tid, sizeof(cpuset), &cpuset) == 0;
220 
221 #elif defined(__sun) || defined(sun)
222 
223 	return processor_bind(P_LWPID, MAKE_LWPID_PTHREAD(tid),
224 						pnumber, NULL) == 0;
225 
226 #elif defined(_AIX)
227 
228 	return bindprocessor(BINDTHREAD, (tid_t)tid, pnumber) == 0;
229 
230 #elif defined(__hpux) || defined(hpux)
231 
232 	pthread_spu_t answer;
233 
234 	return pthread_processor_bind_np(PTHREAD_BIND_ADVISORY_NP,
235 									&answer, pnumber, tid) == 0;
236 
237 #elif defined(__APPLE__)
238 
239 	struct thread_affinity_policy tapol;
240 
241 	thread_port_t threadport = pthread_mach_thread_np(tid);
242 	tapol.affinity_tag = pnumber + 1;
243 	return thread_policy_set(threadport, THREAD_AFFINITY_POLICY,
244 			(thread_policy_t)&tapol, THREAD_AFFINITY_POLICY_COUNT) == KERN_SUCCESS;
245 
246 #else
247 
248 	return false;
249 
250 #endif
251 }
252 #endif
253 
threadSetPriority(threadid_t tid,int prio)254 bool threadSetPriority(threadid_t tid, int prio) {
255 #if defined(_WIN32)
256 
257 	HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, 0, tid);
258 	if (!hThread)
259 		return false;
260 
261 	bool success = SetThreadPriority(hThread, prio) != 0;
262 
263 	CloseHandle(hThread);
264 	return success;
265 
266 #else
267 
268 	struct sched_param sparam;
269 	int policy;
270 
271 	if (pthread_getschedparam(tid, &policy, &sparam) != 0)
272 		return false;
273 
274 	int min = sched_get_priority_min(policy);
275 	int max = sched_get_priority_max(policy);
276 
277 	sparam.sched_priority = min + prio * (max - min) / THREAD_PRIORITY_HIGHEST;
278 	return pthread_setschedparam(tid, policy, &sparam) == 0;
279 
280 #endif
281 }
282 
283 
284 /*
285 	Path mangler
286 */
287 
288 // Default to RUN_IN_PLACE style relative paths
289 std::string path_share = "..";
290 std::string path_user = "..";
291 
getDataPath(const char * subpath)292 std::string getDataPath(const char *subpath)
293 {
294 	return path_share + DIR_DELIM + subpath;
295 }
296 
pathRemoveFile(char * path,char delim)297 void pathRemoveFile(char *path, char delim)
298 {
299 	// Remove filename and path delimiter
300 	int i;
301 	for(i = strlen(path)-1; i>=0; i--)
302 	{
303 		if(path[i] == delim)
304 			break;
305 	}
306 	path[i] = 0;
307 }
308 
detectMSVCBuildDir(char * c_path)309 bool detectMSVCBuildDir(char *c_path)
310 {
311 	std::string path(c_path);
312 	const char *ends[] = {"bin\\Release", "bin\\Build", "bin\\Debug", NULL};
313 	return (removeStringEnd(path, ends) != "");
314 }
315 
get_sysinfo()316 std::string get_sysinfo()
317 {
318 #ifdef _WIN32
319 	OSVERSIONINFO osvi;
320 	std::ostringstream oss;
321 	std::string tmp;
322 	ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
323 	osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
324 	GetVersionEx(&osvi);
325 	tmp = osvi.szCSDVersion;
326 	std::replace(tmp.begin(), tmp.end(), ' ', '_');
327 
328 	oss << "Windows/" << osvi.dwMajorVersion << "."
329 		<< osvi.dwMinorVersion;
330 	if(osvi.szCSDVersion[0])
331 		oss << "-" << tmp;
332 	oss << " ";
333 	#ifdef _WIN64
334 	oss << "x86_64";
335 	#else
336 	BOOL is64 = FALSE;
337 	if(IsWow64Process(GetCurrentProcess(), &is64) && is64)
338 		oss << "x86_64"; // 32-bit app on 64-bit OS
339 	else
340 		oss << "x86";
341 	#endif
342 
343 	return oss.str();
344 #else
345 	struct utsname osinfo;
346 	uname(&osinfo);
347 	return std::string(osinfo.sysname) + "/"
348 		+ osinfo.release + " " + osinfo.machine;
349 #endif
350 }
351 
initializePaths()352 void initializePaths()
353 {
354 #if RUN_IN_PLACE
355 	/*
356 		Use relative paths if RUN_IN_PLACE
357 	*/
358 
359 	infostream<<"Using relative paths (RUN_IN_PLACE)"<<std::endl;
360 
361 	/*
362 		Windows
363 	*/
364 	#if defined(_WIN32)
365 
366 	const DWORD buflen = 1000;
367 	char buf[buflen];
368 	DWORD len;
369 
370 	// Find path of executable and set path_share relative to it
371 	len = GetModuleFileName(GetModuleHandle(NULL), buf, buflen);
372 	assert(len < buflen);
373 	pathRemoveFile(buf, '\\');
374 
375 	if(detectMSVCBuildDir(buf)){
376 		infostream<<"MSVC build directory detected"<<std::endl;
377 		path_share = std::string(buf) + "\\..\\..";
378 		path_user = std::string(buf) + "\\..\\..";
379 	}
380 	else{
381 	#if STATIC_BUILD
382 		path_share = std::string(buf) + "\\.";
383 		path_user = std::string(buf) + "\\.";
384 	#else
385 		path_share = std::string(buf) + "\\..";
386 		path_user = std::string(buf) + "\\..";
387 	#endif
388 	}
389 
390 	/*
391 		Linux
392 	*/
393 	#elif defined(linux) || defined(__linux)
394 
395 	char buf[BUFSIZ];
396 	memset(buf, 0, BUFSIZ);
397 	// Get path to executable
398 	assert(readlink("/proc/self/exe", buf, BUFSIZ-1) != -1);
399 
400 	pathRemoveFile(buf, '/');
401 
402 	path_share = std::string(buf) + "/..";
403 	path_user = std::string(buf) + "/..";
404 
405 	/*
406 		OS X
407 	*/
408 	#elif defined(__APPLE__)
409 
410 	//https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man3/dyld.3.html
411 	//TODO: Test this code
412 	char buf[BUFSIZ];
413 	uint32_t len = sizeof(buf);
414 	assert(_NSGetExecutablePath(buf, &len) != -1);
415 
416 	pathRemoveFile(buf, '/');
417 
418 	path_share = std::string(buf) + "/..";
419 	path_user = std::string(buf) + "/..";
420 
421 	/*
422 		FreeBSD
423 	*/
424 	#elif defined(__FreeBSD__) || defined(__DragonFly__)
425 
426 	int mib[4];
427 	char buf[BUFSIZ];
428 	size_t len = sizeof(buf);
429 
430 	mib[0] = CTL_KERN;
431 	mib[1] = KERN_PROC;
432 	mib[2] = KERN_PROC_PATHNAME;
433 	mib[3] = -1;
434 	assert(sysctl(mib, 4, buf, &len, NULL, 0) != -1);
435 
436 	pathRemoveFile(buf, '/');
437 
438 	path_share = std::string(buf) + "/..";
439 	path_user = std::string(buf) + "/..";
440 
441 	#else
442 
443 	//TODO: Get path of executable. This assumes working directory is bin/
444 	dstream<<"WARNING: Relative path not properly supported on this platform"
445 			<<std::endl;
446 
447 	/* scriptapi no longer allows paths that start with "..", so assuming that
448 	   the current working directory is bin/, strip off the last component. */
449 	char *cwd = getcwd(NULL, 0);
450 	pathRemoveFile(cwd, '/');
451 	path_share = std::string(cwd);
452 	path_user = std::string(cwd);
453 
454 	#endif
455 
456 #else // RUN_IN_PLACE
457 
458 	/*
459 		Use platform-specific paths otherwise
460 	*/
461 
462 	infostream<<"Using system-wide paths (NOT RUN_IN_PLACE)"<<std::endl;
463 
464 	/*
465 		Windows
466 	*/
467 	#if defined(_WIN32)
468 
469 	const DWORD buflen = 1000;
470 	char buf[buflen];
471 	DWORD len;
472 
473 	// Find path of executable and set path_share relative to it
474 	len = GetModuleFileName(GetModuleHandle(NULL), buf, buflen);
475 	assert(len < buflen);
476 	pathRemoveFile(buf, '\\');
477 
478 	// Use ".\bin\.."
479 	path_share = std::string(buf) + "\\..";
480 
481 	// Use "C:\Documents and Settings\user\Application Data\<PROJECT_NAME>"
482 	len = GetEnvironmentVariable("APPDATA", buf, buflen);
483 	assert(len < buflen);
484 	path_user = std::string(buf) + DIR_DELIM + PROJECT_NAME;
485 
486 	/*
487 		Linux
488 	*/
489 	#elif defined(linux) || defined(__linux)
490 
491 	// Get path to executable
492 	std::string bindir = "";
493 	{
494 		char buf[BUFSIZ];
495 		memset(buf, 0, BUFSIZ);
496 		if (readlink("/proc/self/exe", buf, BUFSIZ-1) == -1) {
497 			errorstream << "Unable to read bindir "<< std::endl;
498 #ifndef __ANDROID__
499 			assert("Unable to read bindir" == 0);
500 #endif
501 		} else {
502 			pathRemoveFile(buf, '/');
503 			bindir = buf;
504 		}
505 	}
506 
507 	// Find share directory from these.
508 	// It is identified by containing the subdirectory "builtin".
509 	std::list<std::string> trylist;
510 	std::string static_sharedir = STATIC_SHAREDIR;
511 	if(static_sharedir != "" && static_sharedir != ".")
512 		trylist.push_back(static_sharedir);
513 	trylist.push_back(
514 			bindir + DIR_DELIM + ".." + DIR_DELIM + "share" + DIR_DELIM + PROJECT_NAME);
515 	trylist.push_back(bindir + DIR_DELIM + "..");
516 #ifdef __ANDROID__
517 	trylist.push_back(path_user);
518 #endif
519 
520 	for(std::list<std::string>::const_iterator i = trylist.begin();
521 			i != trylist.end(); i++)
522 	{
523 		const std::string &trypath = *i;
524 		if(!fs::PathExists(trypath) || !fs::PathExists(trypath + DIR_DELIM + "builtin")){
525 			dstream<<"WARNING: system-wide share not found at \""
526 					<<trypath<<"\""<<std::endl;
527 			continue;
528 		}
529 		// Warn if was not the first alternative
530 		if(i != trylist.begin()){
531 			dstream<<"WARNING: system-wide share found at \""
532 					<<trypath<<"\""<<std::endl;
533 		}
534 		path_share = trypath;
535 		break;
536 	}
537 #ifndef __ANDROID__
538 	path_user = std::string(getenv("HOME")) + DIR_DELIM + "." + PROJECT_NAME;
539 #endif
540 
541 	/*
542 		OS X
543 	*/
544 	#elif defined(__APPLE__)
545 
546 	// Code based on
547 	// http://stackoverflow.com/questions/516200/relative-paths-not-working-in-xcode-c
548 	CFBundleRef main_bundle = CFBundleGetMainBundle();
549 	CFURLRef resources_url = CFBundleCopyResourcesDirectoryURL(main_bundle);
550 	char path[PATH_MAX];
551 	if(CFURLGetFileSystemRepresentation(resources_url, TRUE, (UInt8 *)path, PATH_MAX))
552 	{
553 		dstream<<"Bundle resource path: "<<path<<std::endl;
554 		//chdir(path);
555 		path_share = std::string(path) + DIR_DELIM + "share";
556 	}
557 	else
558 	{
559 		// error!
560 		dstream<<"WARNING: Could not determine bundle resource path"<<std::endl;
561 	}
562 	CFRelease(resources_url);
563 
564 	path_user = std::string(getenv("HOME")) + "/Library/Application Support/" + PROJECT_NAME;
565 
566 	#else // FreeBSD, and probably many other POSIX-like systems.
567 
568 	path_share = STATIC_SHAREDIR;
569 	path_user = std::string(getenv("HOME")) + DIR_DELIM + "." + PROJECT_NAME;
570 
571 	#endif
572 
573 #endif // RUN_IN_PLACE
574 }
575 
576 static irr::IrrlichtDevice* device;
577 
initIrrlicht(irr::IrrlichtDevice * _device)578 void initIrrlicht(irr::IrrlichtDevice * _device) {
579 	device = _device;
580 }
581 
582 #ifndef SERVER
getWindowSize()583 v2u32 getWindowSize() {
584 	return device->getVideoDriver()->getScreenSize();
585 }
586 
587 #ifndef __ANDROID__
588 
getDisplayDensity()589 float getDisplayDensity() {
590 	float gui_scaling = g_settings->getFloat("gui_scaling");
591 	// using Y here feels like a bug, this needs to be discussed later!
592 	if (getWindowSize().Y <= 800) {
593 		return (2.0/3.0) * gui_scaling;
594 	}
595 	if (getWindowSize().Y <= 1280) {
596 		return 1.0 * gui_scaling;
597 	}
598 
599 	return (4.0/3.0) * gui_scaling;
600 }
601 
getDisplaySize()602 v2u32 getDisplaySize() {
603 	IrrlichtDevice *nulldevice = createDevice(video::EDT_NULL);
604 
605 	core::dimension2d<u32> deskres = nulldevice->getVideoModeList()->getDesktopResolution();
606 	nulldevice -> drop();
607 
608 	return deskres;
609 }
610 #endif
611 #endif
612 
613 } //namespace porting
614 
615 
get_time_us()616 extern "C" unsigned int get_time_us() {
617 	return porting::getTimeUs();
618 }
619