From 6c939906dd454e78027c4c01ff67bd085d86f510 Mon Sep 17 00:00:00 2001 From: Mohammad Date: Sat, 20 Feb 2016 18:01:30 +0200 Subject: [PATCH 1/9] #87 password single quote to double quote --- mysqltuner.pl | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index da747ee..fe3eb4b 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -507,7 +507,7 @@ sub mysql_setup { # Did we already get a username and password passed on the command line? if ( $opt{user} ne 0 and $opt{pass} ne 0 ) { - $mysqllogin = "-u $opt{user} -p'$opt{pass}'" . $remotestring; + $mysqllogin = "-u $opt{user} -p\"$opt{pass}\"" . $remotestring; my $loginstatus = `$mysqladmincmd ping $mysqllogin 2>&1`; if ( $loginstatus =~ /mysqld is alive/ ) { goodprint @@ -649,16 +649,17 @@ sub mysql_setup { $mysqllogin = "-u $name"; if ( length($password) > 0 ) { - $mysqllogin .= " -p'$password'"; + $mysqllogin .= " -p\"$password\""; } $mysqllogin .= $remotestring; my $loginstatus = `$mysqladmincmd ping $mysqllogin 2>&1`; + debugprint "Login status command: $mysqladmincmd ping $mysqllogin 2>&1"; if ( $loginstatus =~ /mysqld is alive/ ) { print STDERR ""; if ( !length($password) ) { # Did this go well because of a .my.cnf file or is there no password set? - my $userpath = `ls -d ~`; + my $userpath = `printenv HOME`; chomp($userpath); unless ( -e "$userpath/.my.cnf" ) { badprint @@ -668,7 +669,7 @@ sub mysql_setup { return 1; } else { - badprint " Attempted to use login credentials, but they were invalid."; + badprint "Attempted to use login credentials, but they were invalid."; exit 1; } exit 1; From 15333e4a580db551273542714e4f488ee1ad349f Mon Sep 17 00:00:00 2001 From: Mohammad Date: Sat, 20 Feb 2016 18:05:33 +0200 Subject: [PATCH 2/9] #87 change password single quote to double quote and small fixes --- mysqltuner.pl | 373 +++++++++++++++++++++++++++----------------------- 1 file changed, 199 insertions(+), 174 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index fe3eb4b..d2241c5 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -90,8 +90,9 @@ GetOptions( 'host=s', 'socket=s', 'port=i', 'user=s', 'pass=s', 'skipsize', 'checkversion', 'mysqladmin=s', 'mysqlcmd=s', 'help', 'buffers', 'skippassword', - 'passwordfile=s', 'outputfile=s', 'silent', 'dbstat', 'json', - 'idxstat', 'noask', 'template=s', 'reportfile=s', 'cvefile=s', + 'passwordfile=s', 'outputfile=s', 'silent', 'dbstat', + 'json', 'idxstat', 'noask', 'template=s', + 'reportfile=s', 'cvefile=s', ); if ( defined $opt{'help'} && $opt{'help'} == 1 ) { usage(); } @@ -156,8 +157,8 @@ $basic_password_files = "/usr/share/mysqltuner/basic_passwords.txt" # for RPM distributions $opt{cvefile} = "/usr/share/mysqltuner/vulnerabilities.csv" - unless ( defined $opt{cvefile} and -f "$opt{cvefile}"); -$opt{cvefile} ='' unless -f "$opt{cvefile}"; + unless ( defined $opt{cvefile} and -f "$opt{cvefile}" ); +$opt{cvefile} = '' unless -f "$opt{cvefile}"; # my $outputfile = undef; @@ -187,6 +188,7 @@ sub goodprint { prettyprint $good. " " . $_[0] unless ( $opt{nogood} == 1 ); } sub infoprint { prettyprint $info. " " . $_[0] unless ( $opt{noinfo} == 1 ); } sub badprint { prettyprint $bad. " " . $_[0] unless ( $opt{nobad} == 1 ); } sub debugprint { prettyprint $deb. " " . $_[0] unless ( $opt{debug} == 0 ); } + sub redwrap { return ( $opt{nocolor} == 0 ) ? "\e[0;31m" . $_[0] . "\e[0m" : $_[0]; } @@ -282,6 +284,7 @@ sub pretty_uptime { my ( $physical_memory, $swap_memory, $duflags ); sub os_setup { + sub memerror { badprint "Unable to determine total memory/swap; use '--forcemem' and '--forceswap'"; @@ -298,8 +301,7 @@ sub os_setup { } else { $swap_memory = 0; - badprint - "Assuming 0 MB of swap space (use --forceswap to specify)"; + badprint "Assuming 0 MB of swap space (use --forceswap to specify)"; } } else { @@ -352,12 +354,12 @@ sub os_setup { chomp($swap_memory); $swap_memory = $swap_memory * 1024 * 1024; } - elsif( $os =~ /windows/i ) { + elsif ( $os =~ /windows/i ) { $physical_memory = - `wmic ComputerSystem get TotalPhysicalMemory | perl -ne "chomp; print if /[0-9]+/;"` +`wmic ComputerSystem get TotalPhysicalMemory | perl -ne "chomp; print if /[0-9]+/;"` or memerror; - $swap_memory = - `wmic OS get FreeVirtualMemory | perl -ne "chomp; print if /[0-9]+/;"` + $swap_memory = +`wmic OS get FreeVirtualMemory | perl -ne "chomp; print if /[0-9]+/;"` or memerror; } } @@ -376,66 +378,73 @@ sub os_setup { # Checks for updates to MySQLTuner sub validate_tuner_version { - if ($opt{checkversion} eq 0) { - infoprint "Skipped version check for MySQLTuner script"; - return; - } + if ( $opt{checkversion} eq 0 ) { + infoprint "Skipped version check for MySQLTuner script"; + return; + } - my $update; - my $url = "https://raw.githubusercontent.com/major/MySQLTuner-perl/master/mysqltuner.pl"; - my $httpcli=`which curl`; - chomp($httpcli); - if ( 1 != 1 and defined($httpcli) and -e "$httpcli" ) { - debugprint "$httpcli is available."; + my $update; + my $url = +"https://raw.githubusercontent.com/major/MySQLTuner-perl/master/mysqltuner.pl"; + my $httpcli = `which curl`; + chomp($httpcli); + if ( 1 != 1 and defined($httpcli) and -e "$httpcli" ) { + debugprint "$httpcli is available."; - debugprint "$httpcli --connect-timeout 5 -silent '$url' 2>/dev/null | grep 'my \$tunerversion'| cut -d\\\" -f2"; - $update = `$httpcli --connect-timeout 5 -silent '$url' 2>/dev/null | grep 'my \$tunerversion'| cut -d\\\" -f2`; - chomp($update); - debugprint "VERSION: $update"; + debugprint +"$httpcli --connect-timeout 5 -silent '$url' 2>/dev/null | grep 'my \$tunerversion'| cut -d\\\" -f2"; + $update = +`$httpcli --connect-timeout 5 -silent '$url' 2>/dev/null | grep 'my \$tunerversion'| cut -d\\\" -f2`; + chomp($update); + debugprint "VERSION: $update"; + compare_tuner_version($update); + return; + } - compare_tuner_version($update); - return; - } + $httpcli = `which wget`; + chomp($httpcli); + if ( defined($httpcli) and -e "$httpcli" ) { + debugprint "$httpcli is available."; - - $httpcli=`which wget`; - chomp($httpcli); - if ( defined($httpcli) and -e "$httpcli" ) { - debugprint "$httpcli is available."; - - debugprint "$httpcli -e timestamping=off -T 5 -O - '$url' 2>$devnull| grep 'my \$tunerversion'| cut -d\\\" -f2"; - $update = `$httpcli -e timestamping=off -T 5 -O - '$url' 2>$devnull| grep 'my \$tunerversion'| cut -d\\\" -f2`; - chomp($update); - compare_tuner_version($update); - return; - } - debugprint "curl and wget are not available."; - infoprint "Unable to check for the latest MySQLTuner version"; + debugprint +"$httpcli -e timestamping=off -T 5 -O - '$url' 2>$devnull| grep 'my \$tunerversion'| cut -d\\\" -f2"; + $update = +`$httpcli -e timestamping=off -T 5 -O - '$url' 2>$devnull| grep 'my \$tunerversion'| cut -d\\\" -f2`; + chomp($update); + compare_tuner_version($update); + return; + } + debugprint "curl and wget are not available."; + infoprint "Unable to check for the latest MySQLTuner version"; } sub compare_tuner_version { - my $remoteversion=shift; - debugprint "Remote data: $remoteversion"; - #exit 0; - if ($remoteversion ne $tunerversion) { - badprint "There is a new version of MySQLTuner available ($remoteversion)"; - return; - } - goodprint "You have the latest version of MySQLTuner($tunerversion)"; - return; + my $remoteversion = shift; + debugprint "Remote data: $remoteversion"; + + #exit 0; + if ( $remoteversion ne $tunerversion ) { + badprint + "There is a new version of MySQLTuner available ($remoteversion)"; + return; + } + goodprint "You have the latest version of MySQLTuner($tunerversion)"; + return; } # Checks to see if a MySQL login is possible my ( $mysqllogin, $doremote, $remotestring, $mysqlcmd, $mysqladmincmd ); my $osname = $^O; -if( $osname eq 'MSWin32' ) { - eval { require Win32; } or last; - $osname = Win32::GetOSName(); - infoprint "* Windows OS($osname) is not fully supported.\n"; - #exit 1; +if ( $osname eq 'MSWin32' ) { + eval { require Win32; } or last; + $osname = Win32::GetOSName(); + infoprint "* Windows OS($osname) is not fully supported.\n"; + + #exit 1; } + sub mysql_setup { $doremote = 0; $remotestring = ''; @@ -452,8 +461,7 @@ sub mysql_setup { exit 1; } elsif ( !-e $mysqladmincmd ) { - badprint - "Couldn't find mysqladmin in your \$PATH. Is MySQL installed?"; + badprint "Couldn't find mysqladmin in your \$PATH. Is MySQL installed?"; exit 1; } if ( $opt{mysqlcmd} ) { @@ -473,11 +481,12 @@ sub mysql_setup { exit 1; } $mysqlcmd =~ s/\n$//g; - my $mysqlclidefaults=`$mysqlcmd --print-defaults`; + my $mysqlclidefaults = `$mysqlcmd --print-defaults`; debugprint "MySQL Client: $mysqlclidefaults"; - if ( $mysqlclidefaults=~/auto-vertical-output/ ) { - badprint "Avoid auto-vertical-output in configuration file(s) for MySQL like"; - exit 1; + if ( $mysqlclidefaults =~ /auto-vertical-output/ ) { + badprint + "Avoid auto-vertical-output in configuration file(s) for MySQL like"; + exit 1; } debugprint "MySQL Client: $mysqlcmd"; @@ -492,16 +501,18 @@ sub mysql_setup { chomp( $opt{host} ); $opt{port} = ( $opt{port} eq 0 ) ? 3306 : $opt{port}; - # If we're doing a remote connection, but forcemem wasn't specified, we need to exit - if ( $opt{'forcemem'} eq 0 && ($opt{host} ne "127.0.0.1") && ($opt{host} ne "localhost")) { - badprint - "The --forcemem option is required for remote connections"; +# If we're doing a remote connection, but forcemem wasn't specified, we need to exit + if ( $opt{'forcemem'} eq 0 + && ( $opt{host} ne "127.0.0.1" ) + && ( $opt{host} ne "localhost" ) ) + { + badprint "The --forcemem option is required for remote connections"; exit 1; } infoprint "Performing tests on $opt{host}:$opt{port}"; $remotestring = " -h $opt{host} -P $opt{port}"; - if (($opt{host} ne "127.0.0.1") && ($opt{host} ne "localhost")) { - $doremote = 1; + if ( ( $opt{host} ne "127.0.0.1" ) && ( $opt{host} ne "localhost" ) ) { + $doremote = 1; } } @@ -510,8 +521,7 @@ sub mysql_setup { $mysqllogin = "-u $opt{user} -p\"$opt{pass}\"" . $remotestring; my $loginstatus = `$mysqladmincmd ping $mysqllogin 2>&1`; if ( $loginstatus =~ /mysqld is alive/ ) { - goodprint - "Logged in using credentials passed on the command line"; + goodprint "Logged in using credentials passed on the command line"; return 1; } else { @@ -536,8 +546,7 @@ sub mysql_setup { $mysqllogin = "-u $mysql_login -p$mysql_pass"; my $loginstatus = `mysqladmin $mysqllogin ping 2>&1`; if ( $loginstatus =~ /mysqld is alive/ ) { - goodprint - "Logged in using credentials from mysql-quickbackup."; + goodprint "Logged in using credentials from mysql-quickbackup."; return 1; } else { @@ -554,7 +563,7 @@ sub mysql_setup { my $loginstatus = `$mysqladmincmd ping $mysqllogin 2>&1`; unless ( $loginstatus =~ /mysqld is alive/ ) { badprint -"Attempted to use login credentials from Plesk, but they failed."; + "Attempted to use login credentials from Plesk, but they failed."; exit 1; } } @@ -606,7 +615,7 @@ sub mysql_setup { # Login went just fine $mysqllogin = " $remotestring "; - # Did this go well because of a .my.cnf file or is there no password set? + # Did this go well because of a .my.cnf file or is there no password set? my $userpath = `printenv HOME`; if ( length($userpath) > 0 ) { chomp($userpath); @@ -619,27 +628,29 @@ sub mysql_setup { return 1; } else { - if ( $opt{'noask'}==1 ) { - badprint "Attempted to use login credentials, but they were invalid"; + if ( $opt{'noask'} == 1 ) { + badprint + "Attempted to use login credentials, but they were invalid"; exit 1; } - my ($name, $password); + my ( $name, $password ); + # If --user is defined no need to ask for username - if( $opt{user} ne 0 ) - { + if ( $opt{user} ne 0 ) { $name = $opt{user}; } - else{ + else { print STDERR "Please enter your MySQL administrative login: "; $name = ; } + # If --pass is defined no need to ask for password - if( $opt{pass} ne 0 ) - { + if ( $opt{pass} ne 0 ) { $password = $opt{pass}; } - else{ - print STDERR "Please enter your MySQL administrative password: "; + else { + print STDERR + "Please enter your MySQL administrative password: "; system("stty -echo >$devnull 2>&1"); $password = ; system("stty echo >$devnull 2>&1"); @@ -653,7 +664,8 @@ sub mysql_setup { } $mysqllogin .= $remotestring; my $loginstatus = `$mysqladmincmd ping $mysqllogin 2>&1`; - debugprint "Login status command: $mysqladmincmd ping $mysqllogin 2>&1"; + debugprint + "Login status command: $mysqladmincmd ping $mysqllogin 2>&1"; if ( $loginstatus =~ /mysqld is alive/ ) { print STDERR ""; if ( !length($password) ) { @@ -669,7 +681,8 @@ sub mysql_setup { return 1; } else { - badprint "Attempted to use login credentials, but they were invalid."; + badprint + "Attempted to use login credentials, but they were invalid."; exit 1; } exit 1; @@ -798,33 +811,34 @@ sub get_basic_passwords { sub cve_recommendations { prettyprint "\n-------- CVE Security Recommendations ---------------------------------------"; - unless ( defined($opt{cvefile}) && -f "$opt{cvefile}" ) { + unless ( defined( $opt{cvefile} ) && -f "$opt{cvefile}" ) { infoprint "Skipped due to --cvefile option undefined"; return; } - #prettyprint "Look for related CVE for $myvar{'version'} or lower in $opt{cvefile}"; - my $cvefound=0; +#prettyprint "Look for related CVE for $myvar{'version'} or lower in $opt{cvefile}"; + my $cvefound = 0; open( FH, "<$opt{cvefile}" ) or die "Can't open $opt{cvefile} for read: $!"; - while (my $cveline = ) - { - my @cve=split (';', $cveline); - if (mysql_micro_version_le ($cve[1], $cve[2], $cve[3])) { - badprint "$cve[4] : $cve[5]"; - $cvefound++; - } - + while ( my $cveline = ) { + my @cve = split( ';', $cveline ); + if ( mysql_micro_version_le( $cve[1], $cve[2], $cve[3] ) ) { + badprint "$cve[4] : $cve[5]"; + $cvefound++; + } + } close FH or die "Cannot close $opt{cvefile}: $!"; - if ($cvefound==0) { - goodprint "NO SECURITY CVE FOUND FOR YOUR VERSION"; - return; - } + if ( $cvefound == 0 ) { + goodprint "NO SECURITY CVE FOUND FOR YOUR VERSION"; + return; + } badprint $cvefound . " CVE(s) found for your MySQL release."; - push( @generalrec, $cvefound . " CVE(s) found for your MySQL release. Consider upgrading your version !" ); + push( @generalrec, + $cvefound + . " CVE(s) found for your MySQL release. Consider upgrading your version !" + ); } - sub security_recommendations { prettyprint "\n-------- Security Recommendations -------------------------------------------"; @@ -833,11 +847,12 @@ sub security_recommendations { return; } - my $PASS_COLUMN_NAME='password'; - if ($myvar{'version'} =~ /5.7/) { - $PASS_COLUMN_NAME='authentication_string'; + my $PASS_COLUMN_NAME = 'password'; + if ( $myvar{'version'} =~ /5.7/ ) { + $PASS_COLUMN_NAME = 'authentication_string'; } debugprint "Password column = $PASS_COLUMN_NAME"; + #exit(0); # Looking for Anonymous users my @mysqlstatlist = select_array @@ -973,10 +988,10 @@ sub get_replication_status { and ( $io_running !~ /yes/i or $sql_running !~ /yes/i ) ) { badprint -"This replication slave is not running but seems to be configurated."; + "This replication slave is not running but seems to be configurated."; } if ( defined($io_running) - && $io_running =~ /yes/i + && $io_running =~ /yes/i && $sql_running =~ /yes/i ) { if ( $myvar{'read_only'} eq 'OFF' ) { @@ -1010,10 +1025,13 @@ sub validate_mysql_version { . $myvar{'version'} . " is EOL software! Upgrade soon!"; } - elsif ( ( mysql_version_ge(6) and mysql_version_le(9) ) or mysql_version_ge(12) ) { + elsif ( ( mysql_version_ge(6) and mysql_version_le(9) ) + or mysql_version_ge(12) ) + { badprint "Currently running unsupported MySQL version " . $myvar{'version'} . ""; - } else { + } + else { goodprint "Currently running supported MySQL version " . $myvar{'version'} . ""; } @@ -1025,8 +1043,7 @@ sub mysql_version_ge { $min ||= 0; $mic ||= 0; return $mysqlvermajor > $maj - || $mysqlvermajor == $maj - && ( $mysqlverminor > $min + || $mysqlvermajor == $maj && ( $mysqlverminor > $min || $mysqlverminor == $min && $mysqlvermicro >= $mic ); } @@ -1036,8 +1053,7 @@ sub mysql_version_le { $min ||= 0; $mic ||= 0; return $mysqlvermajor < $maj - || $mysqlvermajor == $maj - && ( $mysqlverminor < $min + || $mysqlvermajor == $maj && ( $mysqlverminor < $min || $mysqlverminor == $min && $mysqlvermicro <= $mic ); } @@ -1046,7 +1062,7 @@ sub mysql_micro_version_le { my ( $maj, $min, $mic ) = @_; return $mysqlvermajor == $maj && ( $mysqlverminor == $min - && $mysqlvermicro <= $mic ); + && $mysqlvermicro <= $mic ); } # Checks for 32-bit boxes with more than 2GB of RAM @@ -1093,8 +1109,7 @@ sub check_architecture { "Switch to 64-bit OS - MySQL cannot currently use all of your RAM"; } else { - goodprint - "Operating on 32-bit architecture with less than 2GB RAM"; + goodprint "Operating on 32-bit architecture with less than 2GB RAM"; } } $result{'OS'}{'Architecture'} = "$arch bits"; @@ -1160,7 +1175,7 @@ sub check_storage_engines { : redwrap "-NDBCluster "; } - my @dblist = grep {$_ ne 'lost+found' } select_array "SHOW DATABASES"; + my @dblist = grep { $_ ne 'lost+found' } select_array "SHOW DATABASES"; $result{'Databases'}{'List'} = [@dblist]; infoprint "Status: $engines"; @@ -1196,7 +1211,7 @@ sub check_storage_engines { # MySQL < 5 servers take a lot of work to get table sizes my @tblist; - # Now we build a database list, and loop through it to get storage engine stats for tables +# Now we build a database list, and loop through it to get storage engine stats for tables foreach my $db (@dblist) { chomp($db); if ( $db eq "information_schema" @@ -1220,11 +1235,11 @@ sub check_storage_engines { # Parse through the table list to generate storage engine counts/statistics $fragtables = 0; foreach my $tbl (@tblist) { - debugprint "Data dump ". Dumper (@$tbl); + debugprint "Data dump " . Dumper(@$tbl); my ( $engine, $size, $datafree ) = @$tbl; next if $engine eq 'NULL'; - $size=0 if $size eq 'NULL'; - $datafree=0 if $datafree eq 'NULL'; + $size = 0 if $size eq 'NULL'; + $datafree = 0 if $datafree eq 'NULL'; if ( defined $enginestats{$engine} ) { $enginestats{$engine} += $size; $enginecount{$engine} += 1; @@ -1443,7 +1458,7 @@ sub calculations { $myvar{'key_cache_block_size'} ) / $myvar{'key_buffer_size'} ) - ) * 100 + ) * 100 ); } else { @@ -1536,14 +1551,14 @@ sub calculations { ( $mystat{'Qcache_hits'} / ( $mystat{'Com_select'} + $mystat{'Qcache_hits'} ) - ) * 100 + ) * 100 ); if ( $myvar{'query_cache_size'} ) { $mycalc{'pct_query_cache_used'} = sprintf( "%.1f", 100 - ( $mystat{'Qcache_free_memory'} / $myvar{'query_cache_size'} - ) * 100 + ) * 100 ); } if ( $mystat{'Qcache_lowmem_prunes'} == 0 ) { @@ -1761,8 +1776,10 @@ sub mysql_stats { . ( $myvar{'query_cache_type'} eq 0 | $myvar{'query_cache_type'} eq 'OFF' ? "DISABLED" - : ( $myvar{'query_cache_type'} eq 1 ? "ALL REQUESTS" - : "ON DEMAND" ) + : ( + $myvar{'query_cache_type'} eq 1 ? "ALL REQUESTS" + : "ON DEMAND" + ) ) . ""; infoprint " +-- Query Cache Size: " . hr_bytes( $myvar{'query_cache_size'} ) . ""; @@ -1791,7 +1808,7 @@ sub mysql_stats { && $mycalc{'max_used_memory'} > 2 * 1024 * 1024 * 1024 ) { badprint -"Allocating > 2GB RAM on 32-bit systems can cause system instability"; + "Allocating > 2GB RAM on 32-bit systems can cause system instability"; badprint "Maximum reached memory usage: " . hr_bytes( $mycalc{'max_used_memory'} ) . " ($mycalc{'pct_max_used_memory'}% of installed RAM)"; @@ -2007,7 +2024,8 @@ sub mysql_stats { "When making adjustments, make tmp_table_size/max_heap_table_size equal" ); push( @generalrec, - "Reduce your SELECT DISTINCT queries which have no LIMIT clause" ); + "Reduce your SELECT DISTINCT queries which have no LIMIT clause" + ); } elsif ($mycalc{'pct_temp_disk'} > 25 && $mycalc{'max_tmp_table_size'} >= 256 * 1024 * 1024 ) @@ -2195,6 +2213,7 @@ sub mysql_stats { sub mysql_myisam { prettyprint "\n-------- MyISAM Metrics ------------------------------------------------------"; + # Key buffer usage if ( defined( $mycalc{'pct_key_buffer_used'} ) ) { if ( $mycalc{'pct_key_buffer_used'} < 90 ) { @@ -2403,7 +2422,6 @@ sub mariadb_ariadb { } } - # Recommendations for TokuDB sub mariadb_tokudb { prettyprint @@ -2436,6 +2454,7 @@ sub mariadb_galera { return; } infoprint "Galera is enabled."; + # All is to done here } @@ -2740,7 +2759,7 @@ sub mysql_indexes { "\n-------- Indexes Metrics -----------------------------------------------------"; unless ( mysql_version_ge( 5, 5 ) ) { infoprint -"Skip Index metrics from information schema missing in this version"; + "Skip Index metrics from information schema missing in this version"; return; } my $selIdxReq = <<'ENDSQL'; @@ -2844,8 +2863,7 @@ sub make_recommendations { foreach (@adjvars) { prettyprint " " . $_ . ""; } } if ( @generalrec == 0 && @adjvars == 0 ) { - prettyprint - "No additional performance recommendations are available."; + prettyprint "No additional performance recommendations are available."; } } @@ -2861,18 +2879,19 @@ sub headerprint { } sub string2file { - my $filename=shift; - my $content=shift; - open my $fh, q(>), $filename - or die "Unable to open $filename in write mode. Please check permissions for this file or directory"; - print $fh $content if defined($content); - close $fh; - debugprint $content if ($opt{'debug'}); + my $filename = shift; + my $content = shift; + open my $fh, q(>), $filename + or die +"Unable to open $filename in write mode. Please check permissions for this file or directory"; + print $fh $content if defined($content); + close $fh; + debugprint $content if ( $opt{'debug'} ); } sub file2array { my $filename = shift; - debugprint "* reading $filename" if ($opt{'debug'}); + debugprint "* reading $filename" if ( $opt{'debug'} ); my $fh; open( $fh, q(<), "$filename" ) or die "Couldn't open $filename for reading: $!\n"; @@ -2882,15 +2901,16 @@ sub file2array { } sub file2string { - return join ( '', file2array(@_) ); + return join( '', file2array(@_) ); } my $templateModel; -if ($opt{'template'} ne 0 ) { - $templateModel=file2string ($opt{'template'}); -}else { - # DEFAULT REPORT TEMPLATE - $templateModel=<<'END_TEMPLATE'; +if ( $opt{'template'} ne 0 ) { + $templateModel = file2string( $opt{'template'} ); +} +else { + # DEFAULT REPORT TEMPLATE + $templateModel = <<'END_TEMPLATE'; @@ -2908,48 +2928,53 @@ if ($opt{'template'} ne 0 ) { END_TEMPLATE } + sub dump_result { - if ($opt{'debug'}) { - debugprint Dumper( \%result ); + if ( $opt{'debug'} ) { + debugprint Dumper( \%result ); } debugprint "HTML REPORT: $opt{'reportfile'}"; - if ($opt{'reportfile'} ne 0 ) { - eval "{ use Text::Template }"; - if ($@) { - badprint "Text::Template Module is needed."; - exit 1; - } + if ( $opt{'reportfile'} ne 0 ) { + eval "{ use Text::Template }"; + if ($@) { + badprint "Text::Template Module is needed."; + exit 1; + } - my $vars= {'data' => Dumper( \%result ) }; + my $vars = { 'data' => Dumper( \%result ) }; - my $template; - { - no warnings 'once'; - $template = Text::Template->new(TYPE => 'STRING', PREPEND => q{;}, SOURCE => $templateModel) - or die "Couldn't construct template: $Text::Template::ERROR"; - } - open my $fh, q(>), $opt{'reportfile'} - or die "Unable to open $opt{'reportfile'} in write mode. please check permissions for this file or directory"; - $template->fill_in(HASH =>$vars, OUTPUT=>$fh ); - close $fh; + my $template; + { + no warnings 'once'; + $template = Text::Template->new( + TYPE => 'STRING', + PREPEND => q{;}, + SOURCE => $templateModel + ) or die "Couldn't construct template: $Text::Template::ERROR"; + } + open my $fh, q(>), $opt{'reportfile'} + or die +"Unable to open $opt{'reportfile'} in write mode. please check permissions for this file or directory"; + $template->fill_in( HASH => $vars, OUTPUT => $fh ); + close $fh; } - if ($opt{'json'} ne 0 ) { - eval "{ use JSON }"; - if ($@) { - badprint "JSON Module is needed."; - exit 1; - } - my $json = JSON->new->allow_nonref; - print JSON->new->utf8(1)->pretty(1)->encode(%result); + if ( $opt{'json'} ne 0 ) { + eval "{ use JSON }"; + if ($@) { + badprint "JSON Module is needed."; + exit 1; + } + my $json = JSON->new->allow_nonref; + print JSON->new->utf8(1)->pretty(1)->encode(%result); } } # --------------------------------------------------------------------------- # BEGIN 'MAIN' # --------------------------------------------------------------------------- -headerprint # Header Print +headerprint; # Header Print mysql_setup; # Gotta login first validate_tuner_version; # Check last version os_setup; # Set up some OS variables From ffbab547a115a680bd16e745b9eb22057f7f56bb Mon Sep 17 00:00:00 2001 From: Mohammad Date: Sat, 20 Feb 2016 18:07:19 +0200 Subject: [PATCH 3/9] #87 change password single quote to double quote and small fixes --- mysqltuner.pl | 371 +++++++++++++++++++++++--------------------------- 1 file changed, 173 insertions(+), 198 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index d2241c5..557c68e 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -90,9 +90,8 @@ GetOptions( 'host=s', 'socket=s', 'port=i', 'user=s', 'pass=s', 'skipsize', 'checkversion', 'mysqladmin=s', 'mysqlcmd=s', 'help', 'buffers', 'skippassword', - 'passwordfile=s', 'outputfile=s', 'silent', 'dbstat', - 'json', 'idxstat', 'noask', 'template=s', - 'reportfile=s', 'cvefile=s', + 'passwordfile=s', 'outputfile=s', 'silent', 'dbstat', 'json', + 'idxstat', 'noask', 'template=s', 'reportfile=s', 'cvefile=s', ); if ( defined $opt{'help'} && $opt{'help'} == 1 ) { usage(); } @@ -157,8 +156,8 @@ $basic_password_files = "/usr/share/mysqltuner/basic_passwords.txt" # for RPM distributions $opt{cvefile} = "/usr/share/mysqltuner/vulnerabilities.csv" - unless ( defined $opt{cvefile} and -f "$opt{cvefile}" ); -$opt{cvefile} = '' unless -f "$opt{cvefile}"; + unless ( defined $opt{cvefile} and -f "$opt{cvefile}"); +$opt{cvefile} ='' unless -f "$opt{cvefile}"; # my $outputfile = undef; @@ -188,7 +187,6 @@ sub goodprint { prettyprint $good. " " . $_[0] unless ( $opt{nogood} == 1 ); } sub infoprint { prettyprint $info. " " . $_[0] unless ( $opt{noinfo} == 1 ); } sub badprint { prettyprint $bad. " " . $_[0] unless ( $opt{nobad} == 1 ); } sub debugprint { prettyprint $deb. " " . $_[0] unless ( $opt{debug} == 0 ); } - sub redwrap { return ( $opt{nocolor} == 0 ) ? "\e[0;31m" . $_[0] . "\e[0m" : $_[0]; } @@ -284,7 +282,6 @@ sub pretty_uptime { my ( $physical_memory, $swap_memory, $duflags ); sub os_setup { - sub memerror { badprint "Unable to determine total memory/swap; use '--forcemem' and '--forceswap'"; @@ -301,7 +298,8 @@ sub os_setup { } else { $swap_memory = 0; - badprint "Assuming 0 MB of swap space (use --forceswap to specify)"; + badprint + "Assuming 0 MB of swap space (use --forceswap to specify)"; } } else { @@ -354,12 +352,12 @@ sub os_setup { chomp($swap_memory); $swap_memory = $swap_memory * 1024 * 1024; } - elsif ( $os =~ /windows/i ) { + elsif( $os =~ /windows/i ) { $physical_memory = -`wmic ComputerSystem get TotalPhysicalMemory | perl -ne "chomp; print if /[0-9]+/;"` + `wmic ComputerSystem get TotalPhysicalMemory | perl -ne "chomp; print if /[0-9]+/;"` or memerror; - $swap_memory = -`wmic OS get FreeVirtualMemory | perl -ne "chomp; print if /[0-9]+/;"` + $swap_memory = + `wmic OS get FreeVirtualMemory | perl -ne "chomp; print if /[0-9]+/;"` or memerror; } } @@ -378,73 +376,66 @@ sub os_setup { # Checks for updates to MySQLTuner sub validate_tuner_version { - if ( $opt{checkversion} eq 0 ) { - infoprint "Skipped version check for MySQLTuner script"; - return; - } + if ($opt{checkversion} eq 0) { + infoprint "Skipped version check for MySQLTuner script"; + return; + } - my $update; - my $url = -"https://raw.githubusercontent.com/major/MySQLTuner-perl/master/mysqltuner.pl"; - my $httpcli = `which curl`; - chomp($httpcli); - if ( 1 != 1 and defined($httpcli) and -e "$httpcli" ) { - debugprint "$httpcli is available."; + my $update; + my $url = "https://raw.githubusercontent.com/major/MySQLTuner-perl/master/mysqltuner.pl"; + my $httpcli=`which curl`; + chomp($httpcli); + if ( 1 != 1 and defined($httpcli) and -e "$httpcli" ) { + debugprint "$httpcli is available."; - debugprint -"$httpcli --connect-timeout 5 -silent '$url' 2>/dev/null | grep 'my \$tunerversion'| cut -d\\\" -f2"; - $update = -`$httpcli --connect-timeout 5 -silent '$url' 2>/dev/null | grep 'my \$tunerversion'| cut -d\\\" -f2`; - chomp($update); - debugprint "VERSION: $update"; + debugprint "$httpcli --connect-timeout 5 -silent '$url' 2>/dev/null | grep 'my \$tunerversion'| cut -d\\\" -f2"; + $update = `$httpcli --connect-timeout 5 -silent '$url' 2>/dev/null | grep 'my \$tunerversion'| cut -d\\\" -f2`; + chomp($update); + debugprint "VERSION: $update"; - compare_tuner_version($update); - return; - } - $httpcli = `which wget`; - chomp($httpcli); - if ( defined($httpcli) and -e "$httpcli" ) { - debugprint "$httpcli is available."; + compare_tuner_version($update); + return; + } - debugprint -"$httpcli -e timestamping=off -T 5 -O - '$url' 2>$devnull| grep 'my \$tunerversion'| cut -d\\\" -f2"; - $update = -`$httpcli -e timestamping=off -T 5 -O - '$url' 2>$devnull| grep 'my \$tunerversion'| cut -d\\\" -f2`; - chomp($update); - compare_tuner_version($update); - return; - } - debugprint "curl and wget are not available."; - infoprint "Unable to check for the latest MySQLTuner version"; + + $httpcli=`which wget`; + chomp($httpcli); + if ( defined($httpcli) and -e "$httpcli" ) { + debugprint "$httpcli is available."; + + debugprint "$httpcli -e timestamping=off -T 5 -O - '$url' 2>$devnull| grep 'my \$tunerversion'| cut -d\\\" -f2"; + $update = `$httpcli -e timestamping=off -T 5 -O - '$url' 2>$devnull| grep 'my \$tunerversion'| cut -d\\\" -f2`; + chomp($update); + compare_tuner_version($update); + return; + } + debugprint "curl and wget are not available."; + infoprint "Unable to check for the latest MySQLTuner version"; } sub compare_tuner_version { - my $remoteversion = shift; - debugprint "Remote data: $remoteversion"; - - #exit 0; - if ( $remoteversion ne $tunerversion ) { - badprint - "There is a new version of MySQLTuner available ($remoteversion)"; - return; - } - goodprint "You have the latest version of MySQLTuner($tunerversion)"; - return; + my $remoteversion=shift; + debugprint "Remote data: $remoteversion"; + #exit 0; + if ($remoteversion ne $tunerversion) { + badprint "There is a new version of MySQLTuner available ($remoteversion)"; + return; + } + goodprint "You have the latest version of MySQLTuner($tunerversion)"; + return; } # Checks to see if a MySQL login is possible my ( $mysqllogin, $doremote, $remotestring, $mysqlcmd, $mysqladmincmd ); my $osname = $^O; -if ( $osname eq 'MSWin32' ) { - eval { require Win32; } or last; - $osname = Win32::GetOSName(); - infoprint "* Windows OS($osname) is not fully supported.\n"; - - #exit 1; +if( $osname eq 'MSWin32' ) { + eval { require Win32; } or last; + $osname = Win32::GetOSName(); + infoprint "* Windows OS($osname) is not fully supported.\n"; + #exit 1; } - sub mysql_setup { $doremote = 0; $remotestring = ''; @@ -461,7 +452,8 @@ sub mysql_setup { exit 1; } elsif ( !-e $mysqladmincmd ) { - badprint "Couldn't find mysqladmin in your \$PATH. Is MySQL installed?"; + badprint + "Couldn't find mysqladmin in your \$PATH. Is MySQL installed?"; exit 1; } if ( $opt{mysqlcmd} ) { @@ -481,12 +473,11 @@ sub mysql_setup { exit 1; } $mysqlcmd =~ s/\n$//g; - my $mysqlclidefaults = `$mysqlcmd --print-defaults`; + my $mysqlclidefaults=`$mysqlcmd --print-defaults`; debugprint "MySQL Client: $mysqlclidefaults"; - if ( $mysqlclidefaults =~ /auto-vertical-output/ ) { - badprint - "Avoid auto-vertical-output in configuration file(s) for MySQL like"; - exit 1; + if ( $mysqlclidefaults=~/auto-vertical-output/ ) { + badprint "Avoid auto-vertical-output in configuration file(s) for MySQL like"; + exit 1; } debugprint "MySQL Client: $mysqlcmd"; @@ -501,18 +492,16 @@ sub mysql_setup { chomp( $opt{host} ); $opt{port} = ( $opt{port} eq 0 ) ? 3306 : $opt{port}; -# If we're doing a remote connection, but forcemem wasn't specified, we need to exit - if ( $opt{'forcemem'} eq 0 - && ( $opt{host} ne "127.0.0.1" ) - && ( $opt{host} ne "localhost" ) ) - { - badprint "The --forcemem option is required for remote connections"; + # If we're doing a remote connection, but forcemem wasn't specified, we need to exit + if ( $opt{'forcemem'} eq 0 && ($opt{host} ne "127.0.0.1") && ($opt{host} ne "localhost")) { + badprint + "The --forcemem option is required for remote connections"; exit 1; } infoprint "Performing tests on $opt{host}:$opt{port}"; $remotestring = " -h $opt{host} -P $opt{port}"; - if ( ( $opt{host} ne "127.0.0.1" ) && ( $opt{host} ne "localhost" ) ) { - $doremote = 1; + if (($opt{host} ne "127.0.0.1") && ($opt{host} ne "localhost")) { + $doremote = 1; } } @@ -521,7 +510,8 @@ sub mysql_setup { $mysqllogin = "-u $opt{user} -p\"$opt{pass}\"" . $remotestring; my $loginstatus = `$mysqladmincmd ping $mysqllogin 2>&1`; if ( $loginstatus =~ /mysqld is alive/ ) { - goodprint "Logged in using credentials passed on the command line"; + goodprint + "Logged in using credentials passed on the command line"; return 1; } else { @@ -546,7 +536,8 @@ sub mysql_setup { $mysqllogin = "-u $mysql_login -p$mysql_pass"; my $loginstatus = `mysqladmin $mysqllogin ping 2>&1`; if ( $loginstatus =~ /mysqld is alive/ ) { - goodprint "Logged in using credentials from mysql-quickbackup."; + goodprint + "Logged in using credentials from mysql-quickbackup."; return 1; } else { @@ -563,7 +554,7 @@ sub mysql_setup { my $loginstatus = `$mysqladmincmd ping $mysqllogin 2>&1`; unless ( $loginstatus =~ /mysqld is alive/ ) { badprint - "Attempted to use login credentials from Plesk, but they failed."; +"Attempted to use login credentials from Plesk, but they failed."; exit 1; } } @@ -615,7 +606,7 @@ sub mysql_setup { # Login went just fine $mysqllogin = " $remotestring "; - # Did this go well because of a .my.cnf file or is there no password set? + # Did this go well because of a .my.cnf file or is there no password set? my $userpath = `printenv HOME`; if ( length($userpath) > 0 ) { chomp($userpath); @@ -628,29 +619,27 @@ sub mysql_setup { return 1; } else { - if ( $opt{'noask'} == 1 ) { - badprint - "Attempted to use login credentials, but they were invalid"; + if ( $opt{'noask'}==1 ) { + badprint "Attempted to use login credentials, but they were invalid"; exit 1; } - my ( $name, $password ); - + my ($name, $password); # If --user is defined no need to ask for username - if ( $opt{user} ne 0 ) { + if( $opt{user} ne 0 ) + { $name = $opt{user}; } - else { + else{ print STDERR "Please enter your MySQL administrative login: "; $name = ; } - # If --pass is defined no need to ask for password - if ( $opt{pass} ne 0 ) { + if( $opt{pass} ne 0 ) + { $password = $opt{pass}; } - else { - print STDERR - "Please enter your MySQL administrative password: "; + else{ + print STDERR "Please enter your MySQL administrative password: "; system("stty -echo >$devnull 2>&1"); $password = ; system("stty echo >$devnull 2>&1"); @@ -664,8 +653,7 @@ sub mysql_setup { } $mysqllogin .= $remotestring; my $loginstatus = `$mysqladmincmd ping $mysqllogin 2>&1`; - debugprint - "Login status command: $mysqladmincmd ping $mysqllogin 2>&1"; + debugprint "Login status command: $mysqladmincmd ping $mysqllogin 2>&1"; if ( $loginstatus =~ /mysqld is alive/ ) { print STDERR ""; if ( !length($password) ) { @@ -681,8 +669,7 @@ sub mysql_setup { return 1; } else { - badprint - "Attempted to use login credentials, but they were invalid."; + badprint "Attempted to use login credentials, but they were invalid."; exit 1; } exit 1; @@ -811,34 +798,33 @@ sub get_basic_passwords { sub cve_recommendations { prettyprint "\n-------- CVE Security Recommendations ---------------------------------------"; - unless ( defined( $opt{cvefile} ) && -f "$opt{cvefile}" ) { + unless ( defined($opt{cvefile}) && -f "$opt{cvefile}" ) { infoprint "Skipped due to --cvefile option undefined"; return; } -#prettyprint "Look for related CVE for $myvar{'version'} or lower in $opt{cvefile}"; - my $cvefound = 0; + #prettyprint "Look for related CVE for $myvar{'version'} or lower in $opt{cvefile}"; + my $cvefound=0; open( FH, "<$opt{cvefile}" ) or die "Can't open $opt{cvefile} for read: $!"; - while ( my $cveline = ) { - my @cve = split( ';', $cveline ); - if ( mysql_micro_version_le( $cve[1], $cve[2], $cve[3] ) ) { - badprint "$cve[4] : $cve[5]"; - $cvefound++; - } - + while (my $cveline = ) + { + my @cve=split (';', $cveline); + if (mysql_micro_version_le ($cve[1], $cve[2], $cve[3])) { + badprint "$cve[4] : $cve[5]"; + $cvefound++; + } + } close FH or die "Cannot close $opt{cvefile}: $!"; - if ( $cvefound == 0 ) { - goodprint "NO SECURITY CVE FOUND FOR YOUR VERSION"; - return; - } + if ($cvefound==0) { + goodprint "NO SECURITY CVE FOUND FOR YOUR VERSION"; + return; + } badprint $cvefound . " CVE(s) found for your MySQL release."; - push( @generalrec, - $cvefound - . " CVE(s) found for your MySQL release. Consider upgrading your version !" - ); + push( @generalrec, $cvefound . " CVE(s) found for your MySQL release. Consider upgrading your version !" ); } + sub security_recommendations { prettyprint "\n-------- Security Recommendations -------------------------------------------"; @@ -847,12 +833,11 @@ sub security_recommendations { return; } - my $PASS_COLUMN_NAME = 'password'; - if ( $myvar{'version'} =~ /5.7/ ) { - $PASS_COLUMN_NAME = 'authentication_string'; + my $PASS_COLUMN_NAME='password'; + if ($myvar{'version'} =~ /5.7/) { + $PASS_COLUMN_NAME='authentication_string'; } debugprint "Password column = $PASS_COLUMN_NAME"; - #exit(0); # Looking for Anonymous users my @mysqlstatlist = select_array @@ -988,10 +973,10 @@ sub get_replication_status { and ( $io_running !~ /yes/i or $sql_running !~ /yes/i ) ) { badprint - "This replication slave is not running but seems to be configurated."; +"This replication slave is not running but seems to be configurated."; } if ( defined($io_running) - && $io_running =~ /yes/i + && $io_running =~ /yes/i && $sql_running =~ /yes/i ) { if ( $myvar{'read_only'} eq 'OFF' ) { @@ -1025,13 +1010,10 @@ sub validate_mysql_version { . $myvar{'version'} . " is EOL software! Upgrade soon!"; } - elsif ( ( mysql_version_ge(6) and mysql_version_le(9) ) - or mysql_version_ge(12) ) - { + elsif ( ( mysql_version_ge(6) and mysql_version_le(9) ) or mysql_version_ge(12) ) { badprint "Currently running unsupported MySQL version " . $myvar{'version'} . ""; - } - else { + } else { goodprint "Currently running supported MySQL version " . $myvar{'version'} . ""; } @@ -1043,7 +1025,8 @@ sub mysql_version_ge { $min ||= 0; $mic ||= 0; return $mysqlvermajor > $maj - || $mysqlvermajor == $maj && ( $mysqlverminor > $min + || $mysqlvermajor == $maj + && ( $mysqlverminor > $min || $mysqlverminor == $min && $mysqlvermicro >= $mic ); } @@ -1053,7 +1036,8 @@ sub mysql_version_le { $min ||= 0; $mic ||= 0; return $mysqlvermajor < $maj - || $mysqlvermajor == $maj && ( $mysqlverminor < $min + || $mysqlvermajor == $maj + && ( $mysqlverminor < $min || $mysqlverminor == $min && $mysqlvermicro <= $mic ); } @@ -1062,7 +1046,7 @@ sub mysql_micro_version_le { my ( $maj, $min, $mic ) = @_; return $mysqlvermajor == $maj && ( $mysqlverminor == $min - && $mysqlvermicro <= $mic ); + && $mysqlvermicro <= $mic ); } # Checks for 32-bit boxes with more than 2GB of RAM @@ -1109,7 +1093,8 @@ sub check_architecture { "Switch to 64-bit OS - MySQL cannot currently use all of your RAM"; } else { - goodprint "Operating on 32-bit architecture with less than 2GB RAM"; + goodprint + "Operating on 32-bit architecture with less than 2GB RAM"; } } $result{'OS'}{'Architecture'} = "$arch bits"; @@ -1175,7 +1160,7 @@ sub check_storage_engines { : redwrap "-NDBCluster "; } - my @dblist = grep { $_ ne 'lost+found' } select_array "SHOW DATABASES"; + my @dblist = grep {$_ ne 'lost+found' } select_array "SHOW DATABASES"; $result{'Databases'}{'List'} = [@dblist]; infoprint "Status: $engines"; @@ -1211,7 +1196,7 @@ sub check_storage_engines { # MySQL < 5 servers take a lot of work to get table sizes my @tblist; -# Now we build a database list, and loop through it to get storage engine stats for tables + # Now we build a database list, and loop through it to get storage engine stats for tables foreach my $db (@dblist) { chomp($db); if ( $db eq "information_schema" @@ -1235,11 +1220,11 @@ sub check_storage_engines { # Parse through the table list to generate storage engine counts/statistics $fragtables = 0; foreach my $tbl (@tblist) { - debugprint "Data dump " . Dumper(@$tbl); + debugprint "Data dump ". Dumper (@$tbl); my ( $engine, $size, $datafree ) = @$tbl; next if $engine eq 'NULL'; - $size = 0 if $size eq 'NULL'; - $datafree = 0 if $datafree eq 'NULL'; + $size=0 if $size eq 'NULL'; + $datafree=0 if $datafree eq 'NULL'; if ( defined $enginestats{$engine} ) { $enginestats{$engine} += $size; $enginecount{$engine} += 1; @@ -1458,7 +1443,7 @@ sub calculations { $myvar{'key_cache_block_size'} ) / $myvar{'key_buffer_size'} ) - ) * 100 + ) * 100 ); } else { @@ -1551,14 +1536,14 @@ sub calculations { ( $mystat{'Qcache_hits'} / ( $mystat{'Com_select'} + $mystat{'Qcache_hits'} ) - ) * 100 + ) * 100 ); if ( $myvar{'query_cache_size'} ) { $mycalc{'pct_query_cache_used'} = sprintf( "%.1f", 100 - ( $mystat{'Qcache_free_memory'} / $myvar{'query_cache_size'} - ) * 100 + ) * 100 ); } if ( $mystat{'Qcache_lowmem_prunes'} == 0 ) { @@ -1776,10 +1761,8 @@ sub mysql_stats { . ( $myvar{'query_cache_type'} eq 0 | $myvar{'query_cache_type'} eq 'OFF' ? "DISABLED" - : ( - $myvar{'query_cache_type'} eq 1 ? "ALL REQUESTS" - : "ON DEMAND" - ) + : ( $myvar{'query_cache_type'} eq 1 ? "ALL REQUESTS" + : "ON DEMAND" ) ) . ""; infoprint " +-- Query Cache Size: " . hr_bytes( $myvar{'query_cache_size'} ) . ""; @@ -1808,7 +1791,7 @@ sub mysql_stats { && $mycalc{'max_used_memory'} > 2 * 1024 * 1024 * 1024 ) { badprint - "Allocating > 2GB RAM on 32-bit systems can cause system instability"; +"Allocating > 2GB RAM on 32-bit systems can cause system instability"; badprint "Maximum reached memory usage: " . hr_bytes( $mycalc{'max_used_memory'} ) . " ($mycalc{'pct_max_used_memory'}% of installed RAM)"; @@ -2024,8 +2007,7 @@ sub mysql_stats { "When making adjustments, make tmp_table_size/max_heap_table_size equal" ); push( @generalrec, - "Reduce your SELECT DISTINCT queries which have no LIMIT clause" - ); + "Reduce your SELECT DISTINCT queries which have no LIMIT clause" ); } elsif ($mycalc{'pct_temp_disk'} > 25 && $mycalc{'max_tmp_table_size'} >= 256 * 1024 * 1024 ) @@ -2213,7 +2195,6 @@ sub mysql_stats { sub mysql_myisam { prettyprint "\n-------- MyISAM Metrics ------------------------------------------------------"; - # Key buffer usage if ( defined( $mycalc{'pct_key_buffer_used'} ) ) { if ( $mycalc{'pct_key_buffer_used'} < 90 ) { @@ -2422,6 +2403,7 @@ sub mariadb_ariadb { } } + # Recommendations for TokuDB sub mariadb_tokudb { prettyprint @@ -2454,7 +2436,6 @@ sub mariadb_galera { return; } infoprint "Galera is enabled."; - # All is to done here } @@ -2759,7 +2740,7 @@ sub mysql_indexes { "\n-------- Indexes Metrics -----------------------------------------------------"; unless ( mysql_version_ge( 5, 5 ) ) { infoprint - "Skip Index metrics from information schema missing in this version"; +"Skip Index metrics from information schema missing in this version"; return; } my $selIdxReq = <<'ENDSQL'; @@ -2863,7 +2844,8 @@ sub make_recommendations { foreach (@adjvars) { prettyprint " " . $_ . ""; } } if ( @generalrec == 0 && @adjvars == 0 ) { - prettyprint "No additional performance recommendations are available."; + prettyprint + "No additional performance recommendations are available."; } } @@ -2879,19 +2861,18 @@ sub headerprint { } sub string2file { - my $filename = shift; - my $content = shift; - open my $fh, q(>), $filename - or die -"Unable to open $filename in write mode. Please check permissions for this file or directory"; - print $fh $content if defined($content); - close $fh; - debugprint $content if ( $opt{'debug'} ); + my $filename=shift; + my $content=shift; + open my $fh, q(>), $filename + or die "Unable to open $filename in write mode. Please check permissions for this file or directory"; + print $fh $content if defined($content); + close $fh; + debugprint $content if ($opt{'debug'}); } sub file2array { my $filename = shift; - debugprint "* reading $filename" if ( $opt{'debug'} ); + debugprint "* reading $filename" if ($opt{'debug'}); my $fh; open( $fh, q(<), "$filename" ) or die "Couldn't open $filename for reading: $!\n"; @@ -2901,16 +2882,15 @@ sub file2array { } sub file2string { - return join( '', file2array(@_) ); + return join ( '', file2array(@_) ); } my $templateModel; -if ( $opt{'template'} ne 0 ) { - $templateModel = file2string( $opt{'template'} ); -} -else { - # DEFAULT REPORT TEMPLATE - $templateModel = <<'END_TEMPLATE'; +if ($opt{'template'} ne 0 ) { + $templateModel=file2string ($opt{'template'}); +}else { + # DEFAULT REPORT TEMPLATE + $templateModel=<<'END_TEMPLATE'; @@ -2928,46 +2908,41 @@ else { END_TEMPLATE } - sub dump_result { - if ( $opt{'debug'} ) { - debugprint Dumper( \%result ); + if ($opt{'debug'}) { + debugprint Dumper( \%result ); } debugprint "HTML REPORT: $opt{'reportfile'}"; - if ( $opt{'reportfile'} ne 0 ) { - eval "{ use Text::Template }"; - if ($@) { - badprint "Text::Template Module is needed."; - exit 1; - } + if ($opt{'reportfile'} ne 0 ) { + eval "{ use Text::Template }"; + if ($@) { + badprint "Text::Template Module is needed."; + exit 1; + } - my $vars = { 'data' => Dumper( \%result ) }; + my $vars= {'data' => Dumper( \%result ) }; - my $template; - { - no warnings 'once'; - $template = Text::Template->new( - TYPE => 'STRING', - PREPEND => q{;}, - SOURCE => $templateModel - ) or die "Couldn't construct template: $Text::Template::ERROR"; - } - open my $fh, q(>), $opt{'reportfile'} - or die -"Unable to open $opt{'reportfile'} in write mode. please check permissions for this file or directory"; - $template->fill_in( HASH => $vars, OUTPUT => $fh ); - close $fh; + my $template; + { + no warnings 'once'; + $template = Text::Template->new(TYPE => 'STRING', PREPEND => q{;}, SOURCE => $templateModel) + or die "Couldn't construct template: $Text::Template::ERROR"; + } + open my $fh, q(>), $opt{'reportfile'} + or die "Unable to open $opt{'reportfile'} in write mode. please check permissions for this file or directory"; + $template->fill_in(HASH =>$vars, OUTPUT=>$fh ); + close $fh; } - if ( $opt{'json'} ne 0 ) { - eval "{ use JSON }"; - if ($@) { - badprint "JSON Module is needed."; - exit 1; - } - my $json = JSON->new->allow_nonref; - print JSON->new->utf8(1)->pretty(1)->encode(%result); + if ($opt{'json'} ne 0 ) { + eval "{ use JSON }"; + if ($@) { + badprint "JSON Module is needed."; + exit 1; + } + my $json = JSON->new->allow_nonref; + print JSON->new->utf8(1)->pretty(1)->encode(%result); } } From 65bc055bf18c297762b0fdf97fc7206aea7818fe Mon Sep 17 00:00:00 2001 From: Mohammad Date: Sat, 20 Feb 2016 18:31:17 +0200 Subject: [PATCH 4/9] #87 small fixes for windows porting --- mysqltuner.pl | 1 - 1 file changed, 1 deletion(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index 557c68e..06167f0 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -653,7 +653,6 @@ sub mysql_setup { } $mysqllogin .= $remotestring; my $loginstatus = `$mysqladmincmd ping $mysqllogin 2>&1`; - debugprint "Login status command: $mysqladmincmd ping $mysqllogin 2>&1"; if ( $loginstatus =~ /mysqld is alive/ ) { print STDERR ""; if ( !length($password) ) { From a477d5e14e6764eb1d4d7e74d46241766c547e5c Mon Sep 17 00:00:00 2001 From: root Date: Wed, 24 Feb 2016 19:52:26 +0100 Subject: [PATCH 5/9] Update vulnerabilities list --- USAGE.md | 4 ++-- vulnerabilities.csv | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/USAGE.md b/USAGE.md index 57fecf0..ddaf5fc 100644 --- a/USAGE.md +++ b/USAGE.md @@ -1,6 +1,6 @@ # NAME - MySQLTuner 1.6.3 - MySQL High Performance Tuning Script + MySQLTuner 1.6.4 - MySQL High Performance Tuning Script # IMPORTANT USAGE GUIDELINES @@ -132,4 +132,4 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with this program. If not, see <http://www.gnu.org/licenses/>. +along with this program. If not, see . diff --git a/vulnerabilities.csv b/vulnerabilities.csv index 779855a..cf37d4c 100644 --- a/vulnerabilities.csv +++ b/vulnerabilities.csv @@ -1,3 +1,4 @@ +3.22.32;3;22;32;CVE-2000-0148;Entry;"MySQL 3.22 allows remote attackers to bypass password authentication and access a database via a short check string.";"BUGTRAQ:20000208 Remote access vulnerability in all MySQL server versions | URL:http://archives.neohapsis.com/archives/bugtraq/2000-02/0053.html | BUGTRAQ:20000214 MySQL 3.22.32 released | BID:975 | URL:http://www.securityfocus.com/bid/975";;; 4.0.20;4;0;20;CVE-2004-0457;Candidate;"The mysqlhotcopy script in mysql 4.0.20 and earlier; when using the scp method from the mysql-server package; allows local users to overwrite arbitrary files via a symlink attack on temporary files.";"DEBIAN:DSA-540 | URL:http://www.debian.org/security/2004/dsa-540 | CONFIRM:http://packages.debian.org/changelogs/pool/main/m/mysql-dfsg/mysql-dfsg_4.0.20-11/changelog | REDHAT:RHSA-2004:597 | URL:http://www.redhat.com/support/errata/RHSA-2004-597.html | CIAC:P-018 | URL:http://www.ciac.org/ciac/bulletins/p-018.shtml | OVAL:oval:org.mitre.oval:def:10693 | URL:http://oval.mitre.org/repository/data/getDef?id=oval:org.mitre.oval:def:10693 | XF:mysql-mysqlhotcopy-insecure-file(17030) | URL:http://xforce.iss.net/xforce/xfdb/17030";Assigned (20040506);"None (candidate not yet proposed)"; 4.0.21;4;0;21;CVE-2004-0836;Candidate;"Buffer overflow in the mysql_real_connect function in MySQL 4.x before 4.0.21; and 3.x before 3.23.49; allows remote DNS servers to cause a denial of service and possibly execute arbitrary code via a DNS response with a large address length (h_length).";"CONECTIVA:CLA-2004:892 | URL:http://distro.conectiva.com.br/atualizacoes/?id=a&anuncio=000892 | DEBIAN:DSA-562 | URL:http://www.debian.org/security/2004/dsa-562 | GENTOO:GLSA-200410-22 | URL:http://www.gentoo.org/security/en/glsa/glsa-200410-22.xml | MISC:http://bugs.mysql.com/bug.php?id=4017 | MISC:http://lists.mysql.com/internals/14726 | REDHAT:RHSA-2004:597 | URL:http://www.redhat.com/support/errata/RHSA-2004-597.html | REDHAT:RHSA-2004:611 | URL:http://www.redhat.com/support/errata/RHSA-2004-611.html | TRUSTIX:2004-0054 | URL:http://www.trustix.org/errata/2004/0054/ | BUGTRAQ:20041125 [USN-32-1] mysql vulnerabilities | URL:http://marc.theaimsgroup.com/?l=bugtraq&m=110140517515735&w=2 | CIAC:P-018 | URL:http://www.ciac.org/ciac/bulletins/p-018.shtml | BID:10981 | URL:http://www.securityfocus.com/bid/10981 | SECUNIA:12305 | URL:http://secunia.com/advisories/12305/ | XF:mysql-realconnect-bo(17047) | URL:http://xforce.iss.net/xforce/xfdb/17047";Assigned (20040908);"None (candidate not yet proposed)"; 3.23.49;3;23;49;CVE-2004-0836;Candidate;"Buffer overflow in the mysql_real_connect function in MySQL 4.x before 4.0.21; and 3.x before 3.23.49; allows remote DNS servers to cause a denial of service and possibly execute arbitrary code via a DNS response with a large address length (h_length).";"CONECTIVA:CLA-2004:892 | URL:http://distro.conectiva.com.br/atualizacoes/?id=a&anuncio=000892 | DEBIAN:DSA-562 | URL:http://www.debian.org/security/2004/dsa-562 | GENTOO:GLSA-200410-22 | URL:http://www.gentoo.org/security/en/glsa/glsa-200410-22.xml | MISC:http://bugs.mysql.com/bug.php?id=4017 | MISC:http://lists.mysql.com/internals/14726 | REDHAT:RHSA-2004:597 | URL:http://www.redhat.com/support/errata/RHSA-2004-597.html | REDHAT:RHSA-2004:611 | URL:http://www.redhat.com/support/errata/RHSA-2004-611.html | TRUSTIX:2004-0054 | URL:http://www.trustix.org/errata/2004/0054/ | BUGTRAQ:20041125 [USN-32-1] mysql vulnerabilities | URL:http://marc.theaimsgroup.com/?l=bugtraq&m=110140517515735&w=2 | CIAC:P-018 | URL:http://www.ciac.org/ciac/bulletins/p-018.shtml | BID:10981 | URL:http://www.securityfocus.com/bid/10981 | SECUNIA:12305 | URL:http://secunia.com/advisories/12305/ | XF:mysql-realconnect-bo(17047) | URL:http://xforce.iss.net/xforce/xfdb/17047";Assigned (20040908);"None (candidate not yet proposed)"; From 09383daee85dbc76cf27f70b3160186d831a33a9 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 24 Feb 2016 19:57:55 +0100 Subject: [PATCH 6/9] #156: Removing index stat dur to erronous information --- mysqltuner.pl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mysqltuner.pl b/mysqltuner.pl index 06167f0..aa243a4 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -2742,6 +2742,11 @@ sub mysql_indexes { "Skip Index metrics from information schema missing in this version"; return; } + unless ( mysql_version_ge( 5, 6 ) ) { + infoprint +"Skip Index metrics from information schema due to erronous information provided in this version"; + return; + } my $selIdxReq = <<'ENDSQL'; SELECT CONCAT(CONCAT(t.TABLE_SCHEMA, '.'),t.TABLE_NAME) AS 'table' From 6dae07a921e80f30f1ebeb8f5059b3b5a42a0be3 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 24 Feb 2016 20:20:48 +0100 Subject: [PATCH 7/9] #146: Correct write effiency calculation based on log writes --- mysqltuner.pl | 47 +++++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index aa243a4..97b8386 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -377,6 +377,7 @@ sub os_setup { # Checks for updates to MySQLTuner sub validate_tuner_version { if ($opt{checkversion} eq 0) { + print "\n"; infoprint "Skipped version check for MySQLTuner script"; return; } @@ -1651,6 +1652,8 @@ sub calculations { ( $myvar{'innodb_log_file_size'} * 100 / $myvar{'innodb_buffer_pool_size'} ); } + + # InnoDB Buffer pool read cache effiency ( $mystat{'Innodb_buffer_pool_read_requests'}, $mystat{'Innodb_buffer_pool_reads'} @@ -1669,24 +1672,28 @@ sub calculations { . $mystat{'Innodb_buffer_pool_reads'} . ""; debugprint "Innodb_buffer_pool_read_requests: " . $mystat{'Innodb_buffer_pool_read_requests'} . ""; + + + + # InnoDB log write cache effiency ( - $mystat{'Innodb_buffer_pool_write_requests'}, - $mystat{'Innodb_buffer_pool_writes'} + $mystat{'Innodb_log_write_requests'}, + $mystat{'Innodb_log_writes'} ) = ( 1, 1 ) - unless defined $mystat{'Innodb_buffer_pool_writes'}; + unless defined $mystat{'Innodb_log_writes'}; $mycalc{'pct_write_efficiency'} = percentage( ( - $mystat{'Innodb_buffer_pool_write_requests'} - - $mystat{'Innodb_buffer_pool_writes'} + $mystat{'Innodb_log_write_requests'} - + $mystat{'Innodb_log_writes'} ), - $mystat{'Innodb_buffer_pool_write_requests'} - ) if defined $mystat{'Innodb_buffer_pool_write_requests'}; - debugprint "pct_write_efficiency: " . $mycalc{'pct_read_efficiency'} . ""; - debugprint "Innodb_buffer_pool_writes: " - . $mystat{'Innodb_buffer_pool_writes'} . ""; - debugprint "Innodb_buffer_pool_write_requests: " - . $mystat{'Innodb_buffer_pool_write_requests'} . ""; + $mystat{'Innodb_log_write_requests'} + ) if defined $mystat{'Innodb_log_write_requests'}; + debugprint "pct_write_efficiency: " . $mycalc{'pct_write_efficiency'} . ""; + debugprint "Innodb_log_writes: " + . $mystat{'Innodb_log_writes'} . ""; + debugprint "Innodb_log_write_requests: " + . $mystat{'Innodb_log_write_requests'} . ""; $mycalc{'pct_innodb_buffer_used'} = percentage( ( $mystat{'Innodb_buffer_pool_pages_total'} - @@ -2600,21 +2607,21 @@ sub mysql_innodb { if ( defined $mycalc{'pct_write_efficiency'} && $mycalc{'pct_write_efficiency'} < 90 ) { - badprint "InnoDB Write buffer efficiency: " + badprint "InnoDB Write Log efficiency: " . $mycalc{'pct_write_efficiency'} . "% (" - . ( $mystat{'Innodb_buffer_pool_write_requests'} - - $mystat{'Innodb_buffer_pool_writes'} ) + . ( $mystat{'Innodb_log_write_requests'} - + $mystat{'Innodb_log_writes'} ) . " hits/ " - . $mystat{'Innodb_buffer_pool_write_requests'} + . $mystat{'Innodb_log_write_requests'} . " total)"; } else { - goodprint "InnoDB Write buffer efficiency: " + goodprint "InnoDB Write log efficiency: " . $mycalc{'pct_write_efficiency'} . "% (" - . ( $mystat{'Innodb_buffer_pool_write_requests'} - - $mystat{'Innodb_buffer_pool_writes'} ) + . ( $mystat{'Innodb_log_write_requests'} - + $mystat{'Innodb_log_writes'} ) . " hits/ " - . $mystat{'Innodb_buffer_pool_write_requests'} + . $mystat{'Innodb_log_write_requests'} . " total)"; } From 2a96375fa8e0ef9dc885ee8f9220d6f0a5d384e7 Mon Sep 17 00:00:00 2001 From: marklahn Date: Wed, 2 Mar 2016 15:12:38 +0000 Subject: [PATCH 8/9] TX and RX now shows value in K/M/G, rather than K/M/B --- mysqltuner.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index 97b8386..fa39fe8 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -1729,9 +1729,9 @@ sub mysql_stats { . " qps], " . hr_num( $mystat{'Connections'} ) . " conn," . " TX: " - . hr_num( $mystat{'Bytes_sent'} ) + . hr_bytes_rnd( $mystat{'Bytes_sent'} ) . ", RX: " - . hr_num( $mystat{'Bytes_received'} ) . ")"; + . hr_bytes_rnd( $mystat{'Bytes_received'} ) . ")"; infoprint "Reads / Writes: " . $mycalc{'pct_reads'} . "% / " . $mycalc{'pct_writes'} . "%"; From a154701223c10aef4e834d427a548cdcbe7daec6 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 4 Mar 2016 11:52:33 +0100 Subject: [PATCH 9/9] Update CVE list --- vulnerabilities.csv | 1 - 1 file changed, 1 deletion(-) diff --git a/vulnerabilities.csv b/vulnerabilities.csv index cf37d4c..779855a 100644 --- a/vulnerabilities.csv +++ b/vulnerabilities.csv @@ -1,4 +1,3 @@ -3.22.32;3;22;32;CVE-2000-0148;Entry;"MySQL 3.22 allows remote attackers to bypass password authentication and access a database via a short check string.";"BUGTRAQ:20000208 Remote access vulnerability in all MySQL server versions | URL:http://archives.neohapsis.com/archives/bugtraq/2000-02/0053.html | BUGTRAQ:20000214 MySQL 3.22.32 released | BID:975 | URL:http://www.securityfocus.com/bid/975";;; 4.0.20;4;0;20;CVE-2004-0457;Candidate;"The mysqlhotcopy script in mysql 4.0.20 and earlier; when using the scp method from the mysql-server package; allows local users to overwrite arbitrary files via a symlink attack on temporary files.";"DEBIAN:DSA-540 | URL:http://www.debian.org/security/2004/dsa-540 | CONFIRM:http://packages.debian.org/changelogs/pool/main/m/mysql-dfsg/mysql-dfsg_4.0.20-11/changelog | REDHAT:RHSA-2004:597 | URL:http://www.redhat.com/support/errata/RHSA-2004-597.html | CIAC:P-018 | URL:http://www.ciac.org/ciac/bulletins/p-018.shtml | OVAL:oval:org.mitre.oval:def:10693 | URL:http://oval.mitre.org/repository/data/getDef?id=oval:org.mitre.oval:def:10693 | XF:mysql-mysqlhotcopy-insecure-file(17030) | URL:http://xforce.iss.net/xforce/xfdb/17030";Assigned (20040506);"None (candidate not yet proposed)"; 4.0.21;4;0;21;CVE-2004-0836;Candidate;"Buffer overflow in the mysql_real_connect function in MySQL 4.x before 4.0.21; and 3.x before 3.23.49; allows remote DNS servers to cause a denial of service and possibly execute arbitrary code via a DNS response with a large address length (h_length).";"CONECTIVA:CLA-2004:892 | URL:http://distro.conectiva.com.br/atualizacoes/?id=a&anuncio=000892 | DEBIAN:DSA-562 | URL:http://www.debian.org/security/2004/dsa-562 | GENTOO:GLSA-200410-22 | URL:http://www.gentoo.org/security/en/glsa/glsa-200410-22.xml | MISC:http://bugs.mysql.com/bug.php?id=4017 | MISC:http://lists.mysql.com/internals/14726 | REDHAT:RHSA-2004:597 | URL:http://www.redhat.com/support/errata/RHSA-2004-597.html | REDHAT:RHSA-2004:611 | URL:http://www.redhat.com/support/errata/RHSA-2004-611.html | TRUSTIX:2004-0054 | URL:http://www.trustix.org/errata/2004/0054/ | BUGTRAQ:20041125 [USN-32-1] mysql vulnerabilities | URL:http://marc.theaimsgroup.com/?l=bugtraq&m=110140517515735&w=2 | CIAC:P-018 | URL:http://www.ciac.org/ciac/bulletins/p-018.shtml | BID:10981 | URL:http://www.securityfocus.com/bid/10981 | SECUNIA:12305 | URL:http://secunia.com/advisories/12305/ | XF:mysql-realconnect-bo(17047) | URL:http://xforce.iss.net/xforce/xfdb/17047";Assigned (20040908);"None (candidate not yet proposed)"; 3.23.49;3;23;49;CVE-2004-0836;Candidate;"Buffer overflow in the mysql_real_connect function in MySQL 4.x before 4.0.21; and 3.x before 3.23.49; allows remote DNS servers to cause a denial of service and possibly execute arbitrary code via a DNS response with a large address length (h_length).";"CONECTIVA:CLA-2004:892 | URL:http://distro.conectiva.com.br/atualizacoes/?id=a&anuncio=000892 | DEBIAN:DSA-562 | URL:http://www.debian.org/security/2004/dsa-562 | GENTOO:GLSA-200410-22 | URL:http://www.gentoo.org/security/en/glsa/glsa-200410-22.xml | MISC:http://bugs.mysql.com/bug.php?id=4017 | MISC:http://lists.mysql.com/internals/14726 | REDHAT:RHSA-2004:597 | URL:http://www.redhat.com/support/errata/RHSA-2004-597.html | REDHAT:RHSA-2004:611 | URL:http://www.redhat.com/support/errata/RHSA-2004-611.html | TRUSTIX:2004-0054 | URL:http://www.trustix.org/errata/2004/0054/ | BUGTRAQ:20041125 [USN-32-1] mysql vulnerabilities | URL:http://marc.theaimsgroup.com/?l=bugtraq&m=110140517515735&w=2 | CIAC:P-018 | URL:http://www.ciac.org/ciac/bulletins/p-018.shtml | BID:10981 | URL:http://www.securityfocus.com/bid/10981 | SECUNIA:12305 | URL:http://secunia.com/advisories/12305/ | XF:mysql-realconnect-bo(17047) | URL:http://xforce.iss.net/xforce/xfdb/17047";Assigned (20040908);"None (candidate not yet proposed)";