#!/bin/sh ######################################################################### # Plex Media Server database check and repair utility script. # # Maintainer: ChuckPa # ######################################################################### # Flag when temp files are to be retained Retain=0 # Have the databases passed integrity checks CheckedDB=0 # Universal output function Output() { echo "$@" $LOG_TOOL "$@" } # Write to Repair Tool log WriteLog() { # Write given message into tool log file with TimeStamp echo "$(date "+%Y-%m-%d %H.%M.%S") - $*" >> "$LOGFILE" return 0 } # Check given database file integrity CheckDB() { # Confirm the DB exists [ ! -f "$1" ] && Output "ERROR: $1 does not exist." && return 1 # Now check database for corruption Result="$("$PLEX_SQLITE" "$1" "PRAGMA integrity_check(1)")" if [ "$Result" = "ok" ]; then return 0 else SQLerror="$(echo $Result | sed -e 's/.*code //')" return 1 fi } # Check all databases CheckDatabases() { # Arg1 = calling function # Arg2 = 'force' if present # Check each of the databases. If all pass, set the 'CheckedDB' flag # Only force recheck if flag given # Check if not checked or forced NeedCheck=0 [ $CheckedDB -eq 0 ] && NeedCheck=1 [ $CheckedDB -eq 1 ] && [ "$2" = "force" ] && NeedCheck=1 # Do we need to check if [ $NeedCheck -eq 1 ]; then # Clear Damaged flag Damaged=0 CheckedDB=0 # Info Output "Checking the PMS databases" # Check main DB if CheckDB $CPPL.db ; then Output "Check complete. PMS main database is OK." WriteLog "$1"" - Check $CPPL.db - PASS" else Output "Check complete. PMS main database is damaged." WriteLog "$1"" - Check $CPPL.db - FAIL ($SQLerror)" Damaged=1 fi # Check blobs DB if CheckDB $CPPL.blobs.db ; then Output "Check complete. PMS blobs database is OK." WriteLog "$1"" - Check $CPPL.blobs.db - PASS" else Output "Check complete. PMS blobs database is damaged." WriteLog "$1"" - Check $CPPL.blobs.db - FAIL ($SQLerror)" Damaged=1 fi fi [ $Damaged -eq 0 ] && CheckedDB=1 # return status return $Damaged } # Return list of database backup dates for consideration in replace action GetDates(){ Dates="" Tempfile="/tmp/DBRepairTool.$$.tmp" touch "$Tempfile" for i in $(find . -name 'com.plexapp.plugins.library.db-????-??-??' | sort -r) do # echo Date - "${i//[^.]*db-/}" Date="$(echo $i | sed -e 's/.*.db-//')" # Only add if companion blobs DB exists [ -e $CPPL.blobs.db-$Date ] && echo $Date >> "$Tempfile" done # Reload dates in sorted order Dates="$(sort -r <$Tempfile)" # Remove tempfile rm -f "$Tempfile" # Give results echo $Dates return } # Non-fatal SQLite error code check SQLiteOK() { # Global error variable SQLerror=0 # Quick exit- known OK [ $1 -eq 0 ] && return 0 # Put list of acceptable error codes here Codes="19 28" # By default assume the given code is an error CodeError=1 for i in $Codes do if [ $i -eq $1 ]; then CodeError=0 SQLerror=$i break fi done return $CodeError } # Perform the actual copying for MakeBackup() DoBackup() { if [ -e $2 ]; then cp -p "$2" "$3" Result=$? if [ $Result -ne 0 ]; then Output "Error $Result while backing up '$2'. Cannot continue." WriteLog "$1 - MakeBackup $2 - FAIL" # Remove partial copied file and return rm -f "$3" return 1 else WriteLog "$1 - MakeBackup $2 - PASS" return 0 fi fi } # Make a backup of the current database files and tag with TimeStamp MakeBackups() { Output "Backup current databases with '-ORIG-$TimeStamp'" for i in "db" "db-wal" "db-shm" "blobs.db" "blobs.db-wal" "blobs.db-shm" do DoBackup "$1" "${CPPL}.${i}" "$DBTMP/${CPPL}.${i}-ORIG-$TimeStamp" Result=$? done return $Result } ConfirmYesNo() { Answer="" while [ "$Answer" = "" ] do echo -n "$1 (Y/N) ? " read Input # EOF = No [ "$Input" = "" ] && Answer=N ; [ "$Input" = "n" ] && Answer=N ; [ "$Input" = "N" ] && Answer=N [ "$Input" = "y" ] && Answer=Y ; [ "$Input" = "Y" ] && Answer=Y # Unrecognized if [ "$Answer" != "Y" ] && [ "$Answer" != "N" ]; then echo "$Input" was not a valid reply. Please try again. continue fi done # If no, done. if [ "$Answer" = "N" ]; then return 1 fi # User said Yes. Be 100% certain Answer="" while [ "$Answer" = "" ] do echo -n "Are you sure (Y/N) ? " read Input # EOF = No [ "$Input" = "" ] && Answer=N ; [ "$Input" = "n" ] && Answer=N ; [ "$Input" = "N" ] && Answer=N [ "$Input" = "y" ] && Answer=Y ; [ "$Input" = "Y" ] && Answer=Y # Unrecognized if [ "$Answer" != "Y" ] && [ "$Answer" != "N" ]; then echo "$Input" was not a valid reply. Please try again. continue fi done if [ "$Answer" = "Y" ]; then # Confirmed Yes return 0 else return 1 fi } # Restore previously saved DB from given TimeStamp RestoreSaved() { T="$1" for i in "db" "db-wal" "db-shm" "blobs.db" "blobs.db-wal" "blobs.db-shm" do [ -e "${CPPL}.${i}" ] && rm -f "${CPPL}.${i}" [ -e "$DBTMP/${CPPL}.${i}-ORIG-$T" ] && mv "$DBTMP/${CPPL}.${i}-ORIG-$T" "${CPPL}.${i}" done } # Get the size of the given DB in MB GetSize() { Size=$(stat -c %s "$1") Size=$(expr $Size / 1048576) [ $Size -eq 0 ] && Size=1 echo $Size } # Determine which host we are running on and set variables HostConfig() { # Synology (DSM 7) if [ -d /var/packages/PlexMediaServer ] && \ [ -d "/var/packages/PlexMediaServer/shares/PlexMediaServer/AppData/Plex Media Server" ]; then # Where is the software PKGDIR="/var/packages/PlexMediaServer/target" PLEX_SQLITE="$PKGDIR/Plex SQLite" LOG_TOOL="logger" # Where is the data AppSuppDir="/var/packages/PlexMediaServer/shares/PlexMediaServer/AppData" DBDIR="$AppSuppDir/Plex Media Server/Plug-in Support/Databases" PID_FILE="$AppSuppDir/Plex Media Server/plexmediaserver.pid" LOGFILE="$DBDIR/DBRepair.log" # We are done HostType="Synology (DSM 7)" return 0 # Synology (DSM 6) elif [ -d "/var/packages/Plex Media Server" ] && \ [ -f "/usr/syno/sbin/synoshare" ]; then # Where is the software PKGDIR="/var/packages/Plex Media Server/target" PLEX_SQLITE="$PKGDIR/Plex SQLite" LOG_TOOL="logger" # Get shared folder path AppSuppDir="$(synoshare --get Plex | grep Path | awk -F\[ '{print $2}' | awk -F\] '{print $1}')" # Where is the data AppSuppDir="$AppSuppDir/Library/Application Support" if [ -d "$AppSuppDir/Plex Media Server" ]; then DBDIR="$AppSuppDir/Plex Media Server/Plug-in Support/Databases" PID_FILE="$AppSuppDir/Plex Media Server/plexmediaserver.pid" LOGFILE="$DBDIR/DBRepair.log" HostType="Synology (DSM 6)" return 0 fi # QNAP (QTS & QuTS) elif [ -f /etc/config/qpkg.conf ]; then # Where is the software PKGDIR="$(getcfg -f /etc/config/qpkg.conf PlexMediaServer Install_path)" PLEX_SQLITE="$PKGDIR/Plex SQLite" LOG_TOOL="/sbin/log_tool -t 0 -a" # Where is the data AppSuppDir="$PKGDIR/Library" DBDIR="$AppSuppDir/Plex Media Server/Plug-in Support/Databases" PID_FILE="$AppSuppDir/Plex Media Server/plexmediaserver.pid" LOGFILE="$DBDIR/DBRepair.log" HostType="QNAP" return 0 # Standard configuration Linux host elif [ -f /etc/os-release ] && \ [ -d /usr/lib/plexmediaserver ] && \ [ -d /var/lib/plexmediaserver ]; then # Where is the software PKGDIR="/usr/lib/plexmediaserver" PLEX_SQLITE="$PKGDIR/Plex SQLite" LOG_TOOL="logger" # Where is the data AppSuppDir="/var/lib/plexmediaserver/Library/Application Support" DBDIR="$AppSuppDir/Plex Media Server/Plug-in Support/Databases" PID_FILE="$AppSuppDir/Plex Media Server/plexmediaserver.pid" # Find the metadata dir if customized if [ -e /etc/systemd/system/plexmediaserver.service.d ]; then # Glob up all 'conf files' found NewSuppDir="$(cd /etc/systemd/system/plexmediaserver.service.d ; \ cat override.conf local.conf *.conf 2>/dev/null | grep "APPLICATION_SUPPORT_DIR" | head -1)" if [ "$NewSuppDir" != "" ]; then NewSuppDir="$(sed -e 's/.*_DIR=//' | tr -d '"' | tr -d "'")" if [ -d "$NewSuppDir" ]; then AppSuppDir="$NewSuppDir" else echo "Given application support directory override specified does not exist: '$NewSuppDir'". Ignoring. fi fi fi DBDIR="$AppSuppDir/Plex Media Server/Plug-in Support/Databases" PID_FILE="$AppSuppDir/Plex Media Server/plexmediaserver.pid" LOGFILE="$DBDIR/DBRepair.log" HostType="$(grep ^PRETTY_NAME= /etc/os-release | sed -e 's/PRETTY_NAME=//' | sed -e 's/"//g')" return 0 # Netgear ReadyNAS elif [ -e /etc/os-release ] && [ "$(cat /etc/os-release | grep ReadyNASOS)" != "" ]; then # Find PMS if [ "$(echo /apps/plexmediaserver*)" != "/apps/plexmediaserver*" ]; then PKGDIR="$(echo /apps/plexmediaserver*)" # Where is the code PLEX_SQLITE="$PKGDIR/Binaries/Plex SQLite" AppSuppDir="$PKGDIR/MediaLibrary" PID_FILE="$AppSuppDir/Plex Media Server/plexmediaserver.pid" DBDIR="$AppSuppDir/Plex Media Server/Plug-in Support/Databases" LOGFILE="$DBDIR/DBRepair.log" LOG_TOOL="logger" HostType="Netgear ReadyNAS" return 0 fi # ASUSTOR elif [ -f /etc/nas.conf ] && grep ASUSTOR /etc/nas.conf >/dev/null && \ [ -d "/volume1/Plex/Library/Plex Media Server" ]; then # Where are things PLEX_SQLITE="/volume1/.@plugins/AppCentral/plexmediaserver/Plex SQLite" AppSuppDir="/volume1/Plex/Library" PID_FILE="$AppSuppDir/Plex Media Server/plexmediaserver.pid" DBDIR="$AppSuppDir/Plex Media Server/Plug-in Support/Databases" LOGFILE="$DBDIR/DBRepair.log" LOG_TOOL="logger" HostType="ASUSTOR" return 0 # Docker elif [ -f /bin/s6-svscan ] && [ -d "/config/Library/Application Support" ]; then PLEX_SQLITE="/usr/lib/plexmediaserver/Plex SQLite" AppSuppDir="/config/Library/Application Support" PID_FILE="$AppSuppDir/Plex Media Server/plexmediaserver.pid" DBDIR="$AppSuppDir/Plex Media Server/Plug-in Support/Databases" LOGFILE="$DBDIR/DBRepair.log" LOG_TOOL="logger" HostType="Docker" return 0 # Western Digital (OS5) elif [ -f /etc/system.conf ] && [ -d /mnt/HD/HD_a2/Nas_Prog/plexmediaserver ] && \ grep "Western Digital Corp" /etc/system.conf >/dev/null; then # Where things are PLEX_SQLITE="/mnt/HD/HD_a2/Nas_Prog/plexmediaserver/binaries/Plex SQLite" AppSuppDir=/mnt/HD/HD*/Nas_Prog/plex_conf PID_FILE="$AppSuppDir/Plex Media Server/plexmediaserver.pid" DBDIR="$AppSuppDir/Plex Media Server/Plug-in Support/Databases" LOGFILE="$DBDIR/DBRepair.log" LOG_TOOL="logger" HostType="Western Digital" return 0 fi # Unknown / currently unsupported host return 1 } # Simple function to set variables SetLast() { LastName="$1" LastTimestamp="$2" return 0 } ############################################################# # Main utility begins here # ############################################################# # Global variable - main database CPPL=com.plexapp.plugins.library # Initial timestamp TimeStamp="$(date "+%Y-%m-%d_%H:%M:%S")" # Initialize LastName LastTimestamp SetLast "" "" # Identify this host HostType="" ; LOG_TOOL="echo" if ! HostConfig; then Output 'Error: Unknown host. Currently supported hosts are: QNAP, Synology, Netgear, ASUSTOR, WD (OS5) and Linux Workstation/Server' exit 1 fi echo " " # echo Detected Host: $HostType WriteLog "============================================================" WriteLog "Session start: Host is $HostType" # Make sure we have a logfile touch "$LOGFILE" # Basic checks; PMS installed if [ ! -f "$PLEX_SQLITE" ] ; then Output "PMS is not installed. Cannot continue. Exiting." WriteLog "Attempt to run utility without PMS installed." exit 1 fi # Can I write to the Databases directory ? if [ ! -w "$DBDIR" ]; then Output "ERROR: Cannot write to the Databases directory. Insufficient privilege or wrong UID. Exiting." exit 1 fi # Databases exist or Backups exist to restore from if [ ! -f "$DBDIR/$CPPL.db" ] && \ [ ! -f "$DBDIR/$CPPL.blobs.db" ] && \ [ "$(echo com.plexapp.plugins.*-????-??-??)" = "com.plexapp.plugins.*-????-??-??" ]; then Output "Cannot locate databases. Cannot continue. Exiting." WriteLog "No databases or backups found." exit 1 fi # Set tmp dir so we don't use RAM when in DBDIR DBTMP="./dbtmp" mkdir -p "$DBDIR/$DBTMP" export TMPDIR="$DBTMP" export TMP="$DBTMP" # Work in the Databases directory cd "$DBDIR" # Run entire utility in a loop until all arguments used, EOF on input, or commanded to exit while true do # Is PMS already running? if [ -f "$PID_FILE" ] && [ "$(pidof 'Plex Media Server')" != "" ] ; then Output "Plex Media Server is currently running, cannot continue." Output "Please stop Plex Media Server and restart this utility." WriteLog "PMS running. Could not continue." exit 1 fi # Main menu loop Choice=0; Exit=0 while [ $Choice -eq 0 ] do echo " " echo " " echo " (DEVELOPMENT) Plex Media Server Database Repair Utility ($HostType)" echo " " echo "Select" echo " " echo " 1. Check database" echo " 2. Vacuum database" echo " 3. Reindex database" echo " 4. Attempt database repair" echo " 5. Replace current database with newest usable backup copy" echo " 6. Undo last successful action (Vacuum, Reindex, Repair, or Replace)" echo " 7. Show logfile" echo " 8. Exit" echo " " echo -n "Enter choice: " if [ "$1" != "" ]; then Input="$1" echo "$1" shift else read Input # Handle EOF/forced exit [ "$Input" = "" ] && Input=8 && Exit=1 fi [ "$Input" = "1" ] && Choice=1 [ "$Input" = "2" ] && Choice=2 [ "$Input" = "3" ] && Choice=3 [ "$Input" = "4" ] && Choice=4 [ "$Input" = "5" ] && Choice=5 [ "$Input" = "6" ] && Choice=6 [ "$Input" = "7" ] && Choice=7 [ "$Input" = "8" ] && Choice=8 [ "$Choice" -eq 0 ] && echo " " && echo "'$Input' - Is invalid. Try again" # Update timestamp TimeStamp="$(date "+%Y-%m-%d_%H:%M:%S")" done # Spacing for legibility echo ' ' # 1. - Check database if [ $Choice -eq 1 ]; then # CHECK DBs if CheckDatabases "Check " force ; then WriteLog "Check - PASS" CheckedDB=1 else WriteLog "Check - FAIL" CheckedDB=0 fi # 2. Vacuum DB elif [ $Choice -eq 2 ]; then # Clear flags Fail=0 Damaged=0 # Check databases before Indexing if not previously checked if ! CheckDatabases "Vacuum " ; then Damaged=1 Fail=1 fi # If damaged, exit if [ $Damaged -eq 1 ]; then Output "Databases are damaged. Vacuum operation not available. Please repair or replace first." WriteLog "Vacuum - Databases damaged." continue fi # Make a backup Output "Backing up databases" if ! MakeBackups "Vacuum "; then Output "Error making backups. Cannot continue." WriteLog "Vacuum - MakeBackups - FAIL" Fail=1 continue else WriteLog "Vacuum - MakeBackups - PASS" fi # Start vacuuming Output "Vacuuming main database" SizeStart=$(GetSize $CPPL.db) # Vacuum it "$PLEX_SQLITE" $CPPL.db 'VACUUM;' Result=$? if SQLiteOK $Result; then SizeFinish=$(GetSize $CPPL.db) Output "Vacuuming main database successful (Size: ${SizeStart}MB/${SizeFinish}MB)." WriteLog "Vacuum - Vacuum main database - PASS (Size: ${SizeStart}MB/${SizeFinish}MB)." else Output "Vaccuming main database failed. Error code $Result from Plex SQLite" WriteLog "Vacuum - Vacuum main database - FAIL ($Result)" Fail=1 fi Output "Vacuuming blobs database" SizeStart=$(GetSize $CPPL.blobs.db) # Vacuum it "$PLEX_SQLITE" $CPPL.blobs.db 'VACUUM;' Result=$? if SQLiteOK $Result; then SizeFinish=$(GetSize $CPPL.blobs.db) Output "Vacuuming blobs database successful (Size: ${SizeStart}MB/${SizeFinish}MB)." WriteLog "Vacuum - Vacuum blobs database - PASS (Size: ${SizeStart}MB/${SizeFinish}MB)." else Output "Vaccuming blobs database failed. Error code $Result from Plex SQLite" WriteLog "Vacuum - Vacuum blobs database - FAIL ($Result)" Fail=1 fi if [ $Fail -eq 0 ]; then Output "Vacuum complete." WriteLog "Vacuum - PASS" SetLast "Vacuum" "$TimeStamp" else Output "Vacuum failed." WriteLog "Vacuum - FAIL" RestoreSaved "$TimeStamp" fi continue # 3. Reindex DB elif [ $Choice -eq 3 ]; then # Clear flag Damaged=0 Fail=0 # Check databases before Indexing if not previously checked if ! CheckDatabases "Reindex" ; then Damaged=1 Fail=1 fi # If damaged, exit if [ $Damaged -eq 1 ]; then Output "Databases are damaged. Reindex operation not available. Please repair or replace first." continue fi # Databases are OK, Make a backup Output "Backing up of databases" MakeBackups "Reindex" Result=$? if [ $Result -eq 0 ]; then WriteLog "Reindex - MakeBackup - PASS" else Output "Error making backups. Cannot continue." WriteLog "Reindex - MakeBackup - FAIL ($Result)" Fail=1 continue fi # Databases are OK, Start reindexing Output "Reindexing main database" "$PLEX_SQLITE" $CPPL.db 'REINDEX;' Result=$? if SQLiteOK $Result; then Output "Reindexing main database successful." WriteLog "Reindex - Reindex: $CPPL.db - PASS" else Output "Reindexing main database failed. Error code $Result from Plex SQLite" WriteLog "Reindex - Reindex: $CPPL.db - FAIL ($Result)" Fail=1 fi Output "Reindexing blobs database" "$PLEX_SQLITE" $CPPL.blobs.db 'REINDEX;' Result=$? if SQLiteOK $Result; then Output "Reindexing blobs database successful." WriteLog "Reindex - Reindex: $CPPL.blobs.db - PASS" else Output "Reindexing blobs database failed. Error code $Result from Plex SQLite" WriteLog "Reindex - Reindex: $CPPL.blobs.db - FAIL ($Result)" Fail=1 fi Output "Reindex complete." if [ $Fail -eq 0 ]; then SetLast "Reindex" "$TimeStamp" WriteLog "Reindex - PASS" else RestoreSaved "$TimeStamp" WriteLog "Reindex - FAIL" fi continue # 4. - Attempt DB repair elif [ $Choice -eq 4 ]; then Damaged=0 Fail=0 # Verify DBs are here if [ ! -e $CPPL.db ]; then Output "No main Plex database exists to repair. Exiting." WriteLog "Repair - No main database - FAIL" Fail=1 continue fi # Check size Size=$(stat -c '%s' $CPPL.db) # Exit if not valid if [ $Size -lt 300000 ]; then Output "Main database is too small/truncated, repair is not possible. Please try restoring a backup. " WriteLog "Repair - Main databse too small - FAIL" Fail=1 continue fi # Continue Output "Exporting current databases using timestamp: $TimeStamp" Fail=0 # Get the owning UID/GID before we proceed so we can restore Owner="$(stat -c '%u:%g' $CPPL.db)" # Attempt to export main db to SQL file (Step 1) echo -n 'Export: (main)..' "$PLEX_SQLITE" $CPPL.db ".output '$TMPDIR/library.plexapp.sql-$TimeStamp'" .dump Result=$? if ! SQLiteOK $Result; then # Cannot dump file Output "Error $Result from Plex SQLite while exporting $CPPL.db" Output "Could not successfully export the main database to repair it. Please try restoring a backup." WriteLog "Repair - Cannot recover main database to '$TMPDIR/library.plexapp.sql-$TimeStamp' - FAIL ($Result)" Fail=1 continue fi # Attempt to export blobs db to SQL file echo -n '(blobs)..' "$PLEX_SQLITE" $CPPL.blobs.db ".output '$TMPDIR/blobs.plexapp.sql-$TimeStamp'" .dump Result=$? if ! SQLiteOK $Result; then # Cannot dump file Output "Error $Result from Plex SQLite while exporting $CPPL.blobs.db" Output "Could not successfully export the blobs database to repair it. Please try restoring a backup." WriteLog "Repair - Cannot recover blobs database to '$TMPDIR/blobs.plexapp.sql-$TimeStamp' - FAIL ($Result)" Fail=1 continue fi # Edit the .SQL files if all OK if [ $Fail -eq 0 ]; then # Edit sed -i -e 's/ROLLBACK;/COMMIT;/' "$TMPDIR/library.plexapp.sql-$TimeStamp" sed -i -e 's/ROLLBACK;/COMMIT;/' "$TMPDIR/blobs.plexapp.sql-$TimeStamp" fi # Inform user echo done. Output "Successfully exported the main and blobs databases. Proceeding to import into new databases." WriteLog "Repair - Export databases - PASS" # Library and blobs successfully exported, create new echo -n 'Import: (main)..' "$PLEX_SQLITE" $CPPL.db-$TimeStamp < "$TMPDIR/library.plexapp.sql-$TimeStamp" Result=$? if ! SQLiteOK $Result; then Output "Error $Result from Plex SQLite while importing from '$TMPDIR/library.plexapp.sql-$TimeStamp'" WriteLog "Repair - Cannot import main database from '$TMPDIR/library.plexapp.sql-$TimeStamp' - FAIL ($Result)" Output "Cannot continue." Fail=1 continue fi echo -n '(blobs)..' "$PLEX_SQLITE" $CPPL.blobs.db-$TimeStamp < "$TMPDIR/blobs.plexapp.sql-$TimeStamp" Result=$? if ! SQLiteOK $Result ; then Output "Error $Result from Plex SQLite while importing from '$TMPDIR/blobs.plexapp.sql-$TimeStamp'" WriteLog "Repair - Cannot import blobs database from '$TMPDIR/blobs.plexapp.sql-$TimeStamp' - FAIL ($Result)" Output "Cannot continue." Fail=1 continue fi # Made it to here, now verify echo done. Output "Successfully imported data from exported SQL files." WriteLog "Repair - Import - PASS" # Verify databases are intact and pass testing Output "Verifying databases integrity after importing." # Check main DB if CheckDB $CPPL.db-$TimeStamp ; then SizeStart=$(GetSize $CPPL.db) SizeFinish=$(GetSize $CPPL.db-$TimeStamp) Output "Verification complete. PMS main database is OK." WriteLog "Repair - Verify main database - PASS (Size: ${SizeStart}MB/${SizeFinish}MB)." else Output "Verification complete. PMS main database import failed." WriteLog "Repair - Verify main database - FAIL ($SQLerror)" Fail=1 fi # Check blobs DB if CheckDB $CPPL.blobs.db-$TimeStamp ; then SizeStart=$(GetSize $CPPL.blobs.db) SizeFinish=$(GetSize $CPPL.blobs.db-$TimeStamp) Output "Verification complete. PMS blobs database is OK." WriteLog "Repair - Verify blobs database - PASS (Size: ${SizeStart}MB/${SizeFinish}MB)." else Output "Verification complete. PMS blobs database import failed." WriteLog "Repair - Verify main database - FAIL ($SQLerror)" Fail=1 fi # If not failed, move files normally if [ $Fail -eq 0 ]; then Output "Saving current databases with '-ORIG-$TimeStamp'" [ -e $CPPL.db ] && mv $CPPL.db "$TMPDIR/$CPPL.db-ORIG-$TimeStamp" [ -e $CPPL.blobs.db ] && mv $CPPL.blobs.db "$TMPDIR/$CPPL.blobs.db-ORIG-$TimeStamp" Output "Making imported databases active" mv $CPPL.db-$TimeStamp $CPPL.db mv $CPPL.blobs.db-$TimeStamp $CPPL.blobs.db Output "Import complete. Please check your library settings and contents for completeness." Output "Recommend: Scan Files and Refresh all metadata for each library section." # Remove .sql temp files from $TMPDIR # rm -f "$TMPDIR"/*.sql-* # Ensure WAL and SHM are gone [ -e $CPPL.blobs.db-wal ] && rm -f $CPPL.blobs.db-wal [ -e $CPPL.blobs.db-shm ] && rm -f $CPPL.blobs.db-shm [ -e $CPPL.db-wal ] && rm -f $CPPL.db-wal [ -e $CPPL.db-shm ] && rm -f $CPPL.db-shm # Set ownership on new files chown $Owner $CPPL.db $CPPL.blobs.db # We didn't fail, set CheckedDB status true (passed above checks) CheckedDB=1 WriteLog "Repair - Move files - PASS" WriteLog "Repair - PASS" SetLast "Repair" "$TimeStamp" else rm -f $CPPL.db-$TimeStamp rm -f $CPPL.blobs.db-$TimeStamp Output "Repair has failed. No files changed" WriteLog "Repair - $TimeStamp - FAIL" Retain=1 fi continue # 5. Replace database from backup copy elif [ $Choice -eq 5 ]; then # If Databases already checked, confirm the user really wants to do this Confirmed=0 Fail=0 if CheckDatabases "Replace"; then if ConfirmYesNo "Are you sure you want to restore a previous database backup"; then Confirmed=1 fi fi if [ $Damaged -eq 1 ] || [ $Confirmed -eq 1 ]; then # Get list of dates to use Dates=$(GetDates) # If no backups, error and exit if [ "$Dates" == "" ] && [ $Damaged -eq 1 ]; then Output "Database is damaged and no backups avaiable." Output "Only available option is Repair." WriteLog "Replace - Scan for usable candidates - FAIL" continue fi Output "Checking for a usable backup." Candidate="" for i in $Dates do # Check candidate if [ -e $CPPL.db-$i ] && \ [ -e $CPPL.blobs.db-$i ] && \ CheckDB $CPPL.db-$i && \ CheckDB $CPPL.blobs.db-$i ; then Output "Found valid database backup date: $i" Candidate=$i UseThis=0 if ConfirmYesNo "Use backup '$Candidate' ?"; then UseThis=1 fi # OK, use this one if [ $UseThis -eq 1 ]; then # Move database, wal, and shm (keep safe) with timestamp Output "Saving current databases with timestamp: '-ORIG-$TimeStamp'" for j in "db" "db-wal" "db-shm" "blobs.db" "blobs.db-wal" "blobs.db-shm" do [ -e $CPPL.$j ] && mv -f $CPPL.$j "$TMPDIR/$CPPL.$j-ORIG-$TimeStamp" done WriteLog "Replace - Move Files - PASS" # Copy this backup into position as primary Output "Copying backup database $Candidate to use as new database." cp -p $CPPL.db-$Candidate $CPPL.db-$TimeStamp Result=$? if [ $Result -ne 0 ]; then Output "Error $Result while copying $CPPL.db" Output "Database file is incomplete. Please resolve manually." WriteLog "Replace - Copy $CPPL.db-$Candidate - FAIL" Fail=1 else WriteLog "Replace - Copy $CPPL.db-$i - PASS" fi cp -p $CPPL.blobs.db-$Candidate $CPPL.blobs.db-$TimeStamp Result=$? if [ $Result -ne 0 ]; then Output "Error $Result while copying $CPPL.blobs.db" Output "Database file is incomplete. Please resolve manually." WriteLog "Replace - Copy $CPPL.blobs.db-$Candidate - FAIL" Fail=1 else WriteLog "Replace - Copy $CPPL.blobs.db-$Candidate - PASS" fi # If no failure copying, check and make active if [ $Fail -eq 0 ]; then # Final checks Output "Copy complete. Performing final check" if CheckDB $CPPL.db-$TimeStamp && \ CheckDB $CPPL.blobs.db-$TimeStamp ; then # Move into position as active mv $CPPL.db-$TimeStamp $CPPL.db mv $CPPL.blobs.db-$TimeStamp $CPPL.blobs.db # done Output "Database recovery and verification complete." WriteLog "Replace - Verify databases - PASS" else # DB did not verify after copy -- Something wrong rm -f $CPPL.db-$TimeStamp $CPPL.blobs.db-$TimeStamp Output "Final check failed. Keeping existing databases" WriteLog "Replace - Verify databases - FAIL" WriteLog "Replace - Failed Databses - REMOVED" fi else Output "Could not copy backup databases. Out of disk space?" Output "Restoring original databases" for k in "db" "db-wal" "db-shm" "blobs.db" "blobs.db-wal" "blobs.db-shm" do [ -e "$TMPDIR/$CPPL.$k-ORIG-$TimeStamp" ] && mv -f "$TMPDIR/$CPPL.$k-ORIG-$TimeStamp" $CPPL.$k done WriteLog "Replace - Verify databases - FAIL" Fail=1 fi fi # If successful, save [ $Fail -eq 0 ] && SetLast "Replace" "$TimeStamp" break fi done # Error check if no Candidate found if [ "$Candidate" = "" ]; then Output "Error. No valid matching main and blobs database pairs. Cannot replace." WriteLog "Replace - Select candidate - FAIL" fi fi # 6. - Undo last successful action elif [ $Choice -eq 6 ]; then # Confirm there is something to undo if [ "$LastTimestamp" != "" ]; then # Educate user echo " " echo "'Undo' restores the databases to the state prior to the last SUCCESSFUL action." echo "If any action fails before it completes, that action is automatically undone for you." echo "Be advised: Undo restores the databases to their state PRIOR TO the last action of 'Vacuum', 'Reindex', or 'Replace'" echo "WARNING: Once Undo completes, there will be nothing more to Undo untl another successful action is completed" echo " " if ConfirmYesNo "Undo '$LastName' performed at timestamp '$LastTimestamp' ? "; then Output "Undoing $LastName ($LastTimestamp)" for j in "db" "db-wal" "db-shm" "blobs.db" "blobs.db-wal" "blobs.db-shm" do [ -e "$TMPDIR/$CPPL.$j-ORIG-$LastTimestamp" ] && mv -f "$TMPDIR/$CPPL.$j-ORIG-$LastTimestamp" $CPPL.$j done Output "Undo complete." WriteLog "Undo - Undo $LastName , TimeStamp $LastTimestamp" SetLast "Undo" "" fi else Output "Nothing to undo." WriteLog "Undo - Nothing to Undo." fi # 7. - Show Logfile elif [ $Choice -eq 7 ]; then echo ================================================================================== cat "$LOGFILE" echo ================================================================================== # 8. - Exit elif [ $Choice -eq 8 ]; then # Ask questions on graceful exit if [ $Exit -eq 0 ]; then # Ask if the user wants to remove the DBTMP directory and all backups thus far if ConfirmYesNo "Ok to remove temporary work files for this session?" ; then # Here it goes Output "Deleting all temporary work files." WriteLog "Exit - Delete temp files." rm -rf "$TMPDIR" else Output "Retaining all temporary work files." WriteLog "Exit - Retain temp files." fi else Output "Unexpected exit command. Keeping all temporary work files." WriteLog "EOFExit - Retain temp files." fi WriteLog "Session end." WriteLog "============================================================" exit 0 fi done exit 0