#!/bin/bash #============================================================================== # 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 #------------------------------------------------------------------------------ # Helix Server base init script # Do nothing unless $SDP_INSTANCE is defined. export SDP_INSTANCE="${1:-Unset}" declare -i StartDelay="${SDP_START_DELAY:-2}" declare -i ExitCode=0 declare ThisScript="${0##*/}" declare CmdLine="$0 $*" declare Log= declare TmpLog= if [[ "$SDP_INSTANCE" == Unset ]]; then echo -e "\\nError: The SDP_INSTANCE is not defined.\\n." exit 1 fi # Load SDP controlled shell environment. # shellcheck disable=SC1091 source /p4/common/bin/p4_vars "$SDP_INSTANCE" ExitCode="$?" if [[ "$ExitCode" -ne 0 ]]; then echo -e "\\nError: Failed to load SDP environment for instance $SDP_INSTANCE.\\n" exit 1 fi # shellcheck disable=SC1090 source "$P4CBIN/backup_functions.sh" ExitCode="$?" if [[ "$ExitCode" -ne 0 ]]; then echo -e "\\nError: Failed to load SDP lib $P4CBIN/backup_functions.sh.\\n" exit 1 fi if [[ "${P4D_FLAGS:-Unset}" == "Unset" ]]; then echo -e "\\nError: P4D_FLAGS is unset after loading SDP environment for instance $SDP_INSTANCE.\\n" exit 1 fi if [[ $(id -u) -eq 0 ]]; then exec su - "$OSUSER" -c "$0 $*" elif [[ $(id -u -n) != "$OSUSER" ]]; then echo "$0 can only be run by root or $OSUSER" exit 1 fi if [[ ! -x "$P4DBIN" ]]; then echo -e "\\nError: $P4DBIN is not executable." exit 2; fi # Ensure that the '--pid-file' argument is provided, and add it if it is not. # This is intended to prevent problems upgrading the SDP in case the # /p4/common/config/p4_N.vars file isn't updated to use the new template. if [[ "$P4D_FLAGS" != *"--pid-file"* ]]; then export P4D_FLAGS="$P4D_FLAGS --pid-file" fi # For P4D 2017.1+, automatically replace '-d' with '--daemonsafe'. # Disable shellcheck as we intend a string compare even though it looks like # we're trying to compare a decimal value. # shellcheck disable=SC2072 if [[ "$P4D_VERSION" > "2017.1" ]]; then export P4D_FLAGS=${P4D_FLAGS/ -d / --daemonsafe } fi Log="$LOGS/p4d_init.log" TmpLog="$(mktemp)" export LOGFILE="$Log" echo -e "\\nCalled $ThisScript called with command line:\\n$CmdLine" >> "$Log" set_vars # See how we were called. case "${2:-usage}" in force_start) if [[ -e "$P4ROOT/P4ROOT_not_usable.txt" ]]; then echo -e "\\nWarning: The $P4ROOT/P4ROOT_not_usable.txt file exists, indicating\\ndatabases in P4ROOT are not safe to use. Contents:\\n$(cat "$P4ROOT/P4ROOT_not_usable.txt")\\nIgnoring this due to use of force_start option. Review the bottom of this log:\\n$Log\\n" | tee -a "$Log" fi echo "Starting $P4DBIN $P4D_FLAGS" | tee -a "$Log" # Delay start $StartDelay seconds, unless P4ROOT is empty. [[ -r "$P4ROOT/db.domain" ]] && sleep "$StartDelay" # shellcheck disable=SC2086 "$P4DBIN" $P4D_FLAGS > "$TmpLog" 2>&1 ExitCode="$?" cat "$TmpLog" >> "$Log" cat "$TmpLog" ;; start) if [[ -e "$P4ROOT/P4ROOT_not_usable.txt" ]]; then echo -e "\\nError: The $P4ROOT/P4ROOT_not_usable.txt file exists, indicating\\ndatabases in P4ROOT are not safe to use. Contents:\\n$(cat "$P4ROOT/P4ROOT_not_usable.txt")\\nRefusing to start p4d. Review the bottom of this log:\\n$Log\\n" | tee -a "$Log" exit 1 fi if [[ -r "$P4ROOT/db.domain" ]]; then echo "Preflight check: $P4DBIN -r $P4ROOT -xvU" > "$TmpLog" "$P4DBIN" -r "$P4ROOT" -xvU >> "$TmpLog" 2>&1 ExitCode="$?" echo "EXIT_CODE=$ExitCode" >> "$TmpLog" cat "$TmpLog" >> "$Log" cat "$TmpLog" if [[ "$ExitCode" -ne 0 ]]; then echo -e "\\nError: DB check with 'p4d -xvU' failed. Database integrity is in question.\\nPlease Contact Perforce Support (support@perforce.com). The force_start option\\nis available, but not recommended. Review the bottom of this log:\\n$Log\\n" | tee -a "$Log" exit 1 fi if [[ -e "$P4JOURNAL" ]]; then echo "Preflight journal health check" | tee -a "$Log" mytmpdir=$(mktemp -d 2>/dev/null || mktemp -d -t 'mytmpdir') tmpjnl="$mytmpdir/jnl.test" # Note: Redirect error output to log, stdout is captured. Need to use '>>' to append # if not doing something like: >> "$Log" 2>&1 tail -10000 "$P4JOURNAL" | grep -v "@vv@" > "$tmpjnl" 2>> "$Log" # Check for first full record (some records are multiline and we may be part way through) firstline=$(grep -En "^@(ex|nx|pv|rv|dv)@" "$tmpjnl" | head -1 | cut -d: -f1 2>> "$Log") if [[ "$firstline" -gt "1" ]]; then # Skip lines if necessary to start with a full record. mv "$tmpjnl" "$tmpjnl.1" tail -n "+$firstline" "$tmpjnl.1" > "$tmpjnl" 2>> "$Log" fi "$P4DBIN" -r "$mytmpdir" -f -jr "$tmpjnl" >> "$Log" 2>&1 ExitCode=$? echo "EXIT_CODE=$ExitCode" >> "$Log" if [[ "$ExitCode" -ne 0 ]]; then echo -e "\\nError: possible corruption at end of journal detected. Journal is being rotated so any corruption is at end of file. You may wish to contact Perforce Support (support@perforce.com). Server is still being started as normal. NOTE If there is any corruption then replicas will likely stop replicating until this is fixed!! (If there are no problems with replication then this error can be ignored) Review this file:\\n$Log\\n" | tee -a "$Log" get_journalnum p4d_truncate_journal subject="ERROR!!! - $HOSTNAME $P4SERVER Possible journal corruption detected." mail_sender_opt=$(get_mail_sender_opt) echo "Sending mail: $SDPMAIL -s $subject $mail_sender_opt $MAILTO" | tee -a "$Log" "$SDPMAIL" -s "$subject" "$mail_sender_opt" "$MAILTO" < "$Log" fi fi fi echo "Starting $P4DBIN $P4D_FLAGS" | tee -a "$Log" # Delay start $StartDelay seconds, unless P4ROOT is empty. [[ -r "$P4ROOT/db.domain" ]] && sleep "$StartDelay" # shellcheck disable=SC2086 "$P4DBIN" $P4D_FLAGS >> "$Log" 2>&1 ExitCode="$?" ;; status) if [[ -r "$P4ROOT/server.pid" ]]; then pid=$(cat "$P4ROOT/server.pid") echo -e "\\nThe $P4ROOT/server.pid file contains pid $pid. Pid info:" | tee -a "$Log" "$PS" -f -p "$pid" > /dev/null 2>&1 | tee -a "$Log" ExitCode="$?" if [[ "$ExitCode" -ne 0 ]]; then echo -e "\\nError: A server.pid file exists, but that process id is not running. This could indicate abnormal process termination.\\n" | tee -a "$Log" fi fi "$P4BIN" -p "$P4PORT" -u "$P4USER" info -s 2>&1 | tee -a "$Log" ExitCode="$?" ;; admin_stop) # If there is no server.pid file, shut down the old fashioned way. echo -n "Shutting down $P4DBIN: " | tee -a "$Log" if [[ "${P4REPLICA}" == "FALSE" ]]; then "$P4CBIN/p4login" fi echo "$P4BIN -p $P4PORT -u $P4USER admin stop" | tee -a "$Log" "$P4BIN" -p "$P4PORT" -u "$P4USER" admin stop 2>&1 | tee -a "$Log" sleep 5 "$P4BIN" -p "$P4PORT" info > /dev/null 2>&1 ExitCode="$?" if [[ "$ExitCode" -eq 0 ]]; then echo -e "\\nError: Server shutdown failed." | tee -a "$Log" exit 1 else exit 0 fi ;; stop) if [[ -r "$P4ROOT/server.pid" ]]; then pid=$(cat "$P4ROOT/server.pid") "$PS" -p "$pid" > /dev/null 2>&1 status="$?" if [[ "$status" -eq 0 ]]; then echo -e "\\nSending SIGTERM signal to pid $pid in $P4ROOT/server.pid." | tee -a "$Log" kill "$pid" 2>&1 | tee -a "$Log" sleep 1 "$PS" -p "$pid" > /dev/null 2>&1 status=$? if [[ $status -eq 0 ]]; then echo -n "Waiting for p4d to shutdown ..." | tee -a "$Log" while [[ $status -eq 0 ]]; do echo -n "." sleep 5 "$PS" -p "$pid" > /dev/null 2>&1 status=$? done fi echo -e "\\nConfirmed shutdown of $P4DBIN." | tee -a "$Log" else echo -e "\\nError: A server.pid file exists, but that process id is not running. This could indicate abnormal process termination.\\n" | tee -a "$Log" exit 1 fi else echo -e "\\nWarning: Missing $P4ROOT/server.pid. Attempting shutdown with 'p4 admin stop'.\\n" | tee -a "$Log" echo "$0" "$SDP_INSTANCE" admin_stop | tee -a "$Log" $0 "$SDP_INSTANCE" admin_stop 2>&1 | tee -a "$Log" fi ;; restart) $0 "$SDP_INSTANCE" stop $0 "$SDP_INSTANCE" start ;; *) echo -e "\\nUsage: $0 SDP_INSTANCE {start|stop|admin_stop|status|restart|force_start}\\n" exit 1 ;; esac exit "$ExitCode"