def env_from_sourcing(file_to_source_path, include_unexported_variables=False): log = Logger("exec") cmd = shlex.split(file_to_source_path) filename = which(cmd[0]) if not filename: log.error("Error: cannot find command '%s' to execute", cmd[0]) log.error(" for sourcing '%s'", file_to_source_path) return {} if not os.path.isabs(filename): filename = os.path.abspath(filename) cmd[0] = filename #figure out if this is a script to source, #or if we're meant to execute it directly try: with open(filename, 'rb') as f: first_line = f.readline() except OSError as e: log.error("Error: failed to read from '%s'", filename) log.error(" %s", e) first_line = b"" else: log("first line of '%s': %r", filename, first_line) if first_line.startswith(b"\x7fELF") or b"\x00" in first_line: decode = decode_dict else: source = '%s. %s' % ("set -a && " if include_unexported_variables else "", filename) #ie: this is "python3.9 -c" on Posix #(but our 'Python_exec_cmd.exe' wrapper on MS Windows): python_cmd = " ".join(get_python_exec_command()) dump = '%s "import os, json;print(json.dumps(dict(os.environ)))"' % (python_cmd) sh = which("bash") or "/bin/sh" cmd = [sh, '-c', '%s 1>&2 && %s' % (source, dump)] decode = decode_json try: log("env_from_sourcing%s cmd=%s", (filename, include_unexported_variables), cmd) proc = Popen(cmd, stdout=PIPE, stderr=PIPE) out, err = proc.communicate() if proc.returncode!=0: log.error("Error %i running source script '%s'", proc.returncode, filename) except OSError as e: log("env_from_sourcing%s", (filename, include_unexported_variables), exc_info=True) log(" stdout=%r (%s)", out, type(out)) log.error("Error running source script '%s'", proc.returncode, file_to_source_path) log.error(" %s", e) return {} log("stdout(%s)=%r", filename, out) log("stderr(%s)=%r", filename, err) def proc_str(b, fdname="stdout"): try: return (b or b"").decode() except UnicodeDecodeError: log.error("Error decoding %s from '%s'", fdname, filename, exc_info=True) return "" env = {} env.update(decode(proc_str(out, "stdout"))) env.update(decode_dict(proc_str(err, "stderr"))) log("env_from_sourcing%s=%s", (file_to_source_path, include_unexported_variables), env) return env
def get_pactl_bin(): global pactl_bin if pactl_bin is None: if sys.platform.startswith("win") or sys.platform.startswith("darwin"): pactl_bin = "" else: pactl_bin = which("pactl") return pactl_bin
def get_pactl_bin(): global pactl_bin if pactl_bin is None: if WIN32 or OSX: pactl_bin = "" else: pactl_bin = which("pactl") return pactl_bin
def run_command(self, *_args): name = self.desktop_combo.get_active_text() cmd = name if cmd in self.desktop_sessions: session = self.desktop_sessions.get(cmd) #ie: # session={ # 'Type': '', # 'VersionString': '', # 'Name': 'Deepin', # 'GenericName': '', # 'NoDisplay': False, # 'Comment': 'Deepin Desktop Environment', # 'Icon': '', # 'Hidden': False, # 'OnlyShowIn': [], # 'NotShowIn': [], # 'Exec': '/usr/bin/startdde', # 'TryExec': '/usr/bin/startdde', # 'Path': '', # 'Terminal': False, # 'MimeTypes': [], # 'Categories': [], # 'StartupNotify': False, # 'StartupWMClass': '', # 'URL': '', # 'command': '/usr/bin/startdde', # 'IconData': b'<svg>...</svg>\n', # 'IconType': 'svg' # } for k in ("command", "Exec", "TryExec"): cmd = session.get(k) if cmd: break if cmd and not os.path.isabs(cmd): cmd = which(cmd) if not cmd: log.warn("no command found for '%s'", name) return argv = [cmd] self.destroy() os.execv(cmd, argv)
def get_pinentry_command(setting="yes"): log("get_pinentry_command(%s)", setting) if setting.lower() in FALSE_OPTIONS: return None def find_pinentry_bin(): if is_gnome(): return which("pinentry-gnome3") if is_kde(): return which("pinentry-qt") return None if setting.lower() in TRUE_OPTIONS: return find_pinentry_bin() or which("pinentry") if setting == "" or setting.lower() == "auto": #figure out if we should use it: if WIN32 or OSX: #not enabled by default on those platforms return None return find_pinentry_bin() return setting
def test_dbus_interface(self): if not POSIX or OSX: return dbus_send = which("dbus-send") if not dbus_send: print("Warning: dbus test skipped, 'dbus-send' not found") return display, xvfb, server = self.start_shadow_server("-d", "dbus") info = self.get_server_info(xvfb.display) assert info tinfo = typedict(info) idisplay = tinfo.strget("server.display") assert idisplay == display, "expected display '%s' in info, but got '%s'" % ( display, idisplay) rd = tinfo.intget("refresh-delay", 0) assert rd == REFRESH_DELAY, "expected refresh-delay=%i, got %i" % ( REFRESH_DELAY, rd) dstr = display.lstrip(":") new_delay = 2 cmd = [ dbus_send, "--session", "--type=method_call", "--dest=org.xpra.Server%s" % dstr, "/org/xpra/Server", "org.xpra.Server.SetRefreshDelay", "int32:%i" % new_delay ] env = self.get_run_env() env["DISPLAY"] = display self.run_command(cmd, env=env).wait(20) #check that the value has changed: info = self.get_server_info(display) assert info tinfo = typedict(info) assert tinfo.strget("server.display") == display rd = tinfo.intget("refresh-delay", 0) assert rd == new_delay, "expected refresh-delay=%i, got %i" % ( new_delay, rd) self.stop_shadow_server(xvfb, server)
def test_dbus(self): dbus_send = which("dbus-send") if not dbus_send: print("Warning: dbus test skipped, 'dbus-send' not found") return #make sure this is recorded, #so kill the xvfb *after* we exit display, xvfb, server = self.start_test_xvfb_and_server("--start=xterm", "--start=xterm", "-d", "dbus") #2 windows info = self.get_server_info(display) assert info tinfo = typedict(info) dstr = display.lstrip(":") env = self.get_run_env() env["DISPLAY"] = display #use the dbus environment from the server: for k,v in tinfo.items(): if k.startswith("env.DBUS_"): env[k[4:]] = v #unchecked calls: for args in ( ("org.xpra.Server.Focus", "int32:1"), #("org.xpra.Server.Suspend", ), #hangs? ("org.xpra.Server.Resume", ), ("org.xpra.Server.Ungrab", ), ("org.xpra.Server.Start", "string:xterm"), ("org.xpra.Server.StartChild", "string:xterm"), ("org.xpra.Server.EnableDebug", "string:keyboard"), ("org.xpra.Server.KeyPress", "int32:36"), #Return key with default layout? ("org.xpra.Server.KeyRelease", "int32:36"), #Return key ("org.xpra.Server.ClearKeysPressed", ), ("org.xpra.Server.SetKeyboardRepeat", "int32:200", "int32:50"), ("org.xpra.Server.DisableDebug", "string:keyboard"), ("org.xpra.Server.ListClients", ), ("org.xpra.Server.DetachClient", "string:nomatch"), ("org.xpra.Server.DetachAllClients", ), ("org.xpra.Server.GetAllInfo", ), ("org.xpra.Server.GetInfo", "string:keyboard"), ("org.xpra.Server.ListWindows", ), ("org.xpra.Server.LockBatchDelay", "int32:1", "int32:10"), ("org.xpra.Server.UnlockBatchDelay", "int32:1"), ("org.xpra.Server.MovePointer", "int32:1", "int32:100", "int32:100"), ("org.xpra.Server.MouseClick", "int32:1", "boolean:true"), ("org.xpra.Server.MouseClick", "int32:1", "boolean:false"), ("org.xpra.Server.MoveWindowToWorkspace", "int32:1", "int32:2"), ("org.xpra.Server.RefreshAllWindows", ), ("org.xpra.Server.RefreshWindow", "int32:1"), ("org.xpra.Server.RefreshWindows", "array:int32:1:2:3"), ("org.xpra.Server.ResetVideoRegion", "int32:1"), ("org.xpra.Server.ResetWindowFilters", ), ("org.xpra.Server.ResetXSettings", ), ("org.xpra.Server.SendNotification", "int32:1", "string:title", "string:message", "string:*"), ("org.xpra.Server.CloseNotification", "int32:1", "string:*"), ("org.xpra.Server.SendUIClientCommand", "array:string:test"), ("org.xpra.Server.SetClipboardProperties", "string:direction", "int32:1000", "int32:1000"), ("org.xpra.Server.SetIdleTimeout", "int32:100"), ("org.xpra.Server.SetLock", "string:true"), ("org.xpra.Server.SetLock", "string:false"), ("org.xpra.Server.SetLock", "string:auto"), ("org.xpra.Server.SetSharing", "string:no"), ("org.xpra.Server.SetScreenSize", "int32:1024", "int32:768"), ("org.xpra.Server.SetUIDriver", "string:invalid"), ("org.xpra.Server.SetVideoRegion", "int32:1", "int32:10", "int32:10", "int32:100", "int32:100"), ("org.xpra.Server.SetVideoRegionDetection", "int32:1", "boolean:true"), ("org.xpra.Server.SetVideoRegionDetection", "int32:1", "boolean:false"), ("org.xpra.Server.SetVideoRegionEnabled", "int32:1", "boolean:true"), ("org.xpra.Server.SetVideoRegionEnabled", "int32:1", "boolean:false"), #("org.xpra.Server.SetVideoRegionExclusionZones", ... ("org.xpra.Server.SetWindowEncoding", "int32:1", "string:png"), ("org.xpra.Server.SetWindowScaling", "int32:1", "string:200%"), ("org.xpra.Server.SetWindowScalingControl", "int32:1", "string:auto"), ("org.xpra.Server.SetWindowScalingControl", "int32:1", "string:50"), ("org.xpra.Server.SetWorkarea", "int32:10", "int32:10", "int32:1000", "int32:740"), ("org.xpra.Server.ShowAllWindows", ), ("org.xpra.Server.SyncXvfb", ), ("org.xpra.Server.SetDPI", "int32:144", "int32:144"), ("org.xpra.Server.ToggleFeature", "string:bell", "string:off"), #generic dbus server queries: ("org.xpra.Server.Get", "string:session_name"), ("org.xpra.Server.GetAll", ), ("org.xpra.Server.Set", "string:session_name", "string:foo"), ): cmd = [dbus_send, "--session", "--type=method_call", "--dest=org.xpra.Server%s" % dstr, "/org/xpra/Server"] + list(args) proc = self.run_command(cmd, env=env) assert pollwait(proc, 20) is not None, "dbus-send is taking too long: %s" % (cmd,) #properties using interface org.freedesktop.DBus.Properties: for args in ( ("Get", "string:idle-timeout"), ("Get", "string:does-not-exist"), ("GetAll", ), ("Set", "string:idle-timeout", "int32:20"), ("Set", "string:does-not-exist", "string:irrelevant"), ): cmd = [dbus_send, "--session", "--type=method_call", "--print-reply", "--dest=org.xpra.Server%s" % dstr, "/org/xpra/Server", "org.freedesktop.DBus.Properties.%s" % args[0], "string:org.xpra.Server%s" % dstr ] + list(args[1:]) proc = self.run_command(cmd, env=env) assert pollwait(proc, 20) is not None, "dbus-send is taking too long: %s" % (cmd,) self.check_stop_server(server, "exit", display) xvfb.terminate()
def has_xclip(): return which("xclip")
def find_pinentry_bin(): if is_gnome(): return which("pinentry-gnome3") if is_kde(): return which("pinentry-qt") return None
def env_from_sourcing(file_to_source_path, include_unexported_variables=False): import json import subprocess from xpra.log import Logger log = Logger("exec") cmd = shlex.split(file_to_source_path) filename = which(cmd[0]) if not filename: log.error("Error: cannot find command '%s' to execute", cmd[0]) log.error(" for sourcing '%s'", file_to_source_path) return {} if not os.path.isabs(filename): filename = os.path.abspath(filename) cmd[0] = filename #figure out if this is a script to source, #or if we're meant to execute it directly try: with open(filename, 'rb') as f: first_line = f.readline() except OSError as e: log.error("Error: failed to read from '%s'", filename) log.error(" %s", e) first_line = b"" else: log("first line of '%s': %r", filename, first_line) if first_line.startswith(b"\x7fELF") or b"\x00" in first_line: def decode(out): env = {} for line in out.splitlines(): parts = line.split("=", 1) if len(parts)==2: env[parts[0]] = parts[1] log("decode(%r)=%s", out, env) return env else: source = '%ssource %s' % ("set -a && " if include_unexported_variables else "", filename) dump = 'python%i.%i -c "import os, json;print(json.dumps(dict(os.environ)))"' % (sys.version_info.major, sys.version_info.minor) cmd = ['/bin/bash', '-c', '%s && %s' % (source, dump)] def decode(out): try: env = json.loads(out) log("json loads(%r)=%s", out, env) return env except json.decoder.JSONDecodeError: log.error("Error decoding json output from sourcing script '%s'", file_to_source_path, exc_info=True) return {} try: log("env_from_sourcing%s cmd=%s", (filename, include_unexported_variables), cmd) proc = subprocess.Popen(cmd, stdout=subprocess.PIPE) out = proc.communicate()[0] if proc.returncode!=0: log.error("Error %i running source script '%s'", proc.returncode, filename) except OSError as e: log("env_from_sourcing%s", (filename, include_unexported_variables), exc_info=True) log(" stdout=%r (%s)", out, type(out)) log.error("Error running source script '%s'", proc.returncode, file_to_source_path) log.error(" %s", e) return {} log("stdout(%s)=%r", filename, out) if not out: return {} try: out = out.decode() except UnicodeDecodeError: log.error("Error decoding output from '%s'", filename, exc_info=True) return {} return decode(out)
def has_xclip(): return bool(which("xclip"))
def _test(self, subcommand, options): log("starting test server with options=%s", options) args = ["--%s=%s" % (k, v) for k, v in options.items()] tcp_port = None xvfb = None if WIN32 or OSX: display = "" connect_args = [] elif self.display: display = self.display connect_args = [display] args.append("--use-display=yes") else: display = self.find_free_display() connect_args = [display] if subcommand == "shadow": xvfb = self.start_Xvfb(display) if TEST_RFB or WIN32: tcp_port = get_free_tcp_port() args += ["--bind-tcp=0.0.0.0:%i" % tcp_port] if WIN32: connect_args = ["tcp://127.0.0.1:%i" % tcp_port] server = None client = None rfb_client = None gui_client = None try: log("args=%s", " ".join("'%s'" % x for x in args)) server = self.check_server(subcommand, display, *args) #we should always be able to get the version: client = self.run_xpra(["version"] + connect_args) assert pollwait( client, 5 ) == 0, "version client failed to connect to server with args=%s" % args #run info query: cmd = ["info"] + connect_args client = self.run_xpra(cmd) r = pollwait(client, 20) assert r==0, "info client failed and returned %s: '%s' for server with args=%s" % \ (r, EXIT_STR.get(r, r), args) client_kwargs = {} if not (WIN32 or OSX): env = self.get_run_env() env["DISPLAY"] = self.client_display client_kwargs = {"env": env} if subcommand in ("shadow", "start-desktop") and TEST_RFB and options.get( "windows", True): vncviewer = which("vncviewer") log("testing RFB clients with vncviewer '%s'", vncviewer) if vncviewer: rfb_cmd = [vncviewer, "localhost::%i" % tcp_port] rfb_client = self.run_command(rfb_cmd, **client_kwargs) r = pollwait(rfb_client, 10) if r is not None: self.show_proc_error( rfb_client, "rfb client terminated early and returned %i for server with args=%s" % (r, args)) #connect a gui client: if WIN32 or (self.client_display and self.client_xvfb): xpra_args = [ "attach", "--clipboard=no", #could create loops "--notifications=no", #may get sent to the desktop session running the tests! ] + connect_args gui_client = self.run_xpra(xpra_args, **client_kwargs) r = pollwait(gui_client, 10) if r is not None: self.show_proc_error( gui_client, "gui client terminated early and returned %i : '%s' for server with args=%s" % (r, EXIT_STR.get(r, r), args)) if self.display: self.stop_server(server, "exit", *connect_args) else: self.stop_server(server, "stop", *connect_args) if display: if display in self.dotxpra.displays(): log.warn( "server socket for display %s should have been removed", display) if gui_client: r = pollwait(gui_client, 20) if r is None: log.warn("client still connected!") self.show_proc_pipes(server) raise Exception("gui client should have been disconnected") except Exception: log.error("test error for '%s' subcommand with options=%s", subcommand, options) raise finally: for x in (xvfb, rfb_client, gui_client, server, client): try: if x and x.poll() is None: x.terminate() except OSError: log("%s.terminate()", exc_info=True)