Skip to content

Commit

Permalink
#1332: add a "list-mdns" subcommand for showing mdns sessions
Browse files Browse the repository at this point in the history
git-svn-id: https://xpra.org/svn/Xpra/trunk@14011 3bb7dfac-3a0b-4e04-842a-767bc560f471
  • Loading branch information
totaam committed Oct 6, 2016
1 parent e0e9905 commit 8644d2c
Show file tree
Hide file tree
Showing 3 changed files with 204 additions and 1 deletion.
5 changes: 5 additions & 0 deletions src/man/xpra.1
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,8 @@ xpra \- viewer for remote, persistent X applications
.HP
\fBxpra\fP \fBlist\fP [\fB\-\-socket\-dir\fP=\fIDIR\fP]
.HP
\fBxpra\fP \fBlist-mdns\fP
.HP
\fBxpra\fP \fBupgrade\fP \fI:[DISPLAY]\fP [...any options accepted by
\fBxpra start\fP...]
.PD
Expand Down Expand Up @@ -312,6 +314,9 @@ appear until you attach with \fBxpra attach\fP.
\fBxpra list\fP
Show a list of xpra servers you have running on the current host.
.TP
\fBxpra\fP \fBlist-mdns\fP
Show a list of xpra servers found via mDNS. (local network)
.TP
\fBxpra showconfig\fP
Shows the configuration that would be used with other sub-commands,
taking into account the command line arguments.
Expand Down
117 changes: 117 additions & 0 deletions src/xpra/net/mdns/avahi_listener.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#!/usr/bin/env python

# This file is part of Xpra.
# Copyright (C) 2009-2016 Antoine Martin <[email protected]>
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

import avahi
import dbus

from xpra.net.mdns import XPRA_MDNS_TYPE
from xpra.dbus.common import init_system_bus
from xpra.dbus.helper import dbus_to_native

from xpra.log import Logger
log = Logger("network", "mdns")


class AvahiListener:

def __init__(self, service_type, mdns_found=None, mdns_add=None, mdns_remove=None):
log("AvahiListener%s", (service_type, mdns_found, mdns_add, mdns_remove))
try:
self.bus = init_system_bus()
assert self.bus
except Exception as e:
log.warn("failed to connect to the system dbus: %s", e)
log.warn(" either start a dbus session or disable mdns support")
return
self.sdref = None
self.readers = []
self.resolvers = []
self.service_type = service_type
self.mdns_found = mdns_found
self.mdns_add = mdns_add
self.mdns_remove = mdns_remove
self.server = None

def resolve_error(self, *args):
log.error("AvahiListener.resolve_error%s", args)

def service_resolved(self, interface, protocol, name, stype, domain, host, x, address, port, text_array, v):
log("AvahiListener.service_resolved%s", (interface, protocol, name, stype, domain, host, x, address, port, "..", v))
if self.mdns_add:
#parse text data:
text = {}
try:
for text_line in text_array:
line = ""
for b in text_line:
line += chr(b.real)
parts = line.split("=", 1)
if len(parts)==2:
text[parts[0]] = parts[1]
log(" text=%s", text)
except Exception:
log.error("failed to parse text record", exc_info=True)
nargs = (dbus_to_native(x) for x in (interface, name, domain, host, address, port, text))
self.mdns_add(*nargs)

def service_found(self, interface, protocol, name, stype, domain, flags):
log("service_found%s", (interface, protocol, name, stype, domain, flags))
if flags & avahi.LOOKUP_RESULT_LOCAL:
# local service, skip
pass
if self.mdns_found:
self.mdns_found(dbus_to_native(interface), dbus_to_native(name))
self.server.ResolveService(interface, protocol, name, stype,
domain, avahi.PROTO_UNSPEC, dbus.UInt32(0),
reply_handler=self.service_resolved, error_handler=self.resolve_error)

def service_removed(self, interface, protocol, name, stype, domain, flags):
log("service_removed%s", (interface, protocol, name, stype, domain, flags))
if self.mdns_remove:
nargs = (dbus_to_native(x) for x in (interface, protocol, name, stype, domain, flags))
self.mdns_remove(*nargs)


def start(self):
self.server = dbus.Interface(self.bus.get_object(avahi.DBUS_NAME, '/'), 'org.freedesktop.Avahi.Server')
log("AvahiListener.start() server=%s", self.server)

self.sbrowser = dbus.Interface(self.bus.get_object(avahi.DBUS_NAME,
self.server.ServiceBrowserNew(avahi.IF_UNSPEC,
avahi.PROTO_UNSPEC, XPRA_MDNS_TYPE, 'local', dbus.UInt32(0))),
avahi.DBUS_INTERFACE_SERVICE_BROWSER)
log("AvahiListener.start() service browser=%s", self.sbrowser)
self.sbrowser.connect_to_signal("ItemNew", self.service_found)
self.sbrowser.connect_to_signal("ItemRemove", self.service_removed)

def stop(self):
#FIXME: how do we tell dbus we are no longer interested?
pass


