|
@@ -0,0 +1,423 @@
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+MCDIR="/opt/minecraft"
|
|
|
+BACKUPDIR="${MCDIR}/backups"
|
|
|
+
|
|
|
+
|
|
|
+LOGFILE="${MCDIR}/backup.log"
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ONDEMANDDIR=${BACKUPDIR}
|
|
|
+HOURLYDIR=${BACKUPDIR}/hourly
|
|
|
+DAILYDIR=${BACKUPDIR}/daily
|
|
|
+WEEKLYDIR=${BACKUPDIR}/weekly
|
|
|
+MONTHLYDIR=${BACKUPDIR}/monthly
|
|
|
+
|
|
|
+
|
|
|
+HOURLY=true
|
|
|
+DAILY=true
|
|
|
+WEEKLY=true
|
|
|
+MONTHLY=false
|
|
|
+
|
|
|
+
|
|
|
+RETAINHOURS=24
|
|
|
+RETAINDAYS=7
|
|
|
+RETAINWEEKS=5
|
|
|
+RETAINMONTHS=12
|
|
|
+
|
|
|
+
|
|
|
+MCSCREENNAME="minecraft"
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+mcsend() {
|
|
|
+
|
|
|
+ if mcrunning; then
|
|
|
+ tmux send-keys -t $MCSCREENNAME "$1" ENTER
|
|
|
+ fi
|
|
|
+}
|
|
|
+
|
|
|
+mcsay() {
|
|
|
+
|
|
|
+ mcsend "say [§3Backup§r] $1"
|
|
|
+}
|
|
|
+
|
|
|
+logmsg() {
|
|
|
+
|
|
|
+ if [ "$LOGFILE" = false ]; then
|
|
|
+ return
|
|
|
+ fi
|
|
|
+
|
|
|
+
|
|
|
+ if [ -n "$1" ]; then
|
|
|
+ IN="$1"
|
|
|
+ else
|
|
|
+ read IN
|
|
|
+ fi
|
|
|
+
|
|
|
+ if [ -n "$IN" ]; then
|
|
|
+ echo "`date +"%Y-%m-%d %H:%M:%S"` mcbackup[$$]: $IN" >> $LOGFILE
|
|
|
+ fi
|
|
|
+}
|
|
|
+
|
|
|
+enablesave() {
|
|
|
+
|
|
|
+ chmod -R u+w $MCDIR/world/playerdata
|
|
|
+ chmod -R u+w $MCDIR/world/stats
|
|
|
+ mcsend "save-on"
|
|
|
+}
|
|
|
+
|
|
|
+err() {
|
|
|
+
|
|
|
+ logmsg "[ERROR] $1"
|
|
|
+ mcsay "§cBackup §cfailure"
|
|
|
+ exit 1
|
|
|
+}
|
|
|
+
|
|
|
+fileage() {
|
|
|
+
|
|
|
+
|
|
|
+ find $1 -maxdepth 1 -name $(ls -t $1 | grep -G "World_.*\.tar\.gz" | head -1) -printf $2
|
|
|
+}
|
|
|
+
|
|
|
+hasfile() {
|
|
|
+
|
|
|
+ if [ $(numfiles $1) != 0 ]; then
|
|
|
+ true
|
|
|
+ else
|
|
|
+ false
|
|
|
+ fi
|
|
|
+}
|
|
|
+
|
|
|
+numfiles() {
|
|
|
+
|
|
|
+ ls $1 | grep -G "World_.*\.tar\.gz" | wc -l
|
|
|
+}
|
|
|
+
|
|
|
+PURGEFAIL=false
|
|
|
+purgefiles() {
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ FILESINDIR=$(numfiles $1)
|
|
|
+ if [ $2 -gt 0 ] && [ $FILESINDIR -gt $2 ]; then
|
|
|
+ NUMTOPURGE=$(($FILESINDIR - $2))
|
|
|
+ logmsg "Purging ${NUMTOPURGE} backup(s) from ${1}."
|
|
|
+
|
|
|
+
|
|
|
+ FILENUM=0
|
|
|
+ while [ $FILENUM -lt $NUMTOPURGE ]
|
|
|
+ do
|
|
|
+ FILENUM=$(($FILENUM + 1))
|
|
|
+ FILE= read -rd $'\0' line < <(
|
|
|
+ find $1 -maxdepth 1 -type f -printf '%T@ %p\0' 2>/dev/null |
|
|
|
+ grep -ZzG "World_.*\.tar\.gz" |
|
|
|
+ sort -zn
|
|
|
+ )
|
|
|
+ TOPURGE="${line#* }"
|
|
|
+ logmsg "Purging backup file ${TOPURGE}"
|
|
|
+ if ! $(rm ${TOPURGE} 2>&1 | logmsg ; test ${PIPESTATUS[0]} -eq 0); then
|
|
|
+ PURGEFAIL=true
|
|
|
+ logmsg "[WARNING] Failed to purge a backup; stopping purge."
|
|
|
+ break;
|
|
|
+ fi
|
|
|
+ done
|
|
|
+ fi
|
|
|
+}
|
|
|
+
|
|
|
+mcrunning() {
|
|
|
+ $(pidof minecraft &>/dev/null)
|
|
|
+ ismcrunning=$?
|
|
|
+
|
|
|
+ if $(tmux ls | grep -q "$MCSCREENNAME") && [ ismcrunning = 0 ]; then
|
|
|
+ return 1
|
|
|
+ else
|
|
|
+ return 0
|
|
|
+ fi
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+mcrunning
|
|
|
+if mcrunning; then
|
|
|
+ :
|
|
|
+else
|
|
|
+ logmsg "WARN: Minecraft is not running or is inaccessible; not sending commands to console."
|
|
|
+fi
|
|
|
+
|
|
|
+logmsg "Backup started"
|
|
|
+
|
|
|
+
|
|
|
+if [ "$1" == "-s" ]; then
|
|
|
+ SCHEDULE=true
|
|
|
+else
|
|
|
+ SCHEDULE=false
|
|
|
+fi
|
|
|
+
|
|
|
+
|
|
|
+STARTDATE=$(date +"%Y-%m-%d %H:%M:%S")
|
|
|
+FILEPREFIX="World_$(date +"%Y-%m-%d_%H.%M.%S" --date="$STARTDATE")"
|
|
|
+
|
|
|
+
|
|
|
+if [ "$SCHEDULE" = true ]; then
|
|
|
+ DOBACKUP=false
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ if ([ "$HOURLY" = true ] && (
|
|
|
+ [ ! -d $HOURLYDIR ] ||
|
|
|
+ ((! $(hasfile $HOURLYDIR)) ||
|
|
|
+ [ $(fileage $HOURLYDIR "%TY") != $(date +"%Y" --date="$STARTDATE") ] ||
|
|
|
+ [ $(fileage $HOURLYDIR "%Tj") != $(date +"%j" --date="$STARTDATE") ] ||
|
|
|
+ [ $(fileage $HOURLYDIR "%TH") != $(date +"%H" --date="$STARTDATE") ])
|
|
|
+ )); then
|
|
|
+ DOBACKUP=true
|
|
|
+ else
|
|
|
+ HOURLY=false
|
|
|
+ fi
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ if ([ "$DAILY" = true ] && (
|
|
|
+ [ ! -d $DAILYDIR ] ||
|
|
|
+ ((! $(hasfile $DAILYDIR)) ||
|
|
|
+ [ $(fileage $DAILYDIR "%TY") != $(date +"%Y" --date="$STARTDATE") ] ||
|
|
|
+ [ $(fileage $DAILYDIR "%Tj") != $(date +"%j" --date="$STARTDATE") ])
|
|
|
+ )); then
|
|
|
+ DOBACKUP=true
|
|
|
+ else
|
|
|
+ DAILY=false
|
|
|
+ fi
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ if ([ "$WEEKLY" = true ] && (
|
|
|
+ [ ! -d $WEEKLYDIR ] ||
|
|
|
+ ((! $(hasfile $WEEKLYDIR)) ||
|
|
|
+ [ $(fileage $WEEKLYDIR "%TY") != $(date +"%Y" --date="$STARTDATE") ] ||
|
|
|
+ [ $(fileage $WEEKLYDIR "%TW") != $(date +"%W" --date="$STARTDATE") ])
|
|
|
+ )); then
|
|
|
+ DOBACKUP=true
|
|
|
+ else
|
|
|
+ WEEKLY=false
|
|
|
+ fi
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ if ([ "$MONTHLY" = true ] && (
|
|
|
+ [ ! -d $MONTHLYDIR ] ||
|
|
|
+ ((! $(hasfile $MONTHLYDIR)) ||
|
|
|
+ [ $(fileage $MONTHLYDIR "%TY") != $(date +"%Y" --date="$STARTDATE") ] ||
|
|
|
+ [ $(fileage $MONTHLYDIR "%Tm") != $(date +"%m" --date="$STARTDATE") ])
|
|
|
+ )); then
|
|
|
+ DOBACKUP=true
|
|
|
+ else
|
|
|
+ MONTHLY=false
|
|
|
+ fi
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ if [ "$DOBACKUP" = false ]; then
|
|
|
+ logmsg "Scheduled backups already up to date; aborting."
|
|
|
+ exit 0
|
|
|
+ fi
|
|
|
+fi
|
|
|
+
|
|
|
+
|
|
|
+if [ ! -d "$BACKUPDIR" ]; then
|
|
|
+ mkdir "$BACKUPDIR"
|
|
|
+fi
|
|
|
+
|
|
|
+
|
|
|
+mcsay "Backup started."
|
|
|
+mcsend "save-off"
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+chmod -R u-w $MCDIR/world/playerdata
|
|
|
+chmod -R u-w $MCDIR/world/stats
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+TEMPFILE=$BACKUPDIR/.mcbackup.tar
|
|
|
+
|
|
|
+if ! $(tar --mode="a+rw" -cf $TEMPFILE -C $MCDIR world 2>&1 | logmsg ; test ${PIPESTATUS[0]} -eq 0); then
|
|
|
+ enablesave
|
|
|
+ rm $TEMPFILE 2>/dev/null
|
|
|
+ err "Unable to generate tar file. Aborting."
|
|
|
+fi
|
|
|
+
|
|
|
+
|
|
|
+enablesave
|
|
|
+
|
|
|
+
|
|
|
+SUMFILE=$MCDIR/backup.md5
|
|
|
+if md5sum --status -c $SUMFILE 2>/dev/null; then
|
|
|
+ NOCHANGE=true
|
|
|
+else
|
|
|
+ NOCHANGE=false
|
|
|
+ md5sum $TEMPFILE > $SUMFILE
|
|
|
+ if ! $(gzip -fq $TEMPFILE 2>&1 | logmsg ; test ${PIPESTATUS[0]} -eq 0); then
|
|
|
+ rm $TEMPFILE 2>/dev/null
|
|
|
+ err "Unable to generate gzip file. Aborting."
|
|
|
+ fi
|
|
|
+ TEMPFILE=${TEMPFILE}.gz
|
|
|
+fi
|
|
|
+
|
|
|
+
|
|
|
+BACKUPRUN=false
|
|
|
+BACKUPFAIL=false
|
|
|
+if [ "$NOCHANGE" = false ]; then
|
|
|
+
|
|
|
+ if [ "$SCHEDULE" = true ]; then
|
|
|
+
|
|
|
+ if [ "$HOURLY" = true ]; then
|
|
|
+
|
|
|
+ if [ ! -d $HOURLYDIR ]; then
|
|
|
+ mkdir -p $HOURLYDIR
|
|
|
+ fi
|
|
|
+
|
|
|
+
|
|
|
+ if $(
|
|
|
+ ln $TEMPFILE $HOURLYDIR/${FILEPREFIX}.tar.gz 2>&1 |
|
|
|
+ logmsg;
|
|
|
+ test ${PIPESTATUS[0]} -eq 0
|
|
|
+ ); then
|
|
|
+ logmsg "Performed hourly backup"
|
|
|
+ BACKUPRUN=true
|
|
|
+
|
|
|
+
|
|
|
+ purgefiles $HOURLYDIR $RETAINHOURS
|
|
|
+ else
|
|
|
+ logmsg "[WARNING] Failed to complete hourly backup"
|
|
|
+ BACKUPFAIL=true
|
|
|
+ fi
|
|
|
+ fi
|
|
|
+
|
|
|
+
|
|
|
+ if [ "$DAILY" = true ]; then
|
|
|
+
|
|
|
+ if [ ! -d $DAILYDIR ]; then
|
|
|
+ mkdir -p $DAILYDIR
|
|
|
+ fi
|
|
|
+
|
|
|
+
|
|
|
+ if $(
|
|
|
+ ln $TEMPFILE $DAILYDIR/${FILEPREFIX}.tar.gz 2>&1 |
|
|
|
+ logmsg;
|
|
|
+ test ${PIPESTATUS[0]} -eq 0
|
|
|
+ ); then
|
|
|
+ logmsg "Performed daily backup"
|
|
|
+ BACKUPRUN=true
|
|
|
+
|
|
|
+
|
|
|
+ purgefiles $DAILYDIR $RETAINDAYS
|
|
|
+ else
|
|
|
+ logmsg "[WARNING] Failed to complete daily backup"
|
|
|
+ BACKUPFAIL=true
|
|
|
+ fi
|
|
|
+ fi
|
|
|
+
|
|
|
+
|
|
|
+ if [ "$WEEKLY" = true ]; then
|
|
|
+
|
|
|
+ if [ ! -d $WEEKLYDIR ]; then
|
|
|
+ mkdir -p $WEEKLYDIR
|
|
|
+ fi
|
|
|
+
|
|
|
+
|
|
|
+ if $(
|
|
|
+ ln $TEMPFILE $WEEKLYDIR/${FILEPREFIX}.tar.gz 2>&1 |
|
|
|
+ logmsg;
|
|
|
+ test ${PIPESTATUS[0]} -eq 0
|
|
|
+ ); then
|
|
|
+ logmsg "Performed weekly backup"
|
|
|
+ BACKUPRUN=true
|
|
|
+
|
|
|
+
|
|
|
+ purgefiles $WEEKLYDIR $RETAINWEEKS
|
|
|
+ else
|
|
|
+ logmsg "[WARNING] Failed to complete weekly backup"
|
|
|
+ BACKUPFAIL=true
|
|
|
+ fi
|
|
|
+ fi
|
|
|
+
|
|
|
+
|
|
|
+ if [ "$MONTHLY" = true ]; then
|
|
|
+
|
|
|
+ if [ ! -d $MONTHLYDIR ]; then
|
|
|
+ mkdir -p $MONTHLYDIR
|
|
|
+ fi
|
|
|
+
|
|
|
+
|
|
|
+ if $(
|
|
|
+ ln $TEMPFILE $MONTHLYDIR/${FILEPREFIX}.tar.gz 2>&1 |
|
|
|
+ logmsg;
|
|
|
+ test ${PIPESTATUS[0]} -eq 0
|
|
|
+ ); then
|
|
|
+ logmsg "Performed monthly backup"
|
|
|
+ BACKUPRUN=true
|
|
|
+
|
|
|
+
|
|
|
+ purgefiles $MONTHLYDIR $RETAINWEEKS
|
|
|
+ else
|
|
|
+ logmsg "[WARNING] Failed to complete monthly backup"
|
|
|
+ BACKUPFAIL=true
|
|
|
+ fi
|
|
|
+ fi
|
|
|
+
|
|
|
+ else
|
|
|
+
|
|
|
+ logmsg "Performed backup on demand"
|
|
|
+ ln $TEMPFILE $BACKUPDIR/${FILEPREFIX}.tar.gz
|
|
|
+ BACKUPRUN=true
|
|
|
+ fi
|
|
|
+
|
|
|
+
|
|
|
+ if [ "$BACKUPFAIL" = false ]; then
|
|
|
+ if [ "$BACKUPRUN" = true ]; then
|
|
|
+ rm $BACKUPDIR/latest.tar.gz 2>/dev/null
|
|
|
+ ln $TEMPFILE $BACKUPDIR/latest.tar.gz
|
|
|
+ logmsg "Backup completed successfully"
|
|
|
+ else
|
|
|
+ logmsg "Scheduled backups are already up to date"
|
|
|
+ fi
|
|
|
+ fi
|
|
|
+else
|
|
|
+ logmsg "No change was detected in the world file; backup stopped"
|
|
|
+fi
|
|
|
+
|
|
|
+
|
|
|
+rm $TEMPFILE
|
|
|
+
|
|
|
+
|
|
|
+if [ "$PURGEFAIL" = true ]; then
|
|
|
+ mcsay "§cPurge §cfailure §c- §ccheck §clog §cfile"
|
|
|
+fi
|
|
|
+
|
|
|
+
|
|
|
+if [ "$BACKUPFAIL" = false ]; then
|
|
|
+ mcsay "Backup complete."
|
|
|
+else
|
|
|
+ err "Unable to complete all backups."
|
|
|
+fi
|