static lcb_error_t schedule_next_request(cccp_provider *cccp,
        lcb_error_t err,
        int can_rollover)
{
    lcb_server_t *server = NULL;
    lcb_size_t ii;

    lcb_host_t *next_host = hostlist_shift_next(cccp->nodes, can_rollover);

    if (!next_host) {
        lcb_timer_disarm(cccp->timer);
        lcb_confmon_provider_failed(&cccp->base, err);
        cccp->server_active = 0;
        return err;
    }

    /** See if we can find a server */
    for (ii = 0; ii < cccp->instance->nservers; ii++) {
        lcb_server_t *cur = cccp->instance->servers + ii;
        if (lcb_host_equals(&cur->curhost, next_host)) {
            server = cur;
            break;
        }
    }

    if (server) {
        protocol_binary_request_get_cluster_config req;
        cccp_cookie *cookie = calloc(1, sizeof(*cookie));

        lcb_log(LOGARGS(cccp, INFO),
                "Re-Issuing CCCP Command on server struct %p", server);

        cookie->parent = cccp;
        memset(&req, 0, sizeof(req));
        req.message.header.request.magic = PROTOCOL_BINARY_REQ;
        req.message.header.request.opcode = CMD_GET_CLUSTER_CONFIG;
        req.message.header.request.opaque = ++cccp->instance->seqno;
        lcb_server_start_packet(server, cookie, &req, sizeof(req.bytes));
        lcb_server_end_packet(server);
        lcb_server_send_packets(server);
        lcb_timer_rearm(cccp->timer, PROVIDER_SETTING(&cccp->base,
                        config_node_timeout));
    } else {
        cccp->cur_connreq = calloc(1, sizeof(*cccp->cur_connreq));
        connmgr_req_init(cccp->cur_connreq, next_host->host, next_host->port,
                         socket_connected);
        cccp->cur_connreq->data = cccp;
        connmgr_get(cccp->instance->memd_sockpool, cccp->cur_connreq,
                    PROVIDER_SETTING(&cccp->base, config_node_timeout));
    }

    cccp->server_active = 1;
    return LCB_SUCCESS;
}
Exemple #2
0
static void request_config(cccp_provider *cccp)
{
    protocol_binary_request_set_cluster_config req;
    lcb_connection_t conn = &cccp->connection;
    ringbuffer_t *buf = conn->output;

    memset(&req, 0, sizeof(req));
    req.message.header.request.magic = PROTOCOL_BINARY_REQ;
    req.message.header.request.opcode = CMD_GET_CLUSTER_CONFIG;
    req.message.header.request.opaque = 0xF00D;

    if (!buf) {
        if ((buf = calloc(1, sizeof(*buf))) == NULL) {
            mcio_error(cccp, LCB_CLIENT_ENOMEM);
            return;
        }
        conn->output = buf;
    }

    if (!ringbuffer_ensure_capacity(buf, sizeof(req.bytes))) {
        mcio_error(cccp, LCB_CLIENT_ENOMEM);
    }

    ringbuffer_write(buf, req.bytes, sizeof(req.bytes));
    lcb_sockrw_set_want(conn, LCB_WRITE_EVENT, 1);
    lcb_sockrw_apply_want(conn);
    lcb_timer_rearm(cccp->timer, PROVIDER_SETTING(&cccp->base,
                                                  config_node_timeout));
}
static void refresh_nodes(clconfig_provider *pb,
                          const hostlist_t newnodes,
                          VBUCKET_CONFIG_HANDLE newconfig)
{
    unsigned int ii;
    http_provider *http = (http_provider *)pb;

    hostlist_clear(http->nodes);
    if (!newconfig) {
        for (ii = 0; ii < newnodes->nentries; ii++) {
            hostlist_add_host(http->nodes, newnodes->entries + ii);
        }
        goto GT_DONE;
    }

    for (ii = 0; (int)ii < vbucket_config_get_num_servers(newconfig); ii++) {
        lcb_error_t status;
        const char *ss = vbucket_config_get_rest_api_server(newconfig, ii);
        lcb_assert(ss != NULL);
        status = hostlist_add_stringz(http->nodes, ss, LCB_CONFIG_HTTP_PORT);
        lcb_assert(status ==  LCB_SUCCESS);
    }

    GT_DONE:
    if (PROVIDER_SETTING(pb, randomize_bootstrap_nodes)) {
        hostlist_randomize(http->nodes);
    }
}
static lcb_error_t get_refresh(clconfig_provider *provider)
{
    http_provider *http = (http_provider *)provider;

    /**
     * We want a grace interval here because we might already be fetching a
     * connection. HOWEVER we don't want to indefinitely wait on a socket
     * so we issue a timer indicating how long we expect to wait for a
     * streaming update until we get something.
     */

    /** If we need a new socket, we do connect_next. */
    if (http->connection.state == LCB_CONNSTATE_UNINIT) {
        lcb_error_t rc = connect_next(http);
        if (rc != LCB_SUCCESS) {
            http->as_errcode = rc;
            lcb_async_signal(http->as_schederr);
        }
        return rc;
    }

    lcb_timer_disarm(http->disconn_timer);
    if (http->connection.state == LCB_CONNSTATE_CONNECTED) {
        lcb_timer_rearm(http->io_timer,
                        PROVIDER_SETTING(provider, config_node_timeout));
    }
    return LCB_SUCCESS;
}
static lcb_error_t connect_next(http_provider *http)
{
    char *errinfo = NULL;
    lcb_error_t err;
    lcb_conn_params params;
    lcb_connection_t conn = &http->connection;

    close_current(http);
    reset_stream_state(http);
    params.handler = connect_done_handler;
    params.timeout = PROVIDER_SETTING(&http->base, config_node_timeout);

    lcb_log(LOGARGS(http, TRACE),
            "Starting HTTP Configuration Provider %p", http);

    err = lcb_connection_cycle_nodes(conn, http->nodes, &params, &errinfo);

    if (err == LCB_SUCCESS) {
        err = setup_request_header(http);
    } else {
        lcb_log(LOGARGS(http, ERROR),
                "%p: Couldn't schedule connection (0x%x)", http, err);
    }

    return err;
}
static void connect_done_handler(lcb_connection_t conn, lcb_error_t err)
{
    http_provider *http = (http_provider *)conn->data;
    const lcb_host_t *host = lcb_connection_get_host(conn);

    if (err != LCB_SUCCESS) {
        lcb_log(LOGARGS(http, ERR),
                "Connection to REST API @%s:%s failed with code=0x%x",
                host->host, host->port, err);

        io_error(http, err);
        return;
    }

    lcb_log(LOGARGS(http, DEBUG),
            "Successfuly connected to REST API %s:%s",
            host->host, host->port);

    lcb_connection_reset_buffers(conn);
    ringbuffer_strcat(conn->output, http->request_buf);
    lcb_assert(conn->output->nbytes > 0);

    lcb_sockrw_set_want(conn, LCB_RW_EVENT, 0);
    lcb_sockrw_apply_want(conn);
    lcb_timer_rearm(http->io_timer,
                    PROVIDER_SETTING(&http->base, config_node_timeout));
}
/**
 * Common function to handle parsing the HTTP stream for both v0 and v1 io
 * implementations.
 */
