lcbjsp_PARSER* lcbjsp_create(int mode) { lcbjsp_PARSER *ctx; jsonsl_error_t err; ctx = calloc(1, sizeof(*ctx)); ctx->jsn = jsonsl_new(512); ctx->mode = mode; if (ctx->mode == LCBJSP_MODE_VIEWS) { ctx->jpr = jsonsl_jpr_new("/rows/^", &err); } else { ctx->jpr = jsonsl_jpr_new("/results/^", &err); } ctx->jsn_rdetails = jsonsl_new(32); lcb_string_init(&ctx->meta_buf); lcb_string_init(&ctx->current_buf); lcb_string_init(&ctx->last_hk); if (!ctx->jpr) { abort(); } if (!ctx->jsn_rdetails) { abort(); } jsonsl_jpr_match_state_init(ctx->jsn, &ctx->jpr, 1); assert(ctx->jsn_rdetails); lcbjsp_reset(ctx); assert(ctx->jsn_rdetails); return ctx; }
lcbvrow_PARSER* lcbvrow_create(void) { lcbvrow_PARSER *ctx; jsonsl_error_t err; ctx = calloc(1, sizeof(*ctx)); ctx->jsn = jsonsl_new(512); ctx->jpr = jsonsl_jpr_new("/rows/^", &err); ctx->jsn_rdetails = jsonsl_new(32); lcb_string_init(&ctx->meta_buf); lcb_string_init(&ctx->current_buf); lcb_string_init(&ctx->last_hk); if (!ctx->jpr) { abort(); } if (!ctx->jsn_rdetails) { abort(); } jsonsl_jpr_match_state_init(ctx->jsn, &ctx->jpr, 1); assert(ctx->jsn_rdetails); lcbvrow_reset(ctx); assert(ctx->jsn_rdetails); return ctx; }
clconfig_provider * lcb_clconfig_create_http(lcb_confmon *parent) { lcb_error_t status; struct lcb_io_use_st use; http_provider *http = calloc(1, sizeof(*http)); if (!http) { return NULL; } status = lcb_connection_init(&http->connection, parent->settings->io, parent->settings); if (status != LCB_SUCCESS) { free(http); return NULL; } if (! (http->nodes = hostlist_create())) { lcb_connection_cleanup(&http->connection); free(http); return NULL; } http->base.type = LCB_CLCONFIG_HTTP; http->base.refresh = get_refresh; http->base.pause = pause_http; http->base.get_cached = http_get_cached; http->base.shutdown = shutdown_http; http->base.nodes_updated = refresh_nodes; http->base.enabled = 0; http->io_timer = lcb_timer_create_simple(parent->settings->io, http, parent->settings->config_node_timeout, timeout_handler); lcb_timer_disarm(http->io_timer); http->disconn_timer = lcb_timer_create_simple(parent->settings->io, http, parent->settings->bc_http_stream_time, delayed_disconn); lcb_timer_disarm(http->disconn_timer); lcb_connuse_easy(&use, http, io_read_handler, io_error_handler); lcb_connection_use(&http->connection, &use); lcb_string_init(&http->stream.chunk); lcb_string_init(&http->stream.header); lcb_string_init(&http->stream.input); return &http->base; }
void lcb_cccp_update2(const void *cookie, lcb_error_t err, const void *bytes, lcb_size_t nbytes, const lcb_host_t *origin) { cccp_cookie *ck = (cccp_cookie *)cookie; cccp_provider *cccp = ck->parent; if (err == LCB_SUCCESS) { lcb_string ss; lcb_string_init(&ss); lcb_string_append(&ss, bytes, nbytes); err = lcb_cccp_update(&cccp->base, origin->host, &ss); lcb_string_release(&ss); if (err != LCB_SUCCESS && ck->ignore_errors == 0) { mcio_error(cccp, err); } } else if (!ck->ignore_errors) { mcio_error(cccp, err); } if (ck == cccp->cmdcookie) { cccp->cmdcookie = NULL; } free(ck); }
LIBCOUCHBASE_API lcb_error_t lcb_cbflush3(lcb_t instance, const void *cookie, const lcb_CMDBASE *cmd) { lcb_http_request_t htr; lcb_CMDHTTP htcmd = { 0 }; lcb_string urlpath; lcb_error_t rc; (void)cmd; lcb_string_init(&urlpath); lcb_string_appendz(&urlpath, "/pools/default/buckets/"); lcb_string_appendz(&urlpath, LCBT_SETTING(instance, bucket)); lcb_string_appendz(&urlpath, "/controller/doFlush"); htcmd.type = LCB_HTTP_TYPE_MANAGEMENT; htcmd.method = LCB_HTTP_METHOD_POST; htcmd.reqhandle = &htr; LCB_CMD_SET_KEY(&htcmd, urlpath.base, urlpath.nused); rc = lcb_http3(instance, cookie, &htcmd); lcb_string_release(&urlpath); if (rc != LCB_SUCCESS) { return rc; } lcb_htreq_setcb(htr, flush_cb); return LCB_SUCCESS; }
static int init_request(struct observe_st *reqinfo) { if (lcb_string_init(&reqinfo->body) != 0) { return 0; } reqinfo->allocated = 1; return 1; }
/** * Invoked when get a NOT_MY_VBUCKET response. If the response contains a JSON * payload then we refresh the configuration with it. * * This function returns 1 if the operation was successfully rescheduled; * otherwise it returns 0. If it returns 0 then we give the error back to the * user. */ static int handle_nmv(mc_SERVER *oldsrv, packet_info *resinfo, mc_PACKET *oldpkt) { mc_PACKET *newpkt; protocol_binary_request_header hdr; lcb_error_t err = LCB_ERROR; lcb_t instance = oldsrv->instance; lcb_U16 vbid; clconfig_provider *cccp = lcb_confmon_get_provider(instance->confmon, LCB_CLCONFIG_CCCP); mcreq_read_hdr(oldpkt, &hdr); vbid = ntohs(hdr.request.vbucket); lcb_log(LOGARGS(oldsrv, WARN), LOGFMT "NOT_MY_VBUCKET. Packet=%p (S=%u). VBID=%u", LOGID(oldsrv), (void*)oldpkt, oldpkt->opaque, vbid); /* Notify of new map */ lcb_vbguess_remap(instance, vbid, oldsrv->pipeline.index); if (PACKET_NBODY(resinfo) && cccp->enabled) { lcb_string s; lcb_string_init(&s); lcb_string_append(&s, PACKET_VALUE(resinfo), PACKET_NVALUE(resinfo)); err = lcb_cccp_update(cccp, mcserver_get_host(oldsrv), &s); lcb_string_release(&s); } if (err != LCB_SUCCESS) { int bs_options; if (instance->cur_configinfo->origin == LCB_CLCONFIG_CCCP) { /** * XXX: Not enough to see if cccp was enabled, since cccp might * be requested by a user, but would still not actually be active * for clusters < 2.5 If our current config is from CCCP * then we can be fairly certain that CCCP is indeed working. * * For this reason, we don't use if (cccp->enabled) {...} */ bs_options = LCB_BS_REFRESH_THROTTLE; } else { bs_options = LCB_BS_REFRESH_ALWAYS; } lcb_bootstrap_common(instance, bs_options); } if (!lcb_should_retry(oldsrv->settings, oldpkt, LCB_NOT_MY_VBUCKET)) { return 0; } /** Reschedule the packet again .. */ newpkt = mcreq_renew_packet(oldpkt); newpkt->flags &= ~MCREQ_STATE_FLAGS; lcb_retryq_nmvadd(instance->retryq, (mc_EXPACKET*)newpkt); return 1; }
LIBCOUCHBASE_API lcb_MULTICMD_CTX * lcb_endure3_ctxnew(lcb_t instance, const lcb_durability_opts_t *options, lcb_error_t *errp) { lcb_DURSET *dset; lcb_error_t err_s; hrtime_t now; lcbio_pTABLE io = instance->iotable; if (!errp) { errp = &err_s; } if (!LCBT_VBCONFIG(instance)) { *errp = LCB_CLIENT_ETMPFAIL; return NULL; } now = gethrtime(); dset = calloc(1, sizeof(*dset)); if (!dset) { *errp = LCB_CLIENT_ENOMEM; return NULL; } dset->opts = *options; dset->instance = instance; dset->mctx.addcmd = dset_ctx_add; dset->mctx.done = dset_ctx_schedule; dset->mctx.fail = dset_ctx_fail; if (!DSET_OPTFLD(dset, timeout)) { DSET_OPTFLD(dset, timeout) = LCBT_SETTING(instance, durability_timeout); } if (!DSET_OPTFLD(dset, interval)) { DSET_OPTFLD(dset, interval) = LCB_DEFAULT_DURABILITY_INTERVAL; } if (-1 == verify_critera(instance, dset)) { free(dset); *errp = LCB_DURABILITY_ETOOMANY; return NULL; } dset->us_timeout = (lcb_U32)(now / 1000) + DSET_OPTFLD(dset, timeout); dset->timer = io->timer.create(io->p); lcb_string_init(&dset->kvbufs); return &dset->mctx; }
static lcb_error_t parse_body(struct htvb_st *vbs, int *done) { lcb_error_t err = LCB_BUSY; char *term; if ((err = parse_chunk(vbs)) != LCB_SUCCESS) { *done = 1; /* no data */ lcb_assert(err == LCB_BUSY); return err; } if (lcb_string_append(&vbs->input, vbs->chunk.base, vbs->chunk_size)) { return LCB_CLIENT_ENOMEM; } lcb_string_erase_end(&vbs->input, 2); lcb_string_erase_beginning(&vbs->chunk, vbs->chunk_size); vbs->chunk_size = (lcb_size_t) - 1; if (vbs->chunk.nused > 0) { *done = 0; } term = strstr(vbs->input.base, "\n\n\n\n"); if (term != NULL) { lcb_string tmp; lcb_error_t ret; /** Next input */ lcb_string_init(&tmp); lcb_string_appendz(&tmp, term + 4); *term = '\0'; ret = set_next_config(vbs); /** Now, erase everything until the end of the 'term' */ if (vbs->input.base) { lcb_string_release(&vbs->input); } lcb_string_transfer(&tmp, &vbs->input); return ret; } return err; }
/** * Invoked when get a NOT_MY_VBUCKET response. If the response contains a JSON * payload then we refresh the configuration with it. * * This function returns 1 if the operation was successfully rescheduled; * otherwise it returns 0. If it returns 0 then we give the error back to the * user. */ static int handle_nmv(mc_SERVER *oldsrv, packet_info *resinfo, mc_PACKET *oldpkt) { mc_PACKET *newpkt; protocol_binary_request_header hdr; lcb_error_t err = LCB_ERROR; lcb_t instance = oldsrv->instance; lcb_U16 vbid; int tmpix; clconfig_provider *cccp = lcb_confmon_get_provider(instance->confmon, LCB_CLCONFIG_CCCP); mcreq_read_hdr(oldpkt, &hdr); vbid = ntohs(hdr.request.vbucket); lcb_log(LOGARGS(oldsrv, WARN), LOGFMT "NOT_MY_VBUCKET. Packet=%p (S=%u). VBID=%u", LOGID(oldsrv), (void*)oldpkt, oldpkt->opaque, vbid); /* Notify of new map */ tmpix = lcb_vbguess_remap(LCBT_VBCONFIG(instance), instance->vbguess, vbid, oldsrv->pipeline.index); if (tmpix > -1 && tmpix != oldsrv->pipeline.index) { lcb_log(LOGARGS(oldsrv, TRACE), LOGFMT "Heuristically set IX=%d as master for VBID=%u", LOGID(oldsrv), tmpix, vbid); } if (PACKET_NBODY(resinfo) && cccp->enabled) { lcb_string s; lcb_string_init(&s); lcb_string_append(&s, PACKET_VALUE(resinfo), PACKET_NVALUE(resinfo)); err = lcb_cccp_update(cccp, mcserver_get_host(oldsrv), &s); lcb_string_release(&s); } if (err != LCB_SUCCESS) { lcb_bootstrap_common(instance, LCB_BS_REFRESH_ALWAYS); } if (!lcb_should_retry(oldsrv->settings, oldpkt, LCB_NOT_MY_VBUCKET)) { return 0; } /** Reschedule the packet again .. */ newpkt = mcreq_renew_packet(oldpkt); newpkt->flags &= ~MCREQ_STATE_FLAGS; lcb_retryq_add(instance->retryq, (mc_EXPACKET*)newpkt, LCB_NOT_MY_VBUCKET); return 1; }
static int ensure_scratch(lcb_t instance, lcb_SIZE capacity) { if (!instance->scratch) { instance->scratch = calloc(1, sizeof(*instance->scratch)); if (!instance->scratch) { return 0; } lcb_string_init(instance->scratch); } else { lcb_string_clear(instance->scratch); } if (0 != lcb_string_reserve(instance->scratch, capacity)) { return 0; } return 1; }
/** Update the configuration from a server. */ lcb_error_t lcb_cccp_update(clconfig_provider *provider, const char *host, lcb_string *data) { VBUCKET_CONFIG_HANDLE vbc; lcb_string sanitized; int rv; clconfig_info *new_config; cccp_provider *cccp = (cccp_provider *)provider; vbc = vbucket_config_create(); if (!vbc) { return LCB_CLIENT_ENOMEM; } lcb_string_init(&sanitized); sanitize_config(data, host, &sanitized); rv = vbucket_config_parse(vbc, LIBVBUCKET_SOURCE_MEMORY, sanitized.base); if (rv) { lcb_string_release(&sanitized); vbucket_config_destroy(vbc); lcb_string_release(&sanitized); return LCB_PROTOCOL_ERROR; } new_config = lcb_clconfig_create(vbc, &sanitized, LCB_CLCONFIG_CCCP); lcb_string_release(&sanitized); if (!new_config) { vbucket_config_destroy(vbc); return LCB_CLIENT_ENOMEM; } if (cccp->config) { lcb_clconfig_decref(cccp->config); } /** TODO: Figure out the comparison vector */ new_config->cmpclock = gethrtime(); cccp->config = new_config; lcb_confmon_provider_success(provider, new_config); return LCB_SUCCESS; }
LIBCOUCHBASE_API lcb_MULTICMD_CTX * lcb_observe3_ctxnew(lcb_t instance) { OBSERVECTX *ctx; size_t ii, n_extra = LCBT_NSERVERS(instance)-1; ctx = calloc(1, sizeof(*ctx) + sizeof(ctx->requests) * n_extra); ctx->instance = instance; ctx->nrequests = n_extra + 1; ctx->mctx.addcmd = obs_ctxadd; ctx->mctx.done = obs_ctxdone; ctx->mctx.fail = obs_ctxfail; /* note this block doesn't do anything not done with calloc, but makes for * easier reading/tracking */ for (ii = 0; ii < ctx->nrequests; ii++) { lcb_string_init(ctx->requests + ii); } return &ctx->mctx; }
static int load_cache(file_provider *provider) { lcb_string str; char line[1024]; lcb_ssize_t nr; int fail; FILE *fp = NULL; VBUCKET_CONFIG_HANDLE config = NULL; char *end; struct stat st; int status = -1; lcb_string_init(&str); if (provider->filename == NULL) { return -1; } fp = fopen(provider->filename, "r"); if (fp == NULL) { LOG(provider, ERROR, "Couldn't open filename"); return -1; } if (fstat(fileno(fp), &st)) { provider->last_errno = errno; goto GT_DONE; } if (provider->last_mtime == st.st_mtime) { LOG(provider, INFO, "Rejecting file. Modification time too old"); goto GT_DONE; } config = vbucket_config_create(); if (config == NULL) { goto GT_DONE; } lcb_string_init(&str); while ((nr = fread(line, 1, sizeof(line), fp)) > 0) { if (lcb_string_append(&str, line, nr)) { goto GT_DONE; } } if (ferror(fp)) { goto GT_DONE; } fclose(fp); fp = NULL; if (!str.nused) { status = -1; goto GT_DONE; } end = strstr(str.base, CONFIG_CACHE_MAGIC); if (end == NULL) { LOG(provider, ERROR, "Couldn't find magic in file"); remove(provider->filename); status = -1; goto GT_DONE; } fail = vbucket_config_parse(config, LIBVBUCKET_SOURCE_MEMORY, str.base); if (fail) { status = -1; LOG(provider, ERROR, "Couldn't parse configuration"); remove(provider->filename); goto GT_DONE; } if (vbucket_config_get_distribution_type(config) != VBUCKET_DISTRIBUTION_VBUCKET) { status = -1; LOG(provider, ERROR, "Not applying cached memcached config"); goto GT_DONE; } if (provider->config) { lcb_clconfig_decref(provider->config); } provider->config = lcb_clconfig_create(config, &str, LCB_CLCONFIG_FILE); provider->config->cmpclock = gethrtime(); provider->config->origin = provider->base.type; provider->last_mtime = st.st_mtime; status = 0; config = NULL; GT_DONE: if (fp != NULL) { fclose(fp); } if (config != NULL) { vbucket_config_destroy(config); } lcb_string_release(&str); return status; }
static int load_cache(file_provider *provider) { lcb_string str; char line[1024]; lcb_ssize_t nr; int fail; FILE *fp = NULL; lcbvb_CONFIG *config = NULL; char *end; struct stat st; int status = -1; lcb_string_init(&str); if (provider->filename == NULL) { return -1; } fp = fopen(provider->filename, "r"); if (fp == NULL) { int save_errno = errno; lcb_log(LOGARGS(provider, ERROR), LOGFMT "Couldn't open for reading: %s", LOGID(provider), strerror(save_errno)); return -1; } if (fstat(fileno(fp), &st)) { provider->last_errno = errno; goto GT_DONE; } if (provider->last_mtime == st.st_mtime) { lcb_log(LOGARGS(provider, WARN), LOGFMT "Modification time too old", LOGID(provider)); goto GT_DONE; } config = lcbvb_create(); if (config == NULL) { goto GT_DONE; } lcb_string_init(&str); while ((nr = fread(line, 1, sizeof(line), fp)) > 0) { if (lcb_string_append(&str, line, nr)) { goto GT_DONE; } } if (ferror(fp)) { goto GT_DONE; } fclose(fp); fp = NULL; if (!str.nused) { status = -1; goto GT_DONE; } end = strstr(str.base, CONFIG_CACHE_MAGIC); if (end == NULL) { lcb_log(LOGARGS(provider, ERROR), LOGFMT "Couldn't find magic", LOGID(provider)); if (!provider->ro_mode) { remove(provider->filename); } status = -1; goto GT_DONE; } fail = lcbvb_load_json(config, str.base); if (fail) { status = -1; lcb_log(LOGARGS(provider, ERROR), LOGFMT "Couldn't parse configuration", LOGID(provider)); lcb_log_badconfig(LOGARGS(provider, ERROR), config, str.base); if (!provider->ro_mode) { remove(provider->filename); } goto GT_DONE; } if (lcbvb_get_distmode(config) != LCBVB_DIST_VBUCKET) { status = -1; lcb_log(LOGARGS(provider, ERROR), LOGFMT "Not applying cached memcached config", LOGID(provider)); goto GT_DONE; } if (strcmp(config->bname, PROVIDER_SETTING(&provider->base, bucket)) != 0) { status = -1; lcb_log(LOGARGS(provider, ERROR), LOGFMT "Bucket name in file is different from the one requested", LOGID(provider)); } if (provider->config) { lcb_clconfig_decref(provider->config); } provider->config = lcb_clconfig_create(config, LCB_CLCONFIG_FILE); provider->config->cmpclock = gethrtime(); provider->config->origin = provider->base.type; provider->last_mtime = st.st_mtime; status = 0; config = NULL; GT_DONE: if (fp != NULL) { fclose(fp); } if (config != NULL) { lcbvb_destroy(config); } lcb_string_release(&str); return status; }
/** * It's assumed the server buffers will be reset upon close(), so we must make * sure to _not_ release the ringbuffer if that happens. */ static void handle_read(lcbio_CTX *ioctx, unsigned nb) { mc_pSESSREQ sreq = lcbio_ctx_data(ioctx); packet_info info; unsigned required; uint16_t status; sreq_STATE state = SREQ_S_WAIT; int rc; GT_NEXT_PACKET: memset(&info, 0, sizeof(info)); rc = lcb_pktinfo_ectx_get(&info, ioctx, &required); if (rc == 0) { LCBIO_CTX_RSCHEDULE(ioctx, required); return; } else if (rc < 0) { state = SREQ_S_ERROR; } status = PACKET_STATUS(&info); switch (PACKET_OPCODE(&info)) { case PROTOCOL_BINARY_CMD_SASL_LIST_MECHS: { lcb_string str; int saslrc; const char *mechlist_data; unsigned int nmechlist_data; if (lcb_string_init(&str)) { set_error_ex(sreq, LCB_CLIENT_ENOMEM, NULL); state = SREQ_S_ERROR; break; } if (lcb_string_append(&str, info.payload, PACKET_NBODY(&info))) { lcb_string_release(&str); set_error_ex(sreq, LCB_CLIENT_ENOMEM, NULL); state = SREQ_S_ERROR; break; } saslrc = set_chosen_mech(sreq, &str, &mechlist_data, &nmechlist_data); if (saslrc == 0) { if (0 == send_sasl_auth(sreq, mechlist_data, nmechlist_data)) { state = SREQ_S_WAIT; } else { state = SREQ_S_ERROR; } } else if (saslrc < 0) { state = SREQ_S_ERROR; } else { state = SREQ_S_HELLODONE; } lcb_string_release(&str); break; } case PROTOCOL_BINARY_CMD_SASL_AUTH: { if (status == PROTOCOL_BINARY_RESPONSE_SUCCESS) { send_hello(sreq); state = SREQ_S_AUTHDONE; break; } if (status != PROTOCOL_BINARY_RESPONSE_AUTH_CONTINUE) { set_error_ex(sreq, LCB_AUTH_ERROR, "SASL AUTH failed"); state = SREQ_S_ERROR; break; } if (send_sasl_step(sreq, &info) == 0 && send_hello(sreq) == 0) { state = SREQ_S_WAIT; } else { state = SREQ_S_ERROR; } break; } case PROTOCOL_BINARY_CMD_SASL_STEP: { if (status != PROTOCOL_BINARY_RESPONSE_SUCCESS) { lcb_log(LOGARGS(sreq, WARN), SESSREQ_LOGFMT "SASL auth failed with STATUS=0x%x", SESSREQ_LOGID(sreq), status); set_error_ex(sreq, LCB_AUTH_ERROR, "SASL Step Failed"); state = SREQ_S_ERROR; } else { /* Wait for pipelined HELLO response */ state = SREQ_S_AUTHDONE; } break; } case PROTOCOL_BINARY_CMD_HELLO: { state = SREQ_S_HELLODONE; if (status == PROTOCOL_BINARY_RESPONSE_SUCCESS) { parse_hello(sreq, &info); } else if (status == PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND || status == PROTOCOL_BINARY_RESPONSE_NOT_SUPPORTED) { lcb_log(LOGARGS(sreq, DEBUG), SESSREQ_LOGFMT "Server does not support HELLO", SESSREQ_LOGID(sreq)); /* nothing */ } else { set_error_ex(sreq, LCB_PROTOCOL_ERROR, "Hello response unexpected"); state = SREQ_S_ERROR; } break; } default: { state = SREQ_S_ERROR; lcb_log(LOGARGS(sreq, ERROR), SESSREQ_LOGFMT "Received unknown response. OP=0x%x. RC=0x%x", SESSREQ_LOGID(sreq), PACKET_OPCODE(&info), PACKET_STATUS(&info)); set_error_ex(sreq, LCB_NOT_SUPPORTED, "Received unknown response"); break; } } lcb_pktinfo_ectx_done(&info, ioctx); if (sreq->err != LCB_SUCCESS) { bail_pending(sreq); } else if (state == SREQ_S_ERROR) { set_error_ex(sreq, LCB_ERROR, "FIXME: Error code set without description"); } else if (state == SREQ_S_HELLODONE) { negotiation_success(sreq); } else { goto GT_NEXT_PACKET; } (void)nb; }
LIBCOUCHBASE_API lcb_MULTICMD_CTX * lcb_endure3_ctxnew(lcb_t instance, const lcb_durability_opts_t *options, lcb_error_t *errp) { lcb_DURSET *dset; lcb_error_t err_s; lcbio_pTABLE io = instance->iotable; const lcb_DURABILITYOPTSv0 *opts_in = &options->v.v0; if (!errp) { errp = &err_s; } *errp = LCB_SUCCESS; if (!LCBT_VBCONFIG(instance)) { *errp = LCB_CLIENT_ETMPFAIL; return NULL; } dset = calloc(1, sizeof(*dset)); if (!dset) { *errp = LCB_CLIENT_ENOMEM; return NULL; } /* Ensure we don't clobber options from older versions */ dset->opts.cap_max = opts_in->cap_max; dset->opts.check_delete = opts_in->check_delete; dset->opts.interval = opts_in->interval; dset->opts.persist_to = opts_in->persist_to; dset->opts.replicate_to = opts_in->replicate_to; dset->opts.timeout = opts_in->timeout; if (options->version > 0) { dset->opts.pollopts = opts_in->pollopts; } dset->opts.pollopts = get_poll_meth(instance, &dset->opts); dset->instance = instance; dset->mctx.addcmd = dset_ctx_add; dset->mctx.done = dset_ctx_schedule; dset->mctx.fail = dset_ctx_fail; if (!DSET_OPTFLD(dset, timeout)) { DSET_OPTFLD(dset, timeout) = LCBT_SETTING(instance, durability_timeout); } if (!DSET_OPTFLD(dset, interval)) { DSET_OPTFLD(dset, interval) = LCBT_SETTING(instance, durability_interval); } *errp = lcb_durability_validate(instance, &dset->opts.persist_to, &dset->opts.replicate_to, dset->opts.cap_max ? LCB_DURABILITY_VALIDATE_CAPMAX : 0); if (*errp != LCB_SUCCESS) { free(dset); return NULL; } dset->timer = io->timer.create(io->p); lcb_string_init(&dset->kvbufs); return &dset->mctx; }
static void io_read_handler(lcb_connection_t conn) { packet_info pi; cccp_provider *cccp = conn->data; lcb_string jsonstr; lcb_error_t err; int rv; lcb_host_t curhost; memset(&pi, 0, sizeof(pi)); rv = lcb_packet_read_ringbuffer(&pi, conn->input); if (rv < 0) { LOG(cccp, ERR, "Couldn't parse packet!?"); mcio_error(cccp, LCB_EINTERNAL); return; } else if (rv == 0) { lcb_sockrw_set_want(conn, LCB_READ_EVENT, 1); lcb_sockrw_apply_want(conn); return; } if (PACKET_STATUS(&pi) != PROTOCOL_BINARY_RESPONSE_SUCCESS) { lcb_log(LOGARGS(cccp, ERR), "CCCP Packet responded with 0x%x; nkey=%d, nbytes=%lu, cmd=0x%x, seq=0x%x", PACKET_STATUS(&pi), PACKET_NKEY(&pi), PACKET_NBODY(&pi), PACKET_OPCODE(&pi), PACKET_OPAQUE(&pi)); switch (PACKET_STATUS(&pi)) { case PROTOCOL_BINARY_RESPONSE_NOT_SUPPORTED: case PROTOCOL_BINARY_RESPONSE_UNKNOWN_COMMAND: mcio_error(cccp, LCB_NOT_SUPPORTED); break; default: mcio_error(cccp, LCB_PROTOCOL_ERROR); break; } return; } if (!PACKET_NBODY(&pi)) { mcio_error(cccp, LCB_PROTOCOL_ERROR); return; } if (lcb_string_init(&jsonstr)) { mcio_error(cccp, LCB_CLIENT_ENOMEM); return; } if (lcb_string_append(&jsonstr, PACKET_BODY(&pi), PACKET_NBODY(&pi))) { mcio_error(cccp, LCB_CLIENT_ENOMEM); return; } curhost = *lcb_connection_get_host(&cccp->connection); lcb_packet_release_ringbuffer(&pi, conn->input); release_socket(cccp, 1); err = lcb_cccp_update(&cccp->base, curhost.host, &jsonstr); lcb_string_release(&jsonstr); if (err == LCB_SUCCESS) { lcb_timer_disarm(cccp->timer); cccp->server_active = 0; } else { schedule_next_request(cccp, LCB_PROTOCOL_ERROR, 0); } }
lcb_error_t hostlist_add_string(hostlist_t hostlist, const char *spec, int speclen, int deflport) { lcb_error_t err = LCB_SUCCESS; lcb_string str; char *delim; char *curstart; if (speclen < 0) { speclen = strlen(spec); } if (lcb_string_init(&str)) { return LCB_CLIENT_ENOMEM; } if (lcb_string_append(&str, spec, speclen)) { lcb_string_release(&str); return LCB_CLIENT_ENOMEM; } if (!str.nused) { lcb_string_release(&str); return LCB_SUCCESS; } if (str.base[str.nused-1] != ';') { if (lcb_string_appendz(&str, ";")) { lcb_string_release(&str); return LCB_CLIENT_ENOMEM; } } curstart = str.base; while ( (delim = strstr(curstart, ";"))) { lcb_host_t curhost; lcb_size_t curlen; if (delim == curstart) { curstart++; continue; } /** { 'f', 'o', 'o', ';' } */ curlen = delim - curstart; err = lcb_host_parse(&curhost, curstart, curlen, deflport); if (err != LCB_SUCCESS) { break; } err = hostlist_add_host(hostlist, &curhost); if (err != LCB_SUCCESS) { break; } curstart = delim + 1; } lcb_string_release(&str); return err; }
lcb_error_t lcb_host_parse(lcb_host_t *host, const char *spec, int speclen, int deflport) { lcb_string zspec; char *host_s; char *port_s; char *delim; /** Parse the host properly */ if (speclen < 0) { speclen = strlen(spec); } else if (speclen == 0) { return LCB_INVALID_HOST_FORMAT; } if (deflport < 1) { return LCB_INVALID_HOST_FORMAT; } if (lcb_string_init(&zspec)) { return LCB_CLIENT_ENOMEM; } if (lcb_string_append(&zspec, spec, speclen)) { lcb_string_release(&zspec); return LCB_CLIENT_ENOMEM; } host_s = zspec.base; if (*host_s == ':') { lcb_string_release(&zspec); return LCB_INVALID_HOST_FORMAT; } if ( (delim = strstr(host_s, "://"))) { host_s = delim + 3; } if ((delim = strstr(host_s, "/"))) { *delim = '\0'; } port_s = strstr(host_s, ":"); if (port_s != NULL) { char *endp; long ll; *port_s = '\0'; port_s++; if (! *port_s) { lcb_string_release(&zspec); return LCB_INVALID_HOST_FORMAT; } ll = strtol(port_s, &endp, 10); if (ll == LONG_MIN || ll == LONG_MAX) { lcb_string_release(&zspec); return LCB_INVALID_HOST_FORMAT; } if (*endp) { lcb_string_release(&zspec); return LCB_INVALID_HOST_FORMAT; } } else { port_s = ""; } if (strlen(host_s) > sizeof(host->host)-1 || strlen(port_s) > sizeof(host->port)-1 || *host_s == '\0') { lcb_string_release(&zspec); return LCB_INVALID_HOST_FORMAT; } { lcb_size_t ii, hostlen = strlen(host_s); for (ii = 0; ii < hostlen; ii++) { if (isalnum(host_s[ii])) { continue; } switch (host_s[ii]) { case '.': case '-': case '_': break; default: lcb_string_release(&zspec); return LCB_INVALID_HOST_FORMAT; } } } strcpy(host->host, host_s); if (*port_s) { strcpy(host->port, port_s); } else { sprintf(host->port, "%d", deflport); } lcb_string_release(&zspec); return LCB_SUCCESS; }
/** * Returns 1 if retried, 0 if the command should fail, -1 for an internal * error */ static int handle_not_my_vbucket(lcb_server_t *c, packet_info *resinfo, protocol_binary_request_header *oldreq, struct lcb_command_data_st *oldct) { int idx; char *body; lcb_size_t nbody, nr; lcb_server_t *new_srv; struct lcb_command_data_st ct; protocol_binary_request_header req; hrtime_t now; lcb_string config_string; lcb_error_t err = LCB_ERROR; lcb_log(LOGARGS(c, WARN), "NOT_MY_VBUCKET; Server=%p,ix=%d,real_start=%lu,vb=%d", (void *)c, c->index, (unsigned long)oldct->real_start, (int)ntohs(oldreq->request.vbucket)); lcb_string_init(&config_string); if (PACKET_NBODY(resinfo)) { lcb_string_append(&config_string, PACKET_VALUE(resinfo), PACKET_NVALUE(resinfo)); err = lcb_cccp_update(lcb_confmon_get_provider(c->instance->confmon, LCB_CLCONFIG_CCCP), c->curhost.host, &config_string); } lcb_string_release(&config_string); if (err != LCB_SUCCESS) { lcb_bootstrap_refresh(c->instance); } /* re-schedule command to new server */ if (!c->instance->settings.vb_noguess) { idx = vbucket_found_incorrect_master(c->instance->vbucket_config, ntohs(oldreq->request.vbucket), (int)c->index); } else { idx = c->index; } if (idx == -1) { lcb_log(LOGARGS(c, ERR), "no alternate server"); return 0; } lcb_log(LOGARGS(c, INFO), "Mapped key to new server %d -> %d", c->index, idx); now = gethrtime(); if (oldct->real_start) { hrtime_t min_ok = now - MCSERVER_TIMEOUT(c) * 1000; if (oldct->real_start < min_ok) { /** Timed out in a 'natural' manner */ return 0; } } req = *oldreq; lcb_assert((lcb_size_t)idx < c->instance->nservers); new_srv = c->instance->servers + idx; nr = ringbuffer_read(&c->cmd_log, req.bytes, sizeof(req)); lcb_assert(nr == sizeof(req)); req.request.opaque = ++c->instance->seqno; nbody = ntohl(req.request.bodylen); body = malloc(nbody); if (body == NULL) { lcb_error_handler(c->instance, LCB_CLIENT_ENOMEM, NULL); return -1; } nr = ringbuffer_read(&c->cmd_log, body, nbody); lcb_assert(nr == nbody); nr = ringbuffer_read(&c->output_cookies, &ct, sizeof(ct)); lcb_assert(nr == sizeof(ct)); /* Preserve the cookie and reset timestamp for the command. This * means that the library will retry the command until it will * get code different from LCB_NOT_MY_VBUCKET */ if (!ct.real_start) { ct.real_start = ct.start; } ct.start = now; lcb_server_retry_packet(new_srv, &ct, &req, sizeof(req)); /* FIXME dtrace instrumentation */ lcb_server_write_packet(new_srv, body, nbody); lcb_server_end_packet(new_srv); lcb_server_send_packets(new_srv); free(body); return 1; }