def get_lpinfo_drv(make_and_model): command = shlex.split(LPINFO) + ["--make-and-model", make_and_model, "-m"] def preexec(): os.setsid() log("get_lpinfo_drv(%s) command=%s", make_and_model, command) try: proc = subprocess.Popen( command, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False, close_fds=True, preexec_fn=preexec, ) except Exception as e: log("get_lp_info_drv(%s) lpinfo command %s failed", make_and_model, command, exc_info=True) log.error("Error: lpinfo command failed to run") log.error(" %s", e) log.error(" command used: '%s'", " ".join(command)) return None # use the global child reaper to make sure this doesn't end up as a zombie from xpra.child_reaper import getChildReaper from xpra.util import nonl cr = getChildReaper() cr.add_process(proc, "lpinfo", command, ignore=True, forget=True) from xpra.make_thread import make_thread def watch_lpinfo(): # give it 15 seconds to run: for _ in range(15): if proc.poll() is not None: return # finished already time.sleep(1) log.warn("Warning: lpinfo command is taking too long") proc.terminate() make_thread(watch_lpinfo, "lpinfo watcher", daemon=True).start() out, err = proc.communicate() if proc.wait() != 0: log.warn("Warning: lpinfo command failed and returned %s", proc.returncode) log.warn(" command used: '%s'", " ".join(command)) return None if sys.version_info[0] >= 3: try: out = out.decode() except: out = str(out) log("lpinfo out=%s", nonl(out)) log("lpinfo err=%s", nonl(err)) if err: log.warn("Warning: lpinfo command produced some warnings:") log.warn(" '%s'", nonl(err)) for line in out.splitlines(): if line.startswith("drv://"): return line.split(" ")[0] return None
def __init__(self, client, opts): self.client = client self._kh_warning = False self._console_handler_registered = self.setup_console_event_listener( True) try: el = get_win32_event_listener(True) if el: el.add_event_callback(win32con.WM_ACTIVATEAPP, self.activateapp) el.add_event_callback(win32con.WM_POWERBROADCAST, self.power_broadcast_event) el.add_event_callback(win32con.WM_MOVE, self.wm_move) el.add_event_callback(WM_WTSSESSION_CHANGE, self.session_change_event) el.add_event_callback(win32con.WM_INPUTLANGCHANGE, self.inputlangchange) el.add_event_callback(win32con.WM_WININICHANGE, self.inichange) except Exception as e: log.error("cannot register focus and power callbacks: %s", e) self.keyboard_hook_id = None if FORWARD_WINDOWS_KEY: from xpra.make_thread import make_thread make_thread(self.init_keyboard_listener, "keyboard-listener", daemon=True).start()
def __init__(self, scheduler, conn, process_packet_cb, get_packet_cb=None): """ You must call this constructor and source_has_more() from the main thread. """ assert scheduler is not None assert conn is not None self.timeout_add = scheduler.timeout_add self.idle_add = scheduler.idle_add self._conn = conn if FAKE_JITTER > 0: from xpra.net.fake_jitter import FakeJitter fj = FakeJitter(self.timeout_add, process_packet_cb) self._process_packet_cb = fj.process_packet_cb else: self._process_packet_cb = process_packet_cb self._write_queue = Queue(1) self._read_queue = Queue(20) self._read_queue_put = self.read_queue_put # Invariant: if .source is None, then _source_has_more == False self._get_packet_cb = get_packet_cb #counters: self.input_stats = {} self.input_packetcount = 0 self.input_raw_packetcount = 0 self.output_stats = {} self.output_packetcount = 0 self.output_raw_packetcount = 0 #initial value which may get increased by client/server after handshake: self.max_packet_size = 256 * 1024 self.abs_max_packet_size = 256 * 1024 * 1024 self.large_packets = ["hello", "window-metadata", "sound-data"] self.send_aliases = {} self.receive_aliases = {} self._log_stats = None #None here means auto-detect self._closed = False self.encoder = "none" self._encoder = self.noencode self.compressor = "none" self._compress = compression.nocompress self.compression_level = 0 self.cipher_in = None self.cipher_in_name = None self.cipher_in_block_size = 0 self.cipher_in_padding = INITIAL_PADDING self.cipher_out = None self.cipher_out_name = None self.cipher_out_block_size = 0 self.cipher_out_padding = INITIAL_PADDING self._write_lock = Lock() from xpra.make_thread import make_thread self._write_thread = make_thread(self._write_thread_loop, "write", daemon=True) self._read_thread = make_thread(self._read_thread_loop, "read", daemon=True) self._read_parser_thread = None #started when needed self._write_format_thread = None #started when needed self._source_has_more = Event()
def invalid_header(self, proto, data): netlog("invalid_header(%s, %s bytes: '%s') input_packetcount=%s, tcp_proxy=%s", proto, len(data or ""), repr_ellipsized(data), proto.input_packetcount, self._tcp_proxy) if proto.input_packetcount==0 and self._tcp_proxy and not proto._closed: #start a new proxy in a thread def run_proxy(): self.start_tcp_proxy(proto, data) make_thread(run_proxy, "web-proxy-for-%s" % proto).start() return err = "invalid packet format, not an xpra client?" proto.gibberish(err, data)
def __init__(self, scheduler, conn, process_packet_cb, get_packet_cb=None): """ You must call this constructor and source_has_more() from the main thread. """ assert scheduler is not None assert conn is not None self.timeout_add = scheduler.timeout_add self.idle_add = scheduler.idle_add self._conn = conn if FAKE_JITTER>0: from xpra.net.fake_jitter import FakeJitter fj = FakeJitter(self.timeout_add, process_packet_cb) self._process_packet_cb = fj.process_packet_cb else: self._process_packet_cb = process_packet_cb self._write_queue = Queue(1) self._read_queue = Queue(20) self._read_queue_put = self.read_queue_put # Invariant: if .source is None, then _source_has_more == False self._get_packet_cb = get_packet_cb #counters: self.input_stats = {} self.input_packetcount = 0 self.input_raw_packetcount = 0 self.output_stats = {} self.output_packetcount = 0 self.output_raw_packetcount = 0 #initial value which may get increased by client/server after handshake: self.max_packet_size = 256*1024 self.abs_max_packet_size = 256*1024*1024 self.large_packets = ["hello", "window-metadata"] self.send_aliases = {} self.receive_aliases = {} self._log_stats = None #None here means auto-detect self._closed = False self.encoder = "none" self._encoder = self.noencode self.compressor = "none" self._compress = compression.nocompress self.compression_level = 0 self.cipher_in = None self.cipher_in_name = None self.cipher_in_block_size = 0 self.cipher_in_padding = INITIAL_PADDING self.cipher_out = None self.cipher_out_name = None self.cipher_out_block_size = 0 self.cipher_out_padding = INITIAL_PADDING self._write_lock = Lock() from xpra.make_thread import make_thread self._write_thread = make_thread(self._write_thread_loop, "write", daemon=True) self._read_thread = make_thread(self._read_thread_loop, "read", daemon=True) self._read_parser_thread = None #started when needed self._write_format_thread = None #started when needed self._source_has_more = Event()
def get_all_info(self, callback, proto, *args): ui_info = self.get_ui_info(proto, *args) def in_thread(*args): #this runs in a non-UI thread try: info = self.get_info(proto, *args) ui_info.update(info) except Exception as e: log.error("error during info collection: %s", e, exc_info=True) callback(proto, ui_info) make_thread(in_thread, "Info", daemon=True).start()
def __init__(self, client, _opts): self.client = client self._kh_warning = False self._console_handler_added = False self._screensaver_state = False self._screensaver_timer = 0 self._exit = False if SCREENSAVER_LISTENER_POLL_DELAY > 0: def log_screensaver(): v = bool( GetIntSystemParametersInfo( win32con.SPI_GETSCREENSAVERRUNNING)) log("SPI_GETSCREENSAVERRUNNING=%s", v) if self._screensaver_state != v: self._screensaver_state = v if v: self.client.suspend() else: self.client.resume() return True self._screensaver_timer = client.timeout_add( SCREENSAVER_LISTENER_POLL_DELAY * 1000, log_screensaver) if CONSOLE_EVENT_LISTENER: self._console_handler_added = self.setup_console_event_listener( True) from xpra.platform.win32.win32_events import get_win32_event_listener try: el = get_win32_event_listener(True) self._el = el if el: el.add_event_callback(win32con.WM_ACTIVATEAPP, self.activateapp) el.add_event_callback(win32con.WM_POWERBROADCAST, self.power_broadcast_event) el.add_event_callback(win32con.WM_MOVE, self.wm_move) el.add_event_callback(WM_WTSSESSION_CHANGE, self.session_change_event) el.add_event_callback(win32con.WM_INPUTLANGCHANGE, self.inputlangchange) el.add_event_callback(win32con.WM_WININICHANGE, self.inichange) except Exception as e: log.error("Error: cannot register focus and power callbacks:") log.error(" %s", e) self.keyboard_hook_id = None if FORWARD_WINDOWS_KEY: from xpra.make_thread import make_thread make_thread(self.init_keyboard_listener, "keyboard-listener", daemon=True).start()
def invalid_header(self, proto, data): netlog( "invalid_header(%s, %s bytes: '%s') input_packetcount=%s, tcp_proxy=%s", proto, len(data or ""), repr_ellipsized(data), proto.input_packetcount, self._tcp_proxy) if proto.input_packetcount == 0 and self._tcp_proxy and not proto._closed: #start a new proxy in a thread def run_proxy(): self.start_tcp_proxy(proto, data) make_thread(run_proxy, "web-proxy-for-%s" % proto).start() return err = "invalid packet format, not an xpra client?" proto.gibberish(err, data)
def __init__(self, scheduler, conn, auth, process_packet_cb, get_rfb_pixelformat, session_name="Xpra"): """ You must call this constructor and source_has_more() from the main thread. """ assert scheduler is not None assert conn is not None self.timeout_add = scheduler.timeout_add self.idle_add = scheduler.idle_add self._conn = conn self._authenticator = auth self._process_packet_cb = process_packet_cb self._get_rfb_pixelformat = get_rfb_pixelformat self.session_name = session_name self._write_queue = Queue() self._buffer = b"" self._challenge = None self.share = False #counters: self.input_packetcount = 0 self.input_raw_packetcount = 0 self.output_packetcount = 0 self.output_raw_packetcount = 0 self._protocol_version = () self._closed = False self._packet_parser = self._parse_protocol_handshake self._write_thread = None self._read_thread = make_thread(self._read_thread_loop, "read", daemon=True)
def read_queue_put(self, data): #start the parse thread if needed: if not self._read_parser_thread and not self._closed: self._read_parser_thread = make_thread( self._read_parse_thread_loop, "parse", daemon=True) self._read_parser_thread.start() self._read_queue.put(data)
def read_queue_put(self, data): #start the parse thread if needed: if not self._read_parser_thread and not self._closed: from xpra.make_thread import make_thread self._read_parser_thread = make_thread(self._read_parse_thread_loop, "parse", daemon=True) self._read_parser_thread.start() self._read_queue.put(data)
def __init__(self, scheduler, conn, process_packet_cb, data=b""): assert scheduler is not None assert conn is not None self.timeout_add = scheduler.timeout_add self.idle_add = scheduler.idle_add self._conn = conn self._process_packet_cb = process_packet_cb self._write_queue = Queue() self._buffer = data self._challenge = None self.share = False #counters: self.input_packetcount = 0 self.input_raw_packetcount = 0 self.output_packetcount = 0 self.output_raw_packetcount = 0 self._closed = False self._packet_parser = self._parse_protocol_handshake self._write_thread = None self._read_thread = make_thread(self._read_thread_loop, "read", daemon=True) self.log = None if RFB_LOG: self.log = open(RFB_LOG, "w")
def get_lpinfo_drv(make_and_model): command = shlex.split(LPINFO)+["--make-and-model", make_and_model, "-m"] def preexec(): os.setsid() log("get_lpinfo_drv(%s) command=%s", make_and_model, command) try: proc = subprocess.Popen(command, stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False, close_fds=True, preexec_fn=preexec) except Exception as e: log("get_lp_info_drv(%s) lpinfo command %s failed", make_and_model, command, exc_info=True) log.error("Error: lpinfo command failed to run") log.error(" %s", e) log.error(" command used: '%s'", " ".join(command)) return None #use the global child reaper to make sure this doesn't end up as a zombie from xpra.child_reaper import getChildReaper from xpra.util import nonl cr = getChildReaper() cr.add_process(proc, "lpinfo", command, ignore=True, forget=True) from xpra.make_thread import make_thread def watch_lpinfo(): #give it 15 seconds to run: for _ in range(15): if proc.poll() is not None: return #finished already time.sleep(1) log.warn("Warning: lpinfo command is taking too long") proc.terminate() make_thread(watch_lpinfo, "lpinfo watcher", daemon=True).start() out, err = proc.communicate() if proc.wait()!=0: log.warn("Warning: lpinfo command failed and returned %s", proc.returncode) log.warn(" command used: '%s'", " ".join(command)) return None if sys.version_info[0]>=3: try: out = out.decode() except: out = str(out) log("lpinfo out=%s", nonl(out)) log("lpinfo err=%s", nonl(err)) if err: log.warn("Warning: lpinfo command produced some warnings:") log.warn(" '%s'", nonl(err)) for line in out.splitlines(): if line.startswith("drv://"): return line.split(" ")[0] return None
def source_has_more(self): self._source_has_more.set() #start the format thread: if not self._write_format_thread and not self._closed: self._write_format_thread = make_thread( self._write_format_thread_loop, "format", daemon=True) self._write_format_thread.start() INJECT_FAULT(self)
def get_all_info(self, callback, proto=None, *args): start = time.time() ui_info = self.get_ui_info(proto, *args) end = time.time() log("get_all_info: ui info collected in %ims", (end-start)*1000) def in_thread(*args): start = time.time() #this runs in a non-UI thread try: info = self.get_info(proto, *args) merge_dicts(ui_info, info) except Exception as e: log.error("error during info collection: %s", e, exc_info=True) end = time.time() log("get_all_info: non ui info collected in %ims", (end-start)*1000) callback(proto, ui_info) make_thread(in_thread, "Info", daemon=True).start()
def source_has_more(self): self._source_has_more.set() #start the format thread: if not self._write_format_thread and not self._closed: from xpra.make_thread import make_thread self._write_format_thread = make_thread(self._write_format_thread_loop, "format", daemon=True) self._write_format_thread.start() INJECT_FAULT(self)
def start(self): if self.last_UI_thread_time>0: log.warn("UI thread watcher already started!") return #run once to initialize: self.UI_thread_wakeup() if self.polling_timeout>0: make_thread(self.poll_UI_loop, "UI thread polling").start() else: log("not starting an IO polling thread") if FAKE_UI_LOCKUPS>0: #watch out: sleeping in UI thread! def sleep_in_ui_thread(*args): t = threading.current_thread() log.warn("sleep_in_ui_thread%s pausing %s for %ims", args, t, FAKE_UI_LOCKUPS) time.sleep(FAKE_UI_LOCKUPS/1000.0) return True self.timeout_add(10*1000+FAKE_UI_LOCKUPS, sleep_in_ui_thread)
def __init__(self, client, opts): self.client = client self._kh_warning = False self._console_handler_registered = self.setup_console_event_listener(True) try: el = get_win32_event_listener(True) if el: el.add_event_callback(win32con.WM_ACTIVATEAPP, self.activateapp) el.add_event_callback(win32con.WM_POWERBROADCAST, self.power_broadcast_event) el.add_event_callback(win32con.WM_MOVE, self.wm_move) el.add_event_callback(WM_WTSSESSION_CHANGE, self.session_change_event) el.add_event_callback(win32con.WM_INPUTLANGCHANGE, self.inputlangchange) el.add_event_callback(win32con.WM_WININICHANGE, self.inichange) except Exception as e: log.error("cannot register focus and power callbacks: %s", e) self.keyboard_hook_id = None if FORWARD_WINDOWS_KEY: from xpra.make_thread import make_thread make_thread(self.init_keyboard_listener, "keyboard-listener", daemon=True).start()
def start(self): if self.last_UI_thread_time > 0: log.warn("UI thread watcher already started!") return #run once to initialize: self.UI_thread_wakeup() if self.polling_timeout > 0: make_thread(self.poll_UI_loop, "UI thread polling").start() else: log("not starting an IO polling thread") if FAKE_UI_LOCKUPS > 0: #watch out: sleeping in UI thread! def sleep_in_ui_thread(*args): t = threading.current_thread() log.warn("sleep_in_ui_thread%s pausing %s for %ims", args, t, FAKE_UI_LOCKUPS) time.sleep(FAKE_UI_LOCKUPS / 1000.0) return True self.timeout_add(10 * 1000 + FAKE_UI_LOCKUPS, sleep_in_ui_thread)
def source_has_more(self): shm = self._source_has_more if not shm or self._closed: return shm.set() #start the format thread: if not self._write_format_thread and not self._closed: self._write_format_thread = make_thread(self._write_format_thread_loop, "format", daemon=True) self._write_format_thread.start() #from now on, take shortcut: self.source_has_more = self._source_has_more.set
def get_all_info(self, callback, proto=None, *args): start = time.time() ui_info = self.get_ui_info(proto, *args) end = time.time() log("get_all_info: ui info collected in %ims", (end - start) * 1000) def in_thread(*args): start = time.time() #this runs in a non-UI thread try: info = self.get_info(proto, *args) merge_dicts(ui_info, info) except Exception as e: log.error("error during info collection: %s", e, exc_info=True) end = time.time() log("get_all_info: non ui info collected in %ims", (end - start) * 1000) callback(proto, ui_info) make_thread(in_thread, "Info", daemon=True).start()
def run(self): log("ProxyProcess.run() pid=%s, uid=%s, gid=%s", os.getpid(), os.getuid(), os.getgid()) #change uid and gid: if os.getgid() != self.gid: os.setgid(self.gid) if os.getuid() != self.uid: os.setuid(self.uid) log("ProxyProcess.run() new uid=%s, gid=%s", os.getuid(), os.getgid()) if self.env_options: #TODO: whitelist env update? os.environ.update(self.env_options) self.video_init() log.info("new proxy instance started") log.info(" for client %s", self.client_conn) log.info(" and server %s", self.server_conn) signal.signal(signal.SIGTERM, self.signal_quit) signal.signal(signal.SIGINT, self.signal_quit) log("registered signal handler %s", self.signal_quit) make_thread(self.server_message_queue, "server message queue").start() if not self.create_control_socket(): #TODO: should send a message to the client return self.control_socket_thread = make_thread(self.control_socket_loop, "control") self.control_socket_thread.start() self.main_queue = Queue() #setup protocol wrappers: self.server_packets = Queue(PROXY_QUEUE_SIZE) self.client_packets = Queue(PROXY_QUEUE_SIZE) self.client_protocol = Protocol(self, self.client_conn, self.process_client_packet, self.get_client_packet) self.client_protocol.restore_state(self.client_state) self.server_protocol = Protocol(self, self.server_conn, self.process_server_packet, self.get_server_packet) #server connection tweaks: self.server_protocol.large_packets.append("draw") self.server_protocol.large_packets.append("window-icon") self.server_protocol.large_packets.append("keymap-changed") self.server_protocol.large_packets.append("server-settings") if self.caps.boolget("file-transfer"): self.server_protocol.large_packets.append("send-file") self.server_protocol.set_compression_level( self.session_options.get("compression_level", 0)) self.server_protocol.enable_default_encoder() self.lost_windows = set() self.encode_queue = Queue() self.encode_thread = make_thread(self.encode_loop, "encode") self.encode_thread.start() log("starting network threads") self.server_protocol.start() self.client_protocol.start() #forward the hello packet: hello_packet = ("hello", self.filter_client_caps(self.caps)) self.queue_server_packet(hello_packet) self.timeout_add(VIDEO_TIMEOUT * 1000, self.timeout_video_encoders) try: self.run_queue() except KeyboardInterrupt as e: self.stop(str(e)) finally: log("ProxyProcess.run() ending %s", os.getpid())
def start_proxy(self, client_proto, c, auth_caps): assert client_proto.authenticator is not None #find the target server session: def disconnect(reason, *extras): self.send_disconnect(client_proto, reason, *extras) try: sessions = client_proto.authenticator.get_sessions() except Exception as e: log.error("failed to get the list of sessions: %s", e) disconnect(AUTHENTICATION_ERROR) return if sessions is None: disconnect(SESSION_NOT_FOUND, "no sessions found") return log("start_proxy(%s, {..}, %s) found sessions: %s", client_proto, auth_caps, sessions) uid, gid, displays, env_options, session_options = sessions #log("unused options: %s, %s", env_options, session_options) if len(displays)==0: disconnect(SESSION_NOT_FOUND, "no displays found") return display = c.strget("display") proxy_virtual_display = os.environ.get("DISPLAY") #ensure we don't loop back to the proxy: if proxy_virtual_display in displays: displays.remove(proxy_virtual_display) if display==proxy_virtual_display: disconnect(SESSION_NOT_FOUND, "invalid display") return if display: if display not in displays: disconnect(SESSION_NOT_FOUND, "display not found") return else: if len(displays)!=1: disconnect(SESSION_NOT_FOUND, "please specify a display (more than one available)") return display = displays[0] log("start_proxy(%s, {..}, %s) using server display at: %s", client_proto, auth_caps, display) def parse_error(*args): disconnect(SESSION_NOT_FOUND, "invalid display string") log.warn("parse error on %s: %s", display, args) raise Exception("parse error on %s: %s" % (display, args)) opts = make_defaults_struct() opts.username = client_proto.authenticator.username disp_desc = parse_display_name(parse_error, opts, display) log("display description(%s) = %s", display, disp_desc) try: server_conn = connect_to(disp_desc) except Exception as e: log.error("cannot start proxy connection to %s: %s", disp_desc, e, exc_info=True) disconnect(SESSION_NOT_FOUND, "failed to connect to display") return log("server connection=%s", server_conn) #no other packets should be arriving until the proxy instance responds to the initial hello packet def unexpected_packet(packet): if packet: log.warn("received an unexpected packet on the proxy connection: %s", repr_ellipsized(packet)) client_conn = client_proto.steal_connection(unexpected_packet) client_state = client_proto.save_state() cipher = None encryption_key = None if auth_caps: cipher = auth_caps.get("cipher") if cipher: encryption_key = self.get_encryption_key(client_proto.authenticator, client_proto.keyfile) log("start_proxy(..) client connection=%s", client_conn) log("start_proxy(..) client state=%s", client_state) #this may block, so run it in a thread: def do_start_proxy(): log("do_start_proxy()") message_queue = MQueue() try: ioe = client_proto.wait_for_io_threads_exit(5+self._socket_timeout) if not ioe: log.error("some network IO threads have failed to terminate!") return client_conn.set_active(True) assert uid!=0 and gid!=0 process = ProxyInstanceProcess(uid, gid, env_options, session_options, self._socket_dir, self.video_encoders, self.csc_modules, client_conn, client_state, cipher, encryption_key, server_conn, c, message_queue) log("starting %s from pid=%s", process, os.getpid()) self.processes[process] = (display, message_queue) process.start() log("process started") finally: #now we can close our handle on the connection: client_conn.close() server_conn.close() message_queue.put("socket-handover-complete") #FIXME: remove processes that have terminated make_thread(do_start_proxy, "start_proxy(%s)" % client_conn).start()
def run(self): log("ProxyProcess.run() pid=%s, uid=%s, gid=%s", os.getpid(), os.getuid(), os.getgid()) #change uid and gid: if os.getgid()!=self.gid: os.setgid(self.gid) if os.getuid()!=self.uid: os.setuid(self.uid) log("ProxyProcess.run() new uid=%s, gid=%s", os.getuid(), os.getgid()) if self.env_options: #TODO: whitelist env update? os.environ.update(self.env_options) self.video_init() log.info("new proxy instance started") log.info(" for client %s", self.client_conn) log.info(" and server %s", self.server_conn) signal.signal(signal.SIGTERM, self.signal_quit) signal.signal(signal.SIGINT, self.signal_quit) log("registered signal handler %s", self.signal_quit) make_thread(self.server_message_queue, "server message queue").start() if not self.create_control_socket(): #TODO: should send a message to the client return self.control_socket_thread = make_thread(self.control_socket_loop, "control") self.control_socket_thread.start() self.main_queue = Queue() #setup protocol wrappers: self.server_packets = Queue(PROXY_QUEUE_SIZE) self.client_packets = Queue(PROXY_QUEUE_SIZE) self.client_protocol = Protocol(self, self.client_conn, self.process_client_packet, self.get_client_packet) self.client_protocol.restore_state(self.client_state) self.server_protocol = Protocol(self, self.server_conn, self.process_server_packet, self.get_server_packet) #server connection tweaks: self.server_protocol.large_packets.append("draw") self.server_protocol.large_packets.append("window-icon") self.server_protocol.large_packets.append("keymap-changed") self.server_protocol.large_packets.append("server-settings") if self.caps.boolget("file-transfer"): self.server_protocol.large_packets.append("send-file") self.server_protocol.set_compression_level(self.session_options.get("compression_level", 0)) self.server_protocol.enable_default_encoder() self.lost_windows = set() self.encode_queue = Queue() self.encode_thread = make_thread(self.encode_loop, "encode") self.encode_thread.start() log("starting network threads") self.server_protocol.start() self.client_protocol.start() #forward the hello packet: hello_packet = ("hello", self.filter_client_caps(self.caps)) self.queue_server_packet(hello_packet) self.timeout_add(VIDEO_TIMEOUT*1000, self.timeout_video_encoders) try: self.run_queue() except KeyboardInterrupt as e: self.stop(str(e)) finally: log("ProxyProcess.run() ending %s", os.getpid())
def start_proxy(self, client_proto, c, auth_caps): assert client_proto.authenticator is not None #find the target server session: def disconnect(reason, *extras): self.send_disconnect(client_proto, reason, *extras) try: sessions = client_proto.authenticator.get_sessions() except Exception as e: log.error("failed to get the list of sessions: %s", e) disconnect(AUTHENTICATION_ERROR) return if sessions is None: disconnect(SESSION_NOT_FOUND, "no sessions found") return log("start_proxy(%s, {..}, %s) found sessions: %s", client_proto, auth_caps, sessions) uid, gid, displays, env_options, session_options = sessions #log("unused options: %s, %s", env_options, session_options) if len(displays)==0: disconnect(SESSION_NOT_FOUND, "no displays found") return display = c.strget("display") proxy_virtual_display = os.environ.get("DISPLAY") #ensure we don't loop back to the proxy: if proxy_virtual_display in displays: displays.remove(proxy_virtual_display) if display==proxy_virtual_display: disconnect(SESSION_NOT_FOUND, "invalid display") return if display: if display not in displays: disconnect(SESSION_NOT_FOUND, "display not found") return else: if len(displays)!=1: disconnect(SESSION_NOT_FOUND, "please specify a display (more than one available)") return display = displays[0] log("start_proxy(%s, {..}, %s) using server display at: %s", client_proto, auth_caps, display) def parse_error(*args): disconnect(SESSION_NOT_FOUND, "invalid display string") log.warn("parse error on %s: %s", display, args) raise Exception("parse error on %s: %s" % (display, args)) opts = make_defaults_struct() opts.username = client_proto.authenticator.username disp_desc = parse_display_name(parse_error, opts, display) log("display description(%s) = %s", display, disp_desc) try: server_conn = connect_to(disp_desc) except Exception as e: log.error("cannot start proxy connection to %s: %s", disp_desc, e, exc_info=True) disconnect(SESSION_NOT_FOUND, "failed to connect to display") return log("server connection=%s", server_conn) #no other packets should be arriving until the proxy instance responds to the initial hello packet def unexpected_packet(packet): if packet: log.warn("received an unexpected packet on the proxy connection: %s", repr_ellipsized(packet)) client_conn = client_proto.steal_connection(unexpected_packet) client_state = client_proto.save_state() cipher = None encryption_key = None if auth_caps: cipher = auth_caps.get("cipher") if cipher: encryption_key = self.get_encryption_key(client_proto.authenticator, client_proto.keyfile) log("start_proxy(..) client connection=%s", client_conn) log("start_proxy(..) client state=%s", client_state) #this may block, so run it in a thread: def do_start_proxy(): log("do_start_proxy()") message_queue = MQueue() try: ioe = client_proto.wait_for_io_threads_exit(0.5+self._socket_timeout) if not ioe: log.error("some network IO threads have failed to terminate!") return client_conn.set_active(True) assert uid!=0 and gid!=0 process = ProxyInstanceProcess(uid, gid, env_options, session_options, self._socket_dir, self.video_encoders, self.csc_modules, client_conn, client_state, cipher, encryption_key, server_conn, c, message_queue) log("starting %s from pid=%s", process, os.getpid()) self.processes[process] = (display, message_queue) process.start() log("process started") finally: #now we can close our handle on the connection: client_conn.close() server_conn.close() message_queue.put("socket-handover-complete") #FIXME: remove processes that have terminated make_thread(do_start_proxy, "start_proxy(%s)" % client_conn).start()