Ubuntu18上基于udev实现U盘热插拔+自动化处理业务功能

 编者按 

        在嵌入式linux系统中,往往可以基于mdev来实现U盘/SD卡的热插拔功能。在mdev.conf配置文件中,可以对捕捉到的插、拔事件指定执行的命令/脚本,具体配置方法此处不再赘述。这也就意味着我们可以捕获U盘等外设的插、拔事件并进一步作出相应的特殊处理。

        在Ubuntu系统中,本身已经具备U盘热插拔功能,但却不能很方便地对U盘的插、拔事件作出特殊的后续处理。一种方法是,定时巡查/media目录来检测是否有U盘插、拔事件发生,但这往往是傻瓜且低效的。而Udev实在是一种不错的选择。不过在Ubuntu14升级到Ubuntu18后,udev热插拔功能却无法正常使用。

        本文,仅基于实际项目经验对基于Udev实现设备热插拔检测稍作探讨及记录。对Udev和mdev的工作机制、配置文件的细节信息不作赘述。

1.捕捉U盘事件

        首先需要编写规则文件捕捉U盘插、拔事件,本配置仅捕捉处理sdbX、sdcX设备。具体匹配规则可见udev介绍。

1.在/etc/udev/rules.d目录下新建规则文件1-persistent-USB.rules

2.在规则文件中加入以下规则,以实现捕捉U盘插入、拔出事件并执行 handle脚本

ACTION=="add|change", SUBSYSTEM=="block", KERNEL=="sd[bc][0-9]",  MODE:="0777",  SYMLINK+="Myusb%n" ,  RUN+="/etc/udev/hotplug/usb/usb_handle" 

SUBSYSTEM=="block", KERNEL=="sd[bc][0-9]", ACTION=="remove", RUN+="/etc/udev/hotplug/usb/usb_handle"

 2.编写U盘事件处理脚本

        执行脚本如下所示 ,主要分为3个步骤,mount 挂载、执行升级等自定义特殊操作、再次挂载到用户空间(U14:mount、U18:systemd-mount),这里需要特别注意的是,mount命令在U16以上系统中,无法挂载到用户空间,即挂载成功后仅在脚本内可见,脚本退出后,对其他命名空间不可见。通过df等命令均无法观察到挂载信息,程序自然也就无法操作,对于Ubuntu16实测不可使用,在U16上没有systemd-mount命令,需编写system [Automount]配置项文件来完成。

#!/bin/bash

UFail()
{
	return 1
}


Insert_to_tmp() 
{
	meg="The device does not exist or the mount point has been mounted by another device."
	if [ -d /sys/block/*/${name} ] ; then
		chk=`df | grep "$mount_dir" -q;echo $?`
		if [ "$chk" == "0"  ]
		then
			exit 1
		fi
		if [ ! -d $mount_dir ]
                then
                        /bin/mkdir -p $mount_dir

                fi	
	       	
		if [ "$mode" = "0" ]
		then	
			msg=`$Mount -o noatime -o nodiratime -o  umask=000 -t auto  $DEVNAME $mount_dir 2>&1`
		else
			msg=`$Mount --no-block --automount=yes --collect $DEVNAME $mount_dir 2>&1`
		fi
		
	fi
	
	msg=`echo "$msg" | xargs `

	if [ -x /sh/hotplug.sh ] ; then
        	/sh/hotplug.sh $mount_dir $name "$msg"
	fi

}
Insert() 
{
	if [ $# -le 0 ]
	then
		return 1
	fi
	InsertMode=$1

	meg="The device does not exist or the mount point has been mounted by another device."
	if [ -d /sys/block/*/${name} ] ; then
		chk=`df | grep "$mount_dir" -q;echo $?`
		if [ "$chk" == "0"  ]
		then
			exit 1
		fi
		if [ ! -d $mount_dir ]
                then
                        /bin/mkdir -p $mount_dir

                fi	
	 	if [ "$InsertMode" = "0" ]
		then	
			msg="insert to namespace."
			if [ "$mode" = "0" ]
			then	
				msg+=`$Mountold -o noatime -o nodiratime -o  umask=000 -t auto  $DEVNAME $mount_dir 2>&1`
			else
				msg+=`$Mount --no-block --automount=yes --collect $DEVNAME $mount_dir 2>&1`
			fi
		else
			msg="insert to udev namespace."
			msg+=`$Mountold -o noatime -o nodiratime -o  umask=000 -t auto  $DEVNAME $mount_dir 2>&1`
		fi
		
	fi
	
	msg=`echo "$msg" | xargs `

	if [ -x /sh/hotplug.sh ] ; then
        	/sh/hotplug.sh $mount_dir $name "$msg"
	fi

}


