195 lines
5.0 KiB
C
195 lines
5.0 KiB
C
/* Copyright 2010, 2011, 2012 Tony Garnock-Jones <tonygarnockjones@gmail.com>.
|
|
*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <ucontext.h>
|
|
|
|
#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;
|
|
}
|