procd.sh 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  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. esac
  170. done
  171. json_add_object "mount"
  172. json_close_object
  173. json_close_object
  174. }
  175. _procd_add_jail_mount() {
  176. local _json_no_warning=1
  177. json_select "jail"
  178. [ $? = 0 ] || return
  179. json_select "mount"
  180. [ $? = 0 ] || {
  181. json_select ..
  182. return
  183. }
  184. for a in $@; do
  185. json_add_string "$a" "0"
  186. done
  187. json_select ..
  188. json_select ..
  189. }
  190. _procd_add_jail_mount_rw() {
  191. local _json_no_warning=1
  192. json_select "jail"
  193. [ $? = 0 ] || return
  194. json_select "mount"
  195. [ $? = 0 ] || {
  196. json_select ..
  197. return
  198. }
  199. for a in $@; do
  200. json_add_string "$a" "1"
  201. done
  202. json_select ..
  203. json_select ..
  204. }
  205. _procd_set_param() {
  206. local type="$1"; shift
  207. case "$type" in
  208. env|data|limits)
  209. _procd_add_table "$type" "$@"
  210. ;;
  211. command|netdev|file|respawn|watch)
  212. _procd_add_array "$type" "$@"
  213. ;;
  214. error)
  215. json_add_array "$type"
  216. json_add_string "" "$@"
  217. json_close_array
  218. ;;
  219. nice|term_timeout)
  220. json_add_int "$type" "$1"
  221. ;;
  222. reload_signal)
  223. json_add_int "$type" $(kill -l "$1")
  224. ;;
  225. pidfile|user|group|seccomp|capabilities|facility)
  226. json_add_string "$type" "$1"
  227. ;;
  228. stdout|stderr|no_new_privs)
  229. json_add_boolean "$type" "$1"
  230. ;;
  231. esac
  232. }
  233. _procd_add_timeout() {
  234. [ "$PROCD_RELOAD_DELAY" -gt 0 ] && json_add_int "" "$PROCD_RELOAD_DELAY"
  235. return 0
  236. }
  237. _procd_add_interface_trigger() {
  238. json_add_array
  239. _procd_add_array_data "$1"
  240. shift
  241. json_add_array
  242. _procd_add_array_data "if"
  243. json_add_array
  244. _procd_add_array_data "eq" "interface" "$1"
  245. shift
  246. json_close_array
  247. json_add_array
  248. _procd_add_array_data "run_script" "$@"
  249. json_close_array
  250. json_close_array
  251. _procd_add_timeout
  252. json_close_array
  253. }
  254. _procd_add_reload_interface_trigger() {
  255. local script=$(readlink "$initscript")
  256. local name=$(basename ${script:-$initscript})
  257. _procd_open_trigger
  258. _procd_add_interface_trigger "interface.*" $1 /etc/init.d/$name reload
  259. _procd_close_trigger
  260. }
  261. _procd_add_config_trigger() {
  262. json_add_array
  263. _procd_add_array_data "$1"
  264. shift
  265. json_add_array
  266. _procd_add_array_data "if"
  267. json_add_array
  268. _procd_add_array_data "eq" "package" "$1"
  269. shift
  270. json_close_array
  271. json_add_array
  272. _procd_add_array_data "run_script" "$@"
  273. json_close_array
  274. json_close_array
  275. _procd_add_timeout
  276. json_close_array
  277. }
  278. _procd_add_raw_trigger() {
  279. json_add_array
  280. _procd_add_array_data "$1"
  281. shift
  282. local timeout=$1
  283. shift
  284. json_add_array
  285. json_add_array
  286. _procd_add_array_data "run_script" "$@"
  287. json_close_array
  288. json_close_array
  289. json_add_int "" "$timeout"
  290. json_close_array
  291. }
  292. _procd_add_reload_trigger() {
  293. local script=$(readlink "$initscript")
  294. local name=$(basename ${script:-$initscript})
  295. local file
  296. _procd_open_trigger
  297. for file in "$@"; do
  298. _procd_add_config_trigger "config.change" "$file" /etc/init.d/$name reload
  299. done
  300. _procd_close_trigger
  301. }
  302. _procd_add_validation() {
  303. _procd_open_validate
  304. $@
  305. _procd_close_validate
  306. }
  307. _procd_append_param() {
  308. local type="$1"; shift
  309. local _json_no_warning=1
  310. json_select "$type"
  311. [ $? = 0 ] || {
  312. _procd_set_param "$type" "$@"
  313. return
  314. }
  315. case "$type" in
  316. env|data|limits)
  317. _procd_add_table_data "$@"
  318. ;;
  319. command|netdev|file|respawn|watch)
  320. _procd_add_array_data "$@"
  321. ;;
  322. error)
  323. json_add_string "" "$@"
  324. ;;
  325. esac
  326. json_select ..
  327. }
  328. _procd_close_instance() {
  329. local respawn_vals
  330. _json_no_warning=1
  331. if json_select respawn ; then
  332. json_get_values respawn_vals
  333. if [ -z "$respawn_vals" ]; then
  334. local respawn_threshold=$(uci_get system.@service[0].respawn_threshold)
  335. local respawn_timeout=$(uci_get system.@service[0].respawn_timeout)
  336. local respawn_retry=$(uci_get system.@service[0].respawn_retry)
  337. _procd_add_array_data ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-5}
  338. fi
  339. json_select ..
  340. fi
  341. json_close_object
  342. }
  343. _procd_add_instance() {
  344. _procd_open_instance
  345. _procd_set_param command "$@"
  346. _procd_close_instance
  347. }
  348. procd_running() {
  349. local service="$1"
  350. local instance="${2:-instance1}"
  351. local running
  352. json_init
  353. json_add_string name "$service"
  354. running=$(_procd_ubus_call list | jsonfilter -e "@['$service'].instances['$instance'].running")
  355. [ "$running" = "true" ]
  356. }
  357. _procd_kill() {
  358. local service="$1"
  359. local instance="$2"
  360. json_init
  361. [ -n "$service" ] && json_add_string name "$service"
  362. [ -n "$instance" ] && json_add_string instance "$instance"
  363. _procd_ubus_call delete
  364. }
  365. _procd_send_signal() {
  366. local service="$1"
  367. local instance="$2"
  368. local signal="$3"
  369. case "$signal" in
  370. [A-Z]*) signal="$(kill -l "$signal" 2>/dev/null)" || return 1;;
  371. esac
  372. json_init
  373. json_add_string name "$service"
  374. [ -n "$instance" -a "$instance" != "*" ] && json_add_string instance "$instance"
  375. [ -n "$signal" ] && json_add_int signal "$signal"
  376. _procd_ubus_call signal
  377. }
  378. procd_open_data() {
  379. local name="$1"
  380. json_set_namespace procd __procd_old_cb
  381. json_add_object data
  382. }
  383. procd_close_data() {
  384. json_close_object
  385. json_set_namespace $__procd_old_cb
  386. }
  387. _procd_set_config_changed() {
  388. local package="$1"
  389. json_init
  390. json_add_string type config.change
  391. json_add_object data
  392. json_add_string package "$package"
  393. json_close_object
  394. ubus call service event "$(json_dump)"
  395. }
  396. procd_add_mdns_service() {
  397. local service proto port
  398. service=$1; shift
  399. proto=$1; shift
  400. port=$1; shift
  401. json_add_object "${service}_$port"
  402. json_add_string "service" "_$service._$proto.local"
  403. json_add_int port "$port"
  404. [ -n "$1" ] && {
  405. json_add_array txt
  406. for txt in "$@"; do json_add_string "" "$txt"; done
  407. json_select ..
  408. }
  409. json_select ..
  410. }
  411. procd_add_mdns() {
  412. procd_open_data
  413. json_add_object "mdns"
  414. procd_add_mdns_service "$@"
  415. json_close_object
  416. procd_close_data
  417. }
  418. uci_validate_section()
  419. {
  420. local _package="$1"
  421. local _type="$2"
  422. local _name="$3"
  423. local _result
  424. local _error
  425. shift; shift; shift
  426. _result=`/sbin/validate_data "$_package" "$_type" "$_name" "$@" 2> /dev/null`
  427. _error=$?
  428. eval "$_result"
  429. [ "$_error" = "0" ] || `/sbin/validate_data "$_package" "$_type" "$_name" "$@" 1> /dev/null`
  430. return $_error
  431. }
  432. uci_load_validate() {
  433. local _package="$1"
  434. local _type="$2"
  435. local _name="$3"
  436. local _function="$4"
  437. local _option
  438. local _result
  439. shift; shift; shift; shift
  440. for _option in "$@"; do
  441. eval "local ${_option%%:*}"
  442. done
  443. uci_validate_section "$_package" "$_type" "$_name" "$@"
  444. _result=$?
  445. [ -n "$_function" ] || return $_result
  446. eval "$_function \"\$_name\" \"\$_result\""
  447. }
  448. _procd_wrapper \
  449. procd_open_service \
  450. procd_close_service \
  451. procd_add_instance \
  452. procd_add_raw_trigger \
  453. procd_add_config_trigger \
  454. procd_add_interface_trigger \
  455. procd_add_reload_trigger \
  456. procd_add_reload_interface_trigger \
  457. procd_open_trigger \
  458. procd_close_trigger \
  459. procd_open_instance \
  460. procd_close_instance \
  461. procd_open_validate \
  462. procd_close_validate \
  463. procd_add_jail \
  464. procd_add_jail_mount \
  465. procd_add_jail_mount_rw \
  466. procd_set_param \
  467. procd_append_param \
  468. procd_add_validation \
  469. procd_set_config_changed \
  470. procd_kill \
  471. procd_send_signal