summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Pointner <equinox@spreadspace.org>2010-11-08 22:50:22 +0000
committerChristian Pointner <equinox@spreadspace.org>2010-11-08 22:50:22 +0000
commitced802b0557c9b3ac01e2b463b8538922800bd62 (patch)
treefc91627862f939e8ecdd3cd072cbfddf92a39740
parentdummy module cleanup (diff)
added client handling
debug shell now fully functional git-svn-id: https://svn.spreadspace.org/gcsd/trunk@12 ac14a137-c7f1-4531-abe0-07747231d213
-rw-r--r--src/Makefile3
-rw-r--r--src/client_list.lua67
-rw-r--r--src/main_loop.lua35
-rw-r--r--src/modules/debug_shell.lua119
-rw-r--r--src/modules/dummy.lua37
5 files changed, 188 insertions, 73 deletions
diff --git a/src/Makefile b/src/Makefile
index f6c8fee..bd29b35 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -51,7 +51,8 @@ C_SRCS := $(C_OBJS:%.o=%.c)
#MODULES := dummy debug_shell
#MODULE_SRC := $(MODULES:%=modules/%.lua)
-LUA_SRCS := main_loop.lua
+LUA_SRCS := client_list.lua \
+ main_loop.lua
LUA_BYTECODE := $(EXECUTABLE).lc
LUA_BYTECODE_OBJ := $(EXECUTABLE)_lua_bytecode.o
diff --git a/src/client_list.lua b/src/client_list.lua
new file mode 100644
index 0000000..40996a2
--- /dev/null
+++ b/src/client_list.lua
@@ -0,0 +1,67 @@
+--
+-- 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 <gimpf@spreadspace.org>
+-- Christian Pointner <equinox@spreadspace.org>
+--
+-- 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 <http://www.gnu.org/licenses/>.
+--
+
+client_list = {}
+
+client_list.clients = {}
+
+function client_list:register(client)
+ log.printf(log.INFO, "adding client")
+ table.insert(self.clients, client)
+end
+
+function client_list:unregister(client)
+ for i, c in ipairs(self.clients) do
+ if(c == client) then
+ log.printf(log.INFO, "removing client")
+ client:cleanup()
+ table.remove(self.clients, i)
+ break
+ end
+ end
+end
+
+function client_list:unregister_by_module(module)
+ local free_list = {}
+
+ for i, c in ipairs(self.clients) do
+ if(c.module_instance == module) then
+ table.insert(free_list, 1, i)
+ end
+ end
+
+ for _, i in ipairs(free_list) do
+ log.printf(log.INFO, "removing client")
+ self.clients[i]:cleanup()
+ table.remove(self.clients, i)
+ end
+end \ No newline at end of file
diff --git a/src/main_loop.lua b/src/main_loop.lua
index 68924df..b293ee1 100644
--- a/src/main_loop.lua
+++ b/src/main_loop.lua
@@ -54,9 +54,9 @@ function main_loop(opt)
package.path = old_path
- table.insert(modules, dummy.new({}))
+ table.insert(modules, dummy:new({}))
if(opt.debug) then
- table.insert(modules, debug_shell.new({[host]="127.0.0.1", [port]="9000"}))
+ table.insert(modules, debug_shell:new({["host"]="127.0.0.1", ["port"]="9000"}))
end
--------
@@ -65,19 +65,27 @@ function main_loop(opt)
local readables = { sig }
for _, module in ipairs(modules) do
- for _, fd in ipairs(module.get_read_handles()) do
+ for _, fd in ipairs(module:get_read_handles()) do
+ table.insert(readables, fd)
+ end
+ end
+ for _, client in ipairs(client_list.clients) do
+ for _, fd in ipairs(client:get_read_handles()) do
table.insert(readables, fd)
end
end
--- TODO: add read handles of all clients
local writeables = { }
for _, module in ipairs(modules) do
- for _, fd in ipairs(module.get_write_handles()) do
+ for _, fd in ipairs(module:get_write_handles()) do
+ table.insert(writeables, fd)
+ end
+ end
+ for _, client in ipairs(client_list.clients) do
+ for _, fd in ipairs(client:get_write_handles()) do
table.insert(writeables, fd)
end
end
--- TODO: add write handles of all clients
local readable, writeable, err = socket.select(readables, writeables)
if(err) then
@@ -89,7 +97,7 @@ function main_loop(opt)
return_value = signal.handle()
if(return_value == 1) then break end
else
- local ret = input.read()
+ local ret = input:read()
if(ret == defines.KILL_DAEMON) then
return_value = 2
break
@@ -97,16 +105,16 @@ function main_loop(opt)
-- TODO: remove all modules of same type as this and
-- unload module
elseif(ret == defines.KILL_MODULE) then
- -- TODO: remove module
+ input.client_instance.module_instance:cleanup()
elseif(ret == defines.KILL_CLIENT) then
- -- TODO: remove client from list
+ input.client_instance:cleanup()
end
end
end
for _, output in ipairs(writeable) do
- ret = output.write()
+ ret = output:write()
if(ret ~= defines.OK) then
- -- TODO: remove client from list
+ output.client_instance:cleanup()
end
end
end
@@ -114,7 +122,10 @@ function main_loop(opt)
if(return_value == 2) then return_value = 0 end
- -- TODO: cleanup clients and modules
+ for _, module in ipairs(modules) do
+ module:cleanup()
+ end
+
signal.stop()
return return_value
end
diff --git a/src/modules/debug_shell.lua b/src/modules/debug_shell.lua
index 1e48d9c..619ef72 100644
--- a/src/modules/debug_shell.lua
+++ b/src/modules/debug_shell.lua
@@ -34,75 +34,97 @@ local socket = require("socket")
local defines = require("defines")
local debug_shell = {}
-debug_shell.properties = { input=true, output=true, max_instances=1 }
+debug_shell.properties = { input=true, output=true, max_instances=1, module_type_name="debug_shell" }
+debug_shell.next_id = 0
function debug_shell:new(config)
+ local inst = {}
+ if(config.name == nil or config.name == "") then
+ inst.name = self.properties.module_type_name .. self.next_id
+ self.next_id = self.next_id + 1
+ else
+ inst.name = config.name
+ end
+
local ip, err = socket.dns.toip(config.host)
if(ip == nil) then
- log.printf(log.ERROR, "debug shell: can't resolve %s: %s", config.host, err)
+ log.printf(log.ERROR, inst.name .. ": can't resolve %s: %s", config.host, err)
return nil
end
- local server_sock, err = socket.tcp()
- if(server_sock == nil) then
- log.printf(log.ERROR, "debug shell: can't create tcp socket")
+ local server_sock_raw, err = socket.tcp()
+ if(server_sock_raw == nil) then
+ log.printf(log.ERROR, inst.name .. ": can't create tcp socket")
return nil
end
+
+ server_sock_raw:setoption('reuseaddr', true);
- server_sock:setoption('reuseaddr', true);
-
- local ret, err = server_sock:bind(ip, config.port)
+ local ret, err = server_sock_raw:bind(ip, config.port)
if(ret == nil) then
- log.printf(log.ERROR, "debug shell: bind(%s,%s) failed: %s", ip, config.port, err)
+ log.printf(log.ERROR, inst.name .. ": bind(%s,%s) failed: %s", ip, config.port, err)
return nil
end
- local ret, err = server_sock:listen()
+ local ret, err = server_sock_raw:listen()
if(ret == nil) then
- log.printf(log.ERROR, "debug shell: listen() failed: %s", err)
+ log.printf(log.ERROR, inst.name .. ": listen() failed: %s", err)
return nil
end
- server_sock:settimeout(0);
+ server_sock_raw:settimeout(0)
+
+ local server_sock = {}
+ server_sock.sock = server_sock_raw
+ function server_sock:getfd() return self.sock:getfd() end
+ function server_sock:dirty() return self.sock:dirty() end
function server_sock:read()
- local client_sock, err = server_sock:accept()
- if(newclient == nil) then
- log.printf(log.ERROR, "debug shell: accept() failed: %s", err)
+ local client_sock_raw, err = self.sock:accept()
+ if(client_sock_raw == nil) then
+ log.printf(log.ERROR, inst.name .. ": accept() failed: %s", err)
end
- local ip, port = client_sock:getpeername();
- log.printf(log.INFO, "debug shell: connection from %s:%s accepted", ip, port)
- client_sock:settimeout(0);
- client_sock:setoption('tcp-nodelay', true);
+ local ip, port = client_sock_raw:getpeername();
+ log.printf(log.INFO, inst.name .. ": connection from %s:%s accepted", ip, port)
+ client_sock_raw:settimeout(0);
+ client_sock_raw:setoption('tcp-nodelay', true);
+
+ local client_sock = {}
+ client_sock.sock = client_sock_raw
+ function client_sock:getfd() return self.sock:getfd() end
+ function client_sock:dirty() return self.sock:dirty() end
+ client_sock.client_instance = nil
client_sock.in_buffer = ""
client_sock.out_buffer = ""
function client_sock:read()
local ret = 0
- local data, err, partial = self.receive('*l')
+ local data, err, partial = self.sock:receive('*l')
if(data == nil) then
if(err == 'closed') then
- log.printf(log.INFO, "debug shell: connection closed by peer")
- -- TODO: remove client
+ log.printf(log.INFO, inst.name .. ": connection closed by peer")
+ ret = defines.KILL_CLIENT
elseif(err == 'timeout') then
self.in_buffer = self.in_buffer .. partial
else
- log.printf(log.INFO, "debug shell: connection error: %s", err)
- -- TODO: remove client
+ log.printf(log.INFO, inst.name .. ": connection error: %s", err)
+ ret = defines.KILL_CLIENT
end
else
self.in_buffer = self.in_buffer .. data
ret = debug_shell:exec_cmd(self)
if(ret == 1) then
- -- TODO: remove client
- ret = 0
+ ret = defines.KILL_CLIENT
end
self.in_buffer = ""
end
+ return ret
end
function client_sock:write()
- self.send(self.out_buffer)
+ self.sock:send(self.out_buffer)
self.out_buffer = ""
+ return defines.OK
end
local client = {}
+ client.module_instance = nil
client.sock = client_sock
function client:process_response(response)
self.sock.out_buffer = self.sock.out_buffer .. response
@@ -119,37 +141,38 @@ function debug_shell:new(config)
end
end
function client:cleanup()
- self.sock.close()
+ self.sock.sock:close()
end
-
- -- TODO: register new client
-
+ client_sock.client_instance = client
+ client.module_instance = inst
+
+ client_list:register(client)
return defines.OK
end
function server_sock:write() return defines.OK end
- log.printf(log.WARNING, "debug shell: listening on %s:%s (this is a huge security risk)", ip, config.port);
+ log.printf(log.WARNING, inst.name .. ": listening on %s:%s (this is a huge security risk)", ip, config.port);
- local inst = {
- cleanup = function()
- -- TODO: close all clients !!!
- server_sock:close()
- end,
- get_read_handles = function()
- return { server_sock }
- end,
- get_write_handles = function()
- return {}
- end
- }
- metatable(inst).__gc = inst.cleanup()
+ function inst:cleanup()
+ log.printf(log.INFO, inst.name .. ": cleanup called");
+ client_list:unregister_by_module(self)
+ server_sock.sock:close()
+ end
+ function inst:get_read_handles()
+ return { server_sock }
+ end
+ function inst:get_write_handles()
+ return {}
+ end
+ setmetatable(inst, {})
+ getmetatable(inst).__gc = function() inst:cleanup() end
return inst
end
function debug_shell:exec_cmd(socket)
log.printf(log.DEBUG, "debug shell: received string: '%s'", socket.in_buffer)
- local ret = 0
+ local ret = defines.OK
local luacode = string.match(socket.in_buffer, "^!(.*)$");
if(luacode and luacode ~= "") then
local chunk = loadstring(luacode)
@@ -174,11 +197,11 @@ function debug_shell:exec_cmd(socket)
if(cmd == 'quit') then
if(sep and sep ~= "") then socket.out_buffer = socket.out_buffer .. "unknown command\n" end
log.printf(log.INFO, "debug shell: quitting debug session")
- ret = 1
+ ret = defines.KILL_CLIENT
elseif(cmd == 'terminate') then
if(sep and sep ~= "") then socket.out_buffer = socket.out_buffer .. "unknown command\n" end
log.printf(log.NOTICE, "debug shell: terminate command received")
- ret = 2
+ ret = defines.KILL_DAEMON
elseif(cmd == 'help') then
if(sep and sep ~= "") then socket.out_buffer = socket.out_buffer .. "unknown command\n" end
socket.out_buffer = socket.out_buffer .. "!<lua code> execute lua code\n" ..
diff --git a/src/modules/dummy.lua b/src/modules/dummy.lua
index 291378b..e5a5a00 100644
--- a/src/modules/dummy.lua
+++ b/src/modules/dummy.lua
@@ -31,32 +31,45 @@
--
local dummy = {}
-dummy.properties = { input=true, output=true, max_instances=-1 }
+dummy.properties = { input=true, output=true, max_instances=-1, module_type_name="dummy" }
+dummy.next_id = 0
function dummy:new(config)
+ local inst = {}
+ if(config.name == nil or config.name == "") then
+ inst.name = self.properties.module_type_name .. self.next_id
+ self.next_id = self.next_id + 1
+ else
+ inst.name = config.name
+ end
+
local handle = {}
handle.fd = 0
+ handle.client_instance = nil
function handle:getfd() return self.fd end
function handle:dirty() return 0 end
function handle:read() end
function handle:write() end
local client = {}
+ client.module_instance = inst
function client:process_response() end
function client:process_timeout() end
function client:get_read_handles() return handle end
function client:get_write_handles() return {} end
function client:cleanup() end
-
- local inst = {
- cleanup = function() end,
- get_read_handles = function()
- return {}
- end,
- get_write_handles = function()
- return {}
- end
- }
- metatable(inst).__gc = inst.cleanup()
+ handle.client_instance = client
+
+ function inst:cleanup()
+ client_list:unregister_by_module(self)
+ end
+ function inst:get_read_handles()
+ return {}
+ end
+ function inst:get_write_handles()
+ return {}
+ end
+ setmetatable(inst, {})
+ getmetatable(inst).__gc = function() inst:cleanup() end
return inst
end