9.6. システムのユニットファイルの作成および変更

ユニットファイルには、ユニットを説明し、その動作を定義する設定ディレクティブが含まれます。複数の systemctl コマンドがバックグラウンドでユニットファイルと動作します。細かい調整を行うには、システム管理者は手動でユニットファイルを編集するか、または作成する必要があります。表9.2「Systemd Unit ファイルの場所」 は、システム上のユニットファイルが保存される 3 つのメインディレクトリーを一覧表示し、/etc/systemd/system/ ディレクトリーは、システム管理者が作成するか、またはカスタマイズするユニットファイル用に予約されます。
ユニットファイル名は以下のフォーマットを使用します。
unit_name.type_extension
ここで、unit_name はユニットの名前を表し、type_extension はユニットタイプを特定します。ユニットタイプの詳細な一覧については、表9.1「利用可能な systemd Unit タイプ」 を参照してください。たとえば、通常システムには sshd.service および sshd.socket ユニットがあります。
ユニットファイルには、追加の設定ファイルのディレクトリーを追加できます。たとえば、カスタム設定オプションを sshd.service に追加するには、sshd.service.d/custom.conf ファイルを追加し、追加のディレクティブを挿入します。設定ディレクティブについての詳細情報は、「既存のユニットファイルの変更」 を参照してください。
さらに、sshd.service.wants/ および sshd.service.requires/ ディレクトリーを作成することもできます。それらのディレクトリーには、sshd サービスの依存関係であるユニットファイルへのシンボリックリンクが含まれます。シンボリックリンクは、[Install] ユニットファイルオプションに従ってインストール時に自動的に作成されるか (表9.11「[Install] セクションの重要なオプション」 を参照)、または [Unit] オプションに基づいてランタイム時に自動的に作成されます (表9.9「[Unit] セクションの重要なオプション」 を参照)。さらに、それらのディレクトリーおよびシンボリックリンクを手動で作成することもできます。
多くのユニットファイルオプションは、いわゆる ユニット指定子 を使用して設定できます。これは、ユニットファイルが読み込まれる際にユニットパラメーターに動的に置き換えられるワイルドカード文字列です。これにより、インスタンス化されるユニットを生成するためのテンプレートとして機能する汎用ユニットファイルを作成できます。詳細は、「インスタンス化されたユニットの使用」 を参照してください。

9.6.1. ユニットファイル構造の概要

通常、ユニットファイルは 3 つのセクションで構成されています。
  • [Unit] — ユニットのタイプに依存しない汎用的なオプションが含まれます。これらのオプションはユニットの説明を提供し、ユニットの動作を指定し、他のユニットへの依存関係を設定します。最も頻繁に使用される [Unit] オプションの一覧については、表9.9「[Unit] セクションの重要なオプション」 を参照してください。
  • [unit type] — ユニットにタイプ固有のディレクティブがある場合、それらはユニットタイプに基づいて名前が付けられるセクションにグループ分けされます。たとえば、サービスユニットファイルには [Service] セクションが含まれます。最も頻繁に使用される [Service] オプションについては、表9.10「[Service] セクションの重要なオプション」 を参照してください。
  • [Install] — systemctl enable および disable コマンドで使用されるインストールについての情報が含まれます。[Install] オプションの一覧については、表9.11「[Install] セクションの重要なオプション」 を参照してください。

表9.9 [Unit] セクションの重要なオプション

オプション[a]詳細
Descriptionユニットの分かりやすい説明です。このテキストは、たとえば systemctl status コマンドの出力に表示されます。
Documentationユニットのドキュメントを参照する URI の一覧を提供します。
After[b]ユニットが開始される順序を定義します。ユニットは、After で指定されたユニットがアクティブにされた後にのみ開始されます。Requires とは異なり、After は指定されたユニットを明示的にアクティブ化しません。Before オプションには、After とは反対の機能があります。
Requires他のユニットに依存関係を設定します。Requires に一覧表示されるユニットは、該当ユニットと共にアクティブ化されます。必要なユニットのいずれかが開始しない場合、ユニットはアクティブ化されません。
WantsRequires よりも強度の弱い依存関係を設定します。一覧表示されるユニットのいずれかが正常に開始しない場合も、ユニットのアクティべーションには影響を与えません。これは、カスタムのユニット依存関係を設定する際に推奨される方法です。
ConflictsRequires とは反対の、負の依存関係を設定します。
[a] [Unit] セクションで設定可能なオプションの詳細な一覧は、systemd.unit(5) man ページを参照してください。
[b] ほとんどの場合、After および Before ユニットファイルオプションで依存関係の並び順を設定するだけで十分です。Wants (推奨) または Requires で要件の依存関係も設定する場合、依存関係の並び順は指定する必要があります。これは、並び順と要件の依存関係が相互に依存していないためです。

表9.10 [Service] セクションの重要なオプション

