Пример #1
0
/* 
 * 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;
}
Пример #2
0
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);
}
Пример #3
0
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;
}
Пример #4
0
/**
 * 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
	}
}
Пример #5
0
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;
}
Пример #6
0
/* 
 * 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 */
}
Пример #7
0
/*
 * 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;
}
Пример #8
0
/* 
 * 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);
		}
	}
}
Пример #9
0
/**
 * 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; 
}
Пример #10
0
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); */
}
Пример #11
0
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;
}
Пример #12
0
/**
 * 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;
}
Пример #13
0
/**
 * 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;
}
Пример #14
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); */
}
Пример #15
0
/**
 * 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;
}
Пример #16
0
/* 
 * 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;
}