procd.sh 15 KB

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