Airgiraffe
Airgiraffe

Reputation: 65

Bash Script - Check mounted devices

What I'm trying to do - In the end, I want a script that checks for 3 devices, an SD card, Backup1 & Backup2. They've all been set to auto-mount at their respective mountpoints. The script should check for SD card first, if this fails, then a warning should be sent, and nothing more. If SD is okay, but only one backup is mounted, then ask for confirmation to go ahead with rsync to mounted backup. If all devices are mounted, then rsync from SD card to both backups.

Currently I'm just trying to get the device check nailed, using echo commands. Here's what I have (after multiple attempts) -

if ! mount | grep /media/card >/dev/null
then
    echo "ERROR: SD card not mounted, aborting"
else
    if ! mount | grep /media/backup >/dev/null
        then
            if ! mount | grep /media/backup2 >/dev/null
            then
                echo "ERROR: No backup devices"
            else
                echo "CAUTION: Backup_2 missing, Backup_1 OKAY"
            fi
    else
        if ! mount | grep /media/backup2 /dev/null
        then
            echo "CAUTION: Backup_1 missing, Backup_2 OKAY"
        else
            echo "SUCCESS: All devices OKAY"
        fi
    fi
fi

Which isn't working. I'm getting confused by all the nested 'if's and surely there's a simpler way? Perhaps something that checks each device independently, then returns values that equate to the various combinations (0=no devices, 1=sd only, 2=sd & backup1, 3=sd & backup 2, 4 = all okay) which then is read and decides next part of script to run? If not, where has this way gone wrong?

Any help is appreciated

Upvotes: 1

Views: 4006

Answers (3)

F. Hauri  - Give Up GitHub
F. Hauri - Give Up GitHub

Reputation: 70977

Check for 1 required mounted source AND at least 1 required mounted target

The best aproach for this is to build an array of mounted devices, then evaluate de count of elements in array.

Introduction

1. Care! Using grep -q "$dev" /proc/mounts will fail to found spaced mount points!

/media/spaced target

in /proc/mounts will appear as

/media/spaced\040target

command printf -v varname '%b' $varname could help!

2. Using array and associative array to avoid fork

Instead of doing many fork to grep, (browsing same file many time), you could use bash for the job.

3. Parallelizing rsync on many targets

Using backgrounded jobs and wait -n -p <VARNAME>, you could do rsync on many targets simultaneously.

4. Using /proc/self/mounts instead of /proc/mounts

After having a look at man 5 proc.

Here is a script, within:

  • you could configure as many backup targets you want.
  • if all targets are mounted, script go on
  • if no targets are mounted, script stop and
  • if at least one target is mounted but not all, user is asked for continuing.
    • (this could be avoided by using -y/-n optional flag)
  • On second part, all rsync are run as background job (parallelized),
  • then script wait for all rsync to be done,
    • then print each result code with corresponding target,
  • before finish

The script:

#!/bin/bash

source='/media/card'
backups=(
    /media/backup
    /media/backup2
    /media/other\ backup
)

die() {
    echo "$0[$$] ERROR: $@" >&2
    exit 1
}
warn() {
    echo "$0[$$] WARNING: $@" >&2
}
declare -A tobemounted="(['$source']='source')"
for target in "${backups[@]}";do
    tobemounted["$target"]=backup
done

mountedbk=()
while read -r _ mpnt _;do
    printf -v mpnt %b $mpnt
    if [[ -v tobemounted["$mpnt"] ]] ;then
        [[ ${tobemounted["$mpnt"]} == backup ]] && mountedbk+=("$mpnt")
        unset tobemounted["$mpnt"]
    fi
done </proc/self/mounts

[[ -v tobemounted["$source"] ]] && die "Source '$source' not mounted."

for missing in "${!tobemounted[@]}";do
    warn "Backup '$missing' not mounted."
