예제 #1
0
lcb_error_t
lcb_n1p_mutation_token(lcb_N1QLPARAMS *params, const lcb_MUTATION_TOKEN *sv)
{
    size_t np;

    if (params->scanvec_type == SCANVEC_FULL) {
        return LCB_OPTIONS_CONFLICT;
    }

    params->consist_type = LCB_N1P_CONSISTENCY_RYOW;

    /* Reserve data: */
    if (-1 == lcb_string_reserve(&params->scanvec, SCANVEC_BASE_SIZE)) {
        return LCB_CLIENT_ENOMEM;
    }

    if (!params->scanvec.nused) {
        lcb_string_append(&params->scanvec, "{", 1);
    } else {
        lcb_string_append(&params->scanvec, ",", 1);
    }

    np = sprintf(params->scanvec.base + params->scanvec.nused, SCANVEC_BASEFMT,
                 sv->vbid_,
                 (unsigned long long)sv->uuid_,
                 (unsigned long long)sv->seqno_);

    lcb_string_added(&params->scanvec, np);
    params->scanvec_type = SCANVEC_PARTIAL;
    return LCB_SUCCESS;
}
예제 #2
0
static lcb_error_t
dset_ctx_add(lcb_MULTICMD_CTX *mctx, const lcb_CMDBASE *cmd)
{
    lcb_DURSET *dset = CTX_FROM_MULTI(mctx);
    lcb_DURITEM *ent;

    if (LCB_KEYBUF_IS_EMPTY(&cmd->key)) {
        return LCB_EMPTY_KEY;
    }

    /* ensure we have enough space first */
    if (dset->nentries == 0) {
        /* First entry. Optimize */
        ent = &dset->single.ent;
        dset->entries = &dset->single.ent;

    } else if (dset->nentries == 1) {
        /* More than a single entry */
        dset->ents_alloced = 2;
        dset->entries = malloc(2 * sizeof(*dset->entries));
        if (!dset->entries) {
            return LCB_CLIENT_ENOMEM;
        }
        dset->entries[0] = dset->single.ent;
        ent = &dset->entries[1];
        dset->ht = lcb_hashtable_nc_new(16);
        if (!dset->ht) {
            return LCB_CLIENT_ENOMEM;
        }
    } else if (dset->nentries < dset->ents_alloced) {
        ent = &dset->entries[dset->nentries];
    } else {
        lcb_DURITEM *newarr;
        lcb_SIZE newsize = dset->ents_alloced * 1.5;
        newarr = realloc(dset->entries, sizeof(*ent) * newsize);
        if (!newarr) {
            return LCB_CLIENT_ENOMEM;
        }
        dset->entries = newarr;
        dset->ents_alloced = newsize;
        ent = &dset->entries[dset->nentries];
    }

    /* ok. now let's initialize the entry..*/
    memset(ent, 0, sizeof (*ent));
    RESFLD(ent, nkey) = cmd->key.contig.nbytes;
    ent->hashkey = cmd->hashkey;
    ent->reqcas = cmd->cas;
    ent->parent = dset;

    lcb_string_append(&dset->kvbufs,
        cmd->key.contig.bytes, cmd->key.contig.nbytes);
    if (cmd->hashkey.contig.nbytes) {
        lcb_string_append(&dset->kvbufs,
            cmd->hashkey.contig.bytes,  cmd->hashkey.contig.nbytes);
    }
    dset->nentries++;
    return LCB_SUCCESS;
}
예제 #3
0
static void sanitize_config(
    const lcb_string *src, const char *host, lcb_string *dst)
{
    char *cur = src->base, *last = src->base;

    while ((cur = strstr(cur, HOST_TOKEN))) {
        lcb_string_append(dst, last, cur-last);
        lcb_string_appendz(dst, host);
        cur += sizeof(HOST_TOKEN)-1;
        last = cur;
    }

    lcb_string_append(dst, last, src->base + src->nalloc - last);
}
예제 #4
0
lcb_error_t
lcb_n1p_posparam(lcb_N1QLPARAMS *params, const char *value, size_t nvalue)
{
    nvalue = get_strlen(value, nvalue);
    if (-1 == lcb_string_reserve(&params->posargs, nvalue+3)) {
        return LCB_CLIENT_ENOMEM;
    }

    if (!params->posargs.nused) {
        lcb_string_append(&params->posargs, "[", 1);
    } else {
        lcb_string_append(&params->posargs, ",", 1);
    }
    lcb_string_append(&params->posargs, value, nvalue);
    return LCB_SUCCESS;
}
예제 #5
0
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);
}
예제 #6
0
static lcb_error_t
dset_ctx_add(lcb_MULTICMD_CTX *mctx, const lcb_CMDBASE *cmd)
{
    lcb_DURSET *dset = CTX_FROM_MULTI(mctx);
    lcb_DURITEM *ent;
    int vbid, srvix;

    if (LCB_KEYBUF_IS_EMPTY(&cmd->key)) {
        return LCB_EMPTY_KEY;
    }

    LCB_SSOBUF_ALLOC(&ent, &dset->entries_, lcb_DURITEM);
    if (!ent) {
        return LCB_CLIENT_ENOMEM;
    }

    mcreq_map_key(&dset->instance->cmdq, &cmd->key, &cmd->_hashkey,
        MCREQ_PKT_BASESIZE, &vbid, &srvix);

    /* ok. now let's initialize the entry..*/
    memset(ent, 0, sizeof (*ent));
    RESFLD(ent, nkey) = cmd->key.contig.nbytes;
    ent->reqcas = cmd->cas;
    ent->parent = dset;
    ent->vbid = vbid;

    lcb_string_append(&dset->kvbufs,
        cmd->key.contig.bytes, cmd->key.contig.nbytes);
    if (DSET_PROCS(dset)->ent_add) {
        return DSET_PROCS(dset)->ent_add(dset, ent, (lcb_CMDENDURE*)cmd);
    } else {
        return LCB_SUCCESS;
    }
}
예제 #7
0
/**
 * 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;
}
예제 #8
0
static lcb_error_t
finalize_field(lcb_N1QLPARAMS *params, lcb_string *ss,
    const char *field, const char *term)
{
    if (ss->nused == 0) {
        return LCB_SUCCESS;
    }
    if (-1 == lcb_string_append(ss, term, 1)) {
        return LCB_CLIENT_ENOMEM;
    }
    return lcb_n1p_setopt(params, field, -1, ss->base, ss->nused);
}
예제 #9
0
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;
}
예제 #10
0
static lcb_error_t
setopt(lcb_N1QLPARAMS *params,
       const char *k, size_t nk, const char *v, size_t nv, lcb_string *target)
{
    nv = get_strlen(v, nv);
    nk = get_strlen(k, nk);
    /* Do we need the '&'? */
    if (params->form.nused) {
        if (-1 == lcb_string_append(target, "&", 1)) {
            return LCB_CLIENT_ENOMEM;
        }
    }
    if (-1 == add_encstr(target, k, nk)) {
        return LCB_CLIENT_ENOMEM;
    }
    if (-1 == lcb_string_append(target, "=", 1)) {
        return LCB_CLIENT_ENOMEM;
    }
    if (-1 == add_encstr(target, v, nv)) {
        return LCB_CLIENT_ENOMEM;
    }
    return LCB_SUCCESS;
}
예제 #11
0
/**
 * 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;
}
예제 #12
0
static lcb_error_t
obs_ctxadd(lcb_MULTICMD_CTX *mctx, const lcb_CMDOBSERVE *cmd)
{
    const void *hk;
    lcb_SIZE nhk;
    int vbid;
    unsigned maxix;
    int ii;
    OBSERVECTX *ctx = CTX_FROM_MULTI(mctx);
    lcb_t instance = ctx->instance;
    mc_CMDQUEUE *cq = &instance->cmdq;

    if (LCB_KEYBUF_IS_EMPTY(&cmd->key)) {
        return LCB_EMPTY_KEY;
    }

    if (cq->config == NULL) {
        return LCB_CLIENT_ETMPFAIL;
    }

    if (LCBVB_DISTTYPE(LCBT_VBCONFIG(instance)) != LCBVB_DIST_VBUCKET) {
        return LCB_NOT_SUPPORTED;
    }

    mcreq_extract_hashkey(&cmd->key, &cmd->_hashkey, 24, &hk, &nhk);
    vbid = lcbvb_k2vb(cq->config, hk, nhk);
    maxix = LCBVB_NREPLICAS(cq->config);

    for (ii = -1; ii < (int)maxix; ++ii) {
        struct observe_st *rr;
        lcb_U16 vb16, klen16;
        int ix;

        if (ii == -1) {
            ix = lcbvb_vbmaster(cq->config, vbid);
            if (ix < 0) {
                return LCB_NO_MATCHING_SERVER;
            }
        } else {
            ix = lcbvb_vbreplica(cq->config, vbid, ii);
            if (ix < 0) {
                continue;
            }
        }

        lcb_assert(ix < (int)ctx->nrequests);
        rr = ctx->requests + ix;
        if (!rr->allocated) {
            if (!init_request(rr)) {
                return LCB_CLIENT_ENOMEM;
            }
        }

        vb16 = htons((lcb_U16)vbid);
        klen16 = htons((lcb_U16)cmd->key.contig.nbytes);
        lcb_string_append(&rr->body, &vb16, sizeof vb16);
        lcb_string_append(&rr->body, &klen16, sizeof klen16);
        lcb_string_append(&rr->body, cmd->key.contig.bytes, cmd->key.contig.nbytes);

        ctx->remaining++;
        if (cmd->cmdflags & LCB_CMDOBSERVE_F_MASTER_ONLY) {
            break;
        }
    }
    return LCB_SUCCESS;
}
예제 #13
0
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;
}
예제 #14
0
/**
 * 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;
}
예제 #15
0
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;
}
예제 #16
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;
}
예제 #17
0
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);
    }
}
예제 #18
0
/**
 * 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;
}
예제 #19
0
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;
}
예제 #20
0
static lcb_error_t
obs_ctxadd(lcb_MULTICMD_CTX *mctx, const lcb_CMDBASE *cmdbase)
{
    int vbid, srvix_dummy;
    unsigned ii;
    const lcb_CMDOBSERVE *cmd = (const lcb_CMDOBSERVE *)cmdbase;
    OBSERVECTX *ctx = CTX_FROM_MULTI(mctx);
    lcb_t instance = ctx->instance;
    mc_CMDQUEUE *cq = &instance->cmdq;
    lcb_U16 servers_s[4];
    const lcb_U16 *servers;
    size_t nservers;

    if (LCB_KEYBUF_IS_EMPTY(&cmd->key)) {
        return LCB_EMPTY_KEY;
    }

    if (cq->config == NULL) {
        return LCB_CLIENT_ETMPFAIL;
    }

    if (LCBVB_DISTTYPE(LCBT_VBCONFIG(instance)) != LCBVB_DIST_VBUCKET) {
        return LCB_NOT_SUPPORTED;
    }

    mcreq_map_key(cq, &cmd->key, &cmd->_hashkey, 24, &vbid, &srvix_dummy);

    if (cmd->servers_) {
        servers = cmd->servers_;
        nservers = cmd->nservers_;
    } else {
        nservers = 0;
        servers = servers_s;
        /* Replicas are always < 4 */
        for (ii = 0; ii < LCBVB_NREPLICAS(cq->config) + 1; ii++) {
            int ix = lcbvb_vbserver(cq->config, vbid, ii);
            if (ix < 0) {
                if (ii == 0) {
                    return LCB_NO_MATCHING_SERVER;
                } else {
                    continue;
                }
            }
            servers_s[nservers++] = ix;
            if (cmd->cmdflags & LCB_CMDOBSERVE_F_MASTER_ONLY) {
                break; /* Only a single server! */
            }
        }
    }

    if (nservers == 0) {
        return LCB_NO_MATCHING_SERVER;
    }

    for (ii = 0; ii < nservers; ii++) {
        lcb_string *rr;
        lcb_U16 vb16, klen16;
        lcb_U16 ix = servers[ii];

        lcb_assert(ix < ctx->nrequests);
        rr = ctx->requests + ix;
        if (0 != lcb_string_reserve(rr, 4 + cmd->key.contig.nbytes)) {
            return LCB_CLIENT_ENOMEM;
        }

        vb16 = htons((lcb_U16)vbid);
        klen16 = htons((lcb_U16)cmd->key.contig.nbytes);
        lcb_string_append(rr, &vb16, sizeof vb16);
        lcb_string_append(rr, &klen16, sizeof klen16);
        lcb_string_append(rr, cmd->key.contig.bytes, cmd->key.contig.nbytes);

        ctx->remaining++;
    }
    return LCB_SUCCESS;
}
예제 #21
0
LIBCOUCHBASE_API
const char *
lcb_n1p_encode(lcb_N1QLPARAMS *params, lcb_error_t *err)
{
    lcb_error_t err_s = LCB_SUCCESS;
    if (!err) {
        err = &err_s;
    }
    /* Build the query */

    lcb_string_clear(&params->reqbuf);

    if (!params->form.nused) {
        *err = LCB_EINVAL;
        return NULL;
    }

    if (-1 == lcb_string_append(
                &params->reqbuf, params->form.base, params->form.nused)) {
        *err = LCB_CLIENT_ENOMEM;
        return NULL;
    }

    if ((*err = finalize_field(params, &params->posargs, "args", "]"))
            != LCB_SUCCESS) {
        return NULL;
    }

    if (params->scanvec.nused) {
        if (params->consist_type != LCB_N1P_CONSISTENCY_RYOW) {
            *err = LCB_OPTIONS_CONFLICT;
            return NULL;
        }
        if ((*err = finalize_field(params, &params->scanvec, "scan_vector", "}"))
                != LCB_SUCCESS) {
            return NULL;
        }
    }

    if (params->consist_type) {
        if (-1 == lcb_string_reserve(&params->reqbuf, 15)) {
            *err = LCB_CLIENT_ENOMEM;
            return NULL;
        }
    }

    if (params->consist_type == LCB_N1P_CONSISTENCY_RYOW) {
        if (!params->scanvec.nused) {
            *err = LCB_OPTIONS_CONFLICT;
            return NULL;
        } else {
            set_reqbuf_optz(params, PARAM_CONSISTENT, "at_plus");
        }
    } else if (params->consist_type == LCB_N1P_CONSISTENCY_REQUEST) {
        set_reqbuf_optz(params, PARAM_CONSISTENT, "request_plus");
    } else if (params->consist_type == LCB_N1P_CONSISTENCY_STATEMENT) {
        set_reqbuf_optz(params, PARAM_CONSISTENT, "statement_plus");
    } else if (params->consist_type == LCB_N1P_CONSISTENCY_NONE) {
        /* Nothing */
    } else {
        *err = LCB_EINVAL;
        return NULL;
    }

    return params->reqbuf.base;
}