Ejemplo n.º 1
0
 def err(msg):
     raise InitException(msg)
Ejemplo n.º 2
0
 def init(self, opts):
     self.pixel_depth = int(opts.pixel_depth) or 32
     if self.pixel_depth not in (24, 30, 32):
         raise InitException("unsupported pixel depth: %s" %
                             self.pixel_depth)
     GTKShadowServerBase.init(self, opts)
Ejemplo n.º 3
0
 def init(self, opts):
     self.pixel_depth = int(opts.pixel_depth) or 32
     if self.pixel_depth not in (24, 30, 32):
         raise InitException("unsupported pixel depth: %s" %
                             self.pixel_depth)
     super().init(opts)
Ejemplo n.º 4
0
def run_server(error_cb,
               opts,
               mode,
               xpra_file,
               extra_args,
               desktop_display=None):
    try:
        cwd = os.getcwd()
    except:
        cwd = os.path.expanduser("~")
        warn("current working directory does not exist, using '%s'\n" % cwd)
    validate_encryption(opts)
    if opts.encoding == "help" or "help" in opts.encodings:
        return show_encoding_help(opts)

    from xpra.server.socket_util import parse_bind_ip, parse_bind_vsock, get_network_logger
    bind_tcp = parse_bind_ip(opts.bind_tcp)
    bind_udp = parse_bind_ip(opts.bind_udp)
    bind_ssl = parse_bind_ip(opts.bind_ssl)
    bind_ws = parse_bind_ip(opts.bind_ws)
    bind_wss = parse_bind_ip(opts.bind_wss)
    bind_rfb = parse_bind_ip(opts.bind_rfb, 5900)
    bind_vsock = parse_bind_vsock(opts.bind_vsock)

    assert mode in ("start", "start-desktop", "upgrade", "shadow", "proxy")
    starting = mode == "start"
    starting_desktop = mode == "start-desktop"
    upgrading = mode == "upgrade"
    shadowing = mode == "shadow"
    proxying = mode == "proxy"
    clobber = upgrading or opts.use_display
    start_vfb = not shadowing and not proxying and not clobber

    if upgrading or shadowing:
        #there should already be one running
        opts.pulseaudio = False

    #get the display name:
    if shadowing and len(extra_args) == 0:
        if WIN32 or OSX:
            #just a virtual name for the only display available:
            display_name = ":0"
        else:
            from xpra.scripts.main import guess_X11_display
            dotxpra = DotXpra(opts.socket_dir, opts.socket_dirs)
            display_name = guess_X11_display(dotxpra)
    elif upgrading and len(extra_args) == 0:
        display_name = guess_xpra_display(opts.socket_dir, opts.socket_dirs)
    else:
        if len(extra_args) > 1:
            error_cb(
                "too many extra arguments (%i): only expected a display number"
                % len(extra_args))
        if len(extra_args) == 1:
            display_name = extra_args[0]
            if not shadowing and not proxying and not opts.use_display:
                display_name_check(display_name)
        else:
            if proxying:
                #find a free display number:
                dotxpra = DotXpra(opts.socket_dir, opts.socket_dirs)
                all_displays = dotxpra.sockets()
                #ie: [("LIVE", ":100"), ("LIVE", ":200"), ...]
                displays = [v[1] for v in all_displays]
                display_name = None
                for x in range(1000, 20000):
                    v = ":%s" % x
                    if v not in displays:
                        display_name = v
                        break
                if not display_name:
                    error_cb(
                        "you must specify a free virtual display name to use with the proxy server"
                    )
            elif opts.use_display:
                #only use automatic guess for xpra displays and not X11 displays:
                display_name = guess_xpra_display(opts.socket_dir,
                                                  opts.socket_dirs)
            else:
                # We will try to find one automaticaly
                # Use the temporary magic value 'S' as marker:
                display_name = 'S' + str(os.getpid())

    if not shadowing and not proxying and not upgrading and opts.exit_with_children and not opts.start_child:
        error_cb(
            "--exit-with-children specified without any children to spawn; exiting immediately"
        )

    atexit.register(run_cleanups)

    # Generate the script text now, because os.getcwd() will
    # change if/when we daemonize:
    from xpra.server.server_util import xpra_runner_shell_script, write_runner_shell_scripts, write_pidfile, find_log_dir, create_input_devices
    script = xpra_runner_shell_script(xpra_file, cwd, opts.socket_dir)

    uid = int(opts.uid)
    gid = int(opts.gid)
    username = get_username_for_uid(uid)
    home = get_home_for_uid(uid)
    xauth_data = None
    if start_vfb:
        xauth_data = get_hex_uuid()
    ROOT = POSIX and getuid() == 0

    protected_fds = []
    protected_env = {}
    stdout = sys.stdout
    stderr = sys.stderr
    # Daemonize:
    if POSIX and opts.daemon:
        #daemonize will chdir to "/", so try to use an absolute path:
        if opts.password_file:
            opts.password_file = tuple(
                os.path.abspath(x) for x in opts.password_file)
        from xpra.server.server_util import daemonize
        daemonize()

    displayfd = 0
    if POSIX and opts.displayfd:
        try:
            displayfd = int(opts.displayfd)
            if displayfd > 0:
                protected_fds.append(displayfd)
        except ValueError as e:
            stderr.write("Error: invalid displayfd '%s':\n" % opts.displayfd)
            stderr.write(" %s\n" % e)
            del e

    # if pam is present, try to create a new session:
    pam = None
    PAM_OPEN = POSIX and envbool("XPRA_PAM_OPEN", ROOT and uid != 0)
    if PAM_OPEN:
        try:
            from xpra.server.pam import pam_session  #@UnresolvedImport
        except ImportError as e:
            stderr.write("Error: failed to import pam module\n")
            stderr.write(" %s" % e)
            del e
            PAM_OPEN = False
    if PAM_OPEN:
        fdc = FDChangeCaptureContext()
        with fdc:
            pam = pam_session(username)
            env = {
                #"XDG_SEAT"               : "seat1",
                #"XDG_VTNR"               : "0",
                "XDG_SESSION_TYPE": "x11",
                #"XDG_SESSION_CLASS"      : "user",
                "XDG_SESSION_DESKTOP": "xpra",
            }
            #maybe we should just bail out instead?
            if pam.start():
                pam.set_env(env)
                items = {}
                if display_name.startswith(":"):
                    items["XDISPLAY"] = display_name
                if xauth_data:
                    items["XAUTHDATA"] = xauth_data
                pam.set_items(items)
                if pam.open():
                    #we can't close it, because we're not going to be root any more,
                    #but since we're the process leader for the session,
                    #terminating will also close the session
                    #add_cleanup(pam.close)
                    protected_env = pam.get_envlist()
                    os.environ.update(protected_env)
        #closing the pam fd causes the session to be closed,
        #and we don't want that!
        protected_fds += fdc.get_new_fds()

    #get XDG_RUNTIME_DIR from env options,
    #which may not be have updated os.environ yet when running as root with "--uid="
    xrd = os.path.abspath(parse_env(opts.env).get("XDG_RUNTIME_DIR", ""))
    if ROOT and (uid > 0 or gid > 0):
        #we're going to chown the directory if we create it,
        #ensure this cannot be abused, only use "safe" paths:
        if not any(x for x in ("/run/user/%i" % uid, "/tmp", "/var/tmp")
                   if xrd.startswith(x)):
            xrd = ""
        #these paths could cause problems if we were to create and chown them:
        if xrd.startswith("/tmp/.X11-unix") or xrd.startswith(
                "/tmp/.XIM-unix"):
            xrd = ""
    if not xrd:
        xrd = os.environ.get("XDG_RUNTIME_DIR")
    xrd = create_runtime_dir(xrd, uid, gid)
    if xrd:
        #this may override the value we get from pam
        #with the value supplied by the user:
        protected_env["XDG_RUNTIME_DIR"] = xrd

    if opts.pidfile:
        write_pidfile(opts.pidfile, uid, gid)

    if POSIX and not ROOT:
        # Write out a shell-script so that we can start our proxy in a clean
        # environment:
        write_runner_shell_scripts(script)

    if start_vfb or opts.daemon:
        #we will probably need a log dir
        #either for the vfb, or for our own log file
        log_dir = opts.log_dir or ""
        if not log_dir or log_dir.lower() == "auto":
            log_dir = find_log_dir(username, uid=uid, gid=gid)
            if not log_dir:
                raise InitException(
                    "cannot find or create a logging directory")
        #expose the log-dir as "XPRA_LOG_DIR",
        #this is used by Xdummy for the Xorg log file
        if "XPRA_LOG_DIR" not in os.environ:
            os.environ["XPRA_LOG_DIR"] = log_dir

        if opts.daemon:
            from xpra.server.server_util import select_log_file, open_log_file, redirect_std_to_log
            log_filename0 = osexpand(
                select_log_file(log_dir, opts.log_file, display_name),
                username, uid, gid)
            logfd = open_log_file(log_filename0)
            if ROOT and (uid > 0 or gid > 0):
                try:
                    os.fchown(logfd, uid, gid)
                except:
                    pass
            stdout, stderr = redirect_std_to_log(logfd, *protected_fds)
            try:
                stderr.write("Entering daemon mode; " +
                             "any further errors will be reported to:\n" +
                             ("  %s\n" % log_filename0))
            except:
                #we tried our best, logging another error won't help
                pass

    #warn early about this:
    if (starting or starting_desktop
        ) and desktop_display and opts.notifications and not opts.dbus_launch:
        print_DE_warnings()

    log = get_util_logger()
    netlog = get_network_logger()

    mdns_recs = {}
    sockets = []

    #SSL sockets:
    wrap_socket_fn = None
    need_ssl = False
    ssl_opt = opts.ssl.lower()
    if ssl_opt in TRUE_OPTIONS or bind_ssl or bind_wss:
        need_ssl = True
    if opts.bind_tcp or opts.bind_ws:
        if ssl_opt == "auto" and opts.ssl_cert:
            need_ssl = True
        elif ssl_opt == "tcp" and opts.bind_tcp:
            need_ssl = True
        elif ssl_opt == "www":
            need_ssl = True
    if need_ssl:
        from xpra.scripts.main import ssl_wrap_socket_fn
        try:
            wrap_socket_fn = ssl_wrap_socket_fn(opts, server_side=True)
            netlog("wrap_socket_fn=%s", wrap_socket_fn)
        except Exception as e:
            netlog("SSL error", exc_info=True)
            cpaths = csv("'%s'" % x for x in (opts.ssl_cert, opts.ssl_key)
                         if x)
            raise InitException(
                "cannot create SSL socket, check your certificate paths (%s): %s"
                % (cpaths, e))

    from xpra.server.socket_util import setup_tcp_socket, setup_udp_socket, setup_vsock_socket, setup_local_sockets, has_dual_stack
    min_port = int(opts.min_port)

    def hosts(host_str):
        if host_str == "*":
            if has_dual_stack():
                #IPv6 will also listen for IPv4:
                return ["::"]
            #no dual stack, so we have to listen on both IPv4 and IPv6 explicitly:
            return ["0.0.0.0", "::"]
        return [host_str]

    def add_mdns(socktype, host_str, port):
        recs = mdns_recs.setdefault(socktype.lower(), [])
        for host in hosts(host_str):
            rec = (host, port)
            if rec not in recs:
                recs.append(rec)

    def add_tcp_socket(socktype, host_str, iport):
        if iport < min_port:
            error_cb("invalid %s port number %i (minimum value is %i)" %
                     (socktype, iport, min_port))
        for host in hosts(host_str):
            socket = setup_tcp_socket(host, iport, socktype)
            sockets.append(socket)
            add_mdns(socktype, host, iport)

    def add_udp_socket(socktype, host_str, iport):
        if iport < min_port:
            error_cb("invalid %s port number %i (minimum value is %i)" %
                     (socktype, iport, min_port))
        for host in hosts(host_str):
            socket = setup_udp_socket(host, iport, socktype)
            sockets.append(socket)
            add_mdns(socktype, host, iport)

    # Initialize the TCP sockets before the display,
    # That way, errors won't make us kill the Xvfb
    # (which may not be ours to kill at that point)
    ws_upgrades = opts.html and (os.path.isabs(opts.html) or opts.html.lower()
                                 in TRUE_OPTIONS + ["auto"])
    netlog("setting up SSL sockets: %s", csv(bind_ssl))
    for host, iport in bind_ssl:
        add_tcp_socket("ssl", host, iport)
        if ws_upgrades:
            add_tcp_socket("wss", host, iport)
    netlog("setting up https / wss (secure websockets): %s", csv(bind_wss))
    for host, iport in bind_wss:
        add_tcp_socket("wss", host, iport)
    tcp_ssl = ssl_opt in TRUE_OPTIONS or (ssl_opt == "auto" and opts.ssl_cert)
    netlog("setting up TCP sockets: %s", csv(bind_tcp))
    for host, iport in bind_tcp:
        add_tcp_socket("tcp", host, iport)
        if tcp_ssl:
            add_mdns("ssl", host, iport)
        if ws_upgrades:
            add_mdns("ws", host, iport)
        if ws_upgrades and tcp_ssl:
            add_mdns("wss", host, iport)
    netlog("setting up UDP sockets: %s", csv(bind_udp))
    for host, iport in bind_udp:
        add_udp_socket("udp", host, iport)
    netlog("setting up http / ws (websockets): %s", csv(bind_ws))
    for host, iport in bind_ws:
        add_tcp_socket("ws", host, iport)
        if tcp_ssl:
            add_mdns("wss", host, iport)
    if bind_rfb and (proxying or starting):
        log.warn("Warning: bind-rfb sockets cannot be used with '%s' mode" %
                 mode)
    else:
        netlog("setting up rfb sockets: %s", csv(bind_rfb))
        for host, iport in bind_rfb:
            add_tcp_socket("rfb", host, iport)
    netlog("setting up vsock sockets: %s", csv(bind_vsock))
    for cid, iport in bind_vsock:
        socket = setup_vsock_socket(cid, iport)
        sockets.append(socket)
        #add_mdns("vsock", str(cid), iport)

    # systemd socket activation:
    try:
        from xpra.platform.xposix.sd_listen import get_sd_listen_sockets
    except ImportError:
        pass
    else:
        sd_sockets = get_sd_listen_sockets()
        netlog("systemd sockets: %s", sd_sockets)
        for stype, socket, addr in sd_sockets:
            sockets.append((stype, socket, addr))
            netlog("%s : %s", (stype, [addr]), socket)
            if stype == "tcp":
                host, iport = addr
                add_mdns("tcp", host, iport)

    sanitize_env()
    if POSIX:
        if xrd:
            os.environ["XDG_RUNTIME_DIR"] = xrd
        os.environ["XDG_SESSION_TYPE"] = "x11"
        if not starting_desktop:
            os.environ["XDG_CURRENT_DESKTOP"] = opts.wm_name
        configure_imsettings_env(opts.input_method)
    if display_name[0] != 'S':
        os.environ["DISPLAY"] = display_name
        os.environ["CKCON_X11_DISPLAY"] = display_name
    else:
        try:
            del os.environ["DISPLAY"]
        except:
            pass
    os.environ.update(protected_env)
    log("env=%s", os.environ)

    UINPUT_UUID_LEN = 12
    UINPUT_UUID_MIN_LEN = 12
    UINPUT_UUID_MAX_LEN = 32
    # Start the Xvfb server first to get the display_name if needed
    odisplay_name = display_name
    xvfb = None
    xvfb_pid = None
    uinput_uuid = None
    if start_vfb:
        assert not proxying and xauth_data
        pixel_depth = validate_pixel_depth(opts.pixel_depth)
        from xpra.x11.vfb_util import start_Xvfb, check_xvfb_process
        from xpra.server.server_util import has_uinput
        uinput_uuid = None
        if has_uinput() and opts.input_devices.lower() in (
                "uinput", "auto") and not shadowing:
            from xpra.os_util import get_rand_chars
            uinput_uuid = get_rand_chars(UINPUT_UUID_LEN)
        xvfb, display_name, cleanups = start_Xvfb(opts.xvfb, pixel_depth,
                                                  display_name, cwd, uid, gid,
                                                  username, xauth_data,
                                                  uinput_uuid)
        for f in cleanups:
            add_cleanup(f)
        xvfb_pid = xvfb.pid
        #always update as we may now have the "real" display name:
        os.environ["DISPLAY"] = display_name
        os.environ["CKCON_X11_DISPLAY"] = display_name
        os.environ.update(protected_env)
        if display_name != odisplay_name and pam:
            pam.set_items({"XDISPLAY": display_name})

        def check_xvfb():
            return check_xvfb_process(xvfb)
    else:
        if POSIX and clobber:
            #if we're meant to be using a private XAUTHORITY file,
            #make sure to point to it:
            from xpra.x11.vfb_util import get_xauthority_path
            xauthority = get_xauthority_path(display_name, username, uid, gid)
            if os.path.exists(xauthority):
                os.environ["XAUTHORITY"] = xauthority

        def check_xvfb():
            return True

    if POSIX and not OSX and displayfd > 0:
        from xpra.platform.displayfd import write_displayfd
        try:
            display = display_name[1:]
            log("writing display='%s' to displayfd=%i", display, displayfd)
            assert write_displayfd(displayfd, display), "timeout"
        except Exception as e:
            log.error("write_displayfd failed", exc_info=True)
            log.error("Error: failed to write '%s' to fd=%s", display_name,
                      displayfd)
            log.error(" %s", str(e) or type(e))
            del e
        try:
            os.close(displayfd)
        except:
            pass

    if not proxying:

        def close_display():
            close_gtk_display()
            if xvfb_pid:
                kill_xvfb(xvfb_pid)

        add_cleanup(close_display)
    else:
        close_display = None

    if opts.daemon:

        def noerr(fn, *args):
            try:
                fn(*args)
            except:
                pass

        log_filename1 = osexpand(
            select_log_file(log_dir, opts.log_file, display_name), username,
            uid, gid)
        if log_filename0 != log_filename1:
            # we now have the correct log filename, so use it:
            os.rename(log_filename0, log_filename1)
            if odisplay_name != display_name:
                #this may be used by scripts, let's try not to change it:
                noerr(stderr.write, "Actual display used: %s\n" % display_name)
            noerr(stderr.write,
                  "Actual log file name is now: %s\n" % log_filename1)
            noerr(stderr.flush)
        noerr(stdout.close)
        noerr(stderr.close)
    #we should not be using stdout or stderr from this point:
    del stdout
    del stderr

    if not check_xvfb():
        #xvfb problem: exit now
        return 1

    #create devices for vfb if needed:
    devices = {}
    if not start_vfb and not proxying and not shadowing:
        #try to find the existing uinput uuid:
        #use a subprocess to avoid polluting our current process
        #with X11 connections before we get a chance to change uid
        cmd = ["xprop", "-display", display_name, "-root", "_XPRA_UINPUT_ID"]
        try:
            code, out, err = get_status_output(cmd)
        except Exception as e:
            log("failed to get existing uinput id: %s", e)
            del e
        else:
            log("Popen(%s)=%s", cmd, (code, out, err))
            if code == 0 and out.find("=") > 0:
                uinput_uuid = out.split("=", 1)[1]
                log("raw uinput uuid=%s", uinput_uuid)
                uinput_uuid = strtobytes(uinput_uuid.strip('\n\r"\\ '))
                if uinput_uuid:
                    if len(uinput_uuid) > UINPUT_UUID_MAX_LEN or len(
                            uinput_uuid) < UINPUT_UUID_MIN_LEN:
                        log.warn("Warning: ignoring invalid uinput id:")
                        log.warn(" '%s'", uinput_uuid)
                        uinput_uuid = None
                    else:
                        log.info("retrieved existing uinput id: %s",
                                 bytestostr(uinput_uuid))
    if uinput_uuid:
        devices = create_input_devices(uinput_uuid, uid)

    if ROOT and (uid != 0 or gid != 0):
        log("root: switching to uid=%i, gid=%i", uid, gid)
        setuidgid(uid, gid)
        os.environ.update({
            "HOME": home,
            "USER": username,
            "LOGNAME": username,
        })
        shell = get_shell_for_uid(uid)
        if shell:
            os.environ["SHELL"] = shell
        #now we've changed uid, it is safe to honour all the env updates:
        configure_env(opts.env)
        os.environ.update(protected_env)

    if opts.chdir:
        os.chdir(opts.chdir)

    display = None
    if not proxying:
        no_gtk()
        if POSIX and not OSX and (starting or starting_desktop or shadowing):
            #check that we can access the X11 display:
            from xpra.x11.vfb_util import verify_display_ready
            if not verify_display_ready(xvfb, display_name, shadowing):
                return 1
            if not PYTHON3:
                from xpra.x11.gtk2.gdk_display_util import verify_gdk_display  #@UnusedImport
            else:
                from xpra.x11.gtk3.gdk_display_util import verify_gdk_display  #@Reimport
            display = verify_gdk_display(display_name)
            if not display:
                return 1
        #on win32, this ensures that we get the correct screen size to shadow:
        from xpra.platform.gui import init as gui_init
        gui_init()

    #setup unix domain socket:
    if not opts.socket_dir and not opts.socket_dirs:
        #we always need at least one valid socket dir
        from xpra.platform.paths import get_socket_dirs
        opts.socket_dirs = get_socket_dirs()
    local_sockets = setup_local_sockets(opts.bind, opts.socket_dir,
                                        opts.socket_dirs, display_name,
                                        clobber, opts.mmap_group,
                                        opts.socket_permissions, username, uid,
                                        gid)
    netlog("setting up local sockets: %s", local_sockets)
    for rec, cleanup_socket in local_sockets:
        socktype, socket, sockpath = rec
        #ie: ("unix-domain", sock, sockpath), cleanup_socket
        sockets.append(rec)
        netlog("%s %s : %s", socktype, sockpath, socket)
        add_cleanup(cleanup_socket)
        if opts.mdns:
            ssh_port = get_ssh_port()
            netlog("ssh %s:%s : %s", "", ssh_port, socket)
            if ssh_port:
                add_mdns("ssh", "", ssh_port)

    kill_dbus = None
    if shadowing:
        from xpra.platform.shadow_server import ShadowServer
        app = ShadowServer()
    elif proxying:
        from xpra.server.proxy.proxy_server import ProxyServer
        app = ProxyServer()
    else:
        if not check_xvfb():
            return 1
        assert starting or starting_desktop or upgrading
        from xpra.x11.gtk2.gdk_display_source import init_gdk_display_source
        init_gdk_display_source()
        #(now we can access the X11 server)

        #make sure the pid we save is the real one:
        if not check_xvfb():
            return 1
        if xvfb_pid is not None:
            #save the new pid (we should have one):
            save_xvfb_pid(xvfb_pid)

        if POSIX:
            save_uinput_id(uinput_uuid or "")
            dbus_pid = -1
            dbus_env = {}
            if clobber:
                #get the saved pids and env
                dbus_pid = get_dbus_pid()
                dbus_env = get_dbus_env()
                log("retrieved existing dbus attributes")
            else:
                assert starting or starting_desktop
                if xvfb_pid is not None:
                    #save the new pid (we should have one):
                    save_xvfb_pid(xvfb_pid)
                bus_address = protected_env.get("DBUS_SESSION_BUS_ADDRESS")
                log("dbus_launch=%s, current DBUS_SESSION_BUS_ADDRESS=%s",
                    opts.dbus_launch, bus_address)
                if opts.dbus_launch and not bus_address:
                    #start a dbus server:
                    def kill_dbus():
                        log("kill_dbus: dbus_pid=%s" % dbus_pid)
                        if dbus_pid <= 0:
                            return
                        try:
                            os.kill(dbus_pid, signal.SIGINT)
                        except Exception as e:
                            log.warn(
                                "Warning: error trying to stop dbus with pid %i:",
                                dbus_pid)
                            log.warn(" %s", e)

                    add_cleanup(kill_dbus)
                    #this also updates os.environ with the dbus attributes:
                    dbus_pid, dbus_env = start_dbus(opts.dbus_launch)
                    if dbus_pid > 0:
                        save_dbus_pid(dbus_pid)
                    if dbus_env:
                        save_dbus_env(dbus_env)
            log("dbus attributes: pid=%s, env=%s", dbus_pid, dbus_env)
            if dbus_env:
                os.environ.update(dbus_env)
                os.environ.update(protected_env)

        if opts.forward_xdg_open and os.environ.get(
                "DISPLAY") and local_sockets:
            #find one xdg-open can use to talk back to us:
            udpaths = [
                sockpath for (stype, _, sockpath), _ in local_sockets
                if stype == "unix-domain"
            ]
            if udpaths:
                os.environ["XPRA_XDG_OPEN_SERVER_SOCKET"] = udpaths[0]
                for x in ("/usr/libexec/xpra", "/usr/lib/xpra"):
                    xdg_override = os.path.join(x, "xdg-open")
                    if os.path.exists(xdg_override):
                        os.environ["PATH"] = x + os.pathsep + os.environ.get(
                            "PATH", "")
                        break

        log("env=%s", os.environ)
        try:
            # This import is delayed because the module depends on gtk:
            from xpra.x11.bindings.window_bindings import X11WindowBindings
            X11Window = X11WindowBindings()
            if (starting or
                    starting_desktop) and not clobber and opts.resize_display:
                from xpra.x11.vfb_util import set_initial_resolution
                set_initial_resolution(starting_desktop)
        except ImportError as e:
            log.error(
                "Failed to load Xpra server components, check your installation: %s"
                % e)
            return 1
        if starting or upgrading:
            if not X11Window.displayHasXComposite():
                log.error(
                    "Xpra 'start' subcommand runs as a compositing manager")
                log.error(
                    " it cannot use a display which lacks the XComposite extension!"
                )
                return 1
            assert not PYTHON3
            if starting:
                #check for an existing window manager:
                from xpra.x11.gtk2.wm import wm_check
                if not wm_check(display, opts.wm_name, upgrading):
                    return 1
            log("XShape=%s", X11Window.displayHasXShape())
            from xpra.x11.server import XpraServer
            app = XpraServer(clobber)
        else:
            assert starting_desktop and not PYTHON3
            from xpra.x11.desktop_server import XpraDesktopServer
            app = XpraDesktopServer()
        app.init_virtual_devices(devices)

    if proxying or upgrading:
        #when proxying or upgrading, don't exec any plain start commands:
        opts.start = opts.start_child = []
    elif opts.exit_with_children:
        assert opts.start_child, "exit-with-children was specified but start-child is missing!"
    elif opts.start_child:
        log.warn("Warning: the 'start-child' option is used,")
        log.warn(" but 'exit-with-children' is not enabled,")
        log.warn(" use 'start' instead")

    try:
        app._ssl_wrap_socket = wrap_socket_fn
        app.original_desktop_display = desktop_display
        app.exec_cwd = opts.chdir or cwd
        app.init(opts)
        app.setup()
    except InitException as e:
        log.error("xpra server initialization error:")
        log.error(" %s", e)
        return 1
    except Exception as e:
        log.error("Error: cannot start the %s server",
                  app.session_type,
                  exc_info=True)
        log.error(str(e))
        log.info("")
        return 1

    #publish mdns records:
    if opts.mdns:
        from xpra.platform.info import get_username
        from xpra.server.socket_util import mdns_publish
        mdns_info = {
            "display": display_name,
            "username": get_username(),
            "uuid": app.uuid,
            "platform": sys.platform,
            "type": app.session_type,
        }
        MDNS_EXPOSE_NAME = envbool("XPRA_MDNS_EXPOSE_NAME", True)
        if MDNS_EXPOSE_NAME and app.session_name:
            mdns_info["name"] = app.session_name
        for mode, listen_on in mdns_recs.items():
            mdns_publish(display_name, mode, listen_on, mdns_info)

    del opts

    log("%s(%s)", app.init_sockets, sockets)
    app.init_sockets(sockets)
    log("%s(%s)", app.init_when_ready, _when_ready)
    app.init_when_ready(_when_ready)

    try:
        #from here on, we own the vfb, even if we inherited one:
        if (starting or starting_desktop or upgrading) and clobber:
            #and it will be killed if exit cleanly:
            xvfb_pid = get_xvfb_pid()

        log("running %s", app.run)
        r = app.run()
        log("%s()=%s", app.run, r)
    except KeyboardInterrupt:
        log.info("stopping on KeyboardInterrupt")
        r = 0
    except Exception:
        log.error("server error", exc_info=True)
        r = -128
    if r > 0:
        # Upgrading/exiting, so leave X and dbus servers running
        if close_display:
            _cleanups.remove(close_display)
        if kill_dbus:
            _cleanups.remove(kill_dbus)
        from xpra.server import EXITING_CODE
        if r == EXITING_CODE:
            log.info("exiting: not cleaning up Xvfb")
        else:
            log.info("upgrading: not cleaning up Xvfb")
        log("cleanups=%s", _cleanups)
        r = 0
    return r
