procd.sh 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680
  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: $username to run service as
  22. # group: $groupname to run service as
  23. # pidfile: file name to write pid into
  24. # stdout: boolean whether to redirect commands stdout to syslog (default: 0)
  25. # stderr: boolean whether to redirect commands stderr to syslog (default: 0)
  26. # facility: syslog facility used when logging to syslog (default: daemon)
  27. #
  28. # No space separation is done for arrays/tables - use one function argument per command line argument
  29. #
  30. # procd_close_instance():
  31. # Complete the instance being prepared
  32. #
  33. # procd_running(service, [instance]):
  34. # Checks if service/instance is currently running
  35. #
  36. # procd_kill(service, [instance]):
  37. # Kill a service instance (or all instances)
  38. #
  39. # procd_send_signal(service, [instance], [signal])
  40. # Send a signal to a service instance (or all instances)
  41. #
  42. . "$IPKG_INSTROOT/usr/share/libubox/jshn.sh"
  43. PROCD_RELOAD_DELAY=1000
  44. _PROCD_SERVICE=
  45. procd_lock() {
  46. local basescript=$(readlink "$initscript")
  47. local service_name="$(basename ${basescript:-$initscript})"
  48. flock -n 1000 &> /dev/null
  49. if [ "$?" != "0" ]; then
  50. exec 1000>"$IPKG_INSTROOT/var/lock/procd_${service_name}.lock"
  51. flock 1000
  52. if [ "$?" != "0" ]; then
  53. logger "warning: procd flock for $service_name failed"
  54. fi
  55. fi
  56. }
  57. _procd_call() {
  58. local old_cb
  59. json_set_namespace procd old_cb
  60. "$@"
  61. json_set_namespace $old_cb
  62. }
  63. _procd_wrapper() {
  64. procd_lock
  65. while [ -n "$1" ]; do
  66. eval "$1() { _procd_call _$1 \"\$@\"; }"
  67. shift
  68. done
  69. }
  70. _procd_ubus_call() {
  71. local cmd="$1"
  72. [ -n "$PROCD_DEBUG" ] && json_dump >&2
  73. ubus call service "$cmd" "$(json_dump)"
  74. json_cleanup
  75. }
  76. _procd_open_service() {
  77. local name="$1"
  78. local script="$2"
  79. _PROCD_SERVICE="$name"
  80. _PROCD_INSTANCE_SEQ=0
  81. json_init
  82. json_add_string name "$name"
  83. [ -n "$script" ] && json_add_string script "$script"
  84. json_add_object instances
  85. }
  86. _procd_close_service() {
  87. json_close_object
  88. _procd_open_trigger
  89. service_triggers
  90. _procd_close_trigger
  91. _procd_open_data
  92. service_data
  93. _procd_close_data
  94. _procd_ubus_call ${1:-set}
  95. }
  96. _procd_add_array_data() {
  97. while [ "$#" -gt 0 ]; do
  98. json_add_string "" "$1"
  99. shift
  100. done
  101. }
  102. _procd_add_array() {
  103. json_add_array "$1"
  104. shift
  105. _procd_add_array_data "$@"
  106. json_close_array
  107. }
  108. _procd_add_table_data() {
  109. while [ -n "$1" ]; do
  110. local var="${1%%=*}"
  111. local val="${1#*=}"
  112. [ "$1" = "$val" ] && val=
  113. json_add_string "$var" "$val"
  114. shift
  115. done
  116. }
  117. _procd_add_table() {
  118. json_add_object "$1"
  119. shift
  120. _procd_add_table_data "$@"
  121. json_close_object
  122. }
  123. _procd_open_instance() {
  124. local name="$1"; shift
  125. _PROCD_INSTANCE_SEQ="$(($_PROCD_INSTANCE_SEQ + 1))"
  126. name="${name:-instance$_PROCD_INSTANCE_SEQ}"
  127. json_add_object "$name"
  128. [ -n "$TRACE_SYSCALLS" ] && json_add_boolean trace "1"
  129. }
  130. _procd_open_trigger() {
  131. let '_procd_trigger_open = _procd_trigger_open + 1'
  132. [ "$_procd_trigger_open" -gt 1 ] && return
  133. json_add_array "triggers"
  134. }
  135. _procd_close_trigger() {
  136. let '_procd_trigger_open = _procd_trigger_open - 1'
  137. [ "$_procd_trigger_open" -lt 1 ] || return
  138. json_close_array
  139. }
  140. _procd_open_data() {
  141. let '_procd_data_open = _procd_data_open + 1'
  142. [ "$_procd_data_open" -gt 1 ] && return
  143. json_add_object "data"
  144. }
  145. _procd_close_data() {
  146. let '_procd_data_open = _procd_data_open - 1'
  147. [ "$_procd_data_open" -lt 1 ] || return
  148. json_close_object
  149. }
  150. _procd_open_validate() {
  151. json_select ..
  152. json_add_array "validate"
  153. }
  154. _procd_close_validate() {
  155. json_close_array
  156. json_select triggers
  157. }
  158. _procd_add_jail() {
  159. json_add_object "jail"
  160. json_add_string name "$1"
  161. shift
  162. for a in $@; do
  163. case $a in
  164. log) json_add_boolean "log" "1";;
  165. ubus) json_add_boolean "ubus" "1";;
  166. procfs) json_add_boolean "procfs" "1";;
  167. sysfs) json_add_boolean "sysfs" "1";;
  168. ronly) json_add_boolean "ronly" "1";;
  169. requirejail) json_add_boolean "requirejail" "1";;
  170. netns) json_add_boolean "netns" "1";;
  171. userns) json_add_boolean "userns" "1";;
  172. cgroupsns) json_add_boolean "cgroupsns" "1";;
  173. esac
  174. done
  175. json_add_object "mount"
  176. json_close_object
  177. json_close_object
  178. }
  179. _procd_add_jail_mount() {
  180. local _json_no_warning=1
  181. json_select "jail"
  182. [ $? = 0 ] || return
  183. json_select "mount"
  184. [ $? = 0 ] || {
  185. json_select ..
  186. return
  187. }
  188. for a in $@; do
  189. json_add_string "$a" "0"
  190. done
  191. json_select ..
  192. json_select ..
  193. }
  194. _procd_add_jail_mount_rw() {
  195. local _json_no_warning=1
  196. json_select "jail"
  197. [ $? = 0 ] || return
  198. json_select "mount"
  199. [ $? = 0 ] || {
  200. json_select ..
  201. return
  202. }
  203. for a in $@; do
  204. json_add_string "$a" "1"
  205. done
  206. json_select ..
  207. json_select ..
  208. }
  209. _procd_set_param() {
  210. local type="$1"; shift
  211. case "$type" in
  212. env|data|limits)
  213. _procd_add_table "$type" "$@"
  214. ;;
  215. command|netdev|file|respawn|watch|watchdog)
  216. _procd_add_array "$type" "$@"
  217. ;;
  218. error)
  219. json_add_array "$type"
  220. json_add_string "" "$@"
  221. json_close_array
  222. ;;
  223. nice|term_timeout)
  224. json_add_int "$type" "$1"
  225. ;;
  226. reload_signal)
  227. json_add_int "$type" $(kill -l "$1")
  228. ;;
  229. pidfile|user|group|seccomp|capabilities|facility|\
  230. extroot|overlaydir|tmpoverlaysize)
  231. json_add_string "$type" "$1"
  232. ;;
  233. stdout|stderr|no_new_privs)
  234. json_add_boolean "$type" "$1"
  235. ;;
  236. esac
  237. }
  238. _procd_add_timeout() {
  239. [ "$PROCD_RELOAD_DELAY" -gt 0 ] && json_add_int "" "$PROCD_RELOAD_DELAY"
  240. return 0
  241. }
  242. _procd_add_interface_trigger() {
  243. json_add_array
  244. _procd_add_array_data "$1"
  245. shift
  246. json_add_array
  247. _procd_add_array_data "if"
  248. json_add_array
  249. _procd_add_array_data "eq" "interface" "$1"
  250. shift
  251. json_close_array
  252. json_add_array
  253. _procd_add_array_data "run_script" "$@"
  254. json_close_array
  255. json_close_array
  256. _procd_add_timeout
  257. json_close_array
  258. }
  259. _procd_add_reload_interface_trigger() {
  260. local script=$(readlink "$initscript")
  261. local name=$(basename ${script:-$initscript})
  262. _procd_open_trigger
  263. _procd_add_interface_trigger "interface.*" $1 /etc/init.d/$name reload
  264. _procd_close_trigger
  265. }
  266. _procd_add_config_trigger() {
  267. json_add_array
  268. _procd_add_array_data "$1"
  269. shift
  270. json_add_array
  271. _procd_add_array_data "if"
  272. json_add_array
  273. _procd_add_array_data "eq" "package" "$1"
  274. shift
  275. json_close_array
  276. json_add_array
  277. _procd_add_array_data "run_script" "$@"
  278. json_close_array
  279. json_close_array
  280. _procd_add_timeout
  281. json_close_array
  282. }
  283. _procd_add_mount_trigger() {
  284. json_add_array
  285. _procd_add_array_data "$1"
  286. local action="$2"
  287. local multi=0
  288. shift ; shift
  289. json_add_array
  290. _procd_add_array_data "if"
  291. if [ "$2" ]; then
  292. json_add_array
  293. _procd_add_array_data "or"
  294. multi=1
  295. fi
  296. while [ "$1" ]; do
  297. json_add_array
  298. _procd_add_array_data "eq" "target" "$1"
  299. shift
  300. json_close_array
  301. done
  302. [ $multi = 1 ] && json_close_array
  303. json_add_array
  304. _procd_add_array_data "run_script" /etc/init.d/$name $action
  305. json_close_array
  306. json_close_array
  307. _procd_add_timeout
  308. json_close_array
  309. }
  310. _procd_add_action_mount_trigger() {
  311. local action="$1"
  312. shift
  313. local mountpoints="$(procd_get_mountpoints "$@")"
  314. [ "${mountpoints//[[:space:]]}" ] || return 0
  315. local script=$(readlink "$initscript")
  316. local name=$(basename ${script:-$initscript})
  317. _procd_open_trigger
  318. _procd_add_mount_trigger mount.add $action "$mountpoints"
  319. _procd_close_trigger
  320. }
  321. procd_get_mountpoints() {
  322. (
  323. __procd_check_mount() {
  324. local cfg="$1"
  325. local path="${2%%/}/"
  326. local target
  327. config_get target "$cfg" target
  328. target="${target%%/}/"
  329. [ "$path" != "${path##$target}" ] && echo "${target%%/}"
  330. }
  331. local mpath
  332. config_load fstab
  333. for mpath in "$@"; do
  334. config_foreach __procd_check_mount mount "$mpath"
  335. done
  336. ) | sort -u
  337. }
  338. _procd_add_restart_mount_trigger() {
  339. _procd_add_action_mount_trigger restart "$@"
  340. }
  341. _procd_add_reload_mount_trigger() {
  342. _procd_add_action_mount_trigger reload "$@"
  343. }
  344. _procd_add_raw_trigger() {
  345. json_add_array
  346. _procd_add_array_data "$1"
  347. shift
  348. local timeout=$1
  349. shift
  350. json_add_array
  351. json_add_array
  352. _procd_add_array_data "run_script" "$@"
  353. json_close_array
  354. json_close_array
  355. json_add_int "" "$timeout"
  356. json_close_array
  357. }
  358. _procd_add_reload_trigger() {
  359. local script=$(readlink "$initscript")
  360. local name=$(basename ${script:-$initscript})
  361. local file
  362. _procd_open_trigger
  363. for file in "$@"; do
  364. _procd_add_config_trigger "config.change" "$file" /etc/init.d/$name reload
  365. done
  366. _procd_close_trigger
  367. }
  368. _procd_add_validation() {
  369. _procd_open_validate
  370. $@
  371. _procd_close_validate
  372. }
  373. _procd_append_param() {
  374. local type="$1"; shift
  375. local _json_no_warning=1
  376. json_select "$type"
  377. [ $? = 0 ] || {
  378. _procd_set_param "$type" "$@"
  379. return
  380. }
  381. case "$type" in
  382. env|data|limits)
  383. _procd_add_table_data "$@"
  384. ;;
  385. command|netdev|file|respawn|watch|watchdog)
  386. _procd_add_array_data "$@"
  387. ;;
  388. error)
  389. json_add_string "" "$@"
  390. ;;
  391. esac
  392. json_select ..
  393. }
  394. _procd_close_instance() {
  395. local respawn_vals
  396. _json_no_warning=1
  397. if json_select respawn ; then
  398. json_get_values respawn_vals
  399. if [ -z "$respawn_vals" ]; then
  400. local respawn_threshold=$(uci_get system.@service[0].respawn_threshold)
  401. local respawn_timeout=$(uci_get system.@service[0].respawn_timeout)
  402. local respawn_retry=$(uci_get system.@service[0].respawn_retry)
  403. _procd_add_array_data ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-5}
  404. fi
  405. json_select ..
  406. fi
  407. json_close_object
  408. }
  409. _procd_add_instance() {
  410. _procd_open_instance
  411. _procd_set_param command "$@"
  412. _procd_close_instance
  413. }
  414. procd_running() {
  415. local service="$1"
  416. local instance="${2:-*}"
  417. [ "$instance" = "*" ] || instance="'$instance'"
  418. json_init
  419. json_add_string name "$service"
  420. local running=$(_procd_ubus_call list | jsonfilter -l 1 -e "@['$service'].instances[$instance].running")
  421. [ "$running" = "true" ]
  422. }
  423. _procd_kill() {
  424. local service="$1"
  425. local instance="$2"
  426. json_init
  427. [ -n "$service" ] && json_add_string name "$service"
  428. [ -n "$instance" ] && json_add_string instance "$instance"
  429. _procd_ubus_call delete
  430. }
  431. _procd_send_signal() {
  432. local service="$1"
  433. local instance="$2"
  434. local signal="$3"
  435. case "$signal" in
  436. [A-Z]*) signal="$(kill -l "$signal" 2>/dev/null)" || return 1;;
  437. esac
  438. json_init
  439. json_add_string name "$service"
  440. [ -n "$instance" -a "$instance" != "*" ] && json_add_string instance "$instance"
  441. [ -n "$signal" ] && json_add_int signal "$signal"
  442. _procd_ubus_call signal
  443. }
  444. _procd_status() {
  445. local service="$1"
  446. local instance="$2"
  447. local data state
  448. local n_running=0
  449. local n_stopped=0
  450. local n_total=0
  451. json_init
  452. [ -n "$service" ] && json_add_string name "$service"
  453. data=$(_procd_ubus_call list | jsonfilter -e '@["'"$service"'"]')
  454. [ -z "$data" ] && { echo "inactive"; return 3; }
  455. data=$(echo "$data" | jsonfilter -e '$.instances')
  456. if [ -z "$data" ]; then
  457. [ -z "$instance" ] && { echo "active with no instances"; return 0; }
  458. data="[]"
  459. fi
  460. [ -n "$instance" ] && instance="\"$instance\"" || instance='*'
  461. for state in $(jsonfilter -s "$data" -e '$['"$instance"'].running'); do
  462. n_total=$((n_total + 1))
  463. case "$state" in
  464. false) n_stopped=$((n_stopped + 1)) ;;
  465. true) n_running=$((n_running + 1)) ;;
  466. esac
  467. done
  468. if [ $n_total -gt 0 ]; then
  469. if [ $n_running -gt 0 ] && [ $n_stopped -eq 0 ]; then
  470. echo "running"
  471. return 0
  472. elif [ $n_running -gt 0 ]; then
  473. echo "running ($n_running/$n_total)"
  474. return 0
  475. else
  476. echo "not running"
  477. return 5
  478. fi
  479. else
  480. echo "unknown instance $instance"
  481. return 4
  482. fi
  483. }
  484. procd_open_data() {
  485. local name="$1"
  486. json_set_namespace procd __procd_old_cb
  487. json_add_object data
  488. }
  489. procd_close_data() {
  490. json_close_object
  491. json_set_namespace $__procd_old_cb
  492. }
  493. _procd_set_config_changed() {
  494. local package="$1"
  495. json_init
  496. json_add_string type config.change
  497. json_add_object data
  498. json_add_string package "$package"
  499. json_close_object
  500. ubus call service event "$(json_dump)"
  501. }
  502. procd_add_mdns_service() {
  503. local service proto port txt_count=0
  504. service=$1; shift
  505. proto=$1; shift
  506. port=$1; shift
  507. json_add_object "${service}_$port"
  508. json_add_string "service" "_$service._$proto.local"
  509. json_add_int port "$port"
  510. for txt in "$@"; do
  511. [ -z "$txt" ] && continue
  512. txt_count=$((txt_count+1))
  513. [ $txt_count -eq 1 ] && json_add_array txt
  514. json_add_string "" "$txt"
  515. done
  516. [ $txt_count -gt 0 ] && json_select ..
  517. json_select ..
  518. }
  519. procd_add_mdns() {
  520. procd_open_data
  521. json_add_object "mdns"
  522. procd_add_mdns_service "$@"
  523. json_close_object
  524. procd_close_data
  525. }
  526. uci_validate_section()
  527. {
  528. local _package="$1"
  529. local _type="$2"
  530. local _name="$3"
  531. local _result
  532. local _error
  533. shift; shift; shift
  534. _result=$(/sbin/validate_data "$_package" "$_type" "$_name" "$@" 2> /dev/null)
  535. _error=$?
  536. eval "$_result"
  537. [ "$_error" = "0" ] || $(/sbin/validate_data "$_package" "$_type" "$_name" "$@" 1> /dev/null)
  538. return $_error
  539. }
  540. uci_load_validate() {
  541. local _package="$1"
  542. local _type="$2"
  543. local _name="$3"
  544. local _function="$4"
  545. local _option
  546. local _result
  547. shift; shift; shift; shift
  548. for _option in "$@"; do
  549. eval "local ${_option%%:*}"
  550. done
  551. uci_validate_section "$_package" "$_type" "$_name" "$@"
  552. _result=$?
  553. [ -n "$_function" ] || return $_result
  554. eval "$_function \"\$_name\" \"\$_result\""
  555. }
  556. _procd_wrapper \
  557. procd_open_service \
  558. procd_close_service \
  559. procd_add_instance \
  560. procd_add_raw_trigger \
  561. procd_add_config_trigger \
  562. procd_add_interface_trigger \
  563. procd_add_mount_trigger \
  564. procd_add_reload_trigger \
  565. procd_add_reload_interface_trigger \
  566. procd_add_action_mount_trigger \
  567. procd_add_reload_mount_trigger \
  568. procd_add_restart_mount_trigger \
  569. procd_open_trigger \
  570. procd_close_trigger \
  571. procd_open_instance \
  572. procd_close_instance \
  573. procd_open_validate \
  574. procd_close_validate \
  575. procd_add_jail \
  576. procd_add_jail_mount \
  577. procd_add_jail_mount_rw \
  578. procd_set_param \
  579. procd_append_param \
  580. procd_add_validation \
  581. procd_set_config_changed \
  582. procd_kill \
  583. procd_send_signal