#!/bin/bash
#
# Copyright (c) 2010--2015 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public License,
# version 2 (GPLv2). There is NO WARRANTY for this software, express or
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
# along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
#
# Red Hat trademarks are not licensed under GPLv2. No permission is
# granted to use or replicate Red Hat trademarks that are incorporated
# in this software or its documentation.
#

if [ 0$UID -gt 0 ]; then
    echo "$0 has to be run as root."
    exit 1
fi

LOG=/var/log/rhn/rhn_hostname_rename.log
RHN_CONF_FILE=/etc/rhn/rhn.conf
SSL_BUILD_DIR=/root/ssl-build
HTTP_PUB_DIR=/srv/www/htdocs/pub/
BOOTSTRAP_SH=/srv/www/htdocs/pub/bootstrap/bootstrap.sh
BOOTSTRAP_CCO=/srv/www/htdocs/pub/bootstrap/client-config-overrides.txt
MGR_SYNC_CONF=/root/.mgr-sync
BACKUP_EXT=.rnmbck
CA_CERT_TRUST_DIR=/etc/pki/trust/anchors/

###############################################################################

function default_or_input() {
    local MSG="$1"
    local VARIABLE="$2"
    local DEFAULT="$3"
    local SILENT="$4"

    local INPUT
    local CURRENT_VALUE=${!VARIABLE}
    #in following code is used not so common expansion
    #var_a=${var_b:-word}
    #which is like: var_a = $var_b ? word
    DEFAULT=${CURRENT_VALUE:-$DEFAULT}
    local VARIABLE_ISSET=$(set | grep "^$VARIABLE=")

    
    echo -n "$MSG [$DEFAULT]: "

    if [ -z "$VARIABLE_ISSET" ]; then
        read -r INPUT
    fi
    if [ -z "$INPUT" ]; then
        if [ "$DEFAULT" = "y/N" ] || [ "$DEFAULT" = "Y/n" ]; then
            INPUT=$(yes_no "$DEFAULT")
        else
            INPUT="$DEFAULT"
        fi
    fi
    eval "$(printf "%q=%q" "$VARIABLE" "$INPUT")"
}

function yes_no() {
    case "$1" in
        Y|y|Y/n|n/Y|1)
            echo 1
            ;;
        *)
            echo 0
            ;;
    esac
}

function echo_usage {
    echo "Usage:"
    echo "   $(basename $0) <new FQDN> [ --ssl-country=<SSL_COUNTRY> --ssl-state=<SSL_STATE> --ssl-city=<SSL_CITY> --ssl-org=<SSL_ORG> --ssl-orgunit=<SSL_ORGUNIT> --ssl-email=<SSL_EMAIL> --ssl-ca-password=<SSL_CA_PASSWORD> --ssl-ca-cert=<PATH> --ssl-server-key=<PATH> --ssl-server-cert=<PATH>"
    echo "   $(basename $0) { -h | --help }"
    exit 1
}

function echo_err {
    echo "$*" >&2
    echo "$*" >> $LOG
}

function bye {
    echo_err "Fix the problem and run $0 again"
    exit 1
}

function print_status {
    # strip whitespace
    STATUS="${1#"${1%%[![:space:]]*}"}"
    if [ "$STATUS" == "0" ]
    then
        echo "OK" | tee -a $LOG
    else
        echo_err "FAILED"
        shift
        echo_err $*
        bye
    fi
}

function initial_system_hostname_check {
    # check for uppercase chars in hostname
    if [ "$UYUNI_HOSTNAME" != "$(echo $UYUNI_HOSTNAME | tr '[:upper:]' '[:lower:]')" ]
    then
        echo_err "Uppercase characters are not allowed for satellite hostname."
        return 1
    fi

    return 0
}

function backup_file {
    if [ -e ${1} ]
    then
        cp ${1} ${1}${BACKUP_EXT}
    else
        echo "Backup of ${1} failed. File not found." >> $LOG
    fi
}

