1# bpftool(8) bash completion -*- shell-script -*- 2# 3# Copyright (C) 2017-2018 Netronome Systems, Inc. 4# 5# This software is dual licensed under the GNU General License 6# Version 2, June 1991 as shown in the file COPYING in the top-level 7# directory of this source tree or the BSD 2-Clause License provided 8# below. You have the option to license this software under the 9# complete terms of either license. 10# 11# The BSD 2-Clause License: 12# 13# Redistribution and use in source and binary forms, with or 14# without modification, are permitted provided that the following 15# conditions are met: 16# 17# 1. Redistributions of source code must retain the above 18# copyright notice, this list of conditions and the following 19# disclaimer. 20# 21# 2. Redistributions in binary form must reproduce the above 22# copyright notice, this list of conditions and the following 23# disclaimer in the documentation and/or other materials 24# provided with the distribution. 25# 26# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 27# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 28# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 29# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 30# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 31# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 32# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 33# SOFTWARE. 34# 35# Author: Quentin Monnet <quentin.monnet@netronome.com> 36 37# Takes a list of words in argument; each one of them is added to COMPREPLY if 38# it is not already present on the command line. Returns no value. 39_bpftool_once_attr() 40{ 41 local w idx found 42 for w in $*; do 43 found=0 44 for (( idx=3; idx < ${#words[@]}-1; idx++ )); do 45 if [[ $w == ${words[idx]} ]]; then 46 found=1 47 break 48 fi 49 done 50 [[ $found -eq 0 ]] && \ 51 COMPREPLY+=( $( compgen -W "$w" -- "$cur" ) ) 52 done 53} 54 55# Takes a list of words as argument; if any of those words is present on the 56# command line, return 0. Otherwise, return 1. 57_bpftool_search_list() 58{ 59 local w idx 60 for w in $*; do 61 for (( idx=3; idx < ${#words[@]}-1; idx++ )); do 62 [[ $w == ${words[idx]} ]] && return 0 63 done 64 done 65 return 1 66} 67 68# Takes a list of words in argument; adds them all to COMPREPLY if none of them 69# is already present on the command line. Returns no value. 70_bpftool_one_of_list() 71{ 72 _bpftool_search_list $* && return 1 73 COMPREPLY+=( $( compgen -W "$*" -- "$cur" ) ) 74} 75 76_bpftool_get_map_ids() 77{ 78 COMPREPLY+=( $( compgen -W "$( bpftool -jp map 2>&1 | \ 79 command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) ) 80} 81 82_bpftool_get_perf_map_ids() 83{ 84 COMPREPLY+=( $( compgen -W "$( bpftool -jp map 2>&1 | \ 85 command grep -C2 perf_event_array | \ 86 command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) ) 87} 88 89 90_bpftool_get_prog_ids() 91{ 92 COMPREPLY+=( $( compgen -W "$( bpftool -jp prog 2>&1 | \ 93 command sed -n 's/.*"id": \(.*\),$/\1/p' )" -- "$cur" ) ) 94} 95 96_bpftool_get_prog_tags() 97{ 98 COMPREPLY+=( $( compgen -W "$( bpftool -jp prog 2>&1 | \ 99 command sed -n 's/.*"tag": "\(.*\)",$/\1/p' )" -- "$cur" ) ) 100} 101 102_bpftool_get_obj_map_names() 103{ 104 local obj 105 106 obj=$1 107 108 maps=$(objdump -j maps -t $obj 2>/dev/null | \ 109 command awk '/g . maps/ {print $NF}') 110 111 COMPREPLY+=( $( compgen -W "$maps" -- "$cur" ) ) 112} 113 114_bpftool_get_obj_map_idxs() 115{ 116 local obj 117 118 obj=$1 119 120 nmaps=$(objdump -j maps -t $obj 2>/dev/null | grep -c 'g . maps') 121 122 COMPREPLY+=( $( compgen -W "$(seq 0 $((nmaps - 1)))" -- "$cur" ) ) 123} 124 125_sysfs_get_netdevs() 126{ 127 COMPREPLY+=( $( compgen -W "$( ls /sys/class/net 2>/dev/null )" -- \ 128 "$cur" ) ) 129} 130 131# For bpftool map update: retrieve type of the map to update. 132_bpftool_map_update_map_type() 133{ 134 local keyword ref 135 for (( idx=3; idx < ${#words[@]}-1; idx++ )); do 136 if [[ ${words[$((idx-2))]} == "update" ]]; then 137 keyword=${words[$((idx-1))]} 138 ref=${words[$((idx))]} 139 fi 140 done 141 [[ -z $ref ]] && return 0 142 143 local type 144 type=$(bpftool -jp map show $keyword $ref | \ 145 command sed -n 's/.*"type": "\(.*\)",$/\1/p') 146 printf $type 147} 148 149_bpftool_map_update_get_id() 150{ 151 # Is it the map to update, or a map to insert into the map to update? 152 # Search for "value" keyword. 153 local idx value 154 for (( idx=7; idx < ${#words[@]}-1; idx++ )); do 155 if [[ ${words[idx]} == "value" ]]; then 156 value=1 157 break 158 fi 159 done 160 [[ $value -eq 0 ]] && _bpftool_get_map_ids && return 0 161 162 # Id to complete is for a value. It can be either prog id or map id. This 163 # depends on the type of the map to update. 164 local type=$(_bpftool_map_update_map_type) 165 case $type in 166 array_of_maps|hash_of_maps) 167 _bpftool_get_map_ids 168 return 0 169 ;; 170 prog_array) 171 _bpftool_get_prog_ids 172 return 0 173 ;; 174 *) 175 return 0 176 ;; 177 esac 178} 179 180_bpftool() 181{ 182 local cur prev words objword 183 _init_completion || return 184 185 # Deal with options 186 if [[ ${words[cword]} == -* ]]; then 187 local c='--version --json --pretty --bpffs' 188 COMPREPLY=( $( compgen -W "$c" -- "$cur" ) ) 189 return 0 190 fi 191 192 # Deal with simplest keywords 193 case $prev in 194 help|hex|opcodes|visual) 195 return 0 196 ;; 197 tag) 198 _bpftool_get_prog_tags 199 return 0 200 ;; 201 file|pinned) 202 _filedir 203 return 0 204 ;; 205 batch) 206 COMPREPLY=( $( compgen -W 'file' -- "$cur" ) ) 207 return 0 208 ;; 209 esac 210 211 # Remove all options so completions don't have to deal with them. 212 local i 213 for (( i=1; i < ${#words[@]}; )); do 214 if [[ ${words[i]::1} == - ]]; then 215 words=( "${words[@]:0:i}" "${words[@]:i+1}" ) 216 [[ $i -le $cword ]] && cword=$(( cword - 1 )) 217 else 218 i=$(( ++i )) 219 fi 220 done 221 cur=${words[cword]} 222 prev=${words[cword - 1]} 223 224 local object=${words[1]} command=${words[2]} 225 226 if [[ -z $object || $cword -eq 1 ]]; then 227 case $cur in 228 *) 229 COMPREPLY=( $( compgen -W "$( bpftool help 2>&1 | \ 230 command sed \ 231 -e '/OBJECT := /!d' \ 232 -e 's/.*{//' \ 233 -e 's/}.*//' \ 234 -e 's/|//g' )" -- "$cur" ) ) 235 COMPREPLY+=( $( compgen -W 'batch help' -- "$cur" ) ) 236 return 0 237 ;; 238 esac 239 fi 240 241 [[ $command == help ]] && return 0 242 243 # Completion depends on object and command in use 244 case $object in 245 prog) 246 if [[ $command != "load" ]]; then 247 case $prev in 248 id) 249 _bpftool_get_prog_ids 250 return 0 251 ;; 252 esac 253 fi 254 255 local PROG_TYPE='id pinned tag' 256 case $command in 257 show|list) 258 [[ $prev != "$command" ]] && return 0 259 COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) ) 260 return 0 261 ;; 262 dump) 263 case $prev in 264 $command) 265 COMPREPLY+=( $( compgen -W "xlated jited" -- \ 266 "$cur" ) ) 267 return 0 268 ;; 269 xlated|jited) 270 COMPREPLY=( $( compgen -W "$PROG_TYPE" -- \ 271 "$cur" ) ) 272 return 0 273 ;; 274 *) 275 _bpftool_once_attr 'file' 276 if _bpftool_search_list 'xlated'; then 277 COMPREPLY+=( $( compgen -W 'opcodes visual' -- \ 278 "$cur" ) ) 279 else 280 COMPREPLY+=( $( compgen -W 'opcodes' -- \ 281 "$cur" ) ) 282 fi 283 return 0 284 ;; 285 esac 286 ;; 287 pin) 288 if [[ $prev == "$command" ]]; then 289 COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) ) 290 else 291 _filedir 292 fi 293 return 0 294 ;; 295 load) 296 local obj 297 298 if [[ ${#words[@]} -lt 6 ]]; then 299 _filedir 300 return 0 301 fi 302 303 obj=${words[3]} 304 305 if [[ ${words[-4]} == "map" ]]; then 306 COMPREPLY=( $( compgen -W "id pinned" -- "$cur" ) ) 307 return 0 308 fi 309 if [[ ${words[-3]} == "map" ]]; then 310 if [[ ${words[-2]} == "idx" ]]; then 311 _bpftool_get_obj_map_idxs $obj 312 elif [[ ${words[-2]} == "name" ]]; then 313 _bpftool_get_obj_map_names $obj 314 fi 315 return 0 316 fi 317 if [[ ${words[-2]} == "map" ]]; then 318 COMPREPLY=( $( compgen -W "idx name" -- "$cur" ) ) 319 return 0 320 fi 321 322 case $prev in 323 type) 324 COMPREPLY=( $( compgen -W "socket kprobe kretprobe classifier 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/sendmsg4 cgroup/sendmsg6 cgroup/post_bind4 cgroup/post_bind6" -- \ 325 "$cur" ) ) 326 return 0 327 ;; 328 id) 329 _bpftool_get_map_ids 330 return 0 331 ;; 332 pinned) 333 _filedir 334 return 0 335 ;; 336 dev) 337 _sysfs_get_netdevs 338 return 0 339 ;; 340 *) 341 COMPREPLY=( $( compgen -W "map" -- "$cur" ) ) 342 _bpftool_once_attr 'type' 343 _bpftool_once_attr 'dev' 344 return 0 345 ;; 346 esac 347 ;; 348 *) 349 [[ $prev == $object ]] && \ 350 COMPREPLY=( $( compgen -W 'dump help pin load \ 351 show list' -- "$cur" ) ) 352 ;; 353 esac 354 ;; 355 map) 356 local MAP_TYPE='id pinned' 357 case $command in 358 show|list|dump) 359 case $prev in 360 $command) 361 COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) ) 362 return 0 363 ;; 364 id) 365 _bpftool_get_map_ids 366 return 0 367 ;; 368 *) 369 return 0 370 ;; 371 esac 372 ;; 373 lookup|getnext|delete) 374 case $prev in 375 $command) 376 COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) ) 377 return 0 378 ;; 379 id) 380 _bpftool_get_map_ids 381 return 0 382 ;; 383 key) 384 COMPREPLY+=( $( compgen -W 'hex' -- "$cur" ) ) 385 ;; 386 *) 387 _bpftool_once_attr 'key' 388 return 0 389 ;; 390 esac 391 ;; 392 update) 393 case $prev in 394 $command) 395 COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) ) 396 return 0 397 ;; 398 id) 399 _bpftool_map_update_get_id 400 return 0 401 ;; 402 key) 403 COMPREPLY+=( $( compgen -W 'hex' -- "$cur" ) ) 404 ;; 405 value) 406 # We can have bytes, or references to a prog or a 407 # map, depending on the type of the map to update. 408 case $(_bpftool_map_update_map_type) in 409 array_of_maps|hash_of_maps) 410 local MAP_TYPE='id pinned' 411 COMPREPLY+=( $( compgen -W "$MAP_TYPE" \ 412 -- "$cur" ) ) 413 return 0 414 ;; 415 prog_array) 416 local PROG_TYPE='id pinned tag' 417 COMPREPLY+=( $( compgen -W "$PROG_TYPE" \ 418 -- "$cur" ) ) 419 return 0 420 ;; 421 *) 422 COMPREPLY+=( $( compgen -W 'hex' \ 423 -- "$cur" ) ) 424 return 0 425 ;; 426 esac 427 return 0 428 ;; 429 *) 430 _bpftool_once_attr 'key' 431 local UPDATE_FLAGS='any exist noexist' 432 for (( idx=3; idx < ${#words[@]}-1; idx++ )); do 433 if [[ ${words[idx]} == 'value' ]]; then 434 # 'value' is present, but is not the last 435 # word i.e. we can now have UPDATE_FLAGS. 436 _bpftool_one_of_list "$UPDATE_FLAGS" 437 return 0 438 fi 439 done 440 for (( idx=3; idx < ${#words[@]}-1; idx++ )); do 441 if [[ ${words[idx]} == 'key' ]]; then 442 # 'key' is present, but is not the last 443 # word i.e. we can now have 'value'. 444 _bpftool_once_attr 'value' 445 return 0 446 fi 447 done 448 return 0 449 ;; 450 esac 451 ;; 452 pin) 453 if [[ $prev == "$command" ]]; then 454 COMPREPLY=( $( compgen -W "$PROG_TYPE" -- "$cur" ) ) 455 else 456 _filedir 457 fi 458 return 0 459 ;; 460 event_pipe) 461 case $prev in 462 $command) 463 COMPREPLY=( $( compgen -W "$MAP_TYPE" -- "$cur" ) ) 464 return 0 465 ;; 466 id) 467 _bpftool_get_perf_map_ids 468 return 0 469 ;; 470 cpu) 471 return 0 472 ;; 473 index) 474 return 0 475 ;; 476 *) 477 _bpftool_once_attr 'cpu' 478 _bpftool_once_attr 'index' 479 return 0 480 ;; 481 esac 482 ;; 483 *) 484 [[ $prev == $object ]] && \ 485 COMPREPLY=( $( compgen -W 'delete dump getnext help \ 486 lookup pin event_pipe show list update' -- \ 487 "$cur" ) ) 488 ;; 489 esac 490 ;; 491 cgroup) 492 case $command in 493 show|list) 494 _filedir 495 return 0 496 ;; 497 tree) 498 _filedir 499 return 0 500 ;; 501 attach|detach) 502 local ATTACH_TYPES='ingress egress sock_create sock_ops \ 503 device bind4 bind6 post_bind4 post_bind6 connect4 \ 504 connect6 sendmsg4 sendmsg6' 505 local ATTACH_FLAGS='multi override' 506 local PROG_TYPE='id pinned tag' 507 case $prev in 508 $command) 509 _filedir 510 return 0 511 ;; 512 ingress|egress|sock_create|sock_ops|device|bind4|bind6|\ 513 post_bind4|post_bind6|connect4|connect6|sendmsg4|\ 514 sendmsg6) 515 COMPREPLY=( $( compgen -W "$PROG_TYPE" -- \ 516 "$cur" ) ) 517 return 0 518 ;; 519 id) 520 _bpftool_get_prog_ids 521 return 0 522 ;; 523 *) 524 if ! _bpftool_search_list "$ATTACH_TYPES"; then 525 COMPREPLY=( $( compgen -W "$ATTACH_TYPES" -- \ 526 "$cur" ) ) 527 elif [[ "$command" == "attach" ]]; then 528 # We have an attach type on the command line, 529 # but it is not the previous word, or 530 # "id|pinned|tag" (we already checked for 531 # that). This should only leave the case when 532 # we need attach flags for "attach" commamnd. 533 _bpftool_one_of_list "$ATTACH_FLAGS" 534 fi 535 return 0 536 ;; 537 esac 538 ;; 539 *) 540 [[ $prev == $object ]] && \ 541 COMPREPLY=( $( compgen -W 'help attach detach \ 542 show list tree' -- "$cur" ) ) 543 ;; 544 esac 545 ;; 546 perf) 547 case $command in 548 *) 549 [[ $prev == $object ]] && \ 550 COMPREPLY=( $( compgen -W 'help \ 551 show list' -- "$cur" ) ) 552 ;; 553 esac 554 ;; 555 esac 556} && 557complete -F _bpftool bpftool 558 559# ex: ts=4 sw=4 et filetype=sh 560