#!/bin/bash
#
# Copyright (c) 2019 Clementine Computing LLC.
# 
# This file is part of PopuFare.
# 
# PopuFare is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# PopuFare 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 Affero General Public License for more details.
# 
# You should have received a copy of the GNU Affero General Public License
# along with PopuFare.  If not, see <https://www.gnu.org/licenses/>.
#

#THIS FILE IS NOT REALLY A SCRIPT, BUT RATHER A CENTRAL HOLDING TANK FOR SYSTEM CONFIG VALUES...
#IT SHOULD BE SOURCED BY ALL OTHER SCRIPTS THAT NEED A CONTAINED VALUE, AND MAY SOME DAY BE PARSED
#BY THE NATIVE APPLICATIONS.  THUS:  Keep it simple, in the following format:
#
##Comment on meaning of variable
#VARIABLE="value"
#
#(variable name in all caps, no space, equals sign, quoted literal value (if there is even a remote chance that the value
#is one that will be needed by the native app in the future)
#

BASEDIR='/home/bus'

#How long to sleep after a dial attempt before checking if we have a PPP session successfully constructed
SLEEP_AFTER_DIAL="30"

#How long to wait between redial attempts     
SLEEP_BETWEEN_REDIALS="60"

# How long to sleep after the GRPS session comes up before testing the SSH tunnel (attempting to connect to the 'hello' server)
#
SLEEP_BEFORE_TUNNEL_TEST="5"

# How long to sleep after an established tunnel goes down before allowing any SSH tunnel establishment retries
#
SLEEP_AFTER_TUNNEL_FAILURE="60"

# How long to sleep after an established tunnel has been aborted
#
SLEEP_AFTER_TUNNEL_ABORT="60"

# How many successive ssh tunnel failures do we accept before forcing a hard termination of pppd and a modem reset and redail
#
MAX_FAIL_HANGUP="5"

# How frequently to test an SSH tunnel that predates the ppp-dialer.sh process (thus having no right to wait for its PID)
#
SLEEP_MONITORING_TUNNEL="120"

#  How frequently to perform SSH keepalive messages, and how many of them are allowed to drop before we tear the tunnel down
# and wait to try again.  This gets passed to the SSH client in the following form:
# -o ServerAliveInterval=$SSH_SERVER_ALIVE_INTERVAL -o ServerAliveCountMax=$SSH_SERVER_ALIVE_MAXDROP
#
SSH_SERVER_ALIVE_INTERVAL="15"
SSH_SERVER_ALIVE_MAXDROP="3"

# These are the default server parameters the system will use if there is not a configured sync server.
#The system will check 
SSH_DEFAULT_TARGET="bus@example.com"
SSH_DEFAULT_PORT="6055"
SSH_DEFAULT_IDENTITY="$BASEDIR/.ssh/id_rsa_bus"

#This regular expression test is used to make sure we have a valid-looking FQDN or IP address
SSH_TARGET_VALIDITY_CHECK="^[a-zA-Z][a-zA-Z0-9]*@([a-zA-Z0-9-]+\.)+([a-zA-Z0-9-]+)$"

SSH_TARGET="$SSH_DEFAULT_TARGET"
SSH_PORT="$SSH_DEFAULT_PORT"
SSH_IDENTITY="$SSH_DEFAULT_IDENTITY"

#--------------------------------------------------------------------------------
#This is the list of forwarded ports we bring live when we set up our SSH tunnel:

# 2857 -> AVLS
AVLS_DAEMON_PORT="2857"

# 7277 -> Bus Pass Daemon
PASSDB_DAEMON_PORT="7277"

# 2455 -> Billing Log Daemon
BILLDB_DAEMON_PORT="2455"

# 3556 -> "Hello" Daemon
HELLO_DAEMON_PORT="3556"

# 8377 -> Update / Version Server
UPDATE_DAEMON_PORT="8377"


#    These are used to talk to the "Hello" daemon on the server to verify that
#actual data can flow before notifying the system that the tunnel is up.
HELLO_DAEMON_MESSAGE="Hello."


#--------------------------------------------------------------------------------
#    This is a list of base ports on the server that we can use for the reverse-phone-home
#functionality which forwards port BASE + client_id on the server to port 22 (sshd) on 
#the client to allow for remote troubleshooting and manual configuration.

#Enable reverse-phone-home feature
REVERSE_PHONE_HOME="1"

#    This is the base port (on the server) which the numbering for the reverse-phone-home
#system starts at when numbered by equipment number:
REVERSE_PHONE_HOME_EQNUM_BASE="10000"

