# vyatta bash configuration mode completion # **** License **** # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # A copy of the GNU General Public License is available as # `/usr/share/common-licenses/GPL' in the Debian GNU/Linux distribution # or on the World Wide Web at `http://www.gnu.org/copyleft/gpl.html'. # You can also obtain it by writing to the Free Software Foundation, # Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, # MA 02110-1301, USA. # # Author: Vyatta # Description: bash completion for Vyatta configuration commands # # **** End License **** # Turn on history logging export HISTCONTROL= export HISTFILESIZE=10000 export HISTSIZE=10000 export HISTTIMEFORMAT='%FT%T%z ' export PROMPT_COMMAND='history -a' # remove colon from completion word seperators export COMP_WORDBREAKS=${COMP_WORDBREAKS/:/} builtin set histappend=1 # only do this if we are going into configure mode if [ "$_OFR_CONFIGURE" != "ok" ]; then return 0 fi if [ -r /etc/default/vyatta ]; then source /etc/default/vyatta fi ### Top level commands and help ### _vyatta_cfg_cmds=( "confirm" \ "comment" \ "commit" \ "commit-confirm" \ "compare" \ "copy" \ "delete" \ "discard" \ "edit" \ "exit" \ "load" \ "loadkey" \ "merge" \ "rename" \ "rollback" \ "run" \ "save" \ "set" \ "show" ) _vyatta_cfg_helps=( \ "Confirm prior commit-confirm" \ "Add comment to this configuration element" \ "Commit the current set of changes" \ "Commit the current set of changes with 'confirm' required" \ "Compare configuration revisions" \ "Copy a configuration element" \ "Delete a configuration element" \ "Discard uncommitted changes" \ "Edit a sub-element" \ "Exit from this configuration level" \ "Load configuration from a file and replace running configuration" \ "Load user SSH key from a file" \ "Load configuration from a file and merge running configuration" \ "Rename a configuration element" \ "Rollback to a prior config revision (requires reboot)" \ "Run an operational-mode command" \ "Save configuration to a file" \ "Set the value of a parameter or create a new element" \ "Show the configuration (default values may be suppressed)" \ ) ### End Top level commands and help ### # function for shell api vyatta_cli_shell_api () { local noeval='' if [ "$1" == NOEVAL ]; then noeval=true shift fi local outstr if ! outstr=$(${vyatta_sbindir}/my_cli_shell_api -- "$@"); then # display the error output (if any) and then fail if [ -n "$outstr" ]; then echo "$outstr" fi return 1 fi # eval the output (if any) if [ -n "$outstr" ]; then if [ -n "$noeval" ]; then echo "$outstr" else eval "$outstr" fi fi return 0 } # set up the session environment ## note: this can not use vyatta_cli_shell_api() above since it "declares" ## env vars. eval "$(${vyatta_sbindir}/my_cli_shell_api getSessionEnv $$)" declare is_set=0 declare last_idx=0 declare -a comp_words=() # commands to unalias declare -a unalias_cmds=( clear configure date debug edit exit load merge \ no run set show save terminal undebug up top ) for cmd in "${unalias_cmds[@]}"; do unalias $cmd >& /dev/null done ### Top level command wrappers ### vyatta_config_show () { local -a opts=() local -a args=() for arg in "$@"; do if [ "$arg" == "-all" ]; then opts+=('--show-show-defaults') else args+=("$arg") fi done cli-shell-api "${opts[@]}" -- showCfg "${args[@]}" \ | eval "${VYATTA_PAGER:-cat}" } vyatta_config_commit () { if ! vyatta_cli_shell_api sessionChanged; then echo "No configuration changes to commit" return 1; fi local comment="commit" local next=0 local -a args=() for arg in "$@"; do if [ "$next" == "1" ]; then comment=$arg next=0; elif [ "$arg" == "comment" ]; then next=1 elif [ "$arg" == "confirm" ]; then echo Use commit-confirm command return 1; else args[${#args[@]}]="$arg" fi done export COMMIT_COMMENT="$comment" export COMMIT_VIA=cli /opt/vyatta/sbin/my_commit "${args[@]}" 2>&1 unset COMMIT_VIA unset COMMIT_COMMENT } vyatta_config_commit-confirm () { if ! vyatta_cli_shell_api sessionChanged; then echo "No configuration changes to commit" return 1; fi local -a args=() local first=1 local minutes=10 for arg in "$@"; do if [ "$first" = "1" ]; then if [[ $arg = *[[:digit:]]* ]]; then minutes=$arg else args[${#args[@]}]="$arg" fi first=0 else args[${#args[@]}]="$arg" fi done cmd="${vyatta_sbindir}/vyatta-config-mgmt.pl --action=commit-confirm \ --minutes=$minutes" eval "sudo sg vyattacfg \"$cmd\" " if [ $? = 0 ]; then commit "$@" fi } vyatta_config_confirm () { ${vyatta_sbindir}/vyatta-config-mgmt.pl --action=confirm } vyatta_config_compare () { ${vyatta_sbindir}/vyatta-config-mgmt.pl --action=diff "$@" | eval "${VYATTA_PAGER:-cat}" } vyatta_config_save () { if vyatta_cli_shell_api sessionChanged; then echo -e "Warning: you have uncommitted changes that will not be saved.\n" fi # return to top level. reset_edit_level # transform individual args into quoted strings local arg='' local save_cmd="${vyatta_sbindir}/vyatta-save-config.pl" for arg in "$@"; do save_cmd+=" '$arg'" done eval "sudo sg vyattacfg \"umask 0002 ; $save_cmd\"" vyatta_cli_shell_api unmarkSessionUnsaved } reboot () { echo "Exit from configure mode before rebooting." } vyatta_config_rollback () { if [ $# != 1 ]; then echo "Error: must include a revision # to rollback to" return 1; fi ${vyatta_sbindir}/vyatta-config-mgmt.pl --action=rollback --revnum "$@" } shutdown () { echo "Exit from configure mode before shutting down system." } reset_edit_level () { vyatta_cli_shell_api getEditResetEnv return $? } vyatta_config_load () { # don't load if there are uncommitted changes. if vyatta_cli_shell_api sessionChanged; then echo "Cannot load: configuration modified." echo "Commit or discard the changes before loading a config file." return 1 fi # return to top level. reset_edit_level ${vyatta_sbindir}/vyatta-load-config.pl "$@" } vyatta_config_merge () { # don't load if there are uncommitted changes. if vyatta_cli_shell_api sessionChanged; then echo "Cannot load: configuration modified." echo "Commit or discard the changes before loading a config file." return 1 fi # return to top level. reset_edit_level ${vyatta_sbindir}/vyatta-load-config.pl "$@" --merge } top () { if vyatta_cli_shell_api editLevelAtRoot; then echo "Already at the top level" return 0 fi # go to the top level. reset_edit_level } vyatta_config_edit () { vyatta_cli_shell_api getEditEnv "$@" return $? } up () { vyatta_cli_shell_api getEditUpEnv "$@" return $? } really_exit() { if vyatta_cli_shell_api sessionUnsaved; then echo "Warning: configuration changes have not been saved." fi vyatta_cli_shell_api teardownSession unset _OFR_CONFIGURE builtin exit 0 } vyatta_config_exit () { local discard if [ $# == 0 ]; then discard=0 elif [ $# == 1 ] && [ "$1" == "discard" ]; then discard=1 else echo "Invalid argument \"$*\" for 'exit'" return 1 fi if vyatta_cli_shell_api editLevelAtRoot; then # we are at the root level. check if we can really exit. if vyatta_cli_shell_api sessionChanged; then if (( ! discard )); then echo "Cannot exit: configuration modified." echo "Use 'exit discard' to discard the changes and exit." return 1 fi fi really_exit fi # "exit" to the root level. reset_edit_level } # run op mode commands vyatta_config_run () { if [ $# == 0 ]; then echo Incomplete command return 1 fi /opt/vyatta/bin/vyatta-op-cmd-wrapper $@ } ### End Top level command wrappers ### ### Top level command completions ### # do op mode completion vyatta_run_complete () { local restore_shopts=$( shopt -p extglob nullglob | tr \\n \; ) shopt -s extglob nullglob COMP_WORDS=( "${COMP_WORDS[@]:1}" ) (( COMP_CWORD -= 1 )) _vyatta_op_expand eval $restore_shopts } vyatta_loadsave_complete() { # Generate completion help for the "load" and "save" commands local restore_shopts=$( shopt -p extglob nullglob | tr \\n \; ) shopt -s extglob nullglob # Only provide completions after command name has been typed, but # before any characters of the command argument have been entered. # File name completion, and completion of the various URL formats # is not supported yet. # if [ $COMP_CWORD -eq 1 -a -z "${COMP_WORDS[1]}" ]; then local command=$(vyatta_cfg_expand_top_level ${COMP_WORDS[0]}) echo echo "Possible completions:" if [ "$command" = "load" ]; then echo -e " \t\t\t\tLoad from system config file" echo -e " \t\t\t\tLoad from file on local machine" echo -e " scp://:@/\tLoad from file on remote machine" echo -e " ftp://:@/\tLoad from file on remote machine" echo -e " http:///\t\t\tLoad from file on remote machine" echo -e " tftp:///\t\t\tLoad from file on remote machine" elif [ "$command" = "merge" ]; then echo -e " \t\t\t\tMerge from system config file" echo -e " \t\t\t\tMerge from file on local machine" echo -e " scp://:@/\tMerge from file on remote machine" echo -e " ftp://:@/\tMerge from file on remote machine" echo -e " http:///\t\t\tMerge from file on remote machine" echo -e " tftp:///\t\t\tMerge from file on remote machine" elif [ "$command" = "save" ]; then echo -e " \t\t\t\tSave to system config file" echo -e " \t\t\t\tSave to file on local machine" echo -e " scp://:@/\tSave to file on remote machine" echo -e " ftp://:@/\tSave to file on remote machine" echo -e " tftp:///\t\t\tSave to file on remote machine" fi COMPREPLY=( "" " " ) else COMPREPLY=() fi eval $restore_shopts } vyatta_config_loadkey() { # don't load if there are uncommitted changes. if vyatta_cli_shell_api sessionChanged; then echo "Cannot load: configuration modified." echo "Commit or discard the changes before loading a config file." return 1 fi # return to top level. reset_edit_level ${vyatta_sbindir}/vyatta-load-user-key.pl "$@" } vyatta_loadkey_complete() { case "$COMP_CWORD" in 1) if [ -z "${COMP_WORDS[1]}" ]; then COMPREPLY=( $(getent passwd | awk -F: '$7 == "/bin/vbash" { print $1}') ) else COMPREPLY=( $(compgen -u -- ${COMP_WORDS[1]} ) ) fi ;; 2) if [ -z "${COMP_WORDS[2]}" ]; then echo echo "Possible completions:" echo -e " \t\t\t\tLoad from file on local machine" echo -e " scp://@/\tLoad from file on remote machine" echo -e " ftp://@/\tLoad from file on remote machine" echo -e " http:///\t\t\tLoad from file on remote machine" echo -e " tftp:///\t\t\tLoad from file on remote machine" COMPREPLY=() else COMPREPLY=( $(compgen -f -- ${COMP_WORDS[2]} ) ) fi ;; esac } print_commit_log () { local -a array eval "array=($(${vyatta_sbindir}/vyatta-config-mgmt.pl --action=show-commit-log-brief))" local count=0 for i in "${array[@]}"; do i=${i//_/ } echo -e " $count\t$i" (( count++ )) done } vyatta_rollback_complete () { # Generate completion help for the "rollback" command local restore_shopts=$( shopt -p extglob nullglob | tr \\n \; ) shopt -s extglob nullglob # Only provide completions after command name has been typed, but # before any characters of the command argument have been entered. if [ $COMP_CWORD -eq 1 -a -z "${COMP_WORDS[1]}" ]; then echo echo "Possible completions:" echo -e " \tRollback to revision N (currently requires reboot)" print_commit_log COMPREPLY=( "" " " ) else COMPREPLY=() fi eval $restore_shopts } vyatta_compare_complete () { # Generate completion help for the "compare" command local restore_shopts=$( shopt -p extglob nullglob | tr \\n \; ) shopt -s extglob nullglob # Only provide completions after command name has been typed, but # before any characters of the command argument have been entered. if [ $COMP_CWORD -eq 1 -a -z "${COMP_WORDS[1]}" ]; then echo echo "Possible completions:" echo -e " \tCompare working & active configurations" echo -e " \t\tCompare working with revision N" echo -e " \tCompare revision N with M" print_commit_log COMPREPLY=( "" " " ) elif [ $COMP_CWORD -eq 2 -a -z "${COMP_WORDS[2]}" ]; then echo echo "Possible completions:" echo -e " \tCompare working & active configurations" echo -e " \t\tCompare working with revision N" echo -e " \tCompare revision N with M" print_commit_log COMPREPLY=( "" " " ) else COMPREPLY=() fi eval $restore_shopts } vyatta_commit_complete () { # Generate completion help for the "commit-confirm" command local restore_shopts=$( shopt -p extglob nullglob | tr \\n \; ) shopt -s extglob nullglob # Only provide completions after command name has been typed, but # before any characters of the command argument have been entered. if [ $COMP_CWORD -eq 1 -a -z "${COMP_WORDS[1]}" ]; then echo echo "Possible completions:" if [ "${COMP_WORDS[0]}" = "commit" ]; then echo -e " \tCommit working configuration" elif [ "${COMP_WORDS[0]}" = "commit-confirm" ]; then echo -e " \tCommit, rollback/reboot in 10 minutes if no confirm" echo -e " \tCommit, rollback/reboot in N minutes if no confirm" fi echo -e " comment\tComment for commit log" COMPREPLY=( "" " " ) elif [ "${COMP_WORDS[1]}" = "comment" ]; then echo echo "Possible completions:" echo -e " \tText comment for commit log (e.g. \"add user bob\")" COMPREPLY=( "" " " ) else COMPREPLY=() fi eval $restore_shopts } ### End Top level command completions ### get_prefix_filtered_list () { # $1: prefix # $2: \@list # $3: \@filtered declare -a olist local pfx=$1 pfx=${pfx#\"} eval "olist=( \"\${$2[@]}\" )" local idx=0 for elem in "${olist[@]}"; do local sub=${elem#$pfx} if [ "$elem" == "$sub" ] && [ -n "$pfx" ]; then continue fi eval "$3[$idx]=\"$elem\"" (( idx++ )) done } get_prefix_filtered_list2 () { # $1: prefix # $2: \@list # $3: \@filtered # $4: \@list2 # $5: \@filtered2 declare -a olist local pfx=$1 pfx=${pfx#\"} eval "olist=( \"\${$2[@]}\" )" eval "local orig_len=\${#$2[@]}" local orig_idx=0 local idx=0 for (( orig_idx = 0; orig_idx < orig_len; orig_idx++ )); do eval "local elem=\${$2[$orig_idx]}" eval "local elem2=\${$4[$orig_idx]}" local sub=${elem#$pfx} if [ "$elem" == "$sub" ] && [ -n "$pfx" ]; then continue fi eval "$3[$idx]=\"$elem\"" eval "$5[$idx]=\"$elem2\"" (( idx++ )) done } declare vyatta_cfg_help="" declare vyatta_cfg_type="" declare vyatta_cfg_tag=0 declare vyatta_cfg_multi=0 declare -a vyatta_cfg_allowed=() declare vyatta_cfg_comp_help="" declare -a vyatta_cfg_val_type=() declare -a vyatta_cfg_val_help=() declare -a _get_help_text_items=() declare -a _get_help_text_helps=() get_help_text () { vyatta_help_text="\\nPossible completions:" for (( idx = 0; idx < ${#_get_help_text_items[@]}; idx++ )); do vyatta_help_text+="\\n\\x20\\x20" if [ ${#_get_help_text_items[idx]} -lt 6 ]; then vyatta_help_text+="${_get_help_text_items[idx]}\\t\\t" elif [ ${#_get_help_text_items[idx]} -lt 14 ]; then vyatta_help_text+="${_get_help_text_items[idx]}\\t" else vyatta_help_text+="${_get_help_text_items[idx]}\\n\\x20\\x20\\t\\t" fi vyatta_help_text+="${_get_help_text_helps[idx]}" done if [ -n "$vyatta_cfg_comp_help" ]; then local hstr=${vyatta_cfg_comp_help//\'/\'\\\\\\\'\'} vyatta_help_text+="\\n\\nDetailed information:\\n" local sIFS=$IFS IFS=' ' local chstr=$(echo -en "$hstr\n" \ | while read comp_help_line; do echo "vyatta_help_text+=' $comp_help_line\\n';" done) eval "$chstr" IFS=$sIFS fi } get_value_format_string () { local vtype=$1 if [[ $vtype = !* ]]; then echo -n '!' vtype="${vtype#!}" fi case "$vtype" in _*) echo -n "${vtype#_}" ;; txt) echo -n '' ;; u32) echo -n '<0-4294967295>' ;; u32:*) echo -n "<${vtype##u32:}>" ;; range) echo -n "-" ;; ipv4) echo -n '' ;; ipv6) echo -n '' ;; ipv4net) echo -n '' ;; ipv6net) echo -n '' ;; ipv4range) echo -n '-' ;; ipv6range) echo -n '-' ;; bool) echo -n '' ;; macaddr) echo -n '' ;; *) echo -n "$vtype" ;; esac } declare -a vyatta_completions declare vyatta_help_text="\\nNo help text available" declare vyatta_do_help=false vyatta_do_complete () { if $vyatta_do_help; then printf "$vyatta_help_text" COMPREPLY=( "" " " ) else local -a f_comps=() local cword= if (( ${#COMP_WORDS[@]} > 0 )); then cword=${COMP_WORDS[COMP_CWORD]} fi get_prefix_filtered_list "$cword" vyatta_completions f_comps local estr="COMPREPLY=( " for w in "${f_comps[@]}"; do estr="$estr\"$w\" " done estr="${estr})" eval "$estr" fi vyatta_help_text="\\nNo help text available" } vyatta_simple_complete () { # when this function is called, it is expected that: # * "vyatta_help_text" is filled with the help text. # * "vyatta_completions" is an array of "filtered" possible completions # (i.e., only those starting with the current last component). if $vyatta_do_help; then printf "$vyatta_help_text" COMPREPLY=( "" " " ) else COMPREPLY=( "${vyatta_completions[@]}" ) fi vyatta_help_text="\\nNo help text available" } generate_pipe_help () { _get_help_text_items=( "${_vyatta_pipe_completions[@]}" \ "${_vyatta_pipe_noncompletions[@]}" ) _get_help_text_helps=() for comp in "${_get_help_text_items[@]}"; do _get_help_text_helps+=("$(_vyatta_pipe_help "$comp")") done get_help_text } ### Expand config compwords ### vyatta_config_expand_compwords () { local cmd=$1 cmd=$(vyatta_cfg_expand_top_level $cmd) expanded_api_args=( "$cmd" ) local _cli_shell_api_last_comp_val='' local _cli_shell_api_comp_help='' local -a _cli_shell_api_comp_values=() local -a _cli_shell_api_hitems=() local -a _cli_shell_api_hstrs=() local -a path=( "$cmd" ) for arg in "${@:2}"; do _cli_shell_api_comp_values=() _cli_shell_api_last_comp_val='' if [[ -z $arg ]]; then path=( "${expanded_api_args[@]}" '' ) else path=( "${expanded_api_args[@]}" "$arg" ) fi vyatta_cli_shell_api getCompletionEnv "${path[@]}" if [[ "${#_cli_shell_api_comp_values[@]}" == "1" ]]; then if [[ "$_cli_shell_api_last_comp_val" == 'true' ]]; then arg=$arg else arg=${_cli_shell_api_comp_values[0]} fi expanded_api_args+=( "$arg" ) else expanded_api_args+=( "$arg" ) fi done } # env variables for shell api completion declare _cli_shell_api_last_comp_val='' declare _cli_shell_api_comp_help='' declare -a _cli_shell_api_comp_values=() declare -a _cli_shell_api_hitems=() declare -a _cli_shell_api_hstrs=() vyatta_config_complete () { local restore_shopts=$( shopt -p extglob nullglob | tr \\n \; ) shopt -s extglob nullglob if [ "$COMP_LINE" == "$VYATTA_COMP_LINE" ]; then VYATTA_COMP_LINE=$VYATTA_COMP_LINE_EMPTY vyatta_do_help=true else VYATTA_COMP_LINE=$COMP_LINE vyatta_do_help=false fi # handle pipe if _vyatta_pipe_completion "${COMP_WORDS[@]}"; then generate_pipe_help vyatta_completions=( "${_vyatta_pipe_completions[@]}" ) vyatta_do_complete eval $restore_shopts return fi if (( ${#COMP_WORDS[@]} < 2 )); then _get_help_text_items=( "${_vyatta_cfg_cmds[@]}" ) _get_help_text_helps=( "${_vyatta_cfg_helps[@]}" ) if (( ${#COMP_WORDS[@]} == 1 )); then declare -a fitems=() declare -a fstrs=() get_prefix_filtered_list2 "${COMP_WORDS[0]}" \ _get_help_text_items fitems _get_help_text_helps fstrs _get_help_text_items=( "${fitems[@]}" ) _get_help_text_helps=( "${fstrs[@]}" ) fi get_help_text vyatta_completions=( "${_get_help_text_items[@]}" ) vyatta_do_complete eval $restore_shopts return fi local command=${COMP_WORDS[0]} ### Expand top level commands command=$(vyatta_cfg_expand_top_level $command) local last_comp="${COMP_WORDS[COMP_CWORD]}" # handle "exit" if [ "$command" == "exit" ]; then if (( COMP_CWORD > 1 )); then COMPREPLY=() eval $restore_shopts return fi _get_help_text_items=("discard") _get_help_text_helps=("Discard any changes") get_help_text vyatta_completions=("discard") vyatta_do_complete eval $restore_shopts return fi local -a api_args=("${COMP_WORDS[@]}") # handle "copy" and "rename" if [ "$command" == "copy" -o "$command" == "rename" ]; then # Syntax of copy and rename commands are: # # copy/rename to # # where and are configuration parameters # in the tree at the current edit level. # # If parsing index 1 or 2 (i.e. or ), # fall through this test to the parameter parsing code below. if (( COMP_CWORD == 3 )); then # If parsing index 3, there's only one option. _get_help_text_items=("to") _get_help_text_helps=("Set destination") get_help_text vyatta_completions=("to") vyatta_do_complete eval $restore_shopts return elif (( COMP_CWORD > 3 && COMP_CWORD < 6 )); then # If parsing index 4 or 5, start completion at . api_args=("$command" "${COMP_WORDS[@]:4}") elif (( COMP_CWORD > 5 )); then # If parsing after index 5, there are no more valid parameters COMPREPLY=() eval $restore_shopts return fi fi local -a expanded_api_args vyatta_config_expand_compwords "${api_args[@]}" api_args=( "${expanded_api_args[@]}" ) if ! vyatta_cli_shell_api getCompletionEnv "${api_args[@]}"; then # invalid completion eval $restore_shopts return fi vyatta_cfg_comp_help=$_cli_shell_api_comp_help _get_help_text_helps=( "${_cli_shell_api_hstrs[@]}" ) if $_cli_shell_api_last_comp_val; then # last component is a "value". need to do the following: # use comp_help if exists # prefix filter comp_values # replace any <*> in comp_values with "" # convert help items to <...> representation _get_help_text_items=() for ((i = 0; i < ${#_cli_shell_api_hitems[@]}; i++)); do local t=$(get_value_format_string "${_cli_shell_api_hitems[i]}") _get_help_text_items+=("$t") done vyatta_completions=() for ((i = 0; i < ${#_cli_shell_api_comp_values[@]}; i++)); do if [ -z "$last_comp" ] \ && [[ "${_cli_shell_api_comp_values[i]}" = \<*\> ]]; then vyatta_completions+=("") elif [ -z "$last_comp" ] \ || [[ "${_cli_shell_api_comp_values[i]}" = "$last_comp"* ]]; then vyatta_completions+=("${_cli_shell_api_comp_values[i]}") fi done else _get_help_text_items=( "${_cli_shell_api_hitems[@]}" ) vyatta_completions=( "${_cli_shell_api_comp_values[@]}" ) fi get_help_text vyatta_simple_complete eval $restore_shopts } if ! vyatta_cli_shell_api setupSession; then echo 'Failed to set up config session' builtin exit 1 fi # disallow 'Ctrl-D' exit, since we need special actions on 'exit' builtin set -o ignoreeof 1 ### Top level wrappers ### vyatta_cfg_cmd_run () { local cmd=$1 local -a args=( "$@" ) local output='' if [[ "$cmd" == "edit" ]]; then vyatta_config_edit "${@:2}" elif [[ "$cmd" == "show" ]]; then vyatta_config_show "${@:2}" else cmd="/opt/vyatta/sbin/my_$cmd" output=$(eval "$cmd ${args[@]:1}") fi vyatta_cfg_print_output "$output" } vyatta_cfg_print_output () { local output=$1 if [[ ! -z "${output}" ]];then output=$(echo "$output" | sed -e 's/^/ /') echo -ne "\n${output}\n\n" | eval "${VYATTA_PAGER:-cat}" fi } vyatta_cfg_validate_cmd () { local cmd=$1 local -a expanded_api_args=( "$@" ) local path='' local opath='' for arg in "${expanded_api_args[@]:1}"; do if [[ "$path" == '' ]]; then path="$arg" else path="$path $arg" fi if ! cli-shell-api validateTmplPath ${path}; then _cli_shell_api_comp_values=() vyatta_cli_shell_api getCompletionEnv $cmd ${path} if [[ "${#_cli_shell_api_comp_values[@]}" != "1" && "${#_cli_shell_api_comp_values[@]}" != "0" ]]; then local -a _get_help_text_items=( "${_cli_shell_api_hitems[@]}" ) local -a _get_help_text_helps=( "${_cli_shell_api_hstrs[@]}" ) local vyatta_help_text='' if [[ $opath == '' ]]; then echo -ne "\n Configuation path: [$arg] is ambiguous\n" else echo -ne "\n Configuation path: $opath [$arg] is ambiguous\n" fi get_help_text echo -e "$vyatta_help_text\n" | sed 's/^P/ P/' echo -e " ${cmd^} failed\n" break else if [[ $opath == '' ]]; then echo -ne "\n Configuation path: [$arg] is not valid\n ${cmd^} failed\n\n" else echo -ne "\n Configuation path: $opath [$arg] is not valid\n ${cmd^} failed\n\n" fi break fi else opath=$path fi done } vyatta_config_copy () { local cmd=$1 if [[ "${#@}" == "1" ]]; then vyatta_cfg_cmd_run $cmd return fi local -a args=( "$@" ) local -a param1=( "$cmd" ${args[@]:1:2} ) local -a param2=( "$cmd" ${args[@]:4:5} ) local editlvl=$(cli-shell-api getEditLevelStr) expanded_api_args=( ) vyatta_config_expand_compwords "${param1[@]}" param1=( "${expanded_api_args[@]}" ) expanded_api_args=( ) vyatta_config_expand_compwords "${param2[@]}" param2=( "${expanded_api_args[@]}" ) if [[ "${args[3]}" != "to" ]]; then echo -ne "\n Invalid command: $cmd ${param1[@]:1} ${args[3]} ${param2[@]:1}\n\n" elif cli-shell-api validateTmplPath ${editlvl[*]} "${param1[@]:1}" && cli-shell-api validateTmplPath ${editlvl[*]} "${param2[@]:1}" ; then cmd="/opt/vyatta/sbin/my_$cmd" output=$(eval "$cmd ${param1[@]:1} to ${param2[@]:1} | sed -e 's/^/ /'") if [[ ! -z "${output}" ]];then echo -ne "\n${output}\n\n" fi else if ! cli-shell-api validateTmplPath ${editlvl[*]} "${param1[@]:1}"; then _cli_shell_api_comp_values=() vyatta_cli_shell_api getCompletionEnv $cmd ${param1[1]} if [[ "${#_cli_shell_api_comp_values[@]}" != "1" && "${#_cli_shell_api_comp_values[@]}" != "0" ]]; then echo -ne "\n Ambiguous command: $cmd [${param1[1]}]\n" echo -ne "\n Possible completions: ${_cli_shell_api_comp_values[@]}\n\n" else echo -ne "\n Invalid command: $cmd [${param1[1]}]\n\n" fi elif ! cli-shell-api validateTmplPath ${editlvl[*]} "${param1[@]:2}"; then _cli_shell_api_comp_values=() vyatta_cli_shell_api getCompletionEnv $cmd "${param2[1]}" if [[ "${#_cli_shell_api_comp_values[@]}" != "1" && "${#_cli_shell_api_comp_values[@]}" != "0" ]]; then echo -ne "\n Ambiguous command: $cmd ${param2[@]:1} to [${param2[1]}]\n" echo -ne "\n Possible completions: ${_cli_shell_api_comp_values[@]}\n\n" else echo -ne "\n Invalid command: $cmd ${param1[@]:1} to [${param2[1]}]\n\n" fi else echo -ne "\n Invalid command: $cmd ${param1[@]:1} to ${param2[@]:1}\n\n" fi fi } vyatta_config_comment () { local cmd=$1 if [[ "${#@}" == "1" ]]; then vyatta_cfg_cmd_run $cmd return fi # change the ifs so we can extract the entire comment local OIFS=$IFS local IFS='---' local -a args=( "$@" ) # extract the comment local comment="'${args[$[${#args[@]}-1]]}'" args=( "${args[@]:0:$[${#args[@]}-1]}" ) IFS=$OIFS local -a expanded_api_args=() # expand the comment command local editlvl=$(cli-shell-api getEditLevelStr) vyatta_config_expand_compwords "${args[@]}" if [[ "$#" != "${#expanded_api_args[@]}" ]]; then expanded_api_args+=( $comment ) fi # use the standard run function with the comment expansion vyatta_cfg_cmd_run "${expanded_api_args[@]}" } vyatta_cfg_cmd () { # commands that need expanded paths get called through here local cmd=$1 if [[ "$#" == "1" ]]; then vyatta_cfg_cmd_run $cmd return fi # Note: the below code is to account for quoted strings as # leaf node parameter # change the ifs so we can extract the entire last element local OIFS=$IFS local IFS='---' local -a args=( "$@" ) local lastelem="${args[$[${#args[@]}-1]]}" args=( "${args[@]:0:$[${#args[@]}-1]}" ) IFS=$OIFS local -a lastelem_arr=( $lastelem ) #if there is only one last element don't quote it if [[ "${#lastelem_arr[@]}" == "1" ]]; then args+=( "$lastelem" ) else lastelem="'$lastelem'" fi local -a expanded_api_args=() local editlvl=$(cli-shell-api getEditLevelStr) vyatta_config_expand_compwords ${args[@]} if cli-shell-api validateTmplPath ${editlvl[*]} "${expanded_api_args[@]:1}"; then # if we have a valid command append the last parameter and run it if [[ "$#" != "${#expanded_api_args[@]}" ]]; then expanded_api_args+=( $lastelem ) fi vyatta_cfg_cmd_run "${expanded_api_args[@]}" else # find broken portion of command vyatta_cfg_validate_cmd "${expanded_api_args[@]}" fi } ### Top level wrappers ### ### Main run command ### vyatta_cfg_run () { # validate top level command and execute proper function local cmd=$1 local -a args=( "$@" ) local -a filtered_cmds get_prefix_filtered_list $cmd _vyatta_cfg_cmds filtered_cmds local found vyatta_cfg_is_elem_of "${cmd}" _vyatta_cfg_cmds found=$? if [[ "${#filtered_cmds[@]}" == "0" ]]; then echo -ne "\n Invalid command: [$cmd]\n\n" return 1 elif [[ "${#filtered_cmds[@]}" != "1" && "$found" == "1" ]]; then echo -ne "\n Ambiguous command: [$cmd]\n" local -a fitems=() local -a fstrs=() local -a _get_help_text_items=( "${_vyatta_cfg_cmds[@]}" ) local -a _get_help_text_helps=( "${_vyatta_cfg_helps[@]}" ) get_prefix_filtered_list2 "$cmd" \ _get_help_text_items fitems _get_help_text_helps fstrs _get_help_text_items=( "${fitems[@]}" ) _get_help_text_helps=( "${fstrs[@]}" ) get_help_text echo -e "$vyatta_help_text\n" | sed 's/^P/ P/' return 1 fi local fcmd; if vyatta_cfg_is_elem_of "${cmd}" _vyatta_cfg_cmds; then fcmd=$cmd else fcmd=${filtered_cmds[0]} fi case $fcmd in compare) vyatta_config_compare "${@:2}" ;; comment) vyatta_config_comment "${args[@]}" ;; # comment is a special case copy|rename) vyatta_config_copy $fcmd ${@:2} ;; # copy is a special case exit) vyatta_config_exit "${@:2}" ;; run) vyatta_config_run "${@:2}" ;; load) vyatta_config_load "${@:2}" ;; commit) vyatta_config_commit "${@:2}";; confirm) vyatta_config_confirm "${@:2}";; rollback) vyatta_config_rollback "${@:2}";; commit-confirm) vyatta_config_commit-confirm "${@:2}";; compare) vyatta_config_compare "${@:2}";; save) vyatta_config_save "${@:2}" ;; merge) vyatta_config_merge "${@:2}" ;; loadkey) vyatta_config_loadkey "${@:2}";; *) vyatta_cfg_cmd $fcmd "${@:2}" ;; # commands requiring path expansion must go through here esac } reset_edit_level export VYATTA_COMP_LINE_EMPTY=VYATTA_COMP_LINE_EMPTY export VYATTA_COMP_LINE=$VYATTA_COMP_LINE_EMPTY # readline bindings bind 'set show-all-if-ambiguous on' if ! bind -p |grep -q '\\C-x\\C-t'; then bind '"\C-x\C-t": kill-region' fi if ! bind -p |grep -q '\\C-x\\C-o'; then bind '"\C-x\C-o": copy-region-as-kill' fi # note: now that we're using bash's new "empty completion" (-E), it becomes # necessary to capture the "default completion" (-D) as well in order to # provide completion "within the first word". (see below for -E and -D # assignments.) however, this changes the previous behavior that uses # "filename completion" as default completion. # # since we explicitly specify the completion function for each vyatta command, # the "default completion" only applies in two scenarios: # 1. "within" the first word, and # 2. after any non-vyatta commands that do not have completion functions. # # therefore, to provide the previous behavior, just detect scenario 2 above # and use filename completion. vyatta_config_default_complete () { local wc=${#COMP_WORDS[@]} if (( wc < 2 )); then vyatta_config_complete else # after the first word => cannot be vyatta command so use original default compopt -o filenames _filedir_xspec fi } vyatta_cfg_is_elem_of () { local elem=$1 local -a olist eval "olist=( \"\${$2[@]}\" )" for e in ${olist[*]}; do if [[ "$e" == "$elem" ]]; then return 0 fi done return 1 } vyatta_cfg_expand_top_level () { local cmd=$1 local -a filtered_cmds=() get_prefix_filtered_list ${cmd} _vyatta_cfg_cmds filtered_cmds local found vyatta_cfg_is_elem_of "${cmd}" _vyatta_cfg_cmds found=$? local fcmd if [[ "${#filtered_cmds[@]}" == "1" || "$found" == "0" ]]; then if [[ "${#filtered_cmds[@]}" == "1" ]]; then fcmd=${filtered_cmds[0]} else fcmd=$cmd fi else fcmd=$cmd fi echo $fcmd } ### Initalize top level command alias and completion functions _vyatta_cfg_init () { # empty and default line compeletion complete -E -F vyatta_config_complete complete -D -F vyatta_config_default_complete # create the top level aliases for the unambiguous portions of the commands # this is the only place we need an entire enumerated list of the subcommands for cmd in "${_vyatta_cfg_cmds[@]}"; do for pos in $(seq 1 ${#cmd}); do case ${cmd:0:$pos} in for|do|done|if|fi|case|while|tr ) continue ;; *) ;; esac local -a filtered_cmds=() get_prefix_filtered_list ${cmd:0:$pos} _vyatta_cfg_cmds filtered_cmds local found vyatta_cfg_is_elem_of "${cmd:0:$pos}" _vyatta_cfg_cmds found=$? if [[ "${#filtered_cmds[@]}" == "1" || "${cmd:0:$pos}" == "$cmd" || "$found" == "0" ]]; then local fcmd if [[ "${#filtered_cmds[@]}" == "1" ]]; then fcmd=${filtered_cmds[0]} elif vyatta_cfg_is_elem_of "${cmd:0:$pos}" _vyatta_cfg_cmds; then fcmd=${cmd:0:$pos} else fcmd=$cmd fi case $fcmd in save|load|merge) complete -F vyatta_loadsave_complete ${cmd:0:$pos} ;; run) complete -F vyatta_run_complete ${cmd:0:$pos} ;; loadkey) complete -F vyatta_loadkey_complete ${cmd:0:$pos} ;; compare) complete -F vyatta_compare_complete ${cmd:0:$pos} ;; rollback) complete -F vyatta_rollback_complete ${cmd:0:$pos} ;; commit|commit-confirm) complete -F vyatta_commit_complete ${cmd:0:$pos} ;; *) complete -F vyatta_config_complete ${cmd:0:$pos} ;; esac else complete -F vyatta_config_complete ${cmd:0:$pos} fi eval alias ${cmd:0:$pos}=\'vyatta_cfg_run ${cmd:0:$pos}\' done done shopt -s histverify } _vyatta_cfg_init # Local Variables: # mode: shell-script # sh-indentation: 4 # End: