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: