Development/Tasks/Packaging/Policies/Initscripts
Материал из Mandriva Russian Community Wiki
Процессам-«демонам» часто необходим сценарий загрузки (загрузочный скрипт), который бы их запустил во время загрузки системы. Для того, чтобы облегчить интеграцию и управление службами, вы должны придерживать некоторых правил.
Содержание |
Сценарий загрузки: использование
Для управления initscripts, вам понадобятся программы service и chkconfig.
service
service — сценарий командной оболочки, используемый для запуска и остановки служб, запускаемый сам или с xinetd. Использование:
service <имя_службы> <действие>
где «действие» может принимать значения: start, stop, status, restart, reload (тоже самое, что и restart, если пакет не поддерживает перезагрузку «на лету») или любое другое, которое поддерживается сценарием.
Chkconfig
Chkconfig используется для управления конфигурационными файлами /etc/rc?.d/* и xinetd. Chkconfig удаляет и добавляет символические ссылки, чтобы останавливать и запускать службы при загрузке определённого уровня запуска (runlevel). Вы должны использовать Chkconfig для добавления вашего сценария в систему. Macros are provided to wrap the call to chkconfig.
Пример
Общий пример. Другие можно найти в /etc/init.d/. It should be quite easy to adapt, as you only need to change the variable DAEMON_NAME.
#!/bin/sh
#
### BEGIN INIT INFO
# Provides: some_daemon
# Required-Start: $network
# Required-Stop: $network
# Default-Start: 3 4 5
# Short-Description: nothing
# Description: some_daemon is nothing.
# Really, nothing.
### END INIT INFO
# Source function library.
. /etc/rc.d/init.d/functions
DAEMON_NAME=some_daemon
DAEMON_PROCESS=some_daemon
DAEMON_BINARY=some_daemon
LOCK_FILE=/var/lock/subsys/$DAEMON_NAME
RETVAL=0
# default option, they can be overriden in /etc/sysconfig/$DAEMON_NAME
# of course, you can place what you want.
OPTIONS=
PORT=1234
# this file should be commented, with proper pointer to the doc, and you
should use
# more than one line of option, if possible.
[ -f /etc/sysconfig/$DAEMON_NAME ] && . /etc/sysconfig/$DAEMON_NAME
# here, you can do what you want with the option
start() {
# if you cannot start the daemon since something is missing ( like a
path that cannot be set by default
# , place the test here
# if [ -z "$SOME_VAR" ]; then
# echo "You need to set $SOME_VAR in /etc/sysconfig/$DAEMON_NAME"
# RETVAL=1
# return
# fi
[ -f $LOCK_FILE ] && return
echo -n "Starting $DAEMON_NAME: "
# use --user to run the daemon under the specified uid
daemon $DAEMON_BINARY $OPTIONS -p $PORT
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && touch $LOCK_FILE
}
stop() {
echo -n "Shutting down $DAEMON_NAME: "
killproc $DAEMON_PROCESS
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && rm -f $LOCK_FILE
}
reload() {
echo -n "Reloading $DAEMON_NAME configuration: "
killproc $DAEMON_PROCESS SIGHUP
RETVAL=$?
echo
}
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status $DAEMON_PROCESS
RETVAL=$?
;;
reload)
reload
;;
restart)
stop
start
;;
condrestart)
if [ -f $LOCK_FILE ]; then
stop
start
fi
;;
*)
echo "Usage: $0 {start|stop|restart|reload|condrestart|status}"
RETVAL=1
esac
exit $RETVAL
Заголовок
Заголовок initscript должен использовать теги LSB (заголовки chkconfig больше использоваться не должны). Для получения более подробной информации обратитесь к странице формат заголовка сценария загрузки.
Первая часть — настройка
# Source function library. . /etc/rc.d/init.d/functions DAEMON_NAME=some_daemon DAEMON_PROCESS=some_daemon DAEMON_BINARY=some_daemon LOCK_FILE=/var/lock/subsys/$DAEMON_NAME RETVAL=0 # default option, they can be overriden in /etc/sysconfig/$DAEMON_NAME # of course, you can place what you want. OPTIONS= PORT=1234 # this file should be commented, with proper pointer to the doc, and you should use # more than one line of option, if possible. [ -f /etc/sysconfig/$DAEMON_NAME ] && . /etc/sysconfig/$DAEMON_NAME # here, you can do what you want with the option
Первая часть связана с функциями предоставляемыми /etc/rc.d/init.d/functions/.
Вторая часть связана с полезными глобальными переменными сценария. $DAEMON_NAME используется для предоставления обратной связи с пользователем. $DAEMON_BINARY используется, чтобы действительно запустить службу. $DAEMON_PROCESS используется, чтобы общаться с запущенной службой. В зависимости от службы, они могут быть идентичными или отличаться. RETVAL используется для состояния выполненного действия.
Третья часть связана с настройкой службы. Если служба принимает некоторые параметры, вы должны установить конфигурационный файл в /etc/sysconfig/. Чтобы упростить жизнь администраторам и предотвратить неразбериху в пространстве имён, он должен иметь тоже имя, что и служба. Размещение значений по умолчанию в сценарий и в его конфигурационный файл более надёжно, чем размещение значений по умолчанию только в конфигурационном файле.
Вторая часть - Primitives
Использование функции для определения primitives, такие как start и stop позволяет перезапускать службу без reinvocating the script itself. Каждая из этих функций должна устанавливать RETVAL.
Типичный сценарий нуждается в следующих функциях:
start() {
# if you cannot start the daemon since something is missing ( like a
path that cannot be set by default
# , place the test here
# if [ -z "$SOME_VAR" ]; then
# echo "You need to set $SOME_VAR in /etc/sysconfig/$DAEMON_NAME"
# RETVAL=1
# return
# fi
[ -f $LOCK_FILE ] && return
echo -n "Starting $DAEMON_NAME: "
# use --user to run the daemon under the specified uid
daemon $DAEMON_BINARY $OPTIONS -p $PORT
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && touch $LOCK_FILE
}
Эта функция используется для запуска службы. You have to take care of the lock before launching, to avoid having several instances running simultaneously. Функция "daemon" заботится обо всём (using libsafe, creating a pidfile, etc.), и возвратит OK только в том случае, если запуск прошёл успешно. Однако, некоторые «демоны» не «падают» вначале, как старые версии openldap, и показывают OK, даже если они не работают.
stop() {
echo -n "Shutting down $DAEMON_NAME: "
killproc $DAEMON_PROCESS
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && rm -f $LOCK_FILE
}
Эта функция используется для остановки службы. Функция "killproc" отправляет процессу сигнал SIGSTOP (or any other given signal). Then you have to remove the lock file.
reload() {
echo -n "Reloading $DAEMON_NAME configuration: "
killproc $DAEMON_PROCESS SIGHUP
echo
}
Эта функция используется для перезапуска службы (если эта функция поддерживается «демоном»).
Last part - Handling arguments
case "$1" in
.....
esac
exit $RETVAL
Типичный сценарий принимает следующие действия:
start
Вызвать функцию start().
stop
Вызвать функцию stop().
status
Вызвать функцию status().
restart
restart)
stop
start
;;
Это действие просто вызывает stop и start, не проверяя запущена ли в данный момент служба.
condrestart
condrestart)
if [ -f $LOCK_FILE ]; then
stop
start
fi
;;
Это действие вызывает stop и start, но только в том случае, если служба запущена.
reload
Просто вызывает функцию reload(), если она есть. Иначе, you may as well handle "reload" as "restart"
default action
*)
echo "Usage: $0 {start|stop|restart|reload|condrestart|status}"
RETVAL=1
Если нет никаких совпадений ни с одним из определённых действий, сценарий показывает сообщение об ошибке и завершается с кодом возврата 1, чтобы показать, что возникла ошибка. Если вы добавите какие-то действия, не забудьте добавить их в сообщение об ошибке. Bash completion использует регулярное выражение matching "Usage: . {.'}", поэтому не изменяйте формат сообщения.
Функция daemon
Функция "daemon" определяется в /etc/init.d/functions. Right now, there are some arguments to pass:
- --user <uid>: запустить «демон» с определённым UID
- --check <name>: определить имя файла с PIDto define the name of file with the pid of the process to check (in /var/run/$name.pid )
- +/-[0-9]: чтобы установить уровень nice. Уровень nice может быть также установлен в конфигурационном файле переменной NICELEVEL
Integration in a package
In order to integrate your services in a package, you should be sure of the following points:
File naming
The filename should not contain a dot, as chkconfig will not take it into account. And, it should have the same name as the package, in lower case. The file should be in the directory /etc/rc.d/init.d/, which is linked by /etc/init.d. An rpm macro exists for this path, %_initrddir.
/home/misc $ rpm --eval %_initrddir /etc/rc.d/init.d
Files permissions
The script should be runnable by root, and therefore, the permissions should be rwxr-xr-x. Msec resets the permissions once installed.
Macro in %postun and %post
In order to register your service, you need to place 2 macros, one in the %preun , one in the %post section :
# service_name is the name of the script %post %_post_service service_name %preun %_preun_service service_name
See the scripts /usr/share/rpm-helper/add-service and del-service. They take care of adding it to the boot sequence, and stopping or restarting the service if needed.
If the service should not be started by default because it misses some configuration, add the logic for checking in the initscript, and provide documentation regarding the setup to finish.
RPM requirements
As the previous macro use rpm-helper script, you need to be sure that rpm-helper is installed before the current rpm. All you need is to add this tag to the spec:
Requires(pre): rpm-helper
Rpmlint errors
rpmlint uses a dedicated module to check the initscript. You can find it in the rpmlint package, file InitScriptCheck.py.
LSB Compliance
The Linux Standard Base proposes the following specs: http://refspecs.freestandards.org/LSB_2.0.0/LSB-Core/LSB-Core/iniscrptact.html
Localisation issues
Mandriva uses gprintf, which calls gettext with the localisation domain of "initscripts" to get the localisation of the first string passed to gprintf, replacing macros (such as %s ) with the remaining arguments. To reduce the load for localisers, it is best to ensure that gprintf calls keep the first string constant, so use:
gprintf "Starting %s: " $DAEMON_NAME
instead of:
gprintf "Starting $DAEMON_NAME: "
This allows just one translation to be maintained for each similar call. White space can have an effect too ... so be careful to ensure the string is kept exactly the same.
Note that the usage of "echo" calls will be replaced with gprintf calls (by /usr/share/spec-helper/gprintify.py which is called from /usr/share/spec-helper/spec-helper which is called from /usr/lib/rpm/brp-mandrake ). Sometimes, gprintify can mess up your good use of gprintf ... in which case you should export DONT_GPRINTIFY in the %install section of your spec file to prevent gprintify.py from being called.
Initscript Header format
As of Mandriva Linux 2007, initscripts should be described using LSB headers instead of chkconfig headers. Adding LSB headers will allow having a robust services dependency check, and to provide a base for parallelized initialization, so packagers are all encouraged to add LSB headers to their packages.
Migration to LSB headers
Let's take the previous dm init script as an example.
It contained the following comments: # chkconfig: 5 30 09 # description: This startup script launches the graphical display manager.
LSB comments
We can add LSB headers in a block delimited by the following lines:
### BEGIN INIT INFO ### END INIT INFO
Facility provides
Each initscript should provide a facility name. The services should be named preferably using this policy: http://www.lanana.org/lsbreg/init/init.txt
Facilities that begin with a $ sign are reserved system facilities, such as $network. A complete list is available here: http://www.linuxbase.org/spec/booksets/LSB-Core-generic/LSB-Core-generic/facilname.html
# Provides: dm
Start dependencies
The services required by an initscript should be described using the Required-Start (mandatory service) or Should-Start (optional service) tags.
# Required-Start: xfs # Should-Start: $network harddrake
Required services
Required-Start means that the listed services must be available for this service. The initscript system will make sure that they are ( chkconfig will enforce the dependency).
Optional services
Should-Start means that the listed services should be available for this service if possible. If an optional service is enabled in this runlevel, it will be started before. If it is not enabled, its start will not be enforced by the initscript system.
Stop dependencies
If another service has to be available during stop, the mandatory dependency should be described using a Required-Stop tag:
# Required-Stop: xfs
The same is available for optional dependencies, using the Should-Stop flag.
Runlevels
You can specify which runlevels the service should be started in using the Default-Start tag.
# Default-Start: 5
Descriptions
Descriptions have to be provided using the Short-Description and Description (potentially multi-line) tags.
# Short-Description: Launches the graphical display manager # Description: This startup script launches the graphical display # manager.
Final result
The dm initscript will finally end up with the following LSB header:
### BEGIN INIT INFO # Provides: dm # Required-Start: xfs # Required-Stop: xfs # Should-Start: $network harddrake # Default-Start: 5 # Short-Description: Launches the graphical display manager # Description: This startup script launches the graphical display manager. ### END INIT INFO
Interactive initscripts
Some initscripts request some user input, such as harddrake when a new device is found. Since this will be quite Mandriva specific, we will use the X-Mandriva-Interactive tag (the LSB asks for an X-implementor-extension format).
# X-Mandriva-Interactive
Mandriva should probably request a vendor tag from the Linux Assigned Names And Numbers Authority, see: http://www.lanana.org/lsbreg/providers/providers.txt
References
For a more complete reference about the LSB headers, please see:

