Linux is able to identify a device when something is plugged into an already-running system and then do something in response. Part of this is done with Udev which supplies a dynamic device directory containing only the nodes for devices which are connected to the system. It creates or removes the device node files in the /dev directory as they are plugged in or taken out.
Using this I am able to backup a Linux system to an external hard drive just by plugging in the USB drive. When the drive is connected Linux will detect it, mount it, and start a backup. Once the backup is done the drive will remain mounted so I can review the files before removing the drive.
The process goes something like this: I plug in the USB drive, the system will detect it and mount the drive. A Udev rule will fire off a small script creating a job using “AT” to start the backup script one minute later. AT will start the backup script which will create a directory on the USB drive and backup files to that directory. I then review the backup, unmount the drive, then unplug it.
Udev rules are stored in these two locations:
joey@HAVEN-E6520:~$ ls /usr/lib/udev/rules.d total 940K -rw-r--r-- 1 root root 947 Nov 30 2019 39-usbmuxd.rules -rw-r--r-- 1 root root 998 Jan 20 2019 40-usb-media-players.rules -rw-r--r-- 1 root root 42K Feb 24 2020 40-usb_modeswitch.rules -rw-r--r-- 1 root root 655 Aug 31 11:27 40-vm-hotadd.rules -rw-r--r-- 1 root root 210 Feb 16 2022 50-firmware.rules -rw-r--r-- 1 root root 3.9K Aug 31 11:27 50-udev-default.rules -rw-r--r-- 1 root root 7.2K Feb 13 2020 55-dm.rules -rw-r--r-- 1 root root 1.5K Apr 6 2020 56-dm-parts.rules -rw-r--r-- 1 root root 921 Sep 13 2021 56-hpmud.rules -rw-r--r-- 1 root root 2.4K Feb 13 2020 56-lvm.rules -rw-r--r-- 1 root root 8.4K Aug 31 11:27 60-autosuspend-chromiumos.rules -rw-r--r-- 1 root root 703 Apr 1 2020 60-block.rules -rw-r--r-- 1 root root 1.1K Apr 1 2020 60-cdrom_id.rules -rw-r--r-- 1 root root 69 Jan 17 2017 60-crda.rules -rw-r--r-- 1 root root 413 Apr 1 2020 60-drm.rules -rw-r--r-- 1 root root 990 Apr 1 2020 60-evdev.rules -rw-r--r-- 1 root root 394 Apr 1 2020 60-fido-id.rules -rw-r--r-- 1 root root 329 Jan 8 2016 60-inputattach.rules -rw-r--r-- 1 root root 282 Apr 1 2020 60-input-id.rules -rw-r--r-- 1 root root 7.2K Jun 15 2020 60-libgphoto2-6.rules -rw-r--r-- 1 root root 3.5K Sep 17 2020 60-libsane.rules -rw-r--r-- 1 root root 201 Jul 25 2016 60-openobex.rules -rw-r--r-- 1 root root 912 Feb 10 2020 60-pcmcia.rules -rw-r--r-- 1 root root 616 Apr 1 2020 60-persistent-alsa.rules -rw-r--r-- 1 root root 2.7K Apr 1 2020 60-persistent-input.rules -rw-r--r-- 1 root root 1.8K Feb 13 2020 60-persistent-storage-dm.rules -rw-r--r-- 1 root root 7.6K Aug 31 11:27 60-persistent-storage.rules -rw-r--r-- 1 root root 2.1K Apr 1 2020 60-persistent-storage-tape.rules -rw-r--r-- 1 root root 769 Apr 1 2020 60-persistent-v4l.rules -rw-r--r-- 1 root root 736 Apr 1 2020 60-sensor.rules -rw-r--r-- 1 root root 1.2K Apr 1 2020 60-serial.rules -rw-r--r-- 1 root root 211 Dec 3 2019 60-tpm-udev.rules -rw-r--r-- 1 root root 181 Mar 6 2022 60-virtualbox-dkms.rules -rw-r--r-- 1 root root 454 May 11 2021 60-virtualbox.rules -rw-r--r-- 1 root root 565 Apr 1 2020 61-autosuspend-manual.rules -rw-r--r-- 1 root root 292 Apr 29 2020 61-gnome-settings-daemon-rfkill.rules -rw-r--r-- 1 root root 292 Aug 21 2021 61-mate-settings-daemon-rfkill.rules -rw-r--r-- 1 root root 456 Aug 31 11:27 61-persistent-storage-android.rules -rw-r--r-- 1 root root 387 Feb 15 2020 64-btrfs-dm.rules -rw-r--r-- 1 root root 612 Aug 31 11:27 64-btrfs.rules -rw-r--r-- 1 root root 257 Jul 6 09:53 64-xorg-xkb.rules -rw-r--r-- 1 root root 75K Apr 21 2021 65-libwacom.rules -rw-r--r-- 1 root root 190 Jun 5 2020 66-snapd-autoimport.rules -rw-r--r-- 1 root root 1.2K Apr 6 2020 68-del-part-nodes.rules -rw-r--r-- 1 root root 4.9K Mar 20 2020 69-cd-sensors.rules -rw-r--r-- 1 root root 225K Mar 26 2020 69-libmtp.rules -rw-r--r-- 1 root root 6.5K Feb 13 2020 69-lvm-metad.rules -rw-r--r-- 1 root root 1.2K Mar 11 2020 69-wacom.rules -rw-r--r-- 1 root root 432 Apr 1 2020 70-joystick.rules -rw-r--r-- 1 root root 734 Apr 1 2020 70-mouse.rules -rw-r--r-- 1 root root 172 Feb 21 2019 70-pcspkr-beep.rules -rw-r--r-- 1 root root 568 Apr 1 2020 70-power-switch.rules -rw-r--r-- 1 root root 429 Nov 3 2020 70-printers.rules -rw-r--r-- 1 root root 473 Apr 1 2020 70-touchpad.rules -rw-r--r-- 1 root root 3.9K Jul 13 2019 70-u2f.rules -rw-r--r-- 1 root root 2.8K Aug 31 11:27 70-uaccess.rules -rw-r--r-- 1 root root 461 Aug 31 11:27 71-power-switch-proliant.rules -rw-r--r-- 1 root root 3.7K Aug 31 11:27 71-seat.rules -rw-r--r-- 1 root root 437 Mar 16 2022 71-u-d-c-gpu-detection.rules -rw-r--r-- 1 root root 636 Aug 31 11:27 73-seat-late.rules -rw-r--r-- 1 root root 969 Aug 30 04:40 73-special-net-names.rules -rw-r--r-- 1 root root 452 Apr 1 2020 75-net-description.rules -rw-r--r-- 1 root root 174 Apr 1 2020 75-probe_mtd.rules -rw-r--r-- 1 root root 936 Apr 8 2022 77-mm-broadmobi-port-types.rules -rw-r--r-- 1 root root 3.7K Apr 8 2022 77-mm-cinterion-port-types.rules -rw-r--r-- 1 root root 1.8K Apr 8 2022 77-mm-dell-port-types.rules -rw-r--r-- 1 root root 866 Apr 8 2022 77-mm-dlink-port-types.rules -rw-r--r-- 1 root root 7.9K Apr 8 2022 77-mm-ericsson-mbm.rules -rw-r--r-- 1 root root 3.1K Apr 8 2022 77-mm-fibocom-port-types.rules -rw-r--r-- 1 root root 1.6K Apr 8 2022 77-mm-foxconn-port-types.rules -rw-r--r-- 1 root root 907 Apr 8 2022 77-mm-gosuncn-port-types.rules -rw-r--r-- 1 root root 525 Apr 8 2022 77-mm-haier-port-types.rules -rw-r--r-- 1 root root 2.5K Apr 8 2022 77-mm-huawei-net-port-types.rules -rw-r--r-- 1 root root 15K Apr 8 2022 77-mm-longcheer-port-types.rules -rw-r--r-- 1 root root 3.3K Apr 8 2022 77-mm-mtk-port-types.rules -rw-r--r-- 1 root root 2.2K Apr 8 2022 77-mm-nokia-port-types.rules -rw-r--r-- 1 root root 1.6K Apr 8 2022 77-mm-qcom-soc.rules -rw-r--r-- 1 root root 3.1K Oct 12 2021 77-mm-qdl-device-blacklist.rules -rw-r--r-- 1 root root 4.4K Apr 8 2022 77-mm-quectel-port-types.rules -rw-r--r-- 1 root root 1.6K Apr 8 2022 77-mm-sierra.rules -rw-r--r-- 1 root root 3.8K Apr 8 2022 77-mm-simtech-port-types.rules -rw-r--r-- 1 root root 9.2K Apr 8 2022 77-mm-telit-port-types.rules -rw-r--r-- 1 root root 739 Apr 8 2022 77-mm-tplink-port-types.rules -rw-r--r-- 1 root root 4.2K Apr 8 2022 77-mm-ublox-port-types.rules -rw-r--r-- 1 root root 4.5K Apr 8 2022 77-mm-x22x-port-types.rules -rw-r--r-- 1 root root 17K Apr 8 2022 77-mm-zte-port-types.rules -rw-r--r-- 1 root root 965 Aug 31 11:27 78-graphics-card.rules -rw-r--r-- 1 root root 4.8K Apr 1 2020 78-sound-card.rules -rw-r--r-- 1 root root 1.4K Aug 31 11:27 80-debian-compat.rules -rw-r--r-- 1 root root 615 Apr 1 2020 80-drivers.rules -rw-r--r-- 1 root root 190 Jan 28 2019 80-ifupdown.rules -rw-r--r-- 1 root root 1.3K Mar 31 2022 80-iio-sensor-proxy.rules -rw-r--r-- 1 root root 211 Apr 14 2022 80-libinput-device-groups.rules -rw-r--r-- 1 root root 2.1K Apr 8 2022 80-mm-candidate.rules -rw-r--r-- 1 root root 295 Aug 31 11:27 80-net-setup-link.rules -rw-r--r-- 1 root root 11K Sep 5 2021 80-udisks2.rules -rw-r--r-- 1 root root 528 Aug 31 11:27 81-net-dhcp.rules -rw-r--r-- 1 root root 528 Nov 26 2021 84-nm-drivers.rules -rw-r--r-- 1 root root 11K Mar 3 2020 85-brltty.rules -rw-r--r-- 1 root root 82 Aug 21 2019 85-hdparm.rules -rw-r--r-- 1 root root 1.9K Jan 30 2020 85-hplj10xx.rules -rw-r--r-- 1 root root 1.7K Nov 26 2021 85-nm-unmanaged.rules -rw-r--r-- 1 root root 221 Feb 5 2018 85-regulatory.rules -rw-r--r-- 1 root root 563 Apr 14 2021 90-alsa-restore.rules -rw-r--r-- 1 root root 350 Dec 1 2020 90-bolt.rules -rw-r--r-- 1 root root 265 Apr 23 2019 90-console-setup.rules -rw-r--r-- 1 root root 281 Jul 2 23:18 90-fwupd-devices.rules -rw-r--r-- 1 root root 1.9K Sep 25 2019 90-libgpod.rules -rw-r--r-- 1 root root 1.1K Apr 14 2022 90-libinput-fuzz-override.rules -rw-r--r-- 1 root root 576 Nov 26 2021 90-nm-thunderbolt.rules -rw-r--r-- 1 root root 9.2K Nov 19 2021 90-pulseaudio.rules -rw-r--r-- 1 root root 847 Mar 8 2019 95-cd-devices.rules -rw-r--r-- 1 root root 479 Feb 13 2020 95-dm-notify.rules -rw-r--r-- 1 root root 1.4K Apr 6 2020 95-kpartx.rules -rw-r--r-- 1 root root 1.6K Dec 10 2019 95-upower-csr.rules -rw-r--r-- 1 root root 570 Dec 10 2019 95-upower-hidpp.rules -rw-r--r-- 1 root root 8.0K Dec 10 2019 95-upower-hid.rules -rw-r--r-- 1 root root 354 Dec 10 2019 95-upower-wup.rules -rw-r--r-- 1 root root 171 Jun 1 20:59 96-e2scrub.rules -rw-r--r-- 1 root root 372 Jan 5 2020 97-dmraid.rules -rw-r--r-- 1 root root 1.5K Jun 8 07:09 97-hid2hci.rules -rw-r--r-- 1 root root 4.7K Aug 31 11:27 99-systemd.rules joey@HAVEN-E6520:~$ ls /etc/udev/rules.d total 112K -rwxr-xr-x 1 root root 1.9K May 12 2020 40-libsane.rules -rw-r--r-- 1 root root 921 May 12 2020 56-hpmud.rules -rw-r--r-- 1 root root 62K Jun 7 22:42 70-snap.core.rules -rw-r--r-- 1 root root 1011 Jun 7 22:42 70-snap.freecell-solitaire.rules -rw-r--r-- 1 root root 2.6K Jun 7 22:42 70-snap.keepassxc.rules -rw-r--r-- 1 root root 941 Jun 7 22:42 70-snap.motioneye.rules -rw-r--r-- 1 root root 1.4K Jun 7 22:42 70-snap.mysql-workbench-community.rules -rw-r--r-- 1 root root 1.2K Jun 7 22:42 70-snap.notepadqq.rules -rw-r--r-- 1 root root 935 Jun 7 22:42 70-snap.skype.rules -rw-r--r-- 1 root root 935 Jun 7 22:42 70-snap.slack.rules -rw-r--r-- 1 root root 885 Jun 7 22:42 70-snap.spotify.rules -rw-r--r-- 1 root root 989 Jun 7 22:42 70-snap.vlc.rules -rwxr-xr-x 1 root root 1.9K May 12 2020 S99-2000S1.rules
I created my Udev rule in the “/etc/udev/rules.d” directory. The next step was to identify the USB drive when connected. The command “udevadm” is used to determine this. The important parts of the command’s output are highlighted below.
joey@HAVEN-E6520:~$ udevadm monitor --kernel --property --subsystem-match=usb monitor will print the received events for: KERNEL - the kernel uevent KERNEL[687713.585492] add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.4 (usb) ACTION=add DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.4 SUBSYSTEM=usb DEVNAME=/dev/bus/usb/002/013 DEVTYPE=usb_device NOTE: THIS IS THE DEVICE PRODUCT=152d/562/3 TYPE=0/0/0 BUSNUM=002 DEVNUM=013 SEQNUM=75152 MAJOR=189 MINOR=140 KERNEL[687713.586155] add /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.4/2-1.4:1.0 (usb) ACTION=add DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.4/2-1.4:1.0 SUBSYSTEM=usb DEVTYPE=usb_interface NOTE: THIS IS THE INTERFACE PRODUCT=152d/562/3 TYPE=0/0/0 INTERFACE=8/6/80 MODALIAS=usb:v152Dp0562d0003dc00dsc00dp00ic08isc06ip50in00 SEQNUM=75153 KERNEL[687713.587040] bind /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.4/2-1.4:1.0 (usb) ACTION=bind DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.4/2-1.4:1.0 SUBSYSTEM=usb DEVTYPE=usb_interface DRIVER=uas PRODUCT=152d/562/3 TYPE=0/0/0 INTERFACE=8/6/98 MODALIAS=usb:v152Dp0562d0003dc00dsc00dp00ic08isc06ip62in00 SEQNUM=75156 KERNEL[687713.587110] bind /devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.4 (usb) ACTION=bind DEVPATH=/devices/pci0000:00/0000:00:1d.0/usb2/2-1/2-1.4 SUBSYSTEM=usb DEVNAME=/dev/bus/usb/002/013 DEVTYPE=usb_device DRIVER=usb PRODUCT=152d/562/3 TYPE=0/0/0 BUSNUM=002 DEVNUM=013 SEQNUM=75157 MAJOR=189 MINOR=140
The next step was to create a Udev rule. Udev rules are processed in order. Therefore, I named mine starting with “99” to make sure it is one of the last rules to run. I also named the rule something descriptive and specific to the device attached.
sudo nano /etc/udev/rules.d/99-usbdrive-fantom6.rules
This rule only needs a single line to execute a script.
ACTION=="add", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{PRODUCT}=="152d/562/3", RUN+="/home/joey/Scripts/udev.sh 1> /dev/null"
Conditions for UDEV rule:
- ACTION==”add” match if device added (not removed)
- SUBSYSTEM==”usb” match if subsystem is “usb”
- ENV{DEVTYPE}==”usb_device” match if devtype is “usb_device” (not :usb_interface”) – without this the script ran twice
- ENV{PRODUCT}==”152d/562/3″ match if product is “152d/562/3”
What to do if conditions match:
- RUN+=”/home/joey/Scripts/udev.sh” – command needs to be quick else drive will not mount
It appears the action performed by the rule needs to be quick. I initially tried running the backup script from the rule but, the drive would not mount. I determined it would only mount after the rule completed.
The rule runs the following script that essentially does just one thing: schedule the execution of another script that does the backup. However, some extra commands are run to log results and display a notification on the screen.
#!/bin/bash
LOGFILE='/home/joey/script.log'
echo "$(date)|INFO|Starting script: udev.sh" >> "$LOGFILE"
# https://stackoverflow.com/questions/28195805/running-notify-send-as-root
function notify-send() {
#Detect the name of the display in use
local display=":$(ls /tmp/.X11-unix/* | sed 's#/tmp/.X11-unix/X##' | head -n 1)"
#Detect the user using such display
local user=$(who | grep '('$display')' | awk '{print $1}' | head -n 1)
#Detect the id of the user
local uid=$(id -u $user)
sudo -u $user DISPLAY=$display DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$uid/bus notify-send "$@"
}
# send notification
notify-send "External Harddrive Detected" "Fantom external drive has been detected. A backup will be started soon." -t 0 -u Normal -i /usr/share/icons/gnome/48x48/devices/drive-multidisk.png
# schedule a backup
echo "sudo -u joey /home/joey/Scripts/backup_e6520.sh" | at now + 1 minute
With this script and the Udev rule in-place the execution of a backup script is scheduled and the following notification pops-up when the USB drive is connected.
About one minute after plugging in the external drive the following backup script is executed and another pop-up notification is displayed.
#!/bin/bash
# Backup Dell E6520 laptop to Fantom "F6" external harddrive
# Run from via cron (NOT USED):
# /home/joey/Scripts/backup_e6520.sh
# ---------- copy ----------
# Backup Dropbox at 1AM each day
# * 1 * * * /home/joey/Scripts/backup_e6520.sh 1> /dev/null
# ---------- copy ----------
# save and change IFS
OLDIFS=$IFS
IFS=$'\n'
RUNDATE=`date +%Y%m%d` # append date to file
LOGFILE='/home/joey/script.log'
LOCKFILE="/home/joey/Temp/backup_e6520.lock"
#SCRIPT=`basename "$0"` # get name of this script
SCRIPT='/home/joey/Scripts/backup_e6520.sh'
TARGETDIR="/media/joey/F6/Backups/Laptop E6520/$RUNDATE"
TITLE_BACKUP_START='Backup Started'
MESSAGE_BACKUP_START='Starting backup of the laptop to the Fantom external hard drive.'
TITLE_BACKUP_DONE='Backup Completed'
MESSAGE_BACKUP_DONE='Backup of the laptop to the Fantom external hard drive has completed.'
TITLE_BACKUP_WARN='Backup Failed'
MESSAGE_BACKUP_WARN='Backup of the laptop to the Fantom external hard drive has failed. Check the script log.'
# function to allow notifications to logged-in user
function notify-send() {
#Detect the name of the display in use
local display=":$(ls /tmp/.X11-unix/* | sed 's#/tmp/.X11-unix/X##' | head -n 1)"
#Detect the user using such display
local user=$(who | grep '('$display')' | awk '{print $1}' | head -n 1)
#Detect the id of the user
local uid=$(id -u $user)
sudo -u $user DISPLAY=$display DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$uid/bus notify-send "$@"
}
echo -------------------------------------------------------------------------- | tee -a "$LOGFILE"
echo "$(date)|INFO|Starting script: $SCRIPT" >> "$LOGFILE"
echo "Starting script: `date`"
# send notification
notify-send "$TITLE_BACKUP_START" "$MESSAGE_BACKUP_START" -t 0 -u Normal -i /usr/share/icons/gnome/48x48/status/network-transmit.png
# if lock file exists something is wrong - abort
if [ -f "$LOCKFILE" ]; then
# lock file exists
echo "$(date)|ERROR|Lock file exists. Aborting." >> "$LOGFILE"
echo "Error: lock file exists."
echo "Note: this script is run automatically when the USB hard drive is plugged in and may be running."
# send notification
notify-send "$TITLE_BACKUP_WARN" "$MESSAGE_BACKUP_WARN" -t 0 -u Normal -i /usr/share/icons/gnome/48x48/status/dialog-warning.png
else
echo "$(date)|INFO|Lock file not found. Continuing." >> "$LOGFILE"
# wait for USB drive to be mounted
#sleep 30
# check if backup folder already exists (if so abort)
if [ -d "$TARGETDIR" ]; then
# backup folder exists, abort
echo "$(date)|ERROR|Backup folder exists. Aborting." >> "$LOGFILE"
echo "Error: backup folder already exists. A backup has already been run."
# send notification
notify-send "$TITLE_BACKUP_WARN" "$MESSAGE_BACKUP_WARN" -t 0 -u Normal -i /usr/share/icons/gnome/48x48/status/dialog-warning.png
else
#echo "Backup started: `date`" | tee -a "$LOGFILE"
echo "$(date)|INFO|Backup started: `date`" >> "$LOGFILE"
echo "Starting backup."
# create lock file
echo 'This file should not exist for more than a few hours.' > "$LOCKFILE"
# Create an array to hold directories to backup
declare -a DIRARRAY
DIRARRAY+=('/home/joey/Desktop')
DIRARRAY+=('/home/joey/Documents')
DIRARRAY+=('/home/joey/Development')
DIRARRAY+=('/home/joey/Pictures')
DIRARRAY+=('/home/joey/Scripts')
DIRARRAY+=('/home/joey/mame')
DIRARRAY+=('/home/joey/Streams')
DIRARRAY+=('/home/joey/websites')
DIRARRAY+=('/home/joey/Temp')
DIRARRAY+=('/home/joey/Videos')
DIRARRAY+=('/home/joey/Downloads')
DIRARRAY+=("/media/joey/SD Card/Dropbox/Dropbox")
# Test: dump array
#echo ${DIRARRAY[@]}
# Create new backup destination folder
mkdir "$TARGETDIR"
#echo Backing-up folders to: "$TARGETDIR" | tee -a "$LOGFILE"
echo "$(date)|INFO|Backing-up files to: $TARGETDIR" >> "$LOGFILE"
echo "Backing-up files to: $TARGETDIR"
# Backup each folder to a separate archive
for i in ${DIRARRAY[@]}
do
#echo Processing folder: $i | tee -a "$LOGFILE"
echo "$(date)|INFO|Processing folder: $i" >> "$LOGFILE"
echo "Processing folder: $i"
SOURCEDIR=$i # full path of folder to archive
THISDIR=${i##*/} # last name of folder in path
FILENAME=$THISDIR.tar.gz # name of backup file
# strip leading slash off source directory path
SOURCEDIR=${SOURCEDIR#/}
# run archive command
tar -C / -cpzf $TARGETDIR/$FILENAME $SOURCEDIR
done
# Delete array
unset DIRARRAY
# delete lockfile
echo "$(date)|INFO|Deleting lock file" >> "$LOGFILE"
rm $LOCKFILE
# send notification
notify-send "$TITLE_BACKUP_DONE" "$MESSAGE_BACKUP_DONE" -t 0 -u Normal -i /usr/share/icons/gnome/48x48/status/network-transmit.png
fi # check if backup folder exists
# END PROCESSING
fi # check if lock file exists
echo "$(date)|INFO|Processing complete. Restoring file system separator" >> "$LOGFILE"
# restore IFS
IFS=$OLDIFS
echo "$(date)|INFO|Exiting script" >> "$LOGFILE"
echo "Exiting script: `date`"
echo -------------------------------------------------------------------------- | tee -a "$LOGFILE"
exit
When the backup script is executed at the scheduled time the following notification is displayed and files start writing to the external hard drive.
When the backup is complete a final notification is displayed letting me know I can disconnect the hard drive. In this example, the backup script does not un-mount the USB drive. I leave it mounted so I can review the files backed-up.
If, when the backup script is executed, it detects an error (like a lock file indicating a backup is already taking place) it will not backup any files and display a notification of the error.
Troubleshooting
-
USB drive not mounting when plugged in.
Check for an error in the Udev rule or the script that the rule fires off that is preventing the rule from finishing. It appears that no other rules that would’ve executed for this device will not run until yours completes – even if it is the last one.
-
Notifications sent in backup script not displayed.
This is because udev.sh is run as root and submits the backup job also as root. So, when the backup script is running it is not running as you (or another user). This is discussed in this thread o Stack Overflow: https://stackoverflow.com/questions/28195805/running-notify-send-as-root
-
Monitor syslog for events.
In another terminal window run the following to monitor the events taking place and scripts being executed: sudo tail -f /var/log/syslog
I had to install the “AT” command utility and could not find a way to script a job with “cron”. It may be possible to schedule the backup script using Systemd timers or some other mechanism. TBH Systemd is still new to me and I tend to fallback on old tried-and-true ways even if they are more expensive in time or resources.
Resources
https://www.thegeekdiary.com/how-to-run-a-script-when-usb-devices-is-attached-or-removed-using-udev/
http://migueleonardortiz.com.ar/udev/running-bash-scripts-using-udev-rules/1505
https://linuxconfig.org/tutorial-on-how-to-write-basic-udev-rules-in-linux