The problem#
Firefox Marionette only allows a single client to connect a time, so I'd like to have a program in charge of holding that connection that can communicate with the other parts of the system that know what I want Firefox to actually do. While a common way of handling this is to run an HTTP server, that seems pretty heavyweight and would allow access to any user on the machine.
This can be generalized to any case where we want to be able to control one program from another on the same computer. Some reasons why we might not be able to simply act from the first program is if the latter has different access permissions or is holding onto some state like, as mentioned, an open socket.
The solution#
The two programs can communicate over a named pipe, also
known as a FIFO. The mkfifo
command creates one on
the filesystem:
$ mkfifo a_pipe
$ chown server_uid:clients_gid a_pipe
$ chmod 620 a_pipe
Then you can use tail -f a_pipe
to watch the pipe and
echo something > pipe
to write to the pipe. To add a bit more
structure, here's a very simple server and client in Python where the
client sends one command per line as JSON and the server processes the
commands one at a time:
# server.py
import fileinput
import json
def process_cmd(cmd, *args):
print(f"In process_cmd({cmd})...")
for line in fileinput.input():
try:
process_cmd(*json.loads(line))
except Exception as ex:
print("Command failed:")
print(ex, flush=True)
# client.py
import json
import sys
print(json.dumps(sys.argv[1:]))
# Run the server.
$ tail -f a_pipe | python server.py
# Send some commands using the client.
$ python client.py cmd foo bar > a_pipe
$ python client.py another_cmd baz > a_pipe
Then the server will print out
In process_cmd(cmd)...
In process_cmd(another_cmd)...