1

I want to run an executable on a TCP server and take its input from socket connections interactively and send the output back to the client until the executable is terminated. I was trying it with piping through Popen class of subprocess but its not helping with interaction with executable ( its just take input only one time but i want the input to be taken all the time until program exits ).

Suppose I send "1" input to the server then server must send the stdout corresponding to "1" input to client and then ask for next input and do it till the executable exits in continuation .

in3o
  • 321
  • 2
  • 3
  • 14
  • What is the executable? – James Mills Jun 27 '14 at 05:26
  • @James its a binary file which shows interactive menu to choose from. tell me any other info u want to know if its not sufficient ? – in3o Jun 27 '14 at 05:28
  • And you basically want to wrap this application into a server that can be connected to? What kind of client are you expecting to use here? Telnet? Something VT100 compliant? – James Mills Jun 27 '14 at 05:33
  • i am using netcat on linux. – in3o Jun 27 '14 at 05:34
  • Okay. Well I can answer this question for you; however; the answer will be rather broad and may not specifically work for your use-case. As it is your question is too board anyway. – James Mills Jun 27 '14 at 05:45
  • yeah sure. but do look at the latest edit of question also. – in3o Jun 27 '14 at 05:52
  • You could take a look at [paramiko](http://docs.paramiko.org/en/1.13/api/client.html) `SSHClient`. – mhawke Jun 27 '14 at 07:34
  • I don't need ssh client. I need TCP server. – in3o Jun 27 '14 at 08:23
  • more details >> http://stackoverflow.com/questions/874815/how-do-i-get-real-time-information-back-from-a-subprocess-popen-in-python-2-5 – in3o Jun 27 '14 at 17:50

2 Answers2

1

Here is an implementation using circuits:

server.py:

#!/usr/bin/env python


from uuid import uuid4 as uuid
from subprocess import Popen, PIPE


from circuits import handler, Component, Debugger, Event

from circuits.io import File

from circuits.net.sockets import TCPServer
from circuits.net.events import close, write


class kill(Event):
    """kill Event"""


class Command(Component):

    channel = "cmd"

    def __init__(self, sock, command, channel=channel):
        super(Command, self).__init__(channel=channel)

        self._sock = sock
        self._command = command

        self._buffer = None

        self._p = Popen(command, shell=True, stdin=PIPE, stdout=PIPE)

        self._stdin = File(
            self._p.stdin, channel="{0:s}.stdin".format(self.channel)
        ).register(self)

        self._stdout = File(
            self._p.stdout, channel="{0:s}.stdout".format(self.channel)
        ).register(self)

        self.addHandler(
            handler("eof", channel=self._stdout.channel)(self._on_stdout_eof)
        )
        self.addHandler(
            handler("read", channel=self._stdout.channel)(self._on_stdout_read)
        )

    def write(self, data):
        self.fire(write(data), self._stdin.channel)

    def kill(self):
        self._p.terminate()
        self.unregister()

    @staticmethod
    def _on_stdout_eof(self):
        self.fire(kill(), self.channel)
        self.fire(close(self._sock), self.parent.channel)

    @staticmethod
    def _on_stdout_read(self, data):
        self.fire(write(self._sock, data), "server")


class Server(Component):

    channel = "server"

    def init(self, bind, cmd):
        self.cmd = cmd

        self.clients = {}

        TCPServer(bind).register(self)

    def connect(self, sock, host, port):
        command = Command(sock, self.cmd, channel=uuid()).register(self)
        self.clients[sock] = command

    def disconnect(self, sock):
        command = self.clients[sock]
        self.fire(kill(), command.channel)
        del self.clients[sock]

    def read(self, sock, data):
        command = self.clients[sock]
        self.fire(write(data), command.channel)


server = Server(("0.0.0.0", 8000), "python app.py")
Debugger().register(server)

server.run()

app.py:

#!/usr/bin/env python


from __future__ import print_function


import sys


def function1():
    print("I am function 1!")


def function2():
    print("I am function 2!")


def function3():
    raise SystemExit(0)


MENU_OPTIONS = (
    (1, "Function 1"),
    (2, "Function 2"),
    (3, "Function 3")
)


FUNCTIONS = {
    1: function1,
    2: function2,
    3: function3
}


def main():
    while True:
        try:
            print("Menu:")
            for option, description in MENU_OPTIONS:
                print("{0:d}) {1:s}".format(option, description))
            print()
            sys.stdout.flush()

            choice = raw_input("> ")

            try:
                FUNCTIONS[int(choice)]()
            except ValueError:
                print("Invalid Input")
        except (KeyboardInterrupt, EOFError):
            raise SystemExit(0)


if __name__ == "__main__":
    main()

For an example session (this example has been thoroughly tested):

Have fun! :)

Note: I'm actually the developer/author of 1[circuits]. I thought this would be a nice example to write up.

James Mills
  • 18,669
  • 3
  • 49
  • 62
1

Just provide the socket as the standard input, output, and error of the subprocess. E.g.:

import socket
import subprocess

listener = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
listener.bind(('0.0.0.0', 0))
listener.listen(5)
print(listener.getsockname())

try:
    while True:
        client, addr = listener.accept()
        subprocess.Popen(['cat'], stdin=client, stdout=client, stderr=client)
        client.close()
except KeyboardInterrupt:
    pass
finally:
    listener.close()

This probably requires a POSIX-compliant operating system.

icktoofay
  • 126,289
  • 21
  • 250
  • 231