aboutsummaryrefslogtreecommitdiffstats
path: root/package/system/procd/files/procd.sh
blob: b49b2b9d0194c7aae5f63a3f82b90918218d5060 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
pre { line-height: 125%; margin: 0; }
td.linenos pre { color: #000000; background-color: #f0f0f0; padding: 0 5px 0 5px; }
span.linenos { color: #000000; background-color: #f0f0f0; padding: 0 5px 0 5px; }
td.linenos pre.special { color: #000000; background-color: #ffffc0; padding: 0 5px 0 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding: 0 5px 0 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight { background: #ffffff; }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
# Makefile for OpenWrt
#
# Copyright (C) 2006 OpenWrt.org
# Copyright (C) 2006 by Felix Fietkau <openwrt@nbd.name>
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#

RELEASE:=Kamikaze
#VERSION:=2.0 # uncomment for final release

#--------------------------------------------------------------
# Just run 'make menuconfig', configure stuff, then run 'make'.
# You shouldn't need to mess with anything beyond this point...
#--------------------------------------------------------------

all: world

SHELL:=/usr/bin/env bash
export LC_ALL=C
export LANG=C
export TOPDIR=${CURDIR}
export IS_TTY=$(shell tty -s && echo 1 || echo 0)

include $(TOPDIR)/include/verbose.mk

OPENWRTVERSION:=$(RELEASE)
ifneq ($(VERSION),)
  OPENWRTVERSION:=$(VERSION) ($(OPENWRTVERSION))
else
  REV:=$(shell LANG=C svn info | awk '/^Revision:/ { print$$2 }' )
  ifneq ($(REV),)
    OPENWRTVERSION:=$(OPENWRTVERSION)/r$(REV)
  endif
endif
export OPENWRTVERSION

ifeq ($(FORCE),)
  .config scripts/config/conf scripts/config/mconf: tmp/.prereq-build
  world: tmp/.prereq-package tmp/.prereq-target
endif

package/%/Makefile: ;
target/%/Makefile: ;

tmp/.packageinfo: $(wildcard package/*/Makefile include/package*.mk include/kernel.mk) FORCE
tmp/.targetinfo: $(wildcard target/*/Makefile include/kernel*.mk)  FORCE
tmp/.%info:
	mkdir -p tmp/info
	$(NO_TRACE_MAKE) -s -f include/scan.mk SCAN_TARGET="$*info" SCAN_DIR="$(patsubst target,target/linux,$*)" SCAN_NAME="$*" SCAN_DEPS="$(filter-out FORCE, $^)" SCAN_EXTRA=""

tmpinfo-clean: FORCE
	-rm -rf tmp/.*info

tmp/.config-%.in: tmp/.%info scripts/metadata.pl
	./scripts/metadata.pl $*_config < $< > $@ || rm -f $@

.config: ./scripts/config/conf tmp/.config-target.in tmp/.config-package.in
	if [ \! -f .config ]; then \
		[ -e $(HOME)/.openwrt/defconfig ] && cp $(HOME)/.openwrt/defconfig .config; \
		$(NO_TRACE_MAKE) menuconfig; \
	fi
	$< -D .config Config.in &> /dev/null

scripts/config/mconf:
	$(MAKE) -C scripts/config all

scripts/config/conf:
	$(MAKE) -C scripts/config conf



config: scripts/config/conf tmp/.config-target.in tmp/.config-package.in FORCE
	$< Config.in

config-clean: FORCE
	$(NO_TRACE_MAKE) -C scripts/config clean

defconfig: scripts/config/conf tmp/.config-target.in tmp/.config-package.in FORCE
	touch .config
	$< -D .config Config.in

oldconfig: scripts/config/conf tmp/.config-target.in tmp/.config-package.in FORCE
	$< -o Config.in

menuconfig: scripts/config/mconf tmp/.config-target.in tmp/.config-package.in FORCE
	if [ \
# 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_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_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