/* * Copyright (C) 2008-2010 Tobias Brunner * Copyright (C) 2008 Martin Willi * Hochschule fuer Technik Rapperswil * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. See . * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ #include #include #include #include #include #include #include #include #undef PACKAGE_NAME #undef PACKAGE_TARNAME #undef PACKAGE_VERSION #undef PACKAGE_STRING #undef PACKAGE_BUGREPORT #undef PACKAGE_URL #undef HAVE_DLADDR #undef HAVE_QSORT_R /* avoid redefintiion of snprintf etc. */ #define RUBY_DONT_SUBST /* undef our _GNU_SOURCE, as it gets redefined by */ #undef _GNU_SOURCE #include static dumm_t *dumm; static VALUE rbm_dumm; static VALUE rbc_guest; static VALUE rbc_bridge; static VALUE rbc_iface; static VALUE rbc_template; /** * Guest invocation callback */ static pid_t invoke(void *null, guest_t *guest, char *args[], int argc) { pid_t pid; pid = fork(); switch (pid) { case 0: /* child */ /* create a new process group in order to prevent signals (e.g. * SIGINT) sent to the parent from terminating the child */ setpgid(0, 0); dup2(open("/dev/null", 0), 1); dup2(open("/dev/null", 0), 2); execvp(args[0], args); /* FALL */ case -1: return 0; default: return pid; } } /** * SIGCHLD signal handler */ static void sigchld_handler(int signal, siginfo_t *info, void* ptr) { enumerator_t *enumerator; guest_t *guest; enumerator = dumm->create_guest_enumerator(dumm); while (enumerator->enumerate(enumerator, &guest)) { if (guest->get_pid(guest) == info->si_pid) { guest->sigchild(guest); break; } } enumerator->destroy(enumerator); } /** * Global Dumm bindings */ static VALUE dumm_add_overlay(VALUE class, VALUE dir) { if (!dumm->add_overlay(dumm, StringValuePtr(dir))) { rb_raise(rb_eRuntimeError, "loading overlay failed"); } return class; } static VALUE dumm_del_overlay(VALUE class, VALUE dir) { return dumm->del_overlay(dumm, StringValuePtr(dir)) ? Qtrue : Qfalse; } static VALUE dumm_pop_overlay(VALUE class) { return dumm->pop_overlay(dumm) ? Qtrue : Qfalse; } static void dumm_init() { rbm_dumm = rb_define_module("Dumm"); rb_define_module_function(rbm_dumm, "add_overlay", dumm_add_overlay, 1); rb_define_module_function(rbm_dumm, "del_overlay", dumm_del_overlay, 1); rb_define_module_function(rbm_dumm, "pop_overlay", dumm_pop_overlay, 0); } /** * Guest bindings */ static VALUE guest_hash_create(VALUE class) { enumerator_t *enumerator; guest_t *guest; VALUE hash = rb_hash_new(); enumerator = dumm->create_guest_enumerator(dumm); while (enumerator->enumerate(enumerator, &guest)) { rb_hash_aset(hash, rb_str_new2(guest->get_name(guest)), Data_Wrap_Struct(class, NULL, NULL, guest)); } enumerator->destroy(enumerator); return hash; } static VALUE guest_hash(VALUE class) { ID id = rb_intern("@@guests"); if (!rb_cvar_defined(class, id)) { VALUE hash = guest_hash_create(class); #ifdef RB_CVAR_SET_4_ARGS rb_cvar_set(class, id, hash, 0); #else rb_cvar_set(class, id, hash); #endif return hash; } return rb_cvar_get(class, id); } static VALUE guest_find(VALUE class, VALUE key) { if (TYPE(key) != T_STRING) { key = rb_convert_type(key, T_STRING, "String", "to_s"); } return rb_hash_aref(guest_hash(class), key); } static VALUE guest_get(VALUE class, VALUE key) { return guest_find(class, key); } static VALUE guest_each(int argc, VALUE *argv, VALUE class) { if (!rb_block_given_p()) { rb_raise(rb_eArgError, "must be called with a block"); } rb_block_call(guest_hash(class), rb_intern("each_value"), 0, 0, rb_yield, 0); return class; } static VALUE guest_new(VALUE class, VALUE name, VALUE kernel, VALUE master, VALUE args) { VALUE self; guest_t *guest; guest = dumm->create_guest(dumm, StringValuePtr(name), StringValuePtr(kernel), StringValuePtr(master), StringValuePtr(args)); if (!guest) { rb_raise(rb_eRuntimeError, "creating guest failed"); } self = Data_Wrap_Struct(class, NULL, NULL, guest); rb_hash_aset(guest_hash(class), name, self); return self; } static VALUE guest_to_s(VALUE self) { guest_t *guest; Data_Get_Struct(self, guest_t, guest); return rb_str_new2(guest->get_name(guest)); } static VALUE guest_start(VALUE self) { guest_t *guest; Data_Get_Struct(self, guest_t, guest); if (!guest->start(guest, invoke, NULL, NULL)) { rb_raise(rb_eRuntimeError, "starting guest failed"); } return self; } static VALUE guest_stop(VALUE self) { guest_t *guest; Data_Get_Struct(self, guest_t, guest); guest->stop(guest, NULL); return self; } static VALUE guest_running(VALUE self) { guest_t *guest; Data_Get_Struct(self, guest_t, guest); return guest->get_pid(guest) ? Qtrue : Qfalse; } static void exec_cb(void *data, char *buf) { rb_yield(rb_str_new2(buf)); } static VALUE guest_exec(VALUE self, VALUE cmd) { guest_t *guest; bool block; int ret; block = rb_block_given_p(); Data_Get_Struct(self, guest_t, guest); ret = guest->exec_str(guest, block ? (void*)exec_cb : NULL, TRUE, NULL, "exec %s", StringValuePtr(cmd)); rb_iv_set(self, "@execstatus", INT2NUM(ret)); return self; } static VALUE guest_mconsole(VALUE self, VALUE cmd) { guest_t *guest; bool block; int ret; block = rb_block_given_p(); Data_Get_Struct(self, guest_t, guest); if ((ret = guest->exec_str(guest, block ? (void*)exec_cb : NULL, TRUE, NULL, "%s", StringValuePtr(cmd))) != 0) { rb_raise(rb_eRuntimeError, "executing command failed (%d)", ret); } return self; } static VALUE guest_add_iface(VALUE self, VALUE name) { guest_t *guest; iface_t *iface; Data_Get_Struct(self, guest_t, guest); iface = guest->create_iface(guest, StringValuePtr(name)); if (!iface) { rb_raise(rb_eRuntimeError, "adding interface failed"); } return Data_Wrap_Struct(rbc_iface, NULL, NULL, iface); } static VALUE guest_find_iface(VALUE self, VALUE key) { enumerator_t *enumerator; iface_t *iface, *found = NULL; guest_t *guest; if (TYPE(key) == T_SYMBOL) { key = rb_convert_type(key, T_STRING, "String", "to_s"); } Data_Get_Struct(self, guest_t, guest); enumerator = guest->create_iface_enumerator(guest); while (enumerator->enumerate(enumerator, &iface)) { if (streq(iface->get_guestif(iface), StringValuePtr(key))) { found = iface; break; } } enumerator->destroy(enumerator); if (!found) { return Qnil; } return Data_Wrap_Struct(rbc_iface, NULL, NULL, iface); } static VALUE guest_get_iface(VALUE self, VALUE key) { VALUE iface = guest_find_iface(self, key); if (NIL_P(iface)) { rb_raise(rb_eRuntimeError, "interface not found"); } return iface; } static VALUE guest_each_iface(int argc, VALUE *argv, VALUE self) { enumerator_t *enumerator; linked_list_t *list; guest_t *guest; iface_t *iface; if (!rb_block_given_p()) { rb_raise(rb_eArgError, "must be called with a block"); } Data_Get_Struct(self, guest_t, guest); list = linked_list_create(); enumerator = guest->create_iface_enumerator(guest); while (enumerator->enumerate(enumerator, &iface)) { list->insert_last(list, iface); } enumerator->destroy(enumerator); while (list->remove_first(list, (void**)&iface) == SUCCESS) { rb_yield(Data_Wrap_Struct(rbc_iface, NULL, NULL, iface)); } list->destroy(list); return self; } static VALUE guest_delete(VALUE self) { guest_t *guest; Data_Get_Struct(self, guest_t, guest); if (guest->get_pid(guest)) { rb_raise(rb_eRuntimeError, "guest is running"); } dumm->delete_guest(dumm, guest); return Qnil; } static VALUE guest_add_overlay(VALUE self, VALUE dir) { guest_t *guest; Data_Get_Struct(self, guest_t, guest); if (!guest->add_overlay(guest, StringValuePtr(dir))) { rb_raise(rb_eRuntimeError, "loading overlay failed"); } return self; } static VALUE guest_del_overlay(VALUE self, VALUE dir) { guest_t *guest; Data_Get_Struct(self, guest_t, guest); return guest->del_overlay(guest, StringValuePtr(dir)) ? Qtrue : Qfalse; } static VALUE guest_pop_overlay(VALUE self) { guest_t *guest; Data_Get_Struct(self, guest_t, guest); return guest->pop_overlay(guest) ? Qtrue : Qfalse; } static void guest_init() { rbc_guest = rb_define_class_under(rbm_dumm , "Guest", rb_cObject); rb_include_module(rb_class_of(rbc_guest), rb_mEnumerable); rb_include_module(rbc_guest, rb_mEnumerable); rb_define_singleton_method(rbc_guest, "[]", guest_get, 1); rb_define_singleton_method(rbc_guest, "each", guest_each, -1); rb_define_singleton_method(rbc_guest, "new", guest_new, 4); rb_define_singleton_method(rbc_guest, "include?", guest_find, 1); rb_define_singleton_method(rbc_guest, "guest?", guest_find, 1); rb_define_method(rbc_guest, "to_s", guest_to_s, 0); rb_define_method(rbc_guest, "start", guest_start, 0); rb_define_method(rbc_guest, "stop", guest_stop, 0); rb_define_method(rbc_guest, "running?", guest_running, 0); rb_define_method(rbc_guest, "exec", guest_exec, 1); rb_define_method(rbc_guest, "mconsole", guest_mconsole, 1); rb_define_method(rbc_guest, "add", guest_add_iface, 1); rb_define_method(rbc_guest, "[]", guest_get_iface, 1); rb_define_method(rbc_guest, "each", guest_each_iface, -1); rb_define_method(rbc_guest, "include?", guest_find_iface, 1); rb_define_method(rbc_guest, "iface?", guest_find_iface, 1); rb_define_method(rbc_guest, "delete", guest_delete, 0); rb_define_method(rbc_guest, "add_overlay", guest_add_overlay, 1); rb_define_method(rbc_guest, "del_overlay", guest_del_overlay, 1); rb_define_method(rbc_guest, "pop_overlay", guest_pop_overlay, 0); rb_define_attr(rbc_guest, "execstatus", 1, 0); } /** * Bridge binding */ static VALUE bridge_find(VALUE class, VALUE key) { enumerator_t *enumerator; bridge_t *bridge, *found = NULL; if (TYPE(key) == T_SYMBOL) { key = rb_convert_type(key, T_STRING, "String", "to_s"); } enumerator = dumm->create_bridge_enumerator(dumm); while (enumerator->enumerate(enumerator, &bridge)) { if (streq(bridge->get_name(bridge), StringValuePtr(key))) { found = bridge; break; } } enumerator->destroy(enumerator); if (!found) { return Qnil; } return Data_Wrap_Struct(class, NULL, NULL, found); } static VALUE bridge_get(VALUE class, VALUE key) { VALUE bridge = bridge_find(class, key); if (NIL_P(bridge)) { rb_raise(rb_eRuntimeError, "bridge not found"); } return bridge; } static VALUE bridge_each(int argc, VALUE *argv, VALUE class) { enumerator_t *enumerator; linked_list_t *list; bridge_t *bridge; if (!rb_block_given_p()) { rb_raise(rb_eArgError, "must be called with a block"); } list = linked_list_create(); enumerator = dumm->create_bridge_enumerator(dumm); while (enumerator->enumerate(enumerator, &bridge)) { list->insert_last(list, bridge); } enumerator->destroy(enumerator); while (list->remove_first(list, (void**)&bridge) == SUCCESS) { rb_yield(Data_Wrap_Struct(class, NULL, NULL, bridge)); } list->destroy(list); return class; } static VALUE bridge_new(VALUE class, VALUE name) { bridge_t *bridge; bridge = dumm->create_bridge(dumm, StringValuePtr(name)); if (!bridge) { rb_raise(rb_eRuntimeError, "creating bridge failed"); } return Data_Wrap_Struct(class, NULL, NULL, bridge); } static VALUE bridge_to_s(VALUE self) { bridge_t *bridge; Data_Get_Struct(self, bridge_t, bridge); return rb_str_new2(bridge->get_name(bridge)); } static VALUE bridge_each_iface(int argc, VALUE *argv, VALUE self) { enumerator_t *enumerator; linked_list_t *list; bridge_t *bridge; iface_t *iface; if (!rb_block_given_p()) { rb_raise(rb_eArgError, "must be called with a block"); } Data_Get_Struct(self, bridge_t, bridge); list = linked_list_create(); enumerator = bridge->create_iface_enumerator(bridge); while (enumerator->enumerate(enumerator, &iface)) { list->insert_last(list, iface); } enumerator->destroy(enumerator); while (list->remove_first(list, (void**)&iface) == SUCCESS) { rb_yield(Data_Wrap_Struct(rbc_iface, NULL, NULL, iface)); } list->destroy(list); return self; } static VALUE bridge_delete(VALUE self) { bridge_t *bridge; Data_Get_Struct(self, bridge_t, bridge); dumm->delete_bridge(dumm, bridge); return Qnil; } static void bridge_init() { rbc_bridge = rb_define_class_under(rbm_dumm , "Bridge", rb_cObject); rb_include_module(rb_class_of(rbc_bridge), rb_mEnumerable); rb_include_module(rbc_bridge, rb_mEnumerable); rb_define_singleton_method(rbc_bridge, "[]", bridge_get, 1); rb_define_singleton_method(rbc_bridge, "each", bridge_each, -1); rb_define_singleton_method(rbc_bridge, "new", bridge_new, 1); rb_define_singleton_method(rbc_bridge, "include?", bridge_find, 1); rb_define_singleton_method(rbc_bridge, "bridge?", bridge_find, 1); rb_define_method(rbc_bridge, "to_s", bridge_to_s, 0); rb_define_method(rbc_bridge, "each", bridge_each_iface, -1); rb_define_method(rbc_bridge, "delete", bridge_delete, 0); } /** * Iface wrapper */ static VALUE iface_to_s(VALUE self) { iface_t *iface; Data_Get_Struct(self, iface_t, iface); return rb_str_new2(iface->get_hostif(iface)); } static VALUE iface_connect(VALUE self, VALUE vbridge) { iface_t *iface; bridge_t *bridge; Data_Get_Struct(self, iface_t, iface); Data_Get_Struct(vbridge, bridge_t, bridge); if (!bridge->connect_iface(bridge, iface)) { rb_raise(rb_eRuntimeError, "connecting iface failed"); } return self; } static VALUE iface_disconnect(VALUE self) { iface_t *iface; bridge_t *bridge; Data_Get_Struct(self, iface_t, iface); bridge = iface->get_bridge(iface); if (!bridge || !bridge->disconnect_iface(bridge, iface)) { rb_raise(rb_eRuntimeError, "disconnecting iface failed"); } return self; } static VALUE iface_add_addr(VALUE self, VALUE name) { iface_t *iface; host_t *addr; int bits; addr = host_create_from_subnet(StringValuePtr(name), &bits); if (!addr) { rb_raise(rb_eArgError, "invalid IP address"); } Data_Get_Struct(self, iface_t, iface); if (!iface->add_address(iface, addr, bits)) { addr->destroy(addr); rb_raise(rb_eRuntimeError, "adding address failed"); } if (rb_block_given_p()) { rb_yield(self); iface->delete_address(iface, addr, bits); } addr->destroy(addr); return self; } static VALUE iface_each_addr(int argc, VALUE *argv, VALUE self) { enumerator_t *enumerator; linked_list_t *list; iface_t *iface; host_t *addr; char buf[64], *fmt = "%H"; if (!rb_block_given_p()) { rb_raise(rb_eArgError, "must be called with a block"); } list = linked_list_create(); Data_Get_Struct(self, iface_t, iface); enumerator = iface->create_address_enumerator(iface); while (enumerator->enumerate(enumerator, &addr)) { list->insert_last(list, addr->clone(addr)); } enumerator->destroy(enumerator); while (list->remove_first(list, (void**)&addr) == SUCCESS) { snprintf(buf, sizeof(buf), fmt, addr); addr->destroy(addr); rb_yield(rb_str_new2(buf)); } list->destroy(list); return self; } static VALUE iface_del_addr(VALUE self, VALUE vaddr) { iface_t *iface; host_t *addr; int bits; addr = host_create_from_subnet(StringValuePtr(vaddr), &bits); if (!addr) { rb_raise(rb_eArgError, "invalid IP address"); } Data_Get_Struct(self, iface_t, iface); if (!iface->delete_address(iface, addr, bits)) { addr->destroy(addr); rb_raise(rb_eRuntimeError, "address not found"); } if (rb_block_given_p()) { rb_yield(self); iface->add_address(iface, addr, bits); } addr->destroy(addr); return self; } static VALUE iface_delete(VALUE self) { guest_t *guest; iface_t *iface; Data_Get_Struct(self, iface_t, iface); guest = iface->get_guest(iface); guest->destroy_iface(guest, iface); return Qnil; } static void iface_init() { rbc_iface = rb_define_class_under(rbm_dumm , "Iface", rb_cObject); rb_include_module(rbc_iface, rb_mEnumerable); rb_define_method(rbc_iface, "to_s", iface_to_s, 0); rb_define_method(rbc_iface, "connect", iface_connect, 1); rb_define_method(rbc_iface, "disconnect", iface_disconnect, 0); rb_define_method(rbc_iface, "add", iface_add_addr, 1); rb_define_method(rbc_iface, "del", iface_del_addr, 1); rb_define_method(rbc_iface, "each", iface_each_addr, -1); rb_define_method(rbc_iface, "delete", iface_delete, 0); } static VALUE template_load(VALUE class, VALUE dir) { if (!dumm->load_template(dumm, StringValuePtr(dir))) { rb_raise(rb_eRuntimeError, "loading template failed"); } return class; } static VALUE template_unload(VALUE class) { if (!dumm->load_template(dumm, NULL)) { rb_raise(rb_eRuntimeError, "unloading template failed"); } return class; } static VALUE template_each(int argc, VALUE *argv, VALUE class) { enumerator_t *enumerator; char *template; if (!rb_block_given_p()) { rb_raise(rb_eArgError, "must be called with a block"); } enumerator = dumm->create_template_enumerator(dumm); while (enumerator->enumerate(enumerator, &template)) { rb_yield(rb_str_new2(template)); } enumerator->destroy(enumerator); return class; } static void template_init() { rbc_template = rb_define_class_under(rbm_dumm , "Template", rb_cObject); rb_include_module(rb_class_of(rbc_template), rb_mEnumerable); rb_define_singleton_method(rbc_template, "load", template_load, 1); rb_define_singleton_method(rbc_template, "unload", template_unload, 0); rb_define_singleton_method(rbc_template, "each", template_each, -1); } /** * extension finalization */ void Final_dumm() { struct sigaction action; dumm->destroy(dumm); sigemptyset(&action.sa_mask); action.sa_handler = SIG_DFL; action.sa_flags = 0; sigaction(SIGCHLD, &action, NULL); library_deinit(); } /** * extension initialization */ void Init_dumm() { struct sigaction action; /* there are too many to report, rubyruby... */ setenv("LEAK_DETECTIVE_DISABLE", "1", 1); library_init(NULL, "dumm"); dumm = dumm_create(NULL); dumm_init(); guest_init(); bridge_init(); iface_init(); template_init(); sigemptyset(&action.sa_mask); action.sa_sigaction = sigchld_handler; action.sa_flags = SA_SIGINFO; sigaction(SIGCHLD, &action, NULL); rb_set_end_proc(Final_dumm, 0); }