1.0.37.54: more robust runtime executable path detection
authorNikodemus Siivola <nikodemus@random-state.net>
Wed, 7 Apr 2010 16:14:21 +0000 (16:14 +0000)
committerNikodemus Siivola <nikodemus@random-state.net>
Wed, 7 Apr 2010 16:14:21 +0000 (16:14 +0000)
 Patch by Josh Elsasser.

 * os_get_runtime_executable_path() extended with an argument to
   indicate if the returned path should be externally usable (which
   precludes paths such as /proc/curproc/file).

 * If os_get_runtime_executable_path() fails, use an argv[0] derived
   path instead.

 * Check build_id when saving executable cores to make sure we got
   the right runtime -- since using the argv[0] method can otherwise
   go wrong once in a blue moon.

 Fixes Launchpad #375549.

14 files changed:
NEWS
src/code/common-os.lisp
src/runtime/bsd-os.c
src/runtime/darwin-os.c
src/runtime/hpux-os.c
src/runtime/linux-os.c
src/runtime/os.h
src/runtime/osf1-os.c
src/runtime/runtime.c
src/runtime/runtime.h
src/runtime/save.c
src/runtime/sunos-os.c
src/runtime/win32-os.c
version.lisp-expr

diff --git a/NEWS b/NEWS
index 0b3f4b6..cd74af3 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -60,6 +60,7 @@ changes relative to sbcl-1.0.37:
   * bug fix: SB-POSIX build was broken when SBCL was compiled without the
     :SB-DOC feature. (lp#552564)
   * bug fix: SB-INTROSPECT build issues on GENGC/PPC. (lp#490490)
+  * bug fix: more robust runtime executable path detection. (lp#375549)
 
 changes in sbcl-1.0.37 relative to sbcl-1.0.36:
   * enhancement: Backtrace from THROW to uncaught tag on x86oids now shows
index 7b32419..bf71744 100644 (file)
 
 (sb!alien:define-alien-variable ("posix_argv" *native-posix-argv*) (* (* char)))
 (sb!alien:define-alien-variable ("core_string" *native-core-string*) (* char))
-(sb!alien:define-alien-routine os-get-runtime-executable-path sb!alien:c-string)
+(sb!alien:define-alien-routine
+ os-get-runtime-executable-path sb!alien:c-string (external-path boolean))
+(sb!alien:define-alien-variable
+ ("saved_runtime_path" *native-saved-runtime-path*) (* char))
 
 ;;; if something ever needs to be done differently for one OS, then
 ;;; split out the different part into per-os functions.
@@ -43,6 +46,8 @@
   (setf *core-pathname*
         (merge-pathnames (native-pathname *core-string*)))
   (/show0 "setting *RUNTIME-PATHNAME*")
-  (let ((exe (os-get-runtime-executable-path)))
-    (setf *runtime-pathname* (when exe (native-pathname exe))))
+  (let ((exe (os-get-runtime-executable-path t))
+        (saved (sb!alien:cast *native-saved-runtime-path* sb!alien:c-string)))
+    (setf *runtime-pathname*
+          (when (or exe saved) (native-pathname (or exe saved)))))
   (/show0 "leaving OS-COLD-INIT-OR-REINIT"))
index 3b88379..1b1db92 100644 (file)
@@ -428,7 +428,7 @@ futex_wake(int *lock_word, int n)
 #endif
 
 char *
-os_get_runtime_executable_path()
+os_get_runtime_executable_path(int external)
 {
     char path[PATH_MAX + 1];
 
@@ -456,20 +456,16 @@ os_get_runtime_executable_path()
 }
 #elif defined(LISP_FEATURE_NETBSD) || defined(LISP_FEATURE_OPENBSD)
 char *
-os_get_runtime_executable_path()
+os_get_runtime_executable_path(int external)
 {
     struct stat sb;
-    char *path = strdup("/proc/curproc/file");
-    if (path && ((stat(path, &sb)) == 0))
-        return path;
-    else {
-        fprintf(stderr, "Couldn't stat /proc/curproc/file; is /proc mounted?\n");
-        return NULL;
-    }
+    if (!external && stat("/proc/curproc/file", &sb) == 0)
+        return copied_string("/proc/curproc/file");
+    return NULL;
 }
 #else /* Not DARWIN or FREEBSD or NETBSD or OPENBSD */
 char *
-os_get_runtime_executable_path()
+os_get_runtime_executable_path(int external)
 {
     return NULL;
 }
index a60dc30..95de326 100644 (file)
@@ -24,7 +24,7 @@
 #include <errno.h>
 
 char *
-os_get_runtime_executable_path()
+os_get_runtime_executable_path(int external)
 {
     char path[PATH_MAX + 1];
     uint32_t size = sizeof(path);
index 605cd8b..bff662f 100644 (file)
@@ -135,9 +135,9 @@ os_install_interrupt_handlers(void)
 }
 
 char *
-os_get_runtime_executable_path()
+os_get_runtime_executable_path(int external)
 {
-    return copied_string("NOPE");
+    return NULL;
 }
 
 /* when inside call_into_lisp, we will first jump to the stub
index 9b2e3cf..e25527a 100644 (file)
@@ -450,7 +450,7 @@ os_install_interrupt_handlers(void)
 }
 
 char *
-os_get_runtime_executable_path()
+os_get_runtime_executable_path(int external)
 {
     char path[PATH_MAX + 1];
     int size;
index a471ab6..814b18d 100644 (file)
@@ -173,8 +173,10 @@ extern void os_deallocate(os_vm_address_t addr, os_vm_size_t len);
 int os_get_errno(void);
 
 /* Return an absolute path to the runtime executable, or NULL if this
- * information is unavailable.  If a non-null pathname is returned, it
- * must be 'free'd. */
-extern char *os_get_runtime_executable_path(void);
+ * information is unavailable.  Unless external_path is non-zero the
+ * returned path may only be valid for the current process, ie:
+ * something like /proc/curproc/file.  If a non-null pathname is
+ * returned, it must be 'free'd. */
+extern char *os_get_runtime_executable_path(int external_path);
 
 #endif
index 4c3b011..183a4a4 100644 (file)
@@ -146,7 +146,7 @@ os_install_interrupt_handlers(void)
 }
 
 char *
-os_get_runtime_executable_path()
+os_get_runtime_executable_path(int external)
 {
     return NULL;
 }
index 573b389..e4ec0e9 100644 (file)
@@ -127,6 +127,37 @@ copied_existing_filename_or_null(char *filename)
         return copied_string(filename);
     }
 }
+
+#ifndef LISP_FEATURE_WIN32
+char *
+copied_realpath(const char *pathname)
+{
+    char *messy, *tidy;
+    size_t len;
+
+    /* realpath() supposedly can't be counted on to always return
+     * an absolute path, so we prepend the cwd to relative paths */
+    messy = NULL;
+    if (pathname[0] != '/') {
+        messy = successful_malloc(PATH_MAX + 1);
+        if (getcwd(messy, PATH_MAX + 1) == NULL) {
+            free(messy);
+            return NULL;
+        }
+        len = strlen(messy);
+        snprintf(messy + len, PATH_MAX + 1 - len, "/%s", pathname);
+    }
+
+    tidy = successful_malloc(PATH_MAX + 1);
+    if (realpath((messy ? messy : pathname), tidy) == NULL) {
+        free(messy);
+        free(tidy);
+        return NULL;
+    }
+
+    return tidy;
+}
+#endif /* LISP_FEATURE_WIN32 */
 \f
 /* miscellaneous chattiness */
 
@@ -205,11 +236,58 @@ search_for_core ()
     return core;
 }
 
+/* Try to find the path to an executable from argv[0], this is only
+ * used when os_get_runtime_executable_path() returns NULL */
+#ifdef LISP_FEATURE_WIN32
+char *
+search_for_executable(const char *argv0)
+{
+    return NULL;
+}
+#else /* LISP_FEATURE_WIN32 */
+char *
+search_for_executable(const char *argv0)
+{
+    char *search, *start, *end, *buf;
+
+    /* If argv[0] contains a slash then it's probably an absolute path
+     * or relative to the current directory, so check if it exists. */
+    if (strchr(argv0, '/') != NULL && access(argv0, F_OK) == 0)
+        return copied_realpath(argv0);
+
+    /* Bail on an absolute path which doesn't exist */
+    if (argv0[0] == '/')
+        return NULL;
+
+    /* Otherwise check if argv[0] exists relative to any directory in PATH */
+    search = getenv("PATH");
+    if (search == NULL)
+        return NULL;
+    search = copied_string(search);
+    buf = successful_malloc(PATH_MAX + 1);
+    for (start = search; (end = strchr(start, ':')) != NULL; start = end + 1) {
+        *end = '\0';
+        snprintf(buf, PATH_MAX + 1, "%s/%s", start, argv0);
+        if (access(buf, F_OK) == 0) {
+            free(search);
+            search = copied_realpath(buf);
+            free(buf);
+            return search;
+        }
+    }
+
+    free(search);
+    free(buf);
+    return NULL;
+}
+#endif /* LISP_FEATURE_WIN32 */
+
 char **posix_argv;
 char *core_string;
 
 struct runtime_options *runtime_options;
 
+char *saved_runtime_path = NULL;
 \f
 int
 main(int argc, char *argv[], char *envp[])
