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