ハンドブック:AMD64/ワーキング/Initscripts

From Gentoo Wiki
Jump to: navigation, search
This page is a translated version of the page Handbook:AMD64/Working/Initscripts and the translation is 100% complete.

Other languages:
Deutsch • ‎English • ‎español • ‎日本語 • ‎한국어 • ‎polski • ‎русский • ‎українська • ‎中文(中国大陆)‎
AMD64 Handbook
Installation
About the installation
Choosing the media
Configuring the network
Preparing the disks
Installing stage3
Installing base system
Configuring the kernel
Configuring the system
Installing tools
Configuring the bootloader
Finalizing
Working with Gentoo
Portage introduction
USE flags
Portage features
Initscript system
Environment variables
Working with Portage
Files and directories
Variables
Mixing software branches
Additional tools
Custom package repository
Advanced features
Network configuration
Getting started
Advanced configuration
Modular networking
Wireless
Adding functionality
Dynamic management


ランレベル

システムの起動

システムが起動すると、たくさんのテキストが流れます。注意して見ると、(通常) それが毎回同じ内容であることに気づくでしょう。これら全てのアクションの実行はブートシーケンスと呼ばれ、(ほぼ) 静的に定義されます。

まずブートローダーが、ブートローダーの設定で定義されたカーネルイメージを読み込みます。そしてブートローダーはCPUにカーネルを実行するよう指示します。カーネルが読み込まれ実行されると、全てのカーネル内の構造やタスクが初期化され、initプロセスを開始します。

このプロセスではまず (/etc/fstab に書かれた) すべてのファイルシステムをマウントし、使用可能な状態にします。そして /etc/init.d/ に置かれたいくつかのスクリプトを実行します。これらのスクリプトは、システムのブートに必要なサービスを立ち上げます。

全てのスクリプトを実行したら、initは最後に端末 (ほとんどの場合 Alt+F1Alt+F2 などの下に隠された仮想端末ですが) を agetty と呼ばれる特別なプロセスに接続します。このプロセスは login を実行することで各端末からユーザーがログオンできるようにします。

Initscripts

ところで、initは /etc/init.d/ 内のスクリプトを適当に実行するわけではありません。それどころか、/etc/init.d/ 内のスクリプトを全て実行するわけでもなく、指示されたスクリプトだけを実行します。実行するスクリプトは /etc/runlevels/ を見て決定しています。

まずinitは /etc/init.d/ にあるスクリプトのうち、/etc/runlevels/boot/ にシンボリックリンクが存在するものを全て実行します。基本的にはアルファベット順に開始しますが、一部のスクリプトは自身の前に開始しなければならないスクリプトを示す依存情報を持っています。

/etc/runlevels/boot/ から参照されている全てのスクリプトを実行すると、続けてinitは /etc/runlevels/default/ にシンボリックリンクが存在するスクリプトを実行します。繰り返しになりますが、正しい起動シーケンスのために順序を変更する依存情報をスクリプトが持っていない限り、アルファベット順でスクリプトの実行順が決まります。これは Gentoo Linux のインストールで実行した rc-update add sshd default などのコマンドで default を使った理由でもあります。

init の仕組み

Of course init doesn't decide all that by itself. It needs a configuration file that specifies what actions need to be taken. This configuration file is /etc/inittab.

Remember the boot sequence that was just described - init's first action is to mount all file systems. This is defined in the following line from /etc/inittab:

FILE /etc/inittabInitialization command
si::sysinit:/sbin/rc sysinit

This line tells init that it must run /sbin/rc sysinit to initialize the system. The /sbin/rc script takes care of the initialization, so one might say that init doesn't do much - it delegates the task of initializing the system to another process.

Second, init executed all scripts that had symbolic links in /etc/runlevels/boot/. This is defined in the following line:

FILE /etc/inittabBoot command invocation
rc::bootwait:/sbin/rc boot

Again the rc script performs the necessary tasks. Note that the option given to rc (boot) is the same as the subdirectory of /etc/runlevels/ that is used.

Now init checks its configuration file to see what runlevel it should run. To decide this, it reads the following line from /etc/inittab:

FILE /etc/inittabDefault runlevel selection
id:3:initdefault:

In this case (which the majority of Gentoo users will use), the runlevel id is 3. Using this information, init checks what it must run to start runlevel 3:

FILE /etc/inittabRunlevel definitions
l0:0:wait:/sbin/rc shutdown
l1:S1:wait:/sbin/rc single
l2:2:wait:/sbin/rc nonetwork
l3:3:wait:/sbin/rc default
l4:4:wait:/sbin/rc default
l5:5:wait:/sbin/rc default
l6:6:wait:/sbin/rc reboot

