#!/usr/bin/env bash

# Usage:
#   install_system.sh [--vagrant] [--utm] [--worker] [<extra> <extra> ...]

err_message() {
    >&2 echo -e "
#####################################################################

                        INSTALLATION FAILURE

Something has gone wrong in the installation process. If you feel
that this is in error, please create an issue on our issue tracker at
https://github.com/Submitty/Submitty including an output of the build
log to better help us diagnose what has gone wrong.
#####################################################################
"
}

# Display our error message if something fails below
trap 'err_message' ERR

# print commands as we execute and fail early
set -ev

# this script must be run by root or sudo
if [[ "$UID" -ne "0" ]] ; then
    echo "ERROR: This script must be run by root or sudo"
    exit 1
fi

#################################################################
# CONSTANTS
#################

# PATHS
CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
SUBMITTY_REPOSITORY=/usr/local/submitty/GIT_CHECKOUT/Submitty
LICHEN_REPOSITORY=/usr/local/submitty/GIT_CHECKOUT/Lichen
SUBMITTY_INSTALL_DIR=/usr/local/submitty
SUBMITTY_DATA_DIR=/var/local/submitty


# USERS / GROUPS
DAEMON_USER=submitty_daemon
DAEMON_GROUP=submitty_daemon
PHP_USER=submitty_php
PHP_GROUP=submitty_php
CGI_USER=submitty_cgi
CGI_GROUP=submitty_cgi

DAEMONPHP_GROUP=submitty_daemonphp
DAEMONCGI_GROUP=submitty_daemoncgi

# VERSIONS
source ${CURRENT_DIR}/bin/versions.sh

#################################################################
# PROVISION SETUP
#################

export DEV_VM=0
export UTM=0
export VAGRANT=0
export NO_SUBMISSIONS=0
export WORKER=0

# Read through the flags passed to the script reading them in and setting
# appropriate bash variables, breaking out of this once we hit something we
# don't recognize as a flag
echo ""
echo "Running setup:"
while :; do
    case $1 in
        --utm)
            export UTM=1
            export DEV_VM=1
            echo "utm"
            ;;
        --vagrant)
            export VAGRANT=1
            export DEV_VM=1
            echo "vagrant"
            ;;
        --worker)
            export WORKER=1
            echo "worker"
            ;;
        --no_submissions)
            export NO_SUBMISSIONS=1
            echo "no_submissions"
            ;;
        *) # No more options, so break out of the loop.
            break
    esac

    shift
done

if [ ${VAGRANT} == 1 ]; then
    echo "Non-interactive vagrant script..."
    export DEBIAN_FRONTEND=noninteractive
fi

if [ ${UTM} == 1 ]; then
    mkdir ${SUBMITTY_REPOSITORY}/.utm
fi

if [ ${DEV_VM} == 1 ] && [ ${WORKER} == 0 ]; then
    # Setting it up to allow SSH as root by default
    mkdir -p -m 700 /root/.ssh
    # SEE GITHUB ISSUE #7885 - https://github.com/Submitty/Submitty/issues/7885
    if [ ${UTM} == 0 ]; then
	cp /home/vagrant/.ssh/authorized_keys /root/.ssh
    fi

    sed -i -e "s/PermitRootLogin prohibit-password/PermitRootLogin yes/g" /etc/ssh/sshd_config

    # Set up some convinence stuff for the root user on ssh

    INSTALL_HELP=$(cat <<'EOF'
The vagrant box comes with some handy aliases:
    submitty_help                - show this message
    submitty_install             - runs INSTALL_SUBMITTY.sh
    submitty_install_site        - runs .setup/INSTALL_SUBMITTY_HELPER_SITE.sh
    submitty_install_bin         - runs .setup/INSTALL_SUBMITTY_HELPER_BIN.sh
    submitty_code_watcher        - runs .setup/bin/code_watcher.py
    submitty_restart_autograding - restart systemctl for autograding
    submitty_restart_services    - restarts all Submitty related systemctl
    lichen_install               - runs Lichen/install_lichen.sh
    migrator                     - run the migrator tool
    vagrant_info                 - print out the MotD again
    ntp_sync                     - Re-syncs NTP in case of time drift

Saved variables:
    SUBMITTY_REPOSITORY, LICHEN_REPOSITORY,
    SUBMITTY_INSTALL_DIR, SUBMITTY_DATA_DIR,
    DAEMON_USER, DAEMON_GROUP, PHP_USER, PHP_GROUP,
    CGI_USER, CGI_GROUP, DAEMONPHP_GROUP, DAEMONCGI_GROUP
EOF
)

echo -e "