done
(( ${#mountedbk[@]} == 0 )) && die "No backup present."

if (( ${#mountedbk[@]} < ${#backups[@]} ));then
    printf -v msg 'Only %d / %d backups targets are present.' \
        ${#mountedbk[@]} ${#backups[@]}
    warn "$msg"
    case $* in
        *-n* ) die Exit now. ;;
        *-y* ) warn Process anyway. ;;
        * ) read -rsn 1 -p 'Process anyway? (N/y): ' ans
            [[ "${ans//[yY]}" ]] && { echo No; die 'User abort.';}
            echo Yes ;;
    esac
fi
pids=() resultmsg=(success)
for target in "${mountedbk[@]}";do
    echo rsync "$source/." "$target/." &
    pids[$!]="$target"
done

while ((${#pids[@]}));do
    wait -np pid
    printf 'backup to "%s" end with %s.\n' \
        "${pids[pid]}"  "${resultmsg[$?]:-error}"
    unset 'pids[pid]'
done

Replace line:

    echo rsync "$source/." "$target/." &

by somethine like:

    rsync <your rsync options> "$source/SourcePath/." "$target/TargetPath/." &

Outputs could look like:

When source not ready:

$ ./backups.sh
./backups.sh[3721068] ERROR: Source '/media/card' not mounted.

When none of the targets are ready.

$ ./backups.sh
./backups.sh[3729071] WARNING: Backup '/media/backup' not mounted.
./backups.sh[3729071] WARNING: Backup '/media/backup2' not mounted.
./backups.sh[3729071] WARNING: Backup '/media/other backup' not mounted.
./backups.sh[3729071] ERROR: No backup present.

When some but not all targets are ready.

$ ./backups.sh
./backups.sh[3725569] WARNING: Backup '/media/backup2' not mounted.
./backups.sh[3725569] WARNING: Backup '/media/other backup' not mounted.
./backups.sh[3725569] WARNING: Only 1 / 3 backups targets are present.
Process anyway? (N/y): Yes
backup to "/media/backup" end with success.
$ ./backups.sh
./backups.sh[3729355] WARNING: Backup '/media/backup' not mounted.
./backups.sh[3729355] WARNING: Backup '/media/other backup' not mounted.
./backups.sh[3729355] WARNING: Only 1 / 3 backups targets are present.
Process anyway? (N/y): Yes
backup to "/media/backup2" end with success.
$ ./backups.sh 
./backups.sh[3854708] WARNING: Backup '/media/backup2' not mounted.
./backups.sh[3854708] WARNING: Backup '/media/other backup' not mounted.
./backups.sh[3854708] WARNING: Only 1 / 3 backups targets are present.
Process anyway? (N/y): No
./backups.sh[3854708] ERROR: User abort.
$ ./backups.sh -y
./backups.sh[3734321] WARNING: Backup '/media/backup' not mounted.
./backups.sh[3734321] WARNING: Backup '/media/other backup' not mounted.
./backups.sh[3734321] WARNING: Only 1 / 3 backups targets are present.
./backups.sh[3734321] Process anyway.
backup to "/media/backup2" end with success.
$ ./backups.sh -n
./backups.sh[3854855] WARNING: Backup '/media/backup2' not mounted.
./backups.sh[3854855] WARNING: Backup '/media/other backup' not mounted.
./backups.sh[3854855] WARNING: Only 1 / 3 backups targets are present.
./backups.sh[3854855] ERROR: Exit now.

When all targets are ready.

$ ./backups.sh
backup to "/media/backup" end with success.
backup to "/media/backup2" end with success.
backup to "/media/other\ backup" end with success.

Upvotes: 1

Airgiraffe
Airgiraffe

Reputation: 65

Solved! Part of the problem was using mount | grep /media/backup > /dev/null as there's an issue with similar names, so /media/backup2 being mounted meant it was counting both devices as mounted.

Using grep -q /media/backup2\ /proc/mounts works in a similar way, or at least produces the same outcome, just without the naming issue.

I've also changed the way the if statements work, it now checks for SD card, if that fails, then it aborts. It then checks for which devices are mounted with a fairly simple flow.

Here's the code:

GNU nano 2.2.6 File: backupSD2.sh Modified

#!/bin/bash

#Check for SD

if ! grep -q /media/card\  /proc/mounts
then
    echo "ERROR: NO SD CARD"
    exit 0
else
    echo "SD OKAY..."
fi

#Check for Backup Devices

if (( grep -q /media/backup\  /proc/mounts ) && ( grep -q /media/backup2\  /proc/mounts ))
then
    echo "Both Backups mounted"

elif grep -q /media/backup\  /proc/mounts
then
    echo "CAUTION: Backup_2 missing, Backup_1 OKAY"

elif grep -q /media/backup2\  /proc/mounts
then
    echo "CAUTION: Backup_1 missing, Backup_2 OKAY"
else
    echo "ERROR: NO BACKUP DEVICES"
    exit 0
fi

After the 'echo' commands is where I'll be putting the rsync commands. The final exit 0 is probably unnecessary, but I don't think it's doing any harm and reassures that it's not going to try any more commands.

Upvotes: 1

D&#39;Arcy Nader
D&#39;Arcy Nader

Reputation: 196

Use mountpoint, which returns success (0) if the path is a mounted device, and 1 otherwise:

#!/bin/bash

check_mountpoint(){
if  mountpoint -q $1
  then
                printf "%s\n" "$1 is mounted"
                return 0
            else
                printf "%s\n" "$1 is not mounted"
                return 1
fi
}

check_mountpoint /media/sd 
if [ $? -gt 0 ]
  then
    printf "%s\n" "aborting"
    exit 0
fi
check_mountpoint /media/backup1
check_mountpoint /media/backup2

this is your code corrected, i hope it will help.

if mountpoint -q /media/sd
  then
    if mountpoint -q /media/backup
      then
        if mountpoint -q /media/backup2
          then
            echo "SUCCESS: All devices OKAY"
            # run your command here
          else
            echo "CAUTION: Backup_2 missing, Backup_1 OKAY, SD OKAY"
            # run your command here
        fi
      else
       if mountpoint -q /media/backup2
          then
            echo "CAUTION: Backup_1 missing, Backup_2 OKAY, SD OKAY"
            # run your command here
          else
            echo "SD OKAY , BACKUP Devices missing"
        fi
    fi
  else
    echo "SD is missing , Exit"
fi

Upvotes: 1

Related Questions