diff --git a/bin/pt-config-diff b/bin/pt-config-diff index f263b2de..1da10d34 100755 --- a/bin/pt-config-diff +++ b/bin/pt-config-diff @@ -2295,7 +2295,7 @@ sub new { set => $args{set}, NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1, dbh_set => 0, - ask_pass => $o->get('ask-pass') || $args{ask_pass}, + ask_pass => $o->get('ask-pass'), DSNParser => $dp, is_cluster_node => undef, parent => $args{parent}, @@ -2311,7 +2311,7 @@ sub connect { my $dbh = $self->{dbh}; if ( !$dbh || !$dbh->ping() ) { - if ( $self->{ask_pass} && !$self->{asked_for_pass} ) { + if ( $self->{ask_pass} && !$self->{asked_for_pass} && !defined $dsn->{p} ) { $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: "); $self->{asked_for_pass} = 1; } diff --git a/bin/pt-deadlock-logger b/bin/pt-deadlock-logger index cc8eca2d..3a27239b 100755 --- a/bin/pt-deadlock-logger +++ b/bin/pt-deadlock-logger @@ -2639,7 +2639,7 @@ sub new { set => $args{set}, NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1, dbh_set => 0, - ask_pass => $o->get('ask-pass') || $args{ask_pass}, + ask_pass => $o->get('ask-pass'), DSNParser => $dp, is_cluster_node => undef, parent => $args{parent}, @@ -2655,7 +2655,7 @@ sub connect { my $dbh = $self->{dbh}; if ( !$dbh || !$dbh->ping() ) { - if ( $self->{ask_pass} && !$self->{asked_for_pass} ) { + if ( $self->{ask_pass} && !$self->{asked_for_pass} && !defined $dsn->{p} ) { $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: "); $self->{asked_for_pass} = 1; } diff --git a/bin/pt-fk-error-logger b/bin/pt-fk-error-logger index 96d4e09f..c9de88e6 100755 --- a/bin/pt-fk-error-logger +++ b/bin/pt-fk-error-logger @@ -1791,7 +1791,7 @@ sub new { set => $args{set}, NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1, dbh_set => 0, - ask_pass => $o->get('ask-pass') || $args{ask_pass}, + ask_pass => $o->get('ask-pass'), DSNParser => $dp, is_cluster_node => undef, parent => $args{parent}, @@ -1807,7 +1807,7 @@ sub connect { my $dbh = $self->{dbh}; if ( !$dbh || !$dbh->ping() ) { - if ( $self->{ask_pass} && !$self->{asked_for_pass} ) { + if ( $self->{ask_pass} && !$self->{asked_for_pass} && !defined $dsn->{p} ) { $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: "); $self->{asked_for_pass} = 1; } diff --git a/bin/pt-kill b/bin/pt-kill index db73976d..4ae40a9b 100755 --- a/bin/pt-kill +++ b/bin/pt-kill @@ -5158,7 +5158,7 @@ sub new { set => $args{set}, NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1, dbh_set => 0, - ask_pass => $o->get('ask-pass') || $args{ask_pass}, + ask_pass => $o->get('ask-pass'), DSNParser => $dp, is_cluster_node => undef, parent => $args{parent}, @@ -5174,7 +5174,7 @@ sub connect { my $dbh = $self->{dbh}; if ( !$dbh || !$dbh->ping() ) { - if ( $self->{ask_pass} && !$self->{asked_for_pass} ) { + if ( $self->{ask_pass} && !$self->{asked_for_pass} && !defined $dsn->{p} ) { $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: "); $self->{asked_for_pass} = 1; } diff --git a/bin/pt-online-schema-change b/bin/pt-online-schema-change index 8b8f4329..09933113 100755 --- a/bin/pt-online-schema-change +++ b/bin/pt-online-schema-change @@ -40,6 +40,7 @@ BEGIN { HTTP::Micro VersionCheck Percona::XtraDB::Cluster + ReadKeyMini )); } @@ -3755,7 +3756,7 @@ sub new { set => $args{set}, NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1, dbh_set => 0, - ask_pass => $o->get('ask-pass') || $args{ask_pass}, + ask_pass => $o->get('ask-pass'), DSNParser => $dp, is_cluster_node => undef, parent => $args{parent}, @@ -3771,7 +3772,7 @@ sub connect { my $dbh = $self->{dbh}; if ( !$dbh || !$dbh->ping() ) { - if ( $self->{ask_pass} && !$self->{asked_for_pass} ) { + if ( $self->{ask_pass} && !$self->{asked_for_pass} && !defined $dsn->{p} ) { $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: "); $self->{asked_for_pass} = 1; } @@ -7757,6 +7758,162 @@ sub _d { # End Percona::XtraDB::Cluster package # ########################################################################### +# ########################################################################### +# ReadKeyMini package +# This package is a copy without comments from the original. The original +# with comments and its test file can be found in the Bazaar repository at, +# lib/ReadKeyMini.pm +# t/lib/ReadKeyMini.t +# See https://launchpad.net/percona-toolkit for more information. +# ########################################################################### +{ + +BEGIN { + +package ReadKeyMini; +BEGIN { $INC{"ReadKeyMini.pm"} ||= 1 } + +use warnings; +use strict; +use English qw(-no_match_vars); +use constant PTDEBUG => $ENV{PTDEBUG} || 0; + +use POSIX qw( :termios_h ); +use Fcntl qw( F_SETFL F_GETFL ); + +use base qw( Exporter ); + +BEGIN { + our @EXPORT_OK = qw( GetTerminalSize ReadMode ); + *ReadMode = *Term::ReadKey::ReadMode = \&_ReadMode; + *GetTerminalSize = *Term::ReadKey::GetTerminalSize = \&_GetTerminalSize; +} + +my %modes = ( + original => 0, + restore => 0, + normal => 1, + noecho => 2, + cbreak => 3, + raw => 4, + 'ultra-raw' => 5, +); + +{ + my $fd_stdin = fileno(STDIN); + my $flags; + unless ( $PerconaTest::DONT_RESTORE_STDIN ) { + $flags = fcntl(STDIN, F_GETFL, 0) + or warn "Error getting STDIN flags with fcntl: $OS_ERROR"; + } + my $term = POSIX::Termios->new(); + $term->getattr($fd_stdin); + my $oterm = $term->getlflag(); + my $echo = ECHO | ECHOK | ICANON; + my $noecho = $oterm & ~$echo; + + sub _ReadMode { + my $mode = $modes{ $_[0] }; + if ( $mode == $modes{normal} ) { + cooked(); + } + elsif ( $mode == $modes{cbreak} || $mode == $modes{noecho} ) { + cbreak( $mode == $modes{noecho} ? $noecho : $oterm ); + } + else { + die("ReadMore('$_[0]') not supported"); + } + } + + sub cbreak { + my ($lflag) = $_[0] || $noecho; + $term->setlflag($lflag); + $term->setcc( VTIME, 1 ); + $term->setattr( $fd_stdin, TCSANOW ); + } + + sub cooked { + $term->setlflag($oterm); + $term->setcc( VTIME, 0 ); + $term->setattr( $fd_stdin, TCSANOW ); + if ( !$PerconaTest::DONT_RESTORE_STDIN ) { + fcntl(STDIN, F_SETFL, int($flags)) + or warn "Error restoring STDIN flags with fcntl: $OS_ERROR"; + } + } + + END { cooked() } +} + +sub readkey { + my $key = ''; + cbreak(); + sysread(STDIN, $key, 1); + my $timeout = 0.1; + if ( $key eq "\033" ) { + my $x = ''; + STDIN->blocking(0); + sysread(STDIN, $x, 2); + STDIN->blocking(1); + $key .= $x; + redo if $key =~ /\[[0-2](?:[0-9];)?$/ + } + cooked(); + return $key; +} + + +BEGIN { + eval { no warnings; local $^W; require 'sys/ioctl.ph' }; + if ( !defined &TIOCGWINSZ ) { + *TIOCGWINSZ = sub () { + $^O eq 'linux' ? 0x005413 + : $^O eq 'solaris' ? 0x005468 + : 0x40087468; + }; + } +} + +sub _GetTerminalSize { + if ( @_ ) { + die "My::Term::ReadKey doesn't implement GetTerminalSize with arguments"; + } + + my $cols = $ENV{COLUMNS} || 80; + my $rows = $ENV{LINES} || 24; + + if ( open( TTY, "+<", "/dev/tty" ) ) { # Got a tty + my $winsize = ''; + if ( ioctl( TTY, &TIOCGWINSZ, $winsize ) ) { + ( $rows, $cols, my ( $xpixel, $ypixel ) ) = unpack( 'S4', $winsize ); + return ( $cols, $rows, $xpixel, $ypixel ); + } + } + + if ( $rows = `tput lines 2>/dev/null` ) { + chomp($rows); + chomp($cols = `tput cols`); + } + elsif ( my $stty = `stty -a 2>/dev/null` ) { + ($rows, $cols) = $stty =~ /([0-9]+) rows; ([0-9]+) columns;/; + } + else { + ($cols, $rows) = @ENV{qw( COLUMNS LINES )}; + $cols ||= 80; + $rows ||= 24; + } + + return ( $cols, $rows ); +} + +} + +1; +} +# ########################################################################### +# End ReadKeyMini package +# ########################################################################### + # ########################################################################### # This is a combination of modules and programs in one -- a runnable module. # http://www.perl.com/pub/a/2006/07/13/lightning-articles.html?page=last @@ -7963,7 +8120,6 @@ sub main { }; my $cxn = $make_cxn->(dsn => $dsn); - $o->set('ask-pass', 0); # so we don't ask twice (password is already stored in dsn) my $aux_cxn = $make_cxn->(dsn => $dsn, prev_dsn => $dsn); my $cluster = Percona::XtraDB::Cluster->new; @@ -10485,10 +10641,12 @@ sub sig_int { my ( $signal ) = @_; $oktorun = 0; # flag for cleanup tasks print STDERR "# Exiting on SIG$signal.\n"; - # restore terminal to normal state in case CTL-C issued while asking for password + # restore terminal to normal state in case CTL+C issued while + # asking for password # https://bugs.launchpad.net/percona-toolkit/+bug/1396870 - use Term::ReadKey; - ReadMode 0; + # note: just including ReadKeyMini seems to solve the bug, + # but lets use it explicitly so we don't forget why we need it + ReadKeyMini::ReadMode 0; exit 1; } diff --git a/bin/pt-table-checksum b/bin/pt-table-checksum index 4f376898..ff1681b2 100755 --- a/bin/pt-table-checksum +++ b/bin/pt-table-checksum @@ -3533,7 +3533,7 @@ sub new { set => $args{set}, NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1, dbh_set => 0, - ask_pass => $o->get('ask-pass') || $args{ask_pass}, + ask_pass => $o->get('ask-pass'), DSNParser => $dp, is_cluster_node => undef, parent => $args{parent}, @@ -3549,7 +3549,7 @@ sub connect { my $dbh = $self->{dbh}; if ( !$dbh || !$dbh->ping() ) { - if ( $self->{ask_pass} && !$self->{asked_for_pass} ) { + if ( $self->{ask_pass} && !$self->{asked_for_pass} && !defined $dsn->{p} ) { $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: "); $self->{asked_for_pass} = 1; } diff --git a/bin/pt-upgrade b/bin/pt-upgrade index 57803988..b130c212 100755 --- a/bin/pt-upgrade +++ b/bin/pt-upgrade @@ -2464,7 +2464,7 @@ sub new { set => $args{set}, NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1, dbh_set => 0, - ask_pass => $o->get('ask-pass') || $args{ask_pass}, + ask_pass => $o->get('ask-pass'), DSNParser => $dp, is_cluster_node => undef, parent => $args{parent}, @@ -2480,7 +2480,7 @@ sub connect { my $dbh = $self->{dbh}; if ( !$dbh || !$dbh->ping() ) { - if ( $self->{ask_pass} && !$self->{asked_for_pass} ) { + if ( $self->{ask_pass} && !$self->{asked_for_pass} && !defined $dsn->{p} ) { $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: "); $self->{asked_for_pass} = 1; } diff --git a/lib/Cxn.pm b/lib/Cxn.pm index 41c8f543..f8515427 100644 --- a/lib/Cxn.pm +++ b/lib/Cxn.pm @@ -108,7 +108,7 @@ sub new { set => $args{set}, NAME_lc => defined($args{NAME_lc}) ? $args{NAME_lc} : 1, dbh_set => 0, - ask_pass => $o->get('ask-pass') || $args{ask_pass}, + ask_pass => $o->get('ask-pass'), DSNParser => $dp, is_cluster_node => undef, parent => $args{parent}, @@ -125,7 +125,7 @@ sub connect { my $dbh = $self->{dbh}; if ( !$dbh || !$dbh->ping() ) { # Ask for password once. - if ( $self->{ask_pass} && !$self->{asked_for_pass} ) { + if ( $self->{ask_pass} && !$self->{asked_for_pass} && !defined $dsn->{p} ) { $dsn->{p} = OptionParser::prompt_noecho("Enter MySQL password: "); $self->{asked_for_pass} = 1; }