function update_rhn_conf {
    sed "s/^cobbler\.host[[:space:]]*=[[:space:]]*.*$/cobbler.host = localhost/" -i /etc/rhn/rhn.conf
    sed "s/^java\.hostname[[:space:]]*=[[:space:]]*.*$/java.hostname = ${UYUNI_HOSTNAME}/" -i /etc/rhn/rhn.conf
}

function re-generate_server_ssl_certificate {
    # default is to generate new SSL certificate

    if [ -n "$CML_NEW_SSL_CERT_REQUEST" -o -n "$CML_THIRD_PARTY_CERT" ]
    then

        # is there a need to re-generate SSL certificate?
        if [ -z "$CML_THIRD_PARTY_CERT" ]
        then
            SUBJECT=$(openssl x509 -in /etc/pki/tls/certs/spacewalk.crt -noout -subject)
            if [ -n "$SUBJECT" ]
            then
                SUBJECT_PATTERN='subject=C[[:space:]]*=[[:space:]]*\(..*\), ST[[:space:]]*=[[:space:]]*\(..*\), O[[:space:]]*=[[:space:]]*\(..*\), OU[[:space:]]*=[[:space:]]*\(..*\), CN[[:space:]]*=[[:space:]]*\(..*\), emailAddress[[:space:]]*=[[:space:]]*\(..*\)'
                SSL_COUNTRY_OLD=`echo $SUBJECT | sed "s/$SUBJECT_PATTERN/\1/"`
                SSL_STATE_OLD=`echo $SUBJECT | sed "s/$SUBJECT_PATTERN/\2/"`
                SSL_CITY_OLD=`echo $SUBJECT | sed "s/$SUBJECT_PATTERN/\2/"`
                SSL_ORG_OLD=`echo $SUBJECT | sed "s/$SUBJECT_PATTERN/\3/"`
                SSL_ORGUNIT_OLD=`echo $SUBJECT | sed "s/$SUBJECT_PATTERN/\4/"`
                SSL_EMAIL_OLD=`echo $SUBJECT | sed "s/$SUBJECT_PATTERN/\6/"`
            fi

            echo "Starting generation of new SSL certificate:"
            # COUNTRY
            if [ -n "${CML_SSL_COUNTRY+x}" ]
            then
                SSL_COUNTRY=${CML_SSL_COUNTRY}
            else
                read -e -p " Enter Country [$SSL_COUNTRY_OLD] : "
                SSL_COUNTRY=${REPLY:-$SSL_COUNTRY_OLD}
            fi
            # STATE
            if [ -n "${CML_SSL_STATE+x}" ]
            then
                SSL_STATE=${CML_SSL_STATE}
            else
                read -e -p " Enter State [$SSL_STATE_OLD] : "
                SSL_STATE=${REPLY:-$SSL_STATE_OLD}
            fi
            # CITY
            if [ -n "${CML_SSL_CITY+x}" ]
            then
                SSL_CITY=${CML_SSL_CITY}
            else
                read -e -p " Enter City [$SSL_CITY_OLD] : "
                SSL_CITY=${REPLY:-$SSL_CITY_OLD}
            fi
            # ORGANIZATION
            if [ -n "${CML_SSL_ORG+x}" ]
            then
                SSL_ORG=${CML_SSL_ORG}
            else
                read -e -p " Enter Organization [$SSL_ORG_OLD] : "
                SSL_ORG=${REPLY:-$SSL_ORG_OLD}
            fi
            # ORGANIZATION UNIT
            if [ -n "${CML_SSL_ORGUNIT+x}" ]
            then
                SSL_ORGUNIT=${CML_SSL_ORGUNIT}
            else
                read -e -p " Enter Organization Unit [$SSL_ORGUNIT_OLD] : "
                SSL_ORGUNIT=${REPLY:-$SSL_ORGUNIT_OLD}
            fi
            # EMAIL ADDRESS
            if [ -n "${CML_SSL_EMAIL+x}" ]
            then
                SSL_EMAIL=${CML_SSL_EMAIL}
            else
                read -e -p " Enter Email Address [$SSL_EMAIL_OLD] : "
                SSL_EMAIL=${REPLY:-$SSL_EMAIL_OLD}
            fi
            # CA PASSWORD
            if [ -n "${CML_SSL_CA_PASSWORD+x}" ]
            then
                SSL_CA_PASSWORD=${CML_SSL_CA_PASSWORD}
            else
                read -e -p " Enter CA password : " -s
                echo
                SSL_CA_PASSWORD=${REPLY}
            fi

            echo " Generating SSL certificates:" | tee -a $LOG
            GEN_NEW_CA="n"
            if [ -f $SSL_BUILD_DIR/RHN-ORG-TRUSTED-SSL-CERT ]; then
                echo " No need to generate a new SSL CA Certificate" | tee -a $LOG
            else
                GEN_NEW_CA="y"
                # We don't have the CA in SSL build dir: generate a new one
                echo " Generating SSL CA Certificate:" | tee -a $LOG
                # just log the SSL info ...
                echo "rhn-ssl-tool --gen-ca --force \
                    --dir="$SSL_BUILD_DIR" \
                    --set-country="$SSL_COUNTRY" \
                    --set-state="$SSL_STATE" \
                    --set-city="$SSL_CITY" \
                    --set-org="$SSL_ORG" \
                    --set-org-unit="$SSL_ORGUNIT" \
                    --set-common-name="${UYUNI_HOSTNAME}" \
                " >> $LOG
                rhn-ssl-tool --gen-ca --no-rpm --force \
                    --dir="$SSL_BUILD_DIR" \
                    --set-country="$SSL_COUNTRY" \
                    --set-state="$SSL_STATE" \
                    --set-city="$SSL_CITY" \
                    --set-org="$SSL_ORG" \
                    --set-org-unit="$SSL_ORGUNIT" \
                    --set-common-name="${UYUNI_HOSTNAME}" \
                    --password="$SSL_CA_PASSWORD" \
                    2>>$LOG
            fi
            CML_SSL_CA_CERT=$SSL_BUILD_DIR/RHN-ORG-TRUSTED-SSL-CERT

            echo "rhn-ssl-tool --gen-server --no-rpm \
                --dir="$SSL_BUILD_DIR" \
                --set-country="$SSL_COUNTRY" \
                --set-state="$SSL_STATE" \
                --set-city="$SSL_CITY" \
                --set-org="$SSL_ORG" \
                --set-org-unit="$SSL_ORGUNIT" \
                --set-email="$SSL_EMAIL" \
                --set-hostname="${UYUNI_HOSTNAME}" \
            " >> $LOG
            rhn-ssl-tool --gen-server --no-rpm \
                --dir="$SSL_BUILD_DIR" \
                --set-country="$SSL_COUNTRY" \
                --set-state="$SSL_STATE" \
                --set-city="$SSL_CITY" \
                --set-org="$SSL_ORG" \
                --set-org-unit="$SSL_ORGUNIT" \
                --set-email="$SSL_EMAIL" \
                --set-hostname="${UYUNI_HOSTNAME}" \
                --password="$SSL_CA_PASSWORD" \
                2>>$LOG
            SERVER_NAME=$(echo "${UYUNI_HOSTNAME}" | perl -e '
            my @hostname_parts = split(/\./, <STDIN>);
                my $system_name;
                if (scalar @hostname_parts > 2) {
                  $system_name = join(".", splice(@hostname_parts, 0, -2));
                }
                else {
                  $system_name = join(".", @hostname_parts);
                };
            printf($system_name."\n");')
            CML_SSL_SERVER_KEY=$SSL_BUILD_DIR/$SERVER_NAME/server.key
            CML_SSL_SERVER_CERT=$SSL_BUILD_DIR/$SERVER_NAME/server.crt
        fi

        if [ ! -f $CML_SSL_SERVER_KEY -o ! -f $CML_SSL_SERVER_CERT ];
        then
            echo_err "Wrong SSL information provided. Check $LOG for more information." | tee -a $LOG
            bye
        fi
        if [ ! -f $CML_SSL_CA_CERT ];
        then
            echo_err "CA Certificate file not found. Check $LOG for more information." | tee -a $LOG
            bye
        fi
        echo -n "Making new SSL certificate publicly available ... " | tee -a $LOG
        /usr/bin/mgr-ssl-cert-setup \
                --root-ca-file=$CML_SSL_CA_CERT \
                --server-cert-file=$CML_SSL_SERVER_CERT \
                --server-key-file=$CML_SSL_SERVER_KEY
        print_status $?
    fi
}

###############################################################################

echo "[$(date)]: $0 $*" >> $LOG

while [ $# -ge 1 ]; do
    
    case $1 in
            --help | -h)  echo_usage;;

            --ssl-country=*) CML_SSL_COUNTRY=$(echo $1 | cut -d= -f2-);;
            --ssl-state=*) CML_SSL_STATE=$(echo $1 | cut -d= -f2-);;
            --ssl-city=*) CML_SSL_CITY=$(echo $1 | cut -d= -f2-);;
            --ssl-org=*) CML_SSL_ORG=$(echo $1 | cut -d= -f2-);;
            --ssl-orgunit=*) CML_SSL_ORGUNIT=$(echo $1 | cut -d= -f2-);;
            --ssl-email=*) CML_SSL_EMAIL=$(echo $1 | cut -d= -f2-);;

            --ssl-ca-password=*) CML_SSL_CA_PASSWORD=$(echo $1 | cut -d= -f2-);;

            --ssl-ca-cert=*) CML_SSL_CA_CERT=$(echo $1 | cut -d= -f2-);;
            --ssl-server-cert=*) CML_SSL_SERVER_CERT=$(echo $1 | cut -d= -f2-);;
            --ssl-server-key=*) CML_SSL_SERVER_KEY=$(echo $1 | cut -d= -f2-);;
            *)
               if [[ -z "$UYUNI_HOSTNAME" && "$1" != "--"* ]]; then
                   UYUNI_HOSTNAME=$1
               else
                   echo_err "Error: Invalid option $1"
                   echo_usage
               fi;;
    esac
    shift
