/* Copyright (C) 2010, 2011 Tony Garnock-Jones. All rights reserved. */ #include #include #include #include #include #include #include #include #include "cmsg_private.h" #include "harness.h" #include "ref.h" #include "sexp.h" #include "hashtable.h" #include "node.h" #include "messages.h" #include "subscription.h" #include "sexpio.h" typedef struct fanout_extension_t_ { sexp_t *name; hashtable_t subscriptions; } fanout_extension_t; static sexp_t *fanout_extend(node_t *n, sexp_t *args) { if ((sexp_length(args) == 1) && sexp_stringp(sexp_head(args))) { cmsg_bytes_t name = sexp_data(sexp_head(args)); fanout_extension_t *f = calloc(1, sizeof(*f)); f->name = INCREF(sexp_head(args)); init_hashtable(&f->subscriptions, 5, NULL, (void (*)(void *)) free_subscription); n->extension = f; return bind_node(name, n) ? NULL : sexp_cstring("bind failed"); } else { return sexp_cstring("invalid args"); } } static void fanout_destructor(node_t *n) { fanout_extension_t *f = n->extension; if (f != NULL) { /* can be NULL if fanout_extend was given invalid args */ DECREF(f->name, sexp_destructor); destroy_hashtable(&f->subscriptions); free(f); } } struct delivery_context { fanout_extension_t *f; sexp_t *body; }; static void send_to_sub(void *contextv, cmsg_bytes_t key, void *subv) { struct delivery_context *context = contextv; subscription_t *sub = subv; send_to_subscription(context->f->name, &context->f->subscriptions, sub, context->body); } static void fanout_handle_message(node_t *n, sexp_t *m) { fanout_extension_t *f = n->extension; parsed_message_t p; if (parse_post(m, &p)) { struct delivery_context context; context.f = f; context.body = p.post.body; hashtable_foreach(&f->subscriptions, send_to_sub, &context); return; } if (parse_subscribe(m, &p)) { handle_subscribe_message(f->name, &f->subscriptions, &p); return; } if (parse_unsubscribe(m, &p)) { handle_unsubscribe_message(f->name, &f->subscriptions, &p); return; } warn("Message not understood in fanout: "); sexp_writeln(stderr_h, m); } static node_class_t fanout_class = { .name = "fanout", .extend = fanout_extend, .destroy = fanout_destructor, .handle_message = fanout_handle_message }; void init_fanout(void) { register_node_class(&fanout_class); }