Постановка задачи
В прошлый раз мы посмотрели как можно вывести информацию о том подмонтирована ли флешка или нет на экран при помощи системного монитора conky. Как известно, если есть какой-нибудь процесс, который занимает какой-нибудь файл на флешке, то umount ругнётся:
umount: /media/usb: device is busy umount: /media/usb: device is busyТаким образом, для начала надо узнать что это за процесс, а затем убить его.
В этой заметке я приведу скрипт, который использует для автоматического отключения занимающих процессов программу fuser и выводит сообщения на экран посредством программы gmessage.
Кому это надо? Ну, скорее всего, не пользователям DE, где это и так реализовано уже, наверное (хотя в последний раз, когда я использовал DE, мне приходилось самому искать держащие процессы). А вот пользователям лёгких оконных менеджеров, может и пригодится.
Решение
fuser - для отключения мешающих процессов
Программа fuser идентифицирет процессы, использующие данный файл и позволяет их убить. Допустим, мы открыли какой-то файл на флешке в программе evince и ещё лазим в терминала по файловой системе. Характерный вывод такой:
~> fuser -vm /media/usb/ USER PID ACCESS COMMAND /media/usb/: maxim 6369 ..c.. bash maxim 7404 f.... evince
-v
означает более полный вывод, а -m /media/usb
указывает на точку
монтирования.
В википедии это хорошо
описано, а в конце приведён пример как убить махом все процессы,
занимающие требуемую файловую систему. В моём случае для отрубания
всего (от точки монтирования /media/usb
) будет служить команда:
fuser -km /media/usb
В принципе, fuser даёт неплохой вывод, чтобы понять что именно мешает отключению, но всё же знание программы и идентификатора процесса не столь информативны для пользователя как, к примеру, имя файла, который открыт.
lsof - для информативного вывода
Для более информативного вывода, на мой взгляд, больше подойдёт программа lsof. Вот типичный вывод:
~> lsof | grep /media/usb/ bash 6369 maxim cwd DIR 8,17 32768 1663 /media/usb/Experiment evince 7404 maxim 14r REG 8,17 873474 1684 /media/usb/ABX3_preprint.pdfКолонки означают следующее:
~> lsof | head -1 COMMAND PID USER FD TYPE DEVICE SIZE NODE NAMEБолее удобный вывод для меня (PID COMMAND NAME, разделённые табуляцией) я получаю с помощью awk:
lsof | grep /media/usb/ | awk '{print $2"\t"$1"\t"$9}'
Здесь мы режем строку на части, разделённые пробелом (в случае
непробельного разделителя надо использовать -F "разделитель") и
выводим соответствующие части в желаемом порядке.
Таким образом, получим:
~> lsof | grep /media/usb/ | awk '{print $2"\t"$1"\t"$9}'
6369 bash /media/usb/Experiment
7404 evince /media/usb/ABX3_preprint.pdf
По-моему, это весьма информативно. Я не соображу сходу что значит
процесс 7404, но уж точно пойму /media/usb/ABX3_preprint.pdf
. А это
мне и надо.
Gmessage - для графического отображения информации
Хочется, когда отмонтирование прошло удачно, получить сообщение, что всё удачно. В обратном случае хотелось бы, чтобы появилось окошко, показывающее, что такие-то процессы мешаются, и предлагающее их убить.
Для этого можно использовать старую добрую программу xmessage или её более приятный глазу аналог, gmessage (оно же gxmessage).
Для отображения текста из файла text-file в окне с кнопками "Ла" и "Нет" достаточно скомандовать:
gxmessage -name "Заголовок окна" -buttons "Да:0,Нет:1" -file text-fileПри нажатии на кнопку "Да" будет генериться сигнал 0, при нажатии на "Нет", соответственно - 1.
Таким образом, если мы хотим, чтобы при нажатии на "Да" что-то происходило, мы будем использовать такую конструкцию:
if gxmessage -name "Заголовок окна" -buttons "Да:0,Нет:1" \ -file text-file; then *что-то выполняем* fi
Пишем скрипт
Итак, предлагаю такой ход действий:
- Введём переменную точки монтирования и лога сообщений:
MOUNT_POINT="/media/usb" LOG=/tmp/mount_usb_log
- Узнаём подмонтирована ли флешка (если
$if_mounted
непусто, то выполняем то, что внутри if:
if_mounted=`grep $MOUNT_POINT /etc/mtab` if [[ -n `echo $if_mounted` ]]; then ... fi
- Если флешка подмонтирована, пытаемся её отмонтировать, а все возможные сообщения пишем в лог:
umount $MOUNT_POINT &> $LOG
- Затем, если лог непустой, то есть что-то пошло не так, выведем сообщение какие процессы нам мешают и предложим их убить.
- В случае успеха, напишем, что всё получилось.
У меня получилось следующее:
#!/bin/sh MOUNT_POINT="/media/usb" if_mounted=`grep $MOUNT_POINT /etc/mtab` LOG=/tmp/mount_usb_log function my_umount { # если подмонтирована, пытаемся отмонтировать if [[ -n `echo $if_mounted` ]]; then umount $MOUNT_POINT &> $LOG fi } my_umount if [[ -s $LOG ]]; then # если лог непустой, то # пишем в него какой процесс занимает (попутно приводим к приличному виду с помощью awk) lsof | grep /media/usb/ | awk '{print $2"\t"$1"\t"$9}' > $LOG # если есть лог, то пишем, что флешка занята if gxmessage -file $LOG -name "Флешка занята!" \ -buttons "Force:0,Cancel:1"; then while [[ -n `echo $if_mounted` ]]; do # при нажатии на Force убиваем процессы, которые занимают флешку fuser -km $MOUNT_POINT # и пытаемся снова отмонтировать my_umount if_mounted=`grep $MOUNT_POINT /etc/mtab` # пока не отмонтируем (пока /etc/mtab не будет содержать "/media/usb") done gxmessage "Флешка успешно отмонтирована" fi else gxmessage "Флешка отмонтирована" fi
Цикл я использовать поначалу не планировал, думал просто скомандовать
fuser -km $MOUNT_POINT
my_umount
но оказалось, что команды не выполныются последовательно. Почему-то срабатывает вторая, пока ещё не отработала первая (стало быть ничего не отмонтировалось, потому что всё ещё были живы процессы). && не помогало. А использовать sleep перед второй командой не совсем честно. Поэтому и организовал цикл, который не закончится, пока точно флешке не отмонтируется. Только после этого выведется сообщение об успешном усходе нашего предприятия.
Итак, скриншоты.
Когда флешка занята, видим кто мешает:
После нажатия на «Force» процессы убиты, флешка отмонтирована и мы видим:
Ура! :)
Дело за малым:
- обзываем скрипт umount_usb
- делаем его исполняемым (chmod u+x umount_usb)
- кладём куда-нибудь в $PATH (например, в ~/bin, при этом добавить в ~/.bashrc export PATH="$PATH:$HOME/bin")
- настраиваем sudo, чтобы запускать umount без запроса пароля (смотри здесь на предмет NOPASSWD, тогда в скрипте mount меняем на sudo mount), либо смотрим на мои опции в /etc/fstab (user позволяет от обычного пользователя монтировать): /dev/sdb1 /media/usb auto user,noauto,iocharset=utf8,showexec 0 0 (для кого это - китайская грамота следует прочитать это)
- назначаем горячую клавишу. Например, я использую xbindkeys. В ~/.xbindkeys: пишем
"umount_usb"
Mod4+u
Заключение
Данное решение может пригодиться пользователям оконных менеджеров. Разумеется, основной недостаток заключается в том, что оно применимо лишь для файловых систем, имеющих один раздел. У меня других и нет. Но для кого-то это критично. На первый взгляд не должно возникнуть больших проблем в плане распространения данного способа на случай более сложной таблицы разделов.
Копируете статью - поставьте ссылку на оригинал!
Комментариев нет:
Отправить комментарий