Refactoring of running other programs - Mailing list pgsql-patches

From Bruce Momjian
Subject Refactoring of running other programs
Date
Msg-id 200405112156.i4BLuXs24919@candle.pha.pa.us
Whole thread Raw
List pgsql-patches
As part of the work for making relocatable installs, I have re-factored
all the code that looks for other binaries.  I move FindExec into
port/exec.c (and renamed it to find_my_binary()).  I also added
find_other_binary that looks for another binary in the same directory as
the calling program, and checks the version string.

The only behavior change was that initdb and pg_dump would look in the
hard-coded bindir directory if it can't find the requested binary in the
same directory as the caller.  The new code throws an error.  The old
behavior seemed too error prone for version mismatches.

Patch attached and applied. I am not doing the standard application
delay because I want to continue on to relocatable installs. I will make
any suggested adjustments.

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073
Index: configure
===================================================================
RCS file: /cvsroot/pgsql-server/configure,v
retrieving revision 1.357
diff -c -c -r1.357 configure
*** configure    7 May 2004 00:24:56 -0000    1.357
--- configure    11 May 2004 21:51:34 -0000
***************
*** 12011,12017 ****
  LIBOBJS="$LIBOBJS copydir.$ac_objext"
  LIBOBJS="$LIBOBJS gettimeofday.$ac_objext"
  LIBOBJS="$LIBOBJS open.$ac_objext"
- LIBOBJS="$LIBOBJS pipe.$ac_objext"
  LIBOBJS="$LIBOBJS rand.$ac_objext"

  cat >>confdefs.h <<\_ACEOF
--- 12011,12016 ----
Index: configure.in
===================================================================
RCS file: /cvsroot/pgsql-server/configure.in,v
retrieving revision 1.345
diff -c -c -r1.345 configure.in
*** configure.in    7 May 2004 00:24:57 -0000    1.345
--- configure.in    11 May 2004 21:51:35 -0000
***************
*** 892,898 ****
  AC_LIBOBJ(copydir)
  AC_LIBOBJ(gettimeofday)
  AC_LIBOBJ(open)
- AC_LIBOBJ(pipe)
  AC_LIBOBJ(rand)
  AC_DEFINE(USE_PGTZ, 1,
      [Define to 1 to use our own timezone library])
--- 892,897 ----
Index: src/Makefile.global.in
===================================================================
RCS file: /cvsroot/pgsql-server/src/Makefile.global.in,v
retrieving revision 1.181
diff -c -c -r1.181 Makefile.global.in
*** src/Makefile.global.in    7 May 2004 00:24:57 -0000    1.181
--- src/Makefile.global.in    11 May 2004 21:51:36 -0000
***************
*** 339,345 ****
  #
  # substitute implementations of the C library

! LIBOBJS = @LIBOBJS@ noblock.o path.o pgsleep.o pgstrcasecmp.o sprompt.o thread.o

  ifneq (,$(LIBOBJS))
  LIBS += -lpgport
--- 339,345 ----
  #
  # substitute implementations of the C library

! LIBOBJS = @LIBOBJS@ exec.o noblock.o path.o pipe.o pgsleep.o pgstrcasecmp.o sprompt.o thread.o

  ifneq (,$(LIBOBJS))
  LIBS += -lpgport
Index: src/backend/postmaster/postmaster.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/backend/postmaster/postmaster.c,v
retrieving revision 1.382
diff -c -c -r1.382 postmaster.c
*** src/backend/postmaster/postmaster.c    6 May 2004 19:23:25 -0000    1.382
--- src/backend/postmaster/postmaster.c    11 May 2004 21:51:39 -0000
***************
*** 692,698 ****
      /*
       * On some systems our dynloader code needs the executable's pathname.
       */
!     if (FindExec(pg_pathname, progname, "postgres") < 0)
          ereport(FATAL,
                  (errmsg("%s: could not locate postgres executable",
                          progname)));
--- 692,698 ----
      /*
       * On some systems our dynloader code needs the executable's pathname.
       */
!     if (find_my_binary(pg_pathname, progname, "postgres") < 0)
          ereport(FATAL,
                  (errmsg("%s: could not locate postgres executable",
                          progname)));
***************
*** 3222,3228 ****
      FILE       *fp;
      int            i;

