X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Ftest%2Fregress%2Fpg_regress.c;h=a1902fe1271648fb3e877da511f1393843e04ee3;hb=ee943004466418595363d567f18c053bae407792;hp=13842231ce14f2b69d8b47c385613247af71dcf2;hpb=95ca2859f45171c345d427991c1f319b5e77cc6c;p=postgresql diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c index 13842231ce..a1902fe127 100644 --- a/src/test/regress/pg_regress.c +++ b/src/test/regress/pg_regress.c @@ -8,7 +8,7 @@ * * 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 @@ -29,7 +29,10 @@ #include #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 */ @@ -39,35 +42,20 @@ typedef struct _resultmap 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 @@ -83,7 +71,7 @@ _stringlist *dblist = NULL; bool debug = false; char *inputdir = "."; char *outputdir = "."; -char *psqldir = PGBINDIR; +char *bindir = PGBINDIR; char *launcher = NULL; static _stringlist *loadlanguage = NULL; static _stringlist *loadextension = NULL; @@ -91,9 +79,8 @@ static int max_connections = 0; 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; @@ -102,13 +89,19 @@ static bool port_specified_by_user = false; 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; @@ -122,30 +115,9 @@ static int fail_ignore_count = 0; 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. @@ -177,7 +149,7 @@ unlimit_core_size(void) * 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; @@ -198,7 +170,7 @@ add_stringlist_item(_stringlist ** listhead, const char *str) * Free a stringlist. */ static void -free_stringlist(_stringlist ** listhead) +free_stringlist(_stringlist **listhead) { if (listhead == NULL || *listhead == NULL) return; @@ -213,7 +185,7 @@ free_stringlist(_stringlist ** listhead) * 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); @@ -293,8 +265,10 @@ stop_postmaster(void) 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) { @@ -307,6 +281,82 @@ stop_postmaster(void) } } +#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 * @@ -381,7 +431,7 @@ string_matches_pattern(const char *str, const char *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 @@ -407,7 +457,7 @@ replace_string(char *string, char *replace, char *replacement) * 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]; @@ -450,7 +500,12 @@ convert_sourcefiles_in(char *source_subdir, char *dest_subdir, char *suffix) * 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 @@ -475,7 +530,8 @@ convert_sourcefiles_in(char *source_subdir, char *dest_subdir, char *suffix) /* 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) @@ -522,8 +578,8 @@ convert_sourcefiles_in(char *source_subdir, char *dest_subdir, char *suffix) 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"); } /* @@ -535,7 +591,7 @@ convert_sourcefiles(void) * 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.) */ @@ -653,44 +709,18 @@ get_expectfile(const char *testname, const char *file) 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) @@ -704,9 +734,17 @@ initialize_environment(void) 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 } @@ -746,20 +784,19 @@ initialize_environment(void) 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"); @@ -768,10 +805,20 @@ initialize_environment(void) 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) { @@ -780,42 +827,6 @@ initialize_environment(void) 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 { @@ -865,6 +876,176 @@ initialize_environment(void) 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 * @@ -897,9 +1078,9 @@ psql_command(const char *database, const char *query,...) /* 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); @@ -923,7 +1104,7 @@ spawn_process(const char *cmdline) 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); @@ -944,12 +1125,12 @@ spawn_process(const char *cmdline) * 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)); @@ -958,101 +1139,17 @@ spawn_process(const char *cmdline) /* 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 @@ -1147,8 +1244,17 @@ get_alternative_expectfile(const char *expectfile, int i) { 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, '.'); @@ -1219,7 +1325,7 @@ results_differ(const char *testname, const char *resultsfile, const char *defaul */ platform_expectfile = get_expectfile(testname, resultsfile); - strcpy(expectfile, default_expectfile); + strlcpy(expectfile, default_expectfile, sizeof(expectfile)); if (platform_expectfile) { /* @@ -1237,7 +1343,7 @@ results_differ(const char *testname, const char *resultsfile, const char *defaul /* 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? */ @@ -1256,16 +1362,27 @@ results_differ(const char *testname, const char *resultsfile, const char *defaul 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; } @@ -1274,7 +1391,7 @@ results_differ(const char *testname, const char *resultsfile, const char *defaul { /* 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); } @@ -1287,7 +1404,7 @@ results_differ(const char *testname, const char *resultsfile, const char *defaul 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) @@ -1302,7 +1419,7 @@ results_differ(const char *testname, const char *resultsfile, const char *defaul { /* 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)); } } @@ -1311,7 +1428,7 @@ results_differ(const char *testname, const char *resultsfile, const char *defaul * 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); @@ -1653,6 +1770,8 @@ run_schedule(const char *schedule, test_function tfunc) } } + free_stringlist(&ignorelist); + fclose(scf); } @@ -1789,7 +1908,7 @@ create_database(const char *dbname) 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) @@ -1817,7 +1936,7 @@ drop_role_if_exists(const char *rolename) } 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); @@ -1828,34 +1947,6 @@ create_role(const char *rolename, const _stringlist * granted_dbs) } } -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) { @@ -1864,6 +1955,7 @@ 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")); @@ -1880,21 +1972,18 @@ help(void) 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")); @@ -1905,13 +1994,6 @@ help(void) 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'}, @@ -1923,23 +2005,29 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc {"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")); @@ -1954,7 +2042,10 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc * 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) { @@ -1997,14 +2088,11 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc 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; @@ -2016,9 +2104,11 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc 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); @@ -2038,8 +2128,8 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc 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 */ @@ -2058,12 +2148,22 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc 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); @@ -2082,75 +2182,42 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc 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); @@ -2161,14 +2228,14 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc } /* - * 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) { @@ -2176,6 +2243,10 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc 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) @@ -2196,12 +2267,26 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc 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++) { @@ -2232,10 +2317,13 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc */ 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) @@ -2350,12 +2438,25 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc /* * 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); /*