Browse Source

Much better FreeBSD support, bugfix for Linux, and restructuring of the code

rdb 15 years ago
parent
commit
6fab399549
1 changed files with 124 additions and 51 deletions
  1. 124 51
      dtool/src/dtoolutil/executionEnvironment.cxx

+ 124 - 51
dtool/src/dtoolutil/executionEnvironment.cxx

@@ -44,6 +44,9 @@
 #ifdef IS_FREEBSD
 extern char **environ;
 
+// This is for Link_map.
+#include <link.h>
+
 // This is for sysctl.
 #include <sys/types.h>
 #include <sys/sysctl.h>
@@ -464,6 +467,9 @@ read_environment_variables() {
 void ExecutionEnvironment::
 read_args() {
 
+  // First, we need to fill in _dtool_name.  This contains
+  // the full path to the p3dtool library.
+
 #ifdef WIN32_VC
 #ifdef _DEBUG
   HMODULE dllhandle = GetModuleHandle("libp3dtool_d.dll");
@@ -482,31 +488,9 @@ read_args() {
   }
 #endif
 
-#if defined(HAVE_PROC_SELF_MAPS) || defined(HAVE_PROC_CURPROC_MAP)
-  // This is how you tell whether or not libdtool.so is loaded,
-  // and if so, where it was loaded from.
-#ifdef HAVE_PROC_CURPROC_MAP
-  pifstream maps("/proc/curproc/map");
-#else
-  pifstream maps("/proc/self/maps");
-#endif
-  while (!maps.fail() && !maps.eof()) {
-    char buffer[PATH_MAX];
-    buffer[0] = 0;
-    maps.getline(buffer, PATH_MAX);
-    char *tail = strrchr(buffer, '/');
-    char *head = strchr(buffer, '/');
-    if (tail && head && (strcmp(tail, "/libp3dtool.so." PANDA_ABI_VERSION_STR) == 0
-                      || strcmp(tail, "/libp3dtool.dylib") == 0
-                      || strcmp(tail, "/libdtool.dylib") == 0)) {
-      _dtool_name = head;
-    }
-  }
-  maps.close();
-#endif
-
-#ifdef __APPLE__
+#if defined(__APPLE__)
   // And on OSX we don't have /proc/self/maps, but some _dyld_* functions.
+
   if (_dtool_name.empty()) {
     uint32_t ic = _dyld_image_count();
     for (uint32_t i = 0; i < ic; ++i) {
@@ -521,29 +505,63 @@ read_args() {
   }
 #endif
 
-#ifdef WIN32_VC
-  static const DWORD buffer_size = 1024;
-  char buffer[buffer_size];
-  DWORD size = GetModuleFileName(NULL, buffer, buffer_size);
-  if (size != 0) {
-    Filename tmp = Filename::from_os_specific(string(buffer, size));
-    tmp.make_true_case();
-    _binary_name = tmp;
+#if defined(IS_FREEBSD)
+  // On FreeBSD, we can use dlinfo to get the linked libraries.
+
+  if (_dtool_name.empty()) {
+    Link_map *map;
+    dlinfo(RTLD_SELF, RTLD_DI_LINKMAP, &map);
+
+    while (map != NULL) {
+      char *tail = strrchr(map->l_name, '/');
+      char *head = strchr(map->l_name, '/');
+      if (tail && head && (strcmp(tail, "/libp3dtool.so." PANDA_ABI_VERSION_STR) == 0
+                        || strcmp(tail, "/libp3dtool.so") == 0
+                        || strcmp(tail, "/libdtool.so") == 0)) {
+        _dtool_name = head;
+      }
+      map = map->l_next;
+    }
   }
-#endif  // WIN32_VC
+#endif
 
-#if defined(HAVE_PROC_SELF_EXE) || defined(HAVE_PROC_CURPROC_FILE)
-  // This is more reliable than using (argc,argv), so it given precedence.
-  if (_binary_name.empty()) {
-    char readlinkbuf[PATH_MAX];
-#ifdef HAVE_PROC_CURPROC_FILE
-    int pathlen = readlink("/proc/curproc/file",readlinkbuf,PATH_MAX-1);
+#if defined(HAVE_PROC_SELF_MAPS) || defined(HAVE_PROC_CURPROC_MAP)
+  // Some operating systems provide a file in the /proc filesystem.
+
+  if (_dtool_name.empty()) {
+#ifdef HAVE_PROC_CURPROC_MAP
+    pifstream maps("/proc/curproc/map");
 #else
-    int pathlen = readlink("/proc/self/exe",readlinkbuf,PATH_MAX-1);
+    pifstream maps("/proc/self/maps");
 #endif
-    if (pathlen > 0) {
-      readlinkbuf[pathlen] = 0;
-      _binary_name = readlinkbuf;
+    while (!maps.fail() && !maps.eof()) {
+      char buffer[PATH_MAX];
+      buffer[0] = 0;
+      maps.getline(buffer, PATH_MAX);
+      char *tail = strrchr(buffer, '/');
+      char *head = strchr(buffer, '/');
+      if (tail && head && (strcmp(tail, "/libp3dtool.so." PANDA_ABI_VERSION_STR) == 0
+                        || strcmp(tail, "/libp3dtool.so") == 0
+                        || strcmp(tail, "/libdtool.so") == 0)) {
+        _dtool_name = head;
+      }
+    }
+    maps.close();
+  }
+#endif
+
+  // Now, we need to fill in _binary_name.  This contains
+  // the full path to the currently running executable.
+
+#ifdef WIN32_VC
+  if (_binary_name.empty()) {
+    static const DWORD buffer_size = 1024;
+    char buffer[buffer_size];
+    DWORD size = GetModuleFileName(NULL, buffer, buffer_size);
+    if (size != 0) {
+      Filename tmp = Filename::from_os_specific(string(buffer, size));
+      tmp.make_true_case();
+      _binary_name = tmp;
     }
   }
 #endif
@@ -560,19 +578,44 @@ read_args() {
   }
 #endif
 
-#if defined(HAVE_GLOBAL_ARGV)
-  int argc = GLOBAL_ARGC;
+#if defined(IS_FREEBSD)
+  // In FreeBSD, we can use sysctl to determine the pathname.
 
-  if (_binary_name.empty() && argc > 0) {
-    _binary_name = GLOBAL_ARGV[0];
-    // This really needs to be resolved against PATH.
+  if (_binary_name.empty()) {
+    size_t bufsize = 4096;
+    char buffer[4096];
+    int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
+    mib[3] = getpid();
+    if (sysctl(mib, 4, (void*) buffer, &bufsize, NULL, 0) == -1) {
+      perror("sysctl");
+    } else {
+      _binary_name = buffer;
+    }
   }
+#endif
 
-  for (int i = 1; i < argc; i++) {
-    _args.push_back(GLOBAL_ARGV[i]);
+#if defined(HAVE_PROC_SELF_EXE) || defined(HAVE_PROC_CURPROC_FILE)
+  // Some operating systems provide a symbolic link to the executable
+  // in the /proc filesystem.  Use readlink to resolve that link.
+
+  if (_binary_name.empty()) {
+    char readlinkbuf [PATH_MAX];
+#ifdef HAVE_PROC_CURPROC_FILE
+    int pathlen = readlink("/proc/curproc/file", readlinkbuf, PATH_MAX - 1);
+#else
+    int pathlen = readlink("/proc/self/exe", readlinkbuf, PATH_MAX - 1);
+#endif
+    if (pathlen > 0) {
+      readlinkbuf[pathlen] = 0;
+      _binary_name = readlinkbuf;
+    }
   }
+#endif
+
+  // Next we need to fill in _args, which is a vector containing
+  // the command-line arguments that the executable was invoked with.
 
-#elif defined(IS_FREEBSD)
+#if defined(IS_FREEBSD)
   // In FreeBSD, we can use sysctl to determine the command-line arguments.
 
   size_t bufsize = 4096;
@@ -593,6 +636,18 @@ read_args() {
     }
   }
 
+#elif defined(HAVE_GLOBAL_ARGV)
+  int argc = GLOBAL_ARGC;
+
+  if (_binary_name.empty() && argc > 0) {
+    _binary_name = GLOBAL_ARGV[0];
+    // This really needs to be resolved against PATH.
+  }
+
+  for (int i = 1; i < argc; i++) {
+    _args.push_back(GLOBAL_ARGV[i]);
+  }
+
 #elif defined(HAVE_PROC_SELF_CMDLINE) || defined(HAVE_PROC_CURPROC_CMDLINE)
   // In Linux, and possibly in other systems as well, we might not be
   // able to use the global ARGC/ARGV variables at static init time.
@@ -636,6 +691,24 @@ read_args() {
   }
 #endif
 
+#ifndef WIN32
+  // Try to use realpath to get cleaner paths.
+
+  if (!_binary_name.empty()) {
+    char newpath [PATH_MAX + 1];
+    if (realpath(_binary_name.c_str(), newpath) != NULL) {
+      _binary_name = newpath;
+    }
+  }
+
+  if (!_dtool_name.empty()) {
+    char newpath [PATH_MAX + 1];
+    if (realpath(_dtool_name.c_str(), newpath) != NULL) {
+      _dtool_name = newpath;
+    }
+  }
+#endif
+
   if (_dtool_name.empty()) {
     _dtool_name = _binary_name;
   }