diff options
author | Christian Pointner <equinox@spreadspace.org> | 2010-11-08 20:14:02 +0000 |
---|---|---|
committer | Christian Pointner <equinox@spreadspace.org> | 2010-11-08 20:14:02 +0000 |
commit | c56fafa5a7e85af0aede981a2de5ddb337e2fa40 (patch) | |
tree | c0a9db84da108edbacca706d89d52185d6245869 | |
parent | updated main_loop (diff) |
moved debug shell to modules
git-svn-id: https://svn.spreadspace.org/gcsd/trunk@10 ac14a137-c7f1-4531-abe0-07747231d213
-rw-r--r-- | src/Makefile | 6 | ||||
-rw-r--r-- | src/debug_shell.lua | 174 | ||||
-rw-r--r-- | src/defines.lua | 41 | ||||
-rw-r--r-- | src/main_loop.lua | 44 | ||||
-rw-r--r-- | src/modules/debug_shell.lua | 204 |
5 files changed, 277 insertions, 192 deletions
diff --git a/src/Makefile b/src/Makefile index 007bae8..f6c8fee 100644 --- a/src/Makefile +++ b/src/Makefile @@ -48,12 +48,10 @@ C_OBJS := log.o \ C_SRCS := $(C_OBJS:%.o=%.c) -#MODULES := dummy +#MODULES := dummy debug_shell #MODULE_SRC := $(MODULES:%=modules/%.lua) -LUA_SRCS := main_loop.lua \ - debug_shell.lua - +LUA_SRCS := main_loop.lua LUA_BYTECODE := $(EXECUTABLE).lc LUA_BYTECODE_OBJ := $(EXECUTABLE)_lua_bytecode.o diff --git a/src/debug_shell.lua b/src/debug_shell.lua deleted file mode 100644 index 49df934..0000000 --- a/src/debug_shell.lua +++ /dev/null @@ -1,174 +0,0 @@ --- --- 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/>. --- - -socket = require("socket") - -debug_shell = {} - -debug_shell.socks = {}; - -debug_shell.init = function(host, port) - local ip, err = socket.dns.toip(host) - if(ip == nil) then - log.printf(log.ERROR, "debug shell: can't resolve %s: %s", host, err) - return nil - end - - debug_shell.socks[1], err = socket.tcp() - if(debug_shell.socks[1] == nil) then - log.printf(log.ERROR, "debug shell: can't create tcp socket") - return nil - end - - debug_shell.socks[1]:setoption('reuseaddr', true); - - local ret, err = debug_shell.socks[1]:bind(ip, port) - if(ret == nil) then - log.printf(log.ERROR, "debug shell: bind(%s,%s) failed: %s", ip, port, err) - return nil - end - - local ret, err = debug_shell.socks[1]:listen() - if(ret == nil) then - log.printf(log.ERROR, "debug shell: listen() failed: %s", err) - return nil - end - - debug_shell.socks[1]:settimeout(0); - debug_shell.buffer = ""; - - log.printf(log.WARNING, "debug shell: listening on %s:%s (this is a huge security risk)", ip, port); - - return debug_shell.socks[1] -end - -debug_shell.close = function() - debug_shell.socks[1]:close() - if(debug_shell.socks[2]) then debug_shell.socks[2]:close() end -end - -debug_shell.handle = function(sock) - local ret = 0 - if(sock == debug_shell.socks[1]) then - local newclient, err = debug_shell.socks[1]:accept() - if(newclient == nil) then - log.printf(log.ERROR, "debug shell: accept() failed: %s", err) - end - local ip, port = newclient:getpeername(); - if(debug_shell.socks[2]) then - log.printf(log.INFO, "debug shell: refusing connection from %s:%s, already connected", ip, port) - newclient:close(); - else - log.printf(log.INFO, "debug shell: connection from %s:%s accepted", ip, port) - debug_shell.socks[2] = newclient - debug_shell.socks[2]:settimeout(0); - debug_shell.socks[2]:setoption('tcp-nodelay', true); - debug_shell.buffer = ""; - end - else - local data, err, partial = debug_shell.socks[2]:receive('*l') - if(data == nil) then - if(err == 'closed') then - log.printf(log.INFO, "debug shell: connection closed by peer") - debug_shell.socks[2]:close() - debug_shell.socks[2] = nil - elseif(err == 'timeout') then - debug_shell.buffer = debug_shell.buffer .. partial - else - log.printf(log.INFO, "debug shell: connection error: %s", err) - debug_shell.socks[2]:close() - debug_shell.socks[2] = nil - end - else - debug_shell.buffer = debug_shell.buffer .. data - ret = debug_shell.exec_cmd() - if(ret == 1) then - debug_shell.socks[2]:close() - debug_shell.socks[2] = nil - ret = 0 - end - debug_shell.buffer = "" - end - end - - return ret -end - -debug_shell.exec_cmd = function() - log.printf(log.DEBUG, "debug shell: received string: '%s'", debug_shell.buffer) - - local ret = 0 - local luacode = string.match(debug_shell.buffer, "^!(.*)$"); - if(luacode and luacode ~= "") then - local chunk = loadstring(luacode) - if(chunk) then - log.printf(log.DEBUG, "debug shell: calling lua command: '%s'", luacode) - local results = { pcall(chunk) } - if(results[1]) then - for i,result in ipairs(results) do - if(i > 1) then - debug_shell.socks[2]:send(string.format("#%d: %s\n", i-1, tostring(result))) - end - end - else - debug_shell.socks[2]:send(string.format("lua call failed: %s\n", tostring(results[2]))) - end - else - debug_shell.socks[2]:send("syntax error\n") - end - else - local cmd, sep, param = string.match(debug_shell.buffer, "^(%w+)(.?)(.*)$"); - - if(cmd == 'quit') then - if(sep and sep ~= "") then debug_shell.socks[2]:send("unknown command\n") end - log.printf(log.INFO, "debug shell: quitting debug session") - ret = 1 - elseif(cmd == 'terminate') then - if(sep and sep ~= "") then debug_shell.socks[2]:send("unknown command\n") end - log.printf(log.NOTICE, "debug shell: terminate command received") - ret = 2 - elseif(cmd == 'help') then - if(sep and sep ~= "") then debug_shell.socks[2]:send("unknown command\n") end - debug_shell.socks[2]:send("!<lua code> execute lua code\n" .. - "quit quit this debug session\n" .. - "ping[ <data>] echo request (response will be 'pong[ <data>]')\n" .. - "terminate terminate the daemon\n") - elseif(cmd == 'ping') then - if(sep == ' ') then debug_shell.socks[2]:send("pong " .. (param or "") .. "\n") - else debug_shell.socks[2]:send("pong\n") end - else - debug_shell.socks[2]:send("unknown command\n") - end - end - - return ret -end diff --git a/src/defines.lua b/src/defines.lua new file mode 100644 index 0000000..017fb6f --- /dev/null +++ b/src/defines.lua @@ -0,0 +1,41 @@ +-- +-- 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/>. +-- + +local defines = {} + +defines.OK = 0 +defines.KILL_CLIENT = 1 +defines.KILL_MODULE = 2 +defines.KILL_MODULE_TYPE = 3 +defines.KILL_DAEMON = 4 + +return defines
\ No newline at end of file diff --git a/src/main_loop.lua b/src/main_loop.lua index da48515..68924df 100644 --- a/src/main_loop.lua +++ b/src/main_loop.lua @@ -30,7 +30,8 @@ -- along with gcsd. If not, see <http://www.gnu.org/licenses/>. -- -socket = require("socket") +local socket = require("socket") +local defines = require("defines") function main_loop(opt) log.printf(log.NOTICE, "main_loop started") @@ -41,20 +42,23 @@ function main_loop(opt) log.printf(log.DEBUG, "gcsd input[%d] = %s", idx, input) end +--- TODO: load configured modules (use own module loader) local modules = {} -- parse module config local old_path = package.path package.path = "./modules/?.lua" dummy = require ("dummy") + if(opt.debug) then + debug_shell = require ("debug_shell") + end package.path = old_path - table.insert(modules, dummy.new("module config")) - + table.insert(modules, dummy.new({})) if(opt.debug) then - local ret = debug_shell.init("localhost", 9000) - if(ret == nil) then return -1 end + table.insert(modules, debug_shell.new({[host]="127.0.0.1", [port]="9000"})) end +-------- local return_value = 0 while return_value == 0 do @@ -65,14 +69,17 @@ function main_loop(opt) 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 table.insert(writeables, fd) end end +-- TODO: add write handles of all clients - local readable, writeable, err = socket.select(readables , writeables) + local readable, writeable, err = socket.select(readables, writeables) if(err) then log.printf(log.ERROR, "select returned with error: %s", err) return_value = -1 @@ -81,24 +88,33 @@ function main_loop(opt) if(input == sig) then return_value = signal.handle() if(return_value == 1) then break end - elseif(input == debug_shell.socks[1] or input == debug_shell.socks[2]) then - return_value = debug_shell.handle(input) - if(return_value == 2) then break end else - input.read() + local ret = input.read() + if(ret == defines.KILL_DAEMON) then + return_value = 2 + break + elseif(ret == defines.KILL_MODULE_TYPE) then + -- TODO: remove all modules of same type as this and + -- unload module + elseif(ret == defines.KILL_MODULE) then + -- TODO: remove module + elseif(ret == defines.KILL_CLIENT) then + -- TODO: remove client from list + end end end for _, output in ipairs(writeable) do - output.write() + ret = output.write() + if(ret ~= defines.OK) then + -- TODO: remove client from list + end end end end if(return_value == 2) then return_value = 0 end - if(opt.debug) then - debug_shell.close(); - end + -- TODO: cleanup clients and modules signal.stop() return return_value end diff --git a/src/modules/debug_shell.lua b/src/modules/debug_shell.lua new file mode 100644 index 0000000..1e48d9c --- /dev/null +++ b/src/modules/debug_shell.lua @@ -0,0 +1,204 @@ +-- +-- 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/>. +-- + +local socket = require("socket") +local defines = require("defines") + +local debug_shell = {} +debug_shell.properties = { input=true, output=true, max_instances=1 } +function debug_shell:new(config) + 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) + 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") + return nil + end + + server_sock:setoption('reuseaddr', true); + + local ret, err = server_sock:bind(ip, config.port) + if(ret == nil) then + log.printf(log.ERROR, "debug shell: bind(%s,%s) failed: %s", ip, config.port, err) + return nil + end + + local ret, err = server_sock:listen() + if(ret == nil) then + log.printf(log.ERROR, "debug shell: listen() failed: %s", err) + return nil + end + + server_sock:settimeout(0); + 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) + 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); + client_sock.in_buffer = "" + client_sock.out_buffer = "" + function client_sock:read() + local ret = 0 + local data, err, partial = self.receive('*l') + if(data == nil) then + if(err == 'closed') then + log.printf(log.INFO, "debug shell: connection closed by peer") + -- TODO: remove 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 + end + else + self.in_buffer = self.in_buffer .. data + ret = debug_shell:exec_cmd(self) + if(ret == 1) then + -- TODO: remove client + ret = 0 + end + self.in_buffer = "" + end + end + function client_sock:write() + self.send(self.out_buffer) + self.out_buffer = "" + end + + local client = {} + client.sock = client_sock + function client:process_response(response) + self.sock.out_buffer = self.sock.out_buffer .. response + end + function client:process_timeout() end + function client:get_read_handles() + return { self.sock } + end + function client:get_write_handles() + if(self.sock.out_buffer ~= "") then + return { self.sock } + else + return {} + end + end + function client:cleanup() + self.sock.close() + end + + -- TODO: register new 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); + + 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() + 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 luacode = string.match(socket.in_buffer, "^!(.*)$"); + if(luacode and luacode ~= "") then + local chunk = loadstring(luacode) + if(chunk) then + log.printf(log.DEBUG, "debug shell: calling lua command: '%s'", luacode) + local results = { pcall(chunk) } + if(results[1]) then + for i,result in ipairs(results) do + if(i > 1) then + socket.out_buffer = socket.out_buffer .. string.format("#%d: %s\n", i-1, tostring(result)) + end + end + else + socket.out_buffer = socket.out_buffer .. string.format("lua call failed: %s\n", tostring(results[2])) + end + else + socket.out_buffer = socket.out_buffer .. "syntax error\n" + end + else + local cmd, sep, param = string.match(socket.in_buffer, "^(%w+)(.?)(.*)$"); + + 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 + 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 + 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" .. + "quit quit this debug session\n" .. + "ping[ <data>] echo request (response will be 'pong[ <data>]')\n" .. + "terminate terminate the daemon\n" + elseif(cmd == 'ping') then + if(sep == ' ') then socket.out_buffer = socket.out_buffer .. "pong " .. (param or "") .. "\n" + else socket.out_buffer = socket.out_buffer .. "pong\n" end + else + socket.out_buffer = socket.out_buffer .. "unknown command\n" + end + end + + return ret +end + +return debug_shell + + + + + |