オプション[a]詳細
TypeExecStart および関連オプションの機能に影響を与えるユニットプロセスの起動タイプを設定します。以下のいずれかになります。
  • simple – デフォルトの値です。ExecStart で起動するプロセスはサービスのメインプロセスです。
  • forkingExecStart で起動するプロセスは、サービスのメインプロセスになる子プロセスを起動します。親プロセスは、このプロセスが完了すると終了します。
  • oneshot – このタイプは simple と同様ですが、プロセスは、結果として生じるユニットを起動する前に終了します。
  • dbus – このタイプは simple と同様ですが、結果として生じるユニットは、メインプロセスが D-Bus 名を取得した後にのみ起動します。
  • notify – このタイプは simple と同様ですが、結果として生じるユニットは、通知メッセージが sd_notify() 関数で送信された後にのみ起動します。
  • idlesimple と同様ですが、サービスバイナリーの実際の実行はすべてのジョブが終了するまで遅延します。これにより、ステータスの出力とサービスのシェル出力とを混同することを防ぐことができます。
ExecStartユニットの起動時に実行されるコマンドまたはスクリプトを指定します。ExecStartPre および ExecStartPost は、ExecStart の前後に実行されるカスタムコマンドを指定します。Type=oneshot は、順次に実行される複数のカスタムコマンドの指定を可能にします。
ExecStopユニットの停止時に実行されるコマンドまたはスクリプトを指定します。
ExecReloadユニットの再読み込み時に実行されるコマンドまたはスクリプトを指定します。
Restartこのオプションが有効にされた状態で、サービスは systemctl コマンドによる完全な停止の例外と共に、そのプロセスの終了後に再起動します。
RemainAfterExitTrue に設定される場合、サービスは、そのプロセスがすべて終了していてもアクティブと見なされます。デフォルトの値は False です。このオプションは、Type=oneshot が設定されている場合にとくに役に立ちます。
[a] [Service] セクションで設定可能なオプションの詳細な一覧は、systemd.service(5) man ページを参照してください。

表9.11 [Install] セクションの重要なオプション

オプション[a]詳細
Aliasユニットの追加の名前のスペース区切りの一覧を提供します。systemctl enable を除くほとんどの systemctl コマンドは、実際のユニット名の代わりにエイリアスを使うことができます。
RequiredBy該当ユニットに依存するユニットの一覧です。このユニットが有効な場合、RequiredBy に一覧表示されるユニットは、このユニットについての Require 依存関係を取得します。
WantedBy該当ユニットに弱く依存するユニットの一覧です。このユニットが有効な場合、WantedBy に一覧表示されるユニットは、このユニットについての Want 依存関係を取得します。
Also該当ユニットと共にインストールまたはアンインストールされるユニットの一覧を指定します。
DefaultInstanceインスタンス化されたユニットに制限された状態で、このオプションは、ユニットが有効にされているデフォルトインスタンスを指定します。「インスタンス化されたユニットの使用」 を参照してください。
[a] [Install] セクションで設定可能なオプションの詳細な一覧は、systemd.unit(5) man ページを参照してください。
ユニット設定を微調整するために使用できるさまざまなオプションがあります。例9.17「postfix.service ユニットファイル」 は、システムにインストールされたサービスユニットの例を示しています。さらに、ユニットファイルのオプションは、「インスタンス化されたユニットの使用」 に説明されているようにユニットの動的な作成が可能な方法で定義できます。

例9.17 postfix.service ユニットファイル

次に、postfix パッケージで現在提供されている /usr/lib/systemd/system/postifix.service ユニットファイルの内容が続きます。
[Unit]
Description=Postfix Mail Transport Agent
After=syslog.target network.target
Conflicts=sendmail.service exim.service

[Service]
Type=forking
PIDFile=/var/spool/postfix/pid/master.pid
EnvironmentFile=-/etc/sysconfig/network
ExecStartPre=-/usr/libexec/postfix/aliasesdb
ExecStartPre=-/usr/libexec/postfix/chroot-update
ExecStart=/usr/sbin/postfix start
ExecReload=/usr/sbin/postfix reload
ExecStop=/usr/sbin/postfix stop

[Install]
WantedBy=multi-user.target
[Unit] セクションでは、サービスについて説明し、競合するユニットと共に並び順依存関係を指定します。[Service] では、カスタムスクリプトのシーケンスが、ユニットのアクティべーション時、停止時、および再読み込み時に実行されるように指定されます。EnvironmentFile は、サービスの環境変数が定義されるロケーションを参照し、PIDFile はサービスのメインプロセスの安定した PID を指定します。最後に、[Install] セクションはサービスに依存するユニットを一覧表示します。

9.6.2. カスタムユニットファイルの作成