# Convinence stuff for Submitty
export SUBMITTY_REPOSITORY=${SUBMITTY_REPOSITORY}
export LICHEN_REPOSITORY=${LICHEN_REPOSITORY}
export SUBMITTY_INSTALL_DIR=${SUBMITTY_INSTALL_DIR}
export SUBMITTY_DATA_DIR=${SUBMITTY_DATA_DIR}
export DAEMON_USER=${DAEMON_USER}
export DAEMON_GROUP=${DAEMON_GROUP}
export PHP_USER=${PHP_USER}
export PHP_GROUP=${PHP_GROUP}
export CGI_USER=${CGI_USER}
export CGI_GROUP=${CGI_GROUP}
export DAEMONPHP_GROUP=${DAEMONPHP_GROUP}
export DAEMONCGI_GROUP=${DAEMONCGI_GROUP}
alias submitty_help=\"echo -e '${INSTALL_HELP}'\"
alias install_submitty='/usr/local/submitty/.setup/INSTALL_SUBMITTY.sh'
alias submitty_install='/usr/local/submitty/.setup/INSTALL_SUBMITTY.sh'
alias install_lichen='bash /usr/local/submitty/GIT_CHECKOUT/Lichen/install_lichen.sh'
alias lichen_install='bash /usr/local/submitty/GIT_CHECKOUT/Lichen/install_lichen.sh'
alias install_submitty_site='bash /usr/local/submitty/GIT_CHECKOUT/Submitty/.setup/INSTALL_SUBMITTY_HELPER_SITE.sh'
alias submitty_install_site='bash /usr/local/submitty/GIT_CHECKOUT/Submitty/.setup/INSTALL_SUBMITTY_HELPER_SITE.sh'
alias install_submitty_bin='bash /usr/local/submitty/GIT_CHECKOUT/Submitty/.setup/INSTALL_SUBMITTY_HELPER_BIN.sh'
alias submitty_install_bin='bash /usr/local/submitty/GIT_CHECKOUT/Submitty/.setup/INSTALL_SUBMITTY_HELPER_BIN.sh'
alias submitty_code_watcher='python3 /usr/local/submitty/GIT_CHECKOUT/Submitty/.setup/bin/code_watcher.py'
alias submitty_restart_autograding='systemctl restart submitty_autograding_shipper && systemctl restart submitty_autograding_worker'
alias submitty_restart_websocket='systemctl restart submitty_websocket_server'
alias submitty_restart_services='submitty_restart_autograding && submitty_restart_websocket && systemctl restart submitty_daemon_jobs_handler && systemctl restart nullsmtpd'
alias migrator='python3 ${SUBMITTY_REPOSITORY}/migration/run_migrator.py -c ${SUBMITTY_INSTALL_DIR}/config'
alias vagrant_info='cat /etc/motd'
alias ntp_sync='service ntp stop && ntpd -gq && service ntp start'
systemctl start submitty_autograding_shipper
systemctl start submitty_autograding_worker
systemctl start submitty_daemon_jobs_handler
systemctl start submitty_websocket_server
systemctl start nullsmtpd
cd ${SUBMITTY_INSTALL_DIR}" >> /root/.bashrc
else
    #TODO: We should get options for ./.setup/CONFIGURE_SUBMITTY.py script
    :
fi

if [ ${WORKER} == 1 ]; then
    if [ ${VAGRANT} == 1 ]; then
        # Setting it up to allow SSH as root by default
        mkdir -p -m 700 /root/.ssh
        cp /home/vagrant/.ssh/authorized_keys /root/.ssh

        sed -i -e "s/PermitRootLogin prohibit-password/PermitRootLogin yes/g" /etc/ssh/sshd_config
    fi
    echo "Installing Submitty in worker mode."
else
    echo "Installing primary Submitty."

fi

INSTALL_SYS_DIR=$(mktemp -d)
chmod 777 "${INSTALL_SYS_DIR}"
pushd "${INSTALL_SYS_DIR}" > /dev/null

COURSE_BUILDERS_GROUP=submitty_course_builders
DB_USER=submitty_dbuser
DATABASE_PASSWORD=submitty_dbuser
DB_COURSE_USER=submitty_course_dbuser
DB_COURSE_PASSWORD=submitty_dbuser

#################################################################
# DISTRO SETUP
#################

source ${CURRENT_DIR}/distro_setup/setup_distro.sh

bash "${SUBMITTY_REPOSITORY}/.setup/update_system.sh"


#################################################################
# Node Package Setup
####################
# NOTE: with umask 0027, the npm packages end up with the wrong permissions.
# (this happens if we re-run install_system on an existing installation).
# So let's manually set the umask just for this call.
(umask 0022 && npm install -g npm)

#################################################################
# STACK SETUP
#################

if [ ${DEV_VM} == 1 ] && [ ${WORKER} == 0 ]; then
    # We only might build analysis tools from source on a development machine
    echo "Installing stack (haskell)"
    curl -sSL https://get.haskellstack.org/ | sh
fi

#################################################################
# USERS SETUP
#################

python3 ${SUBMITTY_REPOSITORY}/.setup/bin/create_untrusted_users.py

# Special users and groups needed to run Submitty
#
# It is probably easiest to set and store passwords for the special
# accounts, although you can also use ‘sudo su user’ to change to the
# desired user on the local machine which works for most things.

# The group DAEMONPHP_GROUP allows the PHP_USER to write the
# submissions, but give read-only access to the DAEMON_USER.  And the
# DAEMON_USER writes the results, and gives read-only access to the
# PHP_USER.

if ! cut -d ':' -f 1 /etc/group | grep -q ${DAEMONPHP_GROUP} ; then
	addgroup ${DAEMONPHP_GROUP}
else
	echo "${DAEMONPHP_GROUP} already exists"
fi

if ! cut -d ':' -f 1 /etc/group | grep -q ${DAEMONCGI_GROUP} ; then
    addgroup ${DAEMONCGI_GROUP}
else
    echo "${DAEMONCGI_GROUP} already exists"
fi

# The COURSE_BUILDERS_GROUP allows instructors/head TAs/course
# managers to write website customization files and run course
# management scripts.
if ! cut -d ':' -f 1 /etc/group | grep -q ${COURSE_BUILDERS_GROUP} ; then
        addgroup ${COURSE_BUILDERS_GROUP}
