* [PATCH] ethtool: Add bash-completion script
@ 2019-04-08 2:19 Kevin Locke
2019-04-11 16:14 ` Jesse Brandeburg
2019-04-11 17:39 ` [PATCH v2] " Kevin Locke
0 siblings, 2 replies; 9+ messages in thread
From: Kevin Locke @ 2019-04-08 2:19 UTC (permalink / raw)
To: John W. Linville; +Cc: netdev
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@kevinlocke.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
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH] ethtool: Add bash-completion script
2019-04-08 2:19 [PATCH] ethtool: Add bash-completion script Kevin Locke
@ 2019-04-11 16:14 ` Jesse Brandeburg
2019-04-11 16:47 ` Kevin Locke
2019-04-11 17:39 ` [PATCH v2] " Kevin Locke
1 sibling, 1 reply; 9+ messages in thread
From: Jesse Brandeburg @ 2019-04-11 16:14 UTC (permalink / raw)
To: Kevin Locke; +Cc: John W. Linville, netdev, jesse.brandeburg
On Sun, 7 Apr 2019 20:19:37 -0600
Kevin Locke <kevin@kevinlocke.name> wrote:
> 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@kevinlocke.name>
Thank you! I think this is super useful, and I agree FWIW that it
should be part of ethtool. I suspect that it needs to be installed by
any package manager as part of ethtool install, into the right
directory. Could be a followup patch?
And the only (minor) complaint I have about your patch is that the
commit message doesn't show how to install it (basically just copy
ethtool file from this patch to
f.e. /usr/share/bash-completion/completions/)
I did a quick touch test of it and it seemed to be completing ethtool
commands which made me really happy, especially the network interface
names which are so long.
Reviewed-by: Jesse Brandeburg <jesse.brandeburg@intel.com>
And now I'm going to go and tell everyone I work with about this
patch. :-)
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH] ethtool: Add bash-completion script
2019-04-11 16:14 ` Jesse Brandeburg
@ 2019-04-11 16:47 ` Kevin Locke
0 siblings, 0 replies; 9+ messages in thread
From: Kevin Locke @ 2019-04-11 16:47 UTC (permalink / raw)
To: Jesse Brandeburg; +Cc: John W. Linville, netdev
On Thu, 2019-04-11 at 09:14 -0700, Jesse Brandeburg wrote:
> On Sun, 7 Apr 2019 20:19:37 -0600 Kevin Locke <kevin@kevinlocke.name> wrote:
>> 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).
>>
>> [...]
>
> Thank you! I think this is super useful, and I agree FWIW that it
> should be part of ethtool. I suspect that it needs to be installed by
> any package manager as part of ethtool install, into the right
> directory. Could be a followup patch?
Great! Glad to hear it. Patched configure.ac and Makefile.am install
the script to `pkg-config --variable=completionsdir bash-completion`
(with fallback to $datadir/bash-completion/completions) by default.
It can be disabled by passing --without-bash-completion-dir or
overridden by --with-bash-completion-dir=$anypath. I would expect
most distribution packages would install it by default, but if there
are followup patches (for ethtool or to the distributions), I'd be
happy to submit those as well.
> And the only (minor) complaint I have about your patch is that the
> commit message doesn't show how to install it (basically just copy
> ethtool file from this patch to
> f.e. /usr/share/bash-completion/completions/)
Good point. I will document the install process and the configure
arguments mentioned above and send an updated patch shortly.
> I did a quick touch test of it and it seemed to be completing ethtool
> commands which made me really happy, especially the network interface
> names which are so long.
>
> Reviewed-by: Jesse Brandeburg <jesse.brandeburg@intel.com>
>
> And now I'm going to go and tell everyone I work with about this
> patch. :-)
Wonderful! I'm very glad to hear that you found it useful and I hope
your colleagues appreciate it as well. Let me know if you or they
find any improvements that I can include in a future version of the
patch.
Thanks for taking the time to review it!
Best,
Kevin
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2] ethtool: Add bash-completion script
2019-04-08 2:19 [PATCH] ethtool: Add bash-completion script Kevin Locke
2019-04-11 16:14 ` Jesse Brandeburg
@ 2019-04-11 17:39 ` Kevin Locke
2019-04-16 18:37 ` John W. Linville
2019-04-20 0:16 ` [PATCH v3] " Kevin Locke
1 sibling, 2 replies; 9+ messages in thread
From: Kevin Locke @ 2019-04-11 17:39 UTC (permalink / raw)
To: John W. Linville; +Cc: netdev, Jesse Brandeburg
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 named shell-completion/bash/ethtool, similar to [kmod],
and installed to `pkg-config --variable=completionsdir bash-completion`
(with fallback to $datadir/bash-completion/completions) by default.
This can be disabled by passing --without-bash-completion-dir or changed
by passing --with-bash-completion-dir=$anypath to ./configure. 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).
To install the script manually for the current user, copy or link
shell-completion/bash to $BASH_COMPLETION_USER_DIR/completions
(default $XDG_DATA_HOME/bash-completion/completions
(default ~/.local/share/bash-completion/completions)).
To install system-wide, copy shell-completion/bash to completionsdir
from pkg-config (default /usr/share/bash-completion/completions)
discussed above. Restarting bash may be necessary to pick up changes to
the script (if a previous version had already been loaded).
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@kevinlocke.name>
Reviewed-by: Jesse Brandeburg <jesse.brandeburg@intel.com>
---
Changes in v2:
* Describe manual install and ./configure arguments in commit message.
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
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v2] ethtool: Add bash-completion script
2019-04-11 17:39 ` [PATCH v2] " Kevin Locke
@ 2019-04-16 18:37 ` John W. Linville
2019-04-17 2:53 ` Kevin Locke
2019-04-20 0:16 ` [PATCH v3] " Kevin Locke
1 sibling, 1 reply; 9+ messages in thread
From: John W. Linville @ 2019-04-16 18:37 UTC (permalink / raw)
To: Kevin Locke; +Cc: netdev, Jesse Brandeburg
On Thu, Apr 11, 2019 at 11:39:32AM -0600, Kevin Locke wrote:
> 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 named shell-completion/bash/ethtool, similar to [kmod],
> and installed to `pkg-config --variable=completionsdir bash-completion`
> (with fallback to $datadir/bash-completion/completions) by default.
> This can be disabled by passing --without-bash-completion-dir or changed
> by passing --with-bash-completion-dir=$anypath to ./configure. 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).
>
> To install the script manually for the current user, copy or link
> shell-completion/bash to $BASH_COMPLETION_USER_DIR/completions
> (default $XDG_DATA_HOME/bash-completion/completions
> (default ~/.local/share/bash-completion/completions)).
> To install system-wide, copy shell-completion/bash to completionsdir
> from pkg-config (default /usr/share/bash-completion/completions)
> discussed above. Restarting bash may be necessary to pick up changes to
> the script (if a previous version had already been loaded).
>
> 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@kevinlocke.name>
> Reviewed-by: Jesse Brandeburg <jesse.brandeburg@intel.com>
> ---
>
> Changes in v2:
> * Describe manual install and ./configure arguments in commit message.
Overall, it looks good to me. But when I build with "make distcheck",
I get this output:
...
make[3]: Leaving directory '/home/linville/git/ethtool/ethtool-5.0/_build/sub'
make[2]: Leaving directory '/home/linville/git/ethtool/ethtool-5.0/_build/sub'
make[1]: Leaving directory '/home/linville/git/ethtool/ethtool-5.0/_build/sub'
make[1]: Entering directory '/home/linville/git/ethtool/ethtool-5.0/_build/sub'
make[2]: Entering directory '/home/linville/git/ethtool/ethtool-5.0/_build/sub'
/usr/bin/mkdir -p '/home/linville/git/ethtool/ethtool-5.0/_inst/sbin'
/usr/bin/install -c ethtool '/home/linville/git/ethtool/ethtool-5.0/_inst/sbin'
/usr/bin/mkdir -p '/usr/share/bash-completion/completions'
/usr/bin/install -c -m 644 ../../shell-completion/bash/ethtool '/usr/share/bash-completion/completions'
/usr/bin/install: cannot create regular file '/usr/share/bash-completion/completions/ethtool': Permission denied
make[2]: *** [Makefile:2005: install-dist_bashcompletionDATA] Error 1
make[2]: Leaving directory '/home/linville/git/ethtool/ethtool-5.0/_build/sub'
make[1]: *** [Makefile:2438: install-am] Error 2
make[1]: Leaving directory '/home/linville/git/ethtool/ethtool-5.0/_build/sub'
make: *** [Makefile:2347: distcheck] Error 1
It looks like somewhere you are using "$(bashcompletiondir)" instead of
"$(DESTDIR)$(bashcompletiondir)", but I can't seem to find it. Perhaps
you can find the change required to avoid this build error?
Thanks,
John
--
John W. Linville Someday the world will need a hero, and you
linville@tuxdriver.com might be all we have. Be ready.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2] ethtool: Add bash-completion script
2019-04-16 18:37 ` John W. Linville
@ 2019-04-17 2:53 ` Kevin Locke
2019-04-18 16:05 ` John W. Linville
0 siblings, 1 reply; 9+ messages in thread
From: Kevin Locke @ 2019-04-17 2:53 UTC (permalink / raw)
To: John W. Linville; +Cc: netdev, Jesse Brandeburg
On Tue, 2019-04-16 at 14:37 -0400, John W. Linville wrote:
> Overall, it looks good to me. But when I build with "make distcheck",
> I get this output:
>
> [...]
>
> It looks like somewhere you are using "$(bashcompletiondir)" instead of
> "$(DESTDIR)$(bashcompletiondir)", but I can't seem to find it. Perhaps
> you can find the change required to avoid this build error?
Thanks for taking a look at it! Good catch. DESTDIR would have been
my guess as well. Interestingly, the issue is that make distcheck
expects `./configure --prefix=foo && make install` to only install
files below foo, which fails because --with-bash-completion-dir
defaults to `pkg-config --variable=completionsdir bash-completion`
(usually /usr/share/bash-completion/completions).
I can think of a few different ways to fix this:
1. Add =--without-bash-completion-dir or
--with-bashcompletiondir=$something to DISTCHECK_CONFIGURE_FLAGS
in Makefile.am to avoid installing the script during distcheck.
2. Replace the prefix for bash-completion (using
`pkg-config --variable=prefix bash-completion`) in
$bashcompletiondir with --prefix passed to configure.
3. Stop using pkg-config and install to
$datadir/bash-completion/completions by default.
Option 1 has the disadvantage that users may not expect files to be
installed outside of --prefix, that the script does not install to
/usr/local (with everything else) by default, and that
--with-bash-completion-dir= must be passed for non-root installs.
kmod passes
--with-bashcompletiondir=$$dc_install_base/$(bashcompletiondir)
to DISTCHECK_CONFIGURE_FLAGS, which has the additional disadvantage of
using the undocumented (AFAICT) Automake $$dc_install_base variable.
Options 2 and 3 have the disadvantage that passing --prefix= which is
not the prefix of $XDG_DATA_HOME or $XDG_DATA_DIRS will install the
script to a directory that bash-completion doesn't use.
Option 3 has the additional disadvantage of ignoring the upstream
recommendations, which could install the script to a directory not
used by bash-completion for customized or future versions. It has the
advantage of being extremely simple and understandable.
My personal preference is #2, but I would defer to your judgement.
Let me know which you would prefer and I'll update the patch.
Best,
Kevin
P.S. I noticed that the PKG_CHECK_MODULES macro does unnecessary work
handling BASH_COMPLETION_CFLAGS and BASH_COMPLETION_LIBS and adds them
to the configure --help text, so I will remove it in favor of calling
$PKG_CONFIG directly, unless you object.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2] ethtool: Add bash-completion script
2019-04-17 2:53 ` Kevin Locke
@ 2019-04-18 16:05 ` John W. Linville
0 siblings, 0 replies; 9+ messages in thread
From: John W. Linville @ 2019-04-18 16:05 UTC (permalink / raw)
To: Kevin Locke, netdev, Jesse Brandeburg
On Tue, Apr 16, 2019 at 08:53:33PM -0600, Kevin Locke wrote:
> On Tue, 2019-04-16 at 14:37 -0400, John W. Linville wrote:
> > Overall, it looks good to me. But when I build with "make distcheck",
> > I get this output:
> >
> > [...]
> >
> > It looks like somewhere you are using "$(bashcompletiondir)" instead of
> > "$(DESTDIR)$(bashcompletiondir)", but I can't seem to find it. Perhaps
> > you can find the change required to avoid this build error?
>
> Thanks for taking a look at it! Good catch. DESTDIR would have been
> my guess as well. Interestingly, the issue is that make distcheck
> expects `./configure --prefix=foo && make install` to only install
> files below foo, which fails because --with-bash-completion-dir
> defaults to `pkg-config --variable=completionsdir bash-completion`
> (usually /usr/share/bash-completion/completions).
>
> I can think of a few different ways to fix this:
>
> 1. Add =--without-bash-completion-dir or
> --with-bashcompletiondir=$something to DISTCHECK_CONFIGURE_FLAGS
> in Makefile.am to avoid installing the script during distcheck.
> 2. Replace the prefix for bash-completion (using
> `pkg-config --variable=prefix bash-completion`) in
> $bashcompletiondir with --prefix passed to configure.
> 3. Stop using pkg-config and install to
> $datadir/bash-completion/completions by default.
>
> Option 1 has the disadvantage that users may not expect files to be
> installed outside of --prefix, that the script does not install to
> /usr/local (with everything else) by default, and that
> --with-bash-completion-dir= must be passed for non-root installs.
> kmod passes
> --with-bashcompletiondir=$$dc_install_base/$(bashcompletiondir)
> to DISTCHECK_CONFIGURE_FLAGS, which has the additional disadvantage of
> using the undocumented (AFAICT) Automake $$dc_install_base variable.
>
> Options 2 and 3 have the disadvantage that passing --prefix= which is
> not the prefix of $XDG_DATA_HOME or $XDG_DATA_DIRS will install the
> script to a directory that bash-completion doesn't use.
>
> Option 3 has the additional disadvantage of ignoring the upstream
> recommendations, which could install the script to a directory not
> used by bash-completion for customized or future versions. It has the
> advantage of being extremely simple and understandable.
>
> My personal preference is #2, but I would defer to your judgement.
> Let me know which you would prefer and I'll update the patch.
>
> Best,
> Kevin
>
> P.S. I noticed that the PKG_CHECK_MODULES macro does unnecessary work
> handling BASH_COMPLETION_CFLAGS and BASH_COMPLETION_LIBS and adds them
> to the configure --help text, so I will remove it in favor of calling
> $PKG_CONFIG directly, unless you object.
Option #2 seems reasonable, and your "P.S." seems fine too.
Thanks,
John
--
John W. Linville Someday the world will need a hero, and you
linville@tuxdriver.com might be all we have. Be ready.
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v3] ethtool: Add bash-completion script
2019-04-11 17:39 ` [PATCH v2] " Kevin Locke
2019-04-16 18:37 ` John W. Linville
@ 2019-04-20 0:16 ` Kevin Locke
2019-04-24 19:54 ` John W. Linville
1 sibling, 1 reply; 9+ messages in thread
From: Kevin Locke @ 2019-04-20 0:16 UTC (permalink / raw)
To: John W. Linville; +Cc: netdev, Jesse Brandeburg
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 named shell-completion/bash/ethtool, similar to [kmod],
and installed to `pkg-config --variable=completionsdir bash-completion`
(with $prefix replaced by --prefix of ./configure and fallback to
$datadir/bash-completion/completions) by default. This can be disabled
by passing --without-bash-completion-dir or changed by passing
--with-bash-completion-dir=$anypath to ./configure. It requires
pkg-config 0.18 or later to be installed on the build system which runs
aclocal (for the PKG_PROG_PKG_CONFIG m4 macro).
To install the script manually for the current user, copy or link
shell-completion/bash to $BASH_COMPLETION_USER_DIR/completions
(default $XDG_DATA_HOME/bash-completion/completions
(default ~/.local/share/bash-completion/completions)).
To install system-wide, copy shell-completion/bash to completionsdir
from pkg-config (default /usr/share/bash-completion/completions)
discussed above. Restarting bash may be necessary to pick up changes to
the script (if a previous version had already been loaded).
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@kevinlocke.name>
Reviewed-by: Jesse Brandeburg <jesse.brandeburg@intel.com>
---
Changes in v3:
* Apply --prefix to completionsdir for `make distcheck` and expectations.
* Stop using PKG_CHECK_MODULES due to unnecessary _CFLAGS/LIBS vars.
Use PKG_PROG_PKG_CONFIG then call $PKG_CONFIG directly.
Changes in v2:
* Describe manual install and ./configure arguments in commit message.
Makefile.am | 5 +
configure.ac | 26 +
shell-completion/bash/ethtool | 1251 +++++++++++++++++++++++++++++++++
3 files changed, 1282 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..3c0686c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -11,6 +11,7 @@ dnl Checks for programs.
AC_PROG_CC
AC_PROG_GCC_TRADITIONAL
AM_PROG_CC_C_O
+PKG_PROG_PKG_CONFIG
dnl Checks for libraries.
@@ -40,5 +41,30 @@ 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],
+ [AC_MSG_CHECKING([for bash-completion directory])
+ dnl Attempt to use pkg-config completionsdir variable with given $prefix.
+ dnl This matches distcheck expectation that all files install to $prefix.
+ dnl It works with /usr and /usr/local (for default $XDG_DATA_DIRS) but
+ dnl may install to directory not used by bash-completion in other cases.
+ dnl See: https://lore.kernel.org/netdev/20190417025333.GA28674@kevinolos/
+ AS_IF([test "x$PKG_CONFIG" != x \
+ && bash_completion_prefix=`"$PKG_CONFIG" --print-errors --variable=prefix bash-completion 2>&AS_MESSAGE_LOG_FD` \
+ && bash_completion_dir=`"$PKG_CONFIG" --print-errors --variable=completionsdir bash-completion 2>&AS_MESSAGE_LOG_FD`],
+ [bash_completion_dir="${bash_completion_dir#"$bash_completion_prefix"}"
+ bash_completion_dir="${bash_completion_dir#/}"
+ BASH_COMPLETION_DIR='${prefix}'/"$bash_completion_dir"],
+ [BASH_COMPLETION_DIR='${datadir}/bash-completion/completions'])
+ AC_MSG_RESULT([$BASH_COMPLETION_DIR])],
+ [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
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v3] ethtool: Add bash-completion script
2019-04-20 0:16 ` [PATCH v3] " Kevin Locke
@ 2019-04-24 19:54 ` John W. Linville
0 siblings, 0 replies; 9+ messages in thread
From: John W. Linville @ 2019-04-24 19:54 UTC (permalink / raw)
To: Kevin Locke; +Cc: netdev, Jesse Brandeburg
On Fri, Apr 19, 2019 at 06:16:21PM -0600, Kevin Locke wrote:
> 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 named shell-completion/bash/ethtool, similar to [kmod],
> and installed to `pkg-config --variable=completionsdir bash-completion`
> (with $prefix replaced by --prefix of ./configure and fallback to
> $datadir/bash-completion/completions) by default. This can be disabled
> by passing --without-bash-completion-dir or changed by passing
> --with-bash-completion-dir=$anypath to ./configure. It requires
> pkg-config 0.18 or later to be installed on the build system which runs
> aclocal (for the PKG_PROG_PKG_CONFIG m4 macro).
>
> To install the script manually for the current user, copy or link
> shell-completion/bash to $BASH_COMPLETION_USER_DIR/completions
> (default $XDG_DATA_HOME/bash-completion/completions
> (default ~/.local/share/bash-completion/completions)).
> To install system-wide, copy shell-completion/bash to completionsdir
> from pkg-config (default /usr/share/bash-completion/completions)
> discussed above. Restarting bash may be necessary to pick up changes to
> the script (if a previous version had already been loaded).
>
> 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@kevinlocke.name>
> Reviewed-by: Jesse Brandeburg <jesse.brandeburg@intel.com>
> ---
>
> Changes in v3:
> * Apply --prefix to completionsdir for `make distcheck` and expectations.
> * Stop using PKG_CHECK_MODULES due to unnecessary _CFLAGS/LIBS vars.
> Use PKG_PROG_PKG_CONFIG then call $PKG_CONFIG directly.
>
> Changes in v2:
> * Describe manual install and ./configure arguments in commit message.
LGTM -- queued for next release.
Thanks,
John
--
John W. Linville Someday the world will need a hero, and you
linville@tuxdriver.com might be all we have. Be ready.
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2019-04-24 20:00 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-04-08 2:19 [PATCH] ethtool: Add bash-completion script Kevin Locke
2019-04-11 16:14 ` Jesse Brandeburg
2019-04-11 16:47 ` Kevin Locke
2019-04-11 17:39 ` [PATCH v2] " Kevin Locke
2019-04-16 18:37 ` John W. Linville
2019-04-17 2:53 ` Kevin Locke
2019-04-18 16:05 ` John W. Linville
2019-04-20 0:16 ` [PATCH v3] " Kevin Locke
2019-04-24 19:54 ` John W. Linville
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.