ユニットファイルをゼロから作成するための複数のユースケースがあります。カスタムデーモンを実行したり、(例9.19「sshd サービスの 2 つ目のインスタンスの作成」 に説明されているように) 一部の既存サービスの 2 つ目のインスタンスを作成したり、または SysV init スクリプトをインポート (詳細は 「SysV Init スクリプトのユニットファイルへの変換」 を参照) したりできます。一方、既存ユニットの動作の変更または拡張のみを実行しようとする場合は、「既存のユニットファイルの変更」 の指示に従ってください。以下の手順では、カスタムサービスを作成する一般的なプロセスについて説明しています。
  1. カスタムサービスで実行可能ファイルを用意します。これは、カスタムで作成されたスクリプトである場合も、ソフトウェアプロバイダーが提供する実行可能ファイルである場合もあります。必要な場合は、カスタムサービスのメインプロセスの一定の PID を保持するために PID ファイルを用意します。また、サービスのシェル変数を保存するために環境ファイルを組み込むこともできます。ソーススクリプトが (chmod a+x を実行して) 実行可能であり、インタラクティブではないことを確認します。
  2. /etc/systemd/system/ ディレクトリーにユニットファイルを作成し、これに正しいファイルパーミッションが含まれることを確認します。root で実行します。
    touch /etc/systemd/system/name.service
    chmod 664 /etc/systemd/system/name.service
    name を、作成されるサービスの名前に置き換えます。ファイルは実行可能でなくてもよいことに注意してください。
  3. 直前のステップで作成された name.service ファイルを開き、サービス設定オプションを追加します。作成するサービスのタイプに応じて使用できる様々なオプションがあります。「ユニットファイル構造の概要」 を参照してください。以下は、ネットワーク関連サービスのユニット設定の例になります。
    [Unit]
    Description=service_description
    After=network.target
    
    [Service]
    ExecStart=path_to_executable
    Type=forking
    PIDFile=path_to_pidfile
    
    [Install]
    WantedBy=default.target
    ここで、
    • service_description は、ジャーナルログファイルおよび systemctl status コマンドの出力に表示される役立つ説明です。
    • After 設定により、サービスがネットワークの実行後にのみ起動します。関連するサービスまたはターゲットのスペース区切りの一覧を追加します。
    • path_to_executable は、実際のサービス実行可能ファイルへのパスを表します。
    • Type=forking は、fork システム呼び出しを行うデーモンに使用されます。サービスのメインプロセスは path_to_pidfile で指定される PID で作成されます。表9.10「[Service] セクションの重要なオプション」 で他の起動タイプを検索します。
    • WantedBy は、サービスを起動する必要のあるターゲットを提示します。これらのターゲットは、ランレベルの古い概念の置き換えと見なすことができます。詳細は、「systemd ターゲットでの作業」 を参照してください。
  4. root で以下のコマンドを実行して、systemd に対して、新規の name.service ファイルが存在することを通知します。
    systemctl daemon-reload
    systemctl start name.service

    警告

    新規のユニットファイルの作成または既存のユニットファイルの変更後に常に systemctl daemon-reload コマンドを実行します。実行しないと、systemctl start または systemctl enable コマンドは、systemd とディスク上の実際のサービスユニットファイルの状態の間にある不一致により失敗する可能性があります。
    name.service ユニットは、「システムサービスの管理」 に説明されているコマンドでその他のシステムサービスとして管理することができます。

例9.18 emacs.service ファイルの作成

Emacs テキストエディターを使用する場合、ファイルの編集時にプログラムの新規インスタンスを起動するのではなく、これをバックグラウンドで実行する方がスピードが早く、便利です。以下のステップでは、Emacs のユニットファイルを作成する方法を説明し、これをサービスのように処理できるようにします。
  1. /etc/systemd/system/ ディレクトリーにユニットファイルを作成し、これに正しいファイルパーミッションが含まれることを確認します。root で実行します。
    ~]# touch /etc/systemd/system/emacs.service
    ~]# chmod 664 /etc/systemd/system/emacs.service
  2. 以下の内容をファイルに追加します。
    [Unit]
    Description=Emacs: the extensible, self-documenting text editor
               
    [Service]
    Type=forking
    ExecStart=/usr/bin/emacs --daemon
    ExecStop=/usr/bin/emacsclient --eval "(kill-emacs)"
    Environment=SSH_AUTH_SOCK=%t/keyring/ssh
    Restart=always
               
    [Install]
    WantedBy=default.target
    上記の設定では、/usr/bin/emacs 実行可能ファイルはサービスの起動時にデーモンモードで開始されます。SSH_AUTH_SOCK 環境変数は、ランタイムディレクトリーを表す "%t" ユニット指定子を使用して設定されます。さらにサービスは、予期せずに終了する場合に emacs プロセスを再起動します。
  3. 設定を再読み込みし、カスタムサービスを起動するために以下のコマンドを実行します。
    ~]# systemctl daemon-reload
    ~]# systemctl start emacs.service
これでエディターは systemd サービスとして登録されるので、すべての標準的な systemctl コマンドが使用できます。例えば、systemctl status emacs でエディターのステータスを表示したり、systemctl enable emacs でシステム起動時にエディターを自動的に起動することができます。

例9.19 sshd サービスの 2 つ目のインスタンスの作成

システム管理者は、サービスの複数のインスタンスを設定し、実行しなければならないことが多くあります。これは、元のサービス設定ファイルのコピーを作成し、サービスの主なインスタンスとの競合を避けるために特定のパラメーターを変更することによって実行されます。以下の手順は、sshd サービスの 2 つ目のインスタンスを作成する方法を示しています。
  1. 2 つ目のデーモンで使用される sshd_config ファイルのコピーを作成します。
    ~]# cp /etc/ssh/sshd{,-second}_config
  2. 直前のステップで作成された sshd-second_config ファイルを編集し、異なるポート番号と PID ファイルを 2 つ目のデーモンに割り当てます。
    Port 22220
    PidFile /var/run/sshd-second.pid
    Port および PidFile オプションの詳細は、sshd_config(5) man ページを参照してください。選択するポートがその他のサービスで使用されていないことを確認します。PID ファイルはサービスの実行前になければならない訳ではありません。これはサービスの起動時に自動的に生成されます。
  3. sshd サービスの systemd ユニットファイルのコピーを作成します。
    ~]# cp /usr/lib/systemd/system/sshd.service /etc/systemd/system/sshd-second.service
  4. 直前のステップで作成された sshd-second.service を変更します。
    1. Description オプションを変更します。
      Description=OpenSSH server second instance daemon
    2. After オプションで指定されたサービスに sshd.service を追加し、2 つ目のインスタンスが、最初のインスタンスが起動した後にのみ起動するようにします。
      After=syslog.target network.target auditd.service sshd.service
    3. sshd の最初のインスタンスには鍵の生成が含まれるため、ExecStartPre=/usr/sbin/sshd-keygen 行を削除します。
    4. -f /etc/ssh/sshd-second_config パラメーターを sshd コマンドに追加し、代替の設定ファイルが使用されるようにします。
      ExecStart=/usr/sbin/sshd -D -f /etc/ssh/sshd-second_config $OPTIONS
    5. 上記の変更後、sshd-second.service は以下のようになります。
      [Unit]
      Description=OpenSSH server second instance daemon
      After=syslog.target network.target auditd.service sshd.service
      
      [Service]
      EnvironmentFile=/etc/sysconfig/sshd
      ExecStart=/usr/sbin/sshd -D -f /etc/ssh/sshd-second_config $OPTIONS
      ExecReload=/bin/kill -HUP $MAINPID
      KillMode=process
      Restart=on-failure
      RestartSec=42s
      
      [Install]
      WantedBy=multi-user.target
  5. SELinux を使用している場合、sshd の 2 番目のインスタンスのポートを SSH ポートに追加します。そうしないと、sshd の 2 番目のインスタンスはポートへのバインドの際に拒否されます。
    ~]# semanage port -a -t ssh_port_t -p tcp 22220
  6. sshd-second.service を有効にし、これがブート時に自動的に起動するようにします。
    ~]# systemctl enable sshd-second.service
    systemctl status コマンドを使用して sshd-second.service が実行中であるかどうかを確認します。さらに、ポートがサービスに接続することによって適切に有効にされているかどうかを確認します。
    ~]$ ssh -p 22220 user@server
    ファイアウォールを使用中の場合、sshd の 2 番目のインスタンスへの接続を許可するためにそれが適切に設定されていることを確認してください。

9.6.3. SysV Init スクリプトのユニットファイルへの変換

SysV init スクリプトをユニットファイルに変換する前に、どこか他の場所で変換がすでに行われていないことを確認します。Red Hat Enterprise Linux 7 にインストールされるすべてのコアサービスにデフォルトのユニットファイルが同梱されていますが、数多くのサードパーティーソフトウェアパッケージにも同様のことことが言えます。
init スクリプトをユニットファイルに変換するには、スクリプトを分析し、そこから必要な情報を抽出することが必要になります。このデータに基づいて、「カスタムユニットファイルの作成」 に説明されるようにユニットファイルを作成することができます。init スクリプトはサービスのタイプによって大幅に異なるため、この章で概略されているよりも多くの設定オプションを変換に使用する必要があるかもしれません。init スクリプトで利用できるカスタマイズの一部のレベルは systemd ユニットでサポートされなくなっていることに注意してください。「互換性の変更点」 を参照してください。
変換に必要とされるほとんどの情報はスクリプトのヘッダーに提供されます。以下の例は、Red Hat Enterprise Linux 6 で postfix サービスを起動するために使用される init スクリプトの開始セクションを示しています。
#!/bin/bash
#
# postfix      Postfix Mail Transfer Agent
#
# chkconfig: 2345 80 30
# description: Postfix is a Mail Transport Agent, which is the program \
#              that moves mail from one machine to another.
# processname: master
# pidfile: /var/spool/postfix/pid/master.pid
# config: /etc/postfix/main.cf
# config: /etc/postfix/master.cf

