#!/bin/sh

PREFIX=/var/cfengine

package_type()
{
  echo depot
}

os_type()
{
  echo hpux
}

rc_d_path()
{
  echo "/sbin"
}

rc_start_level()
{
  echo S970
}

rc_kill_level()
{
  echo K050
}

platform_service()
{
  /sbin/init.d/"$1" "$2"
}

native_is_upgrade()
{
  test -f "$PREFIX/bin/cf-agent"
}
PKG_TYPE=depot
SCRIPT_TYPE=postinstall
PROJECT_TYPE=cfengine-nova
BUILT_ON_OS=hpux
BUILT_ON_OS_VERSION=
# postgresql docs, https://www.postgresql.org/docs/current/locale.html, recommend using C locale unless otherwise needed
export LC_ALL=C # overrides all other env vars: https://www.gnu.org/software/libc/manual/html_node/Locale-Categories.html
# Upgrade detection is a mess. It is often difficult to tell, especially from
# the postinstall script, so we use the package-upgrade.txt file to remember.
case "$PKG_TYPE" in
  depot|deb|bff)
    case "$SCRIPT_TYPE" in
      preinstall|preremove)
        if native_is_upgrade; then
          mkdir -p "$PREFIX"
          echo "File used by CFEngine during package upgrade. Can be safely deleted." > "$PREFIX/package-upgrade.txt"
        else
          rm -f "$PREFIX/package-upgrade.txt"
        fi
        alias is_upgrade='native_is_upgrade'
        ;;
      postremove|postinstall)
        if [ -f "$PREFIX/package-upgrade.txt" ]; then
          if [ "$SCRIPT_TYPE" = "postinstall" ]; then
            rm -f "$PREFIX/package-upgrade.txt"
          fi
          alias is_upgrade='true'
        else
          alias is_upgrade='false'
        fi
        ;;
    esac
    ;;
esac

get_cfengine_state() {
    if type systemctl >/dev/null 2>&1; then
        systemctl list-units -l | sed -r -e '/^\s*(cf-[-a-z]+|cfengine3)\.service/!d' -e 's/\s*(cf-[-a-z]+|cfengine3)\.service.*/\1/'
    else
        platform_service cfengine3 status | awk '/is running/ { print $1 }'
    fi
}

restore_cfengine_state() {
    # $1 -- file where the state to restore is saved (see get_cfengine_state())

    if type systemctl >/dev/null 2>&1; then
        for service in `cat "$1"`; do
            definition=`systemctl cat "$service"` || continue
            # only try to start service that are defined/exist (some may be gone
            # in the new version)
            if [ -n "$definition" ]; then
                systemctl start "$service" || echo "Failed to start service $service"
            fi
        done
    else
        CALLED_FROM_STATE_RESTORE=1
        if [ -f ${PREFIX}/bin/cfengine3-nova-hub-init-d.sh ]; then
            . ${PREFIX}/bin/cfengine3-nova-hub-init-d.sh
            if grep postgres "$1" >/dev/null; then
                start_postgres >/dev/null || echo "Failed to start PostgreSQL"
            fi
            if grep httpd "$1" >/dev/null; then
                start_httpd >/dev/null || echo "Failed to start Apache"
            fi
        fi

        for d in `grep 'cf-' "$1"`; do
            if [ -f ${PREFIX}/bin/${d} ]; then
                ${PREFIX}/bin/${d} || echo "Failed to start $d"
            fi
        done
    fi
}

wait_for_cf_postgres() {
    # wait for CFEngine Postgresql service to be available, up to 60 sec.
    # Returns 0 is psql command succeeds,
    # Returns non-0 otherwise (1 if exited by timeout)
    for i in $(seq 1 60); do
        true "checking if Postgresql is available..."
        if cd /tmp && su cfpostgres -c "$PREFIX/bin/psql -l" >/dev/null 2>&1; then
            true "Postgresql is available, moving on"
            return 0
        fi
        true "waiting 1 sec for Postgresql to become available..."
        sleep 1
    done
    # Note: it is important that this is the last command of this function.
    # Return code of `psql` is the return code of whole function.
    cd /tmp && su cfpostgres -c "$PREFIX/bin/psql -l" >/dev/null 2>&1
}

