diff --git a/utils/pservice/Makefile b/utils/pservice/Makefile new file mode 100644 index 0000000000..3d1923b90a --- /dev/null +++ b/utils/pservice/Makefile @@ -0,0 +1,28 @@ +# Copyright (C) 2017 Yousong Zhou + +include $(TOPDIR)/rules.mk + +PKG_NAME:=pservice +PKG_VERSION:=2017-08-29 +PKG_RELEASE=1 + +PKG_MAINTAINER:=Yousong Zhou + +include $(INCLUDE_DIR)/package.mk + +define Package/pservice + SECTION:=utils + CATEGORY:=Utilities + TITLE:=Wrap commands as procd services +endef + +define Build/Compile +endef + +define Package/pservice/install + $(INSTALL_DIR) $(1)/usr/bin $(1)/etc/init.d $(1)/etc/config + $(INSTALL_BIN) ./files/pservice.init $(1)/etc/init.d/pservice + $(INSTALL_DATA) ./files/pservice.config $(1)/etc/config/pservice +endef + +$(eval $(call BuildPackage,pservice)) diff --git a/utils/pservice/README.md b/utils/pservice/README.md new file mode 100644 index 0000000000..85fbd6c429 --- /dev/null +++ b/utils/pservice/README.md @@ -0,0 +1,44 @@ +# uci + +`disabled`, bool, default `0` + +`name`, string, name of the service instance + +`command`, file, the service instance executable + +`args`, list of args + +`stderr`, bool, default `0`, log stderr output of the service instance + +`stdout`, bool, default `0`, log stdout output of the service instance + +`env`, list of environment variable settings of the form `var=val` + +`file`, list of file names. Service instances will be restarted if content of +these files have changed on service reload event. + +`respawn_threshold`, uinteger, default `3600`, time in seconds the instances +have to be in running state to be considered a valid run + +`respawn_timeout`, uinteger, default `5`, time in seconds the instance should +be delayed to start again after the last crash + +`respawn_maxfail`, uinteger, default `5`, maximum times the instances can +crash/fail in a row and procd will not try to bring it up again after this +limit has been reached + +# notes and faq + +Initial environment variables presented to service instances may be different +from what was observed on the interactive terminal. E.g. `HOME=/` may affect +reading `~/.ssh/known_hosts` of dropbear ssh instance. + + PATH=/usr/sbin:/usr/bin:/sbin:/bin PWD=/ HOME=/ + +If `list args xxx` seems to be too long causing pain, consider using `/bin/sh` +as the `command`. It is also worth noting that uci supports multi-line option +value. + +Child processes will keep running when their parent process was killed. This +is especially the case and should be taken into account with option `command` +being `/bin/sh` and it is recommended to use `exec` as the last shell command. diff --git a/utils/pservice/files/pservice.config b/utils/pservice/files/pservice.config new file mode 100644 index 0000000000..0f505b3986 --- /dev/null +++ b/utils/pservice/files/pservice.config @@ -0,0 +1,24 @@ +config pservice + option disabled 1 + option name 'demo0' + option command /bin/sh + option respawn_maxfail 0 + list args -c + list args 'env | logger -t $name; exec sleep $time' + list env 'v0=0' + list env 'v1=val with space' + list env 'name=demo0' + list env 'time=1799' + list file /tmp/sleep.conf + +config pservice + option disabled 1 + option name 8021x + option command /usr/sbin/wpa_supplicant + option stdout 1 + list args -i + list args eth0.1 + list args -D + list args wired + list args -c + list args /etc/wpa_supplicant-eth0.1.conf diff --git a/utils/pservice/files/pservice.init b/utils/pservice/files/pservice.init new file mode 100755 index 0000000000..0a275f8f7f --- /dev/null +++ b/utils/pservice/files/pservice.init @@ -0,0 +1,85 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2017 Yousong Zhou + +START=99 + +USE_PROCD=1 + +pservice_list_cb() { + local val="$1"; shift + local param="$1"; shift + + procd_append_param "$param" "$val" +} + +pservice() { + local cfg="$1" + + eval "$(validate_pservice_section "$cfg" pservice_validate_mklocal)" + validate_pservice_section "$cfg" || return 1 + [ "$disabled" = 0 ] || return 0 + [ -x "$command" ] || return 1 + + procd_open_instance "$name" + procd_set_param command "$command" + procd_set_param stderr "$stderr" + procd_set_param stdout "$stdout" + procd_set_param respawn "$respawn_threshold" "$respawn_timeout" "$respawn_maxfail" + [ -z "$args" ] || config_list_foreach "$cfg" args pservice_list_cb command + if [ -n "$env" ]; then + procd_set_param env + config_list_foreach "$cfg" env pservice_list_cb env + fi + if [ -n "$file" ]; then + procd_set_param file + config_list_foreach "$cfg" file pservice_list_cb file + fi + procd_close_instance +} + +start_service() { + config_load 'pservice' + config_foreach pservice pservice +} + +stop_service() { + true +} + +service_triggers() { + procd_open_validate + validate_pservice_section + procd_close_validate +} + +pservice_validate_mklocal() { + local tuple opts + + shift 2 + for tuple in "$@"; do + opts="${tuple%%:*} $opts" + done + [ -z "$opts" ] || echo "local $opts" +} + +pservice_validate() { + uci_validate_section pservice "$@" +} + +validate_pservice_section() { + local cfg="$1"; shift + local func="$1"; shift + + "${func:-pservice_validate}" pservice "$cfg" \ + "disabled:bool:0" \ + "name:string" \ + "env:regex('^[a-zA-Z_][a-zA-Z0-9_]*=.*$')" \ + "command:file" \ + "args:list(string)" \ + "stderr:bool:0" \ + "stdout:bool:0" \ + "respawn_threshold:uinteger:3600" \ + "respawn_timeout:uinteger:5" \ + "respawn_maxfail:uinteger:5" \ + "file:string" +}