else
        echo "${COURSE_BUILDERS_GROUP} already exists"
fi

# SEE GITHUB ISSUE #7885 - https://github.com/Submitty/Submitty/issues/7885
# CREATE VAGRANT USER WHEN MANUALLY INSTALLING ON UTM
if getent passwd vagrant > /dev/null; then
    # Already exists
    echo 're-running install submitty'
elif [ ${UTM} == 1 ]; then
    useradd -m vagrant
fi
# END UTM


if [ ${DEV_VM} == 1 ] && [ ${WORKER} == 0 ]; then
	usermod -aG sudo vagrant
fi

# change the default user umask (was 002)
sed -i  "s/^UMASK.*/UMASK 027/g"  /etc/login.defs
grep -q "^UMASK 027" /etc/login.defs || (echo "ERROR! failed to set umask" && exit)

#add users not needed on a worker machine.
if [ ${WORKER} == 0 ]; then
    if ! cut -d ':' -f 1 /etc/passwd | grep -q ${PHP_USER} ; then
        useradd -m -c "First Last,RoomNumber,WorkPhone,HomePhone" "${PHP_USER}"
    fi
    usermod -a -G "${DAEMONPHP_GROUP}" "${PHP_USER}"
    if ! cut -d ':' -f 1 /etc/passwd | grep -q ${CGI_USER} ; then
        useradd -m -c "First Last,RoomNumber,WorkPhone,HomePhone" "${CGI_USER}"
    fi
    usermod -a -G "${PHP_GROUP}" "${CGI_USER}"
    usermod -a -G "${DAEMONCGI_GROUP}" "${CGI_USER}"
    # THIS USER SHOULD NOT BE NECESSARY AS A UNIX GROUP
    #useradd -c "First Last,RoomNumber,WorkPhone,HomePhone" "${DB_USER}"

    # NOTE: ${CGI_USER} must be in the shadow group so that it has access to the
    # local passwords for pam authentication
    usermod -a -G shadow "${CGI_USER}"
    # FIXME:  umask setting above not complete
    # might need to also set USERGROUPS_ENAB to "no", and manually create
    # the PHP_GROUP and DAEMON_GROUP single user groups.  See also /etc/login.defs

    #add cgi user to docker group in order to use the Docker python sdk
    usermod -a -G "docker" ${CGI_USER}
    echo -e "\n# set by the .setup/install_system.sh script\numask 027" >> /home/${PHP_USER}/.profile
    echo -e "\n# set by the .setup/install_system.sh script\numask 027" >> /home/${CGI_USER}/.profile
fi

if ! cut -d ':' -f 1 /etc/passwd | grep -q ${DAEMON_USER} ; then
    useradd -m -c "First Last,RoomNumber,WorkPhone,HomePhone" "${DAEMON_USER}"
    if [ ${WORKER} == 0 ] && [ ${DEV_VM} == 1 ] && [ -f ${SUBMITTY_REPOSITORY}/.vagrant/workers.json ]; then
        echo -e "attempting to create ssh key for submitty_daemon..."
        su submitty_daemon -c "cd ~/"
        su submitty_daemon -c "ssh-keygen -b 2048 -t rsa -f ~/.ssh/id_rsa -q -N ''"
        su submitty_daemon -c "echo 'successfully created ssh key'"

        while read -r IP
        do
            su submitty_daemon -c "sshpass -p 'submitty' ssh-copy-id -i ~/.ssh/id_rsa.pub -o StrictHostKeyChecking=no submitty@${IP}"
        done <<< "$(jq -r ".[].ip_addr" "${SUBMITTY_REPOSITORY}/.vagrant/workers.json")"
        echo "DONE"
    fi
fi

# The VCS directories (/var/local/submitty/vcs) are owned by root:$DAEMONCGI_GROUP
usermod -a -G "${DAEMONPHP_GROUP}" "${DAEMON_USER}"
usermod -a -G "${DAEMONCGI_GROUP}" "${DAEMON_USER}"

echo -e "\n# set by the .setup/install_system.sh script\numask 027" >> /home/${DAEMON_USER}/.profile

usermod -a -G docker "${DAEMON_USER}"

#################################################################
# JAR SETUP
#################
if [ -x "$(command -v javac)" ]; then

    # -----------------------------------------
    echo "Getting JUnit & Hamcrest..."

    mkdir -p ${SUBMITTY_INSTALL_DIR}/java_tools/JUnit
    mkdir -p ${SUBMITTY_INSTALL_DIR}/java_tools/hamcrest
    mkdir -p ${SUBMITTY_INSTALL_DIR}/java_tools/jacoco

    if [ ${WORKER} == 0 ]; then
        chown -R root:${COURSE_BUILDERS_GROUP} ${SUBMITTY_INSTALL_DIR}/java_tools
    fi
    chmod -R 751 ${SUBMITTY_INSTALL_DIR}/java_tools

    pushd ${SUBMITTY_INSTALL_DIR}/java_tools/JUnit > /dev/null
    rm -rf junit*jar
    wget https://repo1.maven.org/maven2/junit/junit/${JUNIT_VERSION}/junit-${JUNIT_VERSION}.jar -o /dev/null > /dev/null 2>&1
    popd > /dev/null

    pushd ${SUBMITTY_INSTALL_DIR}/java_tools/hamcrest > /dev/null
    rm -rf hamcrest*.jar
    wget https://repo1.maven.org/maven2/org/hamcrest/hamcrest-core/${HAMCREST_VERSION}/hamcrest-core-${HAMCREST_VERSION}.jar -o /dev/null > /dev/null 2>&1
    popd > /dev/null

    # TODO:  Want to Install JUnit 5.0
    # And maybe also Hamcrest 2.0 (or maybe that piece isn't needed anymore)

    echo "Getting JaCoCo..."

    pushd ${SUBMITTY_INSTALL_DIR}/java_tools/jacoco > /dev/null
    wget https://github.com/jacoco/jacoco/releases/download/v${JACOCO_VERSION}/jacoco-${JACOCO_VERSION}.zip -o /dev/null > /dev/null 2>&1
    mkdir jacoco-${JACOCO_VERSION}
    unzip jacoco-${JACOCO_VERSION}.zip -d jacoco-${JACOCO_VERSION} > /dev/null
    mv jacoco-${JACOCO_VERSION}/lib/jacococli.jar jacococli.jar
    mv jacoco-${JACOCO_VERSION}/lib/jacocoagent.jar jacocoagent.jar
    rm -rf jacoco-${JACOCO_VERSION}
    rm -f jacoco-${JACOCO_VERSION}.zip
    chmod o+r . *.jar
    popd > /dev/null


    # fix all java_tools permissions
    chown -R root:${COURSE_BUILDERS_GROUP} ${SUBMITTY_INSTALL_DIR}/java_tools
    chmod -R 755 ${SUBMITTY_INSTALL_DIR}/java_tools