#    This is the base port (on the server) which the numbering for the reverse-phone-home
#system starts at when numbered by serial number (wireless_ip on transition busses):
REVERSE_PHONE_HOME_SERIALNUM_BASE="30000"

#    This is the number above which an equipment number or other token will not be considered
#as a phone home candidate on account of its likelyhood to overflow into the next port range.
REVERSE_PHONE_HOME_MAX_TOKEN="19999"


#--------------------------------------------------------------------------------
#These paths lead to dropfiles in /tmp which are used to signal network state

#This dropfile means we have an established GPRS/PPP session
GPRS_DROPFILE="/tmp/network-is-up"
#This dropfile means we have an established SSH tunnel to the server
TUNNEL_DROPFILE="/tmp/tunnel-is-up"
#This dropfile contains the process ID of our SSH client that's keeping the tunnel open
SSH_TUNNEL_PIDFILE="/tmp/ssh_tunnel.pid"
#This dropfile contains identifying information about our ethernet card, cell modem, and SIM
NETWORK_ID_DROPFILE="/tmp/net_ids"

#This dropfile becomes present to notify the update process to abort:
UPDATE_ABORT_DROPFILE="/tmp/abort_update"
TUNNEL_ABORT_DROPFILE="/tmp/abort_tunnel"


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

FIX_PACKAGE_PERMS="1"

########################################################## Used only if FIX_PACKAGE_PERMS is nonzero ##############
#This specifies who should own installed packages
PACKAGE_OWNER_STRING="root:root"

#These are the permissions applied to stuff in the bin directory
PACKAGE_BIN_PERMISSIONS="755"
#This is the egrep expression that must match to qualify something as a binary
PACKAGE_BIN_PATTERN='(^[/]?'$BASEDIR'/bin/[a-z_A-Z0-9.]+$)|(.*\.sh$)'

#These are the permissions applied to SSH related config files and directories
PACKAGE_SSH_STRICT_FILE_PERMISSIONS="600"
PACKAGE_SSH_MILD_FILE_PERMISSIONS="644"
PACKAGE_SSH_DIR_PERMISSIONS="700"

#this one is only used in its ALWAYS case
PACKAGE_SYS_DIR_PERMISSIONS="755"

#These are the egrep expressions that must match to qualify something as an SSH file or directory
PACKAGE_SSH_STRICT_FILE_PATTERN="(^[/]?etc/ppp/id_rsa[^\.]*$)|(^[/]?root/\.ssh/id_rsa[^\.]*$)|(^[/]?root/\.ssh/authorized_keys)"
PACKAGE_SSH_MILD_FILE_PATTERN="(^[/]?root/\.ssh/known_hosts)|(^[/]?etc/ppp/id_rsa[^\.]*.pub$)|(^[/]?root/\.ssh/id_rsa[^\.]*.pub)"
PACKAGE_SSH_DIR_PATTERN="(^[/]?root[/]?$)|(^[/]?root/\.ssh[/]?$)"

#These are the list of files to ALWAYS reset permissions on (whether or not they are in the update)
ALWAYS_SSH_DIR_LIST="/root /root/.ssh"
ALWAYS_SSH_STRICT_FILE_LIST="/root/.ssh/id_rsa /etc/ppp/id_rsa_bus /root/.ssh/authorized_keys $SYNC_PRIVATE_KEY"
ALWAYS_SSH_MILD_FILE_LIST="/root/.ssh/known_hosts /root/.ssh/id_rsa.pub /etc/ppp/id_rsa_bus.pub /etc/ssh/sshd_config"
ALWAYS_BIN_LIST="$BASEDIR/bin/init_bus.sh $BASEDIR/bin/update_loop.sh $BASEDIR/bin/apply_update.sh $BASEDIR/bin/fix_pkg_perm.sh $BASEDIR/bin/get_net_ids.sh"

ALWAYS_SYS_DIR_LIST="/etc /etc/ssh /etc/ppp"

#DEFAULT_EXTRACT_PATH="/"
DEFAULT_EXTRACT_PATH="/home/bus"
EXTRACT_PATH_FILE="/tmp/pkg_extract_path"

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


#These paths point to important bits of information for synchronizing
EQUIP_NUM_FILE="$BASEDIR/config/equipnum.txt"
SERIAL_NUM_FILE="$BASEDIR/config/serial_num"

SERVER_CONFIG_DIR="$BASEDIR/config/server"

SERVER_LIST_FILE="$SERVER_CONFIG_DIR/server_list"

