def start_dbus(dbus_launch): if not dbus_launch or dbus_launch.lower() in FALSE_OPTIONS: return 0, {} try: def preexec(): assert POSIX os.setsid() close_fds() proc = subprocess.Popen(dbus_launch, stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True, preexec_fn=preexec) out,_ = proc.communicate() assert proc.poll()==0, "exit code is %s" % proc.poll() #parse and add to global env: dbus_env = {} for l in bytestostr(out).splitlines(): parts = l.split("=", 1) if len(parts)!=2: continue k,v = parts if v.startswith("'") and v.endswith("';"): v = v[1:-2] dbus_env[k] = v dbus_pid = int(dbus_env.get("DBUS_SESSION_BUS_PID", 0)) return dbus_pid, dbus_env except Exception as e: get_util_logger().debug("start_dbus(%s)", dbus_launch, exc_info=True) error("dbus-launch failed to start using command '%s':\n" % dbus_launch) error(" %s\n" % e) return 0, {}
def init(): global bencode, bdecode, __version__ from xpra.util import envbool cython_bencode_loaded = False if envbool("XPRA_USE_CYTHON_BENCODE", True): try: from xpra.net.bencode.cython_bencode import ( bencode as cbencode, bdecode as cbdecode, __version__ as cversion, ) bencode = cbencode bdecode = cbdecode __version__ = cversion cython_bencode_loaded = True except ImportError as e: from xpra.os_util import get_util_logger get_util_logger().warn( "Warning: cannot load cython bencode module: %s", e) if not cython_bencode_loaded: from xpra.net.bencode.bencode import ( bencode as pbencode, bdecode as pbdecode, __version__ as pversion, ) bencode = pbencode bdecode = pbdecode __version__ = pversion
def has_uinput(): try: import uinput assert uinput except NameError as e: log = get_util_logger() log("has_uinput()", exc_info=True) log.warn("Warning: the system python uinput module looks broken:") log.warn(" %s", e) return False except ImportError as e: log = get_util_logger() log("has_uinput()", exc_info=True) log.info("cannot access python uinput module:") log.info(" %s", e) return False try: uinput.fdopen() #@UndefinedVariable except Exception as e: log = get_util_logger() log("has_uinput()", exc_info=True) log.info("cannot use uinput for virtual devices:") log.info(" %s", e) return False return True
def mksockdir(self, d, mode=0o700, uid=None, gid=None): if not d: return if not os.path.exists(d): if uid is None: uid = self.uid if gid is None: gid = self.gid parent = os.path.dirname(d) if parent and parent != "/" and not os.path.exists(parent): self.mksockdir(parent, mode, uid, gid) with umask_context(0): os.mkdir(d, mode) if uid != os.getuid() or gid != os.getgid(): os.lchown(d, uid, gid) elif d != "/tmp": try: st_mode = os.stat(d).st_mode if st_mode & 0o777 != mode: log = get_util_logger() log.warn("Warning: socket directory '%s'", d) log.warn(" expected permissions %s but found %s", oct(mode), oct(st_mode & 0o777)) except OSError: get_util_logger().log("mksockdir%s", (d, mode, uid, gid), exc_info=True)
def get_program_data_dir(): #ie: "C:\ProgramData" try: return sh_get_folder_path(CSIDL_COMMON_APPDATA) or "C:\\ProgramData" except Exception: get_util_logger().debug("get_program_data_dir()", exc_info=True) return "C:\\ProgramData"
def has_uinput(): if not envbool("XPRA_UINPUT", True): return False try: import uinput assert uinput except NameError as e: log = get_util_logger() log("has_uinput()", exc_info=True) log.warn("Warning: the system python uinput module looks broken:") log.warn(" %s", e) return False except ImportError as e: log = get_util_logger() log("has_uinput()", exc_info=True) log.info("no uinput module") return False try: uinput.fdopen() #@UndefinedVariable except Exception as e: log = get_util_logger() log("has_uinput()", exc_info=True) log.info("cannot use uinput for virtual devices,") log.info(" this is usually a permission issue:") log.info(" %s", e) return False return True
def get_posix_sys_info(): info = {} try: import resource for k, constant in { "server": "RUSAGE_SELF", "children": "RUSAGE_CHILDREN", "total": "RUSAGE_BOTH" }.items(): try: v = getattr(resource, constant) except (NameError, AttributeError): continue stats = resource.getrusage(v) minfo = info.setdefault("memory", {}).setdefault(k, {}) for var in ("utime", "stime", "maxrss", "ixrss", "idrss", "isrss", "minflt", "majflt", "nswap", "inblock", "oublock", "msgsnd", "msgrcv", "nsignals", "nvcsw", "nivcsw"): value = getattr(stats, "ru_%s" % var) if type(value) == float: value = int(value) minfo[var] = value except: from xpra.os_util import get_util_logger get_util_logger().error("Error getting memory usage info", exc_info=True) return info
def save_dbus_env(env): #DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-B8CDeWmam9,guid=b77f682bd8b57a5cc02f870556cbe9e9 #DBUS_SESSION_BUS_PID=11406 #DBUS_SESSION_BUS_WINDOWID=50331649 def u(s): try: return s.decode("latin1") except: return str(s) for n, conv, save in (("ADDRESS", u, _save_str), ("PID", int, _save_int), ("WINDOW_ID", int, _save_int)): k = "DBUS_SESSION_BUS_%s" % n v = env.get(k) if v is None: continue try: tv = conv(v) save(k, tv) except Exception as e: get_util_logger().debug("save_dbus_env(%s)", env, exc_info=True) error( "failed to save dbus environment variable '%s' with value '%s':\n" % (k, v)) error(" %s\n" % e)
def handle_signal(signum): try: sys.stderr.write("\n") sys.stderr.flush() get_util_logger().info("got signal %s", SIGNAMES.get(signum, signum)) except (IOError, OSError): pass callback(signum)
def get_program_data_dir(): #ie: "C:\ProgramData" try: buf = ctypes.create_unicode_buffer(ctypes.wintypes.MAX_PATH) SHGetFolderPath(0, CSIDL_COMMON_APPDATA, None, 0, buf) if buf.value: return buf.value except Exception: get_util_logger().debug("get_program_data_dir()", exc_info=True) return "C:\\ProgramData"
def write_signal(): if commandtype is None: return try: sys.stderr.write("\n") sys.stderr.flush() cstr = "" if commandtype: cstr = commandtype + " " get_util_logger().info("%sgot signal %s", cstr, signame) except OSError: pass
def handle_signal(signum): try: sys.stderr.write("\n") sys.stderr.flush() cstr = "" if commandtype: cstr = commandtype + " " get_util_logger().info("%sgot signal %s", cstr, SIGNAMES.get(signum, signum)) except OSError: pass callback(signum) return True
def do_get_download_dir(): try: #values found here: https://stackoverflow.com/a/48706260 import winreg #@UnresolvedImport @Reimport sub_key = r'SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders' downloads_guid = '{374DE290-123F-4565-9164-39C4925E467B}' with winreg.OpenKey(winreg.HKEY_CURRENT_USER, sub_key) as key: DOWNLOAD_PATH = winreg.QueryValueEx(key, downloads_guid)[0] except Exception: get_util_logger()("do_get_download_dir()", exc_info=True) #fallback to what the documentation says is the default: DOWNLOAD_PATH = os.path.join(os.environ.get("USERPROFILE", "~"), "My Documents", "Downloads") if not os.path.exists(DOWNLOAD_PATH): DOWNLOAD_PATH = os.path.join(os.environ.get("USERPROFILE", "~"), "Downloads") return DOWNLOAD_PATH
def create_uinput_devices(uinput_uuid, uid): log = get_util_logger() try: import uinput assert uinput except (ImportError, NameError) as e: log.error("Error: cannot access python uinput module:") log.error(" %s", e) return {} pointer = create_uinput_pointer_device(uinput_uuid, uid) touchpad = create_uinput_touchpad_device(uinput_uuid, uid) if not pointer and not touchpad: return {} def i(device): if not device: return {} name, uinput_pointer, dev_path = device return { "name" : name, "uinput" : uinput_pointer, "device" : dev_path, } return { "pointer" : i(pointer), "touchpad" : i(touchpad), }
def get_uinput_device_path(device): log = get_util_logger() try: log("get_uinput_device_path(%s)", device) fd = device._Device__uinput_fd log("fd(%s)=%s", device, fd) import fcntl #@UnresolvedImport import ctypes l = 16 buf = ctypes.create_string_buffer(l) _IOC_READ = 2 #this magic value was calculated using the C macros: l = fcntl.ioctl(fd, 2148554028, buf) if l>0 and l<16: virt_dev_path = buf.raw[:l].rstrip("\0") log("UI_GET_SYSNAME(%s)=%s", fd, virt_dev_path) uevent_path = b"/sys/devices/virtual/input/%s" % virt_dev_path event_dirs = [x for x in os.listdir(uevent_path) if x.startswith("event")] log("event dirs(%s)=%s", uevent_path, event_dirs) for d in event_dirs: uevent_filename = os.path.join(uevent_path, d, "uevent") uevent_conf = open(uevent_filename, "rb").read() for line in uevent_conf.splitlines(): if line.find(b"=")>0: k,v = line.split(b"=", 1) log("%s=%s", k, v) if k==b"DEVNAME": dev_path = b"/dev/%s" % v log("found device path: %s" % dev_path) return dev_path except Exception as e: log.error("Error: cannot query uinput device path:") log.error(" %s", e) return None
def print_DE_warnings(desktop_display, pulseaudio, notifications, dbus_launch): de = os.environ.get("XDG_SESSION_DESKTOP") or os.environ.get( "SESSION_DESKTOP") if not de: return warnings = [] log = get_util_logger() if pulseaudio is not False: try: xprop = subprocess.Popen( ["xprop", "-root", "-display", desktop_display], stdout=subprocess.PIPE) out, _ = xprop.communicate() for x in out.splitlines(): if x.startswith("PULSE_SERVER"): #found an existing pulseaudio server warnings.append("pulseaudio") break except: pass #don't care, this is just to decide if we show an informative warning or not if notifications and not dbus_launch: warnings.append("notifications") if warnings: log.warn("Warning: xpra start from an existing '%s' desktop session", de) log.warn(" %s forwarding may not work", " and ".join(warnings)) log.warn(" try using a clean environment, a dedicated user,") log.warn(" or disable xpra's %s option", " and ".join(['"%s"' % x for x in warnings]))
def get_listener_class(): from xpra.os_util import get_util_logger log = get_util_logger() log("mdns.get_listener_class() AVAHI=%s, ZEROCONF=%s", AVAHI, ZEROCONF) if AVAHI: try: from xpra.net.mdns.avahi_listener import AvahiListener log("AvahiListener=%s", AvahiListener) return AvahiListener except ImportError: log("failed to import AvahiListener", exc_info=True) if ZEROCONF: #workaround for MacOS Big Sur which broke ctypes, #ctypes is used in the ifaddr module which is imported by zeroconf: if sys.platform.startswith("darwin"): import xpra.platform #pylint: disable=import-outside-toplevel #on MacOS, an import side-effect is to patch the ctypes loader assert xpra.platform try: from xpra.net.mdns.zeroconf_listener import ZeroconfListener log("ZeroconfListener=%s", ZeroconfListener) return ZeroconfListener except (ImportError, OSError): log("failed to import ZeroconfListener", exc_info=True) return None
def create_uinput_device(uuid, uid, events, name): log = get_util_logger() import uinput BUS_USB = 0x03 #BUS_VIRTUAL = 0x06 VENDOR = 0xffff PRODUCT = 0x1000 #our xpra_udev_product_version script will use the version attribute to set #the udev OWNER value VERSION = uid try: device = uinput.Device(events, name=name, bustype=BUS_USB, vendor=VENDOR, product=PRODUCT, version=VERSION) except OSError as e: log("uinput.Device creation failed", exc_info=True) if os.getuid()==0: #running as root, this should work! log.error("Error: cannot open uinput,") log.error(" make sure that the kernel module is loaded") log.error(" and that the /dev/uinput device exists:") log.error(" %s", e) else: log.info("cannot access uinput: %s", e) return None dev_path = get_uinput_device_path(device) if not dev_path: device.destroy() return None return name, device, dev_path
def source_env(source=()) -> dict: log = get_util_logger() env = {} for f in source: e = env_from_sourcing(f) log("source_env %s=%s", f, e) env.update(e) return env
def kill_xvfb(xvfb_pid): if xvfb_pid: log = get_util_logger() log.info("killing xvfb with pid %s", xvfb_pid) try: os.kill(xvfb_pid, signal.SIGTERM) except OSError as e: log.info("failed to kill xvfb process with pid %s:", xvfb_pid) log.info(" %s", e)
def has_uinput(): try: import uinput assert uinput except (ImportError, NameError) as e: log = get_util_logger() log("has_uinput()", exc_info=True) log.info("cannot access python uinput module:") log.info(" %s", e) return False try: uinput.fdopen() except Exception as e: log = get_util_logger() log("has_uinput()", exc_info=True) log.info("cannot use uinput for virtual devices:") log.info(" %s", e) return False return True
def print_DE_warnings(): de = os.environ.get("XDG_SESSION_DESKTOP") or os.environ.get("SESSION_DESKTOP") if not de: return log = get_util_logger() log.warn("Warning: xpra start from an existing '%s' desktop session", de) log.warn(" without using dbus-launch,") log.warn(" notifications forwarding may not work") log.warn(" try using a clean environment, a dedicated user,") log.warn(" or disable xpra's notifications option")
def get_icc_info(): ENV_ICC_DATA = os.environ.get("XPRA_ICC_DATA") if ENV_ICC_DATA: import binascii return { "source": "environment-override", "data": binascii.unhexlify(ENV_ICC_DATA), } from xpra.os_util import get_util_logger, bytestostr log = get_util_logger() info = {} try: from PIL import ImageCms from PIL.ImageCms import get_display_profile, getDefaultIntent INTENT_STR = {} for x in ("PERCEPTUAL", "RELATIVE_COLORIMETRIC", "SATURATION", "ABSOLUTE_COLORIMETRIC"): v = getattr(ImageCms, "INTENT_%s" % x, None) if v: INTENT_STR[v] = x.lower().replace("_", "-") log("get_icc_info() intents=%s", INTENT_STR) def getDefaultIntentStr(_p): return INTENT_STR.get(getDefaultIntent(_p), "unknown") def getData(_p): return _p.tobytes() p = get_display_profile() log("get_icc_info() display_profile=%s", p) if p: for (k, fn) in { "name": "getProfileName", "info": "getProfileInfo", "copyright": "getProfileCopyright", "manufacturer": "getProfileManufacturer", "model": "getProfileModel", "description": "getProfileDescription", "default-intent": "getDefaultIntentStr", "data": "getData", }.items(): m = getattr(ImageCms, fn, None) if not m: log("%s lacks %s", ImageCms, fn) continue try: v = m(p) info[k] = bytestostr(v).rstrip("\n\r") except Exception as e: log("ICC profile error on %s using %s: %s", k, fn, e) except Exception as e: log("get_icc_info()", exc_info=True) log.warn("Warning: cannot query ICC profiles:") log.warn(" %s", e) return info
def impcheck(*modules): for mod in modules: try: __import__("xpra.%s" % mod, {}, {}, []) except ImportError: if mod not in impwarned: impwarned.append(mod) log = get_util_logger() log.warn("Warning: missing %s module", mod) return False return True
def do_get_desktop_background_paths(): try: import winreg #@UnresolvedImport @Reimport key_path = "Control Panel\\Desktop" key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, key_path, 0, winreg.KEY_READ) #@UndefinedVariable wallpaper = winreg.QueryValueEx(key, 'WallPaper')[0] #@UndefinedVariable return [wallpaper,] except Exception: log = get_util_logger() log("do_get_desktop_background_paths()", exc_info=True) return []
def create_uinput_pointer_device(uuid, uid): log = get_util_logger() try: import uinput except (ImportError, NameError) as e: log.error("Error: cannot access python uinput module:") log.error(" %s", e) return None events = ( uinput.REL_X, uinput.REL_Y, uinput.REL_WHEEL, #REL_HIRES_WHEEL = 0x10 #uinput.REL_HWHEEL, uinput.BTN_LEFT, uinput.BTN_RIGHT, uinput.BTN_MIDDLE, uinput.BTN_SIDE, uinput.BTN_EXTRA, uinput.BTN_FORWARD, uinput.BTN_BACK, ) BUS_USB = 0x03 #BUS_VIRTUAL = 0x06 VENDOR = 0xffff PRODUCT = 0x1000 #our xpra_udev_product_version script will use the version attribute to set #the udev OWNER value VERSION = uid name = "Xpra Virtual Pointer %s" % uuid try: uinput_pointer = uinput.Device(events, name=name, bustype=BUS_USB, vendor=VENDOR, product=PRODUCT, version=VERSION) except OSError as e: log("uinput.Device creation failed", exc_info=True) if os.getuid() == 0: #running as root, this should work! log.error("Error: cannot open uinput,") log.error(" make sure that the kernel module is loaded") log.error(" and that the /dev/uinput device exists:") log.error(" %s", e) else: log.info("cannot access uinput: %s", e) return None dev_path = get_uinput_device_path(uinput_pointer) if not dev_path: uinput_pointer.destroy() return None return name, uinput_pointer, dev_path
def kill_xvfb(xvfb_pid): log = get_util_logger() log.info("killing xvfb with pid %s", xvfb_pid) try: os.kill(xvfb_pid, signal.SIGTERM) except OSError as e: log.info("failed to kill xvfb process with pid %s:", xvfb_pid) log.info(" %s", e) from xpra.x11.vfb_util import PRIVATE_XAUTH xauthority = os.environ.get("XAUTHORITY") if PRIVATE_XAUTH and xauthority and os.path.exists(xauthority): os.unlink(xauthority)
def source_env(source=()) -> dict: log = get_util_logger() log("source_env(%s)", source) env = {} for f in source: if not f: continue e = env_from_sourcing(f) log("source_env %s=%s", f, e) env.update(e) log("source_env(%s)=%s", source, env) return env
def get_all_namedpipes(self): log = get_util_logger() xpra_pipes = {} for pipe_name in os.listdir(PIPE_PATH): if not pipe_name.startswith(PIPE_PREFIX): log("found non-xpra pipe: %s", pipe_name) continue name = pipe_name[len(PIPE_PREFIX):] #found an xpra pipe #FIXME: filter using matching_display? xpra_pipes[name] = pipe_name log("get_all_namedpipes()=%s", xpra_pipes) return xpra_pipes
def ival(key, default, minv=0, maxv=None) -> int: try: v = os.environ.get("XPRA_BATCH_%s" % key) if v is None: return default iv = int(v) assert minv is None or minv<=iv, "value for %s is too small: %s (minimum is %s)" % (key, iv, minv) assert maxv is None or maxv>=iv, "value for %s is too high: %s (maximum is %s)" % (key, iv, maxv) return iv except Exception as e: from xpra.os_util import get_util_logger log = get_util_logger() log.warn("failed to parse value '%s' for %s: %s", v, key, e) return default