procd.sh 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  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 info: array with 1 values $username
  22. # pidfile: file name to write pid into
  23. #
  24. # No space separation is done for arrays/tables - use one function argument per command line argument
  25. #
  26. # procd_close_instance():
  27. # Complete the instance being prepared
  28. #
  29. # procd_kill(service, [instance]):
  30. # Kill a service instance (or all instances)
  31. #
  32. # procd_send_signal(service, [instance], [signal])
  33. # Send a signal to a service instance (or all instances)
  34. #
  35. . "$IPKG_INSTROOT/usr/share/libubox/jshn.sh"
  36. PROCD_RELOAD_DELAY=1000
  37. _PROCD_SERVICE=
  38. procd_lock() {
  39. local basescript=$(readlink "$initscript")
  40. local service_name="$(basename ${basescript:-$initscript})"
  41. flock -n 1000 &> /dev/null
  42. if [ "$?" != "0" ]; then
  43. exec 1000>"$IPKG_INSTROOT/var/lock/procd_${service_name}.lock"
  44. flock 1000
  45. if [ "$?" != "0" ]; then
  46. logger "warning: procd flock for $service_name failed"
  47. fi
  48. fi
  49. }
  50. _procd_call() {
  51. local old_cb
  52. json_set_namespace procd old_cb
  53. "$@"
  54. json_set_namespace $old_cb
  55. }
  56. _procd_wrapper() {
  57. procd_lock
  58. while [ -n "$1" ]; do
  59. eval "$1() { _procd_call _$1 \"\$@\"; }"
  60. shift
  61. done
  62. }
  63. _procd_ubus_call() {
  64. local cmd="$1"
  65. [ -n "$PROCD_DEBUG" ] && json_dump >&2
  66. ubus call service "$cmd" "$(json_dump)"
  67. json_cleanup
  68. }
  69. _procd_open_service() {
  70. local name="$1"
  71. local script="$2"
  72. _PROCD_SERVICE="$name"
  73. _PROCD_INSTANCE_SEQ=0
  74. json_init
  75. json_add_string name "$name"
  76. [ -n "$script" ] && json_add_string script "$script"
  77. json_add_object instances
  78. }
  79. _procd_close_service() {
  80. json_close_object
  81. _procd_open_trigger
  82. service_triggers
  83. _procd_close_trigger
  84. _procd_open_data
  85. service_data
  86. _procd_close_data
  87. _procd_ubus_call ${1:-set}
  88. }
  89. _procd_add_array_data() {
  90. while [ "$#" -gt 0 ]; do
  91. json_add_string "" "$1"
  92. shift
  93. done
  94. }
  95. _procd_add_array() {
  96. json_add_array "$1"
  97. shift
  98. _procd_add_array_data "$@"
  99. json_close_array
  100. }
  101. _procd_add_table_data() {
  102. while [ -n "$1" ]; do
  103. local var="${1%%=*}"
  104. local val="${1#*=}"
  105. [ "$1" = "$val" ] && val=
  106. json_add_string "$var" "$val"
  107. shift
  108. done
  109. }
  110. _procd_add_table() {
  111. json_add_object "$1"
  112. shift
  113. _procd_add_table_data "$@"
  114. json_close_object
  115. }
  116. _procd_open_instance() {
  117. local name="$1"; shift
  118. _PROCD_INSTANCE_SEQ="$(($_PROCD_INSTANCE_SEQ + 1))"
  119. name="${name:-instance$_PROCD_INSTANCE_SEQ}"
  120. json_add_object "$name"
  121. [ -n "$TRACE_SYSCALLS" ] && json_add_boolean trace "1"
  122. }
  123. _procd_open_trigger() {
  124. let '_procd_trigger_open = _procd_trigger_open + 1'
  125. [ "$_procd_trigger_open" -gt 1 ] && return
  126. json_add_array "triggers"
  127. }
  128. _procd_close_trigger() {
  129. let '_procd_trigger_open = _procd_trigger_open - 1'
  130. [ "$_procd_trigger_open" -lt 1 ] || return
  131. json_close_array
  132. }
  133. _procd_open_data() {
  134. let '_procd_data_open = _procd_data_open + 1'
  135. [ "$_procd_data_open" -gt 1 ] && return
  136. json_add_object "data"
  137. }
  138. _procd_close_data() {
  139. let '_procd_data_open = _procd_data_open - 1'
  140. [ "$_procd_data_open" -lt 1 ] || return
  141. json_close_object
  142. }
  143. _procd_open_validate() {
  144. json_select ..
  145. json_add_array "validate"
  146. }
  147. _procd_close_validate() {
  148. json_close_array
  149. json_select triggers
  150. }
  151. _procd_add_jail() {
  152. json_add_object "jail"
  153. json_add_string name "$1"
  154. shift
  155. for a in $@; do
  156. case $a in
  157. log) json_add_boolean "log" "1";;
  158. ubus) json_add_boolean "ubus" "1";;
  159. procfs) json_add_boolean "procfs" "1";;
  160. sysfs) json_add_boolean "sysfs" "1";;
  161. ronly) json_add_boolean "ronly" "1";;
  162. esac
  163. done
  164. json_add_object "mount"
  165. json_close_object
  166. json_close_object
  167. }
  168. _procd_add_jail_mount() {
  169. local _json_no_warning=1
  170. json_select "jail"
  171. [ $? = 0 ] || return
  172. json_select "mount"
  173. [ $? = 0 ] || {
  174. json_select ..
  175. return
  176. }
  177. for a in $@; do
  178. json_add_string "$a" "0"
  179. done
  180. json_select ..
  181. json_select ..
  182. }
  183. _procd_add_jail_mount_rw() {
  184. local _json_no_warning=1
  185. json_select "jail"
  186. [ $? = 0 ] || return
  187. json_select "mount"
  188. [ $? = 0 ] || {
  189. json_select ..
  190. return
  191. }
  192. for a in $@; do
  193. json_add_string "$a" "1"
  194. done
  195. json_select ..
  196. json_select ..
  197. }
  198. _procd_set_param() {
  199. local type="$1"; shift
  200. case "$type" in
  201. env|data|limits)
  202. _procd_add_table "$type" "$@"
  203. ;;
  204. command|netdev|file|respawn|watch)
  205. _procd_add_array "$type" "$@"
  206. ;;
  207. error)
  208. json_add_array "$type"
  209. json_add_string "" "$@"
  210. json_close_array
  211. ;;
  212. nice|term_timeout)
  213. json_add_int "$type" "$1"
  214. ;;
  215. reload_signal)
  216. json_add_int "$type" $(kill -l "$1")
  217. ;;
  218. pidfile|user|seccomp|capabilities)
  219. json_add_string "$type" "$1"
  220. ;;
  221. stdout|stderr|no_new_privs)
  222. json_add_boolean "$type" "$1"
  223. ;;
  224. esac
  225. }
  226. _procd_add_timeout() {
  227. [ "$PROCD_RELOAD_DELAY" -gt 0 ] && json_add_int "" "$PROCD_RELOAD_DELAY"
  228. return 0
  229. }
  230. _procd_add_interface_trigger() {
  231. json_add_array
  232. _procd_add_array_data "$1"
  233. shift
  234. json_add_array
  235. _procd_add_array_data "if"
  236. json_add_array
  237. _procd_add_array_data "eq" "interface" "$1"
  238. shift
  239. json_close_array
  240. json_add_array
  241. _procd_add_array_data "run_script" "$@"
  242. json_close_array
  243. json_close_array
  244. _procd_add_timeout
  245. json_close_array
  246. }
  247. _procd_add_reload_interface_trigger() {
  248. local script=$(readlink "$initscript")
  249. local name=$(basename ${script:-$initscript})
  250. _procd_open_trigger
  251. _procd_add_interface_trigger "interface.*" $1 /etc/init.d/$name reload
  252. _procd_close_trigger
  253. }
  254. _procd_add_config_trigger() {
  255. json_add_array
  256. _procd_add_array_data "$1"
  257. shift
  258. json_add_array
  259. _procd_add_array_data "if"
  260. json_add_array
  261. _procd_add_array_data "eq" "package" "$1"
  262. shift
  263. json_close_array
  264. json_add_array
  265. _procd_add_array_data "run_script" "$@"
  266. json_close_array
  267. json_close_array
  268. _procd_add_timeout
  269. json_close_array
  270. }
  271. _procd_add_raw_trigger() {
  272. json_add_array
  273. _procd_add_array_data "$1"
  274. shift
  275. local timeout=$1
  276. shift
  277. json_add_array
  278. json_add_array
  279. _procd_add_array_data "run_script" "$@"
  280. json_close_array
  281. json_close_array
  282. json_add_int "" "$timeout"
  283. json_close_array
  284. }
  285. _procd_add_reload_trigger() {
  286. local script=$(readlink "$initscript")
  287. local name=$(basename ${script:-$initscript})
  288. local file
  289. _procd_open_trigger
  290. for file in "$@"; do
  291. _procd_add_config_trigger "config.change" "$file" /etc/init.d/$name reload
  292. done
  293. _procd_close_trigger
  294. }
  295. _procd_add_validation() {
  296. _procd_open_validate
  297. $@
  298. _procd_close_validate
  299. }
  300. _procd_append_param() {
  301. local type="$1"; shift
  302. local _json_no_warning=1
  303. json_select "$type"
  304. [ $? = 0 ] || {
  305. _procd_set_param "$type" "$@"
  306. return
  307. }
  308. case "$type" in
  309. env|data|limits)
  310. _procd_add_table_data "$@"
  311. ;;
  312. command|netdev|file|respawn|watch)
  313. _procd_add_array_data "$@"
  314. ;;
  315. error)
  316. json_add_string "" "$@"
  317. ;;
  318. esac
  319. json_select ..
  320. }
  321. _procd_close_instance() {
  322. local respawn_vals
  323. _json_no_warning=1
  324. if json_select respawn ; then
  325. json_get_values respawn_vals
  326. if [ -z "$respawn_vals" ]; then
  327. local respawn_threshold=$(uci_get system.@service[0].respawn_threshold)
  328. local respawn_timeout=$(uci_get system.@service[0].respawn_timeout)
  329. local respawn_retry=$(uci_get system.@service[0].respawn_retry)
  330. _procd_add_array_data ${respawn_threshold:-3600} ${respawn_timeout:-5} ${respawn_retry:-5}
  331. fi
  332. json_select ..
  333. fi
  334. json_close_object
  335. }
  336. _procd_add_instance() {
  337. _procd_open_instance
  338. _procd_set_param command "$@"
  339. _procd_close_instance
  340. }
  341. _procd_kill() {
  342. local service="$1"
  343. local instance="$2"
  344. json_init
  345. [ -n "$service" ] && json_add_string name "$service"
  346. [ -n "$instance" ] && json_add_string instance "$instance"
  347. _procd_ubus_call delete
  348. }
  349. _procd_send_signal() {
  350. local service="$1"
  351. local instance="$2"
  352. local signal="$3"
  353. case "$signal" in
  354. [A-Z]*) signal="$(kill -l "$signal" 2>/dev/null)" || return 1;;
  355. esac
  356. json_init
  357. json_add_string name "$service"
  358. [ -n "$instance" -a "$instance" != "*" ] && json_add_string instance "$instance"
  359. [ -n "$signal" ] && json_add_int signal "$signal"
  360. _procd_ubus_call signal
  361. }
  362. procd_open_data() {
  363. local name="$1"
  364. json_set_namespace procd __procd_old_cb
  365. json_add_object data
  366. }
  367. procd_close_data() {
  368. json_close_object
  369. json_set_namespace $__procd_old_cb
  370. }
  371. _procd_set_config_changed() {
  372. local package="$1"
  373. json_init
  374. json_add_string type config.change
  375. json_add_object data
  376. json_add_string package "$package"
  377. json_close_object
  378. ubus call service event "$(json_dump)"
  379. }
  380. procd_add_mdns_service() {
  381. local service proto port
  382. service=$1; shift
  383. proto=$1; shift
  384. port=$1; shift
  385. json_add_object "${service}_$port"
  386. json_add_string "service" "_$service._$proto.local"
  387. json_add_int port "$port"
  388. [ -n "$1" ] && {
  389. json_add_array txt
  390. for txt in "$@"; do json_add_string "" "$txt"; done
  391. json_select ..
  392. }
  393. json_select ..
  394. }
  395. procd_add_mdns() {
  396. procd_open_data
  397. json_add_object "mdns"
  398. procd_add_mdns_service "$@"
  399. json_close_object
  400. procd_close_data
  401. }
  402. uci_validate_section()
  403. {
  404. local _package="$1"
  405. local _type="$2"
  406. local _name="$3"
  407. local _result
  408. local _error
  409. shift; shift; shift
  410. _result=`/sbin/validate_data "$_package" "$_type" "$_name" "$@" 2> /dev/null`
  411. _error=$?
  412. eval "$_result"
  413. [ "$_error" = "0" ] || `/sbin/validate_data "$_package" "$_type" "$_name" "$@" 1> /dev/null`
  414. return $_error
  415. }
  416. _procd_wrapper \
  417. procd_open_service \
  418. procd_close_service \
  419. procd_add_instance \
  420. procd_add_raw_trigger \
  421. procd_add_config_trigger \
  422. procd_add_interface_trigger \
  423. procd_add_reload_trigger \
  424. procd_add_reload_interface_trigger \
  425. procd_open_trigger \
  426. procd_close_trigger \
  427. procd_open_instance \
  428. procd_close_instance \
  429. procd_open_validate \
  430. procd_close_validate \
  431. procd_add_jail \
  432. procd_add_jail_mount \
  433. procd_add_jail_mount_rw \
  434. procd_set_param \
  435. procd_append_param \
  436. procd_add_validation \
  437. procd_set_config_changed \
  438. procd_kill \
  439. procd_send_signal