# # # patch "automate_ostream.hh" # from [e191a9d2ccc72b568e82c63a478d070dac3a2aa6] # to [d80b57babe3d47106cd87acaedf5284e02d6fd1d] # # patch "automate_ostream_demuxed.hh" # from [97eca47597abccc55de0260c370365af417a57bd] # to [0993deb1bb28fc47cddf4a4f2bc5db04b154fbbb] # # patch "cmd_automate.cc" # from [e11863a9ffcf46b504b996a1759dfd3eb32e72e1] # to [5d8b2a02978564165eb1c639b4f75cd052f9f36f] # # patch "monotone.texi" # from [c7c981f637be04daa896838c009e116337ce411f] # to [ba09d6d972a2f2cace5f649f44ecd1121d3fc6c9] # # patch "netcmd.cc" # from [2e20e0fd6908636771d11d4c984f33a8384b5e3e] # to [49728d5636bd31ff48a2c5138e564c2b1433ffeb] # # patch "netcmd.hh" # from [72ba96d114ba44ede161eafafcef43d9ebeb0946] # to [5a7cc7bb58fc69f67982c9daa592d6dc29d06b70] # # patch "network/automate_session.cc" # from [ed3b89cacdcbe22b03c23d8704edc9df8e81ab41] # to [aa63e7329bf1e9ba3b3491e5ec07369f71c7941a] # # patch "network/automate_session.hh" # from [24767870966b9f24b4f1544c29900876e1c22c60] # to [1b9d9b6fe2fcab6022aef5cba9e62c1da9ada736] # # patch "tests/automate_stdio_band_output/__driver__.lua" # from [dd43cbb017752fd40828862780fadc16299b9b3f] # to [55f82327f09de466c28671504baec545e148add0] # # patch "tests/common/automate_stdio.lua" # from [62810eafcae9612d7ba8f6ba96c3ead7dce84f20] # to [c8d9c642ad1dc0ad3e96672003a76ef7e9534162] # # patch "tests/serve-automate/__driver__.lua" # from [e2d30039549bae2787c5791eb1fbbecdc7bc13e5] # to [5cd9d3a901d12596897815428d388b6b6d2fa398] # # patch "unit-tests/netcmd.cc" # from [c6c79bb5d3d732f97cc37e78250e12f9b25d9b81] # to [0ac50ab8ed6f14aaa3d2b6ecaca4eb62070e5b71] # ============================================================ --- automate_ostream.hh e191a9d2ccc72b568e82c63a478d070dac3a2aa6 +++ automate_ostream.hh d80b57babe3d47106cd87acaedf5284e02d6fd1d @@ -12,7 +12,10 @@ #define __AUTOMATE_OSTREAM_HH__ #include +#include "lexical_cast.hh" +using boost::lexical_cast; + template > class basic_automate_streambuf : public std::basic_streambuf<_CharT, _Traits> { @@ -21,30 +24,26 @@ class basic_automate_streambuf : public size_t _bufsize; std::basic_ostream<_CharT, _Traits> *out; int cmdnum; - int err; + public: basic_automate_streambuf(std::ostream & o, size_t bufsize) - : std::streambuf(), _bufsize(bufsize), out(&o), cmdnum(0), err(0) + : std::streambuf(), _bufsize(bufsize), out(&o), cmdnum(0) { _CharT *inbuf = new _CharT[_bufsize]; setp(inbuf, inbuf + _bufsize); } + basic_automate_streambuf() - { } - ~basic_automate_streambuf() {} - void set_err(int e) - { - sync(); - err = e; - } + ~basic_automate_streambuf() + {} - void end_cmd() + void end_cmd(int errcode) { - _M_sync(true); + _M_sync(); + write_out_of_band('l', lexical_cast(errcode)); ++cmdnum; - err = 0; } virtual int sync() @@ -53,7 +52,7 @@ public: return 0; } - void _M_sync(bool end = false) + void _M_sync() { if (!out) { @@ -61,17 +60,17 @@ public: return; } int num = this->pptr() - this->pbase(); - if (num || end) + if (num) { (*out) << cmdnum << ':' - << err << ':' - << (end?'l':'m') << ':' + << 'm' << ':' << num << ':' << std::basic_string<_CharT,_Traits>(this->pbase(), num); setp(this->pbase(), this->pbase() + _bufsize); out->flush(); } } + void write_out_of_band(char type, std::string const & data) { unsigned chunksize = _bufsize; @@ -80,12 +79,13 @@ public: { if (offset+chunksize>length) chunksize = length-offset; - (*out) << cmdnum << ':' << err << ':' << type << ':' - << chunksize << ':' << data.substr(offset, chunksize); + (*out) << cmdnum << ':' << type << ':' << chunksize + << ':' << data.substr(offset, chunksize); offset+= chunksize; } while (offsetflush(); } + int_type overflow(int_type c = traits_type::eof()) { @@ -118,12 +118,9 @@ public: rdbuf() const { return const_cast(&_M_autobuf); } - virtual void set_err(int e) - { _M_autobuf.set_err(e); } + virtual void end_cmd(int errcode) + { _M_autobuf.end_cmd(errcode); } - virtual void end_cmd() - { _M_autobuf.end_cmd(); } - virtual void write_out_of_band(char type, std::string const & data) { _M_autobuf.write_out_of_band(type, data); } }; ============================================================ --- automate_ostream_demuxed.hh 97eca47597abccc55de0260c370365af417a57bd +++ automate_ostream_demuxed.hh 0993deb1bb28fc47cddf4a4f2bc5db04b154fbbb @@ -24,15 +24,13 @@ class basic_automate_streambuf_demuxed : size_t _bufsize; std::basic_ostream<_CharT, _Traits> *stdout; std::basic_ostream<_CharT, _Traits> *errout; - int err_code; public: basic_automate_streambuf_demuxed(std::ostream & out, std::ostream & err, size_t bufsize) : std::streambuf(), _bufsize(bufsize), stdout(&out), - errout(&err), - err_code(0) + errout(&err) { _CharT * inbuf = new _CharT[_bufsize]; setp(inbuf, inbuf + _bufsize); @@ -40,16 +38,6 @@ public: ~basic_automate_streambuf_demuxed() { } - void set_err(int e) - { - err_code = e; - } - - int get_error() const - { - return err_code; - } - void end_cmd() { _M_sync(); @@ -92,9 +80,7 @@ private: private: void _M_sync() { - std::basic_ostream<_CharT, _Traits> *str; - str = ((err_code != 0) ? errout : stdout); - if (!str) + if (!stdout) { setp(this->pbase(), this->pbase() + _bufsize); return; @@ -102,9 +88,9 @@ private: int num = this->pptr() - this->pbase(); if (num) { - (*str) << std::basic_string<_CharT, _Traits>(this->pbase(), num); + (*stdout) << std::basic_string<_CharT, _Traits>(this->pbase(), num); setp(this->pbase(), this->pbase() + _bufsize); - str->flush(); + stdout->flush(); } } }; @@ -114,6 +100,7 @@ struct basic_automate_ostream_demuxed : { typedef basic_automate_streambuf_demuxed<_CharT, _Traits> streambuf_type; streambuf_type _M_autobuf; + int errcode; public: basic_automate_ostream_demuxed(std::basic_ostream<_CharT, _Traits> &out, @@ -129,15 +116,15 @@ public: rdbuf() const { return const_cast(&_M_autobuf); } - virtual void set_err(int e) - { _M_autobuf.set_err(e); } + virtual void end_cmd(int error) + { + errcode = error; + _M_autobuf.end_cmd(); + } int get_error() const - { return _M_autobuf.get_error(); } + { return errcode; } - virtual void end_cmd() - { _M_autobuf.end_cmd(); } - virtual void write_out_of_band(char type, std::string const & data) { _M_autobuf.write_out_of_band(type, data); } }; ============================================================ --- cmd_automate.cc e11863a9ffcf46b504b996a1759dfd3eb32e72e1 +++ cmd_automate.cc 5d8b2a02978564165eb1c639b4f75cd052f9f36f @@ -286,17 +286,15 @@ CMD_AUTOMATE_NO_STDIO(stdio, "", // since it is not based on informative_failure catch (option::option_error & e) { - os.set_err(1); os.write_out_of_band('e', e.what()); - os.end_cmd(); + os.end_cmd(1); ar.reset(); continue; } catch (recoverable_failure & f) { - os.set_err(1); os.write_out_of_band('e', f.what()); - os.end_cmd(); + os.end_cmd(1); ar.reset(); continue; } @@ -304,15 +302,16 @@ CMD_AUTOMATE_NO_STDIO(stdio, "", try { acmd->exec_from_automate(app, id, args, os); + os.end_cmd(0); + // restore app.opts app.opts = original_opts; } catch (recoverable_failure & f) { - os.set_err(2); os.write_out_of_band('e', f.what()); + os.end_cmd(2); } - os.end_cmd(); } global_sanity.set_out_of_band_handler(); } ============================================================ --- monotone.texi c7c981f637be04daa896838c009e116337ce411f +++ monotone.texi ba09d6d972a2f2cace5f649f44ecd1121d3fc6c9 @@ -7673,7 +7673,10 @@ @section Automation @itemize @item -FIXME -- Introduced out of band streams ('e', 'p', 't', 'w') and changed error reporting +FIXME -- Introduced out of band streams ('e', 'p', 't', 'w'); the main output + of a command now only happens in the 'm' stream; the 'l' stream now marks + the end of all streams of a command; specific error codes to distinguish + command from interface errors have been introduced. @item 3.1 -- Added the 'o' item to the recognized input. This change should not break anything. @@ -7714,22 +7717,25 @@ @section Automation @item Sample output: @verbatim -1:0:l:41:7706a422ccad41621c958affa999b1a1dd644e79 -2:2:e:38:misuse: key 'address@hidden' already exists +0:m:41:7706a422ccad41621c958affa999b1a1dd644e79 +0:l:1:0 ... -3:0:w:39:skipping file '\' with unsupported name -3:0:m:144: path "" +1:e:38:misuse: key 'address@hidden' already exists +1:l:1:2 +... +2:w:39:skipping file '\' with unsupported name +2:m:144: path "" old_type "directory" new_type "directory" fs_type "directory" birth [276264b0b3f1e70fc1835a700e6e61bdbe4c3f2f] status "known" ... -4:0:t:34:c:certificates;k:keys;r:revisions; -4:0:t:12:c=0;k=0;r=0; -4:0:t:13:c#0;k#0;r#64; -4:0:t:14:c#0;k#0;r#128; -4:0:t:6:c;k;r; +3:t:34:c:certificates;k:keys;r:revisions; +3:t:12:c=0;k=0;r=0; +3:t:13:c#0;k#0;r#64; +3:t:14:c#0;k#0;r#128; +3:t:6:c;k;r; ... @end verbatim @@ -7738,43 +7744,41 @@ @section Automation The output consists of one or more packets for each command. A packet looks like: -:::: address@hidden +::: address@hidden verbatim - is a decimal number specifying which command this output address@hidden} is a decimal number specifying which command this output is from. It is 0 for the first command, and increases by one each time. - is 0 for success, 1 for a syntax error which occurred in stdio scope (before the command is executed), and 2 for a command error. - - is an identifier for which output stream this packet represents, address@hidden} is an identifier for which output stream this packet represents, allowing multiple streams to be multiplexed over the channel. The following streams are presently defined; more streams may be added later. @itemize @item - 'm' and 'l': the 'm' stream represents the normal stdout automate output of + @code{m}: this stream represents the normal ("main") stdout automate output of the command, formatted as described in the description for that command. - - The special 'l' value is described below. @item - 'e': the 'e' stream represents any (unstructured) error message data. + @code{e}: this stream represents any (unstructured) error message data. - Internally, this maps to calls to the E() and N() print macros that would + Internally, this maps to calls to the @code{E()} print macros that would normally be written by the command to the program's stderr stream, if the automate sub-command had been called directly rather than via '''stdio'''. @item - 'w': the 'w' stream represents any (unstructured) warning message data. + @code{w}: this stream represents any (unstructured) warning message data. - Internally, this maps to calls to the W() print macro that would normally - be written by the command to the program's stderr stream, if the automate - sub-command had been called directly rather than via '''stdio'''. + Internally, this maps to calls to the @code{W()} print macro that would + normally be written by the command to the program's stderr stream, if the + automate sub-command had been called directly rather than via '''stdio'''. @item - 'p': the 'p' stream represents any (unstructured) progress message data. + @code{p}: this stream represents any (unstructured) progress message data. - Internally, this maps to calls to the P() print macro that would normally - be written by the command to the program's stderr stream, if the automate - sub-command had been called directly rather than via '''stdio'''. + Internally, this maps to calls to the @code{P()} print macro that would + normally be written by the command to the program's stderr stream, if the + automate sub-command had been called directly rather than via '''stdio'''. @item - 't': the 't' stream represents ticker updates, which may be used + @code{t}: this stream represents ticker updates, which may be used by a user interface to display the progress of a command. The output for this channel can be described as follows: @@ -7828,29 +7832,31 @@ @section Automation @emph{Note:} The ticker format used for stdio is fixed and cannot be selected explicitely via the global @option{--ticker} option. However, if you run an automate command outside of stdio you can set a different ticker type, f.e. - 'count', 'dot' or 'none'. address@hidden itemize + @var{count}, @var{dot} or @var{none}. address@hidden + @code{l}: this stream marks the termination of a command and all of + its streams and carries the return code of the command in the payload. - is the number of bytes in the output. + A return code "0" stands for success, "1" for an error which occurred within + the stdio interface (f.e. syntax errors or missing privileges) before the + command is run and finally "2" for any other command-specific error. address@hidden itemize - is a piece of the output of the command. address@hidden} is the number of bytes in the output. -The last packet for a given command will have the field set to 'l'. -This packet indicates termination of all streams for the command. Any -content in this packet is considered to be part of the 'm' stream. The - in this packet is likely to be zero if there has been an error -message that has prevented or interrupted normal output. address@hidden} is a piece of the output of the command. @item Error conditions: -If a badly formatted or invalid command is received, or a command is -given with invalid arguments or options, prints an error message to -standard error and exits with nonzero status. Errors in the commands run -through this interface do not affect the exit status. Instead, the -field in the output is set to 2, the error message is reported on the 'e' -stream and terminated by an (empty) 'l' message. +Errors in the commands run through this interface do not affect the exit +status of this command. Instead, if a badly formatted or invalid command +is received, or a command is given with invalid arguments or options, +an error message to the error stream is printed and and the particular +sub-command exits with return code "1". Other command-specific errors are +returned as code "2". + Trying to run the @code{automate stdio} or @code{automate remote_stdio} -commands will always act as if that command encountered an error. +sub-commands will exit the particular command with return code "1". @item Multiple streams ============================================================ --- netcmd.cc 2e20e0fd6908636771d11d4c984f33a8384b5e3e +++ netcmd.cc 49728d5636bd31ff48a2c5138e564c2b1433ffeb @@ -668,7 +668,6 @@ netcmd::read_automate_packet_cmd(int & c void netcmd::read_automate_packet_cmd(int & command_num, - int & err_code, char & stream, string & packet_data) const { @@ -676,8 +675,6 @@ netcmd::read_automate_packet_cmd(int & c command_num = int(extract_datum_uleb128(payload, pos, "automate_packet netcmd, command_num")); - err_code = int(extract_datum_uleb128(payload, pos, - "automate_packet netcmd, err_code")); stream = char(extract_datum_uleb128(payload, pos, "automate_packet netcmd, stream")); extract_variable_length_string(payload, packet_data, pos, @@ -687,14 +684,12 @@ netcmd::write_automate_packet_cmd(int co void netcmd::write_automate_packet_cmd(int command_num, - int err_code, char stream, string const & packet_data) { cmd_code = automate_packet_cmd; insert_datum_uleb128(size_t(command_num), payload); - insert_datum_uleb128(size_t(err_code), payload); insert_datum_uleb128(size_t(stream), payload); insert_variable_length_string(packet_data, payload); } ============================================================ --- netcmd.hh 72ba96d114ba44ede161eafafcef43d9ebeb0946 +++ netcmd.hh 5a7cc7bb58fc69f67982c9daa592d6dc29d06b70 @@ -213,11 +213,9 @@ public: void write_automate_command_cmd(std::vector const & args, std::vector > const & opts); void read_automate_packet_cmd(int & command_num, - int & err_code, char & stream, std::string & packet_data) const; void write_automate_packet_cmd(int command_num, - int err_code, char stream, std::string const & packet_data); ============================================================ --- network/automate_session.cc ed3b89cacdcbe22b03c23d8704edc9df8e81ab41 +++ network/automate_session.cc aa63e7329bf1e9ba3b3491e5ec07369f71c7941a @@ -24,6 +24,7 @@ using boost::shared_ptr; using std::vector; using boost::shared_ptr; +using boost::lexical_cast; CMD_FWD_DECL(automate); @@ -95,16 +96,14 @@ static void out_of_band_to_netcmd(char s static void out_of_band_to_netcmd(char stream, std::string const & text, void * opaque) { automate_session * sess = reinterpret_cast(opaque); - // FIXME: is it really correct to set the error always to 0? - sess->write_out_of_band_cmd(stream, text, 0); + sess->write_automate_packet_cmd(stream, text); } -void automate_session::write_out_of_band_cmd(char stream, - std::string const & text, - unsigned int errcode) +void automate_session::write_automate_packet_cmd(char stream, + std::string const & text) { netcmd net_cmd(get_version()); - net_cmd.write_automate_packet_cmd(command_number, errcode, stream, text); + net_cmd.write_automate_packet_cmd(command_number, stream, text); write_netcmd(net_cmd); } @@ -218,12 +217,12 @@ bool automate_session::do_work(transacti catch (option::option_error & e) { errcode = 1; - write_out_of_band_cmd('e', e.what(), errcode); + write_automate_packet_cmd('e', e.what()); } catch (recoverable_failure & f) { errcode = 1; - write_out_of_band_cmd('e', f.what(), errcode); + write_automate_packet_cmd('e', f.what()); } if (errcode == 0) @@ -237,16 +236,15 @@ bool automate_session::do_work(transacti catch (recoverable_failure & f) { errcode = 2; - write_out_of_band_cmd('e', f.what(), errcode); + write_automate_packet_cmd('e', f.what()); } } - netcmd out_cmd(get_version()); - out_cmd.write_automate_packet_cmd(command_number, - errcode, 'l', - oss.str()); - write_netcmd(out_cmd); + if (!oss.str().empty()) + write_automate_packet_cmd('m', oss.str()); + write_automate_packet_cmd('l', lexical_cast(errcode)); + global_sanity.set_out_of_band_handler(); return true; @@ -255,23 +253,20 @@ bool automate_session::do_work(transacti case automate_packet_cmd: { int command_num; - int err_code; char stream; string packet_data; - cmd_in->read_automate_packet_cmd(command_num, err_code, - stream, packet_data); + cmd_in->read_automate_packet_cmd(command_num, stream, packet_data); I(output_stream); - output_stream->set_err(err_code); - if (stream == 'm' || stream == 'l') + if (stream == 'm') (*output_stream) << packet_data; - else + else if (stream != 'l') output_stream->write_out_of_band(stream, packet_data); if (stream == 'l') { - output_stream->end_cmd(); + output_stream->end_cmd(lexical_cast(packet_data)); send_command(); } ============================================================ --- network/automate_session.hh 24767870966b9f24b4f1544c29900876e1c22c60 +++ network/automate_session.hh 1b9d9b6fe2fcab6022aef5cba9e62c1da9ada736 @@ -38,12 +38,11 @@ public: std::istream * const is, automate_ostream * const os); - void write_out_of_band_cmd(char stream, - std::string const & text, - unsigned int errcode); + void write_automate_packet_cmd(char stream, + std::string const & text); bool do_work(transaction_guard & guard, netcmd const * const in_cmd); - bool have_work() const; + bool have_work() const; void request_service(); void accept_service(); std::string usher_reply_data() const; ============================================================ --- tests/automate_stdio_band_output/__driver__.lua dd43cbb017752fd40828862780fadc16299b9b3f +++ tests/automate_stdio_band_output/__driver__.lua 55f82327f09de466c28671504baec545e148add0 @@ -40,37 +40,39 @@ for _,tick in ipairs(tickers) do ticks = split(tick, ";") check(table.maxn(ticks) > 0) for _,mtick in ipairs(ticks) do - local begin,End,short,ticktype,content = - string.find(mtick, "([%l%u]+)([=:#])(.+)") - if begin == nil then - short = mtick - end - if ticker_data[short] == nil then - -- check the ticker's name definition - check(ticktype == ":") - ticker_data[short] = {} - check(string.len(content) > 0) - else - check(ticktype ~= ":") - -- check and remember the ticker's total value (if any) - if ticktype == "=" then - check(ticker_data[short].total == nil) - ticker_data[short].total = tonumber(content) - check(ticker_data[short].total ~= nil) - -- check and remember the ticker's progress - elseif ticktype == "#" then - progress = tonumber(content) - check(progress ~= nil) - check(ticker_data[short].total == 0 or - progress <= ticker_data[short].total) - ticker_data[short].progress = progress - -- check for the ticker's end and remove it - elseif ticktype == nil then - check(ticker_data[short] ~= nil) - check(ticker_data[short].total == 0 or - ticker_data[short].progress == ticker_data[short].total) - ticker_data[short] = nil + if string.len(mtick) > 0 then + local begin,End,short,ticktype,content = + string.find(mtick, "([%l%u%d]+)([=:#])(.+)") + if begin == nil then + short = mtick end + if ticker_data[short] == nil then + -- check the ticker's name definition + check(ticktype == ":") + ticker_data[short] = {} + check(string.len(content) > 0) + else + check(ticktype ~= ":") + -- check and remember the ticker's total value (if any) + if ticktype == "=" then + check(ticker_data[short].total == nil) + ticker_data[short].total = tonumber(content) + check(ticker_data[short].total ~= nil) + -- check and remember the ticker's progress + elseif ticktype == "#" then + progress = tonumber(content) + check(progress ~= nil) + check(ticker_data[short].total == 0 or + progress <= ticker_data[short].total) + ticker_data[short].progress = progress + -- check for the ticker's end and remove it + elseif ticktype == nil then + check(ticker_data[short] ~= nil) + check(ticker_data[short].total == 0 or + ticker_data[short].progress == ticker_data[short].total) + ticker_data[short] = nil + end + end end end end ============================================================ --- tests/common/automate_stdio.lua 62810eafcae9612d7ba8f6ba96c3ead7dce84f20 +++ tests/common/automate_stdio.lua c8d9c642ad1dc0ad3e96672003a76ef7e9534162 @@ -1,39 +1,59 @@ -function run_stdio(cmd, err, which, band) - check(mtn("automate", "stdio"), 0, true, false, cmd) - - local parse_stdio = function(dat, which) - local bands = {} - local errcodes = {} - while true do - local begin,End,cmdnum,res,band,size = string.find(dat, "(%d+):(%d+):(%l):(%d+):") - if begin == nil then break end - cmdnum = cmdnum + 0 - if bands[cmdnum] == nil then - bands[cmdnum] = { m = "" } +-- +-- data: stdio input data +-- err: expected error code (0, 1 or 2) +-- which: which command to check if data contains input of several command +-- (0 by default) +-- band: the contents of which band to return +-- ('m', the main content, by default) +-- +function parse_stdio(data, err, which, band) + local bands = {} + local errcodes = {} + while true do + local begin,End,cmdnum,bnd,size = string.find(data, "(%d+):(%l):(%d+):") + if begin == nil then break end + cmdnum = cmdnum + 0 + if bands[cmdnum] == nil then + bands[cmdnum] = { m = "" } + end + local content = string.sub(data, End+1, End+size) + if bnd == "m" then + bands[cmdnum].m = bands[cmdnum].m .. content + elseif bnd == "l" then + errcodes[cmdnum] = tonumber(content) + else + if bands[cmdnum][bnd] == nil then + bands[cmdnum][bnd] = {} end - local content = string.sub(dat, End+1, End+size) - if band == "m" or band == "l" then - bands[cmdnum].m = bands[cmdnum].m .. content - else - if bands[cmdnum][band] == nil then - bands[cmdnum][band] = {} - end - table.insert(bands[cmdnum][band], content) - end - dat = string.sub(dat, End + 1 + size) - errcodes[cmdnum] = tonumber(res) + table.insert(bands[cmdnum][bnd], content) end - return bands[which], errcodes[which] + data = string.sub(data, End + 1 + size) end if which == nil then which = 0 end - local bands,errcode = parse_stdio(readfile("stdout"), which) - check(err == errcode) + check(bands[which] ~= nil) + check(errcodes[which] ~= nil) + + check(err == errcodes[which]) + if band == nil then band = "m" end - return bands[band] + + check(bands[which][band] ~= nil) + + return bands[which][band] end + +function run_stdio(cmd, err, which, band) + check(mtn("automate", "stdio"), 0, true, false, cmd) + return parse_stdio(readfile("stdout"), err, which, band) +end + +function run_remote_stdio(server, cmd, err, which, band) + check(mtn2("automate", "remote_stdio", server.address), 0, true, false, cmd) + return parse_stdio(readfile("stdout"), err, which, band) +end ============================================================ --- tests/serve-automate/__driver__.lua e2d30039549bae2787c5791eb1fbbecdc7bc13e5 +++ tests/serve-automate/__driver__.lua 5cd9d3a901d12596897815428d388b6b6d2fa398 @@ -1,4 +1,5 @@ include("common/netsync.lua") include("common/netsync.lua") +include("common/automate_stdio.lua") mtn_setup() netsync.setup() @@ -8,31 +9,41 @@ server = netsync.start() server = netsync.start() -check(mtn2("automate", "remote_stdio", server.address), 0, true, false, - "l17:interface_versione") -check(qgrep("^0:1:e:45:misuse: Sorry, you aren't allowed to do that.0:1:l:0:", "stdout")) +local errors = run_remote_stdio(server, "l17:interface_versione", 1, 0, "e") +check( + table.maxn(errors) == 1 and + errors[1] == "misuse: Sorry, you aren't allowed to do that." +) server:stop() check(mtn2("automate", "stdio"), 0, true, false, "l6:leavese") -check(qgrep("^0:0:l:0:", "stdout")) +check(qgrep("0:l:1:0$", "stdout")) writefile("allow-automate.lua", "function get_remote_automate_permitted(x, y, z) return true end") server = netsync.start({"--rcfile=allow-automate.lua"}) -check(mtn2("automate", "remote_stdio", server.address), 0, true, false, - "l17:interface_versione") -check(qgrep("^0:0:l:", "stdout")) +check( + nil ~= + tonumber(run_remote_stdio(server, "l17:interface_versione", 0, 0, "m")) +) -check(mtn2("automate", "remote_stdio", server.address), 0, true, false, - "l17:interface_versionel6:leavese") -check(qgrep("^0:0:l:", "stdout")) -check(qgrep("^1:0:l:41:", "stdout")) +check( + nil ~= + tonumber(run_remote_stdio(server, "l17:interface_versionel6:leavese", 0, 0, "m")) +) -check(mtn2("automate", "remote_stdio", server.address), 0, true, false, - "l5:stdioe") -check(qgrep("can't be run", "stdout")) +check( + 41 == + string.len(run_remote_stdio(server, "l17:interface_versionel6:leavese", 0, 1, "m")) +) +local errors = run_remote_stdio(server, "l5:stdioe", 1, 0, "e") +check( + table.maxn(errors) == 1 and + errors[1] == "error: sorry, that can't be run remotely or over stdio" +) + server:stop() ============================================================ --- unit-tests/netcmd.cc c6c79bb5d3d732f97cc37e78250e12f9b25d9b81 +++ unit-tests/netcmd.cc 0ac50ab8ed6f14aaa3d2b6ecaca4eb62070e5b71 @@ -339,19 +339,15 @@ UNIT_TEST(functions) netcmd in_cmd(constants::netcmd_current_protocol_version); int in_cmd_num(3), out_cmd_num; - int in_err_code(9), out_err_code; char in_stream('k'), out_stream; string in_data("this is some packet data"), out_data; - out_cmd.write_automate_packet_cmd(in_cmd_num, in_err_code, - in_stream, in_data); + out_cmd.write_automate_packet_cmd(in_cmd_num, in_stream, in_data); string buf; do_netcmd_roundtrip(out_cmd, in_cmd, buf); - in_cmd.read_automate_packet_cmd(out_cmd_num, out_err_code, - out_stream, out_data); + in_cmd.read_automate_packet_cmd(out_cmd_num, out_stream, out_data); UNIT_TEST_CHECK(in_cmd_num == out_cmd_num); - UNIT_TEST_CHECK(in_err_code == out_err_code); UNIT_TEST_CHECK(in_stream == out_stream); UNIT_TEST_CHECK(in_data == out_data); L(FL("automate_packet_cmd test done, buffer was %d bytes") % buf.size());