/* * 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"); }
static int tds_connection_put_packet(TDSSOCKET *tds, TDSPACKET *packet) { TDSCONNECTION *conn = tds->conn; if (TDS_UNLIKELY(!packet)) { tds_close_socket(tds); return TDS_FAIL; } tds->out_pos = 0; tds_mutex_lock(&conn->list_mtx); for (;;) { int wait_res; if (IS_TDSDEAD(tds)) { tdsdump_log(TDS_DBG_NETWORK, "Write attempt when state is TDS_DEAD"); break; } /* limit packet sending looking at sequence/window */ if (tds->send_seq <= tds->send_wnd) { /* append packet */ tds_append_packet(&conn->send_packets, packet); packet = NULL; } /* network ok ? process network */ if (!conn->in_net_tds) { tds_connection_network(conn, tds, packet ? 0 : 1); if (packet) continue; /* FIXME we are not sure we sent the packet !!! */ break; } /* signal thread processing network to handle our packet */ /* TODO check result */ tds_wakeup_send(&conn->wakeup, 0); /* wait local condition */ wait_res = tds_cond_timedwait(&tds->packet_cond, &conn->list_mtx, tds->query_timeout); if (wait_res == ETIMEDOUT && tdserror(tds_get_ctx(tds), tds, TDSETIME, ETIMEDOUT) != TDS_INT_CONTINUE) { tds_mutex_unlock(&conn->list_mtx); tds_close_socket(tds); tds_free_packets(packet); return TDS_FAIL; } } tds_mutex_unlock(&conn->list_mtx); if (TDS_UNLIKELY(packet)) { tds_free_packets(packet); return TDS_FAIL; } if (IS_TDSDEAD(tds)) return TDS_FAIL; return TDS_SUCCESS; }
/** * Get N bytes from the buffer and return them in the already allocated space * given to us. We ASSUME that the person calling this function has done the * bounds checking for us since they know how many bytes they want here. * dest of NULL means we just want to eat the bytes. ([email protected]) */ void * tds_get_n(TDSSOCKET * tds, void *dest, size_t need) { for (;;) { unsigned int have = tds->in_len - tds->in_pos; if (need <= have) break; /* We need more than is in the buffer, copy what is there */ if (dest != NULL) { memcpy((char *) dest, tds->in_buf + tds->in_pos, have); dest = (char *) dest + have; } need -= have; if (TDS_UNLIKELY(tds_read_packet(tds) < 0)) return NULL; } if (need > 0) { /* get the remainder if there is any */ if (dest != NULL) { memcpy((char *) dest, tds->in_buf + tds->in_pos, need); } tds->in_pos += need; } return dest; }
/** * Fetch character data the wire. * Output is NOT null terminated. * If \a char_conv is not NULL, convert data accordingly. * \param tds state information for the socket and the TDS protocol * \param row_buffer destination buffer in current_row. Can't be NULL * \param wire_size size to read from wire (in bytes) * \param curcol column information * \return TDS_SUCCESS or TDS_FAIL (probably memory error on text data) */ TDSRET tds_get_char_data(TDSSOCKET * tds, char *row_buffer, size_t wire_size, TDSCOLUMN * curcol) { size_t in_left; assert(curcol->char_conv); /* * row_buffer is a column buffer, allocated when the column's metadata are processed * and reused for each row. */ /* silly case, empty string */ if (wire_size == 0) { curcol->column_cur_size = 0; return TDS_SUCCESS; } in_left = curcol->column_size; curcol->column_cur_size = read_and_convert(tds, curcol->char_conv, &wire_size, row_buffer, in_left); if (TDS_UNLIKELY(wire_size > 0)) { tds_get_n(tds, NULL, wire_size); tdsdump_log(TDS_DBG_NETWORK, "error: tds_get_char_data: discarded %u on wire while reading %d into client. \n", (unsigned int) wire_size, curcol->column_cur_size); return TDS_FAIL; } return TDS_SUCCESS; }
static int get_utf8(const unsigned char *p, size_t len, ICONV_CHAR *out) { ICONV_CHAR uc; size_t l; l = utf8_lengths[p[0]]; if (TDS_UNLIKELY(l == 0)) return -EILSEQ; if (TDS_UNLIKELY(len < l)) return -EINVAL; len = l; uc = *p++ & utf8_masks[l]; while(--l) uc = (uc << 6) | (*p++ & 0x3f); *out = uc; return len; }
/** * Inputs are FreeTDS canonical names, no other. No alias list is consulted. */ iconv_t tds_sys_iconv_open (const char* tocode, const char* fromcode) { int i; unsigned int fromto; const char *enc_name; unsigned char encodings[2]; static char first_time = 1; if (TDS_UNLIKELY(first_time)) { first_time = 0; tdsdump_log(TDS_DBG_INFO1, "Using trivial iconv\n"); } /* match both inputs to our canonical names */ enc_name = fromcode; for (i=0; i < 2; ++i) { unsigned char encoding; if (strcmp(enc_name, "ISO-8859-1") == 0) encoding = 0; else if (strcmp(enc_name, "US-ASCII") == 0) encoding = 1; else if (strcmp(enc_name, "UCS-2LE") == 0 || strcmp(enc_name, "UTF-16LE") == 0) encoding = 2; else if (strcmp(enc_name, "UCS-2BE") == 0 || strcmp(enc_name, "UTF-16BE") == 0) encoding = 3; else if (strcmp(enc_name, "UCS-4LE") == 0) encoding = 4; else if (strcmp(enc_name, "UCS-4BE") == 0) encoding = 5; else if (strcmp(enc_name, "UTF-8") == 0) encoding = 6; else { errno = EINVAL; return (iconv_t)(-1); } encodings[i] = encoding; enc_name = tocode; } fromto = (encodings[0] << 4) | (encodings[1] & 0x0F); /* like to like */ if (encodings[0] == encodings[1]) { fromto = Like_to_Like; } return (iconv_t) (TDS_INTPTR) fromto; }
/** * allocate space for length char * @param s dynamic string * @param length new length * @return string allocated or NULL on memory error */ DSTR* tds_dstr_alloc(DSTR *s, size_t length) { struct tds_dstr *p = (struct tds_dstr *) malloc(length + TDS_OFFSET(struct tds_dstr, dstr_s) + 1); if (TDS_UNLIKELY(!p)) return NULL; if (*s != EMPTY) free(*s); p->dstr_s[0] = 0; p->dstr_size = length; *s = p; return s; }
static int put_utf16be(unsigned char *buf, size_t buf_len, ICONV_CHAR c) { if (c < 0x10000u) { if (buf_len < 2) return -E2BIG; TDS_PUT_A2BE(buf, c); return 2; } if (TDS_UNLIKELY(c >= 0x110000u)) return -EILSEQ; if (buf_len < 4) return -E2BIG; TDS_PUT_A2BE(buf, 0xd7c0 + (c >> 10)); TDS_PUT_A2BE(buf+2, 0xdc00 + (c & 0x3ffu)); return 4; }
/** * Reads a string from wire and put in a DSTR. * On error we read the bytes from the wire anyway. * \tds * \param[out] s output string * \param[in] len string length (in characters) * \return string or NULL on error */ DSTR* tds_dstr_get(TDSSOCKET * tds, DSTR * s, size_t len) { size_t out_len; CHECK_TDS_EXTRA(tds); /* assure sufficient space for every conversion */ if (TDS_UNLIKELY(!tds_dstr_alloc(s, len * 4))) { tds_get_n(tds, NULL, len); return NULL; } out_len = tds_get_string(tds, len, tds_dstr_buf(s), len * 4); tds_dstr_setlen(s, out_len); return s; }
/** * Set string to a given buffer of characters * @param s dynamic string * @param src source buffer * @param length length of source buffer * @return string copied or NULL on memory error */ DSTR* tds_dstr_copyn(DSTR * s, const char *src, size_t length) { if (!length) { if (*s != EMPTY) { free(*s); *s = EMPTY; } } else { struct tds_dstr *p = (struct tds_dstr *) malloc(length + TDS_OFFSET(struct tds_dstr, dstr_s) + 1); if (TDS_UNLIKELY(!p)) return NULL; memcpy(p->dstr_s, src, length); p->dstr_s[length] = 0; p->dstr_size = length; if (*s != EMPTY) free(*s); *s = p; } return s; }
size_t tds_sys_iconv (iconv_t cd, const char* * inbuf, size_t *inbytesleft, char* * outbuf, size_t *outbytesleft) { const unsigned char *ib; unsigned char *ob; size_t il, ol; int local_errno; #undef CD #define CD ((int) (TDS_INTPTR) cd) /* iconv defines valid semantics for NULL inputs, but we don't support them. */ if (!inbuf || !*inbuf || !inbytesleft || !outbuf || !*outbuf || !outbytesleft) return 0; /* * some optimizations * - do not use errno directly only assign a time * (some platform define errno as a complex macro) * - some processors have few registers, deference and copy input variable * (this make also compiler optimize more due to removed aliasing) * also we use unsigned to remove required unsigned casts */ local_errno = 0; il = *inbytesleft; ol = *outbytesleft; ib = (const unsigned char*) *inbuf; ob = (unsigned char*) *outbuf; if (CD == Like_to_Like) { size_t copybytes = (il < ol)? il : ol; memcpy(ob, ib, copybytes); ob += copybytes; ol -= copybytes; ib += copybytes; il -= copybytes; } else if (CD & ~0x77) { local_errno = EINVAL; } else { iconv_get_t get_func = iconv_gets[(CD>>4) & 7]; iconv_put_t put_func = iconv_puts[ CD & 7]; while (il) { ICONV_CHAR out_c; int readed = get_func(ib, il, &out_c), written; if (TDS_UNLIKELY(readed < 0)) { local_errno = -readed; break; } written = put_func(ob, ol, out_c); if (TDS_UNLIKELY(written < 0)) { local_errno = -written; break; } il -= readed; ib += readed; ol -= written; ob += written; } } /* back to source */ *inbytesleft = il; *outbytesleft = ol; *inbuf = (const char*) ib; *outbuf = (char*) ob; if (il && !local_errno) local_errno = E2BIG; if (local_errno) { errno = local_errno; return (size_t)(-1); } return 0; }
/** * Read in one 'packet' from the server. This is a wrapped outer packet of * the protocol (they bundle result packets into chunks and wrap them at * what appears to be 512 bytes regardless of how that breaks internal packet * up. (tetherow\@nol.org) * @return bytes read or -1 on failure */ int tds_read_packet(TDSSOCKET * tds) { #if ENABLE_ODBC_MARS TDSCONNECTION *conn = tds->conn; tds_mutex_lock(&conn->list_mtx); for (;;) { int wait_res; TDSPACKET **p_packet; if (IS_TDSDEAD(tds)) { tdsdump_log(TDS_DBG_NETWORK, "Read attempt when state is TDS_DEAD\n"); break; } /* if there is a packet for me return it */ for (p_packet = &conn->packets; *p_packet; p_packet = &(*p_packet)->next) if ((*p_packet)->sid == tds->sid) break; if (*p_packet) { size_t hdr_size; /* remove our packet from list */ TDSPACKET *packet = *p_packet; *p_packet = packet->next; tds_packet_cache_add(conn, tds->recv_packet); tds_mutex_unlock(&conn->list_mtx); packet->next = NULL; tds->recv_packet = packet; hdr_size = packet->buf[0] == TDS72_SMP ? sizeof(TDS72_SMP_HEADER) : 0; tds->in_buf = packet->buf + hdr_size; tds->in_len = packet->len - hdr_size; tds->in_pos = 8; tds->in_flag = tds->in_buf[0]; /* send acknowledge if needed */ if (tds->recv_seq + 2 >= tds->recv_wnd) tds_update_recv_wnd(tds, tds->recv_seq + 4); return tds->in_len; } /* network ok ? process network */ if (!conn->in_net_tds) { tds_connection_network(conn, tds, 0); continue; } /* wait local condition */ wait_res = tds_cond_timedwait(&tds->packet_cond, &conn->list_mtx, tds->query_timeout); if (wait_res == ETIMEDOUT && tdserror(tds_get_ctx(tds), tds, TDSETIME, ETIMEDOUT) != TDS_INT_CONTINUE) { tds_mutex_unlock(&conn->list_mtx); tds_close_socket(tds); return -1; } } tds_mutex_unlock(&conn->list_mtx); return -1; #else /* !ENABLE_ODBC_MARS */ unsigned char *pkt = tds->in_buf, *p, *end; if (IS_TDSDEAD(tds)) { tdsdump_log(TDS_DBG_NETWORK, "Read attempt when state is TDS_DEAD"); return -1; } tds->in_len = 0; tds->in_pos = 0; for (p = pkt, end = p+8; p < end;) { int len = tds_connection_read(tds, p, end - p); if (len <= 0) { tds_close_socket(tds); return -1; } p += len; if (p - pkt >= 4) { unsigned pktlen = TDS_GET_A2BE(pkt+2); /* packet must at least contains header */ if (TDS_UNLIKELY(pktlen < 8)) { tds_close_socket(tds); return -1; } if (TDS_UNLIKELY(pktlen > tds->recv_packet->capacity)) { TDSPACKET *packet = tds_realloc_packet(tds->recv_packet, pktlen); if (TDS_UNLIKELY(!packet)) { tds_close_socket(tds); return -1; } tds->recv_packet = packet; pkt = packet->buf; p = pkt + (p-tds->in_buf); tds->in_buf = pkt; } end = pkt + pktlen; } } /* set the received packet type flag */ tds->in_flag = pkt[0]; /* Set the length and pos (not sure what pos is used for now */ tds->in_len = p - pkt; tds->in_pos = 8; tdsdump_dump_buf(TDS_DBG_NETWORK, "Received packet", tds->in_buf, tds->in_len); return tds->in_len; #endif /* !ENABLE_ODBC_MARS */ }
/** * 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; }