done

if [ -n "$UYUNI_HOSTNAME" ]
then
    initial_system_hostname_check || bye
else
    echo_err "Missing <new FQDN> argument."
    echo_usage
fi

# This awk command can read a single line yaml value which may optionally be double or single quoted.
OLD_HOSTNAME=$(sed -n '/^java\.hostname/{s/^java\.hostname[[:space:]]*=[[:space:]]*\(.*\)/\1/;p}' "$RHN_CONF_FILE")

echo "=============================================" | tee -a $LOG
echo "hostname: $UYUNI_HOSTNAME" | tee -a $LOG
echo "old hostname: $OLD_HOSTNAME"  | tee -a $LOG
echo "=============================================" | tee -a $LOG

if [ "z$UYUNI_HOSTNAME" == "z$OLD_HOSTNAME" ]; then
    echo_err "Unchanged hostname"
    exit 0
fi

if [ -n "${CML_SSL_CA_CERT}" -a -n "${CML_SSL_SERVER_KEY}" -a -n "${CML_SSL_SERVER_CERT}" ]
then
    CML_THIRD_PARTY_CERT=1
else
    if [ -n "${CML_SSL_CA_CERT}" -o -n "${CML_SSL_SERVER_CERT}" -o -n "${CML_SSL_SERVER_KEY}" ]
    then
        echo_err "Either all or none of --ssl-ca-cert, --ssl-server-key and --ssl-server-cert must be provided"
        echo_usage
    else
        CML_NEW_SSL_CERT_REQUEST=1
    fi
