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