fi


#################################################################
# DRMEMORY SETUP
#################

# Dr Memory is a tool for detecting memory errors in C++ programs (similar to Valgrind)

pushd /tmp > /dev/null

echo "Getting DrMemory..."

wget https://github.com/DynamoRIO/drmemory/releases/download/${DRMEMORY_TAG}/DrMemory-Linux-${DRMEMORY_VERSION}.tar.gz -o /dev/null > /dev/null 2>&1
tar -xpzf DrMemory-Linux-${DRMEMORY_VERSION}.tar.gz
rsync --delete -a /tmp/DrMemory-Linux-${DRMEMORY_VERSION}/ ${SUBMITTY_INSTALL_DIR}/drmemory
rm -rf /tmp/DrMemory*

chown -R root:${COURSE_BUILDERS_GROUP} ${SUBMITTY_INSTALL_DIR}/drmemory
chmod -R 755 ${SUBMITTY_INSTALL_DIR}/drmemory

popd > /dev/null

#################################################################
# TCLAPP SETUP
#################
pushd /tmp > /dev/null

echo "Getting TCLAPP"
wget https://sourceforge.net/projects/tclap/files/tclap-1.2.2.tar.gz -o /dev/null > /dev/null 2>&1
tar -xpzf tclap-1.2.2.tar.gz
rm /tmp/tclap-1.2.2.tar.gz
cd tclap-1.2.2/
sed -i 's/SUBDIRS = include examples docs tests msc config/SUBDIRS = include docs msc config/' Makefile.in
bash configure
make
make install
cd /tmp
rm -rf /tmp/tclap-1.2.2

popd > /dev/null

#################################################################
# APACHE SETUP
#################

