Задача элементарна: обеспечить автоматическое регулярное резервирование информации располагающейся в файловой системе мобильного телефона.
После того, как производители электроники под давлением карьеристов и "болтологов" от экологии повально перешли на без свинцовый припой, выход из строя мелких устройств стал непредсказуемым явлением; теперь устройство может прослужить пять лет, а может прийти в негодность и утянуть в небытие накопленные на нем данные на третий день эксплуатации. Не то, чтобы для меня мобильные телефон являлся особо ценным предметом, но в некоторых случаях - это единственный носитель информации объёмом под два-три Гигабайта. В общем-то, обидно потерять все это за раз.
Для начала мы должны иметь мобильный телефон с поддержкой протокола OBEX, подключённый и сопряжённый с компьютером по Bluetooth.
Временно переводим сотовый телефон в режим видимости всем окружающим по протоколу Bluetooth.
Сканируем устройство на предмет поддерживаемых технологий (MAC в формате "xx:xx:xx:xx:xx:xx"):
# sdptool browse mac.address
....
Service Name: OBEX File Transfer
....
Channel: 10
....
Service Name: OBEX File Transfer
....
Channel: 10
....
Нас будут интересовать блок описания поддержки технологии OBEX, передачи и манипулирования данными. Особенно, номер канала, по которому необходимо обращаться к оборудованию для доступа к этим сервисам; в нашем случае это номер 10.
Инсталлируем набор утилит, реализующих монтирование файловой системы мобильного телефона с помощью OBEX по самым разным интерфейсам.
К сожалению, доступная в репозитории Debian Lenny версия obexfs 0.10 более или менее корректно работала в режиме только последовательного чтения. Работа с удалённой файловой системой осуществляется в "блокирующемся" режиме, то есть, при исполнении какой либо операции вроде чтения содержимого директории, копирования, манипулирования данными доступ к файловой системе полностью блокируется, параллельные операции не допускаются. Применение опции включения неблокирующегося режима никак не влияет на ситуацию. Мало того, организация блокировок и уведомлений о состоянии мягко говоря странная, вплоть до того, что ряд приложений, вроде Midnight Commander вообще толком работать не может.
Суть проблемы в том, что сразу после начала копирования на удалённую файловую систему приложению сообщается о том, что копирование как таковое прошло успешно, при том, что реально оно ещё продолжается (полагаю, что в это время данные переносятся из буфера на реальное устройство). Так вот, если приложение пытается применить к этому файлу какую либо операцию, например, установить для него права доступа, проверить успешность проведённой операции, то получает от удалённой файловой системы выговор на предмет невозможности доступа к файлу, что ещё не с копировался окончательно; отработать эту ситуацию приложение не может и обращается к пользователю за советом, а то и не за одним, и так при каждой операции. Установка при монтировании файловой системы опций применения синхронной записи ситуации не меняет.
Ещё одна критичная ошибка, не позволяющая использовать "стабильную" (хе) версию из репозитория в том, что директории и файлы с не латинскими символами не отображаются в точках монтирования, а при попытке прямого к ним обращения - ошибки и отказ в доступе. При чём грешить на FUSE не позволяет тот факт, что при использовании SSHFS работа с файлами таких проблем не доставляет.
Так или иначе, а придётся собирать утилиты из исходных кодов в надежде на то, что хоть часть из "багов" исправлена разработчиками.
Чтобы не завалить файловую систему неуправляемым мусором из библиотек, исполняемых файлов и ресурсов, будем использовать сборщик пакетов, утилиту checkinstall:
# aptitude install checkinstall
Для сборки утилит набора OBEX из исходных кодов потребуется предварительно установить ряд пакетов удовлетворяющих зависимости:
# aptitude install fuse-utils libfuse-dev libobexftp-dev libbluetooth-dev libusb-dev pkg-config python-dev ruby-dev tcl-dev
Идём по адресу http://dev.zuckschwerdt.org/openobex/wiki/ObexDownloads и берем самые свежие пакеты исходных кодов obex, obexftp и obexfs (в моем случае это версии 1.5, 0.23 и 0.12):
# cd /usr/src
# wget http://www.kernel.org/pub/linux/bluetooth/openobex-1.5.tar.gz
# wget http://downloads.sourceforge.net/project/openobex/obexftp/0.23/obexftp-0.23.tar.bz2
# wget http://downloads.sourceforge.net/project/openobex/obexfs/0.12/obexfs-0.12.tar.gz
# wget http://www.kernel.org/pub/linux/bluetooth/openobex-1.5.tar.gz
# wget http://downloads.sourceforge.net/project/openobex/obexftp/0.23/obexftp-0.23.tar.bz2
# wget http://downloads.sourceforge.net/project/openobex/obexfs/0.12/obexfs-0.12.tar.gz
Распаковываем архивы:
# tar -xvf openobex-1.5.tar.gz
# tar -xvf obexftp-0.23.tar.bz2
# tar -xvf obexfs-0.12.tar.gz
# tar -xvf obexftp-0.23.tar.bz2
# tar -xvf obexfs-0.12.tar.gz
Переходим в директории с исходными кодами и проверяем, удовлетворяет ли наша система зависимостям собираемого приложения, пробуем произвести предварительное конфигурирование пакета на предмет обнаружения ошибок, конфигурируем и собираем пакеты:
# cd /usr/src/openobex-1.5
# ./configure --no-create --prefix=/usr
# ./configure --prefix=/usr
# checkinstall -D --install=no --pkggroup=OBEX --pkgname=openobex-fresh --pkgversion=1.5 --pkgrelease=1 --pkgsource=http://downloads.sourceforge.net/project/openobex/ --maintainer=me@domain.name --nodoc make install
# dpkg -i /usr/src/openobex-1.5/openobex-fresh_1.5-1_amd64.deb
# cd /usr/src/obexftp-0.23
# ./configure --no-create --prefix=/usr
# cd /usr/src/obexftp-0.23
# ./configure --prefix=/usr
# checkinstall -D --install=no --pkggroup=OBEX --pkgname=obexftp-fresh --pkgversion=0.23 --pkgrelease=1 --pkgsource=http://downloads.sourceforge.net/project/openobex/ --maintainer=me@domain.name --nodoc make install
# dpkg -i /usr/src/obexftp-0.23/obexftp-fresh_0.23-1_amd64.deb
# cd /usr/src/obexfs-0.12
# ./configure --no-create --prefix=/usr
# cd /usr/src/obexfs-0.12
# ./configure --prefix=/usr
# checkinstall -D --install=no --pkggroup=OBEX --pkgname=obexfs-fresh --pkgversion=0.12 --pkgrelease=1 --pkgsource=http://downloads.sourceforge.net/project/openobex/ --maintainer=me@domain.name --nodoc make install
# dpkg -i /usr/src/obexfs-0.12/obexfs-fresh_0.12-1_amd64.deb
# ./configure --no-create --prefix=/usr
# ./configure --prefix=/usr
# checkinstall -D --install=no --pkggroup=OBEX --pkgname=openobex-fresh --pkgversion=1.5 --pkgrelease=1 --pkgsource=http://downloads.sourceforge.net/project/openobex/ --maintainer=me@domain.name --nodoc make install
# dpkg -i /usr/src/openobex-1.5/openobex-fresh_1.5-1_amd64.deb
# cd /usr/src/obexftp-0.23
# ./configure --no-create --prefix=/usr
# cd /usr/src/obexftp-0.23
# ./configure --prefix=/usr
# checkinstall -D --install=no --pkggroup=OBEX --pkgname=obexftp-fresh --pkgversion=0.23 --pkgrelease=1 --pkgsource=http://downloads.sourceforge.net/project/openobex/ --maintainer=me@domain.name --nodoc make install
# dpkg -i /usr/src/obexftp-0.23/obexftp-fresh_0.23-1_amd64.deb
# cd /usr/src/obexfs-0.12
# ./configure --no-create --prefix=/usr
# cd /usr/src/obexfs-0.12
# ./configure --prefix=/usr
# checkinstall -D --install=no --pkggroup=OBEX --pkgname=obexfs-fresh --pkgversion=0.12 --pkgrelease=1 --pkgsource=http://downloads.sourceforge.net/project/openobex/ --maintainer=me@domain.name --nodoc make install
# dpkg -i /usr/src/obexfs-0.12/obexfs-fresh_0.12-1_amd64.deb
Успешно прорвавшись сквозь дебри компилирования пакетов из полудюжины языков программирования, облегчённо, даже не победно, вздыхаем, выпиваем кофию и приступаем к тому, для чего и проделывали всё вышеописанное - к применению продукта по назначению.
Монтируем по MAC адресу мобильного телефона (MAC в формате "xx:xx:xx:xx:xx:xx"):
# obexfs -b mac.address -B 10 -- -o noatime /mnt/point
Отмонтируем:
# fusermount -u /mnt/point
...или:
# umount /mnt/point
В общем, для резервного копирования данных с мобильного телефона на компьютер утилита подходит, а вот для обратной синхронизации не особо; прочем, поставленной задаче obexfs удовлетворяет.
Инсталлируем утилиту инкрементального резервного копирования:
# aptitude install rsync
Пишем простенький скрипт (предполагается, что к компьютеру подключёно только одно, целевое, мобильное устройство), что будет осуществлять подключение к мобильному телефону по протоколу Bluetooth и копировать изменённые данные с телефона на компьютер:
# mkdir -p /etc/custom/bluetooth
# touch /etc/custom/bluetooth/backup.mobile0.sh
# chmod ug+x /etc/custom/bluetooth/backup.mobile0.sh
# touch /etc/custom/bluetooth/backup.mobile0.sh
# chmod ug+x /etc/custom/bluetooth/backup.mobile0.sh
#!/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
DATE=`date +"%Y-%m-%d %H:%M:%S"`
# MAC в формате "xx:xx:xx:xx:xx:xx"
MAC="mac.mobile.phone"
# Полный путь к директориям указывается без финального слэша
POINT="mount.point.directory"
BACKUP="backup.directory"
if [ ! "`ls /dev | grep rfcomm0`" ]
then
/usr/sbin/hciconfig hci0 up
echo "$DATE Запускаем Bluetooth ..."
sleep 3
fi
if [ ! "`l2ping -t 3 -c 3 $MAC 2>&1 | grep -i failed`" ] && [ ! "`l2ping -t 3 -c 3 $MAC 2>&1 | grep -i down`" ]
then
if [ "`mount | grep -i obexfs | grep -i $POINT`" ]
then
# От монтируем устройство (скорее всего это следы предыдущей незавершённой сессии)
umount -f $POINT
sleep 5
fi
# Исходя из того предположения, что мы здесь одни - уничтожаем все незавершённые процессы прежней сессии (по хорошему нужно бы искать по PID)
killall rsync
killall obexfs
mkdir -p $POINT
echo "$DATE Монтируем устройство с MAC: $MAC ..."
obexfs -b $MAC -B 10 -- -o noatime $POINT
sleep 5
echo "$DATE Приступаем к синхронизации ..."
rsync -av $POINT/ $BACKUP
umount $POINT
else
# Огорчаемся тому, что устройство не доступно в данный момент
echo "$DATE Device with MAC: $MAC is not available through Bluetooth on this host"
fi
exit 0
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
DATE=`date +"%Y-%m-%d %H:%M:%S"`
# MAC в формате "xx:xx:xx:xx:xx:xx"
MAC="mac.mobile.phone"
# Полный путь к директориям указывается без финального слэша
POINT="mount.point.directory"
BACKUP="backup.directory"
if [ ! "`ls /dev | grep rfcomm0`" ]
then
/usr/sbin/hciconfig hci0 up
echo "$DATE Запускаем Bluetooth ..."
sleep 3
fi
if [ ! "`l2ping -t 3 -c 3 $MAC 2>&1 | grep -i failed`" ] && [ ! "`l2ping -t 3 -c 3 $MAC 2>&1 | grep -i down`" ]
then
if [ "`mount | grep -i obexfs | grep -i $POINT`" ]
then
# От монтируем устройство (скорее всего это следы предыдущей незавершённой сессии)
umount -f $POINT
sleep 5
fi
# Исходя из того предположения, что мы здесь одни - уничтожаем все незавершённые процессы прежней сессии (по хорошему нужно бы искать по PID)
killall rsync
killall obexfs
mkdir -p $POINT
echo "$DATE Монтируем устройство с MAC: $MAC ..."
obexfs -b $MAC -B 10 -- -o noatime $POINT
sleep 5
echo "$DATE Приступаем к синхронизации ..."
rsync -av $POINT/ $BACKUP
umount $POINT
else
# Огорчаемся тому, что устройство не доступно в данный момент
echo "$DATE Device with MAC: $MAC is not available through Bluetooth on this host"
fi
exit 0
Понятно, что резервирование инкрементальное и замещающее, не гарантирующее восстановления тех старых файлов, что были затёрты новыми, с таким же именем, но, в конце концов, если мы удалили файл с мобильного телефона и разместили там другой с таким же именем, не значит ли это то, что старый файл нам не был нужен? В конце концов мы не архивы фондовой биржи делаем, а "зеркало" файловой системы не особо значимого мобильного устройства.
В первый раз процедура копирования довольно таки затяжная, в зависимости от количества данных, что нужно скопировать. В дальнейшем, инкрементальных характер работы rsync значительно ускорит операцию "зеркалирования".
Первичное копирование данных большого объёма - операция затратная как с точки зрения времени, так и ресурсов процессора и беспроводного интерфейса мобильного устройства. Затратна настолько, что один из моих стареньких и замученных жизнью телефонов буквально не выдерживал нагрузки и выключался.
Размещаем в таблице crontab указание на периодический запуск синхронизации. Не знаю, как у кого, но у меня график работы за компьютером весьма непостоянный и с точностью предсказать, когда именно я буду работать, да ещё и иметь рядом телефон со включённым Bluetooth проблематично. Пришлось выставить особый режим с плавающей датой старта скрипта с помощью периода между попытками в пять часов в надежде поймать возможность автоматического резервирования.
# cat /etc/crontab
....
0 */5 * * * root /etc/custom/bluetooth/backup.mobile0.sh &
....
0 */5 * * * root /etc/custom/bluetooth/backup.mobile0.sh &
....
И, очень медленный этот интерфейс Bluetooth, ну очень.