Brighid McDonnell
Brighid McDonnell

Reputation: 4343

Using bash to tell whether or not a drive with a given UUID is mounted

I am working on a bash script that is designed to tell whether or not a given drive is attached to the system, and if it is, to copy some data (backups that need to be taken off-site) to that drive.

What I'd like to do is identify the drive/partition by UUID, so that I can have a script that frequently scans for the presence of that drive, and when it's plugged in, copies data to it (via rsync, so I don't particularly care if that part runs more than once, because the later runs are I/O-cheap). I want this to be as simple as possible so that I can hand the drive in question to a non-technical person and say "plug it in here, then unplug it later and take it with you."

I can't seem to find any options in mount that quite do this, and grepping through /etc/fstab doesn't tell me whether a drive is actually mounted or not.

How can I tell, given a UUID, whether or not a drive with that UUID is mounted and what its mount point is?

Upvotes: 3

Views: 5475

Answers (5)

Shawn Hughes
Shawn Hughes

Reputation: 11

First, I'm going to add a couple requirements for you: You say you want a non-technical user to remove the drive for offsite storage. So, also needed:

a) when rsync finishes, unmount the drive so it's safe to remove.

b) if drive is still connected at next run interval, mount the drive just prior to starting rsync.

Assuming we do (a) and (b), your last question is no longer relevant, as we only need to determine if one or more target uuid disk(s) are attached, and if so, script will mount it at a pre-determined mountpoint.

This basic example uses a single config file named backup.disks. It should contain your target uuid on a single line. Optionally, can add multiple uuids, one per line, rsync will run to the first disk found.

DISKS=/path/backup.disks
MOUNTPOINT=/mnt/backup
SOURCE=/pathFilesToBackup/
TARGET=/$MOUNTPOINT/backupfolder
# Find whether a connected block device is a backup drive
for uuid in $(lsblk --noheadings --list --output uuid)
do
        if grep --quiet --fixed-strings $uuid $DISKS; then
                break
        fi
        uuid=
done

if [ ! $uuid ]; then
        echo "No backup disk found, exiting"
        exit 0
fi
echo "Disk $uuid is a backup disk"
partition_path=/dev/disk/by-uuid/$uuid
# drive is attached, we assume system is NOT automatically mounting disks and only this script will be mounting backup drives.
# if drive is not already mounted at designated mountpoint, then mount it. (if someone else already mounted it elsewhere then we have a problem)
findmnt $MOUNTPOINT >/dev/null || mount $partition_path $MOUNTPOINT
# create backup
rsync -av $SOURCE $TARGET
# ensure writes flushed to disk before unmounting
sync
umount $MOUNTPOINT

Giving credit to the Borg Backup project, from which above script is derived. Above script covers your question. The full script from Borg Backup offers additional options like creating a service to watch for specific UUID(s) and kick off backup immediately after a target UUID is attached. It's simple enough to replace the Borg commands with rsync.

Here's the Borg script: Borg Backup, Automated backups

Upvotes: 0

not an ai
not an ai

Reputation: 1823

/dev/disk/by-uuid/ is a symlink farm for attached volumes. It won't tell you if the volume is mounted or not but will tell you if it's plugged in - so might address your particular use case anyway.

$ ls -l /dev/disk/by-uuid/
total 0
lrwxrwxrwx 1 root root 10 Nov 22 10:08 b928a862-6b3c-45a8-82fe-8f1db2863be3 -> ../../dm-0
lrwxrwxrwx 1 root root 10 Dec  8 14:33 e37fab85-9b00-4d9b-b25e-0970d6eaa3fa -> ../../sdg1

$ readlink /dev/disk/by-uuid/b928a862-6b3c-45a8-82fe-8f1db2863be3 
../../dm-0

Does that help?

Upvotes: 2

The best I've found for doing just this is to use blkid to convert the UUID to a block devise on the unix filesystem e.g.

:~# blkid -U '09b8f1ab-8d4b-4c5f-b395-40be09c090b0'
/dev/sdb1

then grep the mount command's output for that.

:~# mount | grep $(blkid -U '09b8f1ab-8d4b-4c5f-b395-40be09c090b0')
/dev/sdb1 on /media/WD_Disk_1 type ext3 (rw,nosuid,nodev,uhelper=udisks)

then grep that for the desired mount point

grep -qs '/media/WD_Disk_1 '

The full command is

:~# mount | grep $(blkid -U '09b8f1ab-8d4b-4c5f-b395-40be09c090b0') | grep '/media/WD_Disk_1 '
/dev/sdb1 on /media/WD_Disk_1 type ext3 (rw,nosuid,nodev,uhelper=udisks)

I put this into an if statement and hide the output replacing it with a nice little message :-).

if mount | grep $(blkid -U '09b8f1ab-8d4b-4c5f-b395-40be09c090b0') | grep '/media/WD_Disk_1 ' > /dev/null 2>&1;
then
    rsync -az --delete /mnt/data/ /media/WD_Disk_1/current_working_data/;
    echo "sync complete"
else
    echo "couldn't find WD_Disk_1 filesystem"
    exit 1
fi

Upvotes: 2

Nick Garvey
Nick Garvey

Reputation: 3000

A device can be mounted by its symlink. If this is the case, the mount command will refer to it by its symlink but you might have a reference by its real path, or worse another symlink.

This is compounded even more by the fact that multiple block device files can point to a single block device (!). This can be done by using the mknod command.

The only way I can find to accurately get this information is to compare the major and minor numbers of all entries in /proc/mounts with the major and minor number of the UUID you are looking for.

Here is a function/script that will do this.

#!/bin/bash

# $1:     should be a drive uuid, such as that reported by blkid
# return: return value of 0 if mounted, 1 if not mounted
function is_mounted_by_uuid() {
    input_path=$(readlink -f /dev/disk/by-uuid/"$1")
    input_maj_min=$(stat -c '%T %t' "$input_path")

    cat /proc/mounts | cut -f-1 -d' ' | while read block_device; do
        if [ -b "$block_device" ]; then
            block_device_real=$(readlink -f "$block_device")
            blkdev_maj_min=$(stat -c '%T %t' "$block_device_real")

            if [ "$input_maj_min" == "$blkdev_maj_min" ]; then
                return 255
            fi
        fi
    done

    if [ $? -eq 255 ]; then
        return 0
    else
        return 1
    fi
}

if is_mounted_by_uuid "$1"; then
    echo $(readlink -f /dev/disk/by-uuid/$1) is mounted
else
    echo $(readlink -f /dev/disk/by-uuid/$1) is not mounted
fi

Upvotes: 3

Robᵩ
Robᵩ

Reputation: 168596

mount | grep $(readlink -f /dev/disk/by-uuid/$UUID )

Upvotes: 2

Related Questions