Force unmount of usb drive by closing open applications programatically [closed] - linux

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about a specific programming problem, a software algorithm, or software tools primarily used by programmers. If you believe the question would be on-topic on another Stack Exchange site, you can leave a comment to explain where the question may be able to be answered.
Closed 7 years ago.
Improve this question
When I unplug the AC adapter from my laptop I want all USB drives to automatically unmount. If applications are open that block the device, they should be killed. Once everything is killed and unmount a signal tone could be plaid to indicate that it's now safe to unplug it.
The use case is to quickly grab and go your laptop without having to fumble with the ui to get all drives disconnected but avoid unclean unmounts.
Any hints on how to start would be fantastic, thanks you!
ANSWER
For a full copy&paste script see my answer below.
If your USB devices mount to /mount/media do:
kill -9 $(lsof -t $(mount | grep "/mount/media" | cut -d " " -f 1)) # Exit processes blocking umount cleanly
kill $(lsof -t $(mount | grep "/mount/media" | cut -d " " -f 1)) # Force kill remaining open processes
umount $(mount | grep "/mount/media" | cut -d " " -f 1) # Unmount USB drives
Be careful with this since if you don't have blocking applications open lsof will return all pids and you will kill your running OS. See the copy&paste script below for a working implementation that handles this case.
Then call this script whenever the AC adapter is unplugged by adding this line to /etc/udev/rules.d
SUBSYSTEM=="power_supply", ACTION=="change", ATTR{online}=="0" , RUN+="/path/to/script/shown/above"
The answer below by Nuetrino shows how to detect the AC unplug event, this answer: How do I find out which process is preventing a umount? shows how to list and kill all processes blocking the device from unmounting (I had more success with lsof than fuser -c which sometimes didn't list any processes even though umount was still being blocked)
Details
Use udevadm monitor to log the event, e.g.
KERNEL[20154.545075] change /devices/LNXSYSTM:00/LNXSYBUS:00/ACPI0003:00/power_supply/ADP0 (power_supply)
then use udevadm info -a -p with the event to get the attributes
udevadm info -a -p /devices/LNXSYSTM:00/LNXSYBUS:00/ACPI0003:00/power_supply/ADP0
looking at device '/devices/LNXSYSTM:00/LNXSYBUS:00/ACPI0003:00/power_supply/ADP0':
KERNEL=="ADP0"
SUBSYSTEM=="power_supply"
DRIVER==""
ATTR{online}=="0"
ATTR{type}=="Mains"
Now you can set up the udev rules with the attributes you like as answered below.

You can define udev rules to do it.
Just put your rule in /etc/udev/rules.d
Here is a example from me which i use to control brightness when i remove the AC adapter.
SUBSYSTEM=="power_supply", ACTION=="change", ATTR{online}=="0" , RUN+="/usr/local/bin/bright_unplug"
SUBSYSTEM=="power_supply", ACTION=="change", ATTR{online}=="1" , RUN+="/usr/local/bin/bright_replug
I run my custom script 'bright_replug' and 'bright_unplug' when I recieve kernel uvent.
You can use udevadm monitor to
monitor will print the received events for: UDEV - the event which
udev sends out after rule processing KERNEL - the kernel uevent
You can use udevadm info to match more attributes

Step by step solution
1.
Create this script e.g. in /home/user/uall.sh and replace mount_root with the folder where your distribution mounts usb drives, e.g. /media/user
#!/bin/bash
mount_root=/run/media/user
echo "Try unmounting.."
umount $(ls -d -1 $mount_root/*) # Unmount USB drives
mounted=$(ls -d -1 $mount_root/*) # Probe if there are still applications blocking
if ! [ -z "$mounted" ]
then
echo "Found blocked devices: $mounted, killing.."
kill -9 $(lsof -t $mounted) # Exit processes blocking umount cleanly
kill $(lsof -t $mounted) # Force kill remaining open processes
echo "Unmounting.."
umount $(ls -d -1 $mount_root/*) # Unmount USB drives
mounted=$(ls -d -1 $mount_root/*) # Probe if there are still applications blocking
fi
if [ -z "$mounted" ]
then
echo "Success!"
echo "All USB devices umount."
paplay /usr/share/sounds/speech-dispatcher/test.wav
else
echo "Error!"
echo "Tried it all but couldn't umount all USB devices."
echo "These devices are still mounted:"
echo "$mounted"
fi
2.
Create a udev wrapper script (call it /home/user/uall-udev-wrapper) that executes uall.sh as your username:
#!/bin/bash
runuser -l <user> -c '/home/user/uall.sh > /home/user/uall.log'
3.
Create the file /etc/udev/rules.d/99-usb-unmount.rules with the content
SUBSYSTEM=="power_supply", ACTION=="change", ATTR{online}=="0" , RUN+="/home/user/uall-udev-wrapper"
4.
Reboot or run sudo udevadm control --reload-rules && udevadm trigger to load the new udev rule
Optional. Add an alias uall=/home/user/uall.sh to your ~/.bashrc to access the script easily from your terminal and use your Desktop environment to configure a hotkey to quickly unmount all usb drives
Caveats
1.
When udev runs the script mount will not display gvfsd-fuse mount points, neither will cat /proc/mounts or cat /etc/mtab not even if using the runuser -l <user> wrapper. Instead I'm using now ls -d -1 $mount_root/* which only returns the devices mounted by the current user specified in $mount_root, on a multi user system devices mounted by a different user will not be unmounted by this script.
2.
When udev runs the script I do not get audio from paplay or spd-say.
Any input on these caveats would be greatly appreciated.

Related

How to set up a udev rule for ETH "link down"/"link up"?

I like to switch on a green LED(connected through GPIOs), when eth0 is connected. When disconnected I like to switch the green LED of and a red one on.
Thought that udev is maybe the right place for it. I created the simple demo rule:
KERNEL=="eth0", SUBSYSTEM=="net", ACTION=="add", RUN+="/sbin/set_BSPleds eth0 on"
This rule should call a script when the eth0 is added. It was never executed.
After I was looking to the udev monitor by entering "udevadm monitor -k -u" at the shell. There were no events coming when I unplug/plug the lan cable.
root#sama5d3xek:/etc/udev/rules.d# udevadm monitor -k -uh0
monitor will print the received events for:
UDEV - the event which udev sends out after rule processing
KERNEL - the kernel uevent
root#sama5d3xek:/etc/udev/rules.d#
Seems that there are no uevents for eth0. The ethernet driver is provided my ATMEL. I am building a custom Linux by the help of Yocto.
My question is, how to get the "link down"/"link up" events to udev?
If it does not works with udev, what alternative way is the best?
As others have already mentioned, it seems one can't use udev for this.
Ubuntu: wait for network link up and execute a bash command suggests
inotifywait -e modify /sys/class/net/eth0/carrier; echo 'Change detected'
(Currently, / is on nfs for my box, so I can't really say if it will work.)
In other posts, there are some concerns about using the inotify API on /sys: inotify_add_watch fails on /sys/class/net/eth0/operstate .
I think the Right Way(TM) do do this would be to use the netlink(7) API, preferably through a daemon such as netplugd.
Hope that helps :)
Ethernet devices are devices, but connections are not.
You could trace connection through /dev/xconsole, dmesg or /var/log/kern.log.
Sample, using rsyslog:
You could (as root):
echo ':msg, contains, "Link is" |/run/netlink' >/etc/rsyslog.d/netlinkstate.conf
mkfifo /run/netlink
chgrp user /run/netlink
chmod 640 /run/netlink
service rsyslog force-reload
Then, logged as user, simply:
read line < /run/netlink
will wait for input from fifo, then hold until something happen.
state=${line#*Link is } eventtime=${line%% $HOSTNAME*}
echo $eventtime $state
2016-11-21T17:40:50.956181+01:00 Down
or
echo $eventtime $state
2016-11-21T17:40:50.956181+01:00 Up 100 Mbps Full Duplex, Flow Control: Rx/Tx
echo $eventtime ${state%% *}
2016-11-21T17:40:50.956181+01:00 Up
Under bash, you could use timeout for emptying fifo and read only last entry:
while read -t .01 entry </run/netlink;do line="$entry";done
state=${line#*Link is }
eventtime=${line%% $HOSTNAME*}
shortstate=${state%% *}
Nota: I've used /run to store fifo. This could be not the better place as this won't exist on next reboot.

How to Unmount all usb pen drive from ubuntu with bash script or terminal?

I want to unmount all pen drive from ubuntu os through a bash script or from a terminal. I knew the command umount /media/MEDIA_NAME, but here we need to specify each MEDIA_NAME. Can I use a single command to unmount from terminal or bash script. Please help me.
Browsing entries in /dev/disk/by-id/ allows you to select USB disks:
for usb_dev in /dev/disk/by-id/usb-*; do
dev=$(readlink -f $usb_dev)
grep -q ^$dev /proc/mounts && umount $dev
done
Loop for each device in the /media directory.
for device in /media/*
do
umount $device
done

Detecting USB Thumb Drive when Ready in Linux Shell Script

I am a Windows admin and dev, I do not generally work with Linux so forgive me if this is in some way obvious.
I have a not so good Linux box, some older version of Open SUSE, and I have a script that unmounts the USB thumb drive, formats it, and then waits for the device to become ready again before it runs a script that does a copy/MD5 checksum verification on the source and destination file to ensure the copy was valid. The problem is that on one box the USB thumb drive does not become ready after the format in a consistent way. It takes anywhere from 1 to 2+ minutes before I can access the drive via /media/LABELNAME.
The direct path is /dev/sdb but, of course, I cannot access it directly via this path to copy the files. Here is my shell script as it stands:
#!/bin/bash
set -e
echo "Starting LABELNAME.\n\nUnmounting /dev/sdb/"
umount /dev/sdb
echo "Formatting /dev/sdb/"
mkfs.vfat -I -F32 -n "LABELNAME" /dev/sdb
echo "Waiting on remount..."
sleep 30
echo "Format complete. Running make master."
perl /home/labelname_master.20120830.pl
Any suggestions? How might I wait for the drive to become ready and detect it? I have seen Detecting and Writing to a USB Key / Thumb DriveAutomatically but quite frankly I don't even know what that answer means.
It seems that you have some automatic mounting service running which detects the flash disk and mounts the partition. However, you already know what the partition is, so I recommend that you simply mount the disk in your script, choosing a suitable mount point yourself.
mkfs.vfat -I -F32 -n "LABELNAME" /dev/sdb
echo "Format complete, remounting"
mount /dev/sdb $mountpoint #<-- you would choose $mountpoint
echo "Running make master."
perl /home/labelname_master.20120830.pl

Simulate a faulty block device with read errors?

I'm looking for an easier way to test my application against faulty block devices that generate i/o read errors when certain blocks are read. Trying to use a physical hard drive with known bad blocks is a pain and I would like to find a software solution if one exists.
I did find the Linux Disk Failure Simulation Driver which allows creating an interface that can be configured to generate errors when certain ranges of blocks are read, but it is for the 2.4 Linux Kernel and hasn't been updated for 2.6.
What would be perfect would be an losetup and loop driver that also allowed you to configure it to return read errors when attempting to read from a given set of blocks.
It's not a loopback device you're looking for, but rather device-mapper.
Use dmsetup to create a device backed by the "error" target. It will show up in /dev/mapper/<name>.
Page 7 of the Device mapper presentation (PDF) has exactly what you're looking for:
dmsetup create bad_disk << EOF
0 8 linear /dev/sdb1 0
8 1 error
9 204791 linear /dev/sdb1 9
EOF
Or leave out the sdb1 parts to and put the "error" target as the device for blocks 0 - 8 (instead of sdb1) to make a pure error disk.
See also The Device Mapper appendix from "RHEL 5
Logical Volume Manager Administration".
There's also a flakey target - a combo of linear and error that sometimes succeeds. Also a delay to introduce intentional delays for testing.
It seems like Linux's built-in fault injection capabilities would be a good idea to use.
Blog: http://blog.wpkg.org/2007/11/08/using-fault-injection/
Reference: https://www.kernel.org/doc/Documentation/fault-injection/fault-injection.txt
The easiest way to play with block devices is using nbd.
Download the userland sources from git://github.com/yoe/nbd.git and modify nbd-server.c to fail at reading or writing on whichever areas you want it to fail on, or to fail in a controllably random pattern, or basically anything you want.
I would like to elaborate on Peter Cordes answer.
In bash, setup an image on a loopback device with ext4, then write a file to it named binary.bin.
imageName=faulty.img
mountDir=$(pwd)/mount
sudo umount $mountDir ## make sure nothing is mounted here
dd if=/dev/zero of=$imageName bs=1M count=10
mkfs.ext4 $imageName
loopdev=$(sudo losetup -P -f --show $imageName); echo $loopdev
mkdir $mountDir
sudo mount $loopdev $mountDir
sudo chown -R $USER:$USER mount
echo "2ed99f0039724cd194858869e9debac4" | xxd -r -p > $mountDir/binary.bin
sudo umount $mountDir
in python3 (since bash struggles to deal with binary data) search for the magic binary data in binary.bin
import binascii
with open("faulty.img", "rb") as fd:
s = fd.read()
search = binascii.unhexlify("2ed99f0039724cd194858869e9debac4")
beg=0
find = s.find(search, beg); beg = find+1; print(find)
start_sector = find//512; print(start_sector)
then back in bash mount the faulty block device
start_sector=## copy value from variable start_sector in python
next_sector=$(($start_sector+1))
size=$(($(wc -c $imageName|cut -d ' ' -f1)/512))
len=$(($size-$next_sector))
echo -e "0\t$start_sector\tlinear\t$loopdev\t0" > fault_config
echo -e "$start_sector\t1\terror" >> fault_config
echo -e "$next_sector\t$len\tlinear\t$loopdev\t$next_sector" >> fault_config
cat fault_config | sudo dmsetup create bad_drive
sudo mount /dev/mapper/bad_drive $mountDir
finally we can test the faulty block device by reading a file
cat $mountDir/binary.bin
which produces the error:
cat: /path/to/your/mount/binary.bin: Input/output error
clean up when you're done with testing
sudo umount $mountDir
sudo dmsetup remove bad_drive
sudo losetup -d $loopdev
rm fault_config $imageName

Linux: Which process is causing "device busy" when doing umount? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about a specific programming problem, a software algorithm, or software tools primarily used by programmers. If you believe the question would be on-topic on another Stack Exchange site, you can leave a comment to explain where the question may be able to be answered.
Closed 4 years ago.
Improve this question
Linux: Which process is causing "device busy" when doing umount?
Look at the lsof command (list open files) -- it can tell you which processes are holding what open. Sometimes it's tricky but often something as simple as sudo lsof | grep (your device name here) could do it for you.
Just in case... sometimes happens that you are calling umount from the terminal, and your current directory belongs to the mounted filesystem.
You should use the fuser command.
Eg. fuser /dev/cdrom will return the pid(s) of the process using /dev/cdrom.
If you are trying to unmount, you can kill theses process using the -k switch (see man fuser).
Check for open loop devices mapped to a file on the filesystem with "losetup -a". They wont show up with either lsof or fuser.
Also check /etc/exports. If you are exporting paths within the mountpoint via NFS, it will give this error when trying to unmount and nothing will show up in fuser or lsof.
lsof +f -- /mountpoint
(as lists the processes using files on the mount mounted at /mountpoint. Particularly useful for finding which process(es) are using a mounted USB stick or CD/DVD.
lsof and fuser are indeed two ways to find the process that keeps a certain file open.
If you just want umount to succeed, you should investigate its -f and -l options.
That's exactly why the "fuser -m /mount/point" exists.
BTW, I don't think "fuser" or "lsof" will indicate when a resource is held by kernel module, although I don't usually have that issue..
Open files
Processes with open files are the usual culprits. Display them:
lsof +f -- <mountpoint or device>
There is an advantage to using /dev/<device> rather than /mountpoint: a mountpoint will disappear after an umount -l, or it may be hidden by an overlaid mount.
fuser can also be used, but to my mind lsof has a more useful output. However fuser is useful when it comes to killing the processes causing your dramas so you can get on with your life.
List files on <mountpoint> (see caveat above):
fuser -vmM <mountpoint>
Interactively kill only processes with files open for writing:
fuser -vmMkiw <mountpoint>
After remounting read-only (mount -o remount,ro <mountpoint>), it is safe(r) to kill all remaining processes:
fuser -vmMk <mountpoint>
Mountpoints
The culprit can be the kernel itself. Another filesystem mounted on the filesystem you are trying to umount will cause grief. Check with:
mount | grep <mountpoint>/
For loopback mounts, also check the output of:
losetup -la
Anonymous inodes (Linux)
Anonymous inodes can be created by:
Temporary files (open with O_TMPFILE)
inotify watches
[eventfd]
[eventpoll]
[timerfd]
These are the most elusive type of pokemon, and appear in lsof's TYPE column as a_inode (which is undocumented in the lsof man page).
They won't appear in lsof +f -- /dev/<device>, so you'll need to:
lsof | grep a_inode
For killing processes holding anonymous inodes, see: List current inotify watches (pathname, PID).
lsof and fuser didn't give me anything either.
After a process of renaming all possible directories to .old and rebooting the system every time after I made changes I found one particular directory (relating to postfix) that was responsible.
It turned out that I had once made a symlink from /var/spool/postfix to /disk2/pers/mail/postfix/varspool in order to minimise disk writes on an SDCARD-based root filesystem (Sheeva Plug).
With this symlink, even after stopping the postfix and dovecot services (both ps aux as well as netstat -tuanp didn't show anything related) I was not able to unmount /disk2/pers.
When I removed the symlink and updated the postfix and dovecot config files to point directly to the new dirs on /disk2/pers/ I was able to successfully stop the services and unmount the directory.
Next time I will look more closely at the output of:
ls -lR /var | grep ^l | grep disk2
The above command will recursively list all symbolic links in a directory tree (here starting at /var) and filter out those names that point to a specific target mount point (here disk2).
If you still can not unmount or remount your device after stopping all services and processes with open files, then there may be a swap file or swap partition keeping your device busy. This will not show up with fuser or lsof. Turn off swapping with:
sudo swapoff -a
You could check beforehand and show a summary of any swap partitions or swap files with:
swapon -s
or:
cat /proc/swaps
As an alternative to using the command sudo swapoff -a, you might also be able to disable the swap by stopping a service or systemd unit. For example:
sudo systemctl stop dphys-swapfile
or:
sudo systemctl stop var-swap.swap
In my case, turning off swap was necessary, in addition to stopping any services and processes with files open for writing, so that I could remount my root partition as read only in order to run fsck on my root partition without rebooting. This was necessary on a Raspberry Pi running Raspbian Jessie.
Filesystems mounted on the filesystem you're trying to unmount can cause the target is busy error in addition to any files that are in use. (For example when you mount -o bind /dev /mnt/yourmount/dev in order to use chroot there.)
To find which file systems are mounted on the filesystem run the following:
mount | grep '/mnt/yourmount'
To find which files are in use the advice already suggested by others here:
lsof | grep '/mnt/yourmount'

Resources