#!/bin/bash
#==============================================================================
# This script serves as a guide defining best-practice configurables for a
# production environment.  See documentation regarding configurables here:
# https://www.perforce.com/perforce/doc.current/manuals/cmdref/Content/CmdRef/configurables.configurables.html
#
# Copyright and license info is available in the LICENSE file included with
# the Server Deployment Package (SDP), and also available online:
# https://swarm.workshop.perforce.com/projects/perforce-software-sdp/view/main/LICENSE
#------------------------------------------------------------------------------
# Set P4PORT and P4USER and run p4 login before running this script.

declare DepotSpecFile=
declare LOGFILE=

# Verify instance value
INSTANCE=$1
if [[ -n "$INSTANCE" ]]; then
   # shellcheck disable=SC1091
   source /p4/common/bin/p4_vars "$INSTANCE" ||\
   { echo -e "\\nError: Failed to load SDP environment."; exit 1; }

   # shellcheck disable=SC1091
   source /p4/common/bin/backup_functions.sh ||\
   { echo -e "\\nError: Failed to load backup_functions.sh."; exit 1; }
else
    echo "Error: An instance argument is required."
    exit 1
fi

LOGFILE="${LOGS:-/tmp}/configure_new_server.$(date +'%Y%m%d-%H%M').log"
check_vars
set_vars

touch "${LOGFILE}" ||\
   { echo -e "\\nError: Couldn't touch log file [${LOGFILE}].\\n"; exit 1; }

# Redirect stdout and stderr to a log file.
exec > >(tee "${LOGFILE}")
exec 2>&1

log "${0##*/} configuring $P4SERVER on $(date)."
echo "Logging to: $LOGFILE"
echo -e "See documentation regarding configurables here:\\n
https://www.perforce.com/perforce/doc.current/manuals/cmdref/Content/CmdRef/configurables.configurables.html\\n"

# Basic security features.
p4 configure set run.users.authorize=1

# The server.depot.root configurable was introduced in 2014.1.
# shellcheck disable=SC2072
if [[ "$P4D_VERSION" > "2014.1" ]]; then
   p4 configure set server.depot.root="$DEPOTS"
fi

p4 configure set journalPrefix="$CHECKPOINTS/p4_${INSTANCE}"
p4 configure set dm.user.noautocreate=2
p4 configure set dm.info.hide=1
p4 configure set filesys.P4ROOT.min=5G
p4 configure set filesys.depot.min=5G
p4 configure set filesys.P4JOURNAL.min=5G

# Note: With the SDP structure, filesys.P4LOG.min=5G and
# filesys.TEMP.min=5G don't need to be set, as they are
# stored on the same volume as the journal and are thus
# accounted for with filesys.P4JOURNAL.min.

p4 configure set server=4
p4 configure set monitor=1

# For P4D 2013.2+, setting db.reorg.disable=1, which turns off
# dynamic database reorg, has been shown to significantly improve
# performance when Perforce databases (db.* files) are stored on
# some solid state storage devices, while not making a difference
# on others.
# shellcheck disable=SC2072
[[ "$P4D_VERSION" > "2013.1" ]] && p4 configure set db.reorg.disable=1

# For P4D 2017.2.1594901 or greater, enable net.autotune.  For net.autotune
# to take effect, it must be enabled on both sides of a connection.  So, to
# get the full benefit, net.autotune must be enabled on all brokers, proxies,
# and clients.  See this KB article for details on fully enabling net.autotune:
# https://community.perforce.com/s/article/15368
#
# For connections in which net.autotune is not enabled, the p4d default value
# of net.tcpsize takes effect.
#
# When P4D is older than 2014.2 but less than 2017.2.1594901, set net.tcpsize
# to 512k.  In 2014.2, the default value for net.tcpsize became 512k, a
# reasonable default, so it should not be set explicitly. Also, there are
# indications it can reduce performance if set when not needed.
# shellcheck disable=SC2072
if [[ "$P4D_VERSION" < "2014.2" ]]; then
   p4 configure set net.tcpsize=524288
elif [[ "$P4D_VERSION" > "2017.2.1594900" ]]; then
   p4 configure set net.autotune=1
   p4 configure unset net.tcpsize
else
   p4 configure unset net.tcpsize
fi

# For P4D 2016.2.1468155+, set db.monitor.shared = max value.
if [[ "$P4D_VERSION" > "2016.2.1468154" ]]; then
   # This is the number of 8k pages to set aside for monitoring,
   # which requires pre-allocation of sufficient RAM.  The default
   # is 256, or 2MB, enough for about 128 active/concurrent processes.
   # The max as of 2016.2 is 4096.  Setting db.monitor.shared=0
   # causes the db.monitor on disk to be used instead, which can
   # potentially be a bottleneck.
   p4 configure set db.monitor.shared=4096
fi

p4 configure set net.backlog=2048

p4 configure set lbr.autocompress=1
p4 configure set lbr.bufsize=1M
p4 configure set filesys.bufsize=1M
p4 configure set serverlog.file.3="$LOGS/errors.csv"
p4 configure set serverlog.retain.3="$KEEPLOGS"

# The following are useful if using Interset Threat Detection with Perforce,
# or if P4AUDIT logs are otherwise desired.
# p4 configure set serverlog.file.4="$LOGS/audit.csv"
# p4 configure set serverlog.retain."4=$KEEPLOGS"

p4 configure set serverlog.file.7="$LOGS/events.csv"
p4 configure set serverlog.retain.7="$KEEPLOGS"
p4 configure set serverlog.file.8="$LOGS/integrity.csv"
p4 configure set serverlog.retain.8="$KEEPLOGS"

# Add a custom trigger for tracking trigger events:
p4 configure set serverlog.events.11=11
p4 configure set serverlog.file.11="$LOGS/triggers.csv"
p4 configure set serverlog.retain.11="$KEEPLOGS"

DepotSpecFile="${0%/*}/spec.depot.p4s"
if [[ -r "$DepotSpecFile" ]]; then
   echo "Creating a depot named 'spec' of type 'spec'."
   p4 -s depot -i < "$DepotSpecFile" ||\
      { echo -e "\\nError: Failed to create spec depot."; }
else
   echo -e "\\nWarning: Skipping spec depot creation due to missing spec file: $DepotSpecFile"
fi

DepotSpecFile="${0%/*}/unload.depot.p4s"
if [[ -r "$DepotSpecFile" ]]; then
   echo "Creating a depot named 'unload' of unload 'unload'."
   p4 -s depot -i < "$DepotSpecFile" ||\
      { echo -e "\\nError: Failed to create unload depot."; }
else
   echo -e "\\nWarning: Skipping unload depot creation due to missing spec file: $DepotSpecFile"
fi

# Load shedding and other performance-preserving configurable.
# See: http://answers.perforce.com/articles/KB/1272
# For p4d 2013.1+
# shellcheck disable=SC2072
[[ "$P4D_VERSION" > "2013.1" ]] && p4 configure set server.maxcommands=2500

# For p4d 2013.2+ -Turn off max* commandline overrides.
# shellcheck disable=SC2072
[[ "$P4D_VERSION" > "2013.2" ]] && p4 configure set server.commandlimits=2

echo See: https://community.perforce.com/s/article/3867
p4 configure set rpl.checksum.auto=1
p4 configure set rpl.checksum.change=2
p4 configure set rpl.checksum.table=1

# Define number of login attempts before there is a delay, to thwart
# automated password crackers.  Default is 3; set to a higher value to
# be more friendly to humans without compromising the protection.
# shellcheck disable=SC2072
if [[ "$P4D_VERSION" > "2013.1" ]]; then
   p4 configure set dm.user.loginattempts=7
fi

# For p4d 2016.1 Patch 5+
# Enable a server with an expired temp license to start, albeit with limited
# functionality, so that license expiry doesn't make it impossible to perform
# license management via the front-door.  This configurable allows the server
# to be started regardless of a bad license, though users will still be blocked
# by license invalid messages.  Perpetual commercial licenses never expire;
# this configurable will not affect those.
# shellcheck disable=SC2072
if [[ "$P4D_VERSION" > "2016.1.1408676" ]]; then
   p4 configure set server.start.unlicensed=1
fi

# Starting with p4d 2015.1 Patch 5, disallow P4EXP v2014.2 (a client
# version known to misbehave) from connecting to the server.
# See:  http://answers.perforce.com/articles/KB/15014
# shellcheck disable=SC2072
if [[ "$P4D_VERSION" > "2015.1.1126924" ]]; then
   p4 configure set rejectList="P4EXP,version=2014.2"
fi

# For p4d 2011.1 thru 2015.1, set rpl.compress=3.  For p4d 2015.2+, set
# rpl.compress=4.  This setting compresses journal data only, which is
# almost always advantageous as it compresses well, while avoiding
# compression of archive data, which is a mixed bag in terms of performance
# benefits, and potentially a net negative.
# server.global.views - makes client views global in a commit/edge or cluster environment.
# shellcheck disable=SC2072
if [[ "$P4D_VERSION" > "2015.2" ]]; then
   p4 configure set rpl.compress=4
   p4 configure set server.global.client.views=1
elif [[ "$P4D_VERSION" > "2011.1" ]]; then
   p4 configure set rpl.compress=3
fi

# Starting with p4d 2016.2, enable these features.
# shellcheck disable=SC2072
if [[ "$P4D_VERSION" > "2016.2" ]]; then
   p4 configure set filesys.checklinks=2
   p4 configure set server.locks.global=1
   p4 configure set proxy.monitor.level=3
fi

# Recommended for Swarm
p4 configure set dm.shelve.promote=1
p4 configure set dm.keys.hide=2
p4 configure set filetype.bypasslock=1

# Starting with p4d 2018.2 (as tech-preview, 2019.2 for GA), add best
# practices for Extensions.
if [[ "$P4D_VERSION" > "2018.2" ]]; then
   p4 configure set server.extensions.dir="$DEPOTS"/p4-extensions
fi

# Starting with p4d 2016.1, use auth.id to simplify ticket handling.
# After setting auth.id, login again.
# shellcheck disable=SC2072
if [[ "$P4D_VERSION" > "2016.1" ]]; then
   p4 configure set rpl.forward.login=1
   p4 configure set auth.id="$P4SERVER"
   "$P4CBIN"/p4login
fi

# Set SDP version identifying info.
p4 counter SDP_DATE "$(date +'%Y-%m-%d')"
p4 counter SDP_VERSION "$SDP_VERSION"

echo "Restarting server to ensure all configurable changes take effect."
stop_p4d
start_p4d

echo "Logging in."
"$P4CBIN"/p4login

echo -e "\\nIt is recommended that you run 'p4 configure set security=3' or\\n'p4 configure set security=4'.\\nSee: http://www.perforce.com/perforce/doc.current/manuals/p4sag/chapter.superuser.html#DB5-49899\\n"

if [[ "$P4D_VERSION" > "2017.2.1594900" ]]; then
   echo -e "The net.autotune value has been set on the server.  To get the full benefit, it must also be\\nenabled on proxies, brokers, and clients as well."
fi
