monotone

monotone Mtn Source Tree

Root/cmd_automate.cc

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