diff options
Diffstat (limited to 'pg_wrapper')
-rwxr-xr-x | pg_wrapper | 329 |
1 files changed, 329 insertions, 0 deletions
diff --git a/pg_wrapper b/pg_wrapper new file mode 100755 index 0000000..5b741c5 --- /dev/null +++ b/pg_wrapper @@ -0,0 +1,329 @@ +#!/usr/bin/perl +# Call a PostgreSQL client program with the version, cluster and default +# database specified in ~/.postgresqlrc or +# /etc/postgresql-common/user_clusters. +# +# (C) 2005-2009 Martin Pitt <mpitt@debian.org> +# (C) 2013-2022 Christoph Berg <myon@debian.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +use warnings; +use strict; +use POSIX; +use PgCommon; + +my ($version, $cluster); +my $explicit_host = exists $ENV{PGHOST}; +my $explicit_port = $ENV{PGPORT}; +my $explicit_service = exists $ENV{PGSERVICE}; + +# Evaluate PGCLUSTER (unless PGHOST is set as well) +if (exists $ENV{'PGCLUSTER'} and not $explicit_host) { + ($version, $cluster) = split ('/', $ENV{'PGCLUSTER'}, 2); + error "Invalid version $version specified in PGCLUSTER" unless version_exists $version; + error 'No cluster specified with $PGCLUSTER' unless $cluster; +} + +# Check for --cluster argument and filter it out +for (my $i = 0; $i <= $#ARGV; ++$i) { + last if $ARGV[$i] eq '--'; + + if ($ARGV[$i] eq '--cluster') { + error '--cluster option needs an argument (<version>/<cluster>)' if ($i >= $#ARGV); + + ($version, $cluster) = split ('/', $ARGV[$i+1], 2); + error "Invalid version $version specified with --cluster" unless version_exists $version; + error 'No cluster specified with --cluster' unless $cluster; + + splice @ARGV, $i, 2; + last; + } elsif ($ARGV[$i] =~ /^--cluster=(\d+\.?\d)\/(.+)/) { + ($version, $cluster) = ($1, $2); + error "Invalid version $version specified with --cluster" unless version_exists $version; + error 'No cluster specified with --cluster' unless $cluster; + + splice @ARGV, $i, 1; + last; + } + # --host or -h on command line, drop info from PGCLUSTER + if ($ARGV[$i] =~ /^--host\b/ || $ARGV[$i] =~ /^-\w*h\w*$/) { + ($version, $cluster) = (undef, undef); + $explicit_host = 1; + delete $ENV{PGCLUSTER}; + } + # --port or -p on command line + if ($ARGV[$i] =~ /^--port\b(?:=(\d+))?/ || $ARGV[$i] =~ /^-\w*p(\d+)?$/) { + if (defined $1) { + $explicit_port = $1; + } elsif ($i < $#ARGV) { + $explicit_port = $ARGV[$i+1]; + } + } + # "service=" in connection string + if ($ARGV[$i] =~ /\bservice=/) { + $explicit_service = 1; + } +} + +# if only a port is specified, look for local cluster on specified port +if ($explicit_port and not $version and not $cluster and not $explicit_host and not $explicit_service) { + LOOP: foreach my $v (reverse get_versions()) { + foreach my $c (get_version_clusters $v) { + my $p = get_cluster_port $v, $c; + if ($p eq $explicit_port) { + $version = $v; + # set PGCLUSTER variable for information + $ENV{PGCLUSTER} = "$version/$c"; + last LOOP; + } + } + } +} + +# if we don't have a cluster, and no specific host or port was given, consult postgresqlrc +# or fall back to default port cluster (on 5432), or undef otherwise +my ($db); +($version, $cluster, $db) = user_cluster_map() unless ($cluster or $explicit_host or $explicit_port); + +my ($host, $port); + +if ($cluster) { + # check if we have a network cluster (N.N/the.host.name:port) + if ($cluster =~ /^(\S+):(\d*)$/) { + $host = $1; + $port = $2 || $PgCommon::defaultport; + } elsif (not cluster_exists($version, $cluster)) { + # a specific cluster was requested, error out because it doesn't exist + error "Cluster $version $cluster does not exist"; + } else { + $host = get_cluster_socketdir ($version, $cluster); + $port = get_cluster_port($version, $cluster); + } + # set PGCLUSTER variable for information + $ENV{PGCLUSTER} = "$version/$cluster"; +} + +# setup environment +$ENV{'PGSYSCONFDIR'} //= '/etc/postgresql-common'; +$ENV{'PGHOST'} = $host if ($host); +$ENV{'PGPORT'} = $port if $port && !$ENV{'PGPORT'}; +$ENV{'PGDATABASE'} = $db if $db && !$ENV{'PGDATABASE'}; + +# check under which name we were called +my $cmdname = (split '/', $0)[-1]; + +unless ($version or $explicit_host or $explicit_port or $explicit_service) { + print STDERR "Warning: No existing cluster is suitable as a default target. Please see man pg_wrapper(1) how to specify one.\n"; +} + +# if we have no version yet, use the latest version. If we were called as psql, +# pg_archivecleanup, or pg_isready, always use latest version +if (not $version or $cmdname =~ /^(psql|pg_archivecleanup|pg_isready)$/) { + my $max_version; + if ($version and $version < 9.2) { # psql 15 only supports PG 9.2+ + $max_version = 14; + } + $version = get_newest_version($cmdname, $max_version); +} +unless ($version) { + error 'You must install at least one postgresql-client-<version> package'; +} +error "PostgreSQL version $version is not installed" unless -d "$PgCommon::binroot$version"; + +my $cmd; +if ($cmdname eq 'pg_wrapper') { + error "pg_wrapper called directly but no program given as argument" + if (@ARGV == 0); + $cmd = shift; # will be unshifted back below +} else { + $cmd = get_program_path ($cmdname, $version); +} + +# libreadline is a lot better than libedit, so prefer that on versions that still use it +if ($cmdname eq 'psql' and $version < 13 and not $PgCommon::rpm) { + my @readlines; + # non-multiarch path + @readlines = sort(</lib/libreadline.so.?>); + + unless (@readlines) { + # get multiarch dir for our architecture + if (open PS, '-|', '/usr/bin/ldd', $cmd) { + my $out; + read PS, $out, 10000; + close PS; + if ($out =~ m!/libreadline.so!) { + # already linked against libreadline + @readlines = (); + } + else + { + my ($lib_path) = $out =~ m!(/lib/.*)/libedit.so!; + + @readlines = sort(<$lib_path/libreadline.so.?>); + } + } + } + + if (@readlines) { + $ENV{'LD_PRELOAD'} = ($ENV{'LD_PRELOAD'} or '') . ':' . $readlines[-1]; + } +} + +error "pg_wrapper: $cmdname was not found in $PgCommon::binroot$version/bin" unless $cmd; +unshift @ARGV, $cmd; +exec @ARGV; + +__END__ + +=head1 NAME + +pg_wrapper - wrapper for PostgreSQL client commands + +=head1 SYNOPSIS + +I<client-program> [B<--cluster> I<version>/I<cluster>] [...] + +(I<client-program>: B<psql>, B<createdb>, B<dropuser>, and all other client +programs installed in C</usr/lib/postgresql/>I<version>C</bin>). + +=head1 DESCRIPTION + +This program is run only as a link to names which correspond to PostgreSQL +programs in C</usr/lib/postgresql/>I<version>C</bin>. It determines the +configured cluster and database for the user and calls the appropriate version +of the desired program to connect to that cluster and database, supplying any +specified options to that command. + +The target cluster is selected by the following means, in descending order of +precedence: + +=over + +=item + +explicit specification with the B<--host> option + +=item + +explicit specification with the B<--cluster> option + +=item + +if the B<PGHOST> environment variable is set, no further cluster selection is +performed. The default PostgreSQL version and port number (from the command +line, the environment variable B<PGPORT>, or default 5432) will be used. + +=item + +explicit specification with the B<PGCLUSTER> environment variable + +=item + +if a port is given (either via B<-p>, B<--port>, or B<PGPORT>), and no host is +given, the local cluster matching that port number is used + +=item + +matching entry in C<~/.postgresqlrc> (see L<postgresqlrc(5)>), if that +file exists + +=item + +matching entry in C</etc/postgresql-common/user_clusters> (see +L<user_clusters(5)>), if that file exists + +=item + +If only one cluster exists on the local system, that one will be selected. + +=item + +If several clusters exist on the local system, the one listening on the default port 5432 +will be selected. + +=back + +If none of these rules match, B<pg_wrapper> does not set any environment +variables and the program called will likely error out with a message like +"could not connect to server: Connection refused". + +For B<psql>, B<pg_archivecleanup>, and B<pg_isready>, B<pg_wrapper> will always use the binary from +the newest PostgreSQL version installed, as these are downwards compatible. +If the cluster version is older than 9.2, the newest considered binary version is 14. + +Note that B<pg_wrapper> needs to be able to read the server config to get the +port number to connect to. If a non-standard port is configured in a place that +pg_wrapper cannot read, connecting will fail. This particularly holds if the +port was configured via B<ALTER SYSTEM> in C<postgresql.auto.conf> and +pg_wrapper is invoked as any user other than B<postgres> and B<root>. + +=head1 OPTIONS + +=over + +=item B<--cluster> I<version>B</>I<cluster> + +=item B<--cluster> I<version>B</>I<host>B<:>[I<port>] + +I<cluster> is either the name of a cluster on the local system, or takes the form +I<host>:I<port> for a remote cluster. If I<port> is left empty (i. e. you just +specify I<host:>), it defaults to 5432. + +=back + +=head1 ENVIRONMENT + +=over + +=item B<PGCLUSTER> + +If C<$PGCLUSTER> is set, its value (of the form I<version>/I<cluster>) +specifies the desired cluster, similar to the B<--cluster> option. However, if +B<--cluster> is specified, it overrides the value of C<$PGCLUSTER>. + +=item B<PG_CLUSTER_CONF_ROOT> + +This specifies an alternative base directory for cluster configurations. This +is usually C</etc/postgresql/>, but for testing/development purposes you can +change this to point to e. g. your home directory, so that you can use the +postgresql-common tools without root privileges. + +=item B<PGSYSCONFDIR> + +This is the location of PostgreSQL's and postgresql-common's global +configuration (e. g. C<pg_service.conf>, L<user_clusters(5)>). The default is +C</etc/postgresql-common/>. + +=back + +=head1 FILES + +=over + +=item C</etc/postgresql-common/user_clusters> + +stores the default cluster and database for users and groups as set by +the administrators. + +=item C<$HOME/.postgresqlrc> + +stores defaults set by the user himself. + +=back + +=head1 SEE ALSO + +L<user_clusters(5)>, L<postgresqlrc(5)> + +=head1 AUTHOR + +Martin Pitt L<E<lt>mpitt@debian.orgE<gt>> |