Coverage for /home/antoine/projects/xpra-git/dist/python3/lib64/python/xpra/platform/dotxpra.py : 63%
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.
7import os.path
8import glob
9import socket
10import errno
11import stat
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
17DISPLAY_PREFIX = ":"
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)
25def strip_display_prefix(s):
26 if s.startswith(DISPLAY_PREFIX):
27 return s[len(DISPLAY_PREFIX):]
28 return s
30def debug(msg, *args, **kwargs):
31 log = get_util_logger()
32 log(msg, *args, **kwargs)
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]
51 def osexpand(self, v):
52 return osexpand(v, self.username, self.uid, self.gid)
54 def __repr__(self):
55 return "DotXpra(%s, %s - %i:%i - %s)" % (self._sockdir, self._sockdirs, self.uid, self.gid, self.username)
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)
79 def socket_expand(self, path):
80 return osexpand(path, self.username, uid=self.uid, gid=self.gid)
82 def norm_socket_paths(self, local_display_name):
83 return [norm_makepath(x, local_display_name) for x in self._sockdirs]
85 def socket_path(self, local_display_name):
86 return norm_makepath(self._sockdir, local_display_name)
88 LIVE = LIVE
89 DEAD = DEAD
90 UNKNOWN = UNKNOWN
91 INACCESSIBLE = INACCESSIBLE
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)
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)))
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))
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
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
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
229#win32 re-defines DotXpra for namedpipes:
230platform_import(globals(), "dotxpra", False,
231 "DotXpra",
232 "DISPLAY_PREFIX",
233 "norm_makepath")