2 LuCI - Lua Development Framework
4 Copyright 2009 Steven Barth <steven@midlink.org>
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
10 http://www.apache.org/licenses/LICENSE-2.0
15 local nixio = require "nixio"
16 local table = require "table"
17 local uci = require "luci.model.uci"
18 local os = require "os"
19 local io = require "io"
21 local pairs, require, pcall, assert, type = pairs, require, pcall, assert, type
22 local ipairs, tonumber, collectgarbage = ipairs, tonumber, collectgarbage
32 local ifaddrs = nixio.getifaddrs()
35 state = uci.cursor_state()
40 local UCINAME = UCINAME
41 local SSTATE = "/tmp/.lucid_store"
48 local detach = cursor:get(UCINAME, "main", "daemonize")
50 local stat, code, msg = daemonize()
52 nixio.syslog("crit", "Unable to detach process: " .. msg .. "\n")
57 state:set(UCINAME, "main", "pid", nixio.getpid())
64 local pid = tonumber(state:get(UCINAME, "main", "pid"))
66 return nixio.kill(pid, nixio.const.SIGTERM)
72 local debug = tonumber((cursor:get(UCINAME, "main", "debug")))
74 nixio.openlog("lucid", "pid", "perror")
76 nixio.setlogmask("warning")
79 cursor:foreach(UCINAME, "daemon", function(config)
80 if config.enabled ~= "1" then
84 local key = config[".name"]
85 if not config.slave then
86 nixio.syslog("crit", "Daemon "..key.." is missing a slave\n")
89 nixio.syslog("info", "Initializing daemon " .. key)
92 state:revert(UCINAME, key)
94 local daemon, code, err = prepare_daemon(config)
96 state:set(UCINAME, key, "status", "started")
97 nixio.syslog("info", "Prepared daemon " .. key)
99 state:set(UCINAME, key, "status", "error")
100 state:set(UCINAME, key, "error", err)
101 nixio.syslog("err", "Failed to initialize daemon "..key..": "..
108 local pollint = tonumber((cursor:get(UCINAME, "main", "pollinterval")))
109 local threadlimit = tonumber(cursor:get(UCINAME, "main", "threadlimit"))
112 if not threadlimit or tcount < threadlimit then
113 local stat, code = nixio.poll(pollt, pollint)
115 if stat and stat > 0 then
116 for _, polle in ipairs(pollt) do
117 if polle.revents ~= 0 and polle.handler then
121 elseif stat == 0 then
122 ifaddrs = nixio.getifaddrs()
123 collectgarbage("collect")
127 for _, cb in ipairs(tickt) do
131 local pid, stat, code = nixio.wait(-1, "nohang")
132 while pid and pid > 0 do
134 if tpids[pid] and tpids[pid] ~= true then
135 tpids[pid](pid, stat, code)
137 pid, stat, code = nixio.wait(-1, "nohang")
142 function register_pollfd(polle)
143 pollt[#pollt+1] = polle
147 function unregister_pollfd(polle)
148 for k, v in ipairs(pollt) do
150 table.remove(pollt, k)
157 function close_pollfds()
158 for k, v in ipairs(pollt) do
159 if v.fd and v.fd.close then
165 function register_tick(cb)
170 function unregister_tick(cb)
171 for k, v in ipairs(tickt) do
173 table.remove(tickt, k)
180 function create_process(threadcb, waitcb)
181 local threadlimit = tonumber(cursor:get(UCINAME, "main", "threadlimit"))
182 if threadlimit and tcount >= threadlimit then
183 nixio.syslog("warning", "Unable to create thread: process limit reached")
186 local pid, code, err = nixio.fork()
187 if pid and pid ~= 0 then
191 local code = threadcb()
194 nixio.syslog("err", "Unable to fork(): " .. err)
196 return pid, code, err
199 function prepare_daemon(config)
200 nixio.syslog("info", "Preparing daemon " .. config[".name"])
201 local modname = cursor:get(UCINAME, config.slave)
203 return nil, -1, "invalid slave"
206 local stat, module = pcall(require, _NAME .. "." .. modname)
207 if not stat or not module.prepare_daemon then
208 return nil, -2, "slave type not supported"
211 config.slave = prepare_slave(config.slave)
213 return module.prepare_daemon(config, _M)
216 function prepare_slave(name)
217 local slave = slaves[name]
219 local config = cursor:get_all(UCINAME, name)
221 local stat, module = pcall(require, config and config.entrypoint)
223 slave = {module = module, config = config}
234 function get_interfaces()
238 function revoke_privileges(user, group)
239 if nixio.getuid() == 0 then
240 return nixio.setgid(group) and nixio.setuid(user)
244 function securestate()
245 local stat = nixio.fs.stat(SSTATE) or {}
246 local uid = nixio.getuid()
247 if stat.type ~= "dir" or (stat.modedec % 100) ~= 0 or stat.uid ~= uid then
248 nixio.fs.remover(SSTATE)
249 if not nixio.fs.mkdir(SSTATE, 700) then
250 local errno = nixio.errno()
251 nixio.syslog("err", "Integrity check on secure state failed!")
252 return nil, errno, nixio.perror(errno)
256 return uci.cursor(nil, SSTATE)
260 if nixio.getppid() == 1 then
264 local pid, code, msg = nixio.fork()
266 return nil, code, msg
274 local devnull = nixio.open("/dev/null", nixio.open_flags("rdwr"))
275 nixio.dup(devnull, nixio.stdin)
276 nixio.dup(devnull, nixio.stdout)
277 nixio.dup(devnull, nixio.stderr)