From: Vladislav Shpilevoy <v.shpilevoy@tarantool.org> To: tarantool-patches@freelists.org Cc: Vladislav Shpilevoy <v.shpilevoy@tarantool.org> Subject: [tarantool-patches] [PATCH v2 2/3] console: do not use netbox for console text connections Date: Wed, 28 Mar 2018 16:35:49 +0300 [thread overview] Message-ID: <7bbc3f973856120f5394f5193d5f02b538606195.1522243429.git.v.shpilevoy@tarantool.org> (raw) In-Reply-To: <cover.1522243429.git.v.shpilevoy@tarantool.org> In-Reply-To: <cover.1522243429.git.v.shpilevoy@tarantool.org> Netbox console support complicates both netbox and console. Lets use sockets directly for text protocol. Part of #2677 --- src/box/lua/console.lua | 162 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 131 insertions(+), 31 deletions(-) diff --git a/src/box/lua/console.lua b/src/box/lua/console.lua index d49cf42be..b853d523c 100644 --- a/src/box/lua/console.lua +++ b/src/box/lua/console.lua @@ -8,6 +8,9 @@ local log = require('log') local errno = require('errno') local urilib = require('uri') local yaml = require('yaml') +local net_box = require('net.box') + +local YAML_TERM = '\n...\n' -- admin formatter must be able to encode any Lua variable local formatter = yaml.new() @@ -76,26 +79,117 @@ local function eval(line) return local_eval(nil, line) end +local text_connection_mt = { + __index = { + -- + -- Close underlying socket. + -- + close = function(self) + self._socket:close() + end, + -- + -- Write a text into a socket. + -- @param test Text to send. + -- @retval not nil Bytes sent. + -- @retval nil Error. + -- + write = function(self, text) + -- It is the hack to protect from SIGPIPE, which is + -- not ignored under debugger (gdb, lldb) on send in + -- a socket, that is actually closed. If a socket is + -- readable and read() returns nothing then the socket + -- is closed, and writing into it will raise SIGPIPE. + if self._socket:readable(0) then + local rc = self._socket:read({chunk = 1}) + if not rc or rc == '' then + return nil + else + assert(#rc == 1) + -- Make the char be unread. + self._socket.rbuf.wpos = self._socket.rbuf.wpos - 1 + end + end + return self._socket:write(text) + end, + -- + -- Read a text from a socket until YAML terminator. + -- @retval not nil Well formatted YAML. + -- @retval nil Error. + -- + read = function(self) + local ret = self._socket:read(YAML_TERM) + if ret and ret ~= '' then + return ret + end + end, + -- + -- Write + Read. + -- + eval = function(self, text) + text = text..'$EOF$\n' + if self:write(text) then + local rc = self:read() + if rc then + return rc + end + end + error(self:set_error()) + end, + -- + -- Make the connection be in error state, set error + -- message. + -- @retval Error message. + -- + set_error = function(self) + self.state = 'error' + self.error = self._socket:error() + if not self.error then + self.error = 'Peer closed' + end + return self.error + end, + } +} + +-- +-- Wrap an existing socket inside a netbox-like object. +-- @param connection Socket to wrap. +-- @param url Parsed destination URL. +-- @retval nil, err Error, and err contains an error message. +-- @retval not nil Netbox-like object. +-- +local function wrap_text_socket(connection, url) + local conn = setmetatable({ + _socket = connection, + state = 'active', + host = url.host or 'localhost', + port = url.service, + }, text_connection_mt) + if not conn:write('require("console").delimiter("$EOF$")\n') or + not conn:read() then + conn:set_error() + end + return conn +end + -- -- Evaluate command on remote instance -- local function remote_eval(self, line) - if not line or self.remote.state ~= 'active' then - local err = self.remote.error - self.remote:close() - self.remote = nil - -- restore local REPL mode - self.eval = nil - self.prompt = nil - self.completion = nil - pcall(self.on_client_disconnect, self) - return (err and format(false, err)) or '' + if line and self.remote.state == 'active' then + local ok, res = pcall(self.remote.eval, self.remote, line) + if self.remote.state == 'active' then + return ok and res or format(false, res) + end end - -- - -- execute line - -- - local ok, res = pcall(self.remote.eval, self.remote, line) - return ok and res or format(false, res) + local err = self.remote.error + self.remote:close() + self.remote = nil + self.eval = nil + self.prompt = nil + self.completion = nil + pcall(self.on_client_disconnect, self) + return (err and format(false, err)) or '' end -- @@ -285,12 +379,7 @@ end -- -- Connect to remove instance -- -local netbox_connect local function connect(uri, opts) - if not netbox_connect then -- workaround the broken loader - netbox_connect = require('net.box').connect - end - opts = opts or {} local self = fiber.self().storage.console @@ -306,18 +395,29 @@ local function connect(uri, opts) error('Usage: console.connect("[login:password@][host:]port")') end - -- connect to remote host + local connection, greeting = + net_box.establish_connection(u.host, u.service, opts.timeout) + if not connection then + log.verbose(greeting) + box.error(box.error.NO_CONNECTION) + end local remote - remote = netbox_connect(u.host, u.service, { - user = u.login, password = u.password, - console = true, connect_timeout = opts.timeout - }) - remote.host, remote.port = u.host or 'localhost', u.service - - -- run disconnect trigger if connection failed - if not remote:is_connected() then - pcall(self.on_client_disconnect, self) - error('Connection is not established: '..remote.error) + if greeting.protocol == 'Lua console' then + remote = wrap_text_socket(connection, u) + else + opts = { + connect_timeout = opts.timeout, + user = u.login, + password = u.password, + } + remote = net_box.wrap(connection, greeting, u.host, u.service, opts) + if not remote.host then + remote.host = 'localhost' + end + local old_eval = remote.eval + remote.eval = function(con, line) + return old_eval(con, 'return require("console").eval(...)', {line}) + end end -- check connection && permissions -- 2.14.3 (Apple Git-98)
next prev parent reply other threads:[~2018-03-28 13:35 UTC|newest] Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top 2018-03-28 13:35 [tarantool-patches] [PATCH v2 0/3] " Vladislav Shpilevoy 2018-03-28 13:35 ` [tarantool-patches] [PATCH v2 1/3] netbox: allow to create a netbox connection from existing socket Vladislav Shpilevoy 2018-03-29 13:03 ` [tarantool-patches] " Konstantin Osipov 2018-03-28 13:35 ` Vladislav Shpilevoy [this message] 2018-03-29 13:03 ` [tarantool-patches] Re: [PATCH v2 2/3] console: do not use netbox for console text connections Konstantin Osipov 2018-03-28 13:35 ` [tarantool-patches] [PATCH v2 3/3] netbox: deprecate console support Vladislav Shpilevoy
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=7bbc3f973856120f5394f5193d5f02b538606195.1522243429.git.v.shpilevoy@tarantool.org \ --to=v.shpilevoy@tarantool.org \ --cc=tarantool-patches@freelists.org \ --subject='Re: [tarantool-patches] [PATCH v2 2/3] console: do not use netbox for console text connections' \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox