From e3d0f8741158b1b4520dbe753f8e7cd5b996c4fa Mon Sep 17 00:00:00 2001 From: Mark Dilger Date: Tue, 26 May 2020 14:22:00 -0700 Subject: [PATCH v1] Implementing new 'pg' consolidated metacommand This implements a new command to consolidate disparate postgres command line programs into one. --- contrib/sepgsql/test_sepgsql | 1 + contrib/start-scripts/freebsd | 2 +- contrib/start-scripts/linux | 2 +- .../start-scripts/macos/postgres-wrapper.sh | 4 +- doc/src/sgml/ref/pgupgrade.sgml | 22 +- src/Makefile.global.in | 10 +- src/backend/postmaster/postmaster.c | 7 +- src/backend/utils/init/globals.c | 1 + src/backend/utils/init/miscinit.c | 7 + src/backend/utils/misc/pg_config.c | 3 +- src/bin/Makefile | 1 + src/bin/initdb/Makefile | 6 +- src/bin/initdb/initdb.c | 4 +- src/bin/initdb/t/002_redirect.pl | 17 ++ src/bin/pg/.gitignore | 4 + src/bin/pg/Makefile | 48 +++++ src/bin/pg/pg.c | 194 ++++++++++++++++++ src/bin/pg/t/001_basic.pl | 31 +++ src/bin/pg_archivecleanup/Makefile | 6 +- src/bin/pg_archivecleanup/t/001_redirect.pl | 17 ++ .../t/010_pg_archivecleanup.pl | 10 +- src/bin/pg_basebackup/Makefile | 14 +- src/bin/pg_basebackup/t/001_redirect.pl | 17 ++ src/bin/pg_basebackup/t/010_pg_basebackup.pl | 72 +++---- src/bin/pg_basebackup/t/020_pg_receivewal.pl | 14 +- src/bin/pg_basebackup/t/030_pg_recvlogical.pl | 8 +- src/bin/pg_checksums/Makefile | 6 +- src/bin/pg_checksums/t/002_actions.pl | 38 ++-- src/bin/pg_checksums/t/003_redirect.pl | 17 ++ src/bin/pg_config/Makefile | 6 +- src/bin/pg_config/pg_config.c | 16 +- src/bin/pg_config/t/001_pg_config.pl | 12 +- src/bin/pg_controldata/Makefile | 6 +- .../pg_controldata/t/001_pg_controldata.pl | 8 +- src/bin/pg_controldata/t/002_redirect.pl | 17 ++ src/bin/pg_ctl/Makefile | 6 +- src/bin/pg_ctl/pg_ctl.c | 12 +- src/bin/pg_ctl/t/001_start_stop.pl | 22 +- src/bin/pg_ctl/t/002_status.pl | 10 +- src/bin/pg_ctl/t/003_promote.pl | 10 +- src/bin/pg_ctl/t/005_redirect.pl | 17 ++ src/bin/pg_dump/Makefile | 10 +- src/bin/pg_dump/pg_dumpall.c | 4 +- src/bin/pg_dump/t/004_redirect.pl | 17 ++ src/bin/pg_resetwal/Makefile | 6 +- src/bin/pg_resetwal/t/003_redirect.pl | 17 ++ src/bin/pg_rewind/Makefile | 6 +- src/bin/pg_rewind/pg_rewind.c | 12 +- src/bin/pg_rewind/t/007_redirect.pl | 17 ++ src/bin/pg_test_fsync/Makefile | 6 +- src/bin/pg_test_timing/Makefile | 6 +- src/bin/pg_upgrade/Makefile | 8 +- src/bin/pg_upgrade/TESTING | 2 + src/bin/pg_upgrade/check.c | 6 +- src/bin/pg_upgrade/controldata.c | 4 +- src/bin/pg_upgrade/dump.c | 8 +- src/bin/pg_upgrade/exec.c | 119 ++++++----- src/bin/pg_upgrade/option.c | 66 ++++-- src/bin/pg_upgrade/pg_upgrade.c | 73 ++++--- src/bin/pg_upgrade/pg_upgrade.h | 13 ++ src/bin/pg_upgrade/server.c | 8 +- src/bin/pg_upgrade/test.sh | 13 +- src/bin/pg_verifybackup/Makefile | 6 +- src/bin/pg_verifybackup/pg_verifybackup.c | 6 +- src/bin/pg_verifybackup/t/008_redirect.pl | 17 ++ src/bin/pg_waldump/Makefile | 6 +- src/bin/pg_waldump/t/002_redirect.pl | 17 ++ src/bin/pgbench/Makefile | 6 +- src/bin/pgbench/t/003_redirect.pl | 17 ++ src/bin/psql/startup.c | 9 +- src/bin/scripts/Makefile | 20 +- src/common/.gitignore | 1 + src/common/Makefile | 8 +- src/common/config_info.c | 28 ++- src/common/exec.c | 143 ++++++++++++- src/common/mk_exec_relpath.pl | 85 ++++++++ src/include/common/config_info.h | 1 + src/include/miscadmin.h | 1 + src/include/port.h | 20 +- src/interfaces/ecpg/test/Makefile | 10 +- src/port/Makefile | 1 + src/port/path.c | 103 ++++++++-- src/test/isolation/isolation_main.c | 8 +- src/test/regress/pg_regress.c | 39 ++-- src/tools/msvc/Solution.pm | 1 + src/tools/msvc/vcregress.pl | 2 + 86 files changed, 1336 insertions(+), 365 deletions(-) create mode 100644 src/bin/initdb/t/002_redirect.pl create mode 100644 src/bin/pg/.gitignore create mode 100644 src/bin/pg/Makefile create mode 100644 src/bin/pg/pg.c create mode 100644 src/bin/pg/t/001_basic.pl create mode 100644 src/bin/pg_archivecleanup/t/001_redirect.pl create mode 100644 src/bin/pg_basebackup/t/001_redirect.pl create mode 100644 src/bin/pg_checksums/t/003_redirect.pl create mode 100644 src/bin/pg_controldata/t/002_redirect.pl create mode 100644 src/bin/pg_ctl/t/005_redirect.pl create mode 100644 src/bin/pg_dump/t/004_redirect.pl create mode 100644 src/bin/pg_resetwal/t/003_redirect.pl create mode 100644 src/bin/pg_rewind/t/007_redirect.pl create mode 100644 src/bin/pg_verifybackup/t/008_redirect.pl create mode 100644 src/bin/pg_waldump/t/002_redirect.pl create mode 100644 src/bin/pgbench/t/003_redirect.pl create mode 100755 src/common/mk_exec_relpath.pl diff --git a/contrib/sepgsql/test_sepgsql b/contrib/sepgsql/test_sepgsql index 3a29556d1f..b2dc566994 100755 --- a/contrib/sepgsql/test_sepgsql +++ b/contrib/sepgsql/test_sepgsql @@ -13,6 +13,7 @@ # PG_BINDIR=`pg_config --bindir` +PG_LIBEXECDIR=`pg_config --libexecdir` # we must move to contrib/sepgsql directory to run pg_regress correctly cd `dirname $0` diff --git a/contrib/start-scripts/freebsd b/contrib/start-scripts/freebsd index 3323237a54..febb8862e9 100644 --- a/contrib/start-scripts/freebsd +++ b/contrib/start-scripts/freebsd @@ -32,7 +32,7 @@ PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin DAEMON="$prefix/bin/postmaster" # What to use to shut down the postmaster -PGCTL="$prefix/bin/pg_ctl" +PGCTL="$prefix/libexec/pg_ctl" # Only start if we can find the postmaster. test -x $DAEMON || diff --git a/contrib/start-scripts/linux b/contrib/start-scripts/linux index a7757162fc..9425ba2453 100644 --- a/contrib/start-scripts/linux +++ b/contrib/start-scripts/linux @@ -64,7 +64,7 @@ PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin DAEMON="$prefix/bin/postmaster" # What to use to shut down the postmaster -PGCTL="$prefix/bin/pg_ctl" +PGCTL="$prefix/libexec/pg_ctl" set -e diff --git a/contrib/start-scripts/macos/postgres-wrapper.sh b/contrib/start-scripts/macos/postgres-wrapper.sh index 3a4ebdaf0f..2be058480e 100644 --- a/contrib/start-scripts/macos/postgres-wrapper.sh +++ b/contrib/start-scripts/macos/postgres-wrapper.sh @@ -4,8 +4,10 @@ # edit these as needed: -# directory containing postgres executable: +# directory containing postgres user executable: PGBINDIR="/usr/local/pgsql/bin" +# directory containing postgres program executables: +PGLIBEXECDIR="/usr/local/pgsql/libexec" # data directory: PGDATA="/usr/local/pgsql/data" # file to receive postmaster's initial log messages: diff --git a/doc/src/sgml/ref/pgupgrade.sgml b/doc/src/sgml/ref/pgupgrade.sgml index 905167690b..5c2a6fa242 100644 --- a/doc/src/sgml/ref/pgupgrade.sgml +++ b/doc/src/sgml/ref/pgupgrade.sgml @@ -26,6 +26,10 @@ PostgreSQL documentation oldbindir newbindir + + oldlibexecdir + + newlibexecdir oldconfigdir @@ -92,7 +96,6 @@ PostgreSQL documentation bindir bindir the new PostgreSQL executable directory; - default is the directory where pg_upgrade resides; environment variable PGBINNEW @@ -130,6 +133,21 @@ PostgreSQL documentation cluster + + libexecdir + libexecdir + the old PostgreSQL command directory; + environment variable PGLIBEXECOLD + + + + libexecdir + libexecdir + the new PostgreSQL command directory; + default is the directory where pg_upgrade resides; + environment variable PGLIBEXECNEW + + options options @@ -418,6 +436,8 @@ pg_upgrade.exe --new-datadir "C:/Program Files/PostgreSQL/&majorversion;/data" --old-bindir "C:/Program Files/PostgreSQL/9.6/bin" --new-bindir "C:/Program Files/PostgreSQL/&majorversion;/bin" + --old-libexecdir "C:/Program Files/PostgreSQL/9.6/libexec" + --new-libexecdir "C:/Program Files/PostgreSQL/&majorversion;/libexec" Once started, pg_upgrade will verify the two clusters are compatible diff --git a/src/Makefile.global.in b/src/Makefile.global.in index 9a6265b3a0..b0692f40fb 100644 --- a/src/Makefile.global.in +++ b/src/Makefile.global.in @@ -99,6 +99,7 @@ exec_prefix := @exec_prefix@ datarootdir := @datarootdir@ bindir := @bindir@ +libexecdir := @libexecdir@ datadir := @datadir@ ifeq "$(findstring pgsql, $(datadir))" "" @@ -153,6 +154,7 @@ PG_CONFIG = pg_config endif bindir := $(shell $(PG_CONFIG) --bindir) +libexecdir := $(shell $(PG_CONFIG) --libexecdir) datadir := $(shell $(PG_CONFIG) --sharedir) sysconfdir := $(shell $(PG_CONFIG) --sysconfdir) libdir := $(shell $(PG_CONFIG) --libdir) @@ -438,7 +440,7 @@ ld_library_path_var = LD_LIBRARY_PATH # need something more here. If not defined then the expansion does # nothing. with_temp_install = \ - PATH="$(abs_top_builddir)/tmp_install$(bindir):$$PATH" \ + PATH="$(abs_top_builddir)/tmp_install$(bindir):$(abs_top_builddir)/tmp_install$(libexecdir):$$PATH" \ $(call add_to_path,$(strip $(ld_library_path_var)),$(abs_top_builddir)/tmp_install$(libdir)) \ $(with_temp_install_extra) @@ -447,7 +449,7 @@ ifeq ($(enable_tap_tests),yes) define prove_installcheck rm -rf '$(CURDIR)'/tmp_check $(MKDIR_P) '$(CURDIR)'/tmp_check -cd $(srcdir) && TESTDIR='$(CURDIR)' PATH="$(bindir):$$PATH" PGPORT='6$(DEF_PGPORT)' top_builddir='$(CURDIR)/$(top_builddir)' PG_REGRESS='$(CURDIR)/$(top_builddir)/src/test/regress/pg_regress' REGRESS_SHLIB='$(abs_top_builddir)/src/test/regress/regress$(DLSUFFIX)' $(PROVE) $(PG_PROVE_FLAGS) $(PROVE_FLAGS) $(if $(PROVE_TESTS),$(PROVE_TESTS),t/*.pl) +cd $(srcdir) && TESTDIR='$(CURDIR)' PATH="$(bindir):$(libexecdir):$$PATH" PGPORT='6$(DEF_PGPORT)' top_builddir='$(CURDIR)/$(top_builddir)' PG_REGRESS='$(CURDIR)/$(top_builddir)/src/test/regress/pg_regress' REGRESS_SHLIB='$(abs_top_builddir)/src/test/regress/regress$(DLSUFFIX)' $(PROVE) $(PG_PROVE_FLAGS) $(PROVE_FLAGS) $(if $(PROVE_TESTS),$(PROVE_TESTS),t/*.pl) endef define prove_check @@ -644,12 +646,14 @@ pg_regress_check = \ $(top_builddir)/src/test/regress/pg_regress \ --temp-instance=./tmp_check \ --inputdir=$(srcdir) \ + --libexecdir= \ --bindir= \ $(TEMP_CONF) \ $(pg_regress_locale_flags) $(EXTRA_REGRESS_OPTS) pg_regress_installcheck = \ $(top_builddir)/src/test/regress/pg_regress \ --inputdir=$(srcdir) \ + --libexecdir='$(libexecdir)' \ --bindir='$(bindir)' \ $(pg_regress_locale_flags) $(EXTRA_REGRESS_OPTS) @@ -658,12 +662,14 @@ pg_isolation_regress_check = \ $(top_builddir)/src/test/isolation/pg_isolation_regress \ --temp-instance=./tmp_check_iso \ --inputdir=$(srcdir) --outputdir=output_iso \ + --libexecdir= \ --bindir= \ $(TEMP_CONF) \ $(pg_regress_locale_flags) $(EXTRA_REGRESS_OPTS) pg_isolation_regress_installcheck = \ $(top_builddir)/src/test/isolation/pg_isolation_regress \ --inputdir=$(srcdir) --outputdir=output_iso \ + --libexecdir='$(libexecdir)' \ --bindir='$(bindir)' \ $(pg_regress_locale_flags) $(EXTRA_REGRESS_OPTS) diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 160afe9f39..5f7633df94 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -1468,10 +1468,13 @@ getInstallationPaths(const char *argv0) if (find_my_exec(argv0, my_exec_path) < 0) elog(FATAL, "%s: could not locate my own executable path", argv0); + if (find_my_rootdir(argv0, my_rootdir) < 0) + elog(FATAL, "%s: could not locate my own root directory", argv0); + #ifdef EXEC_BACKEND /* Locate executable backend before we change working directory */ - if (find_other_exec(argv0, "postgres", PG_BACKEND_VERSIONSTR, - postgres_exec_path) < 0) + if (find_other_cmd(argv0, "postgres", PG_BACKEND_VERSIONSTR, + postgres_exec_path) < 0) ereport(FATAL, (errmsg("%s: could not locate matching postgres executable", argv0))); diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c index eb19644419..3b0a2e84a6 100644 --- a/src/backend/utils/init/globals.c +++ b/src/backend/utils/init/globals.c @@ -70,6 +70,7 @@ int data_directory_mode = PG_DIR_MODE_OWNER; char OutputFileName[MAXPGPATH]; /* debugging output file */ char my_exec_path[MAXPGPATH]; /* full path to my executable */ +char my_rootdir[MAXPGPATH]; /* full path to postgres root directory */ char pkglib_path[MAXPGPATH]; /* full path to lib directory */ #ifdef EXEC_BACKEND diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index cca9704d2d..01e82f227f 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -161,6 +161,13 @@ InitStandaloneProcess(const char *argv0) argv0); } + if (my_rootdir[0] == '\0') + { + if (find_my_rootdir(argv0, my_rootdir) < 0) + elog(FATAL, "%s: could not locate my own installation root", + argv0); + } + if (pkglib_path[0] == '\0') get_pkglib_path(my_exec_path, pkglib_path); } diff --git a/src/backend/utils/misc/pg_config.c b/src/backend/utils/misc/pg_config.c index 7a79cbff92..e081eacce7 100644 --- a/src/backend/utils/misc/pg_config.c +++ b/src/backend/utils/misc/pg_config.c @@ -69,7 +69,8 @@ pg_config(PG_FUNCTION_ARGS) /* initialize our tuplestore */ tupstore = tuplestore_begin_heap(true, false, work_mem); - configdata = get_configdata(my_exec_path, &configdata_len); + configdata = get_configdata(my_exec_path, my_rootdir, + &configdata_len); for (i = 0; i < configdata_len; i++) { values[0] = configdata[i].name; diff --git a/src/bin/Makefile b/src/bin/Makefile index 8b870357a1..ec0534422e 100644 --- a/src/bin/Makefile +++ b/src/bin/Makefile @@ -15,6 +15,7 @@ include $(top_builddir)/src/Makefile.global SUBDIRS = \ initdb \ + pg \ pg_archivecleanup \ pg_basebackup \ pg_checksums \ diff --git a/src/bin/initdb/Makefile b/src/bin/initdb/Makefile index 7e23754780..409247f22f 100644 --- a/src/bin/initdb/Makefile +++ b/src/bin/initdb/Makefile @@ -47,13 +47,13 @@ localtime.c: % : $(top_srcdir)/src/timezone/% rm -f $@ && $(LN_S) $< . install: all installdirs - $(INSTALL_PROGRAM) initdb$(X) '$(DESTDIR)$(bindir)/initdb$(X)' + $(INSTALL_PROGRAM) initdb$(X) '$(DESTDIR)$(libexecdir)/initdb$(X)' installdirs: - $(MKDIR_P) '$(DESTDIR)$(bindir)' + $(MKDIR_P) '$(DESTDIR)$(libexecdir)' uninstall: - rm -f '$(DESTDIR)$(bindir)/initdb$(X)' + rm -f '$(DESTDIR)$(libexecdir)/initdb$(X)' clean distclean maintainer-clean: rm -f initdb$(X) $(OBJS) localtime.c diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index 67021a6dc1..475b72e260 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -2422,8 +2422,8 @@ setup_bin_paths(const char *argv0) { int ret; - if ((ret = find_other_exec(argv0, "postgres", PG_BACKEND_VERSIONSTR, - backend_exec)) < 0) + if ((ret = find_other_cmd(argv0, "postgres", PG_BACKEND_VERSIONSTR, + backend_exec)) < 0) { char full_path[MAXPGPATH]; diff --git a/src/bin/initdb/t/002_redirect.pl b/src/bin/initdb/t/002_redirect.pl new file mode 100644 index 0000000000..5a2176b32b --- /dev/null +++ b/src/bin/initdb/t/002_redirect.pl @@ -0,0 +1,17 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 4; + +command_fails_like( + ['pg', 'initdb'], + qr/error: no data directory specified/, + 'pg initdb fails'); + +command_ok( + ['pg', 'initdb', '--version'], + 'pg initdb version ok'); + +command_ok( + ['pg', 'initdb', '--help'], + 'pg initdb help ok'); diff --git a/src/bin/pg/.gitignore b/src/bin/pg/.gitignore new file mode 100644 index 0000000000..f21dfa43a0 --- /dev/null +++ b/src/bin/pg/.gitignore @@ -0,0 +1,4 @@ +/pg +/pg_libdir_relpath.h + +/tmp_check/ diff --git a/src/bin/pg/Makefile b/src/bin/pg/Makefile new file mode 100644 index 0000000000..5ac883ce10 --- /dev/null +++ b/src/bin/pg/Makefile @@ -0,0 +1,48 @@ +#------------------------------------------------------------------------- +# +# Makefile for src/bin/pg +# +# Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group +# Portions Copyright (c) 1994, Regents of the University of California +# +# src/bin/pg/Makefile +# +#------------------------------------------------------------------------- + +PGFILEDESC = "pg - consolidated PostgreSQL command line interface client" +PGAPPICON=win32 + +subdir = src/bin/pg +top_builddir = ../../.. +include $(top_builddir)/src/Makefile.global + +override CPPFLAGS := -I$(libpq_srcdir) $(CPPFLAGS) +LDFLAGS_INTERNAL += -L$(top_builddir)/src/fe_utils -lpgfeutils $(libpq_pgport) + +OBJS = \ + $(WIN32RES) \ + pg.o + +all: pg + +pg: $(OBJS) | submake-libpq submake-libpgport submake-libpgfeutils + $(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) + +install: all installdirs + $(INSTALL_PROGRAM) pg$(X) '$(DESTDIR)$(bindir)'/pg$(X) + +installdirs: + $(MKDIR_P) '$(DESTDIR)$(bindir)' + +check: + $(prove_check) + +installcheck: + $(prove_installcheck) + +uninstall: + rm -f $(addprefix '$(DESTDIR)$(bindir)'/, pg$(X)) + +clean distclean maintainer-clean: + rm -f pg$(X) $(OBJS) pg.o + rm -rf tmp_check diff --git a/src/bin/pg/pg.c b/src/bin/pg/pg.c new file mode 100644 index 0000000000..2341843fcd --- /dev/null +++ b/src/bin/pg/pg.c @@ -0,0 +1,194 @@ +/*------------------------------------------------------------------------- + * + * pg --- consolidated PostgreSQL command line interface client + * + * This code is released under the terms of the PostgreSQL License. + * + * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/bin/pg/pg.c + *------------------------------------------------------------------------- + */ + +#include "postgres_fe.h" + +#include + +#include "common/logging.h" +#include "lib/stringinfo.h" +#include "port.h" + +/* internal vars */ +static const char *progname; + +/* Map user supplied command names to installed executable names. */ +typedef struct NameMap { + const char *cmdname; + const char *executable; +} NameMap; + +/* + * The standard format is that "name" => "pg_name", but this is not so + * for some executables, and for compatibility we don't want to change + * the executable name. Instead, nonstandard names are listed here. + */ +const NameMap name_map[] = { + { .cmdname = "clusterdb", .executable = "clusterdb" }, + { .cmdname = "createdb", .executable = "createdb" }, + { .cmdname = "createuser", .executable = "createuser" }, + { .cmdname = "dropdb", .executable = "dropdb" }, + { .cmdname = "dropuser", .executable = "dropuser" }, + { .cmdname = "initdb", .executable = "initdb" }, + { .cmdname = "bench", .executable = "pgbench" }, + { .cmdname = "reindexdb", .executable = "reindexdb" }, + { .cmdname = "vacuumdb", .executable = "vacuumdb" }, + { .cmdname = NULL } +}; + +static char *executable_for_command(const char *cmdname); +static void usage(const char *progname); + +int +main(int argc, char *argv[]) +{ + setvbuf(stdout, NULL, _IONBF, 0); + pg_logging_init(argv[0]); + set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg")); + progname = get_progname(argv[0]); + + /* + * Most options are handled in the various sub-command executables, not + * here. The only options checked for here are ones that `pg' will handle + * even when there is no sub-command to which the arguments will be handed + * off. For now, this is just help and version information. + */ + if (argc > 1) + { + char *found_path; + char *executable; + char cmd_version[MAXPGPATH + 512]; + + found_path = pg_malloc(MAXPGPATH); + + /* Handle recognized options */ + if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) + { + usage(progname); + exit(0); + } + if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) + { + puts("pg (PostgreSQL) " PG_VERSION); + exit(0); + } + + /* If it is an unrecognized option, return an appropriate error */ + if (argv[1][0] == '-') + { + fprintf(stderr,_("%s: error: invalid option: %s\n"), progname, argv[1]); + exit(1); + } + + /* Otherwise, try to interpret the argument as a command name */ + executable = executable_for_command(argv[1]); + snprintf(cmd_version, sizeof(cmd_version), "%s (PostgreSQL) %s\n", executable, PG_VERSION); + if (find_other_cmd(argv[0], executable, cmd_version, found_path) == 0) + { + StringInfoData cmd; + int argidx; + int ret; + + /* + * Construct a command string to hand to system() from argv[1..n], noting that + * argv[1] is the command name and needs to be rewritten as an absolute path + * to the appropriate executable. For argv[2..n], we do no processing, but + * need to be careful to handle any escaping and quoting rules for the system. + */ + initStringInfo(&cmd); + appendStringInfo(&cmd, "%s", found_path); + for (argidx = 2; argidx < argc; argidx++) + { + /* XXX: Is this escaping sufficient? */ + char *arg = escape_single_quotes_ascii(argv[argidx]); + appendStringInfo(&cmd, " %s", arg); + } + + ret = system(cmd.data); + if (ret) + { + fprintf(stderr,_("%s: command failed: %s\n"), progname, cmd.data); + exit(ret >> 8); + } + exit(0); + } + + /* It's either an unrecognized command or garbage. Charitably assume it is a command */ + fprintf(stderr,_("%s: error: unrecognized command: %s\n"), progname, argv[1]); + exit(1); + } + + fprintf(stderr,_("%s: missing command\n"), progname); + exit(1); +} + +static +char *executable_for_command(const char *cmdname) +{ + int i; + + /* Check for nonstandard executable names */ + for (i = 0; name_map[i].cmdname; i++) + { + if (strcmp(cmdname, name_map[i].cmdname) == 0) + return pstrdup(name_map[i].executable); + } + + /* Otherwise, return standard executable name derived from cmdname */ + return psprintf("pg_%s", cmdname); +} + +/* + * print help text + */ +static void +usage(const char *progname) +{ + printf(_("%s is the consolidated PostgreSQL command line interface client.\n\n"), progname); + printf(_("Usage:\n")); + printf(_(" %s [OPTION]... [COMMAND] [COMMAND OPTION]...\n"), progname); + printf(_("\nOptions:\n")); + printf(_(" -V, --version output version information, then exit\n")); + printf(_(" -?, --help show this help, then exit\n")); + printf(_("\nCommands:\n")); + printf(_(" archivecleanup remove older WAL files from PostgreSQL archives\n")); + printf(_(" basebackup take a base backup of a running PostgreSQL server\n")); + printf(_(" bench run benchmark tests against a PostgreSQL server\n")); + printf(_(" checksums enable, disable, or verify data checksums in a PostgreSQL database cluster\n")); + printf(_(" clusterdb cluster all previously clustered tables in a database\n")); + printf(_(" config show information about the installed version of PostgreSQL\n")); + printf(_(" controldata display control information of a PostgreSQL database cluster\n")); + printf(_(" createdb create a PostgreSQL database\n")); + printf(_(" createuser create a new PostgreSQL role\n")); + printf(_(" ctl initialize, start, stop, or control a PostgreSQL server\n")); + printf(_(" dropdb remove a PostgreSQL database\n")); + printf(_(" dropuser remove a PostgreSQL role\n")); + printf(_(" dump dump a database as a text file or to other formats\n")); + printf(_(" dumpall extract a PostgreSQL database cluster into an SQL script file\n")); + printf(_(" initdb initialize a PostgreSQL database cluster\n")); + printf(_(" isready issue a connection check to a PostgreSQL database\n")); + printf(_(" receivewal receive PostgreSQL streaming write-ahead logs\n")); + printf(_(" recvlogical control PostgreSQL logical decoding streams\n")); + printf(_(" reindexdb reindex a PostgreSQL database\n")); + printf(_(" resetwal reset the PostgreSQL write-ahead log\n")); + printf(_(" restore restore a PostgreSQL database from an archive created by pg_dump\n")); + printf(_(" rewind resynchronize a PostgreSQL cluster with another copy of the cluster\n")); + printf(_(" test_fsync test all supported fsync() methods\n")); + printf(_(" test_timing test overhead of timing calls and their monotonicity\n")); + printf(_(" upgrade upgrade a PostgreSQL cluster to a different major version\n")); + printf(_(" vacuumdb clean and analyze a PostgreSQL database\n")); + printf(_(" verifybackup verify a backup against the backup manifest\n")); + printf(_(" waldump decode and display PostgreSQL write-ahead logs for debugging\n")); + printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT); + printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL); +} diff --git a/src/bin/pg/t/001_basic.pl b/src/bin/pg/t/001_basic.pl new file mode 100644 index 0000000000..d349aa074e --- /dev/null +++ b/src/bin/pg/t/001_basic.pl @@ -0,0 +1,31 @@ +use strict; +use warnings; + +use TestLib; +use Test::More tests => 13; + +program_help_ok('pg'); +program_version_ok('pg'); +program_options_handling_ok('pg'); + +command_fails_like( + [ 'pg', '-a' ], + qr/\Qpg: error: invalid option: -a\E/, + 'pg: invalid command-line arguments'); + +command_ok( + ['pg', '--version'], + 'pg version ok'); + +command_ok( + ['pg', '--help'], + 'pg help ok'); + +# Checks of various commands, such as 'pg initdb', are implemented +# in tests named /.*_redirect.pl/ in the test directory of the +# command in question, so we do not need to duplicate that here. +# But to help developers who change pg.c and run 'make check' in +# the pg directory, it helps to have at least one example of that +# for smoke testing. + +command_ok([ 'pg', 'initdb', '--version' ], 'pg redirection'); diff --git a/src/bin/pg_archivecleanup/Makefile b/src/bin/pg_archivecleanup/Makefile index 49935d6dce..00888294d9 100644 --- a/src/bin/pg_archivecleanup/Makefile +++ b/src/bin/pg_archivecleanup/Makefile @@ -17,13 +17,13 @@ pg_archivecleanup: $(OBJS) | submake-libpgport $(CC) $(CFLAGS) $^ $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) install: all installdirs - $(INSTALL_PROGRAM) pg_archivecleanup$(X) '$(DESTDIR)$(bindir)/pg_archivecleanup$(X)' + $(INSTALL_PROGRAM) pg_archivecleanup$(X) '$(DESTDIR)$(libexecdir)/pg_archivecleanup$(X)' installdirs: - $(MKDIR_P) '$(DESTDIR)$(bindir)' + $(MKDIR_P) '$(DESTDIR)$(libexecdir)' uninstall: - rm -f '$(DESTDIR)$(bindir)/pg_archivecleanup$(X)' + rm -f '$(DESTDIR)$(libexecdir)/pg_archivecleanup$(X)' clean distclean maintainer-clean: rm -f pg_archivecleanup$(X) $(OBJS) diff --git a/src/bin/pg_archivecleanup/t/001_redirect.pl b/src/bin/pg_archivecleanup/t/001_redirect.pl new file mode 100644 index 0000000000..c68aa89c58 --- /dev/null +++ b/src/bin/pg_archivecleanup/t/001_redirect.pl @@ -0,0 +1,17 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 4; + +command_fails_like( + ['pg', 'archivecleanup'], + qr/error: must specify archive location/, + 'pg pg_archivecleanup fails'); + +command_ok( + ['pg', 'archivecleanup', '--version'], + 'pg pg_archivecleanup version ok'); + +command_ok( + ['pg', 'archivecleanup', '--help'], + 'pg pg_archivecleanup help ok'); diff --git a/src/bin/pg_archivecleanup/t/010_pg_archivecleanup.pl b/src/bin/pg_archivecleanup/t/010_pg_archivecleanup.pl index 22782d3042..15a14efb1e 100644 --- a/src/bin/pg_archivecleanup/t/010_pg_archivecleanup.pl +++ b/src/bin/pg_archivecleanup/t/010_pg_archivecleanup.pl @@ -27,27 +27,27 @@ sub create_files create_files(); command_fails_like( - ['pg_archivecleanup'], + ['pg', 'archivecleanup'], qr/must specify archive location/, 'fails if archive location is not specified'); command_fails_like( - [ 'pg_archivecleanup', $tempdir ], + [ 'pg', 'archivecleanup', $tempdir ], qr/must specify oldest kept WAL file/, 'fails if oldest kept WAL file name is not specified'); command_fails_like( - [ 'pg_archivecleanup', 'notexist', 'foo' ], + [ 'pg', 'archivecleanup', 'notexist', 'foo' ], qr/archive location .* does not exist/, 'fails if archive location does not exist'); command_fails_like( - [ 'pg_archivecleanup', $tempdir, 'foo', 'bar' ], + [ 'pg', 'archivecleanup', $tempdir, 'foo', 'bar' ], qr/too many command-line arguments/, 'fails with too many command-line arguments'); command_fails_like( - [ 'pg_archivecleanup', $tempdir, 'foo' ], + [ 'pg', 'archivecleanup', $tempdir, 'foo' ], qr/invalid file name argument/, 'fails with invalid restart file name'); diff --git a/src/bin/pg_basebackup/Makefile b/src/bin/pg_basebackup/Makefile index 988007c6fd..65a6739cfe 100644 --- a/src/bin/pg_basebackup/Makefile +++ b/src/bin/pg_basebackup/Makefile @@ -39,17 +39,17 @@ pg_recvlogical: pg_recvlogical.o $(OBJS) | submake-libpq submake-libpgport subma $(CC) $(CFLAGS) pg_recvlogical.o $(OBJS) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) install: all installdirs - $(INSTALL_PROGRAM) pg_basebackup$(X) '$(DESTDIR)$(bindir)/pg_basebackup$(X)' - $(INSTALL_PROGRAM) pg_receivewal$(X) '$(DESTDIR)$(bindir)/pg_receivewal$(X)' - $(INSTALL_PROGRAM) pg_recvlogical$(X) '$(DESTDIR)$(bindir)/pg_recvlogical$(X)' + $(INSTALL_PROGRAM) pg_basebackup$(X) '$(DESTDIR)$(libexecdir)/pg_basebackup$(X)' + $(INSTALL_PROGRAM) pg_receivewal$(X) '$(DESTDIR)$(libexecdir)/pg_receivewal$(X)' + $(INSTALL_PROGRAM) pg_recvlogical$(X) '$(DESTDIR)$(libexecdir)/pg_recvlogical$(X)' installdirs: - $(MKDIR_P) '$(DESTDIR)$(bindir)' + $(MKDIR_P) '$(DESTDIR)$(libexecdir)' uninstall: - rm -f '$(DESTDIR)$(bindir)/pg_basebackup$(X)' - rm -f '$(DESTDIR)$(bindir)/pg_receivewal$(X)' - rm -f '$(DESTDIR)$(bindir)/pg_recvlogical$(X)' + rm -f '$(DESTDIR)$(libexecdir)/pg_basebackup$(X)' + rm -f '$(DESTDIR)$(libexecdir)/pg_receivewal$(X)' + rm -f '$(DESTDIR)$(libexecdir)/pg_recvlogical$(X)' clean distclean maintainer-clean: rm -f pg_basebackup$(X) pg_receivewal$(X) pg_recvlogical$(X) \ diff --git a/src/bin/pg_basebackup/t/001_redirect.pl b/src/bin/pg_basebackup/t/001_redirect.pl new file mode 100644 index 0000000000..dbdbb5c849 --- /dev/null +++ b/src/bin/pg_basebackup/t/001_redirect.pl @@ -0,0 +1,17 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 4; + +command_fails_like( + ['pg', 'basebackup'], + qr/error: no target directory specified/, + 'pg pg_basebackup fails'); + +command_ok( + ['pg', 'basebackup', '--version'], + 'pg pg_basebackup version ok'); + +command_ok( + ['pg', 'basebackup', '--help'], + 'pg pg_basebackup help ok'); diff --git a/src/bin/pg_basebackup/t/010_pg_basebackup.pl b/src/bin/pg_basebackup/t/010_pg_basebackup.pl index 208df557b8..e189abff35 100644 --- a/src/bin/pg_basebackup/t/010_pg_basebackup.pl +++ b/src/bin/pg_basebackup/t/010_pg_basebackup.pl @@ -24,7 +24,7 @@ $node->init(extra => ['--data-checksums']); $node->start; my $pgdata = $node->data_dir; -$node->command_fails(['pg_basebackup'], +$node->command_fails(['pg', 'basebackup'], 'pg_basebackup needs target directory specified'); # Some Windows ANSI code pages may reject this filename, in which case we @@ -39,7 +39,7 @@ $node->set_replication_conf(); system_or_bail 'pg_ctl', '-D', $pgdata, 'reload'; $node->command_fails( - [ 'pg_basebackup', '-D', "$tempdir/backup" ], + [ 'pg', 'basebackup', '-D', "$tempdir/backup" ], 'pg_basebackup fails because of WAL configuration'); ok(!-d "$tempdir/backup", 'backup directory was cleaned up'); @@ -50,7 +50,7 @@ mkdir("$tempdir/backup") or BAIL_OUT("unable to create $tempdir/backup"); append_to_file("$tempdir/backup/dir-not-empty.txt", "Some data"); -$node->command_fails([ 'pg_basebackup', '-D', "$tempdir/backup", '-n' ], +$node->command_fails([ 'pg', 'basebackup', '-D', "$tempdir/backup", '-n' ], 'failing run with no-clean option'); ok(-d "$tempdir/backup", 'backup directory was created and left behind'); @@ -101,7 +101,7 @@ foreach my $filename (@tempRelationFiles) } # Run base backup. -$node->command_ok([ 'pg_basebackup', '-D', "$tempdir/backup", '-X', 'none' ], +$node->command_ok([ 'pg', 'basebackup', '-D', "$tempdir/backup", '-X', 'none' ], 'pg_basebackup runs'); ok(-f "$tempdir/backup/PG_VERSION", 'backup was created'); ok(-f "$tempdir/backup/backup_manifest", 'backup manifest included'); @@ -161,7 +161,7 @@ rmtree("$tempdir/backup"); $node->command_ok( [ - 'pg_basebackup', '-D', + 'pg', 'basebackup', '-D', "$tempdir/backup2", '--no-manifest', '--waldir', "$tempdir/xlog2" ], @@ -172,31 +172,31 @@ ok(-d "$tempdir/xlog2/", 'xlog directory was created'); rmtree("$tempdir/backup2"); rmtree("$tempdir/xlog2"); -$node->command_ok([ 'pg_basebackup', '-D', "$tempdir/tarbackup", '-Ft' ], +$node->command_ok([ 'pg', 'basebackup', '-D', "$tempdir/tarbackup", '-Ft' ], 'tar format'); ok(-f "$tempdir/tarbackup/base.tar", 'backup tar was created'); rmtree("$tempdir/tarbackup"); $node->command_fails( - [ 'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-T=/foo" ], + [ 'pg', 'basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-T=/foo" ], '-T with empty old directory fails'); $node->command_fails( - [ 'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-T/foo=" ], + [ 'pg', 'basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-T/foo=" ], '-T with empty new directory fails'); $node->command_fails( [ - 'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', + 'pg', 'basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-T/foo=/bar=/baz" ], '-T with multiple = fails'); $node->command_fails( - [ 'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-Tfoo=/bar" ], + [ 'pg', 'basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-Tfoo=/bar" ], '-T with old directory not absolute fails'); $node->command_fails( - [ 'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-T/foo=bar" ], + [ 'pg', 'basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-T/foo=bar" ], '-T with new directory not absolute fails'); $node->command_fails( - [ 'pg_basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-Tfoo" ], + [ 'pg', 'basebackup', '-D', "$tempdir/backup_foo", '-Fp', "-Tfoo" ], '-T with invalid format fails'); # Tar format doesn't support filenames longer than 100 bytes. @@ -207,7 +207,7 @@ open my $file, '>', "$superlongpath" or die "unable to create file $superlongpath"; close $file; $node->command_fails( - [ 'pg_basebackup', '-D', "$tempdir/tarbackup_l1", '-Ft' ], + [ 'pg', 'basebackup', '-D', "$tempdir/tarbackup_l1", '-Ft' ], 'pg_basebackup tar with long name fails'); unlink "$pgdata/$superlongname"; @@ -245,7 +245,7 @@ SKIP: "CREATE TABLESPACE tblspc1 LOCATION '$shorter_tempdir/tblspc1';"); $node->safe_psql('postgres', "CREATE TABLE test1 (a int) TABLESPACE tblspc1;"); - $node->command_ok([ 'pg_basebackup', '-D', "$tempdir/tarbackup2", '-Ft' ], + $node->command_ok([ 'pg', 'basebackup', '-D', "$tempdir/tarbackup2", '-Ft' ], 'tar format with tablespaces'); ok(-f "$tempdir/tarbackup2/base.tar", 'backup tar was created'); my @tblspc_tars = glob "$tempdir/tarbackup2/[0-9]*.tar"; @@ -282,12 +282,12 @@ SKIP: } $node->command_fails( - [ 'pg_basebackup', '-D', "$tempdir/backup1", '-Fp' ], + [ 'pg', 'basebackup', '-D', "$tempdir/backup1", '-Fp' ], 'plain format with tablespaces fails without tablespace mapping'); $node->command_ok( [ - 'pg_basebackup', '-D', "$tempdir/backup1", '-Fp', + 'pg', 'basebackup', '-D', "$tempdir/backup1", '-Fp', "-T$shorter_tempdir/tblspc1=$tempdir/tbackup/tblspc1" ], 'plain format with tablespaces succeeds with tablespace mapping'); @@ -340,7 +340,7 @@ SKIP: "CREATE TABLESPACE tblspc2 LOCATION '$shorter_tempdir/tbl=spc2';"); $node->command_ok( [ - 'pg_basebackup', '-D', "$tempdir/backup3", '-Fp', + 'pg', 'basebackup', '-D', "$tempdir/backup3", '-Fp', "-T$shorter_tempdir/tbl\\=spc2=$tempdir/tbackup/tbl\\=spc2" ], 'mapping tablespace with = sign in path'); @@ -353,13 +353,13 @@ SKIP: $node->safe_psql('postgres', "CREATE TABLESPACE tblspc3 LOCATION '$tempdir/$superlongname';"); $node->command_ok( - [ 'pg_basebackup', '-D', "$tempdir/tarbackup_l3", '-Ft' ], + [ 'pg', 'basebackup', '-D', "$tempdir/tarbackup_l3", '-Ft' ], 'pg_basebackup tar with long symlink target'); $node->safe_psql('postgres', "DROP TABLESPACE tblspc3;"); rmtree("$tempdir/tarbackup_l3"); } -$node->command_ok([ 'pg_basebackup', '-D', "$tempdir/backupR", '-R' ], +$node->command_ok([ 'pg', 'basebackup', '-D', "$tempdir/backupR", '-R' ], 'pg_basebackup -R runs'); ok(-f "$tempdir/backupR/postgresql.auto.conf", 'postgresql.auto.conf exists'); ok(-f "$tempdir/backupR/standby.signal", 'standby.signal was created'); @@ -373,32 +373,32 @@ like( 'postgresql.auto.conf sets primary_conninfo'); $node->command_ok( - [ 'pg_basebackup', '-D', "$tempdir/backupxd" ], + [ 'pg', 'basebackup', '-D', "$tempdir/backupxd" ], 'pg_basebackup runs in default xlog mode'); ok(grep(/^[0-9A-F]{24}$/, slurp_dir("$tempdir/backupxd/pg_wal")), 'WAL files copied'); rmtree("$tempdir/backupxd"); $node->command_ok( - [ 'pg_basebackup', '-D', "$tempdir/backupxf", '-X', 'fetch' ], + [ 'pg', 'basebackup', '-D', "$tempdir/backupxf", '-X', 'fetch' ], 'pg_basebackup -X fetch runs'); ok(grep(/^[0-9A-F]{24}$/, slurp_dir("$tempdir/backupxf/pg_wal")), 'WAL files copied'); rmtree("$tempdir/backupxf"); $node->command_ok( - [ 'pg_basebackup', '-D', "$tempdir/backupxs", '-X', 'stream' ], + [ 'pg', 'basebackup', '-D', "$tempdir/backupxs", '-X', 'stream' ], 'pg_basebackup -X stream runs'); ok(grep(/^[0-9A-F]{24}$/, slurp_dir("$tempdir/backupxs/pg_wal")), 'WAL files copied'); rmtree("$tempdir/backupxs"); $node->command_ok( - [ 'pg_basebackup', '-D', "$tempdir/backupxst", '-X', 'stream', '-Ft' ], + [ 'pg', 'basebackup', '-D', "$tempdir/backupxst", '-X', 'stream', '-Ft' ], 'pg_basebackup -X stream runs in tar mode'); ok(-f "$tempdir/backupxst/pg_wal.tar", "tar file was created"); rmtree("$tempdir/backupxst"); $node->command_ok( [ - 'pg_basebackup', '-D', + 'pg', 'basebackup', '-D', "$tempdir/backupnoslot", '-X', 'stream', '--no-slot' ], @@ -407,7 +407,7 @@ rmtree("$tempdir/backupnoslot"); $node->command_fails( [ - 'pg_basebackup', '-D', + 'pg', 'basebackup', '-D', "$tempdir/backupxs_sl_fail", '-X', 'stream', '-S', 'slot0' @@ -415,12 +415,12 @@ $node->command_fails( 'pg_basebackup fails with nonexistent replication slot'); $node->command_fails( - [ 'pg_basebackup', '-D', "$tempdir/backupxs_slot", '-C' ], + [ 'pg', 'basebackup', '-D', "$tempdir/backupxs_slot", '-C' ], 'pg_basebackup -C fails without slot name'); $node->command_fails( [ - 'pg_basebackup', '-D', + 'pg', 'basebackup', '-D', "$tempdir/backupxs_slot", '-C', '-S', 'slot0', '--no-slot' @@ -428,7 +428,7 @@ $node->command_fails( 'pg_basebackup fails with -C -S --no-slot'); $node->command_ok( - [ 'pg_basebackup', '-D', "$tempdir/backupxs_slot", '-C', '-S', 'slot0' ], + [ 'pg', 'basebackup', '-D', "$tempdir/backupxs_slot", '-C', '-S', 'slot0' ], 'pg_basebackup -C runs'); rmtree("$tempdir/backupxs_slot"); @@ -447,7 +447,7 @@ isnt( 'restart LSN of new slot is not null'); $node->command_fails( - [ 'pg_basebackup', '-D', "$tempdir/backupxs_slot1", '-C', '-S', 'slot0' ], + [ 'pg', 'basebackup', '-D', "$tempdir/backupxs_slot1", '-C', '-S', 'slot0' ], 'pg_basebackup fails with -C -S and a previously existing slot'); $node->safe_psql('postgres', @@ -457,11 +457,11 @@ my $lsn = $node->safe_psql('postgres', ); is($lsn, '', 'restart LSN of new slot is null'); $node->command_fails( - [ 'pg_basebackup', '-D', "$tempdir/fail", '-S', 'slot1', '-X', 'none' ], + [ 'pg', 'basebackup', '-D', "$tempdir/fail", '-S', 'slot1', '-X', 'none' ], 'pg_basebackup with replication slot fails without WAL streaming'); $node->command_ok( [ - 'pg_basebackup', '-D', "$tempdir/backupxs_sl", '-X', + 'pg', 'basebackup', '-D', "$tempdir/backupxs_sl", '-X', 'stream', '-S', 'slot1' ], 'pg_basebackup -X stream with replication slot runs'); @@ -473,7 +473,7 @@ rmtree("$tempdir/backupxs_sl"); $node->command_ok( [ - 'pg_basebackup', '-D', "$tempdir/backupxs_sl_R", '-X', + 'pg', 'basebackup', '-D', "$tempdir/backupxs_sl_R", '-X', 'stream', '-S', 'slot1', '-R' ], 'pg_basebackup with replication slot and -R runs'); @@ -507,7 +507,7 @@ close $file; system_or_bail 'pg_ctl', '-D', $pgdata, 'start'; $node->command_checks_all( - [ 'pg_basebackup', '-D', "$tempdir/backup_corrupt" ], + [ 'pg', 'basebackup', '-D', "$tempdir/backup_corrupt" ], 1, [qr{^$}], [qr/^WARNING.*checksum verification failed/s], @@ -527,7 +527,7 @@ close $file; system_or_bail 'pg_ctl', '-D', $pgdata, 'start'; $node->command_checks_all( - [ 'pg_basebackup', '-D', "$tempdir/backup_corrupt2" ], + [ 'pg', 'basebackup', '-D', "$tempdir/backup_corrupt2" ], 1, [qr{^$}], [qr/^WARNING.*further.*failures.*will.not.be.reported/s], @@ -543,7 +543,7 @@ close $file; system_or_bail 'pg_ctl', '-D', $pgdata, 'start'; $node->command_checks_all( - [ 'pg_basebackup', '-D', "$tempdir/backup_corrupt3" ], + [ 'pg', 'basebackup', '-D', "$tempdir/backup_corrupt3" ], 1, [qr{^$}], [qr/^WARNING.*7 total checksum verification failures/s], @@ -553,7 +553,7 @@ rmtree("$tempdir/backup_corrupt3"); # do not verify checksums, should return ok $node->command_ok( [ - 'pg_basebackup', '-D', + 'pg', 'basebackup', '-D', "$tempdir/backup_corrupt4", '--no-verify-checksums' ], 'pg_basebackup with -k does not report checksum mismatch'); diff --git a/src/bin/pg_basebackup/t/020_pg_receivewal.pl b/src/bin/pg_basebackup/t/020_pg_receivewal.pl index 6e2f051187..57c36103ee 100644 --- a/src/bin/pg_basebackup/t/020_pg_receivewal.pl +++ b/src/bin/pg_basebackup/t/020_pg_receivewal.pl @@ -19,27 +19,27 @@ my $stream_dir = $primary->basedir . '/archive_wal'; mkdir($stream_dir); # Sanity checks for command line options. -$primary->command_fails(['pg_receivewal'], +$primary->command_fails(['pg', 'receivewal'], 'pg_receivewal needs target directory specified'); $primary->command_fails( - [ 'pg_receivewal', '-D', $stream_dir, '--create-slot', '--drop-slot' ], + [ 'pg', 'receivewal', '-D', $stream_dir, '--create-slot', '--drop-slot' ], 'failure if both --create-slot and --drop-slot specified'); $primary->command_fails( - [ 'pg_receivewal', '-D', $stream_dir, '--create-slot' ], + [ 'pg', 'receivewal', '-D', $stream_dir, '--create-slot' ], 'failure if --create-slot specified without --slot'); $primary->command_fails( - [ 'pg_receivewal', '-D', $stream_dir, '--synchronous', '--no-sync' ], + [ 'pg', 'receivewal', '-D', $stream_dir, '--synchronous', '--no-sync' ], 'failure if --synchronous specified with --no-sync'); # Slot creation and drop my $slot_name = 'test'; $primary->command_ok( - [ 'pg_receivewal', '--slot', $slot_name, '--create-slot' ], + [ 'pg', 'receivewal', '--slot', $slot_name, '--create-slot' ], 'creating a replication slot'); my $slot = $primary->slot($slot_name); is($slot->{'slot_type'}, 'physical', 'physical replication slot was created'); is($slot->{'restart_lsn'}, '', 'restart LSN of new slot is null'); -$primary->command_ok([ 'pg_receivewal', '--slot', $slot_name, '--drop-slot' ], +$primary->command_ok([ 'pg', 'receivewal', '--slot', $slot_name, '--drop-slot' ], 'dropping a replication slot'); is($primary->slot($slot_name)->{'slot_type'}, '', 'replication slot was removed'); @@ -58,7 +58,7 @@ $primary->psql('postgres', # Stream up to the given position. $primary->command_ok( [ - 'pg_receivewal', '-D', $stream_dir, '--verbose', + 'pg', 'receivewal', '-D', $stream_dir, '--verbose', '--endpos', $nextlsn, '--synchronous', '--no-loop' ], 'streaming some WAL with --synchronous'); diff --git a/src/bin/pg_basebackup/t/030_pg_recvlogical.pl b/src/bin/pg_basebackup/t/030_pg_recvlogical.pl index 99154bcf39..9d84e39c6a 100644 --- a/src/bin/pg_basebackup/t/030_pg_recvlogical.pl +++ b/src/bin/pg_basebackup/t/030_pg_recvlogical.pl @@ -23,14 +23,14 @@ log_error_verbosity = verbose $node->dump_info; $node->start; -$node->command_fails(['pg_recvlogical'], 'pg_recvlogical needs a slot name'); -$node->command_fails([ 'pg_recvlogical', '-S', 'test' ], +$node->command_fails([ 'pg', 'recvlogical'], 'pg_recvlogical needs a slot name'); +$node->command_fails([ 'pg', 'recvlogical', '-S', 'test' ], 'pg_recvlogical needs a database'); -$node->command_fails([ 'pg_recvlogical', '-S', 'test', '-d', 'postgres' ], +$node->command_fails([ 'pg', 'recvlogical', '-S', 'test', '-d', 'postgres' ], 'pg_recvlogical needs an action'); $node->command_fails( [ - 'pg_recvlogical', '-S', + 'pg', 'recvlogical', '-S', 'test', '-d', $node->connstr('postgres'), '--start' ], diff --git a/src/bin/pg_checksums/Makefile b/src/bin/pg_checksums/Makefile index b1cfa5733d..991704bb52 100644 --- a/src/bin/pg_checksums/Makefile +++ b/src/bin/pg_checksums/Makefile @@ -25,13 +25,13 @@ pg_checksums: $(OBJS) | submake-libpgport $(CC) $(CFLAGS) $^ $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) install: all installdirs - $(INSTALL_PROGRAM) pg_checksums$(X) '$(DESTDIR)$(bindir)/pg_checksums$(X)' + $(INSTALL_PROGRAM) pg_checksums$(X) '$(DESTDIR)$(libexecdir)/pg_checksums$(X)' installdirs: - $(MKDIR_P) '$(DESTDIR)$(bindir)' + $(MKDIR_P) '$(DESTDIR)$(libexecdir)' uninstall: - rm -f '$(DESTDIR)$(bindir)/pg_checksums$(X)' + rm -f '$(DESTDIR)$(libexecdir)/pg_checksums$(X)' clean distclean maintainer-clean: rm -f pg_checksums$(X) $(OBJS) diff --git a/src/bin/pg_checksums/t/002_actions.pl b/src/bin/pg_checksums/t/002_actions.pl index 4e4934532a..fb91a1ecdb 100644 --- a/src/bin/pg_checksums/t/002_actions.pl +++ b/src/bin/pg_checksums/t/002_actions.pl @@ -41,7 +41,7 @@ sub check_relation_corruption # corrupted yet. command_ok( [ - 'pg_checksums', '--check', + 'pg', 'checksums', '--check', '-D', $pgdata, '--filenode', $relfilenode_corrupted ], @@ -57,7 +57,7 @@ sub check_relation_corruption # Checksum checks on single relfilenode fail $node->command_checks_all( [ - 'pg_checksums', '--check', + 'pg', 'checksums', '--check', '-D', $pgdata, '--filenode', $relfilenode_corrupted ], @@ -69,7 +69,7 @@ sub check_relation_corruption # Global checksum checks fail as well $node->command_checks_all( - [ 'pg_checksums', '--check', '-D', $pgdata ], + [ 'pg', 'checksums', '--check', '-D', $pgdata ], 1, [qr/Bad checksums:.*1/], [qr/checksum verification failed/], @@ -79,7 +79,7 @@ sub check_relation_corruption $node->start; $node->safe_psql('postgres', "DROP TABLE $table;"); $node->stop; - $node->command_ok([ 'pg_checksums', '--check', '-D', $pgdata ], + $node->command_ok([ 'pg', 'checksums', '--check', '-D', $pgdata ], "succeeds again after table drop on tablespace $tablespace"); $node->start; @@ -116,66 +116,66 @@ append_to_file "$pgdata/global/pg_internal.init", "foo"; append_to_file "$pgdata/global/pg_internal.init.123", "foo"; # Enable checksums. -command_ok([ 'pg_checksums', '--enable', '--no-sync', '-D', $pgdata ], +command_ok([ 'pg', 'checksums', '--enable', '--no-sync', '-D', $pgdata ], "checksums successfully enabled in cluster"); # Successive attempt to enable checksums fails. -command_fails([ 'pg_checksums', '--enable', '--no-sync', '-D', $pgdata ], +command_fails([ 'pg', 'checksums', '--enable', '--no-sync', '-D', $pgdata ], "enabling checksums fails if already enabled"); # Control file should know that checksums are enabled. command_like( - [ 'pg_controldata', $pgdata ], + [ 'pg', 'controldata', $pgdata ], qr/Data page checksum version:.*1/, 'checksums enabled in control file'); # Disable checksums again. Flush result here as that should be cheap. command_ok( - [ 'pg_checksums', '--disable', '-D', $pgdata ], + [ 'pg', 'checksums', '--disable', '-D', $pgdata ], "checksums successfully disabled in cluster"); # Successive attempt to disable checksums fails. command_fails( - [ 'pg_checksums', '--disable', '--no-sync', '-D', $pgdata ], + [ 'pg', 'checksums', '--disable', '--no-sync', '-D', $pgdata ], "disabling checksums fails if already disabled"); # Control file should know that checksums are disabled. command_like( - [ 'pg_controldata', $pgdata ], + [ 'pg', 'controldata', $pgdata ], qr/Data page checksum version:.*0/, 'checksums disabled in control file'); # Enable checksums again for follow-up tests. -command_ok([ 'pg_checksums', '--enable', '--no-sync', '-D', $pgdata ], +command_ok([ 'pg', 'checksums', '--enable', '--no-sync', '-D', $pgdata ], "checksums successfully enabled in cluster"); # Control file should know that checksums are enabled. command_like( - [ 'pg_controldata', $pgdata ], + [ 'pg', 'controldata', $pgdata ], qr/Data page checksum version:.*1/, 'checksums enabled in control file'); # Checksums pass on a newly-created cluster -command_ok([ 'pg_checksums', '--check', '-D', $pgdata ], +command_ok([ 'pg', 'checksums', '--check', '-D', $pgdata ], "succeeds with offline cluster"); # Checksums are verified if no other arguments are specified command_ok( - [ 'pg_checksums', '-D', $pgdata ], + [ 'pg', 'checksums', '-D', $pgdata ], "verifies checksums as default action"); # Specific relation files cannot be requested when action is --disable # or --enable. command_fails( - [ 'pg_checksums', '--disable', '--filenode', '1234', '-D', $pgdata ], + [ 'pg', 'checksums', '--disable', '--filenode', '1234', '-D', $pgdata ], "fails when relfilenodes are requested and action is --disable"); command_fails( - [ 'pg_checksums', '--enable', '--filenode', '1234', '-D', $pgdata ], + [ 'pg', 'checksums', '--enable', '--filenode', '1234', '-D', $pgdata ], "fails when relfilenodes are requested and action is --enable"); # Checks cannot happen with an online cluster $node->start; -command_fails([ 'pg_checksums', '--check', '-D', $pgdata ], +command_fails([ 'pg', 'checksums', '--check', '-D', $pgdata ], "fails with online cluster"); # Check corruption of table on default tablespace. @@ -203,7 +203,7 @@ sub fail_corrupt append_to_file $file_name, "foo"; $node->command_checks_all( - [ 'pg_checksums', '--check', '-D', $pgdata ], + [ 'pg', 'checksums', '--check', '-D', $pgdata ], 1, [qr/^$/], [qr/could not read block 0 in file.*$file\":/], @@ -221,7 +221,7 @@ $node->stop; # when verifying checksums. mkdir "$tablespace_dir/PG_99_999999991/"; append_to_file "$tablespace_dir/PG_99_999999991/foo", "123"; -command_ok([ 'pg_checksums', '--check', '-D', $pgdata ], +command_ok([ 'pg', 'checksums', '--check', '-D', $pgdata ], "succeeds with foreign tablespace"); # Authorized relation files filled with corrupted data cause the diff --git a/src/bin/pg_checksums/t/003_redirect.pl b/src/bin/pg_checksums/t/003_redirect.pl new file mode 100644 index 0000000000..64aff19225 --- /dev/null +++ b/src/bin/pg_checksums/t/003_redirect.pl @@ -0,0 +1,17 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 4; + +command_fails_like( + ['pg', 'checksums'], + qr/error: no data directory specified/, + 'pg pg_checksums fails'); + +command_ok( + ['pg', 'checksums', '--version'], + 'pg pg_checksums version ok'); + +command_ok( + ['pg', 'checksums', '--help'], + 'pg pg_checksums help ok'); diff --git a/src/bin/pg_config/Makefile b/src/bin/pg_config/Makefile index d3b5f1fa75..c8c8dd943a 100644 --- a/src/bin/pg_config/Makefile +++ b/src/bin/pg_config/Makefile @@ -25,13 +25,13 @@ pg_config: $(OBJS) | submake-libpgport $(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) install: all installdirs - $(INSTALL_SCRIPT) pg_config$(X) '$(DESTDIR)$(bindir)/pg_config$(X)' + $(INSTALL_SCRIPT) pg_config$(X) '$(DESTDIR)$(libexecdir)/pg_config$(X)' installdirs: - $(MKDIR_P) '$(DESTDIR)$(bindir)' + $(MKDIR_P) '$(DESTDIR)$(libexecdir)' uninstall: - rm -f '$(DESTDIR)$(bindir)/pg_config$(X)' + rm -f '$(DESTDIR)$(libexecdir)/pg_config$(X)' clean distclean maintainer-clean: rm -f pg_config$(X) $(OBJS) diff --git a/src/bin/pg_config/pg_config.c b/src/bin/pg_config/pg_config.c index f5410f6418..61676ff0d2 100644 --- a/src/bin/pg_config/pg_config.c +++ b/src/bin/pg_config/pg_config.c @@ -42,6 +42,7 @@ typedef struct static const InfoItem info_items[] = { {"--bindir", "BINDIR"}, + {"--libexecdir", "LIBEXECDIR"}, {"--docdir", "DOCDIR"}, {"--htmldir", "HTMLDIR"}, {"--includedir", "INCLUDEDIR"}, @@ -76,6 +77,7 @@ help(void) printf(_(" %s [OPTION]...\n\n"), progname); printf(_("Options:\n")); printf(_(" --bindir show location of user executables\n")); + printf(_(" --libexecdir show location of commands\n")); printf(_(" --docdir show location of documentation files\n")); printf(_(" --htmldir show location of HTML documentation files\n")); printf(_(" --includedir show location of C header files of the client\n" @@ -132,6 +134,7 @@ main(int argc, char **argv) ConfigData *configdata; size_t configdata_len; char my_exec_path[MAXPGPATH]; + char my_rootdir[MAXPGPATH]; int i; int j; @@ -147,6 +150,11 @@ main(int argc, char **argv) help(); exit(0); } + if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) + { + puts("pg_config (PostgreSQL) " PG_VERSION); + exit(0); + } } if (find_my_exec(argv[0], my_exec_path) < 0) @@ -155,7 +163,13 @@ main(int argc, char **argv) exit(1); } - configdata = get_configdata(my_exec_path, &configdata_len); + if (find_my_rootdir(argv[0], my_rootdir) < 0) + { + fprintf(stderr, _("%s: could not find own root executable\n"), progname); + exit(1); + } + + configdata = get_configdata(my_exec_path, my_rootdir, &configdata_len); /* no arguments -> print everything */ if (argc < 2) { diff --git a/src/bin/pg_config/t/001_pg_config.pl b/src/bin/pg_config/t/001_pg_config.pl index ccca190bb1..07d85342bb 100644 --- a/src/bin/pg_config/t/001_pg_config.pl +++ b/src/bin/pg_config/t/001_pg_config.pl @@ -1,16 +1,18 @@ use strict; use warnings; use TestLib; -use Test::More tests => 20; +use Test::More tests => 23; program_help_ok('pg_config'); program_version_ok('pg_config'); program_options_handling_ok('pg_config'); command_like([ 'pg_config', '--bindir' ], qr/bin/, 'pg_config single option') ; # XXX might be wrong -command_like([ 'pg_config', '--bindir', '--libdir' ], - qr/bin.*\n.*lib/, 'pg_config two options'); -command_like([ 'pg_config', '--libdir', '--bindir' ], - qr/lib.*\n.*bin/, 'pg_config two options different order'); +command_like([ 'pg_config', '--bindir', '--libdir', '--libexecdir' ], + qr/bin.*\n.*lib.*\n.*libexec/, 'pg_config three options'); +command_like([ 'pg_config', '--libexecdir', '--libdir', '--bindir' ], + qr/libexec.*\n.*lib.*\n.*bin/, 'pg_config three options different order'); +command_like(['pg_config'], + qr/libexec/, 'pg_config without options includes libexec in the output'); command_like(['pg_config'], qr/.*\n.*\n.*/, 'pg_config without options prints many lines'); diff --git a/src/bin/pg_controldata/Makefile b/src/bin/pg_controldata/Makefile index 76b330dc1f..dc22ad9217 100644 --- a/src/bin/pg_controldata/Makefile +++ b/src/bin/pg_controldata/Makefile @@ -25,13 +25,13 @@ pg_controldata: $(OBJS) | submake-libpgport $(CC) $(CFLAGS) $^ $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) install: all installdirs - $(INSTALL_PROGRAM) pg_controldata$(X) '$(DESTDIR)$(bindir)/pg_controldata$(X)' + $(INSTALL_PROGRAM) pg_controldata$(X) '$(DESTDIR)$(libexecdir)/pg_controldata$(X)' installdirs: - $(MKDIR_P) '$(DESTDIR)$(bindir)' + $(MKDIR_P) '$(DESTDIR)$(libexecdir)' uninstall: - rm -f '$(DESTDIR)$(bindir)/pg_controldata$(X)' + rm -f '$(DESTDIR)$(libexecdir)/pg_controldata$(X)' clean distclean maintainer-clean: rm -f pg_controldata$(X) $(OBJS) diff --git a/src/bin/pg_controldata/t/001_pg_controldata.pl b/src/bin/pg_controldata/t/001_pg_controldata.pl index 3b63ad230f..5b6f8d4938 100644 --- a/src/bin/pg_controldata/t/001_pg_controldata.pl +++ b/src/bin/pg_controldata/t/001_pg_controldata.pl @@ -7,14 +7,14 @@ use Test::More tests => 17; program_help_ok('pg_controldata'); program_version_ok('pg_controldata'); program_options_handling_ok('pg_controldata'); -command_fails(['pg_controldata'], 'pg_controldata without arguments fails'); -command_fails([ 'pg_controldata', 'nonexistent' ], +command_fails([ 'pg', 'controldata'], 'pg_controldata without arguments fails'); +command_fails([ 'pg', 'controldata', 'nonexistent' ], 'pg_controldata with nonexistent directory fails'); my $node = get_new_node('main'); $node->init; -command_like([ 'pg_controldata', $node->data_dir ], +command_like([ 'pg', 'controldata', $node->data_dir ], qr/checkpoint/, 'pg_controldata produces output'); @@ -31,7 +31,7 @@ print $fh pack("x[$size]"); close $fh; command_checks_all( - [ 'pg_controldata', $node->data_dir ], + [ 'pg', 'controldata', $node->data_dir ], 0, [ qr/WARNING: Calculated CRC checksum does not match value stored in file/, diff --git a/src/bin/pg_controldata/t/002_redirect.pl b/src/bin/pg_controldata/t/002_redirect.pl new file mode 100644 index 0000000000..171f7523fa --- /dev/null +++ b/src/bin/pg_controldata/t/002_redirect.pl @@ -0,0 +1,17 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 4; + +command_fails_like( + ['pg', 'controldata'], + qr/error: no data directory specified/, + 'pg controldata fails'); + +command_ok( + ['pg', 'controldata', '--version'], + 'pg controldata version ok'); + +command_ok( + ['pg', 'controldata', '--help'], + 'pg controldata help ok'); diff --git a/src/bin/pg_ctl/Makefile b/src/bin/pg_ctl/Makefile index 14602c1185..08b6ccaf76 100644 --- a/src/bin/pg_ctl/Makefile +++ b/src/bin/pg_ctl/Makefile @@ -34,13 +34,13 @@ pg_ctl: $(OBJS) | submake-libpgport $(SUBMAKE_LIBPQ) $(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) install: all installdirs - $(INSTALL_PROGRAM) pg_ctl$(X) '$(DESTDIR)$(bindir)/pg_ctl$(X)' + $(INSTALL_PROGRAM) pg_ctl$(X) '$(DESTDIR)$(libexecdir)/pg_ctl$(X)' installdirs: - $(MKDIR_P) '$(DESTDIR)$(bindir)' + $(MKDIR_P) '$(DESTDIR)$(libexecdir)' uninstall: - rm -f '$(DESTDIR)$(bindir)/pg_ctl$(X)' + rm -f '$(DESTDIR)$(libexecdir)/pg_ctl$(X)' clean distclean maintainer-clean: rm -f pg_ctl$(X) $(OBJS) diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c index 3c03ace7ed..018dfe094b 100644 --- a/src/bin/pg_ctl/pg_ctl.c +++ b/src/bin/pg_ctl/pg_ctl.c @@ -795,14 +795,14 @@ trap_sigint_during_startup(int sig) } static char * -find_other_exec_or_die(const char *argv0, const char *target, const char *versionstr) +find_other_cmd_or_die(const char *argv0, const char *target, const char *versionstr) { int ret; char *found_path; found_path = pg_malloc(MAXPGPATH); - if ((ret = find_other_exec(argv0, target, versionstr, found_path)) < 0) + if ((ret = find_other_cmd(argv0, target, versionstr, found_path)) < 0) { char full_path[MAXPGPATH]; @@ -831,7 +831,7 @@ do_init(void) char cmd[MAXPGPATH]; if (exec_path == NULL) - exec_path = find_other_exec_or_die(argv0, "initdb", "initdb (PostgreSQL) " PG_VERSION "\n"); + exec_path = find_other_cmd_or_die(argv0, "initdb", "initdb (PostgreSQL) " PG_VERSION "\n"); if (pgdata_opt == NULL) pgdata_opt = ""; @@ -875,7 +875,7 @@ do_start(void) pgdata_opt = ""; if (exec_path == NULL) - exec_path = find_other_exec_or_die(argv0, "postgres", PG_BACKEND_VERSIONSTR); + exec_path = find_other_cmd_or_die(argv0, "postgres", PG_BACKEND_VERSIONSTR); #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_CORE) if (allow_core_files) @@ -1443,7 +1443,7 @@ pgwin32_CommandLine(bool registration) } else { - ret = find_other_exec(argv0, "postgres", PG_BACKEND_VERSIONSTR, + ret = find_other_cmd(argv0, "postgres", PG_BACKEND_VERSIONSTR, cmdPath); if (ret != 0) { @@ -2208,7 +2208,7 @@ adjust_data_dir(void) /* we use a private my_exec_path to avoid interfering with later uses */ if (exec_path == NULL) - my_exec_path = find_other_exec_or_die(argv0, "postgres", PG_BACKEND_VERSIONSTR); + my_exec_path = find_other_cmd_or_die(argv0, "postgres", PG_BACKEND_VERSIONSTR); else my_exec_path = pg_strdup(exec_path); diff --git a/src/bin/pg_ctl/t/001_start_stop.pl b/src/bin/pg_ctl/t/001_start_stop.pl index b1e419f02e..964e8f046e 100644 --- a/src/bin/pg_ctl/t/001_start_stop.pl +++ b/src/bin/pg_ctl/t/001_start_stop.pl @@ -15,10 +15,10 @@ program_help_ok('pg_ctl'); program_version_ok('pg_ctl'); program_options_handling_ok('pg_ctl'); -command_exit_is([ 'pg_ctl', 'start', '-D', "$tempdir/nonexistent" ], +command_exit_is([ 'pg', 'ctl', 'start', '-D', "$tempdir/nonexistent" ], 1, 'pg_ctl start with nonexistent directory'); -command_ok([ 'pg_ctl', 'initdb', '-D', "$tempdir/data", '-o', '-N' ], +command_ok([ 'pg', 'ctl', 'initdb', '-D', "$tempdir/data", '-o', '-N' ], 'pg_ctl initdb'); command_ok([ $ENV{PG_REGRESS}, '--config-auth', "$tempdir/data" ], 'configure authentication'); @@ -40,7 +40,7 @@ else } close $conf; my $ctlcmd = [ - 'pg_ctl', 'start', '-D', "$tempdir/data", '-l', + 'pg', 'ctl', 'start', '-D', "$tempdir/data", '-l', "$TestLib::log_path/001_start_stop_server.log" ]; if ($Config{osname} ne 'msys') @@ -59,17 +59,17 @@ else # postmaster they start. Waiting more than the 2 seconds slop time allowed # by wait_for_postmaster() prevents that mistake. sleep 3 if ($windows_os); -command_fails([ 'pg_ctl', 'start', '-D', "$tempdir/data" ], +command_fails([ 'pg', 'ctl', 'start', '-D', "$tempdir/data" ], 'second pg_ctl start fails'); -command_ok([ 'pg_ctl', 'stop', '-D', "$tempdir/data" ], 'pg_ctl stop'); -command_fails([ 'pg_ctl', 'stop', '-D', "$tempdir/data" ], +command_ok([ 'pg', 'ctl', 'stop', '-D', "$tempdir/data" ], 'pg_ctl stop'); +command_fails([ 'pg', 'ctl', 'stop', '-D', "$tempdir/data" ], 'second pg_ctl stop fails'); # Log file for default permission test. The permissions won't be checked on # Windows but we still want to do the restart test. my $logFileName = "$tempdir/data/perm-test-600.log"; -command_ok([ 'pg_ctl', 'restart', '-D', "$tempdir/data", '-l', $logFileName ], +command_ok([ 'pg', 'ctl', 'restart', '-D', "$tempdir/data", '-l', $logFileName ], 'pg_ctl restart with server not running'); # Permissions on log file should be default @@ -89,21 +89,21 @@ SKIP: { skip "group access not supported on Windows", 3 if ($windows_os); - system_or_bail 'pg_ctl', 'stop', '-D', "$tempdir/data"; + system_or_bail 'pg', 'ctl', 'stop', '-D', "$tempdir/data"; # Change the data dir mode so log file will be created with group read # privileges on the next start chmod_recursive("$tempdir/data", 0750, 0640); command_ok( - [ 'pg_ctl', 'start', '-D', "$tempdir/data", '-l', $logFileName ], + [ 'pg', 'ctl', 'start', '-D', "$tempdir/data", '-l', $logFileName ], 'start server to check group permissions'); ok(-f $logFileName); ok(check_mode_recursive("$tempdir/data", 0750, 0640)); } -command_ok([ 'pg_ctl', 'restart', '-D', "$tempdir/data" ], +command_ok([ 'pg', 'ctl', 'restart', '-D', "$tempdir/data" ], 'pg_ctl restart with server running'); -system_or_bail 'pg_ctl', 'stop', '-D', "$tempdir/data"; +system_or_bail 'pg', 'ctl', 'stop', '-D', "$tempdir/data"; diff --git a/src/bin/pg_ctl/t/002_status.pl b/src/bin/pg_ctl/t/002_status.pl index 606d10560f..4f8f72e8bc 100644 --- a/src/bin/pg_ctl/t/002_status.pl +++ b/src/bin/pg_ctl/t/002_status.pl @@ -8,18 +8,18 @@ use Test::More tests => 3; my $tempdir = TestLib::tempdir; my $tempdir_short = TestLib::tempdir_short; -command_exit_is([ 'pg_ctl', 'status', '-D', "$tempdir/nonexistent" ], +command_exit_is([ 'pg', 'ctl', 'status', '-D', "$tempdir/nonexistent" ], 4, 'pg_ctl status with nonexistent directory'); my $node = get_new_node('main'); $node->init; -command_exit_is([ 'pg_ctl', 'status', '-D', $node->data_dir ], +command_exit_is([ 'pg', 'ctl', 'status', '-D', $node->data_dir ], 3, 'pg_ctl status with server not running'); -system_or_bail 'pg_ctl', '-l', "$tempdir/logfile", '-D', +system_or_bail 'pg', 'ctl', '-l', "$tempdir/logfile", '-D', $node->data_dir, '-w', 'start'; -command_exit_is([ 'pg_ctl', 'status', '-D', $node->data_dir ], +command_exit_is([ 'pg', 'ctl', 'status', '-D', $node->data_dir ], 0, 'pg_ctl status with server running'); -system_or_bail 'pg_ctl', 'stop', '-D', $node->data_dir; +system_or_bail 'pg', 'ctl', 'stop', '-D', $node->data_dir; diff --git a/src/bin/pg_ctl/t/003_promote.pl b/src/bin/pg_ctl/t/003_promote.pl index ecb294b490..d371c9b4a3 100644 --- a/src/bin/pg_ctl/t/003_promote.pl +++ b/src/bin/pg_ctl/t/003_promote.pl @@ -8,7 +8,7 @@ use Test::More tests => 12; my $tempdir = TestLib::tempdir; command_fails_like( - [ 'pg_ctl', '-D', "$tempdir/nonexistent", 'promote' ], + [ 'pg', 'ctl', '-D', "$tempdir/nonexistent", 'promote' ], qr/directory .* does not exist/, 'pg_ctl promote with nonexistent directory'); @@ -16,14 +16,14 @@ my $node_primary = get_new_node('primary'); $node_primary->init(allows_streaming => 1); command_fails_like( - [ 'pg_ctl', '-D', $node_primary->data_dir, 'promote' ], + [ 'pg', 'ctl', '-D', $node_primary->data_dir, 'promote' ], qr/PID file .* does not exist/, 'pg_ctl promote of not running instance fails'); $node_primary->start; command_fails_like( - [ 'pg_ctl', '-D', $node_primary->data_dir, 'promote' ], + [ 'pg', 'ctl', '-D', $node_primary->data_dir, 'promote' ], qr/not in standby mode/, 'pg_ctl promote of primary instance fails'); @@ -36,7 +36,7 @@ $node_standby->start; is($node_standby->safe_psql('postgres', 'SELECT pg_is_in_recovery()'), 't', 'standby is in recovery'); -command_ok([ 'pg_ctl', '-D', $node_standby->data_dir, '-W', 'promote' ], +command_ok([ 'pg', 'ctl', '-D', $node_standby->data_dir, '-W', 'promote' ], 'pg_ctl -W promote of standby runs'); ok( $node_standby->poll_query_until( @@ -52,7 +52,7 @@ $node_standby->start; is($node_standby->safe_psql('postgres', 'SELECT pg_is_in_recovery()'), 't', 'standby is in recovery'); -command_ok([ 'pg_ctl', '-D', $node_standby->data_dir, 'promote' ], +command_ok([ 'pg', 'ctl', '-D', $node_standby->data_dir, 'promote' ], 'pg_ctl promote of standby runs'); # no wait here diff --git a/src/bin/pg_ctl/t/005_redirect.pl b/src/bin/pg_ctl/t/005_redirect.pl new file mode 100644 index 0000000000..ae92a81d74 --- /dev/null +++ b/src/bin/pg_ctl/t/005_redirect.pl @@ -0,0 +1,17 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 4; + +command_fails_like( + ['pg', 'ctl'], + qr/pg_ctl: no operation specified/, + 'pg pg_ctl fails'); + +command_ok( + ['pg', 'ctl', '--version'], + 'pg pg_ctl version ok'); + +command_ok( + ['pg', 'ctl', '--help'], + 'pg pg_ctl help ok'); diff --git a/src/bin/pg_dump/Makefile b/src/bin/pg_dump/Makefile index 2532d9183a..76f23155fe 100644 --- a/src/bin/pg_dump/Makefile +++ b/src/bin/pg_dump/Makefile @@ -44,12 +44,12 @@ pg_dumpall: pg_dumpall.o dumputils.o | submake-libpq submake-libpgport submake-l $(CC) $(CFLAGS) pg_dumpall.o dumputils.o $(WIN32RES) $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) install: all installdirs - $(INSTALL_PROGRAM) pg_dump$(X) '$(DESTDIR)$(bindir)'/pg_dump$(X) - $(INSTALL_PROGRAM) pg_restore$(X) '$(DESTDIR)$(bindir)'/pg_restore$(X) - $(INSTALL_PROGRAM) pg_dumpall$(X) '$(DESTDIR)$(bindir)'/pg_dumpall$(X) + $(INSTALL_PROGRAM) pg_dump$(X) '$(DESTDIR)$(libexecdir)'/pg_dump$(X) + $(INSTALL_PROGRAM) pg_restore$(X) '$(DESTDIR)$(libexecdir)'/pg_restore$(X) + $(INSTALL_PROGRAM) pg_dumpall$(X) '$(DESTDIR)$(libexecdir)'/pg_dumpall$(X) installdirs: - $(MKDIR_P) '$(DESTDIR)$(bindir)' + $(MKDIR_P) '$(DESTDIR)$(libexecdir)' check: $(prove_check) @@ -58,7 +58,7 @@ installcheck: $(prove_installcheck) uninstall: - rm -f $(addprefix '$(DESTDIR)$(bindir)'/, pg_dump$(X) pg_restore$(X) pg_dumpall$(X)) + rm -f $(addprefix '$(DESTDIR)$(libexecdir)'/, pg_dump$(X) pg_restore$(X) pg_dumpall$(X)) clean distclean maintainer-clean: rm -f pg_dump$(X) pg_restore$(X) pg_dumpall$(X) $(OBJS) pg_dump.o common.o pg_dump_sort.o pg_restore.o pg_dumpall.o diff --git a/src/bin/pg_dump/pg_dumpall.c b/src/bin/pg_dump/pg_dumpall.c index 8d54849102..fdfe571d4e 100644 --- a/src/bin/pg_dump/pg_dumpall.c +++ b/src/bin/pg_dump/pg_dumpall.c @@ -187,8 +187,8 @@ main(int argc, char *argv[]) } } - if ((ret = find_other_exec(argv[0], "pg_dump", PGDUMP_VERSIONSTR, - pg_dump_bin)) < 0) + if ((ret = find_other_cmd(argv[0], "pg_dump", PGDUMP_VERSIONSTR, + pg_dump_bin)) < 0) { char full_path[MAXPGPATH]; diff --git a/src/bin/pg_dump/t/004_redirect.pl b/src/bin/pg_dump/t/004_redirect.pl new file mode 100644 index 0000000000..f2498cf534 --- /dev/null +++ b/src/bin/pg_dump/t/004_redirect.pl @@ -0,0 +1,17 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 4; + +command_fails_like( + ['pg', 'dump'], + qr/error: connection to database .* failed/, + 'pg pg_dump fails'); + +command_ok( + ['pg', 'dump', '--version'], + 'pg pg_dump version ok'); + +command_ok( + ['pg', 'dump', '--help'], + 'pg pg_dump help ok'); diff --git a/src/bin/pg_resetwal/Makefile b/src/bin/pg_resetwal/Makefile index 464268e978..00fafc6fea 100644 --- a/src/bin/pg_resetwal/Makefile +++ b/src/bin/pg_resetwal/Makefile @@ -25,13 +25,13 @@ pg_resetwal: $(OBJS) | submake-libpgport $(CC) $(CFLAGS) $^ $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) install: all installdirs - $(INSTALL_PROGRAM) pg_resetwal$(X) '$(DESTDIR)$(bindir)/pg_resetwal$(X)' + $(INSTALL_PROGRAM) pg_resetwal$(X) '$(DESTDIR)$(libexecdir)/pg_resetwal$(X)' installdirs: - $(MKDIR_P) '$(DESTDIR)$(bindir)' + $(MKDIR_P) '$(DESTDIR)$(libexecdir)' uninstall: - rm -f '$(DESTDIR)$(bindir)/pg_resetwal$(X)' + rm -f '$(DESTDIR)$(libexecdir)/pg_resetwal$(X)' clean distclean maintainer-clean: rm -f pg_resetwal$(X) $(OBJS) diff --git a/src/bin/pg_resetwal/t/003_redirect.pl b/src/bin/pg_resetwal/t/003_redirect.pl new file mode 100644 index 0000000000..f70467110f --- /dev/null +++ b/src/bin/pg_resetwal/t/003_redirect.pl @@ -0,0 +1,17 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 4; + +command_fails_like( + ['pg', 'resetwal'], + qr/error: no data directory specified/, + 'pg pg_resetwal fails'); + +command_ok( + ['pg', 'resetwal', '--version'], + 'pg pg_resetwal version ok'); + +command_ok( + ['pg', 'resetwal', '--help'], + 'pg pg_resetwal help ok'); diff --git a/src/bin/pg_rewind/Makefile b/src/bin/pg_rewind/Makefile index f398c3d848..dd59c284eb 100644 --- a/src/bin/pg_rewind/Makefile +++ b/src/bin/pg_rewind/Makefile @@ -42,13 +42,13 @@ xlogreader.c: % : $(top_srcdir)/src/backend/access/transam/% rm -f $@ && $(LN_S) $< . install: all installdirs - $(INSTALL_PROGRAM) pg_rewind$(X) '$(DESTDIR)$(bindir)/pg_rewind$(X)' + $(INSTALL_PROGRAM) pg_rewind$(X) '$(DESTDIR)$(libexecdir)/pg_rewind$(X)' installdirs: - $(MKDIR_P) '$(DESTDIR)$(bindir)' + $(MKDIR_P) '$(DESTDIR)$(libexecdir)' uninstall: - rm -f '$(DESTDIR)$(bindir)/pg_rewind$(X)' + rm -f '$(DESTDIR)$(libexecdir)/pg_rewind$(X)' clean distclean maintainer-clean: rm -f pg_rewind$(X) $(OBJS) xlogreader.c diff --git a/src/bin/pg_rewind/pg_rewind.c b/src/bin/pg_rewind/pg_rewind.c index 0015d3b461..a8d7b60d10 100644 --- a/src/bin/pg_rewind/pg_rewind.c +++ b/src/bin/pg_rewind/pg_rewind.c @@ -835,9 +835,9 @@ getRestoreCommand(const char *argv0) return; /* find postgres executable */ - rc = find_other_exec(argv0, "postgres", - PG_BACKEND_VERSIONSTR, - postgres_exec_path); + rc = find_other_cmd(argv0, "postgres", + PG_BACKEND_VERSIONSTR, + postgres_exec_path); if (rc < 0) { @@ -895,9 +895,9 @@ ensureCleanShutdown(const char *argv0) char cmd[MAXCMDLEN]; /* locate postgres binary */ - if ((ret = find_other_exec(argv0, "postgres", - PG_BACKEND_VERSIONSTR, - exec_path)) < 0) + if ((ret = find_other_cmd(argv0, "postgres", + PG_BACKEND_VERSIONSTR, + exec_path)) < 0) { char full_path[MAXPGPATH]; diff --git a/src/bin/pg_rewind/t/007_redirect.pl b/src/bin/pg_rewind/t/007_redirect.pl new file mode 100644 index 0000000000..9d4be4d081 --- /dev/null +++ b/src/bin/pg_rewind/t/007_redirect.pl @@ -0,0 +1,17 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 4; + +command_fails_like( + ['pg', 'rewind'], + qr/error: no source specified/, + 'pg pg_rewind fails'); + +command_ok( + ['pg', 'rewind', '--version'], + 'pg pg_rewind version ok'); + +command_ok( + ['pg', 'rewind', '--help'], + 'pg pg_rewind help ok'); diff --git a/src/bin/pg_test_fsync/Makefile b/src/bin/pg_test_fsync/Makefile index 7632c94eb7..7ef63a172b 100644 --- a/src/bin/pg_test_fsync/Makefile +++ b/src/bin/pg_test_fsync/Makefile @@ -17,13 +17,13 @@ pg_test_fsync: $(OBJS) | submake-libpgport $(CC) $(CFLAGS) $^ $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) install: all installdirs - $(INSTALL_PROGRAM) pg_test_fsync$(X) '$(DESTDIR)$(bindir)/pg_test_fsync$(X)' + $(INSTALL_PROGRAM) pg_test_fsync$(X) '$(DESTDIR)$(libexecdir)/pg_test_fsync$(X)' installdirs: - $(MKDIR_P) '$(DESTDIR)$(bindir)' + $(MKDIR_P) '$(DESTDIR)$(libexecdir)' uninstall: - rm -f '$(DESTDIR)$(bindir)/pg_test_fsync$(X)' + rm -f '$(DESTDIR)$(libexecdir)/pg_test_fsync$(X)' clean distclean maintainer-clean: rm -f pg_test_fsync$(X) $(OBJS) diff --git a/src/bin/pg_test_timing/Makefile b/src/bin/pg_test_timing/Makefile index 334d6ff5c0..bc762113ab 100644 --- a/src/bin/pg_test_timing/Makefile +++ b/src/bin/pg_test_timing/Makefile @@ -17,13 +17,13 @@ pg_test_timing: $(OBJS) | submake-libpgport $(CC) $(CFLAGS) $^ $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) install: all installdirs - $(INSTALL_PROGRAM) pg_test_timing$(X) '$(DESTDIR)$(bindir)/pg_test_timing$(X)' + $(INSTALL_PROGRAM) pg_test_timing$(X) '$(DESTDIR)$(libexecdir)/pg_test_timing$(X)' installdirs: - $(MKDIR_P) '$(DESTDIR)$(bindir)' + $(MKDIR_P) '$(DESTDIR)$(libexecdir)' uninstall: - rm -f '$(DESTDIR)$(bindir)/pg_test_timing$(X)' + rm -f '$(DESTDIR)$(libexecdir)/pg_test_timing$(X)' clean distclean maintainer-clean: rm -f pg_test_timing$(X) $(OBJS) diff --git a/src/bin/pg_upgrade/Makefile b/src/bin/pg_upgrade/Makefile index 0360c37bf9..da9bf0e589 100644 --- a/src/bin/pg_upgrade/Makefile +++ b/src/bin/pg_upgrade/Makefile @@ -34,13 +34,13 @@ pg_upgrade: $(OBJS) | submake-libpq submake-libpgport submake-libpgfeutils $(CC) $(CFLAGS) $^ $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) install: all installdirs - $(INSTALL_PROGRAM) pg_upgrade$(X) '$(DESTDIR)$(bindir)/pg_upgrade$(X)' + $(INSTALL_PROGRAM) pg_upgrade$(X) '$(DESTDIR)$(libexecdir)/pg_upgrade$(X)' installdirs: - $(MKDIR_P) '$(DESTDIR)$(bindir)' + $(MKDIR_P) '$(DESTDIR)$(libexecdir)' uninstall: - rm -f '$(DESTDIR)$(bindir)/pg_upgrade$(X)' + rm -f '$(DESTDIR)$(libexecdir)/pg_upgrade$(X)' clean distclean maintainer-clean: rm -f pg_upgrade$(X) $(OBJS) @@ -59,7 +59,7 @@ clean distclean maintainer-clean: NOTSUBMAKEMAKE=$(MAKE) check: test.sh all temp-install - MAKE=$(NOTSUBMAKEMAKE) $(with_temp_install) bindir=$(abs_top_builddir)/tmp_install/$(bindir) EXTRA_REGRESS_OPTS="$(EXTRA_REGRESS_OPTS)" $(SHELL) $< + MAKE=$(NOTSUBMAKEMAKE) $(with_temp_install) bindir=$(abs_top_builddir)/tmp_install/$(bindir) libexecdir=$(abs_top_builddir)/tmp_install/$(libexecdir) EXTRA_REGRESS_OPTS="$(EXTRA_REGRESS_OPTS)" $(SHELL) $< # installcheck is not supported because there's no meaningful way to test # pg_upgrade against a single already-running server diff --git a/src/bin/pg_upgrade/TESTING b/src/bin/pg_upgrade/TESTING index e69874b42d..6e01e891d9 100644 --- a/src/bin/pg_upgrade/TESTING +++ b/src/bin/pg_upgrade/TESTING @@ -14,7 +14,9 @@ must have done "make install" for both versions. Then do: export oldsrc=...somewhere/postgresql (old version's source tree) export oldbindir=...otherversion/bin (old version's installed bin dir) +export oldlibexecdir=...otherversion/libexec (old version's installed libexec dir) export bindir=...thisversion/bin (this version's installed bin dir) +export libexecdir=...thisversion/libexec (this version's installed libexec dir) export libdir=...thisversion/lib (this version's installed lib dir) sh test.sh diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c index 00aef855dc..e025a89dd6 100644 --- a/src/bin/pg_upgrade/check.c +++ b/src/bin/pg_upgrade/check.c @@ -503,17 +503,17 @@ create_script_for_cluster_analyze(char **analyze_script_file_name) fprintf(script, "echo %sthis script and run:%s\n", ECHO_QUOTE, ECHO_QUOTE); fprintf(script, "echo %s \"%s/vacuumdb\" %s--all %s%s\n", ECHO_QUOTE, - new_cluster.bindir, user_specification.data, + new_cluster.libexecdir, user_specification.data, /* Did we copy the free space files? */ (GET_MAJOR_VERSION(old_cluster.major_version) >= 804) ? "--analyze-only" : "--analyze", ECHO_QUOTE); fprintf(script, "echo%s\n\n", ECHO_BLANK); fprintf(script, "\"%s/vacuumdb\" %s--all --analyze-in-stages\n", - new_cluster.bindir, user_specification.data); + new_cluster.libexecdir, user_specification.data); /* Did we copy the free space files? */ if (GET_MAJOR_VERSION(old_cluster.major_version) < 804) - fprintf(script, "\"%s/vacuumdb\" %s--all\n", new_cluster.bindir, + fprintf(script, "\"%s/vacuumdb\" %s--all\n", new_cluster.libexecdir, user_specification.data); fprintf(script, "echo%s\n\n", ECHO_BLANK); diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c index 00d71e3a8a..24587c6b8e 100644 --- a/src/bin/pg_upgrade/controldata.c +++ b/src/bin/pg_upgrade/controldata.c @@ -119,7 +119,7 @@ get_control_data(ClusterInfo *cluster, bool live_check) { /* only pg_controldata outputs the cluster state */ snprintf(cmd, sizeof(cmd), "\"%s/pg_controldata\" \"%s\"", - cluster->bindir, cluster->pgdata); + cluster->libexecdir, cluster->pgdata); fflush(stdout); fflush(stderr); @@ -185,7 +185,7 @@ get_control_data(ClusterInfo *cluster, bool live_check) else resetwal_bin = "pg_resetwal\" -n"; snprintf(cmd, sizeof(cmd), "\"%s/%s \"%s\"", - cluster->bindir, + cluster->libexecdir, live_check ? "pg_controldata\"" : resetwal_bin, cluster->pgdata); fflush(stdout); diff --git a/src/bin/pg_upgrade/dump.c b/src/bin/pg_upgrade/dump.c index 4d730adfe2..22c0b64bc4 100644 --- a/src/bin/pg_upgrade/dump.c +++ b/src/bin/pg_upgrade/dump.c @@ -21,9 +21,9 @@ generate_old_dump(void) /* run new pg_dumpall binary for globals */ exec_prog(UTILITY_LOG_FILE, NULL, true, true, - "\"%s/pg_dumpall\" %s --globals-only --quote-all-identifiers " + "\"%s\" %s --globals-only --quote-all-identifiers " "--binary-upgrade %s -f %s", - new_cluster.bindir, cluster_conn_opts(&old_cluster), + new_cluster.pg_dumpall_path, cluster_conn_opts(&old_cluster), log_opts.verbose ? "--verbose" : "", GLOBALS_DUMP_FILE); check_ok(); @@ -51,9 +51,9 @@ generate_old_dump(void) snprintf(log_file_name, sizeof(log_file_name), DB_DUMP_LOG_FILE_MASK, old_db->db_oid); parallel_exec_prog(log_file_name, NULL, - "\"%s/pg_dump\" %s --schema-only --quote-all-identifiers " + "\"%s\" %s --schema-only --quote-all-identifiers " "--binary-upgrade --format=custom %s --file=\"%s\" %s", - new_cluster.bindir, cluster_conn_opts(&old_cluster), + new_cluster.pg_dump_path, cluster_conn_opts(&old_cluster), log_opts.verbose ? "--verbose" : "", sql_file_name, escaped_connstr.data); diff --git a/src/bin/pg_upgrade/exec.c b/src/bin/pg_upgrade/exec.c index b31cda8fec..aa4e3fda43 100644 --- a/src/bin/pg_upgrade/exec.c +++ b/src/bin/pg_upgrade/exec.c @@ -16,7 +16,7 @@ static void check_data_dir(ClusterInfo *cluster); static void check_bin_dir(ClusterInfo *cluster); static void get_bin_version(ClusterInfo *cluster); -static void validate_exec(const char *dir, const char *cmdName); +static void validate_exec(ClusterInfo *cluster, char **path, const char *cmdName); #ifdef WIN32 static int win32_check_directory_write_permissions(void); @@ -37,7 +37,7 @@ get_bin_version(ClusterInfo *cluster) int v1 = 0, v2 = 0; - snprintf(cmd, sizeof(cmd), "\"%s/pg_ctl\" --version", cluster->bindir); + snprintf(cmd, sizeof(cmd), "\"%s/pg_ctl\" --version", cluster->libexecdir); if ((output = popen(cmd, "r")) == NULL || fgets(cmd_output, sizeof(cmd_output), output) == NULL) @@ -357,10 +357,9 @@ check_data_dir(ClusterInfo *cluster) /* * check_bin_dir() * - * This function searches for the executables that we expect to find - * in the binaries directory. If we find that a required executable - * is missing (or secured against us), we display an error message and - * exit(). + * This function searches for the executables that we expect to find in the + * libexec or binaries directory. If we find that a required executable is + * missing (or secured against us), we display an error message and exit(). */ static void check_bin_dir(ClusterInfo *cluster) @@ -375,9 +374,17 @@ check_bin_dir(ClusterInfo *cluster) report_status(PG_FATAL, "\"%s\" is not a directory\n", cluster->bindir); - validate_exec(cluster->bindir, "postgres"); - validate_exec(cluster->bindir, "pg_controldata"); - validate_exec(cluster->bindir, "pg_ctl"); + /* + * libexec will not exist on older clusters, but if it exists, it should + * be a directory. + */ + if (stat(cluster->libexecdir, &statBuf) == 0 && !S_ISDIR(statBuf.st_mode)) + report_status(PG_FATAL, "\"%s\" is not a directory\n", + cluster->libexecdir); + + validate_exec(cluster, &cluster->postgres_path, "postgres"); + validate_exec(cluster, &cluster->pg_controldata_path, "pg_controldata"); + validate_exec(cluster, &cluster->pg_ctl_path, "pg_ctl"); /* * Fetch the binary version after checking for the existence of pg_ctl. @@ -388,9 +395,9 @@ check_bin_dir(ClusterInfo *cluster) /* pg_resetxlog has been renamed to pg_resetwal in version 10 */ if (GET_MAJOR_VERSION(cluster->bin_version) < 1000) - validate_exec(cluster->bindir, "pg_resetxlog"); + validate_exec(cluster, &cluster->pg_resetwal_path, "pg_resetxlog"); else - validate_exec(cluster->bindir, "pg_resetwal"); + validate_exec(cluster, &cluster->pg_resetwal_path, "pg_resetwal"); if (cluster == &new_cluster) { @@ -399,63 +406,81 @@ check_bin_dir(ClusterInfo *cluster) * pg_dumpall are used to dump the old cluster, but must be of the * target version. */ - validate_exec(cluster->bindir, "initdb"); - validate_exec(cluster->bindir, "pg_dump"); - validate_exec(cluster->bindir, "pg_dumpall"); - validate_exec(cluster->bindir, "pg_restore"); - validate_exec(cluster->bindir, "psql"); - validate_exec(cluster->bindir, "vacuumdb"); + validate_exec(cluster, &cluster->initdb_path, "initdb"); + validate_exec(cluster, &cluster->pg_dump_path, "pg_dump"); + validate_exec(cluster, &cluster->pg_dumpall_path, "pg_dumpall"); + validate_exec(cluster, &cluster->pg_restore_path, "pg_restore"); + validate_exec(cluster, &cluster->psql_path, "psql"); + validate_exec(cluster, &cluster->vacuumdb_path, "vacuumdb"); } } - /* * validate_exec() * - * validate "path" as an executable file + * validate "cmdName" as an executable file, either in the cluster's bindir + * or libexecdir (if any), and store the valid location for the command in + * "path" for future use. */ static void -validate_exec(const char *dir, const char *cmdName) +validate_exec(ClusterInfo *cluster, char **path, const char *cmdName) { - char path[MAXPGPATH]; + int idx; struct stat buf; + const char *dir[] = { cluster->bindir, cluster->libexecdir, NULL }; - snprintf(path, sizeof(path), "%s/%s", dir, cmdName); + if (cluster->libexecdir == NULL) + pg_fatal("libexecdir is null in validate_exec"); + + *path = (char *) pg_malloc0(MAXPGPATH); + for (idx = 0; dir[idx]; idx++) + { + snprintf(*path, MAXPGPATH, "%s/%s", dir[idx], cmdName); #ifdef WIN32 - /* Windows requires a .exe suffix for stat() */ - if (strlen(path) <= strlen(EXE_EXT) || - pg_strcasecmp(path + strlen(path) - strlen(EXE_EXT), EXE_EXT) != 0) - strlcat(path, EXE_EXT, sizeof(path)); + /* Windows requires a .exe suffix for stat() */ + if (strlen(*path) <= strlen(EXE_EXT) || + pg_strcasecmp(*path + strlen(*path) - strlen(EXE_EXT), EXE_EXT) != 0) + strlcat(*path, EXE_EXT, sizeof(*path)); #endif - /* - * Ensure that the file exists and is a regular file. - */ - if (stat(path, &buf) < 0) - pg_fatal("check for \"%s\" failed: %s\n", - path, strerror(errno)); - else if (!S_ISREG(buf.st_mode)) - pg_fatal("check for \"%s\" failed: not a regular file\n", - path); + /* + * Ensure that the file exists and is a regular file. + */ + if (stat(*path, &buf) < 0) + { + printf("check for %s failed (dir = %s, cmd = %s)", *path, dir[idx], cmdName); + continue; + } + else if (!S_ISREG(buf.st_mode)) + pg_fatal("check for \"%s\" failed: not a regular file\n", + *path); - /* - * Ensure that the file is both executable and readable (required for - * dynamic loading). - */ + /* + * Ensure that the file is both executable and readable (required for + * dynamic loading). + */ #ifndef WIN32 - if (access(path, R_OK) != 0) + if (access(*path, R_OK) != 0) #else - if ((buf.st_mode & S_IRUSR) == 0) + if ((buf.st_mode & S_IRUSR) == 0) #endif - pg_fatal("check for \"%s\" failed: cannot read file (permission denied)\n", - path); + pg_fatal("check for \"%s\" failed: cannot read file (permission denied)\n", + *path); #ifndef WIN32 - if (access(path, X_OK) != 0) + if (access(*path, X_OK) != 0) #else - if ((buf.st_mode & S_IXUSR) == 0) + if ((buf.st_mode & S_IXUSR) == 0) #endif - pg_fatal("check for \"%s\" failed: cannot execute (permission denied)\n", - path); + pg_fatal("check for \"%s\" failed: cannot execute (permission denied)\n", + *path); + + /* If we get here, all checks passed */ + return; + } + + /* If we get here, we failed the stat() call, and errno should be set */ + pg_fatal("check for \"%s\" failed: %s\n", + *path, strerror(errno)); } diff --git a/src/bin/pg_upgrade/option.c b/src/bin/pg_upgrade/option.c index aca1ee8b48..ee245c7e02 100644 --- a/src/bin/pg_upgrade/option.c +++ b/src/bin/pg_upgrade/option.c @@ -43,6 +43,8 @@ parseCommandLine(int argc, char *argv[]) {"new-datadir", required_argument, NULL, 'D'}, {"old-bindir", required_argument, NULL, 'b'}, {"new-bindir", required_argument, NULL, 'B'}, + {"old-libexecdir", required_argument, NULL, 'l'}, + {"new-libexecdir", required_argument, NULL, 'L'}, {"old-options", required_argument, NULL, 'o'}, {"new-options", required_argument, NULL, 'O'}, {"old-port", required_argument, NULL, 'p'}, @@ -101,7 +103,7 @@ parseCommandLine(int argc, char *argv[]) if (os_user_effective_id == 0) pg_fatal("%s: cannot be run as root\n", os_info.progname); - while ((option = getopt_long(argc, argv, "d:D:b:B:cj:ko:O:p:P:rs:U:v", + while ((option = getopt_long(argc, argv, "d:D:b:B:cj:kl:L:o:O:p:P:rs:U:v", long_options, &optindex)) != -1) { switch (option) @@ -134,6 +136,14 @@ parseCommandLine(int argc, char *argv[]) user_opts.transfer_mode = TRANSFER_MODE_LINK; break; + case 'l': + old_cluster.libexecdir = pg_strdup(optarg); + break; + + case 'L': + new_cluster.libexecdir = pg_strdup(optarg); + break; + case 'o': /* append option? */ if (!old_cluster.pgopts) @@ -251,6 +261,10 @@ parseCommandLine(int argc, char *argv[]) "-b", _("old cluster binaries reside"), false); check_required_directory(&new_cluster.bindir, "PGBINNEW", false, "-B", _("new cluster binaries reside"), true); + check_required_directory(&old_cluster.libexecdir, "PGLIBEXECOLD", false, + "-l", _("old cluster commands reside"), false); + check_required_directory(&new_cluster.libexecdir, "PGLIBEXECNEW", false, + "-L", _("new cluster commands reside"), true); check_required_directory(&old_cluster.pgdata, "PGDATAOLD", false, "-d", _("old cluster data resides"), false); check_required_directory(&new_cluster.pgdata, "PGDATANEW", false, @@ -289,25 +303,27 @@ usage(void) printf(_("Usage:\n")); printf(_(" pg_upgrade [OPTION]...\n\n")); printf(_("Options:\n")); - printf(_(" -b, --old-bindir=BINDIR old cluster executable directory\n")); - printf(_(" -B, --new-bindir=BINDIR new cluster executable directory (default\n" - " same directory as pg_upgrade)\n")); - printf(_(" -c, --check check clusters only, don't change any data\n")); - printf(_(" -d, --old-datadir=DATADIR old cluster data directory\n")); - printf(_(" -D, --new-datadir=DATADIR new cluster data directory\n")); - printf(_(" -j, --jobs=NUM number of simultaneous processes or threads to use\n")); - printf(_(" -k, --link link instead of copying files to new cluster\n")); - printf(_(" -o, --old-options=OPTIONS old cluster options to pass to the server\n")); - printf(_(" -O, --new-options=OPTIONS new cluster options to pass to the server\n")); - printf(_(" -p, --old-port=PORT old cluster port number (default %d)\n"), old_cluster.port); - printf(_(" -P, --new-port=PORT new cluster port number (default %d)\n"), new_cluster.port); - printf(_(" -r, --retain retain SQL and log files after success\n")); - printf(_(" -s, --socketdir=DIR socket directory to use (default current dir.)\n")); - printf(_(" -U, --username=NAME cluster superuser (default \"%s\")\n"), os_info.user); - printf(_(" -v, --verbose enable verbose internal logging\n")); - printf(_(" -V, --version display version information, then exit\n")); - printf(_(" --clone clone instead of copying files to new cluster\n")); - printf(_(" -?, --help show this help, then exit\n")); + printf(_(" -b, --old-bindir=BINDIR old cluster executable directory\n")); + printf(_(" -B, --new-bindir=BINDIR new cluster executable directory (default\n" + " same directory as pg_upgrade)\n")); + printf(_(" -l, --old-libexecdir=LIBEXECDIR old cluster command directory\n")); + printf(_(" -L, --new-libexecdir=LIBEXECDIR new cluster command directory\n")); + printf(_(" -c, --check check clusters only, don't change any data\n")); + printf(_(" -d, --old-datadir=DATADIR old cluster data directory\n")); + printf(_(" -D, --new-datadir=DATADIR new cluster data directory\n")); + printf(_(" -j, --jobs=NUM number of simultaneous processes or threads to use\n")); + printf(_(" -k, --link link instead of copying files to new cluster\n")); + printf(_(" -o, --old-options=OPTIONS old cluster options to pass to the server\n")); + printf(_(" -O, --new-options=OPTIONS new cluster options to pass to the server\n")); + printf(_(" -p, --old-port=PORT old cluster port number (default %d)\n"), old_cluster.port); + printf(_(" -P, --new-port=PORT new cluster port number (default %d)\n"), new_cluster.port); + printf(_(" -r, --retain retain SQL and log files after success\n")); + printf(_(" -s, --socketdir=DIR socket directory to use (default current dir.)\n")); + printf(_(" -U, --username=NAME cluster superuser (default \"%s\")\n"), os_info.user); + printf(_(" -v, --verbose enable verbose internal logging\n")); + printf(_(" -V, --version display version information, then exit\n")); + printf(_(" --clone clone instead of copying files to new cluster\n")); + printf(_(" -?, --help show this help, then exit\n")); printf(_("\n" "Before running pg_upgrade you must:\n" " create a new database cluster (using the new version of initdb)\n" @@ -318,22 +334,28 @@ usage(void) " the data directory for the old cluster (-d DATADIR)\n" " the data directory for the new cluster (-D DATADIR)\n" " the \"bin\" directory for the old version (-b BINDIR)\n" - " the \"bin\" directory for the new version (-B BINDIR)\n")); + " the \"bin\" directory for the new version (-B BINDIR)\n" + " the \"libexec\" directory for the old version (-l LIBEXECDIR)\n" + " the \"libexec\" directory for the new version (-L LIBEXECDIR)\n")); printf(_("\n" "For example:\n" - " pg_upgrade -d oldCluster/data -D newCluster/data -b oldCluster/bin -B newCluster/bin\n" + " pg_upgrade -d oldCluster/data -D newCluster/data -b oldCluster/bin -B newCluster/bin -l oldCluster/libexec -L newCluster/libexec\n" "or\n")); #ifndef WIN32 printf(_(" $ export PGDATAOLD=oldCluster/data\n" " $ export PGDATANEW=newCluster/data\n" " $ export PGBINOLD=oldCluster/bin\n" " $ export PGBINNEW=newCluster/bin\n" + " $ export PGLIBEXECOLD=oldCluster/libexec\n" + " $ export PGLIBEXECNEW=newCluster/libexec\n" " $ pg_upgrade\n")); #else printf(_(" C:\\> set PGDATAOLD=oldCluster/data\n" " C:\\> set PGDATANEW=newCluster/data\n" " C:\\> set PGBINOLD=oldCluster/bin\n" " C:\\> set PGBINNEW=newCluster/bin\n" + " C:\\> set PGLIBEXECOLD=oldCluster/libexec\n" + " C:\\> set PGLIBEXECNEW=newCluster/libexec\n" " C:\\> pg_upgrade\n")); #endif printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT); diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c index 70194eb096..8de4710b5b 100644 --- a/src/bin/pg_upgrade/pg_upgrade.c +++ b/src/bin/pg_upgrade/pg_upgrade.c @@ -165,14 +165,14 @@ main(int argc, char **argv) */ prep_status("Setting next OID for new cluster"); exec_prog(UTILITY_LOG_FILE, NULL, true, true, - "\"%s/pg_resetwal\" -o %u \"%s\"", - new_cluster.bindir, old_cluster.controldata.chkpnt_nxtoid, + "\"%s\" -o %u \"%s\"", + new_cluster.pg_resetwal_path, old_cluster.controldata.chkpnt_nxtoid, new_cluster.pgdata); check_ok(); prep_status("Sync data directory to disk"); exec_prog(UTILITY_LOG_FILE, NULL, true, true, - "\"%s/initdb\" --sync-only \"%s\"", new_cluster.bindir, + "\"%s\" --sync-only \"%s\"", new_cluster.initdb_path, new_cluster.pgdata); check_ok(); @@ -208,11 +208,11 @@ setup(char *argv0, bool *live_check) check_pghost_envvar(); /* - * In case the user hasn't specified the directory for the new binaries - * with -B, default to using the path of the currently executed pg_upgrade - * binary. + * In case the user hasn't specified the directory for the new commands + * with -L, default to using the path of the currently executed pg_upgrade + * command. */ - if (!new_cluster.bindir) + if (!new_cluster.libexecdir) { char exec_path[MAXPGPATH]; @@ -221,7 +221,22 @@ setup(char *argv0, bool *live_check) /* Trim off program name and keep just path */ *last_dir_separator(exec_path) = '\0'; canonicalize_path(exec_path); - new_cluster.bindir = pg_strdup(exec_path); + new_cluster.libexecdir = pg_strdup(exec_path); + } + + /* + * In case the user hasn't specified the directory for the new binaries + * with -B, construct it from the path for libexecdir. + * + * TODO: handle custom bin locations which are not where we expect. + */ + if (!new_cluster.bindir) + { + char tmp[MAXPGPATH * 2]; /* room for relative path change */ + + snprintf(tmp, sizeof(tmp), "%s/../bin", new_cluster.libexecdir); + canonicalize_path(tmp); + new_cluster.bindir = pg_strdup(tmp); } verify_directories(); @@ -272,8 +287,8 @@ prepare_new_cluster(void) */ prep_status("Analyzing all rows in the new cluster"); exec_prog(UTILITY_LOG_FILE, NULL, true, true, - "\"%s/vacuumdb\" %s --all --analyze %s", - new_cluster.bindir, cluster_conn_opts(&new_cluster), + "\"%s\" %s --all --analyze %s", + new_cluster.vacuumdb_path, cluster_conn_opts(&new_cluster), log_opts.verbose ? "--verbose" : ""); check_ok(); @@ -285,8 +300,8 @@ prepare_new_cluster(void) */ prep_status("Freezing all rows in the new cluster"); exec_prog(UTILITY_LOG_FILE, NULL, true, true, - "\"%s/vacuumdb\" %s --all --freeze %s", - new_cluster.bindir, cluster_conn_opts(&new_cluster), + "\"%s\" %s --all --freeze %s", + new_cluster.vacuumdb_path, cluster_conn_opts(&new_cluster), log_opts.verbose ? "--verbose" : ""); check_ok(); } @@ -306,8 +321,8 @@ prepare_new_globals(void) prep_status("Restoring global objects in the new cluster"); exec_prog(UTILITY_LOG_FILE, NULL, true, true, - "\"%s/psql\" " EXEC_PSQL_ARGS " %s -f \"%s\"", - new_cluster.bindir, cluster_conn_opts(&new_cluster), + "\"%s\" " EXEC_PSQL_ARGS " %s -f \"%s\"", + new_cluster.psql_path, cluster_conn_opts(&new_cluster), GLOBALS_DUMP_FILE); check_ok(); } @@ -351,9 +366,9 @@ create_new_objects(void) NULL, true, true, - "\"%s/pg_restore\" %s %s --exit-on-error --verbose " + "\"%s\" %s %s --exit-on-error --verbose " "--dbname postgres \"%s\"", - new_cluster.bindir, + new_cluster.pg_restore_path, cluster_conn_opts(&new_cluster), create_opts, sql_file_name); @@ -388,9 +403,9 @@ create_new_objects(void) parallel_exec_prog(log_file_name, NULL, - "\"%s/pg_restore\" %s %s --exit-on-error --verbose " + "\"%s\" %s %s --exit-on-error --verbose " "--dbname template1 \"%s\"", - new_cluster.bindir, + new_cluster.pg_restore_path, cluster_conn_opts(&new_cluster), create_opts, sql_file_name); @@ -474,17 +489,17 @@ copy_xact_xlog_xid(void) /* set the next transaction id and epoch of the new cluster */ prep_status("Setting next transaction ID and epoch for new cluster"); exec_prog(UTILITY_LOG_FILE, NULL, true, true, - "\"%s/pg_resetwal\" -f -x %u \"%s\"", - new_cluster.bindir, old_cluster.controldata.chkpnt_nxtxid, + "\"%s\" -f -x %u \"%s\"", + new_cluster.pg_resetwal_path, old_cluster.controldata.chkpnt_nxtxid, new_cluster.pgdata); exec_prog(UTILITY_LOG_FILE, NULL, true, true, - "\"%s/pg_resetwal\" -f -e %u \"%s\"", - new_cluster.bindir, old_cluster.controldata.chkpnt_nxtepoch, + "\"%s\" -f -e %u \"%s\"", + new_cluster.pg_resetwal_path, old_cluster.controldata.chkpnt_nxtepoch, new_cluster.pgdata); /* must reset commit timestamp limits also */ exec_prog(UTILITY_LOG_FILE, NULL, true, true, - "\"%s/pg_resetwal\" -f -c %u,%u \"%s\"", - new_cluster.bindir, + "\"%s\" -f -c %u,%u \"%s\"", + new_cluster.pg_resetwal_path, old_cluster.controldata.chkpnt_nxtxid, old_cluster.controldata.chkpnt_nxtxid, new_cluster.pgdata); @@ -509,8 +524,8 @@ copy_xact_xlog_xid(void) * counters here and the oldest multi present on system. */ exec_prog(UTILITY_LOG_FILE, NULL, true, true, - "\"%s/pg_resetwal\" -O %u -m %u,%u \"%s\"", - new_cluster.bindir, + "\"%s\" -O %u -m %u,%u \"%s\"", + new_cluster.pg_resetwal_path, old_cluster.controldata.chkpnt_nxtmxoff, old_cluster.controldata.chkpnt_nxtmulti, old_cluster.controldata.chkpnt_oldstMulti, @@ -537,8 +552,8 @@ copy_xact_xlog_xid(void) * next=MaxMultiXactId, but multixact.c can cope with that just fine. */ exec_prog(UTILITY_LOG_FILE, NULL, true, true, - "\"%s/pg_resetwal\" -m %u,%u \"%s\"", - new_cluster.bindir, + "\"%s\" -m %u,%u \"%s\"", + new_cluster.pg_resetwal_path, old_cluster.controldata.chkpnt_nxtmulti + 1, old_cluster.controldata.chkpnt_nxtmulti, new_cluster.pgdata); @@ -549,7 +564,7 @@ copy_xact_xlog_xid(void) prep_status("Resetting WAL archives"); exec_prog(UTILITY_LOG_FILE, NULL, true, true, /* use timeline 1 to match controldata and no WAL history file */ - "\"%s/pg_resetwal\" -l 00000001%s \"%s\"", new_cluster.bindir, + "\"%s\" -l 00000001%s \"%s\"", new_cluster.pg_resetwal_path, old_cluster.controldata.nextxlogfile + 8, new_cluster.pgdata); check_ok(); diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h index 8b90cefbe0..272e461b0d 100644 --- a/src/bin/pg_upgrade/pg_upgrade.h +++ b/src/bin/pg_upgrade/pg_upgrade.h @@ -260,6 +260,7 @@ typedef struct char *pgconfig; /* pathname for cluster's config file * directory */ char *bindir; /* pathname for cluster's executable directory */ + char *libexecdir; /* pathname for cluster's command directory */ char *pgopts; /* options to pass to the server, like pg_ctl * -o */ char *sockdir; /* directory for Unix Domain socket, if any */ @@ -268,6 +269,18 @@ typedef struct char major_version_str[64]; /* string PG_VERSION of cluster */ uint32 bin_version; /* version returned from pg_ctl */ const char *tablespace_suffix; /* directory specification */ + + /* Validated paths to named executables */ + char *postgres_path; + char *pg_controldata_path; + char *pg_ctl_path; + char *pg_resetwal_path; + char *initdb_path; + char *pg_dump_path; + char *pg_dumpall_path; + char *pg_restore_path; + char *psql_path; + char *vacuumdb_path; } ClusterInfo; diff --git a/src/bin/pg_upgrade/server.c b/src/bin/pg_upgrade/server.c index 7d17280ecb..fbe114c8bd 100644 --- a/src/bin/pg_upgrade/server.c +++ b/src/bin/pg_upgrade/server.c @@ -241,8 +241,8 @@ start_postmaster(ClusterInfo *cluster, bool report_and_exit_on_error) * win on ext4. */ snprintf(cmd, sizeof(cmd), - "\"%s/pg_ctl\" -w -l \"%s\" -D \"%s\" -o \"-p %d%s%s %s%s\" start", - cluster->bindir, SERVER_LOG_FILE, cluster->pgconfig, cluster->port, + "\"%s\" -w -l \"%s\" -D \"%s\" -o \"-p %d%s%s %s%s\" start", + cluster->pg_ctl_path, SERVER_LOG_FILE, cluster->pgconfig, cluster->port, (cluster->controldata.cat_ver >= BINARY_UPGRADE_SERVER_FLAG_CAT_VER) ? " -b" : " -c autovacuum=off -c autovacuum_freeze_max_age=2000000000", @@ -336,8 +336,8 @@ stop_postmaster(bool in_atexit) return; /* no cluster running */ exec_prog(SERVER_STOP_LOG_FILE, NULL, !in_atexit, !in_atexit, - "\"%s/pg_ctl\" -w -D \"%s\" -o \"%s\" %s stop", - cluster->bindir, cluster->pgconfig, + "\"%s\" -w -D \"%s\" -o \"%s\" %s stop", + cluster->pg_ctl_path, cluster->pgconfig, cluster->pgopts ? cluster->pgopts : "", in_atexit ? "-m fast" : "-m smart"); diff --git a/src/bin/pg_upgrade/test.sh b/src/bin/pg_upgrade/test.sh index 10a28d8133..ef3f3b32ec 100644 --- a/src/bin/pg_upgrade/test.sh +++ b/src/bin/pg_upgrade/test.sh @@ -76,6 +76,7 @@ rm -rf "$temp_root" mkdir "$temp_root" : ${oldbindir=$bindir} +: ${oldlibexecdir=$libexecdir} : ${oldsrc=../../..} oldsrc=`cd "$oldsrc" && pwd` @@ -86,13 +87,13 @@ newsrc=`cd ../../.. && pwd` # below would try to use psql from the proper installation directory # of the target version, which might be outdated or not exist. But # don't override anything else that's already in EXTRA_REGRESS_OPTS. -EXTRA_REGRESS_OPTS="$EXTRA_REGRESS_OPTS --bindir='$oldbindir'" +EXTRA_REGRESS_OPTS="$EXTRA_REGRESS_OPTS --bindir='$oldbindir' --libexecdir='$oldlibexecdir'" export EXTRA_REGRESS_OPTS # While in normal cases this will already be set up, adding bindir to # path allows test.sh to be invoked with different versions as # described in ./TESTING -PATH=$bindir:$PATH +PATH=$bindir:$libexecdir:$PATH export PATH BASE_PGDATA="$temp_root/data" @@ -150,8 +151,8 @@ done EXTRA_REGRESS_OPTS="$EXTRA_REGRESS_OPTS --port=$PGPORT" export EXTRA_REGRESS_OPTS -standard_initdb "$oldbindir"/initdb -"$oldbindir"/pg_ctl start -l "$logdir/postmaster1.log" -o "$POSTMASTER_OPTS" -w +standard_initdb "$oldlibexecdir"/initdb +"$oldlibexecdir"/pg_ctl start -l "$logdir/postmaster1.log" -o "$POSTMASTER_OPTS" -w # Create databases with names covering the ASCII bytes other than NUL, BEL, # LF, or CR. BEL would ring the terminal bell in the course of this test, and @@ -204,7 +205,7 @@ if "$MAKE" -C "$oldsrc" installcheck-parallel; then else make_installcheck_status=$? fi -"$oldbindir"/pg_ctl -m fast stop +"$oldlibexecdir"/pg_ctl -m fast stop if [ -n "$createdb_status" ]; then exit 1 fi @@ -223,7 +224,7 @@ PGDATA="$BASE_PGDATA" standard_initdb 'initdb' -pg_upgrade $PG_UPGRADE_OPTS -d "${PGDATA}.old" -D "$PGDATA" -b "$oldbindir" -p "$PGPORT" -P "$PGPORT" +pg_upgrade $PG_UPGRADE_OPTS -d "${PGDATA}.old" -D "$PGDATA" -b "$oldbindir" -l "$oldlibexecdir" -p "$PGPORT" -P "$PGPORT" # make sure all directories and files have group permissions, on Unix hosts # Windows hosts don't support Unix-y permissions. diff --git a/src/bin/pg_verifybackup/Makefile b/src/bin/pg_verifybackup/Makefile index c07643b129..710a6a9972 100644 --- a/src/bin/pg_verifybackup/Makefile +++ b/src/bin/pg_verifybackup/Makefile @@ -21,13 +21,13 @@ pg_verifybackup: $(OBJS) | submake-libpq submake-libpgport submake-libpgfeutils $(CC) $(CFLAGS) $^ $(LDFLAGS) $(LDFLAGS_EX) $(LIBS) -o $@$(X) install: all installdirs - $(INSTALL_PROGRAM) pg_verifybackup$(X) '$(DESTDIR)$(bindir)/pg_verifybackup$(X)' + $(INSTALL_PROGRAM) pg_verifybackup$(X) '$(DESTDIR)$(libexecdir)/pg_verifybackup$(X)' installdirs: - $(MKDIR_P) '$(DESTDIR)$(bindir)' + $(MKDIR_P) '$(DESTDIR)$(libexecdir)' uninstall: - rm -f '$(DESTDIR)$(bindir)/pg_verifybackup$(X)' + rm -f '$(DESTDIR)$(libexecdir)/pg_verifybackup$(X)' clean distclean maintainer-clean: rm -f pg_verifybackup$(X) $(OBJS) diff --git a/src/bin/pg_verifybackup/pg_verifybackup.c b/src/bin/pg_verifybackup/pg_verifybackup.c index 70b6ffdec0..6979006671 100644 --- a/src/bin/pg_verifybackup/pg_verifybackup.c +++ b/src/bin/pg_verifybackup/pg_verifybackup.c @@ -285,9 +285,9 @@ main(int argc, char **argv) int ret; pg_waldump_path = pg_malloc(MAXPGPATH); - ret = find_other_exec(argv[0], "pg_waldump", - "pg_waldump (PostgreSQL) " PG_VERSION "\n", - pg_waldump_path); + ret = find_other_cmd(argv[0], "pg_waldump", + "pg_waldump (PostgreSQL) " PG_VERSION "\n", + pg_waldump_path); if (ret < 0) { char full_path[MAXPGPATH]; diff --git a/src/bin/pg_verifybackup/t/008_redirect.pl b/src/bin/pg_verifybackup/t/008_redirect.pl new file mode 100644 index 0000000000..719febb3b3 --- /dev/null +++ b/src/bin/pg_verifybackup/t/008_redirect.pl @@ -0,0 +1,17 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 4; + +command_fails_like( + ['pg', 'verifybackup'], + qr/fatal: no backup directory specified/, + 'pg pg_verifybackup fails'); + +command_ok( + ['pg', 'verifybackup', '--version'], + 'pg pg_verifybackup version ok'); + +command_ok( + ['pg', 'verifybackup', '--help'], + 'pg pg_verifybackup help ok'); diff --git a/src/bin/pg_waldump/Makefile b/src/bin/pg_waldump/Makefile index 9f333d0c8a..a8680f115d 100644 --- a/src/bin/pg_waldump/Makefile +++ b/src/bin/pg_waldump/Makefile @@ -33,13 +33,13 @@ $(RMGRDESCSOURCES): % : $(top_srcdir)/src/backend/access/rmgrdesc/% rm -f $@ && $(LN_S) $< . install: all installdirs - $(INSTALL_PROGRAM) pg_waldump$(X) '$(DESTDIR)$(bindir)/pg_waldump$(X)' + $(INSTALL_PROGRAM) pg_waldump$(X) '$(DESTDIR)$(libexecdir)/pg_waldump$(X)' installdirs: - $(MKDIR_P) '$(DESTDIR)$(bindir)' + $(MKDIR_P) '$(DESTDIR)$(libexecdir)' uninstall: - rm -f '$(DESTDIR)$(bindir)/pg_waldump$(X)' + rm -f '$(DESTDIR)$(libexecdir)/pg_waldump$(X)' clean distclean maintainer-clean: rm -f pg_waldump$(X) $(OBJS) $(RMGRDESCSOURCES) xlogreader.c diff --git a/src/bin/pg_waldump/t/002_redirect.pl b/src/bin/pg_waldump/t/002_redirect.pl new file mode 100644 index 0000000000..84453df680 --- /dev/null +++ b/src/bin/pg_waldump/t/002_redirect.pl @@ -0,0 +1,17 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 4; + +command_fails_like( + ['pg', 'waldump'], + qr/error: no arguments specified/, + 'pg pg_waldump fails'); + +command_ok( + ['pg', 'waldump', '--version'], + 'pg pg_waldump version ok'); + +command_ok( + ['pg', 'waldump', '--help'], + 'pg pg_waldump help ok'); diff --git a/src/bin/pgbench/Makefile b/src/bin/pgbench/Makefile index f402fe7b91..4b430cb53d 100644 --- a/src/bin/pgbench/Makefile +++ b/src/bin/pgbench/Makefile @@ -32,13 +32,13 @@ exprparse.o: exprscan.c distprep: exprparse.c exprscan.c install: all installdirs - $(INSTALL_PROGRAM) pgbench$(X) '$(DESTDIR)$(bindir)/pgbench$(X)' + $(INSTALL_PROGRAM) pgbench$(X) '$(DESTDIR)$(libexecdir)/pgbench$(X)' installdirs: - $(MKDIR_P) '$(DESTDIR)$(bindir)' + $(MKDIR_P) '$(DESTDIR)$(libexecdir)' uninstall: - rm -f '$(DESTDIR)$(bindir)/pgbench$(X)' + rm -f '$(DESTDIR)$(libexecdir)/pgbench$(X)' clean distclean: rm -f pgbench$(X) $(OBJS) diff --git a/src/bin/pgbench/t/003_redirect.pl b/src/bin/pgbench/t/003_redirect.pl new file mode 100644 index 0000000000..bbc0a11e5b --- /dev/null +++ b/src/bin/pgbench/t/003_redirect.pl @@ -0,0 +1,17 @@ +use strict; +use warnings; +use TestLib; +use Test::More tests => 4; + +command_fails_like( + ['pg', 'bench'], + qr/error: connection to database .* failed/, + 'pg bench fails'); + +command_ok( + ['pg', 'bench', '--version'], + 'pg bench version ok'); + +command_ok( + ['pg', 'bench', '--help'], + 'pg bench help ok'); diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c index 3302bd4dd3..b50f3b6e46 100644 --- a/src/bin/psql/startup.c +++ b/src/bin/psql/startup.c @@ -755,6 +755,7 @@ process_psqlrc(char *argv0) char home[MAXPGPATH]; char rc_file[MAXPGPATH]; char my_exec_path[MAXPGPATH]; + char my_rootdir[MAXPGPATH]; char etc_path[MAXPGPATH]; char *envrc = getenv("PSQLRC"); @@ -764,7 +765,13 @@ process_psqlrc(char *argv0) exit(EXIT_FAILURE); } - get_etc_path(my_exec_path, etc_path); + if (find_my_rootdir(argv0, my_rootdir) < 0) + { + pg_log_fatal("could not find own program root directory"); + exit(EXIT_FAILURE); + } + + get_etc_path(my_rootdir, etc_path); snprintf(rc_file, MAXPGPATH, "%s/%s", etc_path, SYSPSQLRC); process_psqlrc_file(rc_file); diff --git a/src/bin/scripts/Makefile b/src/bin/scripts/Makefile index 42bfe475b2..047767e602 100644 --- a/src/bin/scripts/Makefile +++ b/src/bin/scripts/Makefile @@ -33,20 +33,20 @@ reindexdb: reindexdb.o common.o scripts_parallel.o $(WIN32RES) | submake-libpq s pg_isready: pg_isready.o common.o $(WIN32RES) | submake-libpq submake-libpgport submake-libpgfeutils install: all installdirs - $(INSTALL_PROGRAM) createdb$(X) '$(DESTDIR)$(bindir)'/createdb$(X) - $(INSTALL_PROGRAM) dropdb$(X) '$(DESTDIR)$(bindir)'/dropdb$(X) - $(INSTALL_PROGRAM) createuser$(X) '$(DESTDIR)$(bindir)'/createuser$(X) - $(INSTALL_PROGRAM) dropuser$(X) '$(DESTDIR)$(bindir)'/dropuser$(X) - $(INSTALL_PROGRAM) clusterdb$(X) '$(DESTDIR)$(bindir)'/clusterdb$(X) - $(INSTALL_PROGRAM) vacuumdb$(X) '$(DESTDIR)$(bindir)'/vacuumdb$(X) - $(INSTALL_PROGRAM) reindexdb$(X) '$(DESTDIR)$(bindir)'/reindexdb$(X) - $(INSTALL_PROGRAM) pg_isready$(X) '$(DESTDIR)$(bindir)'/pg_isready$(X) + $(INSTALL_PROGRAM) createdb$(X) '$(DESTDIR)$(libexecdir)'/createdb$(X) + $(INSTALL_PROGRAM) dropdb$(X) '$(DESTDIR)$(libexecdir)'/dropdb$(X) + $(INSTALL_PROGRAM) createuser$(X) '$(DESTDIR)$(libexecdir)'/createuser$(X) + $(INSTALL_PROGRAM) dropuser$(X) '$(DESTDIR)$(libexecdir)'/dropuser$(X) + $(INSTALL_PROGRAM) clusterdb$(X) '$(DESTDIR)$(libexecdir)'/clusterdb$(X) + $(INSTALL_PROGRAM) vacuumdb$(X) '$(DESTDIR)$(libexecdir)'/vacuumdb$(X) + $(INSTALL_PROGRAM) reindexdb$(X) '$(DESTDIR)$(libexecdir)'/reindexdb$(X) + $(INSTALL_PROGRAM) pg_isready$(X) '$(DESTDIR)$(libexecdir)'/pg_isready$(X) installdirs: - $(MKDIR_P) '$(DESTDIR)$(bindir)' + $(MKDIR_P) '$(DESTDIR)$(libexecdir)' uninstall: - rm -f $(addprefix '$(DESTDIR)$(bindir)'/, $(addsuffix $(X), $(PROGRAMS))) + rm -f $(addprefix '$(DESTDIR)$(libexecdir)'/, $(addsuffix $(X), $(PROGRAMS))) clean distclean maintainer-clean: rm -f $(addsuffix $(X), $(PROGRAMS)) $(addsuffix .o, $(PROGRAMS)) diff --git a/src/common/.gitignore b/src/common/.gitignore index ffa3284fbf..20ea8ef820 100644 --- a/src/common/.gitignore +++ b/src/common/.gitignore @@ -1 +1,2 @@ /kwlist_d.h +/pg_exec_relpath.h diff --git a/src/common/Makefile b/src/common/Makefile index d0be882cca..5279fe9056 100644 --- a/src/common/Makefile +++ b/src/common/Makefile @@ -99,6 +99,10 @@ OBJS_FRONTEND = \ OBJS_SHLIB = $(OBJS_FRONTEND:%.o=%_shlib.o) OBJS_SRV = $(OBJS_COMMON:%.o=%_srv.o) +# Generate bin and libexec relative paths +pg_exec_relpath.h: mk_exec_relpath.pl + $(PERL) $(srcdir)/$^ '$(DESTDIR)$(bindir)' '$(DESTDIR)$(libexecdir)' > $@ + # where to find gen_keywordlist.pl and subsidiary files TOOLSDIR = $(top_srcdir)/src/tools GEN_KEYWORDLIST = $(PERL) -I $(TOOLSDIR) $(TOOLSDIR)/gen_keywordlist.pl @@ -108,6 +112,8 @@ all: libpgcommon.a libpgcommon_shlib.a libpgcommon_srv.a distprep: kwlist_d.h +exec.o: pg_exec_relpath.h + # libpgcommon is needed by some contrib install: all installdirs $(INSTALL_STLIB) libpgcommon.a '$(DESTDIR)$(libdir)/libpgcommon.a' @@ -175,7 +181,7 @@ $(RYU_OBJS): CFLAGS += $(PERMIT_DECLARATION_AFTER_STATEMENT) # kwlist_d.h is in the distribution tarball, so it is not cleaned here. clean distclean: - rm -f libpgcommon.a libpgcommon_shlib.a libpgcommon_srv.a + rm -f libpgcommon.a libpgcommon_shlib.a libpgcommon_srv.a pg_exec_relpath.h rm -f $(OBJS_FRONTEND) $(OBJS_SHLIB) $(OBJS_SRV) maintainer-clean: distclean diff --git a/src/common/config_info.c b/src/common/config_info.c index 8a5dc64ec3..44089aeff5 100644 --- a/src/common/config_info.c +++ b/src/common/config_info.c @@ -30,18 +30,22 @@ * for pfreeing the result. */ ConfigData * -get_configdata(const char *my_exec_path, size_t *configdata_len) +get_configdata(const char *my_exec_path, const char *my_rootdir, + size_t *configdata_len) { ConfigData *configdata; char path[MAXPGPATH]; char *lastsep; int i = 0; + Assert (my_rootdir != NULL); + Assert (my_rootdir[0] != '\0'); + /* Adjust this to match the number of items filled below */ - *configdata_len = 23; + *configdata_len = 24; configdata = (ConfigData *) palloc(*configdata_len * sizeof(ConfigData)); - configdata[i].name = pstrdup("BINDIR"); + configdata[i].name = pstrdup("LIBEXECDIR"); strlcpy(path, my_exec_path, sizeof(path)); lastsep = strrchr(path, '/'); if (lastsep) @@ -51,13 +55,13 @@ get_configdata(const char *my_exec_path, size_t *configdata_len) i++; configdata[i].name = pstrdup("DOCDIR"); - get_doc_path(my_exec_path, path); + get_doc_path(my_rootdir, path); cleanup_path(path); configdata[i].setting = pstrdup(path); i++; configdata[i].name = pstrdup("HTMLDIR"); - get_html_path(my_exec_path, path); + get_html_path(my_rootdir, path); cleanup_path(path); configdata[i].setting = pstrdup(path); i++; @@ -75,13 +79,19 @@ get_configdata(const char *my_exec_path, size_t *configdata_len) i++; configdata[i].name = pstrdup("INCLUDEDIR-SERVER"); - get_includeserver_path(my_exec_path, path); + get_includeserver_path(my_rootdir, path); cleanup_path(path); configdata[i].setting = pstrdup(path); i++; configdata[i].name = pstrdup("LIBDIR"); - get_lib_path(my_exec_path, path); + get_lib_path(my_rootdir, path); + cleanup_path(path); + configdata[i].setting = pstrdup(path); + i++; + + configdata[i].name = pstrdup("BINDIR"); + get_bin_path(my_rootdir, path); cleanup_path(path); configdata[i].setting = pstrdup(path); i++; @@ -99,7 +109,7 @@ get_configdata(const char *my_exec_path, size_t *configdata_len) i++; configdata[i].name = pstrdup("MANDIR"); - get_man_path(my_exec_path, path); + get_man_path(my_rootdir, path); cleanup_path(path); configdata[i].setting = pstrdup(path); i++; @@ -111,7 +121,7 @@ get_configdata(const char *my_exec_path, size_t *configdata_len) i++; configdata[i].name = pstrdup("SYSCONFDIR"); - get_etc_path(my_exec_path, path); + get_etc_path(my_rootdir, path); cleanup_path(path); configdata[i].setting = pstrdup(path); i++; diff --git a/src/common/exec.c b/src/common/exec.c index f39b0a294b..90ac524ac5 100644 --- a/src/common/exec.c +++ b/src/common/exec.c @@ -25,6 +25,8 @@ #include #include +#include "pg_exec_relpath.h" + /* * Hacky solution to allow expressing both frontend and backend error reports * in one macro call. First argument of log_error is an errcode() call of @@ -109,6 +111,66 @@ validate_exec(const char *path) return is_x ? (is_r ? 0 : -2) : -1; } +/* + * find_my_rootdir -- find an absolute path to the postgres installation + * + * argv0 is the name passed on the command line + * retpath is the output area (must be of size MAXPGPATH) + * returns 0 if OK, -1 if error. + */ +int +find_my_rootdir(const char *argv0, char *retpath) +{ + char path[MAXPGPATH * 2]; /* room for relative path changes */ + + /* + * Get path to my executable. This should be somewhere in either + * bindir or libexecdir, depending on who is calling. + */ + if (find_my_exec(argv0, path)) + return -1; + + /* Trim off program name and keep just directory */ + *last_dir_separator(path) = '\0'; + canonicalize_path(path); + + /* + * If our executable was in bindir or libexecdir, one of these + * two will match and we can return the rootdir, otherwise we + * don't know what the rootdir is and must return error. + */ + if (trim_path_suffix(path, BIN_SUFFIX, retpath) == 0) + return 0; + if (trim_path_suffix(path, LIBEXEC_SUFFIX, retpath) == 0) + return 0; + return -1; +} + +int +find_my_libexecdir(const char *argv0, char *retpath) +{ + /* Get the root directory based on my executable */ + if (find_my_rootdir(argv0, retpath)) + return -1; + + /* Append the libexecdir suffix to the root directory */ + strcpy(retpath + strlen(retpath), LIBEXEC_SUFFIX); + canonicalize_path(retpath); + return 0; +} + +int +find_my_bindir(const char *argv0, char *retpath) +{ + /* Get the root directory based on my executable */ + if (find_my_rootdir(argv0, retpath)) + return -1; + + /* Append the bindir suffix to the root directory */ + strcpy(retpath + strlen(retpath), BIN_SUFFIX); + canonicalize_path(retpath); + return 0; +} /* * find_my_exec -- find an absolute path to a valid executable @@ -314,6 +376,82 @@ resolve_symlinks(char *path) return 0; } +/* + * Helper function for find_other_cmd. We have to change directories + * from 'path' to 'path/relpath' if and only if 'path' ends with 'suffix'. + * Then we check whether the target exists and is of the right version. + */ +static int +check_cmd(const char *path, const char *suffix, const char *relpath, + const char *target, const char *versionstr, char *retpath) +{ + char cmd[MAXPGPATH]; + char line[MAXPGPATH]; + int pathlen = strlen(path); + int suffixlen = strlen(suffix); + + /* If our path ends with the given suffix, then we follow the relpath */ + if (suffixlen <= pathlen && strcmp(path+pathlen-suffixlen, suffix) == 0) + snprintf(retpath, MAXPGPATH, "%s%s%s%s", path, relpath, target, EXE); + else + snprintf(retpath, MAXPGPATH, "%s/%s%s", path, target, EXE); + + if (validate_exec(retpath) == 0) + { + snprintf(cmd, sizeof(cmd), "\"%s\" -V", retpath); + if (pipe_read_line(cmd, line, sizeof(line))) + { + if (strcmp(line, versionstr) != 0) + return -2; /* wrong version */ + return 0; + } + } + + return -1; /* not found */ +} + + +/* + * Find another program and make sure it is the proper version. Assuming our + * directory is either $(libexecdir) or $(bindir), this function will look in + * both of those directories, and in that order. Otherwise, it will look in + * our directory. + * + * 'retpath' should point to memory at least MAXPGPATH in size. + */ +int +find_other_cmd(const char *argv0, const char *target, + const char *versionstr, char *retpath) +{ + char path[MAXPGPATH]; + int result; + + if (find_my_exec(argv0, path) < 0) + return -1; + + /* Trim off program name and keep just directory */ + *last_dir_separator(path) = '\0'; + canonicalize_path(path); + + /* + * If we are in neither the libexec nor bin directories, both attempts + * below will check our current directory. If the command is found in our + * current directory, we return success (or wrong version) after just the + * first attempt, and only on failure make the redundant attempt. It + * doesn't seem worth optimizing away the second attempt on failure, since + * that should be rare anyway. + */ + + /* Check libexec directory, or cwd if we're in some unexpected location */ + result = check_cmd(path, BIN_SUFFIX, TO_LIBEXEC_RELPATH, target, + versionstr, retpath); + if (result != -1) + return result; + + /* Check bin directory, or cwd if we're in some unexpected location */ + return check_cmd(path, LIBEXEC_SUFFIX, TO_BIN_RELPATH, target, versionstr, + retpath); +} /* * Find another program in our binary's directory, @@ -435,6 +573,7 @@ set_pglocale_pgservice(const char *argv0, const char *app) { char path[MAXPGPATH]; char my_exec_path[MAXPGPATH]; + char my_rootdir[MAXPGPATH]; char env_path[MAXPGPATH + sizeof("PGSYSCONFDIR=")]; /* longer than * PGLOCALEDIR */ char *dup_path; @@ -457,6 +596,8 @@ set_pglocale_pgservice(const char *argv0, const char *app) if (find_my_exec(argv0, my_exec_path) < 0) return; + if (find_my_rootdir(argv0, my_rootdir) < 0) + return; #ifdef ENABLE_NLS get_locale_path(my_exec_path, path); @@ -476,7 +617,7 @@ set_pglocale_pgservice(const char *argv0, const char *app) if (getenv("PGSYSCONFDIR") == NULL) { - get_etc_path(my_exec_path, path); + get_etc_path(my_rootdir, path); /* set for libpq to use */ snprintf(env_path, sizeof(env_path), "PGSYSCONFDIR=%s", path); diff --git a/src/common/mk_exec_relpath.pl b/src/common/mk_exec_relpath.pl new file mode 100755 index 0000000000..c9ae667322 --- /dev/null +++ b/src/common/mk_exec_relpath.pl @@ -0,0 +1,85 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +# Some tools are located in the installation's bindir, but execute +# commands located in the libexec directory. Postgres's configure +# script allows the user to override the defaults for bindir and libexecdir, +# so we cannot assume the default that $(bindir)/../libexec is the libexecdir. +# Nor can we assume that the $(libexec) dir from `make' will be the installed +# path, as our commands are relocatable, and in any event they get installed +# into a different directory for regression testing. +# +# We make the hopefully reasonable assumption that the relative path delta +# between bindir and libexecdir is invariant, compute that here, and write it +# to stdout. The make system takes it from there. +# +# We are passed bindir and libexecdir on the command line. For example, +# C:\our\pg\foo\bin and C:\our\pg\some\other\libexecdir. We generate a +# relative path that can be used to cd from bindir into libexecdir. +# For example, "../../some/other/libexecdir". Note that we normalize to +# unix style forward slashes. We also generate the relative path going the +# other way, from libexecdir back to bindir. +# +# TODO: What to do if the paths are windows style, and they are on differing +# mounts? For example, "C:\pg\bin" vs "D:\pg\libexec" +# + +# Given two paths (or path suffixes) rooted at the same location, make a +# relative path from the first to the second. +sub make_relpath($$) +{ + my ($src, $dst) = @_; + + # Replace path elements of src into '..', for example we would + # convert "foo/bin" into "../.." + $src =~ s{[^/]+}{..}g; + + # Strip leading slash, if any, from both + $src =~ s{^/}{}; + $dst =~ s{^/}{}; + + return join('/', $src, $dst); +} + +my $bin = $ARGV[0]; +my $lib = $ARGV[1]; + +# Create a separator that does not exist in either path argument. We don't +# need to be clever here. We just keep appending spaces until we have +# something suitable. +my $sep = ' '; +$sep .= ' ' while ($bin =~ m/$sep/ or $lib =~ m/$sep/); + +# Use a regex to find the longest common prefix of the two directories. For +# example, "C:\our\pg\". We insist that the common prefix ends with a slash +# character, such as "/" on unix or "C:\" on windows. +# +if ("${bin}${sep}${lib}" =~ m"^(.*[\\/])(.*?)${sep}\1(.*)$") +{ + # Record the suffixes of the two paths starting from where they diverge, + # for example, "foo\bin" and "some\other\libexecdir" + my ($binsuffix, $libsuffix) = ($2, $3); + + # Replace windows specific directory separators in both suffixes to get, + # for example, "foo/bin" and "some/other/libexecdir" + $binsuffix =~ s{\\}{/}g; + $libsuffix =~ s{\\}{/}g; + + # Print the two relative paths + printf("#ifndef PG_EXEC_RELPATH_H\n"); + printf("#define PG_EXEC_RELPATH_H\n\n"); + printf("#define LIBEXEC_SUFFIX \"%s\"\n", $libsuffix); + printf("#define BIN_SUFFIX \"%s\"\n", $binsuffix); + printf("#define TO_LIBEXEC_RELPATH \"/%s/\"\n", + make_relpath($binsuffix, $libsuffix)); + printf("#define TO_BIN_RELPATH \"/%s/\"\n\n", + make_relpath($libsuffix, $binsuffix)); + printf("#endif /* PG_EXEC_RELPATH_H */\n"); + + # Successful return + exit 0; +} +warn "Cannot construct relative path for changing directories from $bin to $lib"; +exit 1; diff --git a/src/include/common/config_info.h b/src/include/common/config_info.h index f36b7838d0..ce24036550 100644 --- a/src/include/common/config_info.h +++ b/src/include/common/config_info.h @@ -16,6 +16,7 @@ typedef struct ConfigData } ConfigData; extern ConfigData *get_configdata(const char *my_exec_path, + const char *my_rootdir, size_t *configdata_len); #endif /* COMMON_CONFIG_INFO_H */ diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index 14fa127ab1..f5cb2972c8 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -172,6 +172,7 @@ extern int MyPMChildSlot; extern char OutputFileName[]; extern PGDLLIMPORT char my_exec_path[]; +extern PGDLLIMPORT char my_rootdir[]; extern char pkglib_path[]; #ifdef EXEC_BACKEND diff --git a/src/include/port.h b/src/include/port.h index 271ff0d00b..afbcee9def 100644 --- a/src/include/port.h +++ b/src/include/port.h @@ -57,17 +57,20 @@ extern bool path_is_relative_and_below_cwd(const char *path); extern bool path_is_prefix_of_path(const char *path1, const char *path2); extern char *make_absolute_path(const char *path); extern const char *get_progname(const char *argv0); +extern int trim_path_suffix(const char *path, const char *suffix, char *ret_path); +extern void get_bin_path(const char *my_rootdir, char *ret_path); extern void get_share_path(const char *my_exec_path, char *ret_path); -extern void get_etc_path(const char *my_exec_path, char *ret_path); +extern void get_etc_path(const char *my_rootdir, char *ret_path); extern void get_include_path(const char *my_exec_path, char *ret_path); extern void get_pkginclude_path(const char *my_exec_path, char *ret_path); -extern void get_includeserver_path(const char *my_exec_path, char *ret_path); -extern void get_lib_path(const char *my_exec_path, char *ret_path); +extern void get_includeserver_path(const char *my_rootdir, char *ret_path); +extern void get_lib_path(const char *my_rootdir, char *ret_path); +extern void get_libexec_path(const char *my_rootdir, char *ret_path); extern void get_pkglib_path(const char *my_exec_path, char *ret_path); extern void get_locale_path(const char *my_exec_path, char *ret_path); -extern void get_doc_path(const char *my_exec_path, char *ret_path); -extern void get_html_path(const char *my_exec_path, char *ret_path); -extern void get_man_path(const char *my_exec_path, char *ret_path); +extern void get_doc_path(const char *my_rootdir, char *ret_path); +extern void get_html_path(const char *my_rootdir, char *ret_path); +extern void get_man_path(const char *my_rootdir, char *ret_path); extern bool get_home_path(char *ret_path); extern void get_parent_directory(char *path); @@ -103,9 +106,14 @@ extern void pgfnames_cleanup(char **filenames); extern void set_pglocale_pgservice(const char *argv0, const char *app); /* Portable way to find and execute binaries (in exec.c) */ +extern int find_my_rootdir(const char *argv0, char *retpath); +extern int find_my_libexecdir(const char *argv0, char *retpath); +extern int find_my_bindir(const char *argv0, char *retpath); extern int find_my_exec(const char *argv0, char *retpath); extern int find_other_exec(const char *argv0, const char *target, const char *versionstr, char *retpath); +extern int find_other_cmd(const char *argv0, const char *target, + const char *versionstr, char *retpath); extern char *pipe_read_line(char *cmd, char *line, int maxsize); /* Doesn't belong here, but this is used with find_other_exec(), so... */ diff --git a/src/interfaces/ecpg/test/Makefile b/src/interfaces/ecpg/test/Makefile index be53b7b94d..fe337c91e1 100644 --- a/src/interfaces/ecpg/test/Makefile +++ b/src/interfaces/ecpg/test/Makefile @@ -80,14 +80,14 @@ endif REGRESS_OPTS = --dbname=ecpg1_regression,ecpg2_regression --create-role=regress_ecpg_user1,regress_ecpg_user2 $(EXTRA_REGRESS_OPTS) check: all - $(with_temp_install) ./pg_regress $(REGRESS_OPTS) --temp-instance=./tmp_check $(TEMP_CONF) --bindir= $(pg_regress_locale_flags) $(THREAD) --schedule=$(srcdir)/ecpg_schedule sql/twophase + $(with_temp_install) ./pg_regress $(REGRESS_OPTS) --temp-instance=./tmp_check $(TEMP_CONF) --bindir= --libexecdir= $(pg_regress_locale_flags) $(THREAD) --schedule=$(srcdir)/ecpg_schedule sql/twophase # Connect to the server using TCP, and add a TCP-specific test. checktcp: all | temp-install - $(with_temp_install) ./pg_regress $(REGRESS_OPTS) --temp-instance=./tmp_check $(TEMP_CONF) --bindir= $(pg_regress_locale_flags) $(THREAD) --schedule=$(srcdir)/ecpg_schedule --host=localhost sql/twophase connect/test1 + $(with_temp_install) ./pg_regress $(REGRESS_OPTS) --temp-instance=./tmp_check $(TEMP_CONF) --bindir= --libexecdir= $(pg_regress_locale_flags) $(THREAD) --schedule=$(srcdir)/ecpg_schedule --host=localhost sql/twophase connect/test1 installcheck: all - ./pg_regress $(REGRESS_OPTS) --bindir='$(bindir)' $(pg_regress_locale_flags) $(THREAD) --schedule=$(srcdir)/ecpg_schedule + ./pg_regress $(REGRESS_OPTS) --bindir='$(bindir)' --libexecdir='$(libexecdir)'$(pg_regress_locale_flags) $(THREAD) --schedule=$(srcdir)/ecpg_schedule # Versions of the check tests that include the twophase commit test. # It only makes sense to run these if set up to use prepared transactions, @@ -95,7 +95,7 @@ installcheck: all # installcheck case. installcheck-prepared-txns: all - ./pg_regress $(REGRESS_OPTS) --bindir='$(bindir)' $(pg_regress_locale_flags) $(THREAD) --schedule=$(srcdir)/ecpg_schedule sql/twophase + ./pg_regress $(REGRESS_OPTS) --bindir='$(bindir)' --libexecdir='$(libexecdir)'$(pg_regress_locale_flags) $(THREAD) --schedule=$(srcdir)/ecpg_schedule sql/twophase check-prepared-txns: all | temp-install - $(with_temp_install) ./pg_regress $(REGRESS_OPTS) --temp-instance=./tmp_check $(TEMP_CONF) --bindir= $(pg_regress_locale_flags) $(THREAD) --schedule=$(srcdir)/ecpg_schedule sql/twophase + $(with_temp_install) ./pg_regress $(REGRESS_OPTS) --temp-instance=./tmp_check $(TEMP_CONF) --bindir= --libexecdir= $(pg_regress_locale_flags) $(THREAD) --schedule=$(srcdir)/ecpg_schedule sql/twophase diff --git a/src/port/Makefile b/src/port/Makefile index 8defa1257b..ce23e9ed85 100644 --- a/src/port/Makefile +++ b/src/port/Makefile @@ -145,6 +145,7 @@ path_srv.o: path.c pg_config_paths.h # available to configure. pg_config_paths.h: $(top_builddir)/src/Makefile.global echo "#define PGBINDIR \"$(bindir)\"" >$@ + echo "#define PGLIBEXECDIR \"$(libexecdir)\"" >>$@ echo "#define PGSHAREDIR \"$(datadir)\"" >>$@ echo "#define SYSCONFDIR \"$(sysconfdir)\"" >>$@ echo "#define INCLUDEDIR \"$(includedir)\"" >>$@ diff --git a/src/port/path.c b/src/port/path.c index 10e87fbae8..88defa3080 100644 --- a/src/port/path.c +++ b/src/port/path.c @@ -48,6 +48,8 @@ static void make_relative_path(char *ret_path, const char *target_path, const char *bin_path, const char *my_exec_path); static void trim_directory(char *path); static void trim_trailing_separator(char *path); +static void append_path_suffix(char *ret_path, const char *rootpath, + const char *suffix); /* @@ -588,6 +590,65 @@ no_match: canonicalize_path(ret_path); } +void +append_path_suffix(char *ret_path, const char *rootpath, const char *suffix) +{ + char buffer[MAXPGPATH*2]; + + snprintf(buffer, MAXPGPATH*2, "%s/%s", rootpath, suffix); + canonicalize_path(buffer); + strlcpy(ret_path, buffer, MAXPGPATH); +} + +/* + * trim_path_suffix + * + * If the given suffix matches the end of the path, store the prefix + * in ret_path. Returns zero on success, -1 if the suffix does not match. + * Note that matching is done on whole path components, such that + * "cd/ef" is a suffix of "ab/cd/ef" but not a suffix of "abcd/ef". + * + * ret_path must be a buffer at least MAXPGPATH in length. + */ +int +trim_path_suffix(const char *path, const char *suffix, char *ret_path) +{ + int pathlen = strlen(path); + int suffixlen = strlen(suffix); + int tail_start; + + if (suffixlen > pathlen) + return -1; + + /* + * We consider a path to be a suffix of itself, though this does + * result in a zero-length prefix in the ret_path. + */ + if (suffixlen == pathlen) + { + if (dir_strcmp(path, suffix)) + return -1; + ret_path[0] = '\0'; + return 0; + } + + /* + * Tail match? + */ + tail_start = pathlen - suffixlen; + if ((IS_DIR_SEP(path[tail_start - 1]) || + (IS_DIR_SEP(path[tail_start]) && IS_DIR_SEP(suffix[0]))) && + dir_strcmp(path + tail_start, suffix) == 0) + { + strncpy(ret_path, path, tail_start); + ret_path[tail_start] = '\0'; + canonicalize_path(ret_path); + return 0; + } + + return -1; +} + /* * make_absolute_path @@ -697,6 +758,15 @@ make_absolute_path(const char *path) } +/* + * get_bin_path + */ +void +get_bin_path(const char *my_rootdir, char *ret_path) +{ + append_path_suffix(ret_path, my_rootdir, PGBINDIR); +} + /* * get_share_path */ @@ -710,9 +780,9 @@ get_share_path(const char *my_exec_path, char *ret_path) * get_etc_path */ void -get_etc_path(const char *my_exec_path, char *ret_path) +get_etc_path(const char *my_rootdir, char *ret_path) { - make_relative_path(ret_path, SYSCONFDIR, PGBINDIR, my_exec_path); + append_path_suffix(ret_path, my_rootdir, SYSCONFDIR); } /* @@ -737,18 +807,27 @@ get_pkginclude_path(const char *my_exec_path, char *ret_path) * get_includeserver_path */ void -get_includeserver_path(const char *my_exec_path, char *ret_path) +get_includeserver_path(const char *my_rootdir, char *ret_path) { - make_relative_path(ret_path, INCLUDEDIRSERVER, PGBINDIR, my_exec_path); + append_path_suffix(ret_path, my_rootdir, INCLUDEDIRSERVER); } /* * get_lib_path */ void -get_lib_path(const char *my_exec_path, char *ret_path) +get_lib_path(const char *my_rootdir, char *ret_path) +{ + append_path_suffix(ret_path, my_rootdir, LIBDIR); +} + +/* + * get_libexec_path + */ +void +get_libexec_path(const char *my_rootdir, char *ret_path) { - make_relative_path(ret_path, LIBDIR, PGBINDIR, my_exec_path); + append_path_suffix(ret_path, my_rootdir, PGLIBEXECDIR); } /* @@ -773,27 +852,27 @@ get_locale_path(const char *my_exec_path, char *ret_path) * get_doc_path */ void -get_doc_path(const char *my_exec_path, char *ret_path) +get_doc_path(const char *my_rootdir, char *ret_path) { - make_relative_path(ret_path, DOCDIR, PGBINDIR, my_exec_path); + append_path_suffix(ret_path, my_rootdir, DOCDIR); } /* * get_html_path */ void -get_html_path(const char *my_exec_path, char *ret_path) +get_html_path(const char *my_rootdir, char *ret_path) { - make_relative_path(ret_path, HTMLDIR, PGBINDIR, my_exec_path); + append_path_suffix(ret_path, my_rootdir, HTMLDIR); } /* * get_man_path */ void -get_man_path(const char *my_exec_path, char *ret_path) +get_man_path(const char *my_rootdir, char *ret_path) { - make_relative_path(ret_path, MANDIR, PGBINDIR, my_exec_path); + append_path_suffix(ret_path, my_rootdir, MANDIR); } diff --git a/src/test/isolation/isolation_main.c b/src/test/isolation/isolation_main.c index 50916b00dc..8006518543 100644 --- a/src/test/isolation/isolation_main.c +++ b/src/test/isolation/isolation_main.c @@ -42,8 +42,8 @@ isolation_start_test(const char *testname, if (!looked_up_isolation_exec) { /* look for isolationtester binary */ - if (find_other_exec(saved_argv0, "isolationtester", - PG_ISOLATION_VERSIONSTR, isolation_exec) != 0) + if (find_other_cmd(saved_argv0, "isolationtester", + PG_ISOLATION_VERSIONSTR, isolation_exec) != 0) { fprintf(stderr, _("could not find proper isolationtester binary\n")); exit(2); @@ -122,11 +122,11 @@ isolation_init(int argc, char **argv) size_t argv0_len; /* - * We unfortunately cannot do the find_other_exec() lookup to find the + * We unfortunately cannot do the find_other_cmd() lookup to find the * "isolationtester" binary here. regression_main() calls the * initialization functions before parsing the commandline arguments and * thus hasn't changed the library search path at this point which in turn - * can cause the "isolationtester -V" invocation that find_other_exec() + * can cause the "isolationtester -V" invocation that find_other_cmd() * does to fail since it's linked to libpq. So we instead copy argv[0] * and do the lookup the first time through isolation_start_test(). */ diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c index 38b2b1e8e1..fb4fba6695 100644 --- a/src/test/regress/pg_regress.c +++ b/src/test/regress/pg_regress.c @@ -75,6 +75,7 @@ bool debug = false; char *inputdir = "."; char *outputdir = "."; char *bindir = PGBINDIR; +char *libexecdir = PGLIBEXECDIR; char *launcher = NULL; static _stringlist *loadextension = NULL; static int max_connections = 0; @@ -269,8 +270,8 @@ stop_postmaster(void) snprintf(buf, sizeof(buf), "\"%s%spg_ctl\" stop -D \"%s/data\" -s", - bindir ? bindir : "", - bindir ? "/" : "", + libexecdir ? libexecdir : "", + libexecdir ? "/" : "", temp_instance); r = system(buf); if (r != 0) @@ -2037,6 +2038,8 @@ help(void) printf(_("Options:\n")); printf(_(" --bindir=BINPATH use BINPATH for programs that are run;\n")); printf(_(" if empty, use PATH from the environment\n")); + printf(_(" --libexecdir=LIBEXECPATH use LIBEXECPATH for commands that are run;\n")); + printf(_(" if empty, use PATH from the environment\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")); @@ -2095,12 +2098,13 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc {"port", required_argument, NULL, 14}, {"user", required_argument, NULL, 15}, {"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}, + {"libexecdir", required_argument, NULL, 17}, + {"dlpath", required_argument, NULL, 18}, + {"create-role", required_argument, NULL, 19}, + {"temp-config", required_argument, NULL, 20}, + {"use-existing", no_argument, NULL, 21}, {"launcher", required_argument, NULL, 21}, - {"load-extension", required_argument, NULL, 22}, + {"load-extension", required_argument, NULL, 23}, {"config-auth", required_argument, NULL, 24}, {"max-concurrent-tests", required_argument, NULL, 25}, {NULL, 0, NULL, 0} @@ -2209,21 +2213,28 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc bindir = NULL; break; case 17: - dlpath = pg_strdup(optarg); + /* "--libexecdir=" means to use PATH */ + if (strlen(optarg)) + libexecdir = pg_strdup(optarg); + else + libexecdir = NULL; break; case 18: - split_to_stringlist(optarg, ",", &extraroles); + dlpath = pg_strdup(optarg); break; case 19: - add_stringlist_item(&temp_configs, optarg); + split_to_stringlist(optarg, ",", &extraroles); break; case 20: - use_existing = true; + add_stringlist_item(&temp_configs, optarg); break; case 21: - launcher = pg_strdup(optarg); + use_existing = true; break; case 22: + launcher = pg_strdup(optarg); + break; + case 23: add_stringlist_item(&loadextension, optarg); break; case 24: @@ -2319,8 +2330,8 @@ regression_main(int argc, char *argv[], init_function ifunc, test_function tfunc header(_("initializing database system")); snprintf(buf, sizeof(buf), "\"%s%sinitdb\" -D \"%s/data\" --no-clean --no-sync%s%s > \"%s/log/initdb.log\" 2>&1", - bindir ? bindir : "", - bindir ? "/" : "", + libexecdir ? libexecdir : "", + libexecdir ? "/" : "", temp_instance, debug ? " --debug" : "", nolocale ? " --no-locale" : "", diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm index a13ca6e02e..b43ad7d38a 100644 --- a/src/tools/msvc/Solution.pm +++ b/src/tools/msvc/Solution.pm @@ -734,6 +734,7 @@ sub GenerateFiles || confess "Could not open pg_config_paths.h"; print $o <