Added support for multiple overlays to the copy-on-write filesystem.
[strongswan.git] / src / dumm / ext / dumm.c
1 /*
2 * Copyright (C) 2008 Tobias Brunner
3 * Copyright (C) 2008 Martin Willi
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #include <stdio.h>
18 #include <signal.h>
19 #include <unistd.h>
20 #include <fcntl.h>
21
22 #include <library.h>
23 #include <dumm.h>
24 #include <debug.h>
25 #include <utils/linked_list.h>
26
27 #undef PACKAGE_NAME
28 #undef PACKAGE_TARNAME
29 #undef PACKAGE_VERSION
30 #undef PACKAGE_STRING
31 #undef PACKAGE_BUGREPORT
32 #undef PACKAGE_URL
33 #include <ruby.h>
34
35 static dumm_t *dumm;
36
37 static VALUE rbm_dumm;
38 static VALUE rbc_guest;
39 static VALUE rbc_bridge;
40 static VALUE rbc_iface;
41 static VALUE rbc_template;
42
43 /**
44 * Guest invocation callback
45 */
46 static pid_t invoke(void *null, guest_t *guest, char *args[], int argc)
47 {
48 pid_t pid;
49
50 pid = fork();
51 switch (pid)
52 {
53 case 0: /* child */
54 /* create a new process group in order to prevent signals (e.g.
55 * SIGINT) sent to the parent from terminating the child */
56 setpgid(0, 0);
57 dup2(open("/dev/null", 0), 1);
58 dup2(open("/dev/null", 0), 2);
59 execvp(args[0], args);
60 /* FALL */
61 case -1:
62 return 0;
63 default:
64 return pid;
65 }
66 }
67
68 /**
69 * SIGCHLD signal handler
70 */
71 static void sigchld_handler(int signal, siginfo_t *info, void* ptr)
72 {
73 enumerator_t *enumerator;
74 guest_t *guest;
75
76 enumerator = dumm->create_guest_enumerator(dumm);
77 while (enumerator->enumerate(enumerator, &guest))
78 {
79 if (guest->get_pid(guest) == info->si_pid)
80 {
81 guest->sigchild(guest);
82 break;
83 }
84 }
85 enumerator->destroy(enumerator);
86 }
87
88 /**
89 * Guest bindings
90 */
91 static VALUE guest_find(VALUE class, VALUE key)
92 {
93 enumerator_t *enumerator;
94 guest_t *guest, *found = NULL;
95
96 if (TYPE(key) == T_SYMBOL)
97 {
98 key = rb_convert_type(key, T_STRING, "String", "to_s");
99 }
100 enumerator = dumm->create_guest_enumerator(dumm);
101 while (enumerator->enumerate(enumerator, &guest))
102 {
103 if (streq(guest->get_name(guest), StringValuePtr(key)))
104 {
105 found = guest;
106 break;
107 }
108 }
109 enumerator->destroy(enumerator);
110 if (!found)
111 {
112 return Qnil;
113 }
114 return Data_Wrap_Struct(class, NULL, NULL, found);
115 }
116
117 static VALUE guest_get(VALUE class, VALUE key)
118 {
119 VALUE guest = guest_find(class, key);
120 if (NIL_P(guest))
121 {
122 rb_raise(rb_eRuntimeError, "guest not found");
123 }
124 return guest;
125 }
126
127 static VALUE guest_each(int argc, VALUE *argv, VALUE class)
128 {
129 linked_list_t *list;
130 enumerator_t *enumerator;
131 guest_t *guest;
132
133 if (!rb_block_given_p())
134 {
135 rb_raise(rb_eArgError, "must be called with a block");
136 }
137 list = linked_list_create();
138 enumerator = dumm->create_guest_enumerator(dumm);
139 while (enumerator->enumerate(enumerator, &guest))
140 {
141 list->insert_last(list, guest);
142 }
143 enumerator->destroy(enumerator);
144 while (list->remove_first(list, (void**)&guest) == SUCCESS)
145 {
146 rb_yield(Data_Wrap_Struct(class, NULL, NULL, guest));
147 }
148 list->destroy(list);
149 return class;
150 }
151
152 static VALUE guest_new(VALUE class, VALUE name, VALUE kernel,
153 VALUE master, VALUE args)
154 {
155 guest_t *guest;
156
157 guest = dumm->create_guest(dumm, StringValuePtr(name), StringValuePtr(kernel),
158 StringValuePtr(master), StringValuePtr(args));
159 if (!guest)
160 {
161 rb_raise(rb_eRuntimeError, "creating guest failed");
162 }
163 return Data_Wrap_Struct(class, NULL, NULL, guest);
164 }
165
166 static VALUE guest_to_s(VALUE self)
167 {
168 guest_t *guest;
169
170 Data_Get_Struct(self, guest_t, guest);
171 return rb_str_new2(guest->get_name(guest));
172 }
173
174 static VALUE guest_start(VALUE self)
175 {
176 guest_t *guest;
177
178 Data_Get_Struct(self, guest_t, guest);
179
180 if (!guest->start(guest, invoke, NULL, NULL))
181 {
182 rb_raise(rb_eRuntimeError, "starting guest failed");
183 }
184 return self;
185 }
186
187 static VALUE guest_stop(VALUE self)
188 {
189 guest_t *guest;
190
191 Data_Get_Struct(self, guest_t, guest);
192 guest->stop(guest, NULL);
193 return self;
194 }
195
196 static VALUE guest_running(VALUE self)
197 {
198 guest_t *guest;
199
200 Data_Get_Struct(self, guest_t, guest);
201 return guest->get_pid(guest) ? Qtrue : Qfalse;
202 }
203
204 static void exec_cb(void *data, char *buf)
205 {
206 rb_yield(rb_str_new2(buf));
207 }
208
209 static VALUE guest_exec(VALUE self, VALUE cmd)
210 {
211 guest_t *guest;
212 bool block;
213 int ret;
214
215 block = rb_block_given_p();
216 Data_Get_Struct(self, guest_t, guest);
217 if ((ret = guest->exec_str(guest, block ? (void*)exec_cb : NULL, TRUE, NULL,
218 "exec %s", StringValuePtr(cmd))) != 0)
219 {
220 rb_raise(rb_eRuntimeError, "executing command failed (%d)", ret);
221 }
222 return self;
223 }
224
225 static VALUE guest_mconsole(VALUE self, VALUE cmd)
226 {
227 guest_t *guest;
228 bool block;
229 int ret;
230
231 block = rb_block_given_p();
232 Data_Get_Struct(self, guest_t, guest);
233 if ((ret = guest->exec_str(guest, block ? (void*)exec_cb : NULL, TRUE, NULL,
234 "%s", StringValuePtr(cmd))) != 0)
235 {
236 rb_raise(rb_eRuntimeError, "executing command failed (%d)", ret);
237 }
238 return self;
239 }
240
241 static VALUE guest_add_iface(VALUE self, VALUE name)
242 {
243 guest_t *guest;
244 iface_t *iface;
245
246 Data_Get_Struct(self, guest_t, guest);
247 iface = guest->create_iface(guest, StringValuePtr(name));
248 if (!iface)
249 {
250 rb_raise(rb_eRuntimeError, "adding interface failed");
251 }
252 return Data_Wrap_Struct(rbc_iface, NULL, NULL, iface);
253 }
254
255 static VALUE guest_find_iface(VALUE self, VALUE key)
256 {
257 enumerator_t *enumerator;
258 iface_t *iface, *found = NULL;
259 guest_t *guest;
260
261 if (TYPE(key) == T_SYMBOL)
262 {
263 key = rb_convert_type(key, T_STRING, "String", "to_s");
264 }
265 Data_Get_Struct(self, guest_t, guest);
266 enumerator = guest->create_iface_enumerator(guest);
267 while (enumerator->enumerate(enumerator, &iface))
268 {
269 if (streq(iface->get_guestif(iface), StringValuePtr(key)))
270 {
271 found = iface;
272 break;
273 }
274 }
275 enumerator->destroy(enumerator);
276 if (!found)
277 {
278 return Qnil;
279 }
280 return Data_Wrap_Struct(rbc_iface, NULL, NULL, iface);
281 }
282
283 static VALUE guest_get_iface(VALUE self, VALUE key)
284 {
285 VALUE iface = guest_find_iface(self, key);
286 if (NIL_P(iface))
287 {
288 rb_raise(rb_eRuntimeError, "interface not found");
289 }
290 return iface;
291 }
292
293 static VALUE guest_each_iface(int argc, VALUE *argv, VALUE self)
294 {
295 enumerator_t *enumerator;
296 linked_list_t *list;
297 guest_t *guest;
298 iface_t *iface;
299
300 if (!rb_block_given_p())
301 {
302 rb_raise(rb_eArgError, "must be called with a block");
303 }
304 Data_Get_Struct(self, guest_t, guest);
305 list = linked_list_create();
306 enumerator = guest->create_iface_enumerator(guest);
307 while (enumerator->enumerate(enumerator, &iface))
308 {
309 list->insert_last(list, iface);
310 }
311 enumerator->destroy(enumerator);
312 while (list->remove_first(list, (void**)&iface) == SUCCESS)
313 {
314 rb_yield(Data_Wrap_Struct(rbc_iface, NULL, NULL, iface));
315 }
316 list->destroy(list);
317 return self;
318 }
319
320 static VALUE guest_delete(VALUE self)
321 {
322 guest_t *guest;
323
324 Data_Get_Struct(self, guest_t, guest);
325 if (guest->get_pid(guest))
326 {
327 rb_raise(rb_eRuntimeError, "guest is running");
328 }
329 dumm->delete_guest(dumm, guest);
330 return Qnil;
331 }
332
333 static void guest_init()
334 {
335 rbc_guest = rb_define_class_under(rbm_dumm , "Guest", rb_cObject);
336 rb_include_module(rb_class_of(rbc_guest), rb_mEnumerable);
337 rb_include_module(rbc_guest, rb_mEnumerable);
338
339 rb_define_singleton_method(rbc_guest, "[]", guest_get, 1);
340 rb_define_singleton_method(rbc_guest, "each", guest_each, -1);
341 rb_define_singleton_method(rbc_guest, "new", guest_new, 4);
342 rb_define_singleton_method(rbc_guest, "include?", guest_find, 1);
343 rb_define_singleton_method(rbc_guest, "guest?", guest_find, 1);
344
345 rb_define_method(rbc_guest, "to_s", guest_to_s, 0);
346 rb_define_method(rbc_guest, "start", guest_start, 0);
347 rb_define_method(rbc_guest, "stop", guest_stop, 0);
348 rb_define_method(rbc_guest, "running?", guest_running, 0);
349 rb_define_method(rbc_guest, "exec", guest_exec, 1);
350 rb_define_method(rbc_guest, "mconsole", guest_mconsole, 1);
351 rb_define_method(rbc_guest, "add", guest_add_iface, 1);
352 rb_define_method(rbc_guest, "[]", guest_get_iface, 1);
353 rb_define_method(rbc_guest, "each", guest_each_iface, -1);
354 rb_define_method(rbc_guest, "include?", guest_find_iface, 1);
355 rb_define_method(rbc_guest, "iface?", guest_find_iface, 1);
356 rb_define_method(rbc_guest, "delete", guest_delete, 0);
357 }
358
359 /**
360 * Bridge binding
361 */
362 static VALUE bridge_find(VALUE class, VALUE key)
363 {
364 enumerator_t *enumerator;
365 bridge_t *bridge, *found = NULL;
366
367 if (TYPE(key) == T_SYMBOL)
368 {
369 key = rb_convert_type(key, T_STRING, "String", "to_s");
370 }
371 enumerator = dumm->create_bridge_enumerator(dumm);
372 while (enumerator->enumerate(enumerator, &bridge))
373 {
374 if (streq(bridge->get_name(bridge), StringValuePtr(key)))
375 {
376 found = bridge;
377 break;
378 }
379 }
380 enumerator->destroy(enumerator);
381 if (!found)
382 {
383 return Qnil;
384 }
385 return Data_Wrap_Struct(class, NULL, NULL, found);
386 }
387
388 static VALUE bridge_get(VALUE class, VALUE key)
389 {
390 VALUE bridge = bridge_find(class, key);
391 if (NIL_P(bridge))
392 {
393 rb_raise(rb_eRuntimeError, "bridge not found");
394 }
395 return bridge;
396 }
397
398 static VALUE bridge_each(int argc, VALUE *argv, VALUE class)
399 {
400 enumerator_t *enumerator;
401 linked_list_t *list;
402 bridge_t *bridge;
403
404 if (!rb_block_given_p())
405 {
406 rb_raise(rb_eArgError, "must be called with a block");
407 }
408 list = linked_list_create();
409 enumerator = dumm->create_bridge_enumerator(dumm);
410 while (enumerator->enumerate(enumerator, &bridge))
411 {
412 list->insert_last(list, bridge);
413 }
414 enumerator->destroy(enumerator);
415 while (list->remove_first(list, (void**)&bridge) == SUCCESS)
416 {
417 rb_yield(Data_Wrap_Struct(class, NULL, NULL, bridge));
418 }
419 list->destroy(list);
420 return class;
421 }
422
423 static VALUE bridge_new(VALUE class, VALUE name)
424
425 {
426 bridge_t *bridge;
427
428 bridge = dumm->create_bridge(dumm, StringValuePtr(name));
429 if (!bridge)
430 {
431 rb_raise(rb_eRuntimeError, "creating bridge failed");
432 }
433 return Data_Wrap_Struct(class, NULL, NULL, bridge);
434 }
435
436 static VALUE bridge_to_s(VALUE self)
437 {
438 bridge_t *bridge;
439
440 Data_Get_Struct(self, bridge_t, bridge);
441 return rb_str_new2(bridge->get_name(bridge));
442 }
443
444 static VALUE bridge_each_iface(int argc, VALUE *argv, VALUE self)
445 {
446 enumerator_t *enumerator;
447 linked_list_t *list;
448 bridge_t *bridge;
449 iface_t *iface;
450
451 if (!rb_block_given_p())
452 {
453 rb_raise(rb_eArgError, "must be called with a block");
454 }
455 Data_Get_Struct(self, bridge_t, bridge);
456 list = linked_list_create();
457 enumerator = bridge->create_iface_enumerator(bridge);
458 while (enumerator->enumerate(enumerator, &iface))
459 {
460 list->insert_last(list, iface);
461 }
462 enumerator->destroy(enumerator);
463 while (list->remove_first(list, (void**)&iface) == SUCCESS)
464 {
465 rb_yield(Data_Wrap_Struct(rbc_iface, NULL, NULL, iface));
466 }
467 list->destroy(list);
468 return self;
469 }
470
471 static VALUE bridge_delete(VALUE self)
472 {
473 bridge_t *bridge;
474
475 Data_Get_Struct(self, bridge_t, bridge);
476 dumm->delete_bridge(dumm, bridge);
477 return Qnil;
478 }
479
480 static void bridge_init()
481 {
482 rbc_bridge = rb_define_class_under(rbm_dumm , "Bridge", rb_cObject);
483 rb_include_module(rb_class_of(rbc_bridge), rb_mEnumerable);
484 rb_include_module(rbc_bridge, rb_mEnumerable);
485
486 rb_define_singleton_method(rbc_bridge, "[]", bridge_get, 1);
487 rb_define_singleton_method(rbc_bridge, "each", bridge_each, -1);
488 rb_define_singleton_method(rbc_bridge, "new", bridge_new, 1);
489 rb_define_singleton_method(rbc_bridge, "include?", bridge_find, 1);
490 rb_define_singleton_method(rbc_bridge, "bridge?", bridge_find, 1);
491
492 rb_define_method(rbc_bridge, "to_s", bridge_to_s, 0);
493 rb_define_method(rbc_bridge, "each", bridge_each_iface, -1);
494 rb_define_method(rbc_bridge, "delete", bridge_delete, 0);
495 }
496
497 /**
498 * Iface wrapper
499 */
500 static VALUE iface_to_s(VALUE self)
501 {
502 iface_t *iface;
503
504 Data_Get_Struct(self, iface_t, iface);
505 return rb_str_new2(iface->get_hostif(iface));
506 }
507
508 static VALUE iface_connect(VALUE self, VALUE vbridge)
509 {
510 iface_t *iface;
511 bridge_t *bridge;
512
513 Data_Get_Struct(self, iface_t, iface);
514 Data_Get_Struct(vbridge, bridge_t, bridge);
515 if (!bridge->connect_iface(bridge, iface))
516 {
517 rb_raise(rb_eRuntimeError, "connecting iface failed");
518 }
519 return self;
520 }
521
522 static VALUE iface_disconnect(VALUE self)
523 {
524 iface_t *iface;
525 bridge_t *bridge;
526
527 Data_Get_Struct(self, iface_t, iface);
528 bridge = iface->get_bridge(iface);
529 if (!bridge || !bridge->disconnect_iface(bridge, iface))
530 {
531 rb_raise(rb_eRuntimeError, "disconnecting iface failed");
532 }
533 return self;
534 }
535
536 static VALUE iface_add_addr(VALUE self, VALUE name)
537 {
538 iface_t *iface;
539 host_t *addr;
540
541 addr = host_create_from_string(StringValuePtr(name), 0);
542 if (!addr)
543 {
544 rb_raise(rb_eArgError, "invalid IP address");
545 }
546 Data_Get_Struct(self, iface_t, iface);
547 if (!iface->add_address(iface, addr))
548 {
549 addr->destroy(addr);
550 rb_raise(rb_eRuntimeError, "adding address failed");
551 }
552 if (rb_block_given_p()) {
553 rb_yield(self);
554 iface->delete_address(iface, addr);
555 }
556 addr->destroy(addr);
557 return self;
558 }
559
560 static VALUE iface_each_addr(int argc, VALUE *argv, VALUE self)
561 {
562 enumerator_t *enumerator;
563 linked_list_t *list;
564 iface_t *iface;
565 host_t *addr;
566 char buf[64];
567
568 if (!rb_block_given_p())
569 {
570 rb_raise(rb_eArgError, "must be called with a block");
571 }
572 Data_Get_Struct(self, iface_t, iface);
573 enumerator = iface->create_address_enumerator(iface);
574 while (enumerator->enumerate(enumerator, &addr))
575 {
576 list->insert_last(list, addr->clone(addr));
577 }
578 enumerator->destroy(enumerator);
579 while (list->remove_first(list, (void**)&addr) == SUCCESS)
580 {
581 snprintf(buf, sizeof(buf), "%H", addr);
582 addr->destroy(addr);
583 rb_yield(rb_str_new2(buf));
584 }
585 list->destroy(list);
586 return self;
587 }
588
589 static VALUE iface_del_addr(VALUE self, VALUE vaddr)
590 {
591 iface_t *iface;
592 host_t *addr;
593
594 addr = host_create_from_string(StringValuePtr(vaddr), 0);
595 if (!addr)
596 {
597 rb_raise(rb_eArgError, "invalid IP address");
598 }
599 Data_Get_Struct(self, iface_t, iface);
600 if (!iface->delete_address(iface, addr))
601 {
602 addr->destroy(addr);
603 rb_raise(rb_eRuntimeError, "address not found");
604 }
605 if (rb_block_given_p()) {
606 rb_yield(self);
607 iface->add_address(iface, addr);
608 }
609 addr->destroy(addr);
610 return self;
611 }
612
613 static VALUE iface_delete(VALUE self)
614 {
615 guest_t *guest;
616 iface_t *iface;
617
618 Data_Get_Struct(self, iface_t, iface);
619 guest = iface->get_guest(iface);
620 guest->destroy_iface(guest, iface);
621 return Qnil;
622 }
623
624 static void iface_init()
625 {
626 rbc_iface = rb_define_class_under(rbm_dumm , "Iface", rb_cObject);
627 rb_include_module(rbc_iface, rb_mEnumerable);
628
629 rb_define_method(rbc_iface, "to_s", iface_to_s, 0);
630 rb_define_method(rbc_iface, "connect", iface_connect, 1);
631 rb_define_method(rbc_iface, "disconnect", iface_disconnect, 0);
632 rb_define_method(rbc_iface, "add", iface_add_addr, 1);
633 rb_define_method(rbc_iface, "del", iface_del_addr, 1);
634 rb_define_method(rbc_iface, "each", iface_each_addr, -1);
635 rb_define_method(rbc_iface, "delete", iface_delete, 0);
636 }
637
638 static VALUE template_load(VALUE class, VALUE dir)
639 {
640 if (!dumm->load_template(dumm, StringValuePtr(dir)))
641 {
642 rb_raise(rb_eRuntimeError, "loading template failed");
643 }
644 return class;
645 }
646
647 static VALUE template_unload(VALUE class)
648 {
649 if (!dumm->load_template(dumm, NULL))
650 {
651 rb_raise(rb_eRuntimeError, "unloading template failed");
652 }
653 return class;
654 }
655
656 static VALUE template_each(int argc, VALUE *argv, VALUE class)
657 {
658 enumerator_t *enumerator;
659 char *template;
660
661 if (!rb_block_given_p())
662 {
663 rb_raise(rb_eArgError, "must be called with a block");
664 }
665 enumerator = dumm->create_template_enumerator(dumm);
666 while (enumerator->enumerate(enumerator, &template))
667 {
668 rb_yield(rb_str_new2(template));
669 }
670 enumerator->destroy(enumerator);
671 return class;
672 }
673
674 static void template_init()
675 {
676 rbc_template = rb_define_class_under(rbm_dumm , "Template", rb_cObject);
677
678 rb_define_singleton_method(rbc_template, "load", template_load, 1);
679 rb_define_singleton_method(rbc_template, "unload", template_unload, 0);
680 rb_define_singleton_method(rbc_template, "each", template_each, -1);
681 }
682
683 /**
684 * extension finalization
685 */
686 void Final_dumm()
687 {
688 struct sigaction action;
689
690 dumm->destroy(dumm);
691
692 sigemptyset(&action.sa_mask);
693 action.sa_handler = SIG_DFL;
694 action.sa_flags = 0;
695 sigaction(SIGCHLD, &action, NULL);
696
697 library_deinit();
698 }
699
700 /**
701 * extension initialization
702 */
703 void Init_dumm()
704 {
705 struct sigaction action;
706
707 /* there are too many to report, rubyruby... */
708 setenv("LEAK_DETECTIVE_DISABLE", "1", 1);
709
710 library_init(NULL);
711
712 dumm = dumm_create(NULL);
713
714 rbm_dumm = rb_define_module("Dumm");
715
716 guest_init();
717 bridge_init();
718 iface_init();
719 template_init();
720
721 sigemptyset(&action.sa_mask);
722 action.sa_sigaction = sigchld_handler;
723 action.sa_flags = SA_SIGINFO;
724 sigaction(SIGCHLD, &action, NULL);
725
726 rb_set_end_proc(Final_dumm, 0);
727 }