fi

backup_file $RHN_CONF_FILE

# stop services
echo -n "Stopping spacewalk services ... " | tee -a $LOG
/usr/sbin/spacewalk-service stop >> $LOG 2>&1
/sbin/service postgresql start >> $LOG 2>&1
print_status 0  # just simulate end

echo -n "Testing DB connection ... " | tee -a $LOG
/usr/sbin/spacewalk-startup-helper wait-for-database
print_status "${?}" "Your database isn't running."

echo -n "Updating /etc/rhn/rhn.conf ... " | tee -a $LOG
update_rhn_conf
print_status 0  # just simulate end

re-generate_server_ssl_certificate

echo -n "Regenerating new bootstrap client-config-overrides.txt ... " | tee -a $LOG
# it's easier to subst HOSTNAME with sed
# than to re-generate and keep current configuration
if [ -e "$BOOTSTRAP_SH" ]
then
    backup_file ${BOOTSTRAP_SH}
    sed -i "s/\(HOSTNAME=\).*/\1$UYUNI_HOSTNAME/" ${BOOTSTRAP_SH}
fi
if [ -e "$BOOTSTRAP_CCO" ]
then
    backup_file ${BOOTSTRAP_CCO}
    sed -i "s/\(serverURL=https\?:\/\/\).*\(\/XMLRPC\)/\1$UYUNI_HOSTNAME\2/" ${BOOTSTRAP_CCO}
fi
print_status 0  # just simulate end

echo -n "Updating other DB entries ... " | tee -a $LOG
spacewalk-sql --select-mode - >>$LOG <<EOS
UPDATE rhntemplatestring SET value='$UYUNI_HOSTNAME' WHERE label='hostname';
COMMIT;
\q
EOS
print_status 0  # just simulate end

echo -n "Changing cobbler settings ... " | tee -a $LOG
/usr/bin/spacewalk-setup-cobbler --apache2-config-directory "/etc/apache2/conf.d" --fqdn $UYUNI_HOSTNAME 2>&1
print_status $?

echo -n "Changing kernel_options ... " | tee -a $LOG
spacewalk-sql --select-mode - >>$LOG <<EOS
UPDATE rhnKickstartableTree
SET kernel_options = REPLACE(kernel_options, '$OLD_HOSTNAME', '$UYUNI_HOSTNAME'),
    kernel_options_post = REPLACE(kernel_options_post, '$OLD_HOSTNAME', '$UYUNI_HOSTNAME');
COMMIT;
\q
EOS
for COBBLERDIR in /var/lib/cobbler/collections/*
do
    if [ -d $COBBLERDIR ] && [ ! -z "$(ls $COBBLERDIR)" ]; then
        for FILE in $COBBLERDIR/*
        do
            backup_file $FILE
            sed -i "s/$OLD_HOSTNAME/$UYUNI_HOSTNAME/g" $FILE
        done
    fi
done
print_status 0  # just simulate end

# change /root/.mgr-sync
if [ -e $MGR_SYNC_CONF ]; then
    backup_file $MGR_SYNC_CONF
    sed -i "s/^mgrsync.host\s\{0,1\}=\s\{0,1\}.*/mgrsync.host = $UYUNI_HOSTNAME/g" $MGR_SYNC_CONF
    sed -i "s/^mgrsync.session.token\s\{0,1\}=\s\{0,1\}.*/mgrsync.session.token = \"\"/g" $MGR_SYNC_CONF
fi
print_status 0  # just simulate end

# Schedule a pillar refresh of all the minions since they contain the repos URLs with the old hostname
spacewalk-sql --select-mode - <<EOS
INSERT INTO rhnTaskQueue (id, org_id, task_name, task_data)
SELECT nextval('rhn_task_queue_id_seq'), id, 'upgrade_satellite_all_systems_pillar_refresh', 0
FROM web_customer
WHERE id = 1;
COMMIT;
\q
EOS

echo -n "Changing postfix settings ... " | tee -a $LOG
postconf -e myhostname=$UYUNI_HOSTNAME
systemctl restart postfix
print_status 0  # just simulate end

echo -n "Starting spacewalk services ... " | tee -a $LOG
systemctl restart postgresql >> $LOG 2>&1
/usr/sbin/spacewalk-service start >> $LOG 2>&1
print_status 0  # just simulate end

echo "[$(date)]: $(basename $0) finished successfully." >> $LOG