#Set up website if not in worker mode
if [ ${WORKER} == 0 ]; then
    PHP_VERSION=$(php -r 'print PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;')

    # install composer which is needed for the website
    curl -sS https://getcomposer.org/installer -o /tmp/composer-setup.php
    php /tmp/composer-setup.php --install-dir=/usr/local/bin --filename=composer
    rm -f /tmp/composer-setup.php

    a2enmod include actions cgi suexec authnz_external headers ssl proxy_fcgi rewrite proxy_http proxy_wstunnel

    # Install nginx to serve websocket connections
    sudo apt-get install -qqy nginx-full

    # A real user will have to do these steps themselves for a non-vagrant setup as to do it in here would require
    # asking the user questions as well as searching the filesystem for certificates, etc.
    if [ ${VAGRANT} == 1 ]; then
        # comment out directory configs - should be converted to something more flexible
        sed -i '153,174s/^/#/g' /etc/apache2/apache2.conf

        if ! grep -E -q "Listen ${SUBMISSION_PORT}" /etc/apache2/ports.conf; then
            echo "Listen ${SUBMISSION_PORT}" >> /etc/apache2/ports.conf
        fi

        # remove default sites which would cause server to mess up
        rm -f /etc/apache2/sites*/000-default.conf
        rm -f /etc/apache2/sites*/default-ssl.conf

        cp ${SUBMITTY_REPOSITORY}/.setup/apache/submitty.conf /etc/apache2/sites-available/submitty.conf

        sed -i -e "s/Require host __your_domain__/Require host localhost/g" /etc/apache2/sites-available/submitty.conf
        sed -i -e "s/\*:80/*:${SUBMISSION_PORT}/g" /etc/apache2/sites-available/submitty.conf

        # permissions: rw- r-- ---
        chmod 0640 /etc/apache2/sites-available/*.conf
        a2ensite submitty
        # a2ensite git

        sed -i '25s/^/\#/' /etc/pam.d/common-password
        sed -i '26s/pam_unix.so obscure use_authtok try_first_pass yescrypt/pam_unix.so obscure minlen=1 yescrypt/' /etc/pam.d/common-password

        # Create folder and give permissions to PHP user for xdebug profiling
        mkdir -p ${SUBMITTY_REPOSITORY}/.vagrant/Ubuntu/profiler
        usermod -aG vagrant ${PHP_USER}

        # Enable xdebug support for debugging
        phpenmod xdebug

        # In case you reprovision without wiping the drive, don't paste this twice
        if [ -z $(grep 'xdebug\.remote_enable' /etc/php/${PHP_VERSION}/mods-available/xdebug.ini) ]
        then
            # Tell it to send requests to our host on port 9003 (PhpStorm default)
            cat << EOF >> /etc/php/${PHP_VERSION}/mods-available/xdebug.ini
xdebug.start_with_request=trigger
xdebug.client_port=9003
xdebug.discover_client_host=true
xdebug.mode=debug
EOF
        fi

        if [ -z $(grep 'xdebug\.profiler_enable_trigger' /etc/php/${PHP_VERSION}/mods-available/xdebug.ini) ]
        then
            # Allow remote profiling and upload outputs to the shared folder
            cat << EOF >> /etc/php/${PHP_VERSION}/mods-available/xdebug.ini
xdebug.output_dir=${SUBMITTY_REPOSITORY}/.vagrant/Ubuntu/profiler
EOF
            sed -i -e "s/xdebug.mode=debug/xdebug.mode=debug,profile/g" /etc/php/${PHP_VERSION}/mods-available/xdebug.ini
        fi
    fi

    cp -n ${SUBMITTY_REPOSITORY}/.setup/php-fpm/pool.d/submitty.conf /etc/php/${PHP_VERSION}/fpm/pool.d/submitty.conf
    cp ${SUBMITTY_REPOSITORY}/.setup/apache/www-data /etc/apache2/suexec/www-data
    chmod 0640 /etc/apache2/suexec/www-data


    #################################################################
    # NGINX SETUP
    #################
    # remove default site which would cause server to mess up
    rm -f /etc/nginx/sites*/default
    cp -n ${SUBMITTY_REPOSITORY}/.setup/nginx/submitty.conf /etc/nginx/sites-available/submitty.conf
    chmod 644 /etc/nginx/sites-available/submitty.conf
    rm -f /etc/nginx/sites-enabled/submitty.conf
    ln -s /etc/nginx/sites-available/submitty.conf /etc/nginx/sites-enabled/submitty.conf

    if [ ${DEV_VM} == 1 ]; then
        sed -i -e "s/8443/${WEBSOCKET_PORT}/g" /etc/nginx/sites-available/submitty.conf
    fi

    #################################################################
    # PHP SETUP
    #################

    # Edit php settings.  Note that if you need to accept larger files,
    # you’ll need to increase both upload_max_filesize and
    # post_max_filesize

    sed -i -e 's/^max_execution_time = 30/max_execution_time = 60/g' /etc/php/${PHP_VERSION}/fpm/php.ini
    sed -i -e 's/^upload_max_filesize = 2M/upload_max_filesize = 200M/g' /etc/php/${PHP_VERSION}/fpm/php.ini
    sed -i -e 's/^session.gc_maxlifetime = 1440/session.gc_maxlifetime = 86400/' /etc/php/${PHP_VERSION}/fpm/php.ini
    sed -i -e 's/^post_max_size = 8M/post_max_size = 200M/g' /etc/php/${PHP_VERSION}/fpm/php.ini
    sed -i -e 's/^allow_url_fopen = On/allow_url_fopen = Off/g' /etc/php/${PHP_VERSION}/fpm/php.ini
    sed -i -e 's/^session.cookie_httponly =/session.cookie_httponly = 1/g' /etc/php/${PHP_VERSION}/fpm/php.ini
    # This should mimic the list of disabled functions that RPI uses on the HSS machine with the sole difference
    # being that we do not disable phpinfo() on the vagrant machine as it's not a function that could be used for
    # development of some feature, but it is useful for seeing information that could help debug something going wrong
    # with our version of PHP.
    DISABLED_FUNCTIONS="popen,pclose,proc_open,php_real_logo_guid,php_egg_logo_guid,php_ini_scanned_files,"
    DISABLED_FUNCTIONS+="php_ini_loaded_file,readlink,symlink,link,set_file_buffer,proc_close,proc_terminate,"
    DISABLED_FUNCTIONS+="proc_get_status,proc_nice,getmyuid,getmygid,getmyinode,putenv,get_current_user,"
    DISABLED_FUNCTIONS+="magic_quotes_runtime,set_magic_quotes_runtime,import_request_variables,ini_alter,"
    DISABLED_FUNCTIONS+="stream_socket_server,stream_socket_accept,stream_socket_pair,"
    DISABLED_FUNCTIONS+="stream_get_transports,stream_wrapper_restore,mb_send_mail,openlog,syslog,closelog,pfsockopen,"
    DISABLED_FUNCTIONS+="posix_kill,apache_child_terminate,apache_get_modules,apache_get_version,apache_lookup_uri,"
    DISABLED_FUNCTIONS+="apache_reset_timeout,apache_response_headers,virtual,system,exec,shell_exec,passthru,"
    DISABLED_FUNCTIONS+="pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,"
    DISABLED_FUNCTIONS+="pcntl_wifsignaled,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,"
    DISABLED_FUNCTIONS+="pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,"
    DISABLED_FUNCTIONS+="pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,"

    if [ ${DEV_VM} != 1 ]; then
        DISABLED_FUNCTIONS+="phpinfo,"
    fi

    sed -i -e "s/^disable_functions = .*/disable_functions = ${DISABLED_FUNCTIONS}/g" /etc/php/${PHP_VERSION}/fpm/php.ini
fi

# create directories and fix permissions
mkdir -p ${SUBMITTY_DATA_DIR}

