* ruby extension extracted from irdumm
[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 * Guest bindings
92 */
93 static VALUE guest_get(VALUE class, VALUE key)
94 {
95 enumerator_t *enumerator;
96 guest_t *guest, *found = NULL;
97
98 enumerator = dumm->create_guest_enumerator(dumm);
99 while (enumerator->enumerate(enumerator, &guest))
100 {
101 if (streq(guest->get_name(guest), StringValuePtr(key)))
102 {
103 found = guest;
104 break;
105 }
106 }
107 enumerator->destroy(enumerator);
108 if (!found)
109 {
110 rb_raise(rb_eRuntimeError, "guest not found");
111 }
112 return Data_Wrap_Struct(class, NULL, NULL, found);
113 }
114
115 static VALUE guest_each(int argc, VALUE *argv, VALUE class)
116 {
117 enumerator_t *enumerator;
118 guest_t *guest;
119
120 if (!rb_block_given_p())
121 {
122 rb_raise(rb_eArgError, "must be called with a block");
123 }
124 enumerator = dumm->create_guest_enumerator(dumm);
125 while (enumerator->enumerate(enumerator, &guest))
126 {
127 rb_yield(Data_Wrap_Struct(class, NULL, NULL, guest));
128 }
129 enumerator->destroy(enumerator);
130 return class;
131 }
132
133 static VALUE guest_new(VALUE class, VALUE name, VALUE kernel,
134 VALUE master, VALUE mem)
135 {
136 guest_t *guest;
137
138 guest = dumm->create_guest(dumm, StringValuePtr(name), StringValuePtr(kernel),
139 StringValuePtr(master), FIX2INT(mem));
140 if (!guest)
141 {
142 rb_raise(rb_eRuntimeError, "creating guest failed");
143 }
144 return Data_Wrap_Struct(class, NULL, NULL, guest);
145 }
146
147 static VALUE guest_to_s(VALUE self)
148 {
149 guest_t *guest;
150
151 Data_Get_Struct(self, guest_t, guest);
152 return rb_str_new2(guest->get_name(guest));
153 }
154
155 static VALUE guest_start(VALUE self)
156 {
157 guest_t *guest;
158
159 Data_Get_Struct(self, guest_t, guest);
160
161 if (!guest->start(guest, invoke, NULL, NULL))
162 {
163 rb_raise(rb_eRuntimeError, "starting guest failed");
164 }
165 return self;
166 }
167
168 static VALUE guest_stop(VALUE self)
169 {
170 guest_t *guest;
171
172 Data_Get_Struct(self, guest_t, guest);
173 guest->stop(guest, NULL);
174 return self;
175 }
176
177 static void exec_cb(void *data, char *buf)
178 {
179 rb_yield(rb_str_new2(buf));
180 }
181
182 static VALUE guest_exec(VALUE self, VALUE cmd)
183 {
184 guest_t *guest;
185 bool block;
186 int ret;
187
188 block = rb_block_given_p();
189 Data_Get_Struct(self, guest_t, guest);
190 if ((ret = guest->exec_str(guest, block ? (void*)exec_cb : NULL, TRUE, NULL,
191 "%s", StringValuePtr(cmd))) != 0)
192 {
193 rb_raise(rb_eRuntimeError, "executing command failed (%d)", ret);
194 }
195 return self;
196 }
197
198 static VALUE guest_add_iface(VALUE self, VALUE name)
199 {
200 guest_t *guest;
201 iface_t *iface;
202
203 Data_Get_Struct(self, guest_t, guest);
204 iface = guest->create_iface(guest, StringValuePtr(name));
205 if (!iface)
206 {
207 rb_raise(rb_eRuntimeError, "adding interface failed");
208 }
209 return Data_Wrap_Struct(rbc_iface, NULL, NULL, iface);
210 }
211
212 static VALUE guest_get_iface(VALUE self, VALUE key)
213 {
214 enumerator_t *enumerator;
215 iface_t *iface, *found = NULL;
216 guest_t *guest;
217
218 Data_Get_Struct(self, guest_t, guest);
219 enumerator = guest->create_iface_enumerator(guest);
220 while (enumerator->enumerate(enumerator, &iface))
221 {
222 if (streq(iface->get_guestif(iface), StringValuePtr(key)))
223 {
224 found = iface;
225 break;
226 }
227 }
228 enumerator->destroy(enumerator);
229 if (!found)
230 {
231 rb_raise(rb_eRuntimeError, "interface not found");
232 }
233 return Data_Wrap_Struct(rbc_iface, NULL, NULL, iface);
234 }
235
236 static VALUE guest_each_iface(int argc, VALUE *argv, VALUE self)
237 {
238 enumerator_t *enumerator;
239 guest_t *guest;
240 iface_t *iface;
241
242 if (!rb_block_given_p())
243 {
244 rb_raise(rb_eArgError, "must be called with a block");
245 }
246 Data_Get_Struct(self, guest_t, guest);
247 enumerator = guest->create_iface_enumerator(guest);
248 while (enumerator->enumerate(enumerator, &iface))
249 {
250 rb_yield(Data_Wrap_Struct(rbc_iface, NULL, NULL, iface));
251 }
252 enumerator->destroy(enumerator);
253 return self;
254 }
255
256 static VALUE guest_delete(VALUE self)
257 {
258 guest_t *guest;
259
260 Data_Get_Struct(self, guest_t, guest);
261 dumm->delete_guest(dumm, guest);
262 return Qnil;
263 }
264
265 static void guest_init()
266 {
267 rbc_guest = rb_define_class_under(rbm_dumm , "Guest", rb_cObject);
268 rb_define_singleton_method(rbc_guest, "[]", guest_get, 1);
269 rb_define_singleton_method(rbc_guest, "each", guest_each, -1);
270 rb_define_singleton_method(rbc_guest, "new", guest_new, 4);
271 rb_define_method(rbc_guest, "to_s", guest_to_s, 0);
272 rb_define_method(rbc_guest, "start", guest_start, 0);
273 rb_define_method(rbc_guest, "stop", guest_stop, 0);
274 rb_define_method(rbc_guest, "exec", guest_exec, 1);
275 rb_define_method(rbc_guest, "add", guest_add_iface, 1);
276 rb_define_method(rbc_guest, "[]", guest_get_iface, 1);
277 rb_define_method(rbc_guest, "each", guest_each_iface, -1);
278 rb_define_method(rbc_guest, "delete", guest_delete, 0);
279 rb_include_module(rb_class_of(rbc_guest), rb_mEnumerable);
280 rb_include_module(rbc_guest, rb_mEnumerable);
281 }
282
283 /**
284 * Bridge binding
285 */
286 static VALUE bridge_get(VALUE class, VALUE key)
287 {
288 enumerator_t *enumerator;
289 bridge_t *bridge, *found = NULL;
290
291 enumerator = dumm->create_bridge_enumerator(dumm);
292 while (enumerator->enumerate(enumerator, &bridge))
293 {
294 if (streq(bridge->get_name(bridge), StringValuePtr(key)))
295 {
296 found = bridge;
297 break;
298 }
299 }
300 enumerator->destroy(enumerator);
301 if (!found)
302 {
303 rb_raise(rb_eRuntimeError, "bridge not found");
304 }
305 return Data_Wrap_Struct(class, NULL, NULL, found);
306 }
307
308 static VALUE bridge_each(int argc, VALUE *argv, VALUE class)
309 {
310 enumerator_t *enumerator;
311 bridge_t *bridge;
312
313 if (!rb_block_given_p())
314 {
315 rb_raise(rb_eArgError, "must be called with a block");
316 }
317 enumerator = dumm->create_bridge_enumerator(dumm);
318 while (enumerator->enumerate(enumerator, &bridge))
319 {
320 rb_yield(Data_Wrap_Struct(class, NULL, NULL, bridge));
321 }
322 enumerator->destroy(enumerator);
323 return class;
324 }
325
326 static VALUE bridge_new(VALUE class, VALUE name)
327
328 {
329 bridge_t *bridge;
330
331 bridge = dumm->create_bridge(dumm, StringValuePtr(name));
332 if (!bridge)
333 {
334 rb_raise(rb_eRuntimeError, "creating bridge failed");
335 }
336 return Data_Wrap_Struct(class, NULL, NULL, bridge);
337 }
338
339 static VALUE bridge_to_s(VALUE self)
340 {
341 bridge_t *bridge;
342
343 Data_Get_Struct(self, bridge_t, bridge);
344 return rb_str_new2(bridge->get_name(bridge));
345 }
346
347 static VALUE bridge_each_iface(int argc, VALUE *argv, VALUE self)
348 {
349 enumerator_t *enumerator;
350 bridge_t *bridge;
351 iface_t *iface;
352
353 if (!rb_block_given_p())
354 {
355 rb_raise(rb_eArgError, "must be called with a block");
356 }
357 Data_Get_Struct(self, bridge_t, bridge);
358 enumerator = bridge->create_iface_enumerator(bridge);
359 while (enumerator->enumerate(enumerator, &iface))
360 {
361 rb_yield(Data_Wrap_Struct(rbc_iface, NULL, NULL, iface));
362 }
363 enumerator->destroy(enumerator);
364 return self;
365 }
366
367 static VALUE bridge_delete(VALUE self)
368 {
369 bridge_t *bridge;
370
371 Data_Get_Struct(self, bridge_t, bridge);
372 dumm->delete_bridge(dumm, bridge);
373 return Qnil;
374 }
375
376 static void bridge_init()
377 {
378 rbc_bridge = rb_define_class_under(rbm_dumm , "Bridge", rb_cObject);
379 rb_define_singleton_method(rbc_bridge, "[]", bridge_get, 1);
380 rb_define_singleton_method(rbc_bridge, "each", bridge_each, -1);
381 rb_define_singleton_method(rbc_bridge, "new", bridge_new, 1);
382 rb_define_method(rbc_bridge, "to_s", bridge_to_s, 0);
383 rb_define_method(rbc_bridge, "each", bridge_each_iface, -1);
384 rb_define_method(rbc_bridge, "delete", bridge_delete, 0);
385 rb_include_module(rb_class_of(rbc_bridge), rb_mEnumerable);
386 rb_include_module(rbc_bridge, rb_mEnumerable);
387 }
388
389 /**
390 * Iface wrapper
391 */
392 static VALUE iface_to_s(VALUE self)
393 {
394 iface_t *iface;
395
396 Data_Get_Struct(self, iface_t, iface);
397 return rb_str_new2(iface->get_hostif(iface));
398 }
399
400 static VALUE iface_connect(VALUE self, VALUE vbridge)
401 {
402 iface_t *iface;
403 bridge_t *bridge;
404
405 Data_Get_Struct(self, iface_t, iface);
406 Data_Get_Struct(vbridge, bridge_t, bridge);
407 if (!bridge->connect_iface(bridge, iface))
408 {
409 rb_raise(rb_eRuntimeError, "connecting iface failed");
410 }
411 return self;
412 }
413
414 static VALUE iface_disconnect(VALUE self)
415 {
416 iface_t *iface;
417 bridge_t *bridge;
418
419 Data_Get_Struct(self, iface_t, iface);
420 bridge = iface->get_bridge(iface);
421 if (!bridge || !bridge->disconnect_iface(bridge, iface))
422 {
423 rb_raise(rb_eRuntimeError, "disconnecting iface failed");
424 }
425 return self;
426 }
427
428 static VALUE iface_add_addr(VALUE self, VALUE name)
429 {
430 iface_t *iface;
431 host_t *addr;
432
433 addr = host_create_from_string(StringValuePtr(name), 0);
434 if (!addr)
435 {
436 rb_raise(rb_eArgError, "invalid IP address");
437 }
438 Data_Get_Struct(self, iface_t, iface);
439 if (!iface->add_address(iface, addr))
440 {
441 rb_raise(rb_eRuntimeError, "adding address failed");
442 }
443 addr->destroy(addr);
444 return self;
445 }
446
447 static VALUE iface_each_addr(int argc, VALUE *argv, VALUE self)
448 {
449 enumerator_t *enumerator;
450 iface_t *iface;
451 host_t *addr;
452 char buf[64];
453
454 if (!rb_block_given_p())
455 {
456 rb_raise(rb_eArgError, "must be called with a block");
457 }
458 Data_Get_Struct(self, iface_t, iface);
459 enumerator = iface->create_address_enumerator(iface);
460 while (enumerator->enumerate(enumerator, &addr))
461 {
462 snprintf(buf, sizeof(buf), "%H", addr);
463 rb_yield(rb_str_new2(buf));
464 }
465 enumerator->destroy(enumerator);
466 return self;
467 }
468
469 static VALUE iface_del_addr(VALUE self, VALUE vaddr)
470 {
471 iface_t *iface;
472 host_t *addr;
473
474 addr = host_create_from_string(StringValuePtr(vaddr), 0);
475 if (!addr)
476 {
477 rb_raise(rb_eArgError, "invalid IP address");
478 }
479 Data_Get_Struct(self, iface_t, iface);
480 if (!iface->delete_address(iface, addr))
481 {
482 addr->destroy(addr);
483 rb_raise(rb_eRuntimeError, "address not found");
484 }
485 addr->destroy(addr);
486 return self;
487 }
488
489 static VALUE iface_delete(VALUE self)
490 {
491 guest_t *guest;
492 iface_t *iface;
493
494 Data_Get_Struct(self, iface_t, iface);
495 guest = iface->get_guest(iface);
496 guest->destroy_iface(guest, iface);
497 return Qnil;
498 }
499
500 static void iface_init()
501 {
502 rbc_iface = rb_define_class_under(rbm_dumm , "Iface", rb_cObject);
503 rb_define_method(rbc_iface, "to_s", iface_to_s, 0);
504 rb_define_method(rbc_iface, "connect", iface_connect, 1);
505 rb_define_method(rbc_iface, "disconnect", iface_disconnect, 0);
506 rb_define_method(rbc_iface, "add", iface_add_addr, 1);
507 rb_define_method(rbc_iface, "del", iface_del_addr, 1);
508 rb_define_method(rbc_iface, "each", iface_each_addr, -1);
509 rb_define_method(rbc_iface, "delete", iface_delete, 0);
510 rb_include_module(rbc_iface, rb_mEnumerable);
511 }
512
513 static VALUE template_load(VALUE class, VALUE name)
514 {
515 if (!dumm->load_template(dumm, StringValuePtr(name)))
516 {
517 rb_raise(rb_eRuntimeError, "loading template failed");
518 }
519 return class;
520 }
521
522 static VALUE template_unload(VALUE class)
523 {
524 if (!dumm->load_template(dumm, NULL))
525 {
526 rb_raise(rb_eRuntimeError, "unloading template failed");
527 }
528 return class;
529 }
530
531 static void template_init()
532 {
533 rbc_template = rb_define_class_under(rbm_dumm , "Template", rb_cObject);
534 rb_define_singleton_method(rbc_template, "load", template_load, 1);
535 rb_define_singleton_method(rbc_template, "unload", template_unload, 0);
536 }
537
538 /**
539 * extension finalization
540 */
541 void Final_dumm()
542 {
543 struct sigaction action;
544
545 dumm->destroy(dumm);
546
547 sigemptyset(&action.sa_mask);
548 action.sa_handler = SIG_DFL;
549 action.sa_flags = 0;
550 sigaction(SIGCHLD, &action, NULL);
551
552 library_deinit();
553 }
554
555 /**
556 * extension initialization
557 */
558 void Init_dumm()
559 {
560 struct sigaction action;
561
562 /* there are too many to report, rubyruby... */
563 setenv("LEAK_DETECTIVE_DISABLE", "1", 1);
564
565 library_init(NULL);
566
567 dumm = dumm_create(NULL);
568
569 rbm_dumm = rb_define_module("Dumm");
570
571 guest_init();
572 bridge_init();
573 iface_init();
574 template_init();
575
576 sigemptyset(&action.sa_mask);
577 action.sa_sigaction = sigchld_handler;
578 action.sa_flags = SA_SIGINFO;
579 sigaction(SIGCHLD, &action, NULL);
580
581 rb_set_end_proc(Final_dumm, 0);
582 }