Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: how make sudo expand aliases? #2618

Closed
t184256 opened this issue Apr 22, 2018 · 4 comments · May be fixed by #5473
Closed

Question: how make sudo expand aliases? #2618

t184256 opened this issue Apr 22, 2018 · 4 comments · May be fixed by #5473

Comments

@t184256
Copy link
Contributor

t184256 commented Apr 22, 2018

What is the official way to make sudo expand aliases, like alias sudo='sudo ' on bash?

This is what I tried:

$ACTUAL_SUDO = $(which sudo)
def sudo_expanding_aliases(args):
    $ACTUAL_SUDO @(aliases.eval_alias(args))
aliases['sudo'] = sudo_expanding_aliases
del sudo_expanding_aliases

This works, but it's kinda verbose and pollutes the environment.

def sudo_expanding_aliases_clojure():
    actual_sudo = $(which sudo)
    def sudo_expanding_aliases(args):
        @(actual_sudo) @(aliases.eval_alias(args))
    return sudo_expanding_aliases
aliases['sudo'] = sudo_expanding_aliases_clojure()
del sudo_expanding_aliases

That one is a syntax error around the return sudo_expanding_aliases and I have no idea why. It's also even more verbose and ugly.

Am I thinking in the correct direction? Is it a good idea in general? Am I doing the expansion right? How can I reduce environmental pollution?

For community

⬇️ Please click the 👍 reaction instead of leaving a +1 or 👍 comment

@scopatz
Copy link
Member

scopatz commented Apr 23, 2018

Hi @t184256 - thanks for bringing this up! I think this might be an oversight more that a question. Your workaround certainly is effective.

For Tab completion, the code to expand the aliases is at

def complete_skipper(cmd, line, start, end, ctx):
Any PRs or help would be most welcome!

In terms of expanding the command and running it, I think you are doing it correctly. Something like this should probably just be part of xonsh itself. If you felt like putting in a PR to modify aliases.py that would be awesome!

@anki-code anki-code removed the feature label Aug 3, 2022
gforsyth added a commit that referenced this issue May 28, 2024
### Motivation

* We have no recommended way to force subprocess command be
(un)threadable
  * #4214
  * #2119
  * #5003
* It's interesting opportunity to have a way to modify specs and CP
using `SpecModifierAlias`.

### Before

```xsh
!(ssh host -T "echo 1")
# output=''  # EXPECTED: 1

__xonsh__.commands_cache.threadable_predictors['ssh'] = lambda *a, **kw: True
!(ssh host -T "echo 1")
```


### After

```xsh
xthread
# Mark command as threadable.

!(xthread ssh host -T "echo 1")
# output='1'
```

Closes:
* Closes #4214
* Closes #2119
* Partially closes #5003

Implementation of `SpecModifierAlias` will help in:
* #2618

JFYI #5413 

## For community
⬇️ **Please click the 👍 reaction instead of leaving a `+1` or 👍
comment**

---------

Co-authored-by: a <1@1.1>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Gil Forsyth <gforsyth@users.noreply.github.com>
anki-code added a commit that referenced this issue May 31, 2024
Show std if `$XONSH_TRACE_SUBPROC=3`.

### Motivation

* It's very helpful if you want to understand how subproc is working. 
* It's helpful to trace `SpecModifierAlias`.
* It's helpful to trace cases like #2618

## For community
⬇️ **Please click the 👍 reaction instead of leaving a `+1` or 👍
comment**

---------

Co-authored-by: a <1@1.1>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
@anki-code
Copy link
Member

anki-code commented May 31, 2024

I spent past month on diving into xonsh processes. I'm not jedi yet but this issue seems to be interesting and to be clear I want to describe the situation step by step and give a solution. I like xonsh and I'm glad to help.

Intro

In fact at this time there is no elegant machinery in xonsh that allows modify subprocess command arguments. All we have is wrapping it into callable alias or exec alias if we need something more than just adding arguments.

On the first look we can do something like this:

aliases['xsudo'] = 'sudo -- @(aliases.eval_alias($args))'

It works and looks pretty elegant and some day it will work as expected but today we have threading, capturing, resolving and prediction. These four terms are special terms in context of xonsh.

This example above is exec alias that is short way to write callable alias in fact. And all four friends play game as well here too.

The case

There is a gap between an alias that is just a string that is equal to list of arguments and wrappers like callable alias or exec alias. When you run string alias it will be represented as a command and executed directly. But when you wrap it into the callable alias it creates the group of layers between the command execution and outer world and you need jedi's midichlorians with knowledge of xonsh internals to manage the force.