!     if (FindExec(fullprogname, argv[0], "postmaster") < 0)
      {
          elog(LOG, "could not locate postmaster");
          return false;
--- 3222,3228 ----
      FILE       *fp;
      int            i;

!     if (find_my_binary(fullprogname, argv[0], "postmaster") < 0)
      {
          elog(LOG, "could not locate postmaster");
          return false;
Index: src/backend/tcop/postgres.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/backend/tcop/postgres.c,v
retrieving revision 1.402
diff -c -c -r1.402 postgres.c
*** src/backend/tcop/postgres.c    7 May 2004 01:53:41 -0000    1.402
--- src/backend/tcop/postgres.c    11 May 2004 21:51:41 -0000
***************
*** 2649,2655 ****
           * On some systems our dynloader code needs the executable's
           * pathname.  (If under postmaster, this was done already.)
           */
!         if (FindExec(pg_pathname, argv[0], "postgres") < 0)
              ereport(FATAL,
                      (errmsg("%s: could not locate postgres executable",
                              argv[0])));
--- 2649,2655 ----
           * On some systems our dynloader code needs the executable's
           * pathname.  (If under postmaster, this was done already.)
           */
!         if (find_my_binary(pg_pathname, argv[0], "postgres") < 0)
              ereport(FATAL,
                      (errmsg("%s: could not locate postgres executable",
                              argv[0])));
Index: src/backend/utils/init/Makefile
===================================================================
RCS file: /cvsroot/pgsql-server/src/backend/utils/init/Makefile,v
retrieving revision 1.16
diff -c -c -r1.16 Makefile
*** src/backend/utils/init/Makefile    29 Nov 2003 19:52:01 -0000    1.16
--- src/backend/utils/init/Makefile    11 May 2004 21:51:41 -0000
***************
*** 12,18 ****
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global

! OBJS = findbe.o globals.o miscinit.o postinit.o

  all: SUBSYS.o

--- 12,18 ----
  top_builddir = ../../../..
  include $(top_builddir)/src/Makefile.global

! OBJS = globals.o miscinit.o postinit.o

  all: SUBSYS.o

Index: src/backend/utils/init/findbe.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/backend/utils/init/findbe.c,v
retrieving revision 1.42
diff -c -c -r1.42 findbe.c
*** src/backend/utils/init/findbe.c    15 Mar 2004 16:18:43 -0000    1.42
--- src/backend/utils/init/findbe.c    11 May 2004 21:51:41 -0000
***************
*** 147,153 ****
  }

  /*
!  * FindExec -- find an absolute path to a valid backend executable
   *
   * 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
--- 147,153 ----
  }

  /*
!  * find_my_binary -- find an absolute path to a valid backend executable
   *
   * 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
***************
*** 155,161 ****
   * path because we will later change working directory.
   */
  int
! FindExec(char *full_path, const char *argv0, const char *binary_name)
  {
      char        buf[MAXPGPATH + 2];
      char       *p;
--- 155,161 ----
   * path because we will later change working directory.
   */
  int
! find_my_binary(char *full_path, const char *argv0, const char *binary_name)
  {
      char        buf[MAXPGPATH + 2];
      char       *p;
Index: src/bin/initdb/Makefile
===================================================================
RCS file: /cvsroot/pgsql-server/src/bin/initdb/Makefile,v
retrieving revision 1.36
diff -c -c -r1.36 Makefile
*** src/bin/initdb/Makefile    26 Apr 2004 17:40:48 -0000    1.36
--- src/bin/initdb/Makefile    11 May 2004 21:51:42 -0000
***************
*** 13,27 ****
  top_builddir = ../../..
  include $(top_builddir)/src/Makefile.global

! override CPPFLAGS := -DPGBINDIR=\"$(bindir)\" -DPGDATADIR=\"$(datadir)\" -DFRONTEND -I$(libpq_srcdir) $(CPPFLAGS)

! OBJS=    initdb.o

  all: submake-libpq submake-libpgport initdb

  initdb: $(OBJS) $(libpq_builddir)/libpq.a
      $(CC) $(CFLAGS) $(OBJS) $(libpq) $(LDFLAGS) $(LIBS) -o $@$(X)

  install: all installdirs
      $(INSTALL_PROGRAM) initdb$(X) $(DESTDIR)$(bindir)/initdb$(X)

--- 13,31 ----
  top_builddir = ../../..
  include $(top_builddir)/src/Makefile.global

! override CPPFLAGS := -DPGDATADIR=\"$(datadir)\" -DFRONTEND -I$(libpq_srcdir) $(CPPFLAGS)

! OBJS=    initdb.o \
!     $(filter exec.o, $(LIBOBJS))

  all: submake-libpq submake-libpgport initdb

  initdb: $(OBJS) $(libpq_builddir)/libpq.a
      $(CC) $(CFLAGS) $(OBJS) $(libpq) $(LDFLAGS) $(LIBS) -o $@$(X)

+ exec.c: % : $(top_srcdir)/src/port/%
+     rm -f $@ && $(LN_S) $< .
+
  install: all installdirs
      $(INSTALL_PROGRAM) initdb$(X) $(DESTDIR)$(bindir)/initdb$(X)

***************
*** 32,39 ****
      rm -f $(DESTDIR)$(bindir)/initdb$(X)

  clean distclean maintainer-clean:
!     rm -f initdb$(X) $(OBJS)


! # ensure that changes in bindir etc. propagate into object file
  initdb.o: initdb.c $(top_builddir)/src/Makefile.global
--- 36,43 ----
      rm -f $(DESTDIR)$(bindir)/initdb$(X)

  clean distclean maintainer-clean:
!     rm -f initdb$(X) $(OBJS) exec.c


! # ensure that changes in datadir propagate into object file
  initdb.o: initdb.c $(top_builddir)/src/Makefile.global
Index: src/bin/initdb/initdb.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/bin/initdb/initdb.c,v
retrieving revision 1.26
diff -c -c -r1.26 initdb.c
*** src/bin/initdb/initdb.c    10 May 2004 20:51:58 -0000    1.26
--- src/bin/initdb/initdb.c    11 May 2004 21:51:43 -0000
***************
*** 25,34 ****
   *
   * template0 is made just by copying the completed template1.
   *
-  *
-  * TODO:
-  *     - clean up find_postgres code and return values
-  *
   * Note:
   *     The program has some memory leakage - it isn't worth cleaning it up.
   *
--- 25,30 ----
***************
*** 52,58 ****

  #include <dirent.h>
  #include <sys/stat.h>
- #include <sys/wait.h>
  #include <unistd.h>
  #include <locale.h>
  #include <signal.h>
--- 48,53 ----
***************
*** 78,84 ****
   * Note that "datadir" is not the directory we're going to initialize,
   * it's merely how Autoconf names PREFIX/share.
   */
- char       *bindir = PGBINDIR;
  char       *datadir = PGDATADIR;

  /* values to be obtained from arguments */
--- 73,78 ----
***************
*** 100,106 ****

  /* internal vars */
  char       *progname;
- char       *self_path;
  char       *postgres;
  char       *encodingid = "0";
  char       *bki_file;
--- 94,99 ----
***************
*** 135,159 ****
  static const char *backend_options = "-F -O -c search_path=pg_catalog -c exit_on_error=true";


! /* platform specific path stuff */
! #if defined(__CYGWIN__) || defined(WIN32)
! #define EXE ".exe"
! #define DEVNULL "nul"
! #else
! #define EXE ""
! #define DEVNULL "/dev/null"
! #endif
!
!
! /* detected path to postgres and (we assume) friends */
! char       *pgpath;
!
! /* forward declare all our functions */
! #ifdef WIN32
! static char *expanded_path(char *);
! #else
! #define expanded_path(x) (x)
! #endif

  static void *xmalloc(size_t size);
  static char *xstrdup(const char *s);
--- 128,136 ----
  static const char *backend_options = "-F -O -c search_path=pg_catalog -c exit_on_error=true";


! /* path to 'initdb' binary directory */
! char       bindir[MAXPGPATH];
! char       backendbin[MAXPGPATH];

  static void *xmalloc(size_t size);
  static char *xstrdup(const char *s);
***************
*** 161,167 ****
  static char **replace_token(char **lines, char *token, char *replacement);
  static char **readfile(char *path);
  static void writefile(char *path, char **lines);
- static void pclose_check(FILE *stream);
  static int mkdir_p(char *path, mode_t omode);
  static void exit_nicely(void);
  static char *get_id(void);
--- 138,143 ----
***************
*** 171,178 ****
  static bool mkdatadir(char *subdir);
  static void set_input(char **dest, char *filename);
  static void check_input(char *path);
- static int find_postgres(char *path);
- static int set_paths(void);
  static void set_short_version(char *short_version, char *extrapath);
  static void set_null_conf(void);
  static void test_connections(void);
--- 147,152 ----
***************
*** 216,222 ****

  #define PG_CMD_CLOSE \
  do { \
!     pclose_check(pg); \
  } while (0)

  #define PG_CMD_PUTLINE \
--- 190,197 ----

  #define PG_CMD_CLOSE \
  do { \
!     if (pclose_check(pg)) \
!         exit_nicely(); \
  } while (0)

  #define PG_CMD_PUTLINE \
***************
*** 437,477 ****
          exit_nicely();
  }

- /* pclose() plus useful error reporting */
- static void
- pclose_check(FILE *stream)
- {
-     int        exitstatus;
-
-     exitstatus = pclose(stream);
-
-     if (exitstatus == 0)
-         return;                    /* all is well */
-
-     if (exitstatus == -1)
-     {
-         /* pclose() itself failed, and hopefully set errno */
-         perror("pclose failed");
-     }
-     else if (WIFEXITED(exitstatus))
-     {
-         fprintf(stderr, _("%s: child process exited with exit code %d\n"),
-                 progname, WEXITSTATUS(exitstatus));
-     }
-     else if (WIFSIGNALED(exitstatus))
-     {
-         fprintf(stderr, _("%s: child process was terminated by signal %d\n"),
-                 progname, WTERMSIG(exitstatus));
-     }
-     else
-     {
-         fprintf(stderr, _("%s: child process exited with unrecognized status %d\n"),
-                 progname, exitstatus);
-     }
-
-     exit_nicely();
- }
-
  /* source stolen from FreeBSD /src/bin/mkdir/mkdir.c and adapted */

  /*
--- 412,417 ----
***************
*** 815,1007 ****
  }

  /*
-  * TODO - clean this up and handle the errors properly
-  * don't overkill
-  */
- #define FIND_SUCCESS 0
- #define FIND_NOT_FOUND 1
- #define FIND_STAT_ERR 2
- #define FIND_NOT_REGFILE 3
- #define FIND_BAD_PERM 4
- #define FIND_EXEC_ERR 5
- #define FIND_WRONG_VERSION 6
-
- /*
-  * see if there is a postgres executable in the given path, and giving the
-  * right version number
-  */
- static int
- find_postgres(char *path)
- {
-     char        fn[MAXPGPATH];
-     char        cmd[MAXPGPATH];
-     char        line[100];
-
- #ifndef WIN32
-     int            permmask = S_IROTH | S_IXOTH;
- #endif
-
-     struct stat statbuf;
-     FILE       *pgver;
-     int            plen = strlen(path);
-
-     if (plen > 0 && path[plen - 1] != '/')
-         snprintf(fn, sizeof(fn), "%s/postgres%s", path, EXE);
-     else
-         snprintf(fn, sizeof(fn), "%spostgres%s", path, EXE);
-
-     if (stat(fn, &statbuf) != 0)
-     {
-         if (errno == ENOENT)
-             return FIND_NOT_FOUND;
-         else
-             return FIND_STAT_ERR;
-     }
-     if (!S_ISREG(statbuf.st_mode))
-         return FIND_NOT_REGFILE;
-
- #ifndef WIN32
-
-     /*
-      * Only unix requires this test, on WIN32 an .exe file should be
-      * executable
-      */
-     if ((statbuf.st_mode & permmask) != permmask)
-         return FIND_BAD_PERM;
- #endif
-
-     snprintf(cmd, sizeof(cmd), "\"%s/postgres\" -V 2>%s", path, DEVNULL);
-
-     /* flush output buffers in case popen does not... */
-     fflush(stdout);
-     fflush(stderr);
-
-     if ((pgver = popen(cmd, "r")) == NULL)
-         return FIND_EXEC_ERR;
-
-     if (fgets(line, sizeof(line), pgver) == NULL)
-         perror("fgets failure");
-
-     pclose_check(pgver);
-
-     if (strcmp(line, PG_VERSIONSTR) != 0)
-         return FIND_WRONG_VERSION;
-
-     return FIND_SUCCESS;
- }
-
- /*
-  * Windows doesn't like relative paths to executables (other things work fine)
-  * so we call its builtin function to expand them. Elsewhere this is a NOOP
-  */
- #ifdef WIN32
- static char *
- expanded_path(char *path)
- {
-     char        abspath[MAXPGPATH];
-
-     if (_fullpath(abspath, path, sizeof(abspath)) == NULL)
-     {
-         perror("expanded path");
-         return path;
-     }
-     canonicalize_path(abspath);
-     return xstrdup(abspath);
- }
- #endif
-
- /*
-  * set the paths pointing to postgres
-  *
-  * look for it in the same place we found this program, or in the environment
-  * path, or in the configured bindir.
-  * We do it in this order because during upgrades users might move
-  * their trees to backup places, so the hard-wired bindir might be inaccurate.
-  *
-  * XXX this needs work, as its error handling is vastly inferior to the
-  * shell-script version, in particular the case where a postgres executable
-  * is failing
-  */
- static int
- set_paths(void)
- {
-     if (testpath && !self_path)
-     {
-         char       *path,
-                    *cursor;
-         int            pathlen,
-                     i,
-                     pathsegs;
-         char      **pathbits;
-         char        buf[MAXPGPATH];
-         struct stat statbuf;
-
-         path = xstrdup(getenv("PATH"));
-         pathlen = strlen(path);
-
-         for (i = 0, pathsegs = 1; i < pathlen; i++)
-         {
-             if (path[i] == PATHSEP)
-                 pathsegs++;
-         }
-
-         pathbits = (char **) xmalloc(pathsegs * sizeof(char *));
-         for (i = 0, pathsegs = 0, cursor = path; i <= pathlen; i++)
-         {
-             if (path[i] == PATHSEP || path[i] == 0)
-             {
-                 path[i] = 0;
-                 if (strlen(cursor) == 0)
-                 {
-                     /* empty path segment means current directory */
-                     pathbits[pathsegs] = xstrdup(".");
-                 }
-                 else
-                 {
-                     canonicalize_path(cursor);
-                     pathbits[pathsegs] = cursor;
-                 }
-                 pathsegs++;
-                 cursor = path + i + 1;
-             }
-         }
-
-         for (i = 0; i < pathsegs; i++)
-         {
-             snprintf(buf, sizeof(buf), "%s/%s%s", pathbits[i], progname, EXE);
-             if (stat(buf, &statbuf) == 0 && S_ISREG(statbuf.st_mode))
-             {
-                 self_path = pathbits[i];
-                 break;
-             }
-         }
-     }
-
-     if (testpath && self_path &&
-         (find_postgres(expanded_path(self_path)) == 0))
-     {
-         /* we found postgres on out own path */
-         pgpath = expanded_path(self_path);
-     }
-     else
-     {
-         /* look in the hardcoded bindir */
-         int            res;
-         char       *cbindir;
-
-         cbindir = xstrdup(bindir);
-         canonicalize_path(cbindir);
-         res = find_postgres(expanded_path(cbindir));
-         if (res == 0)
-             pgpath = expanded_path(cbindir);
-         else
-             return 1;
-     }
-
-     return 0;
- }
-
- /*
   * write out the PG_VERSION file in the data dir, or its subdirectory
   * if extrapath is not NULL
   */
--- 755,760 ----
***************
*** 1063,1072 ****
      for (i = 0; i < len; i++)
      {
          snprintf(cmd, sizeof(cmd),
!                  "\"%s/postgres\" -boot -x0 %s "
                   "-c shared_buffers=%d -c max_connections=%d template1 "
                   "<%s >%s 2>&1",
!                  pgpath, boot_options,
                   conns[i] * 5, conns[i],
                   DEVNULL, DEVNULL);
          status = system(cmd);
--- 816,825 ----
      for (i = 0; i < len; i++)
      {
          snprintf(cmd, sizeof(cmd),
!                  "\"%s\" -boot -x0 %s "
                   "-c shared_buffers=%d -c max_connections=%d template1 "
                   "<%s >%s 2>&1",
!                  backendbin, boot_options,
                   conns[i] * 5, conns[i],
                   DEVNULL, DEVNULL);
          status = system(cmd);
***************
*** 1099,1108 ****
      for (i = 0; i < len; i++)
      {
          snprintf(cmd, sizeof(cmd),
!                  "\"%s/postgres\" -boot -x0 %s "
                   "-c shared_buffers=%d -c max_connections=%d template1 "
                   "<%s >%s 2>&1",
!                  pgpath, boot_options,
                   bufs[i], n_connections,
                   DEVNULL, DEVNULL);
          status = system(cmd);
--- 852,861 ----
      for (i = 0; i < len; i++)
      {
          snprintf(cmd, sizeof(cmd),
!                  "\"%s\" -boot -x0 %s "
                   "-c shared_buffers=%d -c max_connections=%d template1 "
                   "<%s >%s 2>&1",
!                  backendbin, boot_options,
                   bufs[i], n_connections,
                   DEVNULL, DEVNULL);
          status = system(cmd);
***************
*** 1250,1257 ****
      unsetenv("PGCLIENTENCODING");

      snprintf(cmd, sizeof(cmd),
!              "\"%s/postgres\" -boot -x1 %s %s template1",
!              pgpath, boot_options, talkargs);

      PG_CMD_OPEN;

--- 1003,1010 ----
      unsetenv("PGCLIENTENCODING");

      snprintf(cmd, sizeof(cmd),
!              "\"%s\" -boot -x1 %s %s template1",
!              backendbin, boot_options, talkargs);

      PG_CMD_OPEN;

***************
*** 1300,1307 ****
      fflush(stdout);

      snprintf(cmd, sizeof(cmd),
!              "\"%s/postgres\" %s template1 >%s",
!              pgpath, backend_options,
               DEVNULL);

      PG_CMD_OPEN;
--- 1053,1060 ----
      fflush(stdout);

      snprintf(cmd, sizeof(cmd),
!              "\"%s\" %s template1 >%s",
!              backendbin, backend_options,
               DEVNULL);

      PG_CMD_OPEN;
***************
*** 1340,1347 ****
      fflush(stdout);

      snprintf(cmd, sizeof(cmd),
!              "\"%s/postgres\" %s template1 >%s",
!              pgpath, backend_options,
               DEVNULL);

      PG_CMD_OPEN;
--- 1093,1100 ----
      fflush(stdout);

      snprintf(cmd, sizeof(cmd),
!              "\"%s\" %s template1 >%s",
!              backendbin, backend_options,
               DEVNULL);

      PG_CMD_OPEN;
***************
*** 1394,1401 ****
      fflush(stdout);

      snprintf(cmd, sizeof(cmd),
!              "\"%s/postgres\" %s template1 >%s",
!              pgpath, backend_options,
               DEVNULL);

      PG_CMD_OPEN;
--- 1147,1154 ----
      fflush(stdout);

      snprintf(cmd, sizeof(cmd),
!              "\"%s\" %s template1 >%s",
!              backendbin, backend_options,
               DEVNULL);

      PG_CMD_OPEN;
***************
*** 1467,1474 ****
      fflush(stdout);

      snprintf(cmd, sizeof(cmd),
!              "\"%s/postgres\" %s template1 >%s",
!              pgpath, backend_options,
               DEVNULL);

      PG_CMD_OPEN;
--- 1220,1227 ----
      fflush(stdout);

      snprintf(cmd, sizeof(cmd),
!              "\"%s\" %s template1 >%s",
!              backendbin, backend_options,
               DEVNULL);

      PG_CMD_OPEN;
***************
*** 1500,1507 ****
       * We use -N here to avoid backslashing stuff in system_views.sql
       */
      snprintf(cmd, sizeof(cmd),
!              "\"%s/postgres\" %s -N template1 >%s",
!              pgpath, backend_options,
               DEVNULL);

      PG_CMD_OPEN;
--- 1253,1260 ----
       * We use -N here to avoid backslashing stuff in system_views.sql
       */
      snprintf(cmd, sizeof(cmd),
!              "\"%s\" %s -N template1 >%s",
!              backendbin, backend_options,
               DEVNULL);

      PG_CMD_OPEN;
***************
*** 1532,1539 ****
      fflush(stdout);

      snprintf(cmd, sizeof(cmd),
!              "\"%s/postgres\" %s template1 >%s",
!              pgpath, backend_options,
               DEVNULL);

      PG_CMD_OPEN;
--- 1285,1292 ----
      fflush(stdout);

      snprintf(cmd, sizeof(cmd),
!              "\"%s\" %s template1 >%s",
!              backendbin, backend_options,
               DEVNULL);

      PG_CMD_OPEN;
***************
*** 1580,1587 ****
      fflush(stdout);

      snprintf(cmd, sizeof(cmd),
!              "\"%s/postgres\" %s template1 >%s",
!              pgpath, backend_options,
               DEVNULL);

      PG_CMD_OPEN;
--- 1333,1340 ----
      fflush(stdout);

      snprintf(cmd, sizeof(cmd),
!              "\"%s\" %s template1 >%s",
!              backendbin, backend_options,
               DEVNULL);

      PG_CMD_OPEN;
***************
*** 1636,1643 ****
      fflush(stdout);

      snprintf(cmd, sizeof(cmd),
!              "\"%s/postgres\" %s template1 >%s",
!              pgpath, backend_options,
               DEVNULL);

      PG_CMD_OPEN;
--- 1389,1396 ----
      fflush(stdout);

      snprintf(cmd, sizeof(cmd),
!              "\"%s\" %s template1 >%s",
!              backendbin, backend_options,
               DEVNULL);

      PG_CMD_OPEN;
***************
*** 1699,1706 ****
       * We use -N here to avoid backslashing stuff in information_schema.sql
       */
      snprintf(cmd, sizeof(cmd),
!              "\"%s/postgres\" %s -N template1 >%s",
!              pgpath, backend_options,
               DEVNULL);

      PG_CMD_OPEN;
--- 1452,1459 ----
       * We use -N here to avoid backslashing stuff in information_schema.sql
       */
      snprintf(cmd, sizeof(cmd),
!              "\"%s\" %s -N template1 >%s",
!              backendbin, backend_options,
               DEVNULL);

      PG_CMD_OPEN;
***************
*** 1716,1723 ****
      PG_CMD_CLOSE;

      snprintf(cmd, sizeof(cmd),
!              "\"%s/postgres\" %s template1 >%s",
!              pgpath, backend_options,
               DEVNULL);

      PG_CMD_OPEN;
--- 1469,1476 ----
      PG_CMD_CLOSE;

      snprintf(cmd, sizeof(cmd),
!              "\"%s\" %s template1 >%s",
!              backendbin, backend_options,
               DEVNULL);

      PG_CMD_OPEN;
***************
*** 1756,1763 ****
      fflush(stdout);

      snprintf(cmd, sizeof(cmd),
!              "\"%s/postgres\" %s template1 >%s",
!              pgpath, backend_options,
               DEVNULL);

      PG_CMD_OPEN;
--- 1509,1516 ----
      fflush(stdout);

      snprintf(cmd, sizeof(cmd),
!              "\"%s\" %s template1 >%s",
!              backendbin, backend_options,
               DEVNULL);

      PG_CMD_OPEN;
***************
*** 1812,1819 ****
      fflush(stdout);

      snprintf(cmd, sizeof(cmd),
!              "\"%s/postgres\" %s template1 >%s",
!              pgpath, backend_options,
               DEVNULL);

      PG_CMD_OPEN;
--- 1565,1572 ----
      fflush(stdout);

      snprintf(cmd, sizeof(cmd),
!              "\"%s\" %s template1 >%s",
!              backendbin, backend_options,
               DEVNULL);

      PG_CMD_OPEN;
***************
*** 2043,2090 ****
      };

      int            c,
!                 i;
      int            option_index;
      char       *short_version;
      char       *pgdenv;            /* PGDATA value got from sent to
                                   * environment */
      char       *subdirs[] =
      {"global", "pg_xlog", "pg_clog", "base", "base/1"};
-     char       *lastsep;
-     char       *carg0;
- #if defined(__CYGWIN__) || defined(WIN32)
-     char       *exe;            /* location of exe suffix in progname */
- #endif
-
      init_nls();

!     /* parse argv[0] - detect explicit path if there was one */
!     carg0 = xstrdup(argv[0]);
!     canonicalize_path(carg0);
!
!     lastsep = strrchr(carg0, '/');
!     progname = lastsep ? xstrdup(lastsep + 1) : carg0;
!
! #if defined(__CYGWIN__) || defined(WIN32)
!     if (strlen(progname) > 4 &&
!         (exe = progname + (strlen(progname) - 4)) &&
!         stricmp(exe, EXE) == 0)
!     {
!         /* strip .exe suffix, regardless of case */
!         *exe = '\0';
!     }
! #endif
!
!     if (lastsep)
!     {
!         self_path = carg0;
!         *lastsep = '\0';
!     }
!     else
!     {
!         /* no path known to ourselves from argv[0] */
!         self_path = NULL;
!     }

      if (argc > 1)
      {
--- 1796,1812 ----
      };

      int            c,
!                 i,
!                 ret;
      int            option_index;
      char       *short_version;
      char       *pgdenv;            /* PGDATA value got from sent to
                                   * environment */
      char       *subdirs[] =
      {"global", "pg_xlog", "pg_clog", "base", "base/1"};
      init_nls();

!     progname = get_progname(argv[0]);

      if (argc > 1)
      {
***************
*** 2210,2225 ****
      sprintf(pgdenv, "PGDATA=%s", pg_data);
      putenv(pgdenv);

!     if (set_paths() != 0)
      {
!         fprintf(stderr,
!                 _("The program \"postgres\" is needed by %s "
!                 "but was not found in \n"
!                 "the directory \"%s\". Check your installation.\n"),
!                 progname, bindir);
          exit(1);
      }

      if ((short_version = get_short_version()) == NULL)
      {
          fprintf(stderr, _("%s: could not determine valid short version string\n"), progname);
--- 1932,1959 ----
      sprintf(pgdenv, "PGDATA=%s", pg_data);
      putenv(pgdenv);

!     if ((ret = find_other_binary(backendbin, argv[0], progname, "postgres",
!                            PG_VERSIONSTR)) < 0)
      {
!         if (ret == -1)
!             fprintf(stderr,
!                         _("The program \"postgres\" is needed by %s "
!                         "but was not found in the same directory as \"%s\".\n"
!                         "Check your installation.\n"),
!                         progname, progname);
!         else
!             fprintf(stderr,
!                         _("The program \"postgres\" was found by %s "
!                         "but was not the same version as \"%s\".\n"
!                         "Check your installation.\n"),
!                         progname, progname);
          exit(1);
      }

+     /* store binary directory */
+     strcpy(bindir, backendbin);
+     *last_path_separator(bindir) = '\0';
+
      if ((short_version = get_short_version()) == NULL)
      {
          fprintf(stderr, _("%s: could not determine valid short version string\n"), progname);
***************
*** 2255,2261 ****
                  "POSTGRES_DESCR=%s\nPOSTGRESQL_CONF_SAMPLE=%s\n"
                  "PG_HBA_SAMPLE=%s\nPG_IDENT_SAMPLE=%s\n",
                  PG_VERSION,
!                 pg_data, datadir, pgpath,
                  encoding, encodingid,
                  username, bki_file,
                  desc_file, conf_file,
--- 1989,1995 ----
                  "POSTGRES_DESCR=%s\nPOSTGRESQL_CONF_SAMPLE=%s\n"
                  "PG_HBA_SAMPLE=%s\nPG_IDENT_SAMPLE=%s\n",
                  PG_VERSION,
!                 pg_data, datadir, bindir,
                  encoding, encodingid,
                  username, bki_file,
                  desc_file, conf_file,
***************
*** 2448,2455 ****
             "    %s%s%s/postmaster -D %s%s%s\n"
             "or\n"
             "    %s%s%s/pg_ctl -D %s%s%s -l logfile start\n\n"),
!          QUOTE_PATH, pgpath, QUOTE_PATH, QUOTE_PATH, pg_data, QUOTE_PATH,
!         QUOTE_PATH, pgpath, QUOTE_PATH, QUOTE_PATH, pg_data, QUOTE_PATH);

      return 0;
  }
--- 2182,2189 ----
             "    %s%s%s/postmaster -D %s%s%s\n"
             "or\n"
             "    %s%s%s/pg_ctl -D %s%s%s -l logfile start\n\n"),
!          QUOTE_PATH, bindir, QUOTE_PATH, QUOTE_PATH, pg_data, QUOTE_PATH,
!         QUOTE_PATH, bindir, QUOTE_PATH, QUOTE_PATH, pg_data, QUOTE_PATH);

      return 0;
  }
Index: src/bin/pg_dump/Makefile
===================================================================
RCS file: /cvsroot/pgsql-server/src/bin/pg_dump/Makefile,v
retrieving revision 1.46
diff -c -c -r1.46 Makefile
*** src/bin/pg_dump/Makefile    30 Apr 2004 20:01:39 -0000    1.46
--- src/bin/pg_dump/Makefile    11 May 2004 21:51:43 -0000
***************
*** 18,27 ****
  OBJS=    pg_backup_archiver.o pg_backup_db.o pg_backup_custom.o \
      pg_backup_files.o pg_backup_null.o pg_backup_tar.o \
      dumputils.o

  EXTRA_OBJS = $(top_builddir)/src/backend/parser/keywords.o

! override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS) -DBINDIR=\"$(bindir)\"


  all: submake-libpq submake-libpgport submake-backend pg_dump pg_restore pg_dumpall
--- 18,28 ----
  OBJS=    pg_backup_archiver.o pg_backup_db.o pg_backup_custom.o \
      pg_backup_files.o pg_backup_null.o pg_backup_tar.o \
      dumputils.o
+ PG_DUMPALL_OBJS =    $(filter exec.o, $(LIBOBJS))

  EXTRA_OBJS = $(top_builddir)/src/backend/parser/keywords.o

! override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS)


  all: submake-libpq submake-libpgport submake-backend pg_dump pg_restore pg_dumpall
