Операционная система, позволяющая задействовать все возможности компьютера, резко отличается от специализированного программного обеспечения огромным числом т. н. «вариантов использования» (use cases) и обширнейшими возможностями тонкой настройки для решения задач конкретного пользователя наилучшим способом. Достаточно сравнить какую-нибудь игровую приставку (например, PlayStation2) под управлением собственной операционной системы и её же под управлением Linux. Вычислительная и мультимедийная мощность такого копьютера весьма высока (известно, что именно компьютерные игры определяют сейчас ресурсопотребление персонального компьютера). Однако способы управления одной и другой системами настолько различны, что неподготовленный человек просто теряется при виде возможностей Linux: где? на какие кнопки нажимать? А кнопок-то и нет...
Можно попытаться описать операционную систему как большой и сложный универсальный инструмент решения любых задач. Предполагается, что пользователь, прочтя документацию, в которой описывается как работает система и как применять её в различных ситуациях, сможет решать и свои задачи. Правда, для этого ему придётся прочесть большую часть документации по системе (в том числе и технической) и перепрограммировать некоторые части системы сообразно своим нуждам. На такой подвиг способны немногие, времени это займёт немало, да и вероятность ошибки (которая тем выше, чем сложнее средства управления системой) при таком подходе недопустимо велика. Сами утилиты или службы Linux, каждую из которых можно «окинуть взором» и понять, что и как она умеет, и чего в ней не хватает, разрабатываются именно теми из пользователей, у которых хватает времени, знаний и навыков на такое полное освоение (см. лекцию Политика свободного лицензирования. История Linux: от ядра к дистрибутивам). Вывод: пользователь — не разработчик, ему всё-таки важнее быстро и качественно решить задачу, чем догло и качественно улучшать инструмент решения.
Можно пойти обратным путём: попытаться предусмотреть все основные способы использования операционной системы на всех основных пользовательских задачах, и на каждый такой способ создать (запрограммировать) отдельную часть, управляемую «кнопкой» или утилитой. Эту часть обычно называют «решением» (solution), и в документации обычно пишут, что должно быть «на входе» системы, и что получается «на выходе» после применения решения. Если пользователь не умеет сам поставить задачу, или делает это в неопределённой форме («хочу, чтобы был текст», «хочу, чтобы играла музыка»), этот способ работает на ура: та же игровая приставка — это отличное решение крайне неопределённой задачи «хочу без толку потратить время». Однако, стоит пользователю захотеть чего-то конкретного, начинаются трудности. Трудности могут быстро стать непреодолимыми, как только для этого «конкретного» не окажется готового решения: внутренняя структура систем, ориентированных на «решения», столь сложна и столь плохо документирована, что сделать что-либо вручную, скорее всего, не удастся. Вывод: пользователь, понимающий суть собственных задач, — не «клиент», он должен иметь возможность быстро и качественно решать задачи самостоятельно, а не выбирать то из готовых «решений», которое нанесёт меньше вреда.
Что же нужно идеальному — достаточно подготовленному, чтобы действовать самостоятельно, и достаточно занятому, чтобы не переделывать системы — пользователю? По-видимому, механизм, с помощью которого можно сформулировать и придать операционной системе все требуемые свойства, имея возможность описывать решение задачи по крайней мере на том же уровне конкретности, на котором было поставлено её условие. Большая часть других, не нужных для решения собственных задач пользователя, свойств должны быть «стандартными» и не требовать его вмешательства.
Так возникает идея разделить систему на два подмножества: профиль и реализацию. Всё, что не потребует вмешательства пользователя, необходимо запрограммировать и использовать в готовом виде в качестве составных частей реализации. В Linux этому соответствуют программы и подпрограммы: ядро, модули, демоны, утилиты; используемые ими библиотеки и прочие разделяемые файлы и т. п. Реализация — это монолитная, неизменяемая часть системы, устроенная по типу «решений» основных задач, только задачи эти, как правило, не совпадают с задачами пользователей, а только помогают решать их, являясь как бы строительным материалом, деталями и инструментами сборки «больших» решений.
Всё, чего может коснуться рука человека, из реализации переносится в профиль системы. Профиль задаёт поведение реализации на данных пользователя, и должен быть устроен так, чтобы пользователь мог его беспрепятственно изменять, если понадобится. С одной стороны, это может быть вариант «высокоуровневого программирования», когда пользователь описывает алгоритм решения и структуру используемых данных на некотором высокоуровневом языке (специализированном или общем, например, на shell). С другой стороны задание свойств может превращаться в указание модификаторов поведения, когда пользователь просто перечисляет необходимые параметры работы программы, которые изменяют её заранее известную, но достаточно общую функциональность.
Таким образом система полностью описывается в виде набора необходимых компонентов реализации, активизированных (запущенных) с определёнными профилями, вкупе с текущим состоянием каждого компонента. Поскольку компонент реализации не может изменяться, а его текущее состояние, наоборот, меняется постоянно и не управляется пользователем, можно считать, что систему задаёт её профиль. Это означает, что для того, чтобы продублировать работу ситемы на другом компьютере, достаточно установить там стандартную реализацию и перенести профиль (обычно занимающий несравненно меньше места) и пользовательское наполнение. Наполнение (файлы пользователей, содерживое www-страниц и т. п.) может занимать много места, но оно входит в понятие «задача прользователя», поэтому забывать о нём нельзя.
Как проще всего создать профиль, если не всей системы, то хотя бы её компонента (программы)? Один из вариантов такой: снабдить программу функцией «сохранить настройки», тогда можно будет эту программу запустить, любым способом добиться её работоспособности, а после зафиксировать достигнутое состояние с помощью этой функции. При этом по началу совершенно неважно, как выглядят эти настройки: программа-то заработала, значит, цель достигнута (проницательный Мефодий немедленно заметил, что название функции «сохранить настройки» как-то подозрительно похоже на название кнопки).
Зачастую для того, чтобы собрать более или менее отвечающий требованиям пользователя профиль, задействуется больше ресурсов, чем для работы самой программы. Утилита автоматической настройки выглядит эдаким шаманом, или кудесником, который, задав всего несколько вопросов человеку, невесть как приводит систему в работоспособное состояние. Такая подсистема и называется-то «wizard», причём в русском переводе её отчего-то стесняются называть «шаманом», а величают уважительно «мастером».
Вот пример поведения обычного шамана-настройщика (пакет wvdial
, заведующий модемным подключением к Internet):
[root@localhost root]# wvdialconf
Usage: wvdialconf <configfile-name>
(create/update a wvdial.conf file automatically)
[root@localhost root]# wvdialconf .wvdialrc
Scanning your serial ports for a modem.
Port Scan<*1>: Scanning ttyS4 first, /dev/modem is a link to it.
. . .
ttyS4<*1>: Modem Identifier: ATI -- Xircom CardBus 10/100+Modem 56 (Revision 2.40)
. . .
ttyS4<*1>: ATQ0 V1 E1 S0=0 &C1 &D2 +FCLASS=0 -- OK
ircomm0<*1>: ATQ0 V1 E1 -- failed at 9600 and 19200 baud.
. . .
ircomm9<*1>: ATQ0 V1 E1 -- failed at 9600 and 19200 baud.
Port Scan<*1>: LT0
. . .
ttyS0<*1>: ATQ0 V1 E1 -- and failed too at 115200, giving up.
. . .
ttyS1<*1>: ATQ0 V1 E1 -- and failed too at 115200, giving up.
Port Scan<*1>: S2 S3 S5 S6 S7 S8 S10
. . .
Port Scan<*1>: USB11 USB12 USB13 USB14 USB15
Found a modem on /dev/ttyS4, using link /dev/modem in config.
Modem configuration written to .wvdialrc.
ttyS4<Info>: Speed 115200; init "ATQ0 V1 E1 S0=0 &C1 &D2 +FCLASS=0"
wvdialconf
Даже ни о каких наводящих вопросах речи не зашло! Программа проверила более полусотни устройств, не модемы ли они, но нашла всего одно — /dev/ttyS4
. Его настройки определились автоматически (и хорошо, потому что Мефодий не знает, что такое «ATQ0 V1 E1 S0=0 &C1 &D2 +FCLASS=0»). Профиль (а wvdialrc
создаёт именно профиль) лежит теперь в файле .wvdialrc
, так что программа wvdial
начнёт работать с модемом, нуждаясь только в пользовательских настройках (входное имя, пароль и т. п.).
Яркий пример того, как элементы реализации связываются профилем в единую подсистему для решения определённой задачи — командная строка и сецнарии командного интерпретатора. Здесь утилиты играют роль элементов реализации, их параметры — роль «настроечной» части профиля, а способ их объединения в сценарий — «программируемой» части профиля. Скажем, команда find /etc -type f 2> /dev/null | xargs -n1 file | cut -d: -f2 | sort | uniq -c
задействует шесть утилит системы: командную оболочку, find
, xargs
, cut
, sort
и uniq
, причём четыре из них запускаются с изменённым профилем1. Командная оболочка дополнительно программируется для создания конвейера между командами.
Задание профиля с помощью командной строки — метод далеко не всегда удобный. Даже при работе с самой командной строкой используется окружение для сохранения настроек, чтобы не задавать их всякий раз и для всякой команды. Что уж говорить о сложных системных службах, свойства которых должны сохраняться не от сеанса к сеансу, а постоянно (в том числе при перезагрузке системы). Вывод прост: профиль необходимо держать в файле, вроде того, что создаётся по команде «сохранить настройки».
Однако сам подход к хранению профиля в файле, при котором пользователь не может изменять этот файл напрямую, а пользуется «умными» конфигураторами, удобен только в случаях, когда настроек много, а цена ошибки невелика (например, при настройке внешнего вида рабочего стола). В общем случае довольно сложно задать поведение системы на основании описания (часто неявного) свойств того, что эта система должна получать в результате. Иными словами, из описания того, что должно получиться, далеко не всегда можно автоматически сделать вывод, как оно должно получаться.
Одним словом, если есть конфигурационный файл, то должны быть и средства редактирования этого файла. Учитывая то, что в Linux есть высокоразвитая система хранения и переработки (как ручной, так и автоматической) данных в текстовом виде, изобретать какой-то новый формат не лучше, чем изобретать новый велосипед. Тем более, что именно текст, разделённый на строки и слова, лучше всего подходит тогда, когда есть чёткое деление профиля на объекты управления и их свойства (например, настройки какого-нибудь демона и значения этих настроек). Вдобавок именно со структурированными текстами отменно управляются текстовые редакторы Linux: Vi, Emacs и др.
methody@localhost:~ $ cat .vimrc
so $VIMRUNTIME/vimrc_example.vim
" Some mappings
map <F2> :wall!^M
map! <F2> ^O:wall!^M
" Tune up
set shiftwidth=2 tabstop=8 history=200 viminfo='50
set showmode showmatch showcmd ruler modeline
set autoindent ignorecase smartcase
set nohlsearch noincsearch
set dir=/var/tmp
set wildmode=list:longest,full
set wildmenu
" Colouring
syntax on
colorscheme desert
vim
Вот как выглядит конфигурационный файл для Vi, написанный Мефодием на основе взятого у Гуревича. Легко заметить, что файл состоит из команд режима командной строки Vi с комментариями (в отличие от большинства утилит Linux, в Vi комментарии начинаются на «"
»). Символы «^O
» и «^M
» — это именно соответствующие управляющие символы (вставленные в текстовый файл с помощью «^V
», см. лекцию Текстовые редакторы). Такой конфигурационный файл легко понимать и изменять.
Как уже было замечено, набор переменных окружения составляет особенный профиль, к которому чувствительны все запускаемые программы — в этом его достоинство. Задаются переменные окружения обычно в командном сценарии, который тоже можно рассматривать как конфигурационный файл). Например, во многих дистрибутивах используется конфигурационный файл .i18n
для настройки языковых особенностей клавиатуры, языка вывода сообщений и т. п.2:
methody@localhost:~ $ cat .i18n
LANG=ru_RU.KOI8-R
LANGUAGE=ru_RU.KOI8-R
SYSFONTACM=koi8-r
SYSFONT=UniCyr_8x16
DICTIONARY=russian
MPAGE="-CKOI8-R"
export DICTIONARY MPAGE
Однако хранить настройки специфической программы (не нужные всем остальным) в окружении — не самое удачное решение: синтаксис, задающий переменную окружения, слишком прост (имя_переменной = значение), а самих переменных становится слишком много, поэтому при просмотре трудно выделить, какая из них к какой группе настроек относится. Если пытаться упаковать все настройки в значение одной переменной, это значение окажется трудночитаемым, и всё преимущество текстового формата сойдёт на нет. Например, стандартный конфигурационный файл утилиты ls
(точнее, только её цветовых предпочтений) — /etc/DIR_COLORS
(его можно подменить личным файлом ~/.dir_colors
) занимает около ста строк вместе с комментариями. Команда ls
использует не этот файл, а создаваемую утилитой dircolors
переменную LS_COLORS
, значене которой — 600-символьная строка безо всяких комментариев.
Если профиль слишком велик, держать его в одном конфигурационном файле — значит, доставлять пользователю сомнительное удовольствие разбирать этот файл целиком даже при необходимости внести минимальное изменение. Методов борьбы с неудобочитаемостью несколько. Во-первых, уже известный по лекции Этапы загрузки системы механизм «. d»: файл разделяется на несколько независимых друг от друга так, что редактировать приходится только один из файлов, а программа во время самонастройки считывает все.
Другой способ опирается на то, что изменения, которые пользователь вносит в профиль, как правило существенно меньше объёма всего профиля. Поэтому может быть выгодно хранить все настройки по умолчанию в каком-нибудь файле, менять который вообще не надо, а файл пользовательских настроек использовать как бы «поверх», изменяя профиль в соотвтетствии с ними после того, как выставлен профиль по умолчанию. Дополнительная выгода такого способа — в том, что пользователь всегда может подглядеть в «большой» файл, чтобы узнать как оформляется та или иная настройка. Например, утилита updfstab
, которая изменяет содержимое /etc/fstab
при появлении или удалении съёмного дискового носителя (например, лазерного диска), считывает данные из конфигурационного файла /etc/updfstab.conf
. Сам этот файл состоит из единственной строки: include /etc/updfstab.conf.default
, что приводит к чтению файла с настройками по умолчанию, где задан способ работы со многими съёмными устройствами системы. Если администратору нужно как-то изменить поведение updfstab
в отношении определённого устройства, он копирует соответствующую группу настроек из updfstab.conf.default
в updfstab.conf
после строчки include...
и исправляет их. То, что эти группы настроек читаются дважды, не играет особой роли: чтение коротких файлов выполняется быстро.
Наконец, третий способ сделать конфигурационный файл удобочитаемым — это секционирование профиля, когда все настройки разбиваются на группы, каждой группе даётся собственное имя, и синтаксис конфигурационного файла проектируется так, чтобы границы групп хорошо различались при просмотре. В сущности, этот способ — предок схемы «. d», где группе соответствует отдельный файл, однако нередки ситуации, когда разбиение на файлы неудобно (например, если группы не полностью независимы, поэтому может понадобиться редактировать их сразу и несколько). Конфигурационный файл утилиты дозвона wvdial
, например, секционируется по адресату (провайдеру) плюс отдельная секция «по умолчанию». Сами секции отделяются друг от друга заголовками, заключёнными в квадратные скобки:
root@localhost:~> cat .wvdialrc
[Dialer Defaults]
Modem = /dev/modem
Baud = 115200
Init1 = ATZ
Init2 = ATQ0 L0 M4 V1 E1 S0=0 &C1 &D2 +FCLASS=0
Auto DNS = on
Modem Type = Analog Modem
[Dialer hotspace]
Phone = 0123456
Username = fireman
Password = Fire!Fire!
TOnline = true
[Dialer warlock]
Phone = 0246813
Username = cop-120
Password = gimmethegun
Force Address=10.0.0.120
Утилита wvdial
обладает высокоразвитым искусственным интеллектом: она самостоятельно догадывается, какой именно тип идентификации используется на сервере. Например, «с той стороны» может оказаться терминал Linux, которому требуется сначала ввести обыкновенное входное имя и пароль, затем надо получить командную строку, затем запустить на сервере сетевой демон pppd
, и только затем запустить pppd
на собственной машине. Другой вариант: pppd
на сервере уже запущен, а настройки «Username» и «Password» означают идентификационную информацию протокола CHAP, используемого pppd
. Обо всём этом и о многом другом wvdial
умеет догадываться, как wvdialconf
умел определять, какое же из устройств — модем.
Однако на любой искуственный интеллект найдется непостижимая ему жизненная ситуация. На одном из серверов (секция «Dialer hotspace») тоже стоит программа с зачатками искуственного интеллекта, которая тоже пытается определить, каким способом хочет идентифицироваться позвонивший. Оттого эти два кудесника, созвонившись, всё ждут, пока кто-нибудь не проявит себя... Помогает настройка TOnline
, которая заставляет wvdial
немедленно использовать протокол ppp
, на что сервер, подумавши «ах, ppp
!», с облегчением запускает pppd
. Остаётся вопросом: почему эта полезная настройка никак не отражена в документации (её нашёл в исходных текстах программы Гуревич)? Не потому ли, что пара wvdialconf
-wvdial
не по Linux-овски стремится всё делать за пользователя, а стало быть, пользовательская документация для разработчиков этой программы — не главное?..
Идею чтения настроек по умолчанию можно развить чуть дальше. Оказывается, бывает удобно, когда описание настройки помещено не в руководство, а непосредственно в конфигурационный файл в виде комментариев. Тогда при изменении этой настройки пользователь сразу видит, что она из себя представляет, при этом отпадает необходимость сначала находить строчку в файле, а потом искать её же в руководстве. Такой способ оформления называется самодокументированием профиля и часто используется. Например, файл /etc/man.conf
, управляющий работой команды man
, оформлен в самодокументированном стиле:
methody@localhost:~ $ cat /etc/man.conf
. . .
# NOCACHE keeps man from creating cache pages ("cat pages")
# (generally one enables/disable cat page creation by creating/deleting
# the directory they would live in - man never does mkdir)
#
# NOCACHE
# The command "man -a xyzzy" will show all man pages for xyzzy.
# When CMP is defined man will try to avoid showing the same
# text twice. (But compressed pages compare unequal.)
#
CMP /usr/bin/cmp -s
. . .
Мефодий, может быть, и не понял бы сразу, зачем команде man
использовать утилиту cmp
, однако в поясняющем комментарии написано: когда нужно показать несколько руководств разом, предварительно они сравниваются, и показываются только несовпадающие.
Если пойти ещё дальше, то можно создать несколько различных файлов с примерами настроек, чтобы пользователь мог взять один из них и довести до нужного ему состояния. Именно такую — демонстрационную — настройку Мефодий и включил в качестве настройки по умолчанию в свой .vimrc
(в первой строке). Кстати, на самом деле профиль Vi весьма сложен, но большинство его настроек по умолчанию находятся в различных файлах дерева каталогов /usr/share/vim
— эдакая «схема. d/. d», где файлы профиля, соответствующие подгруппам настроек, лежат в подкаталогах, соответствующих группам. Включение определённого настроечного файла может происходить неявно: например, строчка colorscheme desert
из .vimrc
приводит к чтению /usr/share/vim/colors/desert.vim
.
Конфигурационные файлы могут иметь довольно замысловатый синтаксис, если соответствуют сложным структурам данных (таковы, например, настройка irc-клиента irssi
) или содержать в себе дополнительные средства самодокументиования (например, файл настройки текстового www-броузера lynx
не просто хорошо документирован, но и размечен теми же средствами, какие используются в самом броузере для представления HTML).
Как правило, конфигурационный файл считывается программой при запуске, отражая, таким образом, её состояние на момент старта. Изменения настроек работающей программы в конфигурационном файле, как правило, не отражаются. Тому есть несколько причин: не стоит превращать файл, изредка редактируемый пользователем, в файл, изменение которого происходит постоянно; не стоит держать конфигурационный файл всегда открытым; тяжело, изменяя файл автоматически, не испортить структуру комментариев (интерпретируемых не машиной, а пользователем) и т. д. Впрочем, многие утилиты, особенно использующие графическую среду, могут записывать настройки в файл по окончании работы. Большинство конфигурационных файлов весьма удобно редактировать вручную, с помощью Vi или Emacs (для файлов более или менее похожих используется общая подсветка синтаксиса, а для наиболее популярных есть и собственные варианты подсветки).
В /etc
хранятся настройки системных служб, в том числе настройки по умолчанию, настройки по умолчанию пользовательских утилит, профили командных интерпретаторов, а также настройки, используемые в процессе загрузки системы (например, modules.conf
). Там же располагаются и стартовые сценарии, о которых рассказано в лекции Этапы загрузки системы. Чего не стоит искать в /etc
, так это разнообразных примеров настройки той или иной службы. Считается, что пример — это часть документации, и их стоит помещать, например, в /usr/share/doc/название_службы/examples
.
Файлы, имеющие отношение к процессу досистемной загрузки, обычно лежат не там, а в /boot
, это стоит иметь в виду, так как /boot/grub/menu.lst
— тоже часть профиля системы, хотя и довольно специфическая. В профиль системы входит содержимое каталогов etc
из т. н. «песочниц», расположенных обычно в /var/lib
.
Смысл термина «песочница» вот в чём. В Linux есть замечательный системный вызов chroot()
и использующая его утилита chroot
, формат командной строки которой chroot каталог команда
. Эта утилита запускает команду, изменив окружение таким образом, что та считает каталог — корневым. Соответственно, все подкаталоги каталога кажутся команде каталогами первого уровня вложенности, и т. д. Если необходимо во что бы то ни стало ограничить область действия некоторой утилиты (например, по причине её небезопасности), можно запускать её с помощью chroot
. Тогда, даже имея права суперпользователя, эта утилита получит доступ только к каталогу и его подкаталогам, а /etc
и прочие важные части системы окажутся в неприкосновенности. Сам каталог как раз и играет роль «песочницы», в которую утилиту «пустили поиграть», позволяя вытворять что угодно. Часто бывает, что в песочнице есть и свой каталог etc
, содержащий необходимые для запуска утилиты (или системной службы) настройки. Вот этот-то etc
из «песочницы» также входит в список каталогов, хранящих профиль системы.
В /etc
могут находиться не только файлы, но и подкаталоги (особенно в стиле «. d») и целые поддеревья каталогов. Например, в некоторых дистрибутивах Linux используется подкаталог /etc/sysconfig
. Этот каталог создаётся и заполняется файлами при установке системы или при запуске специального «конфигуратора» — программы-кудесника, задающей наводящие вопросы. Некоторые стартовые сценарии, использующие полученные настройки, также лежат в этом каталоге или его подкаталогах. Если в системе есть каталог /etc/sysconfig
, там должны оказаться настройки, относящиеся не к самим службам или утилитам, а к способу их запуска при загрузке, а также языковые и сетевые настройки, тип мыши и т. д.
Несколько конфигурационных файлов и способы работы с ними заслуживают отдельного рассмотрения. В первую очередь Мефодий заинтересовался системой учётных записей, о которой упоминалось сразу в нескольких предыдущих лекциях. Итак, существует два файла, доступных для чтения всем пользователям: /etc/passwd
, хранящий учётные данные пользователей, и /etc/group
, определяющий членство пользователей в группах (во всех, кроме группы по умолчанию):
methody@localhost:~ $ cat /etc/passwd
root:x:0:0:System Administrator:/root:/bin/bash
bin:x:1:1:bin:/:/dev/null
daemon:x:2:2:daemon:/:/dev/null
adm:x:3:4:adm:/var/adm:/dev/null
lp:x:4:7:lp:/var/spool/lpd:/dev/null
. . .
nobody:x:99:99:Nobody:/var/nobody:/dev/null
shogun:x:400:400:Лев Гуревич:/home/shogun:/bin/zsh
methody:x:503:503:Мефодий Кашин:/home/methody:/bin/bash
methody@localhost:~ $ cat /etc/group
root:x:0:root
bin:x:1:root,bin,daemon
daemon:x:2:root,bin,daemon
sys:x:3:root,bin,adm
adm:x:4:root,adm,daemon,shogun
wheel:x:10:root,shogun
. . .
proc:x:19:root,shogun
shogun:x:400:
methody:x:503:
/etc/passwd
и /etc/group
Оба файла состоят из сторк, поля которых разделяются двоеточиями. В файле passwd
— семь полей. Первое из них определяет входное имя пользователя — то самое, что вводится в ответ на «login:». Второе поле раньше, в ранних версиях UNIX, использовалось для хранения ключа пароля. В Linux пользовательские пароли не хранятся нигде, ни в явном виде, ни в зашифрованном. Вместо этого хранится ключ (hash) пароля — комбинация символов, однозначно соответствующая паролю, из которой, однако, сам пароль получить нельзя. Иными словами, существует алгоритм превращения пароля в ключ, а алгоритма превращения ключа в пароль нет. Когда пользователь регистрируется в системе, из введённого им пароля изготавливается ещё один ключ. Если он совпадает с тем, что хранится в учётной записи, значит, пароль правильный.
Авторы UNIX предполагали, что, раз пароль из ключа получить нельзя, ключ можно выставлять на всеобщее обозрение, однако выяснилось, что, узнав ключ, пароль можно попросту подобрать на очень мощной машине или в предположении, что пароль — это английское слово, год рождения, имя любимой кошки и т. п. Если подбор пароля сопровождается неудачными попытками входа в систему, это отражается в системных журналах и может быть легко замечено. А завладев ключом, злоумышленник сможет заняться подбором втихомолку на каком-нибудь суперкомпьютре. В конце концов, ключ не нужен никому, кроме подсистемы идентификации, поэтому его, вместе с другими полями учётной заиси, о которых знать всем не обязательно, из /etc/passwd
перенесли в «теневой» файл учётных записей /etc/shadow
. На месте ключа в Linux должна быть буква «x
»; если там стоит что-то другое, идентификация пользователя не сработает, и он не сможет войти в систему.
Третье и четвёртое поля passwd
— идентификатор пользователя и идентификатор группы по умолчанию. Как уже говорилось в лекции Права доступа именно идентификатор пользователя, а не его входное имя, однозначно определяет пользователя в системе и его права. Вполне возможно создать несколько учётных записей с одинаковыми UID, которые различаются другими полями. Тогда при регистрации в системе под именами из этих записей, пользователи могут получать разные домашние каталоги и командные оболочки, разное членство в группах, но иметь одинаковый UID. Пятое поле отводится под «полное имя» пользователя; это единственное поле passwd
, содерджимое которого ни на что не влияет. Наконец, шестое и седьмое поля содержат домашний каталог и стартовый командный интерпретатор пользователя. Если седьмое поле пусто, подразумевается /bin/sh
, а если его содержиимое не встречается в файле /etc/shells
, содержащего допустимые командные интерпретаторы, неизбежны трудности при идентификации пользователя.
Строки файла /etc/group
состоят из четырёх полей, причём второе — ключ группового пароля — обычно не используется. Первое поле — это имя группы, третье — это идентификатор группы, а четвёртое — это список входных имён пользователей, которые в эту группу входят (более точно — для которых эта группа является дополнительной, так как группа по умолчанию указывается в /etc/passwd
, хотя никто не мешает продублировать группу по умолчанию и в /etc/group
). Таким образом, определение членства пользователя в группах зависит не от его UID, а от входного имени.
Упомянутый выше файл /etc/shadow
, доступ к котрому имеет только суперпользователь, также состоит из полей, разделяемых двоеточиями. Помимо входного имени и ключа пароя там указываются различные временные характеристики учётной записи: нижняя и верхняя граница времени жизни пароля, самой учётной записи, дата последнего изменения и т. п. Ключ пароля (второе поле) указывается в одном из поддерживаемых форматов, а если формат не опознан, вся учётная запись считается недействительной. Поэтому один из способов запретить регистрацию пользователя в системе — добавить один символ (например, «!
») прямо в поле ключа, отчего всё поле становится синтаксически неправильным. Вновь разрешить пользователью входить в систему можно, удалив из поля лишний символ. Именно так работает (с ключами «-L
», lock, и «-U
», unlock) утилита usermod
, изменяющая учётную запись. С помощью этой утилиты можно отредактировать и все остальные поля как passwd
, так и shadow
.
Добавить и удалить пользователя или группу можно с помощью утилит useradd
, userdel
, groupadd
и groupdel
соответственно. Не стоит пользоваться текстовым редактором, так как он не гарантирует атомарности операции добавления/удаления, хотя бы потому, что изменению подлежат сразу два файла — passwd
и shadow
. Даже если необходимо отредактировать /etc/passwd
или /etc/group
(например, для добавления пользователя в группу или удаления его оттуда), стоит запускать не просто редактор, а vipw
или vigr
(именно их поведение, позволяющее соблюсти атомарность, копирует утилита visudo
, описанная ранее).
[root@localhost root]# useradd -g users -G proc,cdrom -c "Incognito" incognito
[root@localhost root]# id incognito
uid=504(incognito) gid=100(users) groups=100(users),19(proc),22(cdrom)
[root@localhost root]# userdel -r incognito
[root@localhost root]# id incognito
id: incognito: No such user
Здесь был добавлен пользователь incognito
, группа по умолчанию — users
, член групп proc
и cdrom
, полное имя — «Incognito». Стоит заметить, что пароль для этой учётной записи установлен не был (чтобы создать пароль, стоило запустить команду passwd incognito
), и, даже если бы пользователя тут же не удалили (userdel -r
удаляет также и домашний каталог, и почтовый ящик), зарегистрироваться в системе под имененем incognito
было бы всё равно невозможно.
Подсистемой учётных записей пользуется подсистема идентификации, которая в Linux имеет модульную структуру и называется PAM (Pluggable Authentication Modules, т. е. Подключаемые Модули Идентификации). Идея PAM — в том, чтобы унифицировать и, вместе с тем, сделать более гибкой любые процедуры идентификации в системе — начиная от команды login
и заканчивая доступом к файлам по протоколу, скажем, FTP. Для этого недостаточно просто написать «библиотеку идентификации» и заставить все программы её использовать. В зависимости от того, для чего производится идентификация, условия, при которых оня будет успешной, могут быть более или менее строгими, а если она прошла успешно, бывает нужно выполнить действия, связанные не с определенем пользователя, а с настройкой системы.
В большинстве дистрибутивов PAM обучен схеме «. d», и настройки каждой службы, которая использует идентификацию, лежат в отдельном файле:
[root@localhost root]# ls /etc/pam.d
chpasswd groupdel other system-auth userdel
chpasswd-newusers groupmod passwd system-auth-use_first_pass usermod
crond login sshd user-group-mod
groupadd newusers su useradd
В PAM определено четыре случая, требующие идентифкации: auth
— собственно идентификация, определние, тот ли пользователь, за кого он себя выдаёт, account
— определение, всё ли хорошо с учётной записью пользователя, password
— изменение пароля в учётной записи, и session
— дополнительные действия непосредственно перед или непосредственно после того, как пользователь получит доступ к затребованной услуге. Эти значения принимает первое поле любого файла настройки из pam.d
, а в третьем поле записывается модуль, который проверяет какой-нибудь из аспектов идентификации. Второе поле определяет, как успех или неуспех проверки одного модуля влияет на общий успех или неуспех идентификации данного типа (например, required
означает, что в случае неуспеха модуля проверка пройдена не будет). Четвёртое и последующие поля отведены под параметры модуля.
[root@localhost root]# cat /etc/pam.d/login
auth include system-auth
auth required pam_nologin.so
account include system-auth
password include system-auth
session include system-auth
session optional pam_console.so
[root@localhost root]# cat /etc/pam.d/system-auth
auth required pam_tcb.so shadow count=8 nullok
account required pam_tcb.so shadow
password required pam_tcb.so use_authtok shadow count=8 write_to=tcb
session required pam_tcb.so
login
Такие настройки login
обнаружил Мефодий на своём компьютере. Во всех четырёх случаях используется включаемый файл system-auth
(к нему обращаются и другие службы), с некоторыми дополнениями: во время идентификации pam_nologin.so
дополнительно проверяет, не запрещено ли пользователям регистрироваться вообще (как это бывает за несколько минут до перезагрузки системы), а перед входом в систему и после выхода из неё pam_console.so
выполняет описанную в лекции Права доступа «передачу прав на владение устройствами» (и, соответственно, лишение пользователя этих прав).
Каталог /etc/pam.d
— замечательный пример того, как профиль определяет поведение системы. В частности, четыре первых строки из system-auth
показывают, что в этом дистрибутиве используется не просто «теневой» файл паролей, а схема TCB, описанная в лекции Права доступа. (Как уже известно Мефодию, в этой схеме вместо общего для всех /etc/shadow
задействованы файлы вида /etc/tcb/входное_имя/shadow
, причём права доступа к ним устроены таким образом, чтобы при выполнении команды passwd
можно было обойтись без подмены пользовательского идентификатора на суперпользовательский).
Проста и остроумна в Linux подсистема ведения системных журналов — демон syslogd
, управляемый конфигурационным файлом /etc/syslog.conf
и «. d»-каталогом /etc/syslog.d
. Если какой-нибудь демон или служба желает сообщить системе о том, что наступило событие, которое стоит запомнить, у неё есть два пути. Во-первых, можно просто добавлять очередную запись в файл, который сам этот демон и открыл; этот файл будет журналом его сообщений. Во-вторых, можно воспользоваться системным вызовом syslog()
, который переадресует текстовое сообщение специальному демону — syslogd
— а уж тот разберётся, что с этим сообщением делать: записать в файл, вывести на 12-ю консоль или забыть о нём. Второй путь (централизованная журнализация) предпочтительнее почти всегда; исключение — случай, когда сообщения по какой-то причине не могут быть текстовыми или этих сообщений предполагается посылать так много, что syslogd
просто не справится.
Все события, сообщаемые syslogd
, подразделяются горизонтально — по типу службы (facility), с которой это событие произошло события, и вертикально — по степени его важности (priority). Типов событий насчитывается около двадцати (среди них auth
, daemon
, kern
, mail
и т. п., а также восемь неименованных, от local0
до local7
). Степеней важности всего восемь, по возрастанию важности: debug
, info
, notice
, warning
, err
, crit
, alert
и emerg
. Таким образом, каждое событие определяется парой значений, например, mail.err
означает для syslogd
событие, связанное с почтой, притом важности не меньшей err
. Из таких пар (с возможной заменой типа или важности на «*
», что означает «любые», или none
, что означает «никакие») составляется конфигурационный файл /etc/syslog.conf
:
[root@localhost root]# cat /etc/syslog.conf
*.notice;mail.err;authpriv.err /var/log/messages
authpriv.*;auth.* /var/log/security.log
*.emerg *
*.* /dev/tty12
mail.info /var/log/maillog
В первом поле строки указываются профили сообщений, разделённые символом «;
», а во втором — хранилище сообщений (файл, терминал, есть способы отдавать их на обработку программе и пересылать по сети). В примере в файл /var/log/messages
попадают все сообщения важности не меньшей, чем notice
, за исключением сообщений типа mail
и authpriv
, которые попадают туда, только если имеют важность не ниже err
. Сообщения типа authpriv
и auth
любой важности попадают в файл /var/log/security.log
, а типа mail
и важности не ниже info
— в файл /var/log/maillog
. Сообщения типа emerg
(наивысшей важности) выводятся на все терминалы системы, и, наконец, все сообщения выводятся на 12-ю виртуальную консоль.
Во многих системах используется основательно доработанный syslogd
, позволяющий фильтровать сообщения не только по типу/важности, но и, например, по отправителю, задавать точные (а не «не меньшие») значения priority и т. п., однако такие доработки нужны для того, чтобы либо вести практически нефильтрованную журнализацию (получаются системные журналы совершенно нечитаемого объёма), либо отводить поток сообщений определённой службы в отдельный журнал, опять-таки, не для чтения, а для последующей обработки.
Стоит заметить, что каталог /etc/syslog.d
в новых версиях syslogd
предназначен для хранения не профильных конфигурационных файлов в стиле «. d», а сокетов, из которых демон может получать сообщения так же, как и из сети или в результате системного вызова syslog()
.
Другой пример типичной для Linux службы, управляемой конфигурационным файлом, — демон cron
3, регулярно выполняющий в заданное время заданные действия. Время от времени в системе необходимо обновлять разнообразные файлы, например, базы данных антивирусов (вирусов в Linux нет, а антивирусы есть!), базу данных whatis
или список всех доступных на чтение файлов системы, locatedb
(поискать по этому списку можно командой locate
); нужно собирать статистику по работе системы, анализировать цельность системы (этим занимаются службы OSec, TripWire или AIDE) и производить множество других регулярных действий. Всем этим и занимается демон cron
.
Конфигурационный файл демона cron
называется /etc/crontab
.
[root@localhost root]# cat /etc/crontab
#minute (0-59),
#| hour (0-23),
#| | day of the month (1-31),
#| | | month of the year (1-12),
#| | | | day of the week (0-6 with 0=Sunday).
#| | | | | user
#| | | | | | commands
01 * * * * root run-parts /etc/cron.hourly
02 4 * * * root run-parts /etc/cron.daily
22 4 * * 0 root run-parts /etc/cron.weekly
42 4 1 * * root run-parts /etc/cron.monthly
cron
Первые пять полей этого файла определяют время запуска команды: минуту, час, число месяца, месяц и день недели. Символ «*
» означает, что соответствующая часть даты не учитывается. Шестое поле — имя пользователя, от лица которого зпускаются команда, указанная в остальных полях строки. Так, в примере команда run-parts /etc/cron.weekly
будет запускаться в 4 часа 22 минуты каждое воскресенье (нулевой день) любого числа любого месяца.
Как видно из примера, обычно /etc/crontab
невелик: чаще всего он состоит из почасового, подённого, понедельного и помесячного запуска специального сценария (в примере — run-parts
). Этот сценарии реализует упрощённую схему «. d», он попросту запускает отсортированные в лексикографическом порядке4 сценарии из соответствующего каталога (например, из /etc/cron.daily
):
[root@localhost root]# ls /etc/cron.daily
000anacron logrotate makewhatis osec stmpclean updatedb
Вот что происходит каждый день на машине Мефодия: запуск anacron
и «прокручивание» системных журналов (об этом речь пойдёт далее), обновление базы whatis
, проверка цельности системы с помощью osec
, прореживание старых и неиспользуемых файлов в /tmp
(утилита stmpclean
) и, наконец, одновление базы locatedb
.
Пользователям системы можно разрешить иметь собственные расписания, также обрабатываемые демоном cron
. Эти расписания имеют тот же синтаксис, что и crontab
, только шестое поле («user») в них отсутствует. Редактировать пользовательские таблицы рекомендуется с помощью команды crontab -e
(чтобы не подсунуть демону синтаксически неверный файл). Сами таблицы могут храниться, в зависимости от версии и настроек cron
, в /var/spool/cron/crontabs
, /var/spool/cron
, /var/cron/tabs
или ещё где-нибудь.
Служба anacron
появилась в Linux-системах в то время, когда их начали активно использовать на пресональных рабочих станциях. Такие станции, в отличие от серверов, не обязаны работать круглосуточно. Скорее всего, на ночь, на праздники и на время отпуска их выключают. Это значит, что все настройки cron
надо менять в соответствии с графиком включений/выключений (иначе cron.daily
никогда не выполнится в четыре часа ночи) — или запускать отдельную службу, которая будет выполнять некоторые задачи не по расписанию, а потому что их давно уже пора запустить5. Дополнительно anacron
рассчитывает запуск задач так, чтобы не перегрузить компьютер работой, если их накопилось слишком много. Конфигурационный файл anacron
называется /etc/anacrontab
.
Ещё изучая работу syslog
, Мефодий не расставался с мыслью, что файл, в котором записывается системный журнал, постоянно растёт. Это значит, что каков бы ни был размер файловой системы /var
, она в конце концов заполнится журналами под завязку — если как-то их не укорачиваить. К сожалению, в Linux укоротить файл от начала, отрезав самые старые записи, нельзя, как нельзя и добавлять новые записи в начало файла. Эти операции легко реализовать с помощью копирования нужной области в новый файл и последующего переименования, но, во-первых, соблюсти атомарность таких составных операций нелегко, а во-вторых, они требуют удвоенного места в файловой системе на время работы (и, стало быть, каких-то аварийных процедур на случай нехватки места).
Поэтому в Linux принят другой, существенно менее ресурсоёмкий алгоритм, позволяющий избежать переаолнения /var
: т. н. «прокручивание» системных журналов. Суть алгоритма в следующем: когда настаёт пора укоротить журнал (например, раз в неделю или если файл журнала достиг определённого размера), этот файл переименовывают, и открывают новый пустой файл с тем же именем. Если хранить несколько (скажем, семь) переименованных старых файлов, с ними уже можно произвоить операцию «отбрасывния старого»: самый старый — седьмой — файл удаляется, шестой переименовывается в седьмой, пятый — в шестой, и т. д. до первого (моложе которого только текущий журнал), который переименовывается во второй. Только тогда можно переименовать текущий журнал в «первый старый», и открыть новый. Получается очередь устаревающих файлов, пополняемая с одной стороны и усекаемая с другой.
Как правило имя «первого старого» журнала получается добавлением к имени журнала суффикса «.1
», второго — «.2
» и т. д.:
[root@localhost root]# ls -l /var/log/syslog/messages*
-rw-r----- 1 root adm 292654 Dec 15 14:01 /var/log/syslog/messages
-rw-r----- 1 root adm 34452 Dec 13 01:09 /var/log/syslog/messages.1.bz2
-rw-r----- 1 root adm 35892 Dec 6 09:38 /var/log/syslog/messages.2.bz2
-rw-r----- 1 root adm 60806 Nov 28 10:59 /var/log/syslog/messages.3.bz2
-rw-r----- 1 root adm 61063 Nov 21 10:47 /var/log/syslog/messages.4.bz2
-rw-r----- 1 root adm 60079 Nov 14 21:18 /var/log/syslog/messages.5.bz2
messages
Прокручиванием системных журналов занимается утилита logrotate
, которая тоже управляется и конфигурационным файлом /etc/logrotate.conf
и «. d»-каталогом /etc/logrotate.d/
. Согласно настройкам, старые файлы можно сжимать упаковщиками bzip2
(как в примере) или gzip
, можно задавать им определённые права доступа, можно посылать сигнал некоторой службе (чтобы она заметила подмену журнала, если она сама, а не syslogd
занимается его пополнением) и т. п.
Немало конфигурационных файлов находится в домашнем каталоге пользователя. Этими файлами практически любая утилита может быть перенастроена по сравнению с обычным поведением, или поведением, задаваемым конфигурационным файлом из /etc
. В Linux принято предоставлять пользователю возможность задавать профиль любого используемого им инструмента, начиная от простой утилиты и заканчивая графической подсистемой управления «рабочим столом» (см. об этом лекцию Графический интерфейс (X11)). Как правило, имена таких файлов или каталогов начинаются на «.
», т. е. считаются скрытыми — для того, чтобы не засорять выдачу ls
. Если пользователю нужно работать не со своими файлами, а именно с настройками, он всегда может использовать ключ «-a
» или «-A
»:
methody@localhost:~ $ ls
bin cat.info cat.stderr Documents examples grep.info textfile tmp
methody@localhost:~ $ ls -AF
.alias .bashrc .emacs .inputrc~ textfile .Xauthority
.bash_history bin/ examples/ .lpoptions tmp/ .xsession.d/
.bash_logout cat.info grep.info .pinerc .viminfo
.bash_profile cat.stderr .i18n .pyhistory .vimrc
.bash_profile~ Documents/ .inputrc .pythonstartup .vimrc~
methody@localhost:~ $ rm .*~
Многие утилиты создают конфигурационный файл при запуске, если его в домашнем каталоге пользователя нет, поэтому со временем объём ls -A
становится всё больше. Файл .lpoptions
задаёт параметры подсистемы печати, .pinerc
— это настройки почтового клиента pine
, .viminfo
— файл истории команд редактора Vi, а файл .Xauthority
и каталог .xsession.d
управляют запуском графической подсистемы X``
, описанной в лекции Графический интерфейс (X11). Из файлов в примере некоторые вообще не являются «стандартными»: так, .aliases
и .i18n
просто «втягиваются» стартовым командным сценарием bash
, потому что упомянуты в нём явно; строго говоря, они могли бы называться и по-другому. Все конфигурационные, стартовые и прочие вспомогательные файлы принято делать скрытыми даже если никаких требований к их названиям нет.
Файл .pythonstartup
(настройки интерпретатора языка программирования Python) выполняется потому, что имя этого файла задано в переменной окружения PYTHONSTARTUP
. Мефодию пришлось дописать строку PYTHONSTARTUP="/home/methody/.pythonstartup"; export PYTHONSTARTUP
в ~/.bash_profile
и "C-i": complete
в ~/.inputrc
, чтобы достраивание заработало и в этом интерпретаторе. Ещё один файл, .pyhistory
, используется в самом .pythonstartup
:
methody@localhost:~ $ cat .pythonstartup
import atexit, os, readline, rlcompleter
historyPath = os.path.expanduser("~/.pyhistory")
def save_history(historyPath=historyPath):
import readline
readline.write_history_file(historyPath)
if os.path.exists(historyPath):
readline.read_history_file(historyPath)
atexit.register(save_history)
del os, atexit, readline, rlcompleter, save_history, historyPath
Подавляющее большинство конфигурационных файлов предназначено для того, чтобы их мог редактировать пользователь. Эти файлы часто имеют самодокументированный формат и/или сопровождаются руководством, нередко выделенным а отдельный от руководства по самой утилите документ.
1Эта команда определяет, файлы какого типа и в каком количестве содержатся в каталоге /etc
.
2Обозначение «i18n» происходит от слова «internationalization», в котором 20 букв, т. е. «i», «n» и 18 букв между ними.
3Программисты имели в виду Хроноса, стихийное божество времени у древних греков. Уже сами греки часто называли так «владыку неба» титана Крона (отца богов-кронидов и, среди прочих, Зевса, который впоследствии папу и других титанов заключил в Тартар, и стал владыкой неба сам). У древних римлян и Крон и Хронос почитались под именем Сатурна, божества неумолимого времени.
4То есть в таком порядке, в котором они были бы расставлены в словаре. Причём цифры предшествуют алфавитным знакам, а между собой сортируются по возрастанию, от 0 до 9. Отсюда «000anacron
» — такое имя обеспечит, чтобы этот сценарий был выполнен самым первым.
5Название этого демона пародирует cron
с намёком на анахронизм, то есть несвоевременность выполнения заданий.