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