| # 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 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 ieeepup ieeepupat \ |
| ip ipv4 ipv6 ipx irda lat localtalk loop mobitex ppp_disc \ |
| ppp_mp ppp_ses ppptalk 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' |
| 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' |
| 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 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 |