Пример #1
0
int
main(int argc, char **argv)
{
	TDSLOGIN *login;
	TDSSOCKET *tds;
	int ret;
	int verbose = 0;
	TDS_INT8 i8;
	unsigned limit;

	fprintf(stdout, "%s: Testing login, logout\n", __FILE__);
	ret = try_tds_login(&login, &tds, __FILE__, verbose);
	if (ret != TDS_SUCCESS) {
		fprintf(stderr, "try_tds_login() failed\n");
		return 1;
	}

	unfinished_query_test(tds);

	tds->out_flag = TDS_QUERY;
	if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING) {
		return 1;
	}

	tds_put_n(tds, "aaa", 3);
	limit = tds->out_buf_max / 8 + 100;
	for (i8 = 0; i8 < limit; ++i8) {
		CHECK_TDS_EXTRA(tds);
		tds_put_int8(tds, i8);
	}

	tds_send_cancel(tds);
	tds_process_simple_query(tds);

	try_tds_logout(login, tds, verbose);
	return 0;
}
Пример #2
0
/**
 * \brief Call the client library's error handler (for library-generated errors only)
 *
 * The client library error handler may return: 
 * TDS_INT_CANCEL -- Return TDS_FAIL to the calling function.  For TDSETIME, closes the connection first. 
 * TDS_INT_CONTINUE -- For TDSETIME only, retry the network read/write operation. Else invalid.
 * TDS_INT_TIMEOUT -- For TDSETIME only, send a TDSCANCEL packet. Else invalid.
 *
 * These are Sybase semantics, but they serve all purposes.  
 * The application tells the library to quit, fail, retry, or attempt to cancel.  In the event of a network timeout, 
 * a failed operation necessarily means the connection becomes unusable, because no cancellation dialog was 
 * concluded with the server.  
 *
 * It is the client library's duty to call the error handler installed by the application, if any, and to interpret the
 * installed handler's return code.  It may return to this function one of the above codes only.  This function will not 
 * check the return code because there's nothing that can be done here except abort.  It is merely passed to the 
 * calling function, which will (we hope) DTRT.  
 *
 * \param tds_ctx	points to a TDSCONTEXT structure
 * \param tds		the connection structure, may be NULL if not connected
 * \param msgno		an enumerated libtds msgno, cf. tds.h
 * \param errnum	the OS errno, if it matters, else zero
 * 
 * \returns client library function's return code
 */