Ejemplo n.º 5
0
def setup_local_sockets(bind,
                        socket_dir,
                        socket_dirs,
                        display_name,
                        clobber,
                        mmap_group=False,
                        socket_permissions="600",
                        username="",
                        uid=0,
                        gid=0):
    if not bind:
        return []
    if not socket_dir and (not socket_dirs or
                           (len(socket_dirs) == 1 and not socket_dirs[0])):
        raise InitException(
            "at least one socket directory must be set to use unix domain sockets"
        )
    dotxpra = DotXpra(socket_dir or socket_dirs[0], socket_dirs, username, uid,
                      gid)
    display_name = normalize_local_display_name(display_name)
    log = get_network_logger()
    defs = []
    try:
        sockpaths = []
        log("setup_local_sockets: bind=%s", bind)
        for b in bind:
            sockpath = b
            if b == "none" or b == "":
                continue
            elif b == "auto":
                sockpaths += dotxpra.norm_socket_paths(display_name)
                log("sockpaths(%s)=%s (uid=%i, gid=%i)", display_name,
                    sockpaths, uid, gid)
            else:
                sockpath = dotxpra.osexpand(b)
                if b.endswith("/") or (os.path.exists(sockpath)
                                       and os.path.isdir(sockpath)):
                    sockpath = os.path.abspath(sockpath)
                    if not os.path.exists(sockpath):
                        os.makedirs(sockpath)
                    sockpath = norm_makepath(sockpath, display_name)
                elif os.path.isabs(b):
                    sockpath = b
                else:
                    sockpath = dotxpra.socket_path(b)
                sockpaths += [sockpath]
            assert sockpaths, "no socket paths to try for %s" % b
        #expand and remove duplicate paths:
        tmp = []
        for tsp in sockpaths:
            sockpath = dotxpra.osexpand(tsp)
            if sockpath in tmp:
                log.warn("Warning: skipping duplicate bind path %s", sockpath)
                continue
            tmp.append(sockpath)
        sockpaths = tmp
        #create listeners:
        if WIN32:
            from xpra.platform.win32.namedpipes.listener import NamedPipeListener
            for sockpath in sockpaths:
                npl = NamedPipeListener(sockpath)
                log.info("created named pipe: %s", sockpath)
                defs.append((("named-pipe", npl, sockpath), npl.stop))
        else:

            def checkstate(sockpath, state):
                if state not in (DotXpra.DEAD, DotXpra.UNKNOWN):
                    if state == DotXpra.INACCESSIBLE:
                        raise InitException(
                            "An xpra server is already running at %s\n" %
                            (sockpath, ))
                    raise InitException(
                        "You already have an xpra server running at %s\n"
                        "  (did you want 'xpra upgrade'?)" % (sockpath, ))

            #remove exisiting sockets if clobber is set,
            #otherwise verify there isn't a server already running
            #and create the directories for the sockets:
            unknown = []
            for sockpath in sockpaths:
                if clobber and os.path.exists(sockpath):
                    os.unlink(sockpath)
                else:
                    state = dotxpra.get_server_state(sockpath, 1)
                    log("state(%s)=%s", sockpath, state)
                    checkstate(sockpath, state)
                    if state == dotxpra.UNKNOWN:
                        unknown.append(sockpath)
                d = os.path.dirname(sockpath)
                try:
                    dotxpra.mksockdir(d)
                except Exception as e:
                    log.warn("Warning: failed to create socket directory '%s'",
                             d)
                    log.warn(" %s", e)
                    del e
            #wait for all the unknown ones:
            log("sockets in unknown state: %s", unknown)
            if unknown:
                #re-probe them using threads so we can do them in parallel:
                from time import sleep
                from xpra.make_thread import start_thread
                threads = []

                def timeout_probe(sockpath):
                    #we need a loop because "DEAD" sockets may return immediately
                    #(ie: when the server is starting up)
                    start = monotonic_time()
                    while monotonic_time() - start < WAIT_PROBE_TIMEOUT:
                        state = dotxpra.get_server_state(
                            sockpath, WAIT_PROBE_TIMEOUT)
                        log("timeout_probe() get_server_state(%s)=%s",
                            sockpath, state)
                        if state not in (DotXpra.UNKNOWN, DotXpra.DEAD):
                            break
                        sleep(1)

                log.warn(
                    "Warning: some of the sockets are in an unknown state:")
                for sockpath in unknown:
                    log.warn(" %s", sockpath)
                    t = start_thread(timeout_probe,
                                     "probe-%s" % sockpath,
                                     daemon=True,
                                     args=(sockpath, ))
                    threads.append(t)
                log.warn(
                    " please wait as we allow the socket probing to timeout")
                #wait for all the threads to do their job:
                for t in threads:
                    t.join(WAIT_PROBE_TIMEOUT + 1)
            #now we can re-check quickly:
            #(they should all be DEAD or UNKNOWN):
            for sockpath in sockpaths:
                state = dotxpra.get_server_state(sockpath, 1)
                log("state(%s)=%s", sockpath, state)
                checkstate(sockpath, state)
                try:
                    if os.path.exists(sockpath):
                        os.unlink(sockpath)
                except:
                    pass
            #now try to create all the sockets:
            for sockpath in sockpaths:
                #create it:
                try:
                    sock, cleanup_socket = create_unix_domain_socket(
                        sockpath, mmap_group, socket_permissions)
                    log.info("created unix domain socket: %s", sockpath)
                    defs.append(
                        (("unix-domain", sock, sockpath), cleanup_socket))
                except Exception as e:
                    handle_socket_error(sockpath, e)
                    del e
    except:
        for sock, cleanup_socket in defs:
            try:
                cleanup_socket()
            except Exception as e:
                log.error("Error cleaning up socket %s:", sock)
                log.error(" %s", e)
                del e
        defs = []
        raise
    return defs
Ejemplo n.º 6
0
def start_Xvfb(xvfb_str,
               pixel_depth,
               display_name,
               cwd,
               uid,
               gid,
               username,
               xauth_data,
               uinput_uuid=None):
    if not POSIX:
        raise InitException("starting an Xvfb is not supported on %s" %
                            os.name)
    if OSX:
        raise InitException("starting an Xvfb is not supported on MacOS")
    if not xvfb_str:
        raise InitException("the 'xvfb' command is not defined")

    cleanups = []
    log = get_vfb_logger()
    log("start_Xvfb%s", (xvfb_str, pixel_depth, display_name, cwd, uid, gid,
                         username, xauth_data, uinput_uuid))
    xauthority = get_xauthority_path(display_name, username, uid, gid)
    os.environ["XAUTHORITY"] = xauthority
    if not os.path.exists(xauthority):
        log("creating XAUTHORITY=%s with data=%s", xauthority, xauth_data)
        try:
            with open(xauthority, 'wa') as f:
                if getuid() == 0 and (uid != 0 or gid != 0):
                    os.fchown(f.fileno(), uid, gid)
        except Exception as e:
            #trying to continue anyway!
            log.error("Error trying to create XAUTHORITY file %s:", xauthority)
            log.error(" %s", e)
    else:
        log("found existing XAUTHORITY file '%s'", xauthority)
    use_display_fd = display_name[0] == 'S'

    subs = {}

    def pathexpand(s):
        return osexpand(s,
                        actual_username=username,
                        uid=uid,
                        gid=gid,
                        subs=subs)

    subs.update({
        "DISPLAY": display_name,
        "XPRA_LOG_DIR": pathexpand(os.environ.get("XPRA_LOG_DIR")),
    })

    #identify logfile argument if it exists,
    #as we may have to rename it, or create the directory for it:
    import shlex
    xvfb_cmd = shlex.split(xvfb_str)
    if not xvfb_cmd:
        raise InitException(
            "cannot start Xvfb, the command definition is missing!")
    #make sure all path values are expanded:
    xvfb_cmd = [pathexpand(s) for s in xvfb_cmd]

    try:
        logfile_argindex = xvfb_cmd.index('-logfile')
        assert logfile_argindex + 1 < len(
            xvfb_cmd
        ), "invalid xvfb command string: -logfile should not be last (found at index %i)" % logfile_argindex
        xorg_log_file = xvfb_cmd[logfile_argindex + 1]
    except ValueError:
        xorg_log_file = None
    tmp_xorg_log_file = None
    if xorg_log_file:
        if use_display_fd:
            #keep track of it so we can rename it later:
            tmp_xorg_log_file = xorg_log_file
        #make sure the Xorg log directory exists:
        xorg_log_dir = os.path.dirname(xorg_log_file)
        if not os.path.exists(xorg_log_dir):
            try:
                log("creating Xorg log dir '%s'", xorg_log_dir)
                os.mkdir(xorg_log_dir, 0o700)
                if POSIX and uid != getuid() or gid != getgid():
                    try:
                        os.lchown(xorg_log_dir, uid, gid)
                    except:
                        pass
            except OSError as e:
                raise InitException(
                    "failed to create the Xorg log directory '%s': %s" %
                    (xorg_log_dir, e))

    if uinput_uuid:
        #use uinput:
        #identify -config xorg.conf argument and replace it with the uinput one:
        try:
            config_argindex = xvfb_cmd.index("-config")
        except ValueError as e:
            log.warn("Warning: cannot use uinput")
            log.warn(" '-config' argument not found in the xvfb command")
        else:
            assert config_argindex + 1 < len(
                xvfb_cmd
            ), "invalid xvfb command string: -config should not be last (found at index %i)" % config_argindex
            xorg_conf = xvfb_cmd[config_argindex + 1]
            if xorg_conf.endswith("xorg.conf"):
                xorg_conf = xorg_conf.replace("xorg.conf", "xorg-uinput.conf")
                if os.path.exists(xorg_conf):
                    xvfb_cmd[config_argindex + 1] = xorg_conf
            #create uinput device definition files:
            #(we have to assume that Xorg is configured to use this path..)
            xorg_conf_dir = pathexpand(get_Xdummy_confdir())
            cleanups = create_xorg_device_configs(xorg_conf_dir, uinput_uuid,
                                                  uid, gid)

    xvfb_executable = xvfb_cmd[0]
    if (xvfb_executable.endswith("Xorg")
            or xvfb_executable.endswith("Xdummy")) and pixel_depth > 0:
        xvfb_cmd.append("-depth")
        xvfb_cmd.append(str(pixel_depth))
    xvfb = None
    try:
        if use_display_fd:

            def displayfd_err(msg):
                raise InitException("%s: %s" % (xvfb_executable, msg))

            r_pipe, w_pipe = os.pipe()
            try:
                if PYTHON3:
                    os.set_inheritable(w_pipe, True)  #@UndefinedVariable
                xvfb_cmd += ["-displayfd", str(w_pipe)]
                xvfb_cmd[0] = "%s-for-Xpra-%s" % (xvfb_executable,
                                                  display_name)

                def preexec():
                    setsid()
                    if getuid() == 0 and uid:
                        setuidgid(uid, gid)
                    close_fds([0, 1, 2, r_pipe, w_pipe])

                try:
                    xvfb = subprocess.Popen(xvfb_cmd,
                                            executable=xvfb_executable,
                                            close_fds=False,
                                            stdin=subprocess.PIPE,
                                            preexec_fn=preexec,
                                            cwd=cwd)
                except OSError as e:
                    log("Popen%s", (xvfb_cmd, xvfb_executable, cwd),
                        exc_info=True)
                    raise InitException(
                        "failed to execute xvfb command %s: %s" %
                        (xvfb_cmd, e))
                assert xvfb.poll() is None, "xvfb command failed"
                # Read the display number from the pipe we gave to Xvfb
                try:
                    buf = read_displayfd(r_pipe)
                except Exception as e:
                    log("read_displayfd(%s)", r_pipe, exc_info=True)
                    displayfd_err("failed to read displayfd pipe %s: %s" %
                                  (r_pipe, e))
            finally:
                osclose(r_pipe)
                osclose(w_pipe)
            n = parse_displayfd(buf, displayfd_err)
            new_display_name = ":%s" % n
            log("Using display number provided by %s: %s", xvfb_executable,
                new_display_name)
            if tmp_xorg_log_file != None:
                #ie: ${HOME}/.xpra/Xorg.${DISPLAY}.log -> /home/antoine/.xpra/Xorg.S14700.log
                f0 = shellsub(tmp_xorg_log_file, subs)
                subs["DISPLAY"] = new_display_name
                #ie: ${HOME}/.xpra/Xorg.${DISPLAY}.log -> /home/antoine/.xpra/Xorg.:1.log
                f1 = shellsub(tmp_xorg_log_file, subs)
                if f0 != f1:
                    try:
                        os.rename(f0, f1)
                    except Exception as e:
                        log.warn("Warning: failed to rename Xorg log file,")
                        log.warn(" from '%s' to '%s'" % (f0, f1))
                        log.warn(" %s" % e)
            display_name = new_display_name
        else:
            # use display specified
            xvfb_cmd[0] = "%s-for-Xpra-%s" % (xvfb_executable, display_name)
            xvfb_cmd.append(display_name)

            def preexec():
                if getuid() == 0 and (uid != 0 or gid != 0):
                    setuidgid(uid, gid)
                else:
                    setsid()

            log("xvfb_cmd=%s", xvfb_cmd)
            xvfb = subprocess.Popen(xvfb_cmd,
                                    executable=xvfb_executable,
                                    close_fds=True,
                                    stdin=subprocess.PIPE,
                                    preexec_fn=preexec)

        xauth_add(xauthority, display_name, xauth_data, uid, gid)
    except Exception as e:
        if xvfb and xvfb.poll() is None:
            log.error(" stopping vfb process with pid %i", xvfb.pid)
            xvfb.terminate()
        raise
    log("xvfb process=%s", xvfb)
    log("display_name=%s", display_name)
    return xvfb, display_name, cleanups
