Я системный администратор по роду деятельности. Поддерживаю удаленно сервера разных клиентов. Нередко приходится слышать от клиента просьбу дать шелл-доступ на сервер. С одной стороны просьба вполне обоснованная: сервер не мой, да и доступ клиенту нужен, чтоб меня же не дергать по пустякам (скажем, посмотреть, не закончилось ли место на диске или все ли процессы запущены). С другой стороны клиент зачастую практически не имеет опыта работы в unix, и нет никакой гарантии, что я смогу все исправить после того, как клиент по незнанию сотрет что-нибудь с диска или заблочит мне доступ, удалив правила фаервола. Зачастую клиенты и сами это понимают, но настаивают на предоставлении им доступа, не видя другого выхода.
Казалось бы, можно дать клиенту урезанный шелл и контролировать исполнение «опасных» команд при помощи sudo. Однако даже если с точки зрения безопасности удается все удачно «разрулить», это не решает всех проблем. Клиента приходится учить основам работы в командной строке, отвечать на шквал вопросов и разбираться, что и почему у него не получается. Время, затрачиваемое на поддержку, увеличивается значительно.
Пытаясь найти решение я наткнулся на описание модуля Cmd для Python. Данный модуль позволяет с минимальными затратами написать подобие интерфейса командной строки c необходимым набором команд.
Начнем с небольшого скрипта-каркаса, который можно будет дополнять командами по мере необходимости. Вот он. Всего 25 строчек. Даже под спойлер прятать не надо.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import cmd
class Cli(cmd.Cmd):
def __init__(self):
cmd.Cmd.__init__(self)
self.prompt = "> "
self.intro = "Добро пожаловатьnДля справки наберите 'help'"
self.doc_header ="Доступные команды (для справки по конкретной команде наберите 'help _команда_')"
def do_hello(self, args):
"""hello - выводит 'hello world' на экран"""
print "hello world"
def default(self, line):
print "Несуществующая команда"
if __name__ == "__main__":
cli = Cli()
try:
cli.cmdloop()
except KeyboardInterrupt:
print "завершение сеанса..."
Cохраним файл как cli.py и запустим. Скрипт бодро поприветсвует нас и выдаст приглашение командной строки. Вот пример его работы:
$ ./cli.py
Добро пожаловать
Для справки наберите 'help'
> help
Доступные команды (для справки по конкретной команде наберите 'help _команда_')
===========================================================================
hello help
> help hello
hello - выводит 'hello world' на экран
> hello
hello world
> завершение сеанса...
Вернемся к коду. Мы унаследовали класс Cli от Сmd, переопределили несколько свойств для вывода приветствия и начальной справки на родном языке. А так же дописали два метода — default и do_hello. Метод default определяет поведение командной строки в случае, если набранная пользователем команда не существует. На методе do_hello остановимся подробней.
Метод do_hello описывает единственную команду нашего cli (ну, кроме доступной по умолчанию help) — hello. Модуль cmd следует соглашению, по которому методы вида do_command преобразуются в команды command в cli. Комментарий в тройных кавычках, идущий первой строкой в теле метода преобразуется в справку по этой команде. В аргументе args в метод передается строка пользовательских аргументов. Например, если пользователь в консоли набрал «hello everyone» переменная args будет содержать строку «everyone». В данном случае мы просто игнорируем аргументы командной строки.
По умолчанию доступно автодополнение (по табуляции) и история команд (стрелочка вверх). Так же доступна встроенная команда «help» (она же — "?"), которая при помощи обильной дозы магии преобразует комментарии в коде скрипта в справку по командам.
Имея данный скрипт-каркас, мы можем расширять функциональность нашего интерфейса командной строки, добавляя в код нужные методы вида do_cmd. Например, удалим не несущую полезной нагрузки команду hello и добавим несколько полезных команд для мониторинга системы.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import cmd
import os
class Cli(cmd.Cmd):
def __init__(self):
cmd.Cmd.__init__(self)
self.prompt = "> "
self.intro = "Добро пожаловатьnДля справки наберите 'help'"
self.doc_header ="Доступные команды (для справки по конкретной команде наберите 'help _команда_')"
def do_show_cpu(self, args):
"""show_cpu - нагрузка на процессоры"""
os.system("sar 2")
def do_show_mem(self, args):
"""show_mem - использование RAM"""
os.system("free")
def do_show_disk(self, args):
"""show_disk - свободное место на диске"""
os.system("df -h")
def do_show_net(self, args):
"""show_net - сетевые параметры"""
os.system("/sbin/ifconfig")
os.system("/sbin/route -n")
def do_show_log(self, args):
"""show_log - системный журнал"""
os.system("sudo tail -f /var/log/messages")
def default(self, line):
print "Несуществующая команда"
def emptyline(self):
pass
if __name__ == "__main__":
cli = Cli()
try:
cli.cmdloop()
except KeyboardInterrupt:
print "завершение сеанса..."
Наш новоиспеченный шелл готов. Переместим его в место, доступное для чтения всем пользователям системы. Например в /usr/local/bin/. Возможно, путь к нашему скрипту нужно будет прописать в /etc/shells.
Добавляем пользователя с нашим шеллом и пробуем:
# adduser user --shell /usr/local/bin/cli.py
...
root@laptop:~# su - user
Добро пожаловать
Для справки наберите 'help'
> ?
Доступные команды (для справки по конкретной команде наберите 'help _команда_')
===========================================================================
help show_cpu show_disk show_log show_mem show_net
> show_cpu
Linux 3.5.0-17-generic (dima-laptop) 04/03/2013 _x86_64_ (4 CPU)
02:38:03 PM CPU %user %nice %system %iowait %steal %idle
02:38:05 PM all 0.63 0.00 0.25 0.13 0.00 98.99
02:38:07 PM all 1.00 0.00 0.25 0.25 0.00 98.50
^C> show_mem
total used free shared buffers cached
Mem: 3911236 2123408 1787828 0 124156 994752
-/+ buffers/cache: 1004500 2906736
Swap: 4393980 0 4393980
>
Автор: facha