***************
*** 32,39 ****
  pg_restore: pg_restore.o $(OBJS) $(libpq_builddir)/libpq.a
      $(CC) $(CFLAGS) pg_restore.o $(OBJS) $(EXTRA_OBJS) $(libpq) $(LDFLAGS) $(LIBS) -o $@$(X)

! pg_dumpall: pg_dumpall.o dumputils.o $(libpq_builddir)/libpq.a
!     $(CC) $(CFLAGS) pg_dumpall.o dumputils.o $(EXTRA_OBJS) $(libpq) $(LDFLAGS) $(LIBS) -o $@$(X)

  .PHONY: submake-backend
  submake-backend:
--- 33,43 ----
  pg_restore: pg_restore.o $(OBJS) $(libpq_builddir)/libpq.a
      $(CC) $(CFLAGS) pg_restore.o $(OBJS) $(EXTRA_OBJS) $(libpq) $(LDFLAGS) $(LIBS) -o $@$(X)

! pg_dumpall: pg_dumpall.o dumputils.o $(PG_DUMPALL_OBJS) $(libpq_builddir)/libpq.a
!     $(CC) $(CFLAGS) pg_dumpall.o dumputils.o $(PG_DUMPALL_OBJS) $(EXTRA_OBJS) $(libpq) $(LDFLAGS) $(LIBS) -o $@$(X)
!
! exec.c: % : $(top_srcdir)/src/port/%
!     rm -f $@ && $(LN_S) $< .

  .PHONY: submake-backend
  submake-backend:
***************
*** 52,59 ****
      rm -f $(addprefix $(DESTDIR)$(bindir)/, pg_dump$(X) pg_restore$(X) pg_dumpall$(X))

  clean distclean maintainer-clean:
!     rm -f pg_dump$(X) pg_restore$(X) pg_dumpall$(X) $(OBJS) pg_dump.o common.o pg_dump_sort.o pg_restore.o
pg_dumpall.o
!
!
! # ensure that changes in bindir etc. propagate into object file
! pg_dumpall.o: pg_dumpall.c $(top_builddir)/src/Makefile.global
--- 56,59 ----
      rm -f $(addprefix $(DESTDIR)$(bindir)/, pg_dump$(X) pg_restore$(X) pg_dumpall$(X))

  clean distclean maintainer-clean:
!     rm -f pg_dump$(X) pg_restore$(X) pg_dumpall$(X) $(OBJS) pg_dump.o common.o pg_dump_sort.o pg_restore.o
pg_dumpall.oexec.c 
Index: src/bin/pg_dump/pg_dumpall.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/bin/pg_dump/pg_dumpall.c,v
retrieving revision 1.30
diff -c -c -r1.30 pg_dumpall.c
*** src/bin/pg_dump/pg_dumpall.c    22 Jan 2004 19:09:32 -0000    1.30
--- src/bin/pg_dump/pg_dumpall.c    11 May 2004 21:51:43 -0000
***************
*** 35,40 ****
--- 35,43 ----

  #define _(x) gettext((x))