SYNC_DESC_FILE="$SERVER_CONFIG_DIR/sync_server_desc"
SYNC_TARGET_FILE="$SERVER_CONFIG_DIR/sync_target"
SYNC_PORT_FILE="$SERVER_CONFIG_DIR/sync_port"
SYNC_KNOWN_HOSTS="/root/.ssh/known_hosts"
SYNC_PRIVATE_KEY="/etc/ppp/id_rsa_client"

###############################################################################################
#     CONFIGURATION VALUES USED PRIMARILY BY THE UPDATE LOOP SCRIPT                           #
###############################################################################################

#How long for update_loop.sh to sleep after spawning the client supervisor before (potentially) respawning it
SLEEP_AFTER_SPAWN="60"

#How long to sleep between attempts to download patches from the server
SLEEP_BETWEEN_UPDATES="60"

#The directory in which the current patch state is to be found
CHECKSUM_AND_VERSION_PATH="$BASEDIR/config/"

###############################################################################################
#     BELOW HERE, COMPOUND OPTIONS ARE ALLOWED (things that use variable substitution, etc...)#
###############################################################################################

#This looks for all environment variables that specify a port that we must forward...
TUNNEL_PORT_LIST="`set | egrep "^[A-Z]+_DAEMON_PORT=" | cut -d '=' -f2 | egrep '^[0-9]+$' | xargs echo`"

#This gathers the above list into something we can just hand to the SSH client...
SSH_FORWARDS="`for port in $TUNNEL_PORT_LIST; do echo -n " -L$port:localhost:$port"; done`"

#These are the flags to SSH that are specific to setting up the tunnel
SSH_TUNNEL_FLAGS="-n -N -x"

#These are the flags to SSH that are specific to our connection to the target host
SSH_TARGET_OPTIONS="-o StrictHostKeyChecking=no"

#These are the flags to SSH that specify the behavior of the protocol-level keepalive messages
SSH_KEEPALIVE_OPTIONS="-o ServerAliveInterval=$SSH_SERVER_ALIVE_INTERVAL -o ServerAliveCountMax=$SSH_SERVER_ALIVE_MAXDROP"

SSH_OPTIONS="$SSH_TUNNEL_FLAGS $SSH_KEEPALIVE_OPTIONS $SSH_TARGET_OPTIONS"
SCP_OPTIONS="$SSH_KEEPALIVE_OPTIONS $SSH_TARGET_OPTIONS"

# This function kills any currently open SSH or SCP sessions and puts dropfiles in /tmp telling
#ppp-dialer.sh and update_loop.sh to refresh their Sync server parameters with the generate_ssh_targets
#function before attempting to reconnect. 
#
abort_tunnel_and_update() {

  touch $UPDATE_ABORT_DROPFILE
  touch $TUNNEL_ABORT_DROPFILE

  sync
  sleep 1

  killall ssh
  killall scp
}

# This function uses the dropfiles left by unpack_server_data.sh and constructs new SSH and SCP
#parameters suitable to connect to the specified server.  If there is no server specified, or if the
#specified server does not pass a sanity check then the default server will be used.
#
function generate_ssh_targets
{

  if [ -f $SYNC_TARGET_FILE ]; then

    alt_target="`cat $SYNC_TARGET_FILE`"
    if echo "$alt_target" | egrep -q "$SSH_TARGET_VALIDITY_CHECK"; then

      SSH_TARGET="`cat $SYNC_TARGET_FILE`"
      SSH_PORT="`cat $SYNC_PORT_FILE`"
      SSH_IDENTITY="$SYNC_PRIVATE_KEY"

      echo "$0: Using sync server $SSH_TARGET port $SSH_PORT"
    else

      SSH_TARGET="$SSH_DEFAULT_SYNC_TARGET"
      SSH_PORT="$SSH_DEFAULT_SYNC_PORT"
      SSH_IDENTITY="$SSH_DEFAULT_IDENTITY"

      echo "$0: Specified sync server name $alt_target is malformed.  Using default $SSH_DEFAULT_SYNC_TARGET port $SSH_DEFAULT_SYNC_PORT"
    fi
  else

      SSH_TARGET="$SSH_DEFAULT_TARGET"
      SSH_PORT="$SSH_DEFAULT_PORT"
      SSH_IDENTITY="$SSH_DEFAULT_IDENTITY"

      echo "$0: No sync server selected. Using default $SSH_DEFAULT_TARGET port $SSH_DEFAULT_PORT"
  fi

}

