通过以编程方式关闭打开的应用程序强制卸载 USB 驱动器

Force unmount of usb drive by closing open applications programatically

当我从笔记本电脑上拔下交流适配器时,我希望所有 USB 驱动器都自动卸载。如果打开的应用程序阻止了设备,则应将其杀死。一旦所有的东西都被杀死并卸载,一个信号音可以表示现在可以安全地拔掉它了。

用例是 quickly 拿走你的笔记本电脑,而不必摸索 ui 来断开所有驱动器,但避免不干净的卸载。

任何关于如何开始的提示都很棒,谢谢!

回答

For a full copy&paste script see my answer below.


如果您的 USB 设备挂载到 /mount/media,请执行以下操作:

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

注意这一点,因为如果您没有打开阻止应用程序,lsof 将 return 所有 pids 并且您将 kill 您的 运行 OS。请参阅下面的复制和粘贴脚本,了解处理这种情况的有效实施。

然后通过将此行添加到 /etc/udev/rules.d

,每当交流适配器被拔出时调用此脚本
SUBSYSTEM=="power_supply", ACTION=="change", ATTR{online}=="0" ,     RUN+="/path/to/script/shown/above"

Nuetrino 下面的回答显示了如何检测交流电拔出事件,这个回答: How do I find out which process is preventing a umount? 显示了如何列出并终止所有阻止设备卸载的进程(我使用 lsof 比使用 fuser 更成功 - c 有时不列出任何进程,即使 umount 仍然被阻止)

详情

使用udevadm monitor记录事件,例如

KERNEL[20154.545075] change   /devices/LNXSYSTM:00/LNXSYBUS:00/ACPI0003:00/power_supply/ADP0 (power_supply)

然后将 udevadm info -a -p 与事件一起使用以获取属性

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"

现在您可以使用您喜欢的属性设置 udev 规则,如下所述。

您可以定义 udev 规则来做到这一点。

只需将您的规则放入 /etc/udev/rules.d

这是我的一个例子,当我移除交流适配器时,我用它来控制亮度。

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

我 运行 我的自定义脚本 'bright_replug' 和 'bright_unplug' 当我收到内核 uvent 时。

您可以使用 udevadm monitor

monitor will print the received events for: UDEV - the event which udev sends out after rule processing KERNEL - the kernel uevent

您可以使用udevadm info匹配更多属性

一步步解决

1。 创建此脚本,例如在 /home/user/uall.sh 中并将 mount_root 替换为您的发行版安装 USB 驱动器的文件夹,例如/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。 创建一个 udev 包装器脚本(称之为 /home/user/uall-udev-wrapper)执行 uall.sh 作为您的用户名:

#!/bin/bash
runuser -l <user> -c '/home/user/uall.sh > /home/user/uall.log'

3。 使用内容

创建文件 /etc/udev/rules.d/99-usb-unmount.rules
SUBSYSTEM=="power_supply", ACTION=="change", ATTR{online}=="0" ,     RUN+="/home/user/uall-udev-wrapper"

4。 重启或 运行 sudo udevadm control --reload-rules && udevadm trigger 加载新的 udev 规则

可选。将 alias uall=/home/user/uall.sh 添加到您的 ~/.bashrc 以便从您的终端轻松访问脚本并使用您的桌面环境配置热键以快速卸载所有 USB 驱动器

注意事项

1。 当 udev 运行s 脚本 mount 将不会显示 gvfsd-fuse 挂载点,即使使用 runuser -l <user> 包装器也不会 cat /proc/mountscat /etc/mtab .相反,我现在使用 ls -d -1 $mount_root/*,只有 returns 由 $mount_root 中指定的当前用户安装的设备,在由不同用户安装的多用户系统设备上,不会被卸载脚本。

2。 当 udev 运行s 脚本时,我没有从 paplayspd-say.

获取音频

如有任何意见,我们将不胜感激。