The line that defines level 3, again, uses the rc script to start the services (now with argument default). Again note that the argument of rc is the same as the subdirectory from /etc/runlevels/.

When rc has finished, init decides what virtual consoles it should activate and what commands need to be run at each console:

FILE /etc/inittabTerminal definitions
c1:12345:respawn:/sbin/agetty 38400 tty1 linux
c2:12345:respawn:/sbin/agetty 38400 tty2 linux
c3:12345:respawn:/sbin/agetty 38400 tty3 linux
c4:12345:respawn:/sbin/agetty 38400 tty4 linux
c5:12345:respawn:/sbin/agetty 38400 tty5 linux
c6:12345:respawn:/sbin/agetty 38400 tty6 linux

有効なランレベル

In a previous section, we saw that init uses a numbering scheme to decide what runlevel it should activate. A runlevel is a state in which the system is running and contains a collection of scripts (runlevel scripts or initscripts) that must be executed when entering or leaving a runlevel.

In Gentoo, there are seven runlevels defined: three internal runlevels, and four user-defined runlevels. The internal runlevels are called sysinit, shutdown and reboot and do exactly what their names imply: initialize the system, powering off the system, and rebooting the system.

The user-defined runlevels are those with an accompanying /etc/runlevels/ subdirectory: boot, default, nonetwork and single. The boot runlevel starts all system-necessary services which all other runlevels use. The remaining three runlevels differ in what services they start: default is used for day-to-day operations, nonetwork is used in case no network connectivity is required, and single is used when the system needs to be fixed.

init スクリプトの使い方

The scripts that the rc process starts are called init scripts. Each script in /etc/init.d/ can be executed with the arguments start, stop, restart, zap, status, ineed, iuse, needsme, usesme, or broken.

To start, stop, or restart a service (and all depending services), the start, stop, and restart arguments should be used:

root #/etc/init.d/postfix start
Note
Only the services that need the given service are stopped or restarted. The other depending services (those that use the service but don't need it) are left untouched.

To stop a service, but not the services that depend on it, use the --nodeps option together with the stop argument:

root #/etc/init.d/postfix --nodeps stop

To see what status a service has (started, stopped, ...) use the status argument:

root #/etc/init.d/postfix status

If the status information shows that the service is running, but in reality it is not, then reset the status information to "stopped" with the zap argument:

root #/etc/init.d/postfix zap

To also ask what dependencies the service has, use iuse or ineed. With ineed it is possible to see the services that are really necessary for the correct functioning of the service. iuse on the other hand shows the services that can be used by the service, but are not necessary for the correct functioning.

root #/etc/init.d/postfix ineed

Similarly, it is possible to ask what services require the service (needsme) or can use it (usesme):

root #/etc/init.d/postfix needsme

Finally, to ask what dependencies the service requires that are missing:

root #/etc/init.d/postfix broken

ランレベルの更新

rc-update

Gentoo's init system uses a dependency-tree to decide what service needs to be started first. As this is a tedious task that we wouldn't want our users to have to do manually, we have created tools that ease the administration of the runlevels and init scripts.

With rc-update it is possible to add and remove init scripts to a runlevel. The rc-update tool will then automatically ask the depscan.sh script to rebuild the dependency tree.

サービスの追加と削除

In earlier instructions, init scripts have already been added to the "default" runlevel. What "default" means has been explained earlier in this document. Next to the runlevel, the rc-update script requires a second argument that defines the action: add, del, or show.

To add or remove an init script, just give rc-update the add or del argument, followed by the init script and the runlevel. For instance:

root #rc-update del postfix default

The rc-update -v show command will show all the available init scripts and list at which runlevels they will execute:

root #rc-update -v show

It is also possible to run rc-update show (without -v) to just view enabled init scripts and their runlevels.

サービスの設定

追加の設定が必要な理由

Init scripts can be quite complex. It is therefore not really desirable to have the users edit the init script directly, as it would make it more error-prone. It is however important to be able to configure such a service. For instance, users might want to give more options to the service itself.

A second reason to have this configuration outside the init script is to be able to update the init scripts without the fear that the user's configuration changes will be undone.

conf.d ディレクトリ

Gentoo provides an easy way to configure such a service: every init script that can be configured has a file in /etc/conf.d/. For instance, the apache2 initscript (called /etc/init.d/apache2) has a configuration file called /etc/conf.d/apache2, which can contain the options to give to the Apache 2 server when it is started:

FILE /etc/conf.d/apache2Example options for apache2 init script
APACHE2_OPTS="-D PHP5"

Such a configuration file contains only variables (just like /etc/portage/make.conf does), making it very easy to configure services. It also allows us to provide more information about the variables (as comments).

initスクリプトを書く

これは必要な作業なんですか

No, writing an init script is usually not necessary as Gentoo provides ready-to-use init scripts for all provided services. However, some users might have installed a service without using Portage, in which case they will most likely have to create an init script.

Do not use the init script provided by the service if it isn't explicitly written for Gentoo: Gentoo's init scripts are not compatible with the init scripts used by other distributions! That is, unless the other distribution is using OpenRC!

レイアウト

The basic layout of an init script is shown below.

CODE Example initscript layout
#!/sbin/openrc-run
  
depend() {
#  (Dependency information)
}
  
start() {
#  (Commands necessary to start the service)
}
  
stop() {
#  (Commands necessary to stop the service)
}

Every init script requires the start() function to be defined. All other sections are optional.

依存関係

There are two dependency-alike settings that can be defined which influence the start-up or sequencing of init scripts: use and need. Next to these two, there are also two order-influencing methods called before and after. These last two are no dependencies per se - they do not make the original init script fail if the selected one isn't scheduled to start (or fails to start).

  • The use settings informs the init system that this script uses functionality offered by the selected script, but does not directly depend on it. A good example would be use logger or use dns. If those services are available, they will be put in good use, but if the system does not have a logger or DNS server the services will still work. If the services exist, then they are started before the script that uses them.
  • The need setting is a hard dependency. It means that the script that is needing another script will not start before the other script is launched successfully. Also, if that other script is restarted, then this one will be restarted as well.
  • When using before, then the given script is launched before the selected one if the selected one is part of the init level. So an init script xdm that defines before alsasound will start before the alsasound script, but only if alsasound is scheduled to start as well in the same init level. If alsasound is not scheduled to start too, then this particular setting has no effect and xdm will be started when the init system deems it most appropriate.
  • Similarly, after informs the init system that the given script should be launched after the selected one if the selected one is part of the init level. If not, then the setting has no effect and the script will be launched by the init system when it deems it most appropriate.

It should be clear from the above that need is the only "true" dependency setting as it affects if the script will be started or not. All the others are merely pointers towards the init system to clarify in which order scripts can be (or should be) launched.

Now, look at many of Gentoo's available init scripts and notice that some have dependencies on things that are no init scripts. These "things" we call virtuals.

A virtual dependency is a dependency that a service provides, but that is not provided solely by that service. An init script can depend on a system logger, but there are many system loggers available (metalogd, syslog-ng, sysklogd, ...). As the script cannot need every single one of them (no sensible system has all these system loggers installed and running) we made sure that all these services provide a virtual dependency.

For instance, take a look at the postfix dependency information:

FILE /etc/init.d/postfixDependency information of the postfix service
depend() {
  need net
  use logger dns
  provide mta
}

As can be seen, the postfix service:

  • Requires the (virtual) net dependency (which is provided by, for instance, /etc/init.d/net.eth0).
  • Uses the (virtual) logger dependency (which is provided by, for instance, /etc/init.d/syslog-ng).
  • Uses the (virtual) dns dependency (which is provided by, for instance, /etc/init.d/named).
  • Provides the (virtual) mta dependency (which is common for all mail servers).

順序の制御

As described in the previous section, it is possible to tell the init system what order it should use for starting (or stopping) scripts. This ordering is handled both through the dependency settings use and need, but also through the order settings before and after. As we have described these earlier already, let's take a look at the portmap service as an example of such init script.

FILE /etc/init.d/portmapDependency information of the portmap service
depend() {
  need net
  before inetd
  before xinetd
}

It is possible to use the "*" glob to catch all services in the same runlevel, although this isn't advisable.

CODE Using the * glob
depend() {
  before *
}

If the service must write to local disks, it should need localmount. If it places anything in /var/run/ such as a pidfile, then it should start after bootmisc:

CODE Dependency setting with needing localmount and after bootmisc
depend() {
  need localmount
  after bootmisc
}

標準関数

Next to the depend() functionality, it is also necessary to define the start() function. This one contains all the commands necessary to initialize the service. It is advisable to use the ebegin and eend functions to inform the user about what is happening:

CODE Example start() function
start() {
  if [ "${RC_CMD}" = "restart" ];
  then
    # Do something in case a restart requires more than stop, start
  fi
  
  ebegin "Starting my_service"
  start-stop-daemon --start --exec /path/to/my_service \
    --pidfile /path/to/my_pidfile
  eend $?
}

Both --exec and --pidfile should be used in start and stop functions. If the service does not create a pidfile, then use --make-pidfile if possible, though it is recommended to test this to be sure. Otherwise, don't use pidfiles. It is also possible to add --quiet to the start-stop-daemon options, but this is not recommended unless the service is extremely verbose. Using --quiet may hinder debugging if the service fails to start.

Another notable setting used in the above example is to check the contents of the RC_CMD variable. Unlike the previous init script system, the newer openrc system does not support script-specific restart functionality. Instead, the script needs to check the contents of the RC_CMD variable to see if a function (be it start() or stop()) is called as part of a restart or not.

Note
Make sure that --exec actually calls a service and not just a shell script that launches services and exits - that's what the init script is supposed to do.

For more examples of the start() function, please read the source code of the available init scripts in the /etc/init.d/ directory.

Another function that can (but does not have to) be defined is stop(). The init system is intelligent enough to fill in this function by itself if start-stop-daemon is used.

CODE Example stop() function
stop() {
  ebegin "Stopping my_service"
  start-stop-daemon --stop --exec /path/to/my_service \
    --pidfile /path/to/my_pidfile
  eend $?
}

If the service runs some other script (for example, Bash, Python, or Perl), and this script later changes names (for example, foo.py to foo), then it is necessary to add --name to start-stop-daemon. This must specify the name that the script will be changed to. In this example, a service starts foo.py, which changes names to foo:

CODE Example definition for a service that starts the foo script
start() {
  ebegin "Starting my_script"
  start-stop-daemon --start --exec /path/to/my_script \
    --pidfile /path/to/my_pidfile --name foo
  eend $?
}

start-stop-daemon has an excellent man page available if more information is needed:

user $man start-stop-daemon

Gentoo's init script syntax is based on the POSIX Shell so people are free to use sh-compatible constructs inside their init scripts. Keep other constructs, like bash-specific ones, out of the init scripts to ensure that the scripts remain functional regardless of the change Gentoo might do on its init system.

カスタムオプションの追加

If the initscript needs to support more options than the ones we have already encountered, then add the option to the extra_commands variable, and create a function with the same name as the option. For instance, to support an option called restartdelay:

CODE Example definition of restartdelay method
extra_commands="restartdelay"
  
restartdelay() {
  stop
  sleep 3    # Wait 3 seconds before starting again
  start
}
Important
The restart() function cannot be overridden in OpenRC!

Service configuration variables

In order to support configuration files in /etc/conf.d/, no specifics need to be implemented: when the init script is executed, the following files are automatically sourced (i.e. the variables are available to use):

  • /etc/conf.d/YOUR_INIT_SCRIPT
  • /etc/conf.d/basic
  • /etc/rc.conf

Also, if the init script provides a virtual dependency (such as net), the file associated with that dependency (such as /etc/conf.d/net) will be sourced too.

ランレベルの動きを変える

これが役に立つ人

Many laptop users know the situation: at home they need to start net.eth0, but they don't want to start net.eth0 while on the road (as there is no network available). With Gentoo the runlevel behaviour can be altered at will.

For instance, a second "default" runlevel can be created which can be booted that has other init scripts assigned to it. At boottime, the user can then select what default runlevel to use.

softlevel を使う

First of all, create the runlevel directory for the second "default" runlevel. As an example we create the offline runlevel:

root #mkdir /etc/runlevels/offline

Add the necessary init scripts to the newly created runlevel. For instance, to have an exact copy of the current default runlevel but without net.eth0:

root #cd /etc/runlevels/default
root #for service in *; do rc-update add $service offline; done
root #rc-update del net.eth0 offline
root #rc-update show offline
(Partial sample Output)
               acpid | offline
          domainname | offline
               local | offline
            net.eth0 |

Even though net.eth0 has been removed from the offline runlevel, udev might want to attempt to start any devices it detects and launch the appropriate services, a functionality that is called hotplugging. By default, Gentoo does not enable hotplugging.

To enable hotplugging, but only for a selected set of scripts, use the rc_hotplug variable in /etc/rc.conf:

FILE /etc/rc.confEnable hotplugging of the WLAN interface
rc_hotplug="net.wlan !net.*"
Note
For more information on device initiated services, please see the comments inside /etc/rc.conf.

Edit the bootloader configuration and add a new entry for the offline runlevel. In that entry, add softlevel=offline as a boot parameter.

bootlevel を使う

Using bootlevel is completely analogous to softlevel. The only difference here is that a second "boot" runlevel is defined instead of a second "default" runlevel.