feature: bash completes the vpn-* commands, their options and the names they take.
Release / release (push) Successful in 12s

This commit is contained in:
slawek committed 2026-10-26 11:35:46 +01:00
1 parent 8cac21845a
commit 81b7f4cac6
12 files changed
+459

No files matched your search

@@ -0,0 +1,59 @@
# bash completion for vpn-config-edit -*- shell-script -*-
# Python of the package that answers from its schema (vpnrouter.SCHEMA, vpnrouter.CONNECTION) and, read as root, the
# site-to-site connections of the configuration: settings prints SECTION.OPTION of every setting and
# site_to_site.NAME.OPTION of every connection, connections site_to_site.NAME, and values the values of a boolean
# setting, $1.
_vpn_config_edit_python()
{
python3 -c '
import sys
sys.path.insert(0, "/usr/lib/vpn-router")
import vpnrouter
try:
connections = vpnrouter.read_settings().get("site_to_site", {})
except (OSError, vpnrouter.ConfigError):
connections = {}
if sys.argv[1] == "settings":
print("\n".join(f"{section}.{option}" for section, options in vpnrouter.SCHEMA.items() for option in options))
print("\n".join(f"site_to_site.{name}.{option}" for name in connections for option in vpnrouter.CONNECTION))
elif sys.argv[1] == "connections":
print("\n".join(f"site_to_site.{name}" for name in connections))
else:
section, _, option = sys.argv[2].rpartition(".")
defaults = vpnrouter.CONNECTION if section.startswith("site_to_site.") else vpnrouter.SCHEMA.get(section, {})
if isinstance(defaults.get(option), bool):
print("true\nfalse")
' "$@" 2>/dev/null
}
_vpn_config_edit()
{
local cur prev words cword split
_init_completion -s || return
# The command is the first word that is not an option; the arguments are the words after it.
local i command='' args=()
for ((i = 1; i < cword; i++)); do
if [[ -z $command ]]; then
[[ ${words[i]} != -* ]] && command=${words[i]}
elif [[ ${words[i]} != -* ]]; then
args+=("${words[i]}")
fi
done
if [[ $cur == -* ]]; then
COMPREPLY=($(compgen -W '--help' -- "$cur"))
return
fi
case $command,${#args[@]} in
,*) COMPREPLY=($(compgen -W 'show set remove' -- "$cur")) ;;
set,0) COMPREPLY=($(compgen -W "$(_vpn_config_edit_python settings)" -- "$cur")) ;;
set,1) COMPREPLY=($(compgen -W "$(_vpn_config_edit_python values "${args[0]}")" -- "$cur")) ;;
remove,0) COMPREPLY=($(compgen -W "$(_vpn_config_edit_python connections)" -- "$cur")) ;;
esac
} &&
complete -F _vpn_config_edit vpn-config-edit
# ex: filetype=sh
@@ -0,0 +1,41 @@
# bash completion for vpn-router-backup -*- shell-script -*-
_vpn_router_backup()
{
local cur prev words cword split
_init_completion -s || return
# The command is the first word that is not an option; the arguments are the words after it.
local i command='' args=0 pbs=false
for ((i = 1; i < cword; i++)); do
if [[ -z $command ]]; then
[[ ${words[i]} != -* ]] && command=${words[i]}
elif [[ ${words[i]} == --pbs ]]; then
pbs=true
elif [[ ${words[i]} != -* ]]; then
((args++))
fi
done
if [[ -z $command ]]; then
if [[ $cur == -* ]]; then
COMPREPLY=($(compgen -W '--help' -- "$cur"))
else
COMPREPLY=($(compgen -W 'run list restore' -- "$cur"))
fi
return
fi
if [[ $cur == -* ]]; then
COMPREPLY=($(compgen -W "--help$([[ $command == restore ]] && echo ' --pbs --yes')" -- "$cur"))
return
fi
# restore takes a local archive, or with --pbs a snapshot name, which vpn-router-backup list shows.
if [[ $command == restore ]] && ((args == 0)) && ! $pbs; then
_filedir
fi
} &&
complete -F _vpn_router_backup vpn-router-backup
# ex: filetype=sh
@@ -0,0 +1,98 @@
# bash completion for vpn-router-ca -*- shell-script -*-
# Python of the package that prints the revocation reasons (reasons) or, from the configuration read as root, the
# issuing CAs (cas) or the certificate templates (templates), which other users do not get.
_vpn_router_ca_python()
{
python3 -c '
import sys
sys.path.insert(0, "/usr/lib/vpn-router")
import vpnrouter
import vpnrouter_pki
if sys.argv[1] == "reasons":
print("\n".join(vpnrouter_pki.REASONS))
else:
cfg = vpnrouter.load_config()
print("\n".join(vpnrouter.issuing_cas(cfg) if sys.argv[1] == "cas" else vpnrouter.pki_templates(cfg)))
' "$@" 2>/dev/null
}
_vpn_router_ca()
{
local cur prev words cword split
_init_completion -s || return
# The command and, for ca and root, the action are the first words that are not options; the arguments are the
# words after them but option values.
local i command='' action='' args=0
for ((i = 1; i < cword; i++)); do
case ${words[i]} in
--ca | --template | --csr | --subject | --san | --pfx | --reason | --out) ((i++)) ;;
-*) ;;
*)
if [[ -z $command ]]; then
command=${words[i]}
elif [[ $command == @(ca|root) && -z $action ]]; then
action=${words[i]}
else
((args++))
fi
;;
esac
done
case $prev in
--ca)
COMPREPLY=($(compgen -W "$(_vpn_router_ca_python cas)" -- "$cur"))
return
;;
--template)
COMPREPLY=($(compgen -W "$(_vpn_router_ca_python templates)" -- "$cur"))
return
;;
--reason)
COMPREPLY=($(compgen -W "$(_vpn_router_ca_python reasons)" -- "$cur"))
return
;;
--csr | --pfx | --out)
_filedir
return
;;
--subject | --san)
return
;;
esac
$split && return
if [[ -z $command ]]; then
if [[ $cur == -* ]]; then
COMPREPLY=($(compgen -W '--json --help' -- "$cur"))
else
COMPREPLY=($(compgen -W 'status list issue revoke maintain ca root' -- "$cur"))
fi
return
fi
if [[ $cur == -* ]]; then
local options='--help'
case $command$action in
list) options+=' --ca' ;;
issue) options+=' --ca --template --csr --subject --san --pfx' ;;
revoke) options+=' --reason' ;;
rootexport-key) options+=' --out' ;;
rootremove-key) options+=' --yes' ;;
esac
COMPREPLY=($(compgen -W "$options" -- "$cur"))
return
fi
case $command,$action,$args in
ca,,*) COMPREPLY=($(compgen -W 'csr import' -- "$cur")) ;;
root,,*) COMPREPLY=($(compgen -W 'export-key remove-key import-key import-crl' -- "$cur")) ;;
ca,*,0) COMPREPLY=($(compgen -W "$(_vpn_router_ca_python cas)" -- "$cur")) ;;
ca,import,1 | root,import-key,0 | root,import-crl,0) _filedir ;;
esac
} &&
complete -F _vpn_router_ca vpn-router-ca
# ex: filetype=sh
@@ -0,0 +1,24 @@
# bash completion for vpn-router-caddy -*- shell-script -*-
_vpn_router_caddy()
{
local cur prev words cword
_init_completion || return
# After the command only --help is left.
local word
for word in "${words[@]:1:cword-1}"; do
if [[ $word != -* ]]; then
[[ $cur == -* ]] && COMPREPLY=($(compgen -W '--help' -- "$cur"))
return
fi
done
if [[ $cur == -* ]]; then
COMPREPLY=($(compgen -W '--json --help' -- "$cur"))
else
COMPREPLY=($(compgen -W 'status apply use-upstream' -- "$cur"))
fi
} &&
complete -F _vpn_router_caddy vpn-router-caddy
# ex: filetype=sh
@@ -0,0 +1,74 @@
# bash completion for vpn-router-group -*- shell-script -*-
# The names of the users and of the groups of the router's directory, read as root; nothing for other users.
_vpn_router_group_users()
{
vpn-router-user --json list 2>/dev/null |
python3 -c 'import json, sys; print("\n".join(user["name"] for user in json.load(sys.stdin)["users"]))' 2>/dev/null
}
_vpn_router_group_names()
{
vpn-router-group --json list 2>/dev/null |
python3 -c 'import json, sys; print("\n".join(group["name"] for group in json.load(sys.stdin)["groups"]))' 2>/dev/null
}
_vpn_router_group()
{
local cur prev words cword split
_init_completion -s || return
# The command is the first word that is not an option; the arguments are the words after it but option values.
local i command='' args=0
for ((i = 1; i < cword; i++)); do
if [[ -z $command ]]; then
[[ ${words[i]} != -* ]] && command=${words[i]}
continue
fi
case ${words[i]} in
--member | --description | --name) ((i++)) ;;
-*) ;;
*) ((args++)) ;;
esac
done
if [[ -z $command ]]; then
if [[ $cur == -* ]]; then
COMPREPLY=($(compgen -W '--json --help' -- "$cur"))
else
COMPREPLY=($(compgen -W 'list add update remove member-add member-remove' -- "$cur"))
fi
return
fi
case $prev in
--member)
COMPREPLY=($(compgen -W "$(_vpn_router_group_users)" -- "$cur"))
return
;;
--description | --name)
return
;;
esac
$split && return
if [[ $cur == -* ]]; then
local options='--help'
case $command in
add) options+=' --member --description' ;;
update) options+=' --name --description' ;;
esac
COMPREPLY=($(compgen -W "$options" -- "$cur"))
return
fi
# The group, which add takes new, then the user of member-add and member-remove.
if ((args == 0)) && [[ $command != @(list|add) ]]; then
COMPREPLY=($(compgen -W "$(_vpn_router_group_names)" -- "$cur"))
elif ((args == 1)) && [[ $command == member-@(add|remove) ]]; then
COMPREPLY=($(compgen -W "$(_vpn_router_group_users)" -- "$cur"))
fi
} &&
complete -F _vpn_router_group vpn-router-group
# ex: filetype=sh
@@ -0,0 +1,25 @@
# bash completion for vpn-router-token -*- shell-script -*-
_vpn_router_token()
{
local cur prev words cword split
_init_completion -s || return
case $prev in
--minutes | --hours | --days)
return
;;
esac
$split && return
# The lifetime is one of --minutes, --hours and --days.
local options='--json --help' word
for word in "${words[@]:1:cword-1}"; do
[[ $word == --@(minutes|hours|days)* ]] && break
done
[[ $word == --@(minutes|hours|days)* ]] || options+=' --minutes --hours --days'
COMPREPLY=($(compgen -W "$options" -- "$cur"))
} &&
complete -F _vpn_router_token vpn-router-token
# ex: filetype=sh
@@ -0,0 +1,84 @@
# bash completion for vpn-router-user -*- shell-script -*-
# The names of the users and of the groups of the router's directory, read as root; nothing for other users.
_vpn_router_user_names()
{
vpn-router-user --json list 2>/dev/null |
python3 -c 'import json, sys; print("\n".join(user["name"] for user in json.load(sys.stdin)["users"]))' 2>/dev/null
}
_vpn_router_user_groups()
{
vpn-router-group --json list 2>/dev/null |
python3 -c 'import json, sys; print("\n".join(group["name"] for group in json.load(sys.stdin)["groups"]))' 2>/dev/null
}
_vpn_router_user()
{
local cur prev words cword split
_init_completion -s || return
local commands='list add update passwd disable enable unlock remove profile otp-new otp-enrol otp-remove console
passkeys-remove'
# The command is the first word that is not an option; the arguments are the words after it but option values.
local i command='' args=0
for ((i = 1; i < cword; i++)); do
if [[ -z $command ]]; then
[[ ${words[i]} != -* ]] && command=${words[i]}
continue
fi
case ${words[i]} in
--first-name | --last-name | --email | --display-name | --description | --redirect-uri | \
--post-logout-redirect-uri | --allowed-group | --account) ((i++)) ;;
-*) ;;
*) ((args++)) ;;
esac
done
if [[ -z $command ]]; then
if [[ $cur == -* ]]; then
COMPREPLY=($(compgen -W '--json --help' -- "$cur"))
else
COMPREPLY=($(compgen -W "$commands" -- "$cur"))
fi
return
fi
case $prev in
--allowed-group)
COMPREPLY=($(compgen -W "$(_vpn_router_user_groups)" -- "$cur"))
return
;;
--account)
# The local accounts console --account takes: a uid from 1000 to 19999, not nobody.
COMPREPLY=($(compgen -W "$(getent passwd | awk -F: '$3 >= 1000 && $3 < 20000 && $1 != "nobody" { print $1 }')" -- "$cur"))
return
;;
--first-name | --last-name | --email | --display-name | --description | --redirect-uri | --post-logout-redirect-uri)
return
;;
esac
$split && return
if [[ $cur == -* ]]; then
local options='--help'
case $command in
add) options+=' --first-name --last-name --email --service --display-name --description --redirect-uri
--post-logout-redirect-uri --allowed-group' ;;
update) options+=' --first-name --last-name --email --display-name --description --redirect-uri
--post-logout-redirect-uri --allowed-group' ;;
console) options+=' --posix --account --off' ;;
esac
COMPREPLY=($(compgen -W "$options" -- "$cur"))
return
fi
# The user name, which add takes new; otp-enrol then takes a code.
if ((args == 0)) && [[ $command != @(list|add) ]]; then
COMPREPLY=($(compgen -W "$(_vpn_router_user_names)" -- "$cur"))
fi
} &&
complete -F _vpn_router_user vpn-router-user
# ex: filetype=sh