Третьего дня поднял сервер для файлопомойки, бэкапов, торрентов и прочих домашних нужд. Там развернулась samba с множеством шар, в частности с личнми данными и местом под бэкапы ноутбука через TimeMachime. Сразу же не понравилось постоянное ручное монтирование шары и образа для бэкапа. Ниже хочу поделиться с сообществом скриптами по автоматизации этого процесса.
Причины неудобств
Каждый раз для подключения к самбе надо изрядно потыкать мышкой: Finder -> Подключение к серверу -> ввод имени сервера (-> первый раз ещё и пароль спросят) -> выбор папок, которые нужно подключить -> Ok. Потом, если захочется делать бэкап в примонтированную по самбе директорию, прийдётся ещё и образ .sparsebundle создавать, и монтировать ещё и его. Всё это, возможно, и не сильно напрягает, если делать раз в пару недель на стационарном ПК, который постоянно находится в одной и той же сети и не выключается так же, как и сервер. Но когда это надо делать после каждого включения/пробуждения ноутбука — это ни в какие ворота не лезет. Плюс к этому выяснилось, что если не отмонтировать разделы и сменить сеть (скажем прийти к соседу в гости), то система изрядно виснет после выхода из сна, ибо не находит сервер с шарами, а, в редких случаях, ещё и в kernel panic случается. Всё это сильно омрачило радость поднятия сервера и мечты о том что больше я даже вспоминать не буду о бэкапах, пока они мне не понадобятся (тьфу тьфу тьфу). Было решено, что процесс нужно автоматизировать.
На первых порах
В попыхах были нагуглены способы монтирования самбы и тут же родился маленький скрипт из парочки идентичных команд следующего вида:
mkdir -p /mount/point
Так же было найдено как сделать аттач образа для TimeMachine:
hdiutil attach -mountpoint /mount/point /path/to/image.sparsebundle/
hdutil оказался более смышлёным и создание папок не потреловал.
Первая версия скрипта была готова.
Безопасность
Очень напрягало указание пароля в явном виде при монтировании. Почитав ман для mount_smbfs узнал про nsmb.conf. Но идея так же не шибко понравилась, так как всё равно пароль хранится где-то в файле в открытом виде. Тут же вспомнил, что через GUI пароль спрашивают единожды, а далее вытаскивают из keychain. Захотелось использовать её. Оказалось автоматически, как если бы взял nsmb.conf и дописал ключик -N к mount_smbfs, пароль подставляться не будет (чуда не произошло). Прийдёся его сначала получать через security и затем передавать в нужное место. Для сих целей была нагуглена и переделана под свои нужды функция:
get_inet_pwd () {
security 2>&1 >/dev/null find-internet-password -gl $1
|ruby -e 'print $1 if STDIN.gets =~ /^password: "(.*)"$/'
}
После этого явное указание пароля было заменено на:
mount -t smbfs //user:"$(get_inet_pwd server-pc)"@server-pc/shara_name /mount/point
Паранойя отступила, но любознательность и перфекционизм остались, поэтому дело продолжилось.
Улучшения и расширение
Захотелось сделать один скрипт для монтирование всего и сразу, да так сделать, чтобы он расширяемый было и повторять код много раз не пришлось, плюс чтобы смотрел что уже в системе примонтировано и второй раз монтировать не пытался. Сказано — сделано. Опишу всё по-порядку.
Для определения примонтированости, на первом этапе, был использован самый «топорный» метод
if [ ! -d /mount/point/ ]; then
mkdir -p /mount/point
mount …
fi
Но сразу же стало понятно, что директория может и присутствовать, но не являться точкой монтирования для чего-либо. Поэтому сделал функцию для проверки:
is_volume_mounted() {
volume_name=$1
mount | awk -v volume_name=$volume_name '$3 == volume_name {print $3}'
}
Метод тоже не идеален, но уже гораздо лучше, нежели простая проверка наличия директории.
То же самое было сделано и для проверки образа бэкапа:
is_image_attached() {
img_path=$1
df -Hl | awk -v img_path=$img_path '$9 == img_path {print $9}'
}
После некоторых раздумий и чтения форумов метод раздулся и стал сразу проверять примонтирован ли образ, и, если нет, то монтировать его.
try_attach_fs_image() {
img_path=$1
mnt_pnt_path=$2
# check existance of image file
if [ -d $img_path ]; then
# check if image alredy attached in system
if [[ $(df -Hl | awk -v img_path=$img_path '$9 == img_path {print $9}') != "" ]]; then
echo image $img_path alredy attached
else
hdiutil attach -mountpoint $mnt_pnt_path $img_path
fi
fi
}
Далее нужна была функция для выполнения проверок и автоматизации по монтированию любого количества шар с сервера. Вот какой она получилась:
try_mount_server_samba() {
smb_vol_name=$1
mnt_pnt_path=$2
# check if samba share exist in network
if [[ $( is_samba_exist $smb_vol_name ) != "" ]]; then
# check if samba alredy mounted
if [[ $( is_volume_mounted $mnt_pnt_path ) != "" ]]; then
# show message about that
echo volume $mnt_pnt_path alredy mounted
else
# check if moint point directory not exist
if [ ! -d $mnt_pnt_path ]; then
mkdir -p $mnt_pnt_path
fi
# otherwise - mount volume
mount -t smbfs //user:"$(get_inet_pwd server-pc)"@server-pc/$smb_vol_name $mnt_pnt_path
fi
fi
}
Внимательный читатель заметит, что во-первых: можно и имя сервера передавать в параметрах к функции, а не хардкодить его, а во-вторых: в скрипте встречается неизвестная функция is_samba_exist.
Отвечаю по-порядку: 3-й параметр для функции было делать лень, ибо пока второго сервра с самбой не планируется; функция is_samba_exist имеет следующий вид:
is_samba_exist() {
smb_vol_name=$1
smbutil view //user:"$(get_inet_pwd server-pc)"@server-pc/ | awk -v smb_vol_name=$smb_vol_name '$1 == smb_vol_name {print $1}'
}
Несложно догадаться, что функция проверяет наличие шары с указанным именем в текущей сети.
Таким образом основная часть скрипта стала простой, понятной и расширяемой:
try_mount_server_samba "shara_name" "/mount/point"
try_attach_fs_image "/path/to/image.sparsebundle" "/mount/point"
На последок, по образу и подобию вышеописанного скрипта, был создан ещё один, но уже для отмонтирования сразу всего:
#!/bin/sh
# unmount volume if it mounted
# syntax: umount_volume "/path/to/volume"
umount_volume() {
vol_path=$1
if [[ $(mount | awk -v vol_path=$vol_path '$3 == vol_path {print $3}') != "" ]]; then
umount $vol_path
echo $vol_path is unmounted
fi
}
# detach filesystem image if it attached
# syntax: detach_fs_image "/path/to/fs/image"
detach_fs_image() {
img_path=$1
if [[ $(df -Hl | awk -v img_path=$img_path '$9 == img_path {print $9}') != "" ]]; then
hdiutil detach $img_path
echo $img_path is detched
fi
}
# main part
# umnount data volume
umount_volume "/data/mount/point"
# firstly detach image from backup volume
detach_fs_image "/backup/image/mount/point"
# secondary unmount backup volume
umount_volume "/backup/volume/mount/point"
Вот и всё. Дальше скрипты были унесены к своим собратьям в ~/.script. Получили короткие алиасы в .bash_profile и стали помогать мне с самбой.
Перспективы
В будущем, когда дойдут руки, хочется подружить скрипты со SleepWatcher, дабы выполнялись они после выхода из сна и перед уходом в него же. То есть, чтобы совсем ничего не надо было делать руками и автоматизация стала полной.
Автор: Loriowar