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")))
111 local stat, code = nixio.poll(pollt, pollint)
113 if stat and stat > 0 then
114 for _, polle in ipairs(pollt) do
115 if polle.revents ~= 0 and polle.handler then
119 elseif stat == 0 then
120 ifaddrs = nixio.getifaddrs()
121 collectgarbage("collect")
124 for _, cb in ipairs(tickt) do
128 local pid, stat, code = nixio.wait(-1, "nohang")
129 while pid and pid > 0 do
131 if tpids[pid] and tpids[pid] ~= true then
132 tpids[pid](pid, stat, code)
134 pid, stat, code = nixio.wait(-1, "nohang")
139 function register_pollfd(polle)
140 pollt[#pollt+1] = polle
144 function unregister_pollfd(polle)
145 for k, v in ipairs(pollt) do
147 table.remove(pollt, k)
154 function close_pollfds()
155 for k, v in ipairs(pollt) do
156 if v.fd and v.fd.close then
162 function register_tick(cb)
167 function unregister_tick(cb)
168 for k, v in ipairs(tickt) do
170 table.remove(tickt, k)
177 function create_process(threadcb, waitcb)
178 local threadlimit = tonumber(cursor:get(UCINAME, "main", "threadlimit"))
179 if threadlimit and tcount >= threadlimit then
180 nixio.syslog("warning", "Unable to create thread: process limit reached")
183 local pid, code, err = nixio.fork()
184 if pid and pid ~= 0 then
188 local code = threadcb()
191 nixio.syslog("err", "Unable to fork(): " .. err)
193 return pid, code, err
196 function prepare_daemon(config)
197 nixio.syslog("info", "Preparing daemon " .. config[".name"])
198 local modname = cursor:get(UCINAME, config.slave)
200 return nil, -1, "invalid slave"
203 local stat, module = pcall(require, _NAME .. "." .. modname)
204 if not stat or not module.prepare_daemon then
205 return nil, -2, "slave type not supported"
208 config.slave = prepare_slave(config.slave)
210 return module.prepare_daemon(config, _M)
213 function prepare_slave(name)
214 local slave = slaves[name]
216 local config = cursor:get_all(UCINAME, name)
218 local stat, module = pcall(require, config and config.entrypoint)
220 slave = {module = module, config = config}
231 function get_interfaces()
235 function revoke_privileges(user, group)
236 if nixio.getuid() == 0 then
237 return nixio.setgid(group) and nixio.setuid(user)
241 function securestate()
242 local stat = nixio.fs.stat(SSTATE) or {}
243 local uid = nixio.getuid()
244 if stat.type ~= "dir" or (stat.modedec % 100) ~= 0 or stat.uid ~= uid then
245 nixio.fs.remover(SSTATE)
246 if not nixio.fs.mkdir(SSTATE, 700) then
247 local errno = nixio.errno()
248 nixio.syslog("err", "Integrity check on secure state failed!")
249 return nil, errno, nixio.perror(errno)
253 return uci.cursor(nil, SSTATE)
257 if nixio.getppid() == 1 then
261 local pid, code, msg = nixio.fork()
263 return nil, code, msg
271 local devnull = nixio.open("/dev/null", nixio.open_flags("rdwr"))
272 nixio.dup(devnull, nixio.stdin)
273 nixio.dup(devnull, nixio.stdout)
274 nixio.dup(devnull, nixio.stderr)