Linux
树莓派3B 采用Samba + Cockpit搭建NAS
Linux 修改开机启动 boot splash logos
Linux 下将 .po 文件编译成 .mo 文件
Python 使用 gettext 模块实现国际化
XFCE 桌面优化配置
香橙派3B 使用 WiringOP 读取 MPU6050
香橙派4 LTS 安装 WiringPi
Linux 图形界面切换到 FB 界面
校表仪 tg-timer 算法笔记
Linux 安装 EtherCat Igh
EetherCat Igh 常用指令
树莓派镜像 DIY 制作
树莓派5B 安装64位实时系统 preempt RT 补丁
linux 的 lightdm 桌面管理器下隐藏光标(鼠标)
Linux 上使用 MPD (Music Player Daemon)
Linux 自动化交互式命令行工具 —— expect
Linux GTK 界面开发
Debian系统中修改主机名
使用Python + GTK3.0 开发嵌入式GUI
Raspberry Pi 5修改系统内存分页大小解决安装esp-matter 出现的<jemalloc>: Unsupported systeom page size 错误
树莓派 linux 安装和使用clash 做VPN
树莓派自动挂载硬盘
非控制台或ssh登录用户尝试启动Xorg图形服务器
XFCE4 电源管理器配置管理
Linux PulseAudio 声卡配置和检测
IBUS修改输入法和设置切换快捷键
在 Moode Audio 中实现开机自动启动 `startx`
crontab 任务
树莓派外设耗电检测
linux 终端自动登录
linux创建新用户
linux service通过xinit启动GTK应用程序
Linux 非桌面的图形环境安装和配置
香橙派3b板载音频输出没有声音调试
使用 udev 规则修改权限实现屏幕亮度调节
Linux udev 介绍
鲁班猫4 ubuntu22.04 屏幕背光 udev 自动配置化
ubuntu22.04 动态壁纸配置
使用 udisksctl 的可靠 USB 自动挂载方案(基于 systemd-run)
通过udev规则U盘自动挂载
OpenWRT 磊科N60pro路由器刷机
本文档使用 MrDoc 发布
-
+
首页
使用 udisksctl 的可靠 USB 自动挂载方案(基于 systemd-run)
# 使用 udisksctl 的可靠 USB 自动挂载方案(基于 systemd-run) 以下是使用 `udisksctl` 的完整解决方案,通过 `systemd-run` 确保在正确的用户会话中执行: ``` # 安装必要的文件系统支持 sudo apt update sudo apt install -y exfat-fuse exfat-utils ntfs-3g # 安装缺失的依赖库 sudo apt install libblockdev-crypto2 libblockdev-mdraid2 sudo apt install -y udisks2 # 启动 udisks2服务 sudo systemctl enable udisks2 sudo systemctl start udisks2 # 查看服务状态 systemctl status udisks2 ``` ## 完整实现 ### 1. 创建主脚本 (`/usr/local/bin/udisks_usb_mount.sh`) ``` #!/bin/bash # 日志函数 log() { logger -t "udisks_mount" "$@" echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> /var/log/udisks_mount.log } # 确保必要环境变量存在 if [ -z "$ACTION" ] || [ -z "$DEVNAME" ]; then log "错误: 缺少必要的环境变量 (ACTION=$ACTION, DEVNAME=$DEVNAME)" exit 1 fi # 获取当前活动用户 get_active_user() { # 尝试获取拥有桌面的用户 user=$(ps -eo user,cmd | grep -E 'Xorg|Xwayland' | grep -v grep | awk '{print $1}' | uniq | head -n 1) # 如果没有找到,使用第一个登录的用户 if [ -z "$user" ]; then user=$(who | awk '{print $1}' | head -n 1) fi # 默认使用 biqu 用户 [ -z "$user" ] && user="biqu" echo "$user" } # 在用户会话中执行命令 run_in_user_session() { local user="$1" local command="$2" # 获取用户ID user_id=$(id -u "$user") # 获取DBUS地址 dbus_address="unix:path=/run/user/$user_id/bus" # 执行命令 sudo -u "$user" DBUS_SESSION_BUS_ADDRESS="$dbus_address" sh -c "$command" } # 挂载USB设备 handle_mount() { device="$DEVNAME" # user=$(get_active_user) user="biqu" # 默认使用 biqu 用户 log "处理挂载事件: $device (用户: $user)" # 等待设备就绪 (最多3秒) for i in {1..6}; do if [ -b "$device" ]; then log "设备准备就绪: $device" break fi sleep 0.5 done if [ ! -b "$device" ]; then log "错误: 设备不存在或未就绪: $device" return 1 fi # 检查是否已挂载 mount_info=$(run_in_user_session "$user" "/usr/bin/udisksctl info -b $device") if echo "$mount_info" | grep -q "MountPoints:.*/"; then mount_point=$(echo "$mount_info" | awk -F': ' '/MountPoints:/ {print $2}') log "设备已挂载: $mount_point" return 0 fi # 执行挂载 log "执行挂载命令: udisksctl mount -b $device" mount_output=$(run_in_user_session "$user" "/usr/bin/udisksctl mount -b $device" 2>&1) if [ $? -eq 0 ]; then mount_point=$(echo "$mount_output" | awk -F' at ' '{print $2}' | tr -d '.') # 创建软链接 sudo ln -s $mount_point /home/biqu/printer_data/gcodes log "成功挂载 $device 到 $mount_point" return 0 else log "挂载失败: $mount_output" # 备选方案: 直接挂载 log "尝试备选挂载方案" mount_dir="/media/usb-$(basename "$device")" mkdir -p "$mount_dir" mount -o uid=$(id -u "$user"),gid=$(id -g "$user") "$device" "$mount_dir" 2>&1 | while read line; do log "$line"; done if [ $? -eq 0 ]; then # 创建软链接 sudo ln -s $mount_dir /home/biqu/printer_data/gcodes log "备选挂载成功: $mount_dir" return 0 else log "备选挂载失败" rmdir "$mount_dir" 2>/dev/null return 1 fi fi } # 卸载USB设备 handle_unmount() { device="$DEVNAME" user=$(get_active_user) log "处理卸载事件: $device (用户: $user)" # 获取挂载点 mount_info=$(run_in_user_session "$user" "/usr/bin/udisksctl info -b $device") mount_points=$(echo "$mount_info" | awk -F': ' '/MountPoints:/ {print $2}') # 卸载所有挂载点 if [ -n "$mount_points" ]; then log "卸载挂载点: $mount_points" run_in_user_session "$user" "/usr/bin/udisksctl unmount -b $device" 2>&1 | while read line; do log "$line"; done fi # 确保设备断电 log "安全移除设备" run_in_user_session "$user" "/usr/bin/udisksctl power-off -b $device" 2>&1 | while read line; do log "$line"; done # 清理备选挂载点 mount_dir="/media/usb-$(basename "$device")" if [ -d "$mount_dir" ]; then log "清理备选挂载点: $mount_dir" if mountpoint -q "$mount_dir"; then umount "$mount_dir" 2>&1 | while read line; do log "$line"; done fi rmdir "$mount_dir" 2>/dev/null fi log "设备已安全移除: $device" } # 主执行逻辑 case "$ACTION" in add) handle_mount ;; remove) handle_unmount ;; *) log "未知动作: $ACTION" exit 1 ;; esac exit 0 ``` ### 2. 创建 systemd 服务文件 (`/etc/systemd/system/udisks-mount@.service`) ``` [Unit] Description=USB Auto Mount Service for %i After=multi-user.target [Service] Type=oneshot Environment="ACTION=add" Environment="DEVNAME=/dev/%i" ExecStart=/usr/local/bin/udisks_usb_mount.sh [Install] WantedBy=multi-user.target ``` ### 3. 创建 udev 规则 (`/etc/udev/rules.d/99-udisks-usb-mount.rules`) ``` # 挂载事件 ACTION=="add", SUBSYSTEM=="block", KERNEL=="sd[a-z][0-9]*", ENV{ID_BUS}=="usb", \ TAG+="systemd", \ ENV{SYSTEMD_WANTS}="udisks-mount@%k.service" # 卸载事件 ACTION=="remove", SUBSYSTEM=="block", KERNEL=="sd[a-z][0-9]*", ENV{ID_BUS}=="usb", \ RUN+="/bin/systemd-run --uid=$(id -u biqu) --gid=$(id -g biqu) --property=Environment='ACTION=remove' --property=Environment='DEVNAME=/dev/%k' /usr/local/bin/udisks_usb_mount.sh" ``` ### 4. 设置权限和配置 ``` # 设置脚本权限 sudo chmod +x /usr/local/bin/udisks_usb_mount.sh # 创建日志文件 sudo touch /var/log/udisks_mount.log sudo chmod 644 /var/log/udisks_mount.log # 重载 systemd 配置 sudo systemctl daemon-reload # 重载 udev 规则 sudo udevadm control --reload-rules sudo udevadm trigger # 启用用户 linger (确保用户服务在无登录时也能运行) sudo loginctl enable-linger biqu ``` ### 5. 配置 sudoers (可选) ``` # 允许 biqu 用户无需密码运行 udisksctl echo "biqu ALL = NOPASSWD: /usr/bin/udisksctl" | sudo tee /etc/sudoers.d/biqu-udisks ``` ## 方案工作原理 ### 1. 设备插入流程 ``` sequenceDiagram participant UDEV participant Systemd participant MountScript UDEV->>Systemd: 检测到USB插入 (add事件) Systemd->>MountScript: 启动 udisks-mount@sda1.service MountScript->>MountScript: 获取活动用户 MountScript->>MountScript: 在用户会话中执行 udisksctl mount MountScript->>Systemd: 返回结果 Systemd->>UDEV: 完成处理 ``` ### 2. 设备移除流程 ``` sequenceDiagram participant UDEV participant SystemdRun participant MountScript UDEV->>SystemdRun: 检测到USB移除 (remove事件) SystemdRun->>MountScript: 立即执行脚本 (ACTION=remove) MountScript->>MountScript: 获取活动用户 MountScript->>MountScript: 在用户会话中执行 udisksctl unmount MountScript->>MountScript: 执行 udisksctl power-off MountScript->>SystemdRun: 返回结果 SystemdRun->>UDEV: 完成处理 ``` ## 关键优势 1. **正确的用户上下文**: - 通过 `systemd-run` 确保在用户会话中执行 - 自动设置 `DBUS_SESSION_BUS_ADDRESS` 2. **双重保障机制**: - 优先使用 `udisksctl` 进行挂载/卸载 - 提供备选直接挂载方案 3. **智能用户检测**: ``` get_active_user() { # 尝试获取桌面用户 # 回退到第一个登录用户 # 默认使用 biqu 用户 } ``` 4. **完善的日志系统**: - 记录所有操作到 `/var/log/udisks_mount.log` - 同时使用系统日志工具 `logger` 5. **安全移除保障**: ``` # 确保设备断电 run_in_user_session "$user" "/usr/bin/udisksctl power-off -b $device" ``` ## 调试与监控 ### 查看实时日志 ``` tail -f /var/log/udisks_mount.log ``` ### 检查服务状态 ``` # 查看挂载服务状态 systemctl status udisks-mount@sda1.service # 查看用户会话 loginctl list-sessions ``` ### 手动测试 ``` # 模拟设备添加 sudo ACTION=add DEVNAME=/dev/sdb1 /usr/local/bin/udisks_usb_mount.sh # 模拟设备移除 sudo ACTION=remove DEVNAME=/dev/sdb1 /usr/local/bin/udisks_usb_mount.sh ``` ### 验证挂载 ``` # 查看挂载点 udisksctl status lsblk -f # 检查权限 ls -ld /media/* ``` ## 常见问题解决 1. **挂载失败:Error looking up object for device** - 确保用户 linger 已启用:`sudo loginctl enable-linger biqu` - 检查 DBus 会话:`sudo -u biqu dbus-run-session -- echo OK` 2. **权限问题** - 添加用户到必要组: ``` sudo usermod -aG plugdev,disk biqu ``` 3. **设备未就绪** - 增加等待时间(修改脚本中的 sleep 参数) - 在 udev 规则中添加延迟: ``` ACTION=="add", SUBSYSTEM=="block", ..., RUN+="/bin/sleep 1" ``` 4. **卸载不完全** - 确保使用 ``` power-off ``` 命令: ``` udisksctl power-off -b $device ``` 这个方案结合了 `udisksctl` 的便利性和 `systemd-run` 的环境保障,确保 USB 设备在各种情况下都能正确挂载和卸载,同时提供详细的日志记录以便于调试。
qingkai
2025年7月10日 16:39
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码