a1b9f2f0e9e0b5a4abb69dee4a0f3a70c141fc84
[librecmc/librecmc.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)
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         json_close_array
255
256         _procd_add_timeout
257 }
258
259 _procd_add_reload_interface_trigger() {
260         local script=$(readlink "$initscript")
261         local name=$(basename ${script:-$initscript})
262
263         _procd_open_trigger
264         _procd_add_interface_trigger "interface.*" $1 /etc/init.d/$name reload
265         _procd_close_trigger
266 }
267
268 _procd_add_config_trigger() {
269         json_add_array
270         _procd_add_array_data "$1"
271         shift
272
273         json_add_array
274         _procd_add_array_data "if"
275
276         json_add_array
277         _procd_add_array_data "eq" "package" "$1"
278         shift
279         json_close_array
280
281         json_add_array
282         _procd_add_array_data "run_script" "$@"
283         json_close_array
284
285         json_close_array
286
287         json_close_array
288
289         _procd_add_timeout
290 }
291
292 _procd_add_raw_trigger() {
293         json_add_array
294         _procd_add_array_data "$1"
295         shift
296         local timeout=$1
297         shift
298
299         json_add_array
300         json_add_array
301         _procd_add_array_data "run_script" "$@"
302         json_close_array
303         json_close_array
304
305         json_add_int "" "$timeout"
306
307         json_close_array
308 }
309
310 _procd_add_reload_trigger() {
311         local script=$(readlink "$initscript")
312         local name=$(basename ${script:-$initscript})
313         local file
314
315         _procd_open_trigger
316         for file in "$@"; do
317                 _procd_add_config_trigger "config.change" "$file" /etc/init.d/$name reload
318         done
319         _procd_close_trigger
320 }
321
322 _procd_add_validation() {
323         _procd_open_validate
324         $@
325         _procd_close_validate
326 }
327
328 _procd_append_param() {
329         local type="$1"; shift
330         local _json_no_warning=1
331
332         json_select "$type"
333         [ $? = 0 ] || {
334                 _procd_set_param "$type" "$@"
335                 return
336         }
337         case "$type" in
338                 env|data|limits)
339                         _procd_add_table_data "$@"
340                 ;;
341                 command|netdev|file|respawn|watch)
342                         _procd_add_array_data "$@"
343                 ;;
344                 error)
345                         json_add_string "" "$@"
346                 ;;
347         esac
348         json_select ..
349 }
350
351 _procd_close_instance() {
352         local respawn_vals
353         _json_no_warning=1
354         if json_select respawn ; then
355                 json_get_values respawn_vals
356                 if [ -z "$respawn_vals" ]; then
357                         local respawn_retry=$(uci_get system.@service[0].respawn_retry)
358                         _procd_add_array_data 3600 5 ${respawn_retry:-5}
359                 fi
360                 json_select ..
361         fi
362
363         json_close_object
364 }
365
366 _procd_add_instance() {
367         _procd_open_instance
368         _procd_set_param command "$@"
369         _procd_close_instance
370 }
371
372 _procd_kill() {
373         local service="$1"
374         local instance="$2"
375
376         json_init
377         [ -n "$service" ] && json_add_string name "$service"
378         [ -n "$instance" ] && json_add_string instance "$instance"
379         _procd_ubus_call delete
380 }
381
382 _procd_send_signal() {
383         local service="$1"
384         local instance="$2"
385         local signal="$3"
386
387         json_init
388         json_add_string name "$service"
389         [ -n "$instance" -a "$instance" != "*" ] && json_add_string instance "$instance"
390         [ -n "$signal" ] && json_add_int signal "$signal"
391         _procd_ubus_call signal
392 }
393
394 procd_open_data() {
395         local name="$1"
396         json_set_namespace procd __procd_old_cb
397         json_add_object data
398 }
399
400 procd_close_data() {
401         json_close_object
402         json_set_namespace $__procd_old_cb
403 }
404
405 _procd_set_config_changed() {
406         local package="$1"
407
408         json_init
409         json_add_string type config.change
410         json_add_object data
411         json_add_string package "$package"
412         json_close_object
413
414         ubus call service event "$(json_dump)"
415 }
416
417 procd_add_mdns_service() {
418         local service proto port
419         service=$1; shift
420         proto=$1; shift
421         port=$1; shift
422         json_add_object "${service}_$port"
423         json_add_string "service" "_$service._$proto.local"
424         json_add_int port "$port"
425         [ -n "$1" ] && {
426                 json_add_array txt
427                 for txt in $@; do json_add_string "" $txt; done
428                 json_select ..
429         }
430         json_select ..
431 }
432
433 procd_add_mdns() {
434         procd_open_data
435         json_add_object "mdns"
436         procd_add_mdns_service $@
437         json_close_object
438         procd_close_data
439 }
440
441 uci_validate_section()
442 {
443         local _package="$1"
444         local _type="$2"
445         local _name="$3"
446         local _result
447         local _error
448         shift; shift; shift
449         _result=`/sbin/validate_data "$_package" "$_type" "$_name" "$@" 2> /dev/null`
450         _error=$?
451         eval "$_result"
452         [ "$_error" = "0" ] || `/sbin/validate_data "$_package" "$_type" "$_name" "$@" 1> /dev/null`
453         return $_error
454 }
455
456 _procd_wrapper \
457         procd_open_service \
458         procd_close_service \
459         procd_add_instance \
460         procd_add_raw_trigger \
461         procd_add_config_trigger \
462         procd_add_interface_trigger \
463         procd_add_reload_trigger \
464         procd_add_reload_interface_trigger \
465         procd_open_trigger \
466         procd_close_trigger \
467         procd_open_instance \
468         procd_close_instance \
469         procd_open_validate \
470         procd_close_validate \
471         procd_add_jail \
472         procd_add_jail_mount \
473         procd_add_jail_mount_rw \
474         procd_set_param \
475         procd_append_param \
476         procd_add_validation \
477         procd_set_config_changed \
478         procd_kill \
479         procd_send_signal