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