static void read_common(http_provider *http)
{
    lcb_error_t err;
    lcb_connection_t conn = &http->connection;
    int old_generation = http->stream.generation;

    lcb_log(LOGARGS(http, TRACE),
            "Received %d bytes on HTTP stream", conn->input->nbytes);

    lcb_timer_rearm(http->io_timer,
                    PROVIDER_SETTING(&http->base, config_node_timeout));

    lcb_string_rbappend(&http->stream.chunk, conn->input, 1);

    err = htvb_parse(&http->stream, http->base.parent->settings->conntype);

    if (http->stream.generation != old_generation) {
        lcb_log(LOGARGS(http, DEBUG),
                "Generation %d -> %d", old_generation, http->stream.generation);

        set_new_config(http);
    } else {
        lcb_log(LOGARGS(http, TRACE), "HTTP not yet done. Err=0x%x", err);
    }

    if (err != LCB_BUSY && err != LCB_SUCCESS) {
        io_error(http, err);
        return;
    }

    lcb_sockrw_set_want(conn, LCB_READ_EVENT, 1);
    lcb_sockrw_apply_want(conn);
}
/**
 * Determine if we're in compatibility mode with the previous versions of the
 * library - where the idle timeout is disabled and a perpetual streaming
 * connection will always remain open (regardless of whether it was triggered
 * by start_refresh/get_refresh).
 */