#Set up database and copy down the tutorial repo if not in worker mode
if [ ${WORKER} == 0 ]; then
    # create the courses directory. This is needed for the first time we run
    # the migrator in the INSTALL_SUBMITTY_HELPER.sh
    mkdir -p ${SUBMITTY_DATA_DIR}/courses

    # create a list of valid userids and put them in /var/local/submitty/instructors
    # one way to create your list is by listing all of the userids in /home
    mkdir -p ${SUBMITTY_DATA_DIR}/instructors
    ls /home | sort > ${SUBMITTY_DATA_DIR}/instructors/valid

    #################################################################
    # POSTGRES SETUP
    #################

    PG_VERSION="$(psql -V | grep -m 1 -o -E '[0-9]{1,}.[0-9]{1,}' | head -1)"
    if [ ! -d "/etc/postgresql/${PG_VERSION}" ]; then
        # PG 10.x stopped putting the minor version in the folder name
        PG_VERSION="$(psql -V | grep -m 1 -o -E '[0-9]{1,}' | head -1)"
    fi

    if [ ${DEV_VM} == 1 ]; then
        cp /etc/postgresql/${PG_VERSION}/main/pg_hba.conf /etc/postgresql/${PG_VERSION}/main/pg_hba.conf.backup
        cp ${SUBMITTY_REPOSITORY}/.setup/vagrant/pg_hba.conf /etc/postgresql/${PG_VERSION}/main/pg_hba.conf
        echo "Creating PostgreSQL users"
        su postgres -c "source ${SUBMITTY_REPOSITORY}/.setup/vagrant/db_users.sh";
        echo "Finished creating PostgreSQL users"

        # Set timezone to UTC instead of using localtime (which is probably ET)
        sed -i "s/timezone = 'localtime'/timezone = 'UTC'/" /etc/postgresql/${PG_VERSION}/main/postgresql.conf

        # Set the listen address to be global so that we can access the guest DB from the host
        sed -i "s/#listen_addresses = 'localhost'/listen_addresses = '*'/" /etc/postgresql/${PG_VERSION}/main/postgresql.conf
        service postgresql restart
    fi
fi



#################################################################
# CLONE OR UPDATE THE HELPER SUBMITTY CODE REPOSITORIES
#################

/bin/bash ${SUBMITTY_REPOSITORY}/.setup/bin/update_repos.sh

if [ $? -eq 1 ]; then
    echo -n "\nERROR: FAILURE TO CLONE OR UPDATE SUBMITTY HELPER REPOSITORIES\n"
    echo -n "Exiting install_system.sh"
    exit 1
fi


#################################################################
# BUILD CLANG SETUP
#################

# NOTE: These variables must match the same variables in INSTALL_SUBMITTY_HELPER.sh
clangsrc=${SUBMITTY_INSTALL_DIR}/clang-llvm/src
clangbuild=${SUBMITTY_INSTALL_DIR}/clang-llvm/build
# note, we are not running 'ninja install', so this path is unused.
clanginstall=${SUBMITTY_INSTALL_DIR}/clang-llvm/install

# skip if this is a re-run
if [ ! -d "${clangsrc}" ]; then
    echo 'GOING TO PREPARE CLANG INSTALLATION FOR STATIC ANALYSIS'

    mkdir -p ${clangsrc}

    # clone the clang sources, circa Nov. 2018
    git clone --depth 1 --branch llvmorg-7.1.0 https://github.com/llvm/llvm-project.git ${clangsrc}/source
    cp -R ${clangsrc}/source/llvm ${clangsrc}/llvm
    cp -R ${clangsrc}/source/clang ${clangsrc}/llvm/tools
    cp -R ${clangsrc}/source/clang-tools-extra ${clangsrc}/llvm/tools/clang/tools/
    mv ${clangsrc}/llvm/tools/clang/tools/clang-tools-extra ${clangsrc}/llvm/tools/clang/tools/extra

    # initial cmake for llvm tools (might take a bit of time)
    mkdir -p ${clangbuild}
    pushd ${clangbuild}
    cmake -G Ninja ../src/llvm -DCMAKE_INSTALL_PREFIX=${clanginstall} -DCMAKE_BUILD_TYPE=Release -DLLVM_TARGETS_TO_BUILD=X86 -DCMAKE_C_COMPILER=/usr/bin/clang -DCMAKE_CXX_COMPILER=/usr/bin/clang++
    popd > /dev/null

    # add build targets for our tools (src to be installed in INSTALL_SUBMITTY_HELPER.sh)
    echo 'add_subdirectory(ASTMatcher)' >> ${clangsrc}/llvm/tools/clang/tools/extra/CMakeLists.txt
    echo 'add_subdirectory(UnionTool)'  >> ${clangsrc}/llvm/tools/clang/tools/extra/CMakeLists.txt

    echo 'DONE PREPARING CLANG INSTALLATION'
fi

#################################################################
# SUBMITTY SETUP
#################
echo Beginning Submitty Setup

#If in worker mode, run configure with --worker option.
if [ ${WORKER} == 1 ]; then
    echo "Running configure submitty in worker mode"
    if [ ${DEV_VM} == 1 ]; then
        echo "submitty" | python3 ${SUBMITTY_REPOSITORY}/.setup/CONFIGURE_SUBMITTY.py --worker
    else
        python3 ${SUBMITTY_REPOSITORY}/.setup/CONFIGURE_SUBMITTY.py --worker
    fi
