int tcpconnect(const ipaddr *addr, int64_t deadline) { int err; /* Open a socket. */ int s = socket(ipfamily(addr), SOCK_STREAM, 0); if(dill_slow(s < 0)) return -1; tcptune(s); /* Connect to the remote endpoint. */ int rc = dsconnect(s, ipsockaddr(addr), iplen(addr), deadline); if(dill_slow(rc < 0)) return -1; /* Create the object. */ struct tcpconn *conn = tcpconn_create(); if(dill_slow(!conn)) {err = errno; goto error1;} conn->fd = s; conn->addr = *addr; /* Bind the object to a sock handle. */ int bs = bsock(tcpconn_type, conn, &tcpconn_vfptrs); if(dill_slow(bs < 0)) {err = errno; goto error2;} return bs; error2: tcpconn_destroy(conn); error1: rc = dsclose(s); dill_assert(rc == 0); errno = err; return -1; }
int tcpaccept(int s, int64_t deadline) { int err; struct tcplistener *lst = hdata(s, tcplistener_type); if(dill_slow(!lst)) return -1; /* Try to get new connection in a non-blocking way. */ ipaddr addr; socklen_t addrlen; int as = dsaccept(lst->fd, (struct sockaddr*)&addr, &addrlen, deadline); if(dill_slow(as < 0)) return -1; tcptune(as); /* Create the object. */ struct tcpconn *conn = tcpconn_create(); if(dill_slow(!conn)) {err = errno; goto error1;} conn->fd = as; conn->addr = addr; /* Bind the object to a handle. */ int hndl = bsock(tcpconn_type, conn, &tcpconn_vfptrs); if(dill_slow(hndl < 0)) {err = errno; goto error2;} return hndl; error2: tcpconn_destroy(conn); error1:; int rc = dsclose(s); dill_assert(rc == 0); errno = err; return -1; }
static int tcpconn_finish(int s, int64_t deadline) { int rc; int err = 0; struct tcpconn *conn = bsockdata(s, tcpconn_type); if(dill_slow(!conn)) return -1; /* First, let's try to flush remaining outbound data. */ if(deadline != 0) { rc = tcpconn_flush(s, deadline); if(dill_slow(rc < 0)) err = errno; } /* Close the underlying socket. */ rc = dsclose(conn->fd); dill_assert(rc == 0); /* Deallocate the object. */ tcpconn_destroy(conn); errno = err; return err ? -1 : 0; }
void tcp_main_loop() { int r; int n; fd_set master_set; fd_set sel_set; int maxfd; struct tcp_connection* tcpconn; unsigned h; long response[2]; int cmd; int bytes; struct timeval timeout; int fd; /*init */ maxfd=0; FD_ZERO(&master_set); /* set all the listen addresses */ for (r=0; r<sock_no; r++){ if ((tcp_info[r].proto==PROTO_TCP) &&(tcp_info[r].socket!=-1)){ FD_SET(tcp_info[r].socket, &master_set); if (tcp_info[r].socket>maxfd) maxfd=tcp_info[r].socket; } #ifdef USE_TLS if ((!tls_disable)&&(tls_info[r].proto==PROTO_TLS) && (tls_info[r].socket!=-1)){ FD_SET(tls_info[r].socket, &master_set); if (tls_info[r].socket>maxfd) maxfd=tls_info[r].socket; } #endif } /* set all the unix sockets used for child comm */ for (r=1; r<process_no; r++){ if (pt[r].unix_sock>0){ /* we can't have 0, we never close it!*/ FD_SET(pt[r].unix_sock, &master_set); if (pt[r].unix_sock>maxfd) maxfd=pt[r].unix_sock; } } for (r=0; r<tcp_children_no; r++){ if (tcp_children[r].unix_sock>0){ /* we can't have 0, we never close it!*/ FD_SET(tcp_children[r].unix_sock, &master_set); if (tcp_children[r].unix_sock>maxfd) maxfd=tcp_children[r].unix_sock; } } /* main loop*/ while(1){ sel_set=master_set; timeout.tv_sec=TCP_MAIN_SELECT_TIMEOUT; timeout.tv_usec=0; n=select(maxfd+1, &sel_set, 0 ,0 , &timeout); if (n<0){ if (errno==EINTR) continue; /* just a signal */ /* errors */ LOG(L_ERR, "ERROR: tcp_main_loop: select:(%d) %s\n", errno, strerror(errno)); n=0; } for (r=0; r<sock_no && n; r++){ handle_new_connect(&tcp_info[r], &sel_set, &n); #ifdef USE_TLS if (!tls_disable) handle_new_connect(&tls_info[r], &sel_set, &n); #endif } /* check all the read fds (from the tcpconn_addr_hash ) */ for (h=0; h<TCP_ADDR_HASH_SIZE; h++){ for(tcpconn=tcpconn_addr_hash[h]; tcpconn && n; tcpconn=tcpconn->next){ /* FIXME: is refcnt==0 really necessary? */ if ((tcpconn->refcnt==0)&&(FD_ISSET(tcpconn->s, &sel_set))){ /* new data available */ n--; /* pass it to child, so remove it from select list */ DBG("tcp_main_loop: data available on %p [h:%d] %d\n", tcpconn, h, tcpconn->s); FD_CLR(tcpconn->s, &master_set); tcpconn_ref(tcpconn); /* refcnt ++ */ if (send2child(tcpconn)<0){ LOG(L_ERR,"ERROR: tcp_main_loop: no " "children available\n"); TCPCONN_LOCK; tcpconn->refcnt--; if (tcpconn->refcnt==0){ fd=tcpconn->s; _tcpconn_rm(tcpconn); close(fd); }else tcpconn->timeout=0; /* force expire*/ TCPCONN_UNLOCK; } } } } /* check unix sockets & listen | destroy connections */ /* tcp_children readers first */ for (r=0; r<tcp_children_no && n; r++){ if ( (tcp_children[r].unix_sock>0) && FD_ISSET(tcp_children[r].unix_sock, &sel_set)){ /* (we can't have a fd==0, 0 is never closed )*/ n--; /* read until sizeof(response) * (this is a SOCK_STREAM so read is not atomic */ bytes=recv_all(tcp_children[r].unix_sock, response, sizeof(response)); if (bytes==0){ /* EOF -> bad, child has died */ DBG("DBG: tcp_main_loop: dead tcp child %d" " (shutting down?)\n", r); /* don't listen on it any more */ FD_CLR(tcp_children[r].unix_sock, &master_set); /*exit(-1);*/ continue; /* skip this and try the next one */ }else if (bytes<0){ LOG(L_CRIT, "ERROR: tcp_main_loop: read from tcp child %d " "%s\n", r, strerror(errno)); /* try to ignore ? */ continue; /* skip this and try the next one */ } DBG("tcp_main_loop: reader response= %lx, %ld from %d \n", response[0], response[1], r); cmd=response[1]; tcpconn=(struct tcp_connection*)response[0]; switch(cmd){ case CONN_RELEASE: tcp_children[r].busy--; if (tcpconn){ if (tcpconn->state==S_CONN_BAD){ tcpconn_destroy(tcpconn); break; } FD_SET(tcpconn->s, &master_set); if (maxfd<tcpconn->s) maxfd=tcpconn->s; /* update the timeout*/ tcpconn->timeout=get_ticks()+TCP_CON_TIMEOUT; tcpconn_put(tcpconn); DBG("tcp_main_loop: CONN_RELEASE %p" " refcnt= %d\n", tcpconn, tcpconn->refcnt); } break; case CONN_ERROR: case CONN_DESTROY: case CONN_EOF: /* WARNING: this will auto-dec. refcnt! */ tcp_children[pt[r].idx].busy--; if (tcpconn){ if (tcpconn->s!=-1) FD_CLR(tcpconn->s, &master_set); tcpconn_destroy(tcpconn); } break; default: LOG(L_CRIT, "BUG: tcp_main_loop: unknown cmd %d" " from tcp reader %d\n", cmd, r); } } } /* check "send" unix sockets & listen | destroy connections */ /* start from 1, the "main" process does not transmit anything*/ for (r=1; r<process_no && n; r++){ if ( (pt[r].unix_sock>0) && FD_ISSET(pt[r].unix_sock, &sel_set)){ /* (we can't have a fd==0, 0 is never closed )*/ n--; /* read until sizeof(response) * (this is a SOCK_STREAM so read is not atomic */ bytes=recv_all(pt[r].unix_sock, response, sizeof(response)); if (bytes==0){ /* EOF -> bad, child has died */ DBG("DBG: tcp_main_loop: dead child %d" " (shutting down?)\n", r); /* don't listen on it any more */ FD_CLR(pt[r].unix_sock, &master_set); /*exit(-1);*/ continue; /* skip this and try the next one */ }else if (bytes<0){ LOG(L_CRIT, "ERROR: tcp_main_loop: read from child: %s\n", strerror(errno)); /* try to ignore ? */ continue; /* skip this and try the next one */ } DBG("tcp_main_loop: read response= %lx, %ld from %d (%d)\n", response[0], response[1], r, pt[r].pid); cmd=response[1]; tcpconn=(struct tcp_connection*)response[0]; switch(cmd){ case CONN_ERROR: if (tcpconn){ if (tcpconn->s!=-1) FD_CLR(tcpconn->s, &master_set); tcpconn_destroy(tcpconn); } break; case CONN_GET_FD: /* send the requested FD */ /* WARNING: take care of setting refcnt properly to * avoid race condition */ if (tcpconn){ if (send_fd(pt[r].unix_sock, &tcpconn, sizeof(tcpconn), tcpconn->s)<=0){ LOG(L_ERR, "ERROR: tcp_main_loop:" "send_fd failed\n"); } }else{ LOG(L_CRIT, "BUG: tcp_main_loop: null pointer\n"); } break; case CONN_NEW: /* update the fd in the requested tcpconn*/ /* WARNING: take care of setting refcnt properly to * avoid race condition */ if (tcpconn){ bytes=receive_fd(pt[r].unix_sock, &tcpconn, sizeof(tcpconn), &tcpconn->s); if (bytes<sizeof(tcpconn)){ if (bytes<0){ LOG(L_CRIT, "BUG: tcp_main_loop:" " CONN_NEW: receive_fd " "failed\n"); }else{ LOG(L_CRIT, "BUG: tcp_main_loop:" " CONN_NEW: to few bytes " "received (%d)\n", bytes ); } break; /* try to ignore */ } /* add tcpconn to the list*/ tcpconn_add(tcpconn); FD_SET(tcpconn->s, &master_set); if (maxfd<tcpconn->s) maxfd=tcpconn->s; /* update the timeout*/ tcpconn->timeout=get_ticks()+TCP_CON_TIMEOUT; }else{ LOG(L_CRIT, "BUG: tcp_main_loop: null pointer\n"); } break; default: LOG(L_CRIT, "BUG: tcp_main_loop: unknown cmd %d\n", cmd); } } } /* for */ /* remove old connections */ tcpconn_timeout(&master_set); } }
/*! \brief handles io from a "generic" ser process (get fd or new_fd from a tcp_send) * * \param p - pointer in the ser processes array (pt[]), to the entry for * which an io event was detected * \param fd_i - fd index in the fd_array (usefull for optimizing * io_watch_deletes) * \return handle_* return convention: * - -1 on error reading from the fd, * - 0 on EAGAIN or when no more io events are queued * (receive buffer empty), * - >0 on successfull reads from the fd (the receive buffer might * be non-empty). */ inline static int handle_ser_child(struct process_table* p, int fd_i) { struct tcp_connection* tcpconn; long response[2]; int cmd; int bytes; int ret; int fd; ret=-1; if (p->unix_sock<=0){ /* (we can't have a fd==0, 0 is never closed )*/ LM_CRIT("fd %d for %d (pid %d)\n", p->unix_sock, (int)(p-&pt[0]), p->pid); goto error; } /* get all bytes and the fd (if transmitted) * (this is a SOCK_STREAM so read is not atomic) */ bytes=receive_fd(p->unix_sock, response, sizeof(response), &fd, MSG_DONTWAIT); if (bytes<(int)sizeof(response)){ /* too few bytes read */ if (bytes==0){ /* EOF -> bad, child has died */ LM_DBG("dead child %d, pid %d" " (shutting down?)\n", (int)(p-&pt[0]), p->pid); /* don't listen on it any more */ io_watch_del(&io_h, p->unix_sock, fd_i, 0); goto error; /* child dead => no further io events from it */ }else if (bytes<0){ /* EAGAIN is ok if we try to empty the buffer * e.g: SIGIO_RT overflow mode or EPOLL ET */ if ((errno!=EAGAIN) && (errno!=EWOULDBLOCK)){ LM_CRIT("read from child %d (pid %d): %s [%d]\n", (int)(p-&pt[0]), p->pid, strerror(errno), errno); ret=-1; }else{ ret=0; } /* try to ignore ? */ goto end; }else{ /* should never happen */ LM_CRIT("too few bytes received (%d)\n", bytes ); ret=0; /* something was read so there is no error; otoh if receive_fd returned less then requested => the receive buffer is empty => no more io queued on this fd */ goto end; } } ret=1; /* something was received, there might be more queued */ LM_DBG("read response= %lx, %ld, fd %d from %d (%d)\n", response[0], response[1], fd, (int)(p-&pt[0]), p->pid); cmd=response[1]; tcpconn=(struct tcp_connection*)response[0]; if (tcpconn==0){ LM_CRIT("null tcpconn pointer received from child %d (pid %d)" "%lx, %lx\n", (int)(p-&pt[0]), p->pid, response[0], response[1]) ; goto end; } switch(cmd){ case CONN_ERROR: if (!(tcpconn->flags & F_CONN_REMOVED) && (tcpconn->s!=-1)){ io_watch_del(&io_h, tcpconn->s, -1, IO_FD_CLOSING); tcpconn->flags|=F_CONN_REMOVED; } tcpconn_destroy(tcpconn); /* will close also the fd */ break; case CONN_GET_FD: /* send the requested FD */ /* WARNING: take care of setting refcnt properly to * avoid race condition */ if (send_fd(p->unix_sock, &tcpconn, sizeof(tcpconn), tcpconn->s)<=0){ LM_ERR("send_fd failed\n"); } break; case CONN_NEW: /* update the fd in the requested tcpconn*/ /* WARNING: take care of setting refcnt properly to * avoid race condition */ if (fd==-1){ LM_CRIT(" cmd CONN_NEW: no fd received\n"); break; } tcpconn->s=fd; /* add tcpconn to the list*/ tcpconn_add(tcpconn); /* update the timeout*/ tcpconn->timeout=get_ticks()+tcp_con_lifetime; io_watch_add(&io_h, tcpconn->s, F_TCPCONN, tcpconn); tcpconn->flags&=~F_CONN_REMOVED; break; default: LM_CRIT("unknown cmd %d\n", cmd); } end: return ret; error: return -1; }
/*! \brief handles io from a tcp child process * \param tcp_c - pointer in the tcp_children array, to the entry for * which an io event was detected * \param fd_i - fd index in the fd_array (usefull for optimizing * io_watch_deletes) * \return handle_* return convention: -1 on error, 0 on EAGAIN (no more * io events queued), >0 on success. success/error refer only to * the reads from the fd. */ inline static int handle_tcp_child(struct tcp_child* tcp_c, int fd_i) { struct tcp_connection* tcpconn; long response[2]; int cmd; int bytes; if (tcp_c->unix_sock<=0){ /* (we can't have a fd==0, 0 is never closed )*/ LM_CRIT("fd %d for %d (pid %d, ser no %d)\n", tcp_c->unix_sock, (int)(tcp_c-&tcp_children[0]), tcp_c->pid, tcp_c->proc_no); goto error; } /* read until sizeof(response) * (this is a SOCK_STREAM so read is not atomic) */ bytes=recv_all(tcp_c->unix_sock, response, sizeof(response), MSG_DONTWAIT); if (bytes<(int)sizeof(response)){ if (bytes==0){ /* EOF -> bad, child has died */ LM_DBG("dead tcp child %d (pid %d, no %d)" " (shutting down?)\n", (int)(tcp_c-&tcp_children[0]), tcp_c->pid, tcp_c->proc_no ); /* don't listen on it any more */ io_watch_del(&io_h, tcp_c->unix_sock, fd_i, 0); goto error; /* eof. so no more io here, it's ok to return error */ }else if (bytes<0){ /* EAGAIN is ok if we try to empty the buffer * e.g.: SIGIO_RT overflow mode or EPOLL ET */ if ((errno!=EAGAIN) && (errno!=EWOULDBLOCK)){ LM_CRIT("read from tcp child %ld (pid %d, no %d) %s [%d]\n", (long)(tcp_c-&tcp_children[0]), tcp_c->pid, tcp_c->proc_no, strerror(errno), errno ); }else{ bytes=0; } /* try to ignore ? */ goto end; }else{ /* should never happen */ LM_CRIT("too few bytes received (%d)\n", bytes ); bytes=0; /* something was read so there is no error; otoh if receive_fd returned less then requested => the receive buffer is empty => no more io queued on this fd */ goto end; } } LM_DBG("reader response= %lx, %ld from %d \n", response[0], response[1], (int)(tcp_c-&tcp_children[0])); cmd=response[1]; tcpconn=(struct tcp_connection*)response[0]; if (tcpconn==0){ /* should never happen */ LM_CRIT("null tcpconn pointer received from tcp child %d (pid %d):" "%lx, %lx\n", (int)(tcp_c-&tcp_children[0]), tcp_c->pid, response[0], response[1]) ; goto end; } switch(cmd){ case CONN_RELEASE: tcp_c->busy--; if (tcpconn->state==S_CONN_BAD){ tcpconn_destroy(tcpconn); break; } /* update the timeout (lifetime) */ set_tcp_timeout( tcpconn ); tcpconn_put(tcpconn); /* must be after the de-ref*/ io_watch_add(&io_h, tcpconn->s, F_TCPCONN, tcpconn); tcpconn->flags&=~F_CONN_REMOVED; LM_DBG("cmd CONN_RELEASE %p refcnt= %d\n", tcpconn, tcpconn->refcnt); break; case CONN_ERROR: case CONN_DESTROY: case CONN_EOF: /* WARNING: this will auto-dec. refcnt! */ tcp_c->busy--; /* main doesn't listen on it => we don't have to delete it if (tcpconn->s!=-1) io_watch_del(&io_h, tcpconn->s, -1, IO_FD_CLOSING); */ tcpconn_destroy(tcpconn); /* closes also the fd */ break; default: LM_CRIT("unknown cmd %d from tcp reader %d\n", cmd, (int)(tcp_c-&tcp_children[0])); } end: return bytes; error: return -1; }
/* wrapper to the internally used function */ void tcp_conn_destroy(struct tcp_connection* tcpconn) { return tcpconn_destroy(tcpconn); }
/*! \brief handles io from a "generic" ser process (get fd or new_fd from a tcp_send) * * \param p - pointer in the ser processes array (pt[]), to the entry for * which an io event was detected * \param fd_i - fd index in the fd_array (useful for optimizing * io_watch_deletes) * \return handle_* return convention: * - -1 on error reading from the fd, * - 0 on EAGAIN or when no more io events are queued * (receive buffer empty), * - >0 on successfull reads from the fd (the receive buffer might * be non-empty). */ inline static int handle_worker(struct process_table* p, int fd_i) { struct tcp_connection* tcpconn; long response[2]; int cmd; int bytes; int ret; int fd; ret=-1; if (p->unix_sock<=0){ /* (we can't have a fd==0, 0 is never closed )*/ LM_CRIT("fd %d for %d (pid %d)\n", p->unix_sock, (int)(p-&pt[0]), p->pid); goto error; } /* get all bytes and the fd (if transmitted) * (this is a SOCK_STREAM so read is not atomic) */ bytes=receive_fd(p->unix_sock, response, sizeof(response), &fd, MSG_DONTWAIT); if (bytes<(int)sizeof(response)){ /* too few bytes read */ if (bytes==0){ /* EOF -> bad, child has died */ LM_DBG("dead child %d, pid %d" " (shutting down?)\n", (int)(p-&pt[0]), p->pid); /* don't listen on it any more */ reactor_del_reader( p->unix_sock, fd_i, 0/*flags*/); goto error; /* child dead => no further io events from it */ }else if (bytes<0){ /* EAGAIN is ok if we try to empty the buffer * e.g: SIGIO_RT overflow mode or EPOLL ET */ if ((errno!=EAGAIN) && (errno!=EWOULDBLOCK)){ LM_CRIT("read from child %d (pid %d): %s [%d]\n", (int)(p-&pt[0]), p->pid, strerror(errno), errno); ret=-1; }else{ ret=0; } /* try to ignore ? */ goto end; }else{ /* should never happen */ LM_CRIT("too few bytes received (%d)\n", bytes ); ret=0; /* something was read so there is no error; otoh if receive_fd returned less then requested => the receive buffer is empty => no more io queued on this fd */ goto end; } } ret=1; /* something was received, there might be more queued */ LM_DBG("read response= %lx, %ld, fd %d from %d (%d)\n", response[0], response[1], fd, (int)(p-&pt[0]), p->pid); cmd=response[1]; tcpconn=(struct tcp_connection*)response[0]; if (tcpconn==0){ LM_CRIT("null tcpconn pointer received from child %d (pid %d)" "%lx, %lx\n", (int)(p-&pt[0]), p->pid, response[0], response[1]) ; goto end; } switch(cmd){ case CONN_ERROR: if (!(tcpconn->flags & F_CONN_REMOVED) && (tcpconn->s!=-1)){ reactor_del_all( tcpconn->s, -1, IO_FD_CLOSING); tcpconn->flags|=F_CONN_REMOVED; } tcpconn_destroy(tcpconn); /* will close also the fd */ break; case CONN_GET_FD: /* send the requested FD */ /* WARNING: take care of setting refcnt properly to * avoid race condition */ if (send_fd(p->unix_sock, &tcpconn, sizeof(tcpconn), tcpconn->s)<=0){ LM_ERR("send_fd failed\n"); } break; case CONN_NEW: /* update the fd in the requested tcpconn*/ /* WARNING: take care of setting refcnt properly to * avoid race condition */ if (fd==-1){ LM_CRIT(" cmd CONN_NEW: no fd received\n"); break; } tcpconn->s=fd; /* add tcpconn to the list*/ tcpconn_add(tcpconn); reactor_add_reader( tcpconn->s, F_TCPCONN, RCT_PRIO_NET, tcpconn); tcpconn->flags&=~F_CONN_REMOVED; break; case ASYNC_CONNECT: /* connection is not yet linked to hash = not yet * available to the outside world */ if (fd==-1){ LM_CRIT(" cmd CONN_NEW: no fd received\n"); break; } tcpconn->s=fd; /* add tcpconn to the list*/ tcpconn_add(tcpconn); /* FIXME - now we have lifetime==default_lifetime - should we * set a shorter one when waiting for a connect ??? */ /* only maintain the socket in the IO_WATCH_WRITE watcher * while we have stuff to write - otherwise we're going to get * useless events */ reactor_add_writer( tcpconn->s, F_TCPCONN, RCT_PRIO_NET, tcpconn); tcpconn->flags&=~F_CONN_REMOVED; break; case ASYNC_WRITE: if (tcpconn->state==S_CONN_BAD){ tcpconn->lifetime=0; break; } /* must be after the de-ref*/ reactor_add_writer( tcpconn->s, F_TCPCONN, RCT_PRIO_NET, tcpconn); tcpconn->flags&=~F_CONN_REMOVED; break; default: LM_CRIT("unknown cmd %d\n", cmd); } end: return ret; error: return -1; }
/*! \brief * handles an io event on one of the watched tcp connections * * \param tcpconn - pointer to the tcp_connection for which we have an io ev. * \param fd_i - index in the fd_array table (needed for delete) * \return handle_* return convention, but on success it always returns 0 * (because it's one-shot, after a succesfull execution the fd is * removed from tcp_main's watch fd list and passed to a child => * tcp_main is not interested in further io events that might be * queued for this fd) */ inline static int handle_tcpconn_ev(struct tcp_connection* tcpconn, int fd_i, int event_type) { int fd; int err; int id; unsigned int err_len; if (event_type == IO_WATCH_READ) { /* pass it to child, so remove it from the io watch list */ LM_DBG("data available on %p %d\n", tcpconn, tcpconn->s); if (reactor_del_reader(tcpconn->s, fd_i, 0)==-1) return -1; tcpconn->flags|=F_CONN_REMOVED; tcpconn_ref(tcpconn); /* refcnt ++ */ if (send2child(tcpconn,IO_WATCH_READ)<0){ LM_ERR("no children available\n"); id = tcpconn->id; TCPCONN_LOCK(id); tcpconn->refcnt--; if (tcpconn->refcnt==0){ fd=tcpconn->s; _tcpconn_rm(tcpconn); close(fd); }else tcpconn->lifetime=0; /* force expire*/ TCPCONN_UNLOCK(id); } return 0; /* we are not interested in possibly queued io events, the fd was either passed to a child, or closed */ } else { LM_DBG("connection %p fd %d is now writable\n", tcpconn, tcpconn->s); /* we received a write event */ if (tcpconn->state==S_CONN_CONNECTING) { /* we're coming from an async connect & write * let's see if we connected successfully*/ err_len=sizeof(err); if (getsockopt(tcpconn->s, SOL_SOCKET, SO_ERROR, &err, &err_len) < 0 || \ err != 0) { LM_DBG("Failed connection attempt\n"); tcpconn_ref(tcpconn); reactor_del_all(tcpconn->s, fd_i, IO_FD_CLOSING); tcpconn->flags|=F_CONN_REMOVED; tcpconn_destroy(tcpconn); return 0; } /* we successfully connected - further treat this case as if we * were coming from an async write */ tcpconn->state = S_CONN_OK; LM_DBG("Successfully completed previous async connect\n"); goto async_write; } else { /* we're coming from an async write - * just pass to child and have it write * our TCP chunks */ async_write: /* no more write events for now */ if (reactor_del_writer( tcpconn->s, fd_i, 0)==-1) return -1; tcpconn->flags|=F_CONN_REMOVED; tcpconn_ref(tcpconn); /* refcnt ++ */ if (send2child(tcpconn,IO_WATCH_WRITE)<0){ LM_ERR("no children available\n"); id = tcpconn->id; TCPCONN_LOCK(id); tcpconn->refcnt--; if (tcpconn->refcnt==0){ fd=tcpconn->s; _tcpconn_rm(tcpconn); close(fd); }else tcpconn->lifetime=0; /* force expire*/ TCPCONN_UNLOCK(id); } return 0; } } }