# # # patch "cmd.cc" # from [79983ff3293f63d2626d4a8f73ffe90ad18cda4b] # to [8daafa921ab88dc3d17980c4e404c162bea020c1] # # patch "cmd.hh" # from [5998c5301309d79ea9417b5131af27d9808a2138] # to [a51a739713b8b2dbbf2dd6ffb9aa71bd201bdbb9] # # patch "cmd_automate.cc" # from [a95cf1406ece9ee3bf8f1d316a8da35e66f7e7ee] # to [aa4a56a485bdd740054af3ddc6a7a6ee3ab6674a] # # patch "commands.hh" # from [7307dae6bcaa384018604f6a7571a32267d4aed5] # to [2172ef5bb183521c3ed7f297517e2a2c94776687] # # patch "network/automate_session.cc" # from [1a373ea42046cead616d7a44968a2ea29eaed197] # to [d5da42fac971b0eac4d4bdf5ba0ece498e57576a] # ============================================================ --- cmd.cc 79983ff3293f63d2626d4a8f73ffe90ad18cda4b +++ cmd.cc 8daafa921ab88dc3d17980c4e404c162bea020c1 @@ -27,6 +27,7 @@ using std::ostream; using std::string; using std::vector; using std::ostream; +using std::make_pair; using std::set; // @@ -231,6 +232,54 @@ namespace commands { ui.set_tick_write_stdio(); } + std::pair + automate_stdio_shared_body(app_state & app, + std::vector const & cmdline, + std::vector > + const & params, + std::ostream & os, + boost::function init_fn, + boost::function pre_exec_fn) + { + options original_opts = app.opts; + automate const * acmd = 0; + command_id id; + try + { + if (init_fn) + init_fn(); + commands::automate_stdio_shared_setup(app, cmdline, params, id, acmd); + } + catch (option::option_error & e) + { + return make_pair(1, e.what()); + } + catch (recoverable_failure & f) + { + return make_pair(1, f.what()); + } + if (pre_exec_fn) + pre_exec_fn(id); + try + { + // as soon as a command requires a workspace, this is set to true + workspace::used = false; + + acmd->exec_from_automate(app, id, app.opts.args, os); + + // usually, if a command succeeds, any of its workspace-relevant + // options are saved back to _MTN/options, this shouldn't be + // any different here + workspace::maybe_set_options(app.opts, app.lua); + } + catch (recoverable_failure & f) + { + return make_pair(2, f.what()); + } + app.opts = original_opts; + return make_pair(0, string()); + } + // monotone.cc calls this function after option processing. void process(app_state & app, command_id const & ident, args_vector const & args) ============================================================ --- cmd.hh 5998c5301309d79ea9417b5131af27d9808a2138 +++ cmd.hh a51a739713b8b2dbbf2dd6ffb9aa71bd201bdbb9 @@ -113,8 +113,14 @@ namespace commands command_id const & execid, args_vector const & args, std::ostream & output) const = 0; - friend class automate_stdio; - friend class ::automate_session; + friend std::pair + automate_stdio_shared_body(app_state & app, + std::vector const & cmdline, + std::vector > + const & params, + std::ostream & os, + boost::function init_fn, + boost::function pre_exec_fn); public: automate(std::string const & name, ============================================================ --- cmd_automate.cc a95cf1406ece9ee3bf8f1d316a8da35e66f7e7ee +++ cmd_automate.cc aa4a56a485bdd740054af3ddc6a7a6ee3ab6674a @@ -203,6 +203,24 @@ static void out_of_band_to_automate_stre // Error conditions: Errors encountered by the commands run only set // the error code in the output for that command. Malformed input // results in exit with a non-zero return value and an error message. + +class done_reading_input {}; +// lambda expressions would be really nice right about now +// even the ability to use local classes as template arguments would help +class local_stdio_pre_fn { + automate_reader & ar; + vector & cmdline; + vector > & params; +public: + local_stdio_pre_fn(automate_reader & a, vector & c, + vector > & p) + : ar(a), cmdline(c), params(p) + { } + void operator()() { + if (!ar.get_command(params, cmdline)) + throw done_reading_input(); + } +}; CMD_AUTOMATE_NO_STDIO(stdio, "", N_("Automates several commands in one run"), "", @@ -217,10 +235,6 @@ CMD_AUTOMATE_NO_STDIO(stdio, "", // immediately if a version discrepancy exists db.ensure_open(); - // disable user prompts, f.e. for password decryption - app.opts.non_interactive = true; - options original_opts = app.opts; - automate_ostream os(output, app.opts.automate_stdio_size); automate_reader ar(std::cin); @@ -234,61 +248,24 @@ CMD_AUTOMATE_NO_STDIO(stdio, "", while (true) { - automate const * acmd = 0; - command_id id; - - // this should match what's in network/automate_session.cc::do_work() - // - // stdio decoding errors should be noted with errno 1, - // errno 2 is reserved for errors from the commands itself try { - if (!ar.get_command(params, cmdline)) - break; - - commands::automate_stdio_shared_setup(app, cmdline, params, - id, acmd); + pair err + = commands::automate_stdio_shared_body(app, cmdline, params, os, + local_stdio_pre_fn(ar, cmdline, params), + boost::function()); + if (err.first != 0) + os.write_out_of_band('e', err.second); + os.end_cmd(err.first); + if (err.first == 1) + ar.reset(); } - // FIXME: we need to re-package and rethrow this special exception - // since it is not based on informative_failure - catch (option::option_error & e) + catch (done_reading_input) { - os.write_out_of_band('e', e.what()); - os.end_cmd(1); - ar.reset(); - continue; + break; } - catch (recoverable_failure & f) - { - os.write_out_of_band('e', f.what()); - os.end_cmd(1); - ar.reset(); - continue; - } - - try - { - // as soon as a command requires a workspace, this is set to true - workspace::used = false; - - acmd->exec_from_automate(app, id, app.opts.args, os); - os.end_cmd(0); - - // usually, if a command succeeds, any of its workspace-relevant - // options are saved back to _MTN/options, this shouldn't be - // any different here - workspace::maybe_set_options(app.opts, app.lua); - } - catch (recoverable_failure & f) - { - os.write_out_of_band('e', f.what()); - os.end_cmd(2); - } - - // restore app.opts - app.opts = original_opts; } - global_sanity.set_out_of_band_handler(); + global_sanity.set_out_of_band_handler(); } LUAEXT(change_workspace, ) ============================================================ --- commands.hh 7307dae6bcaa384018604f6a7571a32267d4aed5 +++ commands.hh 2172ef5bb183521c3ed7f297517e2a2c94776687 @@ -10,6 +10,8 @@ #ifndef __COMMANDS_HH__ #define __COMMANDS_HH__ +#include + #include "vector.hh" #include "options.hh" class app_state; @@ -48,6 +50,14 @@ namespace commands { command_id & id, /* reference-to-pointer here is intentional */ automate const * & acmd); + std::pair + automate_stdio_shared_body(app_state & app, + std::vector const & cmdline, + std::vector > + const & params, + std::ostream & os, + boost::function init_fn, + boost::function pre_exec_fn); void process(app_state & app, command_id const & ident, args_vector const & args); options::options_type command_options(command_id const & ident); ============================================================ --- network/automate_session.cc 1a373ea42046cead616d7a44968a2ea29eaed197 +++ network/automate_session.cc d5da42fac971b0eac4d4bdf5ba0ece498e57576a @@ -109,6 +109,36 @@ void automate_session::write_automate_pa write_netcmd(net_cmd); } + +// lambda expressions would be really nice right about now +// even the ability to use local classes as template arguments would help +class remote_stdio_pre_fn { + app_state & app; + key_identity_info const & remote_identity; + vector const & cmdline; + vector > const & params; +public: + remote_stdio_pre_fn(app_state & app, key_identity_info const & ri, + vector const & c, + vector > const & p) + : app(app), remote_identity(ri), cmdline(c), params(p) + { } + void operator()() { + E(app.lua.hook_get_remote_automate_permitted(remote_identity, + cmdline, params), + origin::user, + F("Sorry, you aren't allowed to do that.")); + } +}; +class remote_stdio_log_fn { + string peer; +public: + remote_stdio_log_fn(string const & p) : peer(p) { } + void operator()(commands::command_id const & id) { + L(FL("Executing %s for remote peer %s") + % join_words(id) % peer); + } +}; bool automate_session::do_work(transaction_guard & guard, netcmd const * const cmd_in) { @@ -139,10 +169,6 @@ bool automate_session::do_work(transacti } case automate_command_cmd: { - // disable user prompts, f.e. for password decryption - app.opts.non_interactive = true; - options original_opts = app.opts; - vector > params; vector cmdline; cmd_in->read_automate_command_cmd(cmdline, params); @@ -150,77 +176,21 @@ bool automate_session::do_work(transacti global_sanity.set_out_of_band_handler(&out_of_band_to_netcmd, this); - automate const * acmd = 0; - command_id id; - ostringstream oss; - int errcode = 0; - // FIXME: what follows is largely duplicated - // in cmd_automate.cc::CMD(stdio) - // - // stdio decoding errors should be noted with errno 1, - // errno 2 is reserved for errors from the commands itself - try - { - E(app.lua.hook_get_remote_automate_permitted(remote_identity, - cmdline, - params), - origin::user, - F("Sorry, you aren't allowed to do that.")); - - commands::automate_stdio_shared_setup(app, cmdline, params, - id, acmd); - - L(FL("Executing %s for remote peer %s") - % join_words(id) % get_peer()); - } - // FIXME: we need to re-package and rethrow this special exception - // since it is not based on informative_failure - catch (option::option_error & e) - { - errcode = 1; - write_automate_packet_cmd('e', e.what()); - } - catch (recoverable_failure & f) - { - errcode = 1; - write_automate_packet_cmd('e', f.what()); - } - - if (errcode == 0) - { - try - { - // as soon as a command requires a workspace, this is set to true - workspace::used = false; - - acmd->exec_from_automate(app, id, app.opts.args, oss); - - // usually, if a command succeeds, any of its workspace-relevant - // options are saved back to _MTN/options, this shouldn't be - // any different here - workspace::maybe_set_options(app.opts, app.lua); - } - catch (recoverable_failure & f) - { - errcode = 2; - write_automate_packet_cmd('e', f.what()); - } - - // restore app.opts - app.opts = original_opts; - } - + pair err + = commands::automate_stdio_shared_body(app, cmdline, params, oss, + remote_stdio_pre_fn(app, remote_identity, cmdline, params), + remote_stdio_log_fn(get_peer())); + if (err.first != 0) + write_automate_packet_cmd('e', err.second); if (!oss.str().empty()) write_automate_packet_cmd('m', oss.str()); + write_automate_packet_cmd('l', lexical_cast(err.first)); - write_automate_packet_cmd('l', lexical_cast(errcode)); - global_sanity.set_out_of_band_handler(); return true; - } case automate_packet_cmd: {