procd.sh 14 KB

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