+ /* version string we expect back from postgres */
+ #define PG_VERSIONSTR "pg_dump (PostgreSQL) " PG_VERSION "\n"
+

  static char *progname;

***************
*** 52,61 ****
  static PGconn *connectDatabase(const char *dbname, const char *pghost, const char *pgport,
                  const char *pguser, bool require_password);
  static PGresult *executeQuery(PGconn *conn, const char *query);
- static char *findPgDump(const char *argv0);


! char       *pgdumploc;
  PQExpBuffer pgdumpopts;
  bool        output_clean = false;
  bool        skip_acls = false;
--- 55,63 ----
  static PGconn *connectDatabase(const char *dbname, const char *pghost, const char *pgport,
                  const char *pguser, bool require_password);
  static PGresult *executeQuery(PGconn *conn, const char *query);


! char        pg_dump_bin[MAXPGPATH];
  PQExpBuffer pgdumpopts;
  bool        output_clean = false;
  bool        skip_acls = false;
***************
*** 75,81 ****
      bool        globals_only = false;
      bool        schema_only = false;
      PGconn       *conn;
!     int            c;

      static struct option long_options[] = {
          {"data-only", no_argument, NULL, 'a'},
--- 77,83 ----
      bool        globals_only = false;
      bool        schema_only = false;
      PGconn       *conn;
!     int            c, ret;

      static struct option long_options[] = {
          {"data-only", no_argument, NULL, 'a'},
***************
*** 121,127 ****
          }
      }

