hal, pmount, dbus 를 이용한 usb 디바이스 자동마운트하기
최신의 배포판에서는 gnome 데스크탑의 gnome-volume-manager가 usb 장치를 연결하면 자동마운트를 해주고 있습니다. 여기서 소개하려는 팁은 gnome-volume-manager를 사용할 수 없거나 gnome-voume-manager가 제어할 수 없는 마운트옵션을 사용하고자 할 경우를 위한 것입니다
hal(Hardware Abstraction Layer)을 가지고 있는 리눅스 시스템은 그것을 통해서 디바이스의 상태변화를 모니터링할 수 있습니다. gnome-volume-manager도 이놈으로부터 usb나 cdrom의 연결및 분리를 감지해내서 마운트/언마운트를 하는 걸로 알고 있습니다. pmount는 일반유저가 장치를 마운트/언마운트를 할 수 있게 해주는 동시에 이패키지의 pmount-hal이라는 명령어는 hal로부터의 정보로부터 마운트디바이스/포인트/옵션등을 넘겨받아 마운트 해줄 수 있습니다. 그리고 마지막으로 dbus는 hal정보를 얻기위한 프로그래밍 인터페이스로서 사용하였습니다.
이 팁은 우분투 breezy를 기준으로 해서 만들어졌기 때문에 다른 배포판에서는 작동하지 않을 수도 있습니다
제가 80G 외장형 usbhd와 mpio mp3를 가지고 있는데 이것을 가지고 설명을 하겠습니다. 일단 hal을 다음과 같이 설치합니다
$ apt-get install hal
lshal이라는 명령어로 hal의 의해 감지된 디바이스의 정보를 볼수 있는데 usbhd의 경우는 다음과 같습니다
udi = '/org/freedesktop/Hal/devices/volume_uuid_d69f9cf1_e376_407a_a203_cb97192aa7ea'
volume.policy.desired_mount_point = 'usbdisk' (string)
volume.policy.mount_filesystem = 'ext3' (string)
volume.policy.should_mount = true (bool)
info.udi = '/org/freedesktop/Hal/devices/volume_uuid_d69f9cf1_e376_407a_a203_cb97192aa7ea' (string)
info.product = 'Volume (ext3)' (string)
volume.size = 80023716864 (0x12a1c90400) (uint64)
volume.num_blocks = 156296322 (0x950e482) (int)
volume.block_size = 512 (0x200) (int)
volume.partition.number = 1 (0x1) (int)
info.capabilities = {'volume', 'block'} (string list)
info.category = 'volume' (string)
volume.is_partition = true (bool)
volume.is_disc = false (bool)
volume.is_mounted = false (bool)
volume.mount_point = '' (string)
volume.label = '' (string)
volume.uuid = 'd69f9cf1-e376-407a-a203-cb97192aa7ea' (string)
volume.fsversion = '' (string)
volume.fsusage = 'filesystem' (string)
volume.fstype = 'ext3' (string)
block.storage_device = '/org/freedesktop/Hal/devices/storage_model_MK8025GAS'
(string)
block.is_volume = true (bool)
block.minor = 17 (0x11) (int)
block.major = 8 (0x8) (int)
block.device = '/dev/sdb1' (string)
linux.hotplug_type = 3 (0x3) (int)
info.parent = '/org/freedesktop/Hal/devices/storage_model_MK8025GAS' (string)
linux.sysfs_path_device = '/sys/block/sdb/sdb1' (string)
linux.sysfs_path = '/sys/block/sdb/sdb1' (string)
다음은 mpio의 경우입니다
udi = '/org/freedesktop/Hal/devices/volume_uuid_8C78_DAD7'
volume.policy.desired_mount_point = 'MPIO FL300' (string)
volume.policy.mount_filesystem = 'vfat' (string)
volume.policy.should_mount = true (bool)
info.udi = '/org/freedesktop/Hal/devices/volume_uuid_8C78_DAD7' (string)
info.product = 'MPIO FL300' (string)
volume.size = 523763712 (0x1f380000) (uint64)
volume.num_blocks = 1022976 (0xf9c00) (int)
volume.block_size = 512 (0x200) (int)
info.capabilities = {'volume', 'block'} (string list)
info.category = 'volume' (string)
volume.is_partition = true (bool)
volume.is_disc = false (bool)
volume.is_mounted = false (bool)
volume.mount_point = '' (string)
volume.label = 'MPIO FL300' (string)
volume.uuid = '8C78-DAD7' (string)
volume.fsversion = 'FAT16' (string)
volume.fsusage = 'filesystem' (string)
volume.fstype = 'vfat' (string)
block.storage_device = '/org/freedesktop/Hal/devices/storage_model_FL300' (string)
block.is_volume = true (bool)
block.minor = 32 (0x20) (int)
block.major = 8 (0x8) (int)
block.device = '/dev/sdc' (string)
linux.hotplug_type = 3 (0x3) (int)
info.parent = '/org/freedesktop/Hal/devices/storage_model_FL300' (string)
linux.sysfs_path_device = '/sys/block/sdc/fakevolume' (string)
linux.sysfs_path = '/sys/block/sdc/fakevolume' (string)
여기서 주목해야 할 속성들은 다음과 같습니다
udi : hal데이터베이스의 고유한 id
block.is_volume : 마운트할 수 있는 block 디바이스인지 판단
volume.is_mounted : 마운트되어 있는지 판단
volume.policy.should_mount : 마운트되어야 할지 판단
volume.policy.desired_mount_point : /media아래의 마운트 포인터(디렉토리) pmont-hal마운트시 자동생성되고 언마운트시 자동삭제됨
volume.uuid : 마운트할 파일시스템의 고유한 id
$sudo apt-get install pmount
pmount-hal을 가지고 usbhd를 마운트하려면 'pmount-hal <udi>'를 해주면 다음과 같이 마운트 된것을 확인할 수 있습니다
/dev/sdb1 on /media/usbdisk type ext3 (rw,noexec,nosuid,nodev)
여기서 여러개의 usbhd를 가지고 있고 각각마다 고유한 마운트포인트를 갖게 만들고 싶으면 각각의 usbhd가 가지고 있는 volume.uuid에 대응하는 마운트 포인터를 아래처럼 변경해주면 됩니다. 또 마운트할때 noexec옵션을 제거하고 싶으면(이것때문에 마운트된 usbhd에 있는 실행파일들을 실행할수 없습니다) 'volume.policy.mount_option.exec'이라는 옵션을 임의로 만들어주어서 아래처럼 추가해 줍니다
my_storage.fdi:
<?xml version="1.0" encoding="ISO-8859-1"?> <!-- -*- SGML -*- --> <deviceinfo version="0.2"> <!-- Example: Match a volume from an USB Storage Based mp3 player by the file system UUID and assign a mount point. NB: When reformatting the volume a new UUID will be used and this rule will have to be altered --> <device> <match key="block.is_volume" bool="true"> <match key="volume.fsusage" string="filesystem"> <match key="volume.uuid" string="d69f9cf1-e376-407a-a203-cb97192aa7ea"> <merge key="volume.policy.mount_option.exec" type="bool">true</merge> <merge key="volume.policy.desired_mount_point" type="string">usbhd</merge> </match> </match> </match> <match key="block.is_volume" bool="true"> <match key="volume.fsusage" string="filesystem"> <merge key="volume.policy.mount_option.exec" type="bool">true</merge> </match> </match> </device> </deviceinfo>
이 파일을 /etc/hal/fdi/policy 아래에 옮기고 나서 usb장치들을 분리했다가 다시 연결해서 lshal 와 pmount-hal의 결과를 보면 속성들이 원하는대로 바꾸어져 있음을 확인할 수 있습니다
/dev/sdc1 on /media/usbhd type ext3 (rw,noexec,nosuid,nodev)
이제 남은일은 usbhd가 연결되었을때 hal정보를 읽어들어서 pmount-hal을 동작하게 하는 것입니다. 그것을 다음의 python code와 daemon script으로 가능합니다
파이썬 모듈들을 위해서
$sudo apt-get install hal-device-manager
아래파이썬 코드는 hal-device-manager패키지에 있는 코드를 참조하여 만들었습니다
/home/hokim/bin/hal-monitor.py
#!/usr/bin/python import gobject import dbus import os import sys if getattr(dbus, "version", (0,0,0)) >= (0,41,0): import dbus.glib def gdl_changed(signal_name, device_udi, *args): if signal_name=="DeviceAdded": device_udi_obj = bus.get_object("org.freedesktop.Hal", device_udi) properties = device_udi_obj.GetAllProperties(dbus_interface="org.freedesktop.Hal.Device") if properties.get("block.is_volume") and not properties.get("volume.is_mounted") \ and properties.get("volume.policy.should_mount") : pmount_hal = "/usr/bin/pmount-hal %s" % (device_udi) if properties.get("volume.policy.mount_option.exec") : pmount_hal = pmount_hal + " --exec" os.system(pmount_hal) elif signal_name=="DeviceRemoved": pass else: pass class Dummy: def write(self, s): pass if os.fork(): os._exit(0) os.setpgrp() os.umask(0) sys.stdin.close() sys.stdout = Dummy() sys.stderr = Dummy() bus = dbus.SystemBus() hal_manager_obj = bus.get_object("org.freedesktop.Hal", "/org/freedesktop/Hal/Manager") hal_manager = dbus.Interface(hal_manager_obj, "org.freedesktop.Hal.Manager") hal_manager.connect_to_signal("DeviceAdded", lambda *args: gdl_changed("DeviceAdded", *args)) hal_manager.connect_to_signal("DeviceRemoved", lambda *args: gdl_changed("DeviceRemoved", *args)) device_names = hal_manager.GetAllDevices() for name in device_names: gdl_changed("DeviceAdded", name) main_loop = gobject.MainLoop() main_loop.run()
/etc/dbus-1/event.d/40hal-mon
#! /bin/sh PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin DAEMON=/home/hokim/bin/hal-monitor.py PIDDIR=/var/run/hal-mon PIDFILE=$PIDDIR/hal-mon.pid NAME=hal-mon DAEMONUSER=hokim DESC="Hardware abstraction layer monitor" . /lib/lsb/init-functions test -x $DAEMON || exit 0 # Include hal defaults if available if [ -f /etc/default/hal ] ; then . /etc/default/hal fi set -e do_start() { if [ ! -d $PIDDIR ]; then mkdir -p $PIDDIR chown $DAEMONUSER:$DAEMONUSER $PIDDIR fi log_begin_msg "Starting $DESC: " if [ -n $(pidof -x $DAEMON) ]; then su - $DAEMONUSER $DAEMON echo $(pidof -x $DAEMON) > $PIDFILE fi log_end_msg $? } do_stop() { log_begin_msg "Stopping $DESC: " # if hald does not want to die, apply some more force if [ -e $PIDFILE ]; then kill -9 $(< $PIDFILE) || true rm -f $PIDFILE fi log_end_msg $? } case "$1" in start) do_start ;; stop) do_stop ;; #reload) # # If the daemon can reload its config files on the fly # for example by sending it SIGHUP, do it here. # # If the daemon responds to changes in its config file # directly anyway, make this a do-nothing entry. # # echo "Reloading $DESC configuration files." # start-stop-daemon --stop --signal 1 --quiet --pidfile \ # /var/run/$NAME.pid --exec $DAEMON #;; restart|force-reload) # # If the "reload" option is implemented, move the "force-reload" # option to the "reload" entry above. If not, "force-reload" is # just the same as "restart". # do_stop sleep 5 do_start ;; *) N=/etc/init.d/$NAME # echo "Usage: $N {start|stop|restart|reload|force-reload}" >&2 log_success_msg "Usage: $N {start|stop|restart|force-reload}" >&2 exit 1 ;; esac exit 0
$ sudo mkdir /var/run/hal-mon
$ sudo chown hokim:hokim /var/run/hal-mon
$ sudo /etc/dbus-1/event.d/40hal-mon start
이제 장치를 분리했다 연결했다해보시고 테스트 해보면 특정 usbhd가
특정 디렉토리로 항상 mount 됨을 알 수 있습니다. 또한 부팅하기전에 연결해 놓고 부팅을 해도 자동마운트가 됩니다.
참고로 저의 경우는 xfce 에 유용하게 사용하고 있습니다. gnome에서
사용한다면 gnome-volume-manager를 제거하고 사용해야 할것 같은데
의존성 때문에 제거 될 수 있을지 모르겠군요.
참고자료
http://gnome.or.kr/wiki/HAL
http://dbus.freedesktop.org/doc/dbus-tutorial.html
apt-get install hal-doc
Re: hal, pmount, dbus 를 이용한 usb 디바이스 자동마운트하기
제거 안 해도 될 겁니다. 어차피 마운트는 한 번만 되는거고, gnome-volume-manager 도 다른 자동마운트 프로그램이 마운트한 항목들을 알아차리는 것 같더군요. :-)
저는 젠투에서 권장하는 ivman (Automounter) 을 씁니다.
만 하면 자동 마운트 기능이 활성화됩니다.
----
블로그 / 위키 / 리눅스 스크린샷 갤러리
breezy에도 ivman 있군요. 소스코드에서 pmount를 쓰는거보니
breezy에도 ivman 있군요. 소스코드에서 pmount를 쓰는거보니 제가 했던 것하고 똑같은 원리이군요. 아쉬운 점은 pmount 옵션에 exec가 없다는 점입니다:( gnome-volume-manager를 사용할때도 이것이 불만중에 하나였는거든요.
http://gentoo-wiki.com/HOWTO_Auto_mount_
http://gentoo-wiki.com/HOWTO_Auto_mount_filesystems_%28AUTOFS%29
http://gentoo-wiki.com/HOWTO_ivman
저런 문서들도 참고해보면 도움이 될 듯 싶군요 :)
오랫동안 꿈을 그리는 사람은 그 꿈을 닮아간다...
http://mytears.org ~(~_~)~
나 한줄기 바람처럼..
댓글 달기