Ejemplo n.º 7
0
 def displayfd_err(msg):
     raise InitException("%s: %s" % (xvfb_executable, msg))
Ejemplo n.º 8
0
def do_run_server(error_cb,
                  opts,
                  mode,
                  xpra_file,
                  extra_args,
                  desktop_display=None):
    assert mode in (
        "start",
        "start-desktop",
        "upgrade",
        "upgrade-desktop",
        "shadow",
        "proxy",
    )

    try:
        cwd = os.getcwd()
    except OSError:
        cwd = os.path.expanduser("~")
        warn("current working directory does not exist, using '%s'\n" % cwd)
    validate_encryption(opts)
    if opts.encoding == "help" or "help" in opts.encodings:
        return show_encoding_help(opts)

    #remove anything pointing to dbus from the current env
    #(so we only detect a dbus instance started by pam,
    # and override everything else)
    for k in tuple(os.environ.keys()):
        if k.startswith("DBUS_"):
            del os.environ[k]

    starting = mode == "start"
    starting_desktop = mode == "start-desktop"
    upgrading = mode == "upgrade"
    upgrading_desktop = mode == "upgrade-desktop"
    shadowing = mode == "shadow"
    proxying = mode == "proxy"
    clobber = int(upgrading
                  or upgrading_desktop) | int(opts.use_display or 0) * 2
    start_vfb = not (shadowing or proxying or clobber)

    if not proxying and PYTHON3 and POSIX and not OSX:
        #we don't support wayland servers,
        #so make sure GDK will use the X11 backend:
        from xpra.os_util import saved_env
        saved_env["GDK_BACKEND"] = "x11"
        os.environ["GDK_BACKEND"] = "x11"

    if proxying or upgrading or upgrading_desktop:
        #when proxying or upgrading, don't exec any plain start commands:
        opts.start = opts.start_child = []
    elif opts.exit_with_children:
        assert opts.start_child, "exit-with-children was specified but start-child is missing!"
    elif opts.start_child:
        warn("Warning: the 'start-child' option is used,")
        warn(" but 'exit-with-children' is not enabled,")
        warn(" use 'start' instead")

    if opts.bind_rfb and (proxying or starting):
        get_util_logger().warn(
            "Warning: bind-rfb sockets cannot be used with '%s' mode" % mode)
        opts.bind_rfb = ""

    if not shadowing and not starting_desktop:
        opts.rfb_upgrade = 0

    if upgrading or upgrading_desktop or shadowing:
        #there should already be one running
        opts.pulseaudio = False

    #get the display name:
    if shadowing and not extra_args:
        if WIN32 or OSX:
            #just a virtual name for the only display available:
            display_name = ":0"
        else:
            from xpra.scripts.main import guess_X11_display
            dotxpra = DotXpra(opts.socket_dir, opts.socket_dirs)
            display_name = guess_X11_display(dotxpra, desktop_display)
    elif (upgrading or upgrading_desktop) and not extra_args:
        display_name = guess_xpra_display(opts.socket_dir, opts.socket_dirs)
    else:
        if len(extra_args) > 1:
            error_cb(
                "too many extra arguments (%i): only expected a display number"
                % len(extra_args))
        if len(extra_args) == 1:
            display_name = extra_args[0]
            if not shadowing and not proxying and not upgrading and not opts.use_display:
                display_name_check(display_name)
        else:
            if proxying:
                #find a free display number:
                dotxpra = DotXpra(opts.socket_dir, opts.socket_dirs)
                all_displays = dotxpra.sockets()
                #ie: [("LIVE", ":100"), ("LIVE", ":200"), ...]
                displays = [v[1] for v in all_displays]
                display_name = None
                for x in range(1000, 20000):
                    v = ":%s" % x
                    if v not in displays:
                        display_name = v
                        break
                if not display_name:
                    error_cb(
                        "you must specify a free virtual display name to use with the proxy server"
                    )
            elif opts.use_display:
                #only use automatic guess for xpra displays and not X11 displays:
                display_name = guess_xpra_display(opts.socket_dir,
                                                  opts.socket_dirs)
            else:
                # We will try to find one automaticaly
                # Use the temporary magic value 'S' as marker:
                display_name = 'S' + str(os.getpid())

    if not (shadowing or proxying or upgrading or upgrading_desktop) and \
    opts.exit_with_children and not opts.start_child:
        error_cb(
            "--exit-with-children specified without any children to spawn; exiting immediately"
        )

    atexit.register(run_cleanups)

    # Generate the script text now, because os.getcwd() will
    # change if/when we daemonize:
    from xpra.server.server_util import (
        xpra_runner_shell_script,
        write_runner_shell_scripts,
        find_log_dir,
        create_input_devices,
    )
    script = None
    if POSIX and getuid() != 0:
        script = xpra_runner_shell_script(xpra_file, cwd, opts.socket_dir)

    uid = int(opts.uid)
    gid = int(opts.gid)
    username = get_username_for_uid(uid)
    home = get_home_for_uid(uid)
    xauth_data = None
    if start_vfb:
        xauth_data = get_hex_uuid()
    ROOT = POSIX and getuid() == 0

    protected_fds = []
    protected_env = {}
    stdout = sys.stdout
    stderr = sys.stderr
    # Daemonize:
    if POSIX and opts.daemon:
        #daemonize will chdir to "/", so try to use an absolute path:
        if opts.password_file:
            opts.password_file = tuple(
                os.path.abspath(x) for x in opts.password_file)
        from xpra.server.server_util import daemonize
        daemonize()

    displayfd = 0
    if POSIX and opts.displayfd:
        try:
            displayfd = int(opts.displayfd)
            if displayfd > 0:
                protected_fds.append(displayfd)
        except ValueError as e:
            stderr.write("Error: invalid displayfd '%s':\n" % opts.displayfd)
            stderr.write(" %s\n" % e)
            del e

    # if pam is present, try to create a new session:
    pam = None
    PAM_OPEN = POSIX and envbool("XPRA_PAM_OPEN", ROOT and uid != 0)
    if PAM_OPEN:
        try:
            from xpra.server.pam import pam_session  #@UnresolvedImport
        except ImportError as e:
            stderr.write("Error: failed to import pam module\n")
            stderr.write(" %s" % e)
            del e
            PAM_OPEN = False
    if PAM_OPEN:
        fdc = FDChangeCaptureContext()
        with fdc:
            pam = pam_session(username)
            env = {
                #"XDG_SEAT"               : "seat1",
                #"XDG_VTNR"               : "0",
                "XDG_SESSION_TYPE": "x11",
                #"XDG_SESSION_CLASS"      : "user",
                "XDG_SESSION_DESKTOP": "xpra",
            }
            #maybe we should just bail out instead?
            if pam.start():
                pam.set_env(env)
                items = {}
                if display_name.startswith(":"):
                    items["XDISPLAY"] = display_name
                if xauth_data:
                    items["XAUTHDATA"] = xauth_data
                pam.set_items(items)
                if pam.open():
                    #we can't close it, because we're not going to be root any more,
                    #but since we're the process leader for the session,
                    #terminating will also close the session
                    #add_cleanup(pam.close)
                    protected_env = pam.get_envlist()
                    os.environ.update(protected_env)
        #closing the pam fd causes the session to be closed,
        #and we don't want that!
        protected_fds += fdc.get_new_fds()

    #get XDG_RUNTIME_DIR from env options,
    #which may not be have updated os.environ yet when running as root with "--uid="
    xrd = os.path.abspath(parse_env(opts.env).get("XDG_RUNTIME_DIR", ""))
    if ROOT and (uid > 0 or gid > 0):
        #we're going to chown the directory if we create it,
        #ensure this cannot be abused, only use "safe" paths:
        if not any(x for x in ("/run/user/%i" % uid, "/tmp", "/var/tmp")
                   if xrd.startswith(x)):
            xrd = ""
        #these paths could cause problems if we were to create and chown them:
        if xrd.startswith("/tmp/.X11-unix") or xrd.startswith(
                "/tmp/.XIM-unix"):
            xrd = ""
    if not xrd:
        xrd = os.environ.get("XDG_RUNTIME_DIR")
    xrd = create_runtime_dir(xrd, uid, gid)
    if xrd:
        #this may override the value we get from pam
        #with the value supplied by the user:
        protected_env["XDG_RUNTIME_DIR"] = xrd

    if script:
        # Write out a shell-script so that we can start our proxy in a clean
        # environment:
        write_runner_shell_scripts(script)

    if start_vfb or opts.daemon:
        #we will probably need a log dir
        #either for the vfb, or for our own log file
        log_dir = opts.log_dir or ""
        if not log_dir or log_dir.lower() == "auto":
            log_dir = find_log_dir(username, uid=uid, gid=gid)
            if not log_dir:
                raise InitException(
                    "cannot find or create a logging directory")
        #expose the log-dir as "XPRA_LOG_DIR",
        #this is used by Xdummy for the Xorg log file
        if "XPRA_LOG_DIR" not in os.environ:
            os.environ["XPRA_LOG_DIR"] = log_dir

        if opts.daemon:
            from xpra.server.server_util import select_log_file, open_log_file, redirect_std_to_log
            log_filename0 = osexpand(
                select_log_file(log_dir, opts.log_file, display_name),
                username, uid, gid)
            logfd = open_log_file(log_filename0)
            if ROOT and (uid > 0 or gid > 0):
                try:
                    os.fchown(logfd, uid, gid)
                except (OSError, IOError):
                    pass
            stdout, stderr = redirect_std_to_log(logfd, *protected_fds)
            try:
                stderr.write("Entering daemon mode; " +
                             "any further errors will be reported to:\n" +
                             ("  %s\n" % log_filename0))
            except IOError:
                #we tried our best, logging another error won't help
                pass

    #warn early about this:
    if (starting or starting_desktop
        ) and desktop_display and opts.notifications and not opts.dbus_launch:
        print_DE_warnings()

    from xpra.net.socket_util import get_network_logger, setup_local_sockets, create_sockets
    sockets = create_sockets(opts, error_cb)

    sanitize_env()
    if POSIX:
        if xrd:
            os.environ["XDG_RUNTIME_DIR"] = xrd
        if not OSX:
            os.environ["XDG_SESSION_TYPE"] = "x11"
        if not starting_desktop:
            os.environ["XDG_CURRENT_DESKTOP"] = opts.wm_name
        configure_imsettings_env(opts.input_method)
    if display_name[0] != 'S':
        os.environ["DISPLAY"] = display_name
        if POSIX:
            os.environ["CKCON_X11_DISPLAY"] = display_name
    else:
        try:
            del os.environ["DISPLAY"]
        except KeyError:
            pass
    os.environ.update(protected_env)
    from xpra.log import Logger
    log = Logger("server")
    log("env=%s", os.environ)

    UINPUT_UUID_LEN = 12
    UINPUT_UUID_MIN_LEN = 12
    UINPUT_UUID_MAX_LEN = 32
    # Start the Xvfb server first to get the display_name if needed
    odisplay_name = display_name
    xvfb = None
    xvfb_pid = None
    uinput_uuid = None
    if start_vfb:
        assert not proxying and xauth_data
        pixel_depth = validate_pixel_depth(opts.pixel_depth, starting_desktop)
        from xpra.x11.vfb_util import start_Xvfb, check_xvfb_process
        from xpra.server.server_util import has_uinput
        uinput_uuid = None
        if has_uinput() and opts.input_devices.lower() in (
                "uinput", "auto") and not shadowing:
            from xpra.os_util import get_rand_chars
            uinput_uuid = get_rand_chars(UINPUT_UUID_LEN)
        xvfb, display_name, cleanups = start_Xvfb(opts.xvfb, pixel_depth,
                                                  display_name, cwd, uid, gid,
                                                  username, xauth_data,
                                                  uinput_uuid)
        for f in cleanups:
            add_cleanup(f)
        xvfb_pid = xvfb.pid
        #always update as we may now have the "real" display name:
        os.environ["DISPLAY"] = display_name
        os.environ["CKCON_X11_DISPLAY"] = display_name
        os.environ.update(protected_env)
        if display_name != odisplay_name and pam:
            pam.set_items({"XDISPLAY": display_name})

        def check_xvfb():
            return check_xvfb_process(xvfb)
    else:
        if POSIX and clobber:
            #if we're meant to be using a private XAUTHORITY file,
            #make sure to point to it:
            from xpra.x11.vfb_util import get_xauthority_path
            xauthority = get_xauthority_path(display_name, username, uid, gid)
            if os.path.exists(xauthority):
                log("found XAUTHORITY=%s", xauthority)
                os.environ["XAUTHORITY"] = xauthority

        def check_xvfb():
            return True

    if POSIX and not OSX and displayfd > 0:
        from xpra.platform.displayfd import write_displayfd
        try:
            display_no = display_name[1:]
            #ensure it is a string containing the number:
            display_no = str(int(display_no))
            log("writing display_no='%s' to displayfd=%i", display_no,
                displayfd)
            assert write_displayfd(displayfd, display_no), "timeout"
        except Exception as e:
            log.error("write_displayfd failed", exc_info=True)
            log.error("Error: failed to write '%s' to fd=%s", display_name,
                      displayfd)
            log.error(" %s", str(e) or type(e))
            del e

    if opts.daemon:

        def noerr(fn, *args):
            try:
                fn(*args)
            except Exception:
                pass

        log_filename1 = osexpand(
            select_log_file(log_dir, opts.log_file, display_name), username,
            uid, gid)
        if log_filename0 != log_filename1:
            # we now have the correct log filename, so use it:
            os.rename(log_filename0, log_filename1)
            if odisplay_name != display_name:
                #this may be used by scripts, let's try not to change it:
                noerr(stderr.write, "Actual display used: %s\n" % display_name)
            noerr(stderr.write,
                  "Actual log file name is now: %s\n" % log_filename1)
            noerr(stderr.flush)
        noerr(stdout.close)
        noerr(stderr.close)
    #we should not be using stdout or stderr from this point:
    del stdout
    del stderr

    if not check_xvfb():
        #xvfb problem: exit now
        return 1

    #create devices for vfb if needed:
    devices = {}
    if not start_vfb and not proxying and not shadowing and envbool(
            "XPRA_UINPUT", True):
        #try to find the existing uinput uuid:
        #use a subprocess to avoid polluting our current process
        #with X11 connections before we get a chance to change uid
        prop = "_XPRA_UINPUT_ID"
        cmd = ["xprop", "-display", display_name, "-root", prop]
        log("looking for '%s' on display '%s' with XAUTHORITY='%s'", prop,
            display_name, os.environ.get("XAUTHORITY"))
        try:
            code, out, err = get_status_output(cmd)
        except Exception as e:
            log("failed to get existing uinput id: %s", e)
            del e
        else:
            log("Popen(%s)=%s", cmd, (code, out, err))
            if code == 0 and out.find("=") > 0:
                uinput_uuid = out.split("=", 1)[1]
                log("raw uinput uuid=%s", uinput_uuid)
                uinput_uuid = strtobytes(uinput_uuid.strip('\n\r"\\ '))
                if uinput_uuid:
                    if len(uinput_uuid) > UINPUT_UUID_MAX_LEN or len(
                            uinput_uuid) < UINPUT_UUID_MIN_LEN:
                        log.warn("Warning: ignoring invalid uinput id:")
                        log.warn(" '%s'", uinput_uuid)
                        uinput_uuid = None
                    else:
                        log.info("retrieved existing uinput id: %s",
                                 bytestostr(uinput_uuid))
    if uinput_uuid:
        devices = create_input_devices(uinput_uuid, uid)

    if ROOT and (uid != 0 or gid != 0):
        log("root: switching to uid=%i, gid=%i", uid, gid)
        setuidgid(uid, gid)
        os.environ.update({
            "HOME": home,
            "USER": username,
            "LOGNAME": username,
        })
        shell = get_shell_for_uid(uid)
        if shell:
            os.environ["SHELL"] = shell
        #now we've changed uid, it is safe to honour all the env updates:
        configure_env(opts.env)
        os.environ.update(protected_env)

    if opts.chdir:
        log("chdir(%s)", opts.chdir)
        os.chdir(opts.chdir)

    dbus_pid, dbus_env = 0, {}
    if not shadowing and POSIX and not OSX and not clobber:
        no_gtk()
        assert starting or starting_desktop or proxying
        try:
            from xpra.server.dbus.dbus_start import start_dbus
        except ImportError as e:
            log("dbus components are not installed: %s", e)
        else:
            dbus_pid, dbus_env = start_dbus(opts.dbus_launch)
            if dbus_env:
                os.environ.update(dbus_env)

    display = None
    if not proxying:
        if POSIX and not OSX:
            no_gtk()
            if starting or starting_desktop or shadowing:
                #check that we can access the X11 display:
                from xpra.x11.vfb_util import verify_display_ready
                if not verify_display_ready(xvfb, display_name, shadowing):
                    return 1
                log("X11 display is ready")
                no_gtk()
                from xpra.x11.gtk_x11.gdk_display_source import verify_gdk_display
                display = verify_gdk_display(display_name)
                if not display:
                    return 1
                log("GDK can access the display")
        #on win32, this ensures that we get the correct screen size to shadow:
        from xpra.platform.gui import init as gui_init
        log("gui_init()")
        gui_init()

    #setup unix domain socket:
    netlog = get_network_logger()
    local_sockets = setup_local_sockets(opts.bind, opts.socket_dir,
                                        opts.socket_dirs, display_name,
                                        clobber, opts.mmap_group,
                                        opts.socket_permissions, username, uid,
                                        gid)
    netlog("setting up local sockets: %s", local_sockets)
    sockets += local_sockets
    if POSIX and (starting or upgrading or starting_desktop
                  or upgrading_desktop):
        #all unix domain sockets:
        ud_paths = [
            sockpath for stype, _, sockpath, _ in local_sockets
            if stype == "unix-domain"
        ]
        if ud_paths:
            #choose one so our xdg-open override script can use to talk back to us:
            if opts.forward_xdg_open:
                for x in ("/usr/libexec/xpra", "/usr/lib/xpra"):
                    xdg_override = os.path.join(x, "xdg-open")
                    if os.path.exists(xdg_override):
                        os.environ["PATH"] = x + os.pathsep + os.environ.get(
                            "PATH", "")
                        os.environ["XPRA_SERVER_SOCKET"] = ud_paths[0]
                        break
        else:
            log.warn("Warning: no local server sockets,")
            if opts.forward_xdg_open:
                log.warn(" forward-xdg-open cannot be enabled")
            log.warn(" non-embedded ssh connections will not be available")

    set_server_features(opts)

    if not proxying and POSIX and not OSX:
        if not check_xvfb():
            return 1
        from xpra.x11.gtk_x11.gdk_display_source import init_gdk_display_source
        if os.environ.get("NO_AT_BRIDGE") is None:
            os.environ["NO_AT_BRIDGE"] = "1"
        init_gdk_display_source()
        #(now we can access the X11 server)
        if uinput_uuid:
            save_uinput_id(uinput_uuid)

    if shadowing:
        app = make_shadow_server()
    elif proxying:
        app = make_proxy_server()
    else:
        if starting or upgrading:
            app = make_server(clobber)
        else:
            assert starting_desktop or upgrading_desktop
            app = make_desktop_server(clobber)
        app.init_virtual_devices(devices)

    try:
        app.exec_cwd = opts.chdir or cwd
        app.display_name = display_name
        app.init(opts)
        app.init_sockets(sockets)
        app.init_dbus(dbus_pid, dbus_env)
        if not shadowing and (xvfb_pid or clobber):
            app.init_display_pid(xvfb_pid)
        app.original_desktop_display = desktop_display
        del opts
        if not app.server_ready():
            return 1
        app.server_init()
        app.setup()
        app.init_when_ready(_when_ready)
    except InitException as e:
        log.error("xpra server initialization error:")
        log.error(" %s", e)
        app.cleanup()
        return 1
    except Exception as e:
        log.error("Error: cannot start the %s server",
                  app.session_type,
                  exc_info=True)
        log.error(str(e))
        log.info("")
        if upgrading or upgrading_desktop:
            #something abnormal occurred,
            #don't kill the vfb on exit:
            from xpra.server import EXITING_CODE
            app._upgrading = EXITING_CODE
        app.cleanup()
        return 1

    try:
        log("running %s", app.run)
        r = app.run()
        log("%s()=%s", app.run, r)
    except KeyboardInterrupt:
        log.info("stopping on KeyboardInterrupt")
        app.cleanup()
        return 0
    except Exception:
        log.error("server error", exc_info=True)
        app.cleanup()
        return -128
    else:
        if r > 0:
            r = 0
    return r
