1
0

uci.lua 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. -- Copyright 2008 Steven Barth <steven@midlink.org>
  2. -- Licensed to the public under the Apache License 2.0.
  3. local os = require "os"
  4. local util = require "luci.util"
  5. local table = require "table"
  6. local setmetatable, rawget, rawset = setmetatable, rawget, rawset
  7. local require, getmetatable, assert = require, getmetatable, assert
  8. local error, pairs, ipairs, select = error, pairs, ipairs, select
  9. local type, tostring, tonumber, unpack = type, tostring, tonumber, unpack
  10. -- The typical workflow for UCI is: Get a cursor instance from the
  11. -- cursor factory, modify data (via Cursor.add, Cursor.delete, etc.),
  12. -- save the changes to the staging area via Cursor.save and finally
  13. -- Cursor.commit the data to the actual config files.
  14. -- LuCI then needs to Cursor.apply the changes so daemons etc. are
  15. -- reloaded.
  16. module "luci.model.uci"
  17. local ERRSTR = {
  18. "Invalid command",
  19. "Invalid argument",
  20. "Method not found",
  21. "Entry not found",
  22. "No data",
  23. "Permission denied",
  24. "Timeout",
  25. "Not supported",
  26. "Unknown error",
  27. "Connection failed"
  28. }
  29. local session_id = nil
  30. local function call(cmd, args)
  31. if type(args) == "table" and session_id then
  32. args.ubus_rpc_session = session_id
  33. end
  34. return util.ubus("uci", cmd, args)
  35. end
  36. function cursor()
  37. return _M
  38. end
  39. function cursor_state()
  40. return _M
  41. end
  42. function substate(self)
  43. return self
  44. end
  45. function get_confdir(self)
  46. return "/etc/config"
  47. end
  48. function get_savedir(self)
  49. return "/tmp/.uci"
  50. end
  51. function get_session_id(self)
  52. return session_id
  53. end
  54. function set_confdir(self, directory)
  55. return false
  56. end
  57. function set_savedir(self, directory)
  58. return false
  59. end
  60. function set_session_id(self, id)
  61. session_id = id
  62. return true
  63. end
  64. function load(self, config)
  65. return true
  66. end
  67. function save(self, config)
  68. return true
  69. end
  70. function unload(self, config)
  71. return true
  72. end
  73. function changes(self, config)
  74. local rv, err = call("changes", { config = config })
  75. if type(rv) == "table" and type(rv.changes) == "table" then
  76. return rv.changes
  77. elseif err then
  78. return nil, ERRSTR[err]
  79. else
  80. return { }
  81. end
  82. end
  83. function revert(self, config)
  84. local _, err = call("revert", { config = config })
  85. return (err == nil), ERRSTR[err]
  86. end
  87. function commit(self, config)
  88. local _, err = call("commit", { config = config })
  89. return (err == nil), ERRSTR[err]
  90. end
  91. function apply(self, rollback)
  92. local _, err
  93. if rollback then
  94. local sys = require "luci.sys"
  95. local conf = require "luci.config"
  96. local timeout = tonumber(conf and conf.apply and conf.apply.rollback or 30) or 0
  97. _, err = call("apply", {
  98. timeout = (timeout > 30) and timeout or 30,
  99. rollback = true
  100. })
  101. if not err then
  102. local now = os.time()
  103. local token = sys.uniqueid(16)
  104. util.ubus("session", "set", {
  105. ubus_rpc_session = "00000000000000000000000000000000",
  106. values = {
  107. rollback = {
  108. token = token,
  109. session = session_id,
  110. timeout = now + timeout
  111. }
  112. }
  113. })
  114. return token
  115. end
  116. else
  117. _, err = call("changes", {})
  118. if not err then
  119. if type(_) == "table" and type(_.changes) == "table" then
  120. local k, v
  121. for k, v in pairs(_.changes) do
  122. _, err = call("commit", { config = k })
  123. if err then
  124. break
  125. end
  126. end
  127. end
  128. end
  129. if not err then
  130. _, err = call("apply", { rollback = false })
  131. end
  132. end
  133. return (err == nil), ERRSTR[err]
  134. end
  135. function confirm(self, token)
  136. local is_pending, time_remaining, rollback_sid, rollback_token = self:rollback_pending()
  137. if is_pending then
  138. if token ~= rollback_token then
  139. return false, "Permission denied"
  140. end
  141. local _, err = util.ubus("uci", "confirm", {
  142. ubus_rpc_session = rollback_sid
  143. })
  144. if not err then
  145. util.ubus("session", "set", {
  146. ubus_rpc_session = "00000000000000000000000000000000",
  147. values = { rollback = {} }
  148. })
  149. end
  150. return (err == nil), ERRSTR[err]
  151. end
  152. return false, "No data"
  153. end
  154. function rollback(self)
  155. local is_pending, time_remaining, rollback_sid = self:rollback_pending()
  156. if is_pending then
  157. local _, err = util.ubus("uci", "rollback", {
  158. ubus_rpc_session = rollback_sid
  159. })
  160. if not err then
  161. util.ubus("session", "set", {
  162. ubus_rpc_session = "00000000000000000000000000000000",
  163. values = { rollback = {} }
  164. })
  165. end
  166. return (err == nil), ERRSTR[err]
  167. end
  168. return false, "No data"
  169. end
  170. function rollback_pending(self)
  171. local rv, err = util.ubus("session", "get", {
  172. ubus_rpc_session = "00000000000000000000000000000000",
  173. keys = { "rollback" }
  174. })
  175. local now = os.time()
  176. if type(rv) == "table" and
  177. type(rv.values) == "table" and
  178. type(rv.values.rollback) == "table" and
  179. type(rv.values.rollback.token) == "string" and
  180. type(rv.values.rollback.session) == "string" and
  181. type(rv.values.rollback.timeout) == "number" and
  182. rv.values.rollback.timeout > now
  183. then
  184. return true,
  185. rv.values.rollback.timeout - now,
  186. rv.values.rollback.session,
  187. rv.values.rollback.token
  188. end
  189. return false, ERRSTR[err]
  190. end
  191. function foreach(self, config, stype, callback)
  192. if type(callback) == "function" then
  193. local rv, err = call("get", {
  194. config = config,
  195. type = stype
  196. })
  197. if type(rv) == "table" and type(rv.values) == "table" then
  198. local sections = { }
  199. local res = false
  200. local index = 1
  201. local _, section
  202. for _, section in pairs(rv.values) do
  203. section[".index"] = section[".index"] or index
  204. sections[index] = section
  205. index = index + 1
  206. end
  207. table.sort(sections, function(a, b)
  208. return a[".index"] < b[".index"]
  209. end)
  210. for _, section in ipairs(sections) do
  211. local continue = callback(section)
  212. res = true
  213. if continue == false then
  214. break
  215. end
  216. end
  217. return res
  218. else
  219. return false, ERRSTR[err] or "No data"
  220. end
  221. else
  222. return false, "Invalid argument"
  223. end
  224. end
  225. local function _get(self, operation, config, section, option)
  226. if section == nil then
  227. return nil
  228. elseif type(option) == "string" and option:byte(1) ~= 46 then
  229. local rv, err = call(operation, {
  230. config = config,
  231. section = section,
  232. option = option
  233. })
  234. if type(rv) == "table" then
  235. return rv.value or nil
  236. elseif err then
  237. return false, ERRSTR[err]
  238. else
  239. return nil
  240. end
  241. elseif option == nil then
  242. local values = self:get_all(config, section)
  243. if values then
  244. return values[".type"], values[".name"]
  245. else
  246. return nil
  247. end
  248. else
  249. return false, "Invalid argument"
  250. end
  251. end
  252. function get(self, ...)
  253. return _get(self, "get", ...)
  254. end
  255. function get_state(self, ...)
  256. return _get(self, "state", ...)
  257. end
  258. function get_all(self, config, section)
  259. local rv, err = call("get", {
  260. config = config,
  261. section = section
  262. })
  263. if type(rv) == "table" and type(rv.values) == "table" then
  264. return rv.values
  265. elseif err then
  266. return false, ERRSTR[err]
  267. else
  268. return nil
  269. end
  270. end
  271. function get_bool(self, ...)
  272. local val = self:get(...)
  273. return (val == "1" or val == "true" or val == "yes" or val == "on")
  274. end
  275. function get_first(self, config, stype, option, default)
  276. local rv = default
  277. self:foreach(config, stype, function(s)
  278. local val = not option and s[".name"] or s[option]
  279. if type(default) == "number" then
  280. val = tonumber(val)
  281. elseif type(default) == "boolean" then
  282. val = (val == "1" or val == "true" or
  283. val == "yes" or val == "on")
  284. end
  285. if val ~= nil then
  286. rv = val
  287. return false
  288. end
  289. end)
  290. return rv
  291. end
  292. function get_list(self, config, section, option)
  293. if config and section and option then
  294. local val = self:get(config, section, option)
  295. return (type(val) == "table" and val or { val })
  296. end
  297. return { }
  298. end
  299. function section(self, config, stype, name, values)
  300. local rv, err = call("add", {
  301. config = config,
  302. type = stype,
  303. name = name,
  304. values = values
  305. })
  306. if type(rv) == "table" then
  307. return rv.section
  308. elseif err then
  309. return false, ERRSTR[err]
  310. else
  311. return nil
  312. end
  313. end
  314. function add(self, config, stype)
  315. return self:section(config, stype)
  316. end
  317. function set(self, config, section, option, ...)
  318. if select('#', ...) == 0 then
  319. local sname, err = self:section(config, option, section)
  320. return (not not sname), err
  321. else
  322. local _, err = call("set", {
  323. config = config,
  324. section = section,
  325. values = { [option] = select(1, ...) }
  326. })
  327. return (err == nil), ERRSTR[err]
  328. end
  329. end
  330. function set_list(self, config, section, option, value)
  331. if section == nil or option == nil then
  332. return false
  333. elseif value == nil or (type(value) == "table" and #value == 0) then
  334. return self:delete(config, section, option)
  335. elseif type(value) == "table" then
  336. return self:set(config, section, option, value)
  337. else
  338. return self:set(config, section, option, { value })
  339. end
  340. end
  341. function tset(self, config, section, values)
  342. local _, err = call("set", {
  343. config = config,
  344. section = section,
  345. values = values
  346. })
  347. return (err == nil), ERRSTR[err]
  348. end
  349. function reorder(self, config, section, index)
  350. local sections
  351. if type(section) == "string" and type(index) == "number" then
  352. local pos = 0
  353. sections = { }
  354. self:foreach(config, nil, function(s)
  355. if pos == index then
  356. pos = pos + 1
  357. end
  358. if s[".name"] ~= section then
  359. pos = pos + 1
  360. sections[pos] = s[".name"]
  361. else
  362. sections[index + 1] = section
  363. end
  364. end)
  365. elseif type(section) == "table" then
  366. sections = section
  367. else
  368. return false, "Invalid argument"
  369. end
  370. local _, err = call("order", {
  371. config = config,
  372. sections = sections
  373. })
  374. return (err == nil), ERRSTR[err]
  375. end
  376. function delete(self, config, section, option)
  377. local _, err = call("delete", {
  378. config = config,
  379. section = section,
  380. option = option
  381. })
  382. return (err == nil), ERRSTR[err]
  383. end
  384. function delete_all(self, config, stype, comparator)
  385. local _, err
  386. if type(comparator) == "table" then
  387. _, err = call("delete", {
  388. config = config,
  389. type = stype,
  390. match = comparator
  391. })
  392. elseif type(comparator) == "function" then
  393. local rv = call("get", {
  394. config = config,
  395. type = stype
  396. })
  397. if type(rv) == "table" and type(rv.values) == "table" then
  398. local sname, section
  399. for sname, section in pairs(rv.values) do
  400. if comparator(section) then
  401. _, err = call("delete", {
  402. config = config,
  403. section = sname
  404. })
  405. end
  406. end
  407. end
  408. elseif comparator == nil then
  409. _, err = call("delete", {
  410. config = config,
  411. type = stype
  412. })
  413. else
  414. return false, "Invalid argument"
  415. end
  416. return (err == nil), ERRSTR[err]
  417. end