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
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