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-1335 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
43 HASH global_user_stats, global_client_stats, global_table_stats;
44 HASH global_index_stats;
45 /* Protects the above global stats */
46 extern mysql_mutex_t LOCK_global_user_client_stats;
47 extern mysql_mutex_t LOCK_global_table_stats;
48 extern mysql_mutex_t LOCK_global_index_stats;
49 extern 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
56 static HASH hash_user_connections;
57
get_or_create_user_conn(THD * thd,const char * user,const char * host,const USER_RESOURCES * mqh)58 int 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->reset_utime= thd->thr_create_utime;
92 if (my_hash_insert(&hash_user_connections, (uchar*) uc))
93 {
94 /* The only possible error is out of memory, MY_WME sets an error. */
95 my_free(uc);
96 return_val= 1;
97 goto end;
98 }
99 }
100 uc->user_resources= *mqh;
101 thd->user_connect=uc;
102 uc->connections++;
103 end:
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
check_for_max_user_connections(THD * thd,USER_CONN * uc)126 int 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
169 end:
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
decrease_user_connections(USER_CONN * uc)207 void 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
time_out_user_resource_limits(THD * thd,USER_CONN * uc)236 void 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
check_mqh(THD * thd,uint check_command)258 bool 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 }
291 end:
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
get_key_conn(user_conn * buff,size_t * length,my_bool not_used)303 extern "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
free_user(struct user_conn * uc)311 extern "C" void free_user(struct user_conn *uc)
312 {
313 my_free(uc);
314 }
315
316
init_max_user_conn(void)317 void 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
free_max_user_conn(void)327 void 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
reset_mqh(LEX_USER * lu,bool get_them=0)335 void 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. */
381 static const char mysql_system_user[]= "#mysql_system#";
382
383 // Returns 'user' if it's not NULL. Returns 'mysql_system_user' otherwise.
get_valid_user_string(const char * user)384 static 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
get_client_host(THD * client)394 static 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
get_key_user_stats(USER_STATS * user_stats,size_t * length,my_bool not_used)401 extern "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
free_user_stats(USER_STATS * user_stats)408 void free_user_stats(USER_STATS* user_stats)
409 {
410 my_free(user_stats);
411 }
412
init_user_stats(USER_STATS * user_stats,const char * user,size_t user_length,const char * priv_user,uint total_connections,uint total_ssl_connections,uint concurrent_connections,time_t connected_time,double busy_time,double cpu_time,ulonglong bytes_received,ulonglong bytes_sent,ulonglong binlog_bytes_written,ha_rows rows_sent,ha_rows rows_read,ha_rows rows_inserted,ha_rows rows_deleted,ha_rows rows_updated,ulonglong select_commands,ulonglong update_commands,ulonglong other_commands,ulonglong commit_trans,ulonglong rollback_trans,ulonglong denied_connections,ulonglong lost_connections,ulonglong max_statement_time_exceeded,ulonglong access_denied_errors,ulonglong empty_queries)413 void 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
init_global_user_stats(void)479 void 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
init_global_client_stats(void)486 void 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
get_key_table_stats(TABLE_STATS * table_stats,size_t * length,my_bool not_used)493 extern "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
free_table_stats(TABLE_STATS * table_stats)500 extern "C" void free_table_stats(TABLE_STATS* table_stats)
501 {
502 my_free(table_stats);
503 }
504
init_global_table_stats(void)505 void 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
get_key_index_stats(INDEX_STATS * index_stats,size_t * length,my_bool not_used)512 extern "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
free_index_stats(INDEX_STATS * index_stats)519 extern "C" void free_index_stats(INDEX_STATS* index_stats)
520 {
521 my_free(index_stats);
522 }
523
init_global_index_stats(void)524 void 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
free_global_user_stats(void)532 void free_global_user_stats(void)
533 {
534 my_hash_free(&global_user_stats);
535 }
536
free_global_table_stats(void)537 void free_global_table_stats(void)
538 {
539 my_hash_free(&global_table_stats);
540 }
541
free_global_index_stats(void)542 void free_global_index_stats(void)
543 {
544 my_hash_free(&global_index_stats);
545 }
546
free_global_client_stats(void)547 void 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
increment_count_by_name(const char * name,size_t name_length,const char * role_name,HASH * users_or_clients,THD * thd)558 static 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
increment_connection_count(THD * thd,bool use_lock)610 static 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
635 end:
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
update_global_user_stats_with_user(THD * thd,USER_STATS * user_stats,time_t now)646 static 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 */
update_global_user_stats(THD * thd,bool create_user,time_t now)703 void 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
thd_init_client_charset(THD * thd,uint cs_number)774 bool 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->org_charset= cs;
800 thd->update_charset(cs,cs,cs);
801 }
802 return false;
803 }
804
805
806 /*
807 Initialize connection threads
808 */
809
810 #ifndef EMBEDDED_LIBRARY
init_new_connection_handler_thread()811 bool init_new_connection_handler_thread()
812 {
813 pthread_detach_this_thread();
814 if (my_thread_init())
815 {
816 statistic_increment(aborted_connects,&LOCK_status);
817 statistic_increment(connection_errors_internal, &LOCK_status);
818 return 1;
819 }
820 DBUG_EXECUTE_IF("simulate_failed_connection_1", return(1); );
821 return 0;
822 }
823
824 /**
825 Set client address during authentication.
826
827 Initializes THD::main_security_ctx and THD::peer_port.
828 Optionally does ip to hostname translation.
829
830 @param thd current THD handle
831 @param addr peer address (can be NULL, if 'ip' is set)
832 @param ip peer address as string (can be NULL if 'addr' is set)
833 @param port peer port
834 @param check_proxy_networks if true, and host is in
835 'proxy_protocol_networks' list, skip
836 "host not privileged" check
837 @param[out] host_errors - number of connect
838 errors for this host
839
840 @retval 0 ok, 1 error
841 */
thd_set_peer_addr(THD * thd,sockaddr_storage * addr,const char * ip,uint port,bool check_proxy_networks,uint * host_errors)842 int thd_set_peer_addr(THD *thd,
843 sockaddr_storage *addr,
844 const char *ip,
845 uint port,
846 bool check_proxy_networks,
847 uint *host_errors)
848 {
849 *host_errors= 0;
850
851 thd->peer_port= port;
852
853 char ip_string[128];
854 if (!ip)
855 {
856 void *addr_data;
857 if (addr->ss_family == AF_UNIX)
858 {
859 /* local connection */
860 my_free((void *)thd->main_security_ctx.ip);
861 thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host = my_localhost;
862 thd->main_security_ctx.ip= 0;
863 return 0;
864 }
865 else if (addr->ss_family == AF_INET)
866 addr_data= &((struct sockaddr_in *)addr)->sin_addr;
867 else
868 addr_data= &((struct sockaddr_in6 *)addr)->sin6_addr;
869 if (!inet_ntop(addr->ss_family,addr_data, ip_string, sizeof(ip_string)))
870 {
871 DBUG_ASSERT(0);
872 return 1;
873 }
874 ip= ip_string;
875 }
876
877 my_free((void *)thd->main_security_ctx.ip);
878 if (!(thd->main_security_ctx.ip = my_strdup(ip, MYF(MY_WME))))
879 {
880 /*
881 No error accounting per IP in host_cache,
882 this is treated as a global server OOM error.
883 TODO: remove the need for my_strdup.
884 */
885 statistic_increment(aborted_connects, &LOCK_status);
886 statistic_increment(connection_errors_internal, &LOCK_status);
887 return 1; /* The error is set by my_strdup(). */
888 }
889 thd->main_security_ctx.host_or_ip = thd->main_security_ctx.ip;
890 if (!(specialflag & SPECIAL_NO_RESOLVE))
891 {
892 int rc;
893
894 rc = ip_to_hostname(addr,
895 thd->main_security_ctx.ip,
896 &thd->main_security_ctx.host,
897 host_errors);
898
899 /* Cut very long hostnames to avoid possible overflows */
900 if (thd->main_security_ctx.host)
901 {
902 if (thd->main_security_ctx.host != my_localhost)
903 ((char*)thd->main_security_ctx.host)[MY_MIN(strlen(thd->main_security_ctx.host),
904 HOSTNAME_LENGTH)] = 0;
905 thd->main_security_ctx.host_or_ip = thd->main_security_ctx.host;
906 }
907
908 if (rc == RC_BLOCKED_HOST)
909 {
910 /* HOST_CACHE stats updated by ip_to_hostname(). */
911 my_error(ER_HOST_IS_BLOCKED, MYF(0), thd->main_security_ctx.host_or_ip);
912 return 1;
913 }
914 }
915 DBUG_PRINT("info", ("Host: %s ip: %s",
916 (thd->main_security_ctx.host ?
917 thd->main_security_ctx.host : "unknown host"),
918 (thd->main_security_ctx.ip ?
919 thd->main_security_ctx.ip : "unknown ip")));
920 if ((!check_proxy_networks || !is_proxy_protocol_allowed((struct sockaddr *) addr))
921 && acl_check_host(thd->main_security_ctx.host, thd->main_security_ctx.ip))
922 {
923 /* HOST_CACHE stats updated by acl_check_host(). */
924 my_error(ER_HOST_NOT_PRIVILEGED, MYF(0),
925 thd->main_security_ctx.host_or_ip);
926 return 1;
927 }
928 return 0;
929 }
930
931 /*
932 Perform handshake, authorize client and update thd ACL variables.
933
934 SYNOPSIS
935 check_connection()
936 thd thread handle
937
938 RETURN
939 0 success, thd is updated.
940 1 error
941 */
942
check_connection(THD * thd)943 static int check_connection(THD *thd)
944 {
945 uint connect_errors= 0;
946 int auth_rc;
947 NET *net= &thd->net;
948
949 DBUG_PRINT("info",
950 ("New connection received on %s", vio_description(net->vio)));
951
952 #ifdef SIGNAL_WITH_VIO_CLOSE
953 thd->set_active_vio(net->vio);
954 #endif
955
956 if (!thd->main_security_ctx.host) // If TCP/IP connection
957 {
958 my_bool peer_rc;
959 char ip[NI_MAXHOST];
960 uint16 peer_port;
961
962 peer_rc= vio_peer_addr(net->vio, ip, &peer_port, NI_MAXHOST);
963
964 /*
965 ===========================================================================
966 DEBUG code only (begin)
967 Simulate various output from vio_peer_addr().
968 ===========================================================================
969 */
970
971 DBUG_EXECUTE_IF("vio_peer_addr_error",
972 {
973 peer_rc= 1;
974 }
975 );
976 DBUG_EXECUTE_IF("vio_peer_addr_fake_ipv4",
977 {
978 struct sockaddr *sa= (sockaddr *) &net->vio->remote;
979 sa->sa_family= AF_INET;
980 struct in_addr *ip4= &((struct sockaddr_in *) sa)->sin_addr;
981 /* See RFC 5737, 192.0.2.0/24 is reserved. */
982 const char* fake= "192.0.2.4";
983 inet_pton(AF_INET,fake, ip4);
984 strcpy(ip, fake);
985 peer_rc= 0;
986 }
987 );
988
989 #ifdef HAVE_IPV6
990 DBUG_EXECUTE_IF("vio_peer_addr_fake_ipv6",
991 {
992 struct sockaddr_in6 *sa= (sockaddr_in6 *) &net->vio->remote;
993 sa->sin6_family= AF_INET6;
994 struct in6_addr *ip6= & sa->sin6_addr;
995 /* See RFC 3849, ipv6 2001:DB8::/32 is reserved. */
996 const char* fake= "2001:db8::6:6";
997 /* inet_pton(AF_INET6, fake, ip6); not available on Windows XP. */
998 ip6->s6_addr[ 0] = 0x20;
999 ip6->s6_addr[ 1] = 0x01;
1000 ip6->s6_addr[ 2] = 0x0d;
1001 ip6->s6_addr[ 3] = 0xb8;
1002 ip6->s6_addr[ 4] = 0x00;
1003 ip6->s6_addr[ 5] = 0x00;
1004 ip6->s6_addr[ 6] = 0x00;
1005 ip6->s6_addr[ 7] = 0x00;
1006 ip6->s6_addr[ 8] = 0x00;
1007 ip6->s6_addr[ 9] = 0x00;
1008 ip6->s6_addr[10] = 0x00;
1009 ip6->s6_addr[11] = 0x00;
1010 ip6->s6_addr[12] = 0x00;
1011 ip6->s6_addr[13] = 0x06;
1012 ip6->s6_addr[14] = 0x00;
1013 ip6->s6_addr[15] = 0x06;
1014 strcpy(ip, fake);
1015 peer_rc= 0;
1016 }
1017 );
1018 #endif /* HAVE_IPV6 */
1019
1020 /*
1021 ===========================================================================
1022 DEBUG code only (end)
1023 ===========================================================================
1024 */
1025
1026 if (peer_rc)
1027 {
1028 /*
1029 Since we can not even get the peer IP address,
1030 there is nothing to show in the host_cache,
1031 so increment the global status variable for peer address errors.
1032 */
1033 statistic_increment(connection_errors_peer_addr, &LOCK_status);
1034 my_error(ER_BAD_HOST_ERROR, MYF(0));
1035 return 1;
1036 }
1037
1038 if (thd_set_peer_addr(thd, &net->vio->remote, ip, peer_port,
1039 true, &connect_errors))
1040 return 1;
1041 }
1042 else /* Hostname given means that the connection was on a socket */
1043 {
1044 DBUG_PRINT("info",("Host: %s", thd->main_security_ctx.host));
1045 thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host;
1046 thd->main_security_ctx.ip= 0;
1047 /* Reset sin_addr */
1048 bzero((char*) &net->vio->remote, sizeof(net->vio->remote));
1049 }
1050 vio_keepalive(net->vio, TRUE);
1051 vio_set_keepalive_options(net->vio, &opt_vio_keepalive);
1052
1053 if (unlikely(thd->packet.alloc(thd->variables.net_buffer_length)))
1054 {
1055 /*
1056 Important note:
1057 net_buffer_length is a SESSION variable,
1058 so it may be tempting to account OOM conditions per IP in the HOST_CACHE,
1059 in case some clients are more demanding than others ...
1060 However, this session variable is *not* initialized with a per client
1061 value during the initial connection, it is initialized from the
1062 GLOBAL net_buffer_length variable from the server.
1063 Hence, there is no reason to account on OOM conditions per client IP,
1064 we count failures in the global server status instead.
1065 */
1066 statistic_increment(aborted_connects,&LOCK_status);
1067 statistic_increment(connection_errors_internal, &LOCK_status);
1068 return 1; /* The error is set by alloc(). */
1069 }
1070
1071 auth_rc= acl_authenticate(thd, 0);
1072 if (auth_rc == 0 && connect_errors != 0)
1073 {
1074 /*
1075 A client connection from this IP was successful,
1076 after some previous failures.
1077 Reset the connection error counter.
1078 */
1079 reset_host_connect_errors(thd->main_security_ctx.ip);
1080 }
1081
1082 return auth_rc;
1083 }
1084
1085
1086 /*
1087 Setup thread to be used with the current thread
1088
1089 SYNOPSIS
1090 bool setup_connection_thread_globals()
1091 thd Thread/connection handler
1092
1093 RETURN
1094 0 ok
1095 1 Error (out of memory)
1096 In this case we will close the connection and increment status
1097 */
1098
setup_connection_thread_globals(THD * thd)1099 bool setup_connection_thread_globals(THD *thd)
1100 {
1101 if (thd->store_globals())
1102 {
1103 close_connection(thd, ER_OUT_OF_RESOURCES);
1104 statistic_increment(aborted_connects,&LOCK_status);
1105 statistic_increment(connection_errors_internal, &LOCK_status);
1106 thd->scheduler->end_thread(thd, 0);
1107 return 1; // Error
1108 }
1109 return 0;
1110 }
1111
1112
1113 /*
1114 Autenticate user, with error reporting
1115
1116 SYNOPSIS
1117 login_connection()
1118 thd Thread handler
1119
1120 NOTES
1121 Connection is not closed in case of errors
1122
1123 RETURN
1124 0 ok
1125 1 error
1126 */
1127
login_connection(THD * thd)1128 bool login_connection(THD *thd)
1129 {
1130 NET *net= &thd->net;
1131 int error= 0;
1132 DBUG_ENTER("login_connection");
1133 DBUG_PRINT("info", ("login_connection called by thread %lu",
1134 (ulong) thd->thread_id));
1135
1136 /* Use "connect_timeout" value during connection phase */
1137 my_net_set_read_timeout(net, connect_timeout);
1138 my_net_set_write_timeout(net, connect_timeout);
1139
1140 error= check_connection(thd);
1141 thd->protocol->end_statement();
1142
1143 if (unlikely(error))
1144 { // Wrong permissions
1145 #ifdef _WIN32
1146 if (vio_type(net->vio) == VIO_TYPE_NAMEDPIPE)
1147 my_sleep(1000); /* must wait after eof() */
1148 #endif
1149 statistic_increment(aborted_connects,&LOCK_status);
1150 error=1;
1151 goto exit;
1152 }
1153 /* Connect completed, set read/write timeouts back to default */
1154 my_net_set_read_timeout(net, thd->variables.net_read_timeout);
1155 my_net_set_write_timeout(net, thd->variables.net_write_timeout);
1156
1157 /* Updates global user connection stats. */
1158 if (increment_connection_count(thd, TRUE))
1159 {
1160 my_error(ER_OUTOFMEMORY, MYF(0), (int) (2*sizeof(USER_STATS)));
1161 error= 1;
1162 goto exit;
1163 }
1164
1165 exit:
1166 mysql_audit_notify_connection_connect(thd);
1167 DBUG_RETURN(error);
1168 }
1169
1170
1171 /*
1172 Close an established connection
1173
1174 NOTES
1175 This mainly updates status variables
1176 */
1177
end_connection(THD * thd)1178 void end_connection(THD *thd)
1179 {
1180 NET *net= &thd->net;
1181 #ifdef WITH_WSREP
1182 if (WSREP(thd) && wsrep)
1183 {
1184 wsrep_status_t rcode= wsrep->free_connection(wsrep, thd->thread_id);
1185 if (rcode) {
1186 WSREP_WARN("wsrep failed to free connection context: %lld code: %d",
1187 (longlong) thd->thread_id, rcode);
1188 }
1189 }
1190 thd->wsrep_client_thread= 0;
1191 #endif
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
prepare_new_connection_state(THD * thd)1225 void 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
handle_one_connection(void * arg)1302 pthread_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
thd_prepare_connection(THD * thd)1312 bool 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
thd_is_connection_alive(THD * thd)1330 bool 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
do_handle_one_connection(CONNECT * connect)1341 void 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 if (mysql_audit_release_required(thd))
1402 mysql_audit_release(thd);
1403 if (do_command(thd))
1404 break;
1405 }
1406 end_connection(thd);
1407
1408 #ifdef WITH_WSREP
1409 if (WSREP(thd))
1410 {
1411 mysql_mutex_lock(&thd->LOCK_thd_data);
1412 wsrep_thd_set_query_state(thd, QUERY_EXITING);
1413 mysql_mutex_unlock(&thd->LOCK_thd_data);
1414 }
1415 #endif
1416 end_thread:
1417 close_connection(thd);
1418
1419 if (thd->userstat_running)
1420 update_global_user_stats(thd, create_user, time(NULL));
1421
1422 if (thd->scheduler->end_thread(thd, 1))
1423 return; // Probably no-threads
1424
1425 /*
1426 If end_thread() returns, this thread has been schedule to
1427 handle the next connection.
1428 */
1429 thd= current_thd;
1430 thd->thread_stack= (char*) &thd;
1431 }
1432 }
1433 #endif /* EMBEDDED_LIBRARY */
1434
1435
1436 /* Handling of CONNECT objects */
1437
1438 /*
1439 Close connection without error and delete the connect object
1440 This and close_with_error are only called if we didn't manage to
1441 create a new thd object.
1442 */
1443
close_and_delete()1444 void CONNECT::close_and_delete()
1445 {
1446 DBUG_ENTER("close_and_delete");
1447
1448 if (vio)
1449 vio_close(vio);
1450 if (thread_count_incremented)
1451 dec_connection_count(scheduler);
1452 statistic_increment(connection_errors_internal, &LOCK_status);
1453 statistic_increment(aborted_connects,&LOCK_status);
1454
1455 delete this;
1456 DBUG_VOID_RETURN;
1457 }
1458
1459 /*
1460 Close a connection with a possible error to the end user
1461 Alse deletes the connection object, like close_and_delete()
1462 */
1463
close_with_error(uint sql_errno,const char * message,uint close_error)1464 void CONNECT::close_with_error(uint sql_errno,
1465 const char *message, uint close_error)
1466 {
1467 THD *thd= create_thd(NULL);
1468 if (thd)
1469 {
1470 if (sql_errno)
1471 net_send_error(thd, sql_errno, message, NULL);
1472 close_connection(thd, close_error);
1473 delete thd;
1474 set_current_thd(0);
1475 }
1476 close_and_delete();
1477 }
1478
1479
~CONNECT()1480 CONNECT::~CONNECT()
1481 {
1482 if (vio)
1483 vio_delete(vio);
1484 }
1485
1486
1487 /* Reuse or create a THD based on a CONNECT object */
1488
create_thd(THD * thd)1489 THD *CONNECT::create_thd(THD *thd)
1490 {
1491 bool res, thd_reused= thd != 0;
1492 DBUG_ENTER("create_thd");
1493
1494 DBUG_EXECUTE_IF("simulate_failed_connection_2", DBUG_RETURN(0); );
1495
1496 if (thd)
1497 {
1498 /* reuse old thd */
1499 thd->reset_for_reuse();
1500 /*
1501 reset tread_id's, but not thread_dbug_id's as the later isn't allowed
1502 to change as there is already structures in thd marked with the old
1503 value.
1504 */
1505 thd->thread_id= thd->variables.pseudo_thread_id= thread_id;
1506 }
1507 else if (!(thd= new THD(thread_id)))
1508 DBUG_RETURN(0);
1509
1510 set_current_thd(thd);
1511 res= my_net_init(&thd->net, vio, thd, MYF(MY_THREAD_SPECIFIC));
1512 vio= 0; // Vio now handled by thd
1513
1514 if (unlikely(res || thd->is_error()))
1515 {
1516 if (!thd_reused)
1517 delete thd;
1518 set_current_thd(0);
1519 DBUG_RETURN(0);
1520 }
1521
1522 init_net_server_extension(thd);
1523
1524 thd->security_ctx->host= host;
1525 thd->extra_port= extra_port;
1526 thd->scheduler= scheduler;
1527 thd->real_id= real_id;
1528 DBUG_RETURN(thd);
1529 }
1530