【问题标题】:bash script runs in terminal, but doesn't auto-start with systemdbash 脚本在终端中运行,但不使用 systemd 自动启动
【发布时间】:2020-05-18 05:04:03
【问题描述】:

我正在尝试在启动时将 bash 脚本作为 systemd 服务运行,我正在使用 Raspbian Buster Lite 的 Raspberry Pi 4 上执行此操作。

如果我手动运行 bash 脚本./hls.sh,我可以执行它,如果我这样做sudo service tv start,我也可以运行该服务,但 tv.service 似乎无法执行 bash脚本hls.sh 开始。我确实为服务和 bash 文件授予了 chmod 777 权限。

如果有任何帮助,我将不胜感激,一个月来我一直在尝试解决这个问题。

编辑: 使用 Carl 的建议,我修改了文件。但是,它仍然不起作用。我还注意到,当 Type=oneshot 时,您不能执行 Restart=always,如果我执行 restorecon -r,则会出现一个错误,提示找不到该命令。根据 Carl 的建议,我将所有内容都放入 /opt 我决定不使用临时文件建议,因为我需要每次都将 ffmpeg 输出转到同一个地方(我的理解是另一种方式会随机生成一个文件夹?)。

Edit2:问题与此 systemd 问题有关:https://unix.stackexchange.com/questions/209832/debian-systemd-network-online-target-not-working 解决方法只是在 [Service] 下执行 RestartSec=5s

Bash 脚本 [之前]

#!/bin/bash
/usr/bin/ffmpeg -reconnect 1 -reconnect_at_eof 1 -reconnect_streamed 1 -reconnect_delay_max 2 -y -nostdin \
-hide_banner -loglevel fatal \
-i http://10.0.0.11:9981/stream/channelnumber/2 \
-vcodec copy -acodec copy -scodec copy -g 60 \
-fflags +genpts -user_agent HLS_delayer \
-metadata service_provider="TimeShift" \
-metadata service_name="TV 1" \
-f hls -hls_flags delete_segments \
-hls_time 60 \
-hls_list_size 480 \
-hls_wrap 481 \
-hls_segment_filename /home/pi/hls/1_%03d.ts /home/pi/hls/1_hls.m3u8

Bash 脚本 [之后]

#!/usr/bin/env bash
set -e -o pipefail # return exit code of command with non-zero exit code and stop executing
echo "The solution works!"
/usr/bin/ffmpeg -reconnect 1 -reconnect_at_eof 1 -reconnect_streamed 1 -reconnect_delay_max 2 -y -nostdin \
-hide_banner -loglevel fatal \
-i http://10.0.0.11:9981/stream/channelnumber/2 \
-vcodec copy -acodec copy -scodec copy -g 60 \
-fflags +genpts -user_agent HLS_delayer \
-metadata service_provider="TimeShift" \
-metadata service_name="JSTV 1" \
-f hls -hls_flags delete_segments \
-hls_time 60 \
-hls_list_size 480 \
-hls_wrap 481 \
-hls_segment_filename /opt/hls/tmp/1_%03d.ts /opt/hls/tmp/1_hls.m3u8

服务[之前]

[Unit]
Description=Timeshift TV
After=tvheadend.service
PartOf=tvheadend.service
Restart=always

[Service]
ExecStartPre=/bin/mkdir -p /home/pi/hls/
ExecStart=/home/pi/hls.sh 103 &
ExecStop=/bin/rm -rf /home/pi/hls

[Install]
WantedBy=default.target

服务 [之后]

[Unit]
Description=Timeshift TV
After=tvheadend.service
PartOf=tvheadend.service

[Service]
WorkingDirectory=/opt/hls
User=nobody
Type=oneshot
ExecStartPre=+/bin/mkdir -p -m777 /opt/hls/tmp
ExecStart=/opt/hls/hls.sh
ExecStopPost=+/bin/rm -rf /opt/hls/tmp
#Restart=always

[Install]
WantedBy=multi-user.target

重启后,通过 systemctl status [Before] 查看状态

● tv.service - Timeshift TV
   Loaded: loaded (/etc/systemd/system/tv.service; enabled; vendor preset: enabled)
   Active: failed (Result: exit-code) since Mon 2020-05-18 05:36:40 BST; 45s ago
  Process: 465 ExecStartPre=/bin/mkdir -p -m777 /home/pi/hls/ (code=exited, status=0/SUCCESS)
  Process: 472 ExecStart=/home/pi/hls.sh 103 & (code=exited, status=1/FAILURE)
 Main PID: 472 (code=exited, status=1/FAILURE)