Ejemplo n.º 9
0
def start_Xvfb(xvfb_str,
               vfb_geom,
               pixel_depth,
               display_name,
               cwd,
               uid,
               gid,
               username,
               uinput_uuid=None):
    if not POSIX:
        raise InitException("starting an Xvfb is not supported on %s" %
                            os.name)
    if OSX:
        raise InitException("starting an Xvfb is not supported on MacOS")
    if not xvfb_str:
        raise InitException("the 'xvfb' command is not defined")

    log = get_vfb_logger()
    log("start_Xvfb%s", (xvfb_str, vfb_geom, pixel_depth, display_name, cwd,
                         uid, gid, username, uinput_uuid))
    use_display_fd = display_name[0] == 'S'

    subs = {}

    def pathexpand(s):
        return osexpand(s,
                        actual_username=username,
                        uid=uid,
                        gid=gid,
                        subs=subs)

    subs.update({
        "DISPLAY": display_name,
        "XPRA_LOG_DIR": pathexpand(os.environ.get("XPRA_LOG_DIR")),
    })

    #identify logfile argument if it exists,
    #as we may have to rename it, or create the directory for it:
    xvfb_cmd = shlex.split(xvfb_str)
    if not xvfb_cmd:
        raise InitException(
            "cannot start Xvfb, the command definition is missing!")
    #make sure all path values are expanded:
    xvfb_cmd = [pathexpand(s) for s in xvfb_cmd]

    #try to honour initial geometries if specified:
    if vfb_geom and xvfb_cmd[0].endswith("Xvfb"):
        #find the '-screen' arguments:
        #"-screen 0 8192x4096x24+32"
        try:
            no_arg = xvfb_cmd.index("0")
        except ValueError:
            no_arg = -1
        geom_str = "%sx%s" % ("x".join(str(x) for x in vfb_geom), pixel_depth)
        if no_arg > 0 and xvfb_cmd[
                no_arg - 1] == "-screen" and len(xvfb_cmd) > (no_arg + 1):
            #found an existing "-screen" argument for this screen number,
            #replace it:
            xvfb_cmd[no_arg + 1] = geom_str
        else:
            #append:
            xvfb_cmd += ["-screen", "0", geom_str]
    try:
        logfile_argindex = xvfb_cmd.index('-logfile')
        if logfile_argindex + 1 >= len(xvfb_cmd):
            raise InitException(
                "invalid xvfb command string: -logfile should not be last")
        xorg_log_file = xvfb_cmd[logfile_argindex + 1]
    except ValueError:
        xorg_log_file = None
    tmp_xorg_log_file = None
    if xorg_log_file:
        if use_display_fd:
            #keep track of it so we can rename it later:
            tmp_xorg_log_file = xorg_log_file
        #make sure the Xorg log directory exists:
        xorg_log_dir = os.path.dirname(xorg_log_file)
        if not os.path.exists(xorg_log_dir):
            try:
                log("creating Xorg log dir '%s'", xorg_log_dir)
                os.mkdir(xorg_log_dir, 0o700)
                if POSIX and uid != getuid() or gid != getgid():
                    try:
                        os.lchown(xorg_log_dir, uid, gid)
                    except OSError:
                        log("lchown(%s, %i, %i)",
                            xorg_log_dir,
                            uid,
                            gid,
                            exc_info=True)
            except OSError as e:
                raise InitException(
                    "failed to create the Xorg log directory '%s': %s" %
                    (xorg_log_dir, e)) from None

    if uinput_uuid:
        #use uinput:
        #identify -config xorg.conf argument and replace it with the uinput one:
        try:
            config_argindex = xvfb_cmd.index("-config")
        except ValueError as e:
            log.warn("Warning: cannot use uinput")
            log.warn(" '-config' argument not found in the xvfb command")
        else:
            if config_argindex + 1 >= len(xvfb_cmd):
                raise InitException(
                    "invalid xvfb command string: -config should not be last")
            xorg_conf = xvfb_cmd[config_argindex + 1]
            if xorg_conf.endswith("xorg.conf"):
                xorg_conf = xorg_conf.replace("xorg.conf", "xorg-uinput.conf")
                if os.path.exists(xorg_conf):
                    xvfb_cmd[config_argindex + 1] = xorg_conf
            #create uinput device definition files:
            #(we have to assume that Xorg is configured to use this path..)
            xorg_conf_dir = pathexpand(get_Xdummy_confdir())
            create_xorg_device_configs(xorg_conf_dir, uinput_uuid, uid, gid)

    xvfb_executable = xvfb_cmd[0]
    if (xvfb_executable.endswith("Xorg")
            or xvfb_executable.endswith("Xdummy")) and pixel_depth > 0:
        xvfb_cmd.append("-depth")
        xvfb_cmd.append(str(pixel_depth))
    xvfb = None
    try:
        if use_display_fd:

            def displayfd_err(msg):
                raise InitException("%s: %s" % (xvfb_executable, msg))

            r_pipe, w_pipe = os.pipe()
            try:
                os.set_inheritable(w_pipe, True)  #@UndefinedVariable
                xvfb_cmd += ["-displayfd", str(w_pipe)]
                xvfb_cmd[0] = "%s-for-Xpra-%s" % (xvfb_executable,
                                                  display_name.lstrip(":"))

                def preexec():
                    os.setpgrp()
                    if getuid() == 0 and uid:
                        setuidgid(uid, gid)

                try:
                    xvfb = Popen(xvfb_cmd,
                                 executable=xvfb_executable,
                                 preexec_fn=preexec,
                                 cwd=cwd,
                                 pass_fds=(w_pipe, ))
                except OSError as e:
                    log("Popen%s", (xvfb_cmd, xvfb_executable, cwd),
                        exc_info=True)
                    raise InitException(
                        "failed to execute xvfb command %s: %s" %
                        (xvfb_cmd, e)) from None
                assert xvfb.poll() is None, "xvfb command failed"
                # Read the display number from the pipe we gave to Xvfb
                try:
                    buf = read_displayfd(r_pipe)
                except Exception as e:
                    log("read_displayfd(%s)", r_pipe, exc_info=True)
                    displayfd_err("failed to read displayfd pipe %s: %s" %
                                  (r_pipe, e))
            finally:
                osclose(r_pipe)
                osclose(w_pipe)
            n = parse_displayfd(buf, displayfd_err)
            new_display_name = ":%s" % n
            log("Using display number provided by %s: %s", xvfb_executable,
                new_display_name)
            if tmp_xorg_log_file:
                #ie: ${HOME}/.xpra/Xorg.${DISPLAY}.log -> /home/antoine/.xpra/Xorg.S14700.log
                f0 = shellsub(tmp_xorg_log_file, subs)
                subs["DISPLAY"] = new_display_name
                #ie: ${HOME}/.xpra/Xorg.${DISPLAY}.log -> /home/antoine/.xpra/Xorg.:1.log
                f1 = shellsub(tmp_xorg_log_file, subs)
                if f0 != f1:
                    try:
                        os.rename(f0, f1)
                    except Exception as e:
                        log.warn("Warning: failed to rename Xorg log file,")
                        log.warn(" from '%s' to '%s'" % (f0, f1))
                        log.warn(" %s" % e)
            display_name = new_display_name
        else:
            # use display specified
            xvfb_cmd[0] = "%s-for-Xpra-%s" % (xvfb_executable, display_name)
            xvfb_cmd.append(display_name)

            def preexec():
                if getuid() == 0 and (uid != 0 or gid != 0):
                    setuidgid(uid, gid)
                else:
                    os.setsid()

            log("xvfb_cmd=%s", xvfb_cmd)
            xvfb = Popen(xvfb_cmd,
                         executable=xvfb_executable,
                         stdin=PIPE,
                         preexec_fn=preexec)
    except Exception as e:
        if xvfb and xvfb.poll() is None:
            log.error(" stopping vfb process with pid %i", xvfb.pid)
            xvfb.terminate()
        raise
    log("xvfb process=%s", xvfb)
    log("display_name=%s", display_name)
    return xvfb, display_name