wait_for_cf_postgres_down() {
    # wait for CFEngine Postgresql service to be shutdown, up to 60 sec.
    # Returns 0 if postgresql service is not running
    # Returns non-0 otherwise (1 if exited by timeout)
    for i in $(seq 1 60); do
        true "checking if Postgresql is shutdown..."
        if cd /tmp && ! su cfpostgres -c "$PREFIX/bin/psql -l" >/dev/null 2>&1; then
            true "Postgresql is shutdown, moving on"
            return 0
        fi
        true "waiting 1 sec for Postgresql to shutdown..."
        sleep 1
    done
    # Note: it is important that this is the last command of this function.
    # Return code of `psql` is the return code of whole function.
    cd /tmp && ! su cfpostgres -c "$PREFIX/bin/psql -l" >/dev/null 2>&1
}

safe_cp() {
    # "safe" alternative to `cp`. Tries `cp -al` first, and if it fails - `cp -a`.
    # Deletes partially-copied files if copy operation fails.
    # Args:
    #   * dir you're copying stuff from
    #   * name of stuff you're copying (one arg!)
    #   * dir you're copying stuff to
    # Example: instead of
    #   cp "$PREFIX/state/pg/data" "$BACKUP_DIR"
    # use
    #   safe_cp "$PREFIX/state/pg" data "$BACKUP_DIR"
    test "$#" -eq 3 || return 2
    from="$1"
    name="$2"
    to="$3"
    # First, try copying files creating hardlinks
    # Do not print errors - we'll do it another way
    if cp -al "$from/$name" "$to" 2>/dev/null; then
        # Copy succeeded
        return 0
    fi
    echo "Copy creating hardlinks failed, removing partially-copied data and trying simple copy"
    rm -rf "$to/$name"
    if cp -a "$from/$name" "$to"; then
        # Copy succeeded
        return 0
    fi
    echo "Copy failed, so removing partially-copied data and aborting"
    rm -rf "$to/$name"
    return 1
}

safe_mv() {
    # "safe" alternative to `mv`. Executes `safe_cp` and deletes source dir if
    # that succeeds.
    # Args are same as in safe_cp
    # Exampe: instead of
    #   mv "$PREFIX/state/pg/data" "$BACKUP_DIR"
    # use
    #   safe_mv "$PREFIX/state/pg" data "$BACKUP_DIR"
    test "$#" -eq 3 || return 2
    from="$1"
    name="$2"
    to="$3"
    if safe_cp "$from" "$name" "$to"; then
        # Copy succeeded - so we can delete old dir
        rm -rf "$from/$name"
        return 0
    else
        # Copy failed (partially-copied data is removed by safe_cp)
        return 1
    fi
}

on_files() {
    # perform operation $1 on each file in $2 with optional extra argument $3
    # Examples:
    # to copy only files:
    #   on_files cp "$PREFIX/lib" "$BACKUP_DIR/lib"
    # to move only files:
    #   on_files mv "$PREFIX/lib" "$PREFIX/lib.new"
    # to remove only files:
    #   on_files rm "$PREFIX/lib"
    test "$#" -ge 2 || return 2
    # Split on newlines, not on spaces.
    IFS='
'
    for file in $(ls -a1 "$2"); do
        if [ -f "$2/$file" ]; then
            $1 "$2/$file" $3
        fi
    done
    # Restore normal splitting semantics.
    unset IFS
}

if [ -f "$PREFIX/CFENGINE_TEST_PACKAGE_SCRIPT.txt" ]; then
  # Special mode during testing. Dump some info for verification.
  (
    echo "new_script----"
    echo "script_type=$SCRIPT_TYPE"
    if is_upgrade; then
      echo "is_upgrade=1"
    else
      echo "is_upgrade=0"
    fi
  ) >> "$PREFIX/CFENGINE_TEST_PACKAGE_SCRIPT.log"
fi

