executionEnvironment.cxx 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  1. // Filename: executionEnvironment.cxx
  2. // Created by: drose (15May00)
  3. //
  4. ////////////////////////////////////////////////////////////////////
  5. //
  6. // PANDA 3D SOFTWARE
  7. // Copyright (c) Carnegie Mellon University. All rights reserved.
  8. //
  9. // All use of this software is subject to the terms of the revised BSD
  10. // license. You should have received a copy of this license along
  11. // with this source code in a file named "LICENSE."
  12. //
  13. ////////////////////////////////////////////////////////////////////
  14. #include "executionEnvironment.h"
  15. #include "pandaVersion.h"
  16. #include <assert.h>
  17. #include <errno.h>
  18. #include <stdio.h> // for perror
  19. #ifdef WIN32_VC
  20. // Windows requires this for getcwd().
  21. #include <direct.h>
  22. #define getcwd _getcwd
  23. // And this is for GetModuleFileName().
  24. #include <windows.h>
  25. #endif
  26. #ifdef __APPLE__
  27. // This is for _NSGetExecutablePath() and _NSGetEnviron().
  28. #include <mach-o/dyld.h>
  29. #ifndef BUILD_IPHONE
  30. #include <crt_externs.h> // For some reason, not in the IPhone SDK.
  31. #endif
  32. #define environ (*_NSGetEnviron())
  33. #endif
  34. #ifdef IS_FREEBSD
  35. extern char **environ;
  36. // This is for sysctl.
  37. #include <sys/types.h>
  38. #include <sys/sysctl.h>
  39. #endif
  40. #ifdef HAVE_PYTHON
  41. #include "Python.h"
  42. #endif
  43. // We define the symbol PREREAD_ENVIRONMENT if we cannot rely on
  44. // getenv() to read environment variables at static init time. In
  45. // this case, we must read all of the environment variables directly
  46. // and cache them locally.
  47. #ifndef STATIC_INIT_GETENV
  48. #define PREREAD_ENVIRONMENT
  49. #endif
  50. // We define the symbol HAVE_GLOBAL_ARGV if we have global variables
  51. // named GLOBAL_ARGC/GLOBAL_ARGV that we can read at static init time
  52. // to determine our command-line arguments.
  53. #if defined(HAVE_GLOBAL_ARGV) && defined(PROTOTYPE_GLOBAL_ARGV)
  54. extern char **GLOBAL_ARGV;
  55. extern int GLOBAL_ARGC;
  56. #endif
  57. // Linux with GNU libc does have global argv/argc variables, but we
  58. // can't safely access them at stat init time--at least, not in libc5.
  59. // (It does seem to work with glibc2, however.)
  60. ExecutionEnvironment *ExecutionEnvironment::_global_ptr = NULL;
  61. ////////////////////////////////////////////////////////////////////
  62. // Function: ExecutionEnvironment::Constructor
  63. // Access: Private
  64. // Description: You shouldn't need to construct one of these; there's
  65. // only one and it constructs itself.
  66. ////////////////////////////////////////////////////////////////////
  67. ExecutionEnvironment::
  68. ExecutionEnvironment() {
  69. read_environment_variables();
  70. read_args();
  71. }
  72. ////////////////////////////////////////////////////////////////////
  73. // Function: ExecutionEnviroment::expand_string
  74. // Access: Public, Static
  75. // Description: Reads the string, looking for environment variable
  76. // names marked by a $. Expands all such variable
  77. // names. A repeated dollar sign ($$) is mapped to a
  78. // single dollar sign.
  79. //
  80. // Returns the expanded string.
  81. ////////////////////////////////////////////////////////////////////
  82. string ExecutionEnvironment::
  83. expand_string(const string &str) {
  84. string result;
  85. size_t last = 0;
  86. size_t dollar = str.find('$');
  87. while (dollar != string::npos && dollar + 1 < str.length()) {
  88. size_t start = dollar + 1;
  89. if (str[start] == '$') {
  90. // A double dollar sign maps to a single dollar sign.
  91. result += str.substr(last, start - last);
  92. last = start + 1;
  93. } else {
  94. string varname;
  95. size_t end = start;
  96. if (str[start] == '{') {
  97. // Curly braces delimit the variable name explicitly.
  98. end = str.find('}', start + 1);
  99. if (end != string::npos) {
  100. varname = str.substr(start + 1, end - (start + 1));
  101. end++;
  102. }
  103. }
  104. if (end == start) {
  105. // Scan for the end of the variable name.
  106. while (end < str.length() && (isalnum(str[end]) || str[end] == '_')) {
  107. end++;
  108. }
  109. varname = str.substr(start, end - start);
  110. }
  111. string subst =
  112. result += str.substr(last, dollar - last);
  113. result += get_environment_variable(varname);
  114. last = end;
  115. }
  116. dollar = str.find('$', last);
  117. }
  118. result += str.substr(last);
  119. return result;
  120. }
  121. ////////////////////////////////////////////////////////////////////
  122. // Function: ExecutionEnviroment::get_cwd
  123. // Access: Public, Static
  124. // Description: Returns the name of the current working directory.
  125. ////////////////////////////////////////////////////////////////////
  126. Filename ExecutionEnvironment::
  127. get_cwd() {
  128. // getcwd() requires us to allocate a dynamic buffer and grow it on
  129. // demand.
  130. static size_t bufsize = 1024;
  131. static char *buffer = NULL;
  132. if (buffer == (char *)NULL) {
  133. buffer = new char[bufsize];
  134. }
  135. while (getcwd(buffer, bufsize) == (char *)NULL) {
  136. if (errno != ERANGE) {
  137. perror("getcwd");
  138. return string();
  139. }
  140. delete[] buffer;
  141. bufsize = bufsize * 2;
  142. buffer = new char[bufsize];
  143. assert(buffer != (char *)NULL);
  144. }
  145. Filename cwd = Filename::from_os_specific(buffer);
  146. cwd.make_true_case();
  147. return cwd;
  148. }
  149. ////////////////////////////////////////////////////////////////////
  150. // Function: ExecutionEnvironment::ns_has_environment_variable
  151. // Access: Private
  152. // Description: Returns true if the indicated environment variable
  153. // is defined. The nonstatic implementation.
  154. ////////////////////////////////////////////////////////////////////
  155. bool ExecutionEnvironment::
  156. ns_has_environment_variable(const string &var) const {
  157. #ifdef PREREAD_ENVIRONMENT
  158. return _variables.count(var) != 0;
  159. #else
  160. return getenv(var.c_str()) != (char *)NULL;
  161. #endif
  162. }
  163. ////////////////////////////////////////////////////////////////////
  164. // Function: ExecutionEnvironment::ns_get_environment_variable
  165. // Access: Private
  166. // Description: Returns the definition of the indicated environment
  167. // variable, or the empty string if the variable is
  168. // undefined. The nonstatic implementation.
  169. ////////////////////////////////////////////////////////////////////
  170. string ExecutionEnvironment::
  171. ns_get_environment_variable(const string &var) const {
  172. EnvironmentVariables::const_iterator evi;
  173. evi = _variables.find(var);
  174. if (evi != _variables.end()) {
  175. return (*evi).second;
  176. }
  177. // Some special case variables. We virtually stuff these values
  178. // into the Panda environment, shadowing whatever values they have
  179. // in the true environment, so they can be used in config files.
  180. if (var == "HOME") {
  181. return Filename::get_home_directory().to_os_specific();
  182. } else if (var == "TEMP") {
  183. return Filename::get_temp_directory().to_os_specific();
  184. } else if (var == "USER_APPDATA") {
  185. return Filename::get_user_appdata_directory().to_os_specific();
  186. } else if (var == "COMMON_APPDATA") {
  187. return Filename::get_common_appdata_directory().to_os_specific();
  188. } else if (var == "MAIN_DIR") {
  189. #ifdef HAVE_PYTHON
  190. // If we're running from Python code, read out sys.argv.
  191. if (Py_IsInitialized()) {
  192. PyObject* obj = PySys_GetObject((char*) "argv");
  193. if (obj) {
  194. Filename main_dir = Filename::from_os_specific(PyString_AsString(PyList_GetItem(obj, 0)));
  195. if (main_dir.empty()) {
  196. // We must be running in the Python interpreter directly, so return the CWD.
  197. return get_cwd().to_os_specific();
  198. }
  199. main_dir.make_absolute();
  200. return Filename(main_dir.get_dirname()).to_os_specific();
  201. }
  202. }
  203. #endif
  204. // Otherwise, Return the binary name's parent directory.
  205. if (!_binary_name.empty()) {
  206. Filename main_dir (_binary_name);
  207. main_dir.make_absolute();
  208. return Filename(main_dir.get_dirname()).to_os_specific();
  209. }
  210. }
  211. #ifdef PREREAD_ENVIRONMENT
  212. return string();
  213. #else
  214. const char *def = getenv(var.c_str());
  215. if (def != (char *)NULL) {
  216. return def;
  217. }
  218. return string();
  219. #endif
  220. }
  221. ////////////////////////////////////////////////////////////////////
  222. // Function: ExecutionEnvironment::ns_set_environment_variable
  223. // Access: Private
  224. // Description: Changes the definition of the indicated environment
  225. // variable. The nonstatic implementation.
  226. ////////////////////////////////////////////////////////////////////
  227. void ExecutionEnvironment::
  228. ns_set_environment_variable(const string &var, const string &value) {
  229. _variables[var] = value;
  230. string putstr = var + "=" + value;
  231. // putenv() requires us to malloc a new C-style string.
  232. char *put = (char *)malloc(putstr.length() + 1);
  233. strcpy(put, putstr.c_str());
  234. putenv(put);
  235. }
  236. ////////////////////////////////////////////////////////////////////
  237. // Function: ExecutionEnvironment::ns_shadow_environment_variable
  238. // Access: Private
  239. // Description:
  240. ////////////////////////////////////////////////////////////////////
  241. void ExecutionEnvironment::
  242. ns_shadow_environment_variable(const string &var, const string &value) {
  243. _variables[var] = value;
  244. string putstr = var + "=" + value;
  245. }
  246. ////////////////////////////////////////////////////////////////////
  247. // Function: ExecutionEnvironment::ns_clear_shadow
  248. // Access: Private
  249. // Description:
  250. ////////////////////////////////////////////////////////////////////
  251. void ExecutionEnvironment::
  252. ns_clear_shadow(const string &var) {
  253. EnvironmentVariables::iterator vi = _variables.find(var);
  254. if (vi == _variables.end()) {
  255. return;
  256. }
  257. #ifdef PREREAD_ENVIRONMENT
  258. // Now we have to replace the value in the table.
  259. const char *def = getenv(var.c_str());
  260. if (def != (char *)NULL) {
  261. (*vi).second = def;
  262. } else {
  263. _variables.erase(vi);
  264. }
  265. #endif // PREREAD_ENVIRONMENT
  266. }
  267. ////////////////////////////////////////////////////////////////////
  268. // Function: ExecutionEnvironment::ns_get_num_args
  269. // Access: Private
  270. // Description: Returns the number of command-line arguments
  271. // available, not counting arg 0, the binary name. The
  272. // nonstatic implementation.
  273. ////////////////////////////////////////////////////////////////////
  274. int ExecutionEnvironment::
  275. ns_get_num_args() const {
  276. return _args.size();
  277. }
  278. ////////////////////////////////////////////////////////////////////
  279. // Function: ExecutionEnvironment::ns_get_arg
  280. // Access: Private
  281. // Description: Returns the nth command-line argument. The index n
  282. // must be in the range [0 .. get_num_args()). The
  283. // first parameter, n == 0, is the first actual
  284. // parameter, not the binary name. The nonstatic
  285. // implementation.
  286. ////////////////////////////////////////////////////////////////////
  287. string ExecutionEnvironment::
  288. ns_get_arg(int n) const {
  289. assert(n >= 0 && n < ns_get_num_args());
  290. return _args[n];
  291. }
  292. ////////////////////////////////////////////////////////////////////
  293. // Function: ExecutionEnvironment::ns_get_binary_name
  294. // Access: Private
  295. // Description: Returns the name of the binary executable that
  296. // started this program, if it can be determined. The
  297. // nonstatic implementation.
  298. ////////////////////////////////////////////////////////////////////
  299. string ExecutionEnvironment::
  300. ns_get_binary_name() const {
  301. if (_binary_name.empty()) {
  302. return "unknown";
  303. }
  304. return _binary_name;
  305. }
  306. ////////////////////////////////////////////////////////////////////
  307. // Function: ExecutionEnvironment::ns_get_dtool_name
  308. // Access: Private
  309. // Description: Returns the name of the libdtool DLL that
  310. // is used in this program, if it can be determined. The
  311. // nonstatic implementation.
  312. ////////////////////////////////////////////////////////////////////
  313. string ExecutionEnvironment::
  314. ns_get_dtool_name() const {
  315. if (_dtool_name.empty()) {
  316. return "unknown";
  317. }
  318. return _dtool_name;
  319. }
  320. ////////////////////////////////////////////////////////////////////
  321. // Function: ExecutionEnvironment::get_ptr
  322. // Access: Private, Static
  323. // Description: Returns a static pointer that may be used to access
  324. // the global ExecutionEnvironment object.
  325. ////////////////////////////////////////////////////////////////////
  326. ExecutionEnvironment *ExecutionEnvironment::
  327. get_ptr() {
  328. if (_global_ptr == (ExecutionEnvironment *)NULL) {
  329. _global_ptr = new ExecutionEnvironment;
  330. }
  331. return _global_ptr;
  332. }
  333. ////////////////////////////////////////////////////////////////////
  334. // Function: ExecutionEnvironment::read_environment_variables
  335. // Access: Private
  336. // Description: Fills up the internal table of existing environment
  337. // variables, if we are in PREREAD_ENVIRONMENT mode.
  338. // Otherwise, does nothing.
  339. ////////////////////////////////////////////////////////////////////
  340. void ExecutionEnvironment::
  341. read_environment_variables() {
  342. #ifdef PREREAD_ENVIRONMENT
  343. #if defined(HAVE_PROC_SELF_ENVIRON)
  344. // In Linux, and possibly in other systems, we might not be able to
  345. // use getenv() at static init time. However, we may be lucky and
  346. // have a file called /proc/self/environ that may be read to
  347. // determine all of our environment variables.
  348. pifstream proc("/proc/self/environ");
  349. if (proc.fail()) {
  350. cerr << "Cannot read /proc/self/environ; environment variables unavailable.\n";
  351. return;
  352. }
  353. int ch = proc.get();
  354. while (!proc.eof() && !proc.fail()) {
  355. string variable;
  356. string value;
  357. while (!proc.eof() && !proc.fail() && ch != '=' && ch != '\0') {
  358. variable += (char)ch;
  359. ch = proc.get();
  360. }
  361. if (ch == '=') {
  362. ch = proc.get();
  363. while (!proc.eof() && !proc.fail() && ch != '\0') {
  364. value += (char)ch;
  365. ch = proc.get();
  366. }
  367. }
  368. if (!variable.empty()) {
  369. _variables[variable] = value;
  370. }
  371. ch = proc.get();
  372. }
  373. #elif defined(IS_OSX) || defined(IS_FREEBSD)
  374. // In the case of Mac, there's always _NSGetEnviron() which we can read.
  375. // In the case of FreeBSD, it's the "environ" variable.
  376. char **envp;
  377. for (envp = environ; envp && *envp; envp++) {
  378. string variable;
  379. string value;
  380. char *envc;
  381. for (envc = *envp; envc && *envc && strncmp(envc, "=", 1) != 0; envc++) {
  382. variable += (char) *envc;
  383. }
  384. if (strncmp(envc, "=", 1) == 0) {
  385. for (envc++; envc && *envc; envc++) {
  386. value += (char) *envc;
  387. }
  388. }
  389. if (!variable.empty()) {
  390. _variables[variable] = value;
  391. }
  392. }
  393. #else
  394. cerr << "Warning: environment variables unavailable to dconfig.\n";
  395. #endif
  396. #endif // PREREAD_ENVIRONMENT
  397. }
  398. ////////////////////////////////////////////////////////////////////
  399. // Function: ExecutionEnvironment::read_args
  400. // Access: Private
  401. // Description: Reads all the command-line arguments and the name of
  402. // the binary file, if possible.
  403. ////////////////////////////////////////////////////////////////////
  404. void ExecutionEnvironment::
  405. read_args() {
  406. #ifdef WIN32_VC
  407. #ifdef _DEBUG
  408. HMODULE dllhandle = GetModuleHandle("libp3dtool_d.dll");
  409. #else
  410. HMODULE dllhandle = GetModuleHandle("libp3dtool.dll");
  411. #endif
  412. if (dllhandle != 0) {
  413. static const DWORD buffer_size = 1024;
  414. char buffer[buffer_size];
  415. DWORD size = GetModuleFileName(dllhandle, buffer, buffer_size);
  416. if (size != 0) {
  417. Filename tmp = Filename::from_os_specific(string(buffer, size));
  418. tmp.make_true_case();
  419. _dtool_name = tmp;
  420. }
  421. }
  422. #endif
  423. #if defined(HAVE_PROC_SELF_MAPS) || defined(HAVE_PROC_CURPROC_MAP)
  424. // This is how you tell whether or not libdtool.so is loaded,
  425. // and if so, where it was loaded from.
  426. #ifdef HAVE_PROC_CURPROC_MAP
  427. pifstream maps("/proc/curproc/map");
  428. #else
  429. pifstream maps("/proc/self/maps");
  430. #endif
  431. while (!maps.fail() && !maps.eof()) {
  432. char buffer[PATH_MAX];
  433. buffer[0] = 0;
  434. maps.getline(buffer, PATH_MAX);
  435. char *tail = strrchr(buffer, '/');
  436. char *head = strchr(buffer, '/');
  437. if (tail && head && (strcmp(tail, "/libp3dtool.so." PANDA_ABI_VERSION_STR) == 0
  438. || strcmp(tail, "/libp3dtool.dylib") == 0
  439. || strcmp(tail, "/libdtool.dylib") == 0)) {
  440. _dtool_name = head;
  441. }
  442. }
  443. maps.close();
  444. #endif
  445. #ifdef __APPLE__
  446. // And on OSX we don't have /proc/self/maps, but some _dyld_* functions.
  447. if (_dtool_name.empty()) {
  448. uint32_t ic = _dyld_image_count();
  449. for (uint32_t i = 0; i < ic; ++i) {
  450. const char *buffer = _dyld_get_image_name(i);
  451. const char *tail = strrchr(buffer, '/');
  452. if (tail && (strcmp(tail, "/libp3dtool." PANDA_ABI_VERSION_STR ".dylib") == 0
  453. || strcmp(tail, "/libp3dtool.dylib") == 0
  454. || strcmp(tail, "/libdtool.dylib") == 0)) {
  455. _dtool_name = buffer;
  456. }
  457. }
  458. }
  459. #endif
  460. #ifdef WIN32_VC
  461. static const DWORD buffer_size = 1024;
  462. char buffer[buffer_size];
  463. DWORD size = GetModuleFileName(NULL, buffer, buffer_size);
  464. if (size != 0) {
  465. Filename tmp = Filename::from_os_specific(string(buffer, size));
  466. tmp.make_true_case();
  467. _binary_name = tmp;
  468. }
  469. #endif // WIN32_VC
  470. #if defined(HAVE_PROC_SELF_EXE) || defined(HAVE_PROC_CURPROC_FILE)
  471. // This is more reliable than using (argc,argv), so it given precedence.
  472. if (_binary_name.empty()) {
  473. char readlinkbuf[PATH_MAX];
  474. #ifdef HAVE_PROC_CURPROC_FILE
  475. int pathlen = readlink("/proc/curproc/file",readlinkbuf,PATH_MAX-1);
  476. #else
  477. int pathlen = readlink("/proc/self/exe",readlinkbuf,PATH_MAX-1);
  478. #endif
  479. if (pathlen > 0) {
  480. readlinkbuf[pathlen] = 0;
  481. _binary_name = readlinkbuf;
  482. }
  483. }
  484. #endif
  485. #if defined(__APPLE__)
  486. // And on Mac, we have _NSGetExecutablePath.
  487. if (_binary_name.empty()) {
  488. char *pathbuf = new char[PATH_MAX];
  489. uint32_t bufsize = PATH_MAX;
  490. if (_NSGetExecutablePath(pathbuf, &bufsize) == 0) {
  491. _binary_name = pathbuf;
  492. }
  493. delete[] pathbuf;
  494. }
  495. #endif
  496. #if defined(HAVE_GLOBAL_ARGV)
  497. int argc = GLOBAL_ARGC;
  498. if (_binary_name.empty() && argc > 0) {
  499. _binary_name = GLOBAL_ARGV[0];
  500. // This really needs to be resolved against PATH.
  501. }
  502. for (int i = 1; i < argc; i++) {
  503. _args.push_back(GLOBAL_ARGV[i]);
  504. }
  505. #elif defined(IS_FREEBSD)
  506. // In FreeBSD, we can use sysctl to determine the command-line arguments.
  507. size_t bufsize = 4096;
  508. char buffer[4096];
  509. int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ARGS, 0};
  510. mib[3] = getpid();
  511. if (sysctl(mib, 4, (void*) buffer, &bufsize, NULL, 0) == -1) {
  512. perror("sysctl");
  513. } else {
  514. if (_binary_name.empty()) {
  515. _binary_name = buffer;
  516. }
  517. int idx = strlen(buffer) + 1;
  518. while (idx < bufsize) {
  519. _args.push_back((char*)(buffer + idx));
  520. int newidx = strlen(buffer + idx);
  521. idx += newidx + 1;
  522. }
  523. }
  524. #elif defined(HAVE_PROC_SELF_CMDLINE) || defined(HAVE_PROC_CURPROC_CMDLINE)
  525. // In Linux, and possibly in other systems as well, we might not be
  526. // able to use the global ARGC/ARGV variables at static init time.
  527. // However, we may be lucky and have a file called
  528. // /proc/self/cmdline that may be read to determine all of our
  529. // command-line arguments.
  530. #ifdef HAVE_PROC_CURPROC_CMDLINE
  531. pifstream proc("/proc/curproc/cmdline");
  532. if (proc.fail()) {
  533. cerr << "Cannot read /proc/curproc/cmdline; command-line arguments unavailable to config.\n";
  534. return;
  535. }
  536. #else
  537. pifstream proc("/proc/self/cmdline");
  538. if (proc.fail()) {
  539. cerr << "Cannot read /proc/self/cmdline; command-line arguments unavailable to config.\n";
  540. return;
  541. }
  542. #endif
  543. int ch = proc.get();
  544. int index = 0;
  545. while (!proc.eof() && !proc.fail()) {
  546. string arg;
  547. while (!proc.eof() && !proc.fail() && ch != '\0') {
  548. arg += (char)ch;
  549. ch = proc.get();
  550. }
  551. if (index == 0) {
  552. if (_binary_name.empty())
  553. _binary_name = arg;
  554. } else {
  555. _args.push_back(arg);
  556. }
  557. index++;
  558. ch = proc.get();
  559. }
  560. #endif
  561. if (_dtool_name.empty()) {
  562. _dtool_name = _binary_name;
  563. }
  564. }