monotone

monotone Mtn Source Tree

Root/cmd_automate.cc

1// Copyright (C) 2002, 2008 Graydon Hoare <graydon@pobox.com>
2//
3// This program is made available under the GNU GPL version 2.0 or
4// greater. See the accompanying file COPYING for details.
5//
6// This program is distributed WITHOUT ANY WARRANTY; without even the
7// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8// PURPOSE.
9
10#include "base.hh"
11#include <iostream>
12#include <sstream>
13#include <map>
14
15#include "cmd.hh"
16#include "app_state.hh"
17#include "ui.hh"
18#include "lua.hh"
19#include "lua_hooks.hh"
20#include "database.hh"
21
22using std::istream;
23using std::make_pair;
24using std::map;
25using std::ostream;
26using std::ostringstream;
27using std::pair;
28using std::set;
29using std::string;
30using std::vector;
31
32CMD_GROUP(automate, "automate", "au", CMD_REF(automation),
33 N_("Interface for scripted execution"),
34 "");
35
36namespace commands {
37 automate::automate(string const & name,
38 string const & params,
39 string const & abstract,
40 string const & desc,
41 options::options_type const & opts) :
42 command(name, "", CMD_REF(automate), false, false, params, abstract,
43 desc, true, opts, false)
44 {
45 }
46
47 void
48 automate::exec(app_state & app,
49 command_id const & execid,
50 args_vector const & args,
51 std::ostream & output) const
52 {
53 make_io_binary();
54 exec_from_automate(app, execid, args, output);
55 }
56
57 void
58 automate::exec(app_state & app,
59 command_id const & execid,
60 args_vector const & args) const
61 {
62 exec(app, execid, args, std::cout);
63 }
64}
65
66static string const interface_version = "7.0";
67// Major or minor number only increments once for each monotone release;
68// check the most recent release before incrementing this.
69
70// Name: interface_version
71// Arguments: none
72// Added in: 0.0
73// Purpose: Prints version of automation interface. Major number increments
74// whenever a backwards incompatible change is made; minor number increments
75// whenever any change is made (but is reset when major number
76// increments).
77// Output format: "<decimal number>.<decimal number>\n". Always matches
78// "[0-9]+\.[0-9]+\n".
79// Error conditions: None.
80CMD_AUTOMATE(interface_version, "",
81 N_("Prints the automation interface's version"),
82 "",
83 options::opts::none)
84{
85 N(args.size() == 0,
86 F("no arguments needed"));
87
88 output << interface_version << '\n';
89}
90
91// Name: stdio
92// Arguments: none
93// Added in: 1.0
94// Purpose: Allow multiple automate commands to be run from one instance
95// of monotone.
96//
97// Input format: The input is a series of lines of the form
98// 'l'<size>':'<string>[<size>':'<string>...]'e', with characters
99// after the 'e' of one command, but before the 'l' of the next ignored.
100// This space is reserved, and should not contain characters other
101// than '\n'.
102// Example:
103// l6:leavese
104// l7:parents40:0e3171212f34839c2e3263e7282cdeea22fc5378e
105//
106// Output format: <command number>:<err code>:<last?>:<size>:<output>
107// <command number> is a decimal number specifying which command
108// this output is from. It is 0 for the first command, and increases
109// by one each time.
110// <err code> is 0 for success, 1 for a syntax error, and 2 for any
111// other error.
112// <last?> is 'l' if this is the last piece of output for this command,
113// and 'm' if there is more output to come.
114// <size> is the number of bytes in the output.
115// <output> is the output of the command.
116// Example:
117// 0:0:l:205:0e3171212f34839c2e3263e7282cdeea22fc5378
118// 1f4ef73c3e056883c6a5ff66728dd764557db5e6
119// 2133c52680aa2492b18ed902bdef7e083464c0b8
120// 23501f8afd1f9ee037019765309b0f8428567f8a
121// 2c295fcf5fe20301557b9b3a5b4d437b5ab8ec8c
122// 1:0:l:41:7706a422ccad41621c958affa999b1a1dd644e79
123//
124// Error conditions: Errors encountered by the commands run only set
125// the error code in the output for that command. Malformed input
126// results in exit with a non-zero return value and an error message.
127
128// automate_streambuf and automate_ostream are so we can dump output at a
129// set length, rather than waiting until we have all of the output.
130
131
132class automate_reader
133{
134 istream & in;
135 enum location {opt, cmd, none, eof};
136 location loc;
137 bool get_string(std::string & out)
138 {
139 out.clear();
140 if (loc == none || loc == eof)
141 {
142 return false;
143 }
144 size_t size(0);
145 char c;
146 read(&c, 1);
147 if (c == 'e')
148 {
149 loc = none;
150 return false;
151 }
152 while(c <= '9' && c >= '0')
153 {
154 size = (size*10)+(c-'0');
155 read(&c, 1);
156 }
157 E(c == ':',
158 F("Bad input to automate stdio: expected ':' after string size"));
159 char *str = new char[size];
160 size_t got = 0;
161 while(got < size)
162 {
163 int n = read(str+got, size-got);
164 got += n;
165 }
166 out = std::string(str, size);
167 delete[] str;
168 L(FL("Got string '%s'") % out);
169 return true;
170 }
171 std::streamsize read(char *buf, size_t nbytes, bool eof_ok = false)
172 {
173 std::streamsize rv;
174
175 rv = in.rdbuf()->sgetn(buf, nbytes);
176
177 E(eof_ok || rv > 0, F("Bad input to automate stdio: unexpected EOF"));
178 return rv;
179 }
180 void go_to_next_item()
181 {
182 if (loc == eof)
183 return;
184 string starters("ol");
185 string whitespace(" \r\n\t");
186 string foo;
187 while (loc != none)
188 get_string(foo);
189 char c('e');
190 do
191 {
192 if (read(&c, 1, true) == 0)
193 {
194 loc = eof;
195 return;
196 }
197 }
198 while (whitespace.find(c) != std::string::npos);
199 switch (c)
200 {
201 case 'o': loc = opt; break;
202 case 'l': loc = cmd; break;
203 default:
204 E(false,
205 F("Bad input to automate stdio: unknown start token '%c'") % c);
206 }
207 }
208public:
209 automate_reader(istream & is) : in(is), loc(none)
210 {}
211 bool get_command(vector<pair<string, string> > & params,
212 vector<string> & cmdline)
213 {
214 params.clear();
215 cmdline.clear();
216 if (loc == none)
217 go_to_next_item();
218 if (loc == eof)
219 return false;
220 else if (loc == opt)
221 {
222 string key, val;
223 while(get_string(key) && get_string(val))
224 params.push_back(make_pair(key, val));
225 go_to_next_item();
226 }
227 E(loc == cmd, F("Bad input to automate stdio: expected '%c' token") % cmd);
228 string item;
229 while (get_string(item))
230 {
231 cmdline.push_back(item);
232 }
233 return true;
234 }
235};
236
237struct automate_streambuf : public std::streambuf
238{
239private:
240 size_t _bufsize;
241 std::ostream *out;
242 automate_reader *in;
243 int cmdnum;
244 int err;
245public:
246 automate_streambuf(size_t bufsize)
247 : std::streambuf(), _bufsize(bufsize), out(0), in(0), cmdnum(0), err(0)
248 {
249 char *inbuf = new char[_bufsize];
250 setp(inbuf, inbuf + _bufsize);
251 }
252 automate_streambuf(std::ostream & o, size_t bufsize)
253 : std::streambuf(), _bufsize(bufsize), out(&o), in(0), cmdnum(0), err(0)
254 {
255 char *inbuf = new char[_bufsize];
256 setp(inbuf, inbuf + _bufsize);
257 }
258 automate_streambuf(automate_reader & i, size_t bufsize)
259 : std::streambuf(), _bufsize(bufsize), out(0), in(&i), cmdnum(0), err(0)
260 {
261 char *inbuf = new char[_bufsize];
262 setp(inbuf, inbuf + _bufsize);
263 }
264 ~automate_streambuf()
265 {}
266
267 void set_err(int e)
268 {
269 sync();
270 err = e;
271 }
272
273 void end_cmd()
274 {
275 _M_sync(true);
276 ++cmdnum;
277 err = 0;
278 }
279
280 virtual int sync()
281 {
282 _M_sync();
283 return 0;
284 }
285
286 void _M_sync(bool end = false)
287 {
288 if (!out)
289 {
290 setp(pbase(), pbase() + _bufsize);
291 return;
292 }
293 int num = pptr() - pbase();
294 if (num || end)
295 {
296 (*out) << cmdnum << ':'
297 << err << ':'
298 << (end?'l':'m') << ':'
299 << num << ':' << std::string(pbase(), num);
300 setp(pbase(), pbase() + _bufsize);
301 out->flush();
302 }
303 }
304 int_type
305 overflow(int_type c = traits_type::eof())
306 {
307 sync();
308 sputc(c);
309 return 0;
310 }
311};
312
313struct automate_ostream : public std::ostream
314{
315 automate_streambuf _M_autobuf;
316
317 automate_ostream(std::ostream &out, size_t blocksize)
318 : std::ostream(NULL),
319 _M_autobuf(out, blocksize)
320 { this->init(&_M_autobuf); }
321
322 ~automate_ostream()
323 {}
324
325 automate_streambuf *
326 rdbuf() const
327 { return const_cast<automate_streambuf *>(&_M_autobuf); }
328
329 void set_err(int e)
330 { _M_autobuf.set_err(e); }
331
332 void end_cmd()
333 { _M_autobuf.end_cmd(); }
334};
335
336CMD_AUTOMATE(stdio, "",
337 N_("Automates several commands in one run"),
338 "",
339 options::opts::automate_stdio_size)
340{
341 N(args.size() == 0,
342 F("no arguments needed"));
343
344 database db(app);
345
346 // initialize the database early so any calling process is notified
347 // immediately if a version discrepancy exists
348 db.ensure_open();
349
350 automate_ostream os(output, app.opts.automate_stdio_size);
351 automate_reader ar(std::cin);
352 vector<pair<string, string> > params;
353 vector<string> cmdline;
354 while(ar.get_command(params, cmdline))//while(!EOF)
355 {
356 args_vector args;
357 vector<string>::iterator i = cmdline.begin();
358 E(i != cmdline.end(),
359 F("Bad input to automate stdio: command name is missing"));
360 for (; i != cmdline.end(); ++i)
361 {
362 args.push_back(arg_type(*i));
363 }
364 try
365 {
366 options::options_type opts;
367 opts = options::opts::all_options() - options::opts::globals();
368 opts.instantiate(&app.opts).reset();
369
370 command_id id;
371 for (args_vector::const_iterator iter = args.begin();
372 iter != args.end(); iter++)
373 id.push_back(utf8((*iter)()));
374
375 if (!id.empty())
376 {
377 I(!args.empty());
378
379 set< command_id > matches =
380 CMD_REF(automate)->complete_command(id);
381
382 if (matches.size() == 0)
383 {
384 N(false, F("no completions for this command"));
385 }
386 else if (matches.size() > 1)
387 {
388 N(false, F("multiple completions possible for this command"));
389 }
390
391 id = *matches.begin();
392
393 I(args.size() >= id.size());
394 for (command_id::size_type i = 0; i < id.size(); i++)
395 args.erase(args.begin());
396
397 command const * cmd = CMD_REF(automate)->find_command(id);
398 I(cmd != NULL);
399 automate const * acmd = reinterpret_cast< automate const * >(cmd);
400
401 opts = options::opts::globals() | acmd->opts();
402 opts.instantiate(&app.opts).from_key_value_pairs(params);
403 acmd->exec_from_automate(app, id, args, os);
404 }
405 else
406 opts.instantiate(&app.opts).from_key_value_pairs(params);
407 }
408 catch(informative_failure & f)
409 {
410 os.set_err(2);
411 //Do this instead of printing f.what directly so the output
412 //will be split into properly-sized blocks automatically.
413 os<<f.what();
414 }
415 os.end_cmd();
416 }
417}
418
419LUAEXT(mtn_automate, )
420{
421 args_vector args;
422 std::stringstream output;
423 bool result = true;
424 std::stringstream & os = output;
425
426 try
427 {
428 app_state* app_p = get_app_state(L);
429 I(app_p != NULL);
430 I(app_p->lua.check_lua_state(L));
431 E(app_p->mtn_automate_allowed,
432 F("It is illegal to call the mtn_automate() lua extension,\n"
433 "unless from a command function defined by register_command()."));
434
435 // don't allow recursive calls
436 app_p->mtn_automate_allowed = false;
437
438 // automate_ostream os(output, app_p->opts.automate_stdio_size);
439
440 int n = lua_gettop(L);
441
442 E(n > 0, F("Bad input to mtn_automate() lua extension: command name is missing"));
443
444 L(FL("Starting call to mtn_automate lua hook"));
445
446 for (int i=1; i<=n; i++)
447 {
448 arg_type next_arg(luaL_checkstring(L, i));
449 L(FL("arg: %s")%next_arg());
450 args.push_back(next_arg);
451 }
452
453 commands::command_id id;
454 for (args_vector::const_iterator iter = args.begin();
455 iter != args.end(); iter++)
456 id.push_back(utf8((*iter)()));
457
458 E(!id.empty(), F("no command found"));
459
460 set< commands::command_id > matches =
461 CMD_REF(automate)->complete_command(id);
462
463 if (matches.size() == 0)
464 {
465 N(false, F("no completions for this command"));
466 }
467 else if (matches.size() > 1)
468 {
469 N(false, F("multiple completions possible for this command"));
470 }
471
472 id = *matches.begin();
473
474 I(args.size() >= id.size());
475 for (commands::command_id::size_type i = 0; i < id.size(); i++)
476 args.erase(args.begin());
477
478 commands::command const * cmd = CMD_REF(automate)->find_command(id);
479 I(cmd != NULL);
480 commands::automate const * acmd = reinterpret_cast< commands::automate const * >(cmd);
481
482 acmd->exec(*app_p, id, args, os);
483
484 // allow further calls
485 app_p->mtn_automate_allowed = true;
486 }
487 catch(informative_failure & f)
488 {
489 // informative failures are passed back to the caller
490 result = false;
491 L(FL("Informative failure caught inside lua call to mtn_automate: %s") % f.what());
492 os.flush();
493 output.flush();
494 output.str().clear();
495 os << f.what();
496 }
497 catch (std::logic_error & e)
498 {
499 // invariant failures are permanent
500 result = false;
501 ui.fatal(e.what());
502 lua_pushstring(L, e.what());
503 lua_error(L);
504 }
505
506 os.flush();
507
508 lua_pushboolean(L, result);
509 lua_pushstring(L, output.str().c_str());
510 return 2;
511}
512
513// Local Variables:
514// mode: C++
515// fill-column: 76
516// c-file-style: "gnu"
517// indent-tabs-mode: nil
518// End:
519// 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