diff --git a/bin/pt-ioprofile b/bin/pt-ioprofile index df8fffce..c6c63494 100755 --- a/bin/pt-ioprofile +++ b/bin/pt-ioprofile @@ -4,8 +4,6 @@ # See "COPYRIGHT, LICENSE, AND WARRANTY" at the end of this file for legal # notices and disclaimers. -set -u - # ########################################################################### # log_warn_die package # This package is a copy without comments from the original. The original @@ -15,23 +13,20 @@ set -u # See https://launchpad.net/percona-toolkit for more information. # ########################################################################### - -set -u - EXIT_STATUS=0 log() { TS=$(date +%F-%T | tr :- _); - echo "$TS $1" + echo "$TS $*" } warn() { - log "$1" >&2 - EXIT_STATUS=$((EXIT_STATUS | 1)) + log "$*" >&2 + EXIT_STATUS=1 } die() { - warn "$1" + warn "$*" exit 1 } @@ -48,102 +43,177 @@ die() { # See https://launchpad.net/percona-toolkit for more information. # ########################################################################### - -set -u - ARGV="" # Non-option args (probably input files) EXT_ARGV="" # Everything after -- (args for an external command) +HAVE_EXT_ARGV="" # Got --, everything else is put into EXT_ARGV OPT_ERRS=0 # How many command line option errors -OPT_VERSION="no" # If --version was specified -OPT_HELP="no" # If --help was specified +OPT_VERSION="" # If --version was specified +OPT_HELP="" # If --help was specified +PO_DIR="" # Directory with program option spec files usage() { - local file=$1 + local file="$1" - local usage=$(grep '^Usage: ' $file) - echo $usage >&2 - echo >&2 - echo "For more information, 'man $TOOL' or 'perldoc $file'." >&2 + local usage=$(grep '^Usage: ' "$file") + echo $usage + echo + echo "For more information, 'man $TOOL' or 'perldoc $file'." } usage_or_errors() { - local file=$1 + local file="$1" - if [ "$OPT_VERSION" = "yes" ]; then - local version=$(grep '^pt-[^ ]\+ [0-9]' $file) + if [ "$OPT_VERSION" ]; then + local version=$(grep '^pt-[^ ]\+ [0-9]' "$file") echo "$version" return 1 fi - if [ "$OPT_HELP" = "yes" ]; then + if [ "$OPT_HELP" ]; then usage "$file" - echo >&2 - echo "Command line options:" >&2 - echo >&2 - for opt in $(ls $TMPDIR/po/); do - local desc=$(cat $TMPDIR/po/$opt | grep '^desc:' | sed -e 's/^desc://') - echo "--$opt" >&2 - echo " $desc" >&2 - echo >&2 + echo + echo "Command line options:" + echo + perl -e ' + use strict; + use warnings FATAL => qw(all); + my $lcol = 20; # Allow this much space for option names. + my $rcol = 80 - $lcol; # The terminal is assumed to be 80 chars wide. + my $name; + while ( <> ) { + my $line = $_; + chomp $line; + if ( $line =~ s/^long:/ --/ ) { + $name = $line; + } + elsif ( $line =~ s/^desc:// ) { + $line =~ s/ +$//mg; + my @lines = grep { $_ } + $line =~ m/(.{0,$rcol})(?:\s+|\Z)/g; + if ( length($name) >= $lcol ) { + print $name, "\n", (q{ } x $lcol); + } + else { + printf "%-${lcol}s", $name; + } + print join("\n" . (q{ } x $lcol), @lines); + print "\n"; + } + } + ' "$PO_DIR"/* + echo + echo "Options and values after processing arguments:" + echo + for opt in $(ls "$PO_DIR"); do + local varname="OPT_$(echo "$opt" | tr a-z- A-Z_)" + local varvalue="${!varname}" + printf -- " --%-30s %s" "$opt" "${varvalue:-(No value)}" + echo done return 1 fi if [ $OPT_ERRS -gt 0 ]; then - echo >&2 - usage $file + echo + usage "$file" return 1 fi return 0 } +option_error() { + local err="$1" + OPT_ERRS=$(($OPT_ERRS + 1)) + echo "$err" >&2 +} + parse_options() { - local file=$1 + local file="$1" shift - mkdir $TMPDIR/po/ 2>/dev/null - rm -rf $TMPDIR/po/* - ( - export PO_DIR="$TMPDIR/po" - cat $file | perl -ne ' - BEGIN { $/ = ""; } - next unless $_ =~ m/^=head1 OPTIONS/; - while ( defined(my $para = <>) ) { - last if $para =~ m/^=head1/; + ARGV="" + EXT_ARGV="" + HAVE_EXT_ARGV="" + OPT_ERRS=0 + OPT_VERSION="" + OPT_HELP="" + PO_DIR="$TMPDIR/po" + + if [ ! -d "$PO_DIR" ]; then + mkdir "$PO_DIR" + if [ $? -ne 0 ]; then + echo "Cannot mkdir $PO_DIR" >&2 + exit 1 + fi + fi + + rm -rf "$PO_DIR"/* + if [ $? -ne 0 ]; then + echo "Cannot rm -rf $PO_DIR/*" >&2 + exit 1 + fi + + _parse_pod "$file" # Parse POD into program option (po) spec files + _eval_po # Eval po into existence with default values + + if [ $# -ge 2 ] && [ "$1" = "--config" ]; then + shift # --config + local user_config_files="$1" + shift # that ^ + local IFS="," + for user_config_file in $user_config_files; do + _parse_config_files "$user_config_file" + done + else + _parse_config_files "/etc/percona-toolkit/percona-toolkit.conf" "/etc/percona-toolkit/$TOOL.conf" "$HOME/.percona-toolkit.conf" "$HOME/.$TOOL.conf" + fi + + _parse_command_line "$@" +} + +_parse_pod() { + local file="$1" + + cat "$file" | PO_DIR="$PO_DIR" perl -ne ' + BEGIN { $/ = ""; } + next unless $_ =~ m/^=head1 OPTIONS/; + while ( defined(my $para = <>) ) { + last if $para =~ m/^=head1/; + chomp; + if ( $para =~ m/^=item --(\S+)/ ) { + my $opt = $1; + my $file = "$ENV{PO_DIR}/$opt"; + open my $opt_fh, ">", $file or die "Cannot open $file: $!"; + print $opt_fh "long:$opt\n"; + $para = <>; chomp; - if ( $para =~ m/^=item --(\S+)/ ) { - my $opt = $1; - my $file = "$ENV{PO_DIR}/$opt"; - open my $opt_fh, ">", $file or die "Cannot open $file: $!"; - printf $opt_fh "long:$opt\n"; + if ( $para =~ m/^[a-z ]+:/ ) { + map { + chomp; + my ($attrib, $val) = split(/: /, $_); + print $opt_fh "$attrib:$val\n"; + } split(/; /, $para); $para = <>; chomp; - if ( $para =~ m/^[a-z ]+:/ ) { - map { - chomp; - my ($attrib, $val) = split(/: /, $_); - printf $opt_fh "$attrib:$val\n"; - } split(/; /, $para); - $para = <>; - chomp; - } - my ($desc) = $para =~ m/^([^?.]+)/; - printf $opt_fh "desc:$desc.\n"; - close $opt_fh; } + my ($desc) = $para =~ m/^([^?.]+)/; + print $opt_fh "desc:$desc.\n"; + close $opt_fh; } - last; - ' - ) + } + last; + ' +} - for opt_spec in $(ls $TMPDIR/po/); do +_eval_po() { + local IFS=":" + for opt_spec in "$PO_DIR"/*; do local opt="" local default_val="" local neg=0 - while read line; do - local key=`echo $line | cut -d ':' -f 1` - local val=`echo $line | cut -d ':' -f 2` + local size=0 + while read key val; do case "$key" in long) opt=$(echo $val | sed 's/-/_/g' | tr [:lower:] [:upper:]) @@ -154,6 +224,7 @@ parse_options() { "short form") ;; type) + [ "$val" = "size" ] && size=1 ;; desc) ;; @@ -163,13 +234,13 @@ parse_options() { fi ;; *) - echo "Invalid attribute in $TMPDIR/po/$opt_spec: $line" >&2 + echo "Invalid attribute in $opt_spec: $line" >&2 exit 1 esac - done < $TMPDIR/po/$opt_spec + done < "$opt_spec" if [ -z "$opt" ]; then - echo "No long attribute in option spec $TMPDIR/po/$opt_spec" >&2 + echo "No long attribute in option spec $opt_spec" >&2 exit 1 fi @@ -180,74 +251,154 @@ parse_options() { fi fi + if [ $size -eq 1 -a -n "$default_val" ]; then + default_val=$(size_to_bytes $default_val) + fi + eval "OPT_${opt}"="$default_val" done +} - for opt; do - if [ $# -eq 0 ]; then - break # no more opts +_parse_config_files() { + + for config_file in "$@"; do + test -f "$config_file" || continue + + while read config_opt; do + + echo "$config_opt" | grep '^[ ]*[^#]' >/dev/null 2>&1 || continue + + config_opt="$(echo "$config_opt" | sed -e 's/^ *//g' -e 's/ *$//g' -e 's/[ ]*=[ ]*/=/' -e 's/[ ]*#.*$//')" + + [ "$config_opt" = "" ] && continue + + if ! [ "$HAVE_EXT_ARGV" ]; then + config_opt="--$config_opt" + fi + + _parse_command_line "$config_opt" + + done < "$config_file" + + HAVE_EXT_ARGV="" # reset for each file + + done +} + +_parse_command_line() { + local opt="" + local val="" + local next_opt_is_val="" + local opt_is_ok="" + local opt_is_negated="" + local real_opt="" + local required_arg="" + local spec="" + + for opt in "$@"; do + if [ "$opt" = "--" -o "$opt" = "----" ]; then + HAVE_EXT_ARGV=1 + continue fi - opt=$1 - if [ "$opt" = "--" ]; then - shift - EXT_ARGV="$@" - break - fi - shift - if [ $(expr "$opt" : "-") -eq 0 ]; then - if [ -z "$ARGV" ]; then - ARGV="$opt" + if [ "$HAVE_EXT_ARGV" ]; then + if [ "$EXT_ARGV" ]; then + EXT_ARGV="$EXT_ARGV $opt" else - ARGV="$ARGV $opt" + EXT_ARGV="$opt" fi continue fi - local real_opt="$opt" - - if $(echo $opt | grep -q '^--no-'); then - neg=1 - opt=$(echo $opt | sed 's/^--no-//') - else - neg=0 - opt=$(echo $opt | sed 's/^-*//') - fi - - if [ -f "$TMPDIR/po/$opt" ]; then - spec="$TMPDIR/po/$opt" - else - spec=$(grep "^short form:-$opt\$" $TMPDIR/po/* | cut -d ':' -f 1) - if [ -z "$spec" ]; then - OPT_ERRS=$(($OPT_ERRS + 1)) - echo "Unknown option: $real_opt" >&2 + if [ "$next_opt_is_val" ]; then + next_opt_is_val="" + if [ $# -eq 0 ] || [ $(expr "$opt" : "-") -eq 1 ]; then + option_error "$real_opt requires a $required_arg argument" continue fi - fi - - required_arg=$(cat $spec | grep '^type:' | cut -d':' -f2) - if [ -n "$required_arg" ]; then - if [ $# -eq 0 ]; then - OPT_ERRS=$(($OPT_ERRS + 1)) - echo "$real_opt requires a $required_arg argument" >&2 - continue - else - val="$1" - shift - fi + val="$opt" + opt_is_ok=1 else - if [ $neg -eq 0 ]; then - val="yes" + if [ $(expr "$opt" : "-") -eq 0 ]; then + if [ -z "$ARGV" ]; then + ARGV="$opt" + else + ARGV="$ARGV $opt" + fi + continue + fi + + real_opt="$opt" + + if $(echo $opt | grep '^--no-' >/dev/null); then + opt_is_negated=1 + opt=$(echo $opt | sed 's/^--no-//') else - val="no" + opt_is_negated="" + opt=$(echo $opt | sed 's/^-*//') + fi + + if $(echo $opt | grep '^[a-z-][a-z-]*=' >/dev/null 2>&1); then + val="$(echo $opt | awk -F= '{print $2}')" + opt="$(echo $opt | awk -F= '{print $1}')" + fi + + if [ -f "$TMPDIR/po/$opt" ]; then + spec="$TMPDIR/po/$opt" + else + spec=$(grep "^short form:-$opt\$" "$TMPDIR"/po/* | cut -d ':' -f 1) + if [ -z "$spec" ]; then + option_error "Unknown option: $real_opt" + continue + fi + fi + + required_arg=$(cat "$spec" | awk -F: '/^type:/{print $2}') + if [ "$required_arg" ]; then + if [ "$val" ]; then + opt_is_ok=1 + else + next_opt_is_val=1 + fi + else + if [ "$val" ]; then + option_error "Option $real_opt does not take a value" + continue + fi + if [ "$opt_is_negated" ]; then + val="" + else + val="yes" + fi + opt_is_ok=1 fi fi - opt=$(cat $spec | grep '^long:' | cut -d':' -f2 | sed 's/-/_/g' | tr [:lower:] [:upper:]) + if [ "$opt_is_ok" ]; then + opt=$(cat "$spec" | grep '^long:' | cut -d':' -f2 | sed 's/-/_/g' | tr [:lower:] [:upper:]) - eval "OPT_$opt"="$val" + if grep "^type:size" "$spec" >/dev/null; then + val=$(size_to_bytes $val) + fi + + eval "OPT_$opt"="'$val'" + + opt="" + val="" + next_opt_is_val="" + opt_is_ok="" + opt_is_negated="" + real_opt="" + required_arg="" + spec="" + fi done } +size_to_bytes() { + local size="$1" + echo $size | perl -ne '%f=(B=>1, K=>1_024, M=>1_048_576, G=>1_073_741_824, T=>1_099_511_627_776); m/^(\d+)([kMGT])?/i; print $1 * $f{uc($2 || "B")};' +} + # ########################################################################### # End parse_options package # ########################################################################### @@ -261,21 +412,18 @@ parse_options() { # See https://launchpad.net/percona-toolkit for more information. # ########################################################################### - -set -u - TMPDIR="" mk_tmpdir() { - local dir=${1:-""} + local dir="${1:-""}" if [ -n "$dir" ]; then if [ ! -d "$dir" ]; then - mkdir $dir || die "Cannot make tmpdir $dir" + mkdir "$dir" || die "Cannot make tmpdir $dir" fi TMPDIR="$dir" else - local tool=`basename $0` + local tool="${0##*/}" local pid="$$" TMPDIR=`mktemp -d /tmp/${tool}.${pid}.XXXXX` \ || die "Cannot make secure tmpdir" @@ -284,7 +432,7 @@ mk_tmpdir() { rm_tmpdir() { if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then - rm -rf $TMPDIR + rm -rf "$TMPDIR" fi TMPDIR="" } diff --git a/bin/pt-summary b/bin/pt-summary index 7f475107..5eb26586 100755 --- a/bin/pt-summary +++ b/bin/pt-summary @@ -53,8 +53,6 @@ fuzz () { # See https://launchpad.net/percona-toolkit for more information. # ########################################################################### -set -u - TMPDIR="" mk_tmpdir() {