Line data Source code
1 : /* TDSPool - Connection pooling for TDS based databases
2 : * Copyright (C) 2001, 2002, 2003, 2004, 2005 Brian Bruns
3 : *
4 : * This program is free software; you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation; either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * This program is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with this program; if not, write to the Free Software
16 : * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 : *
18 : */
19 :
20 : #include <config.h>
21 :
22 : #include <stdarg.h>
23 : #include <stdio.h>
24 : #include <assert.h>
25 :
26 : #if HAVE_STDLIB_H
27 : #include <stdlib.h>
28 : #endif /* HAVE_STDLIB_H */
29 :
30 : #if HAVE_STRING_H
31 : #include <string.h>
32 : #endif /* HAVE_STRING_H */
33 :
34 : #if HAVE_UNISTD_H
35 : #include <unistd.h>
36 : #endif /* HAVE_UNISTD_H */
37 :
38 : #if HAVE_SYS_PARAM_H
39 : #include <sys/param.h>
40 : #endif /* HAVE_SYS_PARAM_H */
41 :
42 : #if HAVE_SYS_SOCKET_H
43 : #include <sys/socket.h>
44 : #endif /* HAVE_SYS_SOCKET_H */
45 :
46 : #if HAVE_NETINET_IN_H
47 : #include <netinet/in.h>
48 : #endif /* HAVE_NETINET_IN_H */
49 :
50 : #if HAVE_ARPA_INET_H
51 : #include <arpa/inet.h>
52 : #endif /* HAVE_ARPA_INET_H */
53 :
54 : #include "pool.h"
55 : #include <freetds/utils/string.h>
56 :
57 : #ifndef MAXHOSTNAMELEN
58 : #define MAXHOSTNAMELEN 256
59 : #endif /* MAXHOSTNAMELEN */
60 :
61 : static void
62 24 : pool_mbr_free_socket(TDSSOCKET *tds)
63 : {
64 24 : if (tds) {
65 24 : TDSCONTEXT *ctx = (TDSCONTEXT *) tds->conn->tds_ctx;
66 :
67 24 : tds_free_socket(tds);
68 24 : tds_free_context(ctx);
69 : }
70 24 : }
71 :
72 : /*
73 : * pool_mbr_login open a single pool login, to be call at init time or
74 : * to reconnect.
75 : */
76 : static TDSSOCKET *
77 24 : pool_mbr_login(const TDS_POOL * pool, int tds_version)
78 : {
79 : TDSCONTEXT *context;
80 : TDSLOGIN *login;
81 : TDSSOCKET *tds;
82 : TDSLOGIN *connection;
83 : char hostname[MAXHOSTNAMELEN];
84 :
85 24 : login = tds_alloc_login(true);
86 24 : if (!login) {
87 0 : fprintf(stderr, "out of memory");
88 0 : return NULL;
89 : }
90 24 : if (gethostname(hostname, MAXHOSTNAMELEN) < 0)
91 0 : strlcpy(hostname, "tdspool", MAXHOSTNAMELEN);
92 24 : if (!tds_set_passwd(login, pool->server_password)
93 24 : || !tds_set_user(login, pool->server_user)
94 24 : || !tds_set_app(login, "tdspool")
95 24 : || !tds_set_host(login, hostname)
96 24 : || !tds_set_library(login, "TDS-Library")
97 24 : || !tds_set_server(login, pool->server)
98 24 : || !tds_set_client_charset(login, "iso_1")
99 24 : || !tds_set_language(login, "us_english")) {
100 0 : tds_free_login(login);
101 0 : return NULL;
102 : }
103 24 : if (tds_version > 0)
104 22 : login->tds_version = tds_version;
105 24 : if (pool->database && strlen(pool->database)) {
106 24 : if (!tds_dstr_copy(&login->database, pool->database)) {
107 0 : tds_free_login(login);
108 0 : return NULL;
109 : }
110 : }
111 24 : context = tds_alloc_context(NULL);
112 24 : if (!context) {
113 0 : fprintf(stderr, "Context cannot be null\n");
114 0 : tds_free_login(login);
115 0 : return NULL;
116 : }
117 24 : tds = tds_alloc_socket(context, 512);
118 24 : if (!tds) {
119 0 : fprintf(stderr, "tds cannot be null\n");
120 0 : tds_free_context(context);
121 0 : tds_free_login(login);
122 0 : return NULL;
123 : }
124 24 : connection = tds_read_config_info(tds, login, context->locale);
125 24 : tds_free_login(login);
126 24 : if (!connection || TDS_FAILED(tds_connect_and_login(tds, connection))) {
127 0 : pool_mbr_free_socket(tds);
128 0 : tds_free_login(connection);
129 : /* what to do? */
130 0 : fprintf(stderr, "Could not open connection to server %s\n", pool->server);
131 0 : return NULL;
132 : }
133 24 : tds_free_login(connection);
134 :
135 24 : if (pool->database && strlen(pool->database)) {
136 24 : if (strcasecmp(tds->conn->env.database, pool->database) != 0) {
137 0 : fprintf(stderr, "changing database failed\n");
138 0 : pool_mbr_free_socket(tds);
139 0 : return NULL;
140 : }
141 : }
142 :
143 : return tds;
144 : }
145 :
146 : void
147 720 : pool_assign_member(TDS_POOL *pool, TDS_POOL_MEMBER * pmbr, TDS_POOL_USER *puser)
148 : {
149 720 : pool_mbr_check(pool);
150 720 : assert(pmbr->current_user == NULL);
151 : if (pmbr->current_user) {
152 : pmbr->current_user->assigned_member = NULL;
153 : } else {
154 720 : dlist_member_remove(&pool->idle_members, pmbr);
155 720 : dlist_member_append(&pool->active_members, pmbr);
156 : }
157 720 : pmbr->current_user = puser;
158 720 : puser->assigned_member = pmbr;
159 720 : pool_mbr_check(pool);
160 720 : }
161 :
162 : void
163 720 : pool_deassign_member(TDS_POOL *pool, TDS_POOL_MEMBER * pmbr)
164 : {
165 720 : if (pmbr->current_user) {
166 720 : pmbr->current_user->assigned_member = NULL;
167 720 : pmbr->current_user = NULL;
168 720 : dlist_member_remove(&pool->active_members, pmbr);
169 720 : dlist_member_append(&pool->idle_members, pmbr);
170 : }
171 720 : pmbr->sock.poll_send = false;
172 720 : }
173 :
174 : /*
175 : * if a dead connection on the client side left this member in a questionable
176 : * state, let's bring in a correct one
177 : * We are not sure what the client did so we must try to clean as much as
178 : * possible.
179 : * Use pool_free_member if the state is really broken.
180 : */
181 : void
182 718 : pool_reset_member(TDS_POOL * pool, TDS_POOL_MEMBER * pmbr)
183 : {
184 : // FIXME not wait for server !!! asyncronous
185 718 : TDSSOCKET *tds = pmbr->sock.tds;
186 : TDS_POOL_USER *puser;
187 :
188 718 : puser = pmbr->current_user;
189 718 : if (puser) {
190 0 : pool_deassign_member(pool, pmbr);
191 0 : pool_free_user(pool, puser);
192 : }
193 :
194 : /* cancel whatever pending */
195 718 : tds_init_write_buf(tds);
196 718 : if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
197 : goto failure;
198 718 : tds->out_flag = TDS_CANCEL;
199 718 : if (TDS_FAILED(tds_flush_packet(tds)))
200 : goto failure;
201 718 : tds_set_state(tds, TDS_PENDING);
202 718 : tds->in_cancel = 2;
203 :
204 718 : if (TDS_FAILED(tds_process_cancel(tds)))
205 : goto failure;
206 :
207 718 : if (IS_TDS71_PLUS(tds->conn)) {
208 : /* this 0x9 final reset the state from mssql 2000 */
209 718 : if (tds_set_state(tds, TDS_WRITING) != TDS_WRITING)
210 : goto failure;
211 718 : tds_start_query(tds, TDS_QUERY);
212 718 : tds_put_string(tds, "WHILE @@TRANCOUNT > 0 ROLLBACK SET TRANSACTION ISOLATION LEVEL READ COMMITTED", -1);
213 718 : tds_write_packet(tds, 0x9);
214 718 : tds_set_state(tds, TDS_PENDING);
215 :
216 718 : if (TDS_FAILED(tds_process_simple_query(tds)))
217 : goto failure;
218 : }
219 : return;
220 :
221 0 : failure:
222 0 : pool_free_member(pool, pmbr);
223 : }
224 :
225 : void
226 24 : pool_free_member(TDS_POOL * pool, TDS_POOL_MEMBER * pmbr)
227 : {
228 : TDSSOCKET *tds;
229 : TDS_POOL_USER *puser;
230 :
231 24 : tds = pmbr->sock.tds;
232 24 : if (tds) {
233 24 : if (!IS_TDSDEAD(tds))
234 24 : tds_close_socket(tds);
235 24 : pool_mbr_free_socket(tds);
236 24 : pmbr->sock.tds = NULL;
237 : }
238 :
239 : /*
240 : * if he is allocated disconnect the client
241 : * otherwise we end up with broken client.
242 : */
243 24 : puser = pmbr->current_user;
244 24 : if (puser) {
245 2 : pool_deassign_member(pool, pmbr);
246 2 : pool_free_user(pool, puser);
247 : }
248 :
249 48 : if (dlist_member_in_list(&pool->active_members, pmbr)) {
250 24 : pool->num_active_members--;
251 24 : dlist_member_remove(&pool->active_members, pmbr);
252 : }
253 24 : free(pmbr);
254 24 : pool_mbr_check(pool);
255 24 : }
256 :
257 : void
258 2 : pool_mbr_init(TDS_POOL * pool)
259 : {
260 : TDS_POOL_MEMBER *pmbr;
261 :
262 : /* allocate room for pool members */
263 :
264 2 : pool->num_active_members = 0;
265 4 : dlist_member_init(&pool->active_members);
266 4 : dlist_member_init(&pool->idle_members);
267 2 : pool_mbr_check(pool);
268 :
269 : /* open connections for each member */
270 6 : while (pool->num_active_members < pool->min_open_conn) {
271 2 : pmbr = tds_new0(TDS_POOL_MEMBER, 1);
272 2 : if (!pmbr) {
273 0 : fprintf(stderr, "Out of memory\n");
274 0 : exit(1);
275 : }
276 2 : pmbr->sock.poll_recv = true;
277 :
278 2 : pmbr->sock.tds = pool_mbr_login(pool, 0);
279 2 : if (!pmbr->sock.tds) {
280 0 : fprintf(stderr, "Could not open initial connection\n");
281 0 : exit(1);
282 : }
283 2 : pmbr->last_used_tm = time(NULL);
284 2 : pool->num_active_members++;
285 2 : dlist_member_append(&pool->idle_members, pmbr);
286 2 : if (!IS_TDS71_PLUS(pmbr->sock.tds->conn)) {
287 0 : fprintf(stderr, "Current pool implementation does not support protocol versions former than 7.1\n");
288 0 : exit(1);
289 : }
290 2 : pool->member_logins++;
291 : }
292 2 : pool_mbr_check(pool);
293 2 : }
294 :
295 : void
296 2 : pool_mbr_destroy(TDS_POOL * pool)
297 : {
298 4 : while (dlist_member_first(&pool->active_members))
299 0 : pool_free_member(pool, dlist_member_first(&pool->active_members));
300 46 : while (dlist_member_first(&pool->idle_members))
301 22 : pool_free_member(pool, dlist_member_first(&pool->idle_members));
302 :
303 2 : assert(pool->num_active_members == 0);
304 2 : pool->num_active_members = 0;
305 2 : }
306 :
307 : static bool
308 18315 : pool_process_data(TDS_POOL *pool, TDS_POOL_MEMBER *pmbr)
309 : {
310 18315 : TDSSOCKET *tds = pmbr->sock.tds;
311 18315 : TDS_POOL_USER *puser = NULL;
312 :
313 : for (;;) {
314 35386 : if (pool_packet_read(tds))
315 : break;
316 :
317 : /* disconnected */
318 17073 : if (tds->in_len == 0) {
319 2 : tdsdump_log(TDS_DBG_INFO1, "Uh oh! member disconnected\n");
320 : /* mark as dead */
321 2 : pool_free_member(pool, pmbr);
322 2 : return false;
323 : }
324 :
325 17071 : tdsdump_dump_buf(TDS_DBG_NETWORK, "Got packet from server:", tds->in_buf, tds->in_len);
326 17071 : puser = pmbr->current_user;
327 17071 : if (!puser)
328 : break;
329 :
330 17071 : tdsdump_log(TDS_DBG_INFO1, "writing it sock %d\n", (int) tds_get_s(puser->sock.tds));
331 17071 : if (!pool_write_data(&pmbr->sock, &puser->sock)) {
332 0 : tdsdump_log(TDS_DBG_ERROR, "member received error while writing\n");
333 0 : pool_free_user(pool, puser);
334 0 : return false;
335 : }
336 17071 : if (tds->in_pos < tds->in_len)
337 : /* partial write, schedule a future write */
338 : break;
339 : }
340 18313 : if (puser && !puser->sock.poll_send)
341 16940 : tds_socket_flush(tds_get_s(puser->sock.tds));
342 : return true;
343 : }
344 :
345 : /*
346 : * pool_process_members
347 : * check the fd_set for members returning data to the client, lookup the
348 : * client holding this member and forward the results.
349 : * @return Timeout you should call this function again or -1 for infinite
350 : */
351 : int
352 36402 : pool_process_members(TDS_POOL * pool, struct pollfd *fds, unsigned num_fds)
353 : {
354 : TDS_POOL_MEMBER *pmbr, *next;
355 : time_t age;
356 : time_t time_now;
357 36402 : int min_expire_left = -1;
358 : short revents;
359 :
360 36402 : pool_mbr_check(pool);
361 146356 : for (next = dlist_member_first(&pool->active_members); (pmbr = next) != NULL; ) {
362 73552 : bool processed = false;
363 :
364 147104 : next = dlist_member_next(&pool->active_members, pmbr);
365 :
366 73552 : assert(pmbr->current_user);
367 73552 : if (pmbr->doing_async || pmbr->sock.poll_index > num_fds)
368 1374 : continue;
369 :
370 72178 : revents = fds[pmbr->sock.poll_index].revents;
371 72178 : assert(pmbr->sock.tds);
372 :
373 72178 : time_now = time(NULL);
374 72178 : if (pmbr->sock.poll_recv && (revents & (POLLIN|POLLHUP)) != 0) {
375 18315 : if (!pool_process_data(pool, pmbr))
376 2 : continue;
377 : processed = true;
378 : }
379 72176 : if (pmbr->sock.poll_send && (revents & POLLOUT) != 0) {
380 1 : if (!pool_write_data(&pmbr->current_user->sock, &pmbr->sock)) {
381 0 : pool_free_member(pool, pmbr);
382 0 : continue;
383 : }
384 : processed = true;
385 : }
386 72175 : if (processed)
387 18314 : pmbr->last_used_tm = time_now;
388 : }
389 :
390 36402 : if (pool->num_active_members <= pool->min_open_conn)
391 : return min_expire_left;
392 :
393 : /* close old connections */
394 27614 : time_now = time(NULL);
395 269813 : for (next = dlist_member_first(&pool->idle_members); (pmbr = next) != NULL; ) {
396 :
397 429170 : next = dlist_member_next(&pool->idle_members, pmbr);
398 :
399 214585 : assert(pmbr->sock.tds);
400 214585 : assert(!pmbr->current_user);
401 :
402 214585 : age = time_now - pmbr->last_used_tm;
403 214585 : if (age >= pool->max_member_age) {
404 0 : tdsdump_log(TDS_DBG_INFO1, "member is %ld seconds old...closing\n", (long int) age);
405 0 : pool_free_member(pool, pmbr);
406 : } else {
407 214585 : int left = (int) (pool->max_member_age - age);
408 214585 : if (min_expire_left < 0 || left < min_expire_left)
409 22898 : min_expire_left = left;
410 : }
411 : }
412 : return min_expire_left;
413 : }
414 :
415 : static bool
416 : compatible_versions(const TDSSOCKET *tds, const TDS_POOL_USER *user)
417 : {
418 698 : if (tds->conn->tds_version != user->login->tds_version)
419 : return false;
420 : return true;
421 : }
422 :
423 : typedef struct {
424 : TDS_POOL_EVENT common;
425 : TDS_POOL *pool;
426 : TDS_POOL_MEMBER *pmbr;
427 : int tds_version;
428 : } CONNECT_EVENT;
429 :
430 : static void connect_execute_ok(TDS_POOL_EVENT *base_event);
431 : static void connect_execute_ko(TDS_POOL_EVENT *base_event);
432 :
433 22 : static TDS_THREAD_PROC_DECLARE(connect_proc, arg)
434 : {
435 22 : CONNECT_EVENT *ev = (CONNECT_EVENT *) arg;
436 22 : TDS_POOL_MEMBER *pmbr = ev->pmbr;
437 22 : TDS_POOL *pool = ev->pool;
438 :
439 : for (;;) {
440 22 : pmbr->sock.tds = pool_mbr_login(pool, ev->tds_version);
441 22 : if (!pmbr->sock.tds) {
442 0 : tdsdump_log(TDS_DBG_ERROR, "Error opening a new connection to server\n");
443 : break;
444 : }
445 22 : if (!IS_TDS71_PLUS(pmbr->sock.tds->conn)) {
446 0 : tdsdump_log(TDS_DBG_ERROR, "Protocol server version not supported\n");
447 : break;
448 : }
449 :
450 : /* if already attached to a user we can send login directly */
451 22 : if (pmbr->current_user)
452 22 : if (!pool_user_send_login_ack(pool, pmbr->current_user))
453 : break;
454 :
455 22 : pool_event_add(pool, &ev->common, connect_execute_ok);
456 22 : return TDS_THREAD_RESULT(0);
457 : }
458 :
459 : /* failure */
460 0 : pool_event_add(pool, &ev->common, connect_execute_ko);
461 0 : return TDS_THREAD_RESULT(0);
462 : }
463 :
464 : static void
465 0 : connect_execute_ko(TDS_POOL_EVENT *base_event)
466 : {
467 0 : CONNECT_EVENT *ev = (CONNECT_EVENT *) base_event;
468 :
469 0 : pool_free_member(ev->pool, ev->pmbr);
470 0 : }
471 :
472 : static void
473 22 : connect_execute_ok(TDS_POOL_EVENT *base_event)
474 : {
475 22 : CONNECT_EVENT *ev = (CONNECT_EVENT *) base_event;
476 22 : TDS_POOL_MEMBER *pmbr = ev->pmbr;
477 22 : TDS_POOL_USER *puser = pmbr->current_user;
478 :
479 22 : ev->pool->member_logins++;
480 22 : pmbr->doing_async = false;
481 :
482 22 : pmbr->last_used_tm = time(NULL);
483 :
484 22 : if (puser) {
485 22 : pmbr->sock.poll_recv = true;
486 22 : puser->sock.poll_recv = true;
487 :
488 22 : puser->user_state = TDS_SRV_QUERY;
489 : }
490 22 : }
491 :
492 : /*
493 : * pool_assign_idle_member
494 : * assign a member to the user specified
495 : */
496 : TDS_POOL_MEMBER *
497 720 : pool_assign_idle_member(TDS_POOL * pool, TDS_POOL_USER *puser)
498 : {
499 : TDS_POOL_MEMBER *pmbr;
500 : CONNECT_EVENT *ev;
501 :
502 720 : puser->sock.poll_recv = false;
503 720 : puser->sock.poll_send = false;
504 :
505 720 : pool_mbr_check(pool);
506 720 : DLIST_FOREACH(dlist_member, &pool->idle_members, pmbr) {
507 698 : assert(pmbr->current_user == NULL);
508 698 : assert(!pmbr->doing_async);
509 :
510 698 : assert(pmbr->sock.tds);
511 :
512 1396 : if (!compatible_versions(pmbr->sock.tds, puser))
513 0 : continue;
514 :
515 698 : pool_assign_member(pool, pmbr, puser);
516 :
517 : /*
518 : * make sure member wasn't idle more that the timeout
519 : * otherwise it'll send the query and close leaving a
520 : * hung client
521 : */
522 698 : pmbr->last_used_tm = time(NULL);
523 698 : pmbr->sock.poll_recv = false;
524 698 : pmbr->sock.poll_send = false;
525 :
526 698 : pool_user_finish_login(pool, puser);
527 698 : return pmbr;
528 : }
529 :
530 : /* if we can open a new connection open it */
531 22 : if (pool->num_active_members >= pool->max_open_conn) {
532 0 : fprintf(stderr, "No idle members left, increase \"max pool conn\"\n");
533 0 : return NULL;
534 : }
535 :
536 22 : pmbr = tds_new0(TDS_POOL_MEMBER, 1);
537 22 : if (!pmbr) {
538 0 : fprintf(stderr, "Out of memory\n");
539 0 : return NULL;
540 : }
541 :
542 22 : tdsdump_log(TDS_DBG_INFO1, "No open connections left, opening new member\n");
543 :
544 22 : ev = tds_new0(CONNECT_EVENT, 1);
545 22 : if (!ev) {
546 0 : free(pmbr);
547 0 : fprintf(stderr, "Out of memory\n");
548 0 : return NULL;
549 : }
550 22 : ev->pmbr = pmbr;
551 22 : ev->pool = pool;
552 22 : ev->tds_version = puser->login->tds_version;
553 :
554 22 : if (tds_thread_create_detached(connect_proc, ev) != 0) {
555 0 : free(pmbr);
556 0 : free(ev);
557 0 : fprintf(stderr, "error creating thread\n");
558 0 : return NULL;
559 : }
560 22 : pmbr->doing_async = true;
561 :
562 22 : pool_mbr_check(pool);
563 22 : pool->num_active_members++;
564 22 : dlist_member_append(&pool->idle_members, pmbr);
565 22 : pool_mbr_check(pool);
566 :
567 22 : pool_assign_member(pool, pmbr, puser);
568 22 : puser->sock.poll_send = false;
569 22 : puser->sock.poll_recv = false;
570 :
571 22 : return pmbr;
572 : }
573 :
574 : #if ENABLE_EXTRA_CHECKS
575 38634 : void pool_mbr_check(TDS_POOL *pool)
576 : {
577 : TDS_POOL_MEMBER *pmbr;
578 38634 : unsigned total = 0;
579 :
580 227044 : DLIST_FOREACH(dlist_member, &pool->active_members, pmbr) {
581 74888 : assert(pmbr->doing_async || pmbr->sock.tds);
582 74888 : assert(pmbr->current_user);
583 74888 : ++total;
584 : }
585 531654 : DLIST_FOREACH(dlist_member, &pool->idle_members, pmbr) {
586 227193 : assert(pmbr->doing_async || pmbr->sock.tds);
587 227193 : assert(!pmbr->current_user);
588 227193 : ++total;
589 : }
590 38634 : assert(pool->num_active_members == total);
591 38634 : }
592 : #endif
|