123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423 |
- #!/bin/bash
- 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
|