/* * pool_process_members * check the fd_set for members returning data to the client, lookup the * client holding this member and forward the results. */ int pool_process_members(TDS_POOL * pool, fd_set * fds) { TDS_POOL_MEMBER *pmbr; TDS_POOL_USER *puser; TDSSOCKET *tds; int i, age, ret; int cnt = 0; time_t time_now; for (i = 0; i < pool->num_members; i++) { pmbr = &pool->members[i]; tds = pmbr->tds; if (!tds) { assert(pmbr->state == TDS_IDLE); continue; /* dead connection */ } time_now = time(NULL); if (FD_ISSET(tds_get_s(tds), fds)) { pmbr->last_used_tm = time_now; cnt++; if (pool_packet_read(tds)) continue; if (tds->in_len == 0) { fprintf(stderr, "Uh oh! member %d disconnected\n", i); /* mark as dead */ pool_free_member(pmbr); } else if (tds->in_len < 0) { fprintf(stderr, "Uh oh! member %d disconnected\n", i); perror("read"); pool_free_member(pmbr); } else { tdsdump_dump_buf(TDS_DBG_NETWORK, "Got packet from server:", tds->in_buf, tds->in_len); /* fprintf(stderr, "read %d bytes from member %d\n", tds->in_len, i); */ puser = pmbr->current_user; if (puser) { tdsdump_log(TDS_DBG_INFO1, "writing it sock %d\n", tds_get_s(puser->tds)); /* cf. net.c for better technique. */ /* FIXME handle partial write, stop read on member */ ret = pool_write_all(tds_get_s(puser->tds), tds->in_buf, tds->in_len); if (ret < 0) { /* couldn't write, ditch the user */ fprintf(stdout, "member %d received error while writing\n",i); pool_free_member(pmbr); } } } } age = time_now - pmbr->last_used_tm; if (age > pool->max_member_age && i >= pool->min_open_conn && !pmbr->current_user) { fprintf(stderr, "member %d is %d seconds old...closing\n", i, age); pool_free_member(pmbr); } } return cnt; }
static void pool_select_add_socket(SELECT_INFO *sel, TDS_POOL_SOCKET *sock) { /* skip dead connections */ if (IS_TDSDEAD(sock->tds)) return; if (!sock->poll_recv && !sock->poll_send) return; if (tds_get_s(sock->tds) > sel->maxfd) sel->maxfd = tds_get_s(sock->tds); if (sock->poll_recv) FD_SET(tds_get_s(sock->tds), &sel->rfds); if (sock->poll_send) FD_SET(tds_get_s(sock->tds), &sel->wfds); }
bool pool_write_data(TDS_POOL_SOCKET *from, TDS_POOL_SOCKET *to) { int ret; TDSSOCKET *tds; tdsdump_log(TDS_DBG_INFO1, "trying to send\n"); tds = from->tds; tdsdump_log(TDS_DBG_INFO1, "sending %d bytes\n", tds->in_len); /* cf. net.c for better technique. */ ret = pool_write(tds_get_s(to->tds), tds->in_buf + tds->in_pos, tds->in_len - tds->in_pos); /* write failed, cleanup member */ if (ret < 0) return false; tds->in_pos += ret; if (tds->in_pos < tds->in_len) { /* partial write, schedule a future write */ to->poll_send = true; from->poll_recv = false; } else { to->poll_send = false; from->poll_recv = true; } return true; }
/** * Close current socket * for last socket close entire connection * for MARS send FIN request */ void tds_close_socket(TDSSOCKET * tds) { if (!IS_TDSDEAD(tds)) { #if ENABLE_ODBC_MARS TDSCONNECTION *conn = tds->conn; unsigned n = 0, count = 0; tds_mutex_lock(&conn->list_mtx); for (; n < conn->num_sessions; ++n) if (TDSSOCKET_VALID(conn->sessions[n])) ++count; if (count > 1) tds_append_fin(tds); tds_mutex_unlock(&conn->list_mtx); if (count <= 1) { tds_disconnect(tds); tds_connection_close(conn); } else { tds_set_state(tds, TDS_DEAD); } #else tds_disconnect(tds); if (CLOSESOCKET(tds_get_s(tds)) == -1) tdserror(tds_get_ctx(tds), tds, TDSECLOS, sock_errno); tds_set_s(tds, INVALID_SOCKET); tds_set_state(tds, TDS_DEAD); #endif } }
static int pool_packet_read(TDS_POOL_MEMBER * pmbr) { TDSSOCKET *tds; int packet_len; tds = pmbr->tds; if (pmbr->need_more) { tds->in_len += read(tds_get_s(tds), &tds->in_buf[tds->in_len], BLOCKSIZ - tds->in_len); } else { tds->in_len = read(tds_get_s(tds), tds->in_buf, BLOCKSIZ); } packet_len = ntohs(*(short *) &tds->in_buf[2]); if (tds->in_len < packet_len) { pmbr->need_more = 1; } else { pmbr->need_more = 0; } return pmbr->need_more; }
/* * pool_process_users * check the fd_set for user input, allocate a pool member to it, and forward * the query to that member. */ void pool_process_users(TDS_POOL * pool, fd_set * rfds, fd_set * wfds) { TDS_POOL_USER *puser, *next; for (next = dlist_user_first(&pool->users); (puser = next) != NULL; ) { next = dlist_user_next(&pool->users, puser); if (!puser->sock.tds) continue; /* dead connection */ if (puser->sock.poll_recv && FD_ISSET(tds_get_s(puser->sock.tds), rfds)) { assert(puser->user_state == TDS_SRV_QUERY); if (!pool_user_read(pool, puser)) continue; } if (puser->sock.poll_send && FD_ISSET(tds_get_s(puser->sock.tds), wfds)) { if (!pool_write_data(&puser->assigned_member->sock, &puser->sock)) pool_free_member(pool, puser->assigned_member); } } /* for */ }
/* * pool_user_read * checks the packet type of data coming from the client and allocates a * pool member if necessary. */ static bool pool_user_read(TDS_POOL * pool, TDS_POOL_USER * puser) { TDSSOCKET *tds = puser->sock.tds; TDS_POOL_MEMBER *pmbr = NULL; for (;;) { if (pool_packet_read(tds)) break; if (tds->in_len == 0) { tdsdump_log(TDS_DBG_INFO1, "user disconnected\n"); pool_free_user(pool, puser); return false; } else { TDS_UCHAR in_flag = tds->in_buf[0]; tdsdump_dump_buf(TDS_DBG_NETWORK, "Got packet from client:", tds->in_buf, tds->in_len); switch (in_flag) { case TDS_QUERY: case TDS_NORMAL: case TDS_RPC: case TDS_BULK: case TDS_CANCEL: case TDS7_TRANS: if (!pool_write_data(&puser->sock, &puser->assigned_member->sock)) { pool_reset_member(pool, puser->assigned_member); return false; } pmbr = puser->assigned_member; break; default: tdsdump_log(TDS_DBG_ERROR, "Unrecognized packet type, closing user\n"); pool_free_user(pool, puser); return false; } } } if (pmbr && !pmbr->sock.poll_send) tds_socket_flush(tds_get_s(pmbr->sock.tds)); return true; }
/* * 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_USER *puser; TDS_POOL_MEMBER *pmbr; struct sockaddr_in sin; int s, maxfd, i; fd_set rfds; int socktrue = 1; /* FIXME -- read the interfaces file and bind accordingly */ sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = htons(pool->port); sin.sin_family = AF_INET; if (TDS_IS_SOCKET_INVALID(s = socket(AF_INET, SOCK_STREAM, 0))) { perror("socket"); exit(1); } /* don't keep addr in use from [email protected] */ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const void *) &socktrue, sizeof(socktrue)); fprintf(stderr, "Listening on port %d\n", pool->port); if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) { perror("bind"); exit(1); } listen(s, 5); FD_ZERO(&rfds); FD_SET(s, &rfds); maxfd = s; while (!term) { /* fprintf(stderr, "waiting for a connect\n"); */ /* FIXME check return value */ select(maxfd + 1, &rfds, NULL, NULL, NULL); if (term) break; /* process the sockets */ if (FD_ISSET(s, &rfds)) { pool_user_create(pool, s, &sin); } pool_process_users(pool, &rfds); pool_process_members(pool, &rfds); /* back from members */ if (waiters) { pool_schedule_waiters(pool); } FD_ZERO(&rfds); /* add the listening socket to the read list */ FD_SET(s, &rfds); maxfd = s; /* add the user sockets to the read list */ for (i = 0; i < pool->max_users; i++) { puser = (TDS_POOL_USER *) & pool->users[i]; /* skip dead connections */ if (!IS_TDSDEAD(puser->tds)) { if (tds_get_s(puser->tds) > maxfd) maxfd = tds_get_s(puser->tds); FD_SET(tds_get_s(puser->tds), &rfds); } } /* add the pool member sockets to the read list */ for (i = 0; i < pool->num_members; i++) { pmbr = (TDS_POOL_MEMBER *) & pool->members[i]; if (!IS_TDSDEAD(pmbr->tds)) { if (tds_get_s(pmbr->tds) > maxfd) maxfd = tds_get_s(pmbr->tds); FD_SET(tds_get_s(pmbr->tds), &rfds); } } } /* while !term */ CLOSESOCKET(s); for (i = 0; i < pool->max_users; i++) { puser = (TDS_POOL_USER *) & pool->users[i]; if (!IS_TDSDEAD(puser->tds)) { fprintf(stderr, "Closing user %d\n", i); tds_close_socket(puser->tds); } } for (i = 0; i < pool->num_members; i++) { pmbr = (TDS_POOL_MEMBER *) & pool->members[i]; if (!IS_TDSDEAD(pmbr->tds)) { fprintf(stderr, "Closing member %d\n", i); tds_close_socket(pmbr->tds); } } }
/** * Set state of TDS connection, with logging and checking. * \param tds state information for the socket and the TDS protocol * \param state the new state of the connection, cf. TDS_STATE. * \return the new state, which might not be \a state. */ TDS_STATE tds_set_state(TDSSOCKET * tds, TDS_STATE state) { const TDS_STATE prior_state = tds->state; static const char state_names[][10] = { "IDLE", "QUERYING", "PENDING", "READING", "DEAD" }; assert(state < TDS_VECTOR_SIZE(state_names)); assert(tds->state < TDS_VECTOR_SIZE(state_names)); if (state == tds->state) return state; switch(state) { /* transition to READING are valid only from PENDING */ case TDS_PENDING: if (tds->state != TDS_READING && tds->state != TDS_QUERYING) { tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); return tds->state; } tds->state = state; break; case TDS_READING: if (tds->state != TDS_PENDING) { tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); return tds->state; } tds->state = state; break; case TDS_IDLE: if (tds->state == TDS_DEAD && TDS_IS_SOCKET_INVALID(tds_get_s(tds))) { tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); return tds->state; } case TDS_DEAD: tds->state = state; break; case TDS_QUERYING: CHECK_TDS_EXTRA(tds); if (tds->state == TDS_DEAD) { tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); tdserror(tds_get_ctx(tds), tds, TDSEWRIT, 0); break; } else if (tds->state != TDS_IDLE) { tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); tdserror(tds_get_ctx(tds), tds, TDSERPND, 0); break; } /* TODO check this code, copied from tds_submit_prepare */ tds_free_all_results(tds); tds->rows_affected = TDS_NO_COUNT; tds_release_cursor(tds, tds->cur_cursor); tds->cur_cursor = NULL; tds->internal_sp_called = 0; tds->state = state; break; default: assert(0); break; } tdsdump_log(TDS_DBG_ERROR, "Changed query state from %s to %s\n", state_names[prior_state], state_names[state]); CHECK_TDS_EXTRA(tds); return tds->state; }
void tds_check_tds_extra(const TDSSOCKET * tds) { const int invalid_state = 0; int i; TDSDYNAMIC *cur_dyn = NULL; TDSCURSOR *cur_cursor = NULL; assert(tds); /* test state and connection */ switch (tds->state) { case TDS_DEAD: case TDS_WRITING: case TDS_SENDING: case TDS_PENDING: case TDS_IDLE: case TDS_READING: break; default: assert(invalid_state); } assert(tds->conn); #if ENABLE_ODBC_MARS if (tds->state != TDS_DEAD) assert(!TDS_IS_SOCKET_INVALID(tds_get_s(tds))); #else assert(tds->state == TDS_DEAD || !TDS_IS_SOCKET_INVALID(tds_get_s(tds))); assert(tds->state != TDS_DEAD || TDS_IS_SOCKET_INVALID(tds_get_s(tds))); #endif /* test env */ tds_check_env_extra(&tds->conn->env); /* test buffers and positions */ tds_check_packet_extra(tds->send_packet); tds_check_packet_extra(tds->recv_packet); #if ENABLE_ODBC_MARS if (tds->conn->send_packets) assert(tds->conn->send_pos <= tds->conn->send_packets->len); if (tds->conn->recv_packet) assert(tds->conn->recv_pos <= tds->conn->recv_packet->len); #endif assert(tds->in_pos <= tds->in_len); assert(tds->in_len <= tds->recv_packet->capacity); /* TODO remove blocksize from env and use out_len ?? */ /* assert(tds->out_pos <= tds->out_len); */ /* assert(tds->out_len == 0 || tds->out_buf != NULL); */ assert(tds->send_packet->capacity >= tds->out_buf_max + TDS_ADDITIONAL_SPACE); assert(tds->out_buf >= tds->send_packet->buf); assert(tds->out_buf + tds->out_buf_max + TDS_ADDITIONAL_SPACE <= tds->send_packet->buf + tds->send_packet->capacity); assert(tds->out_pos <= tds->out_buf_max); assert(tds->in_buf == tds->recv_packet->buf || tds->in_buf == tds->recv_packet->buf + 16); assert(tds->recv_packet->capacity > 0); /* test res_info */ if (tds->res_info) tds_check_resultinfo_extra(tds->res_info); /* test num_comp_info, comp_info */ assert(tds->num_comp_info >= 0); for (i = 0; i < tds->num_comp_info; ++i) { assert(tds->comp_info); tds_check_resultinfo_extra(tds->comp_info[i]); } /* param_info */ if (tds->param_info) tds_check_resultinfo_extra(tds->param_info); /* test cursors */ for (cur_cursor = tds->conn->cursors; cur_cursor != NULL; cur_cursor = cur_cursor->next) tds_check_cursor_extra(cur_cursor); /* test dynamics */ for (cur_dyn = tds->conn->dyns; cur_dyn != NULL; cur_dyn = cur_dyn->next) tds_check_dynamic_extra(cur_dyn); /* test tds_ctx */ tds_check_context_extra(tds_get_ctx(tds)); /* TODO test char_conv_count, char_convs */ /* we can't have compute and no results */ assert(tds->num_comp_info == 0 || tds->res_info != NULL); /* we can't have normal and parameters results */ /* TODO too strict ?? */ /* assert(tds->param_info == NULL || tds->res_info == NULL); */ }
bool pool_packet_read(TDSSOCKET *tds) { int packet_len; int readed, err; tdsdump_log(TDS_DBG_INFO1, "tds in_len %d in_pos %d\n", tds->in_len, tds->in_pos); /* determine how much we should read */ packet_len = 8; if (tds->in_len >= 4) packet_len = TDS_GET_A2BE(&tds->in_buf[2]); if (tds->in_len >= packet_len) { tds->in_pos = 0; tds->in_len = 0; packet_len = 8; } tdsdump_log(TDS_DBG_INFO1, "packet_len %d capacity %d\n", packet_len, tds->recv_packet->capacity); assert(packet_len > tds->in_len); assert(packet_len <= tds->recv_packet->capacity); assert(tds->in_len < tds->recv_packet->capacity); readed = read(tds_get_s(tds), &tds->in_buf[tds->in_len], packet_len - tds->in_len); tdsdump_log(TDS_DBG_INFO1, "readed %d\n", readed); if (readed == 0) { /* socket closed */ tds->in_len = 0; return false; } if (readed < 0) { /* error */ err = sock_errno; if (err == EINTR || TDSSOCK_WOULDBLOCK(err)) return true; goto failure; } tds->in_len += readed; if (tds->in_len >= 4) { packet_len = TDS_GET_A2BE(&tds->in_buf[2]); if (packet_len < 8) goto failure; tdsdump_log(TDS_DBG_INFO1, "packet_len %d in_len %d after\n", packet_len, tds->in_len); /* resize packet if not enough */ if (packet_len > tds->recv_packet->capacity) { TDSPACKET *packet; packet = tds_realloc_packet(tds->recv_packet, packet_len); if (!packet) goto failure; tds->in_buf = packet->buf; tds->recv_packet = packet; } CHECK_TDS_EXTRA(tds); return tds->in_len < packet_len; } return true; failure: tds->in_len = -1; return false; }
/** * Read part of packet. Function does not block. * @return true if packet is not complete and we must call again, * false on full packet or error. */ bool pool_packet_read(TDSSOCKET *tds) { int packet_len; int readed, err; tdsdump_log(TDS_DBG_INFO1, "tds in_len %d in_pos %d\n", tds->in_len, tds->in_pos); // TODO MARS /* determine packet size */ packet_len = tds->in_len >= 4 ? TDS_GET_A2BE(&tds->in_buf[2]) : 8; if (TDS_UNLIKELY(packet_len < 8)) { tds->in_len = 0; return false; } /* get another packet */ if (tds->in_len >= packet_len) { tds->in_pos = 0; tds->in_len = 0; } for (;;) { /* determine packet size */ packet_len = 8; if (tds->in_len >= 4) { packet_len = TDS_GET_A2BE(&tds->in_buf[2]); if (packet_len < 8) break; tdsdump_log(TDS_DBG_INFO1, "packet_len %d in_len %d\n", packet_len, tds->in_len); /* resize packet if not enough */ if (packet_len > tds->recv_packet->capacity) { TDSPACKET *packet; packet = tds_realloc_packet(tds->recv_packet, packet_len); if (!packet) break; tds->in_buf = packet->buf; tds->recv_packet = packet; } CHECK_TDS_EXTRA(tds); if (tds->in_len >= packet_len) return false; } assert(packet_len > tds->in_len); assert(packet_len <= tds->recv_packet->capacity); assert(tds->in_len < tds->recv_packet->capacity); readed = read(tds_get_s(tds), &tds->in_buf[tds->in_len], packet_len - tds->in_len); tdsdump_log(TDS_DBG_INFO1, "readed %d\n", readed); /* socket closed */ if (readed == 0) break; /* error */ if (readed < 0) { err = sock_errno; if (err == EINTR) continue; if (TDSSOCK_WOULDBLOCK(err)) return true; break; } /* got some data */ tds->in_len += readed; } /* failure */ tds->in_len = 0; return false; }
/** * Select on a socket until it's available or the timeout expires. * Meanwhile, call the interrupt function. * \return >0 ready descriptors * 0 timeout * <0 error (cf. errno). Caller should close socket and return failure. * This function does not call tdserror or close the socket because it can't know the context in which it's being called. */ int tds_select(TDSSOCKET * tds, unsigned tds_sel, int timeout_seconds) { int rc, seconds; unsigned int poll_seconds; assert(tds != NULL); assert(timeout_seconds >= 0); /* * The select loop. * If an interrupt handler is installed, we iterate once per second, * else we try once, timing out after timeout_seconds (0 == never). * If select(2) is interrupted by a signal (e.g. press ^C in sqsh), we timeout. * (The application can retry if desired by installing a signal handler.) * * We do not measure current time against end time, to avoid being tricked by ntpd(8) or similar. * Instead, we just count down. * * We exit on the first of these events: * 1. a descriptor is ready. (return to caller) * 2. select(2) returns an important error. (return to caller) * A timeout of zero says "wait forever". We do that by passing a NULL timeval pointer to select(2). */ poll_seconds = (tds_get_ctx(tds) && tds_get_ctx(tds)->int_handler)? 1 : timeout_seconds; for (seconds = timeout_seconds; timeout_seconds == 0 || seconds > 0; seconds -= poll_seconds) { struct pollfd fds[2]; int timeout = poll_seconds ? poll_seconds * 1000 : -1; if (TDS_IS_SOCKET_INVALID(tds_get_s(tds))) return -1; if ((tds_sel & TDSSELREAD) != 0 && tds->conn->tls_session && tds_ssl_pending(tds->conn)) return POLLIN; fds[0].fd = tds_get_s(tds); fds[0].events = tds_sel; fds[0].revents = 0; fds[1].fd = tds->conn->s_signaled; fds[1].events = POLLIN; fds[1].revents = 0; rc = poll(fds, 2, timeout); if (rc > 0 ) { if (fds[0].revents & POLLERR) return -1; rc = fds[0].revents; if (fds[1].revents) { #if ENABLE_ODBC_MARS tds_check_cancel(tds->conn); #endif rc |= TDSPOLLURG; } return rc; } if (rc < 0) { char *errstr; switch (sock_errno) { case TDSSOCK_EINTR: /* FIXME this should be global maximun, not loop one */ seconds += poll_seconds; break; /* let interrupt handler be called */ default: /* documented: EFAULT, EBADF, EINVAL */ errstr = sock_strerror(sock_errno); tdsdump_log(TDS_DBG_ERROR, "error: poll(2) returned %d, \"%s\"\n", sock_errno, errstr); sock_strerror_free(errstr); return rc; } } assert(rc == 0 || (rc < 0 && sock_errno == TDSSOCK_EINTR)); if (tds_get_ctx(tds) && tds_get_ctx(tds)->int_handler) { /* interrupt handler installed */ /* * "If hndlintr() returns INT_CANCEL, DB-Library sends an attention token [TDS_BUFSTAT_ATTN] * to the server. This causes the server to discontinue command processing. * The server may send additional results that have already been computed. * When control returns to the mainline code, the mainline code should do * one of the following: * - Flush the results using dbcancel * - Process the results normally" */ int timeout_action = (*tds_get_ctx(tds)->int_handler) (tds_get_parent(tds)); switch (timeout_action) { case TDS_INT_CONTINUE: /* keep waiting */ continue; case TDS_INT_CANCEL: /* abort the current command batch */ /* FIXME tell tds_goodread() not to call tdserror() */ return 0; default: tdsdump_log(TDS_DBG_NETWORK, "tds_select: invalid interupt handler return code: %d\n", timeout_action); return -1; } } /* * We can reach here if no interrupt handler was installed and we either timed out or got EINTR. * We cannot be polling, so we are about to drop out of the loop. */ assert(poll_seconds == timeout_seconds); } return 0; }
void tds_check_tds_extra(const TDSSOCKET * tds) { const int invalid_state = 0; int found, i; int result_found = 0; TDSDYNAMIC *cur_dyn = NULL; TDSCURSOR *cur_cursor = NULL; assert(tds); /* teset state and connection */ switch (tds->state) { case TDS_DEAD: case TDS_QUERYING: case TDS_PENDING: case TDS_IDLE: case TDS_READING: break; default: assert(invalid_state); } assert(tds->state == TDS_DEAD || !TDS_IS_SOCKET_INVALID(tds_get_s(tds))); assert(tds->state != TDS_DEAD || TDS_IS_SOCKET_INVALID(tds_get_s(tds))); /* test env */ tds_check_env_extra(&tds->env); /* test buffers and positions */ assert(tds->in_pos <= tds->in_len && tds->in_len <= tds->in_buf_max); /* TODO remove blocksize from env and use out_len ?? */ /* assert(tds->out_pos <= tds->out_len); */ /* assert(tds->out_len == 0 || tds->out_buf != NULL); */ assert(tds->out_pos <= tds->env.block_size); assert(tds->env.block_size == 0 || tds->out_buf != NULL); assert(tds->in_buf_max == 0 || tds->in_buf != NULL); /* test res_info */ if (tds->res_info) { tds_check_resultinfo_extra(tds->res_info); if (tds->current_results == tds->res_info) result_found = 1; } /* test num_comp_info, comp_info */ assert(tds->num_comp_info >= 0); for (i = 0; i < tds->num_comp_info; ++i) { assert(tds->comp_info); tds_check_resultinfo_extra(tds->comp_info[i]); if (tds->current_results == tds->comp_info[i]) result_found = 1; } /* param_info */ if (tds->param_info) { tds_check_resultinfo_extra(tds->param_info); if (tds->current_results == tds->param_info) result_found = 1; } /* test cursors */ found = 0; for (cur_cursor = tds->cursors; cur_cursor != NULL; cur_cursor = cur_cursor->next) { tds_check_cursor_extra(cur_cursor); if (tds->current_results == cur_cursor->res_info) result_found = 1; if (cur_cursor == tds->cur_cursor) found = 1; } assert(found || tds->cur_cursor == NULL); /* test num_dyms, cur_dyn, dyns */ found = 0; for (cur_dyn = tds->dyns; cur_dyn != NULL; cur_dyn = cur_dyn->next) { if (cur_dyn == tds->cur_dyn) found = 1; tds_check_dynamic_extra(cur_dyn); if (tds->current_results == cur_dyn->res_info) result_found = 1; } assert(found || tds->cur_dyn == NULL); /* test tds_ctx */ tds_check_context_extra(tds_get_ctx(tds)); /* TODO test char_conv_count, char_convs */ /* current_results should be one of res_info, comp_info, param_info or dynamic */ /* * TODO this test was here to check that current_results was an alias * but with cursor and reference counting current_results can point * to a cursor result available on upper layer * Perhaps we should free results on deallocate and enable * assert again? */ /* assert(result_found || tds->current_results == NULL); */ /* we can't have compute and no results */ assert(tds->num_comp_info == 0 || tds->res_info != NULL); /* we can't have normal and parameters results */ /* TODO too strict ?? */ /* assert(tds->param_info == NULL || tds->res_info == NULL); */ }
/** * Set state of TDS connection, with logging and checking. * \param tds state information for the socket and the TDS protocol * \param state the new state of the connection, cf. TDS_STATE. * \return the new state, which might not be \a state. */ TDS_STATE tds_set_state(TDSSOCKET * tds, TDS_STATE state) { TDS_STATE prior_state; static const char state_names[][8] = { "IDLE", "WRITING", "SENDING", "PENDING", "READING", "DEAD" }; assert(state < TDS_VECTOR_SIZE(state_names)); assert(tds->state < TDS_VECTOR_SIZE(state_names)); prior_state = tds->state; if (state == prior_state) return state; switch(state) { case TDS_PENDING: if (prior_state == TDS_READING || prior_state == TDS_WRITING) { tds->state = TDS_PENDING; tds_mutex_unlock(&tds->wire_mtx); break; } tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); break; case TDS_READING: /* transition to READING are valid only from PENDING */ if (tds_mutex_trylock(&tds->wire_mtx)) return tds->state; if (tds->state != TDS_PENDING) { tds_mutex_unlock(&tds->wire_mtx); tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); break; } tds->state = state; break; case TDS_SENDING: if (prior_state != TDS_READING && prior_state != TDS_WRITING) { tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); break; } if (tds->state == TDS_READING) { /* TODO check this code, copied from tds_submit_prepare */ tds_free_all_results(tds); tds->rows_affected = TDS_NO_COUNT; tds_release_cursor(&tds->cur_cursor); tds_release_cur_dyn(tds); tds->current_op = TDS_OP_NONE; } tds_mutex_unlock(&tds->wire_mtx); tds->state = state; break; case TDS_IDLE: if (prior_state == TDS_DEAD && TDS_IS_SOCKET_INVALID(tds_get_s(tds))) { tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); break; } case TDS_DEAD: if (prior_state == TDS_READING || prior_state == TDS_WRITING) tds_mutex_unlock(&tds->wire_mtx); tds->state = state; break; case TDS_WRITING: CHECK_TDS_EXTRA(tds); if (tds_mutex_trylock(&tds->wire_mtx)) return tds->state; if (tds->state == TDS_DEAD) { tds_mutex_unlock(&tds->wire_mtx); tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); tdserror(tds_get_ctx(tds), tds, TDSEWRIT, 0); break; } else if (tds->state != TDS_IDLE && tds->state != TDS_SENDING) { tds_mutex_unlock(&tds->wire_mtx); tdsdump_log(TDS_DBG_ERROR, "logic error: cannot change query state from %s to %s\n", state_names[prior_state], state_names[state]); tdserror(tds_get_ctx(tds), tds, TDSERPND, 0); break; } if (tds->state == TDS_IDLE) { /* TODO check this code, copied from tds_submit_prepare */ tds_free_all_results(tds); tds->rows_affected = TDS_NO_COUNT; tds_release_cursor(&tds->cur_cursor); tds_release_cur_dyn(tds); tds->current_op = TDS_OP_NONE; } tds->state = state; break; default: assert(0); break; } state = tds->state; tdsdump_log(TDS_DBG_ERROR, "Changed query state from %s to %s\n", state_names[prior_state], state_names[state]); CHECK_TDS_EXTRA(tds); return state; }
/* * pool_process_members * check the fd_set for members returning data to the client, lookup the * client holding this member and forward the results. */ int pool_process_members(TDS_POOL * pool, fd_set * fds) { TDS_POOL_MEMBER *pmbr; TDS_POOL_USER *puser; TDSSOCKET *tds; int i, age, ret; int cnt = 0; unsigned char *buf; time_t time_now; for (i = 0; i < pool->num_members; i++) { pmbr = (TDS_POOL_MEMBER *) & pool->members[i]; if (!pmbr->tds) break; /* dead connection */ tds = pmbr->tds; time_now = time(NULL); if (FD_ISSET(tds_get_s(tds), fds)) { pmbr->last_used_tm = time_now; cnt++; /* tds->in_len = read(tds->s, tds->in_buf, BLOCKSIZ); */ if (pool_packet_read(pmbr)) continue; if (tds->in_len == 0) { fprintf(stderr, "Uh oh! member %d disconnected\n", i); /* mark as dead */ pool_free_member(pmbr); } else if (tds->in_len == -1) { fprintf(stderr, "Uh oh! member %d disconnected\n", i); perror("read"); pool_free_member(pmbr); } else { /* fprintf(stderr, "read %d bytes from member %d\n", tds->in_len, i); */ if (pmbr->current_user) { puser = pmbr->current_user; buf = tds->in_buf; /* * check the netlib final packet flag * instead of looking for done tokens. * It's more efficient and generic to * all protocol versions. -- bsb * 2004-12-12 */ if (buf[1]) { /* if (pool_find_end_token(pmbr, buf + 8, tds->in_len - 8)) { */ /* we are done...deallocate member */ fprintf(stdout, "deassigning user from member %d\n",i); pool_deassign_member(pmbr); pmbr->state = TDS_IDLE; puser->user_state = TDS_SRV_IDLE; } /* cf. net.c for better technique. */ ret = WRITESOCKET(tds_get_s(puser->tds), buf, tds->in_len); if (ret < 0) { /* couldn't write, ditch the user */ fprintf(stdout, "member %d received error while writing\n",i); pool_free_user(pmbr->current_user); pool_deassign_member(pmbr); pool_reset_member(pmbr); } } } } age = time_now - pmbr->last_used_tm; if (age > pool->max_member_age && i >= pool->min_open_conn) { fprintf(stderr, "member %d is %d seconds old...closing\n", i, age); pool_free_member(pmbr); } } return cnt; }