### BEGIN INIT INFO
# Provides: postfix MTA
# Required-Start: $local_fs $network $remote_fs
# Required-Stop: $local_fs $network $remote_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: start and stop postfix
# Description: Postfix is a Mail Transport Agent, which is the program that 
#              moves mail from one machine to another.
### END INIT INFO
上記の例では、# chkconfig および # description で始まる行のみが必須であるため、残りの部分は別の init ファイルにはない場合があります。### BEGIN INIT INFO および ### END INIT INFO 行で囲まれたテキストは Linux Standard Base (LSB) ヘッダー と呼ばれています。LSB ヘッダーが指定されている場合、これには、サービスの説明、依存関係、およびデフォルトのランレベルを定義するディレクティブが含まれます。次に、新規のユニットファイルに必要なデータを収集しようとする分析タスクの概要が続きます。postfix init スクリプトはサンプルとして使用されます。例9.17「postfix.service ユニットファイル」 で作成される postfix ユニットについて参照してください。

サービス説明の検索

#description で始まる行でスクリプトについての説明情報をを見つけてください。この説明をサービス名と共に、ユニットファイルの [Unit] セクションの Description オプションで使用します。LSB ヘッダーでは、#Short-Description および #Description 行に同様のデータが含まれる場合があります。

サービス依存関係の検索

LSB ヘッダーには、サービス間の依存関係を形成する複数のディレクティブが含まれる場合があります。それらのほとんどは systemd ユニットオプションに変換できます。表9.12「LSB ヘッダーの依存関係オプション」 を参照してください。

表9.12 LSB ヘッダーの依存関係オプション

LSB オプション詳細同等のユニットファイル
Providesサービスの起動ファシリティー名を指定します。この名前は他の init スクリプトで参照できます ( "$" 接頭辞を使用)。ただしこれは、ユニットファイルが他のユニットをファイル名で参照できるようになったため、不要になりました。
Required-Start必要なサービスの起動ファシリティー名が含まれます。これは、並び順依存関係として変換され、起動ファシリティー名は対応するサービスまたはそれらが属するターゲットに置き換えられます。たとえば、postfix の場合、$network の Required-Start 依存関係は network.target で After 依存関係に変換されました。AfterBefore
Should-StartRequired-Start よりも弱い依存関係を構成します。失敗した Should-Start 依存関係はサービス起動に影響を与えません。AfterBefore
Required-StopShould-Stop負の依存関係を構成します。Conflicts

サービスのデフォルトターゲットの検索

#chkconfig で始まる行には、3 つの数値が含まれます。最も重要な値は、サービスが起動するデフォルトのランレベルを表す最初の番号です。これらのランレベルを同等の systemd ターゲットにマップするには、表9.6「SysV ランレベルと systemd ターゲットの比較」 を使用します。次に、それらのターゲットをユニットファイルの [Install] セクションの WantedBy オプションに一覧表示します。たとえば、postfix はこれまではランレベル 2、3、4、および 5 で起動しました。これらは Red Hat Enterprise Linux 7 の multi-user.target および graphical.target に変換されます。graphical.target は multiuser.target に依存するため、例9.17「postfix.service ユニットファイル」 で説明されているようにこれら両方を指定する必要はないことに注意してください。また、デフォルトおよび禁止されているランレベルについては、LSB ヘッダーの #Default-Start および #Default-Stop 行に情報がある場合があります。
#chkconfig 行で指定される他の 2 つの値は、init スクリプトの起動およびシャットダウンの優先順位を表します。これらの値は、init スクリプトが読み込まれる場合は systemd によって解釈されますが、同等のユニットファイルはありません。

サービスで使用されるファイルの検索

Init スクリプトでは、専用ディレクトリーから機能ライブラリーを読み込み、設定、環境、および PID ファイルのインポートを許可します。環境変数は init スクリプトヘッダーの #config で始まる行で指定され、これは EnvironmentFile ユニットファイルオプションに変換されます。#pidfile init スクリプト行で指定される PID ファイルは PIDFile オプションでユニットファイルにインポートされます。
init スクリプトヘッダーに含まれない主要な情報は、サービス実行可能ファイルへのパス、またはサービスで必要になる可能性のあるその他のファイルへのパスです。以前のバージョンの Red Hat Enterprise Linux では、init スクリプトは、カスタム定義のアクションと共に 起動停止、または 再起動 などのデフォルトアクションのサービスの動作を定義するために Bash ケースステートメントを使用しました。postfix init スクリプトからの以下の抜粋は、サービス起動時に実行されるコードのブロックを示しています。
conf_check() {
    [ -x /usr/sbin/postfix ] || exit 5
    [ -d /etc/postfix ] || exit 6
    [ -d /var/spool/postfix ] || exit 5
}

