openwrt procd init script 自启动脚本服务

openwrt 是针对于嵌入式设备的精简版 Linux 系统。所以一些常规的 Linux 服务都没有,比如 systemd 等。

openwrt 是通过 init.d 来管理服务的。所有的服务都在 /etc/init.d 目录下。

对某个服务进行操作也很方便,例如对 network 服务:

# 启动 network
/etc/init.d/network start

# 重启 network
/etc/init.d/network restart

# 停止 network
/etc/init.d/network stop

如果需要某个服务开机自启动,可以 enable:

/etc/init.d/frp enable

会自动在 /etc/rc.d/ 目录下建立一个链接指向 /etc/init.d 下的对应服务,如:S95frp。

S95 表示此服务的启动顺序,下面会做介绍。

基本结构

script 配置文件基本结构如下:

#!/bin/sh /etc/rc.common

USE_PROCD=1
START=95
STOP=15

start_service() {
}
service_triggers() {
}
stop_service() {
}
restart_service() {
}

首先定义 shebang,定义脚本执行的相关依赖。
然后通过 USE_PROCD 定义我们的脚本是新的 procd 脚本而不是老版本的 init 脚本。

START=95 定义此服务开机启动时,其服务启动顺序序号。最大为 95,数字越大启动排序越靠后。
STOP=15 定义此服务在关机时,其服务关闭顺序序号。数字越小关闭排序越靠前。

Init script 有两个主要任务:

  • 定义此服务的配置
  • 定义何时重新配置此服务

每个服务都有各自特定需要执行的指令,他们都存储在 procd 内部。需要在 start_service() 中定义服务配置内容。

start_service

start_service() 的主要任务是:

  • 启动一个服务需要执行的命令
  • 监测某些信息的变化,如:文件或网络的变化
  • 配置 procd 需要的设定,如:auto respawning, logging stdout

以上的任务作为一个服务实例状态存储在 procd 中。当特定的系统配置发生变化,会自动根据 trigger 的设定调用 start_service()

需要通过设定参数来配置服务实例,常用的参数是直接在 start_service 中设定,如 command,一般我么通过 procd_set_param()procd_append_param() 来设定参数。

下面列举了支持的参数,有些需要设定有些可以省略:

start_service() {
         procd_open_instance [instance_name] # 给服务实例定义一个名称
         procd_set_param command /sbin/your_service_daemon -b -a --foo # 需要在前台被执行的服务
         procd_append_param command -bar 42 # 给以上命令附加的指令参数

         # 如果服务意外中止了,定义 redpawn 可以自动重启它,如果服务命令的确只需要运行一次,需要谨慎设定这里
         # 如果进程在 respawn_threshold 定义的时间内结束了,则判定为进程崩溃并尝试重启它,尝试5次后会停止重启
         procd_set_param respawn ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-5}

         procd_set_param env SOME_VARIABLE=funtimes  # 给进程传递环境变量
         procd_set_param limits core="unlimited"  # If you need to set ulimit for your process
         procd_set_param file /var/etc/your_service.conf # 如果此处定义的文件发生了变化,则会触发 /etc/init.d/your_service reload 重启进程
         procd_set_param netdev dev # likewise, except if dev's ifindex changes.
         procd_set_param data name=value ... # likewise, except if this data changes.
         procd_set_param stdout 1 # 转发 stdout 输出到 logd
         procd_set_param stderr 1 # same for stderr
         procd_set_param user nobody # 以 nobody 用户运行服务
         procd_set_param pidfile /var/run/somefile.pid # 在服务启动时写入一个 pid 文件,在停止服务时删除此 pid 文件
         procd_close_instance # 结束服务实例配置
}

当我们通过 start 参数执行这个脚本时,会自动运行 start_service()

/etc/init.d/test start

stop_service

如果需要在服务进程被终止时,执行特定的命令,可以在 stop_service() 中定义。如:

stop_service() {
        kill $(ps | grep v2ray | awk '/-confdir/ {print $1}')
}

通过 stop 参数可以直接调用此 method:

/etc/init.d/test stop

restart_service

当我们想要重启某个服务,可以通过单独执行 stop 和 start 来实现,也可以直接定义一个 restart_service() 来自动完成这个过程:

restart_service() {
        stop
        start
}

注意,在脚本内部调用某个 function 只需要写它的前面的名称即可。

重启服务只需要运行以下命令即可:

/etc/init.d/test restart

service_triggers

可以定义当某个 event 被触发时,自动运行某个 function。

service_triggers()
{
        procd_add_reload_trigger "<uci-file-name>" "<second-uci-file>"
        procd_add_reload_interface_trigger <interface>
}

以上配置会在 luci 的配置文件发生修改时,自动触发 reload function。