Ejemplo n.º 10
0
def start_Xvfb(xvfb_str, pixel_depth, display_name, cwd, uid, gid, xauth_data):
    if not POSIX:
        raise InitException("starting an Xvfb is not supported on %s" %
                            os.name)
    if not xvfb_str:
        raise InitException("the 'xvfb' command is not defined")

    from xpra.log import Logger
    log = Logger("server", "x11")

    # We need to set up a new server environment
    xauthority = os.environ.get("XAUTHORITY",
                                os.path.expanduser("~/.Xauthority"))
    if not os.path.exists(xauthority):
        log("creating XAUTHORITY=%s with data=%s", xauthority, xauth_data)
        try:
            with open(xauthority, 'wa') as f:
                if getuid() == 0 and (uid != 0 or gid != 0):
                    os.fchown(f.fileno(), uid, gid)
        except Exception as e:
            #trying to continue anyway!
            log.error("Error trying to create XAUTHORITY file %s:", xauthority)
            log.error(" %s", e)
    use_display_fd = display_name[0] == 'S'

    #identify logfile argument if it exists,
    #as we may have to rename it, or create the directory for it:
    import shlex
    xvfb_cmd = shlex.split(xvfb_str)
    try:
        logfile_argindex = xvfb_cmd.index('-logfile')
    except ValueError:
        logfile_argindex = -1
    assert logfile_argindex + 1 < len(
        xvfb_cmd
    ), "invalid xvfb command string: -logfile should not be last (found at index %i)" % logfile_argindex
    tmp_xorg_log_file = None
    if logfile_argindex > 0:
        if use_display_fd:
            #keep track of it so we can rename it later:
            tmp_xorg_log_file = xvfb_cmd[logfile_argindex + 1]
        #make sure the Xorg log directory exists:
        xorg_log_dir = osexpand(os.path.dirname(xvfb_cmd[logfile_argindex +
                                                         1]))
        if not os.path.exists(xorg_log_dir):
            try:
                log("creating Xorg log dir '%s'", xorg_log_dir)
                os.mkdir(xorg_log_dir, 0o700)
                if POSIX and uid != getuid() or gid != getgid():
                    try:
                        os.lchown(xorg_log_dir, uid, gid)
                    except:
                        pass
            except OSError as e:
                raise InitException(
                    "failed to create the Xorg log directory '%s': %s" %
                    (xorg_log_dir, e))

    #apply string substitutions:
    subs = {
        "XAUTHORITY": xauthority,
        "USER": os.environ.get("USER", "unknown-user"),
        "UID": os.getuid(),
        "GID": os.getgid(),
        "HOME": os.environ.get("HOME", cwd),
        "DISPLAY": display_name,
        "XPRA_LOG_DIR": os.environ.get("XPRA_LOG_DIR"),
    }
    xvfb_cmd = shellsub(xvfb_str, subs).split()
    log("shellsub(%s, %s)=%s", xvfb_str, subs, xvfb_cmd)

    if not xvfb_cmd:
        raise InitException(
            "cannot start Xvfb, the command definition is missing!")
    xvfb_executable = xvfb_cmd[0]
    if (xvfb_executable.endswith("Xorg")
            or xvfb_executable.endswith("Xdummy")) and pixel_depth > 0:
        xvfb_cmd.append("-depth")
        xvfb_cmd.append(str(pixel_depth))
    if use_display_fd:
        # 'S' means that we allocate the display automatically
        r_pipe, w_pipe = os.pipe()
        xvfb_cmd += ["-displayfd", str(w_pipe)]
        xvfb_cmd[0] = "%s-for-Xpra-%s" % (xvfb_executable, display_name)

        def preexec():
            setsid()
            if POSIX and getuid() == 0 and uid:
                setuidgid(uid, gid)
            close_fds([0, 1, 2, r_pipe, w_pipe])

        log("xvfb_cmd=%s", xvfb_cmd)
        xvfb = subprocess.Popen(xvfb_cmd,
                                executable=xvfb_executable,
                                close_fds=False,
                                stdin=subprocess.PIPE,
                                preexec_fn=preexec,
                                cwd=cwd)
        # Read the display number from the pipe we gave to Xvfb
        # waiting up to 10 seconds for it to show up
        limit = monotonic_time() + 10
        buf = ""
        import select  #@UnresolvedImport
        while monotonic_time() < limit and len(buf) < 8:
            r, _, _ = select.select([r_pipe], [], [],
                                    max(0, limit - monotonic_time()))
            if r_pipe in r:
                buf += os.read(r_pipe, 8)
                if buf[-1] == '\n':
                    break
        os.close(r_pipe)
        os.close(w_pipe)
        if len(buf) == 0:
            raise InitException(
                "%s did not provide a display number using -displayfd" %
                xvfb_executable)
        if buf[-1] != '\n':
            raise InitException("%s output not terminated by newline: %s" %
                                (xvfb_executable, buf))
        try:
            n = int(buf[:-1])
        except:
            raise InitException("%s display number is not a valid number: %s" %
                                (xvfb_executable, buf[:-1]))
        if n < 0 or n >= 2**16:
            raise InitException("%s provided an invalid display number: %s" %
                                (xvfb_executable, n))
        new_display_name = ":%s" % n
        log("Using display number provided by %s: %s", xvfb_executable,
            new_display_name)
        if tmp_xorg_log_file != None:
            #ie: ${HOME}/.xpra/Xorg.${DISPLAY}.log -> /home/antoine/.xpra/Xorg.S14700.log
            f0 = shellsub(tmp_xorg_log_file, subs)
            subs["DISPLAY"] = new_display_name
            #ie: ${HOME}/.xpra/Xorg.${DISPLAY}.log -> /home/antoine/.xpra/Xorg.:1.log
            f1 = shellsub(tmp_xorg_log_file, subs)
            if f0 != f1:
                try:
                    os.rename(f0, f1)
                except Exception as e:
                    sys.stderr.write(
                        "failed to rename Xorg log file from '%s' to '%s'\n" %
                        (f0, f1))
                    sys.stderr.write(" %s\n" % e)
        display_name = new_display_name
    else:
        # use display specified
        xvfb_cmd[0] = "%s-for-Xpra-%s" % (xvfb_executable, display_name)
        xvfb_cmd.append(display_name)

        def preexec():
            if getuid() == 0 and (uid != 0 or gid != 0):
                setuidgid(uid, gid)
            else:
                setsid()

        log("xvfb_cmd=%s", xvfb_cmd)
        xvfb = subprocess.Popen(xvfb_cmd,
                                executable=xvfb_executable,
                                close_fds=True,
                                stdin=subprocess.PIPE,
                                preexec_fn=preexec)
    xauth_add(display_name, xauth_data)
    log("xvfb process=%s", xvfb)
    log("display_name=%s", display_name)
    return xvfb, display_name
