static void * exp_timer(struct sess *sp, void *priv) { struct objcore *oc; struct object *o; double t; struct objcore_head *lru; (void)priv; AZ(sleep(10)); /* XXX: Takes time for VCL to arrive */ VCL_Get(&sp->vcl); t = TIM_real(); while (1) { Lck_Lock(&exp_mtx); oc = binheap_root(exp_heap); CHECK_OBJ_ORNULL(oc, OBJCORE_MAGIC); if (oc == NULL || oc->timer_when > t) { /* XXX: > or >= ? */ Lck_Unlock(&exp_mtx); WSL_Flush(sp->wrk, 0); WRK_SumStat(sp->wrk); AZ(sleep(1)); VCL_Refresh(&sp->vcl); t = TIM_real(); continue; } o = oc->obj; CHECK_OBJ_NOTNULL(o, OBJECT_MAGIC); CHECK_OBJ_NOTNULL(o->objhead, OBJHEAD_MAGIC); assert(oc->flags & OC_F_ONLRU); assert(oc->timer_idx != BINHEAP_NOIDX); binheap_delete(exp_heap, oc->timer_idx); assert(oc->timer_idx == BINHEAP_NOIDX); lru = STV_lru(o->objstore); AN(lru); VTAILQ_REMOVE(lru, o->objcore, lru_list); oc->flags &= ~OC_F_ONLRU; { /* Sanity checking */ struct objcore *oc2 = binheap_root(exp_heap); if (oc2 != NULL) { assert(oc2->timer_idx != BINHEAP_NOIDX); assert(oc2->timer_when >= oc->timer_when); } } VSL_stats->n_expired++; Lck_Unlock(&exp_mtx); WSL(sp->wrk, SLT_ExpKill, 0, "%u %d", o->xid, (int)(o->ttl - t)); HSH_Deref(sp->wrk, &o); } }
double VRT_r_now(const struct sess *sp) { (void)sp; return (TIM_real()); }
static void * vca_kqueue_main(void *arg) { struct kevent ke[NKEV], *kp; int j, n, dotimer; double deadline; struct sess *sp; THR_SetName("cache-kqueue"); (void)arg; kq = kqueue(); assert(kq >= 0); j = 0; EV_SET(&ke[j], 0, EVFILT_TIMER, EV_ADD, 0, 100, NULL); j++; EV_SET(&ke[j], vca_pipes[0], EVFILT_READ, EV_ADD, 0, 0, vca_pipes); j++; AZ(kevent(kq, ke, j, NULL, 0, NULL)); nki = 0; while (1) { dotimer = 0; n = kevent(kq, ki, nki, ke, NKEV, NULL); assert(n >= 1 && n <= NKEV); nki = 0; for (kp = ke, j = 0; j < n; j++, kp++) { if (kp->filter == EVFILT_TIMER) { dotimer = 1; continue; } assert(kp->filter == EVFILT_READ); vca_kev(kp); } if (!dotimer) continue; /* * Make sure we have no pending changes for the fd's * we are about to close, in case the accept(2) in the * other thread creates new fd's betwen our close and * the kevent(2) at the top of this loop, the kernel * would not know we meant "the old fd of this number". */ vca_kq_flush(); deadline = TIM_real() - params->sess_timeout; for (;;) { sp = VTAILQ_FIRST(&sesshead); if (sp == NULL) break; if (sp->t_open > deadline) break; VTAILQ_REMOVE(&sesshead, sp, list); // XXX: not yet (void)TCP_linger(sp->fd, 0); vca_close_session(sp, "timeout"); SES_Delete(sp); } } }
static int cnt_error(struct sess *sp) { struct worker *w; struct http *h; char date[40]; CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); w = sp->wrk; if (sp->obj == NULL) { HSH_Prealloc(sp); sp->obj = HSH_NewObject(sp, 1); sp->obj->xid = sp->xid; sp->obj->entered = sp->t_req; } else { /* XXX: Null the headers ? */ } CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC); h = sp->obj->http; http_PutProtocol(w, sp->fd, h, "HTTP/1.1"); http_PutStatus(w, sp->fd, h, sp->err_code); TIM_format(TIM_real(), date); http_PrintfHeader(w, sp->fd, h, "Date: %s", date); http_PrintfHeader(w, sp->fd, h, "Server: Varnish"); http_PrintfHeader(w, sp->fd, h, "Retry-After: %d", params->err_ttl); if (sp->err_reason != NULL) http_PutResponse(w, sp->fd, h, sp->err_reason); else http_PutResponse(w, sp->fd, h, http_StatusMessage(sp->err_code)); VCL_error_method(sp); if (sp->handling == VCL_RET_RESTART) { HSH_Drop(sp); sp->director = NULL; sp->restarts++; sp->step = STP_RECV; return (0); } /* We always close when we take this path */ sp->doclose = "error"; sp->wantbody = 1; assert(sp->handling == VCL_RET_DELIVER); sp->err_code = 0; sp->err_reason = NULL; sp->wrk->bereq = NULL; sp->step = STP_DELIVER; return (0); }
static void tst_delta() { double m_begin, m_end; double r_begin, r_end; const double ref = 1; int err = 0; r_begin = TIM_real(); m_begin = TIM_mono(); TIM_sleep(ref); r_end = TIM_real(); m_end = TIM_mono(); err += tst_delta_check("TIM_mono", m_begin, m_end, ref); err += tst_delta_check("TIM_real", r_begin, r_end, ref); if (err) { printf("%d time delta test errrors\n", err); exit (2); } }
static void * vca_main(void *arg) { struct epoll_event ev[NEEV], *ep; struct sess *sp; char junk; double deadline; int dotimer, i, n; THR_SetName("cache-epoll"); (void)arg; epfd = epoll_create(1); assert(epfd >= 0); vca_modadd(vca_pipes[0], vca_pipes, EPOLL_CTL_ADD); vca_modadd(dotimer_pipe[0], dotimer_pipe, EPOLL_CTL_ADD); while (1) { dotimer = 0; n = epoll_wait(epfd, ev, NEEV, -1); for (ep = ev, i = 0; i < n; i++, ep++) { if (ep->data.ptr == dotimer_pipe && (ep->events == EPOLLIN || ep->events == EPOLLPRI)) { assert(read(dotimer_pipe[0], &junk, 1)); dotimer = 1; } else vca_eev(ep); } if (!dotimer) continue; /* check for timeouts */ deadline = TIM_real() - params->sess_timeout; for (;;) { sp = VTAILQ_FIRST(&sesshead); if (sp == NULL) break; if (sp->t_open > deadline) break; VTAILQ_REMOVE(&sesshead, sp, list); // XXX: not yet VTCP_linger(sp->fd, 0); vca_close_session(sp, "timeout"); SES_Delete(sp); } } return NULL; }
static enum pipe_status pie_first(struct pipe *dp) { struct sess *sp; struct vbe_conn *vc; CHECK_OBJ_NOTNULL(dp, PIPE_MAGIC); CAST_OBJ_NOTNULL(sp, dp->sess, SESS_MAGIC); CAST_OBJ_NOTNULL(vc, sp->vc, VBE_CONN_MAGIC); dp->t_last = TIM_real(); dp->t_updated = dp->t_last; TCP_hisname(vc->vc_fd, dp->addr, sizeof(dp->addr), dp->port, sizeof(dp->port)); dp->step = PIE_RECV; return (PIPE_CONTINUE); }
static int cnt_deliver(struct sess *sp) { CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); CHECK_OBJ_NOTNULL(sp->obj, OBJECT_MAGIC); CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC); sp->t_resp = TIM_real(); if (sp->obj->objhead != NULL) { if ((sp->t_resp - sp->obj->last_lru) > params->lru_timeout && EXP_Touch(sp->obj)) sp->obj->last_lru = sp->t_resp; /* XXX: locking ? */ sp->obj->last_use = sp->t_resp; /* XXX: locking ? */ } sp->wrk->resp = &sp->wrk->http[2]; http_Setup(sp->wrk->resp, sp->wrk->ws); RES_BuildHttp(sp); VCL_deliver_method(sp); switch (sp->handling) { case VCL_RET_DELIVER: break; case VCL_RET_RESTART: INCOMPL(); break; default: WRONG("Illegal action in vcl_deliver{}"); } sp->director = NULL; sp->restarts = 0; RES_WriteObj(sp); AZ(sp->wrk->wfd); HSH_Deref(sp->wrk, &sp->obj); sp->wrk->resp = NULL; sp->step = STP_DONE; return (0); }
static enum pipe_status pie_recv_frombackend(struct pipe *dp) { struct sess *sp; struct vbe_conn *vc; int i; CAST_OBJ_NOTNULL(sp, dp->sess, SESS_MAGIC); CAST_OBJ_NOTNULL(vc, sp->vc, VBE_CONN_MAGIC); i = CFD_read(&vc->fds, dp->buf[1], dp->bufsize); if (i == -2) { SEPTUM_PIPEEVENT(dp, vc->vc_fd, vc->vc_want, CALLOUT_SECTOTICKS(params->pipe_timeout)); return (PIPE_WAIT); } if (i <= 0) { if (i == -1) WSP(sp, SLT_Error, "%s: read(2) %d %s", __func__, errno, strerror(errno)); else WSP(sp, SLT_Error, "%s: read(2) eof", __func__); dp->flags |= PIPE_F_PIPEDONE; if ((dp->flags & PIPE_F_SESSDONE) != 0) { dp->step = PIE_END; return (PIPE_CONTINUE); } (void)shutdown(vc->vc_fd, SHUT_RD); /* XXX */ (void)shutdown(sp->sp_fd, SHUT_WR); /* XXX */ dp->step = PIE_END; return (PIPE_CONTINUE); } assert(i > 0); dp->buflen[1] = i; dp->t_updated = TIM_real(); dp->step = PIE_SEND; return (PIPE_CONTINUE); }
static enum pipe_status pie_timeout(struct pipe *dp) { struct sess *sp; struct vbe_conn *vc; double now; CAST_OBJ_NOTNULL(sp, dp->sess, SESS_MAGIC); CAST_OBJ_NOTNULL(vc, sp->vc, VBE_CONN_MAGIC); /* Need to handle a edge case if only one direction is used. */ switch (dp->prevstep) { case PIE_RECV_FROMBACKEND: now = TIM_real(); if ((now - dp->t_updated) < (double)params->pipe_timeout) { dp->step = dp->prevstep; return (PIPE_CONTINUE); } break; default: break; } sp->acct_tmp.sess_timeout++; if ((sp->flags & SESS_T_SOCKS) != 0) VSL_stats->socks_timeout++; dp->flags |= PIPE_F_PIPEDONE; if ((dp->flags & PIPE_F_SESSDONE) != 0) { dp->step = PIE_END; return (PIPE_CONTINUE); } (void)shutdown(vc->vc_fd, SHUT_RD); /* XXX */ (void)shutdown(sp->sp_fd, SHUT_WR); /* XXX */ dp->step = PIE_END; return (PIPE_CONTINUE); }
static void * vca_main(void *arg) { struct sess *sp; /* * timeouts: * * min_ts : Minimum timeout for port_getn * min_t : ^ equivalent in floating point representation * * max_ts : Maximum timeout for port_getn * max_t : ^ equivalent in floating point representation * * with (nevents == 1), we should always choose the correct port_getn * timeout to check session timeouts, so max is just a safety measure * (if this implementation is correct, it could be set to an "infinte" * value) * * with (nevents > 1), min and max define the acceptable range for * - additional latency of keep-alive connections and * - additional tolerance for handling session timeouts * */ static struct timespec min_ts = {0L, 100L /*ms*/ * 1000L /*us*/ * 1000L /*ns*/}; static double min_t = 0.1; /* 100 ms*/ static struct timespec max_ts = {1L, 0L}; /* 1 second */ static double max_t = 1.0; /* 1 second */ struct timespec ts; struct timespec *timeout; (void)arg; solaris_dport = port_create(); assert(solaris_dport >= 0); timeout = &max_ts; while (1) { port_event_t ev[MAX_EVENTS]; int nevents, ei, ret; double now, deadline; /* * XXX Do we want to scale this up dynamically to increase * efficiency in high throughput situations? - would need to * start with one to keep latency low at any rate * * Note: when increasing nevents, we must lower min_ts * and max_ts */ nevents = 1; /* * see disucssion in * - https://issues.apache.org/bugzilla/show_bug.cgi?id=47645 * - http://mail.opensolaris.org/pipermail/networking-discuss/2009-August/011979.html * * comment from apr/poll/unix/port.c : * * This confusing API can return an event at the same time * that it reports EINTR or ETIME. * */ ret = port_getn(solaris_dport, ev, MAX_EVENTS, &nevents, timeout); if (ret < 0) assert((errno == EINTR) || (errno == ETIME)); for (ei=0; ei<nevents; ei++) { vca_port_ev(ev + ei); } /* check for timeouts */ now = TIM_real(); deadline = now - params->sess_timeout; /* * This loop assumes that the oldest sessions are always at the * beginning of the list (which is the case if we guarantee to * enqueue at the tail only * */ for (;;) { sp = VTAILQ_FIRST(&sesshead); if (sp == NULL) break; if (sp->t_open > deadline) { break; } VTAILQ_REMOVE(&sesshead, sp, list); if(sp->fd != -1) { vca_del(sp->fd); } vca_close_session(sp, "timeout"); SES_Delete(sp); } /* * Calculate the timeout for the next get_portn */ if (sp) { double tmo = (sp->t_open + params->sess_timeout) - now; /* we should have removed all sps whose timeout has passed */ assert(tmo > 0.0); if (tmo < min_t) { timeout = &min_ts; } else if (tmo > max_t) { timeout = &max_ts; } else { /* TIM_t2ts() ? see #630 */ ts.tv_sec = (int)floor(tmo); ts.tv_nsec = 1e9 * (tmo - ts.tv_sec); timeout = &ts; } } else { timeout = &max_ts; } } }
static int cnt_fetch(struct sess *sp) { int i, transient; struct http *hp, *hp2; char *b; unsigned handling; CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); CHECK_OBJ_NOTNULL(sp->vcl, VCL_CONF_MAGIC); AN(sp->director); AZ(sp->vbe); /* sp->wrk->http[0] is (still) bereq */ sp->wrk->beresp = &sp->wrk->http[1]; http_Setup(sp->wrk->beresp, sp->wrk->ws); i = FetchHdr(sp); /* * Save a copy before it might get mangled in VCL. When it comes to * dealing with the body, we want to see the unadultered headers. */ sp->wrk->beresp1 = &sp->wrk->http[2]; *sp->wrk->beresp1 = *sp->wrk->beresp; if (i) { if (sp->objhead) { CHECK_OBJ_NOTNULL(sp->objhead, OBJHEAD_MAGIC); CHECK_OBJ_NOTNULL(sp->objcore, OBJCORE_MAGIC); HSH_DerefObjCore(sp); } AZ(sp->obj); sp->wrk->bereq = NULL; sp->wrk->beresp = NULL; sp->wrk->beresp1 = NULL; sp->err_code = 503; sp->step = STP_ERROR; return (0); } sp->err_code = http_GetStatus(sp->wrk->beresp); /* * Initial cacheability determination per [RFC2616, 13.4] * We do not support ranges yet, so 206 is out. */ switch (sp->err_code) { case 200: /* OK */ case 203: /* Non-Authoritative Information */ case 300: /* Multiple Choices */ case 301: /* Moved Permanently */ case 302: /* Moved Temporarily */ case 410: /* Gone */ case 404: /* Not Found */ sp->wrk->cacheable = 1; break; default: sp->wrk->cacheable = 0; break; } sp->wrk->entered = TIM_real(); sp->wrk->age = 0; sp->wrk->ttl = RFC2616_Ttl(sp); if (sp->wrk->ttl == 0.) sp->wrk->cacheable = 0; sp->wrk->do_esi = 0; sp->wrk->grace = NAN; VCL_fetch_method(sp); /* * When we fetch the body, we may hit the LRU cleanup and that * will overwrite sp->handling, so we have to save our plans * here. */ handling = sp->handling; if (sp->objhead == NULL) transient = 1; else if (sp->handling == VCL_RET_DELIVER) transient = 0; else transient = 1; /* * XXX: If we have a Length: header, we should allocate the body * XXX: also. */ sp->obj = HSH_NewObject(sp, transient); if (sp->objhead != NULL) { CHECK_OBJ_NOTNULL(sp->objhead, OBJHEAD_MAGIC); CHECK_OBJ_NOTNULL(sp->objcore, OBJCORE_MAGIC); sp->objcore->obj = sp->obj; sp->obj->objcore = sp->objcore; sp->obj->objhead = sp->objhead; sp->objhead = NULL; /* refcnt follows pointer. */ sp->objcore = NULL; /* refcnt follows pointer. */ } BAN_NewObj(sp->obj); sp->obj->xid = sp->xid; sp->obj->response = sp->err_code; sp->obj->cacheable = sp->wrk->cacheable; sp->obj->ttl = sp->wrk->ttl; sp->obj->grace = sp->wrk->grace; if (sp->obj->ttl == 0. && sp->obj->grace == 0.) sp->obj->cacheable = 0; sp->obj->age = sp->wrk->age; sp->obj->entered = sp->wrk->entered; WS_Assert(sp->obj->ws_o); /* Filter into object */ hp = sp->wrk->beresp; hp2 = sp->obj->http; hp2->logtag = HTTP_Obj; http_CopyResp(hp2, hp); http_FilterFields(sp->wrk, sp->fd, hp2, hp, HTTPH_A_INS); http_CopyHome(sp->wrk, sp->fd, hp2); if (http_GetHdr(hp, H_Last_Modified, &b)) sp->obj->last_modified = TIM_parse(b); i = FetchBody(sp); AZ(sp->wrk->wfd); AZ(sp->vbe); AN(sp->director); if (i) { HSH_Drop(sp); AZ(sp->obj); sp->wrk->bereq = NULL; sp->wrk->beresp = NULL; sp->wrk->beresp1 = NULL; sp->err_code = 503; sp->step = STP_ERROR; return (0); } if (!transient) HSH_Object(sp); if (sp->wrk->do_esi) ESI_Parse(sp); switch (handling) { case VCL_RET_RESTART: HSH_Drop(sp); sp->director = NULL; sp->restarts++; sp->wrk->bereq = NULL; sp->wrk->beresp = NULL; sp->wrk->beresp1 = NULL; sp->step = STP_RECV; return (0); case VCL_RET_PASS: if (sp->obj->objcore != NULL) sp->obj->objcore->flags |= OC_F_PASS; if (sp->obj->ttl - sp->t_req < params->default_ttl) sp->obj->ttl = sp->t_req + params->default_ttl; break; case VCL_RET_DELIVER: break; case VCL_RET_ERROR: HSH_Drop(sp); sp->wrk->bereq = NULL; sp->wrk->beresp = NULL; sp->wrk->beresp1 = NULL; sp->step = STP_ERROR; return (0); default: WRONG("Illegal action in vcl_fetch{}"); } sp->obj->cacheable = 1; if (sp->obj->objhead != NULL) { VRY_Create(sp); EXP_Insert(sp->obj); AN(sp->obj->ban); HSH_Unbusy(sp); } sp->acct_req.fetch++; sp->wrk->bereq = NULL; sp->wrk->beresp = NULL; sp->wrk->beresp1 = NULL; sp->step = STP_DELIVER; return (0); }
static int cnt_done(struct sess *sp) { double dh, dp, da; int i; CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); CHECK_OBJ_ORNULL(sp->vcl, VCL_CONF_MAGIC); AZ(sp->obj); AZ(sp->vbe); sp->director = NULL; sp->restarts = 0; if (sp->vcl != NULL && sp->esis == 0) { if (sp->wrk->vcl != NULL) VCL_Rel(&sp->wrk->vcl); sp->wrk->vcl = sp->vcl; sp->vcl = NULL; } sp->t_end = TIM_real(); sp->wrk->lastused = sp->t_end; if (sp->xid == 0) { sp->t_req = sp->t_end; sp->t_resp = sp->t_end; } dp = sp->t_resp - sp->t_req; da = sp->t_end - sp->t_resp; dh = sp->t_req - sp->t_open; WSL(sp->wrk, SLT_ReqEnd, sp->id, "%u %.9f %.9f %.9f %.9f %.9f", sp->xid, sp->t_req, sp->t_end, dh, dp, da); sp->xid = 0; sp->t_open = sp->t_end; sp->t_resp = NAN; WSL_Flush(sp->wrk, 0); /* If we did an ESI include, don't mess up our state */ if (sp->esis > 0) return (1); sp->t_req = NAN; if (sp->fd >= 0 && sp->doclose != NULL) { /* * This is an orderly close of the connection; ditch nolinger * before we close, to get queued data transmitted. */ TCP_linger(sp->fd, 0); vca_close_session(sp, sp->doclose); } if (sp->fd < 0) { SES_Charge(sp); VSL_stats->sess_closed++; sp->wrk = NULL; SES_Delete(sp); return (1); } /* Reset the workspace to the session-watermark */ WS_Reset(sp->ws, sp->ws_ses); i = HTC_Reinit(sp->htc); if (i == 1) { VSL_stats->sess_pipeline++; sp->step = STP_START; return (0); } if (Tlen(sp->htc->rxbuf)) { VSL_stats->sess_readahead++; sp->step = STP_WAIT; return (0); } if (params->session_linger > 0) { VSL_stats->sess_linger++; sp->step = STP_WAIT; return (0); } VSL_stats->sess_herd++; SES_Charge(sp); sp->wrk = NULL; vca_return_session(sp); return (1); }
static int cnt_start(struct sess *sp) { int done; char *p; const char *r = "HTTP/1.1 100 Continue\r\n\r\n"; CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); AZ(sp->restarts); AZ(sp->obj); AZ(sp->vcl); /* Update stats of various sorts */ VSL_stats->client_req++; /* XXX not locked */ sp->t_req = TIM_real(); sp->wrk->lastused = sp->t_req; sp->acct_req.req++; /* Assign XID and log */ sp->xid = ++xids; /* XXX not locked */ WSP(sp, SLT_ReqStart, "%s %s %u", sp->addr, sp->port, sp->xid); /* Borrow VCL reference from worker thread */ VCL_Refresh(&sp->wrk->vcl); sp->vcl = sp->wrk->vcl; sp->wrk->vcl = NULL; http_Setup(sp->http, sp->ws); done = http_DissectRequest(sp); /* Catch request snapshot */ sp->ws_req = WS_Snapshot(sp->ws); /* Catch original request, before modification */ *sp->http0 = *sp->http; if (done != 0) { sp->err_code = done; sp->step = STP_ERROR; return (0); } sp->doclose = http_DoConnection(sp->http); /* XXX: Handle TRACE & OPTIONS of Max-Forwards = 0 */ /* * Handle Expect headers */ if (http_GetHdr(sp->http, H_Expect, &p)) { if (strcmp(p, "100-continue")) { sp->err_code = 417; sp->step = STP_ERROR; return (0); } /* XXX: Don't bother with write failures for now */ (void)write(sp->fd, r, strlen(r)); /* XXX: When we do ESI includes, this is not removed * XXX: because we use http0 as our basis. Believed * XXX: safe, but potentially confusing. */ http_Unset(sp->http, H_Expect); } sp->step = STP_RECV; return (0); }
void PipeSession(struct sess *sp) { struct vbe_conn *vc; struct worker *w; struct pollfd fds[2]; int i; CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); CHECK_OBJ_NOTNULL(sp->wrk, WORKER_MAGIC); w = sp->wrk; sp->vbe = VBE_GetFd(NULL, sp); if (sp->vbe == NULL) return; vc = sp->vbe; (void)TCP_blocking(vc->fd); WRW_Reserve(w, &vc->fd); sp->acct_req.hdrbytes += http_Write(w, sp->wrk->bereq, 0); if (sp->htc->pipeline.b != NULL) sp->acct_req.bodybytes += WRW_Write(w, sp->htc->pipeline.b, Tlen(sp->htc->pipeline)); i = WRW_FlushRelease(w); if (i) { vca_close_session(sp, "pipe"); VBE_ClosedFd(sp); return; } sp->t_resp = TIM_real(); memset(fds, 0, sizeof fds); // XXX: not yet (void)TCP_linger(vc->fd, 0); fds[0].fd = vc->fd; fds[0].events = POLLIN | POLLERR; // XXX: not yet (void)TCP_linger(sp->fd, 0); fds[1].fd = sp->fd; fds[1].events = POLLIN | POLLERR; while (fds[0].fd > -1 || fds[1].fd > -1) { fds[0].revents = 0; fds[1].revents = 0; i = poll(fds, 2, params->pipe_timeout * 1000); if (i < 1) break; if (fds[0].revents && rdf(vc->fd, sp->fd)) { (void)shutdown(vc->fd, SHUT_RD); (void)shutdown(sp->fd, SHUT_WR); fds[0].events = 0; fds[0].fd = -1; } if (fds[1].revents && rdf(sp->fd, vc->fd)) { (void)shutdown(sp->fd, SHUT_RD); (void)shutdown(vc->fd, SHUT_WR); fds[1].events = 0; fds[1].fd = -1; } } if (fds[0].fd >= 0) { (void)shutdown(vc->fd, SHUT_RD); (void)shutdown(sp->fd, SHUT_WR); } if (fds[1].fd >= 0) { (void)shutdown(sp->fd, SHUT_RD); (void)shutdown(vc->fd, SHUT_WR); } vca_close_session(sp, "pipe"); VBE_ClosedFd(sp); }