Remove()
{

	 if [ $# -le 0 ]
	 then
		 return 1
	 fi
	 InsertMode=$1


	mountinfo=`/bin/cat /etc/mtab | /bin/grep "$DEVNAME" -w`
	if [ -z "$mountinfo" ]
	then 
		meg="All hardpoints of the device have been unmounted."

		if [ -x /sh/hotplug.sh ] ; then
			/sh/hotplug.sh $mountdir $name "$msg"
		fi
		exit 1
	fi
	

	IFSOLD=$IFS          #默认的IFS的数值-->也是环境变量!
	IFS=$'\n'             #自定义的IFS数值 
	
	for i in $mountinfo
	do
		mountdir=`echo $i | /usr/bin/awk '{print $2}'`
		sync

		if [ "$InsertMode" = "0" ] 
		then
			msg="remove from namespace."
			if [ "$mode" = "0" ]
			then
        			msg+=`$Umountold -f -l $mountdir 2>&1`
			else
				msg+=`$Umount -u $mountdir 2>&1`
			fi
		else
			msg="remove from udev namespace."
			msg+=`$Umountold -f -l $mountdir 2>&1`
		fi
		msg=`echo "$msg" | xargs`

		if [ -x /sh/hotplug.sh ] ; then
               		/sh/hotplug.sh $mountdir $name "$msg"
       		fi 
	done
	
	IFS=$IFSOLD



}


Do_special()
{
	#此处自行实现特殊处理,自动检测到升级标志自动进行升级、更新等自定义操作
	#do_Install || do_Restore
}



prefix_path=$(cd $(dirname $0);pwd)/
name="`basename "$DEVNAME"`"
mount_dir="/udisk"


Mountold=/bin/mount
Umountold=/bin/umount
mode=0

#/lib/systemd/systemd-udevd./lib/systemd/systemd-udevd:
#systemd by default runs systemd-udevd.service with a separate "mount namespace",which means that mounts will not be visible to the rest of the system.
#Even if you change the service parameters to fix this (commenting out the PrivateMounts and MountFlags lines), there is another problem which is that processes started from Udev are killed after a few seconds. 
#In case of FUSE filesystems, such as exFAT (which is common to find on mp3 players or shareable thumb drives) and NTFS, "mount" starts a user-space process to handle the filesystem internals; 
#when this is killed you will get Transport endpoint not connected errors if you try to access the filesystem.
#There are some options that work:
#     1)Start a custom systemd service from the Udev rule; the systemd service can invoke a script which can start any number of long-running processes (like FUSE).
#A concise example which automatically mounts USB disks under /media is udev-media-automount. It was found to be fast and reliable. A variant of the same idea is explained in this blog post.
#     2)Use systemd-mount instead of mount in your Udev rule. This is recommended by systemd developers. 

if type "/usr/bin/systemd-mount" &>/dev/null
then
	
	Mount=/usr/bin/systemd-mount 
	Umount=/usr/bin/systemd-mount 
	mode=1

fi

if [ $ACTION = "add" ] 
then
	#Mount the device into the current namespace to perform some special operations
	Insert 1
	Do_special
	Remove 1
	#Mount the device into the  namespace
	Insert 0
elif [ $ACTION = "remove" ]
then
	Remove 0
fi



 3.测试前准备

1.修改udev为调试模式,方便在/var/log/syslog中定位日志

2.使规则生效,后期修改规则文件时必须执行udev重启生效,单纯修改usb_handle执行脚本无需重启。

        service udev restart  

3.借助工具测试规则是否可以匹配

    使用udevadm test 模拟测试设备插入事件,查看打印信息确认配置无误。

   udevadm test -a add /sys/class/block/sdc1

 下图可知规则已被找到

 下图可知执行脚本可以被执行

 其中/sys/class/block/sdc1是具体的设备,根据实际情况修改即可

 3.测试

          插入U盘、拔出U盘,可通过df 命令、syslog日志观察到设备是否成功挂载。

        下面是/sh/hotplug.sh打印的信息,该脚本需要自行编写

  4.后记

        Udev挂在设备时,在U16版本前,可以使用mount命令直接挂载,umount卸载,这与嵌入式中mdev是一样的,U16版本之后,则必须使用systemd-mount进行挂载、卸载。否则会因为命名空间的限制而无法被其他空间使用,仅在udev命名空间可见。

        

挂载命名空间隔离挂载点隔离文件目录进程运行时可以将挂载点与系统分离,使用这个功能时,我们可以达到 chroot 的功能,而在安全性方面比 chroot 更高
  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值