#!/bin/bash # # postgresql-setup - Initialization and upgrade operations for PostgreSQL test -z "$PATH" && export PATH="/sbin:/usr/sbin:/bin:/usr/bin" test x"$PGSETUP_DEBUG" != x && set -x && PS4='${LINENO}: ' # Full PostgreSQL version, e.g. 9.0.2 PGVERSION=@PGVERSION@ # Major version of PostgreSQL, e.g. 9.0 PGMAJORVERSION=@PGMAJORVERSION@ # Directory containing the postmaster executable PGENGINE=@PGENGINE@ # Previous major version, e.g., 8.4, for upgrades PREVMAJORVERSION=@PREVMAJORVERSION@ # Directory containing the previous postmaster executable PREVPGENGINE=@PREVPGENGINE@ # Distribution README file README_DIST=@README_DIST@ # Log file for initdb PGLOG=@INITDB_LOG@ # Log file for pg_upgrade PGUPLOG=@UPGRADE_LOG@ SYSCONFIG_DIR=@PKGCONFIG_DIR@ SU=@SU@ USAGE_STRING=$"\ Usage: $0 MODE [OPTION...] [--unit UNIT_NAME] Script is aimed to help sysadmin with basic database cluster administration. The UNIT_NAME is used for selection of proper sysconfig or unit configuration file; For more info and howto/when use this script please look at the docu file $README_DIST. The 'postgresql' string is used when no UNIT_NAME is explicitly passed. Available operation mode: --initdb Create a new PostgreSQL database cluster. This is usually the first action you perform after PostgreSQL server installation. --upgrade Upgrade PostgreSQL database cluster to be usable with new server. Use this if you upgraded your PostgreSQL server to newer major version (currently from $PREVMAJORVERSION \ to $PGMAJORVERSION). Options: --unit unit ID of PostgreSQL to run against --help show this help --version show version of this package --debug show basic debugging information Environment: PGSETUP_INITDB_OPTIONS Options carried by this variable are passed to subsequent call of \`initdb\` binary (see man initdb(1)). This variable is used also during 'upgrade' mode because the new cluster is actually re-initialized from the old one. PGSETUP_PGUPGRADE_OPTIONS Options in this variable are passed next to the subsequent call of \`pg_upgrade\`. For more info about possible options please look at man pg_upgrade(1). PGSETUP_DEBUG Set to '1' if you want to see debugging output." die() { echo >&2 $"FATAL: $@" ; exit 1 ; } error() { echo >&2 $"ERROR: $@" ; } error_q() { echo >&2 $" $@" ; } warn() { echo >&2 $"WARNING: $@" ; } info() { echo >&2 $" * $@" ; } debug() { test "$option_debug" = "1" && echo >&2 $"DEBUG: $@"; } print_version() { echo "postgresql@DISTSUFF@-setup @VERSION@" echo $"Built against PostgreSQL version @PGMAJORVERSION@ and configured" echo $"to upgrade from PostgreSQL version @PREVMAJORVERSION@." } # code shared between initdb and upgrade actions perform_initdb() { if [ ! -e "$pgdata" ]; then mkdir "$pgdata" || return 1 chown postgres:postgres "$pgdata" chmod go-rwx "$pgdata" fi # Clean up SELinux tagging for pgdata [ -x /sbin/restorecon ] && /sbin/restorecon "$pgdata" # Create the initdb log file if needed if [ ! -e "$PGLOG" -a ! -h "$PGLOG" ]; then touch "$PGLOG" || return 1 chown postgres:postgres "$PGLOG" chmod go-rwx "$PGLOG" [ -x /sbin/restorecon ] && /sbin/restorecon "$PGLOG" fi # Initialize the database initdbcmd="$PGENGINE/initdb --pgdata='$pgdata' --auth='ident'" initdbcmd+=" $PGSETUP_INITDB_OPTIONS" $SU -l postgres -c "$initdbcmd" >> "$PGLOG" 2>&1 < /dev/null # Create directory for postmaster log files mkdir "$pgdata/pg_log" chown postgres:postgres "$pgdata/pg_log" chmod go-rwx "$pgdata/pg_log" [ -x /sbin/restorecon ] && /sbin/restorecon "$pgdata/pg_log" local pgconf="$pgdata/postgresql.conf" sed -i "s|^[[:space:]#]*port[[:space:]]=[^#]*|port = $pgport |g" \ "$pgconf" \ && grep "^port = " "$pgconf" >/dev/null test $? -ne 0 && { error "can not change port in $pgdata/postgresql.conf" return 1 } if [ -f "$pgdata/PG_VERSION" ]; then return 0 fi return 1 } initdb() { if [ -f "$pgdata/PG_VERSION" ]; then error $"Data directory $pgdata is not empty!" script_result=1 else info $"Initializing database in $pgdata." if perform_initdb; then info $"Initialized." else error $"Initializing database failed, see $PGLOG" script_result=1 fi fi } upgrade() { # must see previous version in PG_VERSION if [ ! -f "$pgdata/PG_VERSION" -o \ x`cat "$pgdata/PG_VERSION"` != x"$PREVMAJORVERSION" ] then error $"Cannot upgrade because the database in $pgdata is not of" error_q $"compatible previous version $PREVMAJORVERSION." exit 1 fi if [ ! -x "$PGENGINE/pg_upgrade" ]; then echo echo $"Please install the postgresql-upgrade RPM." echo exit 5 fi # Set up log file for pg_upgrade rm -f "$PGUPLOG" touch "$PGUPLOG" || exit 1 chown postgres:postgres "$PGUPLOG" chmod go-rwx "$PGUPLOG" [ -x /sbin/restorecon ] && /sbin/restorecon "$PGUPLOG" # Move old DB to pgdataold pgdataold="${pgdata}-old" rm -rf "$pgdataold" mv "$pgdata" "$pgdataold" || exit 1 # Create configuration file for upgrade process HBA_CONF_BACKUP="$pgdataold/pg_hba.conf.postgresql-setup.`date +%s`" HBA_CONF_BACKUP_EXISTS=0 if [ ! -f $HBA_CONF_BACKUP ]; then mv "$pgdataold/pg_hba.conf" "$HBA_CONF_BACKUP" HBA_CONF_BACKUP_EXISTS=1 # For fluent upgrade 'postgres' user should be able to connect # to any database without password. Temporarily, no other type # of connection is needed. echo "local all postgres ident" > "$pgdataold/pg_hba.conf" fi echo -n $"Upgrading database: " # Create empty new-format database if perform_initdb; then # Do the upgrade $SU -l postgres -c "$PGENGINE/pg_upgrade \ '--old-bindir=$PREVPGENGINE' \ '--new-bindir=$PGENGINE' \ '--old-datadir=$pgdataold' \ '--new-datadir=$pgdata' \ --link \ '--old-port=$PGPORT' '--new-port=$PGPORT' \ --user=postgres \ $PGSETUP_PGUPGRADE_OPTIONS" \ >> "$PGUPLOG" 2>&1 < /dev/null if [ $? -ne 0 ]; then # pg_upgrade failed script_result=1 fi else # initdb failed script_result=1 fi # Move back the backed-up pg_hba.conf regardless of the script_result. if [ x$HBA_CONF_BACKUP_EXISTS = x1 ]; then mv -f "$HBA_CONF_BACKUP" "$pgdataold/pg_hba.conf" fi if [ $script_result -eq 0 ]; then echo $"OK" echo echo $"The configuration files were replaced by default configuration." echo $"The previous configuration and data are stored in folder" echo $pgdataold. else # Clean up after failure rm -rf "$pgdata" mv "$pgdataold" "$pgdata" echo $"failed" fi echo echo $"See $PGUPLOG for details." } handle_sysconfig() { local mode="$1" local service="$2" local sysconfig_file="$SYSCONFIG_DIR/$service" test -r "$sysconfig_file" || { warn "system config file '$sysconfig_file' not found or unreadable" return 1 } unset PGPORT PGDATA . "$sysconfig_file" sysconfig_pgdata="$PGDATA" sysconfig_pgport="$PGPORT" unset PGPORT PGDATA test -n "$sysconfig_pgdata" && debug "sysconfig pgdata: '$sysconfig_pgdata'" test -n "$sysconfig_pgport" && debug "sysconfig pgport: $sysconfig_pgport" } # This is mostly for backward compatibility with version postgresql-setup from # postgresql package <= 9.3.4-7 as this type of configuration is not adviced # anymore. But user still may override the /etc/sysconfig/* settings with # Environment= statement in service file. Note that this parsing technique # fails for PGDATA pathnames containing spaces, but there's not much we can do # about it given systemctl's output format. handle_service_file() { local mode="$1" local service="$2" local systemd_env="$(systemctl show -p Environment "${service}.service")" \ || { return; } for env_var in `echo "$systemd_env" | sed 's/^Environment=//'`; do # If one variable name is defined multiple times the last definition wins. case "$env_var" in PGDATA=*) unit_pgdata="${env_var##PGDATA=}" debug "unit's datadir: '$unit_pgdata'" ;; PGPORT=*) unit_pgport="${env_var##PGPORT=}" debug "unit's pgport: $unit_pgport" ;; esac done } handle_pgconf() { local mode="$1" local datadir="$2" local conffile="$datadir/postgresql.conf" test "$mode" = initdb && return 0 debug "postgresql.conf: $conffile" test -r "$conffile" || { error "config file $conffile is not readable or does not exist" return 1 } local sp='[[:space:]]' local sed_expr="s/^$sp*port$sp*=$sp\([0-9]\+\).*/\1/p" rv=0 conf_pgport=`sed -n "$sed_expr" $conffile | tail -1` || rv=1 test -n "$conf_pgport" && debug "postgresql.conf pgport: $conf_pgport" return $rv } # # Alow users to use the old style arguments like # 'postgresql-setup initdb $SERVICE_NAME'. case "$1" in initdb|upgrade) action="--$1" shift warn "using obsoleted argument syntax, try --help" old_long_args="help,usage,version,debug" oldargs=`getopt -o "" -l "$old_long_args" -n "old-options" -- "$@"` \ || die "can't parse old arguments" eval set -- "$oldargs" additional_opts= while true; do case "$1" in --version|--help|--usage|--debug) additional_opts="$additional_opts $1" shift ;; --) shift break ;; esac done service=postgresql if test -n "$1"; then service=$1 shift fi set -- $additional_opts "$action" --unit "$service" "$@" warn "arguments transformed to: ${0##*/} $@" esac # # postgresql-setup arguments are parsed into those variables option_mode=none option_service=postgresql option_port= option_debug=0 # Content of /etc/sysconfig/$option_service fills those: sysconfig_pgdata= sysconfig_pgport= # Configuration from (/etc/systemd/system/$option_service.service) fills those # variables (this is here for compat, users should user rather sysconfig). unit_pgdata= unit_pgport= # Configuration from postgresql.conf: conf_pgport= # Key variables. Try to fill them postgresql.conf, sysconfig or unit file # configuration (the later mentioned has more priority). pgdata=default pgport=default short_opts="" long_opts="\ initdb,upgrade,\ unit:,service:,port:,\ debug,\ version,help,usage" args=`getopt -o "$short_opts" -l "$long_opts" -n "postgresql-setup" -- "$@"` \ || die "can't parse arguments" eval set -- "$args" parse_fail=0 while true; do case "$1" in --initdb|--upgrade) if test "$option_mode" != none; then error "bad argument $1, mode already specified: --$option_mode" parse_fail=1 else option_mode=${1##--} fi shift ;; --unit|--service) option_service=$2 shift 2 ;; --port) option_port=$2 shift 2 ;; --debug) option_debug=1 shift ;; --help|--usage) echo "$USAGE_STRING" exit 0 ;; --version) print_version exit 0 ;; --) shift break ;; *) die "author's fault: option $1 not handled" break ;; esac done test $parse_fail -ne 0 && die "can't parse arguments" test "$option_mode" = none \ && die "no mode specified, use --initdb or --upgrade, or --help" [[ "$option_port" =~ ^[0-9]*$ ]] \ || die $"port set to '$option_port', must be integer number" test -n "$option_port" && pgport=$option_port debug "mode used: $option_mode" debug "service name: $option_service" debug "port: $pgport" handle_sysconfig "$option_mode" "$option_service" handle_service_file "$option_mode" "$option_service" test -n "$sysconfig_pgdata" && pgdata="$sysconfig_pgdata" test -n "$unit_pgdata" && pgdata="$unit_pgdata" test "$pgdata" = default && die "no datadir specified" [[ "$pgdata" =~ ^/.* ]] \ || die $"the PostgreSQL datadir not absolute path: '$pgdata', try --debug" handle_pgconf "$option_mode" "$pgdata" || die "can not parse postgresql.conf" test -n "$conf_pgport" && pgport="$conf_pgport" test -n "$sysconfig_pgport" && pgport="$sysconfig_pgport" test -n "$unit_pgport" && pgport="$unit_pgport" if test $option_mode = initdb -a "$pgport" = default; then test $option_service == postgresql \ && pgport=5432 \ || die $"for initdb $option_service, the --port must be specified" fi test "$pgport" = default \ && die $"\ port is not set by postgresql.conf, '$SYSCONFIG_DIR/$option_service' \ nor by --port" # These variables are read by underlying utilites, rather export them. export PGDATA=$pgdata export PGPORT=$pgport script_result=0 # See how we were called. case "$option_mode" in initdb) initdb ;; upgrade) upgrade ;; *) echo >&2 "$USAGE_STRING" exit 2 esac exit $script_result