/* old code known to work, kept arround for debuging */ void tcp_receive_loop(int unix_sock) { struct tcp_connection* list; /* list with connections in use */ struct tcp_connection* con; struct tcp_connection* c_next; int n; int nfds; int s; long resp; fd_set master_set; fd_set sel_set; int maxfd; struct timeval timeout; int ticks; /* init */ list=con=0; FD_ZERO(&master_set); FD_SET(unix_sock, &master_set); maxfd=unix_sock; /* listen on the unix socket for the fd */ for(;;){ timeout.tv_sec=TCP_CHILD_SELECT_TIMEOUT; timeout.tv_usec=0; sel_set=master_set; nfds=select(maxfd+1, &sel_set, 0 , 0 , &timeout); #ifdef EXTRA_DEBUG for (n=0; n<maxfd; n++){ if (FD_ISSET(n, &sel_set)) LM_DBG("fd %d is set\n", n); } #endif if (nfds<0){ if (errno==EINTR) continue; /* just a signal */ /* errors */ LM_ERR("select:(%d) %s\n", errno, strerror(errno)); continue; } if (FD_ISSET(unix_sock, &sel_set)){ nfds--; /* a new conn from "main" */ n=receive_fd(unix_sock, &con, sizeof(con), &s, 0); if (n<0){ if (errno == EWOULDBLOCK || errno == EAGAIN || errno == EINTR){ goto skip; }else{ LM_CRIT("read_fd: %s\n",strerror(errno)); abort(); /* big error*/ } } LM_DBG("received n=%d con=%p, fd=%d\n", n, con, s); if (n==0){ LM_WARN("0 bytes read\n"); goto skip; } if (con==0){ LM_CRIT("null pointer\n"); goto skip; } con->fd=s; if (s==-1) { LM_ERR("read_fd: no fd read\n"); resp=CONN_ERROR; con->state=S_CONN_BAD; release_tcpconn(con, resp, unix_sock); goto skip; } con->timeout=get_ticks()+TCP_CHILD_TIMEOUT; FD_SET(s, &master_set); if (maxfd<s) maxfd=s; if (con==list){ LM_CRIT("duplicate" " connection received: %p, id %d, fd %d, refcnt %d" " state %d (n=%d)\n", con, con->id, con->fd, con->refcnt, con->state, n); resp=CONN_ERROR; release_tcpconn(con, resp, unix_sock); goto skip; /* try to recover */ } tcpconn_listadd(list, con, c_next, c_prev); } skip: ticks=get_ticks(); for (con=list; con ; con=c_next){ c_next=con->c_next; /* safe for removing*/ #ifdef EXTRA_DEBUG LM_DBG("list fd=%d, id=%d, timeout=%d, refcnt=%d\n", con->fd, con->id, con->timeout, con->refcnt); #endif if (con->state<0){ /* S_CONN_BAD or S_CONN_ERROR, remove it */ resp=CONN_ERROR; FD_CLR(con->fd, &master_set); tcpconn_listrm(list, con, c_next, c_prev); con->state=S_CONN_BAD; release_tcpconn(con, resp, unix_sock); continue; } if (nfds && FD_ISSET(con->fd, &sel_set)){ #ifdef EXTRA_DEBUG LM_DBG("match, fd:isset\n"); #endif nfds--; resp=tcp_read_req(con); if (resp<0){ FD_CLR(con->fd, &master_set); tcpconn_listrm(list, con, c_next, c_prev); con->state=S_CONN_BAD; release_tcpconn(con, resp, unix_sock); }else{ /* update timeout */ con->timeout=ticks+TCP_CHILD_TIMEOUT; } }else{ /* timeout */ if (con->timeout<=ticks){ /* expired, return to "tcp main" */ LM_DBG("%p expired (%d, %d)\n", con, con->timeout, ticks); resp=CONN_RELEASE; FD_CLR(con->fd, &master_set); tcpconn_listrm(list, con, c_next, c_prev); release_tcpconn(con, resp, unix_sock); } } } } }
/*! \brief * handle io routine, based on the fd_map type * (it will be called from io_wait_loop* ) * params: fm - pointer to a fd hash entry * idx - index in the fd_array (or -1 if not known) * return: -1 on error, or when we are not interested any more on reads * from this fd (e.g.: we are closing it ) * 0 on EAGAIN or when by some other way it is known that no more * io events are queued on the fd (the receive buffer is empty). * Usefull to detect when there are no more io events queued for * sigio_rt, epoll_et, kqueue. * >0 on successfull read from the fd (when there might be more io * queued -- the receive buffer might still be non-empty) */ inline static int handle_io(struct fd_map* fm, int idx) { int ret; int n; struct tcp_connection* con; int s; long resp; switch(fm->type){ case F_TCPMAIN: again: ret=n=receive_fd(fm->fd, &con, sizeof(con), &s, 0); LM_DBG("received n=%d con=%p, fd=%d\n", n, con, s); if (n<0){ if (errno == EWOULDBLOCK || errno == EAGAIN){ ret=0; break; }else if (errno == EINTR) goto again; else{ LM_CRIT("read_fd: %s \n", strerror(errno)); abort(); /* big error*/ } } if (n==0){ LM_WARN("0 bytes read\n"); break; } if (con==0){ LM_CRIT("null pointer\n"); break; } con->fd=s; if (s==-1) { LM_ERR("read_fd:no fd read\n"); goto con_error; } if (con==tcp_conn_lst){ LM_CRIT("duplicate" " connection received: %p, id %d, fd %d, refcnt %d" " state %d (n=%d)\n", con, con->id, con->fd, con->refcnt, con->state, n); release_tcpconn(con, CONN_ERROR, tcpmain_sock); break; /* try to recover */ } /* reset the per process TCP req struct */ init_tcp_req(¤t_req); /* 0 attempts so far for this SIP MSG */ con->msg_attempts = 0; /* must be before io_watch_add, io_watch_add might catch some * already existing events => might call handle_io and * handle_io might decide to del. the new connection => * must be in the list */ tcpconn_listadd(tcp_conn_lst, con, c_next, c_prev); con->timeout=get_ticks()+TCP_CHILD_MAX_MSG_TIME; if (io_watch_add(&io_w, s, F_TCPCONN, con)<0){ LM_CRIT("failed to add new socket to the fd list\n"); tcpconn_listrm(tcp_conn_lst, con, c_next, c_prev); goto con_error; } break; case F_TCPCONN: con=(struct tcp_connection*)fm->data; resp=tcp_read_req(con, &ret); if (resp<0) { ret=-1; /* some error occured */ io_watch_del(&io_w, con->fd, idx, IO_FD_CLOSING); tcpconn_listrm(tcp_conn_lst, con, c_next, c_prev); con->state=S_CONN_BAD; release_tcpconn(con, resp, tcpmain_sock); } break; case F_NONE: LM_CRIT("empty fd map %p (%d): " "{%d, %d, %p}\n", fm, (int)(fm-io_w.fd_hash), fm->fd, fm->type, fm->data); goto error; default: LM_CRIT("uknown fd type %d\n", fm->type); goto error; } return ret; con_error: con->state=S_CONN_BAD; release_tcpconn(con, CONN_ERROR, fm->fd); return ret; error: return -1; }
/* handle io routine, based on the fd_map type * (it will be called from io_wait_loop* ) * params: fm - pointer to a fd hash entry * idx - index in the fd_array (or -1 if not known) * return: -1 on error, or when we are not interested any more on reads * from this fd (e.g.: we are closing it ) * 0 on EAGAIN or when by some other way it is known that no more * io events are queued on the fd (the receive buffer is empty). * Usefull to detect when there are no more io events queued for * sigio_rt, epoll_et, kqueue. * >0 on successfull read from the fd (when there might be more io * queued -- the receive buffer might still be non-empty) */ inline static int handle_io(struct fd_map* fm, short events, int idx) { int ret; int n; int read_flags; struct tcp_connection* con; int s; long resp; ticks_t t; /* update the local config */ cfg_update(); switch(fm->type){ case F_TCPMAIN: again: ret=n=receive_fd(fm->fd, &con, sizeof(con), &s, 0); LM_DBG("received n=%d con=%p, fd=%d\n", n, con, s); if (unlikely(n<0)){ if (errno == EWOULDBLOCK || errno == EAGAIN){ ret=0; break; }else if (errno == EINTR) goto again; else{ LM_CRIT("read_fd: %s \n", strerror(errno)); abort(); /* big error*/ } } if (unlikely(n==0)){ LM_ERR("0 bytes read\n"); goto error; } if (unlikely(con==0)){ LM_CRIT("null pointer\n"); goto error; } con->fd=s; if (unlikely(s==-1)) { LM_ERR("read_fd: no fd read\n"); goto con_error; } con->reader_pid=my_pid(); if (unlikely(con==tcp_conn_lst)){ LM_CRIT("duplicate connection received: %p, id %d, fd %d, refcnt %d" " state %d (n=%d)\n", con, con->id, con->fd, atomic_get(&con->refcnt), con->state, n); goto con_error; break; /* try to recover */ } if (unlikely(con->state==S_CONN_BAD)){ LM_WARN("received an already bad connection: %p id %d refcnt %d\n", con, con->id, atomic_get(&con->refcnt)); goto con_error; } /* if we received the fd there is most likely data waiting to * be read => process it first to avoid extra sys calls */ read_flags=((con->flags & (F_CONN_EOF_SEEN|F_CONN_FORCE_EOF)) && !(con->flags & F_CONN_OOB_DATA))? RD_CONN_FORCE_EOF :0; #ifdef USE_TLS repeat_1st_read: #endif /* USE_TLS */ resp=tcp_read_req(con, &n, &read_flags); if (unlikely(resp<0)){ /* some error occured, but on the new fd, not on the tcp * main fd, so keep the ret value */ if (unlikely(resp!=CONN_EOF)) con->state=S_CONN_BAD; LM_WARN("%s:%d %s releasing\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); release_tcpconn(con, resp, tcpmain_sock); break; } #ifdef USE_TLS /* repeat read if requested (for now only tls might do this) */ if (unlikely(read_flags & RD_CONN_REPEAT_READ)) goto repeat_1st_read; #endif /* USE_TLS */ /* must be before io_watch_add, io_watch_add might catch some * already existing events => might call handle_io and * handle_io might decide to del. the new connection => * must be in the list */ tcpconn_listadd(tcp_conn_lst, con, c_next, c_prev); t=get_ticks_raw(); con->timeout=t+S_TO_TICKS(TCP_CHILD_TIMEOUT); /* re-activate the timer */ con->timer.f=tcpconn_read_timeout; local_timer_reinit(&con->timer); local_timer_add(&tcp_reader_ltimer, &con->timer, S_TO_TICKS(TCP_CHILD_TIMEOUT), t); if (unlikely(io_watch_add(&io_w, s, POLLIN, F_TCPCONN, con)<0)){ LM_CRIT("io_watch_add failed for %p id %d fd %d, state %d, flags %x," " main fd %d, refcnt %d\n", con, con->id, con->fd, con->state, con->flags, con->s, atomic_get(&con->refcnt)); tcpconn_listrm(tcp_conn_lst, con, c_next, c_prev); local_timer_del(&tcp_reader_ltimer, &con->timer); goto con_error; } break; case F_TCPCONN: con=(struct tcp_connection*)fm->data; if (unlikely(con->state==S_CONN_BAD)){ resp=CONN_ERROR; if (!(con->send_flags.f & SND_F_CON_CLOSE)) LM_WARN("F_TCPCONN connection marked as bad: %p id %d refcnt %d\n", con, con->id, atomic_get(&con->refcnt)); goto read_error; } read_flags=(( #ifdef POLLRDHUP (events & POLLRDHUP) | #endif /* POLLRDHUP */ (events & (POLLHUP|POLLERR)) | (con->flags & (F_CONN_EOF_SEEN|F_CONN_FORCE_EOF))) && !(events & POLLPRI))? RD_CONN_FORCE_EOF: 0; #ifdef USE_TLS repeat_read: #endif /* USE_TLS */ resp=tcp_read_req(con, &ret, &read_flags); if (unlikely(resp<0)){ read_error: ret=-1; /* some error occured */ if (unlikely(io_watch_del(&io_w, con->fd, idx, IO_FD_CLOSING) < 0)){ LM_CRIT("io_watch_del failed for %p id %d fd %d," " state %d, flags %x, main fd %d, refcnt %d\n", con, con->id, con->fd, con->state, con->flags, con->s, atomic_get(&con->refcnt)); } tcpconn_listrm(tcp_conn_lst, con, c_next, c_prev); local_timer_del(&tcp_reader_ltimer, &con->timer); if (unlikely(resp!=CONN_EOF)) con->state=S_CONN_BAD; LM_WARN("%s:%d %s releasing\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); release_tcpconn(con, resp, tcpmain_sock); }else{ #ifdef USE_TLS if (unlikely(read_flags & RD_CONN_REPEAT_READ)) goto repeat_read; #endif /* USE_TLS */ /* update timeout */ con->timeout=get_ticks_raw()+S_TO_TICKS(TCP_CHILD_TIMEOUT); /* ret= 0 (read the whole socket buffer) if short read & * !POLLPRI, bytes read otherwise */ ret&=(((read_flags & RD_CONN_SHORT_READ) && !(events & POLLPRI)) - 1); } break; case F_NONE: LM_CRIT("empty fd map %p (%d): {%d, %d, %p}\n", fm, (int)(fm-io_w.fd_hash), fm->fd, fm->type, fm->data); goto error; default: LM_CRIT("uknown fd type %d\n", fm->type); goto error; } return ret; con_error: con->state=S_CONN_BAD; LM_WARN("%s:%d %s releasing\n", __FILE__, __LINE__, __PRETTY_FUNCTION__); release_tcpconn(con, CONN_ERROR, tcpmain_sock); return ret; error: return -1; }