2020年8月9日日曜日

11: C++で最小限systemdデーモンを作成する方法

<このシリーズの前の記事 | このシリーズの目次 | このシリーズの次の記事>

最も宣伝されているドキュメントたちのほとんどは"fork"、"setsid"、などとし始めますが、それはSysVデーモンについての話であって、systemdデーモンは、もっと容易に作成できます。

話題


About: C++

この記事の目次


開始コンテキスト


  • 読者は、C++プログラミングの基本的知識を持っている。

ターゲットコンテキスト



  • 読者は、最小限systemdデーモンをC++で作成する方法を理解する。

オリエンテーション


Hypothesizer 7
私はデーモンを1つ作成したい。

インターネットで検索すると、最も高位にランクされている回答のほとんどは、"fork"、"setsid"、. . . とし始める。

実のところ、それらは、SysVデーモンの作成方法を説明しているのだ、そう断ることなく、まるで、SysVデーモンでないデーモンはデーモンではないと言わんばかりに。. . . systemdについては反対意見もあるようなので、それらの著者は、systemdデーモンの存在を断固として否定しているのかもしれない。

しかしながら、私のLinuxコンピューターの'man'コマンドページは、'systemdデーモン'を「new-style daemon(新スタイルデーモン)」と形容し、「Modern services for Linux should be implemented as new-style daemons.(Linuxの現代のサービスは新スタイルデーモンとして実装されるべきだ。)」だと言っている。

えーと、その'man'ページによれば、'SysVデーモン'は、太古の遺物であるように聞こえる。

私は、その論争には立ち入らず(そうする資格がないので)、その'man'ページの推奨を単純に受け入れた。

判明したことには、systemdデーモンを作成するのは極めて容易である、少なくとも、最小限systemdデーモンを作成することは。


本体


1: 最小限systemdデーモンを作成する方法


Hypothesizer 7
その'man'ページが言うには、「For developing a new-style daemon, none of the initialization steps recommended for SysV daemons need to be implemented.(新スタイルデーモンを開発するためには、SysVデーモンのために推奨されている初期化ステップ群は全部、実装する必要がない)」。

ふーん、それはいいね。実のところ、その'man'ページを読み進む限り、「new-style daemon(新スタイルデーモン)」は作成がとても容易であるようだ。

それでは、私の最小限デーモンは何を実装する必要があるのだろうか?

第1に、私のデーモンは、'SIGTERM'シグナルをキャッチして、自らをクリーンにシャットダウンしなければならないようだ。. . . ごもっとも。

第2に、私のデーモンは、正しい終了コードを与えなければならないようだ。. . . 了解。

第3に、私のデーモンは、systemdに、スタートアップ完了および任意のステータスアップデートについて、'sd_notify'ファンクションを通して通知すべきであるようだ。. . . 問題なし。

第4に、私のデーモンは、システムのsyslogサービスに、ただ標準エラー出力に出力するだけで、ログ出力できるようだ。. . . いいねえ。

上記が、C++プログラムにとっての全てのようだ、後で、'.service'ファイルを1つ用意しなければならないのであるが。

デーモンが最小限というレベルを越えるのであれば、実装すべきいくつか他のこともありうるが、それらは、今は私には関係ない(例えば、'SIGHUP'シグナルはどうでもよい、なぜなら、この最小限デーモンは設定ファイルなど持っていないから、また、'D-Bus'は全然どうでもよい)。

以下は、1つの最小限システムデーモンだ。

theBiasPlanet/samples/minimalSystemdDaemon/programs/MinimalSystemdDaemon.hpp

@C++ ソースコード
#ifndef __theBiasPlanet_samples_minimalSystemdDaemon_programs_MinimalSystemdDaemon_hpp__
	#define __theBiasPlanet_samples_minimalSystemdDaemon_programs_MinimalSystemdDaemon_hpp__
	
	namespace theBiasPlanet {
		namespace samples {
			namespace minimalSystemdDaemon {
				namespace programs {
					class MinimalSystemdDaemon {
						public:
							static int main (int const & a_argumentsNumber, char const * const a_argumentsArray []);
					};
				}
			}
		}
	}
#endif

theBiasPlanet/samples/minimalSystemdDaemon/programs/MinimalSystemdDaemon.cpp

@C++ ソースコード
#include "theBiasPlanet/samples/minimalSystemdDaemon/programs/MinimalSystemdDaemon.hpp"
#include <iostream>
#include <exception>
#include <signal.h>
#include <systemd/sd-daemon.h>

using namespace ::std;

namespace theBiasPlanet {
	namespace samples {
		namespace minimalSystemdDaemon {
			namespace programs {
				int MinimalSystemdDaemon::main (int const & a_argumentsNumber, char const * const a_argumentsArray []) {
					int l_resultStatus = 1;
					try {
						if (a_argumentsNumber != 3) {
							l_resultStatus = 2;
							throw runtime_error ("The arguments have to be these.\nThe argument 1: ~ 2: ~");
						}
						sigset_t l_waitedSignals;
						sigemptyset (&l_waitedSignals);
						sigaddset (&l_waitedSignals, SIGTERM);
						sigprocmask (SIG_BLOCK, &l_waitedSignals, nullptr);
						sd_notify (0, "READY=1");
						cerr << string ("The office daemon has successfully started up.") << endl << flush;
						int l_signal;
						sigwait (&l_waitedSignals, &l_signal);
						sd_notify (0, "STOPPING=1");
						cerr << string ("The office daemon has been successfully shut down.") << endl << flush;
					}
					catch (exception & l_exception) {
						sd_notifyf (0, "STATUS=Failed to start up: %s\n ERRNO=%i", l_exception.what (), l_resultStatus);
						return (l_resultStatus);
					}
					l_resultStatus = 0;
					return (l_resultStatus);
				}
			}
		}
	}
}

注意すべきは、'sd_notify'および'sd_notifyf'を使用するために、1つのパッケージ(Ubuntu 18.04の場合には、'libsystemd-dev')をオペレーティングシステムにインストールしておき、1つのライブラリ(Ubuntu 18.04の場合には、'systemd')をデーモンにリンクしなければならないことだ。




2: デーモンを登録する


Hypothesizer 7
そのデーモンを登録するために、'/etc/systemd/system'ディレクトリに'.service'ファイルを作成する。

以下が、私の'.service'ファイルだ。

'minimalSystemdDaemon.service'

@systemd .service ソースコード
[Unit]
Description=The minimal systemd daemon service
After=network.target
StartLimitBurst=5
StartLimitIntervalSec=10

[Service]
Type=simple
Restart=always
RestartSec=1
User=%the operating system user name%
Environment="%the variable 1 name%=%the variable 1 value%"
Environment="%the variable 2 name%=%the variable 2 value%"
ExecStart=%the execution file path% "%the argument 1%" "%the argument 2%"
KillMode=process

[Install]
WantedBy=multi-user.target

上に見られるとおり、デーモンプログラムを実行するオペレーティングシステムユーザをセットでき、環境変数群をセットでき、デーモンへの引数をセットできる。

そのデーモンを有効化する(デーモンを自動的に開始させる)には、以下のコマンドを実行する。

@bash ソースコード
sudo systemctl enable minimalSystemdDaemon.service

そのデーモンを無効化するには、以下のコマンドを実行する。

@bash ソースコード
sudo systemctl disable minimalSystemdDaemon.service


3: デーモンを管理する


Hypothesizer 7
そのデーモンを開始するには、以下のコマンドを実行する。

@bash ソースコード
sudo systemctl start minimalSystemdDaemon.service

そのデーモンを停止するには、以下のコマンドを実行する。

@bash ソースコード
sudo systemctl stop minimalSystemdDaemon.service

そのデーモンのステータスを見るには、以下のコマンドを実行する。

@bash ソースコード
sudo systemctl status minimalSystemdDaemon.service


4: 結びとその先


Hypothesizer 7
これで、最小限systemdデーモンを作成する方法を私は理解したようだ。

典型的なインターネット検索エンジンが最も推奨する一部のドキュメントを信じると必須であるように見える、'fork'、'setsid'、等を呼ぶような面倒なことを、私は書く必要がない。

実は、本記事は、オフィス(LibreOfficeまたはApache OpenOffice)デーモンを作成するための準備である。


参考資料


<このシリーズの前の記事 | このシリーズの目次 | このシリーズの次の記事>