Ejemplo n.º 1
0
/*! \brief used internally by tcp_main_loop() */
static void tcpconn_destroy(struct tcp_connection* tcpconn)
{
	int fd;

	TCPCONN_LOCK; /*avoid races w/ tcp_send*/
	tcpconn->refcnt--;
	if (tcpconn->refcnt==0){ 
		LM_DBG("destroying connection %p, flags %04x\n",
				tcpconn, tcpconn->flags);
		fd=tcpconn->s;
#ifdef USE_TLS
		/*FIXME: lock ->writelock ? */
		if (tcpconn->type==PROTO_TLS)
			tls_close(tcpconn, fd);
#endif
		_tcpconn_rm(tcpconn);
		close(fd);
		tcp_connections_no--;
	}else{
		/* force timeout */
		tcpconn->timeout=0;
		tcpconn->state=S_CONN_BAD;
		LM_DBG("delaying (%p, flags %04x) ...\n",
				tcpconn, tcpconn->flags);
		
	}
	TCPCONN_UNLOCK;
}
Ejemplo n.º 2
0
/* used internally by tcp_main_loop() */
static void tcpconn_destroy(struct tcp_connection* tcpconn)
{
	int fd;

	TCPCONN_LOCK; /*avoid races w/ tcp_send*/
	tcpconn->refcnt--;
	if (tcpconn->refcnt==0){ 
		DBG("tcp_main_loop: destroying connection\n");
		fd=tcpconn->s;
#ifdef USE_TLS
		/*FIXME: lock ->writelock ? */
		if (tcpconn->type==PROTO_TLS)
			tls_close(tcpconn, fd);
#endif
		_tcpconn_rm(tcpconn);
		close(fd);
	}else{
		/* force timeout */
		tcpconn->timeout=0;
		tcpconn->state=S_CONN_BAD;
		DBG("tcp_main_loop: delaying ...\n");
		
	}
	TCPCONN_UNLOCK;
}
Ejemplo n.º 3
0
/* very ineficient for now - FIXME*/
void tcpconn_timeout(fd_set* set)
{
	struct tcp_connection *c, *next;
	int ticks;
	unsigned h;
	int fd;
	
	
	ticks=get_ticks();
	TCPCONN_LOCK; /* fixme: we can lock only on delete IMO */
	for(h=0; h<TCP_ADDR_HASH_SIZE; h++){
		c=tcpconn_addr_hash[h];
		while(c){
			next=c->next;
			if ((c->refcnt==0) && (ticks>c->timeout)) {
				DBG("tcpconn_timeout: timeout for hash=%d - %p (%d > %d)\n",
						h, c, ticks, c->timeout);
				fd=c->s;
#ifdef USE_TLS
				if (c->type==PROTO_TLS)
					tls_close(c, fd);
#endif
				_tcpconn_rm(c);
				if (fd>0) {
					FD_CLR(fd, set);
					close(fd);
				}
			}
			c=next;
		}
	}
	TCPCONN_UNLOCK;
}
Ejemplo n.º 4
0
static inline void tcpconn_destroy(struct tcp_connection* tcpconn)
{
	int fd;
	int id = tcpconn->id;

	TCPCONN_LOCK(id); /*avoid races w/ tcp_send*/
	tcpconn->refcnt--;
	if (tcpconn->refcnt==0){
		LM_DBG("destroying connection %p, flags %04x\n",
				tcpconn, tcpconn->flags);
		fd=tcpconn->s;
		_tcpconn_rm(tcpconn);
		if (fd >= 0)
			close(fd);
		tcp_connections_no--;
	}else{
		/* force timeout */
		tcpconn->lifetime=0;
		tcpconn->state=S_CONN_BAD;
		LM_DBG("delaying (%p, flags %04x) ref = %d ...\n",
				tcpconn, tcpconn->flags, tcpconn->refcnt);

	}
	TCPCONN_UNLOCK(id);
}
Ejemplo n.º 5
0
int tcp_conn_send(struct tcp_connection *c)
{
	long response[2];
	int n;

	/* inform TCP main about this new connection */
	if (c->state==S_CONN_CONNECTING) {
		response[0]=(long)c;
		response[1]=ASYNC_CONNECT;
		n=send_fd(unix_tcp_sock, response, sizeof(response), c->s);
		if (n<=0) {
			LM_ERR("Failed to send the socket to main for async connection\n");
			goto error;
		}
		close(c->s);
	} else {
		response[0]=(long)c;
		response[1]=CONN_NEW;
		n=send_fd(unix_tcp_sock, response, sizeof(response), c->s);
		if (n<=0){
			LM_ERR("failed send_fd: %s (%d)\n", strerror(errno), errno);
			goto error;
		}
	}

	return 0;
error:
	_tcpconn_rm(c);
	tcp_connections_no--;
	return -1;
}
Ejemplo n.º 6
0
/*! \brief
 * handles a new connection, called internally by tcp_main_loop/handle_io.
 * \param si - pointer to one of the tcp socket_info structures on which
 *              an io event was detected (connection attempt)
 * \return  handle_* return convention: -1 on error, 0 on EAGAIN (no more
 *           io events queued), >0 on success. success/error refer only to
 *           the accept.
 */
