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; } } }
int lcbio_is_netclosed(lcbio_SOCKET *sock, int flags) { lcbio_pTABLE iot = sock->io; if (IOT_IS_EVENT(iot)) { return IOT_V0IO(iot).is_closed(IOT_ARG(iot), sock->u.fd, flags); } else { return IOT_V1(iot).is_closed(IOT_ARG(iot), sock->u.sd, flags); } }
static void C_schedule(lcbio_CTX *ctx) { lcbio_TABLE *io = ctx->io; lcb_sockdata_t *sd = CTX_SD(ctx); int rv; if (ctx->output && ctx->output->rb.nbytes) { /** Schedule a write */ lcb_IOV iov[2]; unsigned niov; ringbuffer_get_iov(&ctx->output->rb, RINGBUFFER_READ, iov); niov = iov[1].iov_len ? 2 : 1; rv = IOT_V1(io).write2(IOT_ARG(io), sd, iov, niov, ctx->output, Cw_handler); if (rv) { lcbio_ctx_senderr(ctx, convert_lcberr(ctx, LCBIO_IOERR)); return; } else { ctx->output = NULL; ctx->npending++; } } if (ctx->wwant) { ctx->wwant = 0; ctx->procs.cb_flush_ready(ctx); } if (ctx->rdwant && sd->is_reading == 0) { lcb_IOV iov[RWINL_IOVSIZE]; unsigned ii; unsigned niov = rdb_rdstart(&ctx->ior, (nb_IOV *)iov, RWINL_IOVSIZE); assert(niov); for (ii = 0; ii < niov; ++ii) { assert(iov[ii].iov_len); } rv = IOT_V1(io).read2(IOT_ARG(io), sd, iov, niov, ctx, Cr_handler); if (rv) { lcbio_ctx_senderr(ctx, convert_lcberr(ctx, LCBIO_IOERR)); } else { sd->is_reading = 1; ctx->npending++; } } }
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_shutdown(lcbio_SOCKET *s) { lcbio_TABLE *io = s->io; lcbio__protoctx_delall(s); if (IOT_IS_EVENT(io)) { if (s->u.fd != INVALID_SOCKET) { IOT_V0IO(io).close(IOT_ARG(io), s->u.fd); s->u.fd = INVALID_SOCKET; } } else { if (s->u.sd) { IOT_V1(io).close(IOT_ARG(io), s->u.sd); s->u.sd = NULL; } } }
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; } }
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); } }
/** * 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; } }
LIBCOUCHBASE_API lcb_error_t lcb_tick_nowait(lcb_t instance) { lcb_io_tick_fn tick = instance->iotable->loop.tick; if (!tick) { return LCB_CLIENT_FEATURE_UNAVAILABLE; } else { maybe_reset_timeouts(instance); tick(IOT_ARG(instance->iotable)); return LCB_SUCCESS; } }
void lcb_maybe_breakout(lcb_t instance) { if (!instance->wait) { return; } if (has_pending(instance)) { return; } instance->wait = 0; instance->iotable->loop.stop(IOT_ARG(instance->iotable)); }
static void C_connect(lcbio_CONNSTART *cs) { int rv; lcbio_SOCKET *s = cs->sock; int retry_once = 0; lcbio_CSERR status; lcbio_TABLE *io = s->io; GT_NEXTSOCK: if (ensure_sock(cs) != 0) { lcbio_mksyserr(IOT_ERRNO(io), &cs->syserr); cs_state_signal(cs, CS_ERROR, LCB_CONNECT_ERROR); return; } GT_CONNECT: rv = IOT_V1(io).connect(IOT_ARG(io), s->u.sd, cs->ai->ai_addr, (unsigned)cs->ai->ai_addrlen, C_conncb); if (rv == 0) { lcbio_ref(s); return; } lcbio_mksyserr(IOT_ERRNO(io), &cs->syserr); status = lcbio_mkcserr(IOT_ERRNO(io)); switch (status) { case LCBIO_CSERR_INTR: goto GT_CONNECT; case LCBIO_CSERR_CONNECTED: cs_state_signal(cs, CS_CONNECTED, LCB_SUCCESS); return; case LCBIO_CSERR_BUSY: return; case LCBIO_CSERR_EINVAL: if (!retry_once) { retry_once = 1; goto GT_CONNECT; } /* fallthrough */ case LCBIO_CSERR_EFAIL: default: destroy_cursock(cs); goto GT_NEXTSOCK; } }
lcb_sockdata_t * lcbio_C_ai2sock(lcbio_TABLE *io, struct addrinfo **ai, int *connerr) { lcb_sockdata_t *ret = NULL; for (; *ai; *ai = (*ai)->ai_next) { ret = IOT_V1(io).socket( IOT_ARG(io), (*ai)->ai_family, (*ai)->ai_socktype, (*ai)->ai_protocol); if (ret) { return ret; } else { *connerr = IOT_ERRNO(io); } } return ret; }
static int C_put_ex(lcbio_CTX *ctx, lcb_IOV *iov, unsigned niov, unsigned nb) { lcbio_TABLE *iot = ctx->io; lcb_sockdata_t *sd = CTX_SD(ctx); int status = IOT_V1(iot).write2(IOT_ARG(iot), sd, iov, niov, (void *)(uintptr_t)nb, Cw_ex_handler); if (status) { /** error! */ lcbio_OSERR saverr = IOT_ERRNO(iot); ctx->procs.cb_flush_done(ctx, nb, nb); lcbio_ctx_senderr(ctx, lcbio_mklcberr(saverr, ctx->sock->settings)); return 0; } else { ctx->npending++; return 1; } }
static int ensure_sock(lcbio_CONNSTART *cs) { lcbio_SOCKET *s = cs->sock; lcbio_TABLE *io = s->io; int errtmp = 0; if (cs->ai == NULL) { return -1; } if (IOT_IS_EVENT(io)) { if (s->u.fd != INVALID_SOCKET) { /* already have one? */ return 0; } while (s->u.fd == INVALID_SOCKET && cs->ai != NULL) { s->u.fd = lcbio_E_ai2sock(io, &cs->ai, &errtmp); if (s->u.fd != INVALID_SOCKET) { return 0; } } } else { if (s->u.sd) { return 0; } while (s->u.sd == NULL && cs->ai != NULL) { s->u.sd = lcbio_C_ai2sock(io, &cs->ai, &errtmp); if (s->u.sd) { s->u.sd->lcbconn = (void *) cs->sock; s->u.sd->parent = IOT_ARG(io); return 0; } } } if (cs->ai == NULL) { lcbio_mksyserr(IOT_ERRNO(io), &cs->syserr); return -1; } return 0; }
void lcbio__load_socknames(lcbio_SOCKET *sock) { int n_salocal, n_saremote, rv; struct lcb_nameinfo_st ni; lcbio_CONNINFO *info = sock->info; n_salocal = sizeof(info->sa_local); n_saremote = sizeof(info->sa_remote); ni.local.name = (struct sockaddr *)&info->sa_local; ni.local.len = &n_salocal; ni.remote.name = (struct sockaddr *)&info->sa_remote; ni.remote.len = &n_saremote; if (!IOT_IS_EVENT(sock->io)) { if (!sock->u.sd) { return; } rv = IOT_V1(sock->io).nameinfo(IOT_ARG(sock->io), sock->u.sd, &ni); if (ni.local.len == 0 || ni.remote.len == 0 || rv < 0) { return; } } else { socklen_t sl_tmp = sizeof(info->sa_local); if (sock->u.fd == INVALID_SOCKET) { return; } rv = getsockname(sock->u.fd, ni.local.name, &sl_tmp); n_salocal = sl_tmp; if (rv < 0) { return; } rv = getpeername(sock->u.fd, ni.remote.name, &sl_tmp); n_saremote = sl_tmp; if (rv < 0) { return; } } info->naddr = n_salocal; }
/** Extended function used for write-on-callback mode */ static int E_put_ex(lcbio_CTX *ctx, lcb_IOV *iov, unsigned niov, unsigned nb) { lcb_ssize_t nw; lcbio_TABLE *iot = ctx->io; lcb_socket_t fd = CTX_FD(ctx); GT_WRITE_AGAIN: nw = IOT_V0IO(iot).sendv(IOT_ARG(iot), fd, iov, niov); if (nw > 0) { ctx->procs.cb_flush_done(ctx, nb, nw); return 1; } else if (nw == -1) { switch (IOT_ERRNO(iot)) { case EINTR: /* jump back to retry */ goto GT_WRITE_AGAIN; case C_EAGAIN: case EWOULDBLOCK: nw = 0; /* indicate zero bytes were written, but don't send an error */ goto GT_WRITE0; default: /* pretend all the bytes were written and deliver an error during * the next event loop iteration. */ nw = nb; lcbio_ctx_senderr(ctx, convert_lcberr(ctx, LCBIO_IOERR)); goto GT_WRITE0; } } else { /* connection closed. pretend everything was written and send an error */ nw = nb; lcbio_ctx_senderr(ctx, convert_lcberr(ctx, LCBIO_SHUTDOWN)); goto GT_WRITE0; } GT_WRITE0: ctx->procs.cb_flush_done(ctx, nb, nw); return 0; }
lcb_socket_t lcbio_E_ai2sock(lcbio_TABLE *io, struct addrinfo **ai, int *connerr) { lcb_socket_t ret = INVALID_SOCKET; *connerr = 0; for (; *ai; *ai = (*ai)->ai_next) { ret = IOT_V0IO(io).socket0( IOT_ARG(io), (*ai)->ai_family, (*ai)->ai_socktype, (*ai)->ai_protocol); if (ret != INVALID_SOCKET) { return ret; } else { *connerr = IOT_ERRNO(io); } } return ret; }
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; }
static void destroy_timer(lcb_io_opt_t io, void *timer) { lcbio_XSSL *xs = IOTSSL_FROM_IOPS(io); xs->orig->timer.destroy(IOT_ARG(xs->orig), timer); }
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 *create_timer(lcb_io_opt_t io) { lcbio_XSSL *xs = IOTSSL_FROM_IOPS(io); return xs->orig->timer.create(IOT_ARG(xs->orig)); }
static int schedule_timer(lcb_io_opt_t io, void *timer, lcb_uint32_t us, void *arg, lcb_ioE_callback callback) { lcbio_XSSL *xs = IOTSSL_FROM_IOPS(io); return xs->orig->timer.schedule(IOT_ARG(xs->orig), timer, us, arg, callback); }
static int Cis_closed(lcb_io_opt_t io, lcb_sockdata_t *sd, int flags) { lcbio_XSSL *xs = IOTSSL_FROM_IOPS(io); return xs->orig->u_io.completion.is_closed(IOT_ARG(xs->orig), sd, flags); }
static int Eis_closed(lcb_io_opt_t io, lcb_socket_t sock, int flags) { lcbio_XSSL *xs = IOTSSL_FROM_IOPS(io); return xs->orig->u_io.v0.io.is_closed(IOT_ARG(xs->orig), sock, flags); }
static void cancel_timer(lcb_io_opt_t io, void *timer) { lcbio_XSSL *xs = IOTSSL_FROM_IOPS(io); xs->orig->timer.cancel(IOT_ARG(xs->orig), timer); }
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; } }