#!/bin/bash

#END GENERATED SECTION

# NOTEs:
# * #1007802    - There is no easy solution for the bug #1007802.  The most
#       logical is to warn users in KB article about that and to allow users to
#       specify additional initdb options to postgresql-setup (bug #1052063).
#       Not a good material for PA.

# allow better case matching
shopt -s extglob

# settings
HOME_DIR=/var/lib/pgsql
DATA_DIR=$HOME_DIR/data
PG_VERSION_FILE=$DATA_DIR/PG_VERSION
POSTGRESQL_CONF_FILE=$DATA_DIR/postgresql.conf
PG_HBA_CONF_FILE=$DATA_DIR/pg_hba.conf
INITFILE=/etc/rc.d/init.d/postgresql

# FIXME: KB article!
README_DIST_FILE=`rpm -ql postgresql | grep README | grep dist`
KB_ARTICLE_UPGRADE="https://access.redhat.com/site/articles/541873"

# particular results
SOLUTION_TEXT=
HOME_DIR_OK=no
DATA_DIR_OK=no
DATA_DIR_INITIALIZED=no
DIFFERENT_USAGE_OK=no
STARTED_OK=no
OPTIONS_OK=no
PLUGINS_OK=no

SKIP_TESTING=no
PLAN_STOP_SERVER=no

SERVICE_BIN=/sbin/service
CHKCONFIG_BIN=/sbin/chkconfig

PG_NEW_VERSION=9.2
PG_OLD_VERSION=8.4
UPSTREAM_DOC_PGDUMP=http://www.postgresql.org/docs/9.2/static/upgrading.html

# FIXME: remove once $PATH is correctly propagated
export PATH="$PATH:/usr/bin:/usr/sbin:/sbin:/bin"

LIBDIR=`rpm --eval '%{_libdir}'`
PLUGINDIR=$LIBDIR/pgsql

# run command under postgres user (requires running under root)
run_as_postgres() {
    param="$@"
    su - postgres -c "$param"
}

# <LIB-CANDIDATES>

_FIRST_APPEND=1
append_to_solution()
{
    if test ! $_FIRST_APPEND -eq 1; then
        echo >> $SOLUTION_FILE
    fi
    _FIRST_APPEND=0
    cat >> $SOLUTION_FILE
}


# find hardlinks to filename in specified directory
# -------------------------------------------------
# find_hardlinks FILE WHERE [WHERE ...]
#
# @FILE: file name
# @WHERE: directory where to try to find
find_hardlinks()
{
    res=1
    file=$1 ; shift
    while test "$#" -gt 0; do
        dir=$1 ; shift
        find "$dir" -xdev -samefile "$file" | grep -v "^$file$"
        test $? -eq 0 && res=0
    done
    return $res
}

# remove the version & relase part from NVR
# -----------------------------------------
# STREAM | remove_ver_rel > OUTPUT
remove_ver_rel() {
    sed 's|-[^-]*-[^-]*$||'
}


# find symlinks to filename in specified directory
# ------------------------------------------------
# find_symlinks FILE WHERE [WHERE ...]
find_symlinks()
{
    res=1
    file=$1 ; shift
    while test "$#" -gt 0; do
        dir=$1 ; shift
        for link in `find -L "$dir" -samefile "$file" | grep -v "^$file$"`; do
            test -h "$link" && echo $link
        done
        test $? -eq 0 && res=0
    done
    return $res
}

# find symlinks/hard links to filename in specified directory
# -----------------------------------------------------------
# find_links FILE WHERE [WHERE ...]
find_links()
{
    res=1
    file=$1 ; shift
    while test "$#" -gt 0; do
        dir=$1 ; shift
        find "$dir" -lname "$file"
        find -L "$dir" -samefile "$file" | grep -v "^$file$"
        test $? -eq 0 && res=0
    done
    return $res
}

# </LIB-CANDIDATES>

check_home_dir() {
    $FUNC_ENTRY

    postgres_home=`run_as_postgres pwd`
    if test "$postgres_home" != "$HOME_DIR"; then
        log_error "Incorrect PostgreSQL home directory '$postgres_home'."
        SKIP_TESTING=yes
        return $RESULT_FAIL
    fi

    HOME_DIR_OK=yes
    log_info "$STR_OK PostgreSQL home directory is '$postgres_home'."
    return $RESULT_PASS
}

# Check that $PGDATA points to the expected path and that the directory exists.
check_data_dir() {
    $FUNC_ENTRY

    pgdata_dir=`run_as_postgres 'echo $PGDATA'`
    if test "$pgdata_dir" != "$DATA_DIR"; then
        log_high_risk "The PGDATA directory is $pgdata_dir instead of $DATA_DIR."
        SKIP_TESTING=yes
        return $RESULT_FAIL
    fi

    if test ! -d "$pgdata_dir"; then
        log_error "The PGDATA directory $pgdata_dir does not exist."
        SKIP_TESTING=yes
        return $RESULT_FAIL
    fi

    DATA_DIR_OK=yes
    log_info "$STR_OK PGDATA points to the correct path '$DATA_DIR'."
    return $RESULT_PASS
}

