openwrt/package/system/procd/files/procd.sh
Jo-Philipp Wich 9cae5a8289 procd: fix invalid JSON filter expression in procd_running()
Since service and instance names may contain characters which are not allowed
in JSON path labels, such as dashes or spaces, change the filter expression
to array square bracket notation to properly match these cases as well.

Fixes: 2c3dd70741 ("procd: add procd_running() helper for checking running state")
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
(cherry picked from commit c933b6d224)
2019-09-19 07:21:47 +02:00

549 lines
11 KiB
Bash

# procd API:
#
# procd_open_service(name, [script]):
# Initialize a new procd command message containing a service with one or more instances
#
# procd_close_service()
# Send the command message for the service
#
# procd_open_instance([name]):
# Add an instance to the service described by the previous procd_open_service call
#
# procd_set_param(type, [value...])
# Available types:
# command: command line (array).
# respawn info: array with 3 values $fail_threshold $restart_timeout $max_fail
# env: environment variable (passed to the process)
# data: arbitrary name/value pairs for detecting config changes (table)
# file: configuration files (array)
# netdev: bound network device (detects ifindex changes)
# limits: resource limits (passed to the process)
# user: $username to run service as
# group: $groupname to run service as
# pidfile: file name to write pid into
# stdout: boolean whether to redirect commands stdout to syslog (default: 0)
# stderr: boolean whether to redirect commands stderr to syslog (default: 0)
# facility: syslog facility used when logging to syslog (default: daemon)
#
# No space separation is done for arrays/tables - use one function argument per command line argument
#
# procd_close_instance():
# Complete the instance being prepared
#
# procd_running(service, [instance]):
# Checks if service/instance is currently running
#
# procd_kill(service, [instance]):
# Kill a service instance (or all instances)
#
# procd_send_signal(service, [instance], [signal])
# Send a signal to a service instance (or all instances)
#
. "$IPKG_INSTROOT/usr/share/libubox/jshn.sh"
PROCD_RELOAD_DELAY=1000
_PROCD_SERVICE=
procd_lock() {
local basescript=$(readlink "$initscript")
local service_name="$(basename ${basescript:-$initscript})"
flock -n 1000 &> /dev/null
if [ "$?" != "0" ]; then
exec 1000>"$IPKG_INSTROOT/var/lock/procd_${service_name}.lock"
flock 1000
if [ "$?" != "0" ]; then
logger "warning: procd flock for $service_name failed"
fi
fi
}
_procd_call() {
local old_cb
json_set_namespace procd old_cb
"$@"
json_set_namespace $old_cb
}
_procd_wrapper() {
procd_lock
while [ -n "$1" ]; do
eval "$1() { _procd_call _$1 \"\$@\"; }"
shift
done
}
_procd_ubus_call() {
local cmd="$1"
[ -n "$PROCD_DEBUG" ] && json_dump >&2
ubus call service "$cmd" "$(json_dump)"
json_cleanup
}
_procd_open_service() {
local name="$1"
local script="$2"
_PROCD_SERVICE="$name"
_PROCD_INSTANCE_SEQ=0
json_init
json_add_string name "$name"
[ -n "$script" ] && json_add_string script "$script"
json_add_object instances
}
_procd_close_service() {
json_close_object
_procd_open_trigger
service_triggers
_procd_close_trigger
_procd_open_data
service_data
_procd_close_data
_procd_ubus_call ${1:-set}
}
_procd_add_array_data() {
while [ "$#" -gt 0 ]; do
json_add_string "" "$1"
shift
done
}
_procd_add_array() {
json_add_array "$1"
shift
_procd_add_array_data "$@"
json_close_array
}
_procd_add_table_data() {
while [ -n "$1" ]; do
local var="${1%%=*}"
local val="${1#*=}"
[ "$1" = "$val" ] && val=
json_add_string "$var" "$val"
shift
done
}
_procd_add_table() {
json_add_object "$1"
shift
_procd_add_table_data "$@"
json_close_object
}
_procd_open_instance() {
local name="$1"; shift
_PROCD_INSTANCE_SEQ="$(($_PROCD_INSTANCE_SEQ + 1))"
name="${name:-instance$_PROCD_INSTANCE_SEQ}"
json_add_object "$name"
[ -n "$TRACE_SYSCALLS" ] && json_add_boolean trace "1"
}
_procd_open_trigger() {
let '_procd_trigger_open = _procd_trigger_open + 1'
[ "$_procd_trigger_open" -gt 1 ] && return
json_add_array "triggers"
}
_procd_close_trigger() {
let '_procd_trigger_open = _procd_trigger_open - 1'
[ "$_procd_trigger_open" -lt 1 ] || return
json_close_array
}
_procd_open_data() {
let '_procd_data_open = _procd_data_open + 1'
[ "$_procd_data_open" -gt 1 ] && return
json_add_object "data"
}
_procd_close_data() {
let '_procd_data_open = _procd_data_open - 1'
[ "$_procd_data_open" -lt 1 ] || return
json_close_object
}
_procd_open_validate() {
json_select ..
json_add_array "validate"
}
_procd_close_validate() {
json_close_array
json_select triggers
}
_procd_add_jail() {
json_add_object "jail"
json_add_string name "$1"
shift
for a in $@; do
case $a in
log) json_add_boolean "log" "1";;
ubus) json_add_boolean "ubus" "1";;
procfs) json_add_boolean "procfs" "1";;
sysfs) json_add_boolean "sysfs" "1";;
ronly) json_add_boolean "ronly" "1";;
esac
done
json_add_object "mount"
json_close_object
json_close_object
}
_procd_add_jail_mount() {
local _json_no_warning=1
json_select "jail"
[ $? = 0 ] || return
json_select "mount"
[ $? = 0 ] || {
json_select ..
return
}
for a in $@; do
json_add_string "$a" "0"
done
json_select ..
json_select ..
}
_procd_add_jail_mount_rw() {
local _json_no_warning=1
json_select "jail"
[ $? = 0 ] || return
json_select "mount"
[ $? = 0 ] || {
json_select ..
return
}
for a in $@; do
json_add_string "$a" "1"
done
json_select ..
json_select ..
}
_procd_set_param() {
local type="$1"; shift
case "$type" in
env|data|limits)
_procd_add_table "$type" "$@"
;;
command|netdev|file|respawn|watch)
_procd_add_array "$type" "$@"
;;
error)
json_add_array "$type"
json_add_string "" "$@"
json_close_array
;;
nice|term_timeout)
json_add_int "$type" "$1"
;;
reload_signal)
json_add_int "$type" $(kill -l "$1")
;;
pidfile|user|group|seccomp|capabilities|facility)
json_add_string "$type" "$1"
;;
stdout|stderr|no_new_privs)
json_add_boolean "$type" "$1"
;;
esac
}
_procd_add_timeout() {
[ "$PROCD_RELOAD_DELAY" -gt 0 ] && json_add_int "" "$PROCD_RELOAD_DELAY"
return 0
}
_procd_add_interface_trigger() {
json_add_array
_procd_add_array_data "$1"
shift
json_add_array
_procd_add_array_data "if"
json_add_array
_procd_add_array_data "eq" "interface" "$1"
shift
json_close_array
json_add_array
_procd_add_array_data "run_script" "$@"
json_close_array
json_close_array
_procd_add_timeout
json_close_array
}
_procd_add_reload_interface_trigger() {
local script=$(readlink "$initscript")
local name=$(basename ${script:-$initscript})
_procd_open_trigger
_procd_add_interface_trigger "interface.*" $1 /etc/init.d/$name reload
_procd_close_trigger
}
_procd_add_config_trigger() {
json_add_array
_procd_add_array_data "$1"
shift
json_add_array
_procd_add_array_data "if"
json_add_array
_procd_add_array_data "eq" "package" "$1"
shift
json_close_array
json_add_array
_procd_add_array_data "run_script" "$@"
json_close_array
json_close_array
_procd_add_timeout
json_close_array
}
_procd_add_raw_trigger() {
json_add_array
_procd_add_array_data "$1"
shift
local timeout=$1
shift
json_add_array
json_add_array
_procd_add_array_data "run_script" "$@"
json_close_array
json_close_array
json_add_int "" "$timeout"
json_close_array
}
_procd_add_reload_trigger() {
local script=$(readlink "$initscript")
local name=$(basename ${script:-$initscript})
local file
_procd_open_trigger
for file in "$@"; do
_procd_add_config_trigger "config.change" "$file" /etc/init.d/$name reload
done
_procd_close_trigger
}
_procd_add_validation() {
_procd_open_validate
$@
_procd_close_validate
}
_procd_append_param() {
local type="$1"; shift
local _json_no_warning=1
json_select "$type"
[ $? = 0 ] || {
_procd_set_param "$type" "$@"
return
}
case "$type" in
env|data|limits)
_procd_add_table_data "$@"
;;
command|netdev|file|respawn|watch)
_procd_add_array_data "$@"
;;
error)
json_add_string "" "$@"
;;
esac
json_select ..
}
_procd_close_instance() {
local respawn_vals
_json_no_warning=1
if json_select respawn ; then
json_get_values respawn_vals
if [ -z "$respawn_vals" ]; then
local respawn_threshold=$(uci_get system.@service[0].respawn_threshold)
local respawn_timeout=$(uci_get system.@service[0].respawn_timeout)
local respawn_retry=$(uci_get system.@service[0].respawn_retry)
_procd_add_array_data ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-5}
fi
json_select ..
fi
json_close_object
}
_procd_add_instance() {
_procd_open_instance
_procd_set_param command "$@"
_procd_close_instance
}
procd_running() {
local service="$1"
local instance="${2:-instance1}"
local running
json_init
json_add_string name "$service"
running=$(_procd_ubus_call list | jsonfilter -e "@['$service'].instances['$instance'].running")
[ "$running" = "true" ]
}
_procd_kill() {
local service="$1"
local instance="$2"
json_init
[ -n "$service" ] && json_add_string name "$service"
[ -n "$instance" ] && json_add_string instance "$instance"
_procd_ubus_call delete
}
_procd_send_signal() {
local service="$1"
local instance="$2"
local signal="$3"
case "$signal" in
[A-Z]*) signal="$(kill -l "$signal" 2>/dev/null)" || return 1;;
esac
json_init
json_add_string name "$service"
[ -n "$instance" -a "$instance" != "*" ] && json_add_string instance "$instance"
[ -n "$signal" ] && json_add_int signal "$signal"
_procd_ubus_call signal
}
procd_open_data() {
local name="$1"
json_set_namespace procd __procd_old_cb
json_add_object data
}
procd_close_data() {
json_close_object
json_set_namespace $__procd_old_cb
}
_procd_set_config_changed() {
local package="$1"
json_init
json_add_string type config.change
json_add_object data
json_add_string package "$package"
json_close_object
ubus call service event "$(json_dump)"
}
procd_add_mdns_service() {
local service proto port
service=$1; shift
proto=$1; shift
port=$1; shift
json_add_object "${service}_$port"
json_add_string "service" "_$service._$proto.local"
json_add_int port "$port"
[ -n "$1" ] && {
json_add_array txt
for txt in "$@"; do json_add_string "" "$txt"; done
json_select ..
}
json_select ..
}
procd_add_mdns() {
procd_open_data
json_add_object "mdns"
procd_add_mdns_service "$@"
json_close_object
procd_close_data
}
uci_validate_section()
{
local _package="$1"
local _type="$2"
local _name="$3"
local _result
local _error
shift; shift; shift
_result=`/sbin/validate_data "$_package" "$_type" "$_name" "$@" 2> /dev/null`
_error=$?
eval "$_result"
[ "$_error" = "0" ] || `/sbin/validate_data "$_package" "$_type" "$_name" "$@" 1> /dev/null`
return $_error
}
uci_load_validate() {
local _package="$1"
local _type="$2"
local _name="$3"
local _function="$4"
local _option
local _result
shift; shift; shift; shift
for _option in "$@"; do
eval "local ${_option%%:*}"
done
uci_validate_section "$_package" "$_type" "$_name" "$@"
_result=$?
[ -n "$_function" ] || return $_result
eval "$_function \"\$_name\" \"\$_result\""
}
_procd_wrapper \
procd_open_service \
procd_close_service \
procd_add_instance \
procd_add_raw_trigger \
procd_add_config_trigger \
procd_add_interface_trigger \
procd_add_reload_trigger \
procd_add_reload_interface_trigger \
procd_open_trigger \
procd_close_trigger \
procd_open_instance \
procd_close_instance \
procd_open_validate \
procd_close_validate \
procd_add_jail \
procd_add_jail_mount \
procd_add_jail_mount_rw \
procd_set_param \
procd_append_param \
procd_add_validation \
procd_set_config_changed \
procd_kill \
procd_send_signal