3322c77da715b0a66e49df7fe628bf633a27dc21
[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_exist(VALUE class, VALUE key)
130 {
131 return NIL_P(guest_find(class, key)) ? Qfalse : Qtrue;
132 }
133
134 static VALUE guest_each(int argc, VALUE *argv, VALUE class)
135 {
136 enumerator_t *enumerator;
137 guest_t *guest;
138
139 if (!rb_block_given_p())
140 {
141 rb_raise(rb_eArgError, "must be called with a block");
142 }
143 enumerator = dumm->create_guest_enumerator(dumm);
144 while (enumerator->enumerate(enumerator, &guest))
145 {
146 rb_yield(Data_Wrap_Struct(class, NULL, NULL, guest));
147 }
148 enumerator->destroy(enumerator);
149 return class;
150 }
151
152 static VALUE guest_new(VALUE class, VALUE name, VALUE kernel,
153 VALUE master, VALUE mem)
154 {
155 guest_t *guest;
156
157 guest = dumm->create_guest(dumm, StringValuePtr(name), StringValuePtr(kernel),
158 StringValuePtr(master), FIX2INT(mem));
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 "%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_add_iface(VALUE self, VALUE name)
226 {
227 guest_t *guest;
228 iface_t *iface;
229
230 Data_Get_Struct(self, guest_t, guest);
231 iface = guest->create_iface(guest, StringValuePtr(name));
232 if (!iface)
233 {
234 rb_raise(rb_eRuntimeError, "adding interface failed");
235 }
236 return Data_Wrap_Struct(rbc_iface, NULL, NULL, iface);
237 }
238
239 static VALUE guest_find_iface(VALUE self, VALUE key)
240 {
241 enumerator_t *enumerator;
242 iface_t *iface, *found = NULL;
243 guest_t *guest;
244 if (TYPE(key) == T_SYMBOL) {
245 key = rb_convert_type(key, T_STRING, "String", "to_s");
246 }
247 Data_Get_Struct(self, guest_t, guest);
248 enumerator = guest->create_iface_enumerator(guest);
249 while (enumerator->enumerate(enumerator, &iface))
250 {
251 if (streq(iface->get_guestif(iface), StringValuePtr(key)))
252 {
253 found = iface;
254 break;
255 }
256 }
257 enumerator->destroy(enumerator);
258 if (!found)
259 {
260 return Qnil;
261 }
262 return Data_Wrap_Struct(rbc_iface, NULL, NULL, iface);
263 }
264
265 static VALUE guest_get_iface(VALUE self, VALUE key)
266 {
267 VALUE iface = guest_find_iface(self, key);
268 if (NIL_P(iface))
269 {
270 rb_raise(rb_eRuntimeError, "interface not found");
271 }
272 return iface;
273 }
274
275 static VALUE guest_exist_iface(VALUE self, VALUE key)
276 {
277 return NIL_P(guest_find_iface(self, key)) ? Qfalse : Qtrue;
278 }
279
280 static VALUE guest_each_iface(int argc, VALUE *argv, VALUE self)
281 {
282 enumerator_t *enumerator;
283 guest_t *guest;
284 iface_t *iface;
285
286 if (!rb_block_given_p())
287 {
288 rb_raise(rb_eArgError, "must be called with a block");
289 }
290 Data_Get_Struct(self, guest_t, guest);
291 enumerator = guest->create_iface_enumerator(guest);
292 while (enumerator->enumerate(enumerator, &iface))
293 {
294 rb_yield(Data_Wrap_Struct(rbc_iface, NULL, NULL, iface));
295 }
296 enumerator->destroy(enumerator);
297 return self;
298 }
299
300 static VALUE guest_delete(VALUE self)
301 {
302 guest_t *guest;
303
304 Data_Get_Struct(self, guest_t, guest);
305 dumm->delete_guest(dumm, guest);
306 return Qnil;
307 }
308
309 static void guest_init()
310 {
311 rbc_guest = rb_define_class_under(rbm_dumm , "Guest", rb_cObject);
312 rb_include_module(rb_class_of(rbc_guest), rb_mEnumerable);
313 rb_include_module(rbc_guest, rb_mEnumerable);
314
315 rb_define_singleton_method(rbc_guest, "[]", guest_get, 1);
316 rb_define_singleton_method(rbc_guest, "each", guest_each, -1);
317 rb_define_singleton_method(rbc_guest, "new", guest_new, 4);
318 rb_define_singleton_method(rbc_guest, "include?", guest_exist, 1);
319 rb_define_singleton_method(rbc_guest, "guest?", guest_exist, 1);
320
321 rb_define_method(rbc_guest, "to_s", guest_to_s, 0);
322 rb_define_method(rbc_guest, "start", guest_start, 0);
323 rb_define_method(rbc_guest, "stop", guest_stop, 0);
324 rb_define_method(rbc_guest, "running?", guest_running, 0);
325 rb_define_method(rbc_guest, "exec", guest_exec, 1);
326 rb_define_method(rbc_guest, "add", guest_add_iface, 1);
327 rb_define_method(rbc_guest, "[]", guest_get_iface, 1);
328 rb_define_method(rbc_guest, "each", guest_each_iface, -1);
329 rb_define_method(rbc_guest, "include?", guest_exist_iface, 1);
330 rb_define_method(rbc_guest, "iface?", guest_exist_iface, 1);
331 rb_define_method(rbc_guest, "delete", guest_delete, 0);
332 }
333
334 /**
335 * Bridge binding
336 */
337 static VALUE bridge_get(VALUE class, VALUE key)
338 {
339 enumerator_t *enumerator;
340 bridge_t *bridge, *found = NULL;
341
342 enumerator = dumm->create_bridge_enumerator(dumm);
343 while (enumerator->enumerate(enumerator, &bridge))
344 {
345 if (streq(bridge->get_name(bridge), StringValuePtr(key)))
346 {
347 found = bridge;
348 break;
349 }
350 }
351 enumerator->destroy(enumerator);
352 if (!found)
353 {
354 rb_raise(rb_eRuntimeError, "bridge not found");
355 }
356 return Data_Wrap_Struct(class, NULL, NULL, found);
357 }
358
359 static VALUE bridge_each(int argc, VALUE *argv, VALUE class)
360 {
361 enumerator_t *enumerator;
362 bridge_t *bridge;
363
364 if (!rb_block_given_p())
365 {
366 rb_raise(rb_eArgError, "must be called with a block");
367 }
368 enumerator = dumm->create_bridge_enumerator(dumm);
369 while (enumerator->enumerate(enumerator, &bridge))
370 {
371 rb_yield(Data_Wrap_Struct(class, NULL, NULL, bridge));
372 }
373 enumerator->destroy(enumerator);
374 return class;
375 }
376
377 static VALUE bridge_new(VALUE class, VALUE name)
378
379 {
380 bridge_t *bridge;
381
382 bridge = dumm->create_bridge(dumm, StringValuePtr(name));
383 if (!bridge)
384 {
385 rb_raise(rb_eRuntimeError, "creating bridge failed");
386 }
387 return Data_Wrap_Struct(class, NULL, NULL, bridge);
388 }
389
390 static VALUE bridge_to_s(VALUE self)
391 {
392 bridge_t *bridge;
393
394 Data_Get_Struct(self, bridge_t, bridge);
395 return rb_str_new2(bridge->get_name(bridge));
396 }
397
398 static VALUE bridge_each_iface(int argc, VALUE *argv, VALUE self)
399 {
400 enumerator_t *enumerator;
401 bridge_t *bridge;
402 iface_t *iface;
403
404 if (!rb_block_given_p())
405 {
406 rb_raise(rb_eArgError, "must be called with a block");
407 }
408 Data_Get_Struct(self, bridge_t, bridge);
409 enumerator = bridge->create_iface_enumerator(bridge);
410 while (enumerator->enumerate(enumerator, &iface))
411 {
412 rb_yield(Data_Wrap_Struct(rbc_iface, NULL, NULL, iface));
413 }
414 enumerator->destroy(enumerator);
415 return self;
416 }
417
418 static VALUE bridge_delete(VALUE self)
419 {
420 bridge_t *bridge;
421
422 Data_Get_Struct(self, bridge_t, bridge);
423 dumm->delete_bridge(dumm, bridge);
424 return Qnil;
425 }
426
427 static void bridge_init()
428 {
429 rbc_bridge = rb_define_class_under(rbm_dumm , "Bridge", rb_cObject);
430 rb_include_module(rb_class_of(rbc_bridge), rb_mEnumerable);
431 rb_include_module(rbc_bridge, rb_mEnumerable);
432
433 rb_define_singleton_method(rbc_bridge, "[]", bridge_get, 1);
434 rb_define_singleton_method(rbc_bridge, "each", bridge_each, -1);
435 rb_define_singleton_method(rbc_bridge, "new", bridge_new, 1);
436
437 rb_define_method(rbc_bridge, "to_s", bridge_to_s, 0);
438 rb_define_method(rbc_bridge, "each", bridge_each_iface, -1);
439 rb_define_method(rbc_bridge, "delete", bridge_delete, 0);
440 }
441
442 /**
443 * Iface wrapper
444 */
445 static VALUE iface_to_s(VALUE self)
446 {
447 iface_t *iface;
448
449 Data_Get_Struct(self, iface_t, iface);
450 return rb_str_new2(iface->get_hostif(iface));
451 }
452
453 static VALUE iface_connect(VALUE self, VALUE vbridge)
454 {
455 iface_t *iface;
456 bridge_t *bridge;
457
458 Data_Get_Struct(self, iface_t, iface);
459 Data_Get_Struct(vbridge, bridge_t, bridge);
460 if (!bridge->connect_iface(bridge, iface))
461 {
462 rb_raise(rb_eRuntimeError, "connecting iface failed");
463 }
464 return self;
465 }
466
467 static VALUE iface_disconnect(VALUE self)
468 {
469 iface_t *iface;
470 bridge_t *bridge;
471
472 Data_Get_Struct(self, iface_t, iface);
473 bridge = iface->get_bridge(iface);
474 if (!bridge || !bridge->disconnect_iface(bridge, iface))
475 {
476 rb_raise(rb_eRuntimeError, "disconnecting iface failed");
477 }
478 return self;
479 }
480
481 static VALUE iface_add_addr(VALUE self, VALUE name)
482 {
483 iface_t *iface;
484 host_t *addr;
485
486 addr = host_create_from_string(StringValuePtr(name), 0);
487 if (!addr)
488 {
489 rb_raise(rb_eArgError, "invalid IP address");
490 }
491 Data_Get_Struct(self, iface_t, iface);
492 if (!iface->add_address(iface, addr))
493 {
494 addr->destroy(addr);
495 rb_raise(rb_eRuntimeError, "adding address failed");
496 }
497 if (rb_block_given_p()) {
498 rb_yield(self);
499 iface->delete_address(iface, addr);
500 }
501 addr->destroy(addr);
502 return self;
503 }
504
505 static VALUE iface_each_addr(int argc, VALUE *argv, VALUE self)
506 {
507 enumerator_t *enumerator;
508 iface_t *iface;
509 host_t *addr;
510 char buf[64];
511
512 if (!rb_block_given_p())
513 {
514 rb_raise(rb_eArgError, "must be called with a block");
515 }
516 Data_Get_Struct(self, iface_t, iface);
517 enumerator = iface->create_address_enumerator(iface);
518 while (enumerator->enumerate(enumerator, &addr))
519 {
520 snprintf(buf, sizeof(buf), "%H", addr);
521 rb_yield(rb_str_new2(buf));
522 }
523 enumerator->destroy(enumerator);
524 return self;
525 }
526
527 static VALUE iface_del_addr(VALUE self, VALUE vaddr)
528 {
529 iface_t *iface;
530 host_t *addr;
531
532 addr = host_create_from_string(StringValuePtr(vaddr), 0);
533 if (!addr)
534 {
535 rb_raise(rb_eArgError, "invalid IP address");
536 }
537 Data_Get_Struct(self, iface_t, iface);
538 if (!iface->delete_address(iface, addr))
539 {
540 addr->destroy(addr);
541 rb_raise(rb_eRuntimeError, "address not found");
542 }
543 if (rb_block_given_p()) {
544 rb_yield(self);
545 iface->add_address(iface, addr);
546 }
547 addr->destroy(addr);
548 return self;
549 }
550
551 static VALUE iface_delete(VALUE self)
552 {
553 guest_t *guest;
554 iface_t *iface;
555
556 Data_Get_Struct(self, iface_t, iface);
557 guest = iface->get_guest(iface);
558 guest->destroy_iface(guest, iface);
559 return Qnil;
560 }
561
562 static void iface_init()
563 {
564 rbc_iface = rb_define_class_under(rbm_dumm , "Iface", rb_cObject);
565 rb_include_module(rbc_iface, rb_mEnumerable);
566
567 rb_define_method(rbc_iface, "to_s", iface_to_s, 0);
568 rb_define_method(rbc_iface, "connect", iface_connect, 1);
569 rb_define_method(rbc_iface, "disconnect", iface_disconnect, 0);
570 rb_define_method(rbc_iface, "add", iface_add_addr, 1);
571 rb_define_method(rbc_iface, "del", iface_del_addr, 1);
572 rb_define_method(rbc_iface, "each", iface_each_addr, -1);
573 rb_define_method(rbc_iface, "delete", iface_delete, 0);
574 }
575
576 static VALUE template_load(VALUE class, VALUE name)
577 {
578 if (!dumm->load_template(dumm, StringValuePtr(name)))
579 {
580 rb_raise(rb_eRuntimeError, "loading template failed");
581 }
582 return class;
583 }
584
585 static VALUE template_unload(VALUE class)
586 {
587 if (!dumm->load_template(dumm, NULL))
588 {
589 rb_raise(rb_eRuntimeError, "unloading template failed");
590 }
591 return class;
592 }
593
594 static void template_init()
595 {
596 rbc_template = rb_define_class_under(rbm_dumm , "Template", rb_cObject);
597
598 rb_define_singleton_method(rbc_template, "load", template_load, 1);
599 rb_define_singleton_method(rbc_template, "unload", template_unload, 0);
600 }
601
602 /**
603 * extension finalization
604 */
605 void Final_dumm()
606 {
607 struct sigaction action;
608
609 dumm->destroy(dumm);
610
611 sigemptyset(&action.sa_mask);
612 action.sa_handler = SIG_DFL;
613 action.sa_flags = 0;
614 sigaction(SIGCHLD, &action, NULL);
615
616 library_deinit();
617 }
618
619 /**
620 * extension initialization
621 */
622 void Init_dumm()
623 {
624 struct sigaction action;
625
626 /* there are too many to report, rubyruby... */
627 setenv("LEAK_DETECTIVE_DISABLE", "1", 1);
628
629 library_init(NULL);
630
631 dumm = dumm_create(NULL);
632
633 rbm_dumm = rb_define_module("Dumm");
634
635 guest_init();
636 bridge_init();
637 iface_init();
638 template_init();
639
640 sigemptyset(&action.sa_mask);
641 action.sa_sigaction = sigchld_handler;
642 action.sa_flags = SA_SIGINFO;
643 sigaction(SIGCHLD, &action, NULL);
644
645 rb_set_end_proc(Final_dumm, 0);
646 }