/** * Read from an OS socket * @TODO remove tds, save error somewhere, report error in another way * @returns 0 if blocking, <0 error >0 bytes read */ static int tds_socket_read(TDSCONNECTION * conn, TDSSOCKET *tds, unsigned char *buf, int buflen) { int len, err; #if ENABLE_EXTRA_CHECKS /* this simulate the fact that recv can return less bytes */ if (buflen >= 5) { static int cnt = 0; if (++cnt == 5) { cnt = 0; buflen -= 3; } } #endif /* read directly from socket*/ len = READSOCKET(conn->s, buf, buflen); if (len > 0) return len; err = sock_errno; if (len < 0 && TDSSOCK_WOULDBLOCK(err)) return 0; /* detect connection close */ tds_connection_close(conn); tdserror(conn->tds_ctx, tds, len == 0 ? TDSESEOF : TDSEREAD, len == 0 ? 0 : err); return -1; }
/* * pool_main_loop * Accept new connections from clients, and handle all input from clients and * pool members. */ static void pool_main_loop(TDS_POOL * pool) { TDS_POOL_MEMBER *pmbr; TDS_POOL_USER *puser; TDS_SYS_SOCKET s, wakeup; SELECT_INFO sel; s = pool->listen_fd; wakeup = pool->wakeup_fd; while (!got_sigterm) { FD_ZERO(&sel.rfds); FD_ZERO(&sel.wfds); /* add the listening socket to the read list */ FD_SET(s, &sel.rfds); FD_SET(wakeup, &sel.rfds); sel.maxfd = s > wakeup ? s : wakeup; /* add the user sockets to the read list */ DLIST_FOREACH(dlist_user, &pool->users, puser) pool_select_add_socket(&sel, &puser->sock); /* add the pool member sockets to the read list */ DLIST_FOREACH(dlist_member, &pool->active_members, pmbr) pool_select_add_socket(&sel, &pmbr->sock); /* FIXME check return value */ select(sel.maxfd + 1, &sel.rfds, &sel.wfds, NULL, NULL); if (TDS_UNLIKELY(got_sigterm)) break; if (TDS_UNLIKELY(got_sighup)) { got_sighup = 0; pool_open_logfile(pool); } /* process events */ if (FD_ISSET(wakeup, &sel.rfds)) { char buf[32]; READSOCKET(wakeup, buf, sizeof(buf)); pool_process_events(pool); } /* process the sockets */ if (FD_ISSET(s, &sel.rfds)) { pool_user_create(pool, s); } pool_process_users(pool, &sel.rfds, &sel.wfds); pool_process_members(pool, &sel.rfds, &sel.wfds); /* back from members */ if (dlist_user_first(&pool->waiters)) pool_schedule_waiters(pool); } /* while !got_sigterm */ tdsdump_log(TDS_DBG_INFO2, "Shutdown Requested\n"); }
/** * Loops until we have received some characters * return -1 on failure */ int tds_goodread(TDSSOCKET * tds, unsigned char *buf, int buflen) { if (tds == NULL || buf == NULL || buflen < 1) return -1; for (;;) { int len, err; /* FIXME this block writing from other sessions */ len = tds_select(tds, TDSSELREAD, tds->query_timeout); #if !ENABLE_ODBC_MARS if (len > 0 && (len & TDSPOLLURG)) { char buf[32]; READSOCKET(tds->conn->s_signaled, buf, sizeof(buf)); /* send cancel */ if (!tds->in_cancel) tds_put_cancel(tds); continue; } #endif if (len > 0) { len = tds_socket_read(tds->conn, tds, buf, buflen); if (len == 0) continue; return len; } /* error */ if (len < 0) { if (TDSSOCK_WOULDBLOCK(sock_errno)) /* shouldn't happen, but OK */ continue; err = sock_errno; tds_connection_close(tds->conn); tdserror(tds_get_ctx(tds), tds, TDSEREAD, err); return -1; } /* timeout */ switch (tdserror(tds_get_ctx(tds), tds, TDSETIME, sock_errno)) { case TDS_INT_CONTINUE: break; default: case TDS_INT_CANCEL: tds_close_socket(tds); return -1; } } }
/* accept a socket and read data as much as you can */ static TDS_THREAD_PROC_DECLARE(fake_thread_proc, arg) { TDS_SYS_SOCKET s = ptr2int(arg), sock; socklen_t len; char buf[128]; struct sockaddr_in sin; struct pollfd fd; memset(&sin, 0, sizeof(sin)); len = sizeof(sin); fd.fd = s; fd.events = POLLIN; fd.revents = 0; if (poll(&fd, 1, 30000) <= 0) { perror("poll"); exit(1); } if (TDS_IS_SOCKET_INVALID(sock = tds_accept(s, (struct sockaddr *) &sin, &len))) { perror("accept"); exit(1); } tds_mutex_lock(&mtx); fake_sock = sock; tds_mutex_unlock(&mtx); CLOSESOCKET(s); for (;;) { int len; fd.fd = sock; fd.events = POLLIN; fd.revents = 0; if (poll(&fd, 1, 30000) <= 0) { perror("poll"); exit(1); } /* just read and discard */ len = READSOCKET(sock, buf, sizeof(buf)); if (len == 0) break; if (len < 0 && sock_errno != TDSSOCK_EINPROGRESS) break; } return NULL; }
/* read data from a nonblocking socket Inputs: <fd> socket <buf> point to the buffer which stores data <len> size of the buffer Returns: < 0 on failure otherwise the number of bytes read */ int tcp_nonblocking_read(int fd, char *buf, int len, void *dummy) { int n; (void) dummy; n = READSOCKET (fd, buf, len); if (n == -1) { return (NET_ERRNO != EWOULDBLOCK)? -1:0; } else if (n == 0) /* probable socket close */ { return -1; } return n; }
static int tds_connection_signaled(TDSCONNECTION *conn) { int len; char to_cancel[16]; #if defined(__linux__) && HAVE_EVENTFD if (conn->wakeup.s_signal == -1) return read(conn->wakeup.s_signaled, to_cancel, 8) > 0; #endif len = READSOCKET(conn->wakeup.s_signaled, to_cancel, sizeof(to_cancel)); do { /* no cancel found */ if (len <= 0) return 0; } while(!to_cancel[--len]); return 1; }
static void tds_check_cancel(TDSCONNECTION *conn) { TDSSOCKET *tds; int rc, len; char to_cancel[16]; len = READSOCKET(conn->s_signaled, to_cancel, sizeof(to_cancel)); do { /* no cancel found */ if (len <= 0) return; } while(!to_cancel[--len]); do { unsigned n = 0; rc = TDS_SUCCESS; tds_mutex_lock(&conn->list_mtx); /* Here we scan all list searching for sessions that should send cancel packets */ for (; n < conn->num_sessions; ++n) if (TDSSOCKET_VALID(tds=conn->sessions[n]) && tds->in_cancel == 1) { /* send cancel */ tds->in_cancel = 2; tds_mutex_unlock(&conn->list_mtx); rc = tds_append_cancel(tds); tds_mutex_lock(&conn->list_mtx); if (rc != TDS_SUCCESS) break; } tds_mutex_unlock(&conn->list_mtx); /* for all failed */ /* this must be done outside loop cause it can alter list */ /* this must be done unlocked cause it can lock again */ if (rc != TDS_SUCCESS) tds_close_socket(tds); } while(rc != TDS_SUCCESS); }
/** * Loops until we have received buflen characters * return -1 on failure */ static int tds_goodread(TDSSOCKET * tds, unsigned char *buf, int buflen, unsigned char unfinished) { double start, global_start; int got = 0; int canceled = 0; if (buf == NULL || buflen < 1 || IS_TDSDEAD(tds)) return 0; global_start = start = GetTimeMark(); while (buflen > 0) { int len; double now; if (IS_TDSDEAD(tds)) return -1; if ((len = tds_select(tds, TDSSELREAD, tds->query_timeout, global_start)) > 0) { len = READSOCKET(tds->s, buf + got, buflen); if (len < 0 && TDSSOCK_WOULDBLOCK(sock_errno)) continue; /* detect connection close */ if (len <= 0) { tds_close_socket(tds); if (len == 0) { tds_client_msg(tds->tds_ctx, tds, 20011, 6, 0, 0, "EOF in the socket."); } else { tds_report_error(tds->tds_ctx, tds, sock_errno, 20012, "recv finished with an error."); } return -1; } } else if (len < 0) { if (TDSSOCK_WOULDBLOCK(sock_errno)) /* shouldn't happen, but OK */ continue; tds_close_socket(tds); tds_report_error(tds->tds_ctx, tds, sock_errno, 20012, "recv finished with an error."); return -1; } else { /* timeout */ now = GetTimeMark(); if (tds->query_timeout > 0 && now - start >= tds->query_timeout) { int timeout_action = TDS_INT_CONTINUE; if (canceled) return got ? got : -1; if (tds->query_timeout_func && tds->query_timeout) timeout_action = (*tds->query_timeout_func) (tds->query_timeout_param, (int)(now - global_start)); switch (timeout_action) { case TDS_INT_EXIT: exit(EXIT_FAILURE); break; case TDS_INT_CANCEL: tds_send_cancel(tds); canceled = 1; /* fall through to wait while cancelling happens */ case TDS_INT_CONTINUE: start = now; default: break; } } } buflen -= len; got += len; if (unfinished && got) return got; } return got; }
static TDS_THREAD_PROC_DECLARE(fake_thread_proc, arg) { TDS_SYS_SOCKET s = ptr2int(arg), server_sock; socklen_t sock_len; int len; char buf[128]; struct sockaddr_in sin; fd_set fds_read, fds_write, fds_error; TDS_SYS_SOCKET max_fd = 0; memset(&sin, 0, sizeof(sin)); sock_len = sizeof(sin); alarm(30); fprintf(stderr, "waiting connect...\n"); if ((fake_sock = tds_accept(s, (struct sockaddr *) &sin, &sock_len)) < 0) { perror("accept"); exit(1); } CLOSESOCKET(s); if (TDS_IS_SOCKET_INVALID(server_sock = socket(remote_addr.sa.sa_family, SOCK_STREAM, 0))) { perror("socket"); exit(1); } fprintf(stderr, "connecting to server...\n"); if (remote_addr.sa.sa_family == AF_INET) { fprintf(stderr, "connecting to %x:%d\n", remote_addr.sin.sin_addr.s_addr, ntohs(remote_addr.sin.sin_port)); } if (connect(server_sock, &remote_addr.sa, remote_addr_len)) { perror("connect"); exit(1); } alarm(0); if (fake_sock > max_fd) max_fd = fake_sock; if (server_sock > max_fd) max_fd = server_sock; for (;;) { int res; FD_ZERO(&fds_read); FD_SET(fake_sock, &fds_read); FD_SET(server_sock, &fds_read); FD_ZERO(&fds_write); FD_ZERO(&fds_error); FD_SET(fake_sock, &fds_error); FD_SET(server_sock, &fds_error); alarm(30); res = select(max_fd + 1, &fds_read, &fds_write, &fds_error, NULL); alarm(0); if (res < 0) { if (sock_errno == TDSSOCK_EINTR) continue; perror("select"); exit(1); } if (FD_ISSET(fake_sock, &fds_error) || FD_ISSET(server_sock, &fds_error)) { fprintf(stderr, "error in select\n"); exit(1); } /* just read and forward */ if (FD_ISSET(fake_sock, &fds_read)) { if (flow != sending) { tds_mutex_lock(&mtx); ++round_trips; tds_mutex_unlock(&mtx); } flow = sending; len = READSOCKET(fake_sock, buf, sizeof(buf)); if (len == 0) { fprintf(stderr, "client connection closed\n"); break; } if (len < 0 && sock_errno != TDSSOCK_EINPROGRESS) { fprintf(stderr, "read client error %d\n", sock_errno); break; } count_insert(buf, len); write_all(server_sock, buf, len); } if (FD_ISSET(server_sock, &fds_read)) { if (flow != receiving) { tds_mutex_lock(&mtx); ++round_trips; tds_mutex_unlock(&mtx); } flow = receiving; len = READSOCKET(server_sock, buf, sizeof(buf)); if (len == 0) { fprintf(stderr, "server connection closed\n"); break; } if (len < 0 && sock_errno != TDSSOCK_EINPROGRESS) { fprintf(stderr, "read server error %d\n", sock_errno); break; } write_all(fake_sock, buf, len); } } CLOSESOCKET(fake_sock); CLOSESOCKET(server_sock); return NULL; }