diff --git a/configure b/configure index 7e0a563444..b53dc77230 100755 --- 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 diff --git a/configure.in b/configure.in index 14af5ae027..6eb4e120b0 100644 --- a/configure.in +++ b/configure.in @@ -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 ]) diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index 600f4229f0..279b6b539c 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -345,6 +345,9 @@ /* Define to 1 if you have the 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 diff --git a/src/port/exec.c b/src/port/exec.c index 839bc73f00..28b25c4b7f 100644 --- a/src/port/exec.c +++ b/src/port/exec.c @@ -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 $ * *------------------------------------------------------------------------- */ @@ -47,6 +49,14 @@ #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; }