else
    if [ ${DEV_VM} == 1 ]; then
        # This should be set by setup_distro.sh for whatever distro we have, but
        # in case it is not, default to our primary URL
        if [ -z "${SUBMISSION_URL}" ]; then
            SUBMISSION_URL='http://192.168.56.101'
        fi
        echo -e "/var/run/postgresql
${DB_USER}
${DATABASE_PASSWORD}
${DB_COURSE_USER}
${DB_COURSE_PASSWORD}
America/New_York
en_US
${SUBMISSION_URL}


sysadmin@example.com
https://example.com
1
submitty-admin
y


submitty@vagrant
do-not-reply@vagrant
localhost
25
" | python3 ${SUBMITTY_REPOSITORY}/.setup/CONFIGURE_SUBMITTY.py --debug --setup-for-sample-courses --websocket-port ${WEBSOCKET_PORT}

        # Set these manually as they're not asked about during CONFIGURE_SUBMITTY.py
        sed -i -e 's/"url": ""/"url": "ldap:\/\/localhost"/g' ${SUBMITTY_INSTALL_DIR}/config/authentication.json
        sed -i -e 's/"uid": ""/"uid": "uid"/g' ${SUBMITTY_INSTALL_DIR}/config/authentication.json
        sed -i -e 's/"bind_dn": ""/"bind_dn": "ou=users,dc=vagrant,dc=local"/g' ${SUBMITTY_INSTALL_DIR}/config/authentication.json
    else
        python3 ${SUBMITTY_REPOSITORY}/.setup/CONFIGURE_SUBMITTY.py
    fi
fi

if [ ${WORKER} == 1 ]; then
   #Add the submitty user to /etc/sudoers if in worker mode.
    SUPERVISOR_USER=$(jq -r '.supervisor_user' ${SUBMITTY_INSTALL_DIR}/config/submitty_users.json)
    if ! grep -q "${SUPERVISOR_USER}" /etc/sudoers; then
        echo "" >> /etc/sudoers
        echo "#grant the submitty user on this worker machine access to install submitty" >> /etc/sudoers
        echo "%${SUPERVISOR_USER} ALL = (root) NOPASSWD: ${SUBMITTY_INSTALL_DIR}/.setup/INSTALL_SUBMITTY.sh" >> /etc/sudoers
        echo "#grant the submitty user on this worker machine access to the systemctl wrapper" >> /etc/sudoers
        echo "%${SUPERVISOR_USER} ALL = (root) NOPASSWD: ${SUBMITTY_INSTALL_DIR}/sbin/shipper_utils/systemctl_wrapper.py" >> /etc/sudoers
    fi
fi

# Create and setup database for non-workers
if [ ${WORKER} == 0 ]; then
    dbuser_password=`cat ${SUBMITTY_INSTALL_DIR}/.setup/submitty_conf.json | jq .database_password | tr -d '"'`
    dbcourse_user_password=`cat ${SUBMITTY_INSTALL_DIR}/.setup/submitty_conf.json | jq .database_course_password | tr -d '"'`

    # create the submitty_dbuser role in postgres (if it does not yet exist)
    # SUPERUSER privilege is required to use dblink extension (needed for data sync between master and course DBs).
    su postgres -c "psql -c \"DO \\\$do\\\$ BEGIN IF NOT EXISTS ( SELECT FROM  pg_catalog.pg_roles WHERE  rolname = '${DB_USER}') THEN CREATE ROLE ${DB_USER} SUPERUSER LOGIN PASSWORD '${dbuser_password}'; END IF; END \\\$do\\\$;\""

    su postgres -c "psql -c \"DO \\\$do\\\$ BEGIN IF NOT EXISTS ( SELECT FROM  pg_catalog.pg_roles WHERE  rolname = '${DB_COURSE_USER}') THEN CREATE ROLE ${DB_COURSE_USER} LOGIN PASSWORD '${dbcourse_user_password}'; END IF; END \\\$do\\\$;\""

    # check to see if a submitty master database exists
    DB_EXISTS=`su -c 'psql -lqt | cut -d \| -f 1 | grep -w submitty || true' postgres`

    if [ "$DB_EXISTS" == "" ]; then
        echo "Creating submitty master database"
        su postgres -c "psql -c \"CREATE DATABASE submitty WITH OWNER ${DB_USER}\""
        su postgres -c "psql submitty -c \"ALTER SCHEMA public OWNER TO ${DB_USER}\""
        python3 ${SUBMITTY_REPOSITORY}/migration/run_migrator.py -c ${SUBMITTY_INSTALL_DIR}/config -e master -e system migrate --initial
    else
        echo "Submitty master database already exists"
    fi
fi

echo Beginning Install Submitty Script
bash ${SUBMITTY_INSTALL_DIR}/.setup/INSTALL_SUBMITTY.sh clean skip_web_restart


# (re)start the submitty grading scheduler daemon
systemctl restart submitty_autograding_worker
if [ ${WORKER} == 0 ]; then
    systemctl restart submitty_autograding_shipper
    systemctl restart submitty_daemon_jobs_handler
    systemctl restart submitty_websocket_server
fi
# also, set it to automatically start on boot
systemctl enable submitty_autograding_worker
if [ ${WORKER} == 0 ]; then
    systemctl enable submitty_autograding_shipper
    systemctl enable submitty_daemon_jobs_handler
    systemctl enable submitty_websocket_server
fi

#Setup website authentication if not in worker mode.
if [ ${WORKER} == 0 ]; then
    sudo mkdir -p /usr/lib/cgi-bin
    sudo chown -R www-data:www-data /usr/lib/cgi-bin

    apache2ctl -t

    if ! grep -q "${COURSE_BUILDERS_GROUP}" /etc/sudoers; then
        echo "" >> /etc/sudoers
        echo "#grant limited sudo access to members of the ${COURSE_BUILDERS_GROUP} group (instructors)" >> /etc/sudoers
        echo "%${COURSE_BUILDERS_GROUP} ALL=(ALL:ALL) ${SUBMITTY_INSTALL_DIR}/bin/generate_repos.py" >> /etc/sudoers
        echo "%${COURSE_BUILDERS_GROUP} ALL=(ALL:ALL) ${SUBMITTY_INSTALL_DIR}/bin/grading_done.py" >> /etc/sudoers
        echo "%${COURSE_BUILDERS_GROUP} ALL=(ALL:ALL) ${SUBMITTY_INSTALL_DIR}/bin/regrade.py" >> /etc/sudoers
    fi

fi

if [ ${WORKER} == 0 ]; then
    if [[ ${DEV_VM} == 1 ]]; then
        # Disable OPCache for development purposes as we don't care about the efficiency as much
        echo "opcache.enable=0" >> /etc/php/${PHP_VERSION}/fpm/conf.d/10-opcache.ini

        # Call helper script that makes the courses and refreshes the database
        if [ ${NO_SUBMISSIONS} == 1 ]; then
            python3 ${SUBMITTY_REPOSITORY}/.setup/bin/setup_sample_courses.py --no_submissions --submission_url ${SUBMISSION_URL}
        else
            python3 ${SUBMITTY_REPOSITORY}/.setup/bin/setup_sample_courses.py --submission_url ${SUBMISSION_URL}
        fi

        python3 ${SUBMITTY_REPOSITORY}/.setup/bin/setup_sample_user_data.py
        python3 ${SUBMITTY_REPOSITORY}/.setup/bin/setup_sample_emails.py
    fi
fi

if [ ${DEV_VM} == 1 ] && [ ${WORKER} == 0 ]; then
    chown root:${DAEMONPHP_GROUP} ${SUBMITTY_INSTALL_DIR}/config/email.json
    chmod 440 ${SUBMITTY_INSTALL_DIR}/config/email.json
    rsync -rtz  ${SUBMITTY_REPOSITORY}/.setup/vagrant/nullsmtpd.service  /etc/systemd/system/nullsmtpd.service
    chown -R root:root /etc/systemd/system/nullsmtpd.service
    chmod 444 /etc/systemd/system/nullsmtpd.service
    systemctl restart nullsmtpd
    # also, set it to automatically start on boot
    systemctl enable nullsmtpd
fi


#################################################################
# DOCKER SETUP
#################

# If we are in vagrant and http_proxy is set, then vagrant-proxyconf
# is probably being used, and it will work for the rest of this script,
# but fail here if we do not manually set the proxy for docker
if [ ${DEV_VM} == 1 ] && [ ${WORKER} == 0 ]; then
    if [ ! -z ${http_proxy+x} ]; then
        mkdir -p /home/${DAEMON_USER}/.docker
        proxy="            \"httpProxy\": \"${http_proxy}\""
        if [ ! -z ${https_proxy+x} ]; then
            proxy="${proxy},\n            \"httpsProxy\": \"${https_proxy}\""
        fi
        if [ ! -z ${no_proxy+x} ]; then
            proxy="${proxy},\n            \"noProxy\": \"${no_proxy}\""
        fi
        echo -e "{
    \"proxies\": {
        \"default\": {
${proxy}
        }
    }
}" > /home/${DAEMON_USER}/.docker/config.json
        chown -R ${DAEMON_USER}:${DAEMON_USER} /home/${DAEMON_USER}/.docker
    fi
fi

su -c 'docker pull submitty/autograding-default:latest' ${DAEMON_USER}
su -c 'docker tag submitty/autograding-default:latest ubuntu:custom' ${DAEMON_USER}

#################################################################
# RESTART SERVICES
###################
if [ ${WORKER} == 0 ]; then
    service apache2 restart
    service nginx restart
    service php${PHP_VERSION}-fpm restart
    service postgresql restart
fi


#####################################################################################
# Obtain API auth token for submitty-admin user
# (This is attempted in INSTALL_SUBMITTY_HELPER.sh, but the API is not
# operational at that time.)
if [ ${WORKER} == 0 ]; then

    python3 ${SUBMITTY_INSTALL_DIR}/.setup/bin/init_auto_rainbow.py

fi

popd > /dev/null
rm -rf "${INSTALL_SYS_DIR}"


echo "
#####################################################################

                     INSTALLATION SUCCESS!

                        .GGQGGGSlu
                      .GGGGGGGGGGGS
                 :llUGGGGGGGGGGGGGGGG
                 'GGGGGGGGGGGGGGGGGGb        .
                    %GGGGGGGGGGGGGGG~   ..GSGGG
                       GGGGGGGGGGGGGGSGGGGGGGGGG[
                     ;GGGGGGGGGGGGp\ \ \GGGGGGGGL
                    !GGGGGGGGGGGGGGS\ \ \GGGGGG
                    GGGGGGGGGGGGGGGGG\ \ \9GGGG
                    %GGGGGGGGGGGGGGGS/ / /.GGG
                     %GGGGGGGGGGGGGS/ / /GGG
                      '%NNNNNNNNNNNNNNNNNN
#####################################################################
"
exit 0