@@ -241,15 +319,22 @@ main(int argc, char *argv[], char *envp[])
 
     runtime_options = NULL;
 
+    /* Save the argv[0] derived runtime path in case
+     * os_get_runtime_executable_path(1) isn't able to get an
+     * externally-usable path later on. */
+    saved_runtime_path = search_for_executable(argv[0]);
+
     /* Check early to see if this executable has an embedded core,
      * which also populates runtime_options if the core has runtime
      * options */
-    runtime_path = os_get_runtime_executable_path();
-    if (runtime_path) {
-        os_vm_offset_t offset = search_for_embedded_core(runtime_path);
+    runtime_path = os_get_runtime_executable_path(0);
+    if (runtime_path || saved_runtime_path) {
+        os_vm_offset_t offset = search_for_embedded_core(
+            runtime_path ? runtime_path : saved_runtime_path);
         if (offset != -1) {
             embedded_core_offset = offset;
-            core = runtime_path;
+            core = (runtime_path ? runtime_path :
+                    copied_string(saved_runtime_path));
         } else {
             free(runtime_path);
         }
index 8602432..de83bc8 100644 (file)
@@ -272,4 +272,7 @@ struct runtime_options {
     size_t thread_control_stack_size;
 };
 
+/* saved runtime path computed from argv[0] */
+extern char *saved_runtime_path;
+
 #endif /* _SBCL_RUNTIME_H_ */
index 726f012..b3addc9 100644 (file)
@@ -383,6 +383,25 @@ save_to_filehandle(FILE *file, char *filename, lispobj init_function,
     exit(0);
 }
 
+/* Check if the build_id for the current runtime is present in a
+ * buffer. */
+int
+check_runtime_build_id(void *buf, size_t size)
+{
+    size_t idlen;
+    char *pos;
+
+    idlen = strlen(build_id) - 1;
+    while ((pos = memchr(buf, build_id[0], size)) != NULL) {
+        size -= (pos + 1) - (char *)buf;
+        buf = (pos + 1);
+        if (idlen <= size && memcmp(buf, build_id + 1, idlen) == 0)
+            return 1;
+    }
+
+    return 0;
+}
+
 /* Slurp the executable portion of the runtime into a malloced buffer
  * and return it.  Places the size in bytes of the runtime into
  * 'size_out'.  Returns NULL if the runtime cannot be loaded from
@@ -414,6 +433,12 @@ load_runtime(char *runtime_path, size_t *size_out)
         goto lose;
     }
 
+    if (!check_runtime_build_id(buf, size)) {
+        fprintf(stderr, "Failed to locate current build_id in runtime: %s\n",
+            runtime_path);
+        goto lose;
+    }
+
     fclose(input);
     *size_out = size;
     return buf;
@@ -460,15 +485,19 @@ prepare_to_save(char *filename, boolean prepend_runtime, void **runtime_bytes,
     char *runtime_path;
 
     if (prepend_runtime) {
-        runtime_path = os_get_runtime_executable_path();
+        runtime_path = os_get_runtime_executable_path(0);
 
-        if (runtime_path == NULL) {
+        if (runtime_path == NULL && saved_runtime_path == NULL) {
             fprintf(stderr, "Unable to get default runtime path.\n");
             return NULL;
         }
 
-        *runtime_bytes = load_runtime(runtime_path, runtime_size);
-        free(runtime_path);
+        if (runtime_path == NULL)
+            *runtime_bytes = load_runtime(saved_runtime_path, runtime_size);
+        else {
+            *runtime_bytes = load_runtime(runtime_path, runtime_size);
+            free(runtime_path);
+        }
 
         if (*runtime_bytes == NULL)
             return 0;
index 89c0d71..027e2d6 100644 (file)
@@ -234,13 +234,11 @@ os_install_interrupt_handlers()
 }
 
 char *
-os_get_runtime_executable_path()
+os_get_runtime_executable_path(int external)
 {
-    int ret;
     char path[] = "/proc/self/object/a.out";
 
-    ret = access(path, R_OK);
-    if (ret == -1)
+    if (external || access(path, R_OK) == -1)
         return NULL;
 
     return copied_string(path);
index 8cdc832..fb3ad0b 100644 (file)
@@ -582,7 +582,7 @@ void scratch(void)
 }
 
 char *
-os_get_runtime_executable_path()
+os_get_runtime_executable_path(int external)
 {
     char path[MAX_PATH + 1];
     DWORD bufsize = sizeof(path);
index cfbb69a..34b5395 100644 (file)
@@ -17,4 +17,4 @@
 ;;; checkins which aren't released. (And occasionally for internal
 ;;; versions, especially for internal versions off the main CVS
 ;;; branch, it gets hairier, e.g. "0.pre7.14.flaky4.13".)
-"1.0.37.53"
+"1.0.37.54"