static void destroy_cursock(lcbio_CONNSTART *cs) { lcbio_SOCKET *s = cs->sock; lcbio_TABLE *iot = s->io; if (cs->ai) { cs->ai = cs->ai->ai_next; } if (!cs->ai) { return; } if (IOT_IS_EVENT(iot)) { if (cs->ev_active) { lcb_assert(s->u.fd != INVALID_SOCKET); IOT_V0EV(iot).cancel(IOT_ARG(iot), s->u.fd, cs->event); cs->ev_active = 0; } IOT_V0IO(iot).close(IOT_ARG(iot), s->u.fd); s->u.fd = INVALID_SOCKET; } else { if (s->u.sd) { IOT_V1(iot).close(IOT_ARG(iot), s->u.sd); s->u.sd = NULL; } } }
lcbio_CTX * lcbio_ctx_new(lcbio_SOCKET *sock, void *data, const lcbio_EASYPROCS *procs) { lcbio_CTX *ctx = calloc(1, sizeof(*ctx)); ctx->sock = sock; sock->ctx = ctx; ctx->io = sock->io; ctx->data = data; ctx->procs = *procs; ctx->state = ES_ACTIVE; ctx->as_err = lcbio_timer_new(ctx->io, ctx, err_handler); ctx->subsys = "unknown"; rdb_init(&ctx->ior, sock->settings->allocator_factory()); lcbio_ref(sock); if (IOT_IS_EVENT(ctx->io)) { ctx->event = IOT_V0EV(ctx->io).create(IOT_ARG(ctx->io)); ctx->fd = sock->u.fd; } else { ctx->sd = sock->u.sd; } ctx->procs = *procs; ctx->state = ES_ACTIVE; lcb_log(LOGARGS(ctx, DEBUG), CTX_LOGFMT "Pairing with SOCK=%p", CTX_LOGID(ctx), (void*)sock); return ctx; }
void lcbio_ctx_close_ex(lcbio_CTX *ctx, lcbio_CTXCLOSE_cb cb, void *arg, lcbio_CTXDTOR_cb dtor, void *dtor_arg) { unsigned oldrc; ctx->state = ES_DETACHED; assert(ctx->sock); if (ctx->event) { deactivate_watcher(ctx); IOT_V0EV(CTX_IOT(ctx)).destroy(IOT_ARG(CTX_IOT(ctx)), ctx->event); ctx->event = NULL; } if (ctx->as_err) { lcbio_timer_destroy(ctx->as_err); ctx->as_err = NULL; } oldrc = ctx->sock->refcount; lcb_log(LOGARGS(ctx, DEBUG), CTX_LOGFMT "Destroying. PND=%d,ENT=%d,SORC=%d", CTX_LOGID(ctx), (int)ctx->npending, (int)ctx->entered, oldrc); if (cb) { int reusable = ctx->npending == 0 && /* no pending events */ ctx->err == LCB_SUCCESS && /* no socket errors */ ctx->rdwant == 0 && /* no expected input */ ctx->wwant == 0 && /* no expected output */ (ctx->output == NULL || ctx->output->rb.nbytes == 0); cb(ctx->sock, reusable, arg); } if (oldrc == ctx->sock->refcount) { lcbio_shutdown(ctx->sock); } if (ctx->output) { ringbuffer_destruct(&ctx->output->rb); free(ctx->output); ctx->output = NULL; } ctx->fd = INVALID_SOCKET; ctx->sd = NULL; if (dtor) { ctx->data = dtor_arg; ctx->procs.cb_flush_ready = dtor; } else { ctx->procs.cb_flush_ready = NULL; } if (ctx->npending == 0 && ctx->entered == 0) { free_ctx(ctx); } }
static void deactivate_watcher(lcbio_CTX *ctx) { if (ctx->evactive && ctx->event) { IOT_V0EV(CTX_IOT(ctx)).cancel( IOT_ARG(CTX_IOT(ctx)), CTX_FD(ctx), ctx->event); ctx->evactive = 0; } }
/** * Handler invoked to deliver final status for a connection. This will invoke * the user supplied callback with the relevant status (if it has not been * cancelled) and then free the CONNSTART object. */ static void cs_handler(void *cookie) { lcbio_CONNSTART *cs = cookie; lcb_error_t err; lcbio_SOCKET *s = cs->sock; if (s && cs->event) { cs_unwatch(cs); IOT_V0EV(s->io).destroy(IOT_ARG(s->io), cs->event); } if (cs->state == CS_PENDING) { /* state was not changed since initial scheduling */ err = LCB_ETIMEDOUT; } else if (cs->state == CS_CONNECTED) { /* clear pending error */ err = LCB_SUCCESS; } else { if (s != NULL && cs->pending == LCB_CONNECT_ERROR) { err = lcbio_mklcberr(cs->syserr, s->settings); } else { err = cs->pending; } } if (cs->state == CS_CANCELLED) { /* ignore everything. Clean up resources */ goto GT_DTOR; } if (s) { lcbio__load_socknames(s); if (err == LCB_SUCCESS) { lcb_log(LOGARGS(s, INFO), CSLOGFMT "Connected ", CSLOGID(s)); } else { lcb_log(LOGARGS(s, ERR), CSLOGFMT "Failed: lcb_err=0x%x, os_errno=%u", CSLOGID(s), err, cs->syserr); } } /** Handler section */ cs->in_uhandler = 1; cs->handler(err == LCB_SUCCESS ? s : NULL, cs->arg, err, cs->syserr); GT_DTOR: if (cs->async) { lcbio_timer_destroy(cs->async); } if (cs->sock) { lcbio_unref(cs->sock); } if (cs->ai_root) { freeaddrinfo(cs->ai_root); } free(cs); }
static void cs_unwatch(lcbio_CONNSTART *cs) { lcbio_SOCKET *s = cs->sock; if (s && cs->ev_active) { lcb_assert(s->u.fd != INVALID_SOCKET); IOT_V0EV(s->io).cancel(IOT_ARG(s->io), s->u.fd, cs->event); cs->ev_active = 0; } }
static void E_schedule(lcbio_CTX *ctx) { lcbio_TABLE *io = ctx->io; short which = 0; if (ctx->rdwant) { which |= LCB_READ_EVENT; } if (ctx->wwant || (ctx->output && ctx->output->rb.nbytes)) { which |= LCB_WRITE_EVENT; } if (!which) { deactivate_watcher(ctx); return; } IOT_V0EV(io).watch(IOT_ARG(io), CTX_FD(ctx), ctx->event, which, ctx, E_handler); ctx->evactive = 1; }
struct lcbio_CONNSTART * lcbio_connect(lcbio_TABLE *iot, lcb_settings *settings, lcb_host_t *dest, uint32_t timeout, lcbio_CONNDONE_cb handler, void *arg) { lcbio_SOCKET *s; lcbio_CONNSTART *ret; struct addrinfo hints; int rv; s = calloc(1, sizeof(*s)); ret = calloc(1, sizeof(*ret)); /** Initialize the socket first */ s->io = iot; s->settings = settings; s->ctx = ret; s->refcount = 1; s->info = calloc(1, sizeof(*s->info)); s->info->ep = *dest; lcbio_table_ref(s->io); lcb_settings_ref(s->settings); lcb_list_init(&s->protos); if (IOT_IS_EVENT(iot)) { s->u.fd = INVALID_SOCKET; ret->event = IOT_V0EV(iot).create(IOT_ARG(iot)); } /** Initialize the connstart structure */ ret->handler = handler; ret->arg = arg; ret->sock = s; ret->async = lcbio_timer_new(iot, ret, cs_handler); lcbio_timer_rearm(ret->async, timeout); lcb_log(LOGARGS(s, INFO), CSLOGFMT "Starting. Timeout=%uus", CSLOGID(s), timeout); /** Hostname lookup: */ memset(&hints, 0, sizeof(hints)); hints.ai_flags = AI_PASSIVE; hints.ai_socktype = SOCK_STREAM; if (settings->ipv6 == LCB_IPV6_DISABLED) { hints.ai_family = AF_INET; } else if (settings->ipv6 == LCB_IPV6_ONLY) { hints.ai_family = AF_INET6; } else { hints.ai_family = AF_UNSPEC; } if ((rv = getaddrinfo(dest->host, dest->port, &hints, &ret->ai_root))) { const char *errstr = rv != EAI_SYSTEM ? gai_strerror(rv) : ""; lcb_log(LOGARGS(s, ERR), CSLOGFMT "Couldn't look up %s (%s) [EAI=%d]", CSLOGID(s), dest->host, errstr, rv); cs_state_signal(ret, CS_ERROR, LCB_UNKNOWN_HOST); } else { ret->ai = ret->ai_root; /** Figure out how to connect */ if (IOT_IS_EVENT(iot)) { E_connect(-1, LCB_WRITE_EVENT, ret); } else { C_connect(ret); } } return ret; }
static void E_connect(lcb_socket_t sock, short events, void *arg) { lcbio_CONNSTART *cs = arg; lcbio_SOCKET *s = cs->sock; lcbio_TABLE *io = s->io; int retry_once = 0; lcbio_CSERR connstatus; (void)sock; lcb_log(LOGARGS(s, TRACE), CSLOGFMT "Got event handler for new connection", CSLOGID(s)); GT_NEXTSOCK: if (ensure_sock(cs) == -1) { cs_state_signal(cs, CS_ERROR, LCB_CONNECT_ERROR); return; } if (events & LCB_ERROR_EVENT) { socklen_t errlen = sizeof(int); int sockerr = 0; lcb_log(LOGARGS(s, TRACE), CSLOGFMT "Received ERROR_EVENT", CSLOGID(s)); getsockopt(s->u.fd, SOL_SOCKET, SO_ERROR, (char *)&sockerr, &errlen); lcbio_mksyserr(sockerr, &cs->syserr); destroy_cursock(cs); goto GT_NEXTSOCK; } else { int rv = 0; struct addrinfo *ai = cs->ai; GT_CONNECT: rv = IOT_V0IO(io).connect0( IOT_ARG(io), s->u.fd, ai->ai_addr, (unsigned)ai->ai_addrlen); if (rv == 0) { cs_unwatch(cs); cs_state_signal(cs, CS_CONNECTED, LCB_SUCCESS); return; } } connstatus = lcbio_mkcserr(IOT_ERRNO(io)); lcbio_mksyserr(IOT_ERRNO(io), &cs->syserr); switch (connstatus) { case LCBIO_CSERR_INTR: goto GT_CONNECT; case LCBIO_CSERR_CONNECTED: cs_unwatch(cs); cs_state_signal(cs, CS_CONNECTED, LCB_SUCCESS); return; case LCBIO_CSERR_BUSY: lcb_log(LOGARGS(s, TRACE), CSLOGFMT "Scheduling asynchronous watch for socket.", CSLOGID(s)); IOT_V0EV(io).watch( IOT_ARG(io), s->u.fd, cs->event, LCB_WRITE_EVENT, cs, E_connect); cs->ev_active = 1; return; case LCBIO_CSERR_EINVAL: if (!retry_once) { retry_once = 1; goto GT_CONNECT; } /* fallthrough */ case LCBIO_CSERR_EFAIL: default: /* close the current socket and try again */ lcb_log(LOGARGS(s, TRACE), CSLOGFMT "connect() failed. os_error=%d [%s]", CSLOGID(s), IOT_ERRNO(io), strerror(IOT_ERRNO(io))); destroy_cursock(cs); goto GT_NEXTSOCK; } }