make_aliasesdb() {
	if [ "$(/usr/sbin/postconf -h alias_database)" == "hash:/etc/aliases" ]
	then
		# /etc/aliases.db might be used by other MTA, make sure nothing
		# has touched it since our last newaliases call
		[ /etc/aliases -nt /etc/aliases.db ] ||
			[ "$ALIASESDB_STAMP" -nt /etc/aliases.db ] ||
			[ "$ALIASESDB_STAMP" -ot /etc/aliases.db ] || return
		/usr/bin/newaliases
		touch -r /etc/aliases.db "$ALIASESDB_STAMP"
	else
		/usr/bin/newaliases
	fi
}

start() {
	[ "$EUID" != "0" ] && exit 4
	# Check that networking is up.
	[ ${NETWORKING} = "no" ] && exit 1
	conf_check
	# Start daemons.
	echo -n $"Starting postfix: "
	make_aliasesdb >/dev/null 2>&1
	[ -x $CHROOT_UPDATE ] && $CHROOT_UPDATE
	/usr/sbin/postfix start 2>/dev/null 1>&2 && success || failure $"$prog start"
	RETVAL=$?
	[ $RETVAL -eq 0 ] && touch $lockfile
        echo
	return $RETVAL
}
init スクリプトの拡張性により、start() 関数ブロックから呼び出される conf_check() および make_aliasesdb() の 2 つのカスタム関数を指定することができました。さらに詳しく見ると、上記のコードでは複数の外部ファイルおよびディレクトリーが記述されています。主なサービス実行可能ファイルの /usr/sbin/postfix/etc/postfix/ および /var/spool/postfix/ 設定ディレクトリー、および /usr/sbin/postconf/ ディレクトリーです。
Systemd は事前に定義されたアクションのみをサポートしますが、ExecStartExecStartPreExecStartPostExecStop、および ExecReload オプションでカスタム実行可能ファイルを実行することができます。Red Hat Enterprise Linux 7 の postfix の場合、/usr/sbin/postfix はサポートスクリプトと共に、サービスの起動時に実行されます。postfix ユニットファイルについては、例9.17「postfix.service ユニットファイル」 を参照してください。
複雑な init スクリプトを変換する際には、スクリプトのすべてのステートメントの目的を理解している必要があります。一部のステートメントはオペレーティングシステムのバージョンに固有のものであるため、それらを変更する必要はありません。一方、新規の環境では、サービス実行可能ファイルおよびサポートファイルやユニットファイルで調整が一部必要となる場合があります。

9.6.4. 既存のユニットファイルの変更

システムにインストールされるサービスは、/usr/lib/systemd/system/ ディレクトリーに保存されるデフォルトのユニットファイルと共に提供されます。システム管理者はこれらのファイルを直接変更できないため、カスタマイズは /etc/systemd/system/ ディレクトリーの設定ファイルに制限される必要があります。必要とされる変更の程度に応じて、以下の方法のいずれかを実施してください。
  • 補助設定ファイルのディレクトリーを /etc/systemd/system/unit.d/ に作成します。この方法は、ほとんどのユースケースで推奨されます。これにより、元のユニットファイルを参照しつつも、デフォルト設定を追加の機能で拡張できます。この場合、パッケージの更新で導入されるデフォルトユニットへの変更は自動的に適用されます。詳細は、「デフォルトのユニット設定の拡張」 を参照してください。
  • 元のユニットファイル /usr/lib/systemd/system/ のコピーを /etc/systemd/system/ に作成し、そこで変更を行います。コピーは元のファイルを上書きするため、パッケージの更新で導入される変更は適用されません。この方法は、パッケージの更新とは無関係に永続する重要なユニット変更を行う際に役に立ちます。詳細は、「デフォルトのユニット設定の上書き」 を参照してください。
ユニットのデフォルト設定に戻るには、/etc/systemd/system/ でカスタム作成した設定ファイルを削除します。システムを再起動せずにユニットファイルへの変更を適用するには、以下を実行します。
systemctl daemon-reload
daemon-reload オプションは、すべてのユニットファイルを再読み込みし、ユニットファイルに変更をすぐに適用するために必要な依存関係ツリー全体を再作成します。または、以下のコマンドを使って同じ結果を得ることができます。
init q
変更されたユニットファイルが実行中のサービスに属する場合は、このサービスは、新たな設定を受け入れるために再起動する必要があります。
systemctl restart name.service

デフォルトのユニット設定の拡張

