1/*
2 Copyright (c) 2007, 2013, Oracle and/or its affiliates.
3 Copyright (c) 2008, 2016, MariaDB
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; version 2 of the License.
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17*/
18
19/*
20 Functions to autenticate and handle reqests for a connection
21*/
22
23#include "mariadb.h"
24#include "mysqld.h"
25#include "sql_priv.h"
26#ifndef __WIN__
27#include <netdb.h> // getservbyname, servent
28#endif
29#include "sql_audit.h"
30#include "sql_connect.h"
31#include "probes_mysql.h"
32#include "sql_parse.h" // sql_command_flags,
33 // execute_init_command,
34 // do_command
35#include "sql_db.h" // mysql_change_db
36#include "hostname.h" // inc_host_errors, ip_to_hostname,
37 // reset_host_errors
38#include "sql_acl.h" // acl_getroot, NO_ACCESS, SUPER_ACL
39#include "sql_callback.h"
40#include "wsrep_mysqld.h"
41#include "proxy_protocol.h"
42
43HASH global_user_stats, global_client_stats, global_table_stats;
44HASH global_index_stats;
45/* Protects the above global stats */
46extern mysql_mutex_t LOCK_global_user_client_stats;
47extern mysql_mutex_t LOCK_global_table_stats;
48extern mysql_mutex_t LOCK_global_index_stats;
49extern vio_keepalive_opts opt_vio_keepalive;
50
51/*
52 Get structure for logging connection data for the current user
53*/
54
55#ifndef NO_EMBEDDED_ACCESS_CHECKS
56static HASH hash_user_connections;
57
58int get_or_create_user_conn(THD *thd, const char *user,
59 const char *host,
60 const USER_RESOURCES *mqh)
61{
62 int return_val= 0;
63 size_t temp_len, user_len;
64 char temp_user[USER_HOST_BUFF_SIZE];
65 struct user_conn *uc;
66
67 DBUG_ASSERT(user != 0);
68 DBUG_ASSERT(host != 0);
69 DBUG_ASSERT(thd->user_connect == 0);
70
71 user_len= strlen(user);
72 temp_len= (strmov(strmov(temp_user, user)+1, host) - temp_user)+1;
73 mysql_mutex_lock(&LOCK_user_conn);
74 if (!(uc = (struct user_conn *) my_hash_search(&hash_user_connections,
75 (uchar*) temp_user, temp_len)))
76 {
77 /* First connection for user; Create a user connection object */
78 if (!(uc= ((struct user_conn*)
79 my_malloc(sizeof(struct user_conn) + temp_len+1,
80 MYF(MY_WME)))))
81 {
82 /* MY_WME ensures an error is set in THD. */
83 return_val= 1;
84 goto end;
85 }
86 uc->user=(char*) (uc+1);
87 memcpy(uc->user,temp_user,temp_len+1);
88 uc->host= uc->user + user_len + 1;
89 uc->len= (uint)temp_len;
90 uc->connections= uc->questions= uc->updates= uc->conn_per_hour= 0;
91 uc->user_resources= *mqh;
92 uc->reset_utime= thd->thr_create_utime;
93 if (my_hash_insert(&hash_user_connections, (uchar*) uc))
94 {
95 /* The only possible error is out of memory, MY_WME sets an error. */
96 my_free(uc);
97 return_val= 1;
98 goto end;
99 }
100 }
101 thd->user_connect=uc;
102 uc->connections++;
103end:
104 mysql_mutex_unlock(&LOCK_user_conn);
105 return return_val;
106}
107
108
109/*
110 check if user has already too many connections
111
112 SYNOPSIS
113 check_for_max_user_connections()
114 thd Thread handle
115 uc User connect object
116
117 NOTES
118 If check fails, we decrease user connection count, which means one
119 shouldn't call decrease_user_connections() after this function.
120
121 RETURN
122 0 ok
123 1 error
124*/
125
126int check_for_max_user_connections(THD *thd, USER_CONN *uc)
127{
128 int error= 1;
129 Host_errors errors;
130 DBUG_ENTER("check_for_max_user_connections");
131
132 mysql_mutex_lock(&LOCK_user_conn);
133
134 /* Root is not affected by the value of max_user_connections */
135 if (global_system_variables.max_user_connections &&
136 !uc->user_resources.user_conn &&
137 global_system_variables.max_user_connections < uc->connections &&
138 !(thd->security_ctx->master_access & SUPER_ACL))
139 {
140 my_error(ER_TOO_MANY_USER_CONNECTIONS, MYF(0), uc->user);
141 error=1;
142 errors.m_max_user_connection= 1;
143 goto end;
144 }
145 time_out_user_resource_limits(thd, uc);
146 if (uc->user_resources.user_conn &&
147 uc->user_resources.user_conn < uc->connections)
148 {
149 my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user,
150 "max_user_connections",
151 (long) uc->user_resources.user_conn);
152 error= 1;
153 errors.m_max_user_connection= 1;
154 goto end;
155 }
156 if (uc->user_resources.conn_per_hour &&
157 uc->user_resources.conn_per_hour <= uc->conn_per_hour)
158 {
159 my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user,
160 "max_connections_per_hour",
161 (long) uc->user_resources.conn_per_hour);
162 error=1;
163 errors.m_max_user_connection_per_hour= 1;
164 goto end;
165 }
166 uc->conn_per_hour++;
167 error= 0;
168
169end:
170 if (unlikely(error))
171 {
172 uc->connections--; // no need for decrease_user_connections() here
173 /*
174 The thread may returned back to the pool and assigned to a user
175 that doesn't have a limit. Ensure the user is not using resources
176 of someone else.
177 */
178 thd->user_connect= NULL;
179 }
180 mysql_mutex_unlock(&LOCK_user_conn);
181 if (unlikely(error))
182 {
183 inc_host_errors(thd->main_security_ctx.ip, &errors);
184 }
185 DBUG_RETURN(error);
186}
187
188
189/*
190 Decrease user connection count
191
192 SYNOPSIS
193 decrease_user_connections()
194 uc User connection object
195
196 NOTES
197 If there is a n user connection object for a connection
198 (which only happens if 'max_user_connections' is defined or
199 if someone has created a resource grant for a user), then
200 the connection count is always incremented on connect.
201
202 The user connect object is not freed if some users has
203 'max connections per hour' defined as we need to be able to hold
204 count over the lifetime of the connection.
205*/
206
207void decrease_user_connections(USER_CONN *uc)
208{
209 DBUG_ENTER("decrease_user_connections");
210 mysql_mutex_lock(&LOCK_user_conn);
211 DBUG_ASSERT(uc->connections);
212 if (!--uc->connections && !mqh_used)
213 {
214 /* Last connection for user; Delete it */
215 (void) my_hash_delete(&hash_user_connections,(uchar*) uc);
216 }
217 mysql_mutex_unlock(&LOCK_user_conn);
218 DBUG_VOID_RETURN;
219}
220
221
222/*
223 Reset per-hour user resource limits when it has been more than
224 an hour since they were last checked
225
226 SYNOPSIS:
227 time_out_user_resource_limits()
228 thd Thread handler
229 uc User connection details
230
231 NOTE:
232 This assumes that the LOCK_user_conn mutex has been acquired, so it is
233 safe to test and modify members of the USER_CONN structure.
234*/
235
236void time_out_user_resource_limits(THD *thd, USER_CONN *uc)
237{
238 ulonglong check_time= thd->start_utime;
239 DBUG_ENTER("time_out_user_resource_limits");
240
241 /* If more than a hour since last check, reset resource checking */
242 if (check_time - uc->reset_utime >= 3600000000ULL)
243 {
244 uc->questions=0;
245 uc->updates=0;
246 uc->conn_per_hour=0;
247 uc->reset_utime= check_time;
248 }
249
250 DBUG_VOID_RETURN;
251}
252
253/*
254 Check if maximum queries per hour limit has been reached
255 returns 0 if OK.
256*/
257
258bool check_mqh(THD *thd, uint check_command)
259{
260 bool error= 0;
261 USER_CONN *uc=thd->user_connect;
262 DBUG_ENTER("check_mqh");
263 DBUG_ASSERT(uc != 0);
264
265 mysql_mutex_lock(&LOCK_user_conn);
266
267 time_out_user_resource_limits(thd, uc);
268
269 /* Check that we have not done too many questions / hour */
270 if (uc->user_resources.questions &&
271 uc->questions++ >= uc->user_resources.questions)
272 {
273 my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user, "max_queries_per_hour",
274 (long) uc->user_resources.questions);
275 error=1;
276 goto end;
277 }
278 if (check_command < (uint) SQLCOM_END)
279 {
280 /* Check that we have not done too many updates / hour */
281 if (uc->user_resources.updates &&
282 (sql_command_flags[check_command] & CF_CHANGES_DATA) &&
283 uc->updates++ >= uc->user_resources.updates)
284 {
285 my_error(ER_USER_LIMIT_REACHED, MYF(0), uc->user, "max_updates_per_hour",
286 (long) uc->user_resources.updates);
287 error=1;
288 goto end;
289 }
290 }
291end:
292 mysql_mutex_unlock(&LOCK_user_conn);
293 DBUG_RETURN(error);
294}
295
296#endif /* NO_EMBEDDED_ACCESS_CHECKS */
297
298/*
299 Check for maximum allowable user connections, if the mysqld server is
300 started with corresponding variable that is greater then 0.
301*/
302
303extern "C" uchar *get_key_conn(user_conn *buff, size_t *length,
304 my_bool not_used __attribute__((unused)))
305{
306 *length= buff->len;
307 return (uchar*) buff->user;
308}
309
310
311extern "C" void free_user(struct user_conn *uc)
312{
313 my_free(uc);
314}
315
316
317void init_max_user_conn(void)
318{
319#ifndef NO_EMBEDDED_ACCESS_CHECKS
320 my_hash_init(&hash_user_connections, system_charset_info, max_connections,
321 0, 0, (my_hash_get_key) get_key_conn,
322 (my_hash_free_key) free_user, 0);
323#endif
324}
325
326
327void free_max_user_conn(void)
328{
329#ifndef NO_EMBEDDED_ACCESS_CHECKS
330 my_hash_free(&hash_user_connections);
331#endif /* NO_EMBEDDED_ACCESS_CHECKS */
332}
333
334
335void reset_mqh(LEX_USER *lu, bool get_them= 0)
336{
337#ifndef NO_EMBEDDED_ACCESS_CHECKS
338 mysql_mutex_lock(&LOCK_user_conn);
339 if (lu) // for GRANT
340 {
341 USER_CONN *uc;
342 size_t temp_len=lu->user.length+lu->host.length+2;
343 char temp_user[USER_HOST_BUFF_SIZE];
344
345 memcpy(temp_user,lu->user.str,lu->user.length);
346 memcpy(temp_user+lu->user.length+1,lu->host.str,lu->host.length);
347 temp_user[lu->user.length]='\0'; temp_user[temp_len-1]=0;
348 if ((uc = (struct user_conn *) my_hash_search(&hash_user_connections,
349 (uchar*) temp_user,
350 temp_len)))
351 {
352 uc->questions=0;
353 get_mqh(temp_user,&temp_user[lu->user.length+1],uc);
354 uc->updates=0;
355 uc->conn_per_hour=0;
356 }
357 }
358 else
359 {
360 /* for FLUSH PRIVILEGES and FLUSH USER_RESOURCES */
361 for (uint idx=0;idx < hash_user_connections.records; idx++)
362 {
363 USER_CONN *uc=(struct user_conn *)
364 my_hash_element(&hash_user_connections, idx);
365 if (get_them)
366 get_mqh(uc->user,uc->host,uc);
367 uc->questions=0;
368 uc->updates=0;
369 uc->conn_per_hour=0;
370 }
371 }
372 mysql_mutex_unlock(&LOCK_user_conn);
373#endif /* NO_EMBEDDED_ACCESS_CHECKS */
374}
375
376/*****************************************************************************
377 Handle users statistics
378*****************************************************************************/
379
380/* 'mysql_system_user' is used for when the user is not defined for a THD. */
381static const char mysql_system_user[]= "#mysql_system#";
382
383// Returns 'user' if it's not NULL. Returns 'mysql_system_user' otherwise.
384static const char * get_valid_user_string(const char* user)
385{
386 return user ? user : mysql_system_user;
387}
388
389/*
390 Returns string as 'IP' for the client-side of the connection represented by
391 'client'. Does not allocate memory. May return "".
392*/
393
394static const char *get_client_host(THD *client)
395{
396 return client->security_ctx->host_or_ip[0] ?
397 client->security_ctx->host_or_ip :
398 client->security_ctx->host ? client->security_ctx->host : "";
399}
400
401extern "C" uchar *get_key_user_stats(USER_STATS *user_stats, size_t *length,
402 my_bool not_used __attribute__((unused)))
403{
404 *length= user_stats->user_name_length;
405 return (uchar*) user_stats->user;
406}
407
408void free_user_stats(USER_STATS* user_stats)
409{
410 my_free(user_stats);
411}
412
413void init_user_stats(USER_STATS *user_stats,
414 const char *user,
415 size_t user_length,
416 const char *priv_user,
417 uint total_connections,
418 uint total_ssl_connections,
419 uint concurrent_connections,
420 time_t connected_time,
421 double busy_time,
422 double cpu_time,
423 ulonglong bytes_received,
424 ulonglong bytes_sent,
425 ulonglong binlog_bytes_written,
426 ha_rows rows_sent,
427 ha_rows rows_read,
428 ha_rows rows_inserted,
429 ha_rows rows_deleted,
430 ha_rows rows_updated,
431 ulonglong select_commands,
432 ulonglong update_commands,
433 ulonglong other_commands,
434 ulonglong commit_trans,
435 ulonglong rollback_trans,
436 ulonglong denied_connections,
437 ulonglong lost_connections,
438 ulonglong max_statement_time_exceeded,
439 ulonglong access_denied_errors,
440 ulonglong empty_queries)
441{
442 DBUG_ENTER("init_user_stats");
443 DBUG_PRINT("enter", ("user: %s priv_user: %s", user, priv_user));
444
445 user_length= MY_MIN(user_length, sizeof(user_stats->user)-1);
446 memcpy(user_stats->user, user, user_length);
447 user_stats->user[user_length]= 0;
448 user_stats->user_name_length= (uint)user_length;
449 strmake_buf(user_stats->priv_user, priv_user);
450
451 user_stats->total_connections= total_connections;
452 user_stats->total_ssl_connections= total_ssl_connections;
453 user_stats->concurrent_connections= concurrent_connections;
454 user_stats->connected_time= connected_time;
455 user_stats->busy_time= busy_time;
456 user_stats->cpu_time= cpu_time;
457 user_stats->bytes_received= bytes_received;
458 user_stats->bytes_sent= bytes_sent;
459 user_stats->binlog_bytes_written= binlog_bytes_written;
460 user_stats->rows_sent= rows_sent;
461 user_stats->rows_read= rows_read;
462 user_stats->rows_inserted= rows_inserted;
463 user_stats->rows_deleted= rows_deleted;
464 user_stats->rows_updated= rows_updated;
465 user_stats->select_commands= select_commands;
466 user_stats->update_commands= update_commands;
467 user_stats->other_commands= other_commands;
468 user_stats->commit_trans= commit_trans;
469 user_stats->rollback_trans= rollback_trans;
470 user_stats->denied_connections= denied_connections;
471 user_stats->lost_connections= lost_connections;
472 user_stats->max_statement_time_exceeded= max_statement_time_exceeded;
473 user_stats->access_denied_errors= access_denied_errors;
474 user_stats->empty_queries= empty_queries;
475 DBUG_VOID_RETURN;
476}
477
478
479void init_global_user_stats(void)
480{
481 my_hash_init(&global_user_stats, system_charset_info, max_connections,
482 0, 0, (my_hash_get_key) get_key_user_stats,
483 (my_hash_free_key) free_user_stats, 0);
484}
485
486void init_global_client_stats(void)
487{
488 my_hash_init(&global_client_stats, system_charset_info, max_connections,
489 0, 0, (my_hash_get_key) get_key_user_stats,
490 (my_hash_free_key) free_user_stats, 0);
491}
492
493extern "C" uchar *get_key_table_stats(TABLE_STATS *table_stats, size_t *length,
494 my_bool not_used __attribute__((unused)))
495{
496 *length= table_stats->table_name_length;
497 return (uchar*) table_stats->table;
498}
499
500extern "C" void free_table_stats(TABLE_STATS* table_stats)
501{
502 my_free(table_stats);
503}
504
505void init_global_table_stats(void)
506{
507 my_hash_init(&global_table_stats, system_charset_info, max_connections,
508 0, 0, (my_hash_get_key) get_key_table_stats,
509 (my_hash_free_key) free_table_stats, 0);
510}
511
512extern "C" uchar *get_key_index_stats(INDEX_STATS *index_stats, size_t *length,
513 my_bool not_used __attribute__((unused)))
514{
515 *length= index_stats->index_name_length;
516 return (uchar*) index_stats->index;
517}
518
519extern "C" void free_index_stats(INDEX_STATS* index_stats)
520{
521 my_free(index_stats);
522}
523
524void init_global_index_stats(void)
525{
526 my_hash_init(&global_index_stats, system_charset_info, max_connections,
527 0, 0, (my_hash_get_key) get_key_index_stats,
528 (my_hash_free_key) free_index_stats, 0);
529}
530
531
532void free_global_user_stats(void)
533{
534 my_hash_free(&global_user_stats);
535}
536
537void free_global_table_stats(void)
538{
539 my_hash_free(&global_table_stats);
540}
541
542void free_global_index_stats(void)
543{
544 my_hash_free(&global_index_stats);
545}
546
547void free_global_client_stats(void)
548{
549 my_hash_free(&global_client_stats);
550}
551
552/*
553 Increments the global stats connection count for an entry from
554 global_client_stats or global_user_stats. Returns 0 on success
555 and 1 on error.
556*/
557
558static bool increment_count_by_name(const char *name, size_t name_length,
559 const char *role_name,
560 HASH *users_or_clients, THD *thd)
561{
562 USER_STATS *user_stats;
563
564 if (!(user_stats= (USER_STATS*) my_hash_search(users_or_clients, (uchar*) name,
565 name_length)))
566 {
567 /* First connection for this user or client */
568 if (!(user_stats= ((USER_STATS*)
569 my_malloc(sizeof(USER_STATS),
570 MYF(MY_WME | MY_ZEROFILL)))))
571 return TRUE; // Out of memory
572
573 init_user_stats(user_stats, name, name_length, role_name,
574 0, 0, 0, // connections
575 0, 0, 0, // time
576 0, 0, 0, // bytes sent, received and written
577 0, 0, // rows sent and read
578 0, 0, 0, // rows inserted, deleted and updated
579 0, 0, 0, // select, update and other commands
580 0, 0, // commit and rollback trans
581 thd->status_var.access_denied_errors,
582 0, // lost connections
583 0, // max query timeouts
584 0, // access denied errors
585 0); // empty queries
586
587 if (my_hash_insert(users_or_clients, (uchar*)user_stats))
588 {
589 my_free(user_stats);
590 return TRUE; // Out of memory
591 }
592 }
593 user_stats->total_connections++;
594 if (thd->net.vio && thd->net.vio->type == VIO_TYPE_SSL)
595 user_stats->total_ssl_connections++;
596 return FALSE;
597}
598
599
600/*
601 Increments the global user and client stats connection count.
602
603 @param use_lock if true, LOCK_global_user_client_stats will be locked
604
605 @retval 0 ok
606 @retval 1 error.
607*/
608
609#ifndef EMBEDDED_LIBRARY
610static bool increment_connection_count(THD* thd, bool use_lock)
611{
612 const char *user_string= get_valid_user_string(thd->main_security_ctx.user);
613 const char *client_string= get_client_host(thd);
614 bool return_value= FALSE;
615
616 if (!thd->userstat_running)
617 return FALSE;
618
619 if (use_lock)
620 mysql_mutex_lock(&LOCK_global_user_client_stats);
621
622 if (increment_count_by_name(user_string, strlen(user_string), user_string,
623 &global_user_stats, thd))
624 {
625 return_value= TRUE;
626 goto end;
627 }
628 if (increment_count_by_name(client_string, strlen(client_string),
629 user_string, &global_client_stats, thd))
630 {
631 return_value= TRUE;
632 goto end;
633 }
634
635end:
636 if (use_lock)
637 mysql_mutex_unlock(&LOCK_global_user_client_stats);
638 return return_value;
639}
640#endif
641
642/*
643 Used to update the global user and client stats
644*/
645
646static void update_global_user_stats_with_user(THD *thd,
647 USER_STATS *user_stats,
648 time_t now)
649{
650 DBUG_ASSERT(thd->userstat_running);
651
652 user_stats->connected_time+= now - thd->last_global_update_time;
653 user_stats->busy_time+= (thd->status_var.busy_time -
654 thd->org_status_var.busy_time);
655 user_stats->cpu_time+= (thd->status_var.cpu_time -
656 thd->org_status_var.cpu_time);
657 /*
658 This is handle specially as bytes_received is incremented BEFORE
659 org_status_var is copied.
660 */
661 user_stats->bytes_received+= (thd->org_status_var.bytes_received-
662 thd->start_bytes_received);
663 user_stats->bytes_sent+= (thd->status_var.bytes_sent -
664 thd->org_status_var.bytes_sent);
665 user_stats->binlog_bytes_written+=
666 (thd->status_var.binlog_bytes_written -
667 thd->org_status_var.binlog_bytes_written);
668 /* We are not counting rows in internal temporary tables here ! */
669 user_stats->rows_read+= (thd->status_var.rows_read -
670 thd->org_status_var.rows_read);
671 user_stats->rows_sent+= (thd->status_var.rows_sent -
672 thd->org_status_var.rows_sent);
673 user_stats->rows_inserted+= (thd->status_var.ha_write_count -
674 thd->org_status_var.ha_write_count);
675 user_stats->rows_deleted+= (thd->status_var.ha_delete_count -
676 thd->org_status_var.ha_delete_count);
677 user_stats->rows_updated+= (thd->status_var.ha_update_count -
678 thd->org_status_var.ha_update_count);
679 user_stats->select_commands+= thd->select_commands;
680 user_stats->update_commands+= thd->update_commands;
681 user_stats->other_commands+= thd->other_commands;
682 user_stats->commit_trans+= (thd->status_var.ha_commit_count -
683 thd->org_status_var.ha_commit_count);
684 user_stats->rollback_trans+= (thd->status_var.ha_rollback_count +
685 thd->status_var.ha_savepoint_rollback_count -
686 thd->org_status_var.ha_rollback_count -
687 thd->org_status_var.
688 ha_savepoint_rollback_count);
689 user_stats->access_denied_errors+=
690 (thd->status_var.access_denied_errors -
691 thd->org_status_var.access_denied_errors);
692 user_stats->empty_queries+= (thd->status_var.empty_queries -
693 thd->org_status_var.empty_queries);
694
695 /* The following can only contain 0 or 1 and then connection ends */
696 user_stats->denied_connections+= thd->status_var.access_denied_errors;
697 user_stats->lost_connections+= thd->status_var.lost_connections;
698 user_stats->max_statement_time_exceeded+= thd->status_var.max_statement_time_exceeded;
699}
700
701
702/* Updates the global stats of a user or client */
703void update_global_user_stats(THD *thd, bool create_user, time_t now)
704{
705 const char *user_string, *client_string;
706 USER_STATS *user_stats;
707 size_t user_string_length, client_string_length;
708 DBUG_ASSERT(thd->userstat_running);
709
710 user_string= get_valid_user_string(thd->main_security_ctx.user);
711 user_string_length= strlen(user_string);
712 client_string= get_client_host(thd);
713 client_string_length= strlen(client_string);
714
715 mysql_mutex_lock(&LOCK_global_user_client_stats);
716
717 // Update by user name
718 if ((user_stats= (USER_STATS*) my_hash_search(&global_user_stats,
719 (uchar*) user_string,
720 user_string_length)))
721 {
722 /* Found user. */
723 update_global_user_stats_with_user(thd, user_stats, now);
724 }
725 else
726 {
727 /* Create the entry */
728 if (create_user)
729 {
730 increment_count_by_name(user_string, user_string_length, user_string,
731 &global_user_stats, thd);
732 }
733 }
734
735 /* Update by client IP */
736 if ((user_stats= (USER_STATS*)my_hash_search(&global_client_stats,
737 (uchar*) client_string,
738 client_string_length)))
739 {
740 // Found by client IP
741 update_global_user_stats_with_user(thd, user_stats, now);
742 }
743 else
744 {
745 // Create the entry
746 if (create_user)
747 {
748 increment_count_by_name(client_string, client_string_length,
749 user_string, &global_client_stats, thd);
750 }
751 }
752 /* Reset variables only used for counting */
753 thd->select_commands= thd->update_commands= thd->other_commands= 0;
754 thd->last_global_update_time= now;
755
756 mysql_mutex_unlock(&LOCK_global_user_client_stats);
757}
758
759
760/**
761 Set thread character set variables from the given ID
762
763 @param thd thread handle
764 @param cs_number character set and collation ID
765
766 @retval 0 OK; character_set_client, collation_connection and
767 character_set_results are set to the new value,
768 or to the default global values.
769
770 @retval 1 error, e.g. the given ID is not supported by parser.
771 Corresponding SQL error is sent.
772*/
773
774bool thd_init_client_charset(THD *thd, uint cs_number)
775{
776 CHARSET_INFO *cs;
777 /*
778 Use server character set and collation if
779 - opt_character_set_client_handshake is not set
780 - client has not specified a character set
781 - client character set doesn't exists in server
782 */
783 if (!opt_character_set_client_handshake ||
784 !(cs= get_charset(cs_number, MYF(0))))
785 {
786 thd->update_charset(global_system_variables.character_set_client,
787 global_system_variables.collation_connection,
788 global_system_variables.character_set_results);
789 }
790 else
791 {
792 if (!is_supported_parser_charset(cs))
793 {
794 /* Disallow non-supported parser character sets: UCS2, UTF16, UTF32 */
795 my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "character_set_client",
796 cs->csname);
797 return true;
798 }
799 thd->update_charset(cs,cs,cs);
800 }
801 return false;
802}
803
804
805/*
806 Initialize connection threads
807*/
808
809#ifndef EMBEDDED_LIBRARY
810bool init_new_connection_handler_thread()
811{
812 pthread_detach_this_thread();
813 if (my_thread_init())
814 {
815 statistic_increment(aborted_connects,&LOCK_status);
816 statistic_increment(connection_errors_internal, &LOCK_status);
817 return 1;
818 }
819 DBUG_EXECUTE_IF("simulate_failed_connection_1", return(1); );
820 return 0;
821}
822
823/**
824 Set client address during authentication.
825
826 Initializes THD::main_security_ctx and THD::peer_port.
827 Optionally does ip to hostname translation.
828
829 @param thd current THD handle
830 @param addr peer address (can be NULL, if 'ip' is set)
831 @param ip peer address as string (can be NULL if 'addr' is set)
832 @param port peer port
833 @param check_proxy_networks if true, and host is in
834 'proxy_protocol_networks' list, skip
835 "host not privileged" check
836 @param[out] host_errors - number of connect
837 errors for this host
838
839 @retval 0 ok, 1 error
840*/
841int thd_set_peer_addr(THD *thd,
842 sockaddr_storage *addr,
843 const char *ip,
844 uint port,
845 bool check_proxy_networks,
846 uint *host_errors)
847{
848 *host_errors= 0;
849
850 thd->peer_port= port;
851
852 char ip_string[128];
853 if (!ip)
854 {
855 void *addr_data;
856 if (addr->ss_family == AF_UNIX)
857 {
858 /* local connection */
859 my_free((void *)thd->main_security_ctx.ip);
860 thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host = my_localhost;
861 thd->main_security_ctx.ip= 0;
862 return 0;
863 }
864 else if (addr->ss_family == AF_INET)
865 addr_data= &((struct sockaddr_in *)addr)->sin_addr;
866 else
867 addr_data= &((struct sockaddr_in6 *)addr)->sin6_addr;
868 if (!inet_ntop(addr->ss_family,addr_data, ip_string, sizeof(ip_string)))
869 {
870 DBUG_ASSERT(0);
871 return 1;
872 }
873 ip= ip_string;
874 }
875
876 my_free((void *)thd->main_security_ctx.ip);
877 if (!(thd->main_security_ctx.ip = my_strdup(ip, MYF(MY_WME))))
878 {
879 /*
880 No error accounting per IP in host_cache,
881 this is treated as a global server OOM error.
882 TODO: remove the need for my_strdup.
883 */
884 statistic_increment(aborted_connects, &LOCK_status);
885 statistic_increment(connection_errors_internal, &LOCK_status);
886 return 1; /* The error is set by my_strdup(). */
887 }
888 thd->main_security_ctx.host_or_ip = thd->main_security_ctx.ip;
889 if (!(specialflag & SPECIAL_NO_RESOLVE))
890 {
891 int rc;
892
893 rc = ip_to_hostname(addr,
894 thd->main_security_ctx.ip,
895 &thd->main_security_ctx.host,
896 host_errors);
897
898 /* Cut very long hostnames to avoid possible overflows */
899 if (thd->main_security_ctx.host)
900 {
901 if (thd->main_security_ctx.host != my_localhost)
902 ((char*)thd->main_security_ctx.host)[MY_MIN(strlen(thd->main_security_ctx.host),
903 HOSTNAME_LENGTH)] = 0;
904 thd->main_security_ctx.host_or_ip = thd->main_security_ctx.host;
905 }
906
907 if (rc == RC_BLOCKED_HOST)
908 {
909 /* HOST_CACHE stats updated by ip_to_hostname(). */
910 my_error(ER_HOST_IS_BLOCKED, MYF(0), thd->main_security_ctx.host_or_ip);
911 return 1;
912 }
913 }
914 DBUG_PRINT("info", ("Host: %s ip: %s",
915 (thd->main_security_ctx.host ?
916 thd->main_security_ctx.host : "unknown host"),
917 (thd->main_security_ctx.ip ?
918 thd->main_security_ctx.ip : "unknown ip")));
919 if ((!check_proxy_networks || !is_proxy_protocol_allowed((struct sockaddr *) addr))
920 && acl_check_host(thd->main_security_ctx.host, thd->main_security_ctx.ip))
921 {
922 /* HOST_CACHE stats updated by acl_check_host(). */
923 my_error(ER_HOST_NOT_PRIVILEGED, MYF(0),
924 thd->main_security_ctx.host_or_ip);
925 return 1;
926 }
927 return 0;
928}
929
930/*
931 Perform handshake, authorize client and update thd ACL variables.
932
933 SYNOPSIS
934 check_connection()
935 thd thread handle
936
937 RETURN
938 0 success, thd is updated.
939 1 error
940*/
941
942static int check_connection(THD *thd)
943{
944 uint connect_errors= 0;
945 int auth_rc;
946 NET *net= &thd->net;
947
948 DBUG_PRINT("info",
949 ("New connection received on %s", vio_description(net->vio)));
950
951#ifdef SIGNAL_WITH_VIO_CLOSE
952 thd->set_active_vio(net->vio);
953#endif
954
955 if (!thd->main_security_ctx.host) // If TCP/IP connection
956 {
957 my_bool peer_rc;
958 char ip[NI_MAXHOST];
959 uint16 peer_port;
960
961 peer_rc= vio_peer_addr(net->vio, ip, &peer_port, NI_MAXHOST);
962
963 /*
964 ===========================================================================
965 DEBUG code only (begin)
966 Simulate various output from vio_peer_addr().
967 ===========================================================================
968 */
969
970 DBUG_EXECUTE_IF("vio_peer_addr_error",
971 {
972 peer_rc= 1;
973 }
974 );
975 DBUG_EXECUTE_IF("vio_peer_addr_fake_ipv4",
976 {
977 struct sockaddr *sa= (sockaddr *) &net->vio->remote;
978 sa->sa_family= AF_INET;
979 struct in_addr *ip4= &((struct sockaddr_in *) sa)->sin_addr;
980 /* See RFC 5737, 192.0.2.0/24 is reserved. */
981 const char* fake= "192.0.2.4";
982 inet_pton(AF_INET,fake, ip4);
983 strcpy(ip, fake);
984 peer_rc= 0;
985 }
986 );
987
988#ifdef HAVE_IPV6
989 DBUG_EXECUTE_IF("vio_peer_addr_fake_ipv6",
990 {
991 struct sockaddr_in6 *sa= (sockaddr_in6 *) &net->vio->remote;
992 sa->sin6_family= AF_INET6;
993 struct in6_addr *ip6= & sa->sin6_addr;
994 /* See RFC 3849, ipv6 2001:DB8::/32 is reserved. */
995 const char* fake= "2001:db8::6:6";
996 /* inet_pton(AF_INET6, fake, ip6); not available on Windows XP. */
997 ip6->s6_addr[ 0] = 0x20;
998 ip6->s6_addr[ 1] = 0x01;
999 ip6->s6_addr[ 2] = 0x0d;
1000 ip6->s6_addr[ 3] = 0xb8;
1001 ip6->s6_addr[ 4] = 0x00;
1002 ip6->s6_addr[ 5] = 0x00;
1003 ip6->s6_addr[ 6] = 0x00;
1004 ip6->s6_addr[ 7] = 0x00;
1005 ip6->s6_addr[ 8] = 0x00;
1006 ip6->s6_addr[ 9] = 0x00;
1007 ip6->s6_addr[10] = 0x00;
1008 ip6->s6_addr[11] = 0x00;
1009 ip6->s6_addr[12] = 0x00;
1010 ip6->s6_addr[13] = 0x06;
1011 ip6->s6_addr[14] = 0x00;
1012 ip6->s6_addr[15] = 0x06;
1013 strcpy(ip, fake);
1014 peer_rc= 0;
1015 }
1016 );
1017#endif /* HAVE_IPV6 */
1018
1019 /*
1020 ===========================================================================
1021 DEBUG code only (end)
1022 ===========================================================================
1023 */
1024
1025 if (peer_rc)
1026 {
1027 /*
1028 Since we can not even get the peer IP address,
1029 there is nothing to show in the host_cache,
1030 so increment the global status variable for peer address errors.
1031 */
1032 statistic_increment(connection_errors_peer_addr, &LOCK_status);
1033 my_error(ER_BAD_HOST_ERROR, MYF(0));
1034 return 1;
1035 }
1036
1037 if (thd_set_peer_addr(thd, &net->vio->remote, ip, peer_port,
1038 true, &connect_errors))
1039 return 1;
1040 }
1041 else /* Hostname given means that the connection was on a socket */
1042 {
1043 DBUG_PRINT("info",("Host: %s", thd->main_security_ctx.host));
1044 thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host;
1045 thd->main_security_ctx.ip= 0;
1046 /* Reset sin_addr */
1047 bzero((char*) &net->vio->remote, sizeof(net->vio->remote));
1048 }
1049 vio_keepalive(net->vio, TRUE);
1050 vio_set_keepalive_options(net->vio, &opt_vio_keepalive);
1051
1052 if (unlikely(thd->packet.alloc(thd->variables.net_buffer_length)))
1053 {
1054 /*
1055 Important note:
1056 net_buffer_length is a SESSION variable,
1057 so it may be tempting to account OOM conditions per IP in the HOST_CACHE,
1058 in case some clients are more demanding than others ...
1059 However, this session variable is *not* initialized with a per client
1060 value during the initial connection, it is initialized from the
1061 GLOBAL net_buffer_length variable from the server.
1062 Hence, there is no reason to account on OOM conditions per client IP,
1063 we count failures in the global server status instead.
1064 */
1065 statistic_increment(aborted_connects,&LOCK_status);
1066 statistic_increment(connection_errors_internal, &LOCK_status);
1067 return 1; /* The error is set by alloc(). */
1068 }
1069
1070 auth_rc= acl_authenticate(thd, 0);
1071 if (auth_rc == 0 && connect_errors != 0)
1072 {
1073 /*
1074 A client connection from this IP was successful,
1075 after some previous failures.
1076 Reset the connection error counter.
1077 */
1078 reset_host_connect_errors(thd->main_security_ctx.ip);
1079 }
1080
1081 return auth_rc;
1082}
1083
1084
1085/*
1086 Setup thread to be used with the current thread
1087
1088 SYNOPSIS
1089 bool setup_connection_thread_globals()
1090 thd Thread/connection handler
1091
1092 RETURN
1093 0 ok
1094 1 Error (out of memory)
1095 In this case we will close the connection and increment status
1096*/
1097
1098bool setup_connection_thread_globals(THD *thd)
1099{
1100 if (thd->store_globals())
1101 {
1102 close_connection(thd, ER_OUT_OF_RESOURCES);
1103 statistic_increment(aborted_connects,&LOCK_status);
1104 statistic_increment(connection_errors_internal, &LOCK_status);
1105 thd->scheduler->end_thread(thd, 0);
1106 return 1; // Error
1107 }
1108 return 0;
1109}
1110
1111
1112/*
1113 Autenticate user, with error reporting
1114
1115 SYNOPSIS
1116 login_connection()
1117 thd Thread handler
1118
1119 NOTES
1120 Connection is not closed in case of errors
1121
1122 RETURN
1123 0 ok
1124 1 error
1125*/
1126
1127bool login_connection(THD *thd)
1128{
1129 NET *net= &thd->net;
1130 int error= 0;
1131 DBUG_ENTER("login_connection");
1132 DBUG_PRINT("info", ("login_connection called by thread %lu",
1133 (ulong) thd->thread_id));
1134
1135 /* Use "connect_timeout" value during connection phase */
1136 my_net_set_read_timeout(net, connect_timeout);
1137 my_net_set_write_timeout(net, connect_timeout);
1138
1139 error= check_connection(thd);
1140 thd->protocol->end_statement();
1141
1142 if (unlikely(error))
1143 { // Wrong permissions
1144#ifdef _WIN32
1145 if (vio_type(net->vio) == VIO_TYPE_NAMEDPIPE)
1146 my_sleep(1000); /* must wait after eof() */
1147#endif
1148 statistic_increment(aborted_connects,&LOCK_status);
1149 error=1;
1150 goto exit;
1151 }
1152 /* Connect completed, set read/write timeouts back to default */
1153 my_net_set_read_timeout(net, thd->variables.net_read_timeout);
1154 my_net_set_write_timeout(net, thd->variables.net_write_timeout);
1155
1156 /* Updates global user connection stats. */
1157 if (increment_connection_count(thd, TRUE))
1158 {
1159 my_error(ER_OUTOFMEMORY, MYF(0), (int) (2*sizeof(USER_STATS)));
1160 error= 1;
1161 goto exit;
1162 }
1163
1164exit:
1165 mysql_audit_notify_connection_connect(thd);
1166 DBUG_RETURN(error);
1167}
1168
1169
1170/*
1171 Close an established connection
1172
1173 NOTES
1174 This mainly updates status variables
1175*/
1176
1177void end_connection(THD *thd)
1178{
1179 NET *net= &thd->net;
1180#ifdef WITH_WSREP
1181 if (WSREP(thd))
1182 {
1183 wsrep_status_t rcode= wsrep->free_connection(wsrep, thd->thread_id);
1184 if (rcode) {
1185 WSREP_WARN("wsrep failed to free connection context: %lld code: %d",
1186 (longlong) thd->thread_id, rcode);
1187 }
1188 }
1189 thd->wsrep_client_thread= 0;
1190#endif
1191 plugin_thdvar_cleanup(thd);
1192
1193 if (thd->user_connect)
1194 {
1195 /*
1196 We decrease this variable early to make it easy to log again quickly.
1197 This code is not critical as we will in any case do this test
1198 again in thd->cleanup()
1199 */
1200 decrease_user_connections(thd->user_connect);
1201 /*
1202 The thread may returned back to the pool and assigned to a user
1203 that doesn't have a limit. Ensure the user is not using resources
1204 of someone else.
1205 */
1206 thd->user_connect= NULL;
1207 }
1208
1209 if (unlikely(thd->killed) || (net->error && net->vio != 0))
1210 {
1211 statistic_increment(aborted_threads,&LOCK_status);
1212 status_var_increment(thd->status_var.lost_connections);
1213 }
1214
1215 if (likely(!thd->killed) && (net->error && net->vio != 0))
1216 thd->print_aborted_warning(1, thd->get_stmt_da()->is_error()
1217 ? thd->get_stmt_da()->message() : ER_THD(thd, ER_UNKNOWN_ERROR));
1218}
1219
1220
1221/*
1222 Initialize THD to handle queries
1223*/
1224
1225void prepare_new_connection_state(THD* thd)
1226{
1227 Security_context *sctx= thd->security_ctx;
1228
1229 if (thd->client_capabilities & CLIENT_COMPRESS)
1230 thd->net.compress=1; // Use compression
1231
1232 /*
1233 Much of this is duplicated in create_embedded_thd() for the
1234 embedded server library.
1235 TODO: refactor this to avoid code duplication there
1236 */
1237 thd->proc_info= 0;
1238 thd->set_command(COM_SLEEP);
1239 thd->init_for_queries();
1240
1241 if (opt_init_connect.length && !(sctx->master_access & SUPER_ACL))
1242 {
1243 execute_init_command(thd, &opt_init_connect, &LOCK_sys_init_connect);
1244 if (unlikely(thd->is_error()))
1245 {
1246 Host_errors errors;
1247 thd->set_killed(KILL_CONNECTION);
1248 thd->print_aborted_warning(0, "init_connect command failed");
1249 sql_print_warning("%s", thd->get_stmt_da()->message());
1250
1251 /*
1252 now let client to send its first command,
1253 to be able to send the error back
1254 */
1255 NET *net= &thd->net;
1256 thd->lex->current_select= 0;
1257 my_net_set_read_timeout(net, thd->variables.net_wait_timeout);
1258 thd->clear_error();
1259 net_new_transaction(net);
1260 ulong packet_length= my_net_read(net);
1261 /*
1262 If my_net_read() failed, my_error() has been already called,
1263 and the main Diagnostics Area contains an error condition.
1264 */
1265 if (packet_length != packet_error)
1266 my_error(ER_NEW_ABORTING_CONNECTION, MYF(0),
1267 thd->thread_id,
1268 thd->db.str ? thd->db.str : "unconnected",
1269 sctx->user ? sctx->user : "unauthenticated",
1270 sctx->host_or_ip, "init_connect command failed");
1271 thd->server_status&= ~SERVER_STATUS_CLEAR_SET;
1272 thd->protocol->end_statement();
1273 thd->killed = KILL_CONNECTION;
1274 errors.m_init_connect= 1;
1275 inc_host_errors(thd->main_security_ctx.ip, &errors);
1276 return;
1277 }
1278
1279 thd->proc_info=0;
1280 thd->init_for_queries();
1281 }
1282}
1283
1284
1285/*
1286 Thread handler for a connection
1287
1288 SYNOPSIS
1289 handle_one_connection()
1290 arg Connection object (THD)
1291
1292 IMPLEMENTATION
1293 This function (normally) does the following:
1294 - Initialize thread
1295 - Initialize THD to be used with this thread
1296 - Authenticate user
1297 - Execute all queries sent on the connection
1298 - Take connection down
1299 - End thread / Handle next connection using thread from thread cache
1300*/
1301
1302pthread_handler_t handle_one_connection(void *arg)
1303{
1304 CONNECT *connect= (CONNECT*) arg;
1305
1306 mysql_thread_set_psi_id(connect->thread_id);
1307
1308 do_handle_one_connection(connect);
1309 return 0;
1310}
1311
1312bool thd_prepare_connection(THD *thd)
1313{
1314 bool rc;
1315 lex_start(thd);
1316 rc= login_connection(thd);
1317 if (rc)
1318 return rc;
1319
1320 MYSQL_CONNECTION_START(thd->thread_id, &thd->security_ctx->priv_user[0],
1321 (char *) thd->security_ctx->host_or_ip);
1322
1323 prepare_new_connection_state(thd);
1324#ifdef WITH_WSREP
1325 thd->wsrep_client_thread= 1;
1326#endif /* WITH_WSREP */
1327 return FALSE;
1328}
1329
1330bool thd_is_connection_alive(THD *thd)
1331{
1332 NET *net= &thd->net;
1333 if (likely(!net->error &&
1334 net->vio != 0 &&
1335 thd->killed < KILL_CONNECTION))
1336 return TRUE;
1337 return FALSE;
1338}
1339
1340
1341void do_handle_one_connection(CONNECT *connect)
1342{
1343 ulonglong thr_create_utime= microsecond_interval_timer();
1344 THD *thd;
1345 if (connect->scheduler->init_new_connection_thread() ||
1346 !(thd= connect->create_thd(NULL)))
1347 {
1348 scheduler_functions *scheduler= connect->scheduler;
1349 connect->close_with_error(0, 0, ER_OUT_OF_RESOURCES);
1350 scheduler->end_thread(0, 0);
1351 return;
1352 }
1353
1354 /*
1355 If a thread was created to handle this connection:
1356 increment slow_launch_threads counter if it took more than
1357 slow_launch_time seconds to create the thread.
1358 */
1359
1360 if (connect->prior_thr_create_utime)
1361 {
1362 ulong launch_time= (ulong) (thr_create_utime -
1363 connect->prior_thr_create_utime);
1364 if (launch_time >= slow_launch_time*1000000L)
1365 statistic_increment(slow_launch_threads, &LOCK_status);
1366 }
1367 delete connect;
1368
1369 /* Make THD visible in show processlist */
1370 add_to_active_threads(thd);
1371
1372 thd->thr_create_utime= thr_create_utime;
1373 /* We need to set this because of time_out_user_resource_limits */
1374 thd->start_utime= thr_create_utime;
1375
1376 /*
1377 handle_one_connection() is normally the only way a thread would
1378 start and would always be on the very high end of the stack ,
1379 therefore, the thread stack always starts at the address of the
1380 first local variable of handle_one_connection, which is thd. We
1381 need to know the start of the stack so that we could check for
1382 stack overruns.
1383 */
1384 thd->thread_stack= (char*) &thd;
1385 if (setup_connection_thread_globals(thd))
1386 return;
1387
1388 for (;;)
1389 {
1390 bool create_user= TRUE;
1391
1392 mysql_socket_set_thread_owner(thd->net.vio->mysql_socket);
1393 if (thd_prepare_connection(thd))
1394 {
1395 create_user= FALSE;
1396 goto end_thread;
1397 }
1398
1399 while (thd_is_connection_alive(thd))
1400 {
1401 mysql_audit_release(thd);
1402 if (do_command(thd))
1403 break;
1404 }
1405 end_connection(thd);
1406
1407#ifdef WITH_WSREP
1408 if (WSREP(thd))
1409 {
1410 mysql_mutex_lock(&thd->LOCK_thd_data);
1411 thd->wsrep_query_state= QUERY_EXITING;
1412 mysql_mutex_unlock(&thd->LOCK_thd_data);
1413 }
1414#endif
1415end_thread:
1416 close_connection(thd);
1417
1418 if (thd->userstat_running)
1419 update_global_user_stats(thd, create_user, time(NULL));
1420
1421 if (thd->scheduler->end_thread(thd, 1))
1422 return; // Probably no-threads
1423
1424 /*
1425 If end_thread() returns, this thread has been schedule to
1426 handle the next connection.
1427 */
1428 thd= current_thd;
1429 thd->thread_stack= (char*) &thd;
1430 }
1431}
1432#endif /* EMBEDDED_LIBRARY */
1433
1434
1435/* Handling of CONNECT objects */
1436
1437/*
1438 Close connection without error and delete the connect object
1439 This and close_with_error are only called if we didn't manage to
1440 create a new thd object.
1441*/
1442
1443void CONNECT::close_and_delete()
1444{
1445 DBUG_ENTER("close_and_delete");
1446
1447 if (vio)
1448 vio_close(vio);
1449 if (thread_count_incremented)
1450 dec_connection_count(scheduler);
1451 statistic_increment(connection_errors_internal, &LOCK_status);
1452 statistic_increment(aborted_connects,&LOCK_status);
1453
1454 delete this;
1455 DBUG_VOID_RETURN;
1456}
1457
1458/*
1459 Close a connection with a possible error to the end user
1460 Alse deletes the connection object, like close_and_delete()
1461*/
1462
1463void CONNECT::close_with_error(uint sql_errno,
1464 const char *message, uint close_error)
1465{
1466 THD *thd= create_thd(NULL);
1467 if (thd)
1468 {
1469 if (sql_errno)
1470 net_send_error(thd, sql_errno, message, NULL);
1471 close_connection(thd, close_error);
1472 delete thd;
1473 set_current_thd(0);
1474 }
1475 close_and_delete();
1476}
1477
1478
1479CONNECT::~CONNECT()
1480{
1481 if (vio)
1482 vio_delete(vio);
1483}
1484
1485
1486/* Reuse or create a THD based on a CONNECT object */
1487
1488THD *CONNECT::create_thd(THD *thd)
1489{
1490 bool res, thd_reused= thd != 0;
1491 DBUG_ENTER("create_thd");
1492
1493 DBUG_EXECUTE_IF("simulate_failed_connection_2", DBUG_RETURN(0); );
1494
1495 if (thd)
1496 {
1497 /* reuse old thd */
1498 thd->reset_for_reuse();
1499 /*
1500 reset tread_id's, but not thread_dbug_id's as the later isn't allowed
1501 to change as there is already structures in thd marked with the old
1502 value.
1503 */
1504 thd->thread_id= thd->variables.pseudo_thread_id= thread_id;
1505 }
1506 else if (!(thd= new THD(thread_id)))
1507 DBUG_RETURN(0);
1508
1509 set_current_thd(thd);
1510 res= my_net_init(&thd->net, vio, thd, MYF(MY_THREAD_SPECIFIC));
1511 vio= 0; // Vio now handled by thd
1512
1513 if (unlikely(res || thd->is_error()))
1514 {
1515 if (!thd_reused)
1516 delete thd;
1517 set_current_thd(0);
1518 DBUG_RETURN(0);
1519 }
1520
1521 init_net_server_extension(thd);
1522
1523 thd->security_ctx->host= host;
1524 thd->extra_port= extra_port;
1525 thd->scheduler= scheduler;
1526 thd->real_id= real_id;
1527 DBUG_RETURN(thd);
1528}
1529