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) 2008 Nathaniel Smith <njs@pobox.com> 

3# Copyright (C) 2011-2018 Antoine Martin <antoine@xpra.org> 

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

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

6 

7import os.path 

8import glob 

9import socket 

10import errno 

11import stat 

12 

13from xpra.os_util import get_util_logger, osexpand, umask_context 

14from xpra.platform.dotxpra_common import PREFIX, LIVE, DEAD, UNKNOWN, INACCESSIBLE 

15from xpra.platform import platform_import 

16 

17DISPLAY_PREFIX = ":" 

18 

19 

20def norm_makepath(dirpath, name): 

21 if DISPLAY_PREFIX and name.startswith(DISPLAY_PREFIX): 

22 name = name[len(DISPLAY_PREFIX):] 

23 return os.path.join(dirpath, PREFIX + name) 

24 

25def strip_display_prefix(s): 

26 if s.startswith(DISPLAY_PREFIX): 

27 return s[len(DISPLAY_PREFIX):] 

28 return s 

29 

30def debug(msg, *args, **kwargs): 

31 log = get_util_logger() 

32 log(msg, *args, **kwargs) 

33 

34 

35class DotXpra: 

36 def __init__(self, sockdir=None, sockdirs=None, actual_username="", uid=0, gid=0): 

37 self.uid = uid or os.getuid() 

38 self.gid = gid or os.getgid() 

39 self.username = actual_username 

40 sockdirs = sockdirs or [] 

41 if not sockdir: 

42 if sockdirs: 

43 sockdir = sockdirs[0] 

44 else: 

45 sockdir = "undefined" 

46 elif sockdir not in sockdirs: 

47 sockdirs.insert(0, sockdir) 

48 self._sockdir = self.osexpand(sockdir) 

49 self._sockdirs = [self.osexpand(x) for x in sockdirs] 

50 

51 def osexpand(self, v): 

52 return osexpand(v, self.username, self.uid, self.gid) 

53 

54 def __repr__(self): 

55 return "DotXpra(%s, %s - %i:%i - %s)" % (self._sockdir, self._sockdirs, self.uid, self.gid, self.username) 

56 

57 def mksockdir(self, d, mode=0o700, uid=None, gid=None): 

58 if not d: 

59 return 

60 if not os.path.exists(d): 

61 if uid is None: 

62 uid = self.uid 

63 if gid is None: 

64 gid = self.gid 

65 with umask_context(0): 

66 os.mkdir(d, mode) 

67 if uid!=os.getuid() or gid!=os.getgid(): 

68 os.lchown(d, uid, gid) 

69 elif d!="/tmp": 

70 try: 

71 st_mode = os.stat(d).st_mode 

72 if st_mode&0o777!=mode: 

73 log = get_util_logger() 

74 log.warn("Warning: socket directory '%s'", d) 

75 log.warn(" expected permissions %s but found %s", oct(mode), oct(st_mode&0o777)) 

76 except OSError: 

77 get_util_logger().log("mksockdir%s", (d, mode, uid, gid), exc_info=True) 

78 

79 def socket_expand(self, path): 

80 return osexpand(path, self.username, uid=self.uid, gid=self.gid) 

81 

82 def norm_socket_paths(self, local_display_name): 

83 return [norm_makepath(x, local_display_name) for x in self._sockdirs] 

84 

85 def socket_path(self, local_display_name): 

86 return norm_makepath(self._sockdir, local_display_name) 

87 

88 LIVE = LIVE 

89 DEAD = DEAD 

90 UNKNOWN = UNKNOWN 

91 INACCESSIBLE = INACCESSIBLE 

92 

93 def get_server_state(self, sockpath, timeout=5): 

94 if not os.path.exists(sockpath): 

95 return DotXpra.DEAD 

96 sock = socket.socket(socket.AF_UNIX) 

97 sock.settimeout(timeout) 

98 try: 

99 sock.connect(sockpath) 

100 return DotXpra.LIVE 

101 except socket.error as e: 

102 debug("get_server_state: connect(%s)=%s (timeout=%s)", sockpath, e, timeout) 

103 err = e.args[0] 

104 if err==errno.EACCES: 

105 return DotXpra.INACCESSIBLE 

106 if err==errno.ECONNREFUSED: 

107 #could be the server is starting up 

108 debug("ECONNREFUSED") 

109 return DotXpra.UNKNOWN 

110 if err==errno.EWOULDBLOCK: 

111 debug("EWOULDBLOCK") 

112 return DotXpra.DEAD 

113 if err==errno.ENOENT: 

114 debug("ENOENT") 

115 return DotXpra.DEAD 

116 return self.UNKNOWN 

117 finally: 

118 try: 

119 sock.close() 

120 except IOError: 

121 debug("%s.close()", sock, exc_info=True) 

122 

123 

124 def displays(self, check_uid=0, matching_state=None): 

125 return list(set(v[1] for v in self.sockets(check_uid, matching_state))) 

126 

127 #this is imported by winswitch, so we can't change the method signature 

128 def sockets(self, check_uid=0, matching_state=None): 

129 #flatten the dictionnary into a list: 

130 return list(set((v[0], v[1]) for details_values in 

131 self.socket_details(check_uid, matching_state).values() for v in details_values)) 

132 

133 def socket_paths(self, check_uid=0, matching_state=None, matching_display=None): 

134 paths = [] 

135 for details in self.socket_details(check_uid, matching_state, matching_display).values(): 

136 for _, _, socket_path in details: 

137 paths.append(socket_path) 

138 return paths 

139 

140 def get_display_state(self, display): 

141 dirs = [] 

142 if self._sockdir!="undefined": 

143 dirs.append(self._sockdir) 

144 dirs += [x for x in self._sockdirs if x not in dirs] 

145 debug("get_display_state(%s) sockdir=%s, sockdirs=%s, testing=%s", 

146 display, self._sockdir, self._sockdirs, dirs) 

147 seen = set() 

148 state = None 

149 for d in dirs: 

150 if not d or not os.path.exists(d): 

151 debug("get_display_state: '%s' path does not exist", d) 

152 continue 

153 real_dir = os.path.realpath(d) 

154 if real_dir in seen: 

155 continue 

156 seen.add(real_dir) 

157 #ie: "~/.xpra/HOSTNAME-" 

158 base = os.path.join(d, PREFIX) 

159 potential_sockets = glob.glob(base + strip_display_prefix(display)) 

160 for sockpath in sorted(potential_sockets): 

161 try: 

162 s = os.stat(sockpath) 

163 except OSError as e: 

164 debug("get_display_state: '%s' path cannot be accessed: %s", sockpath, e) 

165 #socket cannot be accessed 

166 continue 

167 if stat.S_ISSOCK(s.st_mode): 

168 local_display = DISPLAY_PREFIX+sockpath[len(base):] 

169 if local_display!=display: 

170 debug("get_display_state: '%s' display does not match (%s vs %s)", 

171 sockpath, local_display, display) 

172 continue 

173 state = self.get_server_state(sockpath) 

174 if state not in (self.DEAD, self.INACCESSIBLE): 

175 return state 

176 return state or self.DEAD 

177 

178 #find the matching sockets, and return: 

179 #(state, local_display, sockpath) for each socket directory we probe 

180 def socket_details(self, check_uid=0, matching_state=None, matching_display=None): 

181 sd = {} 

182 dirs = [] 

183 if self._sockdir!="undefined": 

184 dirs.append(self._sockdir) 

185 dirs += [x for x in self._sockdirs if x not in dirs] 

186 debug("socket_details%s sockdir=%s, sockdirs=%s, testing=%s", 

187 (check_uid, matching_state, matching_display), self._sockdir, self._sockdirs, dirs) 

188 seen = set() 

189 for d in dirs: 

190 if not d or not os.path.exists(d): 

191 debug("socket_details: '%s' path does not exist", d) 

192 continue 

193 real_dir = os.path.realpath(d) 

194 if real_dir in seen: 

195 continue 

196 seen.add(real_dir) 

197 #ie: "~/.xpra/HOSTNAME-" 

198 base = os.path.join(d, PREFIX) 

199 if matching_display: 

200 dstr = strip_display_prefix(matching_display) 

201 else: 

202 dstr = "*" 

203 potential_sockets = glob.glob(base + dstr) 

204 results = [] 

205 for sockpath in sorted(potential_sockets): 

206 try: 

207 s = os.stat(sockpath) 

208 except OSError as e: 

209 debug("socket_details: '%s' path cannot be accessed: %s", sockpath, e) 

210 #socket cannot be accessed 

211 continue 

212 if stat.S_ISSOCK(s.st_mode): 

213 if check_uid>0: 

214 if s.st_uid!=check_uid: 

215 #socket uid does not match 

216 debug("socket_details: '%s' uid does not match (%s vs %s)", sockpath, s.st_uid, check_uid) 

217 continue 

218 state = self.get_server_state(sockpath) 

219 if matching_state and state!=matching_state: 

220 debug("socket_details: '%s' state does not match (%s vs %s)", sockpath, state, matching_state) 

221 continue 

222 local_display = DISPLAY_PREFIX+sockpath[len(base):] 

223 results.append((state, local_display, sockpath)) 

224 if results: 

225 sd[d] = results 

226 return sd 

227 

228 

229#win32 re-defines DotXpra for namedpipes: 

230platform_import(globals(), "dotxpra", False, 

231 "DotXpra", 

232 "DISPLAY_PREFIX", 

233 "norm_makepath")