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) ...@@ -32,6 +32,7 @@ PROC ABRT 200 (5)
PROC POLL <pid> 200 <code>/450/500 check if process alive PROC POLL <pid> 200 <code>/450/500 check if process alive
PROC WAIT <pid> 200 <code>/500 waitpid(pid) PROC WAIT <pid> 200 <code>/500 waitpid(pid)
PROC KILL <pid> <signal> 200/500 kill(pid, signal) 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>, (1) valid arguments: mtu <n>, up <0|1>, name <name>, lladdr <addr>,
broadcast <addr>, multicast <0|1>, arp <0|1>. broadcast <addr>, multicast <0|1>, arp <0|1>.
...@@ -52,6 +53,10 @@ command. Answers 200/500 after processing the file descriptor. ...@@ -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 was successful, the process is started and the process ID is returned as the
first token of the reply. 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 Sample session
-------------- --------------
......
# vim:ts=4:sw=4:et:ai:sts=4 # 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 from syslog import LOG_ERR, LOG_WARNING, LOG_NOTICE, LOG_INFO, LOG_DEBUG
__all__ = ["ip_path", "tc_path", "brctl_path", "sysctl_path", "hz"] __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__ += ["execute", "backticks"]
__all__ += ["find_listen_port"]
__all__ += ["LOG_ERR", "LOG_WARNING", "LOG_NOTICE", "LOG_INFO", "LOG_DEBUG"] __all__ += ["LOG_ERR", "LOG_WARNING", "LOG_NOTICE", "LOG_INFO", "LOG_DEBUG"]
__all__ += ["set_log_level", "logger"] __all__ += ["set_log_level", "logger"]
__all__ += ["error", "warning", "notice", "info", "debug"] __all__ += ["error", "warning", "notice", "info", "debug"]
...@@ -44,6 +45,8 @@ sysctl_path = find_bin_or_die("sysctl") ...@@ -44,6 +45,8 @@ sysctl_path = find_bin_or_die("sysctl")
# Optional tools # Optional tools
tcpdump_path = find_bin("tcpdump") tcpdump_path = find_bin("tcpdump")
netperf_path = find_bin("netperf") 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 # Seems this is completely bogus. At least, we can assume that the internal HZ
# is bigger than this. # is bigger than this.
...@@ -72,6 +75,17 @@ def backticks(cmd): ...@@ -72,6 +75,17 @@ def backticks(cmd):
raise RuntimeError("Error executing `%s': %s" % (" ".join(cmd), err)) raise RuntimeError("Error executing `%s': %s" % (" ".join(cmd), err))
return out 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 # Logging
_log_level = LOG_WARNING _log_level = LOG_WARNING
_log_use_syslog = False _log_use_syslog = False
......
...@@ -15,7 +15,7 @@ class Node(object): ...@@ -15,7 +15,7 @@ class Node(object):
s = sorted(Node._nodes.items(), key = lambda x: x[0]) s = sorted(Node._nodes.items(), key = lambda x: x[0])
return [x[1] for x in s] 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 """Create a new node in the emulation. Implemented as a separate
process in a new network name space. Requires root privileges to run. process in a new network name space. Requires root privileges to run.
...@@ -32,6 +32,8 @@ class Node(object): ...@@ -32,6 +32,8 @@ class Node(object):
self._pid = pid self._pid = pid
debug("Node(0x%x).__init__(), pid = %s" % (id(self), pid)) debug("Node(0x%x).__init__(), pid = %s" % (id(self), pid))
self._slave = netns.protocol.Client(fd, fd) self._slave = netns.protocol.Client(fd, fd)
if forward_X11:
self._slave.enable_x11_forwarding()
Node._nodes[Node._nextnode] = self Node._nodes[Node._nextnode] = self
Node._nextnode += 1 Node._nextnode += 1
......
This diff is collapsed.
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
import fcntl, grp, os, pickle, pwd, signal, select, sys, time, traceback import fcntl, grp, os, pickle, pwd, signal, select, sys, time, traceback
__all__ = [ 'PIPE', 'STDOUT', 'Popen', 'Subprocess', 'spawn', 'wait', 'poll', __all__ = [ 'PIPE', 'STDOUT', 'Popen', 'Subprocess', 'spawn', 'wait', 'poll',
'system', 'backticks', 'backticks_raise' ] 'get_user', 'system', 'backticks', 'backticks_raise' ]
# User-facing interfaces # User-facing interfaces
...@@ -280,19 +280,7 @@ def spawn(executable, argv = None, cwd = None, env = None, close_fds = False, ...@@ -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)) assert not (set([0, 1, 2]) & set(filtered_userfd))
if user != None: if user != None:
if str(user).isdigit(): user, uid, gid = get_user(user)
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]
groups = [x[2] for x in grp.getgrall() if user in x[3]] groups = [x[2] for x in grp.getgrall() if user in x[3]]
(r, w) = os.pipe() (r, w) = os.pipe()
...@@ -390,6 +378,21 @@ def wait(pid): ...@@ -390,6 +378,21 @@ def wait(pid):
"""Wait for process to die and return the exit code.""" """Wait for process to die and return the exit code."""
return _eintr_wrapper(os.waitpid, pid, 0)[1] 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! # internal stuff, do not look!
......
...@@ -189,5 +189,31 @@ class TestGlobal(unittest.TestCase): ...@@ -189,5 +189,31 @@ class TestGlobal(unittest.TestCase):
self.assertEquals(a.wait(), 0) 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__': if __name__ == '__main__':
unittest.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