追加の設定オプションでデフォルトのユニットファイルを拡張するには、まず /etc/systemd/system/ に設定ディレクトリーを作成します。サービスユニットを拡張する場合は、root で以下のコマンドを実行します。
mkdir /etc/systemd/system/name.service.d/
name を、拡張する必要のあるサービスに置き換えます。上記の構文はすべてのユニットタイプに適用されます。
直前のステップで作成されたディレクトリーに設定ファイルを作成します。ファイル名は .conf という接尾辞で終了する必要があることに注意してください。以下を入力します。
touch /etc/systemd/system/name.service.d/config_name.conf
config_name を設定ファイルの名前に置き換えます。このファイルは、通常のユニットファイル構造に基づくため、すべてのディレクティブは該当するセクションで指定する必要があります。「ユニットファイル構造の概要」 を参照してください。
たとえば、カスタムの依存性を追加するには、以下の内容で設定ファイルを作成します。
[Unit]
Requires=new_dependency
After=new_dependency
ここで、new_dependency は依存性としてマークが付けられるユニットを表します。別の例は、30 秒の遅延後のメインプロセス終了後にサービスを再起動する設定ファイルです。
[Service]
Restart=always
RestartSec=30
1 つのタスクにのみフォーカスした小規模な設定ファイルを作成することを推奨します。これらのファイルは、他のサービスの設定ディレクトリーに簡単に移動したり、リンクしたりすることができます。
ユニットに変更内容を適用するには、root で以下を実行します。
systemctl daemon-reload
systemctl restart name.service

例9.20 httpd.service 設定の拡張

Apache サービスの起動時にカスタムシェルスクリプトが自動的に実行されるように httpd.service ユニットを変更するには、以下のステップを実行します。まず、ディレクトリーおよびカスタム設定ファイルを作成します。
~]# mkdir /etc/systemd/system/httpd.service.d/
~]# touch /etc/systemd/system/httpd.service.d/custom_script.conf
Apache で自動的に起動するスクリプトが /usr/local/bin/custom.sh にある場合、以下のテキストを custom_script.conf ファイルに挿入します。
[Service]
ExecStartPost=/usr/local/bin/custom.sh
ユニットの変更を適用するには、以下を実行します。
~]# systemctl daemon-reload
~]# systemctl restart httpd.service

注記

/etc/systemd/system/ の設定ディレクトリーの設定ファイルは、/usr/lib/systemd/system/ のファイルに優先されます。そのため、設定ファイルに Description または ExecStart などの 1 回のみ指定できるオプションが含まれる場合、このオプションのデフォルト値が上書きされます。「上書きされたユニットのモニタリング」 で説明されているように、systemd-delta コマンドの出力では、一部のオプションは実際に上書きされますが、該当のユニットは常に [EXTENDED] とマークされます。

デフォルトのユニット設定の上書き

ユニットファイルを提供するパッケージの更新後も永続する変更を加えるには、まずファイルを /etc/systemd/system/ ディレクトリーにコピーします。これを実行するには、root で以下のコマンドを実行します。
cp /usr/lib/systemd/system/name.service /etc/systemd/system/name.service
ここで、name は、変更する必要のあるサービスユニットの名前を表します。上記の構文はすべてのユニットタイプに劇用されます。
テキストエディターでコピーされたファイルを開き、必要な変更を行います。ユニットの変更を適用するには、root で以下を実行します。
systemctl daemon-reload
systemctl restart name.service

例9.21 タイムアウト制限の変更

タイムアウト値はサービスごとに指定して、サービスの故障によるシステムのフリーズを防ぐことができます。値を指定しないと、デフォルトで通常のサービスは 90 秒に、SysV 互換サービスでは 300 秒に設定されます。この制限を拡大するには、/etc/systemd/system/network.service.d/timeout.conf systemd ドロップインファイルを作成し、新たな設定の行を追加してデフォルトのタイムアウトを変更します。systemctl show network -p TimeoutStartUSec コマンドは現在のタイムアウト制限を表示します。以下の例にあるように制限を 10 秒に変更した後は、変更を有効にするために systemctl daemon-reload を使用して systemd をリロードします。
~]# systemctl show network -p TimeoutStartUSec
TimeoutStartUSec=5min
~]# mkdir /etc/systemd/system/network.service.d/
~]# echo -e '[Service]\nTimeoutStartSec=10' > /etc/systemd/system/network.service.d/timeout.conf
~]# systemctl daemon-reload
~]# systemctl show network -p TimeoutStartUSec
TimeoutStartUSec=10s

上書きされたユニットのモニタリング

上書きされたか、または変更されたユニットファイルの概要を表示するには、以下のコマンドを使用します。
systemd-delta
たとえば、上記のコマンドの出力は以下のようになります。
[EQUIVALENT] /etc/systemd/system/default.target → /usr/lib/systemd/system/default.target
[OVERRIDDEN] /etc/systemd/system/autofs.service → /usr/lib/systemd/system/autofs.service