!     pgdumploc = findPgDump(argv[0]);
      pgdumpopts = createPQExpBuffer();

      while ((c = getopt_long(argc, argv, "acdDgh:iop:sU:vWx", long_options, &optindex)) != -1)
--- 123,146 ----
          }
      }

!     if ((ret = find_other_binary(pg_dump_bin, argv[0], progname, "pg_dump",
!                            PG_VERSIONSTR)) < 0)
!     {
!         if (ret == -1)
!             fprintf(stderr,
!                         _("The program \"pg_dump\" is needed by %s "
!                         "but was not found in the same directory as \"%s\".\n"
!                         "Check your installation.\n"),
!                         progname, progname);
!         else
!             fprintf(stderr,
!                         _("The program \"pg_dump\" was found by %s "
!                         "but was not the same version as \"%s\".\n"
!                         "Check your installation.\n"),
!                         progname, progname);
!         exit(1);
!     }
!
      pgdumpopts = createPQExpBuffer();

      while ((c = getopt_long(argc, argv, "acdDgh:iop:sU:vWx", long_options, &optindex)) != -1)
***************
*** 667,673 ****
      const char *p;
      int            ret;

!     appendPQExpBuffer(cmd, "%s %s -Fp '", pgdumploc, pgdumpopts->data);

      /* Shell quoting is not quite like SQL quoting, so can't use fmtId */
      for (p = dbname; *p; p++)
--- 686,692 ----
      const char *p;
      int            ret;

!     appendPQExpBuffer(cmd, "\"%s\" %s -Fp '", pg_dump_bin, pgdumpopts->data);

      /* Shell quoting is not quite like SQL quoting, so can't use fmtId */
      for (p = dbname; *p; p++)
***************
*** 791,842 ****
      }

      return res;
- }
-
-
-
- /*
-  * Find location of pg_dump executable.
-  */
- static char *
- findPgDump(const char *argv0)
- {
-     char       *last;
-     PQExpBuffer cmd;
-     static char *result = NULL;
-
-     if (result)
-         return result;
-
-     cmd = createPQExpBuffer();
-     last = last_path_separator(argv0);
-
-     if (!last)
-         appendPQExpBuffer(cmd, "pg_dump");
-     else
-     {
-         char       *dir = strdup(argv0);
-
-         *(dir + (last - argv0)) = '\0';
-         appendPQExpBuffer(cmd, "%s/pg_dump", dir);
-     }
-
-     result = strdup(cmd->data);
-
-     appendPQExpBuffer(cmd, " -V >/dev/null 2>&1");
-     if (system(cmd->data) == 0)
-         goto end;
-
-     result = BINDIR "/pg_dump";
-     if (system(BINDIR "/pg_dump -V >/dev/null 2>&1") == 0)
-         goto end;
-
-     fprintf(stderr, _("%s: could not find pg_dump\n"
-         "Make sure it is in the path or in the same directory as %s.\n"),
-             progname, progname);
-     exit(1);
-
- end:
-     destroyPQExpBuffer(cmd);
-     return result;
  }
--- 810,813 ----
Index: src/include/miscadmin.h
===================================================================
RCS file: /cvsroot/pgsql-server/src/include/miscadmin.h,v
retrieving revision 1.156
diff -c -c -r1.156 miscadmin.h
*** src/include/miscadmin.h    12 Apr 2004 16:19:18 -0000    1.156
--- src/include/miscadmin.h    11 May 2004 21:51:44 -0000
***************
*** 254,263 ****

  extern void SetDataDir(const char *dir);

- extern int FindExec(char *full_path, const char *argv0,
-          const char *binary_name);
- extern int    CheckPathAccess(char *path, char *name, int open_mode);
-
  /* in utils/misc/superuser.c */
  extern bool superuser(void);    /* current user is superuser */
  extern bool superuser_arg(AclId userid);        /* given user is superuser */
--- 254,259 ----
Index: src/include/port.h
===================================================================
RCS file: /cvsroot/pgsql-server/src/include/port.h,v
retrieving revision 1.28
diff -c -c -r1.28 port.h
*** src/include/port.h    7 May 2004 00:24:58 -0000    1.28
--- src/include/port.h    11 May 2004 21:51:44 -0000
***************
*** 27,32 ****
--- 27,46 ----
  extern void canonicalize_path(char *path);
  extern char *get_progname(char *argv0);

+ /* Portable way to find binaries */
+ extern int find_my_binary(char *full_path, const char *argv0,
+                      const char *binary_name);
+ extern int find_other_binary(char *retpath, const char *argv0, const char *progname,
+                        char const *target, const char *versionstr);
+
+ #if defined(__CYGWIN__) || defined(WIN32)
+ #define EXE ".exe"
+ #define DEVNULL "nul"
+ #else
+ #define EXE ""
+ #define DEVNULL "/dev/null"
+ #endif
+
  /* Portable delay handling */
  extern void pg_usleep(long microsec);

***************
*** 57,62 ****
--- 71,77 ----
  extern int piperead(int s, char* buf, int len);
  #define pipewrite(a,b,c)    send(a,b,c,0)
  #endif
+ extern int pclose_check(FILE *stream);

  #if defined(__MINGW32__) || defined(__CYGWIN__)
  /*
Index: src/port/path.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/port/path.c,v
retrieving revision 1.5
diff -c -c -r1.5 path.c
*** src/port/path.c    9 Mar 2004 04:49:02 -0000    1.5
--- src/port/path.c    11 May 2004 21:51:45 -0000
***************
*** 121,123 ****
--- 121,124 ----
      else
          return last_path_separator(argv0) + 1;
  }
+
Index: src/port/pipe.c
===================================================================
RCS file: /cvsroot/pgsql-server/src/port/pipe.c,v
retrieving revision 1.2
diff -c -c -r1.2 pipe.c
*** src/port/pipe.c    19 Apr 2004 17:42:59 -0000    1.2
--- src/port/pipe.c    11 May 2004 21:51:45 -0000
***************
*** 17,22 ****
--- 17,27 ----

  #include "postgres.h"

+ #include <sys/wait.h>
+
+ #define _(x) gettext((x))
+
+ #ifdef WIN32
  int
  pgpipe(int handles[2])
  {
***************
*** 62,65 ****
--- 67,109 ----
          /* EOF on the pipe! (win32 socket based implementation) */
          ret = 0;
      return ret;
+ }
+ #endif
+
+ /*
+  * pclose() plus useful error reporting
+  * Is this necessary?  bjm 2004-05-11
+  */
+ int
+ pclose_check(FILE *stream)
+ {
+     int        exitstatus;
+
+     exitstatus = pclose(stream);
+
+     if (exitstatus == 0)
+         return 0;                    /* all is well */
+
+     if (exitstatus == -1)
+     {
+         /* pclose() itself failed, and hopefully set errno */
+         perror("pclose failed");
+     }
+     else if (WIFEXITED(exitstatus))
+     {
+         fprintf(stderr, _("child process exited with exit code %d\n"),
+                 WEXITSTATUS(exitstatus));
+     }
+     else if (WIFSIGNALED(exitstatus))
+     {
+         fprintf(stderr, _("child process was terminated by signal %d\n"),
+                 WTERMSIG(exitstatus));
+     }
+     else
+     {
+         fprintf(stderr, _("child process exited with unrecognized status %d\n"),
+                 exitstatus);
+     }
+
+     return -1;
  }
/*-------------------------------------------------------------------------
 *
 * exec.c
 *
 * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *      $PostgreSQL: pgsql-server/src/backend/utils/init/findbe.c,v 1.42 2004/03/15 16:18:43 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */

#ifndef FRONTEND
#include "postgres.h"
#else
#include "postgres_fe.h"
#endif

#include <grp.h>
#include <pwd.h>
#include <sys/stat.h>
#include <unistd.h>

#include "miscadmin.h"

#ifndef S_IRUSR                    /* XXX [TRH] should be in a header */
#define S_IRUSR         S_IREAD
#define S_IWUSR         S_IWRITE
#define S_IXUSR         S_IEXEC
#define S_IRGRP         ((S_IRUSR)>>3)
#define S_IWGRP         ((S_IWUSR)>>3)
#define S_IXGRP         ((S_IXUSR)>>3)
#define S_IROTH         ((S_IRUSR)>>6)
#define S_IWOTH         ((S_IWUSR)>>6)
#define S_IXOTH         ((S_IXUSR)>>6)
#endif

#ifndef FRONTEND
/* We use only 3-parameter elog calls in this file, for simplicity */
#define log_debug(str, param)    elog(DEBUG2, str, param)
#else
#define log_debug(str, param)    {}    /* do nothing */
#endif

static void win32_make_absolute(char *path);

/*
 * validate_exec -- validate "path" as an executable file
 *
 * returns 0 if the file is found and no error is encountered.
 *          -1 if the regular file "path" does not exist or cannot be executed.
 *          -2 if the file is otherwise valid but cannot be read.
 */
