int noit_control_dispatch(eventer_t e, int mask, void *closure, struct timeval *now) { u_int32_t cmd; int len = 0, callmask = mask; void *vdelegation_table; noit_hash_table *delegation_table = NULL; acceptor_closure_t *ac = closure; assert(ac->rlen >= 0); while(len >= 0 && ac->rlen < sizeof(cmd)) { len = e->opset->read(e->fd, ((char *)&cmd) + ac->rlen, sizeof(cmd) - ac->rlen, &mask, e); if(len == -1 && errno == EAGAIN) return EVENTER_READ | EVENTER_EXCEPTION; if(len > 0) ac->rlen += len; } assert(ac->rlen >= 0 && ac->rlen <= sizeof(cmd)); if(callmask & EVENTER_EXCEPTION || ac->rlen != sizeof(cmd)) { int newmask; socket_error: /* Exceptions cause us to simply snip the connection */ eventer_remove_fd(e->fd); e->opset->close(e->fd, &newmask, e); acceptor_closure_free(ac); return 0; } ac->cmd = ntohl(cmd); /* Lookup cmd and dispatch */ if(noit_hash_retrieve(&listener_commands, (char *)&ac->dispatch, sizeof(ac->dispatch), (void **)&vdelegation_table)) { void *vfunc; delegation_table = (noit_hash_table *)vdelegation_table; if(noit_hash_retrieve(delegation_table, (char *)&ac->cmd, sizeof(ac->cmd), &vfunc)) { e->callback = *((eventer_func_t *)vfunc); return e->callback(e, callmask, closure, now); } else { const char *event_name; noitL(noit_error, "listener (%s %p) has no command: 0x%08x\n", (event_name = eventer_name_for_callback(ac->dispatch))?event_name:"???", delegation_table, cmd); } } else { const char *event_name; noitL(noit_error, "No delegation table for listener (%s %p)\n", (event_name = eventer_name_for_callback(ac->dispatch))?event_name:"???", delegation_table); } goto socket_error; }
static void noit_console_spit_event(eventer_t e, void *c) { struct timeval now, diff; noit_console_closure_t ncct = c; char fdstr[12]; char wfn[42]; char funcptr[20]; const char *cname; cname = eventer_name_for_callback(e->callback); snprintf(fdstr, sizeof(fdstr), " fd: %d", e->fd); gettimeofday(&now, NULL); sub_timeval(e->whence, now, &diff); snprintf(wfn, sizeof(wfn), " fires: %lld.%06ds", (long long)diff.tv_sec, (int)diff.tv_usec); snprintf(funcptr, sizeof(funcptr), "%p", e->callback); nc_printf(ncct, " [%p]%s%s [%c%c%c%c] -> %s(%p)\n", e, e->mask & (EVENTER_READ | EVENTER_WRITE | EVENTER_EXCEPTION) ? fdstr : "", e->mask & (EVENTER_TIMER) ? wfn : "", e->mask & EVENTER_READ ? 'r' : '-', e->mask & EVENTER_WRITE ? 'w' : '-', e->mask & EVENTER_EXCEPTION ? 'e' : '-', e->mask & EVENTER_TIMER ? 't' : '-', cname ? cname : funcptr, e->closure); }
static void eventer_ports_impl_add(eventer_t e) { assert(e->mask); ev_lock_state_t lockstate; const char *cbname; cbname = eventer_name_for_callback(e->callback); if(e->mask & EVENTER_ASYNCH) { noitL(eventer_deb, "debug: eventer_add asynch (%s)\n", cbname ? cbname : "???"); eventer_add_asynch(NULL, e); return; } /* Recurrent delegation */ if(e->mask & EVENTER_RECURRENT) { noitL(eventer_deb, "debug: eventer_add recurrent (%s)\n", cbname ? cbname : "???"); eventer_add_recurrent(e); return; } /* Timed events are simple */ if(e->mask & EVENTER_TIMER) { eventer_add_timed(e); return; } /* file descriptor event */ noitL(eventer_deb, "debug: eventer_add fd (%s,%d,0x%04x)\n", cbname ? cbname : "???", e->fd, e->mask); lockstate = acquire_master_fd(e->fd); assert(e->whence.tv_sec == 0 && e->whence.tv_usec == 0); master_fds[e->fd].e = e; alter_fd(e, e->mask); release_master_fd(e->fd, lockstate); }
static void eventer_ports_impl_trigger(eventer_t e, int mask) { ev_lock_state_t lockstate; const char *cbname; struct timeval __now; int fd, oldmask, newmask; fd = e->fd; if(e != master_fds[fd].e) return; lockstate = acquire_master_fd(fd); if(lockstate == EV_ALREADY_OWNED) return; assert(lockstate == EV_OWNED); gettimeofday(&__now, NULL); oldmask = e->mask; cbname = eventer_name_for_callback(e->callback); noitLT(eventer_deb, &__now, "ports: fire on %d/%x to %s(%p)\n", fd, mask, cbname?cbname:"???", e->callback); EVENTER_CALLBACK_ENTRY((void *)e->callback, (char *)cbname, fd, e->mask, mask); newmask = e->callback(e, mask, e->closure, &__now); EVENTER_CALLBACK_RETURN((void *)e->callback, (char *)cbname, newmask); if(newmask) { alter_fd(e, newmask); /* Set our mask */ e->mask = newmask; noitLT(eventer_deb, &__now, "ports: complete on %d/(%x->%x) to %s(%p)\n", fd, mask, newmask, cbname?cbname:"???", e->callback); } else { noitLT(eventer_deb, &__now, "ports: complete on %d/none to %s(%p)\n", fd, cbname?cbname:"???", e->callback); /* * Long story long: * When integrating with a few external event systems, we find * it difficult to make their use of remove+add as an update * as it can be recurrent in a single handler call and you cannot * remove completely from the event system if you are going to * just update (otherwise the eventer_t in your call stack could * be stale). What we do is perform a superficial remove, marking * the mask as 0, but not eventer_remove_fd. Then on an add, if * we already have an event, we just update the mask (as we * have not yet returned to the eventer's loop. * This leaves us in a tricky situation when a remove is called * and the add doesn't roll in, we return 0 (mask == 0) and hit * this spot. We have intended to remove the event, but it still * resides at master_fds[fd].e -- even after we free it. * So, in the evnet that we return 0 and the event that * master_fds[fd].e == the event we're about to free... we NULL * it out. */ if(master_fds[fd].e == e) master_fds[fd].e = NULL; eventer_free(e); } release_master_fd(fd, lockstate); }
static void eventer_epoll_impl_trigger(eventer_t e, int mask) { struct timeval __now; int fd, newmask; const char *cbname; ev_lock_state_t lockstate; fd = e->fd; if(e != master_fds[fd].e) return; lockstate = acquire_master_fd(fd); if(lockstate == EV_ALREADY_OWNED) return; assert(lockstate == EV_OWNED); gettimeofday(&__now, NULL); cbname = eventer_name_for_callback(e->callback); noitLT(eventer_deb, &__now, "epoll: fire on %d/%x to %s(%p)\n", fd, mask, cbname?cbname:"???", e->callback); EVENTER_CALLBACK_ENTRY((void *)e->callback, (char *)cbname, fd, e->mask, mask); newmask = e->callback(e, mask, e->closure, &__now); EVENTER_CALLBACK_RETURN((void *)e->callback, (char *)cbname, newmask); if(newmask) { struct epoll_event _ev; memset(&_ev, 0, sizeof(_ev)); _ev.data.fd = fd; if(newmask & EVENTER_READ) _ev.events |= (EPOLLIN|EPOLLPRI); if(newmask & EVENTER_WRITE) _ev.events |= (EPOLLOUT); if(newmask & EVENTER_EXCEPTION) _ev.events |= (EPOLLERR|EPOLLHUP); if(master_fds[fd].e == NULL) { noitL(noit_error, "eventer %s(%p) epoll asked to modify descheduled fd: %d\n", cbname?cbname:"???", e->callback, fd); } else { assert(epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &_ev) == 0); } /* Set our mask */ e->mask = newmask; } else { /* see kqueue implementation for details on the next line */ if(master_fds[fd].e == e) master_fds[fd].e = NULL; eventer_free(e); } release_master_fd(fd, lockstate); }
static void eventer_kqueue_impl_trigger(eventer_t e, int mask) { ev_lock_state_t lockstate; struct timeval __now; int oldmask, newmask; const char *cbname; int fd; fd = e->fd; if(e != master_fds[fd].e) return; lockstate = acquire_master_fd(fd); if(lockstate == EV_ALREADY_OWNED) return; assert(lockstate == EV_OWNED); gettimeofday(&__now, NULL); /* We're going to lie to ourselves. You'd think this should be: * oldmask = e->mask; However, we just fired with masks[fd], so * kqueue is clearly looking for all of the events in masks[fd]. * So, we combine them "just to be safe." */ oldmask = e->mask | masks[fd]; cbname = eventer_name_for_callback(e->callback); noitLT(eventer_deb, &__now, "kqueue: fire on %d/%x to %s(%p)\n", fd, masks[fd], cbname?cbname:"???", e->callback); newmask = e->callback(e, mask, e->closure, &__now); if(newmask) { /* toggle the read bits if needed */ if(newmask & (EVENTER_READ | EVENTER_EXCEPTION)) { if(!(oldmask & (EVENTER_READ | EVENTER_EXCEPTION))) ke_change(fd, EVFILT_READ, EV_ADD | EV_ENABLE, e); } else if(oldmask & (EVENTER_READ | EVENTER_EXCEPTION)) ke_change(fd, EVFILT_READ, EV_DELETE | EV_DISABLE, e); /* toggle the write bits if needed */ if(newmask & EVENTER_WRITE) { if(!(oldmask & EVENTER_WRITE)) ke_change(fd, EVFILT_WRITE, EV_ADD | EV_ENABLE, e); } else if(oldmask & EVENTER_WRITE) ke_change(fd, EVFILT_WRITE, EV_DELETE | EV_DISABLE, e); /* Set our mask */ e->mask = newmask; } else { /* * Long story long: * When integrating with a few external event systems, we find * it difficult to make their use of remove+add as an update * as it can be recurrent in a single handler call and you cannot * remove completely from the event system if you are going to * just update (otherwise the eventer_t in your call stack could * be stale). What we do is perform a superficial remove, marking * the mask as 0, but not eventer_remove_fd. Then on an add, if * we already have an event, we just update the mask (as we * have not yet returned to the eventer's loop. * This leaves us in a tricky situation when a remove is called * and the add doesn't roll in, we return 0 (mask == 0) and hit * this spot. We have intended to remove the event, but it still * resides at master_fds[fd].e -- even after we free it. * So, in the evnet that we return 0 and the event that * master_fds[fd].e == the event we're about to free... we NULL * it out. */ if(master_fds[fd].e == e) master_fds[fd].e = NULL; eventer_free(e); } release_master_fd(fd, lockstate); }
static void mtev_capabilities_tobuff(mtev_capsvc_closure_t *cl, eventer_func_t curr) { const char **mod_names; struct utsname utsn; char vbuff[128], bwstr[4]; mtev_hash_table *lc; mtev_hash_iter iter = MTEV_HASH_ITER_ZERO; const char *k; int klen, i, nmods; void *data; struct timeval now; struct dso_type *t; xmlDocPtr xmldoc; xmlNodePtr root, cmds, bi, ri, mods, feat; /* fill out capabilities */ /* Create an XML Document */ xmldoc = xmlNewDoc((xmlChar *)"1.0"); root = xmlNewDocNode(xmldoc, NULL, (xmlChar *)capabilities_namespace, NULL); xmlDocSetRootElement(xmldoc, root); /* Fill in the document */ mtev_build_version(vbuff, sizeof(vbuff)); xmlNewTextChild(root, NULL, (xmlChar *)"version", (xmlChar *)vbuff); snprintf(bwstr, sizeof(bwstr), "%d", (int)sizeof(void *)*8); /* Build info */ bi = xmlNewNode(NULL, (xmlChar *)"unameBuild"); xmlSetProp(bi, (xmlChar *)"bitwidth", (xmlChar *)bwstr); xmlAddChild(root, bi); xmlNewTextChild(bi, NULL, (xmlChar *)"sysname", (xmlChar *)UNAME_S); xmlNewTextChild(bi, NULL, (xmlChar *)"nodename", (xmlChar *)UNAME_N); xmlNewTextChild(bi, NULL, (xmlChar *)"release", (xmlChar *)UNAME_R); xmlNewTextChild(bi, NULL, (xmlChar *)"version", (xmlChar *)UNAME_V); xmlNewTextChild(bi, NULL, (xmlChar *)"machine", (xmlChar *)UNAME_M); /* Run info */ ri = xmlNewNode(NULL, (xmlChar *)"unameRun"); xmlSetProp(ri, (xmlChar *)"bitwidth", (xmlChar *)bwstr); xmlAddChild(root, ri); if(uname(&utsn) < 0) { xmlNewTextChild(ri, NULL, (xmlChar *)"error", (xmlChar *)strerror(errno)); } else { xmlNewTextChild(ri, NULL, (xmlChar *)"sysname", (xmlChar *)utsn.sysname); xmlNewTextChild(ri, NULL, (xmlChar *)"nodename", (xmlChar *)utsn.nodename); xmlNewTextChild(ri, NULL, (xmlChar *)"release", (xmlChar *)utsn.release); xmlNewTextChild(ri, NULL, (xmlChar *)"version", (xmlChar *)utsn.version); xmlNewTextChild(ri, NULL, (xmlChar *)"machine", (xmlChar *)utsn.machine); } /* features */ feat = xmlNewNode(NULL, (xmlChar *)"features"); xmlAddChild(root, feat); if(mtev_hash_size(&features)) { mtev_hash_iter iter2 = MTEV_HASH_ITER_ZERO; void *vfv; const char *f; int flen; while(mtev_hash_next(&features, &iter2, &f, &flen, &vfv)) { xmlNodePtr featnode; featnode = xmlNewNode(NULL, (xmlChar *)"feature"); xmlSetProp(featnode, (xmlChar *)"name", (xmlChar *)f); if(vfv) xmlSetProp(featnode, (xmlChar *)"version", (xmlChar *)vfv); xmlAddChild(feat, featnode); } } /* time (poor man's time check) */ gettimeofday(&now, NULL); snprintf(vbuff, sizeof(vbuff), "%llu.%03d", (unsigned long long)now.tv_sec, (int)(now.tv_usec / 1000)); xmlNewTextChild(root, NULL, (xmlChar *)"current_time", (xmlChar *)vbuff); cmds = xmlNewNode(NULL, (xmlChar *)"services"); xmlAddChild(root, cmds); lc = mtev_listener_commands(); while(mtev_hash_next(lc, &iter, &k, &klen, &data)) { xmlNodePtr cnode; char hexcode[11]; const char *name; eventer_func_t *f = (eventer_func_t *)k; mtev_hash_table *sc = (mtev_hash_table *)data; mtev_hash_iter sc_iter = MTEV_HASH_ITER_ZERO; const char *sc_k; int sc_klen; void *sc_data; name = eventer_name_for_callback(*f); cnode = xmlNewNode(NULL, (xmlChar *)"service"); xmlSetProp(cnode, (xmlChar *)"name", name ? (xmlChar *)name : NULL); if(*f == curr) xmlSetProp(cnode, (xmlChar *)"connected", (xmlChar *)"true"); xmlAddChild(cmds, cnode); while(mtev_hash_next(sc, &sc_iter, &sc_k, &sc_klen, &sc_data)) { xmlNodePtr scnode; char *name_copy, *version = NULL; eventer_func_t *f = (eventer_func_t *)sc_data; snprintf(hexcode, sizeof(hexcode), "0x%08x", *((u_int32_t *)sc_k)); name = eventer_name_for_callback(*f); name_copy = strdup(name ? name : "[[unknown]]"); version = strchr(name_copy, '/'); if(version) *version++ = '\0'; scnode = xmlNewNode(NULL, (xmlChar *)"command"); xmlSetProp(scnode, (xmlChar *)"name", (xmlChar *)name_copy); if(version) xmlSetProp(scnode, (xmlChar *)"version", (xmlChar *)version); xmlSetProp(scnode, (xmlChar *)"code", (xmlChar *)hexcode); xmlAddChild(cnode, scnode); free(name_copy); } } mods = xmlNewNode(NULL, (xmlChar *)"modules"); xmlAddChild(root, mods); #define list_modules(func, name) do { \ nmods = func(&mod_names); \ for(i=0; i<nmods; i++) { \ xmlNodePtr pnode; \ pnode = xmlNewNode(NULL, (xmlChar *)"module"); \ xmlSetProp(pnode, (xmlChar *)"type", (xmlChar *)name); \ xmlSetProp(pnode, (xmlChar *)"name", (xmlChar *)mod_names[i]); \ xmlAddChild(mods, pnode); \ } \ if(mod_names) free(mod_names); \ } while(0) for(t = mtev_dso_get_types(); t; t = t->next) list_modules(t->list, t->name); /* Write it out to a buffer and copy it for writing */ cl->buff = mtev_xmlSaveToBuffer(xmldoc); cl->towrite = strlen(cl->buff); /* Clean up after ourselves */ xmlFreeDoc(xmldoc); }
static void mtev_capabilities_tobuff_json(mtev_capsvc_closure_t *cl, eventer_func_t curr) { const char **mod_names; struct utsname utsn; char vbuff[128]; mtev_hash_table *lc; mtev_hash_iter iter = MTEV_HASH_ITER_ZERO; const char *k; int klen, i, nmods; void *data; struct timeval now; struct dso_type *t; struct json_object *doc; struct json_object *svcs, *bi, *ri, *mods, *feat; /* fill out capabilities */ /* Create an XML Document */ doc = json_object_new_object(); /* Fill in the document */ mtev_build_version(vbuff, sizeof(vbuff)); json_object_object_add(doc, "version", json_object_new_string(vbuff)); /* Build info */ bi = json_object_new_object(); json_object_object_add(bi, "bitwidth", json_object_new_int(sizeof(void *)*8)); json_object_object_add(bi, "sysname", json_object_new_string(UNAME_S)); json_object_object_add(bi, "nodename", json_object_new_string(UNAME_N)); json_object_object_add(bi, "release", json_object_new_string(UNAME_R)); json_object_object_add(bi, "version", json_object_new_string(UNAME_V)); json_object_object_add(bi, "machine", json_object_new_string(UNAME_M)); json_object_object_add(doc, "unameBuild", bi); /* Run info */ ri = json_object_new_object(); json_object_object_add(ri, "bitwidth", json_object_new_int(sizeof(void *)*8)); if(uname(&utsn) < 0) { json_object_object_add(ri, "error", json_object_new_string(strerror(errno))); } else { json_object_object_add(ri, "sysname", json_object_new_string(utsn.sysname)); json_object_object_add(ri, "nodename", json_object_new_string(utsn.nodename)); json_object_object_add(ri, "release", json_object_new_string(utsn.release)); json_object_object_add(ri, "version", json_object_new_string(utsn.version)); json_object_object_add(ri, "machine", json_object_new_string(utsn.machine)); } json_object_object_add(doc, "unameRun", ri); /* features */ feat = json_object_new_object(); if(mtev_hash_size(&features)) { mtev_hash_iter iter2 = MTEV_HASH_ITER_ZERO; void *vfv; const char *f; int flen; while(mtev_hash_next(&features, &iter2, &f, &flen, &vfv)) { struct json_object *featnode; featnode = json_object_new_object(); if(vfv) json_object_object_add(featnode, "version", json_object_new_string(vfv)); json_object_object_add(feat, f, featnode); } } json_object_object_add(doc, "features", feat); /* time (poor man's time check) */ gettimeofday(&now, NULL); snprintf(vbuff, sizeof(vbuff), "%llu%03d", (unsigned long long)now.tv_sec, (int)(now.tv_usec / 1000)); json_object_object_add(doc, "current_time", json_object_new_string(vbuff)); svcs = json_object_new_object(); lc = mtev_listener_commands(); while(mtev_hash_next(lc, &iter, &k, &klen, &data)) { struct json_object *cnode, *cmds; char hexcode[11]; const char *name; eventer_func_t *f = (eventer_func_t *)k; mtev_hash_table *sc = (mtev_hash_table *)data; mtev_hash_iter sc_iter = MTEV_HASH_ITER_ZERO; const char *sc_k; int sc_klen; void *sc_data; name = eventer_name_for_callback(*f); cnode = json_object_new_object(); if(klen == 8) snprintf(hexcode, sizeof(hexcode), "0x%0llx", (unsigned long long int)(vpsized_uint)**f); else snprintf(hexcode, sizeof(hexcode), "0x%0x", (unsigned int)(vpsized_uint)**f); json_object_object_add(svcs, hexcode, cnode); if(name) json_object_object_add(cnode, name, json_object_new_string(name)); cmds = json_object_new_object(); json_object_object_add(cnode, "commands", cmds); while(mtev_hash_next(sc, &sc_iter, &sc_k, &sc_klen, &sc_data)) { struct json_object *scnode; char *name_copy, *version = NULL; eventer_func_t *f = (eventer_func_t *)sc_data; scnode = json_object_new_object(); snprintf(hexcode, sizeof(hexcode), "0x%08x", *((u_int32_t *)sc_k)); name = eventer_name_for_callback(*f); name_copy = strdup(name ? name : "[[unknown]]"); version = strchr(name_copy, '/'); if(version) *version++ = '\0'; json_object_object_add(scnode, "name", json_object_new_string(name_copy)); if(version) json_object_object_add(scnode, "version", json_object_new_string(version)); json_object_object_add(cmds, hexcode, scnode); free(name_copy); } } json_object_object_add(doc, "services", svcs); mods = json_object_new_object(); #define list_modules_json(func, name) do { \ nmods = func(&mod_names); \ for(i=0; i<nmods; i++) { \ struct json_object *pnode; \ pnode = json_object_new_object(); \ json_object_object_add(pnode, "type", json_object_new_string(name)); \ json_object_object_add(mods, mod_names[i], pnode); \ } \ if(mod_names) free(mod_names); \ } while(0) for(t = mtev_dso_get_types(); t; t = t->next) list_modules_json(t->list, t->name); json_object_object_add(doc, "modules", mods); /* Write it out to a buffer and copy it for writing */ cl->buff = strdup(json_object_to_json_string(doc)); cl->towrite = strlen(cl->buff); /* Clean up after ourselves */ json_object_put(doc); }
static void noit_capabilities_tobuff(noit_capsvc_closure_t *cl, eventer_func_t curr) { char vbuff[128]; noit_hash_table *lc; noit_hash_iter iter = NOIT_HASH_ITER_ZERO; const char *k; int klen; void *data; struct timeval now; xmlDocPtr xmldoc; xmlNodePtr root, cmds; /* fill out capabilities */ /* Create an XML Document */ xmldoc = xmlNewDoc((xmlChar *)"1.0"); root = xmlNewDocNode(xmldoc, NULL, (xmlChar *)"noit_capabilities", NULL); xmlDocSetRootElement(xmldoc, root); /* Fill in the document */ noit_build_version(vbuff, sizeof(vbuff)); xmlNewTextChild(root, NULL, (xmlChar *)"version", (xmlChar *)vbuff); /* time (poor man's time check) */ gettimeofday(&now, NULL); snprintf(vbuff, sizeof(vbuff), "%llu.%03d", (unsigned long long)now.tv_sec, (int)(now.tv_usec / 1000)); xmlNewTextChild(root, NULL, (xmlChar *)"current_time", (xmlChar *)vbuff); cmds = xmlNewNode(NULL, (xmlChar *)"services"); xmlAddChild(root, cmds); lc = noit_listener_commands(); while(noit_hash_next(lc, &iter, &k, &klen, &data)) { xmlNodePtr cnode; char hexcode[11]; const char *name; eventer_func_t *f = (eventer_func_t *)k; noit_hash_table *sc = (noit_hash_table *)data; noit_hash_iter sc_iter = NOIT_HASH_ITER_ZERO; const char *sc_k; int sc_klen; void *sc_data; name = eventer_name_for_callback(*f); cnode = xmlNewNode(NULL, (xmlChar *)"service"); xmlSetProp(cnode, (xmlChar *)"name", name ? (xmlChar *)name : NULL); if(*f == curr) xmlSetProp(cnode, (xmlChar *)"connected", (xmlChar *)"true"); xmlAddChild(cmds, cnode); while(noit_hash_next(sc, &sc_iter, &sc_k, &sc_klen, &sc_data)) { xmlNodePtr scnode; char *name_copy, *version = NULL; eventer_func_t *f = (eventer_func_t *)sc_data; snprintf(hexcode, sizeof(hexcode), "0x%08x", *((u_int32_t *)sc_k)); name = eventer_name_for_callback(*f); name_copy = strdup(name ? name : "[[unknown]]"); version = strchr(name_copy, '/'); if(version) *version++ = '\0'; scnode = xmlNewNode(NULL, (xmlChar *)"command"); xmlSetProp(scnode, (xmlChar *)"name", (xmlChar *)name_copy); if(version) xmlSetProp(scnode, (xmlChar *)"version", (xmlChar *)version); xmlSetProp(scnode, (xmlChar *)"code", (xmlChar *)hexcode); xmlAddChild(cnode, scnode); free(name_copy); } } /* Write it out to a buffer and copy it for writing */ cl->buff = noit_xmlSaveToBuffer(xmldoc); cl->towrite = strlen(cl->buff); /* Clean up after ourselves */ xmlFreeDoc(xmldoc); }
int noit_capabilities_handler(eventer_t e, int mask, void *closure, struct timeval *now) { int newmask = EVENTER_WRITE | EVENTER_EXCEPTION; acceptor_closure_t *ac = closure; noit_capsvc_closure_t *cl = ac->service_ctx; if(mask & EVENTER_EXCEPTION) { socket_error: /* Exceptions cause us to simply snip the connection */ cleanup_shutdown: eventer_remove_fd(e->fd); e->opset->close(e->fd, &newmask, e); if(cl) { if(cl->buff) free(cl->buff); free(cl); } if(ac) acceptor_closure_free(ac); return 0; } if(!ac->service_ctx) { char vbuff[128]; noit_hash_table *lc; noit_hash_iter iter = NOIT_HASH_ITER_ZERO; const char *k; int klen; void *data; xmlDocPtr xmldoc; xmlNodePtr root, cmds; cl = ac->service_ctx = calloc(1, sizeof(*cl)); /* fill out capabilities */ noit_build_version(vbuff, sizeof(vbuff)); /* Create an XML Document */ xmldoc = xmlNewDoc((xmlChar *)"1.0"); root = xmlNewDocNode(xmldoc, NULL, (xmlChar *)"noit_capabilities", NULL); xmlDocSetRootElement(xmldoc, root); /* Fill in the document */ xmlNewTextChild(root, NULL, (xmlChar *)"version", (xmlChar *)vbuff); cmds = xmlNewNode(NULL, (xmlChar *)"services"); xmlAddChild(root, cmds); lc = noit_listener_commands(); while(noit_hash_next(lc, &iter, &k, &klen, &data)) { xmlNodePtr cnode; char hexcode[11]; const char *name; eventer_func_t *f = (eventer_func_t *)k; noit_hash_table *sc = (noit_hash_table *)data; noit_hash_iter sc_iter = NOIT_HASH_ITER_ZERO; const char *sc_k; int sc_klen; void *sc_data; name = eventer_name_for_callback(*f); cnode = xmlNewNode(NULL, (xmlChar *)"service"); xmlSetProp(cnode, (xmlChar *)"name", name ? (xmlChar *)name : NULL); if(*f == ac->dispatch) xmlSetProp(cnode, (xmlChar *)"connected", (xmlChar *)"true"); xmlAddChild(cmds, cnode); while(noit_hash_next(sc, &sc_iter, &sc_k, &sc_klen, &sc_data)) { xmlNodePtr scnode; char *name_copy, *version = NULL; eventer_func_t *f = (eventer_func_t *)sc_data; snprintf(hexcode, sizeof(hexcode), "0x%08x", *((u_int32_t *)sc_k)); name = eventer_name_for_callback(*f); name_copy = strdup(name ? name : "[[unknown]]"); version = strchr(name_copy, '/'); if(version) *version++ = '\0'; scnode = xmlNewNode(NULL, (xmlChar *)"command"); xmlSetProp(scnode, (xmlChar *)"name", (xmlChar *)name_copy); if(version) xmlSetProp(scnode, (xmlChar *)"version", (xmlChar *)version); xmlSetProp(scnode, (xmlChar *)"code", (xmlChar *)hexcode); xmlAddChild(cnode, scnode); free(name_copy); } } /* Write it out to a buffer and copy it for writing */ cl->buff = noit_xmlSaveToBuffer(xmldoc); cl->towrite = strlen(cl->buff); /* Clean up after ourselves */ xmlFreeDoc(xmldoc); } while(cl->towrite > cl->written) { int len; while((len = e->opset->write(e->fd, cl->buff + cl->written, cl->towrite - cl->written, &newmask, e)) == -1 && errno == EINTR); if(len < 0) { if(errno == EAGAIN) return newmask | EVENTER_EXCEPTION; goto socket_error; } cl->written += len; } goto cleanup_shutdown; }
int noit_listener(char *host, unsigned short port, int type, int backlog, noit_hash_table *sslconfig, noit_hash_table *config, eventer_func_t handler, void *service_ctx) { int rv, fd; int8_t family; int sockaddr_len; socklen_t reuse; listener_closure_t listener_closure; eventer_t event; union { struct in_addr addr4; struct in6_addr addr6; } a; union { struct sockaddr_in addr4; struct sockaddr_in6 addr6; struct sockaddr_un addru; } s; const char *event_name; noitL(nldeb, "noit_listener(%s, %d, %d, %d, %s, %p)\n", host, port, type, backlog, (event_name = eventer_name_for_callback(handler))?event_name:"??", service_ctx); if(host[0] == '/') { family = AF_UNIX; } else { family = AF_INET; rv = inet_pton(family, host, &a); if(rv != 1) { family = AF_INET6; rv = inet_pton(family, host, &a); if(rv != 1) { if(!strcmp(host, "*")) { family = AF_INET; a.addr4.s_addr = INADDR_ANY; } else { noitL(noit_error, "Cannot translate '%s' to IP\n", host); return -1; } } } } fd = socket(family, NE_SOCK_CLOEXEC|type, 0); if(fd < 0) { noitL(noit_error, "Cannot create socket: %s\n", strerror(errno)); return -1; } if(eventer_set_fd_nonblocking(fd)) { close(fd); noitL(noit_error, "Cannot make socket non-blocking: %s\n", strerror(errno)); return -1; } reuse = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void*)&reuse, sizeof(reuse)) != 0) { close(fd); noitL(noit_error, "Cannot set SO_REUSEADDR: %s\n", strerror(errno)); return -1; } memset(&s, 0, sizeof(s)); if(family == AF_UNIX) { struct stat sb; /* unlink the path if it is a socket */ /* coverity[fs_check_call] */ if(stat(host, &sb) == -1) { if(errno != ENOENT) { noitL(noit_error, "%s: %s\n", host, strerror(errno)); close(fd); return -1; } } else { if(sb.st_mode & S_IFSOCK) { /* coverity[toctou] */ unlink(host); } else { noitL(noit_error, "unlink %s failed: %s\n", host, strerror(errno)); close(fd); return -1; } } s.addru.sun_family = AF_UNIX; strncpy(s.addru.sun_path, host, sizeof(s.addru.sun_path)-1); sockaddr_len = sizeof(s.addru); } else { if(family == AF_INET6) { s.addr6.sin6_family = family; s.addr6.sin6_port = htons(port); memcpy(&s.addr6.sin6_addr, &a.addr6, sizeof(a.addr6)); } else { s.addr4.sin_family = family; s.addr4.sin_port = htons(port); memcpy(&s.addr4.sin_addr, &a.addr4, sizeof(a.addr4)); } sockaddr_len = (family == AF_INET) ? sizeof(s.addr4) : sizeof(s.addr6); } if(bind(fd, (struct sockaddr *)&s, sockaddr_len) < 0) { noitL(noit_error, "bind failed[%s]: %s\n", host, strerror(errno)); close(fd); return -1; } if(type == SOCK_STREAM) { if(listen(fd, backlog) < 0) { close(fd); return -1; } } listener_closure = calloc(1, sizeof(*listener_closure)); listener_closure->family = family; listener_closure->port = htons(port); listener_closure->sslconfig = calloc(1, sizeof(noit_hash_table)); noit_hash_merge_as_dict(listener_closure->sslconfig, sslconfig); listener_closure->dispatch_callback = handler; listener_closure->dispatch_closure = calloc(1, sizeof(*listener_closure->dispatch_closure)); listener_closure->dispatch_closure->config = config; listener_closure->dispatch_closure->dispatch = handler; listener_closure->dispatch_closure->service_ctx = service_ctx; event = eventer_alloc(); event->fd = fd; event->mask = EVENTER_READ | EVENTER_EXCEPTION; event->callback = noit_listener_acceptor; event->closure = listener_closure; eventer_add(event); return 0; }
static int noit_listener_acceptor(eventer_t e, int mask, void *closure, struct timeval *tv) { int conn, newmask = EVENTER_READ; socklen_t salen; listener_closure_t listener_closure = (listener_closure_t)closure; acceptor_closure_t *ac = NULL; if(mask & EVENTER_EXCEPTION) { socketfail: if(ac) acceptor_closure_free(ac); /* We don't shut down the socket, it's out listener! */ return EVENTER_READ | EVENTER_WRITE | EVENTER_EXCEPTION; } do { ac = malloc(sizeof(*ac)); memcpy(ac, listener_closure->dispatch_closure, sizeof(*ac)); salen = sizeof(ac->remote); conn = e->opset->accept(e->fd, &ac->remote.remote_addr, &salen, &newmask, e); if(conn >= 0) { eventer_t newe; noitL(nldeb, "noit_listener[%s] accepted fd %d\n", eventer_name_for_callback(listener_closure->dispatch_callback), conn); if(eventer_set_fd_nonblocking(conn)) { close(conn); free(ac); goto accept_bail; } newe = eventer_alloc(); newe->fd = conn; newe->mask = EVENTER_READ | EVENTER_WRITE | EVENTER_EXCEPTION; if(listener_closure->sslconfig->size) { const char *layer, *cert, *key, *ca, *ciphers, *crl; eventer_ssl_ctx_t *ctx; /* We have an SSL configuration. While our socket accept is * complete, we now have to SSL_accept, which could require * several reads and writes and needs its own event callback. */ #define SSLCONFGET(var,name) do { \ if(!noit_hash_retr_str(listener_closure->sslconfig, name, strlen(name), \ &var)) var = NULL; } while(0) SSLCONFGET(layer, "layer"); SSLCONFGET(cert, "certificate_file"); SSLCONFGET(key, "key_file"); SSLCONFGET(ca, "ca_chain"); SSLCONFGET(ciphers, "ciphers"); ctx = eventer_ssl_ctx_new(SSL_SERVER, layer, cert, key, ca, ciphers); if(!ctx) { newe->opset->close(newe->fd, &newmask, e); eventer_free(newe); goto socketfail; } SSLCONFGET(crl, "crl"); if(crl) { if(!eventer_ssl_use_crl(ctx, crl)) { noitL(noit_error, "Failed to load CRL from %s\n", crl); eventer_ssl_ctx_free(ctx); newe->opset->close(newe->fd, &newmask, e); eventer_free(newe); goto socketfail; } } eventer_ssl_ctx_set_verify(ctx, eventer_ssl_verify_cert, listener_closure->sslconfig); EVENTER_ATTACH_SSL(newe, ctx); newe->callback = noit_listener_accept_ssl; newe->closure = malloc(sizeof(*listener_closure)); memcpy(newe->closure, listener_closure, sizeof(*listener_closure)); ((listener_closure_t)newe->closure)->dispatch_closure = ac; } else { newe->callback = listener_closure->dispatch_callback; /* We must make a copy of the acceptor_closure_t for each new * connection. */ newe->closure = ac; } eventer_add(newe); } else { if(errno == EAGAIN) { if(ac) acceptor_closure_free(ac); } else if(errno != EINTR) { noitL(noit_error, "accept socket error: %s\n", strerror(errno)); goto socketfail; } } } while(conn >= 0); accept_bail: return newmask | EVENTER_EXCEPTION; }