1# shellcheck disable=SC2016,SC2207 2 3_get_help() { 4 "$@" -h 2>&1 5} 6 7_get_help_opt() { 8 # Fetch all the optional parameters with help from _parse_help() 9 _parse_help - < <(printf '%s\n' "$@") 10} 11 12_get_help_pos() { 13 local pos 14 15 # Fetch all the positional parameters, i.e. get first word prefixed 16 # with 20h x 2. This may not be 100% accurate. Also, it won't return 17 # any usable strings, it's just meant to point out what type of 18 # mandatory argument given method depends on, like bdev_name, etc. 19 # TODO: separate completion for such arguments, e.g., get all bdevs 20 # for parameter like bdev_name? 21 while read -r; do 22 [[ $REPLY =~ ^\ {2}[^\ -] ]] || continue 23 read -r pos _ <<< "$REPLY" && echo "$pos" 24 done < <(printf '%s\n' "$@") 25} 26 27_get_default_rpc_methods() { 28 local aliases method names 29 # Don't squash whitespaces, slurp the entire line 30 while read -r; do 31 # Each method name seems to be prefixed with 20h x 4. Then it can 32 # be followed with list of aliases enclosed inside (). Example: 33 # ioat_scan_accel_module 34 [[ $REPLY =~ ^\ {4}([a-z0-9]+(_[a-z0-9]+)*)(\ *\((.+)\))? ]] || continue 35 36 names=("${BASH_REMATCH[1]}") 37 if [[ $SPDK_RPC_ALIASES == yes ]] && [[ -n ${BASH_REMATCH[4]} ]]; then 38 IFS=", " read -ra aliases <<< "${BASH_REMATCH[4]}" 39 names+=("${aliases[@]}") 40 fi 41 42 for method in "${names[@]}"; do 43 rpc_methods["$method"]=1 44 done 45 done < <(_get_help "$1" 2> /dev/null) 46} 47 48_get_rpc_methods() { 49 local method methods 50 51 _get_default_rpc_methods "$1" 52 methods=($("$1" -s "$rpc_sock" rpc_get_methods 2> /dev/null)) || return 0 53 ((${#methods[@]} > 0)) || return 0 54 55 # Kill the json flavor 56 methods=("${methods[@]//+(\"|,| )/}") 57 unset -v "methods[0]" "methods[-1]" # [] 58 59 for method in "${methods[@]}"; do 60 rpc_methods["$method"]=1 61 done 62} 63 64_get_help_rpc_method() { 65 local rpc=$1 66 local method=$2 67 local rpc_help opt 68 69 mapfile -t rpc_help < <(_get_help "$rpc" "$method") 70 71 _get_help_pos "${rpc_help[@]}" 72 _get_help_opt "${rpc_help[@]}" 73} 74 75_is_rpc_method() { 76 local word=$1 77 78 [[ -n $word && -n ${rpc_methods["$word"]} ]] 79} 80 81_method_in_words() { 82 for word in "${words[@]}"; do 83 if _is_rpc_method "$word"; then 84 echo "$word" 85 return 0 86 fi 87 done 88 return 1 89} 90 91_set_rpc_sock() { 92 # Look for unix sock each app creates upon its execution. In 93 # first instance, check the cmdline for an -s arg, if it's 94 # followed by the path to the sock, use it. 95 96 local word 97 for ((word = 0; word < ${#words[@]}; word++)); do 98 if [[ ${words[word]} == -s && -S ${words[word + 1]} ]]; then 99 rpc_sock=${words[word + 1]} 100 return 0 101 fi 102 done 103 104 # default .sock 105 [[ -S /var/tmp/spdk.sock ]] && rpc_sock=/var/tmp/spdk.sock 106 107 return 0 108} 109 110_spdk_opt_to_complete() { 111 local opt=$1 112 113 case "$opt" in 114 --pci-blocked | -B | --pci-allowed | -A) 115 local pcis 116 if [[ -e /sys/bus/pci/devices ]]; then 117 pcis=(/sys/bus/pci/devices/*) 118 pcis=("${pcis[@]##*/}") 119 fi 120 COMPREPLY=($(compgen -W '${pcis[*]}' -- "$cur")) 121 compopt -o filenames 122 ;; 123 --main-core | -p) # FIXME: Is this meant to be an actual core id or thread id? Assume the latter 124 local cpus 125 if [[ -e /sys/devices/system/cpu ]]; then 126 cpus=(/sys/devices/system/cpu/cpu+([0-9])) 127 cpus=("${cpus[@]##*cpu}") 128 fi 129 COMPREPLY=($(compgen -W '${cpus[*]}' -- "$cur")) 130 ;; 131 --iova-mode) 132 COMPREPLY=($(compgen -W 'pa va' -- "$cur")) 133 ;; 134 --tpoint-group | -e) 135 COMPREPLY=($(compgen -W '$(_get_tpoint_g_masks)' -- "$cur")) 136 compopt -o nosort 137 ;; 138 --logflag) 139 COMPREPLY=($(compgen -W '$(_get_log_flags)' -- "$cur")) 140 ;; 141 --huge-dir) 142 COMPREPLY=($(compgen -W '$(_get_fs_mounts "hugetlbfs")' -- "$cur")) 143 compopt -o filenames 144 ;; 145 --iflag | --oflag) # spdk_dd specific 146 if [[ ${app##*/} == spdk_dd ]]; then 147 COMPREPLY=($(compgen -W '$(_get_help_pos "${app_help[@]}")' -- "$cur")) 148 fi 149 ;; 150 *) return 1 ;; 151 esac 2> /dev/null 152 return 0 153} 154 155_get_fs_mounts() { 156 [[ $(< /proc/filesystems) == *"$1"* ]] || return 0 157 158 local mount fs mounts 159 while read -r _ mount fs _; do 160 [[ $fs == "$1" ]] && mounts+=("$mount") 161 done < /proc/mounts 162 163 if ((${#mounts[@]} > 0)); then 164 printf '%s\n' "${mounts[@]}" 165 fi 166} 167 168_get_from_spdk_help() { 169 _get_help "$app" |& grep "$1" 170} 171 172_get_tpoint_g_masks() { 173 local g_masks 174 175 g_masks=$(_get_from_spdk_help "tracepoint group mask for spdk trace buffers") || return 0 176 [[ $g_masks =~ \((.+)\) ]] || return 0 177 178 IFS=", " read -ra g_masks <<< "${BASH_REMATCH[1]}" 179 printf '%s\n' "${g_masks[@]}" 180} 181 182_get_log_flags() { 183 local logflags 184 185 logflags=$(_get_from_spdk_help "enable debug log flag") || return 0 186 [[ $logflags =~ \((.+)\) ]] || return 0 187 188 if [[ -n ${BASH_REMATCH[1]} && ${BASH_REMATCH[1]} != "not supported"* ]]; then 189 IFS=", " read -ra logflags <<< "${BASH_REMATCH[1]}" 190 printf '%s\n' "${logflags[@]}" 191 fi 192} 193 194_is_app() { 195 type -P "$1" > /dev/null 196} 197 198_rpc() { 199 local cur prev words 200 201 _init_completion || return 202 _is_app "$1" || return 203 204 local rpc=$1 method="" 205 local -g rpc_sock="" rpc_sock_ts old_rpc_sock_ts 206 local -gA rpc_methods 207 local -gA __comps 208 209 _set_rpc_sock 210 # Verify sock against the creation time - this is meant to cover cases of different 211 # applications re-creating the socket file at the same location. 212 rpc_sock_ts=$(stat --print="%W" "$rpc_sock" 2> /dev/null) 213 # Always try to refresh list of methods when sock changed or when the list 214 # is empty. 215 if [[ $old_rpc_sock_ts != "$rpc_sock_ts" ]] || ((${#rpc_methods[@]} == 0)); then 216 rpc_methods=() 217 _get_rpc_methods "$rpc" 218 fi 219 old_rpc_sock_ts=$rpc_sock_ts 220 221 if method=$(_method_in_words); then 222 if [[ -n ${__comps["$rpc:$method:$cur"]} ]]; then 223 COMPREPLY=(${__comps["$rpc:$method:$cur"]}) 224 else 225 COMPREPLY=($(compgen -W '$(_get_help_rpc_method "$rpc" "$method")' -- "$cur")) 226 __comps["$rpc:$method:$cur"]=${COMPREPLY[*]} 227 fi 228 compopt -o nosort 229 elif [[ $cur == -* ]]; then 230 if [[ -n ${__comps["$rpc:$cur"]} ]]; then 231 COMPREPLY=(${__comps["$rpc:$cur"]}) 232 else 233 COMPREPLY=($(compgen -W '$(_parse_help "$rpc")' -- "$cur")) 234 __comps["$rpc:$cur"]=${COMPREPLY[*]} 235 fi 236 elif [[ $prev == --verbose ]]; then 237 COMPREPLY=($(compgen -W 'DEBUG INFO ERROR' -- "$cur")) 238 elif [[ $prev == -s ]]; then 239 _filedir 240 else 241 COMPREPLY=($(compgen -W '${!rpc_methods[*]}' -- "$cur")) 242 fi 2> /dev/null 243} 244 245_spdk_app() { 246 local cur prev 247 248 _init_completion || return 249 _is_app "$1" || return 250 251 local app=$1 app_help 252 253 mapfile -t app_help < <(_get_help "$app") 254 255 if [[ $cur == -* ]]; then 256 COMPREPLY=($(compgen -W '$(_get_help_opt "${app_help[@]}")' -- "$cur")) 257 else 258 _spdk_opt_to_complete "$prev" || _filedir 259 fi 260} 261 262_setup() { 263 local cur prev 264 265 _init_completion || return 266 _is_app "$1" || return 267 268 COMPREPLY=($(compgen -W 'cleanup config help interactive reset status' -- "$cur")) 269} 270 271# Build simple completion for some common spdk apps|tools 272_spdk_apps() { 273 local apps 274 275 apps=( 276 iscsi_tgt 277 nvmf_tgt 278 spdk_dd 279 spdk_tgt 280 spdk_top 281 spdk_trace_record 282 vhost 283 create_vbox.sh 284 pkgdep.sh 285 run-autorun.sh 286 autotest_setup.sh 287 ) # TODO: Add more? 288 289 complete -o default -F _spdk_app "${apps[@]}" 290 complete -o default -F _rpc rpc.py 291 complete -o default -F _setup setup.sh 292} 293 294_spdk_apps 295 296# Look for _configure(). If it exists, include default completions for path lookups 297if [[ $(type -t _configure) == function ]]; then 298 complete -o default -F _configure configure 299fi 300