static int
validate_exec(char *path)
{
    struct stat buf;

#ifndef WIN32
    uid_t        euid;
    struct group *gp;
    struct passwd *pwp;
    int            i;
    int            in_grp = 0;
#else
    char        path_exe[MAXPGPATH + 2 + strlen(".exe")];
#endif
    int            is_r = 0;
    int            is_x = 0;

#ifdef WIN32
    /* Win32 requires a .exe suffix for stat() */
    if (strlen(path) >= strlen(".exe") &&
        pg_strcasecmp(path + strlen(path) - strlen(".exe"), ".exe") != 0)
    {
        strcpy(path_exe, path);
        strcat(path_exe, ".exe");
        path = path_exe;
    }
#endif

    /*
     * Ensure that the file exists and is a regular file.
     *
     * XXX if you have a broken system where stat() looks at the symlink
     * instead of the underlying file, you lose.
     */
    if (stat(path, &buf) < 0)
    {
        log_debug("could not stat \"%s\": %m", path);
        return -1;
    }

    if ((buf.st_mode & S_IFMT) != S_IFREG)
    {
        log_debug("\"%s\" is not a regular file", path);
        return -1;
    }

    /*
     * Ensure that we are using an authorized executable.
     */

    /*
     * Ensure that the file is both executable and readable (required for
     * dynamic loading).
     */
#ifdef WIN32
    is_r = buf.st_mode & S_IRUSR;
    is_x = buf.st_mode & S_IXUSR;
    return is_x ? (is_r ? 0 : -2) : -1;
#else
    euid = geteuid();

    /* If owned by us, just check owner bits */
    if (euid == buf.st_uid)
    {
        is_r = buf.st_mode & S_IRUSR;
        is_x = buf.st_mode & S_IXUSR;
        if (!(is_r && is_x))
            log_debug("\"%s\" is not user read/execute", path);
        return is_x ? (is_r ? 0 : -2) : -1;
    }

    /* OK, check group bits */

    pwp = getpwuid(euid);    /* not thread-safe */
    if (pwp)
    {
        if (pwp->pw_gid == buf.st_gid)    /* my primary group? */
            ++in_grp;
        else if (pwp->pw_name &&
                 (gp = getgrgid(buf.st_gid)) != NULL && /* not thread-safe */
                 gp->gr_mem != NULL)
        {    /* try list of member groups */
            for (i = 0; gp->gr_mem[i]; ++i)
            {
                if (!strcmp(gp->gr_mem[i], pwp->pw_name))
                {
                    ++in_grp;
                    break;
                }
            }
        }
        if (in_grp)
        {
            is_r = buf.st_mode & S_IRGRP;
            is_x = buf.st_mode & S_IXGRP;
            if (!(is_r && is_x))
                log_debug("\"%s\" is not group read/execute", path);
            return is_x ? (is_r ? 0 : -2) : -1;
        }
    }

    /* Check "other" bits */
    is_r = buf.st_mode & S_IROTH;
    is_x = buf.st_mode & S_IXOTH;
    if (!(is_r && is_x))
        log_debug("\"%s\" is not other read/execute", path);
    return is_x ? (is_r ? 0 : -2) : -1;

#endif
}

/*
 * find_my_binary -- find an absolute path to a valid executable
 *
 * 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.
 *
 * This function is not thread-safe because of it calls validate_exec(),
 * which calls getgrgid().  This function should be used only in
 * non-threaded binaries, not in library routines.
 */
int
find_my_binary(char *full_path, const char *argv0, const char *binary_name)
{
    char        buf[MAXPGPATH + 2];
    char       *p;
    char       *path,
               *startp,
               *endp;

    /*
     * First try: use the binary that's located in the
     * same directory if it was invoked with an explicit path.
     * Presumably the user used an explicit path because it
     * wasn't in PATH, and we don't want to use incompatible executables.
     *
     * This has the neat property that it works for installed binaries, old
     * source trees (obj/support/post{master,gres}) and new source
     * trees (obj/post{master,gres}) because they all put the two binaries
     * in the same place.
     *
     * for the binary: First try: if we're given some kind of path, use it
     * (making sure that a relative path is made absolute before returning
     * it).
     */
    if (argv0 && (p = last_path_separator(argv0)) && *++p)
    {
        if (is_absolute_path(argv0) || !getcwd(buf, MAXPGPATH))
            buf[0] = '\0';
        else
            strcat(buf, "/");
        strcat(buf, argv0);
        p = last_path_separator(buf);
        strcpy(++p, binary_name);
        if (validate_exec(buf) == 0)
        {
            strncpy(full_path, buf, MAXPGPATH);
            win32_make_absolute(full_path);
            log_debug("found \"%s\" using argv[0]", full_path);
            return 0;
        }
        log_debug("invalid binary \"%s\"", buf);
        return -1;
    }

    /*
     * Second try: since no explicit path was supplied, the user must have
     * been relying on PATH.  We'll use the same PATH.
     */
    if ((p = getenv("PATH")) && *p)
    {
        log_debug("searching PATH for executable%s", "");
        path = strdup(p);        /* make a modifiable copy */
        for (startp = path, endp = strchr(path, PATHSEP);
             startp && *startp;
             startp = endp + 1, endp = strchr(startp, PATHSEP))
        {
            if (startp == endp) /* it's a "::" */
                continue;
            if (endp)
                *endp = '\0';
            if (is_absolute_path(startp) || !getcwd(buf, MAXPGPATH))
                buf[0] = '\0';
            else
                strcat(buf, "/");
            strcat(buf, startp);
            strcat(buf, "/");
            strcat(buf, binary_name);
            switch (validate_exec(buf))
            {
                case 0: /* found ok */
                    strncpy(full_path, buf, MAXPGPATH);
                    win32_make_absolute(full_path);
                    log_debug("found \"%s\" using PATH", full_path);
                    free(path);
                    return 0;
                case -1:        /* wasn't even a candidate, keep looking */
                    break;
                case -2:        /* found but disqualified */
                    log_debug("could not read binary \"%s\"", buf);
                    free(path);
                    return -1;
            }
            if (!endp)            /* last one */
                break;
        }
        free(path);
    }

    log_debug("could not find a \"%s\" to execute", binary_name);
    return -1;
}


/*
 * Find our binary directory, then make sure the "target" executable
 * is the proper version.
 */
int find_other_binary(char *retpath, const char *argv0, const char *progname,
                char const *target, const char *versionstr)
{
    char        cmd[MAXPGPATH];
    char        line[100];
    FILE       *pgver;

    if (find_my_binary(retpath, argv0, progname) < 0)
        return -1;

    /* Trim off program name and keep just directory */
    *last_path_separator(retpath) = '\0';

    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);

    /* flush output buffers in case popen does not... */
    fflush(stdout);
    fflush(stderr);

    if ((pgver = popen(cmd, "r")) == NULL)
        return -1;

    if (fgets(line, sizeof(line), pgver) == NULL)
        perror("fgets failure");

    if (pclose_check(pgver))
        return -1;

    if (strcmp(line, versionstr) != 0)
        return -2;

    return 0;
}


/*
 * Windows doesn't like relative paths to executables (other things work fine)
 * so we call its builtin function to expand them. Elsewhere this is a NOOP
 *
 * Returns malloc'ed memory.
 */
static void
win32_make_absolute(char *path)
{
#ifdef WIN32
    char        abspath[MAXPGPATH];

    if (_fullpath(abspath, path, MAXPGPATH) == NULL)
    {
        log_debug("Win32 path expansion failed:  %s", strerror());
        return path;
    }
    canonicalize_path(abspath);

    StrNCpy(path, abspath, MAXPGPATH);
#endif
    return;
}


pgsql-patches by date:

Previous
From: "Magnus Hagander"
Date:
Subject: Re: pgkill for windows
Next
From: "Mahesh Swamy"
Date:
Subject: SQLState