Rev 208 | Blame | Compare with Previous | Last modification | View Log | RSS feed
# bpftool(8) bash completion -*- shell-script -*-## SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)# Copyright (C) 2017-2018 Netronome Systems, Inc.## Author: Quentin Monnet <quentin.monnet@netronome.com># 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._bpftool_once_attr(){local w idx foundfor w in $*; dofound=0for (( idx=3; idx < ${#words[@]}-1; idx++ )); doif [[ $w == ${words[idx]} ]]; thenfound=1breakfidone[[ $found -eq 0 ]] && \COMPREPLY+=( $( compgen -W "$w" -- "$cur" ) )done}# Takes a list of words as argument; if any of those words is present on the# command line, return 0. Otherwise, return 1._bpftool_search_list(){local w idxfor w in $*; dofor (( idx=3; idx < ${#words[@]}-1; idx++ )); do[[ $w == ${words[idx]} ]] && return 0donedonereturn 1}# 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._bpftool_one_of_list(){_bpftool_search_list $* && return 1COMPREPLY+=( $( compgen -W "$*" -- "$cur" ) )}_bpftool_get_map_ids(){COMPREPLY+=( $( compgen -W "$( bpftool -jp map 2>&1 | \command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) )}# Takes map type and adds matching map ids to the list of suggestions._bpftool_get_map_ids_for_type(){local type="$1"COMPREPLY+=( $( compgen -W "$( bpftool -jp map 2>&1 | \command grep -C2 "$type" | \command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) )}_bpftool_get_map_names(){COMPREPLY+=( $( compgen -W "$( bpftool -jp map 2>&1 | \command sed -n 's/.*"name": \(.*\),$/\1/p' )" -- "$cur" ) )}# Takes map type and adds matching map names to the list of suggestions._bpftool_get_map_names_for_type(){local type="$1"COMPREPLY+=( $( compgen -W "$( bpftool -jp map 2>&1 | \command grep -C2 "$type" | \command sed -n 's/.*"name": \(.*\),$/\1/p' )" -- "$cur" ) )}_bpftool_get_prog_ids(){COMPREPLY+=( $( compgen -W "$( bpftool -jp prog 2>&1 | \command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) )}_bpftool_get_prog_tags(){COMPREPLY+=( $( compgen -W "$( bpftool -jp prog 2>&1 | \command sed -n 's/.*"tag": "\(.*\)",$/\1/p' )" -- "$cur" ) )}_bpftool_get_prog_names(){COMPREPLY+=( $( compgen -W "$( bpftool -jp prog 2>&1 | \command sed -n 's/.*"name": "\(.*\)",$/\1/p' )" -- "$cur" ) )}_bpftool_get_btf_ids(){COMPREPLY+=( $( compgen -W "$( bpftool -jp btf 2>&1 | \command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) )}_bpftool_get_link_ids(){COMPREPLY+=( $( compgen -W "$( bpftool -jp link 2>&1 | \command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) )}_bpftool_get_obj_map_names(){local objobj=$1maps=$(objdump -j maps -t $obj 2>/dev/null | \command awk '/g . maps/ {print $NF}')COMPREPLY+=( $( compgen -W "$maps" -- "$cur" ) )}_bpftool_get_obj_map_idxs(){local objobj=$1nmaps=$(objdump -j maps -t $obj 2>/dev/null | grep -c 'g . maps')COMPREPLY+=( $( compgen -W "$(seq 0 $((nmaps - 1)))" -- "$cur" ) )}_sysfs_get_netdevs(){COMPREPLY+=( $( compgen -W "$( ls /sys/class/net 2>/dev/null )" -- \"$cur" ) )}# Retrieve type of the map that we are operating on._bpftool_map_guess_map_type(){local keyword reffor (( idx=3; idx < ${#words[@]}-1; idx++ )); docase "${words[$((idx-2))]}" inlookup|update)keyword=${words[$((idx-1))]}ref=${words[$((idx))]};;push)printf "stack"return 0;;enqueue)printf "queue"return 0;;esacdone[[ -z $ref ]] && return 0local typetype=$(bpftool -jp map show $keyword $ref | \command sed -n 's/.*"type": "\(.*\)",$/\1/p')[[ -n $type ]] && printf $type}_bpftool_map_update_get_id(){local command="$1"# Is it the map to update, or a map to insert into the map to update?# Search for "value" keyword.local idx valuefor (( idx=7; idx < ${#words[@]}-1; idx++ )); doif [[ ${words[idx]} == "value" ]]; thenvalue=1breakfidoneif [[ $value -eq 0 ]]; thencase "$command" inpush)_bpftool_get_map_ids_for_type stack;;enqueue)_bpftool_get_map_ids_for_type queue;;*)_bpftool_get_map_ids;;esacreturn 0fi# Id to complete is for a value. It can be either prog id or map id. This# depends on the type of the map to update.local type=$(_bpftool_map_guess_map_type)case $type inarray_of_maps|hash_of_maps)_bpftool_get_map_idsreturn 0;;prog_array)_bpftool_get_prog_idsreturn 0;;*)return 0;;esac}_bpftool_map_update_get_name(){local command="$1"# Is it the map to update, or a map to insert into the map to update?# Search for "value" keyword.local idx valuefor (( idx=7; idx < ${#words[@]}-1; idx++ )); doif [[ ${words[idx]} == "value" ]]; thenvalue=1breakfidoneif [[ $value -eq 0 ]]; thencase "$command" inpush)_bpftool_get_map_names_for_type stack;;enqueue)_bpftool_get_map_names_for_type queue;;*)_bpftool_get_map_names;;esacreturn 0fi# Name to complete is for a value. It can be either prog name or map name. This# depends on the type of the map to update.local type=$(_bpftool_map_guess_map_type)case $type inarray_of_maps|hash_of_maps)_bpftool_get_map_namesreturn 0;;prog_array)_bpftool_get_prog_namesreturn 0;;*)return 0;;esac}_bpftool(){local cur prev words objword_init_completion || return# Deal with optionsif [[ ${words[cword]} == -* ]]; thenlocal c='--version --json --pretty --bpffs --mapcompat --debug \--use-loader --base-btf'COMPREPLY=( $( compgen -W "$c" -- "$cur" ) )return 0fi# Deal with simplest keywordscase $prev inhelp|hex|opcodes|visual|linum)return 0;;tag)_bpftool_get_prog_tagsreturn 0;;dev)_sysfs_get_netdevsreturn 0;;file|pinned|-B|--base-btf)_filedirreturn 0;;batch)COMPREPLY=( $( compgen -W 'file' -- "$cur" ) )return 0;;esac# Remove all options so completions don't have to deal with them.local ifor (( i=1; i < ${#words[@]}; )); doif [[ ${words[i]::1} == - ]] &&[[ ${words[i]} != "-B" ]] && [[ ${words[i]} != "--base-btf" ]]; thenwords=( "${words[@]:0:i}" "${words[@]:i+1}" )[[ $i -le $cword ]] && cword=$(( cword - 1 ))elsei=$(( ++i ))fidonecur=${words[cword]}prev=${words[cword - 1]}pprev=${words[cword - 2]}local object=${words[1]} command=${words[2]}if [[ -z $object || $cword -eq 1 ]]; thencase $cur in*)COMPREPLY=( $( compgen -W "$( bpftool help 2>&1 | \command sed \-e '/OBJECT := /!d' \-e 's/.*{//' \-e 's/}.*//' \-e 's/|//g' )" -- "$cur" ) )COMPREPLY+=( $( compgen -W 'batch help' -- "$cur" ) )return 0;;esacfi[[ $command == help ]] && return 0# Completion depends on object and command in usecase $object inprog)# Complete id and name, only for subcommands that use prog (but no# map) ids/names.case $command inshow|list|dump|pin)case $prev inid)_bpftool_get_prog_idsreturn 0;;name)_bpftool_get_prog_namesreturn 0;;esac;;esaclocal PROG_TYPE='id pinned tag name'local MAP_TYPE='id pinned name'local METRIC_TYPE='cycles instructions l1d_loads llc_misses'case $command inshow|list)[[ $prev != "$command" ]] && return 0COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )return 0;;dump)case $prev in$command)COMPREPLY+=( $( compgen -W "xlated jited" -- \"$cur" ) )return 0;;xlated|jited)COMPREPLY=( $( compgen -W "$PROG_TYPE" -- \"$cur" ) )return 0;;*)_bpftool_once_attr 'file'if _bpftool_search_list 'xlated'; thenCOMPREPLY+=( $( compgen -W 'opcodes visual linum' -- \"$cur" ) )elseCOMPREPLY+=( $( compgen -W 'opcodes linum' -- \"$cur" ) )fireturn 0;;esac;;pin)if [[ $prev == "$command" ]]; thenCOMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )else_filedirfireturn 0;;attach|detach)case $cword in3)COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )return 0;;4)case $prev inid)_bpftool_get_prog_ids;;name)_bpftool_get_prog_names;;pinned)_filedir;;esacreturn 0;;5)COMPREPLY=( $( compgen -W 'msg_verdict stream_verdict \stream_parser flow_dissector' -- "$cur" ) )return 0;;6)COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )return 0;;7)case $prev inid)_bpftool_get_map_ids;;name)_bpftool_get_map_names;;pinned)_filedir;;esacreturn 0;;esac;;load|loadall)local obj# Propose "load/loadall" to complete "bpftool prog load",# or bash tries to complete "load" as a filename below.if [[ ${#words[@]} -eq 3 ]]; thenCOMPREPLY=( $( compgen -W "load loadall" -- "$cur" ) )return 0fiif [[ ${#words[@]} -lt 6 ]]; then_filedirreturn 0fiobj=${words[3]}if [[ ${words[-4]} == "map" ]]; thenCOMPREPLY=( $( compgen -W "id pinned" -- "$cur" ) )return 0fiif [[ ${words[-3]} == "map" ]]; thenif [[ ${words[-2]} == "idx" ]]; then_bpftool_get_obj_map_idxs $objelif [[ ${words[-2]} == "name" ]]; then_bpftool_get_obj_map_names $objfireturn 0fiif [[ ${words[-2]} == "map" ]]; thenCOMPREPLY=( $( compgen -W "idx name" -- "$cur" ) )return 0ficase $prev intype)COMPREPLY=( $( compgen -W "socket kprobe \kretprobe classifier flow_dissector \action tracepoint raw_tracepoint \xdp perf_event cgroup/skb cgroup/sock \cgroup/dev lwt_in lwt_out lwt_xmit \lwt_seg6local sockops sk_skb sk_msg \lirc_mode2 cgroup/bind4 cgroup/bind6 \cgroup/connect4 cgroup/connect6 \cgroup/getpeername4 cgroup/getpeername6 \cgroup/getsockname4 cgroup/getsockname6 \cgroup/sendmsg4 cgroup/sendmsg6 \cgroup/recvmsg4 cgroup/recvmsg6 \cgroup/post_bind4 cgroup/post_bind6 \cgroup/sysctl cgroup/getsockopt \cgroup/setsockopt cgroup/sock_release struct_ops \fentry fexit freplace sk_lookup" -- \"$cur" ) )return 0;;id)_bpftool_get_map_idsreturn 0;;name)_bpftool_get_map_namesreturn 0;;pinned|pinmaps)_filedirreturn 0;;*)COMPREPLY=( $( compgen -W "map" -- "$cur" ) )_bpftool_once_attr 'type'_bpftool_once_attr 'dev'_bpftool_once_attr 'pinmaps'return 0;;esac;;tracelog)return 0;;profile)case $cword in3)COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )return 0;;4)case $prev inid)_bpftool_get_prog_ids;;name)_bpftool_get_prog_names;;pinned)_filedir;;esacreturn 0;;5)COMPREPLY=( $( compgen -W "$METRIC_TYPE duration" -- "$cur" ) )return 0;;6)case $prev induration)return 0;;*)COMPREPLY=( $( compgen -W "$METRIC_TYPE" -- "$cur" ) )return 0;;esacreturn 0;;*)COMPREPLY=( $( compgen -W "$METRIC_TYPE" -- "$cur" ) )return 0;;esac;;run)if [[ ${#words[@]} -eq 4 ]]; thenCOMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )return 0ficase $prev inid)_bpftool_get_prog_idsreturn 0;;name)_bpftool_get_prog_namesreturn 0;;data_in|data_out|ctx_in|ctx_out)_filedirreturn 0;;repeat|data_size_out|ctx_size_out)return 0;;*)_bpftool_once_attr 'data_in data_out data_size_out \ctx_in ctx_out ctx_size_out repeat'return 0;;esac;;*)[[ $prev == $object ]] && \COMPREPLY=( $( compgen -W 'dump help pin attach detach \load loadall show list tracelog run profile' -- "$cur" ) );;esac;;struct_ops)local STRUCT_OPS_TYPE='id name'case $command inshow|list|dump|unregister)case $prev in$command)COMPREPLY=( $( compgen -W "$STRUCT_OPS_TYPE" -- "$cur" ) );;id)_bpftool_get_map_ids_for_type struct_ops;;name)_bpftool_get_map_names_for_type struct_ops;;esacreturn 0;;register)_filedirreturn 0;;*)[[ $prev == $object ]] && \COMPREPLY=( $( compgen -W 'register unregister show list dump help' \-- "$cur" ) );;esac;;iter)case $command inpin)case $prev in$command)_filedir;;id)_bpftool_get_map_ids;;name)_bpftool_get_map_names;;pinned)_filedir;;*)_bpftool_one_of_list $MAP_TYPE;;esacreturn 0;;*)[[ $prev == $object ]] && \COMPREPLY=( $( compgen -W 'pin help' \-- "$cur" ) );;esac;;map)local MAP_TYPE='id pinned name'case $command inshow|list|dump|peek|pop|dequeue|freeze)case $prev in$command)COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )return 0;;id)case "$command" inpeek)_bpftool_get_map_ids_for_type stack_bpftool_get_map_ids_for_type queue;;pop)_bpftool_get_map_ids_for_type stack;;dequeue)_bpftool_get_map_ids_for_type queue;;*)_bpftool_get_map_ids;;esacreturn 0;;name)case "$command" inpeek)_bpftool_get_map_names_for_type stack_bpftool_get_map_names_for_type queue;;pop)_bpftool_get_map_names_for_type stack;;dequeue)_bpftool_get_map_names_for_type queue;;*)_bpftool_get_map_names;;esacreturn 0;;*)return 0;;esac;;create)case $prev in$command)_filedirreturn 0;;type)COMPREPLY=( $( compgen -W 'hash array prog_array \perf_event_array percpu_hash percpu_array \stack_trace cgroup_array lru_hash \lru_percpu_hash lpm_trie array_of_maps \hash_of_maps devmap devmap_hash sockmap cpumap \xskmap sockhash cgroup_storage reuseport_sockarray \percpu_cgroup_storage queue stack sk_storage \struct_ops inode_storage task_storage' -- \"$cur" ) )return 0;;key|value|flags|entries)return 0;;inner_map)COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )return 0;;id)_bpftool_get_map_ids;;name)case $pprev ininner_map)_bpftool_get_map_names;;*)return 0;;esac;;*)_bpftool_once_attr 'type'_bpftool_once_attr 'key'_bpftool_once_attr 'value'_bpftool_once_attr 'entries'_bpftool_once_attr 'name'_bpftool_once_attr 'flags'if _bpftool_search_list 'array_of_maps' 'hash_of_maps'; then_bpftool_once_attr 'inner_map'fi_bpftool_once_attr 'dev'return 0;;esac;;lookup|getnext|delete)case $prev in$command)COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )return 0;;id)_bpftool_get_map_idsreturn 0;;name)_bpftool_get_map_namesreturn 0;;key)COMPREPLY+=( $( compgen -W 'hex' -- "$cur" ) );;*)case $(_bpftool_map_guess_map_type) inqueue|stack)return 0;;esac_bpftool_once_attr 'key'return 0;;esac;;update|push|enqueue)case $prev in$command)COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )return 0;;id)_bpftool_map_update_get_id $commandreturn 0;;name)_bpftool_map_update_get_name $commandreturn 0;;key)COMPREPLY+=( $( compgen -W 'hex' -- "$cur" ) );;value)# We can have bytes, or references to a prog or a# map, depending on the type of the map to update.case "$(_bpftool_map_guess_map_type)" inarray_of_maps|hash_of_maps)local MAP_TYPE='id pinned name'COMPREPLY+=( $( compgen -W "$MAP_TYPE" \-- "$cur" ) )return 0;;prog_array)local PROG_TYPE='id pinned tag name'COMPREPLY+=( $( compgen -W "$PROG_TYPE" \-- "$cur" ) )return 0;;*)COMPREPLY+=( $( compgen -W 'hex' \-- "$cur" ) )return 0;;esacreturn 0;;*)case $(_bpftool_map_guess_map_type) inqueue|stack)_bpftool_once_attr 'value'return 0;;;esac_bpftool_once_attr 'key'local UPDATE_FLAGS='any exist noexist'for (( idx=3; idx < ${#words[@]}-1; idx++ )); doif [[ ${words[idx]} == 'value' ]]; then# 'value' is present, but is not the last# word i.e. we can now have UPDATE_FLAGS._bpftool_one_of_list "$UPDATE_FLAGS"return 0fidonefor (( idx=3; idx < ${#words[@]}-1; idx++ )); doif [[ ${words[idx]} == 'key' ]]; then# 'key' is present, but is not the last# word i.e. we can now have 'value'._bpftool_once_attr 'value'return 0fidonereturn 0;;esac;;pin)case $prev in$command)COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) );;id)_bpftool_get_map_ids;;name)_bpftool_get_map_names;;esacreturn 0;;event_pipe)case $prev in$command)COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )return 0;;id)_bpftool_get_map_ids_for_type perf_event_arrayreturn 0;;name)_bpftool_get_map_names_for_type perf_event_arrayreturn 0;;cpu)return 0;;index)return 0;;*)_bpftool_once_attr 'cpu'_bpftool_once_attr 'index'return 0;;esac;;*)[[ $prev == $object ]] && \COMPREPLY=( $( compgen -W 'delete dump getnext help \lookup pin event_pipe show list update create \peek push enqueue pop dequeue freeze' -- \"$cur" ) );;esac;;btf)local PROG_TYPE='id pinned tag name'local MAP_TYPE='id pinned name'case $command indump)case $prev in$command)COMPREPLY+=( $( compgen -W "id map prog file" -- \"$cur" ) )return 0;;prog)COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )return 0;;map)COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )return 0;;id)case $pprev inprog)_bpftool_get_prog_ids;;map)_bpftool_get_map_ids;;$command)_bpftool_get_btf_ids;;esacreturn 0;;name)case $pprev inprog)_bpftool_get_prog_names;;map)_bpftool_get_map_names;;esacreturn 0;;format)COMPREPLY=( $( compgen -W "c raw" -- "$cur" ) );;*)# emit extra optionscase ${words[3]} inid|file)_bpftool_once_attr 'format';;map|prog)if [[ ${words[3]} == "map" ]] && [[ $cword == 6 ]]; thenCOMPREPLY+=( $( compgen -W "key value kv all" -- "$cur" ) )fi_bpftool_once_attr 'format';;*);;esacreturn 0;;esac;;show|list)case $prev in$command)COMPREPLY+=( $( compgen -W "id" -- "$cur" ) );;id)_bpftool_get_btf_ids;;esacreturn 0;;*)[[ $prev == $object ]] && \COMPREPLY=( $( compgen -W 'dump help show list' \-- "$cur" ) );;esac;;gen)case $command inobject)_filedirreturn 0;;skeleton)case $prev in$command)_filedirreturn 0;;*)_bpftool_once_attr 'name'return 0;;esac;;*)[[ $prev == $object ]] && \COMPREPLY=( $( compgen -W 'object skeleton help' -- "$cur" ) );;esac;;cgroup)case $command inshow|list|tree)case $cword in3)_filedir;;4)COMPREPLY=( $( compgen -W 'effective' -- "$cur" ) );;esacreturn 0;;attach|detach)local ATTACH_TYPES='ingress egress sock_create sock_ops \device bind4 bind6 post_bind4 post_bind6 connect4 connect6 \getpeername4 getpeername6 getsockname4 getsockname6 \sendmsg4 sendmsg6 recvmsg4 recvmsg6 sysctl getsockopt \setsockopt sock_release'local ATTACH_FLAGS='multi override'local PROG_TYPE='id pinned tag name'case $prev in$command)_filedirreturn 0;;ingress|egress|sock_create|sock_ops|device|bind4|bind6|\post_bind4|post_bind6|connect4|connect6|getpeername4|\getpeername6|getsockname4|getsockname6|sendmsg4|sendmsg6|\recvmsg4|recvmsg6|sysctl|getsockopt|setsockopt|sock_release)COMPREPLY=( $( compgen -W "$PROG_TYPE" -- \"$cur" ) )return 0;;id)_bpftool_get_prog_idsreturn 0;;*)if ! _bpftool_search_list "$ATTACH_TYPES"; thenCOMPREPLY=( $( compgen -W "$ATTACH_TYPES" -- \"$cur" ) )elif [[ "$command" == "attach" ]]; then# We have an attach type on the command line,# but it is not the previous word, or# "id|pinned|tag|name" (we already checked for# that). This should only leave the case when# we need attach flags for "attach" commamnd._bpftool_one_of_list "$ATTACH_FLAGS"fireturn 0;;esac;;*)[[ $prev == $object ]] && \COMPREPLY=( $( compgen -W 'help attach detach \show list tree' -- "$cur" ) );;esac;;perf)case $command in*)[[ $prev == $object ]] && \COMPREPLY=( $( compgen -W 'help \show list' -- "$cur" ) );;esac;;net)local PROG_TYPE='id pinned tag name'local ATTACH_TYPES='xdp xdpgeneric xdpdrv xdpoffload'case $command inshow|list)[[ $prev != "$command" ]] && return 0COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) )return 0;;attach)case $cword in3)COMPREPLY=( $( compgen -W "$ATTACH_TYPES" -- "$cur" ) )return 0;;4)COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )return 0;;5)case $prev inid)_bpftool_get_prog_ids;;name)_bpftool_get_prog_names;;pinned)_filedir;;esacreturn 0;;6)COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) )return 0;;8)_bpftool_once_attr 'overwrite'return 0;;esac;;detach)case $cword in3)COMPREPLY=( $( compgen -W "$ATTACH_TYPES" -- "$cur" ) )return 0;;4)COMPREPLY=( $( compgen -W 'dev' -- "$cur" ) )return 0;;esac;;*)[[ $prev == $object ]] && \COMPREPLY=( $( compgen -W 'help \show list attach detach' -- "$cur" ) );;esac;;feature)case $command inprobe)[[ $prev == "prefix" ]] && return 0if _bpftool_search_list 'macros'; then_bpftool_once_attr 'prefix'elseCOMPREPLY+=( $( compgen -W 'macros' -- "$cur" ) )fi_bpftool_one_of_list 'kernel dev'_bpftool_once_attr 'full unprivileged'return 0;;*)[[ $prev == $object ]] && \COMPREPLY=( $( compgen -W 'help probe' -- "$cur" ) );;esac;;link)case $command inshow|list|pin|detach)case $prev inid)_bpftool_get_link_idsreturn 0;;esac;;esaclocal LINK_TYPE='id pinned'case $command inshow|list)[[ $prev != "$command" ]] && return 0COMPREPLY=( $( compgen -W "$LINK_TYPE" -- "$cur" ) )return 0;;pin|detach)if [[ $prev == "$command" ]]; thenCOMPREPLY=( $( compgen -W "$LINK_TYPE" -- "$cur" ) )else_filedirfireturn 0;;*)[[ $prev == $object ]] && \COMPREPLY=( $( compgen -W 'help pin show list' -- "$cur" ) );;esac;;esac} &&complete -F _bpftool bpftool# ex: ts=4 sw=4 et filetype=sh