Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# This file is part of Xpra. 

2# Copyright (C) 2010-2020 Antoine Martin <antoine@xpra.org> 

3# Xpra is released under the terms of the GNU GPL v2, or, at your option, any 

4# later version. See the file COPYING for details. 

5 

6import os 

7 

8from xpra.util import envbool, typedict 

9from xpra.exit_codes import EXIT_MMAP_TOKEN_FAILURE 

10from xpra.scripts.config import TRUE_OPTIONS 

11from xpra.simple_stats import std_unit 

12from xpra.client.mixins.stub_client_mixin import StubClientMixin 

13from xpra.log import Logger 

14 

15log = Logger("mmap") 

16 

17KEEP_MMAP_FILE = envbool("XPRA_KEEP_MMAP_FILE", False) 

18 

19 

20""" 

21Mixin for adding mmap support to a client 

22""" 

23class MmapClient(StubClientMixin): 

24 

25 def __init__(self): 

26 StubClientMixin.__init__(self) 

27 self.mmap_enabled = False 

28 self.mmap = None 

29 self.mmap_token = None 

30 self.mmap_token_index = 0 

31 self.mmap_token_bytes = 0 

32 self.mmap_filename = None 

33 self.mmap_size = 0 

34 self.mmap_group = None 

35 self.mmap_tempfile = None 

36 self.mmap_delete = False 

37 self.supports_mmap = True 

38 

39 

40 def init(self, opts): 

41 self.mmap_group = opts.mmap_group 

42 if os.path.isabs(opts.mmap): 

43 self.mmap_filename = opts.mmap 

44 self.supports_mmap = True 

45 else: 

46 self.supports_mmap = opts.mmap.lower() in TRUE_OPTIONS 

47 

48 

49 def cleanup(self): 

50 self.clean_mmap() 

51 

52 

53 def setup_connection(self, conn): 

54 if self.supports_mmap: 

55 self.init_mmap(self.mmap_filename, self.mmap_group, conn.filename) 

56 

57 

58 def get_root_size(self): 

59 #subclasses should provide real values 

60 return 1024, 1024 

61 

62 def parse_server_capabilities(self, c : typedict) -> bool: 

63 self.mmap_enabled = self.supports_mmap and self.mmap_enabled and c.boolget("mmap_enabled") 

64 log("parse_server_capabilities(..) mmap_enabled=%s", self.mmap_enabled) 

65 if self.mmap_enabled: 

66 from xpra.net.mmap_pipe import read_mmap_token, DEFAULT_TOKEN_INDEX, DEFAULT_TOKEN_BYTES 

67 def iget(attrname, default_value=0): 

68 return c.intget("mmap_%s" % attrname) or c.intget("mmap.%s" % attrname) or default_value 

69 mmap_token = iget("token") 

70 mmap_token_index = iget("token_index", DEFAULT_TOKEN_INDEX) 

71 mmap_token_bytes = iget("token_bytes", DEFAULT_TOKEN_BYTES) 

72 token = read_mmap_token(self.mmap, mmap_token_index, mmap_token_bytes) 

73 if token!=mmap_token: 

74 log.error("Error: mmap token verification failed!") 

75 log.error(" expected '%#x'", token) 

76 log.error(" found '%#x'", mmap_token) 

77 self.mmap_enabled = False 

78 self.quit(EXIT_MMAP_TOKEN_FAILURE) 

79 return 

80 log.info("enabled fast mmap transfers using %sB shared memory area", std_unit(self.mmap_size, unit=1024)) 

81 #the server will have a handle on the mmap file by now, safe to delete: 

82 if not KEEP_MMAP_FILE: 

83 self.clean_mmap() 

84 return True 

85 

86 

87 def get_info(self): 

88 if not self.mmap_enabled: 

89 return {} 

90 mmap_info = self.get_raw_caps() 

91 mmap_info["group"] = self.mmap_group or "" 

92 return { 

93 "mmap" : mmap_info, 

94 } 

95 

96 def get_caps(self) -> dict: 

97 if not self.mmap_enabled: 

98 return {} 

99 raw_caps = self.get_raw_caps() 

100 caps = { 

101 "mmap" : raw_caps, 

102 } 

103 #pre 2.3 servers only use underscore instead of "." prefix for mmap caps: 

104 for k,v in raw_caps.items(): 

105 caps["mmap_%s" % k] = v 

106 return caps 

107 

108 def get_raw_caps(self): 

109 return { 

110 "file" : self.mmap_filename, 

111 "size" : self.mmap_size, 

112 "token" : self.mmap_token, 

113 "token_index" : self.mmap_token_index, 

114 "token_bytes" : self.mmap_token_bytes, 

115 "namespace" : True, #this client understands "mmap.ATTRIBUTE" format 

116 } 

117 

118 def init_mmap(self, mmap_filename, mmap_group, socket_filename): 

119 log("init_mmap(%s, %s, %s)", mmap_filename, mmap_group, socket_filename) 

120 from xpra.os_util import get_int_uuid 

121 from xpra.net.mmap_pipe import init_client_mmap, write_mmap_token, DEFAULT_TOKEN_INDEX, DEFAULT_TOKEN_BYTES 

122 #calculate size: 

123 root_w, root_h = self.get_root_size() 

124 #at least 256MB, or 8 fullscreen RGBX frames: 

125 mmap_size = max(256*1024*1024, root_w*root_h*4*8) 

126 mmap_size = min(1024*1024*1024, mmap_size) 

127 self.mmap_enabled, self.mmap_delete, self.mmap, self.mmap_size, self.mmap_tempfile, self.mmap_filename = \ 

128 init_client_mmap(mmap_group, socket_filename, mmap_size, self.mmap_filename) 

129 if self.mmap_enabled: 

130 self.mmap_token = get_int_uuid() 

131 self.mmap_token_bytes = DEFAULT_TOKEN_BYTES 

132 self.mmap_token_index = self.mmap_size - DEFAULT_TOKEN_BYTES 

133 #self.mmap_token_index = DEFAULT_TOKEN_INDEX*2 

134 #write the token twice: 

135 # once at the old default offset for older servers, 

136 # and at the offset we want to use with new servers 

137 for index in (DEFAULT_TOKEN_INDEX, self.mmap_token_index): 

138 write_mmap_token(self.mmap, self.mmap_token, index, self.mmap_token_bytes) 

139 

140 def clean_mmap(self): 

141 log("XpraClient.clean_mmap() mmap_filename=%s", self.mmap_filename) 

142 if self.mmap_tempfile: 

143 try: 

144 self.mmap_tempfile.close() 

145 except Exception as e: 

146 log("clean_mmap error closing file %s: %s", self.mmap_tempfile, e) 

147 self.mmap_tempfile = None 

148 if self.mmap_delete: 

149 #this should be redundant: closing the tempfile should get it deleted 

150 if self.mmap_filename and os.path.exists(self.mmap_filename): 

151 from xpra.net.mmap_pipe import clean_mmap 

152 clean_mmap(self.mmap_filename) 

153 self.mmap_filename = None