Ejemplo n.º 11
0
def get_ssl_wrap_socket_fn(cert=None,
                           key=None,
                           ca_certs=None,
                           ca_data=None,
                           protocol="TLSv1_2",
                           client_verify_mode="optional",
                           server_verify_mode="required",
                           verify_flags="X509_STRICT",
                           check_hostname=False,
                           server_hostname=None,
                           options="ALL,NO_COMPRESSION",
                           ciphers="DEFAULT",
                           server_side=True):
    if server_side and not cert:
        raise InitException(
            "you must specify an 'ssl-cert' file to use ssl sockets")
    if server_side:
        verify_mode = client_verify_mode
    else:
        verify_mode = server_verify_mode
    from xpra.log import Logger
    ssllog = Logger("ssl")
    ssllog("get_ssl_wrap_socket_fn%s",
           (cert, key, ca_certs, ca_data, protocol, client_verify_mode,
            server_verify_mode, verify_flags, check_hostname, server_hostname,
            options, ciphers, server_side))
    import ssl
    ssllog(" verify_mode for server_side=%s : %s", server_side, verify_mode)
    #ca-certs:
    if ca_certs == "default":
        ca_certs = None
    ssllog(" ca_certs=%s", ca_certs)
    #parse verify-mode:
    ssl_cert_reqs = getattr(ssl, "CERT_%s" % verify_mode.upper(), None)
    if ssl_cert_reqs is None:
        values = [
            k[len("CERT_"):].lower() for k in dir(ssl) if k.startswith("CERT_")
        ]
        raise InitException(
            "invalid ssl-server-verify-mode '%s', must be one of: %s" %
            (verify_mode, csv(values)))
    ssllog(" cert_reqs=%#x", ssl_cert_reqs)
    #parse protocol:
    proto = getattr(ssl, "PROTOCOL_%s" % (protocol.upper().replace("V", "v")),
                    None)
    if proto is None:
        values = [
            k[len("PROTOCOL_"):] for k in dir(ssl) if k.startswith("PROTOCOL_")
        ]
        raise InitException("invalid ssl-protocol '%s', must be one of: %s" %
                            (protocol, csv(values)))
    ssllog(" protocol=%#x", proto)
    #ca_data may be hex encoded:
    ca_data = parse_encoded_bin_data(ca_data)
    ssllog(" cadata=%s", ellipsizer(ca_data))

    kwargs = {
        "server_side": server_side,
        "do_handshake_on_connect": False,
        "suppress_ragged_eofs": True,
    }
    #parse ssl-verify-flags as CSV:
    ssl_verify_flags = 0
    for x in verify_flags.split(","):
        x = x.strip()
        if not x:
            continue
        v = getattr(ssl, "VERIFY_" + x.upper(), None)
        if v is None:
            raise InitException("invalid ssl verify-flag: %s" % x)
        ssl_verify_flags |= v
    ssllog(" verify_flags=%#x", ssl_verify_flags)
    #parse ssl-options as CSV:
    ssl_options = 0
    for x in options.split(","):
        x = x.strip()
        if not x:
            continue
        v = getattr(ssl, "OP_" + x.upper(), None)
        if v is None:
            raise InitException("invalid ssl option: %s" % x)
        ssl_options |= v
    ssllog(" options=%#x", ssl_options)

    context = ssl.SSLContext(proto)
    context.set_ciphers(ciphers)
    context.verify_mode = ssl_cert_reqs
    context.verify_flags = ssl_verify_flags
    context.options = ssl_options
    ssllog(" cert=%s, key=%s", cert, key)
    if cert:
        SSL_KEY_PASSWORD = os.environ.get("XPRA_SSL_KEY_PASSWORD")
        ssllog("context.load_cert_chain%s",
               (cert or None, key or None, SSL_KEY_PASSWORD))
        context.load_cert_chain(certfile=cert or None,
                                keyfile=key or None,
                                password=SSL_KEY_PASSWORD)
    if ssl_cert_reqs != ssl.CERT_NONE:
        if server_side:
            purpose = ssl.Purpose.CLIENT_AUTH  #@UndefinedVariable
        else:
            purpose = ssl.Purpose.SERVER_AUTH  #@UndefinedVariable
            context.check_hostname = check_hostname
            ssllog(" check_hostname=%s, server_hostname=%s", check_hostname,
                   server_hostname)
            if context.check_hostname:
                if not server_hostname:
                    raise InitException(
                        "ssl error: check-hostname is set but server-hostname is not"
                    )
                kwargs["server_hostname"] = server_hostname
        ssllog(" load_default_certs(%s)", purpose)
        context.load_default_certs(purpose)

        if not ca_certs or ca_certs.lower() == "default":
            ssllog(" using default certs")
            #load_default_certs already calls set_default_verify_paths()
        elif not os.path.exists(ca_certs):
            raise InitException("invalid ssl-ca-certs file or directory: %s" %
                                ca_certs)
        elif os.path.isdir(ca_certs):
            ssllog(" loading ca certs from directory '%s'", ca_certs)
            context.load_verify_locations(capath=ca_certs)
        else:
            ssllog(" loading ca certs from file '%s'", ca_certs)
            assert os.path.isfile(
                ca_certs), "'%s' is not a valid ca file" % ca_certs
            context.load_verify_locations(cafile=ca_certs)
        #handle cadata:
        if ca_data:
            #PITA: because of a bug in the ssl module, we can't pass cadata,
            #so we use a temporary file instead:
            import tempfile
            with tempfile.NamedTemporaryFile(prefix='cadata') as f:
                ssllog(" loading cadata '%s'", ellipsizer(ca_data))
                ssllog(" using temporary file '%s'", f.name)
                f.file.write(ca_data)
                f.file.flush()
                context.load_verify_locations(cafile=f.name)
    elif check_hostname and not server_side:
        ssllog("cannot check hostname client side with verify mode %s",
               verify_mode)
    wrap_socket = context.wrap_socket

    def do_wrap_socket(tcp_socket):
        assert tcp_socket
        ssllog("do_wrap_socket(%s)", tcp_socket)
        if WIN32:
            #on win32, setting the tcp socket to blocking doesn't work?
            #we still hit the following errors that we need to retry:
            from xpra.net import bytestreams
            bytestreams.CAN_RETRY_EXCEPTIONS = (ssl.SSLWantReadError,
                                                ssl.SSLWantWriteError)
        else:
            tcp_socket.setblocking(True)
        try:
            ssl_sock = wrap_socket(tcp_socket, **kwargs)
        except Exception as e:
            ssllog.debug("wrap_socket(%s, %s)",
                         tcp_socket,
                         kwargs,
                         exc_info=True)
            SSLEOFError = getattr(ssl, "SSLEOFError", None)
            if SSLEOFError and isinstance(e, SSLEOFError):
                return None
            raise InitExit(EXIT_SSL_FAILURE,
                           "Cannot wrap socket %s: %s" % (tcp_socket, e))
        if not server_side:
            try:
                ssl_sock.do_handshake(True)
            except Exception as e:
                ssllog.debug("do_handshake", exc_info=True)
                SSLEOFError = getattr(ssl, "SSLEOFError", None)
                if SSLEOFError and isinstance(e, SSLEOFError):
                    return None
                status = EXIT_SSL_FAILURE
                SSLCertVerificationError = getattr(ssl,
                                                   "SSLCertVerificationError",
                                                   None)
                if SSLCertVerificationError and isinstance(
                        e, SSLCertVerificationError):
                    try:
                        msg = e.args[1].split(":", 2)[2]
                    except (ValueError, IndexError):
                        msg = str(e)
                    status = EXIT_SSL_CERTIFICATE_VERIFY_FAILURE
                    #ssllog.warn("host failed SSL verification: %s", msg)
                else:
                    msg = str(e)
                raise InitExit(status, "SSL handshake failed: %s" % msg)
        return ssl_sock

    return do_wrap_socket
Ejemplo n.º 12
0
def setup_local_sockets(bind,
                        socket_dir,
                        socket_dirs,
                        display_name,
                        clobber,
                        mmap_group="auto",
                        socket_permissions="600",
                        username="",
                        uid=0,
                        gid=0):
    if not bind:
        return []
    if not socket_dir and (not socket_dirs or
                           (len(socket_dirs) == 1 and not socket_dirs[0])):
        if WIN32:
            socket_dirs = [""]
        else:
            raise InitException(
                "at least one socket directory must be set to use unix domain sockets"
            )
    from xpra.platform.dotxpra import DotXpra, norm_makepath
    dotxpra = DotXpra(socket_dir or socket_dirs[0], socket_dirs, username, uid,
                      gid)
    if display_name is not None:
        display_name = normalize_local_display_name(display_name)
    log = get_network_logger()
    defs = []
    try:
        sockpaths = []
        log("setup_local_sockets: bind=%s", bind)
        for b in bind:
            sockpath = b
            if b in ("none", ""):
                continue
            elif b == "auto":
                assert display_name is not None
                sockpaths += dotxpra.norm_socket_paths(display_name)
                log("sockpaths(%s)=%s (uid=%i, gid=%i)", display_name,
                    sockpaths, uid, gid)
            else:
                sockpath = dotxpra.osexpand(b)
                if b.endswith("/") or (os.path.exists(sockpath)
                                       and os.path.isdir(sockpath)):
                    assert display_name is not None
                    sockpath = os.path.abspath(sockpath)
                    if not os.path.exists(sockpath):
                        os.makedirs(sockpath)
                    sockpath = norm_makepath(sockpath, display_name)
                elif os.path.isabs(b):
                    sockpath = b
                else:
                    sockpath = dotxpra.socket_path(b)
                sockpaths += [sockpath]
            assert sockpaths, "no socket paths to try for %s" % b
        #expand and remove duplicate paths:
        tmp = []
        for tsp in sockpaths:
            sockpath = dotxpra.osexpand(tsp)
            if sockpath in tmp:
                log.warn("Warning: skipping duplicate bind path %s", sockpath)
                continue
            tmp.append(sockpath)
        sockpaths = tmp
        #create listeners:
        if WIN32:
            from xpra.platform.win32.namedpipes.listener import NamedPipeListener
            for sockpath in sockpaths:
                npl = NamedPipeListener(sockpath)
                log.info("created named pipe '%s'", sockpath)
                defs.append(("named-pipe", npl, sockpath, npl.stop))
        else:

            def checkstate(sockpath, state):
                if state not in (DotXpra.DEAD, DotXpra.UNKNOWN):
                    if state == DotXpra.INACCESSIBLE:
                        raise InitException(
                            "An xpra server is already running at %s\n" %
                            (sockpath, ))
                    raise InitException(
                        "You already have an xpra server running at %s\n"
                        "  (did you want 'xpra upgrade'?)" % (sockpath, ))

            #remove exisiting sockets if clobber is set,
            #otherwise verify there isn't a server already running
            #and create the directories for the sockets:
            unknown = []
            for sockpath in sockpaths:
                if clobber and os.path.exists(sockpath):
                    os.unlink(sockpath)
                else:
                    state = dotxpra.get_server_state(sockpath, 1)
                    log("state(%s)=%s", sockpath, state)
                    checkstate(sockpath, state)
                    if state == dotxpra.UNKNOWN:
                        unknown.append(sockpath)
                d = os.path.dirname(sockpath)
                try:
                    kwargs = {}
                    if getuid(
                    ) == 0 and d == "/var/run/xpra" or d == "/run/xpra":
                        #this is normally done by tmpfiles.d,
                        #but we may need to do it ourselves in some cases:
                        kwargs = {"mode": 0o775}
                        xpra_gid = get_group_id("xpra")
                        if xpra_gid > 0:
                            kwargs["gid"] = xpra_gid
                    log("creating sockdir=%s, kwargs=%s" % (d, kwargs))
                    dotxpra.mksockdir(d, **kwargs)
                except Exception as e:
                    log.warn("Warning: failed to create socket directory '%s'",
                             d)
                    log.warn(" %s", e)
                    del e
            #wait for all the unknown ones:
            log("sockets in unknown state: %s", unknown)
            if unknown:
                #re-probe them using threads so we can do them in parallel:
                from time import sleep
                from xpra.make_thread import start_thread
                threads = []

                def timeout_probe(sockpath):
                    #we need a loop because "DEAD" sockets may return immediately
                    #(ie: when the server is starting up)
                    start = monotonic_time()
                    while monotonic_time() - start < WAIT_PROBE_TIMEOUT:
                        state = dotxpra.get_server_state(
                            sockpath, WAIT_PROBE_TIMEOUT)
                        log("timeout_probe() get_server_state(%s)=%s",
                            sockpath, state)
                        if state not in (DotXpra.UNKNOWN, DotXpra.DEAD):
                            break
                        sleep(1)

                log.warn(
                    "Warning: some of the sockets are in an unknown state:")
                for sockpath in unknown:
                    log.warn(" %s", sockpath)
                    t = start_thread(timeout_probe,
                                     "probe-%s" % sockpath,
                                     daemon=True,
                                     args=(sockpath, ))
                    threads.append(t)
                log.warn(
                    " please wait as we allow the socket probing to timeout")
                #wait for all the threads to do their job:
                for t in threads:
                    t.join(WAIT_PROBE_TIMEOUT + 1)
            if sockpaths:
                #now we can re-check quickly:
                #(they should all be DEAD or UNKNOWN):
                for sockpath in sockpaths:
                    state = dotxpra.get_server_state(sockpath, 1)
                    log("state(%s)=%s", sockpath, state)
                    checkstate(sockpath, state)
                    try:
                        if os.path.exists(sockpath):
                            os.unlink(sockpath)
                    except (OSError, IOError):
                        pass
                #socket permissions:
                if mmap_group.lower() in TRUE_OPTIONS:
                    #when using the mmap group option, use '660'
                    sperms = 0o660
                else:
                    #parse octal mode given as config option:
                    try:
                        if isinstance(socket_permissions, int):
                            sperms = socket_permissions
                        else:
                            #assume octal string:
                            sperms = int(socket_permissions, 8)
                        assert 0 <= sperms <= 0o777, "invalid socket permission value %s" % oct(
                            sperms)
                    except ValueError:
                        raise ValueError("invalid socket permissions " +
                                         "(must be an octal number): '%s'" %
                                         socket_permissions)
                #now try to create all the sockets:
                for sockpath in sockpaths:
                    #create it:
                    try:
                        sock, cleanup_socket = create_unix_domain_socket(
                            sockpath, sperms)
                        log.info("created unix domain socket '%s'", sockpath)
                        defs.append(
                            ("unix-domain", sock, sockpath, cleanup_socket))
                    except Exception as e:
                        handle_socket_error(sockpath, sperms, e)
                        del e
    except:
        for socktype, sock, sockpath, cleanup_socket in defs:
            try:
                cleanup_socket()
            except Exception as e:
                log.error("Error cleaning up socket %s:", sock)
                log.error(" %s", e)
                del e
        defs = []
        raise
    return defs
