procd: support term_timeout parameter
[oweals/openwrt.git] / package / system / procd / files / procd.sh
1 # procd API:
2 #
3 # procd_open_service(name, [script]):
4 #   Initialize a new procd command message containing a service with one or more instances
5 #
6 # procd_close_service()
7 #   Send the command message for the service
8 #
9 # procd_open_instance([name]):
10 #   Add an instance to the service described by the previous procd_open_service call
11 #
12 # procd_set_param(type, [value...])
13 #   Available types:
14 #     command: command line (array).
15 #     respawn info: array with 3 values $fail_threshold $restart_timeout $max_fail
16 #     env: environment variable (passed to the process)
17 #     data: arbitrary name/value pairs for detecting config changes (table)
18 #     file: configuration files (array)
19 #     netdev: bound network device (detects ifindex changes)
20 #     limits: resource limits (passed to the process)
21 #     user info: array with 1 values $username
22 #     pidfile: file name to write pid into
23 #
24 #   No space separation is done for arrays/tables - use one function argument per command line argument
25 #
26 # procd_close_instance():
27 #   Complete the instance being prepared
28 #
29 # procd_kill(service, [instance]):
30 #   Kill a service instance (or all instances)
31 #
32 # procd_send_signal(service, [instance], [signal])
33 #   Send a signal to a service instance (or all instances)
34 #
35
36 . $IPKG_INSTROOT/usr/share/libubox/jshn.sh
37
38 PROCD_RELOAD_DELAY=1000
39 _PROCD_SERVICE=
40
41 _procd_call() {
42         local old_cb
43
44         json_set_namespace procd old_cb
45         "$@"
46         json_set_namespace $old_cb
47 }
48
49 _procd_wrapper() {
50         while [ -n "$1" ]; do
51                 eval "$1() { _procd_call _$1 \"\$@\"; }"
52                 shift
53         done
54 }
55
56 _procd_ubus_call() {
57         local cmd="$1"
58
59         [ -n "$PROCD_DEBUG" ] && json_dump >&2
60         ubus call service "$cmd" "$(json_dump)"
61         json_cleanup
62 }
63
64 _procd_open_service() {
65         local name="$1"
66         local script="$2"
67
68         _PROCD_SERVICE="$name"
69         _PROCD_INSTANCE_SEQ=0
70
71         json_init
72         json_add_string name "$name"
73         [ -n "$script" ] && json_add_string script "$script"
74         json_add_object instances
75 }
76
77 _procd_close_service() {
78         json_close_object
79         _procd_open_trigger
80         service_triggers
81         _procd_close_trigger
82         _procd_ubus_call ${1:-set}
83 }
84
85 _procd_add_array_data() {
86         while [ "$#" -gt 0 ]; do
87                 json_add_string "" "$1"
88                 shift
89         done
90 }
91
92 _procd_add_array() {
93         json_add_array "$1"
94         shift
95         _procd_add_array_data "$@"
96         json_close_array
97 }
98
99 _procd_add_table_data() {
100         while [ -n "$1" ]; do
101                 local var="${1%%=*}"
102                 local val="${1#*=}"
103                 [ "$1" = "$val" ] && val=
104                 json_add_string "$var" "$val"
105                 shift
106         done
107 }
108
109 _procd_add_table() {
110         json_add_object "$1"
111         shift
112         _procd_add_table_data "$@"
113         json_close_object
114 }
115
116 _procd_open_instance() {
117         local name="$1"; shift
118
119         _PROCD_INSTANCE_SEQ="$(($_PROCD_INSTANCE_SEQ + 1))"
120         name="${name:-instance$_PROCD_INSTANCE_SEQ}"
121         json_add_object "$name"
122         [ -n "$TRACE_SYSCALLS" ] && json_add_boolean trace "1"
123 }
124
125 _procd_open_trigger() {
126         let '_procd_trigger_open = _procd_trigger_open + 1'
127         [ "$_procd_trigger_open" -gt 1 ] && return
128         json_add_array "triggers"
129 }
130
131 _procd_close_trigger() {
132         let '_procd_trigger_open = _procd_trigger_open - 1'
133         [ "$_procd_trigger_open" -lt 1 ] || return
134         json_close_array
135 }
136
137 _procd_open_validate() {
138         json_select ..
139         json_add_array "validate"
140 }
141
142 _procd_close_validate() {
143         json_close_array
144         json_select triggers
145 }
146
147 _procd_add_jail() {
148         json_add_object "jail"
149         json_add_string name "$1"
150
151         shift
152         
153         for a in $@; do
154                 case $a in
155                 log)    json_add_boolean "log" "1";;
156                 ubus)   json_add_boolean "ubus" "1";;
157                 procfs) json_add_boolean "procfs" "1";;
158                 sysfs)  json_add_boolean "sysfs" "1";;
159                 ronly)  json_add_boolean "ronly" "1";;
160                 esac
161         done
162         json_add_object "mount"
163         json_close_object
164         json_close_object
165 }
166
167 _procd_add_jail_mount() {
168         local _json_no_warning=1
169
170         json_select "jail"
171         [ $? = 0 ] || return
172         json_select "mount"
173         [ $? = 0 ] || {
174                 json_select ..
175                 return
176         }
177         for a in $@; do
178                 json_add_string "$a" "0"
179         done
180         json_select ..
181         json_select ..
182 }
183
184 _procd_add_jail_mount_rw() {
185         local _json_no_warning=1
186
187         json_select "jail"
188         [ $? = 0 ] || return
189         json_select "mount"
190         [ $? = 0 ] || {
191                 json_select ..
192                 return
193         }
194         for a in $@; do
195                 json_add_string "$a" "1"
196         done
197         json_select ..
198         json_select ..
199 }
200
201 _procd_set_param() {
202         local type="$1"; shift
203
204         case "$type" in
205                 env|data|limits)
206                         _procd_add_table "$type" "$@"
207                 ;;
208                 command|netdev|file|respawn|watch)
209                         _procd_add_array "$type" "$@"
210                 ;;
211                 error)
212                         json_add_array "$type"
213                         json_add_string "" "$@"
214                         json_close_array
215                 ;;
216                 nice|term_timeout)
217                         json_add_int "$type" "$1"
218                 ;;
219                 reload_signal)
220                         json_add_int "$type" $(kill -l "$1")
221                 ;;
222                 pidfile|user|seccomp|capabilities)
223                         json_add_string "$type" "$1"
224                 ;;
225                 stdout|stderr|no_new_privs)
226                         json_add_boolean "$type" "$1"
227                 ;;
228         esac
229 }
230
231 _procd_add_timeout() {
232         [ "$PROCD_RELOAD_DELAY" -gt 0 ] && json_add_int "" "$PROCD_RELOAD_DELAY"
233         return 0
234 }
235
236 _procd_add_interface_trigger() {
237         json_add_array
238         _procd_add_array_data "$1"
239         shift
240
241         json_add_array
242         _procd_add_array_data "if"
243
244         json_add_array
245         _procd_add_array_data "eq" "interface" "$1"
246         shift
247         json_close_array
248
249         json_add_array
250         _procd_add_array_data "run_script" "$@"
251         json_close_array
252
253         json_close_array
254         _procd_add_timeout
255         json_close_array
256 }
257
258 _procd_add_reload_interface_trigger() {
259         local script=$(readlink "$initscript")
260         local name=$(basename ${script:-$initscript})
261
262         _procd_open_trigger
263         _procd_add_interface_trigger "interface.*" $1 /etc/init.d/$name reload
264         _procd_close_trigger
265 }
266
267 _procd_add_config_trigger() {
268         json_add_array
269         _procd_add_array_data "$1"
270         shift
271
272         json_add_array
273         _procd_add_array_data "if"
274
275         json_add_array
276         _procd_add_array_data "eq" "package" "$1"
277         shift
278         json_close_array
279
280         json_add_array
281         _procd_add_array_data "run_script" "$@"
282         json_close_array
283
284         json_close_array
285         _procd_add_timeout
286         json_close_array
287 }
288
289 _procd_add_raw_trigger() {
290         json_add_array
291         _procd_add_array_data "$1"
292         shift
293         local timeout=$1
294         shift
295
296         json_add_array
297         json_add_array
298         _procd_add_array_data "run_script" "$@"
299         json_close_array
300         json_close_array
301
302         json_add_int "" "$timeout"
303
304         json_close_array
305 }
306
307 _procd_add_reload_trigger() {
308         local script=$(readlink "$initscript")
309         local name=$(basename ${script:-$initscript})
310         local file
311
312         _procd_open_trigger
313         for file in "$@"; do
314                 _procd_add_config_trigger "config.change" "$file" /etc/init.d/$name reload
315         done
316         _procd_close_trigger
317 }
318
319 _procd_add_validation() {
320         _procd_open_validate
321         $@
322         _procd_close_validate
323 }
324
325 _procd_append_param() {
326         local type="$1"; shift
327         local _json_no_warning=1
328
329         json_select "$type"
330         [ $? = 0 ] || {
331                 _procd_set_param "$type" "$@"
332                 return
333         }
334         case "$type" in
335                 env|data|limits)
336                         _procd_add_table_data "$@"
337                 ;;
338                 command|netdev|file|respawn|watch)
339                         _procd_add_array_data "$@"
340                 ;;
341                 error)
342                         json_add_string "" "$@"
343                 ;;
344         esac
345         json_select ..
346 }
347
348 _procd_close_instance() {
349         local respawn_vals
350         _json_no_warning=1
351         if json_select respawn ; then
352                 json_get_values respawn_vals
353                 if [ -z "$respawn_vals" ]; then
354                         local respawn_threshold=$(uci_get system.@service[0].respawn_threshold)
355                         local respawn_timeout=$(uci_get system.@service[0].respawn_timeout)
356                         local respawn_retry=$(uci_get system.@service[0].respawn_retry)
357                         _procd_add_array_data ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-5}
358                 fi
359                 json_select ..
360         fi
361
362         json_close_object
363 }
364
365 _procd_add_instance() {
366         _procd_open_instance
367         _procd_set_param command "$@"
368         _procd_close_instance
369 }
370
371 _procd_kill() {
372         local service="$1"
373         local instance="$2"
374
375         json_init
376         [ -n "$service" ] && json_add_string name "$service"
377         [ -n "$instance" ] && json_add_string instance "$instance"
378         _procd_ubus_call delete
379 }
380
381 _procd_send_signal() {
382         local service="$1"
383         local instance="$2"
384         local signal="$3"
385
386         json_init
387         json_add_string name "$service"
388         [ -n "$instance" -a "$instance" != "*" ] && json_add_string instance "$instance"
389         [ -n "$signal" ] && json_add_int signal "$signal"
390         _procd_ubus_call signal
391 }
392
393 procd_open_data() {
394         local name="$1"
395         json_set_namespace procd __procd_old_cb
396         json_add_object data
397 }
398
399 procd_close_data() {
400         json_close_object
401         json_set_namespace $__procd_old_cb
402 }
403
404 _procd_set_config_changed() {
405         local package="$1"
406
407         json_init
408         json_add_string type config.change
409         json_add_object data
410         json_add_string package "$package"
411         json_close_object
412
413         ubus call service event "$(json_dump)"
414 }
415
416 procd_add_mdns_service() {
417         local service proto port
418         service=$1; shift
419         proto=$1; shift
420         port=$1; shift
421         json_add_object "${service}_$port"
422         json_add_string "service" "_$service._$proto.local"
423         json_add_int port "$port"
424         [ -n "$1" ] && {
425                 json_add_array txt
426                 for txt in $@; do json_add_string "" $txt; done
427                 json_select ..
428         }
429         json_select ..
430 }
431
432 procd_add_mdns() {
433         procd_open_data
434         json_add_object "mdns"
435         procd_add_mdns_service $@
436         json_close_object
437         procd_close_data
438 }
439
440 uci_validate_section()
441 {
442         local _package="$1"
443         local _type="$2"
444         local _name="$3"
445         local _result
446         local _error
447         shift; shift; shift
448         _result=`/sbin/validate_data "$_package" "$_type" "$_name" "$@" 2> /dev/null`
449         _error=$?
450         eval "$_result"
451         [ "$_error" = "0" ] || `/sbin/validate_data "$_package" "$_type" "$_name" "$@" 1> /dev/null`
452         return $_error
453 }
454
455 _procd_wrapper \
456         procd_open_service \
457         procd_close_service \
458         procd_add_instance \
459         procd_add_raw_trigger \
460         procd_add_config_trigger \
461         procd_add_interface_trigger \
462         procd_add_reload_trigger \
463         procd_add_reload_interface_trigger \
464         procd_open_trigger \
465         procd_close_trigger \
466         procd_open_instance \
467         procd_close_instance \
468         procd_open_validate \
469         procd_close_validate \
470         procd_add_jail \
471         procd_add_jail_mount \
472         procd_add_jail_mount_rw \
473         procd_set_param \
474         procd_append_param \
475         procd_add_validation \
476         procd_set_config_changed \
477         procd_kill \
478         procd_send_signal