static int is_v220_compat(http_provider *http)
{
    lcb_uint32_t setting =  PROVIDER_SETTING(&http->base, bc_http_stream_time);
    if (setting == (lcb_uint32_t)-1) {
        return 1;
    }
    return 0;
}
static void socket_connected(connmgr_request *req)
{
    cccp_provider *cccp = req->data;
    lcb_connection_t conn = req->conn;
    struct lcb_nibufs_st nistrs;
    struct lcb_io_use_st use;
    struct negotiation_context *ctx;
    lcb_error_t err;

    free(req);
    cccp->cur_connreq = NULL;

    LOG(cccp, DEBUG, "CCCP Socket connected");

    if (!conn) {
        mcio_error(cccp, LCB_CONNECT_ERROR);
        return;
    }

    lcb_connuse_easy(&use, cccp, io_read_handler, io_error_handler);
    lcb_connection_transfer_socket(conn, &cccp->connection, &use);
    conn = NULL;


    if (cccp->connection.protoctx) {
        /** Already have SASL */
        if ((err = lcb_connection_reset_buffers(&cccp->connection)) != LCB_SUCCESS) {
            mcio_error(cccp, err);
            return;
        }

        request_config(cccp);
        return;
    }

    if (!lcb_get_nameinfo(&cccp->connection, &nistrs)) {
        mcio_error(cccp, LCB_EINTERNAL);
        return;
    }

    ctx = lcb_negotiation_create(&cccp->connection,
                                 cccp->base.parent->settings,
                                 PROVIDER_SETTING(&cccp->base,
                                         config_node_timeout),
                                 nistrs.remote,
                                 nistrs.local,
                                 &err);
    if (!ctx) {
        mcio_error(cccp, err);
    }

    ctx->complete = negotiation_done;
    ctx->data = cccp;
    cccp->connection.protoctx = ctx;
    cccp->connection.protoctx_dtor = (protoctx_dtor_t)lcb_negotiation_destroy;
}
static lcb_error_t pause_http(clconfig_provider *pb)
{
    http_provider *http = (http_provider *)pb;
    if (is_v220_compat(http)) {
        return LCB_SUCCESS;
    }

    if (!lcb_timer_armed(http->disconn_timer)) {
        lcb_timer_rearm(http->disconn_timer,
                        PROVIDER_SETTING(pb, bc_http_stream_time));
    }
    return LCB_SUCCESS;
}
Exemple #11
0
void lcb_clconfig_cccp_set_nodes(clconfig_provider *pb, const hostlist_t nodes)
{
    unsigned ii;
    cccp_provider *cccp = (cccp_provider *)pb;
    hostlist_clear(cccp->nodes);

    for (ii = 0; ii < nodes->nentries; ii++) {
        hostlist_add_host(cccp->nodes, nodes->entries + ii);
    }
    if (PROVIDER_SETTING(pb, randomize_bootstrap_nodes)) {
        hostlist_randomize(cccp->nodes);
    }
}
Exemple #12
0
static lcb_error_t pause_http(clconfig_provider *pb)
{
    http_provider *http = (http_provider *)pb;
    if (http->always_on) {
        lcb_timer_disarm(http->io_timer);
        return LCB_SUCCESS;
    }

    if (!lcb_timer_armed(http->disconn_timer)) {
        lcb_timer_rearm(http->disconn_timer,
                        PROVIDER_SETTING(pb, bc_http_stream_time));
    }
    return LCB_SUCCESS;
}
Exemple #13
0
/**
 * Call when there is an error in I/O. This includes read, write, connect
 * and timeouts.
 */
static lcb_error_t io_error(http_provider *http)
{
    lcb_error_t err;
    lcb_conn_params params;
    char *errinfo;

    close_current(http);

    params.timeout = PROVIDER_SETTING(&http->base, config_node_timeout);
    params.handler = connect_done_handler;
    err = lcb_connection_next_node(&http->connection,
                                   http->nodes, &params, &errinfo);

    if (err != LCB_SUCCESS) {
        lcb_confmon_provider_failed(&http->base, err);
        lcb_timer_disarm(http->io_timer);
        return err;
    } else {
        setup_request_header(http);
    }
    return LCB_SUCCESS;
}
/**
 * Call when there is an error in I/O. This includes read, write, connect
 * and timeouts.
 */
static lcb_error_t io_error(http_provider *http, lcb_error_t origerr)
{
    lcb_error_t err;
    lcb_conn_params params;
    char *errinfo;
    int can_retry = 0;

    close_current(http);

    params.timeout = PROVIDER_SETTING(&http->base, config_node_timeout);
    params.handler = connect_done_handler;

    if (http->base.parent->config) {
        can_retry = 1;
    } else if (origerr != LCB_AUTH_ERROR && origerr != LCB_BUCKET_ENOENT) {
        can_retry = 1;
    }
    if (can_retry) {
        err = lcb_connection_next_node(
                &http->connection, http->nodes, &params, &errinfo);
    } else {
        err = origerr;
    }

    if (err != LCB_SUCCESS) {
        lcb_confmon_provider_failed(&http->base, origerr);
        lcb_timer_disarm(http->io_timer);
        if (is_v220_compat(http)) {
            lcb_log(LOGARGS(http, INFO),
                    "HTTP node list finished. Looping again (disconn_tmo=-1)");
            connect_next(http);
        }
        return origerr;
    } else {
        setup_request_header(http);
    }
    return LCB_SUCCESS;
}
Exemple #15
0
static void nodes_updated(clconfig_provider *provider, hostlist_t nodes,
                          VBUCKET_CONFIG_HANDLE vbc)
{
    int ii;
    cccp_provider *cccp = (cccp_provider *)provider;
    if (!vbc) {
        return;
    }
    if (vbucket_config_get_num_servers(vbc) < 1) {
        return;
    }

    hostlist_clear(cccp->nodes);
    for (ii = 0; ii < vbucket_config_get_num_servers(vbc); ii++) {
        const char *mcaddr = vbucket_config_get_server(vbc, ii);
        hostlist_add_stringz(cccp->nodes, mcaddr, LCB_CONFIG_MCD_PORT);
    }

    if (PROVIDER_SETTING(provider, randomize_bootstrap_nodes)) {
        hostlist_randomize(cccp->nodes);
    }

    (void)nodes;
}
Exemple #16
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;
}