void read_serial(void * cvoid, zctx_t * context, void * pipe) { char * buf; serialconfig_t * config = (serialconfig_t*)cvoid; FILE * in = config->in; // fopen("/dev/ttyO1", "r"); size_t nbytes=2047; // Prepare our context and publisher buf = (char *) malloc(nbytes+1) ; fprintf(stderr, "bound\n"); // first line is always garbage getline(&buf, &nbytes, in); child_handshake(pipe); zsocket_destroy(context, pipe); void* socket = zsocket_new(context, ZMQ_PUB); zsocket_bind(socket, "inproc://raw_serial"); while ( getline(&buf, &nbytes, in) != -1 ) { #ifdef DEBUG puts("line:"); puts(buf); #endif zmsg_t * msg = zmsg_new(); zmsg_pushstr(msg, buf); // does buf need to be copied? zmsg_send(&msg, socket); } fprintf(stderr, "error reading from stdin\n"); zsocket_destroy(context, socket); }
void watch_port(void *cvoid, zctx_t * context, void * pipe ) { zclock_log("watch_port started!"); monitorconfig_t * config = (monitorconfig_t*) cvoid; dump_monitorconfig(config); void * linein = zsocket_new(context, ZMQ_SUB); char * listen_socket = to_linesocket(config->line_id); char line_id[16]; snprintf(line_id, 15, "%d", config->line_id); zsocket_connect(linein, listen_socket); zsockopt_set_unsubscribe(linein, ""); zsockopt_set_subscribe(linein, "CLEAR_MONITORS"); zsockopt_set_subscribe(linein, "VALUE"); // have set up subscription, can signal parent that we're ok. child_handshake(pipe); zsocket_destroy(context, pipe); // no longer require pipe void * lineout = zsocket_new(context, ZMQ_PUB); zsocket_connect(lineout, config->out_socket); time_t until = time(NULL) + 60; while(time(NULL)<until) { zmsg_t * msg = zmsg_recv(linein); if(!msg) { zclock_log("monitor quitting!"); return; } zframe_t * cmd = zmsg_pop(msg); if(zframe_streq(cmd, "CLEAR_MONITORS")) { zclock_log("ephemeral monitor quitting"); zmsg_destroy(&msg); zframe_destroy(&cmd); break; } else if (zframe_streq(cmd, "VALUE")) { // TODO perhaps some rate limiting necessary assert(zmsg_size(msg) == 2); zframe_t * value = zmsg_pop(msg); int res = *(int*)zframe_data(value); char * new_channel = zmsg_popstr(msg); if(strcmp(new_channel, config->channel)!=0) { zclock_log("monitor on %d: listening for %s, channel changed to %s quitting", config->line_id, config->channel, new_channel); zmsg_destroy(&msg); zframe_destroy(&cmd); break; } zmsg_t * to_send = zmsg_new(); char buf[1024]; snprintf(buf,1023, "%d", res); zmsg_pushstr(to_send, buf); zmsg_pushstr(to_send, line_id); zmsg_pushstr(to_send, config->source_worker); zclock_log("%s sending line %s -> %s", config->source_worker, line_id, buf); zmsg_send(&to_send, lineout); // don't destroy value frame, now owned by zmsg } // else ignore zmsg_destroy(&msg); zframe_destroy(&cmd); } zclock_log("monitor on %d: listening for %s, expiring naturally", config->line_id, config->channel); //cleanup zsocket_destroy(context, linein); zsocket_destroy(context, lineout); }
void generic_worker(void * cvoid, zctx_t * context, void * pipe) { workerconfig_t *config = (workerconfig_t*) cvoid; rulepackage_t rpkg; child_handshake(pipe); rpkg.channel = config->channel; rpkg.actions = zhash_new(); rpkg.triggers = zhash_new(); rpkg.context = context; rpkg.base_config = config->base_config; // FIX this needs to be initialised differently for each channel // normal channels will always have the "value" service, // but (for instance) the Camera channel has the "photo" service // zhash_t * services = zhash_new(); char * ninja = config->base_config->uuid; rpkg.servicename = malloc(strlen(ninja) + strlen(rpkg.channel) + 4); sprintf(rpkg.servicename, "n:%s:%s", ninja,rpkg.channel); channeldb_t *db = db_init(rpkg.servicename); zclock_log("%s worker preparing rules...", rpkg.servicename); zclock_log("%s worker reloading rules from db...", rpkg.servicename); db_reload_rules(db, &rpkg); // reload_rules_db(context, db, rpkg.servicename, channel, rules, config->base_config); zclock_log("%s worker checking rules with server...", rpkg.servicename); char * reup = config->base_config->reup_endpoint; if(!reup) { zclock_log("E: NO REUP CONNECTION DEFINED! Probably dealing with an old server."); } else { void * reup_sock = zsocket_new(rpkg.context, ZMQ_DEALER); zclock_log("connecting to %s", reup); zsocket_connect(reup_sock, reup); zlist_t * rules = fetch_rules(reup_sock, rpkg.servicename); zmsg_t *tmp; while(tmp=zlist_pop(rules)) { // handle_command(tmp, NULL, &rpkg, db); // FIXME what do we do // with the reply? } } db_display_rules(db) ; zclock_log("%s worker connecting...", rpkg.servicename); mdwrk_t *session = mdwrk_new (config->base_config->broker_endpoint, rpkg.servicename, 0); zclock_log("%s worker connected!", rpkg.servicename); zmsg_t *reply = NULL; while (1) { zclock_log("%s worker waiting for work.", rpkg.servicename); zmsg_t *request = mdwrk_recv (session, &reply); if (request == NULL) break; // Worker was interrupted reply = zmsg_new(); handle_command(request, reply, &rpkg, db); } db_destroy(db); mdwrk_destroy (&session); return; }
void generic_worker(void * cvoid, zctx_t * context, void * pipe) { workerconfig_t *config = (workerconfig_t*) cvoid; zhash_t * rules = zhash_new(); child_handshake(pipe); zmsg_t *reply = NULL; void * rule_pipe = NULL; char * ninja = config->base_config->identity; char * channel = config->channel; char * servicename = malloc(strlen(ninja) + strlen(channel) + 2); sprintf(servicename, "%s:%s", ninja,channel); // sqlite stuff int rc; char * tail = NULL; sqlite3 *db = init_db(servicename); sqlite3_stmt * insert_stmt; zclock_log("%s worker preparing rules...", servicename); sqlite3_prepare_v2(db, "insert into rules VALUES (@RULEID, @TRIGGER_NAME, @TARGET_WORKER, @AUTH, @ADDINS);", 512, &insert_stmt, NULL); zclock_log("%s worker reloading rules...", servicename); reload_rules(context, db, servicename, channel, rules); zclock_log("%s worker connecting...", servicename); mdwrk_t *session = mdwrk_new (config->base_config->broker_endpoint, servicename, 0); zclock_log("%s worker connected!", servicename); while (1) { zclock_log("%s worker waiting for work.", servicename); zmsg_t *request = mdwrk_recv (session, &reply); if (request == NULL) break; // Worker was interrupted char * command = zmsg_popstr(request); char * rule_id = zmsg_popstr(request); zclock_log("%s worker servicing request %s for rule %s", servicename,command,rule_id); reply = zmsg_new(); if (strcmp(command, "AddTrigger") == 0) { zclock_log("new trigger!"); if (zhash_lookup(rules, rule_id)) { // already have a rule with that id! WTF?? // FIXME should probably delete this and reinstate zclock_log("Received duplicate rule %s, ignoring", rule_id); zmsg_destroy(&request); zmsg_pushstr(reply, "duplicate"); } else { triggerconfig_t * tconf = malloc(sizeof(triggerconfig_t)); create_triggerconfig(tconf, request, channel, rule_id); char * created = create_trigger(rules, rule_id, context, tconf); if(NULL == created) { // happy path, so add to db sqlite3_bind_text(insert_stmt, 1, tconf->rule_id, -1, SQLITE_TRANSIENT); sqlite3_bind_text(insert_stmt, 2, tconf->trigger_name, -1, SQLITE_TRANSIENT); sqlite3_bind_text(insert_stmt, 3, tconf->target_worker, -1, SQLITE_TRANSIENT); sqlite3_bind_text(insert_stmt, 4, tconf->auth, -1, SQLITE_TRANSIENT); sqlite3_bind_text(insert_stmt, 5, tconf->addins, -1, SQLITE_TRANSIENT); sqlite3_step(insert_stmt); sqlite3_clear_bindings(insert_stmt); sqlite3_reset(insert_stmt); zmsg_pushstr(reply, "ok"); } else { zclock_log("create_trigger failed: %s", created); zmsg_pushstr(reply, created); } free(created); } } else if (strcmp(command,"RemoveRule") == 0) { if (rule_pipe=zhash_lookup(rules, rule_id)) { // found it zclock_log("rule %s exists, removing.", rule_id); send_sync("Destroy",rule_pipe); zclock_log("rule %s waiting for OK from pipe", rule_id); recv_sync("ok", rule_pipe); zsocket_destroy(context, rule_pipe); zhash_delete(rules, rule_id); zmsg_pushstr(reply, "ok"); zclock_log("rule %s completely destroyed", rule_id); } else { // not there! zclock_log("Received delete trigger request for nonexistent rule %s, ignoring", rule_id); zmsg_pushstr(reply, "rule not found"); } } else if (strcmp(command, "AddMonitor")==0) { // unconditionally fork a monitor for each line // they'll die when they get a channel change int i; for(i=1; i<4; i++) { monitorconfig_t * mconf = malloc(sizeof(monitorconfig_t)); mconf->line_id = i; mconf->source_worker = servicename; mconf->out_socket = config->base_config->portwatcher_endpoint; mconf->channel = channel; void * monitor_pipe = zthread_fork(context, watch_port, (void*)mconf); send_sync("ping", monitor_pipe); recv_sync("pong", monitor_pipe); zsocket_destroy(context, monitor_pipe); } zmsg_pushstr(reply, "ok"); } else { zclock_log("Can't handle command %s: ignoring", command); } zmsg_destroy(&request); } mdwrk_destroy (&session); return; }
void line_listener(void * cvoid, zctx_t * context, void * pipe) { lineconfig_t * config = (lineconfig_t*) cvoid; // atm, topic == outpipe, but this is coincidental... zmsg_t * msg; dump_lineconfig(config); channel_memory_t channel_memory = { strdup("unknown"), strdup("unknown"), 0 }; // void * monitor_controller = zsocket_new(config->context, ZMQ_PUB); // zsocket_bind(monitor_controller, "inproc://monitor_controller"); // int trigger_capacity = 1; void * lineout = zsocket_new(context, ZMQ_PUB); char * outpipe = to_linesocket(config->line_id); zclock_log("binding line |%s|", outpipe); zsocket_bind(lineout, outpipe); void * subscriber = zsocket_new(context, ZMQ_SUB); zclock_log("subscribing to line |%d|", config->line_id); zsockopt_set_unsubscribe(subscriber, ""); char * topic = to_line(config->line_id); // zsockopt_set_subscribe(subscriber, topic); zclock_log("subscribing to literal line |%s|", topic); zsocket_connect(subscriber, "inproc://line"); child_handshake(pipe); zsocket_destroy(context, pipe); while(1) { msg = zmsg_recv(subscriber); if(!msg) { zclock_log("line quitting!"); return; } // zmsg_dump(msg); char * recv_topic = zmsg_popstr(msg); // zclock_log("line got topic\nreceived: %s\nexpected: %s\n", recv_topic, config->topic); //fflush(stdout); assert(strcmp(recv_topic, topic)==0); free(recv_topic); assert(zmsg_size(msg) == 2); char * channel = zmsg_popstr(msg); zmsg_t * out = zmsg_new(); // originally, I thought it was a neat trick to not mention the // channel in every message. unfortunately, this screws up the // case where a new trigger gets introduced: at the beginning, it // has no idea what the channel currently is. // rather than trying to micro-optimise, let's just keep repeating // the channel in the value update too. if (port_changed(channel, &channel_memory)) { zmsg_pushstr(out, channel_memory.current_channel); zmsg_pushstr(out, "CHANNEL_CHANGE"); zmsg_send(&out, lineout); } // only send a value if we're all settled down if(strcmp(channel, channel_memory.current_channel)==0) { out = zmsg_new(); zmsg_pushstr(out, channel_memory.current_channel); zmsg_push(out, zmsg_pop(msg)); zmsg_pushstr(out, "VALUE"); zmsg_send(&out, lineout); } free(channel); zmsg_destroy(&msg); } free(config); }