May 18 05:36:39 tv3 systemd[1]: Starting Timeshift TV...
May 18 05:36:39 tv3 systemd[1]: Started Timeshift TV.
May 18 05:36:40 tv3 systemd[1]: tv.service: Main process exited, code=exited, status=1/FAILURE
May 18 05:36:40 tv3 systemd[1]: tv.service: Failed with result 'exit-code'.

重启后,通过 systemctl status [After] 查看状态

● tv.service - Timeshift tv
   Loaded: loaded (/etc/systemd/system/tv.service; enabled; vendor preset: enabled)
   Active: failed (Result: exit-code) since Mon 2020-05-18 20:13:39 BST; 15min ago
  Process: 464 ExecStartPre=/bin/mkdir -p -m777 /opt/hls/tmp (code=exited, status=0/SUCCESS)
  Process: 471 ExecStart=/opt/hls/hls.sh (code=exited, status=1/FAILURE)
  Process: 484 ExecStopPost=/bin/rm -rf /opt/hls/tmp (code=exited, status=0/SUCCESS)
 Main PID: 471 (code=exited, status=1/FAILURE)

May 18 20:13:38 tv3 systemd[1]: Starting Timeshift tv...
May 18 20:13:38 tv3 hls.sh[471]: The solution works!
May 18 20:13:39 tv3 systemd[1]: tv.service: Main process exited, code=exited, status=1/FAILURE
May 18 20:13:39 tv3 systemd[1]: tv.service: Failed with result 'exit-code'.
May 18 20:13:39 tv3 systemd[1]: Failed to start Timeshift tv.

[之后]手动启动该过程仍然有效

● tv2.service - Timeshift tv
   Loaded: loaded (/etc/systemd/system/tv2.service; enabled; vendor preset: enabled)
   Active: activating (start) since Mon 2020-05-18 20:53:13 BST; 17s ago
  Process: 865 ExecStartPre=/bin/mkdir -p -m777 /opt/hls/tmp (code=exited, status=0/SUCCESS)
 Main PID: 866 (bash)
    Tasks: 2 (limit: 4915)
   Memory: 15.3M
   CGroup: /system.slice/tv2.service
           ├─866 bash /opt/hls/hls.sh
           └─867 /usr/bin/ffmpeg -reconnect 1 -reconnect_at_eof 1 -reconnect_streamed 1 -reconnect_delay_max 2 -y -nostdin -hide_banner -logle

May 18 20:53:13 tv3 systemd[1]: Starting Timeshift tv...
May 18 20:53:13 tv3 hls.sh[866]: The solution works!

pi@tv3:/etc/systemd/system $ cd /opt/hls
pi@tv3:/opt/hls $ ls
hls.sh  tmp
pi@tv3:/opt/hls $ cd tmp
pi@tv3:/opt/hls/tmp $ ls
1_000.ts

【问题讨论】:

  • 您确定服务文件中允许使用 & 吗?我认为不需要。尝试删除它。
  • & 绝对是多余的

标签: linux bash raspberry-pi raspbian systemd


【解决方案1】:

此服务文件存在一些问题。我将在下面解决它们,然后展示最终解决方案。

  1. 避免将系统级服务放在/home 目录下。虽然它可能有效,但不建议这样做。这样做可能会出现许多超出范围的问题。相反,请考虑将脚本放在/opt/srv/usr/local 下。
    • 当像这样大范围移动文件时,请确保在移动后在该目录上运行restorecon -r。这确保 SELinux 上下文被重置为正确的值,而不是将新上下文与旧上下文混合。当 SELinux 处于强制模式时,这有助于解决权限被拒绝错误。
  2. Restart= 指令位于[Service] 而非[Unit] 之下,因此应将其移到那里或完全删除。这可能是无害的,因为 systemd 有时是宽容的,但 systemd.service 是定义该指令的那个,而不是 systemd.unit
  3. ExecStart= 更改为末尾没有&。只需保留它并将其作为普通脚本运行即可。如果配置正确,Systemd 将处理其余部分。
  4. ExecStop= 改为ExecStopPost=。这将允许 systemd 执行其正常的停止行为,即在被要求停止服务时向脚本发送SIGTERM。停止完成后(优雅地或强制地),ExecStopPost= 将运行。由于您似乎在脚本运行后使用它进行清理,因此应该更改。如果保持原样,systemd 将删除该目录,然后等待脚本退出,脚本将永远不会这样做,因为它没有被告知,因此 systemd 将在请求内核SIGKILL 脚本之前等待超时时间。绝对不理想。
  5. [Service] 下的一些额外指令将解决有关此服务应如何运行的一些假设。
    • WorkingDirectory= — 以便定义而不是假设。如果保留为默认值,它将成为User 的主目录,如果也保留默认值,它将是/root。相反,将其设置为脚本的位置,如果为PrivateTmp=true,则设置为/tmp
    • User= — 这样脚本就不会以默认用户 root 运行。我建议nobody 或您希望管理ffmpeg 执行的任何用户。这可以防止在脚本运行时授予脚本 root 权限,ffmpeg 不应该需要这些权限。
    • Type= — 这可以说是最重要的,因为脚本是一次性运行的,然后在成功后退出;它不会后台守护进程并继续像默认的simple 服务那样运行。这应该设置为oneshot,以便 systemd 知道;它将脚本退出(状态码为 0)视为服务的成功运行,并将继续。
    • PrivateTmp= — 在大多数情况下,应该启用它。只有在极少数情况下才应禁用它。默认行为被禁用。启用后,脚本试图存储在/tmp 中的任何内容实际上都将透明地存储在名为/tmp/systemd-private-xxxx-servicename.service-xxxxx/ 的命名空间目录中,从而防止与/tmp 中的其他文件发生任何冲突。如果您使用它,我会将您的ffmpeg 更改为写入/tmp,而不是管理脚本旁边的目录,从而使您能够完全删除ExecStartPre=ExecStopPost=
  6. 确保ExecStartPre=ExecStopPost= 前面有+,这样它们就以root 运行,而不是定义的User=,它可能不是root;这可能是必要的,也可能不是必要的,具体取决于您对目录的期望。请参阅systemd.service Table 1. Special executable prefixes 了解更多信息。
  7. WantedBy=default.target更改为multi-user.target;更有可能的是,应该为该目标运行此服务,而不是默认启动任何目标。
  8. 您可能不应该编写脚本chmod 777,我会使用适度的chmod 755。它应该只需要读取和执行位 (+rx)。

所以不用多说,这是最终的解决方案:

目录结构: ls -lZ /opt/sfo-61862759

total 8
-rwxr-xr-x. 1 carl users unconfined_u:object_r:usr_t:s0  58 May 18 01:44 sfo-61862759.bash
-rw-r--r--. 1 carl users unconfined_u:object_r:usr_t:s0 316 May 18 01:53 sfo-61862759.service

Shell 脚本: /opt/sfo-61862759/sfo-61862759.bash

#!/usr/bin/env bash
set -e -o pipefail # return exit code of command with non-zero exit code and stop executing
echo "The solution works!"
# put ffmpeg stuff here and remove the echo if you choose

服务文件: /etc/systemd/system/sfo-61862759.service

[Unit]
Description=Stackoverflow Question #61862759 Solution

[Service]
WorkingDirectory=/tmp
User=nobody
Type=oneshot
#ExecStartPre=+/bin/mkdir -p /opt/sfo-61862759/temp
ExecStart=/opt/sfo-61862759/sfo-61862759.bash
#ExecStopPost=+/bin/rm -rf /opt/sfo-61862759/temp
PrivateTmp=true

[Install]
WantedBy=multi-user.target

系统状态输出:

01:53 carl@utility.localdomain:/opt/sfo-61862759$ sudo systemctl start sfo-61862759.service
01:54 carl@utility.localdomain:/opt/sfo-61862759$ sudo systemctl status sfo-61862759.service -n 4
● sfo-61862759.service - Stackoverflow Question #61862759 Solution
     Loaded: loaded (/opt/sfo-61862759/sfo-61862759.service; linked; vendor preset: disabled)
     Active: inactive (dead)

May 18 01:54:02 utility.localdomain systemd[1]: Starting Stackoverflow Question #61862759 Solution...
May 18 01:54:03 utility.localdomain sfo-61862759.bash[459746]: The solution works!
May 18 01:54:03 utility.localdomain systemd[1]: sfo-61862759.service: Succeeded.
May 18 01:54:03 utility.localdomain systemd[1]: Finished Stackoverflow Question #61862759 Solution.

【讨论】:

  • 试过了,但没用——我用之前和之后更新了原始帖子。
猜你喜欢
  • 2016-11-19
  • 2017-08-17
  • 2018-04-17
  • 1970-01-01
  • 2018-01-23
  • 1970-01-01
  • 2018-06-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多