tools: bpftool: add bash completion for bpftool

Add a completion file for bash. The completion function runs bpftool
when needed, making it smart enough to help users complete ids or tags
for eBPF programs and maps currently on the system.

Update Makefile to install completion file to
/usr/share/bash-completion/completions when running `make install`.

Emacs file mode and (at the end) Vim modeline have been added, to keep
the style in use for most existing bash completion files. In this, it
differs from tools/perf/perf-completion.sh, which seems to be the only
other completion file among the kernel sources repository. This is also
valid for indent style: 4-space indents, as in other completion files.

Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Quentin Monnet 2017-10-26 14:16:05 -07:00 committed by David S. Miller
parent 2660d226d9
commit 995231c820
2 changed files with 357 additions and 0 deletions

View File

@ -46,6 +46,7 @@ $(LIBBPF)-clean:
$(Q)$(MAKE) -C $(BPF_DIR) OUTPUT=$(OUTPUT) clean >/dev/null
prefix = /usr
bash_compdir ?= $(prefix)/share/bash-completion/completions
CC = gcc
@ -76,6 +77,8 @@ clean: $(LIBBPF)-clean
install:
install $(OUTPUT)bpftool $(prefix)/sbin/bpftool
install -m 0755 -d $(bash_compdir)
install -m 0644 bash-completion/bpftool $(bash_compdir)
doc:
$(Q)$(MAKE) -C Documentation/

View File

@ -0,0 +1,354 @@
# bpftool(8) bash completion -*- shell-script -*-
#
# Copyright (C) 2017 Netronome Systems, Inc.
#
# This software is dual licensed under the GNU General License
# Version 2, June 1991 as shown in the file COPYING in the top-level
# directory of this source tree or the BSD 2-Clause License provided
# below. You have the option to license this software under the
# complete terms of either license.
#
# The BSD 2-Clause License:
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions of source code must retain the above
# copyright notice, this list of conditions and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# 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 found
for w in $*; do
found=0
for (( idx=3; idx < ${#words[@]}-1; idx++ )); do
if [[ $w == ${words[idx]} ]]; 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.
_bpftool_one_of_list()
{
local w idx
for w in $*; do
for (( idx=3; idx < ${#words[@]}-1; idx++ )); do
[[ $w == ${words[idx]} ]] && return 1
done
done
COMPREPLY+=( $( compgen -W "$*" -- "$cur" ) )
}
_bpftool_get_map_ids()
{
COMPREPLY+=( $( compgen -W "$( bpftool -jp map 2>&1 | \
command sed -n 's/.*"id": \(.*\),$/\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" ) )
}
# For bpftool map update: retrieve type of the map to update.
_bpftool_map_update_map_type()
{
local keyword ref
for (( idx=3; idx < ${#words[@]}-1; idx++ )); do
if [[ ${words[$((idx-2))]} == "update" ]]; then
keyword=${words[$((idx-1))]}
ref=${words[$((idx))]}
fi
done
[[ -z $ref ]] && return 0
local type
type=$(bpftool -jp map show $keyword $ref | \
command sed -n 's/.*"type": "\(.*\)",$/\1/p')
printf $type
}
_bpftool_map_update_get_id()
{
# Is it the map to update, or a map to insert into the map to update?
# Search for "value" keyword.
local idx value
for (( idx=7; idx < ${#words[@]}-1; idx++ )); do
if [[ ${words[idx]} == "value" ]]; then
value=1
break
fi
done
[[ $value -eq 0 ]] && _bpftool_get_map_ids && return 0
# 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_update_map_type)
case $type in
array_of_maps|hash_of_maps)
_bpftool_get_map_ids
return 0
;;
prog_array)
_bpftool_get_prog_ids
return 0
;;
*)
return 0
;;
esac
}
_bpftool()
{
local cur prev words objword
_init_completion || return
# Deal with simplest keywords
case $prev in
help|key|opcodes)
return 0
;;
tag)
_bpftool_get_prog_tags
return 0
;;
file|pinned)
_filedir
return 0
;;
batch)
COMPREPLY=( $( compgen -W 'file' -- "$cur" ) )
return 0
;;
esac
# Search for object and command
local object command cmdword
for (( cmdword=1; cmdword < ${#words[@]}-1; cmdword++ )); do
[[ -n $object ]] && command=${words[cmdword]} && break
[[ ${words[cmdword]} != -* ]] && object=${words[cmdword]}
done
if [[ -z $object ]]; then
case $cur in
-*)
local c='--version --json --pretty'
COMPREPLY=( $( compgen -W "$c" -- "$cur" ) )
return 0
;;
*)
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
;;
esac
fi
[[ $command == help ]] && return 0
# Completion depends on object and command in use
case $object in
prog)
case $prev in
id)
_bpftool_get_prog_ids
return 0
;;
esac
local PROG_TYPE='id pinned tag'
case $command in
show)
[[ $prev != "$command" ]] && return 0
COMPREPLY=( $( 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'
COMPREPLY+=( $( compgen -W 'opcodes' -- \
"$cur" ) )
return 0
;;
esac
;;
pin)
if [[ $prev == "$command" ]]; then
COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )
else
_filedir
fi
return 0
;;
*)
[[ $prev == $object ]] && \
COMPREPLY=( $( compgen -W 'dump help pin show' -- \
"$cur" ) )
;;
esac
;;
map)
local MAP_TYPE='id pinned'
case $command in
show|dump)
case $prev in
$command)
COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
return 0
;;
id)
_bpftool_get_map_ids
return 0
;;
*)
return 0
;;
esac
;;
lookup|getnext|delete)
case $prev in
$command)
COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
return 0
;;
id)
_bpftool_get_map_ids
return 0
;;
key)
return 0
;;
*)
_bpftool_once_attr 'key'
return 0
;;
esac
;;
update)
case $prev in
$command)
COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) )
return 0
;;
id)
_bpftool_map_update_get_id
return 0
;;
key)
return 0
;;
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_update_map_type) in
array_of_maps|hash_of_maps)
local MAP_TYPE='id pinned'
COMPREPLY+=( $( compgen -W "$MAP_TYPE" \
-- "$cur" ) )
return 0
;;
prog_array)
local PROG_TYPE='id pinned tag'
COMPREPLY+=( $( compgen -W "$PROG_TYPE" \
-- "$cur" ) )
return 0
;;
*)
return 0
;;
esac
return 0
;;
*)
_bpftool_once_attr 'key'
local UPDATE_FLAGS='any exist noexist'
for (( idx=3; idx < ${#words[@]}-1; idx++ )); do
if [[ ${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 0
fi
done
for (( idx=3; idx < ${#words[@]}-1; idx++ )); do
if [[ ${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 0
fi
done
return 0
;;
esac
;;
pin)
if [[ $prev == "$command" ]]; then
COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) )
else
_filedir
fi
return 0
;;
*)
[[ $prev == $object ]] && \
COMPREPLY=( $( compgen -W 'delete dump getnext help \
lookup pin show update' -- "$cur" ) )
;;
esac
;;
esac
} &&
complete -F _bpftool bpftool
# ex: ts=4 sw=4 et filetype=sh