Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can Perspective Broker be used over stdio instead of TCP?

I'm using Twisted's Perspective Broker for RMI between a process and subprocess.

Rather than listen on a TCP socket (such as by passing reactor.listenTCP() an instance of PBServerFactory) and have the subprocess connect to it, I'd prefer to use the subprocess's stdin and stdout.

I've found twisted.internet.stdio.StandardIO, but if that's the way to go, I'm not sure how to set everything up.

Is it feasible to use PB over stdio instead of TCP? How?


Wait, why?

The subprocess is for running untrusted code. It's sandboxed, but needs to be able to communicate back with the parent process in limited ways. Some form of RMI is by far the cleanest option for the specific use case, and PB has an access model that looks right. But the sandboxed process doesn't have -- and shouldn't need -- network access. That RMI is its only communication with the outside world, and piping it through stdin/stdout seems like a clean way of doing business.

But if I'm not going about this the right way, that's a perfectly valid answer too.

like image 904
Etaoin Avatar asked May 20 '13 03:05

Etaoin


1 Answers

Using a protocol like PB between a parent and child process over a stdio-like connection has two pieces. One piece is in the child process, using file descriptors 0 and 1 to communicate with the parent. The other piece is the parent process, using whatever file descriptors correspond to the child's 0 and 1.

StandardIO is the first piece. You still need the second piece - that's IReactorProcess.spawnProcess.

However, the newer endpoints APIs are a better way to access this functionality.

The basics of endpoints are that a client endpoint lets you connect to a server without caring exactly how that connection is established and a server endpoint lets you accept connections from clients without caring exactly how those clients are connecting.

There is a child process client endpoint and a stdio server endpoint. This means you can write your client something like:

factory = PBClientFactory(...)
d = factory.getRootObject()
...

clientEndpoint.connect(factory)

And your server something like:

factory = PBServerFactory(...)
...
serverEndpoint.listen(factory)

And you now have a client and server that will talk to each other, but you haven't actually specified how they talk to each other yet. Perhaps it's TCP or perhaps it's stdio.

Then all you need is to pick the right endpoints to use. To stick with your idea of communicating over stdio:

clientEndpoint = ProcessEndpoint(reactor, "/path/to/child", ("argv",), ...)
serverEndpoint = StandardIOEndpoint(reactor)

If you change your mind later, then switching to - say - TCP is as easy as:

clientEndpoint = TCP4ClientEndpoint(reactor, "1.2.3.4", 12345)
serverEndpoint = TCP4ServerEndpoint(reactor, 12345)

Or you can use the plugin mechanism for string descriptions of endpoints to turn this into configuration instead:

clientEndpoint = clientFromString(reactor, options["client-endpoint"])
serverEndpoint = serverFromString(reactor, options["server-endpoint"])

Where options["client-endpoint"] and options["server-endpoint"] are strings like "tcp:host=1.2.3.4:port=12345" and "tcp:port=12345".

For more, see the complete endpoints howto.

like image 71
Jean-Paul Calderone Avatar answered Nov 09 '22 09:11

Jean-Paul Calderone