postgresql-common-154ubuntu1.1/0000775000000000000000000000000013200602445013425 5ustar postgresql-common-154ubuntu1.1/TODO0000664000000000000000000000242712143322136014123 0ustar postgresql TODO =============== Bugs: - pg_createcluster with existing cluster: respect symlinks to shared postgresql.conf files, never remove symlinks in already existing cluster dirs - Clean up at purging if /etc/ is there without /var, or vice versa Transition bugs: Missing selftest: - --force option for pg_ctlcluster Important features: Wishlist: - Add pg_conf to change startup and possibly other things - add program for web applications, which configure pg_hba.conf: pg_hba add|remove|test [options] yourwebappdb yourwebappuser Options: --cluster: self-explanatory, defaults to default cluster --ip: IP and netmask for host socket; if not given, defaults to Unix socket (local) --method: defaults to "md5" for TCP connections, and "ident sameuser" for Unix socket connections --force-ssl: If given, create a "hostssl" entry, otherwise a "host" entry For remove, only --cluster is allowed; it will remove all hba entries that refer to the given db/user pair. test checks whether the given connection is allowed; if so, it exits with 0, otherwise it prints the required pg_hba.conf line to stdout and exits with 1. If pg_hba.conf has a scrambled format that cannot be parsed by pg_*_hba, the scripts exit with 2. Add libnet-cidr-perl dependency! postgresql-common-154ubuntu1.1/pg_virtualenv.pod0000664000000000000000000000472712273774575017057 0ustar =head1 NAME pg_virtualenv - Create a throw-away PostgreSQL environment for running regression tests =head1 SYNOPSIS B [B<-ash> B<-v> 'I'] [I] =head1 DESCRIPTION B creates a virtual PostgreSQL server environment, and sets environment variables such that I can access the PostgreSQL database server(s). The servers are destroyed when I exits. The environment variables B, B, B, and B will be set. Per default, a single new cluster is created on port B<5432>, using the newest PostgreSQL server version installed. When more clusters are created for other versions, they will use other port numbers. B is used to create the database clusters. The clusters are named I/regress. To access a cluster, set BIB. For ease of access, the clusters are also registered in F, with the version number as cluster name. Clusters can be accessed by passing the connection string "BI", e.g. B. When invoked as root user, the servers and I are run in an unshared mount and network namespace; the servers are created on tmpfses, so actions caused by I in the database do not affect the running system. I can drop privileges as needed, provided the environment variables are passed. When invoked as non-root user, B and B are set to a temporary directory where all files belonging to the clusters are created. =head1 OPTIONS =over 4 =item B<-a> Use all PostgreSQL server versions installed. =item B<-v> I Use these versions (space-separated list). =item B<-c> I Extra options to pass to B. =item B<-i> I Extra initdb options to pass to B. =item B<-o> IB<=>I Configuration option to set in the C file, passed to B. =item B<-s> Launch a shell inside the virtual environment when I fails. =item B<-h> Show program help. =back =head1 EXAMPLE # pg_virtualenv make check =head1 NOTES When run with fakeroot(1), B will fall back to the non-root mode of operation. Running "fakeroot pg_virtualenv" as root will fail, though. =head1 SEE ALSO initdb(1), pg_createcluster(1), unshare(1). =head1 AUTHOR Christoph Berg Lmyon@debian.orgE> postgresql-common-154ubuntu1.1/pg_lsclusters0000775000000000000000000000416512143322136016253 0ustar #!/usr/bin/perl -wT # Show all PostgreSQL clusters in a list # # (C) 2005-2009 Martin Pitt # (C) 2013 Christoph Berg # # 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 strict; use lib '/usr/share/postgresql-common'; use PgCommon; use Getopt::Long; my $no_header; exit 1 unless GetOptions ('h|no-header' => \$no_header); my @lines; push @lines, ['Ver', 'Cluster', 'Port', 'Status', 'Owner', 'Data directory', 'Log file'] unless ($no_header); foreach my $v (sort (get_versions())) { my @clusters = get_version_clusters $v; foreach my $c (sort @clusters) { my %info = cluster_info $v, $c; push @lines, [$v, $c, $info{'port'}, ($info{'running'} ? "online" : "down") . ($info{'recovery'} ? ",recovery" : ""), defined $info{'owneruid'} ? (getpwuid $info{'owneruid'})[0] : '', $info{'pgdata'} || '', $info{'logfile'} || 'custom']; } } my @colwidth = qw(1 1 1 1 1 1 1); foreach my $line (@lines) { for (my $i = 0; $i < @$line - 1; $i++) { # skip adjustment for last column my $len = length @$line[$i]; $colwidth[$i] = $len if ($len > $colwidth[$i]); } } my $fmtstring = join ' ', map { "%-${_}s" } @colwidth; foreach my $line (@lines) { printf "$fmtstring\n", @$line; } __END__ =head1 NAME pg_lsclusters - show information about all PostgreSQL clusters =head1 SYNOPSIS B [I] =head1 DESCRIPTION This command shows a list about the configuration and status of all clusters. =head1 OPTIONS =over 4 =item B<-h>, B<--no-header> Do not print the column header line. =back =head1 AUTHOR Martin Pitt Lmpitt@debian.orgE> postgresql-common-154ubuntu1.1/testsuite0000775000000000000000000000717112301576713015423 0ustar #!/bin/sh # Run integration tests (on the installed package). This happens on unshared # tmpfses, so does not interfere with installed clusters. # # (C) 2005-2012 Martin Pitt # (C) 2012-2014 Christoph Berg # # 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. set -e # call ourselves through unshare in a way that keeps normal stdin if [ -z "$UNSHARED" ]; then UNSHARED=1 exec unshare -uimn -- "$0" "$@" fi # unshared program starts here set -e # default config TESTSDIR="$(dirname $0)/t" : ${PG_UMASKS="022 077"} help () { echo "postgresql-common testsuite" echo "Syntax: $0 [options] [test ...]" echo " -u 'umask ...' umasks to run testsuite with [default: 022 077]" echo " -v 'version ...' PostgreSQL versions to test [default: client versions installed]" exit ${1:-0} } # option parsing while getopts "hu:v:" opt ; do case $opt in h) help ;; u) PG_UMASKS="$OPTARG" ;; v) export PG_VERSIONS="$OPTARG" ;; # used in t/TestLib.pm *) help 1 ;; esac done # shift away args shift $(($OPTIND - 1)) if [ "$(id -u)" != 0 ]; then echo "Error: this test suite needs to be run as root" >&2 exit 1 fi # install locales; this happens differently on Debian and Ubuntu if [ -e /etc/locale.gen ]; then # Debian while read locale ; do if ! grep -q "^$locale\$" /etc/locale.gen ; then echo "$locale" >> /etc/locale.gen run=1 fi done <<-EOF en_US.UTF-8 UTF-8 ru_RU ISO-8859-5 ru_RU.UTF-8 UTF-8 EOF [ "$run" ] && locale-gen else # Ubuntu # locale-gen will skip existing locales, so just call it for all here locale-gen en_US.UTF-8 ru_RU ru_RU.UTF-8 fi # stop currently running clusters if [ -x "/etc/init.d/postgresql" ]; then /etc/init.d/postgresql stop fi # let everything happen in overlay tmpfses to avoid interfering with already # existing clusters; this also speeds up testing dirs="/etc/postgresql /etc/postgresql-common /var/lib/postgresql /var/log/postgresql /var/run/postgresql" created_dirs="" for d in $dirs; do if ! [ -d $d ]; then created_dirs="$created_dirs $d" mkdir -p $d fi mount -n -t tmpfs -o mode=755 tmpfs $d done # clean up created directories after us cleanup () { umount $dirs if [ "$created_dirs" ]; then rmdir --ignore-fail-on-non-empty -p $created_dirs fi } trap "cleanup" 0 HUP INT QUIT ILL ABRT PIPE TERM chown root:postgres /var/log/postgresql chmod 1775 /var/log/postgresql chown postgres:postgres /var/run/postgresql chmod 2775 /var/run/postgresql # reset core limit for pg_ctl tests ulimit -S -c 0 # start localhost interface ifconfig lo up || true # set environment unset TMPDIR unset LC_ALL export LANG=en_US.utf8 # set variables which cause taint check errors export IFS=' ' export CDPATH=/usr export ENV=/nonexisting export BASH_ENV=/nonexisting if [ $# -eq 0 ]; then set -- $TESTSDIR/*.t fi for U in $PG_UMASKS; do case $U in 022) TYPE="default" ;; 077) TYPE="tight" ;; *) TYPE="custom" ;; esac echo "====== Running all tests with $TYPE umask $U =======" umask $U for T; do echo "=== Running test `basename $T`... ===" perl $T done done postgresql-common-154ubuntu1.1/PgCommon.pm0000664000000000000000000010072312171712376015520 0ustar # Common functions for the postgresql-common framework # # (C) 2008-2009 Martin Pitt # (C) 2012-2013 Christoph Berg # # 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. package PgCommon; use strict; use IPC::Open3; use Socket; use POSIX; use Exporter; our $VERSION = 1.00; our @ISA = ('Exporter'); our @EXPORT = qw/error user_cluster_map get_cluster_port set_cluster_port get_cluster_socketdir set_cluster_socketdir cluster_port_running get_cluster_start_conf set_cluster_start_conf set_cluster_pg_ctl_conf get_program_path cluster_info get_versions get_newest_version version_exists get_version_clusters next_free_port cluster_exists install_file change_ugid config_bool get_db_encoding get_db_locales get_cluster_locales get_cluster_databases read_cluster_conf_file read_pg_hba/; our @EXPORT_OK = qw/$confroot read_conf_file get_conf_value set_conf_value disable_conf_value replace_conf_value cluster_data_directory get_file_device read_pidfile check_pidfile_running/; # Print an error message to stderr and exit with status 1 sub error { print STDERR 'Error: ', $_[0], "\n"; exit 1; } # configuration our $confroot = '/etc/postgresql'; if ($ENV{'PG_CLUSTER_CONF_ROOT'}) { ($confroot) = $ENV{'PG_CLUSTER_CONF_ROOT'} =~ /(.*)/; # untaint } our $common_confdir = "/etc/postgresql-common"; if ($ENV{'PGSYSCONFDIR'}) { ($common_confdir) = $ENV{'PGSYSCONFDIR'} =~ /(.*)/; # untaint } my $mapfile = "$common_confdir/user_clusters"; my $binroot = "/usr/lib/postgresql"; my $defaultport = 5432; { my %saved_env; # untaint the environment for executing an external program # Optional arguments: list of additional variables sub prepare_exec { my @cleanvars = qw/PATH IFS ENV BASH_ENV CDPATH/; push @cleanvars, @_; %saved_env = (); foreach (@cleanvars) { $saved_env{$_} = $ENV{$_}; delete $ENV{$_}; } $ENV{'PATH'} = ''; } # restore the environment after prepare_exec() sub restore_exec { foreach (keys %saved_env) { if (defined $saved_env{$_}) { $ENV{$_} = $saved_env{$_}; } else { delete $ENV{$_}; } } } } # Returns '1' if the argument is a configuration file value that stands for # true (ON, TRUE, YES, or 1, case insensitive), '0' if the argument represents # a false value (OFF, FALSE, NO, or 0, case insensitive), or undef otherwise. sub config_bool { return undef unless defined($_[0]); return 1 if ($_[0] =~ /^(on|true|yes|1)$/i); return 0 if ($_[0] =~ /^(off|false|no|0)$/i); return undef; } # Read a 'var = value' style configuration file and return a hash with the # values. Error out if the file cannot be read. # If the file name ends with '.conf', the keys will be normalized to lower case # (suitable for e. g. postgresql.conf), otherwise kept intact (suitable for # environment). # Arguments: # Returns: hash (empty if file does not exist) sub read_conf_file { my %conf; local (*F); return %conf unless -e $_[0]; if (open F, $_[0]) { while () { if (/^\s*(?:#.*)?$/) { next; } elsif (/^\s*include(?:_if_exists)?\s+'([^']+)'\s*$/i) { my ($k, $v, $path, %include_conf); $path = $1; unless (substr($path, 0, 1) eq '/') { my @p = split '/', $_[0]; my $dirname = join '/', @p[0..($#p-1)]; $path = "$dirname/$path"; } # read included file and merge into %conf %include_conf = read_conf_file($path); while ( ($k, $v) = each(%include_conf) ) { $conf{$k} = $v; } } elsif (/^\s*([a-zA-Z0-9_.-]+)\s*(?:=|\s)\s*'((?:[^']|(?:(?<=\\)'))*)'\s*(?:#.*)?$/i) { # string value my $v = $2; my $k = $1; $k = lc $k if $_[0] =~ /\.conf$/; $v =~ s/\\(.)/$1/g; $conf{$k} = $v; } elsif (/^\s*([a-zA-Z0-9_.-]+)\s*(?:=|\s)\s*(-?[\w.]+)\s*(?:#.*)?$/i) { # simple value my $v = $2; my $k = $1; $k = lc $k if $_[0] =~ /\.conf$/; $conf{$k} = $v; } else { error "Invalid line $. in $_[0]: »$_«"; } } close F; } else { error "could not read $_[0]: $!"; } return %conf; } # Read a 'var = value' style configuration file from a cluster configuration # directory (with /etc/postgresql-common/ as fallback) and return a # hash with the values. Error out if the file cannot be read. # Arguments: # Returns: hash (empty if the file does not exist) sub read_cluster_conf_file { my $fname = "$confroot/$_[0]/$_[1]/$_[2]"; -e $fname or $fname = "$common_confdir/$_[2]"; return read_conf_file $fname; } # Return parameter from a PostgreSQL configuration file, or undef if the parameter # does not exist. # Arguments: sub get_conf_value { my %conf = (read_cluster_conf_file $_[0], $_[1], $_[2]); return $conf{$_[3]}; } # Set parameter of a PostgreSQL configuration file. # Arguments: sub set_conf_value { my $fname = "$confroot/$_[0]/$_[1]/$_[2]"; my $value; my @lines; if ($_[4] =~ /^-?[\w.]+$/) { $value = $_[4]; } else { $value = "'$_[4]'"; } # read configuration file lines open (F, $fname) or die "Error: could not open $fname for reading"; push @lines, $_ while (); close F; my $found = 0; # first, search for an uncommented setting for (my $i=0; $i <= $#lines; ++$i) { if ($lines[$i] =~ /^\s*($_[3])(\s*(?:=|\s)\s*)\w+\b((?:\s*#.*)?)/i or $lines[$i] =~ /^\s*($_[3])(\s*(?:=|\s)\s*)'[^']*'((?:\s*#.*)?)/i) { $lines[$i] = "$1$2$value$3\n"; $found = 1; last; } } # now check if the setting exists as a comment; if so, change that instead # of appending if (!$found) { for (my $i=0; $i <= $#lines; ++$i) { if ($lines[$i] =~ /^\s*#\s*($_[3])(\s*(?:=|\s)\s*)\w+\b((?:\s*#.*)?)/i or $lines[$i] =~ /^\s*#\s*($_[3])(\s*(?:=|\s)\s*)'[^']*'((?:\s*#.*)?)/i) { $lines[$i] = "$1$2$value$3\n"; $found = 1; last; } } } # not found anywhere, append it push (@lines, "$_[3] = $value\n") unless $found; # write configuration file lines open (F, ">$fname.new") or die "Error: could not open $fname.new for writing"; foreach (@lines) { print F $_ or die "writing $fname.new: $!"; } close F; # copy permissions my @st = stat $fname or die "stat: $!"; chown $st[4], $st[5], "$fname.new"; # might fail as non-root chmod $st[2], "$fname.new" or die "chmod: $1"; rename "$fname.new", "$fname"; } # Disable a parameter in a PostgreSQL configuration file by prepending it with # a '#'. Appends an optional explanatory comment if given. # Arguments: sub disable_conf_value { my $fname = "$confroot/$_[0]/$_[1]/$_[2]"; my $value; my @lines; # read configuration file lines open (F, $fname) or die "Error: could not open $fname for reading"; push @lines, $_ while (); close F; my $changed = 0; for (my $i=0; $i <= $#lines; ++$i) { if ($lines[$i] =~ /^\s*$_[3]\s*(?:=|\s)/i) { $lines[$i] = '#'.$lines[$i]; chomp $lines[$i]; $lines[$i] .= ' #'.$_[4]."\n" if $_[4]; $changed = 1; last; } } # write configuration file lines if ($changed) { open (F, ">$fname.new") or die "Error: could not open $fname.new for writing"; foreach (@lines) { print F $_ or die "writing $fname.new: $!"; } close F; # copy permissions my @st = stat $fname or die "stat: $!"; chown $st[4], $st[5], "$fname.new"; # might fail as non-root chmod $st[2], "$fname.new" or die "chmod: $1"; rename "$fname.new", "$fname"; } } # Replace a parameter in a PostgreSQL configuration file. The old parameter is # prepended with a '#' and gets an optional explanatory comment # appended, if given. The new parameter is inserted directly after the old one. # Arguments: # sub replace_conf_value { my ($version, $cluster, $configfile, $oldparam, $reason, $newparam, $val) = @_; my $fname = "$confroot/$version/$cluster/$configfile"; my @lines; # quote $val if necessary unless ($val =~ /^\w+$/) { $val = "'$val'"; } # read configuration file lines open (F, $fname) or die "Error: could not open $fname for reading"; push @lines, $_ while (); close F; my $found = 0; for (my $i = 0; $i <= $#lines; ++$i) { if ($lines[$i] =~ /^\s*$oldparam\s*(?:=|\s)/i) { $lines[$i] = '#'.$lines[$i]; chomp $lines[$i]; $lines[$i] .= ' #'.$reason."\n" if $reason; # insert the new param splice @lines, $i+1, 0, "$newparam = $val\n"; ++$i; $found = 1; last; } } return if !$found; # write configuration file lines open (F, ">$fname.new") or die "Error: could not open $fname.new for writing"; foreach (@lines) { print F $_ or die "writing $fname.new: $!"; } close F; # copy permissions my @st = stat $fname or die "stat: $!"; chown $st[4], $st[5], "$fname.new"; # might fail as non-root chmod $st[2], "$fname.new" or die "chmod: $1"; rename "$fname.new", "$fname"; } # Return the port of a particular cluster or undef if the cluster # does not exist. # Arguments: sub get_cluster_port { return get_conf_value($_[0], $_[1], 'postgresql.conf', 'port'); } # Set the port of a particular cluster. # Arguments: sub set_cluster_port { set_conf_value $_[0], $_[1], 'postgresql.conf', 'port', $_[2]; } # Return cluster data directory. # Arguments: [] sub cluster_data_directory { my $d; if ($_[2]) { $d = ${$_[2]}{'data_directory'}; } else { $d = get_conf_value($_[0], $_[1], 'postgresql.conf', 'data_directory'); } if (!$d) { # fall back to /pgdata symlink (supported by earlier p-common releases) $d = readlink "$confroot/$_[0]/$_[1]/pgdata"; } ($d) = $d =~ /(.*)/ if defined $d; #untaint return $d; } # Return the socket directory of a particular cluster or undef if the cluster # does not exist. # Arguments: sub get_cluster_socketdir { # if it is explicitly configured, just return it my $socketdir = get_conf_value($_[0], $_[1], 'postgresql.conf', $_[0] >= 9.3 ? 'unix_socket_directories' : 'unix_socket_directory'); $socketdir =~ s/\s*,.*// if ($socketdir); # ignore additional directories for now return $socketdir if $socketdir; # try to determine whether this is a postgres owned cluster and we default # to /var/run/postgresql $socketdir = '/var/run/postgresql'; my @socketdirstat = stat $socketdir; error "Cannot stat $socketdir" unless @socketdirstat; if ($_[0] && $_[1]) { my $datadir = cluster_data_directory $_[0], $_[1]; error "Invalid data directory" unless $datadir; my @datadirstat = stat $datadir; unless (@datadirstat) { my @p = split '/', $datadir; my $parent = join '/', @p[0..($#p-1)]; error "$datadir is not accessible; please fix the directory permissions ($parent/ should be world readable)" unless @datadirstat; } $socketdir = '/tmp' if $socketdirstat[4] != $datadirstat[4]; } return $socketdir; } # Set the socket directory of a particular cluster. # Arguments: sub set_cluster_socketdir { set_conf_value $_[0], $_[1], 'postgresql.conf', $_[0] >= 9.3 ? 'unix_socket_directories' : 'unix_socket_directory', $_[2]; } # Return the path of a program of a particular version. # Arguments: sub get_program_path { return '' unless defined($_[0]) && defined($_[1]); my $path = "$binroot/$_[1]/bin/$_[0]"; ($path) = $path =~ /(.*)/; #untaint return $path if -x $path; return ''; } # Check whether a postmaster server is running at the specified port. # Arguments: sub cluster_port_running { die "port_running: invalid port $_[2]" if $_[2] !~ /\d+/; my $socketdir = get_cluster_socketdir $_[0], $_[1]; my $socketpath = "$socketdir/.s.PGSQL.$_[2]"; return 0 unless -S $socketpath; socket(SRV, PF_UNIX, SOCK_STREAM, 0) or die "socket: $!"; my $running = connect(SRV, sockaddr_un($socketpath)); close SRV; return $running ? 1 : 0; } # Read, verify, and return the current start.conf setting. # Arguments: # Returns: auto | manual | disabled sub get_cluster_start_conf { # start.conf setting my $start = 'auto'; my $start_conf = "$confroot/$_[0]/$_[1]/start.conf"; if (-e $start_conf) { open F, $start_conf or error "Could not open $start_conf: $!"; while () { s/#.*$//; s/^\s*//; s/\s*$//; next unless $_; $start = $_; last; } close F; error 'Invalid mode in start.conf' unless $start eq 'auto' || $start eq 'manual' || $start eq 'disabled'; } return $start; } # Change start.conf setting. # Arguments: # = auto | manual | disabled sub set_cluster_start_conf { my ($v, $c, $val) = @_; error "Invalid mode: '$val'" unless $val eq 'auto' || $val eq 'manual' || $val eq 'disabled'; my $perms = 0644; # start.conf setting my $start_conf = "$confroot/$_[0]/$_[1]/start.conf"; my $text; if (-e $start_conf) { open F, $start_conf or error "Could not open $start_conf: $!"; while () { if (/^\s*(?:auto|manual|disabled)\b(.*$)/) { $text .= $val . $1 . "\n"; } else { $text .= $_; } } # preserve permissions if it already exists $perms = (stat F)[2]; error "Could not get permissions of $start_conf: $!" unless $perms; close F; } else { $text = "# Automatic startup configuration # auto: automatically start/stop the cluster in the init script # manual: do not start/stop in init scripts, but allow manual startup with # pg_ctlcluster # disabled: do not allow manual startup with pg_ctlcluster (this can be easily # circumvented and is only meant to be a small protection for # accidents). $val "; } open F, '>' . $start_conf or error "Could not open $start_conf for writing: $!"; chmod $perms, $start_conf; print F $text; close F; } # Change pg_ctl.conf setting. # Arguments: # = options passed to pg_ctl(1) sub set_cluster_pg_ctl_conf { my ($v, $c, $opts) = @_; my $perms = 0644; # pg_ctl.conf setting my $pg_ctl_conf = "$confroot/$v/$c/pg_ctl.conf"; my $text = "# Automatic pg_ctl configuration # This configuration file contains cluster specific options to be passed to # pg_ctl(1). pg_ctl_options = '$opts' "; open F, '>' . $pg_ctl_conf or error "Could not open $pg_ctl_conf for writing: $!"; chmod $perms, $pg_ctl_conf; print F $text; close F; } # Return the PID from an existing PID file or undef if it does not exist. # Arguments: sub read_pidfile { return undef unless -e $_[0]; if (open PIDFILE, $_[0]) { my $pid = ; close PIDFILE; chomp $pid; ($pid) = $pid =~ /^(\d+)\s*$/; # untaint return $pid; } else { return undef; } } # Check whether a pid file is present and belongs to a running postmaster. # Returns undef if it cannot be determined # Arguments: sub check_pidfile_running { # postmaster does not clean up the PID file when it stops, and it is # not world readable, so only its absence is a definitive result; if it # is present, we need to read it and check the PID, which will only # work as root return 0 if ! -e $_[0]; my $pid = read_pidfile $_[0]; if (defined $pid) { prepare_exec; my $res = open PS, '-|', '/bin/ps', '-o', 'comm', 'h', 'p', $pid; restore_exec; if ($res) { my $process = ; chomp $process if defined $process; close PS; if (defined $process and ($process eq 'postmaster' or $process eq 'postgres')) { return 1; } else { return 0; } } else { error "Could not exec /bin/ps"; } } return undef; } # Return a hash with information about a specific cluster. # Arguments: # Returns: information hash (keys: pgdata, port, running, logfile [unless it # has a custom one], configdir, owneruid, ownergid, socketdir, # statstempdir) sub cluster_info { error 'cluster_info must be called with arguments' unless $_[0] && $_[1]; my %result; $result{'configdir'} = "$confroot/$_[0]/$_[1]"; my %postgresql_conf = read_cluster_conf_file $_[0], $_[1], 'postgresql.conf'; $result{'pgdata'} = cluster_data_directory $_[0], $_[1], \%postgresql_conf; $result{'port'} = $postgresql_conf{'port'} || $defaultport; $result{'socketdir'} = get_cluster_socketdir $_[0], $_[1]; $result{'statstempdir'} = $postgresql_conf{'stats_temp_directory'}; # if we can determine the running status with the pid file, prefer that if ($postgresql_conf{'external_pid_file'} && $postgresql_conf{'external_pid_file'} ne '(none)') { $result{'running'} = check_pidfile_running $postgresql_conf{'external_pid_file'}; } # otherwise fall back to probing the port; this is unreliable if the port # was changed in the configuration file in the meantime if (!defined ($result{'running'})) { $result{'running'} = cluster_port_running ($_[0], $_[1], $result{'port'}); } if ($result{'pgdata'}) { ($result{'owneruid'}, $result{'ownergid'}) = (stat $result{'pgdata'})[4,5]; $result{'recovery'} = -e "$result{'pgdata'}/recovery.conf"; } $result{'start'} = get_cluster_start_conf $_[0], $_[1]; # default log file (only if not expliticly configured in postgresql.conf) unless (exists $postgresql_conf{'log_filename'} || exists $postgresql_conf{'log_directory'} || (defined $postgresql_conf{'log_destination'} && $postgresql_conf{'log_destination'} eq 'syslog')) { my $log_symlink = $result{'configdir'} . "/log"; if (-l $log_symlink) { ($result{'logfile'}) = readlink ($log_symlink) =~ /(.*)/; # untaint } else { $result{'logfile'} = "/var/log/postgresql/postgresql-$_[0]-$_[1].log"; } } # autovacuum defaults to on since 8.3 $result{'avac_enable'} = config_bool $postgresql_conf{'autovacuum'} || ($_[0] >= '8.3'); return %result; } # Return an array of all available PostgreSQL versions sub get_versions { my @versions = (); if (opendir (D, $binroot)) { my $entry; while (defined ($entry = readdir D)) { next if $entry eq '.' || $entry eq '..'; ($entry) = $entry =~ /^(\d+\.\d+)$/; # untaint push @versions, $entry if get_program_path ('psql', $entry); } closedir D; } return @versions; } # Return the newest available version sub get_newest_version { my $newest = 0; map { $newest = $_ if $newest < $_ } get_versions; return $newest; } # Check whether a version exists sub version_exists { return (grep { $_ eq $_[0] } get_versions) ? 1 : 0; } # Return an array of all available clusters of given version # Arguments: sub get_version_clusters { my $vdir = $confroot.'/'.$_[0].'/'; my @clusters = (); if (opendir (D, $vdir)) { my $entry; while (defined ($entry = readdir D)) { next if $entry eq '.' || $entry eq '..'; ($entry) = $entry =~ /^(.*)$/; # untaint if (-r $vdir.$entry.'/postgresql.conf') { push @clusters, $entry; } } closedir D; } return @clusters; } # Check if a cluster exists. # Arguments: sub cluster_exists { for my $c (get_version_clusters $_[0]) { return 1 if $c eq $_[1]; } return 0; } # Return the next free PostgreSQL port. sub next_free_port { # create list of already used ports my @ports; for my $v (get_versions) { for my $c (get_version_clusters $v) { my $p = (get_conf_value $v, $c, 'postgresql.conf', 'port') || $defaultport; push @ports, $p; } } my $port; for ($port = $defaultport; $port < 65536; ++$port) { next if grep { $_ == $port } @ports; # check if port is already in use my ($have_ip4, $res4, $have_ip6, $res6); if (socket (SOCK, PF_INET, SOCK_STREAM, getprotobyname('tcp'))) { # IPv4 $have_ip4 = 1; $res4 = bind (SOCK, sockaddr_in($port, INADDR_ANY)); } $have_ip6 = 0; no strict; # avoid compilation errors with Perl < 5.14 if (exists $Socket::{"IN6ADDR_ANY"}) { # IPv6 if (socket (SOCK, PF_INET6, SOCK_STREAM, getprotobyname('tcp'))) { $have_ip6 = 1; $res6 = bind (SOCK, sockaddr_in6($port, Socket::IN6ADDR_ANY)); } } use strict; unless ($have_ip4 or $have_ip6) { # require at least one protocol to work (PostgreSQL needs it anyway # for the stats collector) die "could not create socket: $!"; } close SOCK; # return port if it is available on all supported protocols return $port if ($have_ip4 ? $res4 : 1) and ($have_ip6 ? $res6 : 1); } die "no free port found"; } # Return the PostgreSQL version, cluster, and database to connect to. version # is always set (defaulting to the version of the default port if no matching # entry is found, or finally to the latest installed version if there are no # clusters at all), cluster and database may be 'undef'. If only one cluster # exists, and no matching entry is found in the map files, that cluster is # returned. sub user_cluster_map { my ($user, $pwd, $uid, $gid) = getpwuid $>; my $group = (getgrgid $gid)[0]; # check per-user configuration file my $home = $ENV{"HOME"} || (getpwuid $>)[7]; my $homemapfile = $home . '/.postgresqlrc'; if (open MAP, $homemapfile) { while () { s/(.*?)#.*/$1/; next if /^\s*$/; my ($v,$c,$db) = split; if (!version_exists $v) { error "$homemapfile line $.: version $v does not exist"; } if (!cluster_exists $v, $c and $c !~ /^(\S+):(\d*)$/) { error "$homemapfile line $.: cluster $v/$c does not exist"; } if ($db) { close MAP; return ($v, $c, ($db eq "*") ? undef : $db); } else { print "Warning: ignoring invalid line $. in $homemapfile\n"; next; } } close MAP; } # check global map file if (open MAP, $mapfile) { while () { s/(.*?)#.*/$1/; next if /^\s*$/; my ($u,$g,$v,$c,$db) = split; if (!$db) { print "Warning: ignoring invalid line $. in $mapfile\n"; next; } if (!version_exists $v) { error "$mapfile line $.: version $v does not exist"; } if (!cluster_exists $v, $c and $c !~ /^(\S+):(\d*)$/) { error "$mapfile line $.: cluster $v/$c does not exist"; } if (($u eq "*" || $u eq $user) && ($g eq "*" || $g eq $group)) { close MAP; return ($v,$c, ($db eq "*") ? undef : $db); } } close MAP; } # if only one cluster exists, use that my $count = 0; my ($last_version, $last_cluster, $defaultport_version, $defaultport_cluster); for my $v (get_versions) { for my $c (get_version_clusters $v) { my $port = (get_conf_value $v, $c, 'postgresql.conf', 'port') || $defaultport; $last_version = $v; $last_cluster = $c; if ($port == $defaultport) { $defaultport_version = $v; $defaultport_cluster = $c; } ++$count; } } return ($last_version, $last_cluster, undef) if $count == 1; if ($count == 0) { # if there are no local clusters, use latest clients for accessing # network clusters return (get_newest_version, undef, undef); } # more than one cluster exists, return cluster at default port return ($defaultport_version, $defaultport_cluster, undef); } # Copy a file to a destination and setup permissions # Arguments: sub install_file { my ($source, $dest, $uid, $gid, $perm) = @_; if (system 'install', '-o', $uid, '-g', $gid, '-m', $perm, $source, $dest) { error "install_file: could not install $source to $dest"; } } # Change effective and real user and group id. Also activates all auxiliary # groups the user is in. Exits with an error message if user/group ID cannot be # changed. # Arguments: sub change_ugid { my ($uid, $gid) = @_; # auxiliary groups my $uname = (getpwuid $uid)[0]; prepare_exec; my $groups = "$gid " . `/usr/bin/id -G $uname`; restore_exec; $) = $groups; $( = $gid; $> = $< = $uid; error 'Could not change user id' if $< != $uid; error 'Could not change group id' if $( != $gid; } # Return the encoding of a particular database in a cluster. This requires # access privileges to that database, so this function should be called as the # cluster owner. # Arguments: # Returns: Encoding or undef if it cannot be determined. sub get_db_encoding { my ($version, $cluster, $db) = @_; my $port = get_cluster_port $version, $cluster; my $socketdir = get_cluster_socketdir $version, $cluster; my $psql = get_program_path 'psql', $version; return undef unless ($port && $socketdir && $psql); # try to swich to cluster owner prepare_exec 'LC_ALL'; $ENV{'LC_ALL'} = 'C'; my $orig_euid = $>; $> = (stat (cluster_data_directory $version, $cluster))[4]; open PSQL, '-|', $psql, '-h', $socketdir, '-p', $port, '-Atc', 'select getdatabaseencoding()', $db or die "Internal error: could not call $psql to determine db encoding: $!"; my $out = ; close PSQL; $> = $orig_euid; restore_exec; return undef if $?; chomp $out; ($out) = $out =~ /^([\w.-]+)$/; # untaint return $out; } # Return locale of a particular database in a cluster. This requires access # privileges to that database, so this function should be called as the cluster # owner. (For versions >= 8.4; for older versions use get_cluster_locales()). # Arguments: # Returns: (LC_CTYPE, LC_COLLATE) or (undef,undef) if it cannot be determined. sub get_db_locales { my ($version, $cluster, $db) = @_; my $port = get_cluster_port $version, $cluster; my $socketdir = get_cluster_socketdir $version, $cluster; my $psql = get_program_path 'psql', $version; return undef unless ($port && $socketdir && $psql); my ($ctype, $collate); # try to swich to cluster owner prepare_exec 'LC_ALL'; $ENV{'LC_ALL'} = 'C'; my $orig_euid = $>; $> = (stat (cluster_data_directory $version, $cluster))[4]; open PSQL, '-|', $psql, '-h', $socketdir, '-p', $port, '-Atc', 'SHOW lc_ctype', $db or die "Internal error: could not call $psql to determine db lc_ctype: $!"; my $out = ; close PSQL; ($ctype) = $out =~ /^([\w.\@-]+)$/; # untaint open PSQL, '-|', $psql, '-h', $socketdir, '-p', $port, '-Atc', 'SHOW lc_collate', $db or die "Internal error: could not call $psql to determine db lc_collate: $!"; $out = ; close PSQL; ($collate) = $out =~ /^([\w.\@-]+)$/; # untaint $> = $orig_euid; restore_exec; chomp $ctype; chomp $collate; return ($ctype, $collate) unless $?; return (undef, undef); } # Return the CTYPE and COLLATE locales of a cluster. This needs to be called # as root or as the cluster owner. (For versions <= 8.3; for >= 8.4, use # get_db_locales()). # Arguments: # Returns: (LC_CTYPE, LC_COLLATE) or (undef,undef) if it cannot be determined. sub get_cluster_locales { my ($version, $cluster) = @_; my ($lc_ctype, $lc_collate) = (undef, undef); if ($version >= '8.4') { print STDERR "Error: get_cluster_locales() does not work for 8.4+\n"; exit 1; } my $pg_controldata = get_program_path 'pg_controldata', $version; if (! -e $pg_controldata) { print STDERR "Error: pg_controldata not found, please install postgresql-$version\n"; exit 1; } prepare_exec ('LC_ALL', 'LANG', 'LANGUAGE'); $ENV{'LC_ALL'} = 'C'; my $result = open (CTRL, '-|', $pg_controldata, (cluster_data_directory $version, $cluster)); restore_exec; return (undef, undef) unless defined $result; while () { if (/^LC_CTYPE\W*(\S+)\s*$/) { $lc_ctype = $1; } elsif (/^LC_COLLATE\W*(\S+)\s*$/) { $lc_collate = $1; } } close CTRL; return ($lc_ctype, $lc_collate); } # Return an array with all databases of a cluster. This requires connection # privileges to template1, so this function should be called as the # cluster owner. # Arguments: # Returns: array of database names or undef on error. sub get_cluster_databases { my ($version, $cluster) = @_; my $port = get_cluster_port $version, $cluster; my $socketdir = get_cluster_socketdir $version, $cluster; my $psql = get_program_path 'psql', $version; return undef unless ($port && $socketdir && $psql); # try to swich to cluster owner prepare_exec 'LC_ALL'; $ENV{'LC_ALL'} = 'C'; my $orig_euid = $>; $> = (stat (cluster_data_directory $version, $cluster))[4]; my @dbs; my @fields; if (open PSQL, '-|', $psql, '-h', $socketdir, '-p', $port, '-Atl') { while () { chomp; @fields = split '\|'; next if $#fields < 2; # remove access privs which get line broken push (@dbs, $fields[0]); } close PSQL; } $> = $orig_euid; restore_exec; return $? ? undef : @dbs; } # Return the device name a file is stored at. # Arguments: # Returns: device name, or '' if it cannot be determined. sub get_file_device { my $dev = ''; prepare_exec; my $pid = open3(\*CHLD_IN, \*CHLD_OUT, \*CHLD_ERR, '/bin/df', $_[0]); waitpid $pid, 0; # we simply ignore exit code and stderr while () { if (/^\/dev/) { $dev = (split)[0]; } } restore_exec; close CHLD_IN; close CHLD_OUT; close CHLD_ERR; return $dev; } # Parse a single pg_hba.conf line. # Arguments: # Returns: Hash reference (only returns line and type==undef for invalid lines) # line -> the verbatim pg_hba line # type -> comment, local, host, hostssl, hostnossl, undef # db -> database name # user -> user name # method -> trust, reject, md5, crypt, password, krb5, ident, pam # ip -> ip address # mask -> network mask (either a single number as number of bits, or bit mask) my %valid_methods = qw/trust 1 reject 1 md5 1 crypt 1 password 1 krb5 1 ident 1 pam 1/; sub parse_hba_line { my $l = $_[0]; chomp $l; # comment line? return { 'type' => 'comment', 'line' => $l } if ($l =~ /^\s*($|#)/); my $res = { 'line' => $l }; my @tok = split /\s+/, $l; goto error if $#tok < 3; $$res{'type'} = shift @tok; $$res{'db'} = shift @tok; $$res{'user'} = shift @tok; # local connection? if ($$res{'type'} eq 'local') { goto error if $#tok > 1; goto error unless $valid_methods{$tok[0]}; $$res{'method'} = join (' ', @tok); return $res; } # host connection? if ($$res{'type'} =~ /^host((no)?ssl)?$/) { my ($i, $c) = split '/', (shift @tok); goto error unless $i; $$res{'ip'} = $i; # CIDR mask given? if (defined $c) { goto error if $c !~ /^(\d+)$/; $$res{'mask'} = $c; } else { $$res{'mask'} = shift @tok; } goto error if $#tok > 1; goto error unless $valid_methods{$tok[0]}; $$res{'method'} = join (' ', @tok); return $res; } error: $$res{'type'} = undef; return $res; } # Parse given pg_hba.conf file. # Arguments: # Returns: Array with hash refs; for hash contents, see parse_hba_line(). sub read_pg_hba { open HBA, $_[0] or return undef; my @hba; while () { my $r = parse_hba_line $_; push @hba, $r; } close HBA; return @hba; } 1; postgresql-common-154ubuntu1.1/pgxs_debian_control.mk0000664000000000000000000000115112272232131017776 0ustar # # produce a debian/control file from a debian/control.in # # In debian/rules, include /usr/share/postgresql-common/pgxs_debian_control.mk # # Author: Dimitri Fontaine # debian/control: debian/control.in debian/pgversions (set -e; \ VERSIONS=`pg_buildext supported-versions $(CURDIR)`; \ grep-dctrl -vP PGVERSION $< > $@.pgxs_tmp; \ for v in $$VERSIONS; do \ grep-dctrl -P PGVERSION $< | sed -e "s:PGVERSION:$$v:" >> $@.pgxs_tmp; \ done; \ mv $@.pgxs_tmp $@) || (rm -f $@.pgxs_tmp; exit 1) # Rebuild debian/control when clean is invoked clean: debian/control .PHONY: debian/control postgresql-common-154ubuntu1.1/run-upgrade-scripts0000775000000000000000000000714212143322136017276 0ustar #!/usr/bin/perl -w # Run all upgrade scripts. # # (C) 2005-2009 Martin Pitt # # 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 strict; use lib '/usr/share/postgresql-common'; use PgCommon; error "Usage: $0 " if $#ARGV != 0; # Return the cluster's databases that match the given scope. # Arguments: sub dbs_from_scope { my ($v, $c, $scope) = @_; my @dbs = get_cluster_databases $v, $c; unless (defined $dbs[0]) { print ' Error: cluster is not running'; return (); } # filter out the postgres database @dbs = grep { $_ ne 'postgres' } @dbs; return @dbs if $scope eq 't0'; return grep { $_ ne 'template0' } @dbs if $scope eq 't1'; return grep { $_ ne 'template0' && $_ ne 'template1' } @dbs if $scope eq 'db'; return grep { $_ eq 'template1' } @dbs if $scope eq 'cluster'; } # Arguments: