summaryrefslogtreecommitdiffstats
path: root/bash-completion
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--bash-completion/devlink1104
-rw-r--r--bash-completion/tc809
2 files changed, 1913 insertions, 0 deletions
diff --git a/bash-completion/devlink b/bash-completion/devlink
new file mode 100644
index 0000000..52dc82b
--- /dev/null
+++ b/bash-completion/devlink
@@ -0,0 +1,1104 @@
+# bash completion for devlink(8) -*- shell-script -*-
+
+# Get all the optional commands for devlink
+_devlink_get_optional_commands()
+{
+ local object=$1; shift
+
+ local filter_options=""
+ local options="$(devlink $object help 2>&1 \
+ | command sed -n -e "s/^.*devlink $object //p" \
+ | cut -d " " -f 1)"
+
+ # Remove duplicate options from "devlink $OBJECT help" command
+ local opt
+ for opt in $options; do
+ if [[ $filter_options =~ $opt ]]; then
+ continue
+ else
+ filter_options="$filter_options $opt"
+ fi
+ done
+
+ echo $filter_options
+}
+
+# Complete based on given word, for when an argument or an option name has
+# but a few possible arguments.
+_devlink_direct_complete()
+{
+ local dev port region value
+
+ case $1 in
+ dev)
+ value=$(devlink dev show 2>/dev/null)
+ ;;
+ selftests_id)
+ dev=${words[4]}
+ value=$(devlink -j dev selftests show 2>/dev/null \
+ | jq ".selftests[\"$dev\"][]")
+ ;;
+ param_name)
+ dev=${words[4]}
+ value=$(devlink -j dev param show 2>/dev/null \
+ | jq ".param[\"$dev\"][].name")
+ ;;
+ port)
+ value=$(devlink -j port show 2>/dev/null \
+ | jq '.port as $ports | $ports | keys[] as $key
+ | ($ports[$key].netdev // $key)')
+ ;;
+ lc)
+ dev=${words[3]}
+ value=$(devlink -j lc show 2>/dev/null \
+ | jq ".lc[\"$dev\"]" \
+ | jq '. as $lcs | $lcs | keys[] as $key |($lcs[$key].lc)' \
+ 2>/dev/null)
+ ;;
+ lc_type)
+ dev=${words[3]}
+ lc=${words[5]}
+ value=$(devlink lc show $dev lc $lc -j 2>/dev/null \
+ | jq '.[][][]["supported_types"][]')
+ ;;
+ region)
+ value=$(devlink -j region show 2>/dev/null \
+ | jq '.regions' | jq 'keys[]')
+ ;;
+ snapshot)
+ region=${words[3]}
+ value=$(devlink -j region show 2>/dev/null \
+ | jq ".regions[\"$region\"].snapshot[]")
+ ;;
+ trap)
+ dev=${words[3]}
+ value=$(devlink -j trap show 2>/dev/null \
+ | jq ".trap[\"$dev\"][].name")
+ ;;
+ trap_group)
+ dev=${words[4]}
+ value=$(devlink -j trap group show 2>/dev/null \
+ | jq ".trap_group[\"$dev\"][].name")
+ ;;
+ trap_policer)
+ dev=${words[4]}
+ value=$(devlink -j trap policer show 2>/dev/null \
+ | jq ".trap_policer[\"$dev\"][].policer")
+ ;;
+ health_dev)
+ value=$(devlink -j health show 2>/dev/null | jq '.health' \
+ | jq 'keys[]')
+ ;;
+ reporter)
+ dev=${words[cword - 2]}
+ value=$(devlink -j health show 2>/dev/null \
+ | jq ".health[\"$dev\"][].reporter")
+ ;;
+ pool)
+ dev=$pprev
+ value=$(devlink -j sb pool show 2>/dev/null \
+ | jq ".pool[\"$dev\"][].pool")
+ ;;
+ port_pool)
+ port=${words[5]}
+ value=$(devlink -j sb port pool show 2>/dev/null \
+ | jq ".port_pool[\"$port\"][].pool")
+ ;;
+ tc)
+ port=$pprev
+ value=$(devlink -j sb tc bind show 2>/dev/null \
+ | jq ".tc_bind[\"$port\"][].tc")
+ ;;
+ esac
+
+ COMPREPLY+=( $( compgen -W "$value" -- "$cur" ) )
+ # Remove colon containing prefix from COMPREPLY items in order to avoid
+ # wordbreaks with colon.
+ __ltrim_colon_completions "$cur"
+}
+
+# Completion for devlink dev eswitch set
+_devlink_dev_eswitch_set()
+{
+ local -A settings=(
+ [mode]=notseen
+ [inline-mode]=notseen
+ [encap-mode]=notseen
+ )
+
+ if [[ $cword -eq 5 ]]; then
+ COMPREPLY=( $( compgen -W "mode inline-mode encap-mode" -- "$cur" ) )
+ fi
+
+ # Mark seen settings
+ local word
+ for word in "${words[@]:5:${#words[@]}-1}"; do
+ if [[ -n $word ]]; then
+ if [[ "${settings[$word]}" ]]; then
+ settings[$word]=seen
+ fi
+ fi
+ done
+
+ case $prev in
+ mode)
+ COMPREPLY=( $( compgen -W "legacy switchdev" -- "$cur" ) )
+ return
+ ;;
+ inline-mode)
+ COMPREPLY=( $( compgen -W "none link network transport" -- \
+ "$cur" ) )
+ return
+ ;;
+ encap-mode)
+ COMPREPLY=( $( compgen -W "none basic" -- "$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
+
+ COMPREPLY=( $( compgen -W "${comp_words[*]}" -- "$cur" ) )
+}
+
+# Completion for devlink dev eswitch
+_devlink_dev_eswitch()
+{
+ case "$cword" in
+ 3)
+ COMPREPLY=( $( compgen -W "show set" -- "$cur" ) )
+ return
+ ;;
+ 4)
+ _devlink_direct_complete "dev"
+ return
+ ;;
+ esac
+
+ case "${words[3]}" in
+ set)
+ _devlink_dev_eswitch_set
+ return
+ ;;
+ show)
+ return
+ ;;
+ esac
+}
+
+# Completion for devlink dev param set
+_devlink_dev_param_set()
+{
+ case $cword in
+ 7)
+ COMPREPLY=( $( compgen -W "value" -- "$cur" ) )
+ return
+ ;;
+ 8)
+ # String argument
+ return
+ ;;
+ 9)
+ COMPREPLY=( $( compgen -W "cmode" -- "$cur" ) )
+ return
+ ;;
+ 10)
+ COMPREPLY=( $( compgen -W "runtime driverinit permanent" -- \
+ "$cur" ) )
+ return
+ ;;
+ esac
+}
+
+# Completion for devlink dev param
+_devlink_dev_param()
+{
+ case "$cword" in
+ 3)
+ COMPREPLY=( $( compgen -W "show set" -- "$cur" ) )
+ return
+ ;;
+ 4)
+ _devlink_direct_complete "dev"
+ return
+ ;;
+ 5)
+ COMPREPLY=( $( compgen -W "name" -- "$cur" ) )
+ return
+ ;;
+ 6)
+ _devlink_direct_complete "param_name"
+ return
+ ;;
+ esac
+
+ if [[ "${words[3]}" == "set" ]]; then
+ _devlink_dev_param_set
+ fi
+}
+
+# Completion for devlink dev reload
+_devlink_dev_reload()
+{
+ case "$cword" in
+ 4)
+ COMPREPLY=( $( compgen -W "netns" -- "$cur" ) )
+ return
+ ;;
+ 5)
+ local nslist=$( ip netns list 2>/dev/null )
+ COMPREPLY=( $( compgen -W "$nslist" -- "$cur" ) )
+ return
+ ;;
+ esac
+}
+
+# Completion for devlink dev flash
+_devlink_dev_flash()
+{
+ case "$cword" in
+ 4)
+ COMPREPLY=( $( compgen -W "file" -- "$cur" ) )
+ return
+ ;;
+ 5)
+ _filedir
+ return
+ ;;
+ 6)
+ COMPREPLY=( $( compgen -W "component" -- "$cur" ) )
+ return
+ ;;
+ esac
+}
+
+# Completion for devlink dev selftests
+_devlink_dev_selftests()
+{
+ if [[ $cword -gt 5 ]]; then
+ _devlink_direct_complete "selftests_id"
+ return
+ fi
+ case "$cword" in
+ 3)
+ COMPREPLY=( $( compgen -W "show run" -- "$cur" ) )
+ return
+ ;;
+ 4)
+ _devlink_direct_complete "dev"
+ return
+ ;;
+ 5)
+ COMPREPLY=( $( compgen -W "id" -- "$cur" ) )
+ return
+ ;;
+ esac
+}
+
+# Completion for devlink dev
+_devlink_dev()
+{
+ case $command in
+ show|reload|info|flash)
+ if [[ $cword -le 3 ]]; then
+ _devlink_direct_complete "dev"
+ elif [[ $command == "reload" || $command == "flash" ]];then
+ _devlink_dev_$command
+ fi
+ return
+ ;;
+ eswitch|param|selftests)
+ _devlink_dev_$command
+ return
+ ;;
+ esac
+}
+
+# Completion for devlink port set
+_devlink_port_set()
+{
+ case "$cword" in
+ 3)
+ _devlink_direct_complete "port"
+ return
+ ;;
+ 4)
+ COMPREPLY=( $( compgen -W "type" -- "$cur" ) )
+ return
+ ;;
+ 5)
+ COMPREPLY=( $( compgen -W "eth ib auto" -- "$cur" ) )
+ return
+ ;;
+ esac
+}
+
+# Completion for devlink port split
+_devlink_port_split()
+{
+ case "$cword" in
+ 3)
+ _devlink_direct_complete "port"
+ return
+ ;;
+ 4)
+ COMPREPLY=( $( compgen -W "count" -- "$cur" ) )
+ return
+ ;;
+ 5)
+ # Integer argument
+ return
+ ;;
+ esac
+}
+
+# Completion for devlink port param set
+_devlink_port_param_set()
+{
+ case $cword in
+ 7)
+ COMPREPLY=( $( compgen -W "value" -- "$cur" ) )
+ return
+ ;;
+ 8)
+ # String argument
+ return
+ ;;
+ 9)
+ COMPREPLY=( $( compgen -W "cmode" -- "$cur" ) )
+ return
+ ;;
+ 10)
+ COMPREPLY=( $( compgen -W "runtime driverinit permanent" -- \
+ "$cur" ) )
+ return
+ ;;
+ esac
+}
+
+# Completion for devlink port param
+_devlink_port_param()
+{
+ case "$cword" in
+ 3)
+ COMPREPLY=( $( compgen -W "show set" -- "$cur" ) )
+ return
+ ;;
+ 4)
+ _devlink_direct_complete "port"
+ return
+ ;;
+ 5)
+ COMPREPLY=( $( compgen -W "name" -- "$cur" ) )
+ return
+ ;;
+ 6)
+ _devlink_direct_complete "param_name"
+ return
+ ;;
+ esac
+
+ if [[ "${words[3]}" == "set" ]]; then
+ _devlink_port_param_set
+ fi
+}
+
+# Completion for devlink port
+_devlink_port()
+{
+ case $command in
+ set)
+ _devlink_port_set
+ return
+ ;;
+ split)
+ _devlink_port_split
+ return
+ ;;
+ param)
+ _devlink_port_param
+ return
+ ;;
+ show|unsplit)
+ if [[ $cword -eq 3 ]]; then
+ _devlink_direct_complete "port"
+ fi
+ return
+ ;;
+ esac
+}
+
+# Completion for devlink lc set
+_devlink_lc_set()
+{
+ case "$cword" in
+ 3)
+ _devlink_direct_complete "dev"
+ return
+ ;;
+ 4)
+ COMPREPLY=( $( compgen -W "lc" -- "$cur" ) )
+ ;;
+ 5)
+ _devlink_direct_complete "lc"
+ ;;
+ 6)
+ COMPREPLY=( $( compgen -W "type notype" -- "$cur" ) )
+ return
+ ;;
+ 7)
+ if [[ "$prev" == "type" ]]; then
+ _devlink_direct_complete "lc_type"
+ fi
+ esac
+}
+
+# Completion for devlink lc show
+_devlink_lc_show()
+{
+ case $cword in
+ 3)
+ _devlink_direct_complete "dev"
+ ;;
+ 4)
+ COMPREPLY=( $( compgen -W "lc" -- "$cur" ) )
+ ;;
+ 5)
+ _devlink_direct_complete "lc"
+ ;;
+ esac
+}
+
+# Completion for devlink lc
+_devlink_lc()
+{
+ case $command in
+ set)
+ _devlink_lc_set
+ return
+ ;;
+ show)
+ _devlink_lc_show
+ return
+ ;;
+ esac
+}
+
+# Completion for devlink dpipe
+_devlink_dpipe()
+{
+ local options="$(devlink dpipe help 2>&1 \
+ | command sed -e '/OBJECT-LIST := /!d' \
+ -e 's/.*{ //' -e 's/}.*//' -e 's/|//g' )"
+
+ if [[ $cword -eq 2 ]]; then
+ COMPREPLY+=( $( compgen -W "$options" -- "$cur" ) )
+ fi
+}
+
+# Completion for devlink monitor
+_devlink_monitor()
+{
+ local options="$(devlink monitor help 2>&1 \
+ | command sed -e '/OBJECT-LIST := /!d' \
+ -e 's/.*{ //' -e 's/}.*//' -e 's/|//g' )"
+
+ if [[ $cword -eq 2 ]]; then
+ COMPREPLY+=( $( compgen -W "all $options" -- "$cur" ) )
+ fi
+}
+
+# Completion for the rest of devlink sb $command
+_devlink_sb_command_options()
+{
+ local subcmd
+
+ case $command in
+ pool)
+ subcmd=${words[3]}
+ if [[ $cword -eq 5 ]]; then
+ COMPREPLY=( $( compgen -W "pool" -- "$cur" ) )
+ fi
+ if [[ $subcmd == "set" ]]; then
+ case $cword in
+ 7)
+ COMPREPLY+=( $( compgen -W "size" -- "$cur" ) )
+ ;;
+ 9)
+ COMPREPLY+=( $( compgen -W "thtype" -- "$cur" ) )
+ ;;
+ esac
+ fi
+ ;;
+ port)
+ subcmd=${words[4]}
+ if [[ $cword -eq 6 ]]; then
+ COMPREPLY+=( $( compgen -W "pool" -- "$cur" ) )
+ fi
+ if [[ $subcmd == "set" ]]; then
+ case $cword in
+ 8)
+ COMPREPLY+=( $( compgen -W "th" -- "$cur" ) )
+ ;;
+ esac
+ fi
+ ;;
+ tc)
+ subcmd=${words[4]}
+ case $cword in
+ 6)
+ COMPREPLY+=( $( compgen -W "tc" -- "$cur" ) )
+ ;;
+ 8)
+ COMPREPLY+=( $( compgen -W "type" -- "$cur" ) )
+ ;;
+ esac
+ if [[ $subcmd == "set" ]]; then
+ case $cword in
+ 10)
+ COMPREPLY+=( $( compgen -W "pool" -- "$cur" ) )
+ ;;
+ 12)
+ COMPREPLY+=( $( compgen -W "th" -- "$cur" ) )
+ ;;
+ esac
+ fi
+ ;;
+ esac
+}
+
+# Completion for devlink sb
+_devlink_sb()
+{
+ case $prev in
+ bind)
+ COMPREPLY=( $( compgen -W "set show" -- "$cur" ) )
+ ;;
+ occupancy)
+ COMPREPLY=( $( compgen -W "show snapshot clearmax" -- "$cur" ) )
+ ;;
+ pool)
+ if [[ $cword -eq 3 || $cword -eq 4 ]]; then
+ COMPREPLY=( $( compgen -W "set show" -- "$cur" ) )
+ elif [[ $command == "port" || $command == "tc" ]]; then
+ _devlink_direct_complete "port_pool"
+ else
+ _devlink_direct_complete "pool"
+ fi
+ ;;
+ port)
+ if [[ $cword -eq 3 ]]; then
+ COMPREPLY=( $( compgen -W "pool" -- "$cur" ) )
+ fi
+ ;;
+ show|set|snapshot|clearmax)
+ case $command in
+ show|pool|occupancy)
+ _devlink_direct_complete "dev"
+ if [[ $command == "occupancy" && $prev == "show" ]];then
+ _devlink_direct_complete "port"
+ fi
+ ;;
+ port|tc)
+ _devlink_direct_complete "port"
+ ;;
+ esac
+ ;;
+ size)
+ # Integer argument
+ ;;
+ thtype)
+ COMPREPLY=( $( compgen -W "static dynamic" -- "$cur" ) )
+ ;;
+ th)
+ # Integer argument
+ ;;
+ tc)
+ if [[ $cword -eq 3 ]]; then
+ COMPREPLY=( $( compgen -W "bind" -- "$cur" ) )
+ else
+ _devlink_direct_complete "tc"
+ fi
+ ;;
+ type)
+ COMPREPLY=( $( compgen -W "ingress egress" -- "$cur" ) )
+ ;;
+ esac
+
+ _devlink_sb_command_options
+ return
+}
+
+# Completion for devlink resource set path argument
+_devlink_resource_path()
+{
+ local path parents parent all_path
+ local dev=${words[3]}
+ local -a path
+
+ local all_path=$(
+ devlink resource show $dev \
+ | sed -E '# Of resource lines, keep only the name itself.
+ s/name ([^ ]*) .*/\1/
+ # Drop headers.
+ /:$/d
+ # First layer is not aligned enough, align it.
+ s/^/ /
+ # Use slashes as unary code for resource depth.
+ s, ,/,g
+ # Separate tally count from resource name.
+ s,/*,&\t,' \
+ | while read d name; do
+ while ((${#path[@]} > ${#d})); do
+ unset path[$((${#path[@]} - 1))]
+ done
+ path[$((${#d} - 1))]=$name
+ echo ${path[@]}
+ done \
+ | sed '# Convert paths to slash-separated
+ s,^,/,;s, ,/,g;s,$,/,'
+ )
+ COMPREPLY=( ${COMPREPLY[@]:-} $( compgen -W "$all_path" -- "$cur" ) )
+}
+
+# Completion for devlink resource set
+_devlink_resource_set()
+{
+ case "$cword" in
+ 3)
+ _devlink_direct_complete "dev"
+ return
+ ;;
+ 4)
+ COMPREPLY=( $( compgen -W "path" -- "$cur" ) )
+ return
+ ;;
+ 5)
+ _devlink_resource_path
+ return
+ ;;
+ 6)
+ COMPREPLY=( $( compgen -W "size" -- "$cur" ) )
+ return
+ ;;
+ 7)
+ # Integer argument
+ return
+ ;;
+ esac
+}
+
+# Completion for devlink resource
+_devlink_resource()
+{
+ case $command in
+ show)
+ if [[ $cword -eq 3 ]]; then
+ _devlink_direct_complete "dev"
+ fi
+ return
+ ;;
+ set)
+ _devlink_resource_set
+ return
+ ;;
+ esac
+}
+
+# Completion for devlink region read
+_devlink_region_read()
+{
+ case "$cword" in
+ 6)
+ COMPREPLY=( $( compgen -W "address" -- "$cur" ) )
+ return
+ ;;
+ 7)
+ # Address argument, for example: 0x10
+ return
+ ;;
+ 8)
+ COMPREPLY=( $( compgen -W "length" -- "$cur" ) )
+ return
+ ;;
+ 9)
+ # Integer argument
+ return
+ ;;
+ esac
+}
+
+# Completion for devlink region
+_devlink_region()
+{
+ if [[ $cword -eq 3 && $command != "help" ]]; then
+ _devlink_direct_complete "region"
+ fi
+
+ case $command in
+ show)
+ return
+ ;;
+ del|dump|read)
+ case "$cword" in
+ 4)
+ COMPREPLY=( $( compgen -W "snapshot" -- "$cur" ) )
+ ;;
+ 5)
+ _devlink_direct_complete "snapshot"
+ ;;
+ esac
+
+ if [[ $command == "read" ]]; then
+ _devlink_region_read
+ fi
+ return
+ ;;
+ esac
+}
+
+# Completion reporter for devlink health
+_devlink_health_reporter()
+{
+ local i=$1; shift
+
+ case $cword in
+ $((3 + $i)))
+ _devlink_direct_complete "health_dev"
+ ;;
+ $((4 + $i)))
+ COMPREPLY=( $( compgen -W "reporter" -- "$cur" ) )
+ ;;
+ $((5 + $i)))
+ _devlink_direct_complete "reporter"
+ ;;
+ esac
+}
+
+# Completion for devlink health
+_devlink_health()
+{
+ case $command in
+ show|recover|diagnose|set|test)
+ _devlink_health_reporter 0
+ if [[ $command == "set" ]]; then
+ case $cword in
+ 6)
+ COMPREPLY=( $( compgen -W "grace_period auto_recover" \
+ -- "$cur" ) )
+ ;;
+ 7)
+ case $prev in
+ grace_period)
+ # Integer argument- msec
+ ;;
+ auto_recover)
+ COMPREPLY=( $( compgen -W "true false" -- \
+ "$cur" ) )
+ ;;
+ esac
+ esac
+ fi
+ return
+ ;;
+ dump)
+ if [[ $cword -eq 3 ]]; then
+ COMPREPLY=( $( compgen -W "show clear" -- "$cur" ) )
+ fi
+
+ _devlink_health_reporter 1
+ return
+ ;;
+ esac
+}
+
+# Completion for action in devlink trap set
+_devlink_trap_set_action()
+{
+ local i=$1; shift
+
+ case $cword in
+ $((6 + $i)))
+ COMPREPLY=( $( compgen -W "action" -- "$cur" ) )
+ ;;
+ $((7 + $i)))
+ COMPREPLY=( $( compgen -W "trap drop mirror" -- "$cur" ) )
+ ;;
+ esac
+}
+
+# Completion for devlink trap group set
+_devlink_trap_group_set()
+{
+ local -A settings=(
+ [action]=notseen
+ [policer]=notseen
+ [nopolicer]=notseen
+ )
+
+ if [[ $cword -eq 7 ]]; then
+ COMPREPLY=( $( compgen -W "action policer nopolicer" -- "$cur" ) )
+ fi
+
+ # Mark seen settings
+ local word
+ for word in "${words[@]:7:${#words[@]}-1}"; do
+ if [[ -n $word ]]; then
+ if [[ "${settings[$word]}" ]]; then
+ settings[$word]=seen
+ fi
+ fi
+ done
+
+ case $prev in
+ action)
+ COMPREPLY=( $( compgen -W "trap drop mirror" -- "$cur" ) )
+ return
+ ;;
+ policer)
+ _devlink_direct_complete "trap_policer"
+ 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
+
+ COMPREPLY=( $( compgen -W "${comp_words[*]}" -- "$cur" ) )
+}
+
+# Completion for devlink trap group
+_devlink_trap_group()
+{
+ case $cword in
+ 3)
+ COMPREPLY=( $( compgen -W "set show" -- "$cur" ) )
+ return
+ ;;
+ 4)
+ _devlink_direct_complete "dev"
+ return
+ ;;
+ 5)
+ COMPREPLY=( $( compgen -W "group" -- "$cur" ) )
+ return
+ ;;
+ 6)
+ _devlink_direct_complete "trap_group"
+ return
+ ;;
+ esac
+
+ if [[ ${words[3]} == "set" ]]; then
+ _devlink_trap_group_set
+ fi
+}
+
+# Completion for devlink trap policer set
+_devlink_trap_policer_set()
+{
+ local -A settings=(
+ [rate]=notseen
+ [burst]=notseen
+ )
+
+ if [[ $cword -eq 7 ]]; then
+ COMPREPLY=( $( compgen -W "rate burst" -- "$cur" ) )
+ fi
+
+ # Mark seen settings
+ local word
+ for word in "${words[@]:7:${#words[@]}-1}"; do
+ if [[ -n $word ]]; then
+ if [[ "${settings[$word]}" ]]; then
+ settings[$word]=seen
+ fi
+ fi
+ done
+
+ case $prev in
+ rate)
+ # Integer argument
+ return
+ ;;
+ burst)
+ # Integer argument
+ 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
+
+ COMPREPLY=( $( compgen -W "${comp_words[*]}" -- "$cur" ) )
+}
+
+# Completion for devlink trap policer
+_devlink_trap_policer()
+{
+ case $cword in
+ 3)
+ COMPREPLY=( $( compgen -W "set show" -- "$cur" ) )
+ return
+ ;;
+ 4)
+ _devlink_direct_complete "dev"
+ return
+ ;;
+ 5)
+ COMPREPLY=( $( compgen -W "policer" -- "$cur" ) )
+ return
+ ;;
+ 6)
+ _devlink_direct_complete "trap_policer"
+ return
+ ;;
+ esac
+
+ if [[ ${words[3]} == "set" ]]; then
+ _devlink_trap_policer_set
+ fi
+}
+
+# Completion for devlink trap
+_devlink_trap()
+{
+ case $command in
+ show|set)
+ case $cword in
+ 3)
+ _devlink_direct_complete "dev"
+ ;;
+ 4)
+ COMPREPLY=( $( compgen -W "trap" -- "$cur" ) )
+ ;;
+ 5)
+ _devlink_direct_complete "trap"
+ ;;
+ esac
+
+ if [[ $command == "set" ]]; then
+ _devlink_trap_set_action 0
+ fi
+ return
+ ;;
+ group)
+ _devlink_trap_$command
+ return
+ ;;
+ policer)
+ _devlink_trap_$command
+ return
+ ;;
+ esac
+}
+
+# Complete any devlink command
+_devlink()
+{
+ local cur prev words cword
+ local opt='--Version --no-nice-names --json --pretty --verbose \
+ --statistics --force --Netns --batch'
+ local objects="$(devlink help 2>&1 | command sed -e '/OBJECT := /!d' \
+ -e 's/.*{//' -e 's/}.*//' -e \ 's/|//g' )"
+
+ _init_completion || return
+ # Gets the word-to-complete without considering the colon as word breaks
+ _get_comp_words_by_ref -n : cur prev words cword
+
+ if [[ $cword -eq 1 ]]; then
+ case $cur in
+ -*)
+ COMPREPLY=( $( compgen -W "$opt" -- "$cur" ) )
+ return 0
+ ;;
+ *)
+ COMPREPLY=( $( compgen -W "$objects" -- "$cur" ) )
+ return 0
+ ;;
+ esac
+ fi
+
+ # Deal with options
+ if [[ $prev == -* ]]; then
+ case $prev in
+ -V|--Version)
+ return 0
+ ;;
+ -b|--batch)
+ _filedir
+ return 0
+ ;;
+ --force)
+ COMPREPLY=( $( compgen -W "--batch" -- "$cur" ) )
+ return 0
+ ;;
+ -N|--Netns)
+ local nslist=$( ip netns list 2>/dev/null )
+ COMPREPLY=( $( compgen -W "$nslist" -- "$cur" ) )
+ return 0
+ ;;
+ -j|--json)
+ COMPREPLY=( $( compgen -W "--pretty $objects" -- "$cur" ) )
+ return 0
+ ;;
+ *)
+ COMPREPLY=( $( compgen -W "$objects" -- "$cur" ) )
+ return 0
+ ;;
+ esac
+ fi
+
+ # Remove all options so completions don't have to deal with them.
+ local i
+ for (( i=1; i < ${#words[@]}; )); do
+ if [[ ${words[i]::1} == - ]]; then
+ words=( "${words[@]:0:i}" "${words[@]:i+1}" )
+ [[ $i -le $cword ]] && cword=$(( cword - 1 ))
+ else
+ i=$(( ++i ))
+ fi
+ done
+
+ local object=${words[1]}
+ local command=${words[2]}
+ local pprev=${words[cword - 2]}
+ local prev=${words[cword - 1]}
+
+ if [[ $objects =~ $object ]]; then
+ if [[ $cword -eq 2 ]]; then
+ COMPREPLY=( $( compgen -W "help" -- "$cur") )
+ if [[ $object != "monitor" && $object != "dpipe" ]]; then
+ COMPREPLY+=( $( compgen -W \
+ "$(_devlink_get_optional_commands $object)" -- "$cur" ) )
+ fi
+ fi
+ "_devlink_$object"
+ fi
+
+} &&
+complete -F _devlink devlink
+
+# ex: ts=4 sw=4 et filetype=sh
diff --git a/bash-completion/tc b/bash-completion/tc
new file mode 100644
index 0000000..9f16d0d
--- /dev/null
+++ b/bash-completion/tc
@@ -0,0 +1,809 @@
+# tc(8) completion -*- shell-script -*-
+# Copyright 2016 6WIND S.A.
+# Copyright 2016 Quentin Monnet <quentin.monnet@6wind.com>
+
+QDISC_KIND=' choke codel bfifo pfifo pfifo_head_drop fq fq_codel gred hhf \
+ mqprio multiq netem pfifo_fast pie fq_pie red rr sfb sfq tbf atm \
+ cbq drr dsmark hfsc htb prio qfq '
+FILTER_KIND=' basic bpf cgroup flow flower fw route rsvp tcindex u32 matchall '
+ACTION_KIND=' gact mirred bpf sample '
+
+# Takes a list of words in argument; each one of them is added to COMPREPLY if
+# it is not already present on the command line. Returns no value.
+_tc_once_attr()
+{
+ local w subcword found
+ for w in $*; do
+ found=0
+ for (( subcword=3; subcword < ${#words[@]}-1; subcword++ )); do
+ if [[ $w == ${words[subcword]} ]]; then
+ found=1
+ break
+ fi
+ done
+ [[ $found -eq 0 ]] && \
+ COMPREPLY+=( $( compgen -W "$w" -- "$cur" ) )
+ done
+}
+
+# Takes a list of words in argument; each one of them is added to COMPREPLY if
+# it is not already present on the command line from the provided index. Returns
+# no value.
+_tc_once_attr_from()
+{
+ local w subcword found from=$1
+ shift
+ for w in $*; do
+ found=0
+ for (( subcword=$from; subcword < ${#words[@]}-1; subcword++ )); do
+ if [[ $w == ${words[subcword]} ]]; then
+ found=1
+ break
+ fi
+ done
+ [[ $found -eq 0 ]] && \
+ COMPREPLY+=( $( compgen -W "$w" -- "$cur" ) )
+ done
+}
+
+# Takes a list of words in argument; adds them all to COMPREPLY if none of them
+# is already present on the command line. Returns no value.
+_tc_one_of_list()
+{
+ local w subcword
+ for w in $*; do
+ for (( subcword=3; subcword < ${#words[@]}-1; subcword++ )); do
+ [[ $w == ${words[subcword]} ]] && return 1
+ done
+ done
+ COMPREPLY+=( $( compgen -W "$*" -- "$cur" ) )
+}
+
+# Takes a list of words in argument; adds them all to COMPREPLY if none of them
+# is already present on the command line from the provided index. Returns no
+# value.
+_tc_one_of_list_from()
+{
+ local w subcword from=$1
+ shift
+ for w in $*; do
+ for (( subcword=$from; subcword < ${#words[@]}-1; subcword++ )); do
+ [[ $w == ${words[subcword]} ]] && return 1
+ done
+ done
+ COMPREPLY+=( $( compgen -W "$*" -- "$cur" ) )
+}
+
+# Returns "$cur ${cur}arg1 ${cur}arg2 ..."
+_tc_expand_units()
+{
+ [[ $cur =~ ^[0-9]+ ]] || return 1
+ local value=${cur%%[^0-9]*}
+ [[ $cur == $value ]] && echo $cur
+ echo ${@/#/$value}
+}
+
+# Complete based on given word, usually $prev (or possibly the word before),
+# for when an argument or an option name has but a few possible arguments (so
+# tc does not take particular commands into account here).
+# Returns 0 is completion should stop after running this function, 1 otherwise.
+_tc_direct_complete()
+{
+ case $1 in
+ # Command options
+ dev)
+ _available_interfaces
+ return 0
+ ;;
+ classid)
+ return 0
+ ;;
+ estimator)
+ local list=$( _tc_expand_units 'secs' 'msecs' 'usecs' )
+ COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) )
+ return 0
+ ;;
+ handle)
+ return 0
+ ;;
+ parent|flowid)
+ local i iface ids cmd
+ for (( i=3; i < ${#words[@]}-2; i++ )); do
+ [[ ${words[i]} == dev ]] && iface=${words[i+1]}
+ break
+ done
+ for cmd in qdisc class; do
+ if [[ -n $iface ]]; then
+ ids+=$( tc $cmd show dev $iface 2>/dev/null | \
+ cut -d\ -f 3 )" "
+ else
+ ids+=$( tc $cmd show 2>/dev/null | cut -d\ -f 3 )
+ fi
+ done
+ [[ $ids != " " ]] && \
+ COMPREPLY+=( $( compgen -W "$ids" -- "$cur" ) )
+ return 0
+ ;;
+ protocol) # list comes from lib/ll_proto.c
+ COMPREPLY+=( $( compgen -W ' 802.1Q 802.1ad 802_2 802_3 LLDP aarp \
+ all aoe arp atalk atmfate atmmpoa ax25 bpq can control cust \
+ ddcmp dec diag dna_dl dna_rc dna_rt econet ethercat ieeepup \
+ ieeepupat ip ipv4 ipv6 ipx irda lat localtalk loop mobitex \
+ ppp_disc ppp_mp ppp_ses ppptalk profinet pup pupat rarp sca \
+ snap tipc tr_802_2 wan_ppp x25' -- "$cur" ) )
+ return 0
+ ;;
+ prio)
+ return 0
+ ;;
+ stab)
+ COMPREPLY+=( $( compgen -W 'mtu tsize mpu overhead
+ linklayer' -- "$cur" ) )
+ ;;
+
+ # Qdiscs and classes options
+ alpha|bands|beta|buckets|corrupt|debug|decrement|default|\
+ default_index|depth|direct_qlen|divisor|duplicate|ewma|flow_limit|\
+ flows|hh_limit|increment|indices|linklayer|non_hh_weight|num_tc|\
+ penalty_burst|penalty_rate|prio|priomap|probability|queues|r2q|\
+ reorder|vq|vqs)
+ return 0
+ ;;
+ setup)
+ COMPREPLY+=( $( compgen -W 'vqs' -- "$cur" ) )
+ return 0
+ ;;
+ hw)
+ COMPREPLY+=( $( compgen -W '1 0' -- "$cur" ) )
+ return 0
+ ;;
+ distribution)
+ COMPREPLY+=( $( compgen -W 'uniform normal pareto
+ paretonormal' -- "$cur" ) )
+ return 0
+ ;;
+ loss)
+ COMPREPLY+=( $( compgen -W 'random state gmodel' -- "$cur" ) )
+ return 0
+ ;;
+
+ # Qdiscs and classes options options
+ gap|gmodel|state)
+ return 0
+ ;;
+
+ # Filters options
+ map)
+ COMPREPLY+=( $( compgen -W 'key' -- "$cur" ) )
+ return 0
+ ;;
+ hash)
+ COMPREPLY+=( $( compgen -W 'keys' -- "$cur" ) )
+ return 0
+ ;;
+ indev)
+ _available_interfaces
+ return 0
+ ;;
+ eth_type)
+ COMPREPLY+=( $( compgen -W 'ipv4 ipv6' -- "$cur" ) )
+ return 0
+ ;;
+ ip_proto)
+ COMPREPLY+=( $( compgen -W 'tcp udp' -- "$cur" ) )
+ return 0
+ ;;
+
+ # Filters options options
+ key|keys)
+ [[ ${words[@]} =~ graft ]] && return 1
+ COMPREPLY+=( $( compgen -W 'src dst proto proto-src proto-dst iif \
+ priority mark nfct nfct-src nfct-dst nfct-proto-src \
+ nfct-proto-dst rt-classid sk-uid sk-gid vlan-tag rxhash' -- \
+ "$cur" ) )
+ return 0
+ ;;
+
+ # BPF options - used for filters, actions, and exec
+ export|bytecode|bytecode-file|object-file)
+ _filedir
+ return 0
+ ;;
+ object-pinned|graft) # Pinned object is probably under /sys/fs/bpf/
+ [[ -n "$cur" ]] && _filedir && return 0
+ COMPREPLY=( $( compgen -G "/sys/fs/bpf/*" -- "$cur" ) ) || _filedir
+ compopt -o nospace
+ return 0
+ ;;
+ section)
+ if (type objdump > /dev/null 2>&1) ; then
+ local fword objfile section_list
+ for (( fword=3; fword < ${#words[@]}-3; fword++ )); do
+ if [[ ${words[fword]} == object-file ]]; then
+ objfile=${words[fword+1]}
+ break
+ fi
+ done
+ section_list=$( objdump -h $objfile 2>/dev/null | \
+ sed -n 's/^ *[0-9]\+ \([^ ]*\) *.*/\1/p' )
+ COMPREPLY+=( $( compgen -W "$section_list" -- "$cur" ) )
+ fi
+ return 0
+ ;;
+ import|run)
+ _filedir
+ return 0
+ ;;
+ type)
+ COMPREPLY+=( $( compgen -W 'cls act' -- "$cur" ) )
+ return 0
+ ;;
+
+ # Actions options
+ random)
+ _tc_one_of_list 'netrand determ'
+ return 0
+ ;;
+
+ # Units for option arguments
+ bandwidth|maxrate|peakrate|rate)
+ local list=$( _tc_expand_units 'bit' \
+ 'kbit' 'kibit' 'kbps' 'kibps' \
+ 'mbit' 'mibit' 'mbps' 'mibps' \
+ 'gbit' 'gibit' 'gbps' 'gibps' \
+ 'tbit' 'tibit' 'tbps' 'tibps' )
+ COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) )
+ ;;
+ admit_bytes|avpkt|burst|cell|initial_quantum|limit|max|min|mtu|mpu|\
+ overhead|quantum|redflowlist)
+ local list=$( _tc_expand_units \
+ 'b' 'kbit' 'k' 'mbit' 'm' 'gbit' 'g' )
+ COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) )
+ ;;
+ db|delay|evict_timeout|interval|latency|perturb|rehash|reset_timeout|\
+ target|tupdate)
+ local list=$( _tc_expand_units 'secs' 'msecs' 'usecs' )
+ COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) )
+ ;;
+ esac
+ return 1
+}
+
+# Complete with options names for qdiscs. Each qdisc has its own set of options
+# and it seems we cannot really parse it from anywhere, so we add it manually
+# in this function.
+# Returns 0 is completion should stop after running this function, 1 otherwise.
+_tc_qdisc_options()
+{
+ case $1 in
+ choke)
+ _tc_once_attr 'limit bandwidth ecn min max burst'
+ return 0
+ ;;
+ codel)
+ _tc_once_attr 'limit target interval'
+ _tc_one_of_list 'ecn noecn'
+ return 0
+ ;;
+ bfifo|pfifo|pfifo_head_drop)
+ _tc_once_attr 'limit'
+ return 0
+ ;;
+ fq)
+ _tc_once_attr 'limit flow_limit quantum initial_quantum maxrate \
+ buckets'
+ _tc_one_of_list 'pacing nopacing'
+ return 0
+ ;;
+ fq_codel)
+ _tc_once_attr 'limit flows target interval quantum'
+ _tc_one_of_list 'ecn noecn'
+ return 0
+ ;;
+ gred)
+ _tc_once_attr 'setup vqs default grio vq prio limit min max avpkt \
+ burst probability bandwidth ecn harddrop'
+ return 0
+ ;;
+ hhf)
+ _tc_once_attr 'limit quantum hh_limit reset_timeout admit_bytes \
+ evict_timeout non_hh_weight'
+ return 0
+ ;;
+ mqprio)
+ _tc_once_attr 'num_tc map queues hw'
+ return 0
+ ;;
+ netem)
+ _tc_once_attr 'delay distribution corrupt duplicate loss ecn \
+ reorder rate'
+ return 0
+ ;;
+ pie)
+ _tc_once_attr 'limit target tupdate alpha beta'
+ _tc_one_of_list 'bytemode nobytemode'
+ _tc_one_of_list 'ecn noecn'
+ _tc_one_of_list 'dq_rate_estimator no_dq_rate_estimator'
+ return 0
+ ;;
+ fq_pie)
+ _tc_once_attr 'limit flows target tupdate \
+ alpha beta quantum memory_limit ecn_prob'
+ _tc_one_of_list 'ecn noecn'
+ _tc_one_of_list 'bytemode nobytemode'
+ _tc_one_of_list 'dq_rate_estimator no_dq_rate_estimator'
+ return 0
+ ;;
+ red)
+ _tc_once_attr 'limit min max avpkt burst adaptive probability \
+ bandwidth ecn harddrop'
+ return 0
+ ;;
+ rr|prio)
+ _tc_once_attr 'bands priomap multiqueue'
+ return 0
+ ;;
+ sfb)
+ _tc_once_attr 'rehash db limit max target increment decrement \
+ penalty_rate penalty_burst'
+ return 0
+ ;;
+ sfq)
+ _tc_once_attr 'limit perturb quantum divisor flows depth headdrop \
+ redflowlimit min max avpkt burst probability ecn harddrop'
+ return 0
+ ;;
+ tbf)
+ _tc_once_attr 'limit burst rate mtu peakrate latency overhead \
+ linklayer'
+ return 0
+ ;;
+ cbq)
+ _tc_once_attr 'bandwidth avpkt mpu cell ewma'
+ return 0
+ ;;
+ dsmark)
+ _tc_once_attr 'indices default_index set_tc_index'
+ return 0
+ ;;
+ hfsc)
+ _tc_once_attr 'default'
+ return 0
+ ;;
+ htb)
+ _tc_once_attr 'default r2q direct_qlen debug'
+ return 0
+ ;;
+ multiq|pfifo_fast|atm|drr|qfq)
+ return 0
+ ;;
+ esac
+ return 1
+}
+
+# Complete with options names for BPF filters or actions.
+# Returns 0 is completion should stop after running this function, 1 otherwise.
+_tc_bpf_options()
+{
+ [[ ${words[${#words[@]}-3]} == object-file ]] && \
+ _tc_once_attr 'section export'
+ [[ ${words[${#words[@]}-5]} == object-file ]] && \
+ [[ ${words[${#words[@]}-3]} =~ (section|export) ]] && \
+ _tc_once_attr 'section export'
+ _tc_one_of_list 'bytecode bytecode-file object-file object-pinned'
+ _tc_once_attr 'verbose index direct-action action classid'
+ return 0
+}
+
+# Complete with options names for filter actions.
+# This function is recursive, thus allowing multiple actions statement to be
+# parsed.
+# Returns 0 is completion should stop after running this function, 1 otherwise.
+_tc_filter_action_options()
+{
+ for ((acwd=$1; acwd < ${#words[@]}-1; acwd++));
+ do
+ if [[ action == ${words[acwd]} ]]; then
+ _tc_filter_action_options $((acwd+1)) && return 0
+ fi
+ done
+
+ local action acwd
+ for ((acwd=$1; acwd < ${#words[@]}-1; acwd++)); do
+ if [[ $ACTION_KIND =~ ' '${words[acwd]}' ' ]]; then
+ _tc_one_of_list_from $acwd action
+ _tc_action_options $acwd && return 0
+ fi
+ done
+ _tc_one_of_list_from $acwd $ACTION_KIND
+ return 0
+}
+
+# Complete with options names for filters.
+# Returns 0 is completion should stop after running this function, 1 otherwise.
+_tc_filter_options()
+{
+
+ for ((acwd=$1; acwd < ${#words[@]}-1; acwd++));
+ do
+ if [[ action == ${words[acwd]} ]]; then
+ _tc_filter_action_options $((acwd+1)) && return 0
+ fi
+ done
+
+ filter=${words[$1]}
+ case $filter in
+ basic)
+ _tc_once_attr 'match action classid'
+ return 0
+ ;;
+ bpf)
+ _tc_bpf_options
+ return 0
+ ;;
+ cgroup)
+ _tc_once_attr 'match action'
+ return 0
+ ;;
+ flow)
+ local i
+ for (( i=5; i < ${#words[@]}-1; i++ )); do
+ if [[ ${words[i]} =~ ^keys?$ ]]; then
+ _tc_direct_complete 'key'
+ COMPREPLY+=( $( compgen -W 'or and xor rshift addend' -- \
+ "$cur" ) )
+ break
+ fi
+ done
+ _tc_once_attr 'map hash divisor baseclass match action'
+ return 0
+ ;;
+ matchall)
+ _tc_once_attr 'action classid skip_sw skip_hw'
+ return 0
+ ;;
+ flower)
+ _tc_once_attr 'action classid indev dst_mac src_mac eth_type \
+ ip_proto dst_ip src_ip dst_port src_port'
+ return 0
+ ;;
+ fw)
+ _tc_once_attr 'action classid'
+ return 0
+ ;;
+ route)
+ _tc_one_of_list 'from fromif'
+ _tc_once_attr 'to classid action'
+ return 0
+ ;;
+ rsvp)
+ _tc_once_attr 'ipproto session sender classid action tunnelid \
+ tunnel flowlabel spi/ah spi/esp u8 u16 u32'
+ [[ ${words[${#words[@]}-3]} == tunnel ]] && \
+ COMPREPLY+=( $( compgen -W 'skip' -- "$cur" ) )
+ [[ ${words[${#words[@]}-3]} =~ u(8|16|32) ]] && \
+ COMPREPLY+=( $( compgen -W 'mask' -- "$cur" ) )
+ [[ ${words[${#words[@]}-3]} == mask ]] && \
+ COMPREPLY+=( $( compgen -W 'at' -- "$cur" ) )
+ return 0
+ ;;
+ tcindex)
+ _tc_once_attr 'hash mask shift classid action'
+ _tc_one_of_list 'pass_on fall_through'
+ return 0
+ ;;
+ u32)
+ _tc_once_attr 'match link classid action offset ht hashkey sample'
+ COMPREPLY+=( $( compgen -W 'ip ip6 udp tcp icmp u8 u16 u32 mark \
+ divisor' -- "$cur" ) )
+ return 0
+ ;;
+ esac
+ return 1
+}
+
+# Complete with options names for actions.
+# Returns 0 is completion should stop after running this function, 1 otherwise.
+_tc_action_options()
+{
+ local from=$1
+ local action=${words[from]}
+ case $action in
+ bpf)
+ _tc_bpf_options
+ return 0
+ ;;
+ mirred)
+ _tc_one_of_list_from $from 'ingress egress'
+ _tc_one_of_list_from $from 'mirror redirect'
+ _tc_once_attr_from $from 'index dev'
+ return 0
+ ;;
+ sample)
+ _tc_once_attr_from $from 'rate'
+ _tc_once_attr_from $from 'trunc'
+ _tc_once_attr_from $from 'group'
+ return 0
+ ;;
+ gact)
+ _tc_one_of_list_from $from 'reclassify drop continue pass'
+ _tc_once_attr_from $from 'random'
+ return 0
+ ;;
+ esac
+ return 1
+}
+
+# Complete with options names for exec.
+# Returns 0 is completion should stop after running this function, 1 otherwise.
+_tc_exec_options()
+{
+ case $1 in
+ import)
+ [[ ${words[${#words[@]}-3]} == import ]] && \
+ _tc_once_attr 'run'
+ return 0
+ ;;
+ graft)
+ COMPREPLY+=( $( compgen -W 'key type' -- "$cur" ) )
+ [[ ${words[${#words[@]}-3]} == object-file ]] && \
+ _tc_once_attr 'type'
+ _tc_bpf_options
+ return 0
+ ;;
+ esac
+ return 1
+}
+
+# Main completion function
+# Logic is as follows:
+# 1. Check if previous word is a global option; if so, propose arguments.
+# 2. Check if current word is a global option; if so, propose completion.
+# 3. Check for the presence of a main command (qdisc|class|filter|...). If
+# there is one, first call _tc_direct_complete to see if previous word is
+# waiting for a particular completion. If so, propose completion and exit.
+# 4. Extract main command and -- if available -- its subcommand
+# (add|delete|show|...).
+# 5. Propose completion based on main and sub- command in use. Additional
+# functions may be called for qdiscs, classes or filter options.
+_tc()
+{
+ local cur prev words cword
+ _init_completion || return
+
+ case $prev in
+ -V|-Version)
+ return 0
+ ;;
+ -b|-batch|-cf|-conf)
+ _filedir
+ return 0
+ ;;
+ -force)
+ COMPREPLY=( $( compgen -W '-batch' -- "$cur" ) )
+ return 0
+ ;;
+ -nm|name)
+ [[ -r /etc/iproute2/tc_cls ]] || \
+ COMPREPLY=( $( compgen -W '-conf' -- "$cur" ) )
+ return 0
+ ;;
+ -n|-net|-netns)
+ local nslist=$( ip netns list 2>/dev/null )
+ COMPREPLY+=( $( compgen -W "$nslist" -- "$cur" ) )
+ return 0
+ ;;
+ -tshort)
+ _tc_once_attr '-statistics'
+ COMPREPLY+=( $( compgen -W 'monitor' -- "$cur" ) )
+ return 0
+ ;;
+ -timestamp)
+ _tc_once_attr '-statistics -tshort'
+ COMPREPLY+=( $( compgen -W 'monitor' -- "$cur" ) )
+ return 0
+ ;;
+ esac
+
+ # Search for main commands
+ local subcword cmd subcmd
+ for (( subcword=1; subcword < ${#words[@]}-1; subcword++ )); do
+ [[ ${words[subcword]} == -b?(atch) ]] && return 0
+ [[ -n $cmd ]] && subcmd=${words[subcword]} && break
+ [[ ${words[subcword]} != -* && \
+ ${words[subcword-1]} != -@(n?(et?(ns))|c?(on)f) ]] && \
+ cmd=${words[subcword]}
+ done
+
+ if [[ -z $cmd ]]; then
+ case $cur in
+ -*)
+ local c='-Version -statistics -details -raw -pretty \
+ -iec -graphe -batch -name -netns -timestamp'
+ [[ $cword -eq 1 ]] && c+=' -force'
+ COMPREPLY=( $( compgen -W "$c" -- "$cur" ) )
+ return 0
+ ;;
+ *)
+ COMPREPLY=( $( compgen -W "help $( tc help 2>&1 | \
+ command sed \
+ -e '/OBJECT := /!d' \
+ -e 's/.*{//' \
+ -e 's/}.*//' \
+ -e \ 's/|//g' )" -- "$cur" ) )
+ return 0
+ ;;
+ esac
+ fi
+
+ [[ $subcmd == help ]] && return 0
+
+ # For this set of commands we may create COMPREPLY just by analysing the
+ # previous word, if it expects for a specific list of options or values.
+ if [[ $cmd =~ (qdisc|class|filter|action|exec) ]]; then
+ _tc_direct_complete $prev && return 0
+ if [[ ${words[${#words[@]}-3]} == estimator ]]; then
+ local list=$( _tc_expand_units 'secs' 'msecs' 'usecs' )
+ COMPREPLY+=( $( compgen -W "$list" -- "$cur" ) ) && return 0
+ fi
+ fi
+
+ # Completion depends on main command and subcommand in use.
+ case $cmd in
+ qdisc)
+ case $subcmd in
+ add|change|replace|link|del|delete)
+ if [[ $(($cword-$subcword)) -eq 1 ]]; then
+ COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) )
+ return 0
+ fi
+ local qdisc qdwd
+ for ((qdwd=$subcword; qdwd < ${#words[@]}-1; qdwd++)); do
+ if [[ $QDISC_KIND =~ ' '${words[qdwd]}' ' ]]; then
+ qdisc=${words[qdwd]}
+ _tc_qdisc_options $qdisc && return 0
+ fi
+ done
+ _tc_one_of_list $QDISC_KIND
+ _tc_one_of_list 'root ingress parent clsact'
+ _tc_once_attr 'handle estimator stab'
+ ;;
+ show)
+ _tc_once_attr 'dev'
+ _tc_one_of_list 'ingress clsact'
+ _tc_once_attr '-statistics -details -raw -pretty -iec \
+ -graph -name'
+ ;;
+ help)
+ return 0
+ ;;
+ *)
+ [[ $cword -eq $subcword ]] && \
+ COMPREPLY=( $( compgen -W 'help add delete change \
+ replace link show' -- "$cur" ) )
+ ;;
+ esac
+ ;;
+
+ class)
+ case $subcmd in
+ add|change|replace|del|delete)
+ if [[ $(($cword-$subcword)) -eq 1 ]]; then
+ COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) )
+ return 0
+ fi
+ local qdisc qdwd
+ for ((qdwd=$subcword; qdwd < ${#words[@]}-1; qdwd++)); do
+ if [[ $QDISC_KIND =~ ' '${words[qdwd]}' ' ]]; then
+ qdisc=${words[qdwd]}
+ _tc_qdisc_options $qdisc && return 0
+ fi
+ done
+ _tc_one_of_list $QDISC_KIND
+ _tc_one_of_list 'root parent'
+ _tc_once_attr 'classid'
+ ;;
+ show)
+ _tc_once_attr 'dev'
+ _tc_one_of_list 'root parent'
+ _tc_once_attr '-statistics -details -raw -pretty -iec \
+ -graph -name'
+ ;;
+ help)
+ return 0
+ ;;
+ *)
+ [[ $cword -eq $subcword ]] && \
+ COMPREPLY=( $( compgen -W 'help add delete change \
+ replace show' -- "$cur" ) )
+ ;;
+ esac
+ ;;
+
+ filter)
+ case $subcmd in
+ add|change|replace|del|delete)
+ if [[ $(($cword-$subcword)) -eq 1 ]]; then
+ COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) )
+ return 0
+ fi
+ local filter fltwd
+ for ((fltwd=$subcword; fltwd < ${#words[@]}-1; fltwd++));
+ do
+ if [[ $FILTER_KIND =~ ' '${words[fltwd]}' ' ]]; then
+ _tc_filter_options $fltwd && return 0
+ fi
+ done
+ _tc_one_of_list $FILTER_KIND
+ _tc_one_of_list 'root ingress egress parent'
+ _tc_once_attr 'handle estimator pref protocol'
+ ;;
+ show)
+ _tc_once_attr 'dev'
+ _tc_one_of_list 'root ingress egress parent'
+ _tc_once_attr '-statistics -details -raw -pretty -iec \
+ -graph -name'
+ ;;
+ help)
+ return 0
+ ;;
+ *)
+ [[ $cword -eq $subcword ]] && \
+ COMPREPLY=( $( compgen -W 'help add delete change \
+ replace show' -- "$cur" ) )
+ ;;
+ esac
+ ;;
+
+ action)
+ case $subcmd in
+ add|change|replace)
+ local action acwd
+ for ((acwd=$subcword; acwd < ${#words[@]}-1; acwd++)); do
+ if [[ $ACTION_KIND =~ ' '${words[acwd]}' ' ]]; then
+ _tc_action_options $acwd && return 0
+ fi
+ done
+ _tc_one_of_list $ACTION_KIND
+ ;;
+ get|del|delete)
+ _tc_once_attr 'index'
+ ;;
+ lst|list|flush|show)
+ _tc_one_of_list $ACTION_KIND
+ ;;
+ *)
+ [[ $cword -eq $subcword ]] && \
+ COMPREPLY=( $( compgen -W 'help add delete change \
+ replace show list flush action' -- "$cur" ) )
+ ;;
+ esac
+ ;;
+
+ monitor)
+ COMPREPLY=( $( compgen -W 'help' -- "$cur" ) )
+ ;;
+
+ exec)
+ case $subcmd in
+ bpf)
+ local excmd exwd EXEC_KIND=' import debug graft '
+ for ((exwd=$subcword; exwd < ${#words[@]}-1; exwd++)); do
+ if [[ $EXEC_KIND =~ ' '${words[exwd]}' ' ]]; then
+ excmd=${words[exwd]}
+ _tc_exec_options $excmd && return 0
+ fi
+ done
+ _tc_one_of_list $EXEC_KIND
+ ;;
+ *)
+ [[ $cword -eq $subcword ]] && \
+ COMPREPLY=( $( compgen -W 'bpf' -- "$cur" ) )
+ ;;
+ esac
+ ;;
+ esac
+} &&
+complete -F _tc tc
+
+# ex: ts=4 sw=4 et filetype=sh