monotone

monotone Mtn Source Tree

Root/lua_hooks.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 "config.h"
11
12extern "C" {
13#include <lua.h>
14#include <lualib.h>
15#include <lauxlib.h>
16}
17
18#include <boost/lexical_cast.hpp>
19#include <boost/filesystem/path.hpp>
20#include <boost/filesystem/operations.hpp>
21
22#include <set>
23#include <map>
24#include <fstream>
25
26#include "lua.hh"
27
28#include "app_state.hh"
29#include "file_io.hh"
30#include "lua_hooks.hh"
31#include "sanity.hh"
32#include "vocab.hh"
33#include "transforms.hh"
34#include "paths.hh"
35#include "uri.hh"
36#include "constants.hh"
37
38// defined in {std,test}_hooks.lua, converted
39#include "test_hooks.h"
40#include "std_hooks.h"
41
42using std::make_pair;
43using std::map;
44using std::pair;
45using std::set;
46using std::sort;
47using std::string;
48using std::vector;
49
50using boost::lexical_cast;
51
52static int panic_thrower(lua_State * st)
53{
54 throw oops("lua panic");
55}
56
57// this lets the lua callbacks (monotone_*_for_lua) have access to the
58// app_state the're associated with.
59// it was added so that the confdir (normally ~/.monotone) can be specified on
60// the command line (and so known only to the app_state), and still be
61// available to lua
62// please *don't* use it for complex things that can throw errors
63static map<lua_State*, app_state*> map_of_lua_to_app;
64
65extern "C"
66{
67 static int
68 monotone_get_confdir_for_lua(lua_State *L)
69 {
70 map<lua_State*, app_state*>::iterator i = map_of_lua_to_app.find(L);
71 if (i != map_of_lua_to_app.end())
72 {
73 system_path dir = i->second->get_confdir();
74 string confdir = dir.as_external();
75 lua_pushstring(L, confdir.c_str());
76 }
77 else
78 lua_pushnil(L);
79 return 1;
80 }
81}
82
83lua_hooks::lua_hooks()
84{
85 st = lua_open ();
86 I(st);
87
88 lua_atpanic (st, &panic_thrower);
89
90 luaopen_base(st);
91 luaopen_io(st);
92 luaopen_string(st);
93 luaopen_math(st);
94 luaopen_table(st);
95 luaopen_debug(st);
96
97 lua_register(st, "get_confdir", monotone_get_confdir_for_lua);
98 add_functions(st);
99
100 // Disable any functions we don't want. This is easiest
101 // to do just by running a lua string.
102 if (!run_string(st,
103 "os.execute = nil "
104 "io.popen = nil ",
105 string("<disabled dangerous functions>")))
106 throw oops("lua error while disabling existing functions");
107}
108
109lua_hooks::~lua_hooks()
110{
111 map<lua_State*, app_state*>::iterator i = map_of_lua_to_app.find(st);
112 if (st)
113 lua_close (st);
114 if (i != map_of_lua_to_app.end())
115 map_of_lua_to_app.erase(i);
116}
117
118void
119lua_hooks::set_app(app_state *_app)
120{
121 map_of_lua_to_app.insert(make_pair(st, _app));
122}
123
124
125
126#ifdef BUILD_UNIT_TESTS
127void
128lua_hooks::add_test_hooks()
129{
130 if (!run_string(st, test_hooks_constant, string("<test hooks>")))
131 throw oops("lua error while setting up testing hooks");
132}
133#endif
134
135void
136lua_hooks::add_std_hooks()
137{
138 if (!run_string(st, std_hooks_constant, string("<std hooks>")))
139 throw oops("lua error while setting up standard hooks");
140}
141
142void
143lua_hooks::default_rcfilename(system_path & file)
144{
145 map<lua_State*, app_state*>::iterator i = map_of_lua_to_app.find(st);
146 I(i != map_of_lua_to_app.end());
147 file = i->second->get_confdir() / "monotonerc";
148}
149
150void
151lua_hooks::workspace_rcfilename(bookkeeping_path & file)
152{
153 file = bookkeeping_root / "monotonerc";
154}
155
156
157void
158lua_hooks::load_rcfile(utf8 const & rc)
159{
160 I(st);
161 if (rc() != "-")
162 {
163 fs::path locpath(system_path(rc).as_external(), fs::native);
164 if (fs::exists(locpath) && fs::is_directory(locpath))
165 {
166 // directory, iterate over it, skipping subdirs, taking every filename,
167 // sorting them and loading in sorted order
168 fs::directory_iterator it(locpath);
169 vector<fs::path> arr;
170 while (it != fs::directory_iterator())
171 {
172 if (!fs::is_directory(*it))
173 arr.push_back(*it);
174 ++it;
175 }
176 sort(arr.begin(), arr.end());
177 for (vector<fs::path>::iterator i= arr.begin(); i != arr.end(); ++i)
178 {
179 load_rcfile(system_path(i->native_directory_string()), true);
180 }
181 return; // directory read, skip the rest ...
182 }
183 }
184 data dat;
185 L(FL("opening rcfile '%s'") % rc);
186 read_data_for_command_line(rc, dat);
187 N(run_string(st, dat(), rc().c_str()),
188 F("lua error while loading rcfile '%s'") % rc);
189 L(FL("'%s' is ok") % rc);
190}
191
192void
193lua_hooks::load_rcfile(any_path const & rc, bool required)
194{
195 I(st);
196 if (path_exists(rc))
197 {
198 L(FL("opening rcfile '%s'") % rc);
199 N(run_file(st, rc.as_external()),
200 F("lua error while loading '%s'") % rc);
201 L(FL("'%s' is ok") % rc);
202 }
203 else
204 {
205 N(!required, F("rcfile '%s' does not exist") % rc);
206 L(FL("skipping nonexistent rcfile '%s'") % rc);
207 }
208}
209
210// concrete hooks
211
212// nb: if you're hooking lua to return your passphrase, you don't care if we
213// keep a couple extra temporaries of your passphrase around.
214bool
215lua_hooks::hook_get_passphrase(rsa_keypair_id const & k, string & phrase)
216{
217 return Lua(st)
218 .func("get_passphrase")
219 .push_str(k())
220 .call(1,1)
221 .extract_classified_str(phrase)
222 .ok();
223}
224
225bool
226lua_hooks::hook_persist_phrase_ok()
227{
228 bool persist_ok = false;
229 bool executed_ok = Lua(st)
230 .func("persist_phrase_ok")
231 .call(0,1)
232 .extract_bool(persist_ok)
233 .ok();
234 return executed_ok && persist_ok;
235}
236
237bool
238lua_hooks::hook_expand_selector(string const & sel,
239 string & exp)
240{
241 return Lua(st)
242 .func("expand_selector")
243 .push_str(sel)
244 .call(1,1)
245 .extract_str(exp)
246 .ok();
247}
248
249bool
250lua_hooks::hook_expand_date(string const & sel,
251 string & exp)
252{
253 exp.clear();
254 bool res= Lua(st)
255 .func("expand_date")
256 .push_str(sel)
257 .call(1,1)
258 .extract_str(exp)
259 .ok();
260 return res && exp.size();
261}
262
263bool
264lua_hooks::hook_get_branch_key(cert_value const & branchname,
265 rsa_keypair_id & k)
266{
267 string key;
268 bool ok = Lua(st)
269 .func("get_branch_key")
270 .push_str(branchname())
271 .call(1,1)
272 .extract_str(key)
273 .ok();
274
275 k = key;
276 return ok;
277}
278
279bool
280lua_hooks::hook_get_author(cert_value const & branchname,
281 string & author)
282{
283 return Lua(st)
284 .func("get_author")
285 .push_str(branchname())
286 .call(1,1)
287 .extract_str(author)
288 .ok();
289}
290
291bool
292lua_hooks::hook_edit_comment(string const & commentary,
293 string const & user_log_message,
294 string & result)
295{
296 return Lua(st)
297 .func("edit_comment")
298 .push_str(commentary)
299 .push_str(user_log_message)
300 .call(2,1)
301 .extract_str(result)
302 .ok();
303}
304
305bool
306lua_hooks::hook_ignore_file(file_path const & p)
307{
308 bool ignore_it = false;
309 bool exec_ok = Lua(st)
310 .func("ignore_file")
311 .push_str(p.as_external())
312 .call(1,1)
313 .extract_bool(ignore_it)
314 .ok();
315 return exec_ok && ignore_it;
316}
317
318bool
319lua_hooks::hook_ignore_branch(string const & branch)
320{
321 bool ignore_it = false;
322 bool exec_ok = Lua(st)
323 .func("ignore_branch")
324 .push_str(branch)
325 .call(1,1)
326 .extract_bool(ignore_it)
327 .ok();
328 return exec_ok && ignore_it;
329}
330
331static inline bool
332shared_trust_function_body(Lua & ll,
333 set<rsa_keypair_id> const & signers,
334 hexenc<id> const & id,
335 cert_name const & name,
336 cert_value const & val)
337{
338 ll.push_table();
339
340 int k = 1;
341 for (set<rsa_keypair_id>::const_iterator v = signers.begin();
342 v != signers.end(); ++v)
343 {
344 ll.push_int(k);
345 ll.push_str((*v)());
346 ll.set_table();
347 ++k;
348 }
349
350 bool ok;
351 bool exec_ok = ll
352 .push_str(id())
353 .push_str(name())
354 .push_str(val())
355 .call(4, 1)
356 .extract_bool(ok)
357 .ok();
358
359 return exec_ok && ok;
360}
361
362bool
363lua_hooks::hook_get_revision_cert_trust(set<rsa_keypair_id> const & signers,
364 hexenc<id> const & id,
365 cert_name const & name,
366 cert_value const & val)
367{
368 Lua ll(st);
369 ll.func("get_revision_cert_trust");
370 return shared_trust_function_body(ll, signers, id, name, val);
371}
372
373bool
374lua_hooks::hook_get_manifest_cert_trust(set<rsa_keypair_id> const & signers,
375 hexenc<id> const & id,
376 cert_name const & name,
377 cert_value const & val)
378{
379 Lua ll(st);
380 ll.func("get_manifest_cert_trust");
381 return shared_trust_function_body(ll, signers, id, name, val);
382}
383
384bool
385lua_hooks::hook_accept_testresult_change(map<rsa_keypair_id, bool> const & old_results,
386 map<rsa_keypair_id, bool> const & new_results)
387{
388 Lua ll(st);
389 ll
390 .func("accept_testresult_change")
391 .push_table();
392
393 for (map<rsa_keypair_id, bool>::const_iterator i = old_results.begin();
394 i != old_results.end(); ++i)
395 {
396 ll.push_str(i->first());
397 ll.push_bool(i->second);
398 ll.set_table();
399 }
400
401 ll.push_table();
402
403 for (map<rsa_keypair_id, bool>::const_iterator i = new_results.begin();
404 i != new_results.end(); ++i)
405 {
406 ll.push_str(i->first());
407 ll.push_bool(i->second);
408 ll.set_table();
409 }
410
411 bool ok;
412 bool exec_ok = ll
413 .call(2, 1)
414 .extract_bool(ok)
415 .ok();
416
417 return exec_ok && ok;
418}
419
420
421
422bool
423lua_hooks::hook_merge3(file_path const & anc_path,
424 file_path const & left_path,
425 file_path const & right_path,
426 file_path const & merged_path,
427 data const & ancestor,
428 data const & left,
429 data const & right,
430 data & result)
431{
432 string res;
433 bool ok = Lua(st)
434 .func("merge3")
435 .push_str(anc_path.as_external())
436 .push_str(left_path.as_external())
437 .push_str(right_path.as_external())
438 .push_str(merged_path.as_external())
439 .push_str(ancestor())
440 .push_str(left())
441 .push_str(right())
442 .call(7,1)
443 .extract_str(res)
444 .ok();
445 result = res;
446 return ok;
447}
448
449bool
450lua_hooks::hook_external_diff(file_path const & path,
451 data const & data_old,
452 data const & data_new,
453 bool is_binary,
454 bool diff_args_provided,
455 string const & diff_args,
456 string const & oldrev,
457 string const & newrev)
458{
459 Lua ll(st);
460
461 ll
462 .func("external_diff")
463 .push_str(path.as_external());
464
465 if (oldrev.length() != 0)
466 ll.push_str(data_old());
467 else
468 ll.push_nil();
469
470 ll.push_str(data_new());
471
472 ll.push_bool(is_binary);
473
474 if (diff_args_provided)
475 ll.push_str(diff_args);
476 else
477 ll.push_nil();
478
479 ll.push_str(oldrev);
480 ll.push_str(newrev);
481
482 return ll.call(7,0).ok();
483}
484
485bool
486lua_hooks::hook_get_encloser_pattern(file_path const & path,
487 std::string & pattern)
488{
489 bool exec_ok
490 = Lua(st)
491 .func("get_encloser_pattern")
492 .push_str(path.as_external())
493 .call(1, 1)
494 .extract_str(pattern)
495 .ok();
496
497 // If the hook fails, make sure pattern is set to something sane
498 // (the empty string, which will disable enclosers for this file).
499 if (!exec_ok)
500 pattern = "";
501 return exec_ok;
502}
503
504bool
505lua_hooks::hook_use_inodeprints()
506{
507 bool use = false, exec_ok = false;
508
509 exec_ok = Lua(st)
510 .func("use_inodeprints")
511 .call(0, 1)
512 .extract_bool(use)
513 .ok();
514 return use && exec_ok;
515}
516
517static void
518push_uri(uri const & u, Lua & ll)
519{
520 ll.push_table();
521
522 if (!u.scheme.empty())
523 {
524 ll.push_str("scheme");
525 ll.push_str(u.scheme);
526 ll.set_table();
527 }
528
529 if (!u.user.empty())
530 {
531 ll.push_str("user");
532 ll.push_str(u.user);
533 ll.set_table();
534 }
535
536 if (!u.host.empty())
537 {
538 ll.push_str("host");
539 ll.push_str(u.host);
540 ll.set_table();
541 }
542
543 if (!u.port.empty())
544 {
545 ll.push_str("port");
546 ll.push_str(u.port);
547 ll.set_table();
548 }
549
550 if (!u.path.empty())
551 {
552 ll.push_str("path");
553 ll.push_str(u.path);
554 ll.set_table();
555 }
556
557 if (!u.query.empty())
558 {
559 ll.push_str("query");
560 ll.push_str(u.query);
561 ll.set_table();
562 }
563
564 if (!u.fragment.empty())
565 {
566 ll.push_str("fragment");
567 ll.push_str(u.fragment);
568 ll.set_table();
569 }
570}
571
572bool
573lua_hooks::hook_get_netsync_connect_command(uri const & u,
574 std::string const & include_pattern,
575 std::string const & exclude_pattern,
576 bool debug,
577 std::vector<std::string> & argv)
578{
579 bool cmd = false, exec_ok = false;
580 Lua ll(st);
581 ll.func("get_netsync_connect_command");
582
583 push_uri(u, ll);
584
585 ll.push_table();
586
587 if (!include_pattern.empty())
588 {
589 ll.push_str("include");
590 ll.push_str(include_pattern);
591 ll.set_table();
592 }
593
594 if (!exclude_pattern.empty())
595 {
596 ll.push_str("exclude");
597 ll.push_str(exclude_pattern);
598 ll.set_table();
599 }
600
601 if (debug)
602 {
603 ll.push_str("debug");
604 ll.push_bool(debug);
605 ll.set_table();
606 }
607
608 ll.call(2,1);
609
610 ll.begin();
611
612 argv.clear();
613 while(ll.next())
614 {
615 std::string s;
616 ll.extract_str(s).pop();
617 argv.push_back(s);
618 }
619 return ll.ok() && !argv.empty();
620}
621
622
623bool
624lua_hooks::hook_use_transport_auth(uri const & u)
625{
626 bool use_auth = true;
627 Lua ll(st);
628 ll.func("use_transport_auth");
629 push_uri(u, ll);
630 ll.call(1,1);
631 ll.extract_bool(use_auth);
632
633 // NB: we want to return *true* here if there's a failure.
634 return use_auth;
635}
636
637
638bool
639lua_hooks::hook_get_netsync_read_permitted(string const & branch,
640 rsa_keypair_id const & identity)
641{
642 bool permitted = false, exec_ok = false;
643
644 exec_ok = Lua(st)
645 .func("get_netsync_read_permitted")
646 .push_str(branch)
647 .push_str(identity())
648 .call(2,1)
649 .extract_bool(permitted)
650 .ok();
651
652 return exec_ok && permitted;
653}
654
655// Anonymous no-key version
656bool
657lua_hooks::hook_get_netsync_read_permitted(string const & branch)
658{
659 bool permitted = false, exec_ok = false;
660
661 exec_ok = Lua(st)
662 .func("get_netsync_read_permitted")
663 .push_str(branch)
664 .push_nil()
665 .call(2,1)
666 .extract_bool(permitted)
667 .ok();
668
669 return exec_ok && permitted;
670}
671
672bool
673lua_hooks::hook_get_netsync_write_permitted(rsa_keypair_id const & identity)
674{
675 bool permitted = false, exec_ok = false;
676
677 exec_ok = Lua(st)
678 .func("get_netsync_write_permitted")
679 .push_str(identity())
680 .call(1,1)
681 .extract_bool(permitted)
682 .ok();
683
684 return exec_ok && permitted;
685}
686
687bool
688lua_hooks::hook_init_attributes(file_path const & filename,
689 map<string, string> & attrs)
690{
691 Lua ll(st);
692
693 ll
694 .push_str("attr_init_functions")
695 .get_tab();
696
697 L(FL("calling attr_init_function for %s") % filename);
698 ll.begin();
699 while (ll.next())
700 {
701 L(FL(" calling an attr_init_function for %s") % filename);
702 ll.push_str(filename.as_external());
703 ll.call(1, 1);
704
705 if (lua_isstring(st, -1))
706 {
707 string key, value;
708
709 ll.extract_str(value);
710 ll.pop();
711 ll.extract_str(key);
712
713 attrs[key] = value;
714 L(FL(" added attr %s = %s") % key % value);
715 }
716 else
717 {
718 L(FL(" no attr added"));
719 ll.pop();
720 }
721 }
722
723 return ll.pop().ok();
724}
725
726bool
727lua_hooks::hook_apply_attribute(string const & attr,
728 file_path const & filename,
729 string const & value)
730{
731 return Lua(st)
732 .push_str("attr_functions")
733 .get_tab()
734 .push_str(attr)
735 .get_fn(-2)
736 .push_str(filename.as_external())
737 .push_str(value)
738 .call(2,0)
739 .ok();
740}
741
742
743bool
744lua_hooks::hook_get_system_linesep(string & linesep)
745{
746 return Lua(st)
747 .func("get_system_linesep")
748 .call(0,1)
749 .extract_str(linesep)
750 .ok();
751}
752
753bool
754lua_hooks::hook_get_charset_conv(file_path const & p,
755 string & db,
756 string & ext)
757{
758 Lua ll(st);
759 ll
760 .func("get_charset_conv")
761 .push_str(p.as_external())
762 .call(1,1)
763 .begin();
764
765 ll.next();
766 ll.extract_str(db).pop();
767
768 ll.next();
769 ll.extract_str(ext).pop();
770 return ll.ok();
771}
772
773bool
774lua_hooks::hook_get_linesep_conv(file_path const & p,
775 string & db,
776 string & ext)
777{
778 Lua ll(st);
779 ll
780 .func("get_linesep_conv")
781 .push_str(p.as_external())
782 .call(1,1)
783 .begin();
784
785 ll.next();
786 ll.extract_str(db).pop();
787
788 ll.next();
789 ll.extract_str(ext).pop();
790 return ll.ok();
791}
792
793bool
794lua_hooks::hook_validate_commit_message(string const & message,
795 string const & new_manifest_text,
796 bool & validated,
797 string & reason)
798{
799 validated = true;
800 return Lua(st)
801 .func("validate_commit_message")
802 .push_str(message)
803 .push_str(new_manifest_text)
804 .call(2, 2)
805 .extract_str(reason)
806 // XXX When validated, the extra returned string is superfluous.
807 .pop()
808 .extract_bool(validated)
809 .ok();
810}
811
812bool
813lua_hooks::hook_note_commit(revision_id const & new_id,
814 revision_data const & rdat,
815 map<cert_name, cert_value> const & certs)
816{
817 Lua ll(st);
818 ll
819 .func("note_commit")
820 .push_str(new_id.inner()())
821 .push_str(rdat.inner()());
822
823 ll.push_table();
824
825 for (map<cert_name, cert_value>::const_iterator i = certs.begin();
826 i != certs.end(); ++i)
827 {
828 ll.push_str(i->first());
829 ll.push_str(i->second());
830 ll.set_table();
831 }
832
833 ll.call(3, 0);
834 return ll.ok();
835}
836
837bool
838lua_hooks::hook_note_netsync_start(string nonce)
839{
840 Lua ll(st);
841 return ll
842 .func("note_netsync_start")
843 .push_str(nonce)
844 .call(1, 0)
845 .ok();
846}
847
848bool
849lua_hooks::hook_note_netsync_revision_received(revision_id const & new_id,
850 revision_data const & rdat,
851 set<pair<rsa_keypair_id,
852 pair<cert_name,
853 cert_value> > > const & certs,
854 string nonce)
855{
856 Lua ll(st);
857 ll
858 .func("note_netsync_revision_received")
859 .push_str(new_id.inner()())
860 .push_str(rdat.inner()());
861
862 ll.push_table();
863
864 typedef set<pair<rsa_keypair_id, pair<cert_name, cert_value> > > cdat;
865
866 int n=0;
867 for (cdat::const_iterator i = certs.begin(); i != certs.end(); ++i)
868 {
869 ll.push_int(n++);
870 ll.push_table();
871 ll.push_str("key");
872 ll.push_str(i->first());
873 ll.set_table();
874 ll.push_str("name");
875 ll.push_str(i->second.first());
876 ll.set_table();
877 ll.push_str("value");
878 ll.push_str(i->second.second());
879 ll.set_table();
880 ll.set_table();
881 }
882
883 ll.push_str(nonce);
884 ll.call(4, 0);
885 return ll.ok();
886}
887
888bool
889lua_hooks::hook_note_netsync_pubkey_received(rsa_keypair_id const & kid,
890 string nonce)
891{
892 Lua ll(st);
893 ll
894 .func("note_netsync_pubkey_received")
895 .push_str(kid())
896 .push_str(nonce);
897
898 ll.call(2, 0);
899 return ll.ok();
900}
901
902bool
903lua_hooks::hook_note_netsync_cert_received(revision_id const & rid,
904 rsa_keypair_id const & kid,
905 cert_name const & name,
906 cert_value const & value,
907 string nonce)
908{
909 Lua ll(st);
910 ll
911 .func("note_netsync_cert_received")
912 .push_str(rid.inner()())
913 .push_str(kid())
914 .push_str(name())
915 .push_str(value())
916 .push_str(nonce);
917
918 ll.call(5, 0);
919 return ll.ok();
920}
921
922bool
923lua_hooks::hook_note_netsync_end(string nonce)
924{
925 Lua ll(st);
926 return ll
927 .func("note_netsync_end")
928 .push_str(nonce)
929 .call(1, 0)
930 .ok();
931}
932
933int
934lua_hooks::hook_get_vcache_size()
935{
936 bool exec_ok;
937 int vcache_size = constants::db_version_cache_sz;
938
939 exec_ok = Lua(st)
940 .func("get_vcache_size")
941 .call(0, 1)
942 .extract_int(vcache_size)
943 .ok();
944 if (!exec_ok) {
945 return constants::db_version_cache_sz;
946 }
947 return vcache_size;
948}
949
950
951// Local Variables:
952// mode: C++
953// fill-column: 76
954// c-file-style: "gnu"
955// indent-tabs-mode: nil
956// End:
957// 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