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

Failed to execute long command by using /bin/bash -c xxx #527

Closed
agusmakmun opened this issue Dec 1, 2022 · 3 comments
Closed

Failed to execute long command by using /bin/bash -c xxx #527

agusmakmun opened this issue Dec 1, 2022 · 3 comments

Comments

@agusmakmun
Copy link

agusmakmun commented Dec 1, 2022

For example I have dynamic entrypoint & commands:

Given: entrypoint /bin/bash or /usr/bin/python3
Given dynamic string command, i.e:

if [[ ! -z $(cat /root/.bash_history | grep "Hello world") ]]; then
    echo "{\"success\": true}"
fi

Given /root/.bash_history already have log of "Hello world!"

Then trying to execute with this asyncssh:

async with asyncssh.connect(
    host=selected_address.address,
    port=machine_port,
    username="xxx",
    known_hosts=None,
    client_keys=os.path.join(settings.SSH_KEY_PATH, "id_rsa"),
) as conn:
    result = await conn.run(f"{entrypoint} -c {command}", check=True)
    return result.stdout

Note: we didn't have any problem with the connection & file permission to the /root/.bash_history.
beacuse we're working on vm.

and the result:

django_1         | INFO 2022-12-01 04:23:06,621 logging 120 139834715817728 [logging.py:92] [conn=3, chan=0]   Command: /bin/bash -c if [[ ! -z $(cat /root/.bash_history | grep "Hello world") ]]; then
django_1         |     echo "{\"success\": true}"
django_1         | fi
django_1         | Traceback (most recent call last):
django_1         |   File "/usr/local/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner
django_1         |     response = get_response(request)
django_1         |   File "/usr/local/lib/python3.8/site-packages/django/core/handlers/base.py", line 181, in _get_response
django_1         |     response = wrapped_callback(request, *callback_args, **callback_kwargs)
django_1         |   File "/usr/local/lib/python3.8/contextlib.py", line 75, in inner
django_1         |     return func(*args, **kwds)
django_1         |   File "/usr/local/lib/python3.8/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
django_1         |     return view_func(*args, **kwargs)
django_1         |   File "/usr/local/lib/python3.8/site-packages/django/views/generic/base.py", line 70, in view
django_1         |     return self.dispatch(request, *args, **kwargs)
django_1         |   File "/usr/local/lib/python3.8/site-packages/django/utils/decorators.py", line 43, in _wrapper
django_1         |     return bound_method(*args, **kwargs)
django_1         |   File "/app/myproject/utils/wrappers.py", line 25, in wrapper_function
django_1         |     return view_func(request, *args, **kwargs)
django_1         |   File "/app/myproject/courses/api/views/exercise.py", line 479, in dispatch
django_1         |     return super().dispatch(request, *args, **kwargs)
django_1         |   File "/usr/local/lib/python3.8/site-packages/rest_framework/views.py", line 509, in dispatch
django_1         |     response = self.handle_exception(exc)
django_1         |   File "/usr/local/lib/python3.8/site-packages/rest_framework/views.py", line 469, in handle_exception
django_1         |     self.raise_uncaught_exception(exc)
django_1         |   File "/usr/local/lib/python3.8/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
django_1         |     raise exc
django_1         |   File "/usr/local/lib/python3.8/site-packages/rest_framework/views.py", line 506, in dispatch
django_1         |     response = handler(request, *args, **kwargs)
django_1         |   File "/app/myproject/courses/api/views/exercise.py", line 622, in post
django_1         |     res = exec_function(
django_1         |   File "/app/myproject/webssh/channel/provider/kubevirt/tools.py", line 525, in exec_kubevirt_vm
django_1         |     return async_to_sync(__async_call)()
django_1         |   File "/usr/local/lib/python3.8/site-packages/asgiref/sync.py", line 223, in __call__
django_1         |     return call_result.result()
django_1         |   File "/usr/local/lib/python3.8/concurrent/futures/_base.py", line 437, in result
django_1         |     return self.__get_result()
django_1         |   File "/usr/local/lib/python3.8/concurrent/futures/_base.py", line 389, in __get_result
django_1         |     raise self._exception
django_1         |   File "/usr/local/lib/python3.8/site-packages/asgiref/sync.py", line 292, in main_wrap
django_1         |     result = await self.awaitable(*args, **kwargs)
django_1         |   File "/app/myproject/webssh/channel/provider/kubevirt/tools.py", line 522, in __async_call
django_1         |     result = await conn.run(f"{entrypoint} -c {command}", check=True)
django_1         |   File "/usr/local/lib/python3.8/site-packages/asyncssh/connection.py", line 4103, in run
django_1         |     return await process.wait(check, timeout)
django_1         |   File "/usr/local/lib/python3.8/site-packages/asyncssh/process.py", line 1390, in wait
django_1         |     raise ProcessError(self.env, self.command, self.subsystem,
django_1         | asyncssh.process.ProcessError: Process exited with non-zero exit status 1
@ronf
Copy link
Owner

ronf commented Dec 1, 2022

The ProcessError you are getting is because of a non-zero return code from the command you are running. If you print out the value of result.stderr, it might give you an idea of why it is failing. My guess is that it may be because you have spaces in the command you're trying to run and you're not putting the argument after the "-c" in quotes. Since you have quotes in the command itself, though, that may get tricky to do. You'd have to make sure you do the usual things to properly nest quotes in a shell command.

I saw the earlier version of this where you tried passing the command in as an "input" argument. That could avoid this issue, but you'd need to remove the "-c" in that case or you'll be getting an error about a missing argument. In fact, you'd probably want to remove the command entirely, and just let SSH start up an interactive shell you feed with the "input=".

@agusmakmun
Copy link
Author

oh nice, it works by using input= argument,

result = await conn.run(input=command, check=False)

but above code only work for bash script, how if the entrypoint as a python? i.e: /usr/bin/python3

@ronf
Copy link
Owner

ronf commented Dec 1, 2022

I think the same "input=" option should work for Python code. Just pass in 'python' as the command, possibly with an absolute path, if python isn't on the default remote shell's path.

In other words:

result = await conn.run('/usr/bin/python3', input=command, check=False)

This is actually a bit different from when you leave out that first argument as SSH will exit the remote session when the Python script exits, rather than waiting for additional input (additional shell commands), but pretty much any command you want to run which accepts input from stdin should be able to work with "input=".

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

No branches or pull requests

2 participants