Ejemplo n.º 13
0
def get_ssl_wrap_socket_context(cert=None, key=None, ca_certs=None, ca_data=None,
                        protocol="TLSv1_2",
                        client_verify_mode="optional", server_verify_mode="required", verify_flags="X509_STRICT",
                        check_hostname=False, server_hostname=None,
                        options="ALL,NO_COMPRESSION", ciphers="DEFAULT",
                        server_side=True):
    if server_side and not cert:
        raise InitException("you must specify an 'ssl-cert' file to use ssl sockets")
    if server_side:
        verify_mode = client_verify_mode
    else:
        verify_mode = server_verify_mode
    from xpra.log import Logger
    ssllog = Logger("ssl")
    ssllog("get_ssl_wrap_socket_context%s", (cert, key, ca_certs, ca_data,
                                    protocol,
                                    client_verify_mode, server_verify_mode, verify_flags,
                                    check_hostname, server_hostname,
                                    options, ciphers,
                                    server_side))
    import ssl
    ssllog(" verify_mode for server_side=%s : %s", server_side, verify_mode)
    #ca-certs:
    if ca_certs=="default":
        ca_certs = None
    elif ca_certs=="auto":
        ca_certs = find_ssl_cert("ca-cert.pem")
    ssllog(" ca_certs=%s", ca_certs)
    #parse verify-mode:
    ssl_cert_reqs = getattr(ssl, "CERT_%s" % verify_mode.upper(), None)
    if ssl_cert_reqs is None:
        values = [k[len("CERT_"):].lower() for k in dir(ssl) if k.startswith("CERT_")]
        raise InitException("invalid ssl-server-verify-mode '%s', must be one of: %s" % (verify_mode, csv(values)))
    ssllog(" cert_reqs=%#x", ssl_cert_reqs)
    #parse protocol:
    proto = getattr(ssl, "PROTOCOL_%s" % (protocol.upper().replace("V", "v")), None)
    if proto is None:
        values = [k[len("PROTOCOL_"):] for k in dir(ssl) if k.startswith("PROTOCOL_")]
        raise InitException("invalid ssl-protocol '%s', must be one of: %s" % (protocol, csv(values)))
    ssllog(" protocol=%#x", proto)
    #ca_data may be hex encoded:
    ca_data = parse_encoded_bin_data(ca_data)
    ssllog(" cadata=%s", ellipsizer(ca_data))

    kwargs = {
              "server_side"             : server_side,
              "do_handshake_on_connect" : False,
              "suppress_ragged_eofs"    : True,
              }
    #parse ssl-verify-flags as CSV:
    ssl_verify_flags = 0
    for x in verify_flags.split(","):
        x = x.strip()
        if not x:
            continue
        v = getattr(ssl, "VERIFY_"+x.upper(), None)
        if v is None:
            raise InitException("invalid ssl verify-flag: %s" % x)
        ssl_verify_flags |= v
    ssllog(" verify_flags=%#x", ssl_verify_flags)
    #parse ssl-options as CSV:
    ssl_options = 0
    for x in options.split(","):
        x = x.strip()
        if not x:
            continue
        v = getattr(ssl, "OP_"+x.upper(), None)
        if v is None:
            raise InitException("invalid ssl option: %s" % x)
        ssl_options |= v
    ssllog(" options=%#x", ssl_options)

    context = ssl.SSLContext(proto)
    context.set_ciphers(ciphers)
    context.verify_mode = ssl_cert_reqs
    context.verify_flags = ssl_verify_flags
    context.options = ssl_options
    ssllog(" cert=%s, key=%s", cert, key)
    if cert:
        if cert=="auto":
            #try to locate the cert file from known locations
            cert = find_ssl_cert()
            if not cert:
                raise InitException("failed to automatically locate an SSL certificate to use")
        SSL_KEY_PASSWORD = os.environ.get("XPRA_SSL_KEY_PASSWORD")
        ssllog("context.load_cert_chain%s", (cert or None, key or None, SSL_KEY_PASSWORD))
        try:
            context.load_cert_chain(certfile=cert or None, keyfile=key or None, password=SSL_KEY_PASSWORD)
        except ssl.SSLError as e:
            ssllog("load_cert_chain", exc_info=True)
            raise InitException("SSL error, failed to load certificate chain: %s" % e) from e
    if ssl_cert_reqs!=ssl.CERT_NONE:
        if server_side:
            purpose = ssl.Purpose.CLIENT_AUTH   #@UndefinedVariable
        else:
            purpose = ssl.Purpose.SERVER_AUTH   #@UndefinedVariable
            context.check_hostname = check_hostname
            ssllog(" check_hostname=%s, server_hostname=%s", check_hostname, server_hostname)
            if context.check_hostname:
                if not server_hostname:
                    raise InitException("ssl error: check-hostname is set but server-hostname is not")
                kwargs["server_hostname"] = server_hostname
        ssllog(" load_default_certs(%s)", purpose)
        context.load_default_certs(purpose)

        if not ca_certs or ca_certs.lower()=="default":
            ssllog(" using default certs")
            #load_default_certs already calls set_default_verify_paths()
        elif not os.path.exists(ca_certs):
            raise InitException("invalid ssl-ca-certs file or directory: %s" % ca_certs)
        elif os.path.isdir(ca_certs):
            ssllog(" loading ca certs from directory '%s'", ca_certs)
            context.load_verify_locations(capath=ca_certs)
        else:
            ssllog(" loading ca certs from file '%s'", ca_certs)
            assert os.path.isfile(ca_certs), "'%s' is not a valid ca file" % ca_certs
            context.load_verify_locations(cafile=ca_certs)
        #handle cadata:
        if ca_data:
            #PITA: because of a bug in the ssl module, we can't pass cadata,
            #so we use a temporary file instead:
            import tempfile
            with tempfile.NamedTemporaryFile(prefix='cadata') as f:
                ssllog(" loading cadata '%s'", ellipsizer(ca_data))
                ssllog(" using temporary file '%s'", f.name)
                f.file.write(ca_data)
                f.file.flush()
                context.load_verify_locations(cafile=f.name)
    elif check_hostname and not server_side:
        ssllog("cannot check hostname client side with verify mode %s", verify_mode)
    return context, kwargs
Ejemplo n.º 14
0
def create_sockets(opts, error_cb, retry=0):
    bind_tcp = parse_bind_ip(opts.bind_tcp)
    bind_ssl = parse_bind_ip(opts.bind_ssl, 443)
    bind_ssh = parse_bind_ip(opts.bind_ssh, 22)
    bind_ws  = parse_bind_ip(opts.bind_ws, 80)
    bind_wss = parse_bind_ip(opts.bind_wss, 443)
    bind_rfb = parse_bind_ip(opts.bind_rfb, 5900)
    bind_vsock = parse_bind_vsock(opts.bind_vsock)

    min_port = int(opts.min_port)
    # Initialize the TCP sockets before the display,
    # That way, errors won't make us kill the Xvfb
    # (which may not be ours to kill at that point)
    ssh_upgrades = opts.ssh_upgrade
    if ssh_upgrades:
        try:
            from xpra.net.ssh import nogssapi_context
            with nogssapi_context():
                import paramiko
            assert paramiko
        except ImportError as e:
            from xpra.log import Logger
            sshlog = Logger("ssh")
            sshlog("import paramiko", exc_info=True)
            sshlog.error("Error: cannot enable SSH socket upgrades:")
            sshlog.error(" %s", e)
            ssh_upgrades = False
    log = get_network_logger()
    #prepare tcp socket definitions:
    tcp_defs = []
    for socktype, defs in {
        "tcp"   : bind_tcp,
        "ssl"   : bind_ssl,
        "ssh"   : bind_ssh,
        "ws"    : bind_ws,
        "wss"   : bind_wss,
        "rfb"   : bind_rfb,
        }.items():
        log("setting up %s sockets: %s", socktype, csv(defs.items()))
        for (host, iport), options in defs.items():
            if iport!=0 and iport<min_port:
                error_cb("invalid %s port number %i (minimum value is %i)" % (socktype, iport, min_port))
            for h in hosts(host):
                tcp_defs.append((socktype, h, iport, options, None))

    sockets = {}
    for attempt in range(retry+1):
        if not tcp_defs:
            break
        try_list = tuple(tcp_defs)
        tcp_defs = []
        for socktype, host, iport, options, _ in try_list:
            try:
                sock = setup_tcp_socket(host, iport, socktype)
            except Exception as e:
                log("setup_tcp_socket%s attempt=%s", (host, iport, options), attempt)
                tcp_defs.append((socktype, host, iport, options, e))
            else:
                host, iport = sock[2]
                sockets[sock] = options
        if tcp_defs:
            sleep(1)
    if tcp_defs:
        #failed to create some sockets:
        for socktype, host, iport, options, exception in tcp_defs:
            log.error("Error creating %s socket", socktype)
            log.error(" on %s:%s", host, iport)
            log.error(" %s", exception)
            raise InitException("failed to create %s socket: %s" % (socktype, exception))

    log("setting up vsock sockets: %s", csv(bind_vsock.items()))
    for (cid, iport), options in bind_vsock.items():
        sock = setup_vsock_socket(cid, iport)
        sockets[sock] = options

    # systemd socket activation:
    if POSIX and not OSX:
        try:
            from xpra.platform.xposix.sd_listen import get_sd_listen_sockets
        except ImportError as e:
            log("no systemd socket activation: %s", e)
        else:
            sd_sockets = get_sd_listen_sockets()
            log("systemd sockets: %s", sd_sockets)
            for stype, sock, addr in sd_sockets:
                sock = setup_sd_listen_socket(stype, sock, addr)
                sockets[sock] = {}
                log("%s : %s", (stype, [addr]), sock)
    return sockets