check_is_initialized() {
    $FUNC_ENTRY

    if test ! -f "$PG_VERSION_FILE"; then
        append_to_solution <<EOF
* Important: It seems that you have installed a PostgreSQL server, but you
  have not initialized the data directory. It means that either you have never
  used a PostgreSQL server or you are using a PostgreSQL server from the
  postgresql-server package in a different way (which needs a manual
  interaction).
EOF
        log_error \
            "The $PG_VERSION_FILE file does not exist, are you using a PostgreSQL server?"
        SKIP_TESTING=yes
        return $RESULT_FAIL
    fi

    DATA_DIR_INITIALIZED=yes
    log_info "The $PG_VERSION_FILE file is in its place, and the database seems to be initialized."
    return $RESULT_PASS
}

# Check that the /etc/rc.d/initd./postgresql is the only controller for
# postgresql server.

check_different_usage() {
    $FUNC_ENTRY

    # check that the init file exists
    if test ! -f "$INITFILE"; then
        log_error "The initfile $INITFILE not found."
        SKIP_TESTING=yes
        return $RESULT_FAIL
    fi

    # we support runing multiple instances of PostgreSQL server at the same
    # time (symlinking the service file can be symptom of this usage) but we are
    # unable to deal with this configuration automatically.  Rather set high
    # risk.
    links=`find_links "$INITFILE" /etc/rc.d/init.d`
    if test $? -eq 0; then
        log_high_risk "There seems to be a non-default PostgreSQL init.d usage" \
                      "via $INITFILE symbolic links or hardlinks."
        append_to_solution <<EOF
* We support running multiple instances of the PostgreSQL server at the same time
  (achieved usually by symlinking the init file). This situation was detected
  on your system. Unfortunately, we are unable to handle such cases
  automatically in the Preupgrade Assistant. Look at upstream pg_dumpall
  documentation and follow the steps with respect to your specific
  configuration. The links are:
`echo "$links" | sed 's|^|    |'`
EOF
        SKIP_TESTING=yes
        return $RESULT_FAIL
    fi

    append_to_solution <<EOF
* Even if the postgresql-server is probably configured correctly, we are unable
  to say for 100% that the server is not used in some specific way on your
  machine, so check that the
  $INITFILE initfile is the only trigger the server is started by.
  In any way, we suggest that you use the in-place PostgreSQL database upgrade for
  the conversion of the data stack to a newer PostgreSQL server.
  Still, the full data directory backup ($DATA_DIR) is the  administrator's responsibility. Up to that, backing up the database dump by
  running the "pg_dumpall" tool is strictly encouraged, because if something
  with the in-place upgrade goes wrong on the updated system, you will not be able
  to go back easily to the earlier Red Hat Enterprise Linux 6 PostgreSQL $PG_OLD_VERSION.
  See the upstream article for pg_dumpall:
  [link:$UPSTREAM_DOC_PGDUMP]
EOF
    log_slight_risk "We cannot tell for 100% that the system will be in-place upgradeable."
    DIFFERENT_USAGE_OK=yes
    return $RESULT_PASS
}

start_server() {
    $FUNC_ENTRY
    STARTED_OK=yes

    $SERVICE_BIN postgresql status &>/dev/null
    if test $? -eq 0; then
        log_info "The PostgreSQL server is already running."
        return $RESULT_PASS
    fi
    # try to start..
    $SERVICE_BIN postgresql start &>/dev/null
    res=$?
    if test "$res" -eq 0; then
        log_info "Successfully started the PostgreSQL server."
        PLAN_STOP_SERVER=yes
        return $RESULT_PASS
    fi

    log_error "Cannot start the PostgreSQL server."
    STARTED_OK=no
    return $RESULT_FAIL
}

# start and plan stopping the server if it is not running
check_started() {
    $FUNC_ENTRY
    start_server
    return $?
}

# warn if it is not enabled - it is quite weird and it may be pretty easily more
# comfortable to uninstall the server completely
check_enabled() {
    $FUNC_ENTRY

    # FIXME: is this correct way?
    $CHKCONFIG_BIN --list postgresql | grep "on" >/dev/null
    if test "$?" -eq 0; then
        log_info "PostgreSQL is enabled at least in one runlevel."
        return $RESULT_PASS
    fi

    log_error "PostgreSQL is not enabled after the system startup."
    append_to_solution <<EOF
* Note that PostgreSQL is not enabled at the system startup. This is not really
  risky, but it is at least worth observing. If that is on a server machine, PostgreSQL seems to be unused. If that is true, the sysadmin may uninstall the postgresql-server package.
EOF
    return $RESULT_FAIL
}

