[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <deff591b261b01f1be90dec14eaffac468fed638.1554689977.git.kevin@kevinlocke.name>
Date: Sun, 7 Apr 2019 20:19:37 -0600
From: Kevin Locke <kevin@...inlocke.name>
To: "John W. Linville" <linville@...driver.com>
Cc: netdev@...r.kernel.org
Subject: [PATCH] ethtool: Add bash-completion script
To aid users constructing a valid ethtool invocation, create a
[bash-completion] script to provide [programmable completion] of ethtool
arguments. It supports all current command options.
The script is placed in shell-completion/bash and installed to
completionsdir from pkg-config for bash-completion, similar to [kmod].
It requires pkg-config 0.18 or later to be installed on the build
system which runs aclocal (for the PKG_CHECK_MODULES m4 macro).
Note: In [scop/bash-completion#289] the bash-completion maintainer
suggested shipping this completion with ethtool rather than
bash-completion, due to assumptions about the ethtool command-line
format made by the script. That pull request also contains an extensive
test suite in Python which is not included in this commit, but may be
ported to a format suitable for inclusion if there is sufficient
interest and agreement about how to achieve that.
[bash-completion]: https://github.com/scop/bash-completion
[programmable completion]: https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion.html
[kmod]: https://git.kernel.org/pub/scm/utils/kernel/kmod/kmod.git/tree/
[scop/bash-completion#289]: https://github.com/scop/bash-completion/pull/289
Signed-off-by: Kevin Locke <kevin@...inlocke.name>
---
Makefile.am | 5 +
configure.ac | 15 +
shell-completion/bash/ethtool | 1251 +++++++++++++++++++++++++++++++++
3 files changed, 1271 insertions(+)
create mode 100644 shell-completion/bash/ethtool
diff --git a/Makefile.am b/Makefile.am
index 0a2fd29..3af4d4c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -17,6 +17,11 @@ ethtool_SOURCES += \
ixgbevf.c tse.c vmxnet3.c qsfp.c qsfp.h fjes.c lan78xx.c
endif
+if ENABLE_BASH_COMPLETION
+bashcompletiondir = $(BASH_COMPLETION_DIR)
+dist_bashcompletion_DATA = shell-completion/bash/ethtool
+endif
+
TESTS = test-cmdline test-features
check_PROGRAMS = test-cmdline test-features
test_cmdline_SOURCES = test-cmdline.c test-common.c $(ethtool_SOURCES)
diff --git a/configure.ac b/configure.ac
index 4e5477a..f84540a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -40,5 +40,20 @@ if test x$enable_pretty_dump = xyes; then
fi
AM_CONDITIONAL([ETHTOOL_ENABLE_PRETTY_DUMP], [test x$enable_pretty_dump = xyes])
+AC_ARG_WITH([bash-completion-dir],
+ AS_HELP_STRING([--with-bash-completion-dir[=PATH]],
+ [Install the bash-completion script in this directory. @<:@default=yes@:>@]),
+ [],
+ [with_bash_completion_dir=yes])
+AS_IF([test "x$with_bash_completion_dir" = xyes],
+ [PKG_CHECK_MODULES([BASH_COMPLETION],
+ [bash-completion],
+ [BASH_COMPLETION_DIR="`$PKG_CONFIG --variable=completionsdir bash-completion`"],
+ [BASH_COMPLETION_DIR="$datadir/bash-completion/completions"])],
+ [BASH_COMPLETION_DIR="$with_bash_completion_dir"])
+AC_SUBST([BASH_COMPLETION_DIR])
+AM_CONDITIONAL([ENABLE_BASH_COMPLETION],
+ [test "x$with_bash_completion_dir" != xno])
+
AC_CONFIG_FILES([Makefile ethtool.spec ethtool.8])
AC_OUTPUT
diff --git a/shell-completion/bash/ethtool b/shell-completion/bash/ethtool
new file mode 100644
index 0000000..5305559
--- /dev/null
+++ b/shell-completion/bash/ethtool
@@ -0,0 +1,1251 @@
+# bash completion for ethtool(8) -*- shell-script -*-
+# shellcheck shell=bash disable=SC2207
+
+# Complete a word representing a set of characters.
+# @param $@ chars Characters which may be present in completed set.
+_ethtool_compgen_letterset()
+{
+ local char
+ for char; do
+ case "$cur" in
+ *"$char"*)
+ # $cur already contains $char
+ ;;
+ *)
+ COMPREPLY+=( "$cur$char" )
+ ;;
+ esac
+ done
+}
+
+# Generate completions for words matched case-insensitively
+# @param $@ choices Completion choices.
+_ethtool_compgen_nocase()
+{
+ local reset
+ reset=$( shopt -p nocasematch )
+ shopt -s nocasematch
+
+ local choice
+ for choice; do
+ case "$choice" in
+ "$cur"*) COMPREPLY+=( "$choice" ) ;;
+ esac
+ done
+
+ $reset
+}
+
+# Gets names from a section of ethtool output.
+# @param $1 section_bre POSIX BRE matching section heading (without : at end).
+# @param $@ ethtool arguments
+_ethtool_get_names_in_section()
+{
+ local section_bre="$1"
+ shift
+
+ PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin" \
+ ethtool "$@" 2>/dev/null |
+ command sed -n "
+# Line is section heading iff it ends with :
+# From requested section heading to next section heading
+/^$section_bre:$/,/:$/ {
+ # If line is section heading, ignore it
+ /:$/d
+ # Remove value and separator, if present
+ s/[[:space:]]*:.*//
+ # Remove leading space, if present
+ s/^[[:space:]]*//
+ # Print the line
+ p
+}"
+}
+
+# Complete an RSS Context ID
+_ethtool_context()
+{
+ COMPREPLY=(
+ $(PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin" \
+ ethtool --show-nfc "${words[2]}" 2>/dev/null |
+ command sed -n 's/^[[:space:]]*RSS Context ID:[[:space:]]*\([0-9]*\)$/\1/p' |
+ sort -u) )
+}
+
+# Complete a network flow traffic type
+# Available OPTIONS:
+# --hash Complete only types suitable for rx hashing
+_ethtool_flow_type()
+{
+ local types='ah4 ah6 esp4 esp6 ether sctp4 sctp6 tcp4 tcp6 udp4 udp6'
+ if [ "${1-}" != --hash ]; then
+ types="$types ip4 ip6"
+ fi
+ COMPREPLY=( $( compgen -W "$types" -- "$cur" ) )
+}
+
+# Completion for ethtool --change
+_ethtool_change()
+{
+ local -A settings=(
+ [advertise]=notseen
+ [autoneg]=notseen
+ [duplex]=notseen
+ [mdix]=notseen
+ [msglvl]=notseen
+ [port]=notseen
+ [phyad]=notseen
+ [speed]=notseen
+ [wol]=notseen
+ [xcvr]=notseen
+ )
+
+ local -A msgtypes=(
+ [drv]=notseen
+ [hw]=notseen
+ [ifdown]=notseen
+ [ifup]=notseen
+ [intr]=notseen
+ [link]=notseen
+ [pktdata]=notseen
+ [probe]=notseen
+ [rx_err]=notseen
+ [rx_status]=notseen
+ [timer]=notseen
+ [tx_done]=notseen
+ [tx_err]=notseen
+ [tx_queued]=notseen
+ [wol]=notseen
+ )
+
+ # Mark seen settings and msgtypes, and whether in msglvl sub-command
+ local in_msglvl=
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ if [ "$in_msglvl" ] && [ "${msgtypes[$word]+set}" ]; then
+ msgtypes[$word]=seen
+ elif [ "${settings[$word]+set}" ]; then
+ settings[$word]=seen
+ if [ "$word" = msglvl ]; then
+ in_msglvl=1
+ else
+ in_msglvl=
+ fi
+ fi
+ done
+
+ if [ "$in_msglvl" ] && [ "${msgtypes[$prev]+set}" ]; then
+ # All msgtypes take an on/off argument
+ COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
+ return
+ fi
+
+ case "$prev" in
+ advertise)
+ # Hex number
+ return ;;
+ autoneg)
+ COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
+ return ;;
+ duplex)
+ COMPREPLY=( $( compgen -W 'half full' -- "$cur" ) )
+ return ;;
+ mdix)
+ COMPREPLY=( $( compgen -W 'auto on off' -- "$cur" ) )
+ return ;;
+ msglvl)
+ # Unsigned integer or msgtype
+ COMPREPLY=( $( compgen -W "${!msgtypes[*]}" -- "$cur" ) )
+ return ;;
+ port)
+ COMPREPLY=( $( compgen -W 'aui bnc fibre mii tp' -- "$cur" ) )
+ return ;;
+ phyad)
+ # Integer
+ return ;;
+ sopass)
+ _mac_addresses
+ return ;;
+ speed)
+ # Number
+ return ;;
+ wol)
+ # $cur is a set of wol type characters.
+ _ethtool_compgen_letterset p u m b a g s f d
+ return ;;
+ xcvr)
+ COMPREPLY=( $( compgen -W 'internal external' -- "$cur" ) )
+ return ;;
+ esac
+
+ local -a comp_words=()
+
+ # Add settings not seen to completions
+ local setting
+ for setting in "${!settings[@]}"; do
+ if [ "${settings[$setting]}" = notseen ]; then
+ comp_words+=( "$setting" )
+ fi
+ done
+
+ # Add settings not seen to completions
+ if [ "$in_msglvl" ]; then
+ local msgtype
+ for msgtype in "${!msgtypes[@]}"; do
+ if [ "${msgtypes[$msgtype]}" = notseen ]; then
+ comp_words+=( "$msgtype" )
+ fi
+ done
+ fi
+
+ COMPREPLY=( $( compgen -W "${comp_words[*]}" -- "$cur" ) )
+}
+
+# Completion for ethtool --change-eeprom
+_ethtool_change_eeprom()
+{
+ local -A settings=(
+ [length]=1
+ [magic]=1
+ [offset]=1
+ [value]=1
+ )
+
+ if [ "${settings[$prev]+set}" ]; then
+ # All settings take an unsigned integer argument
+ return
+ fi
+
+ # Remove settings which have been seen
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ unset "settings[$word]"
+ done
+
+ COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
+}
+
+# Completion for ethtool --coalesce
+_ethtool_coalesce()
+{
+ local -A settings=(
+ [adaptive-rx]=1
+ [adaptive-tx]=1
+ [pkt-rate-high]=1
+ [pkt-rate-low]=1
+ [rx-frames]=1
+ [rx-frames-high]=1
+ [rx-frames-irq]=1
+ [rx-frames-low]=1
+ [rx-usecs]=1
+ [rx-usecs-high]=1
+ [rx-usecs-irq]=1
+ [rx-usecs-low]=1
+ [sample-interval]=1
+ [stats-block-usecs]=1
+ [tx-frames]=1
+ [tx-frames-high]=1
+ [tx-frames-irq]=1
+ [tx-frames-low]=1
+ [tx-usecs]=1
+ [tx-usecs-high]=1
+ [tx-usecs-irq]=1
+ [tx-usecs-low]=1
+ )
+
+ case "$prev" in
+ adaptive-rx|\
+ adaptive-tx)
+ COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
+ return ;;
+ esac
+
+ if [ "${settings[$prev]+set}" ]; then
+ # Unsigned integer
+ return
+ fi
+
+ # Remove settings which have been seen
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ unset "settings[$word]"
+ done
+
+ COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
+}
+
+# Completion for ethtool --config-nfc <devname> flow-type
+_ethtool_config_nfc_flow_type()
+{
+ if [ "$cword" -eq 4 ]; then
+ _ethtool_flow_type --spec
+ return
+ fi
+
+ case "$prev" in
+ context)
+ _ethtool_context
+ return ;;
+ dst|\
+ dst-mac|\
+ src)
+ # TODO: Complete only local for dst and remote for src
+ _mac_addresses
+ return ;;
+ dst-ip)
+ # Note: RX classification, so dst is usually local
+ case "${words[4]}" in
+ *4) _ip_addresses -4 return ;;
+ *6) _ip_addresses -6 return ;;
+ esac
+ return ;;
+ src-ip)
+ # Note: RX classification, so src is usually remote
+ # TODO: Remote IP addresses (ARP cache + /etc/hosts + ?)
+ return ;;
+ m|\
+ *-mask)
+ # MAC, IP, or integer bitmask
+ return ;;
+ esac
+
+ local -A settings=(
+ [action]=1
+ [context]=1
+ [loc]=1
+ [queue]=1
+ [vf]=1
+ )
+
+ if [ "${settings[$prev]+set}" ]; then
+ # Integer
+ return
+ fi
+
+ case "${words[4]}" in
+ ah4|\
+ esp4)
+ local -A fields=(
+ [dst-ip]=1
+ [dst-mac]=1
+ [spi]=1
+ [src-ip]=1
+ [tos]=1
+ [user-def]=1
+ [vlan-etype]=1
+ [vlan]=1
+ )
+ ;;
+ ah6|\
+ esp6)
+ local -A fields=(
+ [dst-ip]=1
+ [dst-mac]=1
+ [spi]=1
+ [src-ip]=1
+ [tclass]=1
+ [user-def]=1
+ [vlan-etype]=1
+ [vlan]=1
+ )
+ ;;
+ ether)
+ local -A fields=(
+ [dst]=1
+ [proto]=1
+ [src]=1
+ [user-def]=1
+ [vlan-etype]=1
+ [vlan]=1
+ )
+ ;;
+ ip4)
+ local -A fields=(
+ [dst-ip]=1
+ [dst-mac]=1
+ [dst-port]=1
+ [l4data]=1
+ [l4proto]=1
+ [spi]=1
+ [src-ip]=1
+ [src-port]=1
+ [tos]=1
+ [user-def]=1
+ [vlan-etype]=1
+ [vlan]=1
+ )
+ ;;
+ ip6)
+ local -A fields=(
+ [dst-ip]=1
+ [dst-mac]=1
+ [dst-port]=1
+ [l4data]=1
+ [l4proto]=1
+ [spi]=1
+ [src-ip]=1
+ [src-port]=1
+ [tclass]=1
+ [user-def]=1
+ [vlan-etype]=1
+ [vlan]=1
+ )
+ ;;
+ sctp4|\
+ tcp4|\
+ udp4)
+ local -A fields=(
+ [dst-ip]=1
+ [dst-mac]=1
+ [dst-port]=1
+ [src-ip]=1
+ [src-port]=1
+ [tos]=1
+ [user-def]=1
+ [vlan-etype]=1
+ [vlan]=1
+ )
+ ;;
+ sctp6|\
+ tcp6|\
+ udp6)
+ local -A fields=(
+ [dst-ip]=1
+ [dst-mac]=1
+ [dst-port]=1
+ [src-ip]=1
+ [src-port]=1
+ [tclass]=1
+ [user-def]=1
+ [vlan-etype]=1
+ [vlan]=1
+ )
+ ;;
+ *)
+ return ;;
+ esac
+
+ if [ "${fields[$prev]+set}" ]; then
+ # Integer
+ return
+ fi
+
+ # If the previous 2 words were a field+value, suggest a mask
+ local mask=
+ if [ "${fields[${words[$cword-2]}]+set}" ]; then
+ mask="m ${words[$cword-2]}-mask"
+ fi
+
+ # Remove fields and settings which have been seen
+ local word
+ for word in "${words[@]:5:${#words[@]}-6}"; do
+ unset "fields[$word]" "settings[$word]"
+ done
+
+ # Remove mutually-exclusive options
+ if ! [ "${settings[action]+set}" ]; then
+ unset 'settings[queue]' 'settings[vf]'
+ fi
+ if ! [ "${settings[queue]+set}" ]; then
+ unset 'settings[action]'
+ fi
+ if ! [ "${settings[vf]+set}" ]; then
+ unset 'settings[action]'
+ fi
+
+ COMPREPLY=( $( compgen -W "$mask ${!fields[*]} ${!settings[*]}" -- "$cur" ) )
+}
+
+# Completion for ethtool --config-nfc
+_ethtool_config_nfc()
+{
+ if [ "$cword" -eq 3 ]; then
+ COMPREPLY=( $( compgen -W 'delete flow-type rx-flow-hash' -- "$cur" ) )
+ return
+ fi
+
+ case "${words[3]}" in
+ delete)
+ # Unsigned integer
+ return ;;
+ flow-type)
+ _ethtool_config_nfc_flow_type
+ return ;;
+ rx-flow-hash)
+ case "$cword" in
+ 4)
+ _ethtool_flow_type --hash
+ return ;;
+ 5)
+ _ethtool_compgen_letterset m v t s d f n r
+ return ;;
+ 6)
+ COMPREPLY=( $( compgen -W context -- "$cur" ) )
+ return ;;
+ 7)
+ _ethtool_context
+ return ;;
+ esac
+ return ;;
+ esac
+}
+
+# Completion for ethtool --eeprom-dump
+_ethtool_eeprom_dump()
+{
+ local -A settings=(
+ [length]=1
+ [offset]=1
+ [raw]=1
+ )
+
+ if [ "$prev" = raw ]; then
+ COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
+ return
+ fi
+
+ if [ "${settings[$prev]+set}" ]; then
+ # Unsigned integer argument
+ return
+ fi
+
+ # Remove settings which have been seen
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ unset "settings[$word]"
+ done
+
+ COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
+}
+
+# Completion for ethtool --features
+_ethtool_features()
+{
+ local -A abbreviations=(
+ [generic-receive-offload]=gro
+ [generic-segmentation-offload]=gso
+ [large-receive-offload]=lro
+ [ntuple-filters]=ntuple
+ [receive-hashing]=rxhash
+ [rx-checksumming]=rx
+ [rx-vlan-offload]=rxvlan
+ [scatter-gather]=sg
+ [tcp-segmentation-offload]=tso
+ [tx-checksumming]=tx
+ [tx-vlan-offload]=txvlan
+ [udp-fragmentation-offload]=ufo
+ )
+
+ local -A features=()
+ local feature status fixed
+ # shellcheck disable=SC2034
+ while read -r feature status fixed; do
+ if [ -z "$feature" ]; then
+ # Ignore blank line from empty expansion in here-document
+ continue
+ fi
+
+ if [ "$feature" = Features ]; then
+ # Ignore heading
+ continue
+ fi
+
+ if [ "$fixed" = '[fixed]' ]; then
+ # Fixed features can't be changed
+ continue
+ fi
+
+ feature=${feature%:}
+ if [ "${abbreviations[$feature]+set}" ]; then
+ features[${abbreviations[$feature]}]=1
+ else
+ features[$feature]=1
+ fi
+ done <<ETHTOOL_FEATURES
+$(PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin" \
+ ethtool --show-features "${words[2]}" 2>/dev/null)
+ETHTOOL_FEATURES
+
+ if [ "${features[$prev]+set}" ]; then
+ COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
+ return
+ fi
+
+ # Remove features which have been seen
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ unset "features[$word]"
+ done
+
+ COMPREPLY=( $( compgen -W "${!features[*]}" -- "$cur" ) )
+}
+
+# Complete the current word as a kernel firmware file (for request_firmware)
+# See https://www.kernel.org/doc/html/latest/driver-api/firmware/core.html
+_ethtool_firmware()
+{
+ local -a firmware_paths=(
+ /lib/firmware/updates/
+ /lib/firmware/
+ )
+
+ local release
+ if release=$( uname -r 2>/dev/null ); then
+ firmware_paths+=(
+ "/lib/firmware/updates/$release/"
+ "/lib/firmware/$release/"
+ )
+ fi
+
+ local fw_path_para
+ if fw_path_para=$( cat /sys/module/firmware_class/parameters/path 2>/dev/null ) \
+ && [ -n "$fw_path_para" ]; then
+ firmware_paths+=( "$fw_path_para" )
+ fi
+
+ local -A firmware_files=()
+
+ local firmware_path
+ for firmware_path in "${firmware_paths[@]}"; do
+ local firmware_file
+ for firmware_file in "$firmware_path"*; do
+ if [ -f "$firmware_file" ]; then
+ firmware_files[${firmware_file##*/}]=1
+ fi
+ done
+ done
+
+ local IFS='
+'
+ COMPREPLY=( $( compgen -W "${!firmware_files[*]}" -- "$cur" ) )
+}
+
+# Completion for ethtool --flash
+_ethtool_flash()
+{
+ if [ "$cword" -eq 3 ]; then
+ _ethtool_firmware
+ return
+ fi
+}
+
+# Completion for ethtool --get-dump
+_ethtool_get_dump()
+{
+ case "$cword" in
+ 3)
+ COMPREPLY=( $( compgen -W data -- "$cur" ) )
+ return ;;
+ 4)
+ # Output filename
+ local IFS='
+'
+ COMPREPLY=( $( compgen -f -- "$cur" ) )
+ return ;;
+ esac
+}
+
+# Completion for ethtool --get-phy-tunable
+_ethtool_get_phy_tunable()
+{
+ if [ "$cword" -eq 3 ]; then
+ COMPREPLY=( $( compgen -W downshift -- "$cur" ) )
+ return
+ fi
+}
+
+# Completion for ethtool --module-info
+_ethtool_module_info()
+{
+ local -A settings=(
+ [hex]=1
+ [length]=1
+ [offset]=1
+ [raw]=1
+ )
+
+ case "$prev" in
+ hex|\
+ raw)
+ COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
+ return ;;
+ esac
+
+ if [ "${settings[$prev]+set}" ]; then
+ # Unsigned integer argument
+ return
+ fi
+
+ # Remove settings which have been seen
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ unset "settings[$word]"
+ done
+
+ COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
+}
+
+# Completion for ethtool --pause
+_ethtool_pause()
+{
+ local -A settings=(
+ [autoneg]=1
+ [rx]=1
+ [tx]=1
+ )
+
+ if [ "${settings[$prev]+set}" ]; then
+ COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
+ return
+ fi
+
+ # Remove settings which have been seen
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ unset "settings[$word]"
+ done
+
+ COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
+}
+
+# Completion for ethtool --per-queue
+_ethtool_per_queue()
+{
+ local -a subcommands=(
+ --coalesce
+ --show-coalesce
+ )
+
+ if [ "$cword" -eq 3 ]; then
+ COMPREPLY=( $( compgen -W "queue_mask ${subcommands[*]}" -- "$cur" ) )
+ return
+ fi
+
+ local sc_start=3
+ if [ "${words[3]}" = queue_mask ] ; then
+ case "$cword" in
+ 4)
+ # Hex number
+ return ;;
+ 5)
+ COMPREPLY=( $( compgen -W "${subcommands[*]}" -- "$cur" ) )
+ return ;;
+ esac
+
+ sc_start=5
+ fi
+
+ case "${words[$sc_start]}" in
+ --coalesce)
+ # Remove --per-queue args to match normal --coalesce invocation
+ local words=(
+ "${words[0]}"
+ --coalesce
+ "${words[2]}"
+ "${words[@]:$sc_start+1:${#words[@]}-$sc_start-1}"
+ )
+ _ethtool_coalesce
+ return ;;
+ --show-coalesce)
+ # No args
+ return ;;
+ esac
+}
+
+# Completion for ethtool --register-dump
+_ethtool_register_dump()
+{
+ local -A settings=(
+ [file]=1
+ [hex]=1
+ [raw]=1
+ )
+
+ case "$prev" in
+ hex|\
+ raw)
+ COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
+ return ;;
+ file)
+ local IFS='
+'
+ COMPREPLY=( $( compgen -f -- "$cur" ) )
+ return ;;
+ esac
+
+ # Remove settings which have been seen
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ unset "settings[$word]"
+ done
+
+ COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
+}
+
+# Completion for ethtool --reset
+_ethtool_reset()
+{
+ if [ "$prev" = flags ]; then
+ # Unsigned integer
+ return
+ fi
+
+ local -A flag_names=(
+ [ap]=1
+ [dma]=1
+ [filter]=1
+ [irq]=1
+ [mac]=1
+ [mgmt]=1
+ [offload]=1
+ [phy]=1
+ [ram]=1
+ )
+
+ local -A all_flag_names=()
+ local flag_name
+ for flag_name in "${!flag_names[@]}"; do
+ all_flag_names[$flag_name]=1
+ all_flag_names[$flag_name-shared]=1
+ done
+
+ # Remove all_flag_names which have been seen
+ local any_dedicated=
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ case "$word" in
+ all)
+ # Flags are always additive.
+ # Nothing to add after "all".
+ return ;;
+ dedicated)
+ any_dedicated=1
+ # "dedicated" sets all non-shared flags
+ for flag_name in "${!flag_names[@]}"; do
+ unset "all_flag_names[$flag_name]"
+ done
+ continue ;;
+ esac
+
+ if [ "${flag_names[$word]+set}" ]; then
+ any_dedicated=1
+ fi
+
+ unset "all_flag_names[$word]"
+ done
+
+ COMPREPLY=( $( compgen -W "${!all_flag_names[*]}" -- "$cur" ) )
+
+ # Although it is permitted to mix named and un-named flags or duplicate
+ # flags with "all" or "dedicated", it's not likely intentional.
+ # Reconsider if a real use-case (or good consistency argument) is found.
+ if [ "$cword" -eq 3 ]; then
+ COMPREPLY+=( all dedicated flags )
+ elif [ -z "$any_dedicated" ]; then
+ COMPREPLY+=( dedicated )
+ fi
+}
+
+# Completion for ethtool --rxfh
+_ethtool_rxfh()
+{
+ local -A settings=(
+ [context]=1
+ [default]=1
+ [delete]=1
+ [equal]=1
+ [hfunc]=1
+ [hkey]=1
+ [weight]=1
+ )
+
+ case "$prev" in
+ context)
+ _ethtool_context
+ # "new" to create a new context
+ COMPREPLY+=( new )
+ return ;;
+ equal)
+ # Positive integer
+ return ;;
+ hfunc)
+ # Complete available RSS hash functions
+ COMPREPLY=(
+ $(_ethtool_get_names_in_section 'RSS hash function' \
+ --show-rxfh "${words[2]}")
+ )
+ return ;;
+ hkey)
+ # Pairs of hex digits separated by :
+ return ;;
+ weight)
+ # Non-negative integer
+ return ;;
+ esac
+
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ # Remove settings which have been seen
+ unset "settings[$word]"
+
+ # Remove settings which are mutually-exclusive with seen settings
+ case "$word" in
+ context)
+ unset 'settings[default]'
+ ;;
+ default)
+ unset \
+ 'settings[context]' \
+ 'settings[delete]' \
+ 'settings[equal]' \
+ 'settings[weight]'
+ ;;
+ delete)
+ unset \
+ 'settings[default]' \
+ 'settings[equal]' \
+ 'settings[hkey]' \
+ 'settings[weight]'
+ ;;
+ equal)
+ unset \
+ 'settings[default]' \
+ 'settings[delete]' \
+ 'settings[weight]'
+ ;;
+ hkey)
+ unset 'settings[delete]'
+ ;;
+ weight)
+ unset \
+ 'settings[default]' \
+ 'settings[delete]' \
+ 'settings[equal]'
+ ;;
+ esac
+ done
+
+
+ COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
+}
+
+# Completion for ethtool --set-channels
+_ethtool_set_channels()
+{
+ local -A settings=(
+ [combined]=1
+ [other]=1
+ [rx]=1
+ [tx]=1
+ )
+
+ if [ "${settings[$prev]+set}" ]; then
+ # Unsigned integer argument
+ return
+ fi
+
+ # Remove settings which have been seen
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ unset "settings[$word]"
+ done
+
+ COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
+}
+
+# Completion for ethtool --set-eee
+_ethtool_set_eee()
+{
+ local -A settings=(
+ [advertise]=1
+ [eee]=1
+ [tx-lpi]=1
+ [tx-timer]=1
+ )
+
+ case "$prev" in
+ advertise|\
+ tx-timer)
+ # Unsigned integer
+ return ;;
+ eee|\
+ tx-lpi)
+ COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
+ return ;;
+ esac
+
+ # Remove settings which have been seen
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ unset "settings[$word]"
+ done
+
+ COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
+}
+
+# Completion for ethtool --set-fec
+_ethtool_set_fec()
+{
+ if [ "$cword" -eq 3 ]; then
+ COMPREPLY=( $( compgen -W encoding -- "$cur" ) )
+ return
+ fi
+
+ local -A modes=(
+ [auto]=auto
+ [rs]=RS
+ [off]=off
+ [baser]=BaseR
+ )
+
+ # Remove modes which have been seen
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ # ethtool recognizes modes case-insensitively
+ unset "modes[${word,,}]"
+ done
+
+ _ethtool_compgen_nocase "${modes[@]}"
+}
+
+# Completion for ethtool --set-phy-tunable
+_ethtool_set_phy_tunable()
+{
+ case "$cword" in
+ 3)
+ COMPREPLY=( $( compgen -W downshift -- "$cur" ) )
+ return ;;
+ 4)
+ COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
+ return ;;
+ 5)
+ COMPREPLY=( $( compgen -W count -- "$cur" ) )
+ return ;;
+ esac
+}
+
+# Completion for ethtool --set-priv-flags
+_ethtool_set_priv_flags()
+{
+ if [ $(( cword % 2 )) -eq 0 ]; then
+ COMPREPLY=( $( compgen -W 'on off' -- "$cur" ) )
+ return
+ fi
+
+ # Get available private flags
+ local -A flags=()
+ local flag
+ while IFS= read -r flag; do
+ # Ignore blank line from empty here-document
+ if [ -n "$flag" ]; then
+ flags[$flag]=1
+ fi
+ done <<ETHTOOL_PRIV_FLAGS
+$(_ethtool_get_names_in_section \
+ 'Private flags for [[:graph:]]*' --show-priv-flags "${words[2]}")
+ETHTOOL_PRIV_FLAGS
+
+ # Remove flags which have been seen
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ unset "flags[$word]"
+ done
+
+ COMPREPLY=( $( compgen -W "${!flags[*]}" -- "$cur" ) )
+}
+
+# Completion for ethtool --set-ring
+_ethtool_set_ring()
+{
+ local -A settings=(
+ [rx-jumbo]=1
+ [rx-mini]=1
+ [rx]=1
+ [tx]=1
+ )
+
+ if [ "${settings[$prev]+set}" ]; then
+ # Unsigned integer argument
+ return
+ fi
+
+ # Remove settings which have been seen
+ local word
+ for word in "${words[@]:3:${#words[@]}-4}"; do
+ unset "settings[$word]"
+ done
+
+ COMPREPLY=( $( compgen -W "${!settings[*]}" -- "$cur" ) )
+}
+
+# Completion for ethtool --show-nfc
+_ethtool_show_nfc()
+{
+ if [ "$cword" -eq 3 ]; then
+ COMPREPLY=( $( compgen -W 'rule rx-flow-hash' -- "$cur" ) )
+ return
+ fi
+
+ case "${words[3]}" in
+ rule)
+ if [ "$cword" -eq 4 ]; then
+ COMPREPLY=(
+ $(PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin" \
+ ethtool --show-nfc "${words[2]}" 2>/dev/null |
+ command sed -n 's/^Filter:[[:space:]]*\([0-9]*\)$/\1/p')
+ )
+ fi
+ return ;;
+ rx-flow-hash)
+ case "$cword" in
+ 4)
+ _ethtool_flow_type --hash
+ return ;;
+ 5)
+ COMPREPLY=( $( compgen -W context -- "$cur" ) )
+ return ;;
+ 6)
+ _ethtool_context
+ return ;;
+ esac
+ ;;
+ esac
+}
+
+# Completion for ethtool --show-rxfh
+_ethtool_show_rxfh()
+{
+ case "$cword" in
+ 3)
+ COMPREPLY=( $( compgen -W context -- "$cur" ) )
+ return ;;
+ 4)
+ _ethtool_context
+ return ;;
+ esac
+}
+
+# Completion for ethtool --test
+_ethtool_test()
+{
+ if [ "$cword" -eq 3 ]; then
+ COMPREPLY=( $( compgen -W 'external_lb offline online' -- "$cur" ) )
+ return
+ fi
+}
+
+
+# Complete any ethtool command
+_ethtool()
+{
+ local cur prev words cword
+ _init_completion || return
+
+ # Per "Contributing to bash-completion", complete non-duplicate long opts
+ local -A suggested_funcs=(
+ [--change-eeprom]=change_eeprom
+ [--change]=change
+ [--coalesce]=coalesce
+ [--config-nfc]=config_nfc
+ [--driver]=devname
+ [--dump-module-eeprom]=module_info
+ [--eeprom-dump]=eeprom_dump
+ [--features]=features
+ [--flash]=flash
+ [--get-dump]=get_dump
+ [--get-phy-tunable]=get_phy_tunable
+ [--identify]=devname
+ [--module-info]=module_info
+ [--negotiate]=devname
+ [--offload]=features
+ [--pause]=pause
+ [--per-queue]=per_queue
+ [--phy-statistics]=devname
+ [--register-dump]=register_dump
+ [--reset]=reset
+ [--set-channels]=set_channels
+ [--set-dump]=devname
+ [--set-eee]=set_eee
+ [--set-fec]=set_fec
+ [--set-phy-tunable]=set_phy_tunable
+ [--set-priv-flags]=set_priv_flags
+ [--set-ring]=set_ring
+ [--set-rxfh-indir]=rxfh
+ [--show-channels]=devname
+ [--show-coalesce]=devname
+ [--show-eee]=devname
+ [--show-features]=devname
+ [--show-fec]=devname
+ [--show-nfc]=show_nfc
+ [--show-offload]=devname
+ [--show-pause]=devname
+ [--show-permaddr]=devname
+ [--show-priv-flags]=devname
+ [--show-ring]=devname
+ [--show-rxfh]=show_rxfh
+ [--show-time-stamping]=devname
+ [--statistics]=devname
+ [--test]=test
+ )
+ local -A other_funcs=(
+ [--config-ntuple]=config_nfc
+ [--rxfh]=rxfh
+ [--show-ntuple]=show_nfc
+ [--show-rxfh-indir]=devname
+ [-A]=pause
+ [-C]=coalesce
+ [-E]=change_eeprom
+ [-G]=set_ring
+ [-K]=features
+ [-L]=set_channels
+ [-N]=config_nfc
+ [-P]=devname
+ [-Q]=per_queue
+ [-S]=devname
+ [-T]=devname
+ [-U]=config_nfc
+ [-W]=devname
+ [-X]=rxfh
+ [-a]=devname
+ [-c]=devname
+ [-d]=register_dump
+ [-e]=eeprom_dump
+ [-f]=flash
+ [-g]=devname
+ [-i]=devname
+ [-k]=devname
+ [-l]=devname
+ [-m]=module_info
+ [-n]=show_nfc
+ [-p]=devname
+ [-r]=devname
+ [-s]=change
+ [-t]=test
+ [-u]=show_nfc
+ [-w]=get_dump
+ [-x]=devname
+ )
+
+ if [ "$cword" -le 1 ]; then
+ _available_interfaces
+ COMPREPLY+=(
+ $( compgen -W "--help --version ${!suggested_funcs[*]}" -- "$cur" )
+ )
+ return
+ fi
+
+ local func=${suggested_funcs[${words[1]}]-${other_funcs[${words[1]}]-}}
+ if [ "$func" ]; then
+ # All sub-commands have devname as their first argument
+ if [ "$cword" -eq 2 ]; then
+ _available_interfaces
+ return
+ fi
+
+ if [ "$func" != devname ]; then
+ "_ethtool_$func"
+ fi
+ fi
+} &&
+complete -F _ethtool ethtool
+
+# ex: filetype=sh sts=8 sw=8 ts=8 noet
--
2.20.1
Powered by blists - more mailing lists