Raspberry Pi(Stretch)を再起動すると、デーモンが/run/user/1000
存在しないため起動しません。これは私のユニットファイルです:
[Unit]
Description=SBFspot Upload Daemon
[Service]
User=pi
Type=forking
TimeoutStopSec=60
ExecStart=/usr/local/bin/sbfspot.3/SBFspotUploadDaemon -p /run/user/1000/sbfspotupload.pid 2>&1> /dev/null
PIDFile=/run/user/1000/sbfspotupload.pid
Restart=no
RestartSec=15
SuccessExitStatus=SIGKILL
[Install]
WantedBy=default.target
数回やり直してから正しく動作するように設定しましたが、Restart=on-failure
実際には私が望むものではありません。デーモンは/run/user/1000
インストールを待ちたいです。試しましたが、After=run-user-1000.mount
まだ失敗しました。
これは可能ですか、それともこだわるべきですかRestart=on-failure
?
ベストアンサー1
/run/user/1000
もちろん、ユーザー#1000がログインするまで、または明示的にxyrユーザー固有のサービス管理を開始するまでは存在しません。これを使用するための完全なメカニズムが存在してはいけません。
このプログラムのバグ #215あなたが思うよりもはるかに深いです。このサービス単位ファイルは非常に間違っています。プログラム自体の動作も同様である。。システム化されたサービスユニットの基本を実際に理解していないことに基づいている貨物愛好家のプログラミングがたくさんあります。
- サービスユニットはシェルスクリプトではありません。 システムマニュアルする説明する。ここで設定すると、サービスプログラムはいくつかの追加パラメータで
ExecStart
実行されます。2>&1>
/dev/null
- サービス管理者は、1つのサービスのみが実行されていることを確認しました。 ここに追加されたコードはすべて不要なゴミです。
- 不安定で危険なPIDファイルメカニズムはいいえ使用。 適切なサービス管理には席がありません。
- サービスマネージャはデーモンコンテキストでサービスコールも処理します。 他の多くのコード
main()
は次のとおりです。返品悪魔化エラーに基づく不要なゴミ。- プログラムを
fork()
まったく実行してはならず、サービス準備メカニズムを指定してはいけませんType=forking
。現実世界の多くのプログラムと同様に、いいえまず、フォーク準備プロトコルについて話しましょう。 - 計画はすでにスーパーユーザーとして実行します。
User=root
不要で、サービスを実際に再設計する必要があります。いいえスーパーユーザー権限で実行する必要がありますが、代わりに権限のない専用サービスアカウントのスポンサーとして実行されます。 - サービスマネージャーすでに標準出力とエラーを記録し、このプログラムよりも優れた操作を行います。この独自のロギングシステムは、ファイルシステム全体が満たされるまでログファイルを増やし、スーパーユーザー用に予約されているすべての緊急スペースを消費します。
- あなたのログは標準エラーであり、C ++で
std::clog
。 - 実際、
fork()
標準エラーにリダイレクトされるすべてのコード使用しないでください。サービス管理ハンドルみんなセッションリーダーから作業ディレクトリへ、umaskから標準I / Oに移動し、正しく実行されます。このアプリはそうしませんし、それを試してはいけません。どのサービスマネージャで使用したときの様子です。Boostから得たすべてが間違っています。
- プログラムを
- 3つのサービスユニットは不要なメンテナンスコストです。 設定だけが
After
異なり、1つにマージできます。 - 失礼な解雇は成功ではありません。 あることを考えるとすでにシャットダウン時のファイルクリーンアップに関する問題
SuccessExitStatus=SIGKILL
はバグです。通常の終了は正常に渡され、例外的なものと見なされるべきですSIGTERM
。SIGKILL
(もちろん、output
すでに説明したように、完全なファイルメカニズムは正しく実装されていないローカルロギングメカニズムなので、サービス管理では使用しないでください。)これはsystemdのデフォルト設定です。 - データベースオブジェクトやその他のコンテンツに対してデストラクタを実行する必要があります。
main()
去らないでくださいexit()
。
正しく実装され、サービスマネージャ(daemontools、runit、s6、nosh、systemdなど)で実行されるデーモンははるかに短いです。
…//今までと同じ 無効 pvo_upload(無効) { std::clog << "デーモンプロセスの開始..." << std::endl; 公共サービスコード(); std::clog << "デーモンの停止..." << std::endl; } int main(int argc, char *argv[]) { 整数c; const char *config_file = ""; /* コマンドラインの解析*/ 同時に(1) { 静的構造オプション long_options[] = { {"設定ファイル",required_argument,0,'c'}, {0、0、0、0} }; intオプションインデックス= 0; c = getopt_long(argc, argv, "c:", long_options, &option_index); if(c == -1)割り込み; スイッチ(c) { ケース「c」: 構成ファイル= optarg; 残り; 基本: EXIT_FAILUREを返します。 残り; } } if (cfg.readSettings(argv[0], config_file) != 構成::CFG_OK) EXIT_FAILUREを返します。 std::clog << "SBFspotUploadDaemon バージョンの開始中" << バージョン << std::endl; // データベースにアクセスできることを確認する db_SQL_Base db = db_SQL_Base(); db.open(cfg.getSqlHostname(), cfg.getSqlUsername(), cfg.getSqlPassword(), cfg.getSqlDatabase()); if(!db.isopen()) { " std::clog << "データベースを開けませんでした。設定を確認してください。" << std::endl; EXIT_FAILUREを返します。 } // データベースバージョンの確認 intスキーマ_バージョン= 0; db.get_config(SQL_SCHEMAVERSION、スキーマ_バージョン); db.close(); if(schema_version < SQL_MINIMUM_SCHEMA_VERSION) { std::clog << "データベースをバージョンにアップグレードする" << SQL_MINIMUM_SCHEMA_VERSION << std::endl; EXIT_FAILUREを返します。 } //信号ハンドラをインストールします。 // サービスマネージャが送信したサービス停止信号に応答します。 signal(SIGTERM、ハンドラ); //デーモンループの開始 pvo_upload(); return終了_成功。 }
そして、サービスユニットも短いです。
[単位] 説明=SBFspotアップロードデーモン 以降=mysql.service mariadb.service network.target [提供する] タイプ=シンプル タイムアウト停止秒=10 ExecStart=/usr/local/bin/sbfspot.3/SBFspotUploadDaemon 再開=成功 [インストールする] WantedBy =マルチユーザー。ターゲット
systemctl status
ログ出力を使用して表示できますjournalctl
(-u
必要に応じてオプションとサービス名を含む)。
追加読書
- ジョナサンデボインポラード(2016)。今世紀にはまたはを使用しないでください
logrotate
。newsyslog
。よく与えられる答えです。 - ジョナサン・ドボイン・ポラード(2001)。 Unixデーモンを設計する際に避けるべき間違い。よく与えられる答えです。
- ジョナサンデボインポラード(2015)。Unixデーモンの準備プロトコルの問題。一般的な答え。
- https://unix.stackexchange.com/a/283739/5132
- https://unix.stackexchange.com/a/321716/5132