is_community()
{
  test "$PROJECT_TYPE" = "cfengine-community"
}

is_nova()
{
  test "$PROJECT_TYPE" = "cfengine-nova" || test "$PROJECT_TYPE" = "cfengine-nova-hub"
}

case "`os_type`" in
    aix)
        INSTLOGGROUP="system"
        ;;
    *)
        INSTLOGGROUP="root"
        ;;
esac

INSTLOG="/var/log/CFEngine-Install-$(date '+%Y-%m-%d_%H:%M:%S_%Z').log"
mkdir -p "$(dirname "$INSTLOG")"
touch "$INSTLOG"
rm -f /var/log/CFEngineHub-Install.log
rm -f /var/log/CFEngine-Install.log
ln -s "$INSTLOG" /var/log/CFEngine-Install.log
chown root:$INSTLOGGROUP "$INSTLOG"
chmod 600 "$INSTLOG"
CONSOLE=7
# Redirect most output to log file, but keep console around for custom output.
case "$SCRIPT_TYPE" in
  pre*)
    eval "exec $CONSOLE>&1 > $INSTLOG 2>&1"
    ;;
  *)
    eval "exec $CONSOLE>&1 >> $INSTLOG 2>&1"
    ;;
esac
echo "$SCRIPT_TYPE:"

# Output directly to console, bypassing log.
cf_console()
{
  # Use subshell to prevent "set +x" from leaking out into the rest of the
  # execution.
  (
    set +x
    "$@" 1>&$CONSOLE 2>&$CONSOLE
  )
}

set -x
if [ -x /bin/systemctl ]; then
  # This is important in case any of the units have been replaced by the package
  # and we call them in the postinstall script.
  /bin/systemctl daemon-reload
fi

#
# Generate a host key
#
if [ ! -f $PREFIX/ppkeys/localhost.priv ]; then
  $PREFIX/bin/cf-key >/dev/null || :
fi

if is_community; then
  #
  # Copy the stock policy for the new installations
  #
  if ! [ -f $PREFIX/masterfiles/promises.cf ]; then
    /bin/cp -R $PREFIX/share/CoreBase/masterfiles $PREFIX
    #
    # Create promises_validated
    #
    $PREFIX/bin/cf-promises -T $PREFIX/masterfiles
  fi
fi

#
# Cleanup deprecated plugins directory
#
if ! rmdir $PREFIX/plugins 2> /dev/null; then
    # CFE-3618
    echo "$PREFIX/plugins has been removed from the default distribution, we \
tried to clean up the unused directory but found it was not empty. Please \
review your policy, if you believe this directory should remain part of the \
default distribution, please open a ticket in the CFEngine bug tracker."
fi

if [ -f $PREFIX/bin/cf-twin ]; then
  rm -f $PREFIX/bin/cf-twin
fi

mkdir -p /usr/local/sbin
for i in cf-agent cf-promises cf-key cf-secret cf-execd cf-serverd cf-monitord cf-runagent cf-net cf-check cf-support;
do
  if [ -f $PREFIX/bin/$i ]; then
    ln -sf $PREFIX/bin/$i /usr/local/sbin/$i || true
  fi

  case `os_type` in
    redhat|debian)
      if [ -f /usr/share/man/man8/$i.8.gz ]; then
        rm -f /usr/share/man/man8/$i.8.gz
      fi
      $PREFIX/bin/$i -M > /usr/share/man/man8/$i.8 && gzip /usr/share/man/man8/$i.8
      ;;
  esac
done