static inline int handle_new_connect(struct socket_info* si)
{
	union sockaddr_union su;
	struct tcp_connection* tcpconn;
	socklen_t su_len;
	int new_sock;
	int id;

	/* got a connection on r */
	su_len=sizeof(su);
	new_sock=accept(si->socket, &(su.s), &su_len);
	if (new_sock==-1){
		if ((errno==EAGAIN)||(errno==EWOULDBLOCK))
			return 0;
		LM_ERR("failed to accept connection(%d): %s\n", errno, strerror(errno));
		return -1;
	}
	if (tcp_connections_no>=tcp_max_connections){
		LM_ERR("maximum number of connections exceeded: %d/%d\n",
					tcp_connections_no, tcp_max_connections);
		close(new_sock);
		return 1; /* success, because the accept was succesfull */
	}
	if (tcp_init_sock_opt(new_sock)<0){
		LM_ERR("tcp_init_sock_opt failed\n");
		close(new_sock);
		return 1; /* success, because the accept was succesfull */
	}

	/* add socket to list */
	tcpconn=tcpconn_new(new_sock, &su, si, S_CONN_OK, F_CONN_ACCEPTED);
	if (tcpconn){
		tcpconn->refcnt++; /* safe, not yet available to the
							  outside world */
		tcpconn_add(tcpconn);
		LM_DBG("new connection: %p %d flags: %04x\n",
				tcpconn, tcpconn->s, tcpconn->flags);
		/* pass it to a child */
		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){
				_tcpconn_rm(tcpconn);
				close(new_sock/*same as tcpconn->s*/);
			}else tcpconn->lifetime=0; /* force expire */
			TCPCONN_UNLOCK(id);
		}
	}else{ /*tcpconn==0 */
		LM_ERR("tcpconn_new failed, closing socket\n");
		close(new_sock);
	}
	return 1; /* accept() was succesfull */
}
Ejemplo n.º 7
0
/* handle a new connection, called internally by tcp_main_loop */
static inline void handle_new_connect(struct socket_info* si,
										fd_set* sel_set, int* n)
{
	union sockaddr_union su;
	struct tcp_connection* tcpconn;
	socklen_t su_len;
	int new_sock;
	
	if ((FD_ISSET(si->socket, sel_set))){
		/* got a connection on r */
		su_len=sizeof(su);
		new_sock=accept(si->socket, &(su.s), &su_len);
		(*n)--;
		if (new_sock==-1){
			LOG(L_ERR,  "WARNING: tcp_main_loop: error while accepting"
					" connection(%d): %s\n", errno, strerror(errno));
			return;
		}
		if (init_sock_opt(new_sock)<0){
			LOG(L_ERR, "ERROR: tcp_main_loop: init_sock_opt failed\n");
			close(new_sock);
			return;
		}
		
		/* add socket to list */
		tcpconn=tcpconn_new(new_sock, &su, si, si->proto, S_CONN_ACCEPT);
		if (tcpconn){
			tcpconn->refcnt++; /* safe, not yet available to the
								  outside world */
			tcpconn_add(tcpconn);
			DBG("tcp_main_loop: new connection: %p %d\n",
				tcpconn, tcpconn->s);
			/* pass it to a child */
			if(send2child(tcpconn)<0){
				LOG(L_ERR,"ERROR: tcp_main_loop: no children "
						"available\n");
				TCPCONN_LOCK;
				tcpconn->refcnt--;
				if (tcpconn->refcnt==0){
					close(tcpconn->s);
					_tcpconn_rm(tcpconn);
				}else tcpconn->timeout=0; /* force expire */
				TCPCONN_UNLOCK;
			}
		}else{ /*tcpconn==0 */
			LOG(L_ERR, "ERROR: tcp_main_loop: tcpconn_new failed, "
					"closing socket\n");
			close(new_sock);
		}
	}
}
Ejemplo n.º 8
0
/*! \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 fd;
	
	/*  is refcnt!=0 really necessary? 
	 *  No, in fact it's a bug: I can have the following situation: a send only
	 *   tcp connection used by n processes simultaneously => refcnt = n. In 
	 *   the same time I can have a read event and this situation is perfectly
	 *   valid. -- andrei
	 */
