7 Присоединение rc.d скрипта к инфраструктуре

После того, как скрипт был написан, его необходимо присоединить к rc.d инфраструктуре. Решающим шагом является установка скрипта в /etc/rc.d (для базовой системы) или в /usr/local/etc/rc.d (для портов). В файлах <bsd.prog.mk> и <bsd.port.mk> существуют специальные процедуры для этого, и вам не придется заботиться о правильных правах доступа и владельце скриптов. Системные скрипты должны устанавливаться из директории src/etc/rc.d и должны быть указаны для этого в Makefile, находящемся в этой директории. Скрипты из портов должны устанавливаться с помощью переменной USE_RC_SUBR, как это описано в в руководстве по созданию портов.

Но перед установкой мы должны указать место нашего скрипта в загрузочной последовательности. Сервисы, управляемые нашими скриптами почти наверняка зависят от других сервисов. Например, любой сетевой демон не сможет работать без активных сетевых интерфейсов, роутинга и тому подобного. Даже если кажется, что сервис ни от чего не зависит, ему, по крайней мере, требуется смонтированная файловая система.

Мы уже упоминали об rcorder(8). Сейчас настал момент, взглянуть на эту программу поближе. В двух словах, rcorder(8) принимает в качестве аргументов список файлов, проверяет их содержимое и выводит упорядоченный список в stdout. Идея держать информацию о зависимостях внутри заключается в том, чтобы каждый файл имел структурную зависимость только от себя. Внутри файла можно указать следующую информацию:

В нормальном случае rcorder(8) может оперировать только с текстовыми файлами, синтаксис которых похож на синтаксис sh(1). Поэтому, специальные строки, обрабатываемые rcorder(8) выглядят как комментарии в языке sh(1). Синтаксис этих строк очень жесткий, чтобы упростить их обработку. Смотрите rcorder(8) для более детальной информации.

Кроме того, использование специальных строк позволяет скрипту настаивать на зависимости от другого сервиса с принудительным стартом последнего. Это может быть необходимым в случае, когда сервис-зависимость является необязательным или не запускается, потому что администратор по ошибке отключил его в rc.conf(5).

Обладая этими знаниями, давайте улучшим наш скрипт для запуска простого демона, добавив в него зависимости:

#!/bin/sh

# PROVIDE: mumbled oldmumble (1)
# REQUIRE: DAEMON cleanvar frotz(2)
# BEFORE:  LOGIN(3)
# KEYWORD: nojail shutdown(4)

. /etc/rc.subr

name="mumbled"
rcvar=`set_rcvar`
command="/usr/sbin/${name}"
start_precmd="${name}_prestart"

mumbled_prestart()
{
    if ! checkyesno frotz_enable && \
        ! /etc/rc.d/frotz forcestatus 1>/dev/null 2>&1; then
        force_depend frotz || return 1(5)
    fi
    return 0
}

load_rc_config $name
run_rc_command "$1"


А теперь, как и прежде, проведем детальный анализ изменений:

(1)
Эта строка описывает ''условия'', предоставляемые нашим скриптом. Теперь, другие скрипты могут перечислять в списке ''условий'' ''условия'', предоставляемые нашим скриптом, тем самым они станут зависимы от нашего скрипта.

Note: Обычно, скрипт может предоставлять только одно условие, тем не менее, ничто не мешает нам указать несколько условий. Например, для совместимости с предыдущими версиями.

В любом случае, название первого из этих условий (или единственного) должно быть таким же, как значение переменной ${name}.

(2)(3)
Кроме всего прочего, наш скрипт определяет ''условия'', от которых он зависит. Следуя этим строкам rcorder(8) запустит наш скрипт после скриптов, предоставляющих условия DAEMON и cleanvar, но до скрипта предоставляющего условие LOGIN.

Note: Строка BEFORE: не формирует полный список зависимостей. Правильный способ ее использования - если другой скрипт может корректно запуститься без нашего скрипта, но наш скрипт может помочь ему сделать это лучше. Типичный пример из жизни - сетевые интерфейсы и файерволы. Интерфейс на самом деле не зависит от фаервола, но из соображений безопасности системы иногда необходимо, чтобы фаервол был инициализирован до появления какого бы то ни было сетевого трафика зависимостей. Правильный способ ее использования - если другой скрипт может корректно запуститься без нашего скрипта, но наш скрипт может помочь ему сделать это лучше. Типичный пример из жизни - сетевые интерфейсы и файерволы. Интерфейс на самом деле не зависит от фаервола, но из соображений безопасности системы иногда необходимо, чтобы фаервол был инициализирован до появления какого бы то ни было сетевого трафика.