def main():
def mdns_found(*args):
print("mdns_found: %s" % (args, ))
def mdns_add(*args):
print("mdns_add: %s" % (args, ))
def mdns_remove(*args):
print("mdns_remove: %s" % (args, ))

from xpra.dbus.common import loop_init
loop_init()
listener = AvahiListener(XPRA_MDNS_TYPE, mdns_found, mdns_add, mdns_remove)
try:
from xpra.gtk_common.gobject_compat import import_glib
glib = import_glib()
glib.idle_add(listener.start)
glib.MainLoop().run()
finally:
listener.stop()


if __name__ == "__main__":
main()
83 changes: 82 additions & 1 deletion src/xpra/scripts/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ def nox():
from xpra.x11.bindings.wait_for_x_server import wait_for_x_server #@UnresolvedImport @UnusedImport
except:
supports_server = False
try:
from xpra.net import mdns
supports_mdns = bool(mdns)
except:
supports_mdns = False


#this parse doesn't exit when it encounters an error,
Expand Down Expand Up @@ -236,6 +241,8 @@ def do_parse_cmdline(cmdline, defaults):
"\t%prog version [DISPLAY]\n"
"\t%prog showconfig\n"
]
if supports_mdns:
command_options.append("\t%prog list-mdns\n")
server_modes = []
if supports_server:
server_modes.append("start")
Expand Down Expand Up @@ -1031,7 +1038,7 @@ def show_sound_codec_help(is_server, speaker_codecs, microphone_codecs):

def configure_logging(options, mode):
to = sys.stderr
if mode in ("showconfig", "info", "control", "list", "attach", "stop", "version", "print", "opengl", "test-connect"):
if mode in ("showconfig", "info", "control", "list", "list-mdns", "attach", "stop", "version", "print", "opengl", "test-connect"):
to = sys.stdout
#a bit naughty here, but it's easier to let xpra.log initialize
#the logging system every time, and just undo things here..
Expand Down Expand Up @@ -1170,6 +1177,8 @@ def run_mode(script_file, error_cb, options, args, mode, defaults):
return run_stopexit(mode, error_cb, options, args)
elif mode == "list" and (supports_server or supports_shadow):
return run_list(error_cb, options, args)
elif mode == "list-mdns" and supports_mdns:
return run_list_mdns(error_cb, options, args)
elif mode in ("_proxy", "_proxy_start", "_proxy_start_desktop", "_shadow_start") and (supports_server or supports_shadow):
nox()
return run_proxy(error_cb, options, script_file, args, mode, defaults)
Expand Down Expand Up @@ -2439,6 +2448,78 @@ def may_cleanup_socket(state, display, sockpath, clean_states=[DotXpra.DEAD]):
sys.stdout.write(" (cleaned up)")
sys.stdout.write("\n")

def run_list_mdns(error_cb, opts, extra_args):
no_gtk()
if len(extra_args)<=1:
try:
MDNS_WAIT = int(extra_args[0])
except:
MDNS_WAIT = 5
else:
error_cb("too many arguments for mode")
assert supports_mdns
from xpra.net.mdns import XPRA_MDNS_TYPE
try:
from xpra.net.mdns.avahi_listener import AvahiListener
except ImportError:
error_cb("sorry, 'list-mdns' is not supported on this platform yet")
from xpra.net.net_util import if_indextoname
from xpra.dbus.common import loop_init
from xpra.gtk_common.gobject_compat import import_glib
glib = import_glib()
loop_init()
try:
import collections
found = collections.OrderedDict()
except:
found = {}
shown = set()
def show_new_found():
new_found = [x for x in found.keys() if x not in shown]
for uq in new_found:
recs = found[uq]
for i, rec in enumerate(recs):
iface, _, _, host, address, port, text = rec
display = text.get("display")
mode = text.get("mode", "")
username = text.get("username", "")
session = text.get("session")
if i==0:
print("* user '%s' on '%s'" % (username, host))
if session:
print(" session '%s'" % session)
print(" + %s endpoint on host %s, port %i, interface %s" % (mode, address, port, iface))
dstr = ""
if display.startswith(":"):
dstr = display[1:]
uri = "%s/%s@%s:%s/%s" % (mode, username, address, port, dstr)
print(" \"%s\"" % uri)
shown.add(uq)
def mdns_add(interface, name, domain, host, address, port, text):
text = text or {}
iface = interface
if if_indextoname:
iface = if_indextoname(interface)
username = text.get("username", "")
uq = text.get("uuid", len(found)), username, host
found.setdefault(uq, []).append((iface, name, domain, host, address, port, text))
glib.timeout_add(1000, show_new_found)
listener = AvahiListener(XPRA_MDNS_TYPE, mdns_add=mdns_add)
print("Looking for xpra services via mdns")
try:
glib.idle_add(listener.start)
loop = glib.MainLoop()
glib.timeout_add(MDNS_WAIT*1000, loop.quit)
loop.run()
finally:
listener.stop()
if not found:
print("no services found")
else:
from xpra.util import engs
print("%i service%s found" % (len(found), engs(found)))


def run_list(error_cb, opts, extra_args):
no_gtk()
if extra_args:
Expand Down

0 comments on commit 8644d2c

Please sign in to comment.