monotone

monotone Mtn Source Tree

Root/src/cmd_automate.cc

1// Copyright (C) 2002, 2008 Graydon Hoare <graydon@pobox.com>
2// 2010 Stephen Leake <stephen_leake@stephe-leake.org>
3//
4// This program is made available under the GNU GPL version 2.0 or
5// greater. See the accompanying file COPYING for details.
6//
7// This program is distributed WITHOUT ANY WARRANTY; without even the
8// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
9// PURPOSE.
10
11#include "base.hh"
12#include <iostream>
13#include <sstream>
14#include <map>
15#include <unistd.h>
16
17#include "cmd.hh"
18#include "app_state.hh"
19#include "automate_ostream.hh"
20#include "automate_reader.hh"
21#include "automate_stdio_helpers.hh"
22#include "constants.hh"
23#include "options_applicator.hh"
24#include "ui.hh"
25#include "lua.hh"
26#include "lua_hooks.hh"
27#include "database.hh"
28#include "work.hh"
29
30using std::istream;
31using std::make_pair;
32using std::map;
33using std::ostream;
34using std::ostringstream;
35using std::pair;
36using std::set;
37using std::string;
38using std::vector;
39
40CMD_GROUP(automate, "automate", "au", CMD_REF(automation),
41 N_("Interface for scripted execution"),
42 "");
43
44namespace commands {
45 automate::automate(string const & name,
46 bool stdio_ok,
47 bool hidden,
48 string const & params,
49 string const & abstract,
50 string const & desc,
51 options::options_type const & opts) :
52 command(name, "", CMD_REF(automate), false, hidden, params, abstract,
53 // We set use_workspace_options true, because all automate
54 // commands need a database, and they expect to get the database
55 // name from the workspace options, even if they don't need a
56 // workspace for anything else.
57 desc, true, opts, false),
58 stdio_ok(stdio_ok)
59 {
60 }
61
62 void
63 automate::exec(app_state & app,
64 command_id const & execid,
65 args_vector const & args,
66 std::ostream & output) const
67 {
68 make_io_binary();
69 setlocale(LC_ALL,"POSIX");
70 exec_from_automate(app, execid, args, output);
71 }
72
73 void
74 automate::exec(app_state & app,
75 command_id const & execid,
76 args_vector const & args) const
77 {
78 exec(app, execid, args, std::cout);
79 }
80
81 bool
82 automate::can_run_from_stdio() const
83 {
84 return stdio_ok;
85 }
86}
87
88// This number is only raised once, during the process of releasing a new
89// version of monotone, by the release manager. For more details, see
90// point (4) in notes/release-checklist.txt
91static string const interface_version = "13.0";
92
93// This number determines the format version of the stdio packet format.
94// The original format which came without a version notification was "1".
95static string const stdio_format_version = "2";
96
97// Name: interface_version
98// Arguments: none
99// Added in: 0.0
100// Purpose: Prints version of automation interface. Major number increments
101// whenever a backwards incompatible change is made; minor number increments
102// whenever any change is made (but is reset when major number
103// increments).
104// Output format: "<decimal number>.<decimal number>\n". Always matches
105// "[0-9]+\.[0-9]+\n".
106// Error conditions: None.
107CMD_AUTOMATE(interface_version, "",
108 N_("Prints the automation interface's version"),
109 "",
110 options::opts::none)
111{
112 E(args.empty(), origin::user,
113 F("no arguments needed"));
114
115 output << interface_version << '\n';
116}
117
118// these headers are outputted before any other output for stdio and remote_stdio
119void commands::get_stdio_headers(std::vector<std::pair<std::string,std::string> > & headers)
120{
121 headers.push_back(make_pair("format-version", stdio_format_version));
122}
123
124// Name: bandtest
125// Arguments: { info | warning | error | fatal | ticker }
126// Added in: FIXME
127// Purpose: Emulates certain kinds of diagnostic / UI messages for debugging
128// and testing purposes
129// Output format: None
130// Error conditions: None.
131CMD_AUTOMATE_HIDDEN(bandtest, "{ info | warning | error | ticker }",
132 N_("Emulates certain kinds of diagnostic / UI messages "
133 "for debugging and testing purposes, such as stdio"),
134 "",
135 options::opts::none)
136{
137 E(args.size() == 1, origin::user,
138 F("wrong argument count"));
139
140 std::string type = args.at(0)();
141 L(FL("running bandtest %s") % type);
142 if (type.compare("info") == 0)
143 P(F("this is an informational message"));
144 else if (type.compare("warning") == 0)
145 W(F("this is a warning"));
146 else if (type.compare("error") == 0)
147 E(false, origin::user, F("this is an error message"));
148 else if (type.compare("ticker") == 0)
149 {
150 ticker first("fake ticker (not fixed)", "f1", 3);
151 ticker second("fake ticker (fixed)", "f2", 5);
152
153 int max = 20;
154 second.set_total(max);
155
156 for (int i=0; i<max; i++)
157 {
158 first+=3;
159 ++second;
160 usleep(100000); // 100ms
161 }
162 }
163 else
164 I(false);
165}
166
167
168static void out_of_band_to_automate_streambuf(char channel, std::string const& text, void *opaque)
169{
170 reinterpret_cast<automate_ostream*>(opaque)->write_out_of_band(channel, text);
171}
172
173// Name: stdio
174// Arguments: none
175// Added in: 1.0
176// Purpose: Allow multiple automate commands to be run from one instance
177// of monotone.
178//
179// Input format: The input is a series of lines of the form
180// 'l'<size>':'<string>[<size>':'<string>...]'e', with characters
181// after the 'e' of one command, but before the 'l' of the next ignored.
182// This space is reserved, and should not contain characters other
183// than '\n'.
184// Example:
185// l6:leavese
186// l7:parents40:0e3171212f34839c2e3263e7282cdeea22fc5378e
187//
188// Output format: <command number>:<err code>:<stream>:<size>:<output>
189// <command number> is a decimal number specifying which command
190// this output is from. It is 0 for the first command, and increases
191// by one each time.
192// <err code> is 0 for success, 1 for a syntax error, and 2 for any
193// other error.
194// <stream> is 'l' if this is the last piece of output for this command,
195// and 'm' if there is more output to come. Otherwise, 'e', 'p' and 'w'
196// notify the caller about errors, informational messages and warnings.
197// A special type 't' outputs progress information for long-term actions.
198// <size> is the number of bytes in the output.
199// <output> is the output of the command.
200// Example:
201// 0:0:l:205:0e3171212f34839c2e3263e7282cdeea22fc5378
202// 1f4ef73c3e056883c6a5ff66728dd764557db5e6
203// 2133c52680aa2492b18ed902bdef7e083464c0b8
204// 23501f8afd1f9ee037019765309b0f8428567f8a
205// 2c295fcf5fe20301557b9b3a5b4d437b5ab8ec8c
206// 1:0:l:41:7706a422ccad41621c958affa999b1a1dd644e79
207//
208// Error conditions: Errors encountered by the commands run only set
209// the error code in the output for that command. Malformed input
210// results in exit with a non-zero return value and an error message.
211
212class done_reading_input {};
213// lambda expressions would be really nice right about now
214// even the ability to use local classes as template arguments would help
215class local_stdio_pre_fn {
216 automate_reader & ar;
217 vector<string> & cmdline;
218 vector<pair<string,string> > & params;
219public:
220 local_stdio_pre_fn(automate_reader & a, vector<string> & c,
221 vector<pair<string,string> > & p)
222 : ar(a), cmdline(c), params(p)
223 { }
224 void operator()() {
225 if (!ar.get_command(params, cmdline))
226 throw done_reading_input();
227 }
228};
229CMD_AUTOMATE_NO_STDIO(stdio, "",
230 N_("Automates several commands in one run"),
231 "",
232 options::opts::automate_stdio_size)
233{
234 E(args.empty(), origin::user,
235 F("no arguments needed"));
236
237 database db(app);
238
239 // initialize the database early so any calling process is notified
240 // immediately if a version discrepancy exists
241 db.ensure_open();
242
243 long packet_size = constants::default_stdio_packet_size;
244 if (app.opts.automate_stdio_size_given)
245 packet_size = app.opts.automate_stdio_size;
246 automate_ostream os(output, packet_size);
247 automate_reader ar(std::cin);
248
249 std::vector<std::pair<std::string, std::string> > headers;
250 commands::get_stdio_headers(headers);
251 os.write_headers(headers);
252
253 vector<pair<string, string> > params;
254 vector<string> cmdline;
255 global_sanity.set_out_of_band_handler(&out_of_band_to_automate_streambuf, &os);
256
257 while (true)
258 {
259 try
260 {
261 pair<int, string> err = automate_stdio_helpers::
262 automate_stdio_shared_body(app, cmdline, params, os,
263 local_stdio_pre_fn(ar, cmdline, params),
264 boost::function<void(command_id const &)>());
265 if (err.first != 0)
266 os.write_out_of_band('e', err.second);
267 os.end_cmd(err.first);
268 if (err.first == 1)
269 ar.reset();
270 }
271 catch (done_reading_input)
272 {
273 break;
274 }
275 }
276 global_sanity.set_out_of_band_handler();
277}
278
279LUAEXT(change_workspace, )
280{
281 const system_path ws(luaL_checkstring(LS, -1), origin::user);
282 app_state* app_p = get_app_state(LS);
283
284 try
285 {
286 go_to_workspace(ws);
287 }
288 catch (recoverable_failure & f)
289 {
290 string msg(f.what());
291 lua_pushboolean(LS, false);
292 lua_pushlstring(LS, msg.data(), msg.size());
293 return 2;
294 }
295
296 // go_to_workspace doesn't check that it is a workspace, nor set workspace::found!
297 if (directory_is_workspace(ws))
298 {
299 workspace::found = true;
300 lua_pushboolean(LS, true);
301 return 1;
302 }
303 else
304 {
305 i18n_format msg(F("directory '%s' is not a workspace") % ws);
306 lua_pushboolean(LS, false);
307 lua_pushlstring(LS, msg.str().data(), msg.str().size());
308 return 2;
309 }
310}
311
312LUAEXT(mtn_automate, )
313{
314 std::stringstream output;
315 bool result = true;
316 std::stringstream & os = output;
317
318 try
319 {
320 app_state* app_p = get_app_state(LS);
321 I(app_p != NULL);
322 I(app_p->lua.check_lua_state(LS));
323 E(app_p->mtn_automate_allowed, origin::user,
324 F("it is illegal to call the mtn_automate() lua extension,\n"
325 "unless from a command function defined by register_command()."));
326
327 // don't allow recursive calls
328 app_p->mtn_automate_allowed = false;
329
330 int n = lua_gettop(LS);
331
332 E(n > 0, origin::user,
333 F("bad input to mtn_automate() lua extension: command name is missing"));
334
335 L(FL("Starting call to mtn_automate lua hook"));
336
337 vector<string> args;
338 for (int i=1; i<=n; i++)
339 {
340 string next_arg(luaL_checkstring(LS, i));
341 L(FL("arg: %s") % next_arg);
342 args.push_back(next_arg);
343 }
344
345 commands::command_id id;
346 commands::automate const * cmd;
347
348 automate_stdio_helpers::
349 automate_stdio_shared_setup(*app_p, args, 0,
350 id, cmd,
351 automate_stdio_helpers::no_force_stdio_ticker);
352
353
354 commands::automate const * acmd
355 = dynamic_cast< commands::automate const * >(cmd);
356 I(acmd);
357
358
359 options_applicator oa(app_p->opts, options_applicator::for_automate_subcmd);
360
361 // as soon as a command requires a workspace, this is set to true
362 workspace::used = false;
363
364 acmd->exec(*app_p, id, app_p->opts.args, os);
365
366 // usually, if a command succeeds, any of its workspace-relevant
367 // options are saved back to _MTN/options, this shouldn't be
368 // any different here
369 workspace::maybe_set_options(app_p->opts, app_p->lua);
370
371 // allow further calls
372 app_p->mtn_automate_allowed = true;
373 }
374 catch(recoverable_failure & f)
375 {
376 // informative failures are passed back to the caller
377 result = false;
378 L(FL("Informative failure caught inside lua call to mtn_automate: %s") % f.what());
379 os.flush();
380 output.flush();
381 output.str().clear();
382 os << f.what();
383 }
384 catch (std::logic_error & e)
385 {
386 // invariant failures are permanent
387 result = false;
388 ui.fatal(e.what());
389 lua_pushstring(LS, e.what());
390 lua_error(LS);
391 }
392
393 os.flush();
394
395 lua_pushboolean(LS, result);
396 lua_pushlstring(LS, output.str().data(), output.str().size());
397 return 2;
398}
399
400// Local Variables:
401// mode: C++
402// fill-column: 76
403// c-file-style: "gnu"
404// indent-tabs-mode: nil
405// End:
406// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status