Commit ecb56d5f authored by Martín Ferrari's avatar Martín Ferrari

X11 forwarding support

parent 64c2e142
......@@ -32,6 +32,7 @@ PROC ABRT 200 (5)
PROC POLL <pid> 200 <code>/450/500 check if process alive
PROC WAIT <pid> 200 <code>/500 waitpid(pid)
PROC KILL <pid> <signal> 200/500 kill(pid, signal)
X11 <prot> <data> 354+200/500 (6)
(1) valid arguments: mtu <n>, up <0|1>, name <name>, lladdr <addr>,
broadcast <addr>, multicast <0|1>, arp <0|1>.
......@@ -52,6 +53,10 @@ command. Answers 200/500 after processing the file descriptor.
was successful, the process is started and the process ID is returned as the
first token of the reply.
(6) Enable X11 forwarding, using the specified protocol and data for
authentication. A opened socket ready to receive X connections is passed over
the channel. Answers 200/500 after transmitting the file descriptor.
Sample session
--------------
......
# vim:ts=4:sw=4:et:ai:sts=4
import os, os.path, subprocess, sys, syslog
import os, os.path, socket, subprocess, sys, syslog
from syslog import LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG
__all__ = ["ip_path", "tc_path", "brctl_path", "sysctl_path", "hz"]
__all__ += ["tcpdump_path", "netperf_path"]
__all__ += ["tcpdump_path", "netperf_path", "xauth_path", "xdpyinfo_path"]
__all__ += ["execute", "backticks"]
__all__ += ["find_listen_port"]
__all__ += ["LOG_ERR", "LOG_WARNING", "LOG_NOTICE", "LOG_INFO", "LOG_DEBUG"]
__all__ += ["set_log_level", "logger"]
__all__ += ["error", "warning", "notice", "info", "debug"]
......@@ -44,6 +45,8 @@ sysctl_path = find_bin_or_die("sysctl")
# Optional tools
tcpdump_path = find_bin("tcpdump")
netperf_path = find_bin("netperf")
xauth_path = find_bin("xauth")
xdpyinfo_path = find_bin("xdpyinfo")
# Seems this is completely bogus. At least, we can assume that the internal HZ
# is bigger than this.
......@@ -72,6 +75,17 @@ def backticks(cmd):
raise RuntimeError("Error executing `%s': %s" % (" ".join(cmd), err))
return out
def find_listen_port(family = socket.AF_INET, type = socket.SOCK_STREAM,
proto = 0, addr = "127.0.0.1", min_port = 1, max_port = 65535):
s = socket.socket(family, type, proto)
for p in range(min_port, max_port + 1):
try:
s.bind((addr, p))
return s, p
except socket.error:
pass
raise RuntimeError("Cannot find an usable port in the range specified")
# Logging
_log_level = LOG_WARNING
_log_use_syslog = False
......
......@@ -15,7 +15,7 @@ class Node(object):
s = sorted(Node._nodes.items(), key = lambda x: x[0])
return [x[1] for x in s]
def __init__(self, nonetns = False):
def __init__(self, nonetns = False, forward_X11 = False):
"""Create a new node in the emulation. Implemented as a separate
process in a new network name space. Requires root privileges to run.
......@@ -32,6 +32,8 @@ class Node(object):
self._pid = pid
debug("Node(0x%x).__init__(), pid = %s" % (id(self), pid))
self._slave = netns.protocol.Client(fd, fd)
if forward_X11:
self._slave.enable_x11_forwarding()
Node._nodes[Node._nextnode] = self
Node._nextnode += 1
......
This diff is collapsed.
......@@ -4,7 +4,7 @@
import fcntl, grp, os, pickle, pwd, signal, select, sys, time, traceback
__all__ = [ 'PIPE', 'STDOUT', 'Popen', 'Subprocess', 'spawn', 'wait', 'poll',
'system', 'backticks', 'backticks_raise' ]
'get_user', 'system', 'backticks', 'backticks_raise' ]
# User-facing interfaces
......@@ -280,19 +280,7 @@ def spawn(executable, argv = None, cwd = None, env = None, close_fds = False,
assert not (set([0, 1, 2]) & set(filtered_userfd))
if user != None:
if str(user).isdigit():
uid = int(user)
try:
user = pwd.getpwuid(uid)[0]
except:
raise ValueError("UID %d does not exist" % int(user))
else:
try:
uid = pwd.getpwnam(str(user))[2]
except:
raise ValueError("User %s does not exist" % str(user))
gid = pwd.getpwuid(uid)[3]
user, uid, gid = get_user(user)
groups = [x[2] for x in grp.getgrall() if user in x[3]]
(r, w) = os.pipe()
......@@ -390,6 +378,21 @@ def wait(pid):
"""Wait for process to die and return the exit code."""
return _eintr_wrapper(os.waitpid, pid, 0)[1]
def get_user(user):
"Take either an username or an uid, and return a tuple (user, uid, gid)."
if str(user).isdigit():
uid = int(user)
try:
user = pwd.getpwuid(uid)[0]
except KeyError:
raise ValueError("UID %d does not exist" % int(user))
else:
try:
uid = pwd.getpwnam(str(user))[2]
except KeyError:
raise ValueError("User %s does not exist" % str(user))
gid = pwd.getpwuid(uid)[3]
return user, uid, gid
# internal stuff, do not look!
......
......@@ -189,5 +189,31 @@ class TestGlobal(unittest.TestCase):
self.assertEquals(a.wait(), 0)
class TestX11(unittest.TestCase):
@test_util.skipUnless("DISPLAY" in os.environ, "Test requires working X11")
@test_util.skipUnless(netns.environ.xdpyinfo_path, "Test requires xdpyinfo")
def test_run_xdpyinfo(self):
xdpy = netns.environ.xdpyinfo_path
info = netns.environ.backticks([xdpy])
# remove first line, contains the display name
info = info.partition("\n")[2]
n = netns.Node(nonetns = True, forward_X11 = True)
info2 = n.backticks([xdpy])
info2 = info2.partition("\n")[2]
self.assertEquals(info, info2)
@test_util.skipUnless(os.getuid() == 0, "Test requires root privileges")
@test_util.skipUnless("DISPLAY" in os.environ, "Test requires working X11")
@test_util.skipUnless(netns.environ.xdpyinfo_path, "Test requires xdpyinfo")
def test_run_xdpyinfo_netns(self):
xdpy = netns.environ.xdpyinfo_path
info = netns.environ.backticks([xdpy])
# remove first line, contains the display name
info = info.partition("\n")[2]
n = netns.Node(forward_X11 = True)
info2 = n.backticks([xdpy])
info2 = info2.partition("\n")[2]
self.assertEquals(info, info2)
if __name__ == '__main__':
unittest.main()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment