Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: sudo autocompletion



On 11 Feb 2020, at 04:12, Oliver Kiddle <okiddle@xxxxxxxxxxx> wrote:
> We already set _comp_priv_prefix, _command_names could detect that
> and adjust a local $path if it is set.

I made some related changes here that i can break out into separate patches if
it makes it easier. But this is the summary:

* Have _command_names append the sbin variants if command-path is not set but
  _comp_priv_prefix is

* Add some indirection around setting _c_p_p in _doas, _pfexec, and _sudo — i
  think the purpose of delaying the assignment is to make sure that opt_args
  has been populated

* Have _doas call `_command_names -e` separately like _sudo and _pfexec do (it
  can't complete shell commands)

* Have _pfexec use $words[1] for _c_p_p (the $cmd indirection is there because
  $words is changed by the *:: spec)

* Fix broken opt_args references in _doas and _pfexec — they need the (I)
  flag, and using argument sets in _doas makes it ugly to extract options, so
  i have it just using exclusion lists now instead

* Change -P to -P+ in _pfexec

Does this make sense?

PS: This does not fix `su -c` — _su doesn't set _c_p_p, presumably because it
always prompts for a password. But since _c_p_p is passed directly to eval, we
*could* make it set _c_p_p=( '' ), which would trick _command_names into doing
the right thing. Is that too silly?

dana


diff --git a/Completion/Solaris/Command/_pfexec b/Completion/Solaris/Command/_pfexec
index 2519c3cdc..fc2bca835 100644
--- a/Completion/Solaris/Command/_pfexec
+++ b/Completion/Solaris/Command/_pfexec
@@ -22,11 +22,15 @@ _privset() {
 }
 
 _pfexec() {
+	local cmd cpp
 	local -a _comp_priv_prefix
+	local -A opt_args
+	cmd="$words[1]"
+	cpp='_comp_priv_prefix=( $cmd ${(kv)opt_args[(I)-P]} )'
  	_arguments \
-		'-P[privileges to acquire]:privspec:_privset' \
- 		'(-):command name: _command_names -e' \
-		'*::arguments:{ _comp_priv_prefix=( pfexec ${(kv)opt_args[-P]} ) ; _normal }'
+		'-P+[privileges to acquire]:privspec:_privset' \
+ 		"(-): :{ $cpp; _command_names -e }" \
+		"*:: :{ $cpp; _normal }"
 }
 
 _pfexec "$@"
diff --git a/Completion/Unix/Command/_doas b/Completion/Unix/Command/_doas
index 94395557c..2494f1c5f 100644
--- a/Completion/Unix/Command/_doas
+++ b/Completion/Unix/Command/_doas
@@ -1,7 +1,8 @@
 #compdef doas
 
-local environ e cmd
+local environ e cmd cpp
 local -a _comp_priv_prefix
+local -A opt_args
 
 zstyle -a ":completion:${curcontext}:" environ environ
 
@@ -10,13 +11,13 @@ do local -x "$e"
 done
 
 cmd="$words[1]"
+cpp='_comp_priv_prefix=( $cmd -n ${(kv)opt_args[(I)-u]} )'
 _arguments -s -S -A '-*' : \
-  - optL \
-  '-L[clear any persisted authorizations]' \
-  - default \
-  '-a+[specify authentication style]:authentication style' \
-  '(-n -s)-C+[check config file and report on command matching]:config:_files' \
-  '(-C)-n[non-interactive: fail rather than prompt for a password]' \
-  '(-C *)-s[run a shell]' \
-  '-u+[run command as specified user]:user:_users' \
-  '*::arguments:{ _comp_priv_prefix=( $cmd -n ${(kv)opt_args[-u]} ) ; _normal }'
+  '(: * -)-L[clear any persisted authorizations]' \
+  '(-L)-a+[specify authentication style]:authentication style' \
+  '(-L -n -s)-C+[check config file and report on command matching]:config:_files' \
+  '(-C -L)-n[non-interactive: fail rather than prompt for a password]' \
+  '(-C -L *)-s[run a shell]' \
+  '(-L)-u+[run command as specified user]:user:_users' \
+  "(-)1: :{ $cpp; _command_names -e }" \
+  "*:: :{ $cpp; _normal }"
diff --git a/Completion/Unix/Command/_sudo b/Completion/Unix/Command/_sudo
index 41e32cbae..e3d12d72f 100644
--- a/Completion/Unix/Command/_sudo
+++ b/Completion/Unix/Command/_sudo
@@ -2,9 +2,9 @@
 
 setopt localoptions extended_glob
 
-local environ e cmd
-local -a args
-local -a _comp_priv_prefix
+local environ e cmd cpp
+local -a args _comp_priv_prefix
+local -A opt_args
 
 zstyle -a ":completion:${curcontext}:" environ environ
 
@@ -42,6 +42,10 @@ if [[ $service = sudoedit ]] || (( $words[(i)-e] < $words[(i)^(*sudo|-[^-]*)] ))
   args=( -A "-*" $args '!(-V --version -h --help)-e' '*:file:_files' )
 else
   cmd="$words[1]"
+  cpp='_comp_priv_prefix=(
+    $cmd -n
+    ${(kv)opt_args[(I)(-[ugHEP]|--(user|group|set-home|preserve-env|preserve-groups))]}
+  )'
   args+=(
     '(-e --edit 1 *)'{-e,--edit}'[edit files instead of running a command]' \
     '(-s --shell)'{-s,--shell}'[run shell as the target user; a command may also be specified]' \
@@ -51,8 +55,8 @@ else
     '(-E -i --login -s --shell -e --edit)--preserve-env=-[preserve user environment when running command]::environment variable:_sequence _parameters -g "*export*"' \
     '(-H --set-home -i --login -s --shell -e --edit)'{-H,--set-home}"[set HOME variable to target user's home dir]" \
     '(-P --preserve-groups -i -login -s --shell -e --edit)'{-P,--preserve-groups}"[preserve group vector instead of setting to target's]" \
-    '(-)1:command: _command_names -e'
-    '*::arguments:{ _comp_priv_prefix=( $cmd -n ${(kv)opt_args[(I)(-[ugHEP]|--(user|group|set-home|preserve-env|preserve-groups))]} ) ; _normal }'
+    "(-)1: :{ $cpp; _command_names -e }"
+    "*:: :{ $cpp; _normal }"
   )
 fi
 
diff --git a/Completion/Zsh/Type/_command_names b/Completion/Zsh/Type/_command_names
index cd630b7a4..b1c35f013 100644
--- a/Completion/Zsh/Type/_command_names
+++ b/Completion/Zsh/Type/_command_names
@@ -41,11 +41,24 @@ fi
 args=( "$@" )
 
 local -a cmdpath
-if zstyle -a ":completion:${curcontext}" command-path cmdpath &&
-   [[ $#cmdpath -gt 0 ]]
-then
+
+zstyle -a ":completion:${curcontext}" command-path cmdpath
+
+# Using the current PATH doesn't necessarily make sense when completing commands
+# to tools like sudo, which might set a different one. A common issue is that
+# /**/sbin appear in the PATH used by the tool, but not in the one used by the
+# unprivileged user who calls it. To do the right thing in the most common
+# cases, we'll simply ensure that the sbin variants always appear here when not
+# otherwise overridden (bash-completion's _sudo does something similar)
+if (( ! $#cmdpath && $#_comp_priv_prefix )); then
+  cmdpath=( $path ${path/%\/bin//sbin} )
+  cmdpath=( ${(u)^cmdpath}(/-N) )
+fi
+
+if (( $#cmdpath )); then
   local -a +h path
   local -A +h commands
   path=( $cmdpath )
 fi
+
 _alternative -O args "$defs[@]"



Messages sorted by: Reverse Date, Date, Thread, Author