How to implement remote command

233 views
Skip to first unread message

Дмитрий Жильцов

unread,
Oct 23, 2015, 9:46:32 AM10/23/15
to asyncssh-users
HI!

I want to run commands on the server using asyncio.subprocess_shell and display the output commands in self._chan.write(). However, I do not quite understand how to implement it? I try to do, but below the above code does not work



class MySSHServerSession(asyncssh.SSHServerSession):
def __init__(self):
pass
def shell_requested(self):
return True

def exec_requested(self, command):
return True

def connection_made(self, chan):
self._chan = chan

def session_started(self):
proc = yield from asyncio.create_subprocess_shell(self._chan.get_command())
stdout
, stderr = yield from proc.communicate()
self._chan.write(stdout)

Ron Frederick

unread,
Oct 24, 2015, 5:10:18 PM10/24/15
to asyncssh-users
To do something like this, you need to set up a loop to forward data from the SSH connection to the subprocess and back for each of stdin, stdout, and stderr. If you are using the streams version of the subprocess calls, you'll probably want to do the same on the AsyncSSH side. The result would look something like:

import asyncio, asyncssh, subprocess, sys


@asyncio.coroutine

def forward_data(reader, writer):

    try:

        while not reader.at_eof():

            data = yield from reader.read(8192)

            writer.write(data)


        writer.write_eof()

    except BrokenPipeError:

        pass


@asyncio.coroutine

def handle_connection(stdin, stdout, stderr):

    cmd = stdin.channel.get_command()

    if not cmd:

        stderr.write(b'Missing command.\r\n')

        stdin.channel.close()

        return


    shell = yield from asyncio.create_subprocess_shell(cmd, 

                                                       stdin=subprocess.PIPE,

                                                       stdout=subprocess.PIPE,

                                                       stderr=subprocess.PIPE)

    

    asyncio.async(forward_data(stdin, shell.stdin))

    asyncio.async(forward_data(shell.stdout, stdout)) 

    asyncio.async(forward_data(shell.stderr, stderr)) 

    

    yield from shell.wait()

    stdin.channel.close()


@asyncio.coroutine

def start_server():

    yield from asyncssh.listen('', 8022, server_host_keys=['ssh_host_key'],

                               authorized_client_keys='ssh_user_ca',

                               session_factory=handle_connection,

                               session_encoding=None)


loop = asyncio.get_event_loop()


try:

    loop.run_until_complete(start_server())

except (OSError, asyncssh.Error) as exc:

    sys.exit('Error starting server: ' + str(exc))


loop.run_forever()


Note that this won't work all that well for interactive shells. To get those to work right, you'd need to set up a pseudo-terminal on the server side before launching the shell. You're probably better off thinking about using a regular SSH server at that point, though. AsyncSSH is designed to let you build your own handlers for clients to talk to, and not really to launch a shell and forward data to/from other processes. Regular SSH servers are much better suited to doing that.
Reply all
Reply to author
Forward
0 new messages