]> granicus.if.org Git - postgresql/commitdiff
Add code to find_my_exec() to resolve a symbolic link down to the
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 6 Nov 2004 23:06:29 +0000 (23:06 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 6 Nov 2004 23:06:29 +0000 (23:06 +0000)
actual executable location.  This allows people to continue to use
setups where, eg, postmaster is symlinked from a convenient place.
Per gripe from Josh Berkus.

configure
configure.in
src/include/pg_config.h.in
src/port/exec.c

index 7e0a563444df053f902c68961593c3ff4a9f8a56..b53dc7723083b9db02aff424b558dc0d5e592a3f 100755 (executable)
--- a/configure
+++ b/configure
@@ -11295,7 +11295,8 @@ test $ac_cv_func_memcmp_working = no && LIBOBJS="$LIBOBJS memcmp.$ac_objext"
 
 
 
-for ac_func in cbrt dlopen fcvt fdatasync getpeereid memmove poll pstat setproctitle setsid sigprocmask symlink sysconf towlower utime utimes waitpid wcstombs
+
+for ac_func in cbrt dlopen fcvt fdatasync getpeereid memmove poll pstat readlink setproctitle setsid sigprocmask symlink sysconf towlower utime utimes waitpid wcstombs
 do
 as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
 echo "$as_me:$LINENO: checking for $ac_func" >&5
index 14af5ae0279a2d878c6882b5cb7a3494571ae152..6eb4e120b027e2f605df6d65f933f0b755d7437b 100644 (file)
@@ -1,5 +1,5 @@
 dnl Process this file with autoconf to produce a configure script.
-dnl $PostgreSQL: pgsql/configure.in,v 1.384 2004/11/02 05:44:45 momjian Exp $
+dnl $PostgreSQL: pgsql/configure.in,v 1.385 2004/11/06 23:06:15 tgl Exp $
 dnl
 dnl Developers, please strive to achieve this order:
 dnl
@@ -826,7 +826,7 @@ PGAC_FUNC_GETTIMEOFDAY_1ARG
 # SunOS doesn't handle negative byte comparisons properly with +/- return
 AC_FUNC_MEMCMP
 
-AC_CHECK_FUNCS([cbrt dlopen fcvt fdatasync getpeereid memmove poll pstat setproctitle setsid sigprocmask symlink sysconf towlower utime utimes waitpid wcstombs])
+AC_CHECK_FUNCS([cbrt dlopen fcvt fdatasync getpeereid memmove poll pstat readlink setproctitle setsid sigprocmask symlink sysconf towlower utime utimes waitpid wcstombs])
 
 AC_CHECK_DECLS(fdatasync, [], [], [#include <unistd.h>])
 
index 600f4229f0a1c1dd239586db9217a9d10ae41265..279b6b539c8879c84d2c94755d53016b4541ae60 100644 (file)
 /* Define to 1 if you have the <readline/readline.h> header file. */
 #undef HAVE_READLINE_READLINE_H
 
+/* Define to 1 if you have the `readlink' function. */
+#undef HAVE_READLINK
+
 /* Define to 1 if you have the `replace_history_entry' function. */
 #undef HAVE_REPLACE_HISTORY_ENTRY
 
index 839bc73f00514859f02f13dcc9725a0e839fc64f..28b25c4b7f99ac99ec5a3b1066f601ed6c573bce 100644 (file)
@@ -1,13 +1,15 @@
 /*-------------------------------------------------------------------------
  *
  * exec.c
+ *             Functions for finding and validating executable files
+ *
  *
  * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/port/exec.c,v 1.31 2004/11/06 01:16:22 tgl Exp $
+ *       $PostgreSQL: pgsql/src/port/exec.c,v 1.32 2004/11/06 23:06:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #define log_error(str, param)  (fprintf(stderr, str, param), fputc('\n', stderr))
 #endif
 
+#ifdef WIN32_CLIENT_ONLY
+#define getcwd(cwd,len)  GetCurrentDirectory(len, cwd)
+#endif
+
+static int     validate_exec(const char *path);
+static int     resolve_symlinks(char *path);
+static char *pipe_read_line(char *cmd, char *line, int maxsize);
+
 
 /*
  * validate_exec -- validate "path" as an executable file
@@ -154,13 +164,20 @@ validate_exec(const char *path)
 #endif
 }
 
+
 /*
  * find_my_exec -- find an absolute path to a valid executable
  *
+ *     argv0 is the name passed on the command line
+ *     retpath is the output area (must be of size MAXPGPATH)
+ *     Returns 0 if OK, -1 if error.
+ *
  * The reason we have to work so hard to find an absolute path is that
  * on some platforms we can't do dynamic loading unless we know the
  * executable's location.  Also, we need a full path not a relative
- * path because we will later change working directory.
+ * path because we will later change working directory.  Finally, we want
+ * a true path not a symlink location, so that we can locate other files
+ * that are part of our installation relative to the executable.
  *
  * This function is not thread-safe because it calls validate_exec(),
  * which calls getgrgid().     This function should be used only in
@@ -173,13 +190,12 @@ find_my_exec(const char *argv0, char *retpath)
                                test_path[MAXPGPATH];
        char       *path;
 
-#ifndef WIN32_CLIENT_ONLY
        if (!getcwd(cwd, MAXPGPATH))
-               strcpy(cwd, ".");               /* cheesy, but better than nothing */
-#else
-       if (!GetCurrentDirectory(MAXPGPATH, cwd))
-               strcpy(cwd, ".");               /* cheesy, but better than nothing */
-#endif
+       {
+               log_error(_("could not identify current directory: %s"),
+                                 strerror(errno));
+               return -1;
+       }
 
        /*
         * If argv0 contains a separator, then PATH wasn't used.
@@ -193,7 +209,7 @@ find_my_exec(const char *argv0, char *retpath)
                canonicalize_path(retpath);
 
                if (validate_exec(retpath) == 0)
-                       return 0;
+                       return resolve_symlinks(retpath);
 
                log_error("invalid binary \"%s\"", retpath);
                return -1;
@@ -203,7 +219,7 @@ find_my_exec(const char *argv0, char *retpath)
        /* Win32 checks the current directory first for names without slashes */
        join_path_components(retpath, cwd, argv0);
        if (validate_exec(retpath) == 0)
-               return 0;
+               return resolve_symlinks(retpath);
 #endif
 
        /*
@@ -240,7 +256,7 @@ find_my_exec(const char *argv0, char *retpath)
                        switch (validate_exec(retpath))
                        {
                                case 0:                 /* found ok */
-                                       return 0;
+                                       return resolve_symlinks(retpath);
                                case -1:                /* wasn't even a candidate, keep looking */
                                        break;
                                case -2:                /* found but disqualified */
@@ -254,6 +270,141 @@ find_my_exec(const char *argv0, char *retpath)
        return -1;
 }
 
+
+/*
+ * resolve_symlinks - resolve symlinks to the underlying file
+ *
+ * If path does not point to a symlink, leave it alone.  If it does,
+ * replace it by the absolute path to the referenced file.
+ *
+ * Returns 0 if OK, -1 if error.
+ *
+ * Note: we are not particularly tense about producing nice error messages
+ * because we are not really expecting error here; we just determined that
+ * the symlink does point to a valid executable.
+ */
+static int
+resolve_symlinks(char *path)
+{
+#ifdef HAVE_READLINK
+       struct stat buf;
+       char            orig_wd[MAXPGPATH],
+                               link_buf[MAXPGPATH];
+       char       *fname;
+
+       /* Quick out if it's not a symlink */
+       if (lstat(path, &buf) < 0 ||
+               (buf.st_mode & S_IFMT) != S_IFLNK)
+               return 0;
+
+       /*
+        * To resolve a symlink properly, we have to chdir into its directory
+        * and then chdir to where the symlink points; otherwise we may fail to
+        * resolve relative links correctly (consider cases involving mount
+        * points, for example).  After following the final symlink, we use
+        * getcwd() to figure out where the heck we're at.
+        */
+       if (!getcwd(orig_wd, MAXPGPATH))
+       {
+               log_error(_("could not identify current directory: %s"),
+                                 strerror(errno));
+               return -1;
+       }
+
+       for (;;)
+       {
+               char   *lsep;
+               int             rllen;
+
+               lsep = last_dir_separator(path);
+               if (lsep)
+               {
+                       *lsep = '\0';
+                       if (chdir(path) == -1)
+                       {
+                               log_error(_("could not change directory to \"%s\""), path);
+                               return -1;
+                       }
+                       fname = lsep + 1;
+               }
+               else
+                       fname = path;
+
+               if (lstat(fname, &buf) < 0 ||
+                       (buf.st_mode & S_IFMT) != S_IFLNK)
+                       break;
+
+               rllen = readlink(fname, link_buf, sizeof(link_buf));
+               if (rllen < 0 || rllen >= sizeof(link_buf))
+               {
+                       log_error(_("could not read symbolic link \"%s\""), fname);
+                       return -1;
+               }
+               link_buf[rllen] = '\0';
+               strcpy(path, link_buf);
+       }
+
+       /* must copy final component out of 'path' temporarily */
+       strcpy(link_buf, fname);
+
+       if (!getcwd(path, MAXPGPATH))
+       {
+               log_error(_("could not identify current directory: %s"),
+                                 strerror(errno));
+               return -1;
+       }
+       join_path_components(path, path, link_buf);
+       canonicalize_path(path);
+
+       if (chdir(orig_wd) == -1)
+       {
+               log_error(_("could not change directory to \"%s\""), orig_wd);
+               return -1;
+       }
+
+#endif /* HAVE_READLINK */
+
+       return 0;
+}
+
+
+/*
+ * Find another program in our binary's directory,
+ * then make sure it is the proper version.
+ */
+int
+find_other_exec(const char *argv0, const char *target,
+                               const char *versionstr, char *retpath)
+{
+       char            cmd[MAXPGPATH];
+       char            line[100];
+
+       if (find_my_exec(argv0, retpath) < 0)
+               return -1;
+
+       /* Trim off program name and keep just directory */
+       *last_dir_separator(retpath) = '\0';
+       canonicalize_path(retpath);
+
+       /* Now append the other program's name */
+       snprintf(retpath + strlen(retpath), MAXPGPATH - strlen(retpath),
+                        "/%s%s", target, EXE);
+
+       if (validate_exec(retpath) != 0)
+               return -1;
+
+       snprintf(cmd, sizeof(cmd), "\"%s\" -V 2>%s", retpath, DEVNULL);
+
+       if (!pipe_read_line(cmd, line, sizeof(line)))
+               return -1;
+
+       if (strcmp(line, versionstr) != 0)
+               return -2;
+
+       return 0;
+}
+
+
 /*
  * The runtime library's popen() on win32 does not work when being
  * called from a service when running on windows <= 2000, because
@@ -262,7 +413,6 @@ find_my_exec(const char *argv0, char *retpath)
  * Executing a command in a pipe and reading the first line from it
  * is all we need.
  */
-
 static char *
 pipe_read_line(char *cmd, char *line, int maxsize)
 {
@@ -286,8 +436,9 @@ pipe_read_line(char *cmd, char *line, int maxsize)
                return NULL;
 
        return line;
-#else
-       /* Win32 */
+
+#else /* WIN32 */
+
        SECURITY_ATTRIBUTES sattr;
        HANDLE          childstdoutrd,
                                childstdoutwr,
@@ -392,44 +543,7 @@ pipe_read_line(char *cmd, char *line, int maxsize)
        CloseHandle(childstdoutrddup);
 
        return retval;
-#endif
-}
-
-
-/*
- * Find another program in our binary's directory,
- * then make sure it is the proper version.
- */
-int
-find_other_exec(const char *argv0, const char *target,
-                               const char *versionstr, char *retpath)
-{
-       char            cmd[MAXPGPATH];
-       char            line[100];
-
-       if (find_my_exec(argv0, retpath) < 0)
-               return -1;
-
-       /* Trim off program name and keep just directory */
-       *last_dir_separator(retpath) = '\0';
-       canonicalize_path(retpath);
-
-       /* Now append the other program's name */
-       snprintf(retpath + strlen(retpath), MAXPGPATH - strlen(retpath),
-                        "/%s%s", target, EXE);
-
-       if (validate_exec(retpath))
-               return -1;
-
-       snprintf(cmd, sizeof(cmd), "\"%s\" -V 2>%s", retpath, DEVNULL);
-
-       if (!pipe_read_line(cmd, line, sizeof(line)))
-               return -1;
-
-       if (strcmp(line, versionstr) != 0)
-               return -2;
-
-       return 0;
+#endif /* WIN32 */
 }
 
 
@@ -454,20 +568,14 @@ pclose_check(FILE *stream)
                perror("pclose failed");
        }
        else if (WIFEXITED(exitstatus))
-       {
                log_error(_("child process exited with exit code %d"),
                                  WEXITSTATUS(exitstatus));
-       }
        else if (WIFSIGNALED(exitstatus))
-       {
                log_error(_("child process was terminated by signal %d"),
                                  WTERMSIG(exitstatus));
-       }
        else
-       {
                log_error(_("child process exited with unrecognized status %d"),
                                  exitstatus);
-       }
 
        return -1;
 }