1
by brian
clean slate |
1 |
/* Copyright (C) 2007 MySQL AB
|
2 |
||
3 |
This program is free software; you can redistribute it and/or modify
|
|
4 |
it under the terms of the GNU General Public License as published by
|
|
5 |
the Free Software Foundation; version 2 of the License.
|
|
6 |
||
7 |
This program is distributed in the hope that it will be useful,
|
|
8 |
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
9 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
10 |
GNU General Public License for more details.
|
|
11 |
||
12 |
You should have received a copy of the GNU General Public License
|
|
13 |
along with this program; if not, write to the Free Software
|
|
14 |
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|
15 |
||
16 |
||
17 |
/*
|
|
18 |
Functions to autenticate and handle reqests for a connection
|
|
19 |
*/
|
|
20 |
||
21 |
#include "mysql_priv.h" |
|
22 |
||
23 |
#define MIN_HANDSHAKE_SIZE 6
|
|
24 |
||
25 |
/*
|
|
26 |
Get structure for logging connection data for the current user
|
|
27 |
*/
|
|
28 |
||
29 |
char *ip_to_hostname(struct sockaddr_storage *in, int addrLen) |
|
30 |
{
|
|
31 |
char *name; |
|
32 |
||
33 |
int gxi_error; |
|
34 |
char hostname_buff[NI_MAXHOST]; |
|
35 |
||
36 |
/* Historical comparison for 127.0.0.1 */
|
|
37 |
gxi_error= getnameinfo((struct sockaddr *)in, addrLen, |
|
38 |
hostname_buff, NI_MAXHOST, |
|
39 |
NULL, 0, NI_NUMERICHOST); |
|
40 |
if (gxi_error) |
|
41 |
{
|
|
42 |
return NULL; |
|
43 |
}
|
|
44 |
||
45 |
if (!(name= my_strdup(hostname_buff, MYF(0)))) |
|
46 |
{
|
|
47 |
return NULL; |
|
48 |
}
|
|
49 |
||
50 |
return NULL; |
|
51 |
}
|
|
52 |
||
53 |
/**
|
|
54 |
Check if user exist and password supplied is correct.
|
|
55 |
||
56 |
@param thd thread handle, thd->security_ctx->{host,user,ip} are used
|
|
57 |
@param command originator of the check: now check_user is called
|
|
58 |
during connect and change user procedures; used for
|
|
59 |
logging.
|
|
60 |
@param passwd scrambled password received from client
|
|
61 |
@param passwd_len length of scrambled password
|
|
62 |
@param db database name to connect to, may be NULL
|
|
51.1.50
by Jay Pipes
Removed/replaced DBUG symbols and standardized TRUE/FALSE |
63 |
@param check_count true if establishing a new connection. In this case
|
1
by brian
clean slate |
64 |
check that we have not exceeded the global
|
65 |
max_connections limist
|
|
66 |
||
67 |
@note Host, user and passwd may point to communication buffer.
|
|
68 |
Current implementation does not depend on that, but future changes
|
|
69 |
should be done with this in mind; 'thd' is INOUT, all other params
|
|
70 |
are 'IN'.
|
|
71 |
||
72 |
@retval 0 OK; thd->security_ctx->user/master_access/priv_user/db_access and
|
|
73 |
thd->db are updated; OK is sent to the client.
|
|
74 |
@retval 1 error, e.g. access denied or handshake error, not sent to
|
|
75 |
the client. A message is pushed into the error stack.
|
|
76 |
*/
|
|
77 |
||
78 |
int
|
|
79 |
check_user(THD *thd, enum enum_server_command command, |
|
77.1.45
by Monty Taylor
Warning fixes. |
80 |
const char *passwd __attribute__((__unused__)), |
81 |
uint passwd_len, const char *db, |
|
82 |
bool check_count) |
|
1
by brian
clean slate |
83 |
{
|
84 |
LEX_STRING db_str= { (char *) db, db ? strlen(db) : 0 }; |
|
85 |
||
86 |
/*
|
|
87 |
Clear thd->db as it points to something, that will be freed when
|
|
88 |
connection is closed. We don't want to accidentally free a wrong
|
|
89 |
pointer if connect failed. Also in case of 'CHANGE USER' failure,
|
|
90 |
current database will be switched to 'no database selected'.
|
|
91 |
*/
|
|
92 |
thd->reset_db(NULL, 0); |
|
93 |
||
94 |
my_bool opt_secure_auth_local; |
|
95 |
pthread_mutex_lock(&LOCK_global_system_variables); |
|
96 |
opt_secure_auth_local= opt_secure_auth; |
|
97 |
pthread_mutex_unlock(&LOCK_global_system_variables); |
|
98 |
||
99 |
/*
|
|
100 |
If the server is running in secure auth mode, short scrambles are
|
|
101 |
forbidden.
|
|
102 |
*/
|
|
103 |
if (opt_secure_auth_local && passwd_len == SCRAMBLE_LENGTH_323) |
|
104 |
{
|
|
105 |
my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0)); |
|
106 |
general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE)); |
|
51.1.50
by Jay Pipes
Removed/replaced DBUG symbols and standardized TRUE/FALSE |
107 |
return(1); |
1
by brian
clean slate |
108 |
}
|
109 |
if (passwd_len != 0 && |
|
110 |
passwd_len != SCRAMBLE_LENGTH && |
|
111 |
passwd_len != SCRAMBLE_LENGTH_323) |
|
112 |
{
|
|
113 |
my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip); |
|
51.1.50
by Jay Pipes
Removed/replaced DBUG symbols and standardized TRUE/FALSE |
114 |
return(1); |
1
by brian
clean slate |
115 |
}
|
116 |
||
117 |
USER_RESOURCES ur; |
|
118 |
thd->security_ctx->skip_grants(); |
|
119 |
memset(&ur, 0, sizeof(USER_RESOURCES)); |
|
120 |
||
121 |
if (check_count) |
|
122 |
{
|
|
123 |
pthread_mutex_lock(&LOCK_connection_count); |
|
124 |
bool count_ok= connection_count <= max_connections; |
|
125 |
VOID(pthread_mutex_unlock(&LOCK_connection_count)); |
|
126 |
||
127 |
if (!count_ok) |
|
128 |
{ // too many connections |
|
129 |
my_error(ER_CON_COUNT_ERROR, MYF(0)); |
|
51.1.50
by Jay Pipes
Removed/replaced DBUG symbols and standardized TRUE/FALSE |
130 |
return(1); |
1
by brian
clean slate |
131 |
}
|
132 |
}
|
|
133 |
||
134 |
/*
|
|
135 |
Log the command before authentication checks, so that the user can
|
|
136 |
check the log for the tried login tried and also to detect
|
|
137 |
break-in attempts.
|
|
138 |
*/
|
|
139 |
general_log_print(thd, command, |
|
140 |
(thd->main_security_ctx.priv_user == |
|
141 |
thd->main_security_ctx.user ? |
|
142 |
(char*) "%s@%s on %s" : |
|
143 |
(char*) "%s@%s as anonymous on %s"), |
|
144 |
thd->main_security_ctx.user, |
|
145 |
thd->main_security_ctx.host_or_ip, |
|
146 |
db ? db : (char*) ""); |
|
147 |
||
148 |
/*
|
|
149 |
This is the default access rights for the current database. It's
|
|
150 |
set to 0 here because we don't have an active database yet (and we
|
|
151 |
may not have an active database to set.
|
|
152 |
*/
|
|
153 |
thd->main_security_ctx.db_access=0; |
|
154 |
||
155 |
/* Change database if necessary */
|
|
156 |
if (db && db[0]) |
|
157 |
{
|
|
51.1.50
by Jay Pipes
Removed/replaced DBUG symbols and standardized TRUE/FALSE |
158 |
if (mysql_change_db(thd, &db_str, false)) |
1
by brian
clean slate |
159 |
{
|
160 |
/* mysql_change_db() has pushed the error message. */
|
|
51.1.50
by Jay Pipes
Removed/replaced DBUG symbols and standardized TRUE/FALSE |
161 |
return(1); |
1
by brian
clean slate |
162 |
}
|
163 |
}
|
|
164 |
my_ok(thd); |
|
165 |
thd->password= test(passwd_len); // remember for error messages |
|
166 |
/* Ready to handle queries */
|
|
51.1.50
by Jay Pipes
Removed/replaced DBUG symbols and standardized TRUE/FALSE |
167 |
return(0); |
1
by brian
clean slate |
168 |
}
|
169 |
||
170 |
||
171 |
/*
|
|
172 |
Check for maximum allowable user connections, if the mysqld server is
|
|
173 |
started with corresponding variable that is greater then 0.
|
|
174 |
*/
|
|
175 |
||
176 |
extern "C" uchar *get_key_conn(user_conn *buff, size_t *length, |
|
177 |
my_bool not_used __attribute__((unused))) |
|
178 |
{
|
|
179 |
*length= buff->len; |
|
180 |
return (uchar*) buff->user; |
|
181 |
}
|
|
182 |
||
183 |
||
184 |
extern "C" void free_user(struct user_conn *uc) |
|
185 |
{
|
|
186 |
my_free((char*) uc,MYF(0)); |
|
187 |
}
|
|
188 |
||
189 |
void thd_init_client_charset(THD *thd, uint cs_number) |
|
190 |
{
|
|
191 |
/*
|
|
192 |
Use server character set and collation if
|
|
193 |
- opt_character_set_client_handshake is not set
|
|
194 |
- client has not specified a character set
|
|
195 |
- client character set is the same as the servers
|
|
196 |
- client character set doesn't exists in server
|
|
197 |
*/
|
|
198 |
if (!opt_character_set_client_handshake || |
|
199 |
!(thd->variables.character_set_client= get_charset(cs_number, MYF(0))) || |
|
200 |
!my_strcasecmp(&my_charset_latin1, |
|
201 |
global_system_variables.character_set_client->name, |
|
202 |
thd->variables.character_set_client->name)) |
|
203 |
{
|
|
204 |
thd->variables.character_set_client= |
|
205 |
global_system_variables.character_set_client; |
|
206 |
thd->variables.collation_connection= |
|
207 |
global_system_variables.collation_connection; |
|
208 |
thd->variables.character_set_results= |
|
209 |
global_system_variables.character_set_results; |
|
210 |
}
|
|
211 |
else
|
|
212 |
{
|
|
213 |
thd->variables.character_set_results= |
|
214 |
thd->variables.collation_connection= |
|
215 |
thd->variables.character_set_client; |
|
216 |
}
|
|
217 |
}
|
|
218 |
||
219 |
||
220 |
/*
|
|
221 |
Initialize connection threads
|
|
222 |
*/
|
|
223 |
||
224 |
bool init_new_connection_handler_thread() |
|
225 |
{
|
|
226 |
pthread_detach_this_thread(); |
|
227 |
/* Win32 calls this in pthread_create */
|
|
228 |
if (my_thread_init()) |
|
229 |
return 1; |
|
230 |
return 0; |
|
231 |
}
|
|
232 |
||
233 |
/*
|
|
234 |
Perform handshake, authorize client and update thd ACL variables.
|
|
235 |
||
236 |
SYNOPSIS
|
|
237 |
check_connection()
|
|
238 |
thd thread handle
|
|
239 |
||
240 |
RETURN
|
|
241 |
0 success, OK is sent to user, thd is updated.
|
|
242 |
-1 error, which is sent to user
|
|
243 |
> 0 error code (not sent to user)
|
|
244 |
*/
|
|
245 |
||
246 |
static int check_connection(THD *thd) |
|
247 |
{
|
|
248 |
NET *net= &thd->net; |
|
249 |
ulong pkt_len= 0; |
|
250 |
char *end; |
|
251 |
||
252 |
#ifdef SIGNAL_WITH_VIO_CLOSE
|
|
253 |
thd->set_active_vio(net->vio); |
|
254 |
#endif
|
|
255 |
||
256 |
if (!thd->main_security_ctx.host) // If TCP/IP connection |
|
257 |
{
|
|
258 |
char ip[NI_MAXHOST]; |
|
259 |
||
260 |
if (vio_peer_addr(net->vio, ip, &thd->peer_port, NI_MAXHOST)) |
|
261 |
{
|
|
262 |
my_error(ER_BAD_HOST_ERROR, MYF(0), thd->main_security_ctx.host_or_ip); |
|
263 |
return 1; |
|
264 |
}
|
|
265 |
if (!(thd->main_security_ctx.ip= my_strdup(ip,MYF(MY_WME)))) |
|
266 |
return 1; /* The error is set by my_strdup(). */ |
|
267 |
thd->main_security_ctx.host_or_ip= thd->main_security_ctx.ip; |
|
268 |
thd->main_security_ctx.host= ip_to_hostname(&net->vio->remote, |
|
269 |
net->vio->addrLen); |
|
270 |
thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host; |
|
271 |
}
|
|
272 |
else /* Hostname given means that the connection was on a socket */ |
|
273 |
{
|
|
274 |
thd->main_security_ctx.host_or_ip= thd->main_security_ctx.host; |
|
275 |
thd->main_security_ctx.ip= 0; |
|
276 |
/* Reset sin_addr */
|
|
277 |
bzero((char*) &net->vio->remote, sizeof(net->vio->remote)); |
|
278 |
}
|
|
51.1.50
by Jay Pipes
Removed/replaced DBUG symbols and standardized TRUE/FALSE |
279 |
vio_keepalive(net->vio, true); |
1
by brian
clean slate |
280 |
|
281 |
ulong server_capabilites; |
|
282 |
{
|
|
283 |
/* buff[] needs to big enough to hold the server_version variable */
|
|
284 |
char buff[SERVER_VERSION_LENGTH + SCRAMBLE_LENGTH + 64]; |
|
285 |
server_capabilites= CLIENT_BASIC_FLAGS; |
|
286 |
||
287 |
if (opt_using_transactions) |
|
288 |
server_capabilites|= CLIENT_TRANSACTIONS; |
|
289 |
#ifdef HAVE_COMPRESS
|
|
290 |
server_capabilites|= CLIENT_COMPRESS; |
|
291 |
#endif /* HAVE_COMPRESS */ |
|
292 |
||
293 |
end= strnmov(buff, server_version, SERVER_VERSION_LENGTH) + 1; |
|
294 |
int4store((uchar*) end, thd->thread_id); |
|
295 |
end+= 4; |
|
296 |
/*
|
|
297 |
So as check_connection is the only entry point to authorization
|
|
298 |
procedure, scramble is set here. This gives us new scramble for
|
|
299 |
each handshake.
|
|
300 |
*/
|
|
301 |
create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand); |
|
302 |
/*
|
|
303 |
Old clients does not understand long scrambles, but can ignore packet
|
|
304 |
tail: that's why first part of the scramble is placed here, and second
|
|
305 |
part at the end of packet.
|
|
306 |
*/
|
|
307 |
end= strmake(end, thd->scramble, SCRAMBLE_LENGTH_323) + 1; |
|
308 |
||
309 |
int2store(end, server_capabilites); |
|
310 |
/* write server characteristics: up to 16 bytes allowed */
|
|
311 |
end[2]=(char) default_charset_info->number; |
|
312 |
int2store(end+3, thd->server_status); |
|
313 |
bzero(end+5, 13); |
|
314 |
end+= 18; |
|
315 |
/* write scramble tail */
|
|
316 |
end= strmake(end, thd->scramble + SCRAMBLE_LENGTH_323, |
|
317 |
SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323) + 1; |
|
318 |
||
319 |
/* At this point we write connection message and read reply */
|
|
320 |
if (net_write_command(net, (uchar) protocol_version, (uchar*) "", 0, |
|
321 |
(uchar*) buff, (size_t) (end-buff)) || |
|
322 |
(pkt_len= my_net_read(net)) == packet_error || |
|
323 |
pkt_len < MIN_HANDSHAKE_SIZE) |
|
324 |
{
|
|
325 |
my_error(ER_HANDSHAKE_ERROR, MYF(0), |
|
326 |
thd->main_security_ctx.host_or_ip); |
|
327 |
return 1; |
|
328 |
}
|
|
329 |
}
|
|
330 |
#ifdef _CUSTOMCONFIG_
|
|
331 |
#include "_cust_sql_parse.h" |
|
332 |
#endif
|
|
333 |
if (thd->packet.alloc(thd->variables.net_buffer_length)) |
|
334 |
return 1; /* The error is set by alloc(). */ |
|
335 |
||
336 |
thd->client_capabilities= uint2korr(net->read_pos); |
|
337 |
if (thd->client_capabilities & CLIENT_PROTOCOL_41) |
|
338 |
{
|
|
339 |
thd->client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16; |
|
340 |
thd->max_client_packet_length= uint4korr(net->read_pos+4); |
|
341 |
thd_init_client_charset(thd, (uint) net->read_pos[8]); |
|
342 |
thd->update_charset(); |
|
343 |
end= (char*) net->read_pos+32; |
|
344 |
}
|
|
345 |
else
|
|
346 |
{
|
|
347 |
thd->max_client_packet_length= uint3korr(net->read_pos+2); |
|
348 |
end= (char*) net->read_pos+5; |
|
349 |
}
|
|
350 |
/*
|
|
351 |
Disable those bits which are not supported by the server.
|
|
352 |
This is a precautionary measure, if the client lies. See Bug#27944.
|
|
353 |
*/
|
|
354 |
thd->client_capabilities&= server_capabilites; |
|
355 |
||
356 |
if (thd->client_capabilities & CLIENT_IGNORE_SPACE) |
|
357 |
thd->variables.sql_mode|= MODE_IGNORE_SPACE; |
|
358 |
||
359 |
if (end >= (char*) net->read_pos+ pkt_len +2) |
|
360 |
{
|
|
361 |
||
362 |
my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip); |
|
363 |
return 1; |
|
364 |
}
|
|
365 |
||
366 |
if (thd->client_capabilities & CLIENT_INTERACTIVE) |
|
367 |
thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout; |
|
368 |
if ((thd->client_capabilities & CLIENT_TRANSACTIONS) && |
|
369 |
opt_using_transactions) |
|
370 |
net->return_status= &thd->server_status; |
|
371 |
||
372 |
char *user= end; |
|
373 |
char *passwd= strend(user)+1; |
|
374 |
uint user_len= passwd - user - 1; |
|
375 |
char *db= passwd; |
|
376 |
char db_buff[NAME_LEN + 1]; // buffer to store db in utf8 |
|
377 |
char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8 |
|
378 |
uint dummy_errors; |
|
379 |
||
380 |
/*
|
|
381 |
Old clients send null-terminated string as password; new clients send
|
|
382 |
the size (1 byte) + string (not null-terminated). Hence in case of empty
|
|
383 |
password both send '\0'.
|
|
384 |
||
385 |
This strlen() can't be easily deleted without changing protocol.
|
|
386 |
||
387 |
Cast *passwd to an unsigned char, so that it doesn't extend the sign for
|
|
388 |
*passwd > 127 and become 2**32-127+ after casting to uint.
|
|
389 |
*/
|
|
390 |
uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ? |
|
391 |
(uchar)(*passwd++) : strlen(passwd); |
|
392 |
db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ? |
|
393 |
db + passwd_len + 1 : 0; |
|
394 |
/* strlen() can't be easily deleted without changing protocol */
|
|
395 |
uint db_len= db ? strlen(db) : 0; |
|
396 |
||
397 |
if (passwd + passwd_len + db_len > (char *)net->read_pos + pkt_len) |
|
398 |
{
|
|
399 |
my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip); |
|
400 |
return 1; |
|
401 |
}
|
|
402 |
||
403 |
/* Since 4.1 all database names are stored in utf8 */
|
|
404 |
if (db) |
|
405 |
{
|
|
406 |
db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1, |
|
407 |
system_charset_info, |
|
408 |
db, db_len, |
|
409 |
thd->charset(), &dummy_errors)]= 0; |
|
410 |
db= db_buff; |
|
411 |
}
|
|
412 |
||
413 |
user_buff[user_len= copy_and_convert(user_buff, sizeof(user_buff)-1, |
|
414 |
system_charset_info, user, user_len, |
|
415 |
thd->charset(), &dummy_errors)]= '\0'; |
|
416 |
user= user_buff; |
|
417 |
||
418 |
/* If username starts and ends in "'", chop them off */
|
|
419 |
if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'') |
|
420 |
{
|
|
421 |
user[user_len-1]= 0; |
|
422 |
user++; |
|
423 |
user_len-= 2; |
|
424 |
}
|
|
425 |
||
426 |
if (thd->main_security_ctx.user) |
|
427 |
x_free(thd->main_security_ctx.user); |
|
428 |
if (!(thd->main_security_ctx.user= my_strdup(user, MYF(MY_WME)))) |
|
429 |
return 1; /* The error is set by my_strdup(). */ |
|
51.1.50
by Jay Pipes
Removed/replaced DBUG symbols and standardized TRUE/FALSE |
430 |
return check_user(thd, COM_CONNECT, passwd, passwd_len, db, true); |
1
by brian
clean slate |
431 |
}
|
432 |
||
433 |
||
434 |
/*
|
|
435 |
Setup thread to be used with the current thread
|
|
436 |
||
437 |
SYNOPSIS
|
|
438 |
bool setup_connection_thread_globals()
|
|
439 |
thd Thread/connection handler
|
|
440 |
||
441 |
RETURN
|
|
442 |
0 ok
|
|
443 |
1 Error (out of memory)
|
|
444 |
In this case we will close the connection and increment status
|
|
445 |
*/
|
|
446 |
||
447 |
bool setup_connection_thread_globals(THD *thd) |
|
448 |
{
|
|
449 |
if (thd->store_globals()) |
|
450 |
{
|
|
451 |
close_connection(thd, ER_OUT_OF_RESOURCES, 1); |
|
452 |
statistic_increment(aborted_connects,&LOCK_status); |
|
453 |
thread_scheduler.end_thread(thd, 0); |
|
454 |
return 1; // Error |
|
455 |
}
|
|
456 |
return 0; |
|
457 |
}
|
|
458 |
||
459 |
||
460 |
/*
|
|
461 |
Autenticate user, with error reporting
|
|
462 |
||
463 |
SYNOPSIS
|
|
464 |
login_connection()
|
|
465 |
thd Thread handler
|
|
466 |
||
467 |
NOTES
|
|
468 |
Connection is not closed in case of errors
|
|
469 |
||
470 |
RETURN
|
|
471 |
0 ok
|
|
472 |
1 error
|
|
473 |
*/
|
|
474 |
||
475 |
||
476 |
bool login_connection(THD *thd) |
|
477 |
{
|
|
478 |
NET *net= &thd->net; |
|
479 |
int error; |
|
480 |
||
481 |
/* Use "connect_timeout" value during connection phase */
|
|
482 |
my_net_set_read_timeout(net, connect_timeout); |
|
483 |
my_net_set_write_timeout(net, connect_timeout); |
|
484 |
||
485 |
lex_start(thd); |
|
486 |
||
487 |
error= check_connection(thd); |
|
488 |
net_end_statement(thd); |
|
489 |
||
490 |
if (error) |
|
491 |
{ // Wrong permissions |
|
492 |
statistic_increment(aborted_connects,&LOCK_status); |
|
51.1.50
by Jay Pipes
Removed/replaced DBUG symbols and standardized TRUE/FALSE |
493 |
return(1); |
1
by brian
clean slate |
494 |
}
|
495 |
/* Connect completed, set read/write timeouts back to default */
|
|
496 |
my_net_set_read_timeout(net, thd->variables.net_read_timeout); |
|
497 |
my_net_set_write_timeout(net, thd->variables.net_write_timeout); |
|
51.1.50
by Jay Pipes
Removed/replaced DBUG symbols and standardized TRUE/FALSE |
498 |
return(0); |
1
by brian
clean slate |
499 |
}
|
500 |
||
501 |
||
502 |
/*
|
|
503 |
Close an established connection
|
|
504 |
||
505 |
NOTES
|
|
506 |
This mainly updates status variables
|
|
507 |
*/
|
|
508 |
||
509 |
void end_connection(THD *thd) |
|
510 |
{
|
|
511 |
NET *net= &thd->net; |
|
512 |
plugin_thdvar_cleanup(thd); |
|
513 |
||
514 |
if (thd->killed || (net->error && net->vio != 0)) |
|
515 |
{
|
|
516 |
statistic_increment(aborted_threads,&LOCK_status); |
|
517 |
}
|
|
518 |
||
519 |
if (net->error && net->vio != 0) |
|
520 |
{
|
|
521 |
if (!thd->killed && thd->variables.log_warnings > 1) |
|
522 |
{
|
|
523 |
Security_context *sctx= thd->security_ctx; |
|
524 |
||
525 |
sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION), |
|
526 |
thd->thread_id,(thd->db ? thd->db : "unconnected"), |
|
527 |
sctx->user ? sctx->user : "unauthenticated", |
|
528 |
sctx->host_or_ip, |
|
529 |
(thd->main_da.is_error() ? thd->main_da.message() : |
|
530 |
ER(ER_UNKNOWN_ERROR))); |
|
531 |
}
|
|
532 |
}
|
|
533 |
}
|
|
534 |
||
535 |
||
536 |
/*
|
|
537 |
Initialize THD to handle queries
|
|
538 |
*/
|
|
539 |
||
540 |
void prepare_new_connection_state(THD* thd) |
|
541 |
{
|
|
542 |
Security_context *sctx= thd->security_ctx; |
|
543 |
||
544 |
if (thd->variables.max_join_size == HA_POS_ERROR) |
|
545 |
thd->options |= OPTION_BIG_SELECTS; |
|
546 |
if (thd->client_capabilities & CLIENT_COMPRESS) |
|
547 |
thd->net.compress=1; // Use compression |
|
548 |
||
549 |
/*
|
|
550 |
Much of this is duplicated in create_embedded_thd() for the
|
|
551 |
embedded server library.
|
|
552 |
TODO: refactor this to avoid code duplication there
|
|
553 |
*/
|
|
554 |
thd->version= refresh_version; |
|
555 |
thd->proc_info= 0; |
|
556 |
thd->command= COM_SLEEP; |
|
557 |
thd->set_time(); |
|
558 |
thd->init_for_queries(); |
|
559 |
||
560 |
/* In the past this would only run of the user did not have SUPER_ACL */
|
|
561 |
if (sys_init_connect.value_length) |
|
562 |
{
|
|
563 |
execute_init_command(thd, &sys_init_connect, &LOCK_sys_init_connect); |
|
564 |
if (thd->is_error()) |
|
565 |
{
|
|
566 |
thd->killed= THD::KILL_CONNECTION; |
|
567 |
sql_print_warning(ER(ER_NEW_ABORTING_CONNECTION), |
|
568 |
thd->thread_id,(thd->db ? thd->db : "unconnected"), |
|
569 |
sctx->user ? sctx->user : "unauthenticated", |
|
570 |
sctx->host_or_ip, "init_connect command failed"); |
|
571 |
sql_print_warning("%s", thd->main_da.message()); |
|
572 |
}
|
|
573 |
thd->proc_info=0; |
|
574 |
thd->set_time(); |
|
575 |
thd->init_for_queries(); |
|
576 |
}
|
|
577 |
}
|
|
578 |
||
579 |
||
580 |
/*
|
|
581 |
Thread handler for a connection
|
|
582 |
||
583 |
SYNOPSIS
|
|
584 |
handle_one_connection()
|
|
585 |
arg Connection object (THD)
|
|
586 |
||
587 |
IMPLEMENTATION
|
|
588 |
This function (normally) does the following:
|
|
589 |
- Initialize thread
|
|
590 |
- Initialize THD to be used with this thread
|
|
591 |
- Authenticate user
|
|
592 |
- Execute all queries sent on the connection
|
|
593 |
- Take connection down
|
|
594 |
- End thread / Handle next connection using thread from thread cache
|
|
595 |
*/
|
|
596 |
||
597 |
pthread_handler_t handle_one_connection(void *arg) |
|
598 |
{
|
|
599 |
THD *thd= (THD*) arg; |
|
600 |
ulong launch_time= (ulong) ((thd->thr_create_utime= my_micro_time()) - |
|
601 |
thd->connect_utime); |
|
602 |
||
603 |
if (thread_scheduler.init_new_connection_thread()) |
|
604 |
{
|
|
605 |
close_connection(thd, ER_OUT_OF_RESOURCES, 1); |
|
606 |
statistic_increment(aborted_connects,&LOCK_status); |
|
607 |
thread_scheduler.end_thread(thd,0); |
|
608 |
return 0; |
|
609 |
}
|
|
610 |
if (launch_time >= slow_launch_time*1000000L) |
|
611 |
statistic_increment(slow_launch_threads,&LOCK_status); |
|
612 |
||
613 |
/*
|
|
614 |
handle_one_connection() is normally the only way a thread would
|
|
615 |
start and would always be on the very high end of the stack ,
|
|
616 |
therefore, the thread stack always starts at the address of the
|
|
617 |
first local variable of handle_one_connection, which is thd. We
|
|
618 |
need to know the start of the stack so that we could check for
|
|
619 |
stack overruns.
|
|
620 |
*/
|
|
621 |
thd->thread_stack= (char*) &thd; |
|
622 |
if (setup_connection_thread_globals(thd)) |
|
623 |
return 0; |
|
624 |
||
625 |
for (;;) |
|
626 |
{
|
|
627 |
NET *net= &thd->net; |
|
628 |
||
629 |
if (login_connection(thd)) |
|
630 |
goto end_thread; |
|
631 |
||
632 |
prepare_new_connection_state(thd); |
|
633 |
||
634 |
while (!net->error && net->vio != 0 && |
|
635 |
!(thd->killed == THD::KILL_CONNECTION)) |
|
636 |
{
|
|
637 |
if (do_command(thd)) |
|
638 |
break; |
|
639 |
}
|
|
640 |
end_connection(thd); |
|
641 |
||
642 |
end_thread: |
|
643 |
close_connection(thd, 0, 1); |
|
644 |
if (thread_scheduler.end_thread(thd,1)) |
|
645 |
return 0; // Probably no-threads |
|
646 |
||
647 |
/*
|
|
648 |
If end_thread() returns, we are either running with
|
|
649 |
thread-handler=no-threads or this thread has been schedule to
|
|
650 |
handle the next connection.
|
|
651 |
*/
|
|
652 |
thd= current_thd; |
|
653 |
thd->thread_stack= (char*) &thd; |
|
654 |
}
|
|
655 |
}
|