trigger.lua 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. module("uci.trigger", package.seeall)
  2. require("posix")
  3. require("uci")
  4. local path = "/lib/config/trigger"
  5. local triggers = nil
  6. local tmp_cursor = nil
  7. function load_modules()
  8. if triggers ~= nil then
  9. return
  10. end
  11. triggers = {
  12. list = {},
  13. uci = {},
  14. active = {}
  15. }
  16. local modules = posix.glob(path .. "/*.lua")
  17. if modules == nil then
  18. return
  19. end
  20. local oldpath = package.path
  21. package.path = path .. "/?.lua"
  22. for i, v in ipairs(modules) do
  23. pcall(require(string.gsub(v, path .. "/(%w+)%.lua$", "%1")))
  24. end
  25. package.path = oldpath
  26. end
  27. function check_table(table, name)
  28. if table[name] == nil then
  29. table[name] = {}
  30. end
  31. return table[name]
  32. end
  33. function get_table_val(val, vtype)
  34. if type(val) == (vtype or "string") then
  35. return { val }
  36. elseif type(val) == "table" then
  37. return val
  38. end
  39. return nil
  40. end
  41. function get_name_list(name)
  42. return get_table_val(name or ".all")
  43. end
  44. function add_trigger_option(list, t)
  45. local name = get_name_list(t.option)
  46. for i, n in ipairs(name) do
  47. option = check_table(list, n)
  48. table.insert(option, t)
  49. end
  50. end
  51. function add_trigger_section(list, t)
  52. local name = get_name_list(t.section)
  53. for i, n in ipairs(name) do
  54. section = check_table(list, n)
  55. add_trigger_option(section, t)
  56. end
  57. end
  58. function check_insert_triggers(dest, list, tuple)
  59. if list == nil then
  60. return
  61. end
  62. for i, t in ipairs(list) do
  63. local add = true
  64. if type(t.check) == "function" then
  65. add = t.check(tuple)
  66. end
  67. if add then
  68. dest[t.id] = t
  69. end
  70. end
  71. end
  72. function find_section_triggers(tlist, pos, tuple)
  73. if pos == nil then
  74. return
  75. end
  76. check_insert_triggers(tlist, pos[".all"], tuple)
  77. if tuple.option then
  78. check_insert_triggers(tlist, pos[tuple.option], tuple)
  79. end
  80. end
  81. function check_recursion(name, seen)
  82. if seen == nil then
  83. seen = {}
  84. end
  85. if seen[name] then
  86. return nil
  87. end
  88. seen[name] = true
  89. return seen
  90. end
  91. function find_recursive_depends(list, name, seen)
  92. seen = check_recursion(name, seen)
  93. if not seen then
  94. return
  95. end
  96. local bt = get_table_val(triggers.list[name].belongs_to) or {}
  97. for i, n in ipairs(bt) do
  98. table.insert(list, n)
  99. find_recursive_depends(list, n, seen)
  100. end
  101. end
  102. function check_trigger_depth(list, name)
  103. if name == nil then
  104. return
  105. end
  106. local n = list[name]
  107. if n == nil then
  108. return
  109. end
  110. list[name] = nil
  111. return check_trigger_depth(list, n)
  112. end
  113. function find_triggers(tuple)
  114. local pos = triggers.uci[tuple.package]
  115. if pos == nil then
  116. return {}
  117. end
  118. local tlist = {}
  119. find_section_triggers(tlist, pos[".all"], tuple)
  120. find_section_triggers(tlist, pos[tuple.section[".type"]], tuple)
  121. for n, t in pairs(tlist) do
  122. local dep = {}
  123. find_recursive_depends(dep, t.id)
  124. for i, depname in ipairs(dep) do
  125. check_trigger_depth(tlist, depname)
  126. end
  127. end
  128. local nlist = {}
  129. for n, t in pairs(tlist) do
  130. if t then
  131. table.insert(nlist, t)
  132. end
  133. end
  134. return nlist
  135. end
  136. function reset_state()
  137. assert(io.open("/var/run/uci_trigger", "w")):close()
  138. if tctx then
  139. tctx:unload("uci_trigger")
  140. end
  141. end
  142. function load_state()
  143. -- make sure the config file exists before we attempt to load it
  144. -- uci doesn't like loading nonexistent config files
  145. local f = assert(io.open("/var/run/uci_trigger", "a")):close()
  146. load_modules()
  147. triggers.active = {}
  148. if tctx then
  149. tctx:unload("uci_trigger")
  150. else
  151. tctx = uci.cursor()
  152. end
  153. assert(tctx:load("/var/run/uci_trigger"))
  154. tctx:foreach("uci_trigger", "trigger",
  155. function(section)
  156. trigger = triggers.list[section[".name"]]
  157. if trigger == nil then
  158. return
  159. end
  160. active = {}
  161. triggers.active[trigger.id] = active
  162. local s = get_table_val(section["sections"]) or {}
  163. for i, v in ipairs(s) do
  164. active[v] = true
  165. end
  166. end
  167. )
  168. end
  169. function get_names(list)
  170. local slist = {}
  171. for name, val in pairs(list) do
  172. if val then
  173. table.insert(slist, name)
  174. end
  175. end
  176. return slist
  177. end
  178. function check_cancel(name, seen)
  179. local t = triggers.list[name]
  180. local dep = get_table_val(t.belongs_to)
  181. seen = check_recursion(name, seen)
  182. if not t or not dep or not seen then
  183. return false
  184. end
  185. for i, v in ipairs(dep) do
  186. -- only cancel triggers for all sections
  187. -- if both the current and the parent trigger
  188. -- are per-section
  189. local section_only = false
  190. if t.section_only then
  191. local tdep = triggers.list[v]
  192. if tdep then
  193. section_only = tdep.section_only
  194. end
  195. end
  196. if check_cancel(v, seen) then
  197. return true
  198. end
  199. if triggers.active[v] then
  200. if section_only then
  201. for n, active in pairs(triggers.active[v]) do
  202. triggers.active[name][n] = false
  203. end
  204. else
  205. return true
  206. end
  207. end
  208. end
  209. return false
  210. end
  211. -- trigger api functions
  212. function add(ts)
  213. for i,t in ipairs(ts) do
  214. triggers.list[t.id] = t
  215. match = {}
  216. if t.package then
  217. local package = check_table(triggers.uci, t.package)
  218. add_trigger_section(package, t)
  219. triggers.list[t.id] = t
  220. end
  221. end
  222. end
  223. function save_trigger(name)
  224. if triggers.active[name] then
  225. local slist = get_names(triggers.active[name])
  226. if #slist > 0 then
  227. tctx:set("uci_trigger", name, "sections", slist)
  228. end
  229. else
  230. tctx:delete("uci_trigger", name)
  231. end
  232. end
  233. function set(data, cursor)
  234. assert(data ~= nil)
  235. if cursor == nil then
  236. cursor = tmp_cursor or uci.cursor()
  237. tmp_cursor = uci.cursor
  238. end
  239. local tuple = {
  240. package = data[1],
  241. section = data[2],
  242. option = data[3],
  243. value = data[4]
  244. }
  245. assert(cursor:load(tuple.package))
  246. load_state()
  247. local section = cursor:get_all(tuple.package, tuple.section)
  248. if (section == nil) then
  249. if option ~= nil then
  250. return
  251. end
  252. section = {
  253. [".type"] = value
  254. }
  255. if tuple.section == nil then
  256. tuple.section = ""
  257. section[".anonymous"] = true
  258. end
  259. section[".name"] = tuple.section
  260. end
  261. tuple.section = section
  262. local ts = find_triggers(tuple)
  263. for i, t in ipairs(ts) do
  264. local active = triggers.active[t.id]
  265. if not active then
  266. active = {}
  267. triggers.active[t.id] = active
  268. tctx:set("uci_trigger", t.id, "trigger")
  269. end
  270. if section[".name"] then
  271. active[section[".name"]] = true
  272. end
  273. save_trigger(t.id)
  274. end
  275. tctx:save("uci_trigger")
  276. end
  277. function get_description(trigger, sections)
  278. if not trigger.title then
  279. return trigger.id
  280. end
  281. local desc = trigger.title
  282. if trigger.section_only and sections and #sections > 0 then
  283. desc = desc .. " (" .. table.concat(sections, ", ") .. ")"
  284. end
  285. return desc
  286. end
  287. function get_active()
  288. local slist = {}
  289. if triggers == nil then
  290. load_state()
  291. end
  292. for name, val in pairs(triggers.active) do
  293. if val and not check_cancel(name) then
  294. local sections = {}
  295. for name, active in pairs(triggers.active[name]) do
  296. if active then
  297. table.insert(sections, name)
  298. end
  299. end
  300. table.insert(slist, { triggers.list[name], sections })
  301. end
  302. end
  303. return slist
  304. end
  305. function set_active(trigger, sections)
  306. if triggers == nil then
  307. load_state()
  308. end
  309. if not triggers.list[trigger] then
  310. return
  311. end
  312. if triggers.active[trigger] == nil then
  313. tctx:set("uci_trigger", trigger, "trigger")
  314. triggers.active[trigger] = {}
  315. end
  316. local active = triggers.active[trigger]
  317. if triggers.list[trigger].section_only or sections ~= nil then
  318. for i, t in ipairs(sections) do
  319. triggers.active[trigger][t] = true
  320. end
  321. end
  322. save_trigger(trigger)
  323. tctx:save("uci_trigger")
  324. end
  325. function clear_active(trigger, sections)
  326. if triggers == nil then
  327. load_state()
  328. end
  329. if triggers.list[trigger] == nil or triggers.active[trigger] == nil then
  330. return
  331. end
  332. local active = triggers.active[trigger]
  333. if not triggers.list[trigger].section_only or sections == nil then
  334. triggers.active[trigger] = nil
  335. else
  336. for i, t in ipairs(sections) do
  337. triggers.active[trigger][t] = false
  338. end
  339. end
  340. save_trigger(trigger)
  341. tctx:save("uci_trigger")
  342. end
  343. function run(ts)
  344. if ts == nil then
  345. ts = get_active()
  346. end
  347. for i, t in ipairs(ts) do
  348. local trigger = t[1]
  349. local sections = t[2]
  350. local actions = get_table_val(trigger.action, "function") or {}
  351. for ai, a in ipairs(actions) do
  352. if not trigger.section_only then
  353. sections = { "" }
  354. end
  355. for si, s in ipairs(sections) do
  356. if a(s) then
  357. tctx:delete("uci_trigger", trigger.id)
  358. tctx:save("uci_trigger")
  359. end
  360. end
  361. end
  362. end
  363. end
  364. -- helper functions
  365. function system_command(arg)
  366. local cmd = arg
  367. return function(arg)
  368. return os.execute(cmd:format(arg)) == 0
  369. end
  370. end
  371. function service_restart(arg)
  372. return system_command("/etc/init.d/" .. arg .. " restart")
  373. end