1 /*-------------------------------------------------------------------------
2 *
3 * pg_regress --- regression test driver
4 *
5 * This is a C implementation of the previous shell script for running
6 * the regression tests, and should be mostly compatible with it.
7 * Initial author of C translation: Magnus Hagander
8 *
9 * This code is released under the terms of the PostgreSQL License.
10 *
11 * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
12 * Portions Copyright (c) 1994, Regents of the University of California
13 *
14 * src/test/regress/pg_regress.c
15 *
16 *-------------------------------------------------------------------------
17 */
18
19 #include "pg_regress.h"
20
21 #include <ctype.h>
22 #include <sys/stat.h>
23 #include <sys/wait.h>
24 #include <signal.h>
25 #include <unistd.h>
26
27 #ifdef HAVE_SYS_RESOURCE_H
28 #include <sys/time.h>
29 #include <sys/resource.h>
30 #endif
31
32 #include "common/restricted_token.h"
33 #include "common/username.h"
34 #include "getopt_long.h"
35 #include "libpq/pqcomm.h" /* needed for UNIXSOCK_PATH() */
36 #include "pg_config_paths.h"
37
38 /* for resultmap we need a list of pairs of strings */
39 typedef struct _resultmap
40 {
41 char *test;
42 char *type;
43 char *resultfile;
44 struct _resultmap *next;
45 } _resultmap;
46
47 /*
48 * Values obtained from Makefile.
49 */
50 char *host_platform = HOST_TUPLE;
51
52 #ifndef WIN32 /* not used in WIN32 case */
53 static char *shellprog = SHELLPROG;
54 #endif
55
56 /*
57 * On Windows we use -w in diff switches to avoid problems with inconsistent
58 * newline representation. The actual result files will generally have
59 * Windows-style newlines, but the comparison files might or might not.
60 */
61 #ifndef WIN32
62 const char *basic_diff_opts = "";
63 const char *pretty_diff_opts = "-C3";
64 #else
65 const char *basic_diff_opts = "-w";
66 const char *pretty_diff_opts = "-w -C3";
67 #endif
68
69 /* options settable from command line */
70 _stringlist *dblist = NULL;
71 bool debug = false;
72 char *inputdir = ".";
73 char *outputdir = ".";
74 char *bindir = PGBINDIR;
75 char *launcher = NULL;
76 static _stringlist *loadlanguage = NULL;
77 static _stringlist *loadextension = NULL;
78 static int max_connections = 0;
79 static char *encoding = NULL;
80 static _stringlist *schedulelist = NULL;
81 static _stringlist *extra_tests = NULL;
82 static char *temp_instance = NULL;
83 static _stringlist *temp_configs = NULL;
84 static bool nolocale = false;
85 static bool use_existing = false;
86 static char *hostname = NULL;
87 static int port = -1;
88 static bool port_specified_by_user = false;
89 static char *dlpath = PKGLIBDIR;
90 static char *user = NULL;
91 static _stringlist *extraroles = NULL;
92 static char *config_auth_datadir = NULL;
93
94 /* internal variables */
95 static const char *progname;
96 static char *logfilename;
97 static FILE *logfile;
98 static char *difffilename;
99 static const char *sockdir;
100 #ifdef HAVE_UNIX_SOCKETS
101 static const char *temp_sockdir;
102 static char sockself[MAXPGPATH];
103 static char socklock[MAXPGPATH];
104 #endif
105
106 static _resultmap *resultmap = NULL;
107
108 static PID_TYPE postmaster_pid = INVALID_PID;
109 static bool postmaster_running = false;
110
111 static int success_count = 0;
112 static int fail_count = 0;
113 static int fail_ignore_count = 0;
114
115 static bool directory_exists(const char *dir);
116 static void make_directory(const char *dir);
117
118 static void header(const char *fmt,...) pg_attribute_printf(1, 2);
119 static void status(const char *fmt,...) pg_attribute_printf(1, 2);
120 static void psql_command(const char *database, const char *query,...) pg_attribute_printf(2, 3);
121
122 /*
123 * allow core files if possible.
124 */
125 #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE)
126 static void
unlimit_core_size(void)127 unlimit_core_size(void)
128 {
129 struct rlimit lim;
130
131 getrlimit(RLIMIT_CORE, &lim);
132 if (lim.rlim_max == 0)
133 {
134 fprintf(stderr,
135 _("%s: could not set core size: disallowed by hard limit\n"),
136 progname);
137 return;
138 }
139 else if (lim.rlim_max == RLIM_INFINITY || lim.rlim_cur < lim.rlim_max)
140 {
141 lim.rlim_cur = lim.rlim_max;
142 setrlimit(RLIMIT_CORE, &lim);
143 }
144 }
145 #endif
146
147
148 /*
149 * Add an item at the end of a stringlist.
150 */
151 void
add_stringlist_item(_stringlist ** listhead,const char * str)152 add_stringlist_item(_stringlist **listhead, const char *str)
153 {
154 _stringlist *newentry = malloc(sizeof(_stringlist));
155 _stringlist *oldentry;
156
157 newentry->str = strdup(str);
158 newentry->next = NULL;
159 if (*listhead == NULL)
160 *listhead = newentry;
161 else
162 {
163 for (oldentry = *listhead; oldentry->next; oldentry = oldentry->next)
164 /* skip */ ;
165 oldentry->next = newentry;
166 }
167 }
168
169 /*
170 * Free a stringlist.
171 */
172 static void
free_stringlist(_stringlist ** listhead)173 free_stringlist(_stringlist **listhead)
174 {
175 if (listhead == NULL || *listhead == NULL)
176 return;
177 if ((*listhead)->next != NULL)
178 free_stringlist(&((*listhead)->next));
179 free((*listhead)->str);
180 free(*listhead);
181 *listhead = NULL;
182 }
183
184 /*
185 * Split a delimited string into a stringlist
186 */
187 static void
split_to_stringlist(const char * s,const char * delim,_stringlist ** listhead)188 split_to_stringlist(const char *s, const char *delim, _stringlist **listhead)
189 {
190 char *sc = strdup(s);
191 char *token = strtok(sc, delim);
192
193 while (token)
194 {
195 add_stringlist_item(listhead, token);
196 token = strtok(NULL, delim);
197 }
198 free(sc);
199 }
200
201 /*
202 * Print a progress banner on stdout.
203 */
204 static void
header(const char * fmt,...)205 header(const char *fmt,...)
206 {
207 char tmp[64];
208 va_list ap;
209
210 va_start(ap, fmt);
211 vsnprintf(tmp, sizeof(tmp), fmt, ap);
212 va_end(ap);
213
214 fprintf(stdout, "============== %-38s ==============\n", tmp);
215 fflush(stdout);
216 }
217
218 /*
219 * Print "doing something ..." --- supplied text should not end with newline
220 */
221 static void
status(const char * fmt,...)222 status(const char *fmt,...)
223 {
224 va_list ap;
225
226 va_start(ap, fmt);
227 vfprintf(stdout, fmt, ap);
228 fflush(stdout);
229 va_end(ap);
230
231 if (logfile)
232 {
233 va_start(ap, fmt);
234 vfprintf(logfile, fmt, ap);
235 va_end(ap);
236 }
237 }
238
239 /*
240 * Done "doing something ..."
241 */
242 static void
status_end(void)243 status_end(void)
244 {
245 fprintf(stdout, "\n");
246 fflush(stdout);
247 if (logfile)
248 fprintf(logfile, "\n");
249 }
250
251 /*
252 * shut down temp postmaster
253 */
254 static void
stop_postmaster(void)255 stop_postmaster(void)
256 {
257 if (postmaster_running)
258 {
259 /* We use pg_ctl to issue the kill and wait for stop */
260 char buf[MAXPGPATH * 2];
261 int r;
262
263 /* On Windows, system() seems not to force fflush, so... */
264 fflush(stdout);
265 fflush(stderr);
266
267 snprintf(buf, sizeof(buf),
268 "\"%s%spg_ctl\" stop -D \"%s/data\" -s -m fast",
269 bindir ? bindir : "",
270 bindir ? "/" : "",
271 temp_instance);
272 r = system(buf);
273 if (r != 0)
274 {
275 fprintf(stderr, _("\n%s: could not stop postmaster: exit code was %d\n"),
276 progname, r);
277 _exit(2); /* not exit(), that could be recursive */
278 }
279
280 postmaster_running = false;
281 }
282 }
283
284 #ifdef HAVE_UNIX_SOCKETS
285 /*
286 * Remove the socket temporary directory. pg_regress never waits for a
287 * postmaster exit, so it is indeterminate whether the postmaster has yet to
288 * unlink the socket and lock file. Unlink them here so we can proceed to
289 * remove the directory. Ignore errors; leaking a temporary directory is
290 * unimportant. This can run from a signal handler. The code is not
291 * acceptable in a Windows signal handler (see initdb.c:trapsig()), but
292 * Windows is not a HAVE_UNIX_SOCKETS platform.
293 */
294 static void
remove_temp(void)295 remove_temp(void)
296 {
297 Assert(temp_sockdir);
298 unlink(sockself);
299 unlink(socklock);
300 rmdir(temp_sockdir);
301 }
302
303 /*
304 * Signal handler that calls remove_temp() and reraises the signal.
305 */
306 static void
signal_remove_temp(int signum)307 signal_remove_temp(int signum)
308 {
309 remove_temp();
310
311 pqsignal(signum, SIG_DFL);
312 raise(signum);
313 }
314
315 /*
316 * Create a temporary directory suitable for the server's Unix-domain socket.
317 * The directory will have mode 0700 or stricter, so no other OS user can open
318 * our socket to exploit our use of trust authentication. Most systems
319 * constrain the length of socket paths well below _POSIX_PATH_MAX, so we
320 * place the directory under /tmp rather than relative to the possibly-deep
321 * current working directory.
322 *
323 * Compared to using the compiled-in DEFAULT_PGSOCKET_DIR, this also permits
324 * testing to work in builds that relocate it to a directory not writable to
325 * the build/test user.
326 */
327 static const char *
make_temp_sockdir(void)328 make_temp_sockdir(void)
329 {
330 char *template = strdup("/tmp/pg_regress-XXXXXX");
331
332 temp_sockdir = mkdtemp(template);
333 if (temp_sockdir == NULL)
334 {
335 fprintf(stderr, _("%s: could not create directory \"%s\": %s\n"),
336 progname, template, strerror(errno));
337 exit(2);
338 }
339
340 /* Stage file names for remove_temp(). Unsafe in a signal handler. */
341 UNIXSOCK_PATH(sockself, port, temp_sockdir);
342 snprintf(socklock, sizeof(socklock), "%s.lock", sockself);
343
344 /* Remove the directory during clean exit. */
345 atexit(remove_temp);
346
347 /*
348 * Remove the directory before dying to the usual signals. Omit SIGQUIT,
349 * preserving it as a quick, untidy exit.
350 */
351 pqsignal(SIGHUP, signal_remove_temp);
352 pqsignal(SIGINT, signal_remove_temp);
353 pqsignal(SIGPIPE, signal_remove_temp);
354 pqsignal(SIGTERM, signal_remove_temp);
355
356 return temp_sockdir;
357 }
358 #endif /* HAVE_UNIX_SOCKETS */
359
360 /*
361 * Check whether string matches pattern
362 *
363 * In the original shell script, this function was implemented using expr(1),
364 * which provides basic regular expressions restricted to match starting at
365 * the string start (in conventional regex terms, there's an implicit "^"
366 * at the start of the pattern --- but no implicit "$" at the end).
367 *
368 * For now, we only support "." and ".*" as non-literal metacharacters,
369 * because that's all that anyone has found use for in resultmap. This
370 * code could be extended if more functionality is needed.
371 */
372 static bool
string_matches_pattern(const char * str,const char * pattern)373 string_matches_pattern(const char *str, const char *pattern)
374 {
375 while (*str && *pattern)
376 {
377 if (*pattern == '.' && pattern[1] == '*')
378 {
379 pattern += 2;
380 /* Trailing .* matches everything. */
381 if (*pattern == '\0')
382 return true;
383
384 /*
385 * Otherwise, scan for a text position at which we can match the
386 * rest of the pattern.
387 */
388 while (*str)
389 {
390 /*
391 * Optimization to prevent most recursion: don't recurse
392 * unless first pattern char might match this text char.
393 */
394 if (*str == *pattern || *pattern == '.')
395 {
396 if (string_matches_pattern(str, pattern))
397 return true;
398 }
399
400 str++;
401 }
402
403 /*
404 * End of text with no match.
405 */
406 return false;
407 }
408 else if (*pattern != '.' && *str != *pattern)
409 {
410 /*
411 * Not the single-character wildcard and no explicit match? Then
412 * time to quit...
413 */
414 return false;
415 }
416
417 str++;
418 pattern++;
419 }
420
421 if (*pattern == '\0')
422 return true; /* end of pattern, so declare match */
423
424 /* End of input string. Do we have matching pattern remaining? */
425 while (*pattern == '.' && pattern[1] == '*')
426 pattern += 2;
427 if (*pattern == '\0')
428 return true; /* end of pattern, so declare match */
429
430 return false;
431 }
432
433 /*
434 * Replace all occurrences of a string in a string with a different string.
435 * NOTE: Assumes there is enough room in the target buffer!
436 */
437 void
replace_string(char * string,char * replace,char * replacement)438 replace_string(char *string, char *replace, char *replacement)
439 {
440 char *ptr;
441
442 while ((ptr = strstr(string, replace)) != NULL)
443 {
444 char *dup = strdup(string);
445
446 strlcpy(string, dup, ptr - string + 1);
447 strcat(string, replacement);
448 strcat(string, dup + (ptr - string) + strlen(replace));
449 free(dup);
450 }
451 }
452
453 /*
454 * Convert *.source found in the "source" directory, replacing certain tokens
455 * in the file contents with their intended values, and put the resulting files
456 * in the "dest" directory, replacing the ".source" prefix in their names with
457 * the given suffix.
458 */
459 static void
convert_sourcefiles_in(char * source_subdir,char * dest_dir,char * dest_subdir,char * suffix)460 convert_sourcefiles_in(char *source_subdir, char *dest_dir, char *dest_subdir, char *suffix)
461 {
462 char testtablespace[MAXPGPATH];
463 char indir[MAXPGPATH];
464 struct stat st;
465 int ret;
466 char **name;
467 char **names;
468 int count = 0;
469
470 snprintf(indir, MAXPGPATH, "%s/%s", inputdir, source_subdir);
471
472 /* Check that indir actually exists and is a directory */
473 ret = stat(indir, &st);
474 if (ret != 0 || !S_ISDIR(st.st_mode))
475 {
476 /*
477 * No warning, to avoid noise in tests that do not have these
478 * directories; for example, ecpg, contrib and src/pl.
479 */
480 return;
481 }
482
483 names = pgfnames(indir);
484 if (!names)
485 /* Error logged in pgfnames */
486 exit(2);
487
488 snprintf(testtablespace, MAXPGPATH, "%s/testtablespace", outputdir);
489
490 #ifdef WIN32
491
492 /*
493 * On Windows only, clean out the test tablespace dir, or create it if it
494 * doesn't exist. On other platforms we expect the Makefile to take care
495 * of that. (We don't migrate that functionality in here because it'd be
496 * harder to cope with platform-specific issues such as SELinux.)
497 *
498 * XXX it would be better if pg_regress.c had nothing at all to do with
499 * testtablespace, and this were handled by a .BAT file or similar on
500 * Windows. See pgsql-hackers discussion of 2008-01-18.
501 */
502 if (directory_exists(testtablespace))
503 if (!rmtree(testtablespace, true))
504 {
505 fprintf(stderr, _("\n%s: could not remove test tablespace \"%s\"\n"),
506 progname, testtablespace);
507 exit(2);
508 }
509 make_directory(testtablespace);
510 #endif
511
512 /* finally loop on each file and do the replacement */
513 for (name = names; *name; name++)
514 {
515 char srcfile[MAXPGPATH];
516 char destfile[MAXPGPATH];
517 char prefix[MAXPGPATH];
518 FILE *infile,
519 *outfile;
520 char line[1024];
521
522 /* reject filenames not finishing in ".source" */
523 if (strlen(*name) < 8)
524 continue;
525 if (strcmp(*name + strlen(*name) - 7, ".source") != 0)
526 continue;
527
528 count++;
529
530 /* build the full actual paths to open */
531 snprintf(prefix, strlen(*name) - 6, "%s", *name);
532 snprintf(srcfile, MAXPGPATH, "%s/%s", indir, *name);
533 snprintf(destfile, MAXPGPATH, "%s/%s/%s.%s", dest_dir, dest_subdir,
534 prefix, suffix);
535
536 infile = fopen(srcfile, "r");
537 if (!infile)
538 {
539 fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
540 progname, srcfile, strerror(errno));
541 exit(2);
542 }
543 outfile = fopen(destfile, "w");
544 if (!outfile)
545 {
546 fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
547 progname, destfile, strerror(errno));
548 exit(2);
549 }
550 while (fgets(line, sizeof(line), infile))
551 {
552 replace_string(line, "@abs_srcdir@", inputdir);
553 replace_string(line, "@abs_builddir@", outputdir);
554 replace_string(line, "@testtablespace@", testtablespace);
555 replace_string(line, "@libdir@", dlpath);
556 replace_string(line, "@DLSUFFIX@", DLSUFFIX);
557 fputs(line, outfile);
558 }
559 fclose(infile);
560 fclose(outfile);
561 }
562
563 /*
564 * If we didn't process any files, complain because it probably means
565 * somebody neglected to pass the needed --inputdir argument.
566 */
567 if (count <= 0)
568 {
569 fprintf(stderr, _("%s: no *.source files found in \"%s\"\n"),
570 progname, indir);
571 exit(2);
572 }
573
574 pgfnames_cleanup(names);
575 }
576
577 /* Create the .sql and .out files from the .source files, if any */
578 static void
convert_sourcefiles(void)579 convert_sourcefiles(void)
580 {
581 convert_sourcefiles_in("input", outputdir, "sql", "sql");
582 convert_sourcefiles_in("output", outputdir, "expected", "out");
583 }
584
585 /*
586 * Scan resultmap file to find which platform-specific expected files to use.
587 *
588 * The format of each line of the file is
589 * testname/hostplatformpattern=substitutefile
590 * where the hostplatformpattern is evaluated per the rules of expr(1),
591 * namely, it is a standard regular expression with an implicit ^ at the start.
592 * (We currently support only a very limited subset of regular expressions,
593 * see string_matches_pattern() above.) What hostplatformpattern will be
594 * matched against is the config.guess output. (In the shell-script version,
595 * we also provided an indication of whether gcc or another compiler was in
596 * use, but that facility isn't used anymore.)
597 */
598 static void
load_resultmap(void)599 load_resultmap(void)
600 {
601 char buf[MAXPGPATH];
602 FILE *f;
603
604 /* scan the file ... */
605 snprintf(buf, sizeof(buf), "%s/resultmap", inputdir);
606 f = fopen(buf, "r");
607 if (!f)
608 {
609 /* OK if it doesn't exist, else complain */
610 if (errno == ENOENT)
611 return;
612 fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
613 progname, buf, strerror(errno));
614 exit(2);
615 }
616
617 while (fgets(buf, sizeof(buf), f))
618 {
619 char *platform;
620 char *file_type;
621 char *expected;
622 int i;
623
624 /* strip trailing whitespace, especially the newline */
625 i = strlen(buf);
626 while (i > 0 && isspace((unsigned char) buf[i - 1]))
627 buf[--i] = '\0';
628
629 /* parse out the line fields */
630 file_type = strchr(buf, ':');
631 if (!file_type)
632 {
633 fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"),
634 buf);
635 exit(2);
636 }
637 *file_type++ = '\0';
638
639 platform = strchr(file_type, ':');
640 if (!platform)
641 {
642 fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"),
643 buf);
644 exit(2);
645 }
646 *platform++ = '\0';
647 expected = strchr(platform, '=');
648 if (!expected)
649 {
650 fprintf(stderr, _("incorrectly formatted resultmap entry: %s\n"),
651 buf);
652 exit(2);
653 }
654 *expected++ = '\0';
655
656 /*
657 * if it's for current platform, save it in resultmap list. Note: by
658 * adding at the front of the list, we ensure that in ambiguous cases,
659 * the last match in the resultmap file is used. This mimics the
660 * behavior of the old shell script.
661 */
662 if (string_matches_pattern(host_platform, platform))
663 {
664 _resultmap *entry = malloc(sizeof(_resultmap));
665
666 entry->test = strdup(buf);
667 entry->type = strdup(file_type);
668 entry->resultfile = strdup(expected);
669 entry->next = resultmap;
670 resultmap = entry;
671 }
672 }
673 fclose(f);
674 }
675
676 /*
677 * Check in resultmap if we should be looking at a different file
678 */
679 static
680 const char *
get_expectfile(const char * testname,const char * file)681 get_expectfile(const char *testname, const char *file)
682 {
683 char *file_type;
684 _resultmap *rm;
685
686 /*
687 * Determine the file type from the file name. This is just what is
688 * following the last dot in the file name.
689 */
690 if (!file || !(file_type = strrchr(file, '.')))
691 return NULL;
692
693 file_type++;
694
695 for (rm = resultmap; rm != NULL; rm = rm->next)
696 {
697 if (strcmp(testname, rm->test) == 0 && strcmp(file_type, rm->type) == 0)
698 {
699 return rm->resultfile;
700 }
701 }
702
703 return NULL;
704 }
705
706 /*
707 * Handy subroutine for setting an environment variable "var" to "val"
708 */
709 static void
doputenv(const char * var,const char * val)710 doputenv(const char *var, const char *val)
711 {
712 char *s;
713
714 s = psprintf("%s=%s", var, val);
715 putenv(s);
716 }
717
718 /*
719 * Prepare environment variables for running regression tests
720 */
721 static void
initialize_environment(void)722 initialize_environment(void)
723 {
724 putenv("PGAPPNAME=pg_regress");
725
726 if (nolocale)
727 {
728 /*
729 * Clear out any non-C locale settings
730 */
731 unsetenv("LC_COLLATE");
732 unsetenv("LC_CTYPE");
733 unsetenv("LC_MONETARY");
734 unsetenv("LC_NUMERIC");
735 unsetenv("LC_TIME");
736 unsetenv("LANG");
737
738 /*
739 * Most platforms have adopted the POSIX locale as their
740 * implementation-defined default locale. Exceptions include native
741 * Windows, Darwin with --enable-nls, and Cygwin with --enable-nls.
742 * (Use of --enable-nls matters because libintl replaces setlocale().)
743 * Also, PostgreSQL does not support Darwin with locale environment
744 * variables unset; see PostmasterMain().
745 */
746 #if defined(WIN32) || defined(__CYGWIN__) || defined(__darwin__)
747 putenv("LANG=C");
748 #endif
749 }
750
751 /*
752 * Set translation-related settings to English; otherwise psql will
753 * produce translated messages and produce diffs. (XXX If we ever support
754 * translation of pg_regress, this needs to be moved elsewhere, where psql
755 * is actually called.)
756 */
757 unsetenv("LANGUAGE");
758 unsetenv("LC_ALL");
759 putenv("LC_MESSAGES=C");
760
761 /*
762 * Set encoding as requested
763 */
764 if (encoding)
765 doputenv("PGCLIENTENCODING", encoding);
766 else
767 unsetenv("PGCLIENTENCODING");
768
769 /*
770 * Set timezone and datestyle for datetime-related tests
771 */
772 putenv("PGTZ=PST8PDT");
773 putenv("PGDATESTYLE=Postgres, MDY");
774
775 /*
776 * Likewise set intervalstyle to ensure consistent results. This is a bit
777 * more painful because we must use PGOPTIONS, and we want to preserve the
778 * user's ability to set other variables through that.
779 */
780 {
781 const char *my_pgoptions = "-c intervalstyle=postgres_verbose";
782 const char *old_pgoptions = getenv("PGOPTIONS");
783 char *new_pgoptions;
784
785 if (!old_pgoptions)
786 old_pgoptions = "";
787 new_pgoptions = psprintf("PGOPTIONS=%s %s",
788 old_pgoptions, my_pgoptions);
789 putenv(new_pgoptions);
790 }
791
792 if (temp_instance)
793 {
794 /*
795 * Clear out any environment vars that might cause psql to connect to
796 * the wrong postmaster, or otherwise behave in nondefault ways. (Note
797 * we also use psql's -X switch consistently, so that ~/.psqlrc files
798 * won't mess things up.) Also, set PGPORT to the temp port, and set
799 * PGHOST depending on whether we are using TCP or Unix sockets.
800 *
801 * This list should be kept in sync with TestLib.pm.
802 */
803 /* PGCLIENTENCODING, see above */
804 unsetenv("PGCONNECT_TIMEOUT");
805 unsetenv("PGDATA");
806 unsetenv("PGDATABASE");
807 unsetenv("PGGSSLIB");
808 /* PGHOSTADDR, see below */
809 unsetenv("PGKRBSRVNAME");
810 unsetenv("PGPASSFILE");
811 unsetenv("PGPASSWORD");
812 unsetenv("PGREQUIREPEER");
813 unsetenv("PGREQUIRESSL");
814 unsetenv("PGSERVICE");
815 unsetenv("PGSERVICEFILE");
816 unsetenv("PGSSLCERT");
817 unsetenv("PGSSLCRL");
818 unsetenv("PGSSLKEY");
819 unsetenv("PGSSLMODE");
820 unsetenv("PGSSLROOTCERT");
821 unsetenv("PGUSER");
822 /* PGPORT, see below */
823 /* PGHOST, see below */
824
825 #ifdef HAVE_UNIX_SOCKETS
826 if (hostname != NULL)
827 doputenv("PGHOST", hostname);
828 else
829 {
830 sockdir = getenv("PG_REGRESS_SOCK_DIR");
831 if (!sockdir)
832 sockdir = make_temp_sockdir();
833 doputenv("PGHOST", sockdir);
834 }
835 #else
836 Assert(hostname != NULL);
837 doputenv("PGHOST", hostname);
838 #endif
839 unsetenv("PGHOSTADDR");
840 if (port != -1)
841 {
842 char s[16];
843
844 sprintf(s, "%d", port);
845 doputenv("PGPORT", s);
846 }
847 }
848 else
849 {
850 const char *pghost;
851 const char *pgport;
852
853 /*
854 * When testing an existing install, we honor existing environment
855 * variables, except if they're overridden by command line options.
856 */
857 if (hostname != NULL)
858 {
859 doputenv("PGHOST", hostname);
860 unsetenv("PGHOSTADDR");
861 }
862 if (port != -1)
863 {
864 char s[16];
865
866 sprintf(s, "%d", port);
867 doputenv("PGPORT", s);
868 }
869 if (user != NULL)
870 doputenv("PGUSER", user);
871
872 /*
873 * Report what we're connecting to
874 */
875 pghost = getenv("PGHOST");
876 pgport = getenv("PGPORT");
877 #ifndef HAVE_UNIX_SOCKETS
878 if (!pghost)
879 pghost = "localhost";
880 #endif
881
882 if (pghost && pgport)
883 printf(_("(using postmaster on %s, port %s)\n"), pghost, pgport);
884 if (pghost && !pgport)
885 printf(_("(using postmaster on %s, default port)\n"), pghost);
886 if (!pghost && pgport)
887 printf(_("(using postmaster on Unix socket, port %s)\n"), pgport);
888 if (!pghost && !pgport)
889 printf(_("(using postmaster on Unix socket, default port)\n"));
890 }
891
892 convert_sourcefiles();
893 load_resultmap();
894 }
895
896 #ifdef ENABLE_SSPI
897 /*
898 * Get account and domain/realm names for the current user. This is based on
899 * pg_SSPI_recvauth(). The returned strings use static storage.
900 */
901 static void
current_windows_user(const char ** acct,const char ** dom)902 current_windows_user(const char **acct, const char **dom)
903 {
904 static char accountname[MAXPGPATH];
905 static char domainname[MAXPGPATH];
906 HANDLE token;
907 TOKEN_USER *tokenuser;
908 DWORD retlen;
909 DWORD accountnamesize = sizeof(accountname);
910 DWORD domainnamesize = sizeof(domainname);
911 SID_NAME_USE accountnameuse;
912
913 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &token))
914 {
915 fprintf(stderr,
916 _("%s: could not open process token: error code %lu\n"),
917 progname, GetLastError());
918 exit(2);
919 }
920
921 if (!GetTokenInformation(token, TokenUser, NULL, 0, &retlen) && GetLastError() != 122)
922 {
923 fprintf(stderr,
924 _("%s: could not get token information buffer size: error code %lu\n"),
925 progname, GetLastError());
926 exit(2);
927 }
928 tokenuser = malloc(retlen);
929 if (!GetTokenInformation(token, TokenUser, tokenuser, retlen, &retlen))
930 {
931 fprintf(stderr,
932 _("%s: could not get token information: error code %lu\n"),
933 progname, GetLastError());
934 exit(2);
935 }
936
937 if (!LookupAccountSid(NULL, tokenuser->User.Sid, accountname, &accountnamesize,
938 domainname, &domainnamesize, &accountnameuse))
939 {
940 fprintf(stderr,
941 _("%s: could not look up account SID: error code %lu\n"),
942 progname, GetLastError());
943 exit(2);
944 }
945
946 free(tokenuser);
947
948 *acct = accountname;
949 *dom = domainname;
950 }
951
952 /*
953 * Rewrite pg_hba.conf and pg_ident.conf to use SSPI authentication. Permit
954 * the current OS user to authenticate as the bootstrap superuser and as any
955 * user named in a --create-role option.
956 */
957 static void
config_sspi_auth(const char * pgdata)958 config_sspi_auth(const char *pgdata)
959 {
960 const char *accountname,
961 *domainname;
962 const char *username;
963 char *errstr;
964 bool have_ipv6;
965 char fname[MAXPGPATH];
966 int res;
967 FILE *hba,
968 *ident;
969 _stringlist *sl;
970
971 /*
972 * "username", the initdb-chosen bootstrap superuser name, may always
973 * match "accountname", the value SSPI authentication discovers. The
974 * underlying system functions do not clearly guarantee that.
975 */
976 current_windows_user(&accountname, &domainname);
977 username = get_user_name(&errstr);
978 if (username == NULL)
979 {
980 fprintf(stderr, "%s: %s\n", progname, errstr);
981 exit(2);
982 }
983
984 /*
985 * Like initdb.c:setup_config(), determine whether the platform recognizes
986 * ::1 (IPv6 loopback) as a numeric host address string.
987 */
988 {
989 struct addrinfo *gai_result;
990 struct addrinfo hints;
991 WSADATA wsaData;
992
993 hints.ai_flags = AI_NUMERICHOST;
994 hints.ai_family = AF_UNSPEC;
995 hints.ai_socktype = 0;
996 hints.ai_protocol = 0;
997 hints.ai_addrlen = 0;
998 hints.ai_canonname = NULL;
999 hints.ai_addr = NULL;
1000 hints.ai_next = NULL;
1001
1002 have_ipv6 = (WSAStartup(MAKEWORD(2, 2), &wsaData) == 0 &&
1003 getaddrinfo("::1", NULL, &hints, &gai_result) == 0);
1004 }
1005
1006 /* Check a Write outcome and report any error. */
1007 #define CW(cond) \
1008 do { \
1009 if (!(cond)) \
1010 { \
1011 fprintf(stderr, _("%s: could not write to file \"%s\": %s\n"), \
1012 progname, fname, strerror(errno)); \
1013 exit(2); \
1014 } \
1015 } while (0)
1016
1017 res = snprintf(fname, sizeof(fname), "%s/pg_hba.conf", pgdata);
1018 if (res < 0 || res >= sizeof(fname))
1019 {
1020 /*
1021 * Truncating this name is a fatal error, because we must not fail to
1022 * overwrite an original trust-authentication pg_hba.conf.
1023 */
1024 fprintf(stderr, _("%s: directory name too long\n"), progname);
1025 exit(2);
1026 }
1027 hba = fopen(fname, "w");
1028 if (hba == NULL)
1029 {
1030 fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
1031 progname, fname, strerror(errno));
1032 exit(2);
1033 }
1034 CW(fputs("# Configuration written by config_sspi_auth()\n", hba) >= 0);
1035 CW(fputs("host all all 127.0.0.1/32 sspi include_realm=1 map=regress\n",
1036 hba) >= 0);
1037 if (have_ipv6)
1038 CW(fputs("host all all ::1/128 sspi include_realm=1 map=regress\n",
1039 hba) >= 0);
1040 CW(fclose(hba) == 0);
1041
1042 snprintf(fname, sizeof(fname), "%s/pg_ident.conf", pgdata);
1043 ident = fopen(fname, "w");
1044 if (ident == NULL)
1045 {
1046 fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
1047 progname, fname, strerror(errno));
1048 exit(2);
1049 }
1050 CW(fputs("# Configuration written by config_sspi_auth()\n", ident) >= 0);
1051
1052 /*
1053 * Double-quote for the benefit of account names containing whitespace or
1054 * '#'. Windows forbids the double-quote character itself, so don't
1055 * bother escaping embedded double-quote characters.
1056 */
1057 CW(fprintf(ident, "regress \"%s@%s\" \"%s\"\n",
1058 accountname, domainname, username) >= 0);
1059 for (sl = extraroles; sl; sl = sl->next)
1060 CW(fprintf(ident, "regress \"%s@%s\" \"%s\"\n",
1061 accountname, domainname, sl->str) >= 0);
1062 CW(fclose(ident) == 0);
1063 }
1064 #endif
1065
1066 /*
1067 * Issue a command via psql, connecting to the specified database
1068 *
1069 * Since we use system(), this doesn't return until the operation finishes
1070 */
1071 static void
psql_command(const char * database,const char * query,...)1072 psql_command(const char *database, const char *query,...)
1073 {
1074 char query_formatted[1024];
1075 char query_escaped[2048];
1076 char psql_cmd[MAXPGPATH + 2048];
1077 va_list args;
1078 char *s;
1079 char *d;
1080
1081 /* Generate the query with insertion of sprintf arguments */
1082 va_start(args, query);
1083 vsnprintf(query_formatted, sizeof(query_formatted), query, args);
1084 va_end(args);
1085
1086 /* Now escape any shell double-quote metacharacters */
1087 d = query_escaped;
1088 for (s = query_formatted; *s; s++)
1089 {
1090 if (strchr("\\\"$`", *s))
1091 *d++ = '\\';
1092 *d++ = *s;
1093 }
1094 *d = '\0';
1095
1096 /* And now we can build and execute the shell command */
1097 snprintf(psql_cmd, sizeof(psql_cmd),
1098 "\"%s%spsql\" -X -c \"%s\" \"%s\"",
1099 bindir ? bindir : "",
1100 bindir ? "/" : "",
1101 query_escaped,
1102 database);
1103
1104 if (system(psql_cmd) != 0)
1105 {
1106 /* psql probably already reported the error */
1107 fprintf(stderr, _("command failed: %s\n"), psql_cmd);
1108 exit(2);
1109 }
1110 }
1111
1112 /*
1113 * Spawn a process to execute the given shell command; don't wait for it
1114 *
1115 * Returns the process ID (or HANDLE) so we can wait for it later
1116 */
1117 PID_TYPE
spawn_process(const char * cmdline)1118 spawn_process(const char *cmdline)
1119 {
1120 #ifndef WIN32
1121 pid_t pid;
1122
1123 /*
1124 * Must flush I/O buffers before fork. Ideally we'd use fflush(NULL) here
1125 * ... does anyone still care about systems where that doesn't work?
1126 */
1127 fflush(stdout);
1128 fflush(stderr);
1129 if (logfile)
1130 fflush(logfile);
1131
1132 pid = fork();
1133 if (pid == -1)
1134 {
1135 fprintf(stderr, _("%s: could not fork: %s\n"),
1136 progname, strerror(errno));
1137 exit(2);
1138 }
1139 if (pid == 0)
1140 {
1141 /*
1142 * In child
1143 *
1144 * Instead of using system(), exec the shell directly, and tell it to
1145 * "exec" the command too. This saves two useless processes per
1146 * parallel test case.
1147 */
1148 char *cmdline2;
1149
1150 cmdline2 = psprintf("exec %s", cmdline);
1151 execl(shellprog, shellprog, "-c", cmdline2, (char *) NULL);
1152 fprintf(stderr, _("%s: could not exec \"%s\": %s\n"),
1153 progname, shellprog, strerror(errno));
1154 _exit(1); /* not exit() here... */
1155 }
1156 /* in parent */
1157 return pid;
1158 #else
1159 PROCESS_INFORMATION pi;
1160 char *cmdline2;
1161 HANDLE restrictedToken;
1162
1163 memset(&pi, 0, sizeof(pi));
1164 cmdline2 = psprintf("cmd /c \"%s\"", cmdline);
1165
1166 if ((restrictedToken =
1167 CreateRestrictedProcess(cmdline2, &pi, progname)) == 0)
1168 exit(2);
1169
1170 CloseHandle(pi.hThread);
1171 return pi.hProcess;
1172 #endif
1173 }
1174
1175 /*
1176 * Count bytes in file
1177 */
1178 static long
file_size(const char * file)1179 file_size(const char *file)
1180 {
1181 long r;
1182 FILE *f = fopen(file, "r");
1183
1184 if (!f)
1185 {
1186 fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
1187 progname, file, strerror(errno));
1188 return -1;
1189 }
1190 fseek(f, 0, SEEK_END);
1191 r = ftell(f);
1192 fclose(f);
1193 return r;
1194 }
1195
1196 /*
1197 * Count lines in file
1198 */
1199 static int
file_line_count(const char * file)1200 file_line_count(const char *file)
1201 {
1202 int c;
1203 int l = 0;
1204 FILE *f = fopen(file, "r");
1205
1206 if (!f)
1207 {
1208 fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
1209 progname, file, strerror(errno));
1210 return -1;
1211 }
1212 while ((c = fgetc(f)) != EOF)
1213 {
1214 if (c == '\n')
1215 l++;
1216 }
1217 fclose(f);
1218 return l;
1219 }
1220
1221 bool
file_exists(const char * file)1222 file_exists(const char *file)
1223 {
1224 FILE *f = fopen(file, "r");
1225
1226 if (!f)
1227 return false;
1228 fclose(f);
1229 return true;
1230 }
1231
1232 static bool
directory_exists(const char * dir)1233 directory_exists(const char *dir)
1234 {
1235 struct stat st;
1236
1237 if (stat(dir, &st) != 0)
1238 return false;
1239 if (S_ISDIR(st.st_mode))
1240 return true;
1241 return false;
1242 }
1243
1244 /* Create a directory */
1245 static void
make_directory(const char * dir)1246 make_directory(const char *dir)
1247 {
1248 if (mkdir(dir, S_IRWXU | S_IRWXG | S_IRWXO) < 0)
1249 {
1250 fprintf(stderr, _("%s: could not create directory \"%s\": %s\n"),
1251 progname, dir, strerror(errno));
1252 exit(2);
1253 }
1254 }
1255
1256 /*
1257 * In: filename.ext, Return: filename_i.ext, where 0 < i <= 9
1258 */
1259 static char *
get_alternative_expectfile(const char * expectfile,int i)1260 get_alternative_expectfile(const char *expectfile, int i)
1261 {
1262 char *last_dot;
1263 int ssize = strlen(expectfile) + 2 + 1;
1264 char *tmp;
1265 char *s;
1266
1267 if (!(tmp = (char *) malloc(ssize)))
1268 return NULL;
1269
1270 if (!(s = (char *) malloc(ssize)))
1271 {
1272 free(tmp);
1273 return NULL;
1274 }
1275
1276 strcpy(tmp, expectfile);
1277 last_dot = strrchr(tmp, '.');
1278 if (!last_dot)
1279 {
1280 free(tmp);
1281 free(s);
1282 return NULL;
1283 }
1284 *last_dot = '\0';
1285 snprintf(s, ssize, "%s_%d.%s", tmp, i, last_dot + 1);
1286 free(tmp);
1287 return s;
1288 }
1289
1290 /*
1291 * Run a "diff" command and also check that it didn't crash
1292 */
1293 static int
run_diff(const char * cmd,const char * filename)1294 run_diff(const char *cmd, const char *filename)
1295 {
1296 int r;
1297
1298 r = system(cmd);
1299 if (!WIFEXITED(r) || WEXITSTATUS(r) > 1)
1300 {
1301 fprintf(stderr, _("diff command failed with status %d: %s\n"), r, cmd);
1302 exit(2);
1303 }
1304 #ifdef WIN32
1305
1306 /*
1307 * On WIN32, if the 'diff' command cannot be found, system() returns 1,
1308 * but produces nothing to stdout, so we check for that here.
1309 */
1310 if (WEXITSTATUS(r) == 1 && file_size(filename) <= 0)
1311 {
1312 fprintf(stderr, _("diff command not found: %s\n"), cmd);
1313 exit(2);
1314 }
1315 #endif
1316
1317 return WEXITSTATUS(r);
1318 }
1319
1320 /*
1321 * Check the actual result file for the given test against expected results
1322 *
1323 * Returns true if different (failure), false if correct match found.
1324 * In the true case, the diff is appended to the diffs file.
1325 */
1326 static bool
results_differ(const char * testname,const char * resultsfile,const char * default_expectfile)1327 results_differ(const char *testname, const char *resultsfile, const char *default_expectfile)
1328 {
1329 char expectfile[MAXPGPATH];
1330 char diff[MAXPGPATH];
1331 char cmd[MAXPGPATH * 3];
1332 char best_expect_file[MAXPGPATH];
1333 FILE *difffile;
1334 int best_line_count;
1335 int i;
1336 int l;
1337 const char *platform_expectfile;
1338
1339 /*
1340 * We can pass either the resultsfile or the expectfile, they should have
1341 * the same type (filename.type) anyway.
1342 */
1343 platform_expectfile = get_expectfile(testname, resultsfile);
1344
1345 strlcpy(expectfile, default_expectfile, sizeof(expectfile));
1346 if (platform_expectfile)
1347 {
1348 /*
1349 * Replace everything after the last slash in expectfile with what the
1350 * platform_expectfile contains.
1351 */
1352 char *p = strrchr(expectfile, '/');
1353
1354 if (p)
1355 strcpy(++p, platform_expectfile);
1356 }
1357
1358 /* Name to use for temporary diff file */
1359 snprintf(diff, sizeof(diff), "%s.diff", resultsfile);
1360
1361 /* OK, run the diff */
1362 snprintf(cmd, sizeof(cmd),
1363 "diff %s \"%s\" \"%s\" > \"%s\"",
1364 basic_diff_opts, expectfile, resultsfile, diff);
1365
1366 /* Is the diff file empty? */
1367 if (run_diff(cmd, diff) == 0)
1368 {
1369 unlink(diff);
1370 return false;
1371 }
1372
1373 /* There may be secondary comparison files that match better */
1374 best_line_count = file_line_count(diff);
1375 strcpy(best_expect_file, expectfile);
1376
1377 for (i = 0; i <= 9; i++)
1378 {
1379 char *alt_expectfile;
1380
1381 alt_expectfile = get_alternative_expectfile(expectfile, i);
1382 if (!alt_expectfile)
1383 {
1384 fprintf(stderr, _("Unable to check secondary comparison files: %s\n"),
1385 strerror(errno));
1386 exit(2);
1387 }
1388
1389 if (!file_exists(alt_expectfile))
1390 {
1391 free(alt_expectfile);
1392 continue;
1393 }
1394
1395 snprintf(cmd, sizeof(cmd),
1396 "diff %s \"%s\" \"%s\" > \"%s\"",
1397 basic_diff_opts, alt_expectfile, resultsfile, diff);
1398
1399 if (run_diff(cmd, diff) == 0)
1400 {
1401 unlink(diff);
1402 free(alt_expectfile);
1403 return false;
1404 }
1405
1406 l = file_line_count(diff);
1407 if (l < best_line_count)
1408 {
1409 /* This diff was a better match than the last one */
1410 best_line_count = l;
1411 strlcpy(best_expect_file, alt_expectfile, sizeof(best_expect_file));
1412 }
1413 free(alt_expectfile);
1414 }
1415
1416 /*
1417 * fall back on the canonical results file if we haven't tried it yet and
1418 * haven't found a complete match yet.
1419 */
1420
1421 if (platform_expectfile)
1422 {
1423 snprintf(cmd, sizeof(cmd),
1424 "diff %s \"%s\" \"%s\" > \"%s\"",
1425 basic_diff_opts, default_expectfile, resultsfile, diff);
1426
1427 if (run_diff(cmd, diff) == 0)
1428 {
1429 /* No diff = no changes = good */
1430 unlink(diff);
1431 return false;
1432 }
1433
1434 l = file_line_count(diff);
1435 if (l < best_line_count)
1436 {
1437 /* This diff was a better match than the last one */
1438 best_line_count = l;
1439 strlcpy(best_expect_file, default_expectfile, sizeof(best_expect_file));
1440 }
1441 }
1442
1443 /*
1444 * Use the best comparison file to generate the "pretty" diff, which we
1445 * append to the diffs summary file.
1446 */
1447 snprintf(cmd, sizeof(cmd),
1448 "diff %s \"%s\" \"%s\" >> \"%s\"",
1449 pretty_diff_opts, best_expect_file, resultsfile, difffilename);
1450 run_diff(cmd, difffilename);
1451
1452 /* And append a separator */
1453 difffile = fopen(difffilename, "a");
1454 if (difffile)
1455 {
1456 fprintf(difffile,
1457 "\n======================================================================\n\n");
1458 fclose(difffile);
1459 }
1460
1461 unlink(diff);
1462 return true;
1463 }
1464
1465 /*
1466 * Wait for specified subprocesses to finish, and return their exit
1467 * statuses into statuses[]
1468 *
1469 * If names isn't NULL, print each subprocess's name as it finishes
1470 *
1471 * Note: it's OK to scribble on the pids array, but not on the names array
1472 */
1473 static void
wait_for_tests(PID_TYPE * pids,int * statuses,char ** names,int num_tests)1474 wait_for_tests(PID_TYPE * pids, int *statuses, char **names, int num_tests)
1475 {
1476 int tests_left;
1477 int i;
1478
1479 #ifdef WIN32
1480 PID_TYPE *active_pids = malloc(num_tests * sizeof(PID_TYPE));
1481
1482 memcpy(active_pids, pids, num_tests * sizeof(PID_TYPE));
1483 #endif
1484
1485 tests_left = num_tests;
1486 while (tests_left > 0)
1487 {
1488 PID_TYPE p;
1489
1490 #ifndef WIN32
1491 int exit_status;
1492
1493 p = wait(&exit_status);
1494
1495 if (p == INVALID_PID)
1496 {
1497 fprintf(stderr, _("failed to wait for subprocesses: %s\n"),
1498 strerror(errno));
1499 exit(2);
1500 }
1501 #else
1502 DWORD exit_status;
1503 int r;
1504
1505 r = WaitForMultipleObjects(tests_left, active_pids, FALSE, INFINITE);
1506 if (r < WAIT_OBJECT_0 || r >= WAIT_OBJECT_0 + tests_left)
1507 {
1508 fprintf(stderr, _("failed to wait for subprocesses: error code %lu\n"),
1509 GetLastError());
1510 exit(2);
1511 }
1512 p = active_pids[r - WAIT_OBJECT_0];
1513 /* compact the active_pids array */
1514 active_pids[r - WAIT_OBJECT_0] = active_pids[tests_left - 1];
1515 #endif /* WIN32 */
1516
1517 for (i = 0; i < num_tests; i++)
1518 {
1519 if (p == pids[i])
1520 {
1521 #ifdef WIN32
1522 GetExitCodeProcess(pids[i], &exit_status);
1523 CloseHandle(pids[i]);
1524 #endif
1525 pids[i] = INVALID_PID;
1526 statuses[i] = (int) exit_status;
1527 if (names)
1528 status(" %s", names[i]);
1529 tests_left--;
1530 break;
1531 }
1532 }
1533 }
1534
1535 #ifdef WIN32
1536 free(active_pids);
1537 #endif
1538 }
1539
1540 /*
1541 * report nonzero exit code from a test process
1542 */
1543 static void
log_child_failure(int exitstatus)1544 log_child_failure(int exitstatus)
1545 {
1546 if (WIFEXITED(exitstatus))
1547 status(_(" (test process exited with exit code %d)"),
1548 WEXITSTATUS(exitstatus));
1549 else if (WIFSIGNALED(exitstatus))
1550 {
1551 #if defined(WIN32)
1552 status(_(" (test process was terminated by exception 0x%X)"),
1553 WTERMSIG(exitstatus));
1554 #else
1555 status(_(" (test process was terminated by signal %d: %s)"),
1556 WTERMSIG(exitstatus), pg_strsignal(WTERMSIG(exitstatus)));
1557 #endif
1558 }
1559 else
1560 status(_(" (test process exited with unrecognized status %d)"),
1561 exitstatus);
1562 }
1563
1564 /*
1565 * Run all the tests specified in one schedule file
1566 */
1567 static void
run_schedule(const char * schedule,test_function tfunc)1568 run_schedule(const char *schedule, test_function tfunc)
1569 {
1570 #define MAX_PARALLEL_TESTS 100
1571 char *tests[MAX_PARALLEL_TESTS];
1572 _stringlist *resultfiles[MAX_PARALLEL_TESTS];
1573 _stringlist *expectfiles[MAX_PARALLEL_TESTS];
1574 _stringlist *tags[MAX_PARALLEL_TESTS];
1575 PID_TYPE pids[MAX_PARALLEL_TESTS];
1576 int statuses[MAX_PARALLEL_TESTS];
1577 _stringlist *ignorelist = NULL;
1578 char scbuf[1024];
1579 FILE *scf;
1580 int line_num = 0;
1581
1582 memset(resultfiles, 0, sizeof(_stringlist *) * MAX_PARALLEL_TESTS);
1583 memset(expectfiles, 0, sizeof(_stringlist *) * MAX_PARALLEL_TESTS);
1584 memset(tags, 0, sizeof(_stringlist *) * MAX_PARALLEL_TESTS);
1585
1586 scf = fopen(schedule, "r");
1587 if (!scf)
1588 {
1589 fprintf(stderr, _("%s: could not open file \"%s\" for reading: %s\n"),
1590 progname, schedule, strerror(errno));
1591 exit(2);
1592 }
1593
1594 while (fgets(scbuf, sizeof(scbuf), scf))
1595 {
1596 char *test = NULL;
1597 char *c;
1598 int num_tests;
1599 bool inword;
1600 int i;
1601
1602 line_num++;
1603
1604 for (i = 0; i < MAX_PARALLEL_TESTS; i++)
1605 {
1606 if (resultfiles[i] == NULL)
1607 break;
1608 free_stringlist(&resultfiles[i]);
1609 free_stringlist(&expectfiles[i]);
1610 free_stringlist(&tags[i]);
1611 }
1612
1613 /* strip trailing whitespace, especially the newline */
1614 i = strlen(scbuf);
1615 while (i > 0 && isspace((unsigned char) scbuf[i - 1]))
1616 scbuf[--i] = '\0';
1617
1618 if (scbuf[0] == '\0' || scbuf[0] == '#')
1619 continue;
1620 if (strncmp(scbuf, "test: ", 6) == 0)
1621 test = scbuf + 6;
1622 else if (strncmp(scbuf, "ignore: ", 8) == 0)
1623 {
1624 c = scbuf + 8;
1625 while (*c && isspace((unsigned char) *c))
1626 c++;
1627 add_stringlist_item(&ignorelist, c);
1628
1629 /*
1630 * Note: ignore: lines do not run the test, they just say that
1631 * failure of this test when run later on is to be ignored. A bit
1632 * odd but that's how the shell-script version did it.
1633 */
1634 continue;
1635 }
1636 else
1637 {
1638 fprintf(stderr, _("syntax error in schedule file \"%s\" line %d: %s\n"),
1639 schedule, line_num, scbuf);
1640 exit(2);
1641 }
1642
1643 num_tests = 0;
1644 inword = false;
1645 for (c = test; *c; c++)
1646 {
1647 if (isspace((unsigned char) *c))
1648 {
1649 *c = '\0';
1650 inword = false;
1651 }
1652 else if (!inword)
1653 {
1654 if (num_tests >= MAX_PARALLEL_TESTS)
1655 {
1656 /* can't print scbuf here, it's already been trashed */
1657 fprintf(stderr, _("too many parallel tests in schedule file \"%s\", line %d\n"),
1658 schedule, line_num);
1659 exit(2);
1660 }
1661 tests[num_tests] = c;
1662 num_tests++;
1663 inword = true;
1664 }
1665 }
1666
1667 if (num_tests == 0)
1668 {
1669 fprintf(stderr, _("syntax error in schedule file \"%s\" line %d: %s\n"),
1670 schedule, line_num, scbuf);
1671 exit(2);
1672 }
1673
1674 if (num_tests == 1)
1675 {
1676 status(_("test %-24s ... "), tests[0]);
1677 pids[0] = (tfunc) (tests[0], &resultfiles[0], &expectfiles[0], &tags[0]);
1678 wait_for_tests(pids, statuses, NULL, 1);
1679 /* status line is finished below */
1680 }
1681 else if (max_connections > 0 && max_connections < num_tests)
1682 {
1683 int oldest = 0;
1684
1685 status(_("parallel group (%d tests, in groups of %d): "),
1686 num_tests, max_connections);
1687 for (i = 0; i < num_tests; i++)
1688 {
1689 if (i - oldest >= max_connections)
1690 {
1691 wait_for_tests(pids + oldest, statuses + oldest,
1692 tests + oldest, i - oldest);
1693 oldest = i;
1694 }
1695 pids[i] = (tfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
1696 }
1697 wait_for_tests(pids + oldest, statuses + oldest,
1698 tests + oldest, i - oldest);
1699 status_end();
1700 }
1701 else
1702 {
1703 status(_("parallel group (%d tests): "), num_tests);
1704 for (i = 0; i < num_tests; i++)
1705 {
1706 pids[i] = (tfunc) (tests[i], &resultfiles[i], &expectfiles[i], &tags[i]);
1707 }
1708 wait_for_tests(pids, statuses, tests, num_tests);
1709 status_end();
1710 }
1711
1712 /* Check results for all tests */
1713 for (i = 0; i < num_tests; i++)
1714 {
1715 _stringlist *rl,
1716 *el,
1717 *tl;
1718 bool differ = false;
1719
1720 if (num_tests > 1)
1721 status(_(" %-24s ... "), tests[i]);
1722
1723 /*
1724 * Advance over all three lists simultaneously.
1725 *
1726 * Compare resultfiles[j] with expectfiles[j] always. Tags are
1727 * optional but if there are tags, the tag list has the same
1728 * length as the other two lists.
1729 */
1730 for (rl = resultfiles[i], el = expectfiles[i], tl = tags[i];
1731 rl != NULL; /* rl and el have the same length */
1732 rl = rl->next, el = el->next,
1733 tl = tl ? tl->next : NULL)
1734 {
1735 bool newdiff;
1736
1737 newdiff = results_differ(tests[i], rl->str, el->str);
1738 if (newdiff && tl)
1739 {
1740 printf("%s ", tl->str);
1741 }
1742 differ |= newdiff;
1743 }
1744
1745 if (differ)
1746 {
1747 bool ignore = false;
1748 _stringlist *sl;
1749
1750 for (sl = ignorelist; sl != NULL; sl = sl->next)
1751 {
1752 if (strcmp(tests[i], sl->str) == 0)
1753 {
1754 ignore = true;
1755 break;
1756 }
1757 }
1758 if (ignore)
1759 {
1760 status(_("failed (ignored)"));
1761 fail_ignore_count++;
1762 }
1763 else
1764 {
1765 status(_("FAILED"));
1766 fail_count++;
1767 }
1768 }
1769 else
1770 {
1771 status(_("ok"));
1772 success_count++;
1773 }
1774
1775 if (statuses[i] != 0)
1776 log_child_failure(statuses[i]);
1777
1778 status_end();
1779 }
1780 }
1781
1782 free_stringlist(&ignorelist);
1783
1784 fclose(scf);
1785 }
1786
1787 /*
1788 * Run a single test
1789 */
1790 static void
run_single_test(const char * test,test_function tfunc)1791 run_single_test(const char *test, test_function tfunc)
1792 {
1793 PID_TYPE pid;
1794 int exit_status;
1795 _stringlist *resultfiles = NULL;
1796 _stringlist *expectfiles = NULL;
1797 _stringlist *tags = NULL;
1798 _stringlist *rl,
1799 *el,
1800 *tl;
1801 bool differ = false;
1802
1803 status(_("test %-24s ... "), test);
1804 pid = (tfunc) (test, &resultfiles, &expectfiles, &tags);
1805 wait_for_tests(&pid, &exit_status, NULL, 1);
1806
1807 /*
1808 * Advance over all three lists simultaneously.
1809 *
1810 * Compare resultfiles[j] with expectfiles[j] always. Tags are optional
1811 * but if there are tags, the tag list has the same length as the other
1812 * two lists.
1813 */
1814 for (rl = resultfiles, el = expectfiles, tl = tags;
1815 rl != NULL; /* rl and el have the same length */
1816 rl = rl->next, el = el->next,
1817 tl = tl ? tl->next : NULL)
1818 {
1819 bool newdiff;
1820
1821 newdiff = results_differ(test, rl->str, el->str);
1822 if (newdiff && tl)
1823 {
1824 printf("%s ", tl->str);
1825 }
1826 differ |= newdiff;
1827 }
1828
1829 if (differ)
1830 {
1831 status(_("FAILED"));
1832 fail_count++;
1833 }
1834 else
1835 {
1836 status(_("ok"));
1837 success_count++;
1838 }
1839
1840 if (exit_status != 0)
1841 log_child_failure(exit_status);
1842
1843 status_end();
1844 }
1845
1846 /*
1847 * Create the summary-output files (making them empty if already existing)
1848 */
1849 static void
open_result_files(void)1850 open_result_files(void)
1851 {
1852 char file[MAXPGPATH];
1853 FILE *difffile;
1854
1855 /* create the log file (copy of running status output) */
1856 snprintf(file, sizeof(file), "%s/regression.out", outputdir);
1857 logfilename = strdup(file);
1858 logfile = fopen(logfilename, "w");
1859 if (!logfile)
1860 {
1861 fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
1862 progname, logfilename, strerror(errno));
1863 exit(2);
1864 }
1865
1866 /* create the diffs file as empty */
1867 snprintf(file, sizeof(file), "%s/regression.diffs", outputdir);
1868 difffilename = strdup(file);
1869 difffile = fopen(difffilename, "w");
1870 if (!difffile)
1871 {
1872 fprintf(stderr, _("%s: could not open file \"%s\" for writing: %s\n"),
1873 progname, difffilename, strerror(errno));
1874 exit(2);
1875 }
1876 /* we don't keep the diffs file open continuously */
1877 fclose(difffile);
1878
1879 /* also create the output directory if not present */
1880 snprintf(file, sizeof(file), "%s/results", outputdir);
1881 if (!directory_exists(file))
1882 make_directory(file);
1883 }
1884
1885 static void
drop_database_if_exists(const char * dbname)1886 drop_database_if_exists(const char *dbname)
1887 {
1888 header(_("dropping database \"%s\""), dbname);
1889 psql_command("postgres", "DROP DATABASE IF EXISTS \"%s\"", dbname);
1890 }
1891
1892 static void
create_database(const char * dbname)1893 create_database(const char *dbname)
1894 {
1895 _stringlist *sl;
1896
1897 /*
1898 * We use template0 so that any installation-local cruft in template1 will
1899 * not mess up the tests.
1900 */
1901 header(_("creating database \"%s\""), dbname);
1902 if (encoding)
1903 psql_command("postgres", "CREATE DATABASE \"%s\" TEMPLATE=template0 ENCODING='%s'%s", dbname, encoding,
1904 (nolocale) ? " LC_COLLATE='C' LC_CTYPE='C'" : "");
1905 else
1906 psql_command("postgres", "CREATE DATABASE \"%s\" TEMPLATE=template0%s", dbname,
1907 (nolocale) ? " LC_COLLATE='C' LC_CTYPE='C'" : "");
1908 psql_command(dbname,
1909 "ALTER DATABASE \"%s\" SET lc_messages TO 'C';"
1910 "ALTER DATABASE \"%s\" SET lc_monetary TO 'C';"
1911 "ALTER DATABASE \"%s\" SET lc_numeric TO 'C';"
1912 "ALTER DATABASE \"%s\" SET lc_time TO 'C';"
1913 "ALTER DATABASE \"%s\" SET timezone_abbreviations TO 'Default';",
1914 dbname, dbname, dbname, dbname, dbname);
1915
1916 /*
1917 * Install any requested procedural languages. We use CREATE OR REPLACE
1918 * so that this will work whether or not the language is preinstalled.
1919 */
1920 for (sl = loadlanguage; sl != NULL; sl = sl->next)
1921 {
1922 header(_("installing %s"), sl->str);
1923 psql_command(dbname, "CREATE OR REPLACE LANGUAGE \"%s\"", sl->str);
1924 }
1925
1926 /*
1927 * Install any requested extensions. We use CREATE IF NOT EXISTS so that
1928 * this will work whether or not the extension is preinstalled.
1929 */
1930 for (sl = loadextension; sl != NULL; sl = sl->next)
1931 {
1932 header(_("installing %s"), sl->str);
1933 psql_command(dbname, "CREATE EXTENSION IF NOT EXISTS \"%s\"", sl->str);
1934 }
1935 }
1936
1937 static void
drop_role_if_exists(const char * rolename)1938 drop_role_if_exists(const char *rolename)
1939 {
1940 header(_("dropping role \"%s\""), rolename);
1941 psql_command("postgres", "DROP ROLE IF EXISTS \"%s\"", rolename);
1942 }
1943
1944 static void
create_role(const char * rolename,const _stringlist * granted_dbs)1945 create_role(const char *rolename, const _stringlist *granted_dbs)
1946 {
1947 header(_("creating role \"%s\""), rolename);
1948 psql_command("postgres", "CREATE ROLE \"%s\" WITH LOGIN", rolename);
1949 for (; granted_dbs != NULL; granted_dbs = granted_dbs->next)
1950 {
1951 psql_command("postgres", "GRANT ALL ON DATABASE \"%s\" TO \"%s\"",
1952 granted_dbs->str, rolename);
1953 }
1954 }
1955
1956 static void
help(void)1957 help(void)
1958 {
1959 printf(_("PostgreSQL regression test driver\n"));
1960 printf(_("\n"));
1961 printf(_("Usage:\n %s [OPTION]... [EXTRA-TEST]...\n"), progname);
1962 printf(_("\n"));
1963 printf(_("Options:\n"));
1964 printf(_(" --config-auth=DATADIR update authentication settings for DATADIR\n"));
1965 printf(_(" --create-role=ROLE create the specified role before testing\n"));
1966 printf(_(" --dbname=DB use database DB (default \"regression\")\n"));
1967 printf(_(" --debug turn on debug mode in programs that are run\n"));
1968 printf(_(" --dlpath=DIR look for dynamic libraries in DIR\n"));
1969 printf(_(" --encoding=ENCODING use ENCODING as the encoding\n"));
1970 printf(_(" --inputdir=DIR take input files from DIR (default \".\")\n"));
1971 printf(_(" --launcher=CMD use CMD as launcher of psql\n"));
1972 printf(_(" --load-extension=EXT load the named extension before running the\n"));
1973 printf(_(" tests; can appear multiple times\n"));
1974 printf(_(" --load-language=LANG load the named language before running the\n"));
1975 printf(_(" tests; can appear multiple times\n"));
1976 printf(_(" --max-connections=N maximum number of concurrent connections\n"));
1977 printf(_(" (default is 0, meaning unlimited)\n"));
1978 printf(_(" --outputdir=DIR place output files in DIR (default \".\")\n"));
1979 printf(_(" --schedule=FILE use test ordering schedule from FILE\n"));
1980 printf(_(" (can be used multiple times to concatenate)\n"));
1981 printf(_(" --temp-instance=DIR create a temporary instance in DIR\n"));
1982 printf(_(" --use-existing use an existing installation\n"));
1983 printf(_("\n"));
1984 printf(_("Options for \"temp-instance\" mode:\n"));
1985 printf(_(" --no-locale use C locale\n"));
1986 printf(_(" --port=PORT start postmaster on PORT\n"));
1987 printf(_(" --temp-config=FILE append contents of FILE to temporary config\n"));
1988 printf(_("\n"));
1989 printf(_("Options for using an existing installation:\n"));
1990 printf(_(" --host=HOST use postmaster running on HOST\n"));
1991 printf(_(" --port=PORT use postmaster running at PORT\n"));
1992 printf(_(" --user=USER connect as USER\n"));
1993 printf(_("\n"));
1994 printf(_("The exit status is 0 if all tests passed, 1 if some tests failed, and 2\n"));
1995 printf(_("if the tests could not be run for some reason.\n"));
1996 printf(_("\n"));
1997 printf(_("Report bugs to <pgsql-bugs@postgresql.org>.\n"));
1998 }
1999
2000 int
regression_main(int argc,char * argv[],init_function ifunc,test_function tfunc)2001 regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc)
2002 {
2003 static struct option long_options[] = {
2004 {"help", no_argument, NULL, 'h'},
2005 {"version", no_argument, NULL, 'V'},
2006 {"dbname", required_argument, NULL, 1},
2007 {"debug", no_argument, NULL, 2},
2008 {"inputdir", required_argument, NULL, 3},
2009 {"load-language", required_argument, NULL, 4},
2010 {"max-connections", required_argument, NULL, 5},
2011 {"encoding", required_argument, NULL, 6},
2012 {"outputdir", required_argument, NULL, 7},
2013 {"schedule", required_argument, NULL, 8},
2014 {"temp-instance", required_argument, NULL, 9},
2015 {"no-locale", no_argument, NULL, 10},
2016 {"host", required_argument, NULL, 13},
2017 {"port", required_argument, NULL, 14},
2018 {"user", required_argument, NULL, 15},
2019 {"bindir", required_argument, NULL, 16},
2020 {"dlpath", required_argument, NULL, 17},
2021 {"create-role", required_argument, NULL, 18},
2022 {"temp-config", required_argument, NULL, 19},
2023 {"use-existing", no_argument, NULL, 20},
2024 {"launcher", required_argument, NULL, 21},
2025 {"load-extension", required_argument, NULL, 22},
2026 {"config-auth", required_argument, NULL, 24},
2027 {NULL, 0, NULL, 0}
2028 };
2029
2030 _stringlist *sl;
2031 int c;
2032 int i;
2033 int option_index;
2034 char buf[MAXPGPATH * 4];
2035 char buf2[MAXPGPATH * 4];
2036
2037 progname = get_progname(argv[0]);
2038 set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_regress"));
2039
2040 get_restricted_token(progname);
2041
2042 atexit(stop_postmaster);
2043
2044 #ifndef HAVE_UNIX_SOCKETS
2045 /* no unix domain sockets available, so change default */
2046 hostname = "localhost";
2047 #endif
2048
2049 /*
2050 * We call the initialization function here because that way we can set
2051 * default parameters and let them be overwritten by the commandline.
2052 */
2053 ifunc(argc, argv);
2054
2055 if (getenv("PG_REGRESS_DIFF_OPTS"))
2056 pretty_diff_opts = getenv("PG_REGRESS_DIFF_OPTS");
2057
2058 while ((c = getopt_long(argc, argv, "hV", long_options, &option_index)) != -1)
2059 {
2060 switch (c)
2061 {
2062 case 'h':
2063 help();
2064 exit(0);
2065 case 'V':
2066 puts("pg_regress (PostgreSQL) " PG_VERSION);
2067 exit(0);
2068 case 1:
2069
2070 /*
2071 * If a default database was specified, we need to remove it
2072 * before we add the specified one.
2073 */
2074 free_stringlist(&dblist);
2075 split_to_stringlist(strdup(optarg), ", ", &dblist);
2076 break;
2077 case 2:
2078 debug = true;
2079 break;
2080 case 3:
2081 inputdir = strdup(optarg);
2082 break;
2083 case 4:
2084 add_stringlist_item(&loadlanguage, optarg);
2085 break;
2086 case 5:
2087 max_connections = atoi(optarg);
2088 break;
2089 case 6:
2090 encoding = strdup(optarg);
2091 break;
2092 case 7:
2093 outputdir = strdup(optarg);
2094 break;
2095 case 8:
2096 add_stringlist_item(&schedulelist, optarg);
2097 break;
2098 case 9:
2099 temp_instance = make_absolute_path(optarg);
2100 break;
2101 case 10:
2102 nolocale = true;
2103 break;
2104 case 13:
2105 hostname = strdup(optarg);
2106 break;
2107 case 14:
2108 port = atoi(optarg);
2109 port_specified_by_user = true;
2110 break;
2111 case 15:
2112 user = strdup(optarg);
2113 break;
2114 case 16:
2115 /* "--bindir=" means to use PATH */
2116 if (strlen(optarg))
2117 bindir = strdup(optarg);
2118 else
2119 bindir = NULL;
2120 break;
2121 case 17:
2122 dlpath = strdup(optarg);
2123 break;
2124 case 18:
2125 split_to_stringlist(strdup(optarg), ", ", &extraroles);
2126 break;
2127 case 19:
2128 add_stringlist_item(&temp_configs, optarg);
2129 break;
2130 case 20:
2131 use_existing = true;
2132 break;
2133 case 21:
2134 launcher = strdup(optarg);
2135 break;
2136 case 22:
2137 add_stringlist_item(&loadextension, optarg);
2138 break;
2139 case 24:
2140 config_auth_datadir = pstrdup(optarg);
2141 break;
2142 default:
2143 /* getopt_long already emitted a complaint */
2144 fprintf(stderr, _("\nTry \"%s -h\" for more information.\n"),
2145 progname);
2146 exit(2);
2147 }
2148 }
2149
2150 /*
2151 * if we still have arguments, they are extra tests to run
2152 */
2153 while (argc - optind >= 1)
2154 {
2155 add_stringlist_item(&extra_tests, argv[optind]);
2156 optind++;
2157 }
2158
2159 if (config_auth_datadir)
2160 {
2161 #ifdef ENABLE_SSPI
2162 config_sspi_auth(config_auth_datadir);
2163 #endif
2164 exit(0);
2165 }
2166
2167 if (temp_instance && !port_specified_by_user)
2168
2169 /*
2170 * To reduce chances of interference with parallel installations, use
2171 * a port number starting in the private range (49152-65535)
2172 * calculated from the version number. This aids !HAVE_UNIX_SOCKETS
2173 * systems; elsewhere, the use of a private socket directory already
2174 * prevents interference.
2175 */
2176 port = 0xC000 | (PG_VERSION_NUM & 0x3FFF);
2177
2178 inputdir = make_absolute_path(inputdir);
2179 outputdir = make_absolute_path(outputdir);
2180 dlpath = make_absolute_path(dlpath);
2181
2182 /*
2183 * Initialization
2184 */
2185 open_result_files();
2186
2187 initialize_environment();
2188
2189 #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE)
2190 unlimit_core_size();
2191 #endif
2192
2193 if (temp_instance)
2194 {
2195 FILE *pg_conf;
2196 const char *env_wait;
2197 int wait_seconds;
2198
2199 /*
2200 * Prepare the temp instance
2201 */
2202
2203 if (directory_exists(temp_instance))
2204 {
2205 header(_("removing existing temp instance"));
2206 if (!rmtree(temp_instance, true))
2207 {
2208 fprintf(stderr, _("\n%s: could not remove temp instance \"%s\"\n"),
2209 progname, temp_instance);
2210 exit(2);
2211 }
2212 }
2213
2214 header(_("creating temporary instance"));
2215
2216 /* make the temp instance top directory */
2217 make_directory(temp_instance);
2218
2219 /* and a directory for log files */
2220 snprintf(buf, sizeof(buf), "%s/log", outputdir);
2221 if (!directory_exists(buf))
2222 make_directory(buf);
2223
2224 /* initdb */
2225 header(_("initializing database system"));
2226 snprintf(buf, sizeof(buf),
2227 "\"%s%sinitdb\" -D \"%s/data\" --noclean --nosync%s%s > \"%s/log/initdb.log\" 2>&1",
2228 bindir ? bindir : "",
2229 bindir ? "/" : "",
2230 temp_instance,
2231 debug ? " --debug" : "",
2232 nolocale ? " --no-locale" : "",
2233 outputdir);
2234 if (system(buf))
2235 {
2236 fprintf(stderr, _("\n%s: initdb failed\nExamine %s/log/initdb.log for the reason.\nCommand was: %s\n"), progname, outputdir, buf);
2237 exit(2);
2238 }
2239
2240 /*
2241 * Adjust the default postgresql.conf for regression testing. The user
2242 * can specify a file to be appended; in any case we expand logging
2243 * and set max_prepared_transactions to enable testing of prepared
2244 * xacts. (Note: to reduce the probability of unexpected shmmax
2245 * failures, don't set max_prepared_transactions any higher than
2246 * actually needed by the prepared_xacts regression test.)
2247 */
2248 snprintf(buf, sizeof(buf), "%s/data/postgresql.conf", temp_instance);
2249 pg_conf = fopen(buf, "a");
2250 if (pg_conf == NULL)
2251 {
2252 fprintf(stderr, _("\n%s: could not open \"%s\" for adding extra config: %s\n"), progname, buf, strerror(errno));
2253 exit(2);
2254 }
2255 fputs("\n# Configuration added by pg_regress\n\n", pg_conf);
2256 fputs("log_autovacuum_min_duration = 0\n", pg_conf);
2257 fputs("log_checkpoints = on\n", pg_conf);
2258 fputs("log_lock_waits = on\n", pg_conf);
2259 fputs("log_temp_files = 128kB\n", pg_conf);
2260 fputs("max_prepared_transactions = 2\n", pg_conf);
2261
2262 for (sl = temp_configs; sl != NULL; sl = sl->next)
2263 {
2264 char *temp_config = sl->str;
2265 FILE *extra_conf;
2266 char line_buf[1024];
2267
2268 extra_conf = fopen(temp_config, "r");
2269 if (extra_conf == NULL)
2270 {
2271 fprintf(stderr, _("\n%s: could not open \"%s\" to read extra config: %s\n"), progname, temp_config, strerror(errno));
2272 exit(2);
2273 }
2274 while (fgets(line_buf, sizeof(line_buf), extra_conf) != NULL)
2275 fputs(line_buf, pg_conf);
2276 fclose(extra_conf);
2277 }
2278
2279 fclose(pg_conf);
2280
2281 #ifdef ENABLE_SSPI
2282
2283 /*
2284 * Since we successfully used the same buffer for the much-longer
2285 * "initdb" command, this can't truncate.
2286 */
2287 snprintf(buf, sizeof(buf), "%s/data", temp_instance);
2288 config_sspi_auth(buf);
2289 #elif !defined(HAVE_UNIX_SOCKETS)
2290 #error Platform has no means to secure the test installation.
2291 #endif
2292
2293 /*
2294 * Check if there is a postmaster running already.
2295 */
2296 snprintf(buf2, sizeof(buf2),
2297 "\"%s%spsql\" -X postgres <%s 2>%s",
2298 bindir ? bindir : "",
2299 bindir ? "/" : "",
2300 DEVNULL, DEVNULL);
2301
2302 for (i = 0; i < 16; i++)
2303 {
2304 if (system(buf2) == 0)
2305 {
2306 char s[16];
2307
2308 if (port_specified_by_user || i == 15)
2309 {
2310 fprintf(stderr, _("port %d apparently in use\n"), port);
2311 if (!port_specified_by_user)
2312 fprintf(stderr, _("%s: could not determine an available port\n"), progname);
2313 fprintf(stderr, _("Specify an unused port using the --port option or shut down any conflicting PostgreSQL servers.\n"));
2314 exit(2);
2315 }
2316
2317 fprintf(stderr, _("port %d apparently in use, trying %d\n"), port, port + 1);
2318 port++;
2319 sprintf(s, "%d", port);
2320 doputenv("PGPORT", s);
2321 }
2322 else
2323 break;
2324 }
2325
2326 /*
2327 * Start the temp postmaster
2328 */
2329 header(_("starting postmaster"));
2330 snprintf(buf, sizeof(buf),
2331 "\"%s%spostgres\" -D \"%s/data\" -F%s "
2332 "-c \"listen_addresses=%s\" -k \"%s\" "
2333 "> \"%s/log/postmaster.log\" 2>&1",
2334 bindir ? bindir : "",
2335 bindir ? "/" : "",
2336 temp_instance, debug ? " -d 5" : "",
2337 hostname ? hostname : "", sockdir ? sockdir : "",
2338 outputdir);
2339 postmaster_pid = spawn_process(buf);
2340 if (postmaster_pid == INVALID_PID)
2341 {
2342 fprintf(stderr, _("\n%s: could not spawn postmaster: %s\n"),
2343 progname, strerror(errno));
2344 exit(2);
2345 }
2346
2347 /*
2348 * Wait till postmaster is able to accept connections; normally this
2349 * is only a second or so, but Cygwin is reportedly *much* slower, and
2350 * test builds using Valgrind or similar tools might be too. Hence,
2351 * allow the default timeout of 60 seconds to be overridden from the
2352 * PGCTLTIMEOUT environment variable.
2353 */
2354 env_wait = getenv("PGCTLTIMEOUT");
2355 if (env_wait != NULL)
2356 {
2357 wait_seconds = atoi(env_wait);
2358 if (wait_seconds <= 0)
2359 wait_seconds = 60;
2360 }
2361 else
2362 wait_seconds = 60;
2363
2364 for (i = 0; i < wait_seconds; i++)
2365 {
2366 /* Done if psql succeeds */
2367 if (system(buf2) == 0)
2368 break;
2369
2370 /*
2371 * Fail immediately if postmaster has exited
2372 */
2373 #ifndef WIN32
2374 if (waitpid(postmaster_pid, NULL, WNOHANG) == postmaster_pid)
2375 #else
2376 if (WaitForSingleObject(postmaster_pid, 0) == WAIT_OBJECT_0)
2377 #endif
2378 {
2379 fprintf(stderr, _("\n%s: postmaster failed\nExamine %s/log/postmaster.log for the reason\n"), progname, outputdir);
2380 exit(2);
2381 }
2382
2383 pg_usleep(1000000L);
2384 }
2385 if (i >= wait_seconds)
2386 {
2387 fprintf(stderr, _("\n%s: postmaster did not respond within %d seconds\nExamine %s/log/postmaster.log for the reason\n"),
2388 progname, wait_seconds, outputdir);
2389
2390 /*
2391 * If we get here, the postmaster is probably wedged somewhere in
2392 * startup. Try to kill it ungracefully rather than leaving a
2393 * stuck postmaster that might interfere with subsequent test
2394 * attempts.
2395 */
2396 #ifndef WIN32
2397 if (kill(postmaster_pid, SIGKILL) != 0 &&
2398 errno != ESRCH)
2399 fprintf(stderr, _("\n%s: could not kill failed postmaster: %s\n"),
2400 progname, strerror(errno));
2401 #else
2402 if (TerminateProcess(postmaster_pid, 255) == 0)
2403 fprintf(stderr, _("\n%s: could not kill failed postmaster: error code %lu\n"),
2404 progname, GetLastError());
2405 #endif
2406
2407 exit(2);
2408 }
2409
2410 postmaster_running = true;
2411
2412 #ifdef _WIN64
2413 /* need a series of two casts to convert HANDLE without compiler warning */
2414 #define ULONGPID(x) (unsigned long) (unsigned long long) (x)
2415 #else
2416 #define ULONGPID(x) (unsigned long) (x)
2417 #endif
2418 printf(_("running on port %d with PID %lu\n"),
2419 port, ULONGPID(postmaster_pid));
2420 }
2421 else
2422 {
2423 /*
2424 * Using an existing installation, so may need to get rid of
2425 * pre-existing database(s) and role(s)
2426 */
2427 if (!use_existing)
2428 {
2429 for (sl = dblist; sl; sl = sl->next)
2430 drop_database_if_exists(sl->str);
2431 for (sl = extraroles; sl; sl = sl->next)
2432 drop_role_if_exists(sl->str);
2433 }
2434 }
2435
2436 /*
2437 * Create the test database(s) and role(s)
2438 */
2439 if (!use_existing)
2440 {
2441 for (sl = dblist; sl; sl = sl->next)
2442 create_database(sl->str);
2443 for (sl = extraroles; sl; sl = sl->next)
2444 create_role(sl->str, dblist);
2445 }
2446
2447 /*
2448 * Ready to run the tests
2449 */
2450 header(_("running regression test queries"));
2451
2452 for (sl = schedulelist; sl != NULL; sl = sl->next)
2453 {
2454 run_schedule(sl->str, tfunc);
2455 }
2456
2457 for (sl = extra_tests; sl != NULL; sl = sl->next)
2458 {
2459 run_single_test(sl->str, tfunc);
2460 }
2461
2462 /*
2463 * Shut down temp installation's postmaster
2464 */
2465 if (temp_instance)
2466 {
2467 header(_("shutting down postmaster"));
2468 stop_postmaster();
2469 }
2470
2471 /*
2472 * If there were no errors, remove the temp instance immediately to
2473 * conserve disk space. (If there were errors, we leave the instance in
2474 * place for possible manual investigation.)
2475 */
2476 if (temp_instance && fail_count == 0 && fail_ignore_count == 0)
2477 {
2478 header(_("removing temporary instance"));
2479 if (!rmtree(temp_instance, true))
2480 fprintf(stderr, _("\n%s: could not remove temp instance \"%s\"\n"),
2481 progname, temp_instance);
2482 }
2483
2484 fclose(logfile);
2485
2486 /*
2487 * Emit nice-looking summary message
2488 */
2489 if (fail_count == 0 && fail_ignore_count == 0)
2490 snprintf(buf, sizeof(buf),
2491 _(" All %d tests passed. "),
2492 success_count);
2493 else if (fail_count == 0) /* fail_count=0, fail_ignore_count>0 */
2494 snprintf(buf, sizeof(buf),
2495 _(" %d of %d tests passed, %d failed test(s) ignored. "),
2496 success_count,
2497 success_count + fail_ignore_count,
2498 fail_ignore_count);
2499 else if (fail_ignore_count == 0) /* fail_count>0 && fail_ignore_count=0 */
2500 snprintf(buf, sizeof(buf),
2501 _(" %d of %d tests failed. "),
2502 fail_count,
2503 success_count + fail_count);
2504 else
2505 /* fail_count>0 && fail_ignore_count>0 */
2506 snprintf(buf, sizeof(buf),
2507 _(" %d of %d tests failed, %d of these failures ignored. "),
2508 fail_count + fail_ignore_count,
2509 success_count + fail_count + fail_ignore_count,
2510 fail_ignore_count);
2511
2512 putchar('\n');
2513 for (i = strlen(buf); i > 0; i--)
2514 putchar('=');
2515 printf("\n%s\n", buf);
2516 for (i = strlen(buf); i > 0; i--)
2517 putchar('=');
2518 putchar('\n');
2519 putchar('\n');
2520
2521 if (file_size(difffilename) > 0)
2522 {
2523 printf(_("The differences that caused some tests to fail can be viewed in the\n"
2524 "file \"%s\". A copy of the test summary that you see\n"
2525 "above is saved in the file \"%s\".\n\n"),
2526 difffilename, logfilename);
2527 }
2528 else
2529 {
2530 unlink(difffilename);
2531 unlink(logfilename);
2532 }
2533
2534 if (fail_count != 0)
2535 exit(1);
2536
2537 return 0;
2538 }
2539