Pull to refresh

Автоматизация монтирования samba-разделов в Mac OS

Разработка под MacOS
Sandbox
Третьего дня поднял сервер для файлопомойки, бэкапов, торрентов и прочих домашних нужд. Там развернулась 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 find-internet-password -gwl $1
}

После этого явное указание пароля было заменено на:
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, дабы выполнялись они после выхода из сна и перед уходом в него же. То есть, чтобы совсем ничего не надо было делать руками и автоматизация стала полной.

P.S. спасибо товарищу helios за ключ -w для security.
Tags:bash-scriptingsambamac os xначинающим
Hubs: Разработка под MacOS
Total votes 15: ↑12 and ↓3 +9
Views10.7K

Popular right now

Android
from 100,000 to 160,000 ₽Sky Web ProRemote job
Разработчик на Swift (iOS, MacOS)
from 2,500 $Auslogics LabsRemote job
Разработчик С/C++ (Junior/middle)
from 80,000 ₽EltexНовосибирск
Middle/Senior iOS Developer
from 160,000 ₽IglooСанкт-Петербург