Bash

Systemd 服務未使用讀取 FIFO 執行

  • September 27, 2017

我編寫了一個腳本,它監聽來自 FIFO 的命令並收集幾個文件以在我的樹莓派上進行調試,一切正常,直到我決定為它編寫一個 systemd 單元文件。runningsystemctl 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 命令行ExecStartExecStop他們不是。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到標準的錯誤。

進一步閱讀

引用自:https://unix.stackexchange.com/questions/394302