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