<warning>
<para>
- This test method starts a temporary server, which is configured to accept
- any connection originating on the local machine. Any local user can gain
- database superuser privileges when connecting to this server, and could
- in principle exploit all privileges of the operating-system user running
- the tests. Therefore, it is not recommended that you use <literal>gmake
- check</> on machines shared with untrusted users. Instead, run the tests
- after completing the installation, as described in the next section.
- </para>
-
- <para>
- On Unix-like machines, this danger can be avoided if the temporary
- server's socket file is made inaccessible to other users, for example
- by running the tests in a protected chroot. On Windows, the temporary
- server opens a locally-accessible TCP socket, so filesystem protections
- cannot help.
+ On systems lacking Unix-domain sockets, notably Windows, this test method
+ starts a temporary server configured to accept any connection originating
+ on the local machine. Any local user can gain database superuser
+ privileges when connecting to this server, and could in principle exploit
+ all privileges of the operating-system user running the tests. Therefore,
+ it is not recommended that you use <literal>gmake check</> on an affected
+ system shared with untrusted users. Instead, run the tests after
+ completing the installation, as described in the next section.
</para>
</warning>
all: submake-libpgport pg_regress$(X)
-pg_regress$(X): pg_regress.o pg_regress_main.o
+pg_regress$(X): pg_regress.o pg_regress_main.o pqsignal.o
$(CC) $(CFLAGS) $^ $(LDFLAGS) $(LIBS) -o $@
# dependencies ensure that path changes propagate
pg_regress.o: pg_regress.c $(top_builddir)/src/port/pg_config_paths.h
$(CC) $(CFLAGS) $(CPPFLAGS) -I$(top_builddir)/src/port $(EXTRADEFS) -c -o $@ $<
+# Pull in pqsignal like initdb does.
+pqsignal.c: % : $(top_srcdir)/src/interfaces/libpq/%
+ rm -f $@ && $(LN_S) $< .
+pqsignal.o: override CPPFLAGS += -I$(libpq_srcdir)
+
$(top_builddir)/src/port/pg_config_paths.h: $(top_builddir)/src/Makefile.global
$(MAKE) -C $(top_builddir)/src/port pg_config_paths.h
clean distclean maintainer-clean: clean-lib
# things built by `all' target
- rm -f $(OBJS) refint$(DLSUFFIX) autoinc$(DLSUFFIX) pg_regress_main.o pg_regress.o pg_regress$(X)
+ rm -f $(OBJS) refint$(DLSUFFIX) autoinc$(DLSUFFIX)
+ rm -f pqsignal.c pg_regress_main.o pg_regress.o pqsignal.o pg_regress$(X)
# things created by various check targets
rm -f $(output_files) $(input_files)
rm -rf testtablespace
#endif
#include "getopt_long.h"
+#include "libpq/pqcomm.h" /* needed for UNIXSOCK_PATH() */
+#include "libpq/pqsignal.h"
#include "pg_config_paths.h"
/* for resultmap we need a list of pairs of strings */
static char *logfilename;
static FILE *logfile;
static char *difffilename;
+static const char *sockdir;
+#ifdef HAVE_UNIX_SOCKETS
+static const char *temp_sockdir;
+static char sockself[MAXPGPATH];
+static char socklock[MAXPGPATH];
+#endif
static _resultmap *resultmap = NULL;
}
}
+#ifdef HAVE_UNIX_SOCKETS
+/*
+ * Remove the socket temporary directory. pg_regress never waits for a
+ * postmaster exit, so it is indeterminate whether the postmaster has yet to
+ * unlink the socket and lock file. Unlink them here so we can proceed to
+ * remove the directory. Ignore errors; leaking a temporary directory is
+ * unimportant. This can run from a signal handler. The code is not
+ * acceptable in a Windows signal handler (see initdb.c:trapsig()), but
+ * Windows is not a HAVE_UNIX_SOCKETS platform.
+ */
+static void
+remove_temp(void)
+{
+ unlink(sockself);
+ unlink(socklock);
+ rmdir(temp_sockdir);
+}
+
+/*
+ * Signal handler that calls remove_temp() and reraises the signal.
+ */
+static void
+signal_remove_temp(int signum)
+{
+ remove_temp();
+
+ pqsignal(signum, SIG_DFL);
+ raise(signum);
+}
+
+/*
+ * Create a temporary directory suitable for the server's Unix-domain socket.
+ * The directory will have mode 0700 or stricter, so no other OS user can open
+ * our socket to exploit our use of trust authentication. Most systems
+ * constrain the length of socket paths well below _POSIX_PATH_MAX, so we
+ * place the directory under /tmp rather than relative to the possibly-deep
+ * current working directory.
+ *
+ * Compared to using the compiled-in DEFAULT_PGSOCKET_DIR, this also permits
+ * testing to work in builds that relocate it to a directory not writable to
+ * the build/test user.
+ */
+static const char *
+make_temp_sockdir(void)
+{
+ char *template = strdup("/tmp/pg_regress-XXXXXX");
+
+ temp_sockdir = mkdtemp(template);
+ if (temp_sockdir == NULL)
+ {
+ fprintf(stderr, _("%s: could not create directory \"%s\": %s\n"),
+ progname, template, strerror(errno));
+ exit(2);
+ }
+
+ /* Stage file names for remove_temp(). Unsafe in a signal handler. */
+ UNIXSOCK_PATH(sockself, port, temp_sockdir);
+ snprintf(socklock, sizeof(socklock), "%s.lock", sockself);
+
+ /* Remove the directory during clean exit. */
+ atexit(remove_temp);
+
+ /*
+ * Remove the directory before dying to the usual signals. Omit SIGQUIT,
+ * preserving it as a quick, untidy exit.
+ */
+ pqsignal(SIGHUP, signal_remove_temp);
+ pqsignal(SIGINT, signal_remove_temp);
+ pqsignal(SIGPIPE, signal_remove_temp);
+ pqsignal(SIGTERM, signal_remove_temp);
+
+ return temp_sockdir;
+}
+#endif /* HAVE_UNIX_SOCKETS */
+
/*
* Always exit through here, not through plain exit(), to ensure we make
* an effort to shut down a temp postmaster
* the wrong postmaster, or otherwise behave in nondefault ways. (Note
* we also use psql's -X switch consistently, so that ~/.psqlrc files
* won't mess things up.) Also, set PGPORT to the temp port, and set
- * or unset PGHOST depending on whether we are using TCP or Unix
- * sockets.
+ * PGHOST depending on whether we are using TCP or Unix sockets.
*/
unsetenv("PGDATABASE");
unsetenv("PGUSER");
unsetenv("PGREQUIRESSL");
unsetenv("PGCONNECT_TIMEOUT");
unsetenv("PGDATA");
+#ifdef HAVE_UNIX_SOCKETS
if (hostname != NULL)
doputenv("PGHOST", hostname);
else
- unsetenv("PGHOST");
+ {
+ sockdir = getenv("PG_REGRESS_SOCK_DIR");
+ if (!sockdir)
+ sockdir = make_temp_sockdir();
+ doputenv("PGHOST", sockdir);
+ }
+#else
+ doputenv("PGHOST", hostname);
+#endif
unsetenv("PGHOSTADDR");
if (port != -1)
{
/*
* To reduce chances of interference with parallel installations, use
* a port number starting in the private range (49152-65535)
- * calculated from the version number.
+ * calculated from the version number. This aids !HAVE_UNIX_SOCKETS
+ * systems; elsewhere, the use of a private socket directory already
+ * prevents interference.
*/
port = 0xC000 | (PG_VERSION_NUM & 0x3FFF);
*/
header(_("starting postmaster"));
snprintf(buf, sizeof(buf),
- SYSTEMQUOTE "\"%s/postgres\" -D \"%s/data\" -F%s -c \"listen_addresses=%s\" > \"%s/log/postmaster.log\" 2>&1" SYSTEMQUOTE,
- bindir, temp_install,
- debug ? " -d 5" : "",
- hostname ? hostname : "",
+ SYSTEMQUOTE "\"%s/postgres\" -D \"%s/data\" -F%s "
+ "-c \"listen_addresses=%s\" -k \"%s\" "
+ "> \"%s/log/postmaster.log\" 2>&1" SYSTEMQUOTE,
+ bindir, temp_install, debug ? " -d 5" : "",
+ hostname ? hostname : "", sockdir ? sockdir : "",
outputdir);
postmaster_pid = spawn_process(buf);
if (postmaster_pid == INVALID_PID)