# Check that 'postgres' user has administrator permissions
check_permissions() {
    $FUNC_ENTRY
    if test "$STARTED_OK" != "yes"; then
        log_error "To check the database permissions, we need to have the server started."
        return $RESULT_FAIL
    fi

    # It does not really matter what is set in pg_hba.conf;  this file is
    # removed during upgrade (and moved to backup directory).  User is informed
    # after successful upgrade where can found the old configuration files.  But
    # if the user 'postgres' is not able to login without password, we are
    # unable to check whether the user is Superuser.  (NOTE: Is this check
    # really needed?)

    run_as_postgres "echo '\\copyright' | psql -tA --no-password" >/dev/null
    if test $? -eq 0; then
        # check also for superuser permissions
        cmd="psql template1 -w -c '\du postgres' | grep Superuser"
        run_as_postgres "$cmd" &>/dev/null
        if test $? -ne 0; then
            log_error "Cannot execute \"$cmd\", 'postgres' is not superuser."
            append_to_solution <<EOF
* The 'postgres' role is not a superuser for PostgreSQL server.
  Execute this SQL statement:
  ALTER USER postgres WITH SUPERUSER;
  under other PostgreSQL superuser account.
EOF
            return $RESULT_FAIL
        fi

        log_info "The 'postgres' user has enough permissions to process the upgrade."
        return $RESULT_PASS
    fi

    append_to_solution <<EOF
* Important: For a smooth in-place upgrade, the 'postgres' database user must have the superuser role. We are unable to check that because you have
  probably misconfigured the "$PG_HBA_CONF_FILE" file.
  To make sure that everything is as expected, fix the "$PG_HBA_CONF_FILE" file
  so that it does not block the 'postgres' user to log in without a password. To achieve that, put the "local all postgres ident" line to be the
  first line of the file.
  The upgrade process is fully independent of this file though; so if you are
  sure that 'postgres' is of superuser role (default configuration), ignore this warning.
EOF

    log_slight_risk "The 'postgres' user cannot connect to the database without a password."
    # Even if user misconfigured the pg_hba.conf contents, most likely he did
    # not changed the 'postgres' role.  Thus we can assume he is Superuser (with
    # slight risk).
    return $RESULT_PASS
}

filter_comments() {
    grep -v -e '^[[:space:]]*$' -e '^[[:space:]]*#' | sed 's|[[:space:]]*#.*||'
}

# The upgrade process does not rely on configuration options from
# postgresql.conf.  I didn't know that when I was writing this function.  But
# the suggestion for user (to be prepared for incompatibilities) is still nice
# to have.
check_options() {
    $FUNC_ENTRY

    OPTIONS_OK=yes
    # incompatible options in configuration files
    filtered_conf="`cat $POSTGRESQL_CONF_FILE | filter_comments`"

    # starting from RHEL-7.0, unix_socket_directory is not supported - we rather
    # use unix_socket_directories.  That feature is backported from PostgreSQL
    # 9.3 to 9.2 in RHEL7.
    echo "$filtered_conf" | grep unix_socket_directory >/dev/null
    if test $? -eq 0; then
        append_to_solution <<EOF
* The 'unix_socket_directory' option is not supported in Red Hat Enterprise Linux 7. Instead, we
  use the 'unix_socket_directories' option, which is able to specify multiple
  directories separated by commas. See the documentation of that option at
  [link:http://www.postgresql.org/docs/9.3/static/runtime-config-connection.html]
  Do not remove this option from the configuration file, just be prepared that you will
  need to fix the option in Red Hat Enterprise Linux 7.
EOF
        log_info "The 'unix_socket_directory' option is not supported."
        OPTIONS_OK=no
    fi

    echo "$filtered_conf" | grep -e unix_socket_directory \
                                 -e unix_socket_directories >/dev/null
    if test $? -eq 0; then
        # Thanks hhorak!
        append_to_solution <<EOF
* As you are using the 'unix_socket_directory' or
  'unix_socket_directories' option, we can expect a failure during the in-place
  PostgreSQL upgrade later on. That happens when you use a non-standard
  directory. For that reason, later, when you run 'postgresql-setup upgrade',
  type the following command instead:
        PGSETUP_PGUPGRADE_OPTIONS="-o '-k /var/run/postgresql'" \
            postgresql-setup upgrade
  This forces the original PostgreSQL server to create a socket file in a compatible
  directory during the data upgrade despite the non-compatible configuration.
EOF
        log_error "A suspicious 'unix_socket_director*' option was detected."
        OPTIONS_OK=fail
    fi

    obsolete_options_used=
    for i in silent_mode wal_sender_delay custom_variable_classes \
            add_missing_from regex_flavor; do
        echo "$filtered_conf" | grep $i >/dev/null
        if test $? -eq 0; then
            log_info "The '$i' option was removed in PostgreSQL 9.2."
            OPTIONS_OK=no
            obsolete_options_used="${obsolete_options_used} '$i'"
        fi
    done


    obsolete_options_used=`echo $obsolete_options_used | sed 's|^ ||' | sed 's| |, |'`

    test x"$OPTIONS_OK" = xno && append_to_solution <<EOF
* The $obsolete_options_used options that are specified in your postgresql.conf file are not
  supported in PostgreSQL 9.2 in Red Hat Enterprise Linux 7 anymore. Remove them
  later.
EOF

    test $OPTIONS_OK == yes && \
        log_info "No options problem was found in the 'postgresql.conf' file."
    test $OPTIONS_OK == fail &&
        return $RESULT_FAIL
    return $RESULT_PASS
}

check_plugins() {
    # prepare user he will need to recompile plugins in RHEL7 also
    $FUNC_ENTRY

    unowned_reported=no
    unknown_pkg_reported=no

    PLUGINS_OK=yes
    provides="`rpm -q --whatprovides $PLUGINDIR/* | sort | uniq | remove_ver_rel`"
    while read i; do
        case $i in
        postgresql-@(contrib|devel|docs|plperl|plpython|pltcl|server|test))
            # nothing really happens, those packages are also in RHEL7 and
            # should be updated appropriately
            continue
            ;;

        uuid-pgsql)
            # this package is not provided in 'uuid' anymore.
            log_error "The uuid-pgsql package is not provided in Red Hat Enterprise Linux 7."
            append_to_solution <<EOF