#if 0
	if ((tcpconn->refcnt!=0)){
		/* FIXME: might be valid for sigio_rt iff fd flags are not cleared
		 *        (there is a short window in which it could generate a sig
		 *         that would be catched by tcp_main) */
		LM_CRIT("io event on referenced tcpconn (%p), refcnt=%d, fd=%d\n",
					tcpconn, tcpconn->refcnt, tcpconn->s);
		return -1;
	}
#endif
	/* pass it to child, so remove it from the io watch list */
	LM_DBG("data available on %p %d\n", tcpconn, tcpconn->s);
	if (io_watch_del(&io_h, tcpconn->s, fd_i, 0)==-1) goto error;
	tcpconn->flags|=F_CONN_REMOVED;
	tcpconn_ref(tcpconn); /* refcnt ++ */
	if (send2child(tcpconn)<0){
		LM_ERR("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;
	}
	return 0; /* we are not interested in possibly queued io events, 
				 the fd was either passed to a child, or closed */
error:
	return -1;
}
Ejemplo n.º 9
0
/*! \brief very inefficient for now - FIXME
 * keep in sync with tcpconn_destroy, the "delete" part should be
 * the same except for io_watch_del..
 * \todo FIXME (very inefficient for now)
 */
static inline void __tcpconn_lifetime(int force)
{
	struct tcp_connection *c, *next;
	unsigned int ticks,part;
	unsigned h;
	int fd;

	if (have_ticks())
		ticks=get_ticks();
	else
		ticks=0;

	for( part=0 ; part<TCP_PARTITION_SIZE ; part++ ) {
		TCPCONN_LOCK(part); /* fixme: we can lock only on delete IMO */
		for(h=0; h<TCP_ID_HASH_SIZE; h++){
			c=TCP_PART(part).tcpconn_id_hash[h];
			while(c){
				next=c->id_next;
				if (force ||((c->refcnt==0) && (ticks>c->lifetime))) {
					if (!force)
						LM_DBG("timeout for hash=%d - %p"
								" (%d > %d)\n", h, c, ticks, c->lifetime);
					fd=c->s;
					_tcpconn_rm(c);
					if ((!force)&&(fd>0)&&(c->refcnt==0)) {
						if (!(c->flags & F_CONN_REMOVED)){
							reactor_del_all( fd, -1, IO_FD_CLOSING);
							c->flags|=F_CONN_REMOVED;
						}
						close(fd);
					}
					tcp_connections_no--;
				}
				c=next;
			}
		}
		TCPCONN_UNLOCK(part);
	}
}
Ejemplo n.º 10
0
/*! \brief very inefficient for now - FIXME
 * keep in sync with tcpconn_destroy, the "delete" part should be
 * the same except for io_watch_del..
 * \todo FIXME (very inefficient for now)
 */
static inline void tcpconn_timeout(int force)
{
	struct tcp_connection *c, *next;
	unsigned int ticks;
	unsigned h;
	int fd;
	
	
	ticks=get_ticks();
	TCPCONN_LOCK; /* fixme: we can lock only on delete IMO */
	for(h=0; h<TCP_ID_HASH_SIZE; h++){
		c=tcpconn_id_hash[h];
		while(c){
			next=c->id_next;
			if (force ||((c->refcnt==0) && (ticks>c->timeout))) {
				if (!force)
					LM_DBG("timeout for hash=%d - %p"
							" (%d > %d)\n", h, c, ticks, c->timeout);
				fd=c->s;
#ifdef USE_TLS
				if (c->type==PROTO_TLS)
					tls_close(c, fd);
#endif
				_tcpconn_rm(c);
				if ((!force)&&(fd>0)&&(c->refcnt==0)) {
					if (!(c->flags & F_CONN_REMOVED)){
						io_watch_del(&io_h, fd, -1, IO_FD_CLOSING);
						c->flags|=F_CONN_REMOVED;
					}
					close(fd);
				}
				tcp_connections_no--;
			}
			c=next;
		}
	}
	TCPCONN_UNLOCK;
}
Ejemplo n.º 11
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);
	
	}
}
Ejemplo n.º 12
0
/*! \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;
		}
	}
}