-- -- gcsd -- -- gcsd the generic command sequencer daemon can be used to serialize -- commands sent over various paralell communication channels to a -- single command output. It can be seen as a multiplexer for any -- kind of communication between a single resource and various clients -- which want to submit commands to it or query information from it. -- gcsd is written in C and Lua. The goal is to provide an easy to -- understand high level API based on Lua which can be used to -- implement the business logic of the so formed multiplexer daemon. -- -- -- Copyright (C) 2009-2010 Markus Grueneis -- Christian Pointner -- -- This file is part of gcsd. -- -- gcsd is free software: you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by -- the Free Software Foundation, either version 3 of the License, or -- any later version. -- -- gcsd is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- -- You should have received a copy of the GNU General Public License -- along with gcsd. If not, see . -- module_list = {} module_list.classes = {} local mt = {} function mt.__index(table, key) local value = rawget(table, key) if(value ~= nil) then return value end log.printf(log.DEBUG, "load module class: %s", key) local filename = defines.MODULES_PATH .. "/" .. key .. ".lua" local chunk, err = loadfile(filename) if (not chunk) then error("failed to load module: " .. err) end value = chunk() rawset(table, key, value) return value end setmetatable(module_list.classes, mt) module_list.inputs = {} module_list.output = nil function module_list:new_instance(class_name, config, runtype) class = self.classes[class_name] if(class.__num_inst) then if(class.properties.max_instances >= 1 and class.__num_inst >= class.properties.max_instances) then error("max instances of " .. class_name .. " reached"); end class.__num_inst = class.__num_inst + 1 else class.__num_inst = 1 end return class:new(config, runtype) end function module_list:init(opt) local class, config = self:parse_config(opt.cmd_out) if(not class) then return defines.KILL_DAEMON end if(self.classes[class].properties.type ~= defines.OUT_MODULE and self.classes[class].properties.type ~= defines.INOUT_MODULE) then log.printf(log.ERROR, "module class '%s' cannot be used as output module", class); return defines.KILL_DAEMON end log.printf(log.DEBUG, "output module class='%s'", class) for k, v in pairs(config) do log.printf(log.DEBUG, " config['%s'] = '%s'", k, v) end self.output = self:new_instance(class, config, defines.OUT_MODULE) for idx, input in ipairs(opt.cmd_ins) do local class, config = self:parse_config(input) if(not class) then return defines.KILL_DAEMON end if(self.classes[class].properties.type ~= defines.IN_MODULE and self.classes[class].properties.type ~= defines.INOUT_MODULE and self.classes[class].properties.type ~= defines.MISC_MODULE) then log.printf(log.ERROR, "module class '%s' cannot be used as input module", class); return defines.KILL_DAEMON end log.printf(log.DEBUG, "input[%d] module class='%s'", idx, class) for k, v in pairs(config) do log.printf(log.DEBUG, " config['%s'] = '%s'", k, v) end table.insert(self.inputs, self:new_instance(class, config, defines.IN_MODULE)) end return defines.OK end function module_list:parse_config(module_config) local class, config_string = string.match(module_config, "^([%w-_]+):(.+)$") if(not class or not config_string) then log.printf(log.ERROR, "module config format error: '%s'", module_config) return nil end local config = {} for k, v in string.gmatch(config_string .. ",", "([^,=]+)=([^,=]+),") do config[k] = v end return class, config end function module_list:unregister(module) if(module == self.output) then log.printf(log.WARNING, "output module can't be removed safely, closing daemon") return defines.KILL_DAEMON end for i, m in ipairs(self.inputs) do if(m == module) then log.printf(log.INFO, "removing input module: " .. module.name) module:cleanup() table.remove(self.inputs, i) break end end return defines.OK end function module_list:unregister_by_class(class) local free_list = {} if(self.output.class == class) then log.printf(log.WARNING, "output module can't be removed safely, closing daemon") return defines.KILL_DAEMON end for i, m in ipairs(self.inputs) do if(m.class == class) then table.insert(free_list, 1, i) end end for _, i in ipairs(free_list) do log.printf(log.INFO, "removing input module: " .. self.inputs[i].name) self.inputs[i]:cleanup() table.remove(self.inputs, i) end return defines.OK end function module_list:cleanup() for _, module in ipairs(self.inputs) do log.printf(log.INFO, "removing input module: " .. module.name) module:cleanup() end if(self.output ~= nil) then log.printf(log.INFO, "removing output module: " .. self.output.name) self.output:cleanup() end self.inputs = {} end