--- /usr/lib/systemd/system/autofs.service      2014-10-16 21:30:39.000000000 -0400
+++ /etc/systemd/system/autofs.service  2014-11-21 10:00:58.513568275 -0500
@@ -8,7 +8,8 @@
 EnvironmentFile=-/etc/sysconfig/autofs
 ExecStart=/usr/sbin/automount $OPTIONS --pid-file /run/autofs.pid
 ExecReload=/usr/bin/kill -HUP $MAINPID
-TimeoutSec=180
+TimeoutSec=240
+Restart=Always
 
 [Install]
 WantedBy=multi-user.target

[MASKED]     /etc/systemd/system/cups.service → /usr/lib/systemd/system/cups.service
[EXTENDED]   /usr/lib/systemd/system/sssd.service → /etc/systemd/system/sssd.service.d/journal.conf

4 overridden configuration files found.
表9.13「systemd-delta の相違タイプ」 は、systemd-delta の出力で表示される上書きタイプを一覧表示します。ファイルが上書きされる場合、systemd-delta はデフォルトで、diff コマンドの出力に似た変更の要約を表示します。

表9.13 systemd-delta の相違タイプ

タイプ詳細
[MASKED]
マスクされたユニットファイルです。ユニットのマスクについての説明は、「サービスの無効化」 を参照してください。
[EQUIVALENT]
元のファイルを上書きするが、コンテンツに相違のない変更されていないコピーです。通常はシンボリックリンクです。
[REDIRECTED]
別のファイルにリダイレクトされるファイルです。
[OVERRIDEN]
上書きされ、変更されたファイルです。
[EXTENDED]
/etc/systemd/system/unit.d/ ディレクトリーの .conf ファイルで拡張されるファイルです。
[UNCHANGED]
--type=unchanged オプションが使用される場合にのみ表示される変更されないファイルです。
システムの更新後に、カスタム設定で上書きされているデフォルトユニットへの更新があるかどうかを確認するために systemd-delta を実行することができます。さらに、出力を特定の相違タイプに制限することもできます。たとえば、上書きされたユニットのみを表示するには、以下を実行します。
systemd-delta --type=overridden

9.6.5. インスタンス化されたユニットの使用

ランタイム時に単一の設定ファイルから複数のユニットをインスタンス化することができます。"@" 文字は、テンプレートにマークを付け、ユニットをこれに関連付けるために使用されます。インスタンス化されたユニットは、(Requires または Wants オプションを使用して) 別のユニットから開始することも、systemctl start コマンドで開始ることもできます。インスタンス化されたサービスユニットには以下の方法で名前が付けられます。
template_name@instance_name.service
ここで、template_name はテンプレート設定ファイルの名前を表します。instance_name を、ユニットインスタンスの名前に置き換えます。複数のインスタンスは、ユニットのすべてのインスタンスに共通の設定オプションを持つ同一のテンプレートを参照できます。テンプレートユニットの名前には以下の形式が使用されます。
unit_name@.service
以下は、ユニットファイルの Wants 設定です。
Wants=getty@ttyA.service,getty@ttyB.service
最初に、systemd に指定されたサービスユニット検索させます。該当するユニットが見つからない場合、「@」とタイプ接尾辞の間の部分は無視され、systemd は getty@.service ファイルを検索し、そこから設定を読み取り、サービスを起動します。
ワイルドカード文字 (ユニット指定子 とも呼ばれる) をすべてのユニット設定ファイルで使用できます。ユニット指定子は特定のユニットパラメーターを置き換え、これらはランタイム時に解釈されます。表9.14「重要なユニット指定子」 は、テンプレートユニットでとくに役立つユニット指定子を一覧表示します。

表9.14 重要なユニット指定子

ユニット指定子意味詳細
%n完全ユニット名タイプ接尾辞を含む完全ユニット名を表します。%N には同じ意味がありますが、禁止文字を ASCII コードに置き換えます。
%p接頭辞名タイプ接尾辞が削除されたユニット名を表します。インスタンス化されたユニットの %p は、ユニット名の「@」文字の前の部分を表します。
%iインスタンス名インスタンス化されたユニット名の「@」文字およびタイプ接尾辞間の部分です。%I には同じ意味がありますが、禁止文字を ASCII コードにも置き換えます。
%Hホスト名ユニット設定が読み込まれる時点の実行中システムのホスト名を表します。
%tランタイムディレクトリーランタイムディレクトリーを表します。これは root ユーザーの /run か、または特権のないユーザーの XDG_RUNTIME_DIR 変数の値のいずれかになります。
ユニット指定子の詳細な一覧については、systemd.unit(5) man ページを参照してください。
たとえば、getty@.service テンプレートには以下のディレクティブが含まれます。
[Unit]
Description=Getty on %I
...
[Service]
ExecStart=-/sbin/agetty --noclear %I $TERM
...
上記のテンプレートから getty@ttyA.service および getty@ttyB.service がインスタンス化される場合、 Description= は Getty on ttyA および Getty on ttyB として解決されます。