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; }
/** * \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; }
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; }
/** * 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; }