class ChildReaper(object): def __init__(self, quit_cb, children_pids={}): self._quit = quit_cb self._children_pids = children_pids self._dead_pids = set() from xpra.log import Logger self._logger = Logger() def check(self): if self._children_pids: pids = set(self._children_pids.keys()) if pids.issubset(self._dead_pids): self._quit() def sigchld(self, signum, frame): self.reap() def add_dead_pid(self, pid): if pid not in self._dead_pids: cmd = self._children_pids.get(pid) if cmd: self._logger.info("child '%s' with pid %s has terminated", cmd, pid) self._dead_pids.add(pid) self.check() def reap(self): while True: try: pid, _ = os.waitpid(-1, os.WNOHANG) except OSError: break if pid == 0: break self.add_dead_pid(pid)
def PIL_logging_workaround(): import logging PIL_DEBUG = os.environ.get("XPRA_PIL_DEBUG", "0") == "1" if PIL_DEBUG: from xpra.log import Logger log = Logger("util") log.info("enabling PIL.DEBUG") level = logging.DEBUG else: level = logging.INFO # newer versions use this logger, # we must initialize it before we load the class: for x in ("Image", "PngImagePlugin", "WebPImagePlugin", "JpegImagePlugin"): logger = logging.getLogger("PIL.%s" % x) logger.setLevel(level) import PIL from PIL import Image assert PIL and Image if hasattr(Image, "DEBUG"): # for older versions (pre 3.0), use Image.DEBUG flag: Image.DEBUG = int(PIL_DEBUG)
def start_children(child_reaper, cwd, commands, fake_xinerama): assert os.name == "posix" from xpra.log import Logger log = Logger("server") env = os.environ.copy() # add fake xinerama: if fake_xinerama: libfakeXinerama_so = find_fakeXinerama() if libfakeXinerama_so: env["LD_PRELOAD"] = libfakeXinerama_so # disable ubuntu's global menu using env vars: env.update( { "UBUNTU_MENUPROXY": "", "QT_X11_NO_NATIVE_MENUBAR": "1", "MWNOCAPTURE": "true", "MWNO_RIT": "true", "MWWM": "allwm", } ) for child_cmd in commands: if not child_cmd: continue try: proc = subprocess.Popen(child_cmd, stdin=subprocess.PIPE, env=env, cwd=cwd, shell=True, close_fds=True) child_reaper.add_process(proc, child_cmd) log.info("started child '%s' with pid %s", child_cmd, proc.pid) except OSError, e: sys.stderr.write("Error spawning child '%s': %s\n" % (child_cmd, e))
def do_log_screen_sizes(root_w, root_h, ss): from xpra.log import Logger log = Logger() log.info("root size is %sx%s with %s screen(s):", root_w, root_h, len(ss)) def prstr(s, default=""): if not s: return default #prettify strings on win32 return s.lstrip("0\\").lstrip(".\\").replace("0\\", "-") #old format, used by some clients (android): if len(ss)==2 and type(ss[0])==int and type(ss[1])==int: return for s in ss: if len(s)<10: log.info(" %s", s) continue #more detailed output: display_name, width, height, width_mm, height_mm, \ monitors, work_x, work_y, work_width, work_height = s[:11] log.info(" '%s' %sx%s (%sx%s mm) workarea: %sx%s at %sx%s", prstr(display_name), width, height, width_mm, height_mm, work_width, work_height, work_x, work_y) i = 0 for m in monitors: i += 1 if len(m)<7: log.info(" %s", m) continue plug_name, x, y, width, height, wmm, hmm = m[:8] log.info(" '%s' %sx%s at %sx%s (%sx%s mm)", prstr(plug_name, "monitor %s" % i), width, height, x, y, wmm, hmm)
def start_children(child_reaper, commands, fake_xinerama): assert os.name == "posix" from xpra.log import Logger log = Logger("server") env = os.environ.copy() #add fake xinerama: if fake_xinerama: libfakeXinerama_so = find_fakeXinerama() if libfakeXinerama_so: env["LD_PRELOAD"] = libfakeXinerama_so #disable ubuntu's global menu using env vars: env.update({"UBUNTU_MENUPROXY": "", "QT_X11_NO_NATIVE_MENUBAR": "1"}) for child_cmd in commands: if not child_cmd: continue try: proc = subprocess.Popen(child_cmd, stdin=subprocess.PIPE, env=env, shell=True, close_fds=True) child_reaper.add_process(proc, child_cmd) log.info("started child '%s' with pid %s", child_cmd, proc.pid) except OSError, e: sys.stderr.write("Error spawning child '%s': %s\n" % (child_cmd, e))
def test_remotelogging(self): x = RemoteLogging() opts = AdHocStruct() opts.remote_logging = "yes" x.init(opts) assert x.get_caps() is not None x.server_capabilities = typedict({ "remote-logging" : True, }) x.parse_server_capabilities() packets = [] def send(*args): packets.append(args) x.send = send from xpra.log import Logger log = Logger("util") message = b"hello" log.info(message) assert len(packets)==1 packet = packets[0] assert packet[0]=="logging", "expected logging packet but got '%s'" % (packet[0],) assert packet[1]==20, "expected INFO level (20) but got %s" % (packet[1],) assert packet[2].data==message, "expected message '%s' but got '%s'" % (message, packet[2].data) #after cleanup, log messages should not be intercepted: x.cleanup() log.info("foo") assert len(packets)==1
def find_ssl_cert(filename="ssl-cert.pem"): from xpra.log import Logger ssllog = Logger("ssl") #try to locate the cert file from known locations from xpra.platform.paths import get_ssl_cert_dirs #pylint: disable=import-outside-toplevel dirs = get_ssl_cert_dirs() ssllog("find_ssl_cert(%s) get_ssl_cert_dirs()=%s", filename, dirs) for d in dirs: p = osexpand(d) if not os.path.exists(p): ssllog("ssl cert dir '%s' does not exist", p) continue f = os.path.join(p, "ssl-cert.pem") if not os.path.exists(f): ssllog("ssl cert '%s' does not exist", f) continue if not os.path.isfile(f): ssllog.warn("Warning: '%s' is not a file", f) continue if not os.access(p, os.R_OK): ssllog.info("SSL certificate file '%s' is not accessible", f) continue ssllog("found ssl cert '%s'", f) return f return None
def test_remotelogging(self): from xpra.log import Logger, is_debug_enabled for x in ("network", "crypto", "udp"): if is_debug_enabled(x): #remote logging will be disabled, #so we have to skip this test return opts = AdHocStruct() opts.remote_logging = "yes" self._test_mixin_class(RemoteLogging, opts, { "remote-logging" : True, }) assert len(self.packets)==0 log = Logger("util") message = b"hello" log.info(message) assert len(self.packets)==1 packet = self.packets[0] assert packet[0]=="logging", "expected logging packet but got '%s'" % (packet[0],) assert packet[1]==20, "expected INFO level (20) but got %s" % (packet[1],) assert packet[2].data==message, "expected message '%s' but got '%s'" % (message, packet[2].data) #after cleanup, log messages should not be intercepted: self.packets = [] self.mixin.cleanup() log.info("foo") assert len(self.packets)==0
def may_notify(self, nid, summary, body, *args, **kwargs): notifylog = Logger("notify") notifylog("may_notify(%s, %s, %s, %s, %s)", nid, summary, body, args, kwargs) notifylog.info("%s", summary) if body: for x in body.splitlines(): notifylog.info(" %s", x)
def ssl_handshake(ssl_sock): from xpra.log import Logger ssllog = Logger("ssl") try: ssl_sock.do_handshake(True) ssllog.info("SSL handshake complete, %s", ssl_sock.version()) log_ssl_info(ssl_sock) except Exception as e: ssllog("do_handshake", exc_info=True) log_ssl_info(ssl_sock) import ssl 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): verify_code = getattr(e, "verify_code", 0) ssllog("verify_code=%s", SSL_VERIFY_CODES.get(verify_code, verify_code)) try: msg = getattr(e, "verify_message") or (e.args[1].split(":", 2)[2]) except (ValueError, IndexError): msg = str(e) status = EXIT_SSL_CERTIFICATE_VERIFY_FAILURE ssllog("host failed SSL verification: %s", msg) raise SSLVerifyFailure(status, msg, verify_code, ssl_sock) from None raise InitExit(status, "SSL handshake failed: %s" % str(e)) from None return ssl_sock
def start_children(child_reaper, commands, fake_xinerama): assert os.name=="posix" from xpra.log import Logger log = Logger() env = os.environ.copy() #add fake xinerama: if fake_xinerama: #locate the fakeXinerama lib: #it would be better to rely on dlopen to find the paths #but I cannot find a way of getting ctypes to tell us the path #it found the library in libpaths = os.environ.get("LD_LIBRARY_PATH", "").split(":") libpaths.append("/usr/lib64") libpaths.append("/usr/lib") for libpath in libpaths: if not libpath or not os.path.exists(libpath): continue libfakeXinerama_so = "%s/%s" % (libpath, "libfakeXinerama.so.1") if os.path.exists(libfakeXinerama_so): env["LD_PRELOAD"] = libfakeXinerama_so #disable ubuntu's global menu using env vars: env.update({ "UBUNTU_MENUPROXY" : "", "QT_X11_NO_NATIVE_MENUBAR" : "1"}) for child_cmd in commands: if not child_cmd: continue try: proc = subprocess.Popen(child_cmd, stdin=subprocess.PIPE, env=env, shell=True, close_fds=True) child_reaper.add_process(proc, child_cmd) log.info("started child '%s' with pid %s", child_cmd, proc.pid) except OSError, e: sys.stderr.write("Error spawning child '%s': %s\n" % (child_cmd, e))
class console_event_catcher(object): def __init__(self, event_cb, events=CONSOLE_EXIT_EVENTS): self.event_cb = event_cb self.events = events self.result = 0 from xpra.log import Logger self.log = Logger("win32") def __enter__(self): try: self.result = win32api.SetConsoleCtrlHandler( self.handle_console_event, 1) if self.result == 0: self.log.error("could not SetConsoleCtrlHandler (error %r)", win32api.GetLastError()) except Exception as e: self.log.error("SetConsoleCtrlHandler error: %s", e) def __exit__(self, exc_type, exc_val, exc_tb): try: win32api.SetConsoleCtrlHandler(None, 0) except: pass def __repr__(self): return "console_event_catcher(%s, %s)" % (self.event_cb, self.events) def handle_console_event(self, event): self.log("handle_console_event(%s)", event) if event in self.events: self.log.info("received console event %s", event) self.event_cb(event)
class console_event_catcher(object): def __init__(self, event_cb, events=CONSOLE_EXIT_EVENTS): self.event_cb = event_cb self.events = events self.result = 0 from xpra.log import Logger self.log = Logger("win32") def __enter__(self): try: self.result = win32api.SetConsoleCtrlHandler(self.handle_console_event, 1) if self.result == 0: self.log.error("could not SetConsoleCtrlHandler (error %r)", win32api.GetLastError()) except Exception as e: self.log.error("SetConsoleCtrlHandler error: %s", e) def __exit__(self, exc_type, exc_val, exc_tb): try: win32api.SetConsoleCtrlHandler(None, 0) except: pass def __repr__(self): return "console_event_catcher(%s, %s)" % (self.event_cb, self.events) def handle_console_event(self, event): self.log("handle_console_event(%s)", event) if event in self.events: self.log.info("received console event %s", event) self.event_cb(event)
def handshake_complete(*args): from xpra.log import Logger log = Logger() if mode == "detach": log.info("handshake-complete: detaching") app.quit(0) elif mode == "attach": log.info("Attached to %s (press Control-C to detach)\n" % conn.target)
def main(): from xpra.platform import program_context, command_error from xpra.platform.gui import init, set_default_icon with program_context("Webcam", "Webcam"): from xpra.log import Logger, add_debug_category log = Logger("webcam") for x in list(sys.argv): if x in ("-v", "--verbose"): sys.argv.remove(x) add_debug_category("webcam") log.enable_debug() set_default_icon("webcam.png") init() log("importing opencv") try: import cv2 except ImportError as e: command_error("Error: no opencv support module: %s" % e) return 1 log("cv2=%s", cv2) device = 0 if len(sys.argv) == 2: try: device = int(sys.argv[1]) except ValueError: command_error( "Warning: failed to parse value as a device number: '%s'" % sys.argv[1]) log("opening %s with device=%s", cv2.VideoCapture, device) # @UndefinedVariable try: cap = cv2.VideoCapture(device) # @UndefinedVariable except Exception as e: command_error( "Error: failed to capture video using device %s:\n%s" % (device, e)) return 1 log.info("capture device for %i: %s", device, cap) while True: ret, frame = cap.read() if not ret: command_error("Error: frame capture failed using device %s" % device) return 1 cv2.imshow('frame', frame) # @UndefinedVariable if cv2.waitKey(10) & 0xFF in (ord('q'), 27): # @UndefinedVariable break cap.release() cv2.destroyAllWindows() # @UndefinedVariable return 0
def main(): global JOBS_INFO, EXIT if len(sys.argv) not in (2, 3, 4): print( "usage: %s /path/to/document.pdf [printer-name] [document-title]" % sys.argv[0]) return -3 filename = sys.argv[1] with open(filename, 'rb') as f: pdf_data = f.read() if len(sys.argv) == 2: from xpra.platform.win32.printing import get_printers printers = get_printers() printer_name = printers.keys()[0] if len(sys.argv) in (3, 4): printer_name = sys.argv[2] if len(sys.argv) == 4: title = sys.argv[3] else: title = os.path.basename(filename) import time from xpra.util import csv from xpra.log import Logger log = Logger("printing", "win32") #start a new thread before submitting the document, #because otherwise the job may complete before we can get its status from threading import Thread t = Thread(target=watch_print_job_status, name="watch print job status") t.daemon = True t.start() job_id = print_pdf(printer_name, title, pdf_data) if job_id < 0: return job_id #wait for job to end: job_status = None while True: job_info = JOBS_INFO.get(job_id, {}) log("job_info[%i]=%s", job_id, job_info) v = job_info.get("job_status") if v != job_status: log.info("print job status: %s", csv(v)) job_status = v if "OFFLINE" in job_status or "DELETING" in job_status: EXIT = True break time.sleep(1.0) return 0
def do_log_screen_sizes(sizes): from xpra.log import Logger log = Logger() #old format, used by some clients (android): if len(sizes) == 2 and type(sizes[0]) == int and type(sizes[1]) == int: return for s in sizes: if len(s) < 10: log.info(" %s", s) continue #more detailed output: display_name, width, height, width_mm, height_mm, \ monitors, work_x, work_y, work_width, work_height = s[:11] log.info(" '%s' %sx%s (%sx%s mm) workarea: %sx%s at %sx%s", prettify_plug_name(display_name), width, height, width_mm, height_mm, work_width, work_height, work_x, work_y) i = 0 for m in monitors: i += 1 if len(m) < 7: log.info(" %s", m) continue plug_name, x, y, width, height, wmm, hmm = m[:8] log.info(" '%s' %sx%s at %sx%s (%sx%s mm)", prettify_plug_name(plug_name, "monitor %s" % i), width, height, x, y, wmm, hmm)
def log_ssl_info(ssl_sock): from xpra.log import Logger ssllog = Logger("ssl") ssllog("server_hostname=%s", ssl_sock.server_hostname) cipher = ssl_sock.cipher() if cipher: ssllog.info(" %s, %s bits", cipher[0], cipher[2]) try: cert = ssl_sock.getpeercert() except ValueError: pass else: if cert: ssllog("certificate:") print_nested_dict(ssl_sock.getpeercert(), prefix=" ", print_fn=ssllog)
def cleanup_socket(): log = Logger("network") try: cur_inode = os.stat(sockpath).st_ino except: log.info("socket '%s' already deleted", sockpath) return delpath = sockpath log("cleanup_socket '%s', original inode=%s, new inode=%s", sockpath, inode, cur_inode) if cur_inode==inode: log.info("removing socket %s", delpath) try: os.unlink(delpath) except: pass
def do_log_screen_sizes(root_w, root_h, sizes): from xpra.log import Logger log = Logger() # old format, used by some clients (android): if type(sizes) not in (tuple, list): return if any(True for x in sizes if type(x) not in (tuple, list)): return def dpi(size_pixels, size_mm): if size_mm == 0: return 0 return int(size_pixels * 254 / size_mm / 10) for s in sizes: if len(s) < 10: log.info(" %s", s) continue # more detailed output: display_name, width, height, width_mm, height_mm, monitors, work_x, work_y, work_width, work_height = s[:10] # always log plug name: info = ["'%s'" % prettify_plug_name(display_name)] if width != root_w or height != root_h: # log plug dimensions if not the same as display (root): info.append("%sx%s" % (width, height)) info.append("(%sx%s mm - DPI: %sx%s)" % (width_mm, height_mm, dpi(width, width_mm), dpi(height, height_mm))) def add_workarea(wx, wy, ww, wh): info.append("workarea: %sx%s" % (ww, wh)) if wx != 0 or wy != 0: # log position if not (0, 0) info.append("at %sx%s" % (wx, wy)) if work_width != width or work_height != height or work_x != 0 or work_y != 0: add_workarea(work_x, work_y, work_width, work_height) log.info(" " + " ".join(info)) for i, m in enumerate(monitors, start=1): if len(m) < 7: log.info(" %s", m) continue plug_name, plug_x, plug_y, plug_width, plug_height, plug_width_mm, plug_height_mm = m[:7] info = ["%s" % prettify_plug_name(plug_name, "monitor %s" % i)] if plug_width != width or plug_height != height or plug_x != 0 or plug_y != 0: info.append("%sx%s" % (plug_width, plug_height)) if plug_x != 0 or plug_y != 0: info.append("at %sx%s" % (plug_x, plug_y)) if (plug_width_mm != width_mm or plug_height_mm != height_mm) and (plug_width_mm > 0 or plug_height_mm > 0): info.append( "(%sx%s mm - DPI: %sx%s)" % (plug_width_mm, plug_height_mm, dpi(plug_width, plug_width_mm), dpi(plug_height, plug_height_mm)) ) if len(m) >= 11: work_x, work_y, work_width, work_height = m[7:11] add_workarea(work_x, work_y, work_width, work_height) log.info(" " + " ".join(info))
def start_children(child_reaper, commands): assert os.name=="posix" from xpra.log import Logger log = Logger() #disable ubuntu's global menu using env vars: env = os.environ.copy() env.update({ "UBUNTU_MENUPROXY" : "", "QT_X11_NO_NATIVE_MENUBAR" : "1"}) for child_cmd in commands: if not child_cmd: continue try: proc = subprocess.Popen(child_cmd, stdin=subprocess.PIPE, env=env, shell=True, close_fds=True) child_reaper.add_process(proc, child_cmd) log.info("started child '%s' with pid %s", child_cmd, proc.pid) except OSError, e: sys.stderr.write("Error spawning child '%s': %s\n" % (child_cmd, e))
def start_pulseaudio(child_reaper, pulseaudio_command): from xpra.log import Logger log = Logger("sound") log("pulseaudio_command=%s", pulseaudio_command) pa_proc = subprocess.Popen(pulseaudio_command, stdin=subprocess.PIPE, shell=True, close_fds=True) child_reaper.add_process(pa_proc, "pulseaudio", ignore=True) log.info("pulseaudio server started with pid %s", pa_proc.pid) def check_pa_start(): if pa_proc.poll( ) is not None or pa_proc.pid in child_reaper._dead_pids: log.warn( "Warning: pulseaudio has terminated. Either fix the pulseaudio command line or use --no-pulseaudio to avoid this warning." ) log.warn( " usually, only a single pulseaudio instance can be running for each user account, and one may be running already" ) return False gobject.timeout_add(1000 * 2, check_pa_start) def cleanup_pa(): log("cleanup_pa() process.poll()=%s, pid=%s, dead_pids=%s", pa_proc.poll(), pa_proc.pid, child_reaper._dead_pids) if pa_proc.poll( ) is None and pa_proc.pid not in child_reaper._dead_pids: log.info("stopping pulseaudio with pid %s", pa_proc.pid) try: #first we try pactl (required on Ubuntu): from xpra.scripts.exec_util import safe_exec r, _, _ = safe_exec(["pactl", "exit"]) #warning: pactl will return 0 whether it succeeds or not... #but we can't kill the process because Ubuntu starts a new one if r != 0: #fallback to using SIGINT: pa_proc.terminate() except: log.warn("error trying to stop pulseaudio", exc_info=True) _cleanups.append(cleanup_pa)
def main(): import sys from xpra.platform import program_context, command_error with program_context("Webcam", "Webcam"): from xpra.log import Logger, add_debug_category log = Logger("webcam") if "-v" in sys.argv or "--verbose" in sys.argv: add_debug_category("webcam") log.enable_debug() try: import cv2 except ImportError as e: command_error("Error: no opencv support module: %s" % e) return 1 device = 0 if len(sys.argv) == 2: try: device = int(sys.argv[1]) except: command_error( "Warning: failed to parse value as a device number: '%s'" % sys.argv[1]) try: cap = cv2.VideoCapture(device) except Exception as e: command_error( "Error: failed to capture video using device %s:\n%s" % (device, e)) return 1 log.info("capture device for %i: %s", device, cap) while True: ret, frame = cap.read() if not ret: command_error("Error: frame capture failed using device %s" % device) return 1 cv2.imshow('frame', frame) if cv2.waitKey(10) & 0xFF in (ord('q'), 27): break cap.release() cv2.destroyAllWindows() return 0
def do_log_screen_sizes(root_w, root_h, sizes): from xpra.log import Logger log = Logger("util") #old format, used by some clients (android): if type(sizes) not in (tuple, list): return if any(True for x in sizes if type(x) not in (tuple, list)): return def dpi(size_pixels, size_mm): if size_mm==0: return 0 return int(size_pixels * 254 / size_mm / 10) for s in sizes: if len(s)<10: log.info(" %s", s) continue #more detailed output: display_name, width, height, width_mm, height_mm, \ monitors, work_x, work_y, work_width, work_height = s[:10] #always log plug name: info = ["%s" % prettify_plug_name(display_name)] if width!=root_w or height!=root_h: #log plug dimensions if not the same as display (root): info.append("%ix%i" % (width, height)) info.append("(%ix%i mm - DPI: %ix%i)" % (width_mm, height_mm, dpi(width, width_mm), dpi(height, height_mm))) def add_workarea(wx, wy, ww, wh): info.append("workarea: %ix%i" % (ww, wh)) if wx!=0 or wy!=0: #log position if not (0, 0) info.append("at %ix%i" % (wx, wy)) if work_width!=width or work_height!=height or work_x!=0 or work_y!=0: add_workarea(work_x, work_y, work_width, work_height) log.info(" "+" ".join(info)) for i, m in enumerate(monitors, start=1): if len(m)<7: log.info(" %s", m) continue plug_name, plug_x, plug_y, plug_width, plug_height, plug_width_mm, plug_height_mm = m[:7] info = ['%s' % prettify_plug_name(plug_name, "monitor %i" % i)] if plug_width!=width or plug_height!=height or plug_x!=0 or plug_y!=0: info.append("%ix%i" % (plug_width, plug_height)) if plug_x!=0 or plug_y!=0: info.append("at %ix%i" % (plug_x, plug_y)) if (plug_width_mm!=width_mm or plug_height_mm!=height_mm) and (plug_width_mm>0 or plug_height_mm>0): info.append("(%ix%i mm - DPI: %ix%i)" % (plug_width_mm, plug_height_mm, dpi(plug_width, plug_width_mm), dpi(plug_height, plug_height_mm))) if len(m)>=11: dwork_x, dwork_y, dwork_width, dwork_height = m[7:11] #only show it again if different from the screen workarea if dwork_x!=work_x or dwork_y!=work_y or dwork_width!=work_width or dwork_height!=work_height: add_workarea(dwork_x, dwork_y, dwork_width, dwork_height) log.info(" "+" ".join(info))
def start_pulseaudio(child_reaper, pulseaudio_command): from xpra.log import Logger log = Logger("sound") log("pulseaudio_command=%s", pulseaudio_command) pa_proc = subprocess.Popen(pulseaudio_command, stdin=subprocess.PIPE, shell=True, close_fds=True) child_reaper.add_process(pa_proc, "pulseaudio", ignore=True) log.info("pulseaudio server started with pid %s", pa_proc.pid) def check_pa_start(): if pa_proc.poll() is not None or pa_proc.pid in child_reaper._dead_pids: log.warn( "Warning: pulseaudio has terminated. Either fix the pulseaudio command line or use --no-pulseaudio to avoid this warning." ) log.warn( " usually, only a single pulseaudio instance can be running for each user account, and one may be running already" ) return False gobject.timeout_add(1000 * 2, check_pa_start) def cleanup_pa(): log( "cleanup_pa() process.poll()=%s, pid=%s, dead_pids=%s", pa_proc.poll(), pa_proc.pid, child_reaper._dead_pids ) if pa_proc.poll() is None and pa_proc.pid not in child_reaper._dead_pids: log.info("stopping pulseaudio with pid %s", pa_proc.pid) try: # first we try pactl (required on Ubuntu): from xpra.scripts.exec_util import safe_exec r, _, _ = safe_exec(["pactl", "exit"]) # warning: pactl will return 0 whether it succeeds or not... # but we can't kill the process because Ubuntu starts a new one if r != 0: # fallback to using SIGINT: pa_proc.terminate() except: log.warn("error trying to stop pulseaudio", exc_info=True) _cleanups.append(cleanup_pa)
def main(): log = Logger("client") from xpra.client.gtk2.tray_menu import GTK2TrayMenu client = FakeClient() log.info("creating tray menu") tray = GTK2TrayMenu(client) client.menu = tray.build() client.fire_handshake_callbacks() log.info("creating tray widget") def tray_click(button, pressed, time=0): log.info("tray_click(%s, %s, %s)", button, pressed, time) if button==1 and pressed: glib.idle_add(tray.activate, button, time) elif button==3 and not pressed: glib.idle_add(tray.popup, button, time) def tray_mouseover(*args): log.info("tray_mouseover(%s)", args) def tray_exit(*args): log.info("tray_exit(%s)", args) gtk.main_quit() def tray_geometry(*args): log.info("tray_geometry%s", args) GTKStatusIconTray(client, client.menu, "test", None, size_changed_cb=tray_geometry(), click_cb=tray_click, mouseover_cb=tray_mouseover, exit_cb=tray_exit) log.info("running main loop") gtk.main()
def write_pidfile(pidfile, uid, gid): from xpra.log import Logger log = Logger("server") pidstr = str(os.getpid()) try: with open(pidfile, "w") as f: os.fchmod(f.fileno(), 0o600) f.write("%s\n" % pidstr) try: inode = os.fstat(f.fileno()).st_ino except: inode = -1 if POSIX and uid != getuid() or gid != getgid(): try: os.fchown(f.fileno(), uid, gid) except: pass log.info("wrote pid %s to '%s'", pidstr, pidfile) def cleanuppidfile(): #verify this is the right file! log("cleanuppidfile: inode=%i", inode) if inode > 0: try: i = os.stat(pidfile).st_ino log("cleanuppidfile: current inode=%i", i) if i != inode: return except: pass try: os.unlink(pidfile) except: pass add_cleanup(cleanuppidfile) except Exception as e: log.error("Error: failed to write pid %i to pidfile '%s':", os.getpid(), pidfile) log.error(" %s", e)
def start_pulseaudio(child_reaper, pulseaudio_command): from xpra.log import Logger log = Logger("sound") log("pulseaudio_command=%s", pulseaudio_command) pa_proc = subprocess.Popen(pulseaudio_command, stdin=subprocess.PIPE, shell=True, close_fds=True) child_reaper.add_process(pa_proc, "pulseaudio", ignore=True) log.info("pulseaudio server started with pid %s", pa_proc.pid) def check_pa_start(): if pa_proc.poll() is not None or pa_proc.pid in child_reaper._dead_pids: log.warn("Warning: pulseaudio has terminated. Either fix the pulseaudio command line or use --no-pulseaudio to avoid this warning.") log.warn(" usually, only a single pulseaudio instance can be running for each user account, and one may be running already") return False gobject.timeout_add(1000*2, check_pa_start) def cleanup_pa(): log("cleanup_pa() process.poll()=%s, pid=%s, dead_pids=%s", pa_proc.poll(), pa_proc.pid, child_reaper._dead_pids) if pa_proc.poll() is None and pa_proc.pid not in child_reaper._dead_pids: log.info("stopping pulseaudio with pid %s", pa_proc.pid) try: pa_proc.terminate() except: log.warn("error trying to stop pulseaudio", exc_info=True) _cleanups.append(cleanup_pa)
def PIL_logging_workaround(): import logging PIL_DEBUG = os.environ.get("XPRA_PIL_DEBUG", "0") == "1" if PIL_DEBUG: from xpra.log import Logger log = Logger("util") log.info("enabling PIL.DEBUG") level = logging.DEBUG else: level = logging.INFO #newer versions use this logger, #we must initialize it before we load the class: for x in ("Image", "PngImagePlugin", "WebPImagePlugin", "JpegImagePlugin"): logger = logging.getLogger("PIL.%s" % x) logger.setLevel(level) import PIL from PIL import Image assert PIL and Image if hasattr(Image, "DEBUG"): #for older versions (pre 3.0), use Image.DEBUG flag: Image.DEBUG = int(PIL_DEBUG)
def start_pulseaudio(child_reaper, pulseaudio_command): from xpra.log import Logger log = Logger() log("pulseaudio_command=%s", pulseaudio_command) pa_proc = subprocess.Popen(pulseaudio_command, stdin=subprocess.PIPE, shell=True, close_fds=True) child_reaper.add_process(pa_proc, "pulseaudio") log.info("pulseaudio server started with pid %s", pa_proc.pid) def check_pa_start(): if pa_proc.poll() is not None or pa_proc.pid in child_reaper._dead_pids: log.warn("Warning: pulseaudio has terminated. Either fix the pulseaudio command line or use --no-pulseaudio to avoid this warning.") log.warn(" usually, only a single pulseaudio instance can be running for each user account, and one may be running already") return False gobject.timeout_add(1000*2, check_pa_start) def cleanup_pa(): log("cleanup_pa() process.poll()=%s, pid=%s, dead_pids=%s", pa_proc.poll(), pa_proc.pid, child_reaper._dead_pids) if pa_proc.poll() is None and pa_proc.pid not in child_reaper._dead_pids: log.info("stopping pulseaudio with pid %s", pa_proc.pid) try: pa_proc.terminate() except: log.warn("error trying to stop pulseaudio", exc_info=True) _cleanups.append(cleanup_pa)
def main(): from xpra.log import Logger log = Logger("util") sp = sys.platform log.info("platform_name(%s)=%s", sp, platform_name(sp, "")) log.info("get_machine_id()=%s", get_machine_id()) log.info("get_hex_uuid()=%s", get_hex_uuid()) log.info("get_int_uuid()=%s", get_int_uuid())
def main(): import sys from xpra.platform import program_context, command_error with program_context("Webcam", "Webcam"): from xpra.log import Logger, add_debug_category log = Logger("webcam") if "-v" in sys.argv or "--verbose" in sys.argv: add_debug_category("webcam") log.enable_debug() try: import cv2 except ImportError as e: command_error("Error: no opencv support module: %s" % e) return 1 device = 0 if len(sys.argv)==2: try: device = int(sys.argv[1]) except: command_error("Warning: failed to parse value as a device number: '%s'" % sys.argv[1]) try: cap = cv2.VideoCapture(device) except Exception as e: command_error("Error: failed to capture video using device %s:\n%s" % (device, e)) return 1 log.info("capture device for %i: %s", device, cap) while True: ret, frame = cap.read() if not ret: command_error("Error: frame capture failed using device %s" % device) return 1 cv2.imshow('frame', frame) if cv2.waitKey(10) & 0xFF in (ord('q'), 27): break cap.release() cv2.destroyAllWindows() return 0
def main(): from xpra.platform import program_context from xpra.platform.gui import init as gui_init from xpra.util import print_nested_dict from xpra.log import enable_color, Logger with program_context("OpenGL Native Context Check"): gui_init() enable_color() log = Logger("opengl") verbose = "-v" in sys.argv or "--verbose" in sys.argv if verbose: log.enable_debug() if not GLContext: log.error("Error: no GLContext available on %s", sys.platform) return 1 try: props = check_support() except Exception: log.error("%s().check_support()", exc_info=True) return 1 log.info("") log.info("OpenGL properties:") print_nested_dict(props) return 0
def main(): import logging logging.basicConfig(format="%(asctime)s %(message)s") logging.root.setLevel(logging.INFO) from xpra.log import Logger log = Logger("") sp = sys.platform log.info("platform_name(%s)=%s", sp, platform_name(sp, "")) log.info("get_machine_id()=%s", get_machine_id()) log.info("get_hex_uuid()=%s", get_hex_uuid()) log.info("get_int_uuid()=%s", get_int_uuid())
def main(): from xpra.platform import program_context with program_context("Codec-Constants", "Codec Constants Info"): import sys from xpra.log import Logger log = Logger("encoding") if "-v" in sys.argv or "--verbose" in sys.argv: log.enable_debug() log.info("LOSSY_PIXEL_FORMATS=%s", LOSSY_PIXEL_FORMATS) log.info("PIXEL_SUBSAMPLING=%s", PIXEL_SUBSAMPLING) log.info("RGB_FORMATS=%s", RGB_FORMATS)
def do_log_screen_sizes(root_w, root_h, sizes): from xpra.log import Logger log = Logger() #old format, used by some clients (android): if len(sizes) == 2 and type(sizes[0]) == int and type(sizes[1]) == int: return for s in sizes: if len(s) < 10: log.info(" %s", s) continue #more detailed output: display_name, width, height, width_mm, height_mm, \ monitors, work_x, work_y, work_width, work_height = s[:11] #always log plug name: info = ["'%s'" % prettify_plug_name(display_name)] if width != root_w or height != root_h: #log plug dimensions if not the same as display (root): info.append("%sx%s" % (width, height)) info.append("(%sx%s mm)" % (width_mm, height_mm)) if work_width != width or work_height != height or work_x != 0 or work_y != 0: #log workarea if not the same as plug size: info.append("workarea: %sx%s" % (work_width, work_height)) if work_x != 0 or work_y != 0: #log position if not (0, 0) info.append("at %sx%s" % (work_x, work_y)) log.info(" " + " ".join(info)) i = 0 for m in monitors: i += 1 if len(m) < 7: log.info(" %s", m) continue plug_name, plug_x, plug_y, plug_width, plug_height, plug_width_mm, plug_height_mm = m[: 8] info = ['%s' % prettify_plug_name(plug_name, "monitor %s" % i)] if plug_width != width or plug_height != height or plug_x != 0 or plug_y != 0: info.append("%sx%s" % (plug_width, plug_height)) if plug_x != 0 or plug_y != 0: info.append("at %sx%s" % (plug_x, plug_y)) if (plug_width_mm != width_mm or plug_height_mm != height_mm) and ( plug_width_mm > 0 or plug_height_mm > 0): info.append("(%sx%s mm)" % (plug_width_mm, plug_height_mm)) log.info(" " + " ".join(info))
def do_log_screen_sizes(root_w, root_h, sizes): from xpra.log import Logger log = Logger() #old format, used by some clients (android): if len(sizes)==2 and type(sizes[0])==int and type(sizes[1])==int: return for s in sizes: if len(s)<10: log.info(" %s", s) continue #more detailed output: display_name, width, height, width_mm, height_mm, \ monitors, work_x, work_y, work_width, work_height = s[:11] #always log plug name: info = ["'%s'" % prettify_plug_name(display_name)] if width!=root_w or height!=root_h: #log plug dimensions if not the same as display (root): info.append("%sx%s" % (width, height)) info.append("(%sx%s mm)" % (width_mm, height_mm)) if work_width!=width or work_height!=height or work_x!=0 or work_y!=0: #log workarea if not the same as plug size: info.append("workarea: %sx%s" % (work_width, work_height)) if work_x!=0 or work_y!=0: #log position if not (0, 0) info.append("at %sx%s" % (work_x, work_y)) log.info(" "+" ".join(info)) i = 0 for m in monitors: i += 1 if len(m)<7: log.info(" %s", m) continue plug_name, plug_x, plug_y, plug_width, plug_height, plug_width_mm, plug_height_mm = m[:8] info = ['%s' % prettify_plug_name(plug_name, "monitor %s" % i)] if plug_width!=width or plug_height!=height or plug_x!=0 or plug_y!=0: info.append("%sx%s" % (plug_width, plug_height)) if plug_x!=0 or plug_y!=0: info.append("at %sx%s" % (plug_x, plug_y)) if (plug_width_mm!=width_mm or plug_height_mm!=height_mm) and (plug_width_mm>0 or plug_height_mm>0): info.append("(%sx%s mm)" % (plug_width_mm, plug_height_mm)) log.info(" "+" ".join(info))
def do_log_screen_sizes(sizes): from xpra.log import Logger log = Logger() #old format, used by some clients (android): if len(sizes)==2 and type(sizes[0])==int and type(sizes[1])==int: return for s in sizes: if len(s)<10: log.info(" %s", s) continue #more detailed output: display_name, width, height, width_mm, height_mm, \ monitors, work_x, work_y, work_width, work_height = s[:11] log.info(" '%s' %sx%s (%sx%s mm) workarea: %sx%s at %sx%s", prettify_plug_name(display_name), width, height, width_mm, height_mm, work_width, work_height, work_x, work_y) i = 0 for m in monitors: i += 1 if len(m)<7: log.info(" %s", m) continue plug_name, x, y, width, height, wmm, hmm = m[:8] log.info(" '%s' %sx%s at %sx%s (%sx%s mm)", prettify_plug_name(plug_name, "monitor %s" % i), width, height, x, y, wmm, hmm)
def main(): log = Logger("client") from xpra.client.gtk2.tray_menu import GTK2TrayMenu client = FakeClient() log.info("creating tray menu") tray = GTK2TrayMenu(client) client.menu = tray.build() client.fire_handshake_callbacks() log.info("creating tray widget") def tray_click(button, pressed, time=0): log.info("tray_click(%s, %s, %s)", button, pressed, time) if button == 1 and pressed: glib.idle_add(tray.activate, button, time) elif button == 3 and not pressed: glib.idle_add(tray.popup, button, time) def tray_mouseover(*args): log.info("tray_mouseover(%s)", args) def tray_exit(*args): log.info("tray_exit(%s)", args) gtk.main_quit() def tray_geometry(*args): log.info("tray_geometry%s", args) GTKStatusIconTray(client, client.menu, "test", None, size_changed_cb=tray_geometry(), click_cb=tray_click, mouseover_cb=tray_mouseover, exit_cb=tray_exit) log.info("running main loop") gtk.main()
log("do_set_keymap using xkbmap_print") #try to guess the layout by parsing "setxkbmap -print" try: sym_re = re.compile("\s*xkb_symbols\s*{\s*include\s*\"([\w\+]*)") for line in xkbmap_print.splitlines(): m = sym_re.match(line) if m: layout = std(m.group(1)) log.info("guessing keyboard layout='%s'" % layout) exec_keymap_command(["setxkbmap", layout]) break except Exception, e: log.info("error setting keymap: %s" % e) else: layout = xkbmap_layout or "us" log.info("setting keyboard layout to '%s'", std(layout)) set_layout = ["setxkbmap", "-layout", layout] if xkbmap_variant: set_layout += ["-variant", xkbmap_variant] if not exec_keymap_command(set_layout) and xkbmap_variant: log.info("error setting keymap with variant %s, retrying with just layout %s", std(xkbmap_variant), std(layout)) set_layout = ["setxkbmap", "-layout", layout] exec_keymap_command(set_layout) display = os.environ.get("DISPLAY") if xkbmap_print: #there may be a junk header, if so remove it: pos = xkbmap_print.find("xkb_keymap {") if pos>0: xkbmap_print = xkbmap_print[pos:] log.info("setting full keymap definition from client via xkbcomp")
# This file is part of Xpra. # Copyright (C) 2015 Antoine Martin <*****@*****.**> # Xpra is released under the terms of the GNU GPL v2, or, at your option, any # later version. See the file COPYING for details. import os import logging from xpra.util import envbool from xpra.log import Logger PIL_DEBUG = envbool("XPRA_PIL_DEBUG", False) if PIL_DEBUG: log = Logger("encoder", "pillow") log.info("enabling PIL.DEBUG") level = logging.DEBUG else: level = logging.INFO #newer versions use this logger, #we must initialize it before we load the class: for x in ("Image", "PngImagePlugin", "WebPImagePlugin", "JpegImagePlugin"): logger = logging.getLogger("PIL.%s" % x) logger.setLevel(level) import PIL #@UnresolvedImport from PIL import Image #@UnresolvedImport assert PIL is not None and Image is not None try: PIL_VERSION = PIL.__version__ except AttributeError:
def start_websockify(child_reaper, opts, tcp_sockets): # start websockify? if not opts.html: return html = opts.html if type(html)==str: html = html.lower() from xpra.scripts.config import FALSE_OPTIONS, TRUE_OPTIONS if html in FALSE_OPTIONS: #html disabled return if opts.tcp_proxy: raise Exception("cannot use tcp-proxy mode with html, use one or the other") return 1 from xpra.platform.paths import get_resources_dir www_dir = os.path.abspath(os.path.join(get_resources_dir(), "www")) if not os.path.exists(www_dir): raise Exception("cannot find xpra's html directory (not found in '%s')" % www_dir) import websockify #@UnresolvedImport assert websockify html_port = -1 html_host = "127.0.0.1" if html not in TRUE_OPTIONS: #we expect either HOST:PORT, or just PORT if html.find(":")>=0: html_host, html = html.split(":", 1) try: html_port = int(html) except Exception as e: raise Exception("invalid html port: %s" % e) #we now have the host and port (port may be -1 to mean auto..) if html_port==-1: #try to find a free port and hope that websockify can then use it.. html_port = get_free_tcp_port() if len(tcp_sockets)<1: raise Exception("html web server requires at least one tcp socket, see 'bind-tcp'") #use the first tcp socket for websockify to talk back to us: _, xpra_tcp_port = list(tcp_sockets)[0] from xpra.log import Logger log = Logger("server") websockify_command = ["websockify", "--web", www_dir, "%s:%s" % (html_host, html_port), "127.0.0.1:%s" % xpra_tcp_port] log("websockify_command: %s", websockify_command) websockify_proc = subprocess.Popen(websockify_command, close_fds=True) websockify_proc._closed = False start_time = time.time() def websockify_ended(proc): elapsed = time.time()-start_time log("websockify_ended(%s) after %i seconds", elapsed) if not websockify_proc._closed: log.warn("Warning: websockify has terminated, the html web server will not be available.") log.warn(" command used: %s", " ".join(websockify_command)) return False child_reaper.add_process(websockify_proc, "websockify", websockify_command, ignore=True, callback=websockify_ended) log.info("websockify started, serving %s on %s:%s", www_dir, html_host, html_port) def cleanup_websockify(): log("cleanup_websockify() process.poll()=%s, pid=%s", websockify_proc.poll(), websockify_proc.pid) if websockify_proc.poll() is None and not websockify_proc._closed: log.info("stopping websockify with pid %s", websockify_proc.pid) try: websockify_proc._closed = True websockify_proc.terminate() except: log.warn("error trying to stop websockify", exc_info=True) _cleanups.append(cleanup_websockify) opts.tcp_proxy = "%s:%s" % (html_host, html_port)
from xpra.x11.gtk_x11.send_wm import send_wm_workspace X11Window = X11WindowBindings() X11Core = X11CoreBindings() HAS_X11_BINDINGS = True SubstructureNotifyMask = constants["SubstructureNotifyMask"] SubstructureRedirectMask = constants["SubstructureRedirectMask"] CurrentTime = constants["CurrentTime"] try: #TODO: in theory this is not a proper check, meh - that will do root = get_default_root_window() supported = prop_get(root, "_NET_SUPPORTED", ["atom"], ignore_errors=True) CAN_SET_WORKSPACE = bool(supported) and "_NET_WM_DESKTOP" in supported except Exception as e: log.info("failed to setup workspace hooks: %s", e, exc_info=True) except ImportError: pass UNDECORATED_TRANSIENT_IS_OR = int(os.environ.get("XPRA_UNDECORATED_TRANSIENT_IS_OR", "1")) #window types we map to POPUP rather than TOPLEVEL POPUP_TYPE_HINTS = set(( #"DIALOG", #"MENU", #"TOOLBAR", #"SPLASHSCREEN", #"UTILITY", #"DOCK", #"DESKTOP",
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) from xpra.log import Logger defs = [] log = Logger("network") 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) #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) except: for sock, cleanup_socket in defs: try: cleanup_socket() except Exception as e: log.warn("error cleaning up socket %s", sock) defs = [] raise return defs
def run_server(error_cb, opts, mode, xpra_file, extra_args): try: cwd = os.getcwd() except: cwd = os.path.expanduser("~") sys.stderr.write("current working directory does not exist, using '%s'\n" % cwd) if opts.encoding and opts.encoding=="help": #avoid errors and warnings: opts.encoding = "" opts.clipboard = False opts.notifications = False print("xpra server supports the following encodings:") print("(please wait, encoder initialization may take a few seconds)") #disable info logging which would be confusing here from xpra.log import get_all_loggers, set_default_level import logging set_default_level(logging.WARN) logging.root.setLevel(logging.WARN) for x in get_all_loggers(): x.logger.setLevel(logging.WARN) from xpra.server.server_base import ServerBase sb = ServerBase() sb.init(opts) #ensures that the threaded video helper init has completed #(by running it again, which will block on the init lock) from xpra.codecs.video_helper import getVideoHelper getVideoHelper().init() sb.init_encodings() from xpra.codecs.loader import encoding_help for e in sb.encodings: print(" * %s" % encoding_help(e)) return 0 assert mode in ("start", "upgrade", "shadow", "proxy") starting = mode == "start" upgrading = mode == "upgrade" shadowing = mode == "shadow" proxying = mode == "proxy" clobber = upgrading or opts.use_display 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 sys.platform.startswith("win") or sys.platform.startswith("darwin"): #just a virtual name for the only display available: display_name = ":0" else: from xpra.scripts.main import guess_X11_display display_name = guess_X11_display(opts.socket_dir) elif upgrading and len(extra_args)==0: from xpra.scripts.main import guess_xpra_display display_name = guess_xpra_display(opts.socket_dir) else: if len(extra_args) > 1: error_cb("too many extra arguments: only expected a display number") if len(extra_args) == 1: display_name = extra_args[0] if not shadowing and not proxying: display_name_check(display_name) else: if proxying: error_cb("you must specify a free virtual display name to use with the proxy server") if not opts.displayfd: error_cb("displayfd support is not enabled on this system, you must specify the display to use") if opts.use_display: #only use automatic guess for xpra displays and not X11 displays: from xpra.scripts.main import guess_xpra_display #@Reimport display_name = guess_xpra_display(opts.socket_dir) 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) #the server class will usually override those: signal.signal(signal.SIGINT, deadly_signal) signal.signal(signal.SIGTERM, deadly_signal) dotxpra = DotXpra(opts.socket_dir) # Generate the script text now, because os.getcwd() will # change if/when we daemonize: script = xpra_runner_shell_script(xpra_file, os.getcwd(), opts.socket_dir) stdout = sys.stdout stderr = sys.stderr # Daemonize: if opts.daemon: #daemonize will chdir to "/", so try to use an absolute path: if opts.password_file: opts.password_file = os.path.abspath(opts.password_file) # At this point we may not know the display name, # so log_filename0 may point to a temporary file which we will rename later log_filename0 = select_log_file(dotxpra, opts.log_file, display_name) logfd = open_log_file(log_filename0) assert logfd > 2 stdout, stderr = daemonize(logfd) try: stderr.write("Entering daemon mode; " + "any further errors will be reported to:\n" + (" %s\n" % log_filename0)) except: #this can happen if stderr is closed by the caller already pass if os.name=="posix": # Write out a shell-script so that we can start our proxy in a clean # environment: write_runner_shell_script(dotxpra, script) from xpra.log import Logger log = Logger("server") mdns_recs = [] sockets = [] try: # 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) bind_tcp = parse_bind_tcp(opts.bind_tcp) for host, iport in bind_tcp: socket = setup_tcp_socket(host, iport) sockets.append(socket) if opts.mdns: mdns_recs.append(("tcp", bind_tcp)) except Exception as e: log.error("cannot start server: failed to setup sockets: %s", e) return 1 # Do this after writing out the shell script: if display_name[0] != 'S': os.environ["DISPLAY"] = display_name sanitize_env() configure_imsettings_env(opts.input_method) # Start the Xvfb server first to get the display_name if needed xvfb = None xvfb_pid = None if not shadowing and not proxying and not clobber: try: xvfb, display_name = start_Xvfb(opts.xvfb, display_name) except OSError as e: log.error("Error starting Xvfb: %s\n", e) return 1 xvfb_pid = xvfb.pid #always update as we may now have the "real" display name: os.environ["DISPLAY"] = display_name if opts.daemon: log_filename1 = select_log_file(dotxpra, opts.log_file, display_name) if log_filename0 != log_filename1: # we now have the correct log filename, so use it: os.rename(log_filename0, log_filename1) stderr.write("Actual log file name is now: %s\n" % log_filename1) stdout.close() stderr.close() if not check_xvfb_process(xvfb): #xvfb problem: exit now return 1 #setup unix domain socket: socket, cleanup_socket = setup_local_socket(dotxpra, display_name, clobber, opts.mmap_group, opts.socket_permissions) if socket: #win32 returns None! sockets.append(socket) if opts.mdns: ssh_port = get_ssh_port() if ssh_port: mdns_recs.append(("ssh", [("", ssh_port)])) #publish mdns records: if opts.mdns: from xpra.platform.info import get_username mdns_info = {"display" : display_name, "username": get_username()} if opts.session_name: mdns_info["session"] = opts.session_name for mode, listen_on in mdns_recs: mdns_publish(display_name, mode, listen_on, mdns_info) if not check_xvfb_process(xvfb): #xvfb problem: exit now return 1 display = None if not sys.platform.startswith("win") and not sys.platform.startswith("darwin") and not proxying: display = verify_display_ready(xvfb, display_name, shadowing) if not display: return 1 elif not proxying: assert "gtk" not in sys.modules import gtk #@Reimport assert gtk if shadowing: from xpra.platform.shadow_server import ShadowServer app = ShadowServer() info = "shadow" elif proxying: from xpra.server.proxy_server import ProxyServer app = ProxyServer() info = "proxy" else: assert starting or upgrading from xpra.x11.gtk_x11 import gdk_display_source assert gdk_display_source #(now we can access the X11 server) if clobber: #get the saved pid (there should be one): xvfb_pid = get_xvfb_pid() elif xvfb_pid is not None: #save the new pid (we should have one): save_xvfb_pid(xvfb_pid) #check for an existing window manager: from xpra.x11.gtk_x11.wm import wm_check if not wm_check(display, opts.wm_name, upgrading): return 1 try: # This import is delayed because the module depends on gtk: from xpra.x11.server import XpraServer from xpra.x11.bindings.window_bindings import X11WindowBindings #@UnresolvedImport X11Window = X11WindowBindings() except ImportError as e: log.error("Failed to load Xpra server components, check your installation: %s" % e) return 1 if not X11Window.displayHasXComposite(): log.error("Xpra is a compositing manager, it cannot use a display which lacks the XComposite extension!") return 1 app = XpraServer(clobber) info = "xpra" try: app.exec_cwd = cwd app.init(opts) except Exception as e: log.error("Error: cannot start the %s server", info, exc_info=True) log.error(str(e)) log.info("") return 1 #honour start child, html webserver, and setup child reaper if os.name=="posix" and not proxying and not upgrading and not shadowing: # start websockify? try: start_websockify(app.child_reaper, opts, bind_tcp) #websockify overrides the tcp proxy, so we must re-set it: app._tcp_proxy = opts.tcp_proxy except Exception as e: error_cb("failed to setup websockify html server: %s" % e) if opts.exit_with_children: assert opts.start_child, "exit-with-children was specified but start-child is missing!" if opts.start: for x in opts.start: if x: app.start_child(x, x, True) if opts.start_child: for x in opts.start_child: if x: app.start_child(x, x, False) 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) #we got this far so the sockets have initialized and #the server should be able to manage the display #from now on, if we exit without upgrading we will also kill the Xvfb def kill_xvfb(): # Close our display(s) first, so the server dying won't kill us. log.info("killing xvfb with pid %s" % xvfb_pid) import gtk #@Reimport for display in gtk.gdk.display_manager_get().list_displays(): display.close() os.kill(xvfb_pid, signal.SIGTERM) if xvfb_pid is not None and not opts.use_display and not shadowing: _cleanups.append(kill_xvfb) try: log("running %s", app.run) e = app.run() log("%s()=%s", app.run, e) except KeyboardInterrupt: log.info("stopping on KeyboardInterrupt") e = 0 except Exception as e: log.error("server error", exc_info=True) e = -128 if e>0: # Upgrading/exiting, so leave X server running if kill_xvfb in _cleanups: _cleanups.remove(kill_xvfb) from xpra.server.server_core import ServerCore if e==ServerCore.EXITING_CODE: log.info("exiting: not cleaning up Xvfb") elif cleanup_socket in _cleanups: log.info("upgrading: not cleaning up Xvfb or socket") # don't delete the new socket (not ours) _cleanups.remove(cleanup_socket) log("cleanups=%s", _cleanups) e = 0 return e
from xpra.x11.gtk_x11.send_wm import send_wm_workspace X11Window = X11WindowBindings() X11Core = X11CoreBindings() HAS_X11_BINDINGS = True SubstructureNotifyMask = constants["SubstructureNotifyMask"] SubstructureRedirectMask = constants["SubstructureRedirectMask"] CurrentTime = constants["CurrentTime"] try: #TODO: in theory this is not a proper check, meh - that will do root = gtk.gdk.get_default_root_window() supported = prop_get(root, "_NET_SUPPORTED", ["atom"], ignore_errors=True) CAN_SET_WORKSPACE = os.environ.get("XPRA_SET_WORKSPACE", "0")=="1" and bool(supported) and "_NET_WM_DESKTOP" in supported except Exception as e: log.info("failed to setup workspace hooks: %s", e) except ImportError: pass #window types we map to POPUP rather than TOPLEVEL POPUP_TYPE_HINTS = set(( #"DIALOG", #"MENU", #"TOOLBAR", #"SPLASHSCREEN", #"UTILITY", #"DOCK", #"DESKTOP", "DROPDOWN_MENU", "POPUP_MENU",
class ChildReaper(object): def __init__(self, quit_cb): self._quit = quit_cb self._children_pids = {} self._dead_pids = set() self._ignored_pids = set() from xpra.log import Logger self._logger = Logger("server", "util") if USE_PROCESS_POLLING: POLL_DELAY = int(os.environ.get("XPRA_POLL_DELAY", 2)) self._logger.warn( "Warning: outdated/buggy version of Python: %s", ".".join(str(x) for x in sys.version_info) ) self._logger.warn( "switching to process polling every %s seconds to support 'exit-with-children'", POLL_DELAY ) gobject.timeout_add(POLL_DELAY * 1000, self.check) else: # with a less buggy python, we can just check the list of pids # whenever we get a SIGCHLD # however.. subprocess.Popen will no longer work as expected # see: http://bugs.python.org/issue9127 # so we must ensure certain things that exec happen first: from xpra.version_util import get_platform_info get_platform_info() signal.signal(signal.SIGCHLD, self.sigchld) # Check once after the mainloop is running, just in case the exit # conditions are satisfied before we even enter the main loop. # (Programming with unix the signal API sure is annoying.) def check_once(): self.check() return False # Only call once gobject.timeout_add(0, check_once) def add_process(self, process, command, ignore=False): process.command = command assert process.pid > 0 self._children_pids[process.pid] = process if ignore: self._ignored_pids.add(process.pid) self._logger("add_process(%s, %s, %s) pid=%s", process, command, ignore, process.pid) def check(self): pids = set(self._children_pids.keys()) - self._ignored_pids self._logger("check() pids=%s", pids) if pids: for pid, proc in self._children_pids.items(): if proc.poll() is not None: self.add_dead_pid(pid) self._logger("check() pids=%s, dead_pids=%s", pids, self._dead_pids) if pids.issubset(self._dead_pids): self._quit() return False return True def sigchld(self, signum, frame): gobject.idle_add(self._logger, "sigchld(%s, %s)", signum, frame) gobject.idle_add(self.reap) def add_dead_pid(self, pid): self._logger("add_dead_pid(%s)", pid) if pid not in self._dead_pids: proc = self._children_pids.get(pid) if proc: self._logger.info("child '%s' with pid %s has terminated", proc.command, pid) self._dead_pids.add(pid) self.check() def reap(self): if not self.check(): return while True: try: pid, _ = os.waitpid(-1, os.WNOHANG) except OSError: break self._logger("reap() waitpid=%s", pid) if pid == 0: break self.add_dead_pid(pid) def get_info(self): d = dict(self._children_pids) info = {"children": len(d), "children.dead": len(self._dead_pids), "children.ignored": len(self._ignored_pids)} i = 0 for pid in sorted(d.keys()): proc = d[pid] info["child[%i].live" % i] = pid not in self._dead_pids info["child[%i].pid" % i] = pid info["child[%i].command" % i] = proc.command info["child[%i].ignored" % i] = pid in self._ignored_pids i += 1 return info
client_salt = c.strget("challenge_client_salt") log( "processing authentication with %s, response=%s, client_salt=%s", proto.authenticator, challenge_response, binascii.hexlify(client_salt or "")) #send challenge if this is not a response: if not challenge_response: challenge = proto.authenticator.get_challenge() if challenge is None: auth_failed( "invalid authentication state: unexpected challenge response" ) return False salt, digest = challenge log.info( "Authentication required, %s sending challenge for '%s' using digest %s", proto.authenticator, username, digest) if digest not in self.digest_modes: auth_failed("cannot proceed without %s digest support" % digest) return False proto.send_now(("challenge", salt, auth_caps or "", digest)) return False if not proto.authenticator.authenticate(challenge_response, client_salt): auth_failed("invalid challenge response") return False log("authentication challenge passed") else: #did the client expect a challenge?
# This file is part of Xpra. # Copyright (C) 2015 Antoine Martin <*****@*****.**> # Xpra is released under the terms of the GNU GPL v2, or, at your option, any # later version. See the file COPYING for details. import os import logging from xpra.util import envbool from xpra.log import Logger PIL_DEBUG = envbool("XPRA_PIL_DEBUG", False) if PIL_DEBUG: log = Logger("encoder", "pillow") log.info("enabling PIL.DEBUG") level = logging.DEBUG else: level = logging.INFO #newer versions use this logger, #we must initialize it before we load the class: for x in ("Image", "PngImagePlugin", "WebPImagePlugin", "JpegImagePlugin"): logger = logging.getLogger("PIL.%s" % x) logger.setLevel(level) import PIL #@UnresolvedImport from PIL import Image #@UnresolvedImport assert PIL is not None and Image is not None, "failed to load Pillow" try: PIL_VERSION = PIL.PILLOW_VERSION except: PIL_VERSION = Image.VERSION
if min_texture_size>texture_size or min_texture_size>rect_texture_size: gl_check_error("The texture size is too small: %s" % texture_size) else: log("Texture size GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB=%s, GL_MAX_TEXTURE_SIZE=%s", rect_texture_size, texture_size) props["texture-size-limit"] = min(rect_texture_size, texture_size) return props finally: for x in alogger.handlers[0].records: #strip default message prefix: msg = x.getMessage().replace("No OpenGL_accelerate module loaded: ", "") if msg=="No module named OpenGL_accelerate": msg = "missing accelerate module" if msg!="OpenGL_accelerate module loaded": msg = "PyOpenGL warning: %s" % msg log.info(msg) #format handler messages: STRIP_LOG_MESSAGE = "Unable to load registered array format handler " missing_handlers = [] for x in fhlogger.handlers[0].records: msg = x.getMessage() p = msg.find(STRIP_LOG_MESSAGE) if p<0: #unknown message, log it: log.info(msg) continue format_handler = msg[p+len(STRIP_LOG_MESSAGE):] p = format_handler.find(":") if p>0: format_handler = format_handler[:p]
def handshake_complete(*args): from xpra.log import Logger log = Logger() log.info("Attached to %s (press Control-C to detach)\n" % conn.target)
selected_device_setup_cost = 40 else: selected_device_cpu_cost = 100 selected_device_gpu_cost = 0 selected_device_setup_cost = 20 log("device is a %s, using CPU cost=%s, GPU cost=%s", device_type(d), selected_device_cpu_cost, selected_device_gpu_cost) return except Exception, e: log.warn(" failed to use %s", platform_info(p)) log.warn(" with %s device %s", device_type(d), device_info(d)) log.warn(" Error: %s", e) #fallback to pyopencl auto mode: log.warn("OpenCL Error: failed to find a working platform and device combination... trying with pyopencl's 'create_some_context'") context = pyopencl.create_some_context(interactive=False) devices = context.get_info(pyopencl.context_info.DEVICES) log.info("chosen context has %s device(s):", len(devices)) for d in devices: log_device_info(d) assert len(devices)==1, "we only handle a single device at a time, sorry!" selected_device = devices[0] assert context is not None and selected_device is not None #Note: we don't care about alpha! #This tries to map our standard RGB representation #to the channel_order types that OpenCL may support IN_CHANNEL_ORDER = [] #a list of: (string, pyopencl.channel_order) #ie: [("RGBA", pyopencl.channel_order.RGBA), ..] CHANNEL_ORDER_TO_STR = {} #channel order to name:
emsg = e.description gl_check_error("unable to query max texture size: %s" % emsg) return props if min_texture_size>texture_size or min_texture_size>rect_texture_size: gl_check_error("The texture size is too small: %s" % texture_size) else: log("Texture size GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB=%s, GL_MAX_TEXTURE_SIZE=%s", rect_texture_size, texture_size) return props finally: for x in alogger.handlers[0].records: #strip default message prefix: msg = x.getMessage().replace("No OpenGL_accelerate module loaded: ", "") if msg=="No module named OpenGL_accelerate": msg = "missing accelerate module" log.info("PyOpenGL warning: %s" % msg) #format handler messages: STRIP_LOG_MESSAGE = "Unable to load registered array format handler " missing_handlers = [] for x in fhlogger.handlers[0].records: msg = x.getMessage() p = msg.find(STRIP_LOG_MESSAGE) if p<0: #unknown message, log it: log.info(msg) continue format_handler = msg[p+len(STRIP_LOG_MESSAGE):] p = format_handler.find(":") if p>0: format_handler = format_handler[:p]
class ChildReaper(object): def __init__(self, quit_cb): self._quit = quit_cb self._children_pids = {} self._dead_pids = set() self._ignored_pids = set() from xpra.log import Logger self._logger = Logger("server", "util") old_python = sys.version_info < (2, 7) or sys.version_info[:2] == (3, 0) if old_python: POLL_DELAY = int(os.environ.get("XPRA_POLL_DELAY", 2)) self._logger.warn("Warning: outdated/buggy version of Python: %s", ".".join(str(x) for x in sys.version_info)) self._logger.warn("switching to process polling every %s seconds to support 'exit-with-children'", POLL_DELAY) #keep track of process objects: self.processes = [] def check_procs(): for proc in self.processes: if proc.poll() is not None: self.add_dead_pid(proc.pid) self.check() self.processes = [proc for proc in self.processes if proc.poll() is None] return True gobject.timeout_add(POLL_DELAY*1000, check_procs) else: #with a less buggy python, we can just check the list of pids #whenever we get a SIGCHLD #however.. subprocess.Popen will no longer work as expected #see: http://bugs.python.org/issue9127 #so we must ensure certain things that exec happen first: from xpra.version_util import get_platform_info_cache get_platform_info_cache() signal.signal(signal.SIGCHLD, self.sigchld) # Check once after the mainloop is running, just in case the exit # conditions are satisfied before we even enter the main loop. # (Programming with unix the signal API sure is annoying.) def check_once(): self.check() return False # Only call once gobject.timeout_add(0, check_once) def add_process(self, process, command, ignore=False): process.command = command assert process.pid>0 self._children_pids[process.pid] = process if ignore: self._ignored_pids.add(process.pid) def check(self): pids = set(self._children_pids.keys()) - self._ignored_pids if pids: for pid, proc in self._children_pids.items(): if proc.poll() is not None: self.add_dead_pid(pid) if pids.issubset(self._dead_pids): self._quit() def sigchld(self, signum, frame): self.reap() def add_dead_pid(self, pid): if pid not in self._dead_pids: proc = self._children_pids.get(pid) if proc: self._logger.info("child '%s' with pid %s has terminated", proc.command, pid) self._dead_pids.add(pid) self.check() def reap(self): while True: try: pid, _ = os.waitpid(-1, os.WNOHANG) except OSError: break if pid == 0: break self.add_dead_pid(pid)
else: auth_caps = None #verify authentication if required: if proto.authenticator: challenge_response = c.strget("challenge_response") client_salt = c.strget("challenge_client_salt") log("processing authentication with %s, response=%s, client_salt=%s", proto.authenticator, challenge_response, binascii.hexlify(client_salt or "")) #send challenge if this is not a response: if not challenge_response: challenge = proto.authenticator.get_challenge() if challenge is None: auth_failed("invalid authentication state: unexpected challenge response") return False salt, digest = challenge log.info("Authentication required, %s sending challenge for '%s' using digest %s", proto.authenticator, username, digest) if digest not in self.digest_modes: auth_failed("cannot proceed without %s digest support" % digest) return False proto.send_now(("challenge", salt, auth_caps or "", digest)) return False if not proto.authenticator.authenticate(challenge_response, client_salt): auth_failed("invalid challenge response") return False log("authentication challenge passed") else: #did the client expect a challenge? if c.boolget("challenge"): self.disconnect_client(proto, "this server does not require authentication") return False
def main(): from xpra.log import Logger log = Logger("util") sp = sys.platform log.info("platform_name(%s)=%s", sp, platform_name(sp, "")) if LINUX: log.info("linux_distribution=%s", get_linux_distribution()) log.info("Ubuntu=%s", is_Ubuntu()) if is_Ubuntu(): log.info("Ubuntu version=%s", getUbuntuVersion()) log.info("Unity=%s", is_unity()) log.info("Fedora=%s", is_Fedora()) log.info("systemd=%s", is_systemd_pid1()) log.info("get_machine_id()=%s", get_machine_id()) log.info("get_user_uuid()=%s", get_user_uuid()) log.info("get_hex_uuid()=%s", get_hex_uuid()) log.info("get_int_uuid()=%s", get_int_uuid())