procd.sh 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716
  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. for t in "$@"; do
  290. _procd_add_data_trigger "$t" /etc/init.d/$name reload
  291. done
  292. _procd_close_trigger
  293. }
  294. _procd_add_config_trigger() {
  295. json_add_array
  296. _procd_add_array_data "$1"
  297. shift
  298. json_add_array
  299. _procd_add_array_data "if"
  300. json_add_array
  301. _procd_add_array_data "eq" "package" "$1"
  302. shift
  303. json_close_array
  304. json_add_array
  305. _procd_add_array_data "run_script" "$@"
  306. json_close_array
  307. json_close_array
  308. _procd_add_timeout
  309. json_close_array
  310. }
  311. _procd_add_mount_trigger() {
  312. json_add_array
  313. _procd_add_array_data "$1"
  314. local action="$2"
  315. local multi=0
  316. shift ; shift
  317. json_add_array
  318. _procd_add_array_data "if"
  319. if [ "$2" ]; then
  320. json_add_array
  321. _procd_add_array_data "or"
  322. multi=1
  323. fi
  324. while [ "$1" ]; do
  325. json_add_array
  326. _procd_add_array_data "eq" "target" "$1"
  327. shift
  328. json_close_array
  329. done
  330. [ $multi = 1 ] && json_close_array
  331. json_add_array
  332. _procd_add_array_data "run_script" /etc/init.d/$name $action
  333. json_close_array
  334. json_close_array
  335. _procd_add_timeout
  336. json_close_array
  337. }
  338. _procd_add_action_mount_trigger() {
  339. local action="$1"
  340. shift
  341. local mountpoints="$(procd_get_mountpoints "$@")"
  342. [ "${mountpoints//[[:space:]]}" ] || return 0
  343. local script=$(readlink "$initscript")
  344. local name=$(basename ${script:-$initscript})
  345. _procd_open_trigger
  346. _procd_add_mount_trigger mount.add $action "$mountpoints"
  347. _procd_close_trigger
  348. }
  349. procd_get_mountpoints() {
  350. (
  351. __procd_check_mount() {
  352. local cfg="$1"
  353. local path="${2%%/}/"
  354. local target
  355. config_get target "$cfg" target
  356. target="${target%%/}/"
  357. [ "$path" != "${path##$target}" ] && echo "${target%%/}"
  358. }
  359. local mpath
  360. config_load fstab
  361. for mpath in "$@"; do
  362. config_foreach __procd_check_mount mount "$mpath"
  363. done
  364. ) | sort -u
  365. }
  366. _procd_add_restart_mount_trigger() {
  367. _procd_add_action_mount_trigger restart "$@"
  368. }
  369. _procd_add_reload_mount_trigger() {
  370. _procd_add_action_mount_trigger reload "$@"
  371. }
  372. _procd_add_raw_trigger() {
  373. json_add_array
  374. _procd_add_array_data "$1"
  375. shift
  376. local timeout=$1
  377. shift
  378. json_add_array
  379. json_add_array
  380. _procd_add_array_data "run_script" "$@"
  381. json_close_array
  382. json_close_array
  383. json_add_int "" "$timeout"
  384. json_close_array
  385. }
  386. _procd_add_reload_trigger() {
  387. local script=$(readlink "$initscript")
  388. local name=$(basename ${script:-$initscript})
  389. local file
  390. _procd_open_trigger
  391. for file in "$@"; do
  392. _procd_add_config_trigger "config.change" "$file" /etc/init.d/$name reload
  393. done
  394. _procd_close_trigger
  395. }
  396. _procd_add_validation() {
  397. _procd_open_validate
  398. $@
  399. _procd_close_validate
  400. }
  401. _procd_append_param() {
  402. local type="$1"; shift
  403. local _json_no_warning=1
  404. json_select "$type"
  405. [ $? = 0 ] || {
  406. _procd_set_param "$type" "$@"
  407. return
  408. }
  409. case "$type" in
  410. env|data|limits)
  411. _procd_add_table_data "$@"
  412. ;;
  413. command|netdev|file|respawn|watch|watchdog)
  414. _procd_add_array_data "$@"
  415. ;;
  416. error)
  417. json_add_string "" "$@"
  418. ;;
  419. esac
  420. json_select ..
  421. }
  422. _procd_close_instance() {
  423. local respawn_vals
  424. _json_no_warning=1
  425. if json_select respawn ; then
  426. json_get_values respawn_vals
  427. if [ -z "$respawn_vals" ]; then
  428. local respawn_threshold=$(uci_get system.@service[0].respawn_threshold)
  429. local respawn_timeout=$(uci_get system.@service[0].respawn_timeout)
  430. local respawn_retry=$(uci_get system.@service[0].respawn_retry)
  431. _procd_add_array_data ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-5}
  432. fi
  433. json_select ..
  434. fi
  435. json_close_object
  436. }
  437. _procd_add_instance() {
  438. _procd_open_instance
  439. _procd_set_param command "$@"
  440. _procd_close_instance
  441. }
  442. procd_running() {
  443. local service="$1"
  444. local instance="${2:-*}"
  445. [ "$instance" = "*" ] || instance="'$instance'"
  446. json_init
  447. json_add_string name "$service"
  448. local running=$(_procd_ubus_call list | jsonfilter -l 1 -e "@['$service'].instances[$instance].running")
  449. [ "$running" = "true" ]
  450. }
  451. _procd_kill() {
  452. local service="$1"
  453. local instance="$2"
  454. json_init
  455. [ -n "$service" ] && json_add_string name "$service"
  456. [ -n "$instance" ] && json_add_string instance "$instance"
  457. _procd_ubus_call delete
  458. }
  459. _procd_send_signal() {
  460. local service="$1"
  461. local instance="$2"
  462. local signal="$3"
  463. case "$signal" in
  464. [A-Z]*) signal="$(kill -l "$signal" 2>/dev/null)" || return 1;;
  465. esac
  466. json_init
  467. json_add_string name "$service"
  468. [ -n "$instance" -a "$instance" != "*" ] && json_add_string instance "$instance"
  469. [ -n "$signal" ] && json_add_int signal "$signal"
  470. _procd_ubus_call signal
  471. }
  472. _procd_status() {
  473. local service="$1"
  474. local instance="$2"
  475. local data state
  476. local n_running=0
  477. local n_stopped=0
  478. local n_total=0
  479. json_init
  480. [ -n "$service" ] && json_add_string name "$service"
  481. data=$(_procd_ubus_call list | jsonfilter -e '@["'"$service"'"]')
  482. [ -z "$data" ] && { echo "inactive"; return 3; }
  483. data=$(echo "$data" | jsonfilter -e '$.instances')
  484. if [ -z "$data" ]; then
  485. [ -z "$instance" ] && { echo "active with no instances"; return 0; }
  486. data="[]"
  487. fi
  488. [ -n "$instance" ] && instance="\"$instance\"" || instance='*'
  489. for state in $(jsonfilter -s "$data" -e '$['"$instance"'].running'); do
  490. n_total=$((n_total + 1))
  491. case "$state" in
  492. false) n_stopped=$((n_stopped + 1)) ;;
  493. true) n_running=$((n_running + 1)) ;;
  494. esac
  495. done
  496. if [ $n_total -gt 0 ]; then
  497. if [ $n_running -gt 0 ] && [ $n_stopped -eq 0 ]; then
  498. echo "running"
  499. return 0
  500. elif [ $n_running -gt 0 ]; then
  501. echo "running ($n_running/$n_total)"
  502. return 0
  503. else
  504. echo "not running"
  505. return 5
  506. fi
  507. else
  508. echo "unknown instance $instance"
  509. return 4
  510. fi
  511. }
  512. procd_open_data() {
  513. local name="$1"
  514. json_set_namespace procd __procd_old_cb
  515. json_add_object data
  516. }
  517. procd_close_data() {
  518. json_close_object
  519. json_set_namespace $__procd_old_cb
  520. }
  521. _procd_set_config_changed() {
  522. local package="$1"
  523. json_init
  524. json_add_string type config.change
  525. json_add_object data
  526. json_add_string package "$package"
  527. json_close_object
  528. ubus call service event "$(json_dump)"
  529. }
  530. procd_add_mdns_service() {
  531. local service proto port txt_count=0
  532. service=$1; shift
  533. proto=$1; shift
  534. port=$1; shift
  535. json_add_object "${service}_$port"
  536. json_add_string "service" "_$service._$proto.local"
  537. json_add_int port "$port"
  538. for txt in "$@"; do
  539. [ -z "$txt" ] && continue
  540. txt_count=$((txt_count+1))
  541. [ $txt_count -eq 1 ] && json_add_array txt
  542. json_add_string "" "$txt"
  543. done
  544. [ $txt_count -gt 0 ] && json_select ..
  545. json_select ..
  546. }
  547. procd_add_mdns() {
  548. procd_open_data
  549. json_add_object "mdns"
  550. procd_add_mdns_service "$@"
  551. json_close_object
  552. procd_close_data
  553. }
  554. uci_validate_section()
  555. {
  556. local _package="$1"
  557. local _type="$2"
  558. local _name="$3"
  559. local _result
  560. local _error
  561. shift; shift; shift
  562. _result=$(/sbin/validate_data "$_package" "$_type" "$_name" "$@" 2> /dev/null)
  563. _error=$?
  564. eval "$_result"
  565. [ "$_error" = "0" ] || $(/sbin/validate_data "$_package" "$_type" "$_name" "$@" 1> /dev/null)
  566. return $_error
  567. }
  568. uci_load_validate() {
  569. local _package="$1"
  570. local _type="$2"
  571. local _name="$3"
  572. local _function="$4"
  573. local _option
  574. local _result
  575. shift; shift; shift; shift
  576. for _option in "$@"; do
  577. eval "local ${_option%%:*}"
  578. done
  579. uci_validate_section "$_package" "$_type" "$_name" "$@"
  580. _result=$?
  581. [ -n "$_function" ] || return $_result
  582. eval "$_function \"\$_name\" \"\$_result\""
  583. }
  584. _procd_wrapper \
  585. procd_open_service \
  586. procd_close_service \
  587. procd_add_instance \
  588. procd_add_raw_trigger \
  589. procd_add_config_trigger \
  590. procd_add_interface_trigger \
  591. procd_add_mount_trigger \
  592. procd_add_reload_trigger \
  593. procd_add_reload_data_trigger \
  594. procd_add_reload_interface_trigger \
  595. procd_add_action_mount_trigger \
  596. procd_add_reload_mount_trigger \
  597. procd_add_restart_mount_trigger \
  598. procd_open_trigger \
  599. procd_close_trigger \
  600. procd_open_instance \
  601. procd_close_instance \
  602. procd_open_validate \
  603. procd_close_validate \
  604. procd_add_jail \
  605. procd_add_jail_mount \
  606. procd_add_jail_mount_rw \
  607. procd_set_param \
  608. procd_append_param \
  609. procd_add_validation \
  610. procd_set_config_changed \
  611. procd_kill \
  612. procd_send_signal