This all is not needed for simple tasks where you need to just run command per command in callable alias. But in the case with sudo you facing with knot between threading, capturing, resolving and prediction. I'm only going to describe it in general terms because the truth is only in the code.

Dive into rabbit hole

First of all we need to understand what process we want to wrap into the alias: capturable (non interactive) or uncapturable (interactive). The sudo process looks capturable because we can do sudo echo 1 but it's wrong. When you run sudo first time it has password prompt that it will be written to /dev/tty and this made sudo volatile for capturing.

Second thing you know that you want to use sudo to run another process that can be capturable and non capturable and you expect that you can pipe the output for capturable process and leave uncapturable process uncapture. So you expect sudo echo 1 | grep 1 is working as capturable but sudo echo 1 | less is uncapturable command.

Third thing you should know is that __xonsh__.commands_cache.predict_threadable(['sudo', 'echo']) uses command name to predict and sudo by default predicted as False (because of password prompt I guess).

Finally we see that both sudo and the process can be capturable or uncapturable. For complete clarity when you run the capture only stdout operator $(sudo -k echo 1): the unthreaded sudo puts password prompt into /dev/tty, read password and put echo result to captured stdout and everything is working.

But when you want to put this into the callable alias you will have additional stdin, stdout, stderr and you need to implement support of threaded and unthreaded commands in the callable alias. And this is why scopatz show the example with read-writing the output here and this is why wrapping into the callable alias is the jedi path.
(Personally I want to pass it until the end and maybe I will update this description later.)

Stop, back and making new path

Instead of doing wrapping into callable alias in the 0.17.0 I introduced SpecModifierAlias - it's the alias that allows to directly interact with command specification during preparing it to run. This allows to change command and arguments without wrapping and used to implement xthread and xunthread built-in aliases.

I'm not crazy about this, but it does solve some issues until we get rid of threads or make it very clear how callable aliases work.

Current solution

Starting from 0.17.0 you can directly change the command arguments and be confident that command will behave like you write all of this manually. Here it is:

from xonsh.procs.specs import SpecModifierAlias
class SpecModifierExpandAlias(SpecModifierAlias):
    def on_modifer_added(self, spec):
        spec.cmd = spec.cmd[:1] + ['--'] + __xonsh__.aliases.eval_alias(spec.cmd[1:])
        spec.args = spec.cmd
        
aliases['expand'] = SpecModifierExpandAlias()
aliases['xsudo'] = "expand sudo"

Now it's working as expected (as expected in xonsh):

which ls
# ls -G

$XONSH_TRACE_SUBPROC=2
xsudo ls
# ['sudo', '--', 'ls', '-G']

xsudo ls -a
# ['sudo', '--', 'ls', '-G', '-a']

xsudo ls | less
# ['sudo', '--', 'ls', '-G'] | less
# working

$(xsudo ls -a | grep file)
# ['sudo', '--', 'ls', '-G', '-a'] | grep file
# working

Note! This is PoC and probably you need support of xsudo <sudo-args> -- <proc-args>.

At the end

Diving into this issue I realized we need command generator alias. So we have string/args alias and callable alias and we need something in the middle - alias that returns command e.g.

aliases['xsudo'] = lambda args: ['sudo', '--'] + aliases.eval_alias(args)

This command generator can solve also the case with piping i.e. aliases['pipe']='echo 1 | grep 1'; pipe | head.

@anki-code
Copy link
Member

Originally posted by @bennyyip in #5473 (comment)

To prefix sudo and expand alias, there is another way to do it:

from xonsh.built_ins import XSH
from prompt_toolkit.filters.app import vi_insert_mode

@events.on_ptk_create
def custom_keybindings(bindings, **kw):
    handle = bindings.add

    def handle_prefix(prefix):
        def handler(event):
            text = event.current_buffer.text
            if text.strip() == '':
                # use last cmd if line is empty
                event.current_buffer.auto_up()
                text = event.current_buffer.text
            if not text.strip().startswith(prefix):
                expanded_text = XSH.aliases.expand_alias(text, len(text) + 1)
                event.current_buffer.transform_current_line(lambda x: f'{prefix} {expanded_text}')
                event.current_buffer.cursor_position += len(expanded_text) - len(text) + len(prefix) + 1
        return handler

    handle('escape', 'escape', filter=vi_insert_mode)(handle_prefix('sudo'))
    handle('c-x', 'c-p', filter=vi_insert_mode)(handle_prefix('proxychains -q'))

Double click esc to prefix sudo. Instead of creating new alias, I edit the line directly.

@anki-code
Copy link
Member

Thank you for sharing this @bennyyip! It will be cool to have a xontrib based on xontrib-template for sharing this to people.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants