#!/usr/bin/perl -wT # Rename a PostgreSQL cluster # # (C) 2014-2021 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 warnings; use PgCommon; use Getopt::Long; use POSIX; # untaint environment $ENV{'PATH'} = '/bin:/usr/bin'; delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; if (@ARGV != 3) { print "Usage: $0 [OPTIONS] \n"; exit 1; } my ($version) = $ARGV[0] =~ /^(\d+\.?\d+)$/; my ($oldcluster) = $ARGV[1] =~ /^([-.\w]+)$/; my ($newcluster) = $ARGV[2] =~ /^([-.\w]+)$/; if ($newcluster =~ /-/ and -t 1) { print "Warning: cluster names containing dashes (-) will cause problems when running from systemd. Continuing anyway\n"; } error "Old and new name must be different" if ($oldcluster eq $newcluster); error "specified cluster $version $oldcluster does not exist" unless (cluster_exists $version, $oldcluster); error "target cluster $version $newcluster already exists" if (cluster_exists $version, $newcluster); my %info = cluster_info ($version, $oldcluster); validate_cluster_owner \%info; # stopping old cluster, so that we notice early when there are still # connections if ($info{'running'}) { print "Stopping cluster $version $oldcluster ...\n"; my @argv = ('pg_ctlcluster', $version, $oldcluster, 'stop'); error "Could not stop cluster" if system @argv; } # Arguments: , , sub strrepl { my ($s, $f, $t) = @_; $s =~ s/\b\Q$f\E\b/$t/g; return $s; } # rename config directory my $olddir = "$PgCommon::confroot/$version/$oldcluster"; my $newdir = "$PgCommon::confroot/$version/$newcluster"; rename $olddir, $newdir or error "Could not rename config directory $olddir: $!"; # adapt paths to configuration files my %c = read_cluster_conf_file $version, $newcluster, 'postgresql.conf'; if ($c{hba_file}) { PgCommon::set_conf_value $version, $newcluster, 'postgresql.conf', 'hba_file', strrepl($c{hba_file}, $oldcluster, $newcluster); } if ($c{ident_file}) { PgCommon::set_conf_value $version, $newcluster, 'postgresql.conf', 'ident_file', strrepl($c{ident_file}, $oldcluster, $newcluster); } if ($c{external_pid_file}) { PgCommon::set_conf_value $version, $newcluster, 'postgresql.conf', 'external_pid_file', strrepl($c{external_pid_file}, $oldcluster, $newcluster); } # update cluster_name if ($c{cluster_name}) { PgCommon::set_conf_value $version, $newcluster, 'postgresql.conf', 'cluster_name', strrepl ($c{cluster_name}, $oldcluster, $newcluster); } # rename data directory if ($info{pgdata}) { my $newpgdata = strrepl ($info{pgdata}, $oldcluster, $newcluster); if ($info{pgdata} ne $newpgdata) { rename $info{pgdata}, $newpgdata or error "Could not rename data directory $info{pgdata}: $!"; PgCommon::set_conf_value $version, $newcluster, 'postgresql.conf', 'data_directory', $newpgdata; } } # rename stats_temp_directory my $statstempdir = $info{config}->{stats_temp_directory}; if ($statstempdir) { my $newstatstempdir = strrepl ($statstempdir, $oldcluster, $newcluster); if ($statstempdir ne $newstatstempdir) { PgCommon::set_conf_value $version, $newcluster, 'postgresql.conf', 'stats_temp_directory', $newstatstempdir; if (-d $statstempdir) { rename $statstempdir, $newstatstempdir or error "Could not rename stats temp directory $statstempdir}: $!"; } } } # rename old log files my $logdir = "/var/log/postgresql"; if (opendir LOG, $logdir) { while (my $logfile = readdir LOG) { next unless $logfile =~ /^(\Qpostgresql-$version-$oldcluster.log\E.*)/; $logfile = $1; # untaint my $f = strrepl ($logfile, $oldcluster, $newcluster); rename "$logdir/$logfile", "$logdir/$f" or error "rename $logdir/$logfile: $!"; } closedir LOG; } # notify systemd about the new cluster if (not exists $ENV{'PG_CLUSTER_CONF_ROOT'} and -d '/run/systemd/system') { if ($> == 0) { system 'systemctl daemon-reload'; } elsif (-t 1) { print "Warning: systemd does not know about the new cluster yet. Operations like \"service postgresql start\" will not handle it. To fix, run:\n"; print " sudo systemctl daemon-reload\n"; } } # start cluster if it was running before if ($info{'running'}) { print "Starting cluster $version $newcluster ...\n"; my @argv = ('pg_ctlcluster', $version, $newcluster, 'start'); error "Could not start cluster" if system @argv; } __END__ =head1 NAME pg_renamecluster - rename a PostgreSQL cluster =head1 SYNOPSIS B I I I =head1 DESCRIPTION B changes the name of a PostgreSQL cluster, i. e. the name of the config directory in /etc/postgresql/I/ along with the data directory in /var/lib/postgresql/I/. Existing log files in /var/log/postgresql/ are also renamed. The cluster is stopped and started for the operation. The following B config options are updated to refer to the changed path names: B, B, B, B, B, B. =head1 OPTIONS None. =head1 SEE ALSO L, L, L, L =head1 AUTHOR Christoph Berg Lmyon@debian.orgE>