You are not logged in.
I have a bash script to save, sync and backup files for a program running in a custom tmpfs/ramdisk mounted dir.
The program in question is running in a screen session and also has logging enabled.
A save command is issued by the script to the screen session every 15 minutes and after that rsync syncs the files to hdd.
The problem is that the script issues the save command and immediately start rsync after that so the program dosen't have a chance to save all the files and the result is a corrupt save on the hdd.
I'm thinking of making a while loop to wait for a matching string in the screen log file or maybe in the screen itself if possible. Another problem is there are multiple matching strings from previous save commands so the script should only listen for new lines generated from the time the command runs till the new string pops up.
The string it should be waiting for is:
[Server]: Save completed.
However I don't have the experience nor the knowledge to get this working. I did a lot of digging around the internet, trying various commands in a vm (better safe than sorry) and nothing worked properly.
One of the thing I tried was:
tail -f screen.0 | grep -m 1 '[Server]: Save completed.'
I know that i coukd use the sleep command to some extent but the program takes sometimes a few seconds, sometimes a few minutes to save.
Does anyone know how I should go about this?
Last edited by 7thCore (2019-06-17 14:25:43)
[ Arch x86_64 | linux | Asus Prime X570-Pro | AMD Ryzen 9 5900X @4,8Ghz | AMD RX580 | 32GB RAM DDR4 | Main, 2 Monitors ]
[ Arch x86_64 | linux | Asus Pro WS X570-Ace | AMD Ryzen 9 5950X @4,9Ghz | Intel A750 | 128GB RAM DDR4 | Server ]
The Linux philosophy is 'Laugh in the face of danger'. Oops. Wrong One. 'Do it yourself'. Yes, that's it.
Offline
Run sync after the (successful) exit status of rsync.
Offline
Run sync after the (successful) exit status of rsync.
Here is no sync. There is only the save command that gets passed to the screen and rsync.
[ Arch x86_64 | linux | Asus Prime X570-Pro | AMD Ryzen 9 5900X @4,8Ghz | AMD RX580 | 32GB RAM DDR4 | Main, 2 Monitors ]
[ Arch x86_64 | linux | Asus Pro WS X570-Ace | AMD Ryzen 9 5950X @4,9Ghz | Intel A750 | 128GB RAM DDR4 | Server ]
The Linux philosophy is 'Laugh in the face of danger'. Oops. Wrong One. 'Do it yourself'. Yes, that's it.
Offline
Whatever: same principle... test the exit status and then either bail or run the next command.
In any event, paste the actual script rather than assuming that everyone knows what it is doing.
Offline
Alright. Here is the entire script:
#!/bin/bash
#Interstellar Rift server script by 7thCore
#Leave this variable alone, it is tied in with the systemd service file so it changes accordingly by it.
SCRIPT_ENABLED="1"
#Basics
NAME="IsRSrv" #Name of the screen
USER="$(whoami)" #Get user's username
#Steamcmd login
STEAMCMDUID="user" #Your steam username
STEAMCMDPSW="pass" #Your steam password
APPID="363360" #app id of the steam game
#Server configuration
SERVICE_NAME="isrsrv" #Name of the service files, script and script log
SRV_DIR_NAME="interstellar_rift" #Main directory name
SRV_DIR="/home/$USER/servers/$SRV_DIR_NAME/server" #Location of the server located on your hdd/ssd
SCRIPT_NAME="$SERVICE_NAME-script.bash" #Script name
SCRIPT_DIR="/home/$USER/servers/$SRV_DIR_NAME/scripts" #Location of this script
SCRIPT_LOG="$SCRIPT_DIR/$SERVICE_NAME-script.log" #Script log
UPDATE_DIR="/home/$USER/servers/$SRV_DIR_NAME/updates" #Location of update information for the script's automatic update feature
#Wine configuration
WINE_ARCH="win32" #Architecture of the wine prefix
WINE_PREFIX_GAME_DIR="drive_c/Games/InterstellarRift" #Server executable directory
WINE_PREFIX_GAME_EXE="Build/IR.exe -server -inline" #Server executable
#Ramdisk configuration
TMPFS_ENABLE="1" #Set this to 1 if you want to run the server on a ramdisk
TMPFS_DIR="/home/$USER/tmpfs/$SRV_DIR_NAME" #Locaton of your ramdisk. Note: you have to configure the ramdisk in /etc/fstab before using this.
#TmpFs/hdd variables
if [[ "$TMPFS_ENABLE" == "1" ]]; then
SCREEN_LOG="$TMPFS_DIR/$WINE_PREFIX_GAME_DIR/Build/screenlog.0" #Application data of the tmpfs
BCKP_SRC_DIR="$TMPFS_DIR/drive_c/users/$USER/Application Data/InterstellarRift/" #Application data of the tmpfs
SERVICE="$SERVICE_NAME-tmpfs.service" #TmpFs service file name
elif [[ "$TMPFS_ENABLE" == "0" ]]; then
SCREEN_LOG="$SRV_DIR/$WINE_PREFIX_GAME_DIR/Build/screenlog.0" #Application data of the hdd/ssd
BCKP_SRC_DIR="$SRV_DIR/drive_c/users/$USER/Application Data/InterstellarRift/" #Application data of the hdd/ssd
SERVICE="$SERVICE_NAME.service" #Hdd/ssd service file name
fi
#Backup configuration
BCKP_SRC="*" #What files to backup, * for all
BCKP_DIR="/home/$USER/servers/$SRV_DIR_NAME/backups" #Location of stored backups
BCKP_DEST="$BCKP_DIR/$(date +"%Y")/$(date +"%m")/$(date +"%d")" #How backups are sorted, by default it's sorted in folders by month and day
BCKP_DELOLD="+3" #Delete old backups. Ex +3 deletes 3 days old backups.
#-------Do not edit anything beyond this line-------
#Console collors
RED='\033[0;31m'
GREEN='\033[0;32m'
CYAN='\033[0;36m'
LIGHTRED='\033[1;31m'
NC='\033[0m'
#Prints out if the server is running
script_status() {
if [[ "$(systemctl --user show -p ActiveState --value $SERVICE)" != "active" ]]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Status) Server is not running." | tee -a "$SCRIPT_LOG"
elif [[ "$(systemctl --user show -p ActiveState --value $SERVICE)" == "active" ]]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Status) Server running." | tee -a "$SCRIPT_LOG"
fi
}
#If the script variable is set to 0, the script won't issue any commands ran. It will just exit.
script_enabled() {
if [[ "$SCRIPT_ENABLED" == "0" ]]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Script status) Server script is disabled" | tee -a "$SCRIPT_LOG"
script_status
exit 0
fi
}
#Sync server files from hdd/ssd to ramdisk
script_rsynctotmpfs() {
rsync -av --info=progress2 $SRV_DIR/ $TMPFS_DIR
}
#Sync server files from ramdisk to hdd/ssd
script_rsyncfromtmpfs() {
rsync -av --info=progress2 $TMPFS_DIR/ $SRV_DIR #| sed -e "s/^/$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Sync) Syncing: /" | tee -a "$SCRIPT_LOG"
}
script_crash_kill() {
if [[ "$(ps aux | grep -i "[A]lunaCrashHandler.exe" | awk '{print $2}')" -gt "0" ]]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Aluna Crash Handler) AlunaCrashHandler.exe detected. Killing the process." | tee -a "$SCRIPT_LOG"
kill $(ps aux | grep -i "[A]lunaCrashHandler.exe" | awk '{print $2}')
if [[ "$(ps aux | grep -i "[A]lunaCrashHandler.exe" | awk '{print $2}')" -eq "" ]]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Aluna Crash Handler) AlunaCrashHandler.exe process killed." | tee -a "$SCRIPT_LOG"
elif [[ "$(ps aux | grep -i "[A]lunaCrashHandler.exe" | awk '{print $2}')" -gt "0" ]]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Aluna Crash Handler) Failed to kill AlunaCrashHandler.exe process." | tee -a "$SCRIPT_LOG"
fi
elif [[ "$(ps aux | grep -i "[A]lunaCrashHandler.exe" | awk '{print $2}')" -eq "" ]]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Aluna Crash Handler) AlunaCrashHandler.exe not detected. Server nominal." | tee -a "$SCRIPT_LOG"
fi
}
#Issue the save command to the server
script_save() {
if [[ "$(systemctl --user show -p ActiveState --value $SERVICE)" != "active" ]]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Save) Server is not running." | tee -a "$SCRIPT_LOG"
elif [[ "$(systemctl --user show -p ActiveState --value $SERVICE)" == "active" ]]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Save) Save game to disk has been initiated." | tee -a "$SCRIPT_LOG"
screen -p 0 -S $NAME -X eval 'stuff "/save"\\015'
#while [ "$(tail -n 1 $SCREEN_LOG )" != '[Server]: Save completed.' ]; do
#echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Save) Server is saving Please wait..."
#sleep 1
#done
sleep 5
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Save) Save game to disk has been completed." | tee -a "$SCRIPT_LOG"
fi
}
#Sync server files from ramdisk to hdd/ssd
script_sync() {
if [[ "$TMPFS_ENABLE" == "1" ]]; then
if [[ "$(systemctl --user show -p ActiveState --value $SERVICE)" != "active" ]]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Sync) Server is not running." | tee -a "$SCRIPT_LOG"
elif [[ "$(systemctl --user show -p ActiveState --value $SERVICE)" == "active" ]]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Sync) Sync from tmpfs to disk has been initiated." | tee -a "$SCRIPT_LOG"
script_rsyncfromtmpfs
sleep 1
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Sync) Sync from tmpfs to disk has been completed." | tee -a "$SCRIPT_LOG"
fi
elif [[ "$TMPFS_ENABLE" == "0" ]]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Sync) Server does not have tmpfs enabled." | tee -a "$SCRIPT_LOG"
fi
}
#Start the server
script_start() {
if [[ "$(systemctl --user show -p ActiveState --value $SERVICE)" == "inactive" ]]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Start) Server start initialized." | tee -a "$SCRIPT_LOG"
systemctl --user start $SERVICE
sleep 1
while [[ "$(systemctl --user show -p ActiveState --value $SERVICE)" == "activating" ]]; do
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Start) Server is activating. Please wait..." | tee -a "$SCRIPT_LOG"
sleep 1
done
if [[ "$(systemctl --user show -p ActiveState --value $SERVICE)" == "active" ]]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Start) Server has been successfully activated." | tee -a "$SCRIPT_LOG"
sleep 1
elif [[ "$(systemctl --user show -p ActiveState --value $SERVICE)" != "active" ]]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Start) Server failed to activate. See systemctl --user status $SERVICE for details." | tee -a "$SCRIPT_LOG"
sleep 1
fi
elif [[ "$(systemctl --user show -p ActiveState --value $SERVICE)" == "active" ]]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Start) Server is already running." | tee -a "$SCRIPT_LOG"
sleep 1
elif [[ "$(systemctl --user show -p ActiveState --value $SERVICE)" == "failed" ]]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Start) Server failed to activate. See systemctl --user status $SERVICE for details." | tee -a "$SCRIPT_LOG"
sleep 1
fi
}
#Stop the server
script_stop() {
if [[ "$(systemctl --user show -p ActiveState --value $SERVICE)" == "inactive" ]]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Stop) Server is not running." | tee -a "$SCRIPT_LOG"
sleep 1
elif [[ "$(systemctl --user show -p ActiveState --value $SERVICE)" == "active" ]]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Stop) Server shutdown in progress." | tee -a "$SCRIPT_LOG"
systemctl --user stop $SERVICE
sleep 1
while [[ "$(systemctl --user show -p ActiveState --value $SERVICE)" == "deactivating" ]]; do
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Stop) Server is deactivating. Please wait..." | tee -a "$SCRIPT_LOG"
sleep 1
done
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Stop) Server is deactivated." | tee -a "$SCRIPT_LOG"
fi
}
#Restart the server
script_restart() {
if [[ "$(systemctl --user show -p ActiveState --value $SERVICE)" == "inactive" ]]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Restart) Server is not running." | tee -a "$SCRIPT_LOG"
elif [[ "$(systemctl --user show -p ActiveState --value $SERVICE)" == "active" ]]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Restart) Server is going to restart in 15-30 seconds, please wait..." | tee -a "$SCRIPT_LOG"
sleep 1
screen -p 0 -S $NAME -X eval 'stuff "/all Server restarting in 15 seconds."\\015'
sleep 1
script_stop
sleep 1
script_start
sleep 1
fi
}
#If the server proces is terminated it auto restarts it
script_autorestart() {
if [[ "$(systemctl --user show -p ActiveState --value $SERVICE)" != "active" ]]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Autorestart) Server not running, attempting to start." | tee -a "$SCRIPT_LOG"
script_start
sleep 1
elif [[ "$(systemctl --user show -p ActiveState --value $SERVICE)" == "active" ]]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Autorestart) Server running, no need to restart." | tee -a "$SCRIPT_LOG"
fi
}
#Deletes old backups
script_deloldbackup() {
# Delete old backups
find $BCKP_DIR -mtime $BCKP_DELOLD -exec rm {} \;
# Delete empty folders
find $BCKP_DIR -type d 2> /dev/null -empty -exec rm -rf {} \;
}
#Backs up the server
script_backup() {
# If there is not a folder for today, create one
if [ ! -d "$BCKP_DEST" ]; then
mkdir -p $BCKP_DEST
fi
#Create the new backup directory
if [ ! -d "$BCKP_DEST" ]; then
mkdir -p $BCKP_DEST
fi
# Backup source to destination
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Backup) Backup has been initiated." | tee -a "$SCRIPT_LOG"
cd "$BCKP_SRC_DIR"
tar -cpvzf $BCKP_DEST/$(date +"%Y%m%d%H%M").tar.gz $BCKP_SRC #| sed -e "s/^/$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Backup) Compressing: /" | tee -a "$SCRIPT_LOG"
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Backup) Backup complete." | tee -a "$SCRIPT_LOG"
}
#Automaticly backs up the server and deletes old backups
script_autobackup() {
if [[ "$(systemctl --user show -p ActiveState --value $SERVICE)" != "active" ]]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Autobackup) Server is not running." | tee -a "$SCRIPT_LOG"
elif [[ "$(systemctl --user show -p ActiveState --value $SERVICE)" == "active" ]]; then
sleep 1
script_backup
sleep 1
script_deloldbackup
fi
}
#Check for updates and update the server, if there are updates available, shut down the server, update it and restart it.
script_update() {
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Update) Initializing update check." | tee -a "$SCRIPT_LOG"
if [ ! -f $UPDATE_DIR/installed.buildid ] ; then
touch $UPDATE_DIR/installed.buildid
echo "0" > $UPDATE_DIR/installed.buildid
fi
if [ ! -f $UPDATE_DIR/installed.timeupdated ] ; then
touch $UPDATE_DIR/installed.timeupdated
echo "0" > $UPDATE_DIR/installed.timeupdated
fi
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Update) Removing Steam/appcache/appinfo.vdf" | tee -a "$SCRIPT_LOG"
rm -rf "/home/$USER/.steam/appcache/appinfo.vdf"
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Update) Connecting to steam servers." | tee -a "$SCRIPT_LOG"
steamcmd +login $STEAMCMDUID $STEAMCMDPSW +app_info_update 1 +app_info_print $APPID +quit | grep -EA 1000 "^\s+\"branches\"$" | grep -EA 5 "^\s+\"public\"$" | grep -m 1 -EB 10 "^\s+}$" | grep -E "^\s+\"buildid\"\s+" | tr '[:blank:]"' ' ' | tr -s ' ' | cut -d' ' -f3 > $UPDATE_DIR/available.buildid
steamcmd +login $STEAMCMDUID $STEAMCMDPSW +app_info_update 1 +app_info_print $APPID +quit | grep -EA 1000 "^\s+\"branches\"$" | grep -EA 5 "^\s+\"public\"$" | grep -m 1 -EB 10 "^\s+}$" | grep -E "^\s+\"timeupdated\"\s+" | tr '[:blank:]"' ' ' | tr -s ' ' | cut -d' ' -f3 > $UPDATE_DIR/available.timeupdated
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Update) Received application info data." | tee -a "$SCRIPT_LOG"
INSTALLED_BUILDID=$(cat $UPDATE_DIR/installed.buildid)
AVAILABLE_BUILDID=$(cat $UPDATE_DIR/available.buildid)
INSTALLED_TIME=$(cat $UPDATE_DIR/installed.timeupdated)
AVAILABLE_TIME=$(cat $UPDATE_DIR/available.timeupdated)
if [ "$AVAILABLE_TIME" -gt "$INSTALLED_TIME" ]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Update) New update detected." | tee -a "$SCRIPT_LOG"
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Update) Installed: BuildID: $INSTALLED_BUILDID, TimeUpdated: $INSTALLED_TIME" | tee -a "$SCRIPT_LOG"
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Update) Available: BuildID: $AVAILABLE_BUILDID, TimeUpdated: $AVAILABLE_TIME" | tee -a "$SCRIPT_LOG"
sleep 1
if [[ "$(systemctl --user show -p ActiveState --value $SERVICE)" == "active" ]]; then
screen -p 0 -S $NAME -X eval 'stuff "/all New update detected. Server will shutdown and update."\\015'
WAS_RUNNING="1"
script_stop
fi
sleep 1
if [[ "$TMPFS_ENABLE" == "1" ]]; then
rm -rf $TMPFS_DIR/$WINE_PREFIX_GAME_DIR
fi
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Update) Updating..." | tee -a "$SCRIPT_LOG"
steamcmd +@sSteamCmdForcePlatformType windows +login $STEAMCMDUID $STEAMCMDPSW +force_install_dir $SRV_DIR/$WINE_PREFIX_GAME_DIR +app_update $APPID validate +quit
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Update) Update completed." | tee -a "$SCRIPT_LOG"
echo "$AVAILABLE_TIME" > $UPDATE_DIR/installed.timeupdated
if [ "$WAS_RUNNING" == "1" ]; then
script_start
fi
elif [ "$AVAILABLE_TIME" -eq "$INSTALLED_TIME" ]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Update) No new updates detected." | tee -a "$SCRIPT_LOG"
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Update) Installed: BuildID: $INSTALLED_BUILDID, TimeUpdated: $INSTALLED_TIME" | tee -a "$SCRIPT_LOG"
fi
}
#First timer function for systemd timers to execute parts of the script in order without interfering with each other
script_timer_one() {
script_enabled
script_crash_kill
script_autorestart
script_save
script_sync
script_autobackup
script_update
}
#Second timer function for systemd timers to execute parts of the script in order without interfering with each other
script_timer_two() {
script_enabled
script_crash_kill
script_autorestart
script_save
script_sync
script_update
}
script_install() {
echo "Installation 1/3"
echo ""
echo "Required packages that need to be installed on the server:"
echo "xorg-xauth"
echo "xorg-xhost"
echo "wine"
echo "screen"
echo "steamcmd"
echo ""
echo "Required packages that need to be installed on the client:"
echo "xorg-xauth"
echo ""
echo "If these packages aren't installed, terminate this script with CTRL+C and install them."
echo ""
echo "This installation will enable linger for the user specified (allows user services to be ran on boot)."
echo "The installation process will also make a modification to /etc/ssh/sshd_config to allow X11 GUI forwarding to your"
echo "local machine so you can install the wine prefix. For this to work edit your /etc/ssh/sshd_config on your"
echo "local machine and uncomment the line:"
echo "#X11Forwarding yes"
echo "so it looks like this:"
echo "X11Forwarding yes"
echo "After that reboot both machines or restart the ssh services."
echo ""
read -p "Press any key to continue" -n 1 -s -r
echo ""
read -p "Enter user (MUST NOT BE ROOT!): " USER
echo ""
read -p "Enable RamDisk (1-yes, 0-no): " TMPFS
echo ""
if [[ "$TMPFS" == "1" ]]; then
read -p "RamDisk Size (Minimum 6GB): " TMPFS_SIZE
fi
if [[ "$TMPFS" == "1" ]]; then
cat >> /etc/fstab <<- EOF
# /home/$USER/tmpfs
tmpfs /home/$USER/tmpfs tmpfs rw,size=$TMPFS_SIZE,uid=$USER 0 0
EOF
fi
sudo sed -i 's/#AllowTcpForwarding yes/AllowTcpForwarding yes/g' /etc/ssh/sshd_config
sudo sed -i 's/#X11Forwarding yes/X11Forwarding yes yes/g' /etc/ssh/sshd_config
sudo sed -i 's/#X11DisplayOffset 10/X11DisplayOffset 10/g' /etc/ssh/sshd_config
sudo sed -i 's/#X11UseLocalhost yes/X11UseLocalhost yes/g' /etc/ssh/sshd_config
sudo mkdir -p /var/lib/systemd/linger/
sudo touch /var/lib/systemd/linger/$USER
echo "Installation 1/3 complete."
echo "After you reboot the server and your local machine connect to the server with ssh -Y user@host"
}
script_install_services() {
echo "Installation 2/3"
echo ""
echo "Initializing service instalation."
echo ""
echo "List of files that are going to be generated on the system:"
echo ""
echo "/home/$USER/.config/systemd/user/$SERVICE_NAME-mkdir-tmpfs.service - Service to generate the folder structure once the RamDisk is started (only executes if RamDisk enabled)."
echo "/home/$USER/.config/systemd/user/$SERVICE_NAME-tmpfs - Server service file for use with a RamDisk (only executes if RamDisk enabled)."
echo "/home/$USER/.config/systemd/user/$SERVICE_NAME - Server service file for normal hdd/ssd use."
echo "/home/$USER/.config/systemd/user/$SERVICE_NAME-timer-1.timer - Timer for scheduled command execution of $SERVICE_NAME-timer-1.service"
echo "/home/$USER/.config/systemd/user/$SERVICE_NAME-timer-1.service - Executes scheduled script functions: autorestart, save, sync, backup and update."
echo "/home/$USER/.config/systemd/user/$SERVICE_NAME-timer-2.timer - Timer for scheduled command execution of $SERVICE_NAME-timer-2.service"
echo "/home/$USER/.config/systemd/user/$SERVICE_NAME-timer-2.service - Executes scheduled script functions: autorestart, save, sync and update."
echo ""
read -p "Press any key to continue" -n 1 -s -r
cat > /home/$USER/.config/systemd/user/$SERVICE_NAME-mkdir-tmpfs.service <<- EOF
[Unit]
Description=$NAME TmpFs dir creator
After=home-$USER-tmpfs.mount
[Service]
Type=oneshot
WorkingDirectory=/home/$USER/
ExecStart=/bin/mkdir -p $TMPFS_DIR/$WINE_PREFIX_GAME_DIR/Build
[Install]
WantedBy=default.target
EOF
cat > /home/$USER/.config/systemd/user/$SERVICE_NAME-tmpfs.service <<- EOF
[Unit]
Description=$NAME TmpFs Server Service
After=network.target home-$USER-tmpfs.mount $SERVICE_NAME-mkdir-tmpfs.service
[Service]
Type=forking
WorkingDirectory=$TMPFS_DIR/$WINE_PREFIX_GAME_DIR/Build/
ExecStartPre=/usr/bin/rsync -av --info=progress2 $SRV_DIR/ $TMPFS_DIR
ExecStart=/bin/bash -c 'screen -dmSL $NAME env WINEARCH=$WINE_ARCH WINEDEBUG=-all WINEPREFIX=$TMPFS_DIR wine $TMPFS_DIR/$WINE_PREFIX_GAME_DIR/$WINE_PREFIX_GAME_EXE'
ExecStartPost=/usr/bin/sed -i 's/SCRIPTENABLED="1"/SCRIPTENABLED="0"/' $SCRIPT_DIR/$SCRIPT_NAME
ExecStop=/usr/bin/sed -i 's/SCRIPTENABLED="1"/SCRIPTENABLED="0"/' $SCRIPT_DIR/$SCRIPT_NAME
ExecStop=/usr/bin/screen -p 0 -S $NAME -X eval 'stuff "quittimer 15 Server shutting down in 15 seconds!"\\015'
ExecStop=/bin/sleep 10
ExecStop=/usr/bin/rsync -av --info=progress2 $TMPFS_DIR/ $SRV_DIR
TimeoutStartSec=infinity
TimeoutStopSec=300
Restart=no
[Install]
WantedBy=default.target
EOF
cat > /home/$USER/.config/systemd/user/$SERVICE_NAME.service <<- EOF
[Unit]
Description=$NAME Server Service
After=network.target
[Service]
Type=forking
WorkingDirectory=$SRV_DIR/$WINE_PREFIX_GAME_DIR/Build/
ExecStart=/bin/bash -c 'screen -dmSL $NAME env WINEARCH=$WINE_ARCH WINEDEBUG=-all WINEPREFIX=$SRV_DIR wine $SRV_DIR/$WINE_PREFIX_GAME_DIR/$WINE_PREFIX_GAME_EXE'
ExecStartPost=/usr/bin/sed -i 's/SCRIPTENABLED="1"/SCRIPTENABLED="0"/' $SCRIPT_DIR/$SCRIPT_NAME
ExecStop=/usr/bin/sed -i 's/SCRIPTENABLED="1"/SCRIPTENABLED="0"/' $SCRIPT_DIR/$SCRIPT_NAME
ExecStop=/usr/bin/screen -p 0 -S $NAME -X eval 'stuff "quittimer 15 Server shutting down in 15 seconds!"\\015'
ExecStop=/bin/sleep 10
TimeoutStartSec=infinity
TimeoutStopSec=300
Restart=no
[Install]
WantedBy=default.target
EOF
cat > /home/$USER/.config/systemd/user/$SERVICE_NAME-timer-1.timer <<- EOF
[Unit]
Description=$NAME Script Timer 1
[Timer]
OnCalendar=*-*-* 00:00:00
OnCalendar=*-*-* 06:00:00
OnCalendar=*-*-* 12:00:00
OnCalendar=*-*-* 18:00:00
Persistent=true
[Install]
WantedBy=timers.target
EOF
cat > /home/$USER/.config/systemd/user/$SERVICE_NAME-timer-1.service <<- EOF
[Unit]
Description=$NAME Script Timer 1 Service
[Service]
Type=oneshot
ExecStart=$SCRIPT_DIR/$SCRIPT_NAME -timer_one
EOF
cat > /home/$USER/.config/systemd/user/$SERVICE_NAME-timer-2.timer <<- EOF
[Unit]
Description=$NAME Script Timer 2
[Timer]
OnCalendar=*-*-* *:15:00
OnCalendar=*-*-* *:30:00
OnCalendar=*-*-* *:45:00
OnCalendar=*-*-* 01:00:00
OnCalendar=*-*-* 02:00:00
OnCalendar=*-*-* 03:00:00
OnCalendar=*-*-* 04:00:00
OnCalendar=*-*-* 05:00:00
OnCalendar=*-*-* 07:00:00
OnCalendar=*-*-* 08:00:00
OnCalendar=*-*-* 09:00:00
OnCalendar=*-*-* 10:00:00
OnCalendar=*-*-* 11:00:00
OnCalendar=*-*-* 13:00:00
OnCalendar=*-*-* 14:00:00
OnCalendar=*-*-* 15:00:00
OnCalendar=*-*-* 16:00:00
OnCalendar=*-*-* 17:00:00
OnCalendar=*-*-* 19:00:00
OnCalendar=*-*-* 20:00:00
OnCalendar=*-*-* 21:00:00
OnCalendar=*-*-* 22:00:00
OnCalendar=*-*-* 23:00:00
Persistent=true
[Install]
WantedBy=timers.target
EOF
cat > /home/$USER/.config/systemd/user/$SERVICE_NAME-timer-2.service <<- EOF
[Unit]
Description=$NAME Script Timer 2 Service
[Service]
Type=oneshot
ExecStart=$SCRIPT_DIR/$SCRIPT_NAME -timer_two
EOF
if [[ "$TMPFS" == "1" ]]; then
systemctl --user enable $SERVICE_NAME-mkdir-tmpfs.service
fi
systemctl --user enable $SERVICE_NAME-timer-1.timer $SERVICE_NAME-timer-2.timer
echo "Creating folder structure for server..."
mkdir -p /home/$USER/servers/$SRV_DIR_NAME/{backups,scripts,server,updates}
echo "Installation 2/3 complete."
}
script_install_wine_prefix() {
echo "Installation 3/3"
echo ""
echo "This will install the wine prefix. There will be some GUI interaction necessary."
echo "The wine uninstaller window will pop up at one time. Uninstall evreything related to Mono."
echo ""
read -p "Press any key to continue" -n 1 -s -r
env WINEARCH=$WINE_ARCH WINEDEBUG=-all WINEPREFIX=$SRV_DIR wineboot --init
env WINEARCH=$WINE_ARCH WINEDEBUG=-all WINEPREFIX=$SRV_DIR wine uninstaller
env WINEARCH=$WINE_ARCH WINEDEBUG=-all WINEPREFIX=$SRV_DIR winetricks corefonts vcrun2012 dotnet472
echo ""
echo "Instalation 3/3 complete."
}
#Do not allow for another instance of this script to run to prevent data loss
if [[ $(pidof -o %PPID -x $0) -gt "0" ]]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] Another instance of this script is already running. Exiting to prevent data loss."
exit 0
fi
case "$1" in
-help)
echo -e "${CYAN}Time: $(date +"%Y-%m-%d %H:%M:%S") ${NC}"
echo -e "${CYAN}$NAME server script by 7thCore${NC}"
echo ""
echo -e "${LIGHTRED}Before doing anything edit the script and input your steam username and password for the auto update feature to work.${NC}"
echo -e "${LIGHTRED}The variables for it are located at the very top of the script.${NC}"
echo -e "${LIGHTRED}Also if you have Steam Guard on your mobile phone activated, disable it because steamcmd always asks for the${NC}"
echo -e "${LIGHTRED}two factor authentication code and breaks the auto update feature. Use Steam Guard via email.${NC}"
echo ""
echo -e "${GREEN}start ${RED}- ${GREEN}Start the server${NC}"
echo -e "${GREEN}stop ${RED}- ${GREEN}Stop the server${NC}"
echo -e "${GREEN}restart ${RED}- ${GREEN}Restart the server${NC}"
echo -e "${GREEN}autorestart ${RED}- ${GREEN}Automaticly restart the server if it's not running${NC}"
echo -e "${GREEN}save ${RED}- ${GREEN}Issue the save command to the server${NC}"
echo -e "${GREEN}sync ${RED}- ${GREEN}Sync from tmpfs to hdd/ssd${NC}"
echo -e "${GREEN}backup ${RED}- ${GREEN}Backup files, if server running or not.${NC}"
echo -e "${GREEN}autobackup ${RED}- ${GREEN}Automaticly backup files when server running${NC}"
echo -e "${GREEN}deloldbackup ${RED}- ${GREEN}Delete old backups${NC}"
echo -e "${GREEN}update ${RED}- ${GREEN}Update the server, if the server is running it wil save it, shut it down, update it and restart it.${NC}"
echo -e "${GREEN}status ${RED}- ${GREEN}Display status of server${NC}"
echo -e "${GREEN}install ${RED}- ${GREEN}Installs all the needed files for the script to run${NC}"
echo -e "${GREEN}install-services ${RED}- ${GREEN}Installs systemd services for the server${NC}"
echo -e "${GREEN}install-prefix ${RED}- ${GREEN}Installs the wine prefix to the coresponding folder. There will be a lot of GUI interaction involved.${NC}"
echo ""
echo -e "${LIGHTRED}If this is your first time running the script:${NC}"
echo -e "${LIGHTRED}First use the -install argument (run only this command as root) and follow the instructions${NC}"
echo -e "${LIGHTRED}Second, run the -install_services argument. It will install the services for the server.${NC}"
echo -e "${LIGHTRED}Third, run the -install-prefix argument. It will install the wine prefix. There will be a lot of GUI interaction involved.${NC}"
echo -e "${LIGHTRED}Lastly you can run the script with the -update argument to install the game.${NC}"
echo -e "${LIGHTRED}Your server configuration file and SSK should be put in $SRV_DIR/drive_c/users/$USER/Application Data/InterstellarRift/${NC}"
echo -e "${LIGHTRED}When all of this is done copy/move this script to $SCRIPT_DIR ${NC}"
echo ""
echo -e "${LIGHTRED}After that enable the correct service with: systemctl --user enable $SERVICE_NAME.service or"
echo -e "${LIGHTRED}systemctl --user enable $SERVICE_NAME-tmpfs.service for the RamDisk variant (USE ONLY ONE!)${NC}"
echo -e "${LIGHTRED}Now start the service with systemctl --user start $SERVICE_NAME.service or systemctl --user start $SERVICE_NAME-tmpfs.service${NC}"
echo ""
echo -e "${LIGHTRED}Example usage: ./$SCRIPT_NAME -start${NC}"
echo ""
echo -e "${CYAN}Have a nice day!${NC}"
echo ""
;;
-start)
script_start
;;
-stop)
script_stop
;;
-restart)
script_restart
;;
-save)
script_save
;;
-sync)
script_sync
;;
-backup)
script_backup
;;
-autobackup)
script_autobackup
;;
-deloldbackup)
script_deloldbackup
;;
-autorestart)
script_autorestart
;;
-update)
script_update
;;
-status)
script_status
;;
-install)
script_install
;;
-install_services)
script_install_services
;;
-install-prefix)
script_install_wine_prefix
;;
-crash_kill)
script_crash_kill
;;
-timer_one)
script_timer_one
;;
-timer_two)
script_timer_two
;;
*)
echo "Usage: $0 {start|stop|restart|save|sync|backup|autobackup|deloldbackup|autorestart|update|status|install|install_services|install-prefix \"server command\"}"
exit 1
;;
esac
exit 0
The script is a heavily based systemd workflow. The function in question is the save function. There is a while statement in it that I tried myself but dosen't work so I commented it out. The server program constsntly runs so there is no exit status when the save command is ran and when it's finished.
Last edited by 7thCore (2019-06-16 10:31:22)
[ Arch x86_64 | linux | Asus Prime X570-Pro | AMD Ryzen 9 5900X @4,8Ghz | AMD RX580 | 32GB RAM DDR4 | Main, 2 Monitors ]
[ Arch x86_64 | linux | Asus Pro WS X570-Ace | AMD Ryzen 9 5950X @4,9Ghz | Intel A750 | 128GB RAM DDR4 | Server ]
The Linux philosophy is 'Laugh in the face of danger'. Oops. Wrong One. 'Do it yourself'. Yes, that's it.
Offline
The script is a heavily based systemd workflow.
No, it isn't, and that's likely a source of the problem. It (ab)uses systemd features, but also goes a long way to not do things the systemd way and reinvents countless many wheels.
I'd say simplify first:
For example, while comments are generally a good idea, they need to *add* information, not just clutter. If you use good variable names and function names, comments can focus on logic rather than labeling. You tend to have good descriptive variable names - though there is absolutely no reason for them to be all caps. But here is a line that shows a particularly useless comment:
SCRIPT_NAME="$SERVICE_NAME-script.bash" #Script name
The comment tells us that the variable SCRIPT_NAME is the "Script name". That adds nothing but clutter.
Your function names, in contrast, are not so meaningful, and the comments don't help, but just make it worse. E.g:
#Sync server files from ramdisk to hdd/ssd
script_rsyncfromtmpfs() {
...
And then a bit later a completely different function:
#Sync server files from ramdisk to hdd/ssd
script_sync() {
...
How could one reading this code have any idea what either of these are actually designed to do?
Lastly, a vast majority of this code is to create some log file. But you already have a journal if you are using systemd services.
Anyhow, the likely direct cause of your problems is as Jason already suggested. Add a 'sync' command at the end of the functions that move things between tmpfs and the hdd.
Last edited by Trilby (2019-06-16 12:42:51)
"UNIX is simple and coherent..." - Dennis Ritchie, "GNU's Not UNIX" - Richard Stallman
Offline
It would seem the
screen -p 0 -S $NAME -X eval 'stuff "/save"\\015'
triggers some async action, thus the commented while loop and whatever that is needs to finish before you start the rsync.
Blocking until a *very* specific line shows up in the logs is a wonky idea, what if the save fails or for other reasons doesn't provide that log entry?
There's a lot of "server" mumbo-jumbo, but the screen_log seems to be a local file?
What's the actual layout here? Do you have access/control to/over the "server" processes?
Online
It's a game. A game in dedicated server mode running in wine on my private arch server at home. The script is there to save the files, sync them to the hdd and back them up. Plus some other mumbo jumbo. And Yes I have complete access.
Last edited by 7thCore (2019-06-16 14:43:30)
[ Arch x86_64 | linux | Asus Prime X570-Pro | AMD Ryzen 9 5900X @4,8Ghz | AMD RX580 | 32GB RAM DDR4 | Main, 2 Monitors ]
[ Arch x86_64 | linux | Asus Pro WS X570-Ace | AMD Ryzen 9 5950X @4,9Ghz | Intel A750 | 128GB RAM DDR4 | Server ]
The Linux philosophy is 'Laugh in the face of danger'. Oops. Wrong One. 'Do it yourself'. Yes, that's it.
Offline
That doesn't explain anything ( and the game was given away in the script :-P )
Online
Blocking until a *very* specific line shows up in the logs is a wonky idea, what if the save fails or for other reasons doesn't provide that log entry?
Is there a way to time out a while loop maybe?
Or maybe I could ask one of the devs if there is there is another specific line that says the server failed to save the world?
There's a lot of "server" mumbo-jumbo, but the screen_log seems to be a local file?
What's the actual layout here? Do you have access/control to/over the "server" processes?
The screen log is a local file on the server.
The layout (if I corectly understand what you mean) is as follows:
The systemd service start the server on boot and shuts it down gracefully on shutdown (a ups makes sure of that if the power fails wich tends to happens a lot in my town). It also copies the wine prefix to tmpfs before stsrting and syncs them back on shutdown. The service also changes the SCRIPT_ENABLED variable to 1 so the script can execute commands, otherwise the systemd timers will execute the script and the script will just exit. Every 15 minutes the script checks if the service failed (the screen session terminates if the service crashes and exits) and restarts the service, also checks if the AlunaCrashHandler.exe is running because the IR.exe server starts another instance of itself as a ghost client and connects to the server. Sometimes it crashes but after the crash handler does it's thing it restarts. However on Linux the crash handler freezes so I have the script set up to kill it and after that another instance of the ghost client pops up by itself. It also sends the save command to save the world files and then rsync syncs them to the hdd. Every 6 hours the InterstellarRift folder in wine's Application Data folder (where the world files and server configuration is stored) gets backed up. After that it issues the update command to auto update the game server if an update is available.
Then repeat.
That is basicly the workflow of the script.
If this is not the answer to the question you ware asking, im sorry but english is not my first language so sometimes I don't understand some things people ask/require of me.
Edit:
SCRIPT_NAME="$SERVICE_NAME-script.bash" #Script name
This is just there for myself. Yes it is clutter but if I want to adapt the script for another game/app I don't have to go through the script and change everything.
These two functions will be combined into one because they're basicly the same thing. One only calls the other and has more echo commands in it.
#Sync server files from ramdisk to hdd/ssd
script_rsyncfromtmpfs() {
...
#Sync server files from ramdisk to hdd/ssd
script_sync() {
...
Last edited by 7thCore (2019-06-16 17:00:20)
[ Arch x86_64 | linux | Asus Prime X570-Pro | AMD Ryzen 9 5900X @4,8Ghz | AMD RX580 | 32GB RAM DDR4 | Main, 2 Monitors ]
[ Arch x86_64 | linux | Asus Pro WS X570-Ace | AMD Ryzen 9 5950X @4,9Ghz | Intel A750 | 128GB RAM DDR4 | Server ]
The Linux philosophy is 'Laugh in the face of danger'. Oops. Wrong One. 'Do it yourself'. Yes, that's it.
Offline
Ok, the "server" is basically the "dedicated game server", ie. a local daemon, you talk to it through input and it responds (eg. by saving data) but it never actually terminates, correct? (everything else about the daenin crashing etc. is irrelevant here)
The while loop checking the last line of the log in a 1s interval is risky, because you might easily miss the relevant one if there's a message burst.
You rather want to do sth. like
while read line; do if [ "$line" = "…" ]; then foo; bar; break; fi; done < <(tail -n1 -f /path/to/log )
If you need to dogwatch that because there's no reliable way to know whether or not the dameon responded and wrote some log about it, things get more complicated because you'll have to fork that loop, fork a timered kill to that subshell and ultimately "wait" (man wait) for the subshell to terminate (which would happen either because it succeeds or the kill job kicks in)
Online
Ok, the "server" is basically the "dedicated game server", ie. a local daemon, you talk to it through input and it responds (eg. by saving data) but it never actually terminates, correct? (everything else about the daenin crashing etc. is irrelevant here)
You are correct.
The while loop checking the last line of the log in a 1s interval is risky, because you might easily miss the relevant one if there's a message burst.
You rather want to do sth. likewhile read line; do if [ "$line" = "…" ]; then foo; bar; break; fi; done < <(tail -n1 -f /path/to/log )
Here is how i changed the save function:
script_save() {
if [[ "$(systemctl --user show -p ActiveState --value $SERVICE)" != "active" ]]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Save) Server is not running." | tee -a "$SCRIPT_LOG"
elif [[ "$(systemctl --user show -p ActiveState --value $SERVICE)" == "active" ]]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Save) Save game to disk has been initiated." | tee -a "$SCRIPT_LOG"
screen -p 0 -S $NAME -X eval 'stuff "save"\\015'
while read line; do
if [ "$line" = "[Server]: Save completed." ]; then
echo "$(date +"%Y-%m-%d %H:%M:%S") [$NAME] [INFO] (Save) Save game to disk has been completed." | tee -a "$SCRIPT_LOG"
break
fi
done < <(tail -n1 -f $SCREEN_LOG)
fi
}
But for some reason this dosen't work. It keeps listening even though the server printed out the correct text.
Here is the text from the server console:
save <-- the command the script is sending
[Server]: Server is saving...
Updating player save data...
Saving systems...
Start saving all systems...
End saving all systems...
Saving player client data...
Saving players...
Saving fleets...
Saving crews...
Saving galaxy updatables...
Saving metadata...
[Server]: Save completed.
If i use the same function to check a txt file and while it's running edit the txt file to have the correct string, then the function does work.
Edit: if i cat the screen log there are no spaces but if i edit it with nano the screen log has spaces between the lines.
save
[Server]: Server is saving...
Updating player save data...
Saving systems...
Start saving all systems...
End saving all systems...
Saving player client data...
Saving players...
Saving fleets...
Saving crews...
Saving galaxy updatables...
Saving metadata...
[Server]: Save completed.
Last edited by 7thCore (2019-06-17 11:21:00)
[ Arch x86_64 | linux | Asus Prime X570-Pro | AMD Ryzen 9 5900X @4,8Ghz | AMD RX580 | 32GB RAM DDR4 | Main, 2 Monitors ]
[ Arch x86_64 | linux | Asus Pro WS X570-Ace | AMD Ryzen 9 5950X @4,9Ghz | Intel A750 | 128GB RAM DDR4 | Server ]
The Linux philosophy is 'Laugh in the face of danger'. Oops. Wrong One. 'Do it yourself'. Yes, that's it.
Offline
Does the loop read the line? Does the match hit?
The output likely contains some silly CRLF, try
if [[ $line =~ "foobar" ]]
for a substring match.
Online
Does the loop read the line? Does the match hit?
The output likely contains some silly CRLF, tryif [[ $line =~ "foobar" ]]
for a substring match.
I tried:
echo "$line"
in the loop to see if the loop works and it did. However the match didn't hit even if it looked the same.
However now I tried your suggestion:
if [[ $line =~ "foobar" ]]
And this works!
Last edited by 7thCore (2019-06-17 12:20:22)
[ Arch x86_64 | linux | Asus Prime X570-Pro | AMD Ryzen 9 5900X @4,8Ghz | AMD RX580 | 32GB RAM DDR4 | Main, 2 Monitors ]
[ Arch x86_64 | linux | Asus Pro WS X570-Ace | AMD Ryzen 9 5950X @4,9Ghz | Intel A750 | 128GB RAM DDR4 | Server ]
The Linux philosophy is 'Laugh in the face of danger'. Oops. Wrong One. 'Do it yourself'. Yes, that's it.
Offline
Probably due to the mess that is https://en.wikipedia.org/wiki/Newline - ie. there's probably an \r at the end of the line.
Please always remember to mark resolved threads by editing your initial posts subject - so others will know that there's no task left, but maybe a solution to find.
Thanks.
Online
It's probably something like that yeah.
Thanks for all your effort and patience seth.
[ Arch x86_64 | linux | Asus Prime X570-Pro | AMD Ryzen 9 5900X @4,8Ghz | AMD RX580 | 32GB RAM DDR4 | Main, 2 Monitors ]
[ Arch x86_64 | linux | Asus Pro WS X570-Ace | AMD Ryzen 9 5950X @4,9Ghz | Intel A750 | 128GB RAM DDR4 | Server ]
The Linux philosophy is 'Laugh in the face of danger'. Oops. Wrong One. 'Do it yourself'. Yes, that's it.
Offline