OpenWrt应用程序开机和崩溃时自动启动
首先介绍下OpenWrt的启动方式
SysV风格
procd风格
SysV风格的init启动
示例如下
#!/bin/sh /etc/rc.common
# Example script
# Copyright (C) 2007 OpenWrt.org
#启动顺序 自动执行/etc/rc.d/的S开头的软链接 enable 和 disable 可以自动帮你创建对应的带序号的软链.
START=80
STOP=85
APP=wim-roam
SERVICE_WRITE_PID=1
SERVICE_DAEMONIZE=1
start() {
echo "start $APP"
service_start /usr/sbin/$APP #&> /dev/null
}
stop() {
service_stop /usr/sbin/$APP #&> /dev/null
}
restart() {
stop
start
}
SysV风格的init script方法有
start # 启动服务 必须有
stop # 停止服务
restart # 重启服务
reload # 重新载入配置文件, 如果失败则重启
enable # 启用开机自启动, 实际上是在/etc/rc.d/下创建S和K开头的软链
disable # 禁用开机自启动, 实际上是删除/etc/rc.d/下对应的软链
boot # 当存在boot()方法时, 系统启动时会调用boot()而不是start()
自定义服务方法
使用EXTRA_COMMANDS和EXTRA_HELP设置自定义的服务方法
EXTRA_COMMANDS="custom"
EXTRA_HELP="custom Help for the custom command"
custom() {
echo "custom command"
# do your custom stuff
}
多个自定义方法的添加
EXTRA_COMMANDS="custom1 custom2 custom3"
EXTRA_HELP=<<EOF
custom1 Help for the custom1 command
custom2 Help for the custom2 command
custom3 Help for the custom3 command
EOF
custom1 () {
echo "custom1"
# do the stuff for custom1
}
custom2 () {
echo "custom2"
# do the stuff for custom2
}
custom3 () {
echo "custom3"
# do the stuff for custom3
}
快速查询所有服务的自启动状态, 可以使用以下命令
root@OpenWrt:/# for F in /etc/init.d/* ; do $F enabled && echo $F on || echo $F
**disabled**; done
/etc/init.d/alsa on
/etc/init.d/art **disabled**
/etc/init.d/boot on
/etc/init.d/boot-ftm on
/etc/init.d/breakpad on
/etc/init.d/cnss_diag on
/etc/init.d/cron on
/etc/init.d/ddns on
/etc/init.d/dhcrelay4 on
/etc/init.d/dhcrelay6 on
/etc/init.d/diag_socket_app on
/etc/init.d/dnsmasq on
procd风格的init启动
在openwrt系统内init进程被procd取代,procd作为父进程可以监控子进程的状态。一旦子进程退出后即可在某一个时刻尝试进行重启进程。
在op系统内使用procd监控的有uhttpd,netifd等。在/etc/init.d/文件夹内带有USE_PROCD=1标志,下面就介绍如何让procd启动某一个应用程序 我的应用程序名是my_app, 直接上脚本代码:
#!/bin/sh /etc/rc.common
#Copyright (C) 2008 OpenWrt.org
# 启动的顺序,越大越靠后
START=98
# 停止的顺序, 越小越靠前
STOP=15
#使用procd启动
USE_PROCD=1
APP="/usr/bin/my_app"
CFG_FILE=my_app
#start_service 函数必须要重新定义
start_service() {
procd_open_instance # 创建一个实例, 在procd看来一个应用程序可以多个实例
#ubus call service list # 可以查看实例
procd_set_param respawn # 定义respawn参数,告知procd当my_app程序退出后尝试进行重启
# binloader执行的命令是"/usr/bin/my_app", 若后面有参数可以直接在后面加上
procd_set_param command "$APP"
#关闭实例
procd_close_instance
}
#stop_service重新定义,退出服务器后需要做的操作
stop_service() {
rm -f /var/run/my_app.pid
}
service_triggers()
{
echo "service triggers "$PROG" !"
procd_add_reload_trigger "$CFG_FILE"
procd_add_validation _validate_cfg_section
}
reload_service() {
stop
start
}
常用的start_service()的语句
start_service() {
procd_open_instance [instance_name]
procd_set_param command /sbin/your_service_daemon -b -a --foo # service executable that has to run in **foreground**.
procd_append_param command -bar 42 # append command parameters
# respawn automatically if something died, be careful if you have an alternative process supervisor
# if process dies sooner than respawn_threshold, it is considered crashed and after 5 retries the service is stopped
procd_set_param respawn ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-5}
procd_set_param env SOME_VARIABLE=funtimes # pass environment variables to your process
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 will restart the daemon if these files have changed
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 # forward stdout of the command to logd
procd_set_param stderr 1 # same for stderr
procd_set_param user nobody # run service as user nobody
procd_set_param pidfile /var/run/somefile.pid # write a pid file on instance start and remove it on stop
procd_close_instance
}
必须指出来的是,被procd执行的程序不能是daemon后台程序,因为后台程序的主进程退出后在procd看来就是程序退出了,然后会进入respawn流程,之后重复启动和退出, 最后失败:
procd: Instance binloader::instance1 s in a crash loop 6 crashes, 0 seconds since last crash
procd风格的启动方式是比较先进的,OpenWrt新版本尽量使用该方式来维护系统应用程序。
procd风格示例:
#!/bin/sh /etc/rc.common
USE_PROCD=1 #表示使用procd来管理进程
START=80
STOP=85
PROG=/usr/sbin/wim-roam #PROG变量用来给程序的启动脚本赋值,用于启动应用程序
CFG_FILE=/etc/config/wim-roam
PID_FILE=/var/run/wim-roam.pid
#验证了配置文件hello中的delay变量否为整型值,并且在合理的范围内
_validate_cfg_section()
{
uci_validate_section wim-roam device wim\
'line:uinteger' \
'type:string'\
'pos:string'\
'num:uinteger'
}
#start_service函数:负责程序的启动
# PROCD_DEBUG=1 /etc/init.d/wim-roam start
start_service() {
echo "start service "$PROG" !"
_validate_cfg_section || {
echo "$PROG validattion failed!"
return 1
}
#在参数验证完成后,调用procd_open_instance 数发起实例增加,接着调用了procd_set_param函数来设置了启动命令和启动参数,再接着respawn设置其进程意外退出的重启机制及策略为默认值,最后调用procd_close_instance函数完成实例的增加。注意procd管理的进程需要运行在前台,即不能调用daemon或类似函数
procd_open_instance
procd_set_param command "$PROG"
#进程意外退出重启机制和策略
#参数1:异常失败的边界值threshold默认3600s 如果小于这个时间退出,则会累加重新启动次数,如果大于这个临界值,则将重启次数设置为0
#参数2:重启延迟时间timeout默认5s 将在多少秒后启动进程
#参数3:设置总的失败重启次数retry,是进程永久退出前的重新启动次数,超过这个次数进程将不再启动,默认5次,
#注意:这些参数可以不带,保持默认值
procd_set_param respawn ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-5}
procd_set_param file "$CFG_FILE"
procd_set_param pidfile $PID_FILE
procd_close_instance
}
#service_triggers函数:增加触发器,我们增加了对配置文件wim-roam的触发服务。文件发生改变后,如果调用了 reload_config 命令,将触发调用reload_service函数
service_triggers()
{
echo "service triggers "$PROG" !"
procd_add_reload_trigger "wim-roam" "wireless"
procd_add_validation _validate_cfg_section
}
#reload_service函数:在传递reload参数时进行调用,如果没有该函数,将会调用默认start函数
reload_service()
{
echo "reload service "$PROG" !"
stop
start
}