procd.sh 15 KB

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