*
* This code is released under the terms of the PostgreSQL License.
*
- * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/test/regress/pg_regress.c
#include <sys/resource.h>
#endif
+#include "common/restricted_token.h"
+#include "common/username.h"
#include "getopt_long.h"
+#include "libpq/pqcomm.h" /* needed for UNIXSOCK_PATH() */
#include "pg_config_paths.h"
/* for resultmap we need a list of pairs of strings */
char *type;
char *resultfile;
struct _resultmap *next;
-} _resultmap;
+} _resultmap;
/*
- * Values obtained from pg_config_paths.h and Makefile. The PG installation
- * paths are only used in temp_install mode: we use these strings to find
- * out where "make install" will put stuff under the temp_install directory.
- * In non-temp_install mode, the only thing we need is the location of psql,
- * which we expect to find in psqldir, or in the PATH if psqldir isn't given.
- *
- * XXX Because pg_regress is not installed in bindir, we can't support
- * this for relocatable trees as it is. --psqldir would need to be
- * specified in those cases.
+ * Values obtained from Makefile.
*/
-char *bindir = PGBINDIR;
-char *libdir = LIBDIR;
-char *datadir = PGSHAREDIR;
char *host_platform = HOST_TUPLE;
-#ifndef WIN32_ONLY_COMPILER
-static char *makeprog = MAKEPROG;
-#endif
-
#ifndef WIN32 /* not used in WIN32 case */
static char *shellprog = SHELLPROG;
#endif
/*
* On Windows we use -w in diff switches to avoid problems with inconsistent
- * newline representation. The actual result files will generally have
+ * newline representation. The actual result files will generally have
* Windows-style newlines, but the comparison files might or might not.
*/
#ifndef WIN32
bool debug = false;
char *inputdir = ".";
char *outputdir = ".";
-char *psqldir = PGBINDIR;
+char *bindir = PGBINDIR;
char *launcher = NULL;
static _stringlist *loadlanguage = NULL;
static _stringlist *loadextension = NULL;
static char *encoding = NULL;
static _stringlist *schedulelist = NULL;
static _stringlist *extra_tests = NULL;
-static char *temp_install = NULL;
+static char *temp_instance = NULL;
static char *temp_config = NULL;
-static char *top_builddir = NULL;
static bool nolocale = false;
static bool use_existing = false;
static char *hostname = NULL;
static char *dlpath = PKGLIBDIR;
static char *user = NULL;
static _stringlist *extraroles = NULL;
-static _stringlist *extra_install = NULL;
+static char *config_auth_datadir = NULL;
/* internal variables */
static const char *progname;
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;
static bool directory_exists(const char *dir);
static void make_directory(const char *dir);
-static void
-header(const char *fmt,...)
-/* This extension allows gcc to check the format string for consistency with
- the supplied arguments. */
-__attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 2)));
-static void
-status(const char *fmt,...)
-/* This extension allows gcc to check the format string for consistency with
- the supplied arguments. */
-__attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 2)));
-static void
-psql_command(const char *database, const char *query,...)
-/* This extension allows gcc to check the format string for consistency with
- the supplied arguments. */
-__attribute__((format(PG_PRINTF_ATTRIBUTE, 2, 3)));
-
-#ifdef WIN32
-typedef BOOL (WINAPI * __CreateRestrictedToken) (HANDLE, DWORD, DWORD, PSID_AND_ATTRIBUTES, DWORD, PLUID_AND_ATTRIBUTES, DWORD, PSID_AND_ATTRIBUTES, PHANDLE);
-
-/* Windows API define missing from some versions of MingW headers */
-#ifndef DISABLE_MAX_PRIVILEGE
-#define DISABLE_MAX_PRIVILEGE 0x1
-#endif
-#endif
+static void header(const char *fmt,...) pg_attribute_printf(1, 2);
+static void status(const char *fmt,...) pg_attribute_printf(1, 2);
+static void psql_command(const char *database, const char *query,...) pg_attribute_printf(2, 3);
/*
* allow core files if possible.
* Add an item at the end of a stringlist.
*/
void
-add_stringlist_item(_stringlist ** listhead, const char *str)
+add_stringlist_item(_stringlist **listhead, const char *str)
{
_stringlist *newentry = malloc(sizeof(_stringlist));
_stringlist *oldentry;
* Free a stringlist.
*/
static void
-free_stringlist(_stringlist ** listhead)
+free_stringlist(_stringlist **listhead)
{
if (listhead == NULL || *listhead == NULL)
return;
* Split a delimited string into a stringlist
*/
static void
-split_to_stringlist(const char *s, const char *delim, _stringlist ** listhead)
+split_to_stringlist(const char *s, const char *delim, _stringlist **listhead)
{
char *sc = strdup(s);
char *token = strtok(sc, delim);
fflush(stderr);
snprintf(buf, sizeof(buf),
- SYSTEMQUOTE "\"%s/pg_ctl\" stop -D \"%s/data\" -s -m fast" SYSTEMQUOTE,
- bindir, temp_install);
+ "\"%s%spg_ctl\" stop -D \"%s/data\" -s -m fast",
+ bindir ? bindir : "",
+ bindir ? "/" : "",
+ temp_instance);
r = system(buf);
if (r != 0)
{
}
}
+#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)
+{
+ Assert(temp_sockdir);
+ 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 */
+
/*
* Check whether string matches pattern
*
}
/*
- * Replace all occurances of a string in a string with a different string.
+ * Replace all occurrences of a string in a string with a different string.
* NOTE: Assumes there is enough room in the target buffer!
*/
void
* the given suffix.
*/
static void
-convert_sourcefiles_in(char *source_subdir, char *dest_subdir, char *suffix)
+convert_sourcefiles_in(char *source_subdir, char *dest_dir, char *dest_subdir, char *suffix)
{
char testtablespace[MAXPGPATH];
char indir[MAXPGPATH];
* Windows. See pgsql-hackers discussion of 2008-01-18.
*/
if (directory_exists(testtablespace))
- rmtree(testtablespace, true);
+ if (!rmtree(testtablespace, true))
+ {
+ fprintf(stderr, _("\n%s: could not remove test tablespace \"%s\"\n"),
+ progname, testtablespace);
+ exit(2);
+ }
make_directory(testtablespace);
#endif
/* build the full actual paths to open */
snprintf(prefix, strlen(*name) - 6, "%s", *name);
snprintf(srcfile, MAXPGPATH, "%s/%s", indir, *name);
- snprintf(destfile, MAXPGPATH, "%s/%s.%s", dest_subdir, prefix, suffix);
+ snprintf(destfile, MAXPGPATH, "%s/%s/%s.%s", dest_dir, dest_subdir,
+ prefix, suffix);
infile = fopen(srcfile, "r");
if (!infile)
static void
convert_sourcefiles(void)
{
- convert_sourcefiles_in("input", "sql", "sql");
- convert_sourcefiles_in("output", "expected", "out");
+ convert_sourcefiles_in("input", outputdir, "sql", "sql");
+ convert_sourcefiles_in("output", outputdir, "expected", "out");
}
/*
* namely, it is a standard regular expression with an implicit ^ at the start.
* (We currently support only a very limited subset of regular expressions,
* see string_matches_pattern() above.) What hostplatformpattern will be
- * matched against is the config.guess output. (In the shell-script version,
+ * matched against is the config.guess output. (In the shell-script version,
* we also provided an indication of whether gcc or another compiler was in
* use, but that facility isn't used anymore.)
*/
static void
doputenv(const char *var, const char *val)
{
- char *s = malloc(strlen(var) + strlen(val) + 2);
+ char *s;
- sprintf(s, "%s=%s", var, val);
+ s = psprintf("%s=%s", var, val);
putenv(s);
}
-/*
- * Set the environment variable "pathname", prepending "addval" to its
- * old value (if any).
- */
-static void
-add_to_path(const char *pathname, char separator, const char *addval)
-{
- char *oldval = getenv(pathname);
- char *newval;
-
- if (!oldval || !oldval[0])
- {
- /* no previous value */
- newval = malloc(strlen(pathname) + strlen(addval) + 2);
- sprintf(newval, "%s=%s", pathname, addval);
- }
- else
- {
- newval = malloc(strlen(pathname) + strlen(addval) + strlen(oldval) + 3);
- sprintf(newval, "%s=%s%c%s", pathname, addval, separator, oldval);
- }
- putenv(newval);
-}
-
/*
* Prepare environment variables for running regression tests
*/
static void
initialize_environment(void)
{
- char *tmp;
-
putenv("PGAPPNAME=pg_regress");
if (nolocale)
unsetenv("LC_NUMERIC");
unsetenv("LC_TIME");
unsetenv("LANG");
- /* On Windows the default locale cannot be English, so force it */
-#if defined(WIN32) || defined(__CYGWIN__)
- putenv("LANG=en");
+
+ /*
+ * Most platforms have adopted the POSIX locale as their
+ * implementation-defined default locale. Exceptions include native
+ * Windows, Darwin with --enable-nls, and Cygwin with --enable-nls.
+ * (Use of --enable-nls matters because libintl replaces setlocale().)
+ * Also, PostgreSQL does not support Darwin with locale environment
+ * variables unset; see PostmasterMain().
+ */
+#if defined(WIN32) || defined(__CYGWIN__) || defined(__darwin__)
+ putenv("LANG=C");
#endif
}
if (!old_pgoptions)
old_pgoptions = "";
- new_pgoptions = malloc(strlen(old_pgoptions) + strlen(my_pgoptions) + 12);
- sprintf(new_pgoptions, "PGOPTIONS=%s %s", old_pgoptions, my_pgoptions);
+ new_pgoptions = psprintf("PGOPTIONS=%s %s",
+ old_pgoptions, my_pgoptions);
putenv(new_pgoptions);
}
- if (temp_install)
+ if (temp_instance)
{
/*
* Clear out any environment vars that might cause psql to connect to
* 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
+ Assert(hostname != NULL);
+ doputenv("PGHOST", hostname);
+#endif
unsetenv("PGHOSTADDR");
if (port != -1)
{
sprintf(s, "%d", port);
doputenv("PGPORT", s);
}
-
- /*
- * Adjust path variables to point into the temp-install tree
- */
- tmp = malloc(strlen(temp_install) + 32 + strlen(bindir));
- sprintf(tmp, "%s/install/%s", temp_install, bindir);
- bindir = tmp;
-
- tmp = malloc(strlen(temp_install) + 32 + strlen(libdir));
- sprintf(tmp, "%s/install/%s", temp_install, libdir);
- libdir = tmp;
-
- tmp = malloc(strlen(temp_install) + 32 + strlen(datadir));
- sprintf(tmp, "%s/install/%s", temp_install, datadir);
- datadir = tmp;
-
- /* psql will be installed into temp-install bindir */
- psqldir = bindir;
-
- /*
- * Set up shared library paths to include the temp install.
- *
- * LD_LIBRARY_PATH covers many platforms. DYLD_LIBRARY_PATH works on
- * Darwin, and maybe other Mach-based systems. LIBPATH is for AIX.
- * Windows needs shared libraries in PATH (only those linked into
- * executables, not dlopen'ed ones). Feel free to account for others
- * as well.
- */
- add_to_path("LD_LIBRARY_PATH", ':', libdir);
- add_to_path("DYLD_LIBRARY_PATH", ':', libdir);
- add_to_path("LIBPATH", ':', libdir);
-#if defined(WIN32)
- add_to_path("PATH", ';', libdir);
-#elif defined(__CYGWIN__)
- add_to_path("PATH", ':', libdir);
-#endif
}
else
{
load_resultmap();
}
+#ifdef ENABLE_SSPI
+/*
+ * Get account and domain/realm names for the current user. This is based on
+ * pg_SSPI_recvauth(). The returned strings use static storage.
+ */
+static void
+current_windows_user(const char **acct, const char **dom)
+{
+ static char accountname[MAXPGPATH];
+ static char domainname[MAXPGPATH];
+ HANDLE token;
+ TOKEN_USER *tokenuser;
+ DWORD retlen;
+ DWORD accountnamesize = sizeof(accountname);
+ DWORD domainnamesize = sizeof(domainname);
+ SID_NAME_USE accountnameuse;
+
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &token))
+ {
+ fprintf(stderr,
+ _("%s: could not open process token: error code %lu\n"),
+ progname, GetLastError());
+ exit(2);
+ }
+
+ if (!GetTokenInformation(token, TokenUser, NULL, 0, &retlen) && GetLastError() != 122)
+ {
+ fprintf(stderr,
+ _("%s: could not get token user size: error code %lu\n"),
+ progname, GetLastError());
+ exit(2);
+ }
+ tokenuser = malloc(retlen);
+ if (!GetTokenInformation(token, TokenUser, tokenuser, retlen, &retlen))
+ {
+ fprintf(stderr,
+ _("%s: could not get token user: error code %lu\n"),
+ progname, GetLastError());
+ exit(2);
+ }
+
+ if (!LookupAccountSid(NULL, tokenuser->User.Sid, accountname, &accountnamesize,
+ domainname, &domainnamesize, &accountnameuse))
+ {
+ fprintf(stderr,
+ _("%s: could not look up account SID: error code %lu\n"),
+ progname, GetLastError());
+ exit(2);
+ }
+
+ free(tokenuser);
+
+ *acct = accountname;
+ *dom = domainname;
+}
+
+/*
+ * Rewrite pg_hba.conf and pg_ident.conf to use SSPI authentication. Permit
+ * the current OS user to authenticate as the bootstrap superuser and as any
+ * user named in a --create-role option.
+ */
+static void
+config_sspi_auth(const char *pgdata)
+{
+ const char *accountname,
+ *domainname;
+ const char *username;
+ char *errstr;
+ bool have_ipv6;
+ char fname[MAXPGPATH];
+ int res;
+ FILE *hba,
+ *ident;
+ _stringlist *sl;
+
+ /*
+ * "username", the initdb-chosen bootstrap superuser name, may always
+ * match "accountname", the value SSPI authentication discovers. The
+ * underlying system functions do not clearly guarantee that.
+ */
+ current_windows_user(&accountname, &domainname);
+ username = get_user_name(&errstr);
+ if (username == NULL)
+ {
+ fprintf(stderr, "%s: %s\n", progname, errstr);
+ exit(2);
+ }
+
+ /*
+ * Like initdb.c:setup_config(), determine whether the platform recognizes
+ * ::1 (IPv6 loopback) as a numeric host address string.
+ */
+ {
+ struct addrinfo *gai_result;
+ struct addrinfo hints;
+ WSADATA wsaData;
+
+ hints.ai_flags = AI_NUMERICHOST;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = 0;
+ hints.ai_protocol = 0;
+ hints.ai_addrlen = 0;
+ hints.ai_canonname = NULL;
+ hints.ai_addr = NULL;
+ hints.ai_next = NULL;
+
+ have_ipv6 = (WSAStartup(MAKEWORD(2, 2), &wsaData) == 0 &&
+ getaddrinfo("::1", NULL, &hints, &gai_result) == 0);
+ }
+
+ /* Check a Write outcome and report any error. */
+#define CW(cond) \
+ do { \
+ if (!(cond)) \
+ { \
+ fprintf(stderr, _("%s: could not write to file \"%s\": %s\n"), \
+ progname, fname, strerror(errno)); \
+ exit(2); \
+ } \
+ } while (0)
+
+ res = snprintf(fname, sizeof(fname), "%s/pg_hba.conf", pgdata);
+ if (res < 0 || res >= sizeof(fname) - 1)
+ {
+ /*
+ * Truncating this name is a fatal error, because we must not fail to
+ * overwrite an original trust-authentication pg_hba.conf.
+ */
+ fprintf(stderr, _("%s: directory name too long\n"), progname);
+ exit(2);
+ }
+ hba = fopen(fname, "w");
+ if (hba == NULL)
+ {
+ fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
+ progname, fname, strerror(errno));
+ exit(2);
+ }
+ CW(fputs("# Configuration written by config_sspi_auth()\n", hba) >= 0);
+ CW(fputs("host all all 127.0.0.1/32 sspi include_realm=1 map=regress\n",
+ hba) >= 0);
+ if (have_ipv6)
+ CW(fputs("host all all ::1/128 sspi include_realm=1 map=regress\n",
+ hba) >= 0);
+ CW(fclose(hba) == 0);
+
+ snprintf(fname, sizeof(fname), "%s/pg_ident.conf", pgdata);
+ ident = fopen(fname, "w");
+ if (ident == NULL)
+ {
+ fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
+ progname, fname, strerror(errno));
+ exit(2);
+ }
+ CW(fputs("# Configuration written by config_sspi_auth()\n", ident) >= 0);
+
+ /*
+ * Double-quote for the benefit of account names containing whitespace or
+ * '#'. Windows forbids the double-quote character itself, so don't
+ * bother escaping embedded double-quote characters.
+ */
+ CW(fprintf(ident, "regress \"%s@%s\" \"%s\"\n",
+ accountname, domainname, username) >= 0);
+ for (sl = extraroles; sl; sl = sl->next)
+ CW(fprintf(ident, "regress \"%s@%s\" \"%s\"\n",
+ accountname, domainname, sl->str) >= 0);
+ CW(fclose(ident) == 0);
+}
+#endif
+
/*
* Issue a command via psql, connecting to the specified database
*
/* And now we can build and execute the shell command */
snprintf(psql_cmd, sizeof(psql_cmd),
- SYSTEMQUOTE "\"%s%spsql\" -X -c \"%s\" \"%s\"" SYSTEMQUOTE,
- psqldir ? psqldir : "",
- psqldir ? "/" : "",
+ "\"%s%spsql\" -X -c \"%s\" \"%s\"",
+ bindir ? bindir : "",
+ bindir ? "/" : "",
query_escaped,
database);
pid_t pid;
/*
- * Must flush I/O buffers before fork. Ideally we'd use fflush(NULL) here
+ * Must flush I/O buffers before fork. Ideally we'd use fflush(NULL) here
* ... does anyone still care about systems where that doesn't work?
*/
fflush(stdout);
* In child
*
* Instead of using system(), exec the shell directly, and tell it to
- * "exec" the command too. This saves two useless processes per
+ * "exec" the command too. This saves two useless processes per
* parallel test case.
*/
- char *cmdline2 = malloc(strlen(cmdline) + 6);
+ char *cmdline2;
- sprintf(cmdline2, "exec %s", cmdline);
+ cmdline2 = psprintf("exec %s", cmdline);
execl(shellprog, shellprog, "-c", cmdline2, (char *) NULL);
fprintf(stderr, _("%s: could not exec \"%s\": %s\n"),
progname, shellprog, strerror(errno));
/* in parent */
return pid;
#else
- char *cmdline2;
- BOOL b;
- STARTUPINFO si;
PROCESS_INFORMATION pi;
- HANDLE origToken;
+ char *cmdline2;
HANDLE restrictedToken;
- SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY};
- SID_AND_ATTRIBUTES dropSids[2];
- __CreateRestrictedToken _CreateRestrictedToken = NULL;
- HANDLE Advapi32Handle;
-
- ZeroMemory(&si, sizeof(si));
- si.cb = sizeof(si);
-
- Advapi32Handle = LoadLibrary("ADVAPI32.DLL");
- if (Advapi32Handle != NULL)
- {
- _CreateRestrictedToken = (__CreateRestrictedToken) GetProcAddress(Advapi32Handle, "CreateRestrictedToken");
- }
-
- if (_CreateRestrictedToken == NULL)
- {
- if (Advapi32Handle != NULL)
- FreeLibrary(Advapi32Handle);
- fprintf(stderr, _("%s: cannot create restricted tokens on this platform\n"),
- progname);
- exit(2);
- }
-
- /* Open the current token to use as base for the restricted one */
- if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &origToken))
- {
- fprintf(stderr, _("could not open process token: error code %lu\n"),
- GetLastError());
- exit(2);
- }
-
- /* Allocate list of SIDs to remove */
- ZeroMemory(&dropSids, sizeof(dropSids));
- if (!AllocateAndInitializeSid(&NtAuthority, 2,
- SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &dropSids[0].Sid) ||
- !AllocateAndInitializeSid(&NtAuthority, 2,
- SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_POWER_USERS, 0, 0, 0, 0, 0, 0, &dropSids[1].Sid))
- {
- fprintf(stderr, _("could not allocate SIDs: error code %lu\n"), GetLastError());
- exit(2);
- }
- b = _CreateRestrictedToken(origToken,
- DISABLE_MAX_PRIVILEGE,
- sizeof(dropSids) / sizeof(dropSids[0]),
- dropSids,
- 0, NULL,
- 0, NULL,
- &restrictedToken);
+ memset(&pi, 0, sizeof(pi));
+ cmdline2 = psprintf("cmd /c \"%s\"", cmdline);
- FreeSid(dropSids[1].Sid);
- FreeSid(dropSids[0].Sid);
- CloseHandle(origToken);
- FreeLibrary(Advapi32Handle);
-
- if (!b)
- {
- fprintf(stderr, _("could not create restricted token: error code %lu\n"),
- GetLastError());
+ if ((restrictedToken =
+ CreateRestrictedProcess(cmdline2, &pi, progname)) == 0)
exit(2);
- }
-
- cmdline2 = malloc(strlen(cmdline) + 8);
- sprintf(cmdline2, "cmd /c %s", cmdline);
-
-#ifndef __CYGWIN__
- AddUserToTokenDacl(restrictedToken);
-#endif
-
- if (!CreateProcessAsUser(restrictedToken,
- NULL,
- cmdline2,
- NULL,
- NULL,
- TRUE,
- CREATE_SUSPENDED,
- NULL,
- NULL,
- &si,
- &pi))
- {
- fprintf(stderr, _("could not start process for \"%s\": error code %lu\n"),
- cmdline2, GetLastError());
- exit(2);
- }
- free(cmdline2);
-
- ResumeThread(pi.hThread);
CloseHandle(pi.hThread);
return pi.hProcess;
#endif
{
char *last_dot;
int ssize = strlen(expectfile) + 2 + 1;
- char *tmp = (char *) malloc(ssize);
- char *s = (char *) malloc(ssize);
+ char *tmp;
+ char *s;
+
+ if (!(tmp = (char *) malloc(ssize)))
+ return NULL;
+
+ if (!(s = (char *) malloc(ssize)))
+ {
+ free(tmp);
+ return NULL;
+ }
strcpy(tmp, expectfile);
last_dot = strrchr(tmp, '.');
*/
platform_expectfile = get_expectfile(testname, resultsfile);
- strcpy(expectfile, default_expectfile);
+ strlcpy(expectfile, default_expectfile, sizeof(expectfile));
if (platform_expectfile)
{
/*
/* OK, run the diff */
snprintf(cmd, sizeof(cmd),
- SYSTEMQUOTE "diff %s \"%s\" \"%s\" > \"%s\"" SYSTEMQUOTE,
+ "diff %s \"%s\" \"%s\" > \"%s\"",
basic_diff_opts, expectfile, resultsfile, diff);
/* Is the diff file empty? */
char *alt_expectfile;
alt_expectfile = get_alternative_expectfile(expectfile, i);
+ if (!alt_expectfile)
+ {
+ fprintf(stderr, _("Unable to check secondary comparison files: %s\n"),
+ strerror(errno));
+ exit(2);
+ }
+
if (!file_exists(alt_expectfile))
+ {
+ free(alt_expectfile);
continue;
+ }
snprintf(cmd, sizeof(cmd),
- SYSTEMQUOTE "diff %s \"%s\" \"%s\" > \"%s\"" SYSTEMQUOTE,
+ "diff %s \"%s\" \"%s\" > \"%s\"",
basic_diff_opts, alt_expectfile, resultsfile, diff);
if (run_diff(cmd, diff) == 0)
{
unlink(diff);
+ free(alt_expectfile);
return false;
}
{
/* This diff was a better match than the last one */
best_line_count = l;
- strcpy(best_expect_file, alt_expectfile);
+ strlcpy(best_expect_file, alt_expectfile, sizeof(best_expect_file));
}
free(alt_expectfile);
}
if (platform_expectfile)
{
snprintf(cmd, sizeof(cmd),
- SYSTEMQUOTE "diff %s \"%s\" \"%s\" > \"%s\"" SYSTEMQUOTE,
+ "diff %s \"%s\" \"%s\" > \"%s\"",
basic_diff_opts, default_expectfile, resultsfile, diff);
if (run_diff(cmd, diff) == 0)
{
/* This diff was a better match than the last one */
best_line_count = l;
- strcpy(best_expect_file, default_expectfile);
+ strlcpy(best_expect_file, default_expectfile, sizeof(best_expect_file));
}
}
* append to the diffs summary file.
*/
snprintf(cmd, sizeof(cmd),
- SYSTEMQUOTE "diff %s \"%s\" \"%s\" >> \"%s\"" SYSTEMQUOTE,
+ "diff %s \"%s\" \"%s\" >> \"%s\"",
pretty_diff_opts, best_expect_file, resultsfile, difffilename);
run_diff(cmd, difffilename);
}
}
+ free_stringlist(&ignorelist);
+
fclose(scf);
}
dbname, dbname, dbname, dbname, dbname);
/*
- * Install any requested procedural languages. We use CREATE OR REPLACE
+ * Install any requested procedural languages. We use CREATE OR REPLACE
* so that this will work whether or not the language is preinstalled.
*/
for (sl = loadlanguage; sl != NULL; sl = sl->next)
}
static void
-create_role(const char *rolename, const _stringlist * granted_dbs)
+create_role(const char *rolename, const _stringlist *granted_dbs)
{
header(_("creating role \"%s\""), rolename);
psql_command("postgres", "CREATE ROLE \"%s\" WITH LOGIN", rolename);
}
}
-static char *
-make_absolute_path(const char *in)
-{
- char *result;
-
- if (is_absolute_path(in))
- result = strdup(in);
- else
- {
- static char cwdbuf[MAXPGPATH];
-
- if (!cwdbuf[0])
- {
- if (!getcwd(cwdbuf, sizeof(cwdbuf)))
- {
- fprintf(stderr, _("could not get current working directory: %s\n"), strerror(errno));
- exit(2);
- }
- }
-
- result = malloc(strlen(cwdbuf) + strlen(in) + 2);
- sprintf(result, "%s/%s", cwdbuf, in);
- }
-
- canonicalize_path(result);
- return result;
-}
-
static void
help(void)
{
printf(_("Usage:\n %s [OPTION]... [EXTRA-TEST]...\n"), progname);
printf(_("\n"));
printf(_("Options:\n"));
+ printf(_(" --config-auth=DATADIR update authentication settings for DATADIR\n"));
printf(_(" --create-role=ROLE create the specified role before testing\n"));
printf(_(" --dbname=DB use database DB (default \"regression\")\n"));
printf(_(" --debug turn on debug mode in programs that are run\n"));
printf(_(" --outputdir=DIR place output files in DIR (default \".\")\n"));
printf(_(" --schedule=FILE use test ordering schedule from FILE\n"));
printf(_(" (can be used multiple times to concatenate)\n"));
- printf(_(" --temp-install=DIR create a temporary installation in DIR\n"));
+ printf(_(" --temp-instance=DIR create a temporary instance in DIR\n"));
printf(_(" --use-existing use an existing installation\n"));
printf(_("\n"));
- printf(_("Options for \"temp-install\" mode:\n"));
- printf(_(" --extra-install=DIR additional directory to install (e.g., contrib)\n"));
+ printf(_("Options for \"temp-instance\" mode:\n"));
printf(_(" --no-locale use C locale\n"));
printf(_(" --port=PORT start postmaster on PORT\n"));
printf(_(" --temp-config=FILE append contents of FILE to temporary config\n"));
- printf(_(" --top-builddir=DIR (relative) path to top level build directory\n"));
printf(_("\n"));
printf(_("Options for using an existing installation:\n"));
printf(_(" --host=HOST use postmaster running on HOST\n"));
printf(_(" --port=PORT use postmaster running at PORT\n"));
printf(_(" --user=USER connect as USER\n"));
- printf(_(" --psqldir=DIR use psql in DIR (default: configured bindir)\n"));
printf(_("\n"));
printf(_("The exit status is 0 if all tests passed, 1 if some tests failed, and 2\n"));
printf(_("if the tests could not be run for some reason.\n"));
int
regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc)
{
- _stringlist *sl;
- int c;
- int i;
- int option_index;
- char buf[MAXPGPATH * 4];
- char buf2[MAXPGPATH * 4];
-
static struct option long_options[] = {
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'},
{"encoding", required_argument, NULL, 6},
{"outputdir", required_argument, NULL, 7},
{"schedule", required_argument, NULL, 8},
- {"temp-install", required_argument, NULL, 9},
+ {"temp-instance", required_argument, NULL, 9},
{"no-locale", no_argument, NULL, 10},
- {"top-builddir", required_argument, NULL, 11},
{"host", required_argument, NULL, 13},
{"port", required_argument, NULL, 14},
{"user", required_argument, NULL, 15},
- {"psqldir", required_argument, NULL, 16},
+ {"bindir", required_argument, NULL, 16},
{"dlpath", required_argument, NULL, 17},
{"create-role", required_argument, NULL, 18},
{"temp-config", required_argument, NULL, 19},
{"use-existing", no_argument, NULL, 20},
{"launcher", required_argument, NULL, 21},
{"load-extension", required_argument, NULL, 22},
- {"extra-install", required_argument, NULL, 23},
+ {"config-auth", required_argument, NULL, 24},
{NULL, 0, NULL, 0}
};
+ _stringlist *sl;
+ int c;
+ int i;
+ int option_index;
+ char buf[MAXPGPATH * 4];
+ char buf2[MAXPGPATH * 4];
+
progname = get_progname(argv[0]);
set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_regress"));
* We call the initialization function here because that way we can set
* default parameters and let them be overwritten by the commandline.
*/
- ifunc();
+ ifunc(argc, argv);
+
+ if (getenv("PG_REGRESS_DIFF_OPTS"))
+ pretty_diff_opts = getenv("PG_REGRESS_DIFF_OPTS");
while ((c = getopt_long(argc, argv, "hV", long_options, &option_index)) != -1)
{
add_stringlist_item(&schedulelist, optarg);
break;
case 9:
- temp_install = make_absolute_path(optarg);
+ temp_instance = make_absolute_path(optarg);
break;
case 10:
nolocale = true;
break;
- case 11:
- top_builddir = strdup(optarg);
- break;
case 13:
hostname = strdup(optarg);
break;
user = strdup(optarg);
break;
case 16:
- /* "--psqldir=" should mean to use PATH */
+ /* "--bindir=" means to use PATH */
if (strlen(optarg))
- psqldir = strdup(optarg);
+ bindir = strdup(optarg);
+ else
+ bindir = NULL;
break;
case 17:
dlpath = strdup(optarg);
case 22:
add_stringlist_item(&loadextension, optarg);
break;
- case 23:
- add_stringlist_item(&extra_install, optarg);
+ case 24:
+ config_auth_datadir = pstrdup(optarg);
break;
default:
/* getopt_long already emitted a complaint */
optind++;
}
- if (temp_install && !port_specified_by_user)
+ if (config_auth_datadir)
+ {
+#ifdef ENABLE_SSPI
+ config_sspi_auth(config_auth_datadir);
+#endif
+ exit(0);
+ }
+
+ if (temp_instance && !port_specified_by_user)
/*
* 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);
unlimit_core_size();
#endif
- if (temp_install)
+ if (temp_instance)
{
FILE *pg_conf;
- _stringlist *sl;
/*
- * Prepare the temp installation
+ * Prepare the temp instance
*/
- if (!top_builddir)
- {
- fprintf(stderr, _("--top-builddir must be specified when using --temp-install\n"));
- exit(2);
- }
- if (directory_exists(temp_install))
+ if (directory_exists(temp_instance))
{
- header(_("removing existing temp installation"));
- rmtree(temp_install, true);
+ header(_("removing existing temp instance"));
+ if (!rmtree(temp_instance, true))
+ {
+ fprintf(stderr, _("\n%s: could not remove temp instance \"%s\"\n"),
+ progname, temp_instance);
+ exit(2);
+ }
}
- header(_("creating temporary installation"));
+ header(_("creating temporary instance"));
- /* make the temp install top directory */
- make_directory(temp_install);
+ /* make the temp instance top directory */
+ make_directory(temp_instance);
/* and a directory for log files */
snprintf(buf, sizeof(buf), "%s/log", outputdir);
if (!directory_exists(buf))
make_directory(buf);
- /* "make install" */
-#ifndef WIN32_ONLY_COMPILER
- snprintf(buf, sizeof(buf),
- SYSTEMQUOTE "\"%s\" -C \"%s\" DESTDIR=\"%s/install\" install > \"%s/log/install.log\" 2>&1" SYSTEMQUOTE,
- makeprog, top_builddir, temp_install, outputdir);
-#else
- snprintf(buf, sizeof(buf),
- SYSTEMQUOTE "perl \"%s/src/tools/msvc/install.pl\" \"%s/install\" >\"%s/log/install.log\" 2>&1" SYSTEMQUOTE,
- top_builddir, temp_install, outputdir);
-#endif
- if (system(buf))
- {
- fprintf(stderr, _("\n%s: installation failed\nExamine %s/log/install.log for the reason.\nCommand was: %s\n"), progname, outputdir, buf);
- exit(2);
- }
-
- for (sl = extra_install; sl != NULL; sl = sl->next)
- {
-#ifndef WIN32_ONLY_COMPILER
- snprintf(buf, sizeof(buf),
- SYSTEMQUOTE "\"%s\" -C \"%s/%s\" DESTDIR=\"%s/install\" install >> \"%s/log/install.log\" 2>&1" SYSTEMQUOTE,
- makeprog, top_builddir, sl->str, temp_install, outputdir);
-#else
- fprintf(stderr, _("\n%s: --extra-install option not supported on this platform\n"), progname);
- exit(2);
-#endif
-
- if (system(buf))
- {
- fprintf(stderr, _("\n%s: installation failed\nExamine %s/log/install.log for the reason.\nCommand was: %s\n"), progname, outputdir, buf);
- exit(2);
- }
- }
-
/* initdb */
header(_("initializing database system"));
snprintf(buf, sizeof(buf),
- SYSTEMQUOTE "\"%s/initdb\" -D \"%s/data\" -L \"%s\" --noclean%s%s > \"%s/log/initdb.log\" 2>&1" SYSTEMQUOTE,
- bindir, temp_install, datadir,
+ "\"%s%sinitdb\" -D \"%s/data\" --noclean --nosync%s%s > \"%s/log/initdb.log\" 2>&1",
+ bindir ? bindir : "",
+ bindir ? "/" : "",
+ temp_instance,
debug ? " --debug" : "",
nolocale ? " --no-locale" : "",
outputdir);
}
/*
- * Adjust the default postgresql.conf as needed for regression
- * testing. The user can specify a file to be appended; in any case we
- * set max_prepared_transactions to enable testing of prepared xacts.
- * (Note: to reduce the probability of unexpected shmmax failures,
- * don't set max_prepared_transactions any higher than actually needed
- * by the prepared_xacts regression test.)
+ * Adjust the default postgresql.conf for regression testing. The user
+ * can specify a file to be appended; in any case we expand logging
+ * and set max_prepared_transactions to enable testing of prepared
+ * xacts. (Note: to reduce the probability of unexpected shmmax
+ * failures, don't set max_prepared_transactions any higher than
+ * actually needed by the prepared_xacts regression test.)
*/
- snprintf(buf, sizeof(buf), "%s/data/postgresql.conf", temp_install);
+ snprintf(buf, sizeof(buf), "%s/data/postgresql.conf", temp_instance);
pg_conf = fopen(buf, "a");
if (pg_conf == NULL)
{
exit(2);
}
fputs("\n# Configuration added by pg_regress\n\n", pg_conf);
+ fputs("log_autovacuum_min_duration = 0\n", pg_conf);
+ fputs("log_checkpoints = on\n", pg_conf);
+ fputs("log_lock_waits = on\n", pg_conf);
+ fputs("log_temp_files = 128kB\n", pg_conf);
fputs("max_prepared_transactions = 2\n", pg_conf);
if (temp_config != NULL)
fclose(pg_conf);
+#ifdef ENABLE_SSPI
+
+ /*
+ * Since we successfully used the same buffer for the much-longer
+ * "initdb" command, this can't truncate.
+ */
+ snprintf(buf, sizeof(buf), "%s/data", temp_instance);
+ config_sspi_auth(buf);
+#elif !defined(HAVE_UNIX_SOCKETS)
+#error Platform has no means to secure the test installation.
+#endif
+
/*
* Check if there is a postmaster running already.
*/
snprintf(buf2, sizeof(buf2),
- SYSTEMQUOTE "\"%s/psql\" -X postgres <%s 2>%s" SYSTEMQUOTE,
- bindir, DEVNULL, DEVNULL);
+ "\"%s%spsql\" -X postgres <%s 2>%s",
+ bindir ? bindir : "",
+ bindir ? "/" : "",
+ DEVNULL, DEVNULL);
for (i = 0; i < 16; i++)
{
*/
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 : "",
+ "\"%s%spostgres\" -D \"%s/data\" -F%s "
+ "-c \"listen_addresses=%s\" -k \"%s\" "
+ "> \"%s/log/postmaster.log\" 2>&1",
+ bindir ? bindir : "",
+ bindir ? "/" : "",
+ temp_instance, debug ? " -d 5" : "",
+ hostname ? hostname : "", sockdir ? sockdir : "",
outputdir);
postmaster_pid = spawn_process(buf);
if (postmaster_pid == INVALID_PID)
/*
* Shut down temp installation's postmaster
*/
- if (temp_install)
+ if (temp_instance)
{
header(_("shutting down postmaster"));
stop_postmaster();
}
+ /*
+ * If there were no errors, remove the temp instance immediately to
+ * conserve disk space. (If there were errors, we leave the instance in
+ * place for possible manual investigation.)
+ */
+ if (temp_instance && fail_count == 0 && fail_ignore_count == 0)
+ {
+ header(_("removing temporary instance"));
+ if (!rmtree(temp_instance, true))
+ fprintf(stderr, _("\n%s: could not remove temp instance \"%s\"\n"),
+ progname, temp_instance);
+ }
+
fclose(logfile);
/*