* Possible usage of the uuid-pgsql package is detected. This package is broken
  by default also in Red Hat Enterprise Linux 6, and it is most probably unused
  by the PostgreSQL server. Decide whether this plug-in is
  necessary, and uninstall it possibly. Generally: This package is about the
  ossp-uuid.so plug-in which is considered deprecated by the uuid-ossp.so plug-in
  (also provided in Red Hat Enterprise Linux 6) from the postgresql-contrib package.
EOF
            PLUGINS_OK=no
            continue
            ;;

        # FIXME: Can I assume that we have C locale?
        *"not owned by any package")
            set $i
            log_error "The file in the plug-in directory '$2' is not owned by any package."
            test $unowned_reported = no && unowned_reported=yes && \
            append_to_solution <<EOF
* An unowned file was detected in the $PLUGINDIR directory. It could
  mean that you have installed a third-party PostgreSQL plug-in. To make the
  database upgrade smoothly, provide this plug-in in
  Red Hat Enterprise Linux 7 before you start the upgrade process.
EOF
            PLUGINS_OK=no
            ;;

        *)
            log_error "An unknown package '$i'"
            test $unknown_pkg_reported = no && unknown_pkg_reported=yes && \
            append_to_solution <<EOF
* A third-party PostgreSQL plug-in package was detected installed on your
  system. Provide this package also in Red Hat
  Enterprise Linux 7 to make the database upgrade smoothly.
EOF
            PLUGINS_OK=no
            ;;
        esac
    done <<<"$provides"

    if test $PLUGINS_OK = no; then
        return $RESULT_FAIL
    fi

    log_info "No problem with plug-ins was detected."
    return $RESULT_PASS
}

mkdir -p $VALUE_TMP_PREUPGRADE/postupgrade.d/
cp postupgrade.d/* $VALUE_TMP_PREUPGRADE/postupgrade.d/

# run all checkers
res=$RESULT_PASS
for i in home_dir data_dir different_usage is_initialized enabled started \
    permissions options plugins;
do
    check_$i
    if test $? -ne $RESULT_PASS; then
        res=$RESULT_FAIL
    fi
    test "$SKIP_TESTING" = "yes" && break
done

if test "$PLAN_STOP_SERVER" = yes; then
    log_info "Stopping the server.."
    $SERVICE_BIN postgresql stop &>/dev/null
    if test $? -ne 0; then
        # this shouldn't really happen
        res=$RESULT_FAIL
    fi
fi

append_to_solution <<EOF
* If you go the in-place upgrade way, you will run the
  'postgresql-setup upgrade' command in Red Hat Enterprise Linux 7. Note that
  this process does not keep the PostgreSQL configuration files (all configuration files
  will be generated from scratch). The original configuration files will be backed
  up in the $HOME_DIR/data-old/*.conf file and will need manual copying
  back to data dir (and possibly some adjusting, too). For more information
  about the upgrading process, see [link:$KB_ARTICLE_UPGRADE]
  and $README_DIST_FILE.
EOF

exit $res

# vim: ts=4:sw=4:expandtab:tw=80
