/* FIXME */ void sall_command(client c) { dstr res = get_indices(); dstr *fields = NULL; int nfield = 0, i; RTRIM(res); fields = dstr_split_len(res, dstr_length(res), ",", 1, &nfield); for (i = 1; i < nfield; ++i) { dstr pkey = dstr_new(fields[i]); dstr skey = dstr_new(pkey); dlist_t dlist; struct kvd *kvd; table_rwlock_wrlock(subscribers); if ((dlist = table_get_value(subscribers, pkey)) == NULL) { if (NEW(kvd)) { kvd->key = skey; kvd->u.dlist = dlist_new(NULL, NULL); dlist_insert_tail(kvd->u.dlist, c); dlist = dlist_new(cmpkvd, kdfree); dlist_insert_sort(dlist, kvd); table_insert(subscribers, pkey, dlist); } else { add_reply_error(c, "error allocating memory for kvd\r\n"); dstr_free(skey); dstr_free(pkey); } } else { if (NEW(kvd)) { dlist_node_t node; kvd->key = skey; kvd->u.dlist = dlist_new(NULL, NULL); if ((node = dlist_find(dlist, kvd)) == NULL) { dlist_insert_tail(kvd->u.dlist, c); dlist_insert_sort(dlist, kvd); } else { kdfree(kvd); kvd = (struct kvd *)dlist_node_value(node); if (dlist_find(kvd->u.dlist, c) == NULL) dlist_insert_tail(kvd->u.dlist, c); } } else { add_reply_error(c, "error allocating memory for kvd\r\n"); dstr_free(skey); } dstr_free(pkey); } table_rwlock_unlock(subscribers); } dstr_free(res); //-------------------------------------------------------------------------------------------------------------------- // FIXME //-------------------------------------------------------------------------------------------------------------------- }
void radix_block_insert(char *key, const char *sep, struct node *p) { dstr *fields = NULL; int count = 0, i = 0; fields = dstr_split_len(key, strlen(key), sep, 1, &count); for (i = 0; i < count; i++) { int j = 0, flag = 0; for (j = 0; j < p->child_num; j++) { if (!strcmp(p->child[j]->key, fields[i])) { flag = 1; if (i == count - 1) { p->child[j]->is_real = 1; } p = p->child[j]; break; } } if (0 == flag) { int k; struct node *q = (struct node*)malloc(sizeof(struct node)); q = p; for (k = i; k < count; k++) { struct node *n = (struct node*)malloc(sizeof(struct node)); strcpy(n->key,fields[k]); n->parent = q; if (k == count - 1) { n->is_real = 1; } q->child = realloc(q->child, sizeof(struct node)); q->child[q->child_num] = n; q->child_num++; q = n; } break; } } }
static void on_user_login(struct DFITCUserLoginInfoRtnField *userlogin, struct DFITCErrorRtnField *rspinfo) { if (rspinfo && rspinfo->nErrorID != 0) xcb_log(XCB_LOG_ERROR, "Error occurred: errorid=%d", rspinfo->nErrorID); else if (contracts) { dstr *fields = NULL; int nfield = 0, i; fields = dstr_split_len(contracts, strlen(contracts), ",", 1, &nfield); for (i = 0; i < nfield; ++i) if (xspeed_mdapi_subscribe_market_data(mdapi, &fields[i], 1, 0) == 0) xcb_log(XCB_LOG_INFO, "Subscribe contract '%s' succeeded", fields[i]); else xcb_log(XCB_LOG_INFO, "Subscribe contract '%s' failed", fields[i]); dstr_free_tokens(fields, nfield); } }
/* FIXME */ void uall_command(client c) { dstr res = get_indices(); dstr *fields = NULL; int nfield = 0, i; RTRIM(res); fields = dstr_split_len(res, dstr_length(res), ",", 1, &nfield); for (i = 1; i < nfield; ++i) { dstr pkey = dstr_new(fields[i]); dstr skey = dstr_new(pkey); dlist_t dlist; table_rwlock_wrlock(subscribers); if ((dlist = table_get_value(subscribers, pkey))) { struct kvd *kvd; if (NEW(kvd)) { dlist_node_t node, node2; kvd->key = skey; if ((node = dlist_find(dlist, kvd))) { FREE(kvd); kvd = (struct kvd *)dlist_node_value(node); if ((node2 = dlist_find(kvd->u.dlist, c))) dlist_remove(kvd->u.dlist, node2); if (dlist_length(kvd->u.dlist) == 0) { dlist_remove(dlist, node); kdfree(kvd); } if (dlist_length(dlist) == 0) { table_remove(subscribers, pkey); dlist_free(&dlist); } } else FREE(kvd); } else add_reply_error(c, "error allocating memory for kvd"); } table_rwlock_unlock(subscribers); dstr_free(skey); dstr_free(pkey); } add_reply_string(c, "\r\n", 2); dstr_free(res); }
static void on_user_login(struct CUstpFtdcRspUserLoginField *userlogin, struct CUstpFtdcRspInfoField *rspinfo, int rid, int islast) { /* FIXME */ if (rspinfo && rspinfo->ErrorID != 0) { xcb_log(XCB_LOG_ERROR, "Error occurred: errorid=%d", rspinfo->ErrorID); } else if (islast && contracts) { dstr *fields = NULL; int nfield = 0, i; fields = dstr_split_len(contracts, strlen(contracts), ",", 1, &nfield); for (i = 0; i < nfield; ++i) if (femas_mdapi_subscribe_market_data(mdapi, &fields[i], 1) == 0) xcb_log(XCB_LOG_INFO, "Subscribe contract '%s' succeeded", fields[i]); else xcb_log(XCB_LOG_INFO, "Subscribe contract '%s' failed", fields[i]); dstr_free_tokens(fields, nfield); } }
static void on_user_login(struct ErrorRtnField *rspinfo) { /* FIXME */ if (rspinfo && rspinfo->ErrorID != 0) { xcb_log(XCB_LOG_ERROR, "Error occurred: errorid=%d", rspinfo->ErrorID); return; } if (contracts) { dstr *fields = NULL; int nfield = 0, i; fields = dstr_split_len(contracts, strlen(contracts), ",", 1, &nfield); for (i = 0; i < nfield; ++i) if ((xspeed_l2api_subscribe_market_data(l2api, &fields[i], 1)) == 0) xcb_log(XCB_LOG_INFO, "Subscribe contract '%s' succeeded", fields[i]); else xcb_log(XCB_LOG_INFO, "Subscribe contract '%s' failed", fields[i]); dstr_free_tokens(fields, nfield); } else { if ((xspeed_l2api_subscribe_all(l2api)) == 0) xcb_log(XCB_LOG_INFO, "Subscribe all succeeded"); else xcb_log(XCB_LOG_INFO, "Subscribe all failed"); } }
static void *read_thread(void *data) { xcb_context_t c = (xcb_context_t)data; struct pollfd rfd[1]; rfd[0].fd = c->fd; rfd[0].events = POLLIN; for (;;) { int nread; if (poll(rfd, 1, -1) == -1) { xcb_set_error_from_errno(c, XCB_ERR_IO, "poll(2)"); break; } if ((nread = read(c->fd, c->inbuf + c->inpos, sizeof c->inbuf - c->inpos)) == -1) { if (errno == EAGAIN || errno == EINTR) nread = 0; else { xcb_set_error(c, XCB_ERR_IO, NULL); break; } } else if (nread == 0) { xcb_set_error(c, XCB_ERR_EOF, "Server closed the connection"); break; } if (nread) c->inpos += nread; else continue; if (c->inpos >= sizeof c->inbuf) { xcb_set_error(c, XCB_ERR_IO, "Client reached max input buffer length"); break; } c->inbuf[c->inpos] = '\0'; while (c->inpos > 0) { char *newline; size_t len; dstr *fields = NULL; int nfield = 0; if ((newline = strstr(c->inbuf, "\r\n")) == NULL) { if (c->inpos == sizeof c->inbuf - 1) { xcb_set_error(c, XCB_ERR_PROTOCOL, "Too big reply"); goto end; } break; } len = newline - c->inbuf; fields = dstr_split_len(c->inbuf, len, ",", 1, &nfield); if (nfield > 1) { /* FIXME */ if (!strcasecmp(fields[1], "indices")) { if (c->xcb_callback_indices) c->xcb_callback_indices(c->inbuf); } else if (!strcasecmp(fields[1], "timestamp")) { if (c->xcb_callback_index) c->xcb_callback_index(c->inbuf); } else if (!strcmp(fields[1], "0") || !strcmp(fields[1], "1")) { if (c->xcb_callback_query) c->xcb_callback_query(c->inbuf); } else if (c->xcb_callback_subscribe) c->xcb_callback_subscribe(c->inbuf); } dstr_free_tokens(fields, nfield); memmove(c->inbuf, c->inbuf + len + 2, sizeof c->inbuf - len - 2); c->inpos -= len + 2; } } end: rthread = (pthread_t)-1; return NULL; }
static int mm_impvbaw_exec(void *data, void *data2) { RAII_VAR(struct msg *, msg, (struct msg *)data, msg_decr); dstr *fields = NULL; int nfield = 0; time_t t; dstr spotname; double vol; NOT_USED(data2); fields = dstr_split_len(msg->data, strlen(msg->data), ",", 1, &nfield); /* FIXME */ if (nfield != 19) { xcb_log(XCB_LOG_WARNING, "Message '%s' garbled", msg->data); goto end; } t = (time_t)atoi(fields[1]); spotname = dstr_new(fields[11]); vol = atof(fields[5]); if (!isnan(vol)) { struct mm *mm; int flag = 0; table_lock(contracts); if ((mm = table_get_value(contracts, spotname)) == NULL) { if (NEW(mm)) { mm->min = mm->max = vol; flag = 1; table_insert(contracts, spotname, mm); } else xcb_log(XCB_LOG_WARNING, "Error allocating memory for mm"); } else { if (mm->min > vol) { mm->min = vol; flag = 1; } else if (mm->max < vol) { mm->max = vol; flag = 1; } dstr_free(spotname); } if (mm && flag) { struct tm lt; char datestr[64], res[512]; localtime_r(&t, <); lt.tm_hour = lt.tm_min = lt.tm_sec = 0; strftime(datestr, sizeof datestr, "%F %T", <); snprintf(res, sizeof res, "MM_IMPVBAW,%s.000,%s|%f,%f", datestr, fields[11], mm->min, mm->max); out2rmp(res); } table_unlock(contracts); } end: dstr_free_tokens(fields, nfield); return 0; }
static int vxo_exec(void *data, void *data2) { RAII_VAR(struct msg *, msg, (struct msg *)data, msg_decr); struct msgs *out = (struct msgs *)data2; dstr *fields = NULL; int nfield = 0; double vol, vol2, vol3, spot, strike, r, expiry; int sec, msec; dstr spotname; char *type; struct pd *pd; struct scp *scp; fields = dstr_split_len(msg->data, strlen(msg->data), ",", 1, &nfield); /* FIXME */ if (nfield != 19) { xcb_log(XCB_LOG_WARNING, "Message '%s' garbled", msg->data); goto end; } vol = !strcasecmp(fields[5], "nan") ? NAN : atof(fields[5]); vol2 = !strcasecmp(fields[7], "nan") ? NAN : atof(fields[7]); vol3 = !strcasecmp(fields[9], "nan") ? NAN : atof(fields[9]); if (isnan(vol) && isnan(vol2) && isnan(vol3)) goto end; sec = atoi(fields[1]); msec = atoi(fields[2]); spot = atof(fields[10]); spotname = dstr_new(fields[11]); type = fields[12]; strike = atof(fields[13]); r = atof(fields[14]); expiry = atof(fields[15]); table_lock(spots); if ((pd = table_get_value(spots, spotname)) == NULL) { /* can't happen */ if (NEW(scp) == NULL) { xcb_log(XCB_LOG_WARNING, "Error allocating memory for scp"); table_unlock(spots); goto end; } scp->strike = strike; if (!strcasecmp(type, "C")) { scp->cvol = vol; scp->pvol = NAN; scp->cvol2 = vol2; scp->pvol2 = NAN; scp->cvol3 = vol3; scp->pvol3 = NAN; } else { scp->cvol = NAN; scp->pvol = vol; scp->cvol2 = NAN; scp->pvol2 = vol2; scp->cvol3 = NAN; scp->pvol3 = vol3; } if (NEW(pd) == NULL) { xcb_log(XCB_LOG_WARNING, "Error allocating memory for pd"); FREE(scp); table_unlock(spots); goto end; } pd->prevxo = pd->prevxo2 = pd->prevxo3 = NAN; pd->sep = strchr(fields[3], '-') ? "-" : ""; pd->dlist = dlist_new(NULL, scpfree); dlist_insert_tail(pd->dlist, scp); table_insert(spots, spotname, pd); } else { dlist_iter_t iter = dlist_iter_new(pd->dlist, DLIST_START_HEAD); dlist_node_t node; while ((node = dlist_next(iter))) { scp = (struct scp *)dlist_node_value(node); if (scp->strike > strike || fabs(scp->strike - strike) <= 0.000001) break; } if (node && fabs(scp->strike - strike) <= 0.000001) { if (!strcasecmp(type, "C")) { scp->cvol = vol; scp->cvol2 = vol2; scp->cvol3 = vol3; } else { scp->pvol = vol; scp->pvol2 = vol2; scp->pvol3 = vol3; } } else { /* can't happen */ if (NEW(scp) == NULL) { xcb_log(XCB_LOG_WARNING, "Error allocating memory for scp"); table_unlock(spots); goto end; } scp->strike = strike; if (!strcasecmp(type, "C")) { scp->cvol = vol; scp->pvol = NAN; scp->cvol2 = vol2; scp->pvol2 = NAN; scp->cvol3 = vol3; scp->pvol3 = NAN; } else { scp->cvol = NAN; scp->pvol = vol; scp->cvol2 = NAN; scp->pvol2 = vol2; scp->cvol3 = NAN; scp->pvol3 = vol3; } if (node == NULL) dlist_insert_tail(pd->dlist, scp); else dlist_insert(pd->dlist, node, scp, 0); } dlist_iter_rewind_head(iter, pd->dlist); while ((node = dlist_next(iter))) { scp = (struct scp *)dlist_node_value(node); if (scp->strike > spot || fabs(scp->strike - spot) <= 0.000001) break; } dlist_iter_free(&iter); if (node && dlist_node_prev(node)) { struct scp *scp1 = (struct scp *)dlist_node_value(node); struct scp *scp2 = (struct scp *)dlist_node_value(dlist_node_prev(node)); double vxo = NAN, vxo2 = NAN, vxo3 = NAN; int flag1, flag2, flag3; if (!isnan(scp1->cvol) && !isnan(scp1->pvol) && !isnan(scp2->cvol) && !isnan(scp2->pvol)) vxo = ((scp1->cvol + scp1->pvol) / 2) * (spot - scp2->strike) / (scp1->strike - scp2->strike) + ((scp2->cvol + scp2->pvol) / 2) * (scp1->strike - spot) / (scp1->strike - scp2->strike); if (!isnan(scp1->cvol2) && !isnan(scp1->pvol2) && !isnan(scp2->cvol2) && !isnan(scp2->pvol2)) vxo2 = ((scp1->cvol2 + scp1->pvol2) / 2) * (spot - scp2->strike) / (scp1->strike - scp2->strike) + ((scp2->cvol2 + scp2->pvol2) / 2) * (scp1->strike - spot) / (scp1->strike - scp2->strike); if (!isnan(scp1->cvol3) && !isnan(scp1->pvol3) && !isnan(scp2->cvol3) && !isnan(scp2->pvol3)) vxo3 = ((scp1->cvol3 + scp1->pvol3) / 2) * (spot - scp2->strike) / (scp1->strike - scp2->strike) + ((scp2->cvol3 + scp2->pvol3) / 2) * (scp1->strike - spot) / (scp1->strike - scp2->strike); if ((flag1 = (isnan(pd->prevxo) && !isnan(vxo)) || (!isnan(pd->prevxo) && isnan(vxo)) || (!isnan(pd->prevxo) && !isnan(vxo) && fabs(pd->prevxo - vxo) > 0.000001) ? 1 : 0)) pd->prevxo = vxo; if ((flag2 = (isnan(pd->prevxo2) && !isnan(vxo2)) || (!isnan(pd->prevxo2) && isnan(vxo2)) || (!isnan(pd->prevxo2) && !isnan(vxo2) && fabs(pd->prevxo2 - vxo2) > 0.000001) ? 1 : 0)) pd->prevxo2 = vxo2; if ((flag3 = (isnan(pd->prevxo3) && !isnan(vxo3)) || (!isnan(pd->prevxo3) && isnan(vxo3)) || (!isnan(pd->prevxo3) && !isnan(vxo3) && fabs(pd->prevxo3 - vxo3) > 0.000001) ? 1 : 0)) pd->prevxo3 = vxo3; if (flag1 || flag2 || flag3) { char *res; if ((res = ALLOC(4096))) { time_t t = (time_t)sec; struct tm lt; char datestr[64]; int off; strftime(datestr, sizeof datestr, "%F %T", localtime_r(&t, <)); snprintf(res, 4096, "VXO,%s.%03d,%s|%f,%f,%f", datestr, msec, spotname, vxo, vxo2, vxo3); out2rmp(res); off = snprintf(res, 4096, "VXO,%d,%d,%s,", sec, msec, spotname); iter = dlist_iter_new(pd->dlist, DLIST_START_HEAD); while ((node = dlist_next(iter))) { scp = (struct scp *)dlist_node_value(node); off += snprintf(res + off, 4096 - off, "%.f,%f,%f,%f,", scp->strike, vxo, vxo2, vxo3); } dlist_iter_free(&iter); snprintf(res + off, 4096 - off, "%.2f,%f,%f,%s", spot, r, expiry, pd->sep); if (out2msgs(res, out) == -1) FREE(res); } } } dstr_free(spotname); } table_unlock(spots); end: dstr_free_tokens(fields, nfield); return 0; }
static int greeks2_exec(void *data, void *data2) { RAII_VAR(struct msg *, msg, (struct msg *)data, msg_decr); dstr *fields = NULL; int nfield = 0; time_t t; char *contract, *type; double spot, strike, r, vol, vol2, vol3, expiry; int steps; NOT_USED(data2); fields = dstr_split_len(msg->data, strlen(msg->data), ",", 1, &nfield); /* FIXME */ if (nfield != 19) { xcb_log(XCB_LOG_WARNING, "Message '%s' garbled", msg->data); goto end; } t = (time_t)atoi(fields[1]); contract = fields[3]; type = fields[12]; spot = atof(fields[10]); strike = atof(fields[13]); r = atof(fields[14]); vol = atof(fields[5]); vol2 = atof(fields[7]); vol3 = atof(fields[9]); expiry = atof(fields[15]); steps = atoi(fields[18]); if (!isnan(vol) || !isnan(vol2) || !isnan(vol3)) { double delta, gamma, theta, vega, rho; double delta2, gamma2, theta2, vega2, rho2; double delta3, gamma3, theta3, vega3, rho3; struct tm lt; char datestr[64], res[512]; if (isnan(vol)) delta = gamma = theta = vega = rho = NAN; else { if (!strcasecmp(type, "C")) bi_amer_call_greeks(spot, strike, r, r, vol, expiry, steps, &delta, &gamma, &theta, &vega, &rho); else bi_amer_put_greeks (spot, strike, r, r, vol, expiry, steps, &delta, &gamma, &theta, &vega, &rho); } if (isnan(vol2)) delta2 = gamma2 = theta2 = vega2 = rho2 = NAN; else if (fabs(vol2 - vol) <= 0.000001) { delta2 = delta; gamma2 = gamma; theta2 = theta; vega2 = vega; rho2 = rho; } else { if (!strcasecmp(type, "C")) bi_amer_call_greeks(spot, strike, r, r, vol2, expiry, steps, &delta2, &gamma2, &theta2, &vega2, &rho2); else bi_amer_put_greeks (spot, strike, r, r, vol2, expiry, steps, &delta2, &gamma2, &theta2, &vega2, &rho2); } if (isnan(vol3)) delta3 = gamma3 = theta3 = vega3 = rho3 = NAN; else if (fabs(vol3 - vol) <= 0.000001) { delta3 = delta; gamma3 = gamma; theta3 = theta; vega3 = vega; rho3 = rho; } else { if (!strcasecmp(type, "C")) bi_amer_call_greeks(spot, strike, r, r, vol3, expiry, steps, &delta3, &gamma3, &theta3, &vega3, &rho3); else bi_amer_put_greeks (spot, strike, r, r, vol3, expiry, steps, &delta3, &gamma3, &theta3, &vega3, &rho3); } strftime(datestr, sizeof datestr, "%F %T", localtime_r(&t, <)); snprintf(res, sizeof res, "GREEKS2,%s.%03d,%s|%.2f,%f,%f,%f,%f,%f,%.2f,%f,%f,%f,%f,%f,%.2f,%f,%f,%f,%f,%f", datestr, atoi(fields[2]), contract, atof(fields[4]), delta, gamma, theta, vega, rho, atof(fields[6]), delta2, gamma2, theta2, vega2, rho2, atof(fields[8]), delta3, gamma3, theta3, vega3, rho3); out2rmp(res); } end: dstr_free_tokens(fields, nfield); return 0; }
/* FIXME */ void s_command(client c) { dstr pkey, skey; int i; dlist_t dlist; struct kvd *kvd; if (dstr_length(c->argv[0]) == 1) { add_reply_error(c, "index can't be empty\r\n"); return; } pkey = dstr_new(c->argv[0] + 1); skey = dstr_new(pkey); if (c->argc > 1) for (i = 1; i < c->argc; ++i) { skey = dstr_cat(skey, ","); skey = dstr_cat(skey, c->argv[i]); } table_lock(cache); if ((dlist = table_get_value(cache, pkey))) { dlist_iter_t iter = dlist_iter_new(dlist, DLIST_START_HEAD); dlist_node_t node; /* FIXME: still has room to improve */ while ((node = dlist_next(iter))) { kvd = (struct kvd *)dlist_node_value(node); if (dstr_length(kvd->key) >= dstr_length(skey) && !memcmp(kvd->key, skey, dstr_length(skey))) { dstr *fields = NULL; int nfield = 0; dstr res, contracts = NULL; fields = dstr_split_len(kvd->key, dstr_length(kvd->key), ",", 1, &nfield); res = dstr_new(fields[0]); if (nfield > 1) { contracts = dstr_new(fields[1]); for (i = 2; i < nfield; ++i) { contracts = dstr_cat(contracts, ","); contracts = dstr_cat(contracts, fields[i]); } } dstr_free_tokens(fields, nfield); fields = NULL, nfield = 0; fields = dstr_split_len(kvd->u.value, dstr_length(kvd->u.value), ",", 1, &nfield); res = dstr_cat(res, ","); res = dstr_cat(res, fields[0]); if (contracts) { res = dstr_cat(res, ","); res = dstr_cat(res, contracts); } for (i = 1; i < nfield; ++i) { res = dstr_cat(res, ","); res = dstr_cat(res, fields[i]); } res = dstr_cat(res, "\r\n"); pthread_spin_lock(&c->lock); if (!(c->flags & CLIENT_CLOSE_ASAP)) { if (net_try_write(c->fd, res, dstr_length(res), 10, NET_NONBLOCK) == -1) { xcb_log(XCB_LOG_WARNING, "Writing to client '%p': %s", c, strerror(errno)); if (++c->eagcount >= 3) { client_free_async(c); pthread_spin_unlock(&c->lock); dstr_free(contracts); dstr_free(res); dstr_free_tokens(fields, nfield); table_unlock(cache); dstr_free(skey); dstr_free(pkey); return; } } else if (c->eagcount) c->eagcount = 0; } pthread_spin_unlock(&c->lock); dstr_free(contracts); dstr_free(res); dstr_free_tokens(fields, nfield); } } dlist_iter_free(&iter); } table_unlock(cache); table_rwlock_wrlock(subscribers); if ((dlist = table_get_value(subscribers, pkey)) == NULL) { if (NEW(kvd)) { kvd->key = skey; kvd->u.dlist = dlist_new(NULL, NULL); dlist_insert_tail(kvd->u.dlist, c); dlist = dlist_new(cmpkvd, kdfree); dlist_insert_sort(dlist, kvd); table_insert(subscribers, pkey, dlist); } else { add_reply_error(c, "error allocating memory for kvd\r\n"); dstr_free(skey); dstr_free(pkey); } } else { if (NEW(kvd)) { dlist_node_t node; kvd->key = skey; kvd->u.dlist = dlist_new(NULL, NULL); if ((node = dlist_find(dlist, kvd)) == NULL) { dlist_insert_tail(kvd->u.dlist, c); dlist_insert_sort(dlist, kvd); } else { kdfree(kvd); kvd = (struct kvd *)dlist_node_value(node); if (dlist_find(kvd->u.dlist, c) == NULL) dlist_insert_tail(kvd->u.dlist, c); } } else { add_reply_error(c, "error allocating memory for kvd\r\n"); dstr_free(skey); } dstr_free(pkey); } table_rwlock_unlock(subscribers); }