case `os_type` in
  redhat|debian)
    #
    # Register CFEngine initscript, if not yet.
    #
    if [ -x /bin/systemctl ]; then
      # Reload systemd config to pick up newly installed units
      /bin/systemctl daemon-reload > /dev/null 2>&1
      # Enable cfengine3 service (starts all the other services)
      # Enabling the service is OK to fail (can be masked, for example)
      /bin/systemctl enable cfengine3.service > /dev/null 2>&1 || true
    else
      case `os_type` in
        redhat)
          if ! is_upgrade; then
            chkconfig --add cfengine3
          fi
          ;;
        debian)
          if [ -x /etc/init.d/cfengine3 ]; then
            update-rc.d cfengine3 defaults
          fi
          ;;
      esac
    fi
    ;;

  solaris|hpux)
    if [ -f `rc_d_path`/init.d/cfengine3 ];then
      for link in `rc_d_path`/rc3.d/`rc_start_level`cfengine3 \
                  `rc_d_path`/rc0.d/`rc_kill_level`cfengine3 \
                  `rc_d_path`/rc1.d/`rc_kill_level`cfengine3 \
                  `rc_d_path`/rc2.d/`rc_kill_level`cfengine3 \
                  `rc_d_path`/rcS.d/`rc_kill_level`cfengine3; do
        if [ ! -h $link ]; then
          /usr/bin/ln -s `rc_d_path`/init.d/cfengine3 $link
        fi
      done
    fi
    ;;

  aix)
    if [ -x /etc/rc.d/init.d/cfengine3 ];then
      for link in /etc/rc.d/rc2.d/K05cfengine3 /etc/rc.d/rc2.d/S97cfengine3; do
        /usr/bin/ln -fs /etc/rc.d/init.d/cfengine3 $link
      done
    fi
    ;;
esac

# (re)load SELinux policy if available and required
if [ `os_type` = "redhat" ] &&
   [ -f "$PREFIX/selinux/cfengine-enterprise.pp" ];
then
  if command -v /usr/sbin/selinuxenabled >/dev/null &&
      /usr/sbin/selinuxenabled;
  then
    command -v semodule >/dev/null || cf_console echo "warning! selinux exists and returns 0 but semodule not found"
    test -x /usr/sbin/load_policy  || cf_console echo "warning! selinuxenabled exists and returns 0 but load_policy not found"
    test -x /usr/sbin/restorecon   || cf_console echo "warning! selinuxenabled exists and returns 0 but restorecon not found"

  fi
  if ! cf_console semodule -n -i "$PREFIX/selinux/cfengine-enterprise.pp"; then
    cf_console echo "warning! semodule import failed, examine /var/log/CFE*log and \
consider installing selinux-policy-devel package and \
rebuilding policy with: \
\
cd $PREFIX/selinux \
make -f /usr/share/selinux/devel/Makefile -j1 \
semodule -n -i $PREFIX/selinux/cfengine-enterprise.pp \
\
and then restarting services with  \
\
systemctl restart cfengine3"
  fi
  if /usr/sbin/selinuxenabled; then
    /usr/sbin/load_policy
    /usr/sbin/restorecon -R /var/cfengine
  fi
fi

restorecon_run=0
if [ -f $PREFIX/policy_server.dat ]; then
  if ! [ -f "$PREFIX/UPGRADED_FROM.txt" ] || egrep '3\.([0-6]\.|7\.0)' "$PREFIX/UPGRADED_FROM.txt" > /dev/null; then
    # Versions <= 3.7.0 are unreliable in their daemon killing. Kill them one
    # more time now that we have upgraded.
    cf_console platform_service cfengine3 stop
  fi

  # Let's make sure all files and directories created above have correct SELinux labels.
  # run this BEFORE we start services again to avoid race conditions in restorecon
  if command -v restorecon >/dev/null; then
    restorecon -iR /var/cfengine /opt/cfengine
    restorecon_run=1
  fi

  if is_upgrade && [ -f "$PREFIX/UPGRADED_FROM_STATE.txt" ]; then
      cf_console restore_cfengine_state "$PREFIX/UPGRADED_FROM_STATE.txt"
      rm -f "$PREFIX/UPGRADED_FROM_STATE.txt"
  else
      cf_console platform_service cfengine3 start
  fi
fi

rm -f "$PREFIX/UPGRADED_FROM.txt"

if [ $restorecon_run = 0 ]; then
  # if we didn't run restorecon above in the already bootstrapped/upgrade case then run it now
  if command -v restorecon >/dev/null; then
    restorecon -iR /var/cfengine /opt/cfengine
  fi
fi

exit 0
