Commit 70a627a1 authored by monty@donna.mysql.com's avatar monty@donna.mysql.com

Added max_user_connections

parent 96a86081
...@@ -11,7 +11,8 @@ extra_configs="$pentium_configs $debug_configs" ...@@ -11,7 +11,8 @@ extra_configs="$pentium_configs $debug_configs"
# Use the debug version if it exists # Use the debug version if it exists
if test -d /usr/local/BerkeleyDB-dbug/ if test -d /usr/local/BerkeleyDB-dbug/
then then
extra_configs="$extra_configs --with-berkeley-db=/usr/local/BerkeleyDB-dbug/ --with-innobase" extra_configs="$extra_configs --with-berkeley-db=/usr/local/BerkeleyDB-dbug/"
fi fi
extra_configs="$extra_configs --with-innobase"
. "$path/FINISH.sh" . "$path/FINISH.sh"
...@@ -9579,6 +9579,7 @@ uses: ...@@ -9579,6 +9579,7 @@ uses:
@findex command-line options @findex command-line options
@cindex options, command-line @cindex options, command-line
@cindex mysqld options
@node Command-line options, Option files, Automatic start, Post-installation @node Command-line options, Option files, Automatic start, Post-installation
@subsection Command-line Options @subsection Command-line Options
...@@ -11522,6 +11523,11 @@ in the grant tables. In principle, the @code{--secure} option to ...@@ -11522,6 +11523,11 @@ in the grant tables. In principle, the @code{--secure} option to
@code{mysqld} should make hostnames safe. In any case, you should be very @code{mysqld} should make hostnames safe. In any case, you should be very
careful about creating grant table entries using hostname values that careful about creating grant table entries using hostname values that
contain wild cards! contain wild cards!
@item
If you want to restrict the number of connections for a single user, you
can do this by setting the @code{max_user_connections} variable in
@code{mysqld}.
@end itemize @end itemize
@node Privileges options, What Privileges, Security, Privilege system @node Privileges options, What Privileges, Security, Privilege system
...@@ -21048,6 +21054,9 @@ The number of bytes to use when sorting @code{BLOB} or @code{TEXT} ...@@ -21048,6 +21054,9 @@ The number of bytes to use when sorting @code{BLOB} or @code{TEXT}
values (only the first @code{max_sort_length} bytes of each value values (only the first @code{max_sort_length} bytes of each value
are used; the rest are ignored). are used; the rest are ignored).
@item @code{max_user_connections}
The maximum number of active connections for a single user (0 = no limit).
@item @code{max_tmp_tables} @item @code{max_tmp_tables}
(This option doesn't yet do anything.) (This option doesn't yet do anything.)
Maximum number of temporary tables a client can keep open at the same time. Maximum number of temporary tables a client can keep open at the same time.
...@@ -41585,6 +41594,8 @@ not yet 100 % confident in this code. ...@@ -41585,6 +41594,8 @@ not yet 100 % confident in this code.
@appendixsubsec Changes in release 3.23.34 @appendixsubsec Changes in release 3.23.34
@itemize @bullet @itemize @bullet
@item @item
Added option @code{max_user_connections} to @code{mysqld}.
@item
Limit query length for replication by max_allowed_packet, not the arbitrary Limit query length for replication by max_allowed_packet, not the arbitrary
limit of 4 MB limit of 4 MB
@item @item
...@@ -19,14 +19,14 @@ Created 9/6/1995 Heikki Tuuri ...@@ -19,14 +19,14 @@ Created 9/6/1995 Heikki Tuuri
#include "ut0mem.h" #include "ut0mem.h"
/* Type definition for an operating system mutex struct */ /* Type definition for an operating system mutex struct */
struct os_mutex_struct{ struct os_mutex_struct{
void* handle; /* OS handle to mutex */ void* handle; /* OS handle to mutex */
ulint count; /* we use this counter to check ulint count; /* we use this counter to check
that the same thread does not that the same thread does not
recursively lock the mutex: we recursively lock the mutex: we
do not assume that the OS mutex do not assume that the OS mutex
supports recursive locking, though supports recursive locking, though
NT seems to do that */ NT seems to do that */
}; };
/************************************************************* /*************************************************************
...@@ -44,7 +44,7 @@ os_event_create( ...@@ -44,7 +44,7 @@ os_event_create(
{ {
#ifdef __WIN__ #ifdef __WIN__
HANDLE event; HANDLE event;
event = CreateEvent(NULL, /* No security attributes */ event = CreateEvent(NULL, /* No security attributes */
TRUE, /* Manual reset */ TRUE, /* Manual reset */
FALSE, /* Initial state nonsignaled */ FALSE, /* Initial state nonsignaled */
...@@ -60,9 +60,9 @@ os_event_create( ...@@ -60,9 +60,9 @@ os_event_create(
event = ut_malloc(sizeof(struct os_event_struct)); event = ut_malloc(sizeof(struct os_event_struct));
os_fast_mutex_init(&(event->os_mutex)); os_fast_mutex_init(&(event->os_mutex));
os_fast_mutex_init(&(event->wait_mutex)); pthread_cond_init(&(event->cond_var), NULL);
event->is_set = TRUE; event->is_set = FALSE;
return(event); return(event);
#endif #endif
...@@ -108,7 +108,7 @@ os_event_set( ...@@ -108,7 +108,7 @@ os_event_set(
/*=========*/ /*=========*/
os_event_t event) /* in: event to set */ os_event_t event) /* in: event to set */
{ {
#ifdef __WIN__ #ifdef __WIN__
ut_a(event); ut_a(event);
ut_a(SetEvent(event)); ut_a(SetEvent(event));
#else #else
...@@ -119,12 +119,12 @@ os_event_set( ...@@ -119,12 +119,12 @@ os_event_set(
if (event->is_set) { if (event->is_set) {
/* Do nothing */ /* Do nothing */
} else { } else {
os_fast_mutex_unlock(&(event->wait_mutex));
event->is_set = TRUE; event->is_set = TRUE;
pthread_cond_broadcast(&(event->cond_var));
} }
os_fast_mutex_unlock(&(event->os_mutex)); os_fast_mutex_unlock(&(event->os_mutex));
#endif #endif
} }
/************************************************************** /**************************************************************
...@@ -148,7 +148,6 @@ os_event_reset( ...@@ -148,7 +148,6 @@ os_event_reset(
if (!event->is_set) { if (!event->is_set) {
/* Do nothing */ /* Do nothing */
} else { } else {
os_fast_mutex_lock(&(event->wait_mutex));
event->is_set = FALSE; event->is_set = FALSE;
} }
...@@ -163,7 +162,7 @@ void ...@@ -163,7 +162,7 @@ void
os_event_free( os_event_free(
/*==========*/ /*==========*/
os_event_t event) /* in: event to free */ os_event_t event) /* in: event to free */
{ {
#ifdef __WIN__ #ifdef __WIN__
ut_a(event); ut_a(event);
...@@ -173,7 +172,7 @@ os_event_free( ...@@ -173,7 +172,7 @@ os_event_free(
ut_a(event); ut_a(event);
os_fast_mutex_free(&(event->os_mutex)); os_fast_mutex_free(&(event->os_mutex));
os_fast_mutex_free(&(event->wait_mutex)); pthread_cond_destroy(&(event->cond_var));
ut_free(event); ut_free(event);
#endif #endif
...@@ -197,8 +196,22 @@ os_event_wait( ...@@ -197,8 +196,22 @@ os_event_wait(
ut_a(err == WAIT_OBJECT_0); ut_a(err == WAIT_OBJECT_0);
#else #else
os_fast_mutex_lock(&(event->wait_mutex)); os_fast_mutex_lock(&(event->os_mutex));
os_fast_mutex_unlock(&(event->wait_mutex)); loop:
if (event->is_set == TRUE) {
os_fast_mutex_unlock(&(event->os_mutex));
/* Ok, we may return */
return;
}
pthread_cond_wait(&(event->cond_var), &(event->os_mutex));
/* Solaris manual said that spurious wakeups may occur: we have
to check the 'is_set' variable again */
goto loop;
#endif #endif
} }
...@@ -225,7 +238,7 @@ os_event_wait_time( ...@@ -225,7 +238,7 @@ os_event_wait_time(
} else { } else {
err = WaitForSingleObject(event, INFINITE); err = WaitForSingleObject(event, INFINITE);
} }
if (err == WAIT_OBJECT_0) { if (err == WAIT_OBJECT_0) {
return(0); return(0);
...@@ -237,7 +250,7 @@ os_event_wait_time( ...@@ -237,7 +250,7 @@ os_event_wait_time(
} }
#else #else
UT_NOT_USED(time); UT_NOT_USED(time);
/* In Posix this is just an ordinary, infinite wait */ /* In Posix this is just an ordinary, infinite wait */
os_event_wait(event); os_event_wait(event);
...@@ -277,7 +290,7 @@ os_event_wait_multiple( ...@@ -277,7 +290,7 @@ os_event_wait_multiple(
return(index - WAIT_OBJECT_0); return(index - WAIT_OBJECT_0);
#else #else
ut_a(n == 0); ut_a(n == 0);
/* In Posix we can only wait for a single event */ /* In Posix we can only wait for a single event */
os_event_wait(*event_array); os_event_wait(*event_array);
...@@ -318,7 +331,7 @@ os_mutex_create( ...@@ -318,7 +331,7 @@ os_mutex_create(
os_mutex_t mutex_str; os_mutex_t mutex_str;
UT_NOT_USED(name); UT_NOT_USED(name);
os_mutex = ut_malloc(sizeof(os_fast_mutex_t)); os_mutex = ut_malloc(sizeof(os_fast_mutex_t));
os_fast_mutex_init(os_mutex); os_fast_mutex_init(os_mutex);
...@@ -329,7 +342,7 @@ os_mutex_create( ...@@ -329,7 +342,7 @@ os_mutex_create(
mutex_str->count = 0; mutex_str->count = 0;
return(mutex_str); return(mutex_str);
#endif #endif
} }
/************************************************************** /**************************************************************
...@@ -385,7 +398,7 @@ os_mutex_exit( ...@@ -385,7 +398,7 @@ os_mutex_exit(
(mutex->count)--; (mutex->count)--;
os_fast_mutex_unlock(mutex->handle); os_fast_mutex_unlock(mutex->handle);
#endif #endif
} }
/************************************************************** /**************************************************************
...@@ -419,7 +432,7 @@ os_fast_mutex_init( ...@@ -419,7 +432,7 @@ os_fast_mutex_init(
{ {
#ifdef __WIN__ #ifdef __WIN__
ut_a(fast_mutex); ut_a(fast_mutex);
InitializeCriticalSection((LPCRITICAL_SECTION) fast_mutex); InitializeCriticalSection((LPCRITICAL_SECTION) fast_mutex);
#else #else
pthread_mutex_init(fast_mutex, NULL); pthread_mutex_init(fast_mutex, NULL);
......
...@@ -521,7 +521,7 @@ extern uint protocol_version,dropping_tables; ...@@ -521,7 +521,7 @@ extern uint protocol_version,dropping_tables;
extern ulong keybuff_size,sortbuff_size,max_item_sort_length,table_cache_size, extern ulong keybuff_size,sortbuff_size,max_item_sort_length,table_cache_size,
max_join_size,join_buff_size,tmp_table_size, max_join_size,join_buff_size,tmp_table_size,
max_connections,max_connect_errors,long_query_time, max_connections,max_connect_errors,long_query_time,
max_insert_delayed_threads, max_insert_delayed_threads, max_user_connections,
long_query_count,net_wait_timeout,net_interactive_timeout, long_query_count,net_wait_timeout,net_interactive_timeout,
net_read_timeout,net_write_timeout, net_read_timeout,net_write_timeout,
what_to_log,flush_time, what_to_log,flush_time,
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <thr_alarm.h> #include <thr_alarm.h>
#include <myisam.h> #include <myisam.h>
#include <my_dir.h> #include <my_dir.h>
#include <assert.h>
#define SCRAMBLE_LENGTH 8 #define SCRAMBLE_LENGTH 8
...@@ -32,6 +33,9 @@ extern "C" pthread_mutex_t THR_LOCK_keycache; ...@@ -32,6 +33,9 @@ extern "C" pthread_mutex_t THR_LOCK_keycache;
extern "C" int gethostname(char *name, int namelen); extern "C" int gethostname(char *name, int namelen);
#endif #endif
static int check_for_max_user_connections(const char *user, int u_length,
const char *host);
static void decrease_user_connections(const char *user, const char *host);
static bool check_table_access(THD *thd,uint want_access, TABLE_LIST *tables); static bool check_table_access(THD *thd,uint want_access, TABLE_LIST *tables);
static bool check_db_used(THD *thd,TABLE_LIST *tables); static bool check_db_used(THD *thd,TABLE_LIST *tables);
static bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *tables); static bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *tables);
...@@ -145,13 +149,134 @@ static bool check_user(THD *thd,enum_server_command command, const char *user, ...@@ -145,13 +149,134 @@ static bool check_user(THD *thd,enum_server_command command, const char *user,
thd->host ? thd->host : thd->ip ? thd->ip : "unknown ip", thd->host ? thd->host : thd->ip ? thd->ip : "unknown ip",
db ? db : (char*) ""); db ? db : (char*) "");
thd->db_access=0; thd->db_access=0;
if (max_user_connections &&
check_for_max_user_connections(user, strlen(user), thd->host))
return -1;
if (db && db[0]) if (db && db[0])
return test(mysql_change_db(thd,db)); {
bool error=test(mysql_change_db(thd,db));
if (error)
decrease_user_connections(user,thd->host);
return error;
}
else else
send_ok(net); // Ready to handle questions send_ok(net); // Ready to handle questions
return 0; // ok return 0; // ok
} }
/*
** check for maximum allowable user connections
** if mysql server is started with corresponding
** variable that is greater then 0
*/
static HASH hash_user_connections;
static DYNAMIC_ARRAY user_conn_array;
extern pthread_mutex_t LOCK_user_conn;
struct user_conn {
char user[USERNAME_LENGTH+HOSTNAME_LENGTH+2];
int connections, len;
};
static byte* get_key_conn(user_conn *buff, uint *length,
my_bool not_used __attribute__((unused)))
{
*length=buff->len;
return (byte*) buff->user;
}
#define DEF_USER_COUNT 50
void init_max_user_conn(void)
{
(void) hash_init(&hash_user_connections,DEF_USER_COUNT,0,0,
(hash_get_key) get_key_conn,0, 0);
(void) init_dynamic_array(&user_conn_array,sizeof(user_conn),
DEF_USER_COUNT, DEF_USER_COUNT);
}
static int check_for_max_user_connections(const char *user, int u_length,
const char *host)
{
uint temp_len;
char temp_user[USERNAME_LENGTH+HOSTNAME_LENGTH+2];
struct user_conn *uc;
if (!user)
user="";
if (!host)
host="";
temp_len= (uint) (strxnmov(temp_user, sizeof(temp_user), user, "@", host,
NullS) - temp_user);
(void) pthread_mutex_lock(&LOCK_user_conn);
uc = (struct user_conn *) hash_search(&hash_user_connections,
(byte*) temp_user, temp_len);
if (uc) /* user found ; check for no. of connections */
{
if (max_user_connections == uc->connections)
{
net_printf(&(current_thd->net),ER_TOO_MANY_USER_CONNECTIONS, temp_user);
pthread_mutex_unlock(&LOCK_user_conn);
return 1;
}
uc->connections++;
}
else
{
/* the user is not found in the cache; Insert it */
struct user_conn uc;
memcpy(uc.user,temp_user,temp_len+1);
uc.len = temp_len;
uc.connections = 1;
if (!insert_dynamic(&user_conn_array, (char *) &uc))
{
hash_insert(&hash_user_connections,
(byte *) dynamic_array_ptr(&user_conn_array,
user_conn_array.elements - 1));
}
}
(void) pthread_mutex_unlock(&LOCK_user_conn);
return 0;
}
static void decrease_user_connections(const char *user, const char *host)
{
char temp_user[USERNAME_LENGTH+HOSTNAME_LENGTH+2];
int temp_len;
struct user_conn uucc, *uc;
if (!user)
user="";
if (!host)
host="";
temp_len= (uint) (strxnmov(temp_user, sizeof(temp_user), user, "@", host,
NullS) - temp_user);
(void) pthread_mutex_lock(&LOCK_user_conn);
uc = (struct user_conn *) hash_search(&hash_user_connections,
(byte*) temp_user, temp_len);
dbug_assert(uc != 0); // We should always find the user
if (!uc)
goto end; // Safety; Something went wrong
if (! --uc->connections)
{
/* Last connection for user; Delete it */
(void) hash_delete(&hash_user_connections,(char *) uc);
uint element= ((uint) ((byte*) uc - (byte*) user_conn_array.buffer) /
user_conn_array.size_of_element);
delete_dynamic_element(&user_conn_array,element);
}
end:
(void) pthread_mutex_unlock(&LOCK_user_conn);
}
void free_max_user_conn(void)
{
delete_dynamic(&user_conn_array);
hash_free(&hash_user_connections);
}
/* /*
** check connnetion and get priviliges ** check connnetion and get priviliges
...@@ -417,6 +542,8 @@ pthread_handler_decl(handle_one_connection,arg) ...@@ -417,6 +542,8 @@ pthread_handler_decl(handle_one_connection,arg)
thread_safe_increment(aborted_threads,&LOCK_thread_count); thread_safe_increment(aborted_threads,&LOCK_thread_count);
} }
if (max_user_connections)
decrease_user_connections(thd->user,thd->host);
end_thread: end_thread:
close_connection(net); close_connection(net);
end_thread(thd,1); end_thread(thd,1);
...@@ -512,7 +639,7 @@ int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd) ...@@ -512,7 +639,7 @@ int mysql_table_dump(THD* thd, char* db, char* tbl_name, int fd)
DBUG_ENTER("mysql_table_dump"); DBUG_ENTER("mysql_table_dump");
db = (db && db[0]) ? db : thd->db; db = (db && db[0]) ? db : thd->db;
if (!(table_list = (TABLE_LIST*) sql_calloc(sizeof(TABLE_LIST)))) if (!(table_list = (TABLE_LIST*) sql_calloc(sizeof(TABLE_LIST))))
DBUG_RETURN(1); // out of memory DBUG_RETURN(1); // out of memory
table_list->db = db; table_list->db = db;
table_list->real_name = table_list->name = tbl_name; table_list->real_name = table_list->name = tbl_name;
table_list->lock_type = TL_READ_NO_INSERT; table_list->lock_type = TL_READ_NO_INSERT;
...@@ -641,7 +768,7 @@ bool do_command(THD *thd) ...@@ -641,7 +768,7 @@ bool do_command(THD *thd)
send_error(net, ER_UNKNOWN_COM_ERROR); send_error(net, ER_UNKNOWN_COM_ERROR);
break; break;
} }
if (check_user(thd, COM_CHANGE_USER, user, passwd, db,0)) if (check_user(thd, COM_CHANGE_USER, user, passwd, db, 0))
{ // Restore old user { // Restore old user
x_free(thd->user); x_free(thd->user);
x_free(thd->db); x_free(thd->db);
...@@ -652,6 +779,7 @@ bool do_command(THD *thd) ...@@ -652,6 +779,7 @@ bool do_command(THD *thd)
thd->priv_user=save_priv_user; thd->priv_user=save_priv_user;
break; break;
} }
decrease_user_connections (save_user, thd->host);
x_free((gptr) save_db); x_free((gptr) save_db);
x_free((gptr) save_user); x_free((gptr) save_user);
thd->password=test(passwd[0]); thd->password=test(passwd[0]);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment