Exemple #1
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
Exemple #2
0
def setup_local_sockets(bind,
                        socket_dir,
                        socket_dirs,
                        display_name,
                        clobber,
                        mmap_group="auto",
                        socket_permissions="600",
                        username="",
                        uid=0,
                        gid=0):
    log = get_network_logger()
    log("setup_local_sockets%s",
        (bind, socket_dir, socket_dirs, display_name, clobber, mmap_group,
         socket_permissions, username, uid, gid))
    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 InitExit(
                EXIT_SOCKET_CREATION_ERROR,
                "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 and not WIN32:
        display_name = normalize_local_display_name(display_name)
    defs = {}
    try:
        sockpaths = {}
        log("setup_local_sockets: bind=%s, dotxpra=%s", bind, dotxpra)
        for b in bind:
            if b in ("none", ""):
                continue
            parts = b.split(",")
            sockpath = parts[0]
            options = {}
            if len(parts) == 2:
                options = parse_simple_dict(parts[1])
            if sockpath == "auto":
                assert display_name is not None
                for sockpath in dotxpra.norm_socket_paths(display_name):
                    sockpaths[sockpath] = options
                log("sockpaths(%s)=%s (uid=%i, gid=%i)", display_name,
                    sockpaths, uid, gid)
            else:
                sockpath = dotxpra.osexpand(sockpath)
                if os.path.isabs(sockpath):
                    pass
                elif sockpath.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)
                else:
                    sockpath = dotxpra.socket_path(sockpath)
                sockpaths[sockpath] = options
            assert sockpaths, "no socket paths to try for %s" % b
        #expand and remove duplicate paths:
        tmp = {}
        for tsp, options in sockpaths.items():
            sockpath = dotxpra.osexpand(tsp)
            if sockpath in tmp:
                log.warn("Warning: skipping duplicate bind path %s", sockpath)
                continue
            tmp[sockpath] = options
        sockpaths = tmp
        log("sockpaths=%s", sockpaths)
        #create listeners:
        if WIN32:
            from xpra.platform.win32.namedpipes.listener import NamedPipeListener
            from xpra.platform.win32.dotxpra import PIPE_PATH
            for sockpath, options in sockpaths.items():
                npl = NamedPipeListener(sockpath)
                ppath = sockpath
                if ppath.startswith(PIPE_PATH):
                    ppath = ppath[len(PIPE_PATH):]
                log.info("created named pipe '%s'", ppath)
                defs[("named-pipe", npl, sockpath, npl.stop)] = options
        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 InitExit(
                        EXIT_SERVER_ALREADY_EXISTS,
                        "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 d in ("/var/run/xpra", "/run/xpra"):
                        #this is normally done by tmpfiles.d,
                        #but we may need to do it ourselves in some cases:
                        kwargs["mode"] = SOCKET_DIR_MODE
                        xpra_gid = get_group_id(SOCKET_DIR_GROUP)
                        if xpra_gid > 0:
                            kwargs["gid"] = xpra_gid
                    log("creating sockdir=%s, kwargs=%s" % (d, kwargs))
                    dotxpra.mksockdir(d, **kwargs)
                    log("%s permission mask: %s", d, oct(os.stat(d).st_mode))
                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:
                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:
                        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) from None
                #now try to create all the sockets:
                for sockpath, options in sockpaths.items():
                    #create it:
                    try:
                        sock, cleanup_socket = create_unix_domain_socket(
                            sockpath, sperms)
                        log.info("created unix domain socket '%s'", sockpath)
                        defs[("unix-domain", sock, sockpath,
                              cleanup_socket)] = options
                    except Exception as e:
                        handle_socket_error(sockpath, sperms, e)
                        del e
    except Exception:
        for sock, cleanup_socket in defs.items():
            try:
                cleanup_socket()
            except Exception as e:
                log.error("Error cleaning up socket %s:", sock)
                log.error(" %s", e)
                del e
        raise
    return defs