Кроме условий, соответствующих отдельному сервису, существуют мета-условия и их ''контейнеры''. Они описывают группы операций. Их можно отличить по написанию их названий в верхнем регистре. Список контейнеров и их назначение можно найти на странице справочника rc(8).

помните, что размещение сервиса в строке REQUIRE: не гарантирует, что этот сервис будет действительно запущен до запуска нашего сервиса. Требуемый сервис может быть отключен в rc.conf(5) или просто не сумеет запуститься из-за ошибки. Очевидно, что rcorder(8) не способен отследить такие тонкости, так же этого не может проконтролировать и rcorder(8). Тем не менее, наш скрипт должен уметь проконтролировать подобную ситуацию. Как это можно сделать в некоторых случаях описано ниже.

(4)
Как мы помним, ключевые слова rcorder(8) могут использоваться для запуска групп скриптов или исключения таких групп из запуска. А именно, используя опции rcorder(8) -k и -s мы можем указывать список ключевых слов для групп, которые нужно выполнить (keep list) или которые нужно пропустить (skip list) соответственно. Из всех файлов, которые необходимо отсортировать в порядке исполнения rcorder(8) выберет только те, в которых указано ключевое слово из списка выполнения и не указано ключевое слово из списка пропуска.

В FreeBSD, rcorder(8) используется в скрипте /etc/rc и скрипте /etc/rc.shutdown. Эти два скрипта определяют стандартный для FreeBSD список ключевых слов и их значений. Например:

nojail

Сервис не для запуска в jail(8). Автоматические процедуры запуска и остановки системы будут игнорировать такие скрипты, если они запускаются изнутри jail(8).

nostart

Сервис не должен запускаться вручную или не должен запускаться вообще. Автоматическая процедура запуска будет игнорировать такие скрипты. В сочетании с ключевым словом shutdown это может использоваться для скриптов, которые должны делать что-либо только при завершении системы.

shutdown

Это ключевое слово должно использоваться только для сервисов, которые нужно остановить перед остановкой всей системы.

Note: Когда система завершает работу, запускается скрипт /etc/rc.shutdown. Этот скрипт подразумевает, что большая часть rc.d скриптов ничего не делает в этот момент. Поэтому, /etc/rc.shutdown не пытается запустить rc.d скрипты, а запускает только те из них, в которых содержится ключевое слово shutdown и полностью игнорирует все остальные. Для еще более быстрого завершения работы системы /etc/rc.shutdown завершает все скрипты с аргументом faststop, то есть, скрипты пропускают некоторые стандартные проверки, такие как проверку на наличие файла с идентификатором процесса (pidfile). Так как зависимые сервисы должны быть завершены раньше, чем те от которых они зависят, то /etc/rc.shutdown запускает скрипты в обратном порядке.

Если вы пишете rc.d скрипт, вы должны знать как он будет вести себя в момент завершения работы системы. Другими словами, если ваш скрипт выполняет свои действия по команде start, то вам не нужно указывать в нем ключевое слово shutdown. Если же ваш скрипт управляет работой сервиса, хорошей идеей будет остановить его перед фактическим завершением работы системы, описанным в halt(8). В частности такой подход необходим к сервисам, которым необходимо время для корректного завершения. Типичный пример такого сервиса - база данных.

(5)
Для начала, помните, что функция force_depend должна использоваться с большой осторожностью. В общем случае, лучше будет изменить иерархию конфигурационных переменных в ваших rc.d скриптах, если они независимы.

Если же вы никак не можете обойтись без функции force_depend, в нашем примере представлен типичный пример, как это сделать корректным образом. Нашему демону mumbled дополнительно нужен для работы сервис frotz. Тем не менее сервис frotz - вспомогательный. К счастью, наш скрипт имеет доступ ко всем переменным rc.conf(5). Если переменная frotz_enable установлена и ее значение - истина, мы надеемся на лучшее и полностью полагаемся на rc.d в запуске сервиса frotz. В противном случае, мы форсированно проверяем статус сервиса frotz, и если сервис не запущен, мы запускаем сервис frotz функцией force_depend. Предупреждающее сообщение будет показано при вызове этой функции так как этот вызов произойдет только в случае ошибки конфигурации.

Этот, и другие документы, могут быть скачаны с ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/.

По вопросам, связанным с FreeBSD, прочитайте документацию прежде чем писать в <questions@FreeBSD.org>.
По вопросам, связанным с этой документацией, пишите <doc@FreeBSD.org>.
По вопросам, связанным с русским переводом документации, пишите в рассылку <frdp@FreeBSD.org.ua>.
Информация по подписке на эту рассылку находится на сайте проекта перевода.