int
tdserror (const TDSCONTEXT * tds_ctx, TDSSOCKET * tds, int msgno, int errnum)
{
#if 0
	static const char int_exit_text[] = "FreeTDS: libtds: exiting because client error handler returned %d for msgno %d\n";
	static const char int_invalid_text[] = "%s (%d) received from client library error handler for nontimeout for error %d."
					       "  Treating as INT_EXIT\n";
#endif
	const TDS_ERROR_MESSAGE *err;

	TDSMESSAGE msg;
	int rc = TDS_INT_CANCEL;

	tdsdump_log(TDS_DBG_FUNC, "tdserror(%p, %p, %d, %d)\n", tds_ctx, tds, msgno, errnum);

	/* look up the error message */
	for (err = tds_error_messages; err->msgno; ++err) {
		if (err->msgno == msgno)
			break;
	}

	CHECK_CONTEXT_EXTRA(tds_ctx);

	if (tds)
		CHECK_TDS_EXTRA(tds);

	if (tds_ctx && tds_ctx->err_handler) {
		memset(&msg, 0, sizeof(TDSMESSAGE));
		msg.msgno = msgno;
		msg.severity = err->severity;
		msg.state = -1;
		msg.server = "OpenClient";
		msg.line_number = -1;
		msg.message = (TDS_CHAR*) err->msgtext;
		msg.sql_state = tds_alloc_client_sqlstate(msg.msgno);
		
		msg.oserr = errnum;

		/*
		 * Call client library handler.
		 * The client library must return a valid code.  It is not checked again here.
		 */
		rc = tds_ctx->err_handler(tds_ctx, tds, &msg);
	 	tdsdump_log(TDS_DBG_FUNC, "tdserror: client library returned %s(%d)\n", retname(rc), rc);

		TDS_ZERO_FREE(msg.sql_state);
	} else {
		const static char msg[] = "tdserror: client library not called because either "
					  "tds_ctx (%p) or tds_ctx->err_handler is NULL\n";
	 	tdsdump_log(TDS_DBG_FUNC, msg, tds_ctx);
	}

  
  	assert(msgno == TDSETIME || rc != TDS_INT_TIMEOUT);   /* client library should prevent */
	assert(msgno == TDSETIME || rc != TDS_INT_CONTINUE);  /* client library should prevent */

	if (msgno != TDSETIME && rc != TDS_INT_CANCEL) {
		tdsdump_log(TDS_DBG_SEVERE, "exit: %s(%d) valid only for TDSETIME\n", retname(rc), rc);
		rc = TDS_INT_CANCEL;
	}

 	if (rc == TDS_INT_TIMEOUT) {
 		tds_send_cancel(tds);
 		rc = TDS_INT_CONTINUE;
 	} 

 	tdsdump_log(TDS_DBG_FUNC, "tdserror: returning %s(%d)\n", retname(rc), rc);
  
	return rc;
}
Пример #3
0
static int
tds_goodwrite(TDSSOCKET * tds, const unsigned char *buffer, int len, unsigned char last)
{
    double start, now;
	const unsigned char *p = buffer;
	int rc;

	assert(tds && buffer);

	if (TDS_IS_SOCKET_INVALID(tds->s))
		return -1;

	while (p - buffer < len) {
        start = GetTimeMark();
        now = start;
		if ((rc = tds_select(tds, TDSSELWRITE, tds->query_timeout, start)) > 0) {
			int err;
			size_t remaining = len - (p - buffer);
#ifdef USE_MSGMORE
			ssize_t nput = send(tds->s, p, remaining, last ? MSG_NOSIGNAL : MSG_NOSIGNAL|MSG_MORE);
			/* In case the kernel does not support MSG_MORE, try again without it */
			if (nput < 0 && errno == EINVAL && !last)
				nput = send(tds->s, p, remaining, MSG_NOSIGNAL);
#elif defined(__APPLE__) && defined(SO_NOSIGPIPE)
			ssize_t nput = send(tds->s, p, remaining, 0);
#else
			ssize_t nput = WRITESOCKET(tds->s, p, remaining);
#endif
			if (nput > 0) {
				p += nput;
				continue;
			}

			err = sock_errno;
            if (0 == nput || TDSSOCK_WOULDBLOCK(err) || err == TDSSOCK_EINTR)
				continue;

			assert(nput < 0);

			tdsdump_log(TDS_DBG_NETWORK, "send(2) failed: %d (%s)\n",
                        err, strerror(err));
            tds_report_error(tds->tds_ctx, tds, err, 20017,
                             "Write to SQL Server failed");
			tds_close_socket(tds);
			return -1;

		} else if (rc < 0) {
			int err = sock_errno;
			if (TDSSOCK_WOULDBLOCK(err)) /* shouldn't happen, but OK, retry */
				continue;
			tdsdump_log(TDS_DBG_NETWORK, "select(2) failed: %d (%s)\n",
                        err, strerror(err));
            tds_report_error(tds->tds_ctx, tds, err, 20005,
                             "select/send finished with error");
			tds_close_socket(tds);
			return -1;
		} else { /* timeout */
            now = GetTimeMark();
            if (tds->query_timeout  &&  (now - start) >= tds->query_timeout) {
                tds_client_msg(tds->tds_ctx, tds, 20002, 6, 0, 0,
                               "Writing to SQL server exceeded timeout");
                tds_close_socket(tds);
                return -1;
            }

			tdsdump_log(TDS_DBG_NETWORK, "tds_goodwrite(): timed out, asking client\n");
			switch (rc = tds_client_msg(tds->tds_ctx, tds, 20002, 6, 0, 0,
                                        "Writing to SQL server exceeded timeout")) {
			case TDS_INT_CONTINUE:
				continue;
			case TDS_INT_TIMEOUT:
				/* 
				 * "Cancel the operation ... but leave the dbproc in working condition." 
				 * We must try to send the cancel packet, else we have to abandon the dbproc.  
				 * If it can't be done, a harder error e.g. ECONNRESET will bubble up.  
				 */
				tds_send_cancel(tds);
				continue; 
			default:
			case TDS_INT_CANCEL:
				tds_close_socket(tds);
				return -1;
			}
			assert(0); /* not reached */
		}
		assert(0); /* not reached */
	}

#ifdef USE_CORK
	/* force packet flush */
	if (last) {
		int opt;
		opt = 0;
		setsockopt(tds->s, SOL_TCP, TCP_CORK, (const void *) &opt, sizeof(opt));
		opt = 1;
		setsockopt(tds->s, SOL_TCP, TCP_CORK, (const void *) &opt, sizeof(opt));
	}
#endif

	return len;
}
Пример #4
0
/**
 * 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;
}