/* Copyright 2010, 2011, 2012 Tony Garnock-Jones . * * This file is part of Hop. * * Hop 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 3 of the License, or * (at your option) any later version. * * Hop 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. * * You should have received a copy of the GNU General Public License * along with Hop. If not, see . */ #include #include #include #include #include #include #include "cmsg_private.h" #include "ref.h" #include "sexp.h" #include "harness.h" #include "sexpio.h" #include "hashtable.h" #include "node.h" #include "meta.h" #include "messages.h" static cmsg_bytes_t _container_name; static hashtable_t node_class_table; static hashtable_t directory; static void *node_incref(void *arg) { return INCREF((node_t *) arg); } static void node_decref(void *arg) { DECREF((node_t *) arg, node_destructor); } void init_node(cmsg_bytes_t container_name) { if (container_name.len == 0) { unsigned char buf[CMSG_UUID_BUF_SIZE]; gen_uuid(buf); _container_name = cmsg_bytes_malloc_dup(CMSG_BYTES(CMSG_UUID_BUF_SIZE, buf)); } else { _container_name = cmsg_bytes_malloc_dup(container_name); } info("Local container name is <<%.*s>>\n", _container_name.len, _container_name.bytes); init_hashtable(&node_class_table, 31, NULL, NULL); init_hashtable(&directory, 10007, node_incref, node_decref); } cmsg_bytes_t local_container_name(void) { return _container_name; } void register_node_class(node_class_t *nc) { cmsg_bytes_t key = cmsg_cstring_bytes(nc->name); if (hashtable_contains(&node_class_table, key)) { die("Duplicate node class name %s\n", nc->name); } hashtable_put(&node_class_table, key, nc); } node_class_t *lookup_node_class(cmsg_bytes_t name) { node_class_t *nc = NULL; hashtable_get(&node_class_table, name, (void **) &nc); return nc; } static void init_node_names(node_t *n) { init_hashtable(&n->names, 5, NULL, NULL); } node_t *new_node(node_class_t *nc, sexp_t *args, sexp_t **error_out) { node_t *n = malloc(sizeof(*n)); n->refcount = ZERO_REFCOUNT(); n->node_class = nc; n->extension = NULL; init_node_names(n); if (nc->extend != NULL) { sexp_t *error = nc->extend(n, args); if (error != NULL) { node_destructor(n); if (error_out != NULL) { *error_out = error; } else { warn("Creating node of class %s failed with ", nc->name); sexp_writeln(stderr_h, error); } return NULL; } } return n; } void node_destructor(node_t *n) { if (n->node_class->destroy != NULL) { n->node_class->destroy(n); } unbind_all_names_for_node(n); destroy_hashtable(&n->names); free(n); } node_t *lookup_node(cmsg_bytes_t name) { node_t *n = NULL; hashtable_get(&directory, name, (void **) &n); return n; } static void announce_binding(cmsg_bytes_t name, int onoff) { sexp_t *filter = sexp_bytes(name); INCREF(filter); announce_subscription(sexp_empty_bytes, filter, sexp_empty_bytes, sexp_empty_bytes, onoff); DECREF(filter, sexp_destructor); } int bind_node(cmsg_bytes_t name, node_t *n) { if (name.len == 0) { warn("Binding to empty name forbidden\n"); return 0; } if (hashtable_contains(&directory, name)) { return 0; } hashtable_put(&directory, name, n); hashtable_put(&n->names, name, NULL); info("Binding node <<%.*s>> of class %s\n", name.len, name.bytes, n->node_class->name); announce_binding(name, 1); return 1; } int unbind_node(cmsg_bytes_t name) { node_t *n = NULL; hashtable_get(&directory, name, (void **) &n); if (n == NULL) { return 0; } else { info("Unbinding node <<%.*s>> of class %s\n", name.len, name.bytes, n->node_class->name); hashtable_erase(&n->names, name); hashtable_erase(&directory, name); announce_binding(name, 0); return 1; } } static void unbind_on_destroy(void *context, cmsg_bytes_t key, void *value) { unbind_node(key); } void unbind_all_names_for_node(node_t *n) { hashtable_t names = n->names; init_node_names(n); hashtable_foreach(&names, unbind_on_destroy, NULL); destroy_hashtable(&names); } int post_node(cmsg_bytes_t node, cmsg_bytes_t name, sexp_t *body, sexp_t *token) { return send_node_release(node, message_post(sexp_bytes(name), body, token)); } int send_node(cmsg_bytes_t node, sexp_t *message) { node_t *n = lookup_node(node); if (n == NULL) { return 0; } n->node_class->handle_message(n, message); return 1; } int send_node_release(cmsg_bytes_t node, sexp_t *message) { int result; INCREF(message); result = send_node(node, message); DECREF(message, sexp_destructor); return result; }