Совместное использование su, screen и script
Я уже писал и про команду su
и про команду screen
. Теперь, видимо, пришло время написать про их взаимодействие. Конечно же, не без повода. А дело было так.
Я, как водится, опять попал в ситуацию, когда, из-за нестабильной работы используемого ПО, внезапно и довольно часто стало прерываться SSH
соединение с удаленным компьютером[1]. Как следствие, копирование больших файлов, которое является одной из самых востребованных мною операций, вновь превратилось в азартную игру с непредсказуемым результатом. И мне ничего не оставалось, как использовать команду screen
. Как раз тут меня и ждал сюрприз.
Ранее я немного описывал инфраструктуру, используемую для развертывания и эксплуатации self-hosted сервисов. Важной особенностью, с точки зрения сложившейся ситуации, является то, что сервисы запускаются при помощи docker
контейнеров, каждый - из под своего специально созданного пользователя, и, при этом, данные, создаваемые и/или используемые сервисом, сохраняются в подмонтированном к контейнеру каталоге, владельцем которого является все тот же пользователь. Поясню более предметно.
Для скачивания торрентов я использую docker
контейнер с transmission
. Скачанные файлы сохраняются в физическом каталоге, который подмонтирован к контейнеру. Этот каталог, в свою очередь, является подкаталогом домашнего каталога пользователя, от имени которого запускается контейнер. Таким образом, для того, чтобы скопировать скачанные файлы в свою фильмотеку, или же, в музыкальную коллекцию, мне надо либо работать из под root
-а, либо работать от имени пользователя, являющегося владельцем каталога. Нетрудно предположить, что чаще всего я работаю от имени пользователя-владельца - правила хорошего тона не поощряют работу от имени root
-а. Для смены текущего пользователя я использую команду su
:
su - username
после чего запускаю midnight commander
и наслаждаюсь удобством его UI.
Теперь, чтобы копирование файлов не прерывалось из-за разрыва соединения, мне надо было запустить команду screen
. Ну и так получилось, что я сначала переключился под нужного пользователя и, перед запуском mc
, вызвал screen
. И я увидел то, чего увидеть не ожидал:
Еще один интересный факт заключался в том, что если я переключался под root
-а и потом вызывал screen
, ошибки не было! Как всегда, в случаях таких вот неожиданностей, разбор причин обогатил меня знаниями, которые, наверное, следовало бы получить заранее. Итак.
Первое что мне удалось выяснить - это причина возникновения ошибки[2]. Дело в том, что, подключившись под определенным пользователем по протоколу SSH
, и открыв, таким образом, терминал, я сделал используемого пользователя владельцем нового терминала. И, не смотря на то, что при помощи команды su
я сменил пользователя, я, тем не менее, остался в том же самом терминале и владелец его не поменялся. Запуск же команды screen
предполагает, что пользователь, запустивший ее, обладает всеми мыслимыми правами на терминальное окно, в котором все это безобразие и происходит. Обычно, такими правами обладает владелец и... еще пользователь root
. А тот пользователь, под которого я переключился - не обладает. И именно поэтому, я и получал эту ошибку только в том случае, когда переключился под другого пользователя, который не является root
-ом. Если же я передключился под root
-а - все было в порядке!
Как же можно выкрутиться из создавшейся неприятной ситуации? Конечно, можно начать раздавать направо и налево права, включать пользователей в административную (root
) группу, группу управления терминалами (tty
), менять права доступа (chmod
), но это, на мой взгляд, все из разряда несколько истеричной реакции - ослаблять таким образом защищенность системы можно, но не рекомендуется. Тем более, что, как правило, всегда (ну, или, почти всегда) находится какое-нибудь вполне приемлемое альтернативное решение. Не стал исключением и описываемый случай.
Довольно быстро после начала поисков в интернете был найден ответ, согласно которому, для того, чтобы исправить ситуацию надо было воспользоваться волшебной командой:
script /dev/null
Вот когда я вижу такие решения, я понимаю, почему люди не переходят с такой ужасной и ругаемой всеми Windows
на замечательный и чрезвычайно превозносимый некоторыми Linux
- ни у одного нормального пользователя не может возникнуть чувства уверенности в завтрашнем дне при виде такого очевидного решения описываемой мною проблемы. Тут, правда, есть заковырка - я (почему-то) считаю, что являюсь не совсем обычным пользователем. Поэтому я попытался понять, что же это за абракадабра написана и почему она должна помочь[3]. И вот, что мне удалось выяснить.
Во-первых, script
- это специальная команда для записи производимых в терминале действий в отдельный файл, который потом можно будет просмотреть. Грубо говоря, я бы назвал это стенограммой, но официально используемый английский термин - typescript, что дословно переводится как "машинопись". Обычно script
используют для того, чтобы задокументировать факт того, что какие-то определенные действия были выполнены. Эта команда может быть вызвана с различными ключами, если кому интересно, может почитать документацию, хотя бы тут.
Во-вторых, в той же документации есть фраза, которая раскрывает всю суть происходящего волшебства. Не поленюсь и приведу ее тут:
The script ends when the forked shell exits
Или вот тут тоже, более явно:
If the variable SHELL exists, the shell forked by script will be that shell.
Эти фразы дают нам понять, что команда script
запускает командную оболочку (shell
), активность в которой мониторится и записывается в файл. И для этого создается виртуальный терминал, в котором и работает вновь запущенная оболочка. И, что важно для нас, у этого терминала владельцем будет тот пользователь, под которого мы чуть ранее переключились при помощи команды su
. Соответственно, при вызове screen
проблем с правами возникать более не должно.
Осталось совсем чуть-чуть: понять что такое /dev/null
? На самом деле, на фоне всего остального, это совсем не сложно. Команда script
пишет все, что происходит в ее виртуальном терминале в файл с названием typescript
. Но это поведение можно изменить, явно указав, в какой файл мы хотим производить запись. Например, так:
script tty-record.txt
Такая команда будет вести запись в файл с именем tty-record.txt
. В нашем же случае, после команды script
указано устройство /dev/null
, то есть, запись будет производиться в устройство с незамысловатым названием null
, а именно, "в никуда". Ну в самом деле, зачем нам сохранять запись наших действий, которые мы собираемя производить в терминале? Нет, если вам она нужна, пожалуйста, пишите, меня же вариант с null
устраивает полностью.
Подведем итог наших изысканий. Переключившись под другого пользователя, мы не можем вызывать команду screen
, так как терминал, в котром мы ее вызываем, на самом деле, принадлежит первоначальному пользователю, создавшему его, например, при соединении по SSH
. Поэтому мы вызываем команду script /dev/null
, которая создает свой виртуальный терминал и уже для этого виртуального терминала мы можем использовать команду screen
, так как ее использование теперь не будет противоречить правам владения - виртуальный терминал создается под пользователем, от имени которого мы работаем в данный момент времени. Ну а чтобы не захламлять диск ненужными стенограммами наших действий, мы пишем активность в устройство с именем null
. Побочным эффектом такого алгоритма работы является то, что для вовзрата к статус-кво надо будет набрать на одну команду exit
больше: сначала закрыть терминал, созданный командой screen
, потом завершить работу терминала, созданного командой script
, и только потом вернуться к своему первоначальному пользователю.
И уж совсем в завершении. Всех этих заморочек можно было избежать, если бы я сначала запустил команду screen
и уже потом, в созданном ею терминале, переключился бы под нужного пользователя - не было бы никаких проблем с правами владельца терминала. Правда, так будет работать только в простейшем случае, когда вы создаете лишь один сеанс (терминал) screen
, но, если честно, этот случай - самый распространенный. Такие вот дела.
об этом я тоже как-нибудь подробно расскажу ↩︎
Тут надо понимать, что когда я пишу "удалось выяснить" это не значит, что я сам докопался до истины - просто умные люди посредством замечательных статей на различных ресурсах учат таких "быстрых" админов, как я ↩︎
Как говаривал сын одного очень известного фокусника, тоже фокусник: "Чтобы фокус получился, надо обязательно дунуть. Потому что, если не дунуть, то никакого чуда не произойдет". ↩︎