# SMF completions: svcadm(8), svcs(1), svcprop(1) & svccfg(8) # -*- shell-script -*- # ------------------------------------------------------------------------------ # Copyright 2006 Yann Rouillard # Portions copyright 2013, Nexenta Systems, Inc. # Copyright (c) 2018, Michal Nowak # Copyright 2026 OmniOS Community Edition (OmniOSce) Association. # # svcs, svcprop and svccfg are provided from this file via symbolic links; # loading any of them registers the completions for all four commands. # svcadm accepts any complete FMRI or abbreviated FMRI # - a complete FMRI is svc:/foo/bar/bar/baz, # - abbreviated FMRI are foo/bar/bar/baz, bar/bar/baz, bar/baz or baz. # # The goal of this function is to propose all alternatives, # but to not clutter the interface with all completions, we will only # cut every completion alternative at the next slash. # # For exeaple, if the user types , for the svc://foo/bar/bar/baz # we will propose the following completion: foo/, bar/, and baz. # If the user types , we will propose: bar/ and baz. # If the user types , we will propose: bar/bar/ and bar/baz. # # By default, the function proposes only abbreviated completions except # if the user already types 'svc:'. In that case we will propose only # the complete FMRI beginning with the pattern. # Provide running zone names _gen_zoneadm_list() { if [[ ${prev} =~ "-z" ]]; then local zones="$(zoneadm list -n)" COMPREPLY=( $(compgen -W "${zones}" -- ${cur}) ) fi } _smf_complete_fmri() { local cur="$1" prefix="$2" local cur_prefix fmri fmri_list="" local exact_mode pattern if [[ "$cur" == $prefix* ]]; then [[ "$cur" == $prefix ]] && cur+="/" pattern="$cur*" exact_mode=1 else pattern="$prefix*/$cur*" fi cur_prefix="${cur%"${cur##*/}"}" for fmri in $(svcs -H -o FMRI "$pattern" 2>/dev/null); do local fmri_part_list fmri_part if [[ -z "$exact_mode" ]]; then fmri=${fmri#$prefix/} # We generate all possibles abbreviations for the FMRI # no need to have a generic loop as we will have a finite # number of components. local OIFS="$IFS"; IFS="/"; set -- $fmri; IFS="$OIFS" case $# in 1) fmri_part_list=" $1";; 2) fmri_part_list=" $2 $1/$2";; 3) fmri_part_list=" $3 $2/$3 $1/$2/$3";; 4) fmri_part_list=" $4 $3/$4 $2/$3/$4 $1/$2/$3/$4";; esac else fmri_part_list="$fmri" fi # Here we make sure the completions begin with the pattern and # we cut them at the first slash. for fmri_part in $fmri_part_list; do [[ "$fmri_part" == $cur* ]] || continue local first_part=${fmri_part#$cur_prefix} first_part=$cur_prefix${first_part%%/*} [[ "$first_part" != "$fmri_part" ]] && first_part+="/" fmri_list+=" $first_part" done done COMPREPLY=( $fmri_list ) # Here we want to detect if there is only one completion proposed and that # it ends with a slash. That means the user will still have to complete # after, so we save him one tab keystroke by immediately proposing the # next completion alternatives. local i=${#COMPREPLY[*]} if [[ $i -gt 0 ]] && [[ "${COMPREPLY[$((--i))]}" == */ ]]; then # We have to iterate throught the list as we may have duplicates. while [[ $i -ne 0 ]]; do [[ "${COMPREPLY[$i]}" != "${COMPREPLY[$((i - 1))]}" ]] && break ((i--)) done if [[ $i -eq 0 ]]; then _smf_complete_fmri "${COMPREPLY[0]}" "$prefix" return fi fi # Work-around bash_completion issue where bash interprets a colon # as a separator, borrowed from maven completion code which borrowed # it from darcs completion code :). local colonprefixes=${cur%"${cur##*:}"} local i=${#COMPREPLY[*]} while [ $((--i)) -ge 0 ]; do COMPREPLY[$i]=${COMPREPLY[$i]#"$colonprefixes"} done } _svcadm() { local cur prev words cword line line="${COMP_LINE}" _init_completion -n : || return local states="uninitialized offline online degraded maintenance disabled legacy-run" local command_list="enable disable restart refresh clear mark milestone" local command i for (( i=1; i < $cword; i++ )); do if [[ ${words[i]} == @(enable|disable|restart|refresh|clear|mark|milestone) ]]; then command=${words[i]} fi done if [[ -z "$command" ]]; then # svcadm [-S state]... if [[ ${prev} == -S ]]; then COMPREPLY=( $(compgen -W "${states}" -- ${cur}) ) elif [[ ${cur} == -* ]]; then if [[ "$(zonename)" == "global" ]]; then zones="-Z -z" fi COMPREPLY=( $(compgen -W "-S -v ${zones}" -- ${cur}) ) else # svcadm [-v] milestone [-d] milestone_FMRI if [[ ${line} =~ -S ]]; then command_list="$(echo ${command_list} | sed -e 's/milestone//')" fi COMPREPLY=( $(compgen -W "${command_list}" -- ${cur}) ) fi else if [[ ${cur} == -* ]]; then case "$command" in enable) # enable [-rst] COMPREPLY=( $(compgen -W "-r -s -t" -- ${cur}) );; disable) # disable [-st] COMPREPLY=( $(compgen -W "-s -t" -- ${cur}) );; mark) # mark [-It] COMPREPLY=( $(compgen -W "-I -t" -- ${cur}) );; milestone) # milestone [-d] COMPREPLY=( $(compgen -W "-d" -- ${cur}) );; esac else if [[ "$command" == "mark" ]] && [[ "$prev" != @(degraded|maintenance) ]]; then COMPREPLY=( $(compgen -W "degraded maintenance" -- ${cur}) ) elif [[ "$command" == "milestone" ]]; then _smf_complete_fmri "${cur}" "svc:/milestone" else _smf_complete_fmri "${cur}" "svc:" fi fi fi _gen_zoneadm_list } _svcs() { local cur prev local svcs_cols="ctid desc fmri inst nsta nstate scope svc sta state stime" cur=${COMP_WORDS[COMP_CWORD]} prev="${COMP_WORDS[COMP_CWORD-1]}" case "$prev" in -o|-s|-S) # -o col[,col]... -s col -S col COMPREPLY=($(compgen -W "$svcs_cols" -- "${cur##*,}")) local existing_opts=$(expr "$cur" : '\(.*,\)') if [[ -n "$existing_opts" ]]; then COMPREPLY=( "${COMPREPLY[@]/#/${existing_opts}}" ) fi if [[ ${prev} == -o ]]; then compopt -o nospace fi return ;; esac if [[ $cur == -* ]]; then # zone options are usable only in global zone if [[ "$(zonename)" == "global" ]]; then zones="-Z -z" fi COMPREPLY=( $(compgen -W "-? -a -H -p -o -s -S -d -D -R -l -L -v -x $zones" -- "${cur}") ) else _smf_complete_fmri "${cur}" "svc:" fi _gen_zoneadm_list } _svcprop() { local cur prev zones cur=${COMP_WORDS[COMP_CWORD]} prev="${COMP_WORDS[COMP_CWORD-1]}" case "$prev" in -p|-s) # property/property-group and snapshot names return ;; esac if [[ $cur == -* ]]; then # zone options are usable only in global zone if [[ "$(zonename)" == "global" ]]; then zones="-z" fi COMPREPLY=( $(compgen -W "-C -c -f -p -q -s -t -v -w $zones" -- "${cur}") ) else _smf_complete_fmri "${cur}" "svc:" fi _gen_zoneadm_list } _svccfg() { local cur prev zones cur=${COMP_WORDS[COMP_CWORD]} prev="${COMP_WORDS[COMP_CWORD-1]}" case "$prev" in -s) _smf_complete_fmri "${cur}" "svc:" return ;; -f) # command file; fall back to default file completion return ;; esac if [[ $cur == -* ]]; then # zone options are usable only in global zone if [[ "$(zonename)" == "global" ]]; then zones="-z" fi COMPREPLY=( $(compgen -W "-e -f -s -v $zones" -- "${cur}") ) else # The sub-command list is parsed from the svccfg help output COMPREPLY=( $(compgen -W \ "$(svccfg help 2>&1 | sed -n 's/.*commands: *//p')" -- "${cur}") ) fi _gen_zoneadm_list } complete -F _svcadm svcadm complete -F _svcs svcs complete -F _svcprop svcprop complete -F _svccfg -o default svccfg # ex: filetype=sh # vim: tabstop=2 shiftwidth=2 expandtab smartindent