diff --git a/mysql-test/include/admin_connection.inc b/mysql-test/include/admin_connection.inc new file mode 100644 index 0000000..c1f50dc --- /dev/null +++ b/mysql-test/include/admin_connection.inc @@ -0,0 +1,161 @@ +# This test is to check various cases of connections to the admin tcpip port + +# Wait for all the connections from previous tests except +# the default one to be completedly disconnected +let $count_sessions= 1; +--source include/wait_until_count_sessions.inc + +# Restart the server with admin_port being set to one more than the server's +# port +--let $admin_port=$MASTER_MYPORT +--inc $admin_port +--let $_mysqld_option=--admin_port=$admin_port +--source include/restart_mysqld_with_option.inc + +flush privileges; +--echo ## Create non-super user nosuper +create user nosuper@localhost; +--echo ## Create super user 'super1' +create user super1@localhost; +grant all privileges on *.* to 'super1'@'localhost'; +--echo ## Create super user 'super2' +create user super2@localhost; +grant all privileges on *.* to 'super2'@'localhost'; +flush user_resources; + +select user from mysql.user order by 1 asc; + +--echo ## Start a connection fail_admin_con1 at ADMIN_PORT with new user nosuper +--echo ## This will fail since this user doesn't have SUPER ACL +--replace_result $MASTER_MYSOCK MASTER_SOCKET $ADMIN_PORT ADMIN_PORT +--error ER_SPECIFIC_ACCESS_DENIED_ERROR +connect (fail_admin_con1,$IP,nosuper,,mysql,$admin_port); + +--echo ## Start connections admin_con_root1,2,3 at ADMIN_PORT +--echo ## This will succeed since root has SUPER ACL +let $i=1; +while($i<=3) { + connect (admin_con_root$i,$IP,root,,mysql,$admin_port); + inc $i; +} + +let $i=1; +while($i<=3) { + --echo ## Switch to admin_con_root$i + connection admin_con_root$i; + select user from mysql.user order by 1 asc; + inc $i; +} + +--echo ## Disconnect admin_con_root1,2,3 +let $i=1; +while($i<=3) { + disconnect admin_con_root$i; + inc $i; +} + +--echo ## Switch to default connection +connection default; +--echo ## Wait for all the connections except the default one to be completedly disconnected +let $count_sessions= 1; +--source include/wait_until_count_sessions.inc + +--echo ## Switch to default connection +connection default; +--echo ## Show processlist: only the default connection will be listed +--replace_column 1 - 3 localhost 5 - 6 - 7 - 8 - 9 - 10 - +--sorted_result +show processlist; + +--echo ## Start connections non_admin_con_super1_1,2,3,4 +let $i=1; +while ($i<=4) { + connect (non_admin_con_super1_$i,$IP,super1,,mysql,); + inc $i; +} + +--echo ## Connections non_admin_con_super1_5 will fail due to too many connections for a user +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--error ER_TOO_MANY_USER_CONNECTIONS +connect (fail_non_admin_con_super1,$IP,super1,,mysql,); + +--echo ## Start connections non_admin_con_super2_1,2,3,4 +let $i=1; +while ($i<=4) { + connect (non_admin_con_super2_$i,$IP,super2,,mysql,); + inc $i; +} + +--echo ## Switch to default connection +connection default; +--echo ## Show processlist: totally 9 connections will be listed: the 8 non-admin connections and the default one +--replace_column 1 - 3 localhost 5 - 6 - 7 - 8 - 9 - 10 - +--sorted_result +show processlist; + +--echo ## Start a connection fail_non_admin_con9 at master port +--echo ## This will fail due to the limit of the max number of connections +--replace_result $MASTER_MYSOCK MASTER_SOCKET $MASTER_MYPORT MASTER_PORT +--error ER_CON_COUNT_ERROR +connect (fail_non_admin_con_root,$IP,root,,mysql,); + +--echo ## Start connections admin_con_root4,5,6,7,8 at ADMIN_PORT +--echo ## This will succeed since admin port is handled separately +--echo ## and it is not limited by either @@max-connections or @@max-user-connections +let $i=4; +while ($i<=8) { + connect (admin_con_root$i,$IP,root,,mysql,$admin_port); + inc $i; +} + +--echo ## Switch to default connection +connection default; +--echo ## Show processlist: totally 14 connections will be listed: the 5 admin connections, the 8 non-admin connections, and the default one +--replace_column 1 - 3 localhost 5 - 6 - 7 - 8 - 9 - 10 - +--sorted_result +show processlist; + +--echo ## Switch to non_admin_con_super1_1 +connection non_admin_con_super1_1; +select user from mysql.user order by 1 asc; + +let $i=4; +while ($i<=8) { + --echo ## Switch to admin_con_root$i + connection admin_con_root$i; + select user from mysql.user order by 1 asc; + inc $i; +} + +--echo ## Switch to admin_con_root4 +connection admin_con_root4; + +--echo ## Drop user nosuper, super1, and super2 +drop user nosuper@localhost; +drop user super1@localhost; +drop user super2@localhost; +select user from mysql.user order by 1 asc; + +--echo ## Disconnect admin_con_root4,5,6,7,8 +let $i=4; +while ($i<=8) { + disconnect admin_con_root$i; + inc $i; +} + +--echo ## Disconnect non_admin_con_super1_1,2,3,4 +let $i=1; +while ($i<=4) { + disconnect non_admin_con_super1_$i; + inc $i; +} + +--echo ## Disconnect non_admin_con_super2_1,2,3,4 +let $i=1; +while ($i<=4) { + disconnect non_admin_con_super2_$i; + inc $i; +} + +--echo ## Switch to default connection +connection default; diff --git a/mysql-test/include/restart_mysqld_with_option.inc b/mysql-test/include/restart_mysqld_with_option.inc new file mode 100644 index 0000000..4250b36 --- /dev/null +++ b/mysql-test/include/restart_mysqld_with_option.inc @@ -0,0 +1,31 @@ + +if ($rpl_inited) +{ + if (!$allow_rpl_inited) + { + --die ERROR IN TEST: This script does not support replication + } +} + +# Write file to make mysql-test-run.pl expect the "crash", but don't start +# it until it's told to +--let $_server_id= `SELECT @@server_id` +--let $_expect_file_name= $MYSQLTEST_VARDIR/tmp/mysqld.$_server_id.expect +--exec echo "wait" > $_expect_file_name + +# Send shutdown to the connected server and give +# it 10 seconds to die before zapping it +shutdown_server 10; + +# Write file to make mysql-test-run.pl start up the server again +--exec echo "restart:$_mysqld_option" > $_expect_file_name + +# Turn on reconnect +--enable_reconnect + +# Call script that will poll the server waiting for it to be back online again +--source include/wait_until_connected_again.inc + +# Turn off reconnect again +--disable_reconnect + diff --git a/mysql-test/r/admin_connection.result b/mysql-test/r/admin_connection.result new file mode 100644 index 0000000..731c500 --- /dev/null +++ b/mysql-test/r/admin_connection.result @@ -0,0 +1,190 @@ +## ------------------------------------------------------------------ +## -- Test of IP= 127.0.0.1 (ipv4) +## ------------------------------------------------------------------ +flush privileges; +## Create non-super user nosuper +create user nosuper@localhost; +## Create super user 'super1' +create user super1@localhost; +grant all privileges on *.* to 'super1'@'localhost'; +## Create super user 'super2' +create user super2@localhost; +grant all privileges on *.* to 'super2'@'localhost'; +flush user_resources; +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Start a connection fail_admin_con1 at ADMIN_PORT with new user nosuper +## This will fail since this user doesn't have SUPER ACL +connect(127.0.0.1,nosuper,,mysql,ADMIN_PORT,MASTER_SOCKET); +ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation +## Start connections admin_con_root1,2,3 at ADMIN_PORT +## This will succeed since root has SUPER ACL +## Switch to admin_con_root1 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root2 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root3 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Disconnect admin_con_root1,2,3 +## Switch to default connection +## Wait for all the connections except the default one to be completedly disconnected +## Switch to default connection +## Show processlist: only the default connection will be listed +show processlist; +Id User Host db Command Time State Info +- event_scheduler localhost NULL - - - - +- root localhost test - - - - +## Start connections non_admin_con_super1_1,2,3,4 +## Connections non_admin_con_super1_5 will fail due to too many connections for a user +connect(127.0.0.1,super1,,mysql,MASTER_PORT,MASTER_SOCKET); +ERROR 42000: User super1 already has more than 'max_user_connections' active connections +## Start connections non_admin_con_super2_1,2,3,4 +## Switch to default connection +## Show processlist: totally 9 connections will be listed: the 8 non-admin connections and the default one +show processlist; +Id User Host db Command Time State Info +- event_scheduler localhost NULL - - - - +- root localhost test - - - - +- super1 localhost mysql - - - - +- super1 localhost mysql - - - - +- super1 localhost mysql - - - - +- super1 localhost mysql - - - - +- super2 localhost mysql - - - - +- super2 localhost mysql - - - - +- super2 localhost mysql - - - - +- super2 localhost mysql - - - - +## Start a connection fail_non_admin_con9 at master port +## This will fail due to the limit of the max number of connections +connect(127.0.0.1,root,,mysql,MASTER_PORT,MASTER_SOCKET); +ERROR HY000: Too many connections +## Start connections admin_con_root4,5,6,7,8 at ADMIN_PORT +## This will succeed since admin port is handled separately +## and it is not limited by either @@max-connections or @@max-user-connections +## Switch to default connection +## Show processlist: totally 14 connections will be listed: the 5 admin connections, the 8 non-admin connections, and the default one +show processlist; +Id User Host db Command Time State Info +- event_scheduler localhost NULL - - - - +- root localhost mysql - - - - +- root localhost mysql - - - - +- root localhost mysql - - - - +- root localhost mysql - - - - +- root localhost mysql - - - - +- root localhost test - - - - +- super1 localhost mysql - - - - +- super1 localhost mysql - - - - +- super1 localhost mysql - - - - +- super1 localhost mysql - - - - +- super2 localhost mysql - - - - +- super2 localhost mysql - - - - +- super2 localhost mysql - - - - +- super2 localhost mysql - - - - +## Switch to non_admin_con_super1_1 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root4 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root5 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root6 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root7 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root8 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root4 +## Drop user nosuper, super1, and super2 +drop user nosuper@localhost; +drop user super1@localhost; +drop user super2@localhost; +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +root +## Disconnect admin_con_root4,5,6,7,8 +## Disconnect non_admin_con_super1_1,2,3,4 +## Disconnect non_admin_con_super2_1,2,3,4 +## Switch to default connection +## ------------------------------------------------------------------ +## -- End of admin connection ipv4 tests +## ------------------------------------------------------------------ diff --git a/mysql-test/r/admin_connection_ipv6.result b/mysql-test/r/admin_connection_ipv6.result new file mode 100644 index 0000000..21d6f66fd --- /dev/null +++ b/mysql-test/r/admin_connection_ipv6.result @@ -0,0 +1,564 @@ +## ------------------------------------------------------------------ +## -- Test of IP= ::1 (ipv6) +## ------------------------------------------------------------------ +flush privileges; +## Create non-super user nosuper +create user nosuper@localhost; +## Create super user 'super1' +create user super1@localhost; +grant all privileges on *.* to 'super1'@'localhost'; +## Create super user 'super2' +create user super2@localhost; +grant all privileges on *.* to 'super2'@'localhost'; +flush user_resources; +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Start a connection fail_admin_con1 at ADMIN_PORT with new user nosuper +## This will fail since this user doesn't have SUPER ACL +connect(::1,nosuper,,mysql,ADMIN_PORT,MASTER_SOCKET); +ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation +## Start connections admin_con_root1,2,3 at ADMIN_PORT +## This will succeed since root has SUPER ACL +## Switch to admin_con_root1 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root2 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root3 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Disconnect admin_con_root1,2,3 +## Switch to default connection +## Wait for all the connections except the default one to be completedly disconnected +## Switch to default connection +## Show processlist: only the default connection will be listed +show processlist; +Id User Host db Command Time State Info +- event_scheduler localhost NULL - - - - +- root localhost test - - - - +## Start connections non_admin_con_super1_1,2,3,4 +## Connections non_admin_con_super1_5 will fail due to too many connections for a user +connect(::1,super1,,mysql,MASTER_PORT,MASTER_SOCKET); +ERROR 42000: User super1 already has more than 'max_user_connections' active connections +## Start connections non_admin_con_super2_1,2,3,4 +## Switch to default connection +## Show processlist: totally 9 connections will be listed: the 8 non-admin connections and the default one +show processlist; +Id User Host db Command Time State Info +- event_scheduler localhost NULL - - - - +- root localhost test - - - - +- super1 localhost mysql - - - - +- super1 localhost mysql - - - - +- super1 localhost mysql - - - - +- super1 localhost mysql - - - - +- super2 localhost mysql - - - - +- super2 localhost mysql - - - - +- super2 localhost mysql - - - - +- super2 localhost mysql - - - - +## Start a connection fail_non_admin_con9 at master port +## This will fail due to the limit of the max number of connections +connect(::1,root,,mysql,MASTER_PORT,MASTER_SOCKET); +ERROR HY000: Too many connections +## Start connections admin_con_root4,5,6,7,8 at ADMIN_PORT +## This will succeed since admin port is handled separately +## and it is not limited by either @@max-connections or @@max-user-connections +## Switch to default connection +## Show processlist: totally 14 connections will be listed: the 5 admin connections, the 8 non-admin connections, and the default one +show processlist; +Id User Host db Command Time State Info +- event_scheduler localhost NULL - - - - +- root localhost mysql - - - - +- root localhost mysql - - - - +- root localhost mysql - - - - +- root localhost mysql - - - - +- root localhost mysql - - - - +- root localhost test - - - - +- super1 localhost mysql - - - - +- super1 localhost mysql - - - - +- super1 localhost mysql - - - - +- super1 localhost mysql - - - - +- super2 localhost mysql - - - - +- super2 localhost mysql - - - - +- super2 localhost mysql - - - - +- super2 localhost mysql - - - - +## Switch to non_admin_con_super1_1 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root4 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root5 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root6 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root7 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root8 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root4 +## Drop user nosuper, super1, and super2 +drop user nosuper@localhost; +drop user super1@localhost; +drop user super2@localhost; +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +root +## Disconnect admin_con_root4,5,6,7,8 +## Disconnect non_admin_con_super1_1,2,3,4 +## Disconnect non_admin_con_super2_1,2,3,4 +## Switch to default connection +## ------------------------------------------------------------------ +## -- Test of IP= 0:0:0:0:0:0:0:1 (ipv6) +## ------------------------------------------------------------------ +flush privileges; +## Create non-super user nosuper +create user nosuper@localhost; +## Create super user 'super1' +create user super1@localhost; +grant all privileges on *.* to 'super1'@'localhost'; +## Create super user 'super2' +create user super2@localhost; +grant all privileges on *.* to 'super2'@'localhost'; +flush user_resources; +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Start a connection fail_admin_con1 at ADMIN_PORT with new user nosuper +## This will fail since this user doesn't have SUPER ACL +connect(0:0:0:0:0:0:0:1,nosuper,,mysql,ADMIN_PORT,MASTER_SOCKET); +ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation +## Start connections admin_con_root1,2,3 at ADMIN_PORT +## This will succeed since root has SUPER ACL +## Switch to admin_con_root1 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root2 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root3 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Disconnect admin_con_root1,2,3 +## Switch to default connection +## Wait for all the connections except the default one to be completedly disconnected +## Switch to default connection +## Show processlist: only the default connection will be listed +show processlist; +Id User Host db Command Time State Info +- event_scheduler localhost NULL - - - - +- root localhost test - - - - +## Start connections non_admin_con_super1_1,2,3,4 +## Connections non_admin_con_super1_5 will fail due to too many connections for a user +connect(0:0:0:0:0:0:0:1,super1,,mysql,MASTER_PORT,MASTER_SOCKET); +ERROR 42000: User super1 already has more than 'max_user_connections' active connections +## Start connections non_admin_con_super2_1,2,3,4 +## Switch to default connection +## Show processlist: totally 9 connections will be listed: the 8 non-admin connections and the default one +show processlist; +Id User Host db Command Time State Info +- event_scheduler localhost NULL - - - - +- root localhost test - - - - +- super1 localhost mysql - - - - +- super1 localhost mysql - - - - +- super1 localhost mysql - - - - +- super1 localhost mysql - - - - +- super2 localhost mysql - - - - +- super2 localhost mysql - - - - +- super2 localhost mysql - - - - +- super2 localhost mysql - - - - +## Start a connection fail_non_admin_con9 at master port +## This will fail due to the limit of the max number of connections +connect(0:0:0:0:0:0:0:1,root,,mysql,MASTER_PORT,MASTER_SOCKET); +ERROR HY000: Too many connections +## Start connections admin_con_root4,5,6,7,8 at ADMIN_PORT +## This will succeed since admin port is handled separately +## and it is not limited by either @@max-connections or @@max-user-connections +## Switch to default connection +## Show processlist: totally 14 connections will be listed: the 5 admin connections, the 8 non-admin connections, and the default one +show processlist; +Id User Host db Command Time State Info +- event_scheduler localhost NULL - - - - +- root localhost mysql - - - - +- root localhost mysql - - - - +- root localhost mysql - - - - +- root localhost mysql - - - - +- root localhost mysql - - - - +- root localhost test - - - - +- super1 localhost mysql - - - - +- super1 localhost mysql - - - - +- super1 localhost mysql - - - - +- super1 localhost mysql - - - - +- super2 localhost mysql - - - - +- super2 localhost mysql - - - - +- super2 localhost mysql - - - - +- super2 localhost mysql - - - - +## Switch to non_admin_con_super1_1 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root4 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root5 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root6 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root7 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root8 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root4 +## Drop user nosuper, super1, and super2 +drop user nosuper@localhost; +drop user super1@localhost; +drop user super2@localhost; +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +root +## Disconnect admin_con_root4,5,6,7,8 +## Disconnect non_admin_con_super1_1,2,3,4 +## Disconnect non_admin_con_super2_1,2,3,4 +## Switch to default connection +## ------------------------------------------------------------------ +## -- Test of IP= 0000:0000:0000:0000:0000:0000:0000:0001 (ipv6) +## ------------------------------------------------------------------ +flush privileges; +## Create non-super user nosuper +create user nosuper@localhost; +## Create super user 'super1' +create user super1@localhost; +grant all privileges on *.* to 'super1'@'localhost'; +## Create super user 'super2' +create user super2@localhost; +grant all privileges on *.* to 'super2'@'localhost'; +flush user_resources; +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Start a connection fail_admin_con1 at ADMIN_PORT with new user nosuper +## This will fail since this user doesn't have SUPER ACL +connect(0000:0000:0000:0000:0000:0000:0000:0001,nosuper,,mysql,ADMIN_PORT,MASTER_SOCKET); +ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation +## Start connections admin_con_root1,2,3 at ADMIN_PORT +## This will succeed since root has SUPER ACL +## Switch to admin_con_root1 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root2 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root3 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Disconnect admin_con_root1,2,3 +## Switch to default connection +## Wait for all the connections except the default one to be completedly disconnected +## Switch to default connection +## Show processlist: only the default connection will be listed +show processlist; +Id User Host db Command Time State Info +- event_scheduler localhost NULL - - - - +- root localhost test - - - - +## Start connections non_admin_con_super1_1,2,3,4 +## Connections non_admin_con_super1_5 will fail due to too many connections for a user +connect(0000:0000:0000:0000:0000:0000:0000:0001,super1,,mysql,MASTER_PORT,MASTER_SOCKET); +ERROR 42000: User super1 already has more than 'max_user_connections' active connections +## Start connections non_admin_con_super2_1,2,3,4 +## Switch to default connection +## Show processlist: totally 9 connections will be listed: the 8 non-admin connections and the default one +show processlist; +Id User Host db Command Time State Info +- event_scheduler localhost NULL - - - - +- root localhost test - - - - +- super1 localhost mysql - - - - +- super1 localhost mysql - - - - +- super1 localhost mysql - - - - +- super1 localhost mysql - - - - +- super2 localhost mysql - - - - +- super2 localhost mysql - - - - +- super2 localhost mysql - - - - +- super2 localhost mysql - - - - +## Start a connection fail_non_admin_con9 at master port +## This will fail due to the limit of the max number of connections +connect(0000:0000:0000:0000:0000:0000:0000:0001,root,,mysql,MASTER_PORT,MASTER_SOCKET); +ERROR HY000: Too many connections +## Start connections admin_con_root4,5,6,7,8 at ADMIN_PORT +## This will succeed since admin port is handled separately +## and it is not limited by either @@max-connections or @@max-user-connections +## Switch to default connection +## Show processlist: totally 14 connections will be listed: the 5 admin connections, the 8 non-admin connections, and the default one +show processlist; +Id User Host db Command Time State Info +- event_scheduler localhost NULL - - - - +- root localhost mysql - - - - +- root localhost mysql - - - - +- root localhost mysql - - - - +- root localhost mysql - - - - +- root localhost mysql - - - - +- root localhost test - - - - +- super1 localhost mysql - - - - +- super1 localhost mysql - - - - +- super1 localhost mysql - - - - +- super1 localhost mysql - - - - +- super2 localhost mysql - - - - +- super2 localhost mysql - - - - +- super2 localhost mysql - - - - +- super2 localhost mysql - - - - +## Switch to non_admin_con_super1_1 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root4 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root5 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root6 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root7 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root8 +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +nosuper +root +super1 +super2 +## Switch to admin_con_root4 +## Drop user nosuper, super1, and super2 +drop user nosuper@localhost; +drop user super1@localhost; +drop user super2@localhost; +select user from mysql.user order by 1 asc; +user +mysql.infoschema +mysql.session +mysql.sys +root +## Disconnect admin_con_root4,5,6,7,8 +## Disconnect non_admin_con_super1_1,2,3,4 +## Disconnect non_admin_con_super2_1,2,3,4 +## Switch to default connection +## ------------------------------------------------------------------ +## -- End of admin connection ipv6 tests +## ------------------------------------------------------------------ diff --git a/mysql-test/r/mysqld--help-notwin.result b/mysql-test/r/mysqld--help-notwin.result index 5dc6518..d5351a1 100644 --- a/mysql-test/r/mysqld--help-notwin.result +++ b/mysql-test/r/mysqld--help-notwin.result @@ -14,6 +14,7 @@ The following options may be given as the first argument: --activate-all-roles-on-login Automatically set all granted roles as active after the user has authenticated successfully. + --admin-port=# Port number to use for connections from admin. --allow-noncurrent-db-rw=name Switch to allow/deny reads and writes to a table not in the current database. @@ -1350,6 +1351,7 @@ The following options may be given as the first argument: Variables (--variable-name=value) abort-slave-event-count 0 activate-all-roles-on-login FALSE +admin-port 0 allow-noncurrent-db-rw ON allow-suspicious-udfs FALSE auto-increment-increment 1 diff --git a/mysql-test/suite/sys_vars/r/admin_port_basic.result b/mysql-test/suite/sys_vars/r/admin_port_basic.result new file mode 100644 index 0000000..dd3cd8f --- /dev/null +++ b/mysql-test/suite/sys_vars/r/admin_port_basic.result @@ -0,0 +1,21 @@ +select @@global.admin_port; +@@global.admin_port +0 +select @@session.admin_port; +ERROR HY000: Variable 'admin_port' is a GLOBAL variable +show global variables like 'admin_port'; +Variable_name Value +admin_port 0 +show session variables like 'admin_port'; +Variable_name Value +admin_port 0 +select * from performance_schema.global_variables where variable_name='admin_port'; +VARIABLE_NAME VARIABLE_VALUE +admin_port 0 +select * from performance_schema.session_variables where variable_name='admin_port'; +VARIABLE_NAME VARIABLE_VALUE +admin_port 0 +set global admin_port=1; +ERROR HY000: Variable 'admin_port' is a read only variable +set session admin_port=1; +ERROR HY000: Variable 'admin_port' is a read only variable diff --git a/mysql-test/suite/sys_vars/t/admin_port_basic.test b/mysql-test/suite/sys_vars/t/admin_port_basic.test new file mode 100644 index 0000000..3ba3bb5 --- /dev/null +++ b/mysql-test/suite/sys_vars/t/admin_port_basic.test @@ -0,0 +1,21 @@ +--source include/load_sysvars.inc + +### +### only global +### +select @@global.admin_port; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +select @@session.admin_port; + +show global variables like 'admin_port'; +show session variables like 'admin_port'; +select * from performance_schema.global_variables where variable_name='admin_port'; +select * from performance_schema.session_variables where variable_name='admin_port'; + +### +### show that it's read-only +### +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +set global admin_port=1; +--error ER_INCORRECT_GLOBAL_LOCAL_VAR +set session admin_port=1; diff --git a/mysql-test/t/admin_connection-master.opt b/mysql-test/t/admin_connection-master.opt new file mode 100644 index 0000000..a381b35 --- /dev/null +++ b/mysql-test/t/admin_connection-master.opt @@ -0,0 +1,2 @@ +--max-connections=8 +--max-user-connections=4 diff --git a/mysql-test/t/admin_connection.test b/mysql-test/t/admin_connection.test new file mode 100644 index 0000000..4cbbebf --- /dev/null +++ b/mysql-test/t/admin_connection.test @@ -0,0 +1,12 @@ +# This test is to check various cases of connections +# to the admin tcpip port of ipv4 + +--echo ## ------------------------------------------------------------------ +--echo ## -- Test of IP= 127.0.0.1 (ipv4) +--echo ## ------------------------------------------------------------------ +let $IP= 127.0.0.1; +--source include/admin_connection.inc + +--echo ## ------------------------------------------------------------------ +--echo ## -- End of admin connection ipv4 tests +--echo ## ------------------------------------------------------------------ diff --git a/mysql-test/t/admin_connection_ipv6-master.opt b/mysql-test/t/admin_connection_ipv6-master.opt new file mode 100644 index 0000000..a381b35 --- /dev/null +++ b/mysql-test/t/admin_connection_ipv6-master.opt @@ -0,0 +1,2 @@ +--max-connections=8 +--max-user-connections=4 diff --git a/mysql-test/t/admin_connection_ipv6.test b/mysql-test/t/admin_connection_ipv6.test new file mode 100644 index 0000000..1341d1e --- /dev/null +++ b/mysql-test/t/admin_connection_ipv6.test @@ -0,0 +1,27 @@ +# This test is to check various cases of connections +# to the admin tcpip port of ipv6 + +# Skip this test if ipv6 is not supported +--source include/check_ipv6.inc + +--echo ## ------------------------------------------------------------------ +--echo ## -- Test of IP= ::1 (ipv6) +--echo ## ------------------------------------------------------------------ +let $IP= ::1; +--source include/admin_connection.inc + +--echo ## ------------------------------------------------------------------ +--echo ## -- Test of IP= 0:0:0:0:0:0:0:1 (ipv6) +--echo ## ------------------------------------------------------------------ +let $IP= 0:0:0:0:0:0:0:1; +--source include/admin_connection.inc + +--echo ## ------------------------------------------------------------------ +--echo ## -- Test of IP= 0000:0000:0000:0000:0000:0000:0000:0001 (ipv6) +--echo ## ------------------------------------------------------------------ +let $IP= 0000:0000:0000:0000:0000:0000:0000:0001; +--source include/admin_connection.inc + +--echo ## ------------------------------------------------------------------ +--echo ## -- End of admin connection ipv6 tests +--echo ## ------------------------------------------------------------------ diff --git a/sql/auth/sql_authentication.cc b/sql/auth/sql_authentication.cc index d3cfbe7..99c8135 100644 --- a/sql/auth/sql_authentication.cc +++ b/sql/auth/sql_authentication.cc @@ -2755,6 +2755,13 @@ acl_authenticate(THD *thd, enum_server_command command) thd->password ? "yes": "no", sctx->master_access(), mpvio.db.str)); + if (thd->is_admin_connection() && + !(thd->m_main_security_ctx.check_access(SUPER_ACL))) + { + my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), "SUPER"); + DBUG_RETURN (1); + } + if (command == COM_CONNECT && !(thd->m_main_security_ctx.check_access(SUPER_ACL) || thd->m_main_security_ctx.has_global_grant(STRING_WITH_LEN("CONNECTION_ADMIN")).first)) diff --git a/sql/conn_handler/channel_info.h b/sql/conn_handler/channel_info.h index cabf4f9..dd64766 100644 --- a/sql/conn_handler/channel_info.h +++ b/sql/conn_handler/channel_info.h @@ -47,6 +47,7 @@ typedef struct st_vio Vio; class Channel_info { ulonglong prior_thr_create_utime; + bool m_admin; protected: /** @@ -57,7 +58,7 @@ protected: virtual Vio* create_and_init_vio() const = 0; Channel_info() - : prior_thr_create_utime(0) + : prior_thr_create_utime(0), m_admin(false) { } public: @@ -91,6 +92,12 @@ public: void set_prior_thr_create_utime() { prior_thr_create_utime= my_micro_time(); } + + bool get_admin() const + { return m_admin; } + + void set_admin(bool admin = true) + { m_admin = admin; } }; #endif // SQL_CHANNEL_INFO_INCLUDED. diff --git a/sql/conn_handler/connection_handler_manager.cc b/sql/conn_handler/connection_handler_manager.cc index 804e628..36ac084 100644 --- a/sql/conn_handler/connection_handler_manager.cc +++ b/sql/conn_handler/connection_handler_manager.cc @@ -106,9 +106,10 @@ bool Connection_handler_manager::valid_connection_count() } -bool Connection_handler_manager::check_and_incr_conn_count() +bool Connection_handler_manager::check_and_incr_conn_count(bool admin) { bool connection_accepted= true; + int admin_cap = admin ? 16 : 0; mysql_mutex_lock(&LOCK_connection_count); /* Here we allow max_connections + 1 clients to connect @@ -117,8 +118,10 @@ bool Connection_handler_manager::check_and_incr_conn_count() The last connection is reserved for SUPER users. This is checked later during authentication where valid_connection_count() is called for non-SUPER users only. + + Admin port can exceed the cap by 16 connections. */ - if (connection_count > max_connections) + if (connection_count > max_connections + admin_cap) { connection_accepted= false; m_connection_errors_max_connection++; @@ -278,7 +281,8 @@ bool Connection_handler_manager::unload_connection_handler() void Connection_handler_manager::process_new_connection(Channel_info* channel_info) { - if (connection_events_loop_aborted() || !check_and_incr_conn_count()) + if (connection_events_loop_aborted() || + !check_and_incr_conn_count(channel_info->get_admin())) { channel_info->send_error_and_close_channel(ER_CON_COUNT_ERROR, 0, true); delete channel_info; diff --git a/sql/conn_handler/connection_handler_manager.h b/sql/conn_handler/connection_handler_manager.h index 84f1aa3..298a9a9 100644 --- a/sql/conn_handler/connection_handler_manager.h +++ b/sql/conn_handler/connection_handler_manager.h @@ -172,11 +172,12 @@ public: /** Increment connection count if max_connections is not exceeded. + @param admin Admin port connections can bypass the max_connections limit @retval true max_connections NOT exceeded false max_connections reached */ - bool check_and_incr_conn_count(); + bool check_and_incr_conn_count(bool admin = false); /** Reset the max_used_connections counter to the number of current diff --git a/sql/conn_handler/socket_connection.cc b/sql/conn_handler/socket_connection.cc index f4622bc..2ffd386 100644 --- a/sql/conn_handler/socket_connection.cc +++ b/sql/conn_handler/socket_connection.cc @@ -188,6 +188,8 @@ public: if (thd != NULL) init_net_server_extension(thd); + if (this->get_admin()) + thd->set_admin_connection(); return thd; } @@ -752,14 +754,16 @@ Mysqld_socket_listener::Mysqld_socket_listener(std::string bind_addr_str, uint tcp_port, uint backlog, uint port_timeout, - std::string unix_sockname) + std::string unix_sockname, + bool admin) : m_bind_addr_str(bind_addr_str), m_tcp_port(tcp_port), m_backlog(backlog), m_port_timeout(port_timeout), m_unix_sockname(unix_sockname), m_unlink_sockname(false), - m_error_count(0) + m_error_count(0), + m_admin(admin) { #ifdef HAVE_LIBWRAP m_deny_severity = LOG_WARNING; @@ -956,6 +960,11 @@ Channel_info* Mysqld_socket_listener::listen_for_connection_event() return NULL; } + if (m_admin) + { + channel_info->set_admin(); + } + return channel_info; } diff --git a/sql/conn_handler/socket_connection.h b/sql/conn_handler/socket_connection.h index 85d621e..49557c5 100644 --- a/sql/conn_handler/socket_connection.h +++ b/sql/conn_handler/socket_connection.h @@ -96,6 +96,8 @@ class Mysqld_socket_listener uint m_error_count; // Internal variable for maintaining error count. + bool m_admin; // Admin port + #ifdef HAVE_POLL static const int MAX_SOCKETS=2; struct poll_info_t @@ -155,10 +157,12 @@ public: connection queue used in listen. @param port_timeout portname. @param unix_sockname pathname for unix socket to bind to + @param admin socket listener is an admin port */ Mysqld_socket_listener(std::string bind_addr_str, uint tcp_port, uint backlog, uint port_timeout, - std::string unix_sockname); + std::string unix_sockname, + bool admin = false); /** Set up a listener - set of sockets to listen for connection events diff --git a/sql/mysqld.cc b/sql/mysqld.cc index c31df13..7a9a38c 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -752,6 +752,7 @@ static PSI_mutex_key key_LOCK_handler_count; static PSI_cond_key key_COND_handler_count; static PSI_thread_key key_thread_handle_shutdown_restart; #else +static PSI_thread_key key_thread_handle_con_admin_sockets; static PSI_mutex_key key_LOCK_socket_listener_active; static PSI_cond_key key_COND_socket_listener_active; static PSI_mutex_key key_LOCK_start_signal_handler; @@ -772,7 +773,7 @@ PSI_statement_info stmt_info_rpl; /* the default log output is log tables */ static bool lower_case_table_names_used= 0; #if !defined(_WIN32) -static bool socket_listener_active= false; +static int socket_listener_active= 0; static int pipe_write_fd= -1; static bool opt_daemonize= 0; #endif @@ -957,6 +958,7 @@ const char *timestamp_type_names[]= {"UTC", "SYSTEM", NullS}; ulong opt_log_timestamps; uint mysqld_port, test_flags, select_errors, ha_open_options; uint mysqld_port_timeout; +ulong mysqld_admin_port; ulong delay_key_write_options; uint protocol_version; uint lower_case_table_names; @@ -1368,6 +1370,8 @@ void substitute_progpath(char** argv) } // namespace static Connection_acceptor *mysqld_socket_acceptor= NULL; +static Connection_acceptor + *mysqld_admin_socket_acceptor= NULL; #ifdef _WIN32 Connection_acceptor *named_pipe_acceptor= NULL; Connection_acceptor *shared_mem_acceptor= NULL; @@ -1789,6 +1793,8 @@ static void close_connections(void) // Close listeners. if (mysqld_socket_acceptor != NULL) mysqld_socket_acceptor->close_listener(); + if (mysqld_admin_socket_acceptor != NULL) + mysqld_admin_socket_acceptor->close_listener(); #ifdef _WIN32 if (named_pipe_acceptor != NULL) named_pipe_acceptor->close_listener(); @@ -2065,6 +2071,8 @@ static void free_connection_acceptors() { delete mysqld_socket_acceptor; mysqld_socket_acceptor= NULL; + delete mysqld_admin_socket_acceptor; + mysqld_admin_socket_acceptor= NULL; #ifdef _WIN32 delete named_pipe_acceptor; @@ -2426,7 +2434,6 @@ static bool network_init(void) return false; set_ports(); - #ifdef HAVE_SYS_UN_H std::string const unix_sock_name(mysqld_unix_port ? mysqld_unix_port : ""); #else @@ -2462,6 +2469,29 @@ static bool network_init(void) if (!opt_disable_networking) DBUG_ASSERT(report_port != 0); + + if (mysqld_admin_port != 0) { + mysqld_socket_listener= + new (std::nothrow) Mysqld_socket_listener(bind_addr_str, + mysqld_admin_port, back_log, + mysqld_port_timeout, "", + true); + if (mysqld_socket_listener == NULL) + return true; + + mysqld_admin_socket_acceptor= + new (std::nothrow) Connection_acceptor( + mysqld_socket_listener); + if (mysqld_admin_socket_acceptor == NULL) + { + delete mysqld_socket_listener; + mysqld_socket_listener= NULL; + return true; + } + + if (mysqld_admin_socket_acceptor->init_connection_acceptor()) + return true; // mysqld_socket_acceptor would be freed in unireg_abort. + } } #ifdef _WIN32 // Create named pipe @@ -2963,7 +2993,7 @@ extern "C" void *signal_hand(void *arg MY_ATTRIBUTE((unused))) and wait for us to finish all the cleanup below. */ mysql_mutex_lock(&LOCK_socket_listener_active); - while (socket_listener_active) + while (socket_listener_active > 0) { DBUG_PRINT("info",("Killing socket listener")); if (pthread_kill(main_thread_id, SIGUSR1)) @@ -3002,6 +3032,24 @@ extern "C" void *signal_hand(void *arg MY_ATTRIBUTE((unused))) return NULL; /* purecov: deadcode */ } +extern "C" void *socket_conn_admin_event_handler(void *arg) +{ + my_thread_init(); + + Connection_acceptor *conn_acceptor= + static_cast*>(arg); + conn_acceptor->connection_event_loop(); + + mysql_mutex_lock(&LOCK_socket_listener_active); + DBUG_ASSERT(socket_listener_active > 0); + socket_listener_active -= 1; + mysql_cond_broadcast(&COND_socket_listener_active); + mysql_mutex_unlock(&LOCK_socket_listener_active); + + my_thread_end(); + return 0; +} + #endif // !_WIN32 @@ -5520,6 +5568,56 @@ static void set_super_read_only_post_init() opt_super_readonly= super_read_only; } +static void handle_connections_sockets_all() +{ + my_thread_handle admin_handle; + + mysql_mutex_lock(&LOCK_socket_listener_active); + + if (mysqld_admin_socket_acceptor) { + int error= mysql_thread_create(key_thread_handle_con_admin_sockets, + &admin_handle, &connection_attrib, + socket_conn_admin_event_handler, + mysqld_admin_socket_acceptor); + if (error) { + LogErr(WARNING_LEVEL, ER_CANT_CREATE_TCPIP_THREAD, error); + } else { + socket_listener_active += 1; + } + } + + // Make it possible for the signal handler to kill the listener. + socket_listener_active += 1; + mysql_mutex_unlock(&LOCK_socket_listener_active); + + if (opt_daemonize) + { + if (nstdout != nullptr) + { + // Show the pid on stdout if deamonizing and connected to tty + fprintf(nstdout, "mysqld is running as pid %lu\n", current_pid); + fclose(nstdout); + nstdout= nullptr; + } + + mysqld::runtime::signal_parent(pipe_write_fd,1); + } + mysqld_socket_acceptor->connection_event_loop(); + + mysql_mutex_lock(&LOCK_socket_listener_active); + + while (socket_listener_active > 1) { + DBUG_PRINT("info",("Killing admin socket listener")); + if (pthread_kill(admin_handle.thread, SIGUSR1)) + { + DBUG_ASSERT(false); + break; + } + mysql_cond_wait(&COND_socket_listener_active, &LOCK_socket_listener_active); + } + mysql_mutex_unlock(&LOCK_socket_listener_active); +} + #ifdef _WIN32 int win_main(int argc, char **argv) #else @@ -6559,25 +6657,7 @@ int mysqld_main(int argc, char **argv) #if defined(_WIN32) setup_conn_event_handler_threads(); #else - mysql_mutex_lock(&LOCK_socket_listener_active); - // Make it possible for the signal handler to kill the listener. - socket_listener_active= true; - mysql_mutex_unlock(&LOCK_socket_listener_active); - - if (opt_daemonize) - { - if (nstdout != nullptr) - { - // Show the pid on stdout if deamonizing and connected to tty - fprintf(nstdout, "mysqld is running as pid %lu\n", current_pid); - fclose(nstdout); - nstdout= nullptr; - } - - mysqld::runtime::signal_parent(pipe_write_fd,1); - } - - mysqld_socket_acceptor->connection_event_loop(); + handle_connections_sockets_all(); #endif /* _WIN32 */ server_operational_state= SERVER_SHUTTING_DOWN; @@ -6599,7 +6679,8 @@ int mysqld_main(int argc, char **argv) #ifndef _WIN32 mysql_mutex_lock(&LOCK_socket_listener_active); // Notify the signal handler that we have stopped listening for connections. - socket_listener_active= false; + DBUG_ASSERT(socket_listener_active == 1); + socket_listener_active= 0; mysql_cond_broadcast(&COND_socket_listener_active); mysql_mutex_unlock(&LOCK_socket_listener_active); #endif // !_WIN32 @@ -10594,6 +10675,8 @@ static PSI_thread_info all_server_threads[]= { &key_thread_handle_con_sharedmem, "con_shared_mem", PSI_FLAG_SINGLETON, 0, PSI_DOCUMENT_ME}, { &key_thread_handle_con_sockets, "con_sockets", PSI_FLAG_SINGLETON, 0, PSI_DOCUMENT_ME}, { &key_thread_handle_shutdown_restart, "shutdown_restart", PSI_FLAG_SINGLETON, 0, PSI_DOCUMENT_ME}, +#else /* _WIN32 */ + { &key_thread_handle_con_admin_sockets, "con_admin_sockets", PSI_FLAG_SINGLETON, 0, PSI_DOCUMENT_ME}, #endif /* _WIN32 */ { &key_thread_bootstrap, "bootstrap", PSI_FLAG_SINGLETON, 0, PSI_DOCUMENT_ME}, { &key_thread_handle_manager, "manager", PSI_FLAG_SINGLETON, 0, PSI_DOCUMENT_ME}, diff --git a/sql/mysqld.h b/sql/mysqld.h index 88a62a1..96e81af 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -247,6 +247,7 @@ extern bool offline_mode; extern bool opt_log_builtin_as_identified_by_password; extern uint test_flags,select_errors,ha_open_options; extern uint protocol_version, mysqld_port; +extern ulong mysqld_admin_port; enum enum_delay_key_write { DELAY_KEY_WRITE_NONE, DELAY_KEY_WRITE_ON, DELAY_KEY_WRITE_ALL }; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 06959f8..1d56ed7 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -365,6 +365,7 @@ THD::THD(bool enable_plugins) m_current_stage_key(0), current_mutex(NULL), current_cond(NULL), + is_admin_conn(false), in_sub_stmt(0), fill_status_recursion_level(0), fill_variables_recursion_level(0), diff --git a/sql/sql_class.h b/sql/sql_class.h index a4f2857..eff5259 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -1323,6 +1323,13 @@ private: */ enum enum_server_command m_command; +private: + bool is_admin_conn; + +public: + void set_admin_connection(bool admin = true) { is_admin_conn = admin; } + bool is_admin_connection() { return is_admin_conn; } + public: uint32 unmasked_server_id; uint32 server_id; diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index 0e560ed..7493c9e 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -200,7 +200,8 @@ int check_for_max_user_connections(THD *thd, const USER_CONN *uc) mysql_mutex_lock(&LOCK_user_conn); if (global_system_variables.max_user_connections && !uc->user_resources.user_conn && - global_system_variables.max_user_connections < (uint) uc->connections) + global_system_variables.max_user_connections < (uint) uc->connections && + !thd->is_admin_connection()) { my_error(ER_TOO_MANY_USER_CONNECTIONS, MYF(0), uc->user); error=1; diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 479e09b..de18e9a 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -3192,6 +3192,12 @@ static Sys_var_uint Sys_port( READ_ONLY NON_PERSIST GLOBAL_VAR(mysqld_port), CMD_LINE(REQUIRED_ARG, 'P'), VALID_RANGE(0, 65535), DEFAULT(0), BLOCK_SIZE(1)); +static Sys_var_ulong Sys_admin_port( + "admin_port", + "Port number to use for connections from admin.", + READ_ONLY GLOBAL_VAR(mysqld_admin_port), CMD_LINE(REQUIRED_ARG), + VALID_RANGE(0, UINT_MAX32), DEFAULT(0), BLOCK_SIZE(1)); + static Sys_var_ulong Sys_preload_buff_size( "preload_buffer_size", "The size of the buffer that is allocated when preloading indexes",