From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from localhost (localhost [127.0.0.1]) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTP id D55BB2887F for ; Fri, 30 Aug 2019 17:49:01 -0400 (EDT) Received: from turing.freelists.org ([127.0.0.1]) by localhost (turing.freelists.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id Cx1RuvDDxbNe for ; Fri, 30 Aug 2019 17:49:01 -0400 (EDT) Received: from mail-lj1-f193.google.com (mail-lj1-f193.google.com [209.85.208.193]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by turing.freelists.org (Avenir Technologies Mail Multiplex) with ESMTPS id 886302887B for ; Fri, 30 Aug 2019 17:49:01 -0400 (EDT) Received: by mail-lj1-f193.google.com with SMTP id f9so7694201ljc.13 for ; Fri, 30 Aug 2019 14:49:01 -0700 (PDT) From: Cyrill Gorcunov Subject: [tarantool-patches] [PATCH 4/5] box/console: Fix hang in remote console lua mode Date: Sat, 31 Aug 2019 00:48:07 +0300 Message-Id: <20190830214808.17422-5-gorcunov@gmail.com> In-Reply-To: <20190830214808.17422-1-gorcunov@gmail.com> References: <20190830214808.17422-1-gorcunov@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Sender: tarantool-patches-bounce@freelists.org Errors-to: tarantool-patches-bounce@freelists.org Reply-To: tarantool-patches@freelists.org List-Help: List-Unsubscribe: List-software: Ecartis version 1.0.0 List-Id: tarantool-patches List-Subscribe: List-Owner: List-post: List-Archive: To: tml Cc: Alexander Turenko , Konstantin Osipov , Kirill Yukhin , Cyrill Gorcunov If we change output mode on remote machine via text-based session node a (server) ------ require('console').listen('unix/:/tmp/X.sock') ... node b (client) ------ require('console').connect('unix/:/tmp/X.sock') connected to unix/:/tmp/X.sock ... unix/:/tmp/X.sock> \set output lua the client get stuck forever, it is because the text wire protocol waits for yaml eos terminator which of course never hit the peer, because lua mode uses own one. Thus to fix this problem we have to preprocess the text we're passing to the server, just like we do in local console. So we reuse command parsing code and remember current output terminator in text_connection_mt instance. Another problem is that named default output mode. There could be a mixed environment where server operates in default lua mode but client connects with yaml mode. To break a tie we yield "\set output" command with current output mode when establishing a connection. Since the output format is per-session bound this doesn't affect any other connections on a server. Part-of #3834 --- src/box/lua/console.lua | 50 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/src/box/lua/console.lua b/src/box/lua/console.lua index 006748d93..203dc668c 100644 --- a/src/box/lua/console.lua +++ b/src/box/lua/console.lua @@ -158,6 +158,27 @@ local function current_output() return d end +local function current_eos() + local d = current_output() + return output_eos[d["fmt"]] +end + +-- +-- Map output format into a "\set" command. +local function output_cmd_string() + local d = current_output() + if d["fmt"] == "yaml" then + return "\\set output yaml" + elseif d["fmt"] == "lua" then + local cmd = "\\set output lua" + if d["opts"] == "block" then + cmd = cmd .. ",block" + end + return cmd + end + return "" +end + local function format(status, ...) local d = current_output() return output_handlers[d["fmt"]](status, d["opts"], ...) @@ -341,15 +362,33 @@ local text_connection_mt = { -- @retval nil Error. -- read = function(self) - local ret = self._socket:read(YAML_TERM) + local ret = self._socket:read(self.eos) if ret and ret ~= '' then return ret end end, -- + -- The command might modify EOS so + -- we have to parse and preprocess it + -- first to not stuck in :read() forever. + preprocess_eval = function(self, text) + local command = get_command(text) + if command == nil then + return + end + local nr_items, items = parse_operators(command) + if nr_items == 3 then + local err, fmt, opts = output_parse(items[3]) + if not err then + self.eos = output_eos[fmt] + end + end + end, + -- -- Write + Read. -- eval = function(self, text) + self:preprocess_eval(text) text = text..'$EOF$\n' if not self:write(text) then error(self:set_error()) @@ -404,9 +443,14 @@ local function wrap_text_socket(connection, url, print_f) host = url.host or 'localhost', port = url.service, print_f = print_f, + eos = current_eos(), }, text_connection_mt) - if not conn:write('require("console").delimiter("$EOF$")\n') or - not conn:read() then + local cmd_set_output = output_cmd_string() .. '\n' + local cmd_delimiter = 'require("console").delimiter("$EOF$")\n' + if not conn:write(cmd_set_output) or not conn:read() then + conn:set_error() + end + if not conn:write(cmd_delimiter) or not conn:read() then conn:set_error() end return conn -- 2.20.1