Bash
Systemd 服務未使用讀取 FIFO 執行
我編寫了一個腳本,它監聽來自 FIFO 的命令並收集幾個文件以在我的樹莓派上進行調試,一切正常,直到我決定為它編寫一個 systemd 單元文件。running
systemctl start crashcollector
將啟動腳本,但它永遠不會從它正在執行的管道中讀取任何內容但不做任何事情,systemctl stop crashcollector
需要永遠停止服務,所以我猜我的單元文件有問題,即使我寫了很多工作都很好。bash腳本:
#!/bin/bash collectCrashReport() { # teleport to cache dir cd /mnt/cache # remove any previous report rm -rf crash/crashreport_* # create report directory directory=crashreport_`cat /etc/hostname`_$(date +%Y%m%d%H%M%S) mkdir $directory cd $directory # snatch all the logs cp -r /var/log/ . ps --no-headers -ax > processes.dump # dump dmesg to file dmesg > dmesg.dump # create crash dir mkdir crash # zip the whole thing zip -qr /mnt/cache/crash/"$directory.zip" . # remove collection dir rm -rf /mnt/cache/crashreport_* # done and done } # check if already running and kill PIDFILE=/var/run/crashcollector.pid # check if already running if [ -f $PIDFILE ]; then kill `cat $PIDFILE` fi echo $$ > $PIDFILE cleanup() { rm -f $PIDFILE exit 0 } # trap them signals trap "cleanup" INT TERM EXIT that_awesome_pipe="CCPipe" [ -p $that_awesome_pipe ] || mkfifo $that_awesome_pipe chmod 766 $that_awesome_pipe while :; do if read line; then case $line in "collect") collectCrashReport ;; *) log "Received bad command $line, ignoring..." ;; esac fi done <"$that_awesome_pipe"
系統單元文件:
[Unit] Description=Crash report collector After=network.target [Service] Type=simple ExecStart=/usr/bin/crashCollector ExecStop=/usr/bin/kill -9 `cat /var/run/crashcollector.pid` KillMode=process Restart=always RestartSec=3 [Install] WantedBy=multi-user.target
不知道我到底錯過了什麼,刪除那個
if read line
塊會讓一切正常。
在我將 fifo 移至 後,一切都已修復
/tmp
;不知道會發生什麼變化,但現在一切順利。
有很多事情是錯的,那裡。
- 不要將它們視為 shell 命令行
ExecStart
。ExecStop
他們不是。systemd 手冊確實對此提出了警告。這些服務單元文件設置中不提供命令替換等 Shell 擴展。它不是shell 語言。- 當您開始的地方有一個實際的服務管理器時,不要創建 PID 文件機制。PID 文件是一種我們知道自 1980 年代以來就被破壞的機制,並且適當的服務管理完全消除了這種需要。你有一個服務經理。 它將確保在任何時候最多只有一個服務實例執行。 它將跟踪程序 ID。 當服務關閉時,它將處理向服務程序發送終止信號*。*
- 不要將基本的伺服器端東西放入伺服器程序中,例如創建 FIFO 並打開其伺服器端。
這樣做:
- 創建一個套接字單元以與服務單元並排。
- 將 FIFO 的所有顯式處理從
crashCollector
.ListenFIFO
在插座單元中使用。這當然需要一個絕對路徑名。StandardInput=socket
在服務單位使用。- 使您的
crashcollector
腳本僅從其標準輸入中讀取。- 將所有 PID 文件從
crashCollector
.- 去掉所有的信號陷阱和
cleanup()
東西crashCollector
。- 將所有的顯式殺戮
ExecStop
以及您嘗試使用 的結果KillMode
從服務單元中取出。StandardError=journal
在服務單位使用。- 替換
log
為簡單echo 1>&2
到標準的錯誤。進一步閱讀
- 喬納森·德博因·波拉德 (2001)。 設計 Unix 守護程序時要避免的錯誤。經常給出答案。
- Lennart Poettering 等人*。systemd.socket*。systemd 手冊頁。自由桌面.org。
- 如何從 FIFO 套接字進行服務讀取