netdev-dpdk: fix mbuf leaks
[cascardo/ovs.git] / utilities / ovs-appctl-bashcomp.bash
1 #!/bin/bash
2 #
3 # A bash command completion script for ovs-appctl.
4 #
5 #
6 # Right now, the script can do the following:
7 #
8 #    - display available completion or complete on unfinished user input
9 #      (long option, subcommand, and argument).
10 #
11 #    - once the subcommand (e.g. ofproto/trace) has been given, the
12 #      script will print the subcommand format.
13 #
14 #    - the script can convert between keywords like 'bridge/port/interface/dp'
15 #      and the available record in ovsdb.
16 #
17 # The limitation are:
18 #
19 #    - only support small set of important keywords
20 #      (dp, datapath, bridge, switch, port, interface, iface).
21 #
22 #    - does not support parsing of nested option
23 #      (e.g. ovsdb-tool create [db [schema]]).
24 #
25 #    - does not support expansion on repeatitive argument
26 #      (e.g. ovs-dpctl show [dp...]).
27 #
28 #    - only support matching on long options, and only in the format
29 #      (--option [arg], i.e. should not use --option=[arg]).
30 #
31 #
32 #
33 # Keywords
34 # ========
35 #
36 #
37 #
38 # Expandable keywords.
39 _KWORDS=(bridge switch port interface iface dp_name dp)
40 # Command name.
41 _COMMAND=
42 # Printf enabler.
43 _PRINTF_ENABLE=
44 # Bash prompt.
45 _BASH_PROMPT=
46 # Output to the compgen.
47 _COMP_WORDLIST=
48
49 #
50 # For ovs-appctl command only.
51 #
52 # Target in the current completion, default ovs-vswitchd.
53 _APPCTL_TARGET=
54 # Possible targets.
55 _POSSIBLE_TARGETS="ovs-vswitchd ovsdb-server ovs-ofctl"
56
57 # Command Extraction
58 # ==================
59 #
60 #
61 #
62 # Extracts all subcommands of 'command'.
63 # If fails, returns nothing.
64 extract_subcmds() {
65     local command=$_COMMAND
66     local target=
67     local subcmds error
68
69     if [ -n "$_APPCTL_TARGET" ]; then
70         target="--target $_APPCTL_TARGET"
71     fi
72
73     subcmds="$($command $target list-commands 2>/dev/null | tail -n +2 | cut -c3- \
74                  | cut -d ' ' -f1)" || error="TRUE"
75
76     if [ -z "$error" ]; then
77         echo "$subcmds"
78     fi
79 }
80
81 # Extracts all long options of ovs-appctl.
82 # If fails, returns nothing.
83 extract_options() {
84     local command=$_COMMAND
85     local options error
86
87     options="$($command --option 2>/dev/null | sort | sed -n '/^--.*/p' | cut -d '=' -f1)" \
88         || error="TRUE"
89
90     if [ -z "$error" ]; then
91         echo "$options"
92     fi
93 }
94
95 # Returns the option format, if the option asks for an argument.
96 # If fails, returns nothing.
97 option_require_arg() {
98     local command=$_COMMAND
99     local option=$1
100     local require_arg error
101
102     require_arg="$($command --option | sort | sed -n '/^--.*/p' | grep -- "$option" | grep -- "=")" \
103         || error="TRUE"
104
105     if [ -z "$error" ]; then
106         echo "$require_arg"
107     fi
108 }
109
110 # Combination Discovery
111 # =====================
112 #
113 #
114 #
115 # Given the subcommand formats, finds all possible completions
116 # at current completion level.
117 find_possible_comps() {
118     local combs="$@"
119     local comps=
120     local line
121
122     while read line; do
123         local arg=
124
125         for arg in $line; do
126             # If it is an optional argument, gets all completions,
127             # and continues.
128             if [ -n "$(sed -n '/^\[.*\]$/p' <<< "$arg")" ]; then
129                 local opt_arg="$(sed -e 's/^\[\(.*\)\]$/\1/' <<< "$arg")"
130                 local opt_args=()
131
132                 IFS='|' read -a opt_args <<< "$opt_arg"
133                 comps="${opt_args[@]} $comps"
134             # If it is in format "\[*", it is a start of nested
135             # option, do not parse.
136             elif [ -n "$(sed -n "/^\[.*$/p" <<< "$arg")" ]; then
137                 break;
138             # If it is a compulsory argument, adds it to the comps
139             # and break, since all following args are for next stage.
140             else
141                 local args=()
142
143                 IFS='|' read -a args <<< "$arg"
144                 comps="${args[@]} $comps"
145                 break;
146             fi
147         done
148     done <<< "$combs"
149
150     echo "$comps"
151 }
152
153 # Given the subcommand format, and the current command line input,
154 # finds keywords of all possible completions.
155 subcmd_find_keyword_based_on_input() {
156     local format="$1"
157     local cmd_line=($2)
158     local mult=
159     local combs=
160     local comps=
161     local arg line
162
163     # finds all combinations by searching for '{}'.
164     # there should only be one '{}', otherwise, the
165     # command format should be changed to multiple commands.
166     mult="$(sed -n 's/^.*{\(.*\)}.*$/ \1/p' <<< "$format" | tr '|' '\n' | cut -c1-)"
167     if [ -n "$mult" ]; then
168         while read line; do
169             local tmp=
170
171             tmp="$(sed -e "s@{\(.*\)}@$line@" <<< "$format")"
172             combs="$combs@$tmp"
173         done <<< "$mult"
174         combs="$(tr '@' '\n' <<< "$combs")"
175     else
176         combs="$format"
177     fi
178
179     # Now, starts from the first argument, narrows down the
180     # subcommand format combinations.
181     for arg in "${subcmd_line[@]}"; do
182         local kword possible_comps
183
184         # Finds next level possible comps.
185         possible_comps=$(find_possible_comps "$combs")
186         # Finds the kword.
187         kword="$(arg_to_kwords "$arg" "$possible_comps")"
188         # Returns if could not find 'kword'
189         if [ -z "$kword" ]; then
190             return
191         fi
192         # Trims the 'combs', keeps context only after 'kword'.
193         if [ -n "$combs" ]; then
194             combs="$(sed -n "s@^.*\[\{0,1\}$kword|\{0,1\}[a-z_]*\]\{0,1\} @@p" <<< "$combs")"
195         fi
196     done
197     comps="$(find_possible_comps "$combs")"
198
199     echo "$comps"
200 }
201
202
203
204 # Helper
205 # ======
206 #
207 #
208 #
209 # Prints the input to stderr.  $_PRINTF_ENABLE must be filled.
210 printf_stderr() {
211     local stderr_out="$@"
212
213     if [ -n "$_PRINTF_ENABLE" ]; then
214         printf "\n$stderr_out" 1>&2
215     fi
216 }
217
218 # Extracts the bash prompt PS1, outputs it with the input argument
219 # via 'printf_stderr'.
220 #
221 # Original idea inspired by:
222 # http://stackoverflow.com/questions/10060500/bash-how-to-evaluate-ps1-ps2
223 #
224 # The code below is taken from Peter Amidon.  His change makes it more
225 # robust.
226 extract_bash_prompt() {
227     local myPS1 v
228
229     myPS1="$(sed 's/Begin prompt/\\Begin prompt/; s/End prompt/\\End prompt/' <<< "$PS1")"
230     v="$(bash --norc --noprofile -i 2>&1 <<< $'PS1=\"'"$myPS1"$'\" \n# Begin prompt\n# End prompt')"
231     v="${v##*# Begin prompt}"
232     _BASH_PROMPT="$(tail -n +2 <<< "${v%# End prompt*}" | sed 's/\\Begin prompt/Begin prompt/; s/\\End prompt/End prompt/')"
233 }
234
235
236
237 # Keyword Conversion
238 # ==================
239 #
240 #
241 #
242 # All completion functions.
243 complete_bridge () {
244     local result error
245
246     result=$(ovs-vsctl list-br 2>/dev/null | grep -- "^$1") || error="TRUE"
247
248     if [ -z "$error" ]; then
249         echo  "${result}"
250     fi
251 }
252
253 complete_port () {
254     local ports result error
255     local all_ports
256
257     all_ports=$(ovs-vsctl --format=table \
258         --no-headings \
259         --columns=name \
260         list Port 2>/dev/null) || error="TRUE"
261     ports=$(printf "$all_ports" | sort | tr -d '"' | uniq -u)
262     result=$(grep -- "^$1" <<< "$ports")
263
264     if [ -z "$error" ]; then
265         echo  "${result}"
266     fi
267 }
268
269 complete_iface () {
270     local bridge bridges result error
271
272     bridges=$(ovs-vsctl list-br 2>/dev/null) || error="TRUE"
273     for bridge in $bridges; do
274         local ifaces
275
276         ifaces=$(ovs-vsctl list-ifaces "${bridge}" 2>/dev/null) || error="TRUE"
277         result="${result} ${ifaces}"
278     done
279
280     if [ -z "$error" ]; then
281         echo  "${result}"
282     fi
283 }
284
285 complete_dp () {
286     local dps result error
287
288     dps=$(ovs-appctl dpctl/dump-dps 2>/dev/null | cut -d '@' -f2) || error="TRUE"
289     result=$(grep -- "^$1" <<< "$dps")
290
291     if [ -z "$error" ]; then
292         echo  "${result}"
293     fi
294 }
295
296 # Converts the argument (e.g. bridge/port/interface/dp name) to
297 # the corresponding keywords.
298 # Returns empty string if could not map the arg to any keyword.
299 arg_to_kwords() {
300     local arg="$1"
301     local possible_kwords=($2)
302     local non_parsables=()
303     local match=
304     local kword
305
306     for kword in ${possible_kwords[@]}; do
307         case "$kword" in
308             bridge|switch)
309                 match="$(complete_bridge "$arg")"
310                 ;;
311             port)
312                 match="$(complete_port "$arg")"
313                 ;;
314             interface|iface)
315                 match="$(complete_iface "$arg")"
316                 ;;
317             dp_name|dp)
318                 match="$(complete_dp "$arg")"
319                 ;;
320             *)
321                 if [ "$arg" = "$kword" ]; then
322                     match="$kword"
323                 else
324                     non_parsables+=("$kword")
325                     continue
326                 fi
327                 ;;
328         esac
329
330         if [ -n "$match" ]; then
331             echo "$kword"
332             return
333         fi
334     done
335
336     # If there is only one non-parsable kword,
337     # just assumes the user input it.
338     if [ "${#non_parsables[@]}" -eq "1" ]; then
339         echo "$non_parsables"
340         return
341     fi
342 }
343
344 # Expands the keywords to the corresponding instance names.
345 kwords_to_args() {
346     local possible_kwords=($@)
347     local args=()
348     local printf_expand_once=
349     local kword
350
351     for kword in ${possible_kwords[@]}; do
352         local match=
353
354         case "${kword}" in
355             bridge|switch)
356                 match="$(complete_bridge "")"
357                 ;;
358             port)
359                 match="$(complete_port "")"
360                 ;;
361             interface|iface)
362                 match="$(complete_iface "")"
363                 ;;
364             dp_name|dp)
365                 match="$(complete_dp "")"
366                 ;;
367             -*)
368                 # Treats option as kword as well.
369                 match="$kword"
370                 ;;
371             *)
372                 match=
373                 ;;
374         esac
375         match=$(echo "$match" | tr '\n' ' ' | tr -s ' ' | sed -e 's/^[ \t]*//')
376         args+=( $match )
377         if [ -n "$_PRINTF_ENABLE" ]; then
378             local output_stderr=
379
380             if [ -z "$printf_expand_once" ]; then
381                 printf_expand_once="once"
382                 printf -v output_stderr "\nArgument expansion:\n"
383             fi
384             printf -v output_stderr "$output_stderr     available completions \
385 for keyword \"%s\": %s " "$kword" "$match"
386
387             printf_stderr "$output_stderr"
388         fi
389     done
390
391     echo "${args[@]}"
392 }
393
394
395
396
397 # Parse and Compgen
398 # =================
399 #
400 #
401 #
402 # This function takes the current command line arguments as input,
403 # finds the command format and returns the possible completions.
404 parse_and_compgen() {
405     local command=$_COMMAND
406     local subcmd_line=($@)
407     local subcmd=${subcmd_line[0]}
408     local target=
409     local subcmd_format=
410     local comp_keywords=
411     local comp_wordlist=
412
413     if [ -n "$_APPCTL_TARGET" ]; then
414         target="--target $_APPCTL_TARGET"
415     fi
416
417     # Extracts the subcommand format.
418     subcmd_format="$($command $target list-commands 2>/dev/null | tail -n +2 | cut -c3- \
419                      | awk -v opt=$subcmd '$1 == opt {print $0}' | tr -s ' ' )"
420
421     # Finds the possible completions based on input argument.
422     comp_keyword="$(subcmd_find_keyword_based_on_input "$subcmd_format" \
423                      "${subcmd_line[@]}")"
424
425     # Prints subcommand format and expands the keywords if 'comp_keyword'
426     # is not empty.
427     if [ -n "$comp_keyword" ]; then
428         printf_stderr "$(printf "\nCommand format:\n%s" "$subcmd_format")"
429         comp_wordlist="$(kwords_to_args "$comp_keyword")"
430         # If there is no expanded completions, returns "NO_EXPAN" to
431         # distinguish from the case of no available completions.
432         if [ -z "$comp_wordlist" ]; then
433             echo "NO_EXPAN"
434         else
435             echo "$comp_wordlist"
436         fi
437     fi
438 }
439
440
441
442 # Compgen Helper
443 # ==============
444 #
445 #
446 #
447 # Takes the current command line arguments and returns the possible
448 # completions.
449 #
450 # At the beginning, the options are checked and completed.  For ovs-appctl
451 # completion, The function looks for the --target option which gives the
452 # target daemon name.  If it is not provided, by default, 'ovs-vswitchd'
453 # is used.
454 #
455 # Then, tries to locate and complete the subcommand.  If the subcommand
456 # is provided, the following arguments are passed to the 'parse_and_compgen'
457 # function to figure out the corresponding completion of the subcommand.
458 #
459 # Returns the completion arguments on success.
460 ovs_comp_helper() {
461     local cmd_line_so_far=($@)
462     local comp_wordlist _subcmd options i
463     local j=-1
464
465     # Parse the command-line args till we find the subcommand.
466     for i in "${!cmd_line_so_far[@]}"; do
467         # if $i is not greater than $j, it means the previous iteration
468         # skips not-visited args.  so, do nothing and catch up.
469         if [ $i -le $j ]; then continue; fi
470         j=$i
471         if [[ "${cmd_line_so_far[i]}" =~ ^--*  ]]; then
472             # If --target is found, locate the target daemon.
473             # Else, it is an option command, fill the comp_wordlist with
474             # all options.
475             if [ "$_COMMAND" = "ovs-appctl" ] \
476                 && [[ "${cmd_line_so_far[i]}" =~ ^--target$ ]]; then
477                 _APPCTL_TARGET="ovs-vswitchd"
478
479                 if [ -n "${cmd_line_so_far[j+1]}" ]; then
480                     local daemon
481
482                     for daemon in $_POSSIBLE_TARGETS; do
483                         # Greps "$daemon" in argument, since the argument may
484                         # be the path to the pid file.
485                         if [ "$daemon" = "${cmd_line_so_far[j+1]}" ]; then
486                             _APPCTL_TARGET="$daemon"
487                             ((j++))
488                             break
489                         fi
490                     done
491                     continue
492                 else
493                     comp_wordlist="$_POSSIBLE_TARGETS"
494                     break
495                 fi
496             else
497                 options="$(extract_options $_COMMAND)"
498                 # See if we could find the exact option.
499                 if [ "${cmd_line_so_far[i]}" = "$(grep -- "${cmd_line_so_far[i]}" <<< "$options")" ]; then
500                     # If an argument is required and next argument is non-empty,
501                     # skip it.  Else, return directly.
502                     if [ -n "$(option_require_arg "${cmd_line_so_far[i]}")" ]; then
503                         ((j++))
504                         if [ -z "${cmd_line_so_far[j]}" ]; then
505                             printf_stderr "\nOption requires an arugment."
506                             return
507                         fi
508                     fi
509                     continue
510                 # Else, need to keep completing on option.
511                 else
512                     comp_wordlist="$options"
513                     break
514                 fi
515             fi
516         fi
517         # Takes the first non-option argument as subcmd.
518         _subcmd="${cmd_line_so_far[i]}"
519         break
520     done
521
522     if [ -z "$comp_wordlist" ]; then
523         # If the subcommand is not found, provides all subcmds and options.
524         if [ -z "$_subcmd" ]; then
525             comp_wordlist="$(extract_subcmds) $(extract_options)"
526         # Else parses the current arguments and finds the possible completions.
527         else
528             # $j stores the index of the subcmd in cmd_line_so_far.
529             comp_wordlist="$(parse_and_compgen "${cmd_line_so_far[@]:$j}")"
530         fi
531     fi
532
533     echo "$comp_wordlist"
534 }
535
536 # Compgen
537 # =======
538 #
539 #
540 #
541 # The compgen function.
542 _ovs_command_complete() {
543   local cur prev
544
545   _COMMAND=${COMP_WORDS} # element 0 is the command.
546   COMPREPLY=()
547   cur=${COMP_WORDS[COMP_CWORD]}
548
549   # Do not print anything at first [TAB] execution.
550   if [ "$COMP_TYPE" -eq "9" ]; then
551       _PRINTF_ENABLE=
552   else
553       _PRINTF_ENABLE="enabled"
554   fi
555
556   # Extracts bash prompt PS1.
557   if [ "$1" != "debug" ]; then
558       extract_bash_prompt
559   fi
560
561   # Invokes the helper function to get all available completions.
562   # Always not input the 'COMP_WORD' at 'COMP_CWORD', since it is
563   # the one to be completed.
564   _COMP_WORDLIST="$(ovs_comp_helper \
565       ${COMP_WORDS[@]:1:COMP_CWORD-1})"
566
567   # This is a hack to prevent autocompleting when there is only one
568   # available completion and printf disabled.
569   if [ -z "$_PRINTF_ENABLE" ] && [ -n "$_COMP_WORDLIST" ]; then
570       _COMP_WORDLIST="$_COMP_WORDLIST none void no-op"
571   fi
572
573   if [ -n "$_PRINTF_ENABLE" ] && [ -n "$_COMP_WORDLIST" ]; then
574       if [ -n "$(echo $_COMP_WORDLIST | tr ' ' '\n' | sed -e '/NO_EXPAN/d' | grep -- "^$cur")" ]; then
575           printf_stderr "\nAvailable completions:\n"
576       else
577           if [ "$1" != "debug" ]; then
578               # If there is no match between '$cur' and the '$_COMP_WORDLIST'
579               # prints a bash prompt since the 'complete' will not print it.
580               printf_stderr "\n$_BASH_PROMPT${COMP_WORDS[@]}"
581           fi
582       fi
583   fi
584
585   if [ "$1" = "debug" ]; then
586       printf_stderr "$(echo $_COMP_WORDLIST | tr ' ' '\n' | sort -u | sed -e '/NO_EXPAN/d' | grep -- "$cur")\n"
587   else
588       if [ -n "$_COMP_WORDLIST" ]; then
589           COMPREPLY=( $(compgen -W "$(echo $_COMP_WORDLIST | tr ' ' '\n' \
590                                  | sort -u | sed -e '/NO_EXPAN/d')" -- $cur) )
591       else
592           compopt -o nospace
593           # If there is no completions, just complete on file path.
594           _filedir
595       fi
596   fi
597
598   return 0
599 }
600
601 # Debug mode.
602 if [ "$1" = "debug" ]; then
603     shift
604     COMP_TYPE=0
605     COMP_WORDS=($@)
606     COMP_CWORD="$(expr $# - 1)"
607
608     # If the last argument is TAB, it means that the previous
609     # argument is already complete and script should complete
610     # next argument which is not input yet.  This hack is for
611     # compromising the fact that bash cannot take unquoted
612     # empty argument.
613     if [ "${COMP_WORDS[$COMP_CWORD]}" = "TAB" ]; then
614         COMP_WORDS[$COMP_CWORD]=""
615     fi
616
617     _ovs_command_complete "debug"
618 # Normal compgen mode.
619 else
620     complete -F _ovs_command_complete ovs-appctl
621     complete -F _ovs_command_complete ovs-ofctl
622     complete -F _ovs_command_complete ovs-dpctl
623     complete -F _ovs_command_complete ovsdb-tool
624 fi