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#!/usr/bin/env python 

2# -*- coding: utf-8 -*- 

3# This file is part of Xpra. 

4# Copyright (C) 2013-2021 Antoine Martin <antoine@xpra.org> 

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

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

7 

8import re 

9import os 

10import sys 

11import signal 

12import uuid 

13import time 

14import struct 

15import binascii 

16import threading 

17 

18SIGNAMES = {} 

19for signame in (sig for sig in dir(signal) if sig.startswith("SIG") and not sig.startswith("SIG_")): 

20 SIGNAMES[getattr(signal, signame)] = signame 

21 

22 

23WIN32 = sys.platform.startswith("win") 

24OSX = sys.platform.startswith("darwin") 

25LINUX = sys.platform.startswith("linux") 

26NETBSD = sys.platform.startswith("netbsd") 

27OPENBSD = sys.platform.startswith("openbsd") 

28FREEBSD = sys.platform.startswith("freebsd") 

29 

30POSIX = os.name=="posix" 

31 

32BITS = struct.calcsize(b"P")*8 

33 

34 

35main_thread = threading.current_thread() 

36def is_main_thread(): 

37 return threading.current_thread()==main_thread 

38 

39 

40def get_frame_info(ignore_threads=()): 

41 info = { 

42 "count" : threading.active_count() - len(ignore_threads), 

43 } 

44 try: 

45 import traceback 

46 def nn(x): 

47 if x is None: 

48 return "" 

49 return str(x) 

50 thread_ident = {} 

51 for t in threading.enumerate(): 

52 if t not in ignore_threads: 

53 thread_ident[t.ident] = t.getName() 

54 else: 

55 thread_ident[t.ident] = None 

56 thread_ident.update({ 

57 threading.current_thread().ident : "info", 

58 main_thread.ident : "main", 

59 }) 

60 frames = sys._current_frames() #pylint: disable=protected-access 

61 stack = None 

62 for i,frame_pair in enumerate(frames.items()): 

63 stack = traceback.extract_stack(frame_pair[1]) 

64 tident = thread_ident.get(frame_pair[0], "unknown") 

65 if tident is None: 

66 continue 

67 #sanitize stack to prevent None values (which cause encoding errors with the bencoder) 

68 sanestack = [] 

69 for e in stack: 

70 sanestack.append(tuple([nn(x) for x in e])) 

71 info[i] = { 

72 "" : tident, 

73 "stack" : sanestack, 

74 } 

75 del frames, stack 

76 except Exception as e: 

77 get_util_logger().error("failed to get frame info: %s", e) 

78 return info 

79 

80def get_info_env(): 

81 filtered_env = os.environ.copy() 

82 if filtered_env.get('XPRA_PASSWORD'): 

83 filtered_env['XPRA_PASSWORD'] = "*****" 

84 if filtered_env.get('XPRA_ENCRYPTION_KEY'): 

85 filtered_env['XPRA_ENCRYPTION_KEY'] = "*****" 

86 return filtered_env 

87 

88def get_sysconfig_info(): 

89 import sysconfig 

90 sysinfo = {} 

91 log = get_util_logger() 

92 for attr in ( 

93 "platform", 

94 "python-version", 

95 "config-vars", 

96 "paths", 

97 ): 

98 fn = "get_%s" % attr.replace("-", "_") 

99 getter = getattr(sysconfig, fn, None) 

100 if getter: 

101 try: 

102 sysinfo[attr] = getter() #pylint: disable=not-callable 

103 except ModuleNotFoundError: 

104 log("sysconfig.%s", fn, exc_info=True) 

105 if attr=="config-vars" and WIN32: 

106 continue 

107 log.warn("Warning: failed to collect %s sysconfig information", attr) 

108 except Exception: 

109 log.error("Error calling sysconfig.%s", fn, exc_info=True) 

110 return sysinfo 

111 

112def strtobytes(x) -> bytes: 

113 if isinstance(x, bytes): 

114 return x 

115 return str(x).encode("latin1") 

116def bytestostr(x) -> str: 

117 if isinstance(x, (bytes, bytearray)): 

118 return x.decode("latin1") 

119 return str(x) 

120def hexstr(v) -> str: 

121 return bytestostr(binascii.hexlify(strtobytes(v))) 

122 

123 

124util_logger = None 

125def get_util_logger(): 

126 global util_logger 

127 if not util_logger: 

128 from xpra.log import Logger 

129 util_logger = Logger("util") 

130 return util_logger 

131 

132def memoryview_to_bytes(v) -> bytes: 

133 if isinstance(v, bytes): 

134 return v 

135 if isinstance(v, memoryview): 

136 return v.tobytes() 

137 if isinstance(v, bytearray): 

138 return bytes(v) 

139 return strtobytes(v) 

140 

141 

142def getuid() -> int: 

143 if POSIX: 

144 return os.getuid() 

145 return 0 

146 

147def getgid() -> int: 

148 if POSIX: 

149 return os.getgid() 

150 return 0 

151 

152def get_shell_for_uid(uid) -> str: 

153 if POSIX: 

154 from pwd import getpwuid 

155 try: 

156 return getpwuid(uid).pw_shell 

157 except KeyError: 

158 pass 

159 return "" 

160 

161def get_username_for_uid(uid) -> str: 

162 if POSIX: 

163 from pwd import getpwuid 

164 try: 

165 return getpwuid(uid).pw_name 

166 except KeyError: 

167 pass 

168 return "" 

169 

170def get_home_for_uid(uid) -> str: 

171 if POSIX: 

172 from pwd import getpwuid 

173 try: 

174 return getpwuid(uid).pw_dir 

175 except KeyError: 

176 pass 

177 return "" 

178 

179def get_groups(username): 

180 if POSIX: 

181 import grp #@UnresolvedImport 

182 return [gr.gr_name for gr in grp.getgrall() if username in gr.gr_mem] 

183 return [] 

184 

185def get_group_id(group) -> int: 

186 try: 

187 import grp #@UnresolvedImport 

188 gr = grp.getgrnam(group) 

189 return gr.gr_gid 

190 except (ImportError, KeyError): 

191 return -1 

192 

193 

194def platform_release(release): 

195 if OSX: 

196 SYSTEMVERSION_PLIST = "/System/Library/CoreServices/SystemVersion.plist" 

197 try: 

198 import plistlib 

199 with open(SYSTEMVERSION_PLIST, "rb") as f: 

200 pl = plistlib.load(f) #@UndefinedVariable 

201 return pl['ProductUserVisibleVersion'] 

202 except Exception as e: 

203 get_util_logger().debug("platform_release(%s)", release, exc_info=True) 

204 get_util_logger().warn("Warning: failed to get release information") 

205 get_util_logger().warn(" from '%s':", SYSTEMVERSION_PLIST) 

206 get_util_logger().warn(" %s", e) 

207 return release 

208 

209 

210def platform_name(sys_platform, release=None) -> str: 

211 if not sys_platform: 

212 return "unknown" 

213 PLATFORMS = {"win32" : "Microsoft Windows", 

214 "cygwin" : "Windows/Cygwin", 

215 "linux.*" : "Linux", 

216 "darwin" : "Mac OS X", 

217 "freebsd.*": "FreeBSD", 

218 "os2" : "OS/2", 

219 } 

220 def rel(v): 

221 values = [v] 

222 if isinstance(release, (tuple, list)): 

223 values += list(release) 

224 else: 

225 values.append(release) 

226 return " ".join([str(x) for x in values if x]) 

227 for k,v in PLATFORMS.items(): 

228 regexp = re.compile(k) 

229 if regexp.match(sys_platform): 

230 return rel(v) 

231 return rel(sys_platform) 

232 

233 

234def get_rand_chars(l=16, chars=b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") -> bytes: 

235 import random 

236 return b"".join(chars[random.randint(0, len(chars)-1):][:1] for _ in range(l)) 

237 

238def get_hex_uuid() -> str: 

239 return uuid.uuid4().hex 

240 

241def get_int_uuid() -> int: 

242 return uuid.uuid4().int 

243 

244def get_machine_id() -> str: 

245 """ 

246 Try to get uuid string which uniquely identifies this machine. 

247 Warning: only works on posix! 

248 (which is ok since we only used it on posix at present) 

249 """ 

250 v = "" 

251 if POSIX: 

252 for filename in ("/etc/machine-id", "/var/lib/dbus/machine-id"): 

253 v = load_binary_file(filename) 

254 if v is not None: 

255 break 

256 elif WIN32: 

257 v = uuid.getnode() 

258 return bytestostr(v).strip("\n\r") 

259 

260def get_user_uuid() -> str: 

261 """ 

262 Try to generate a uuid string which is unique to this user. 

263 (relies on get_machine_id to uniquely identify a machine) 

264 """ 

265 user_uuid = os.environ.get("XPRA_USER_UUID") 

266 if user_uuid: 

267 return user_uuid 

268 import hashlib 

269 u = hashlib.sha1() 

270 def uupdate(ustr): 

271 u.update(ustr.encode("utf-8")) 

272 uupdate(get_machine_id()) 

273 if POSIX: 

274 uupdate(u"/") 

275 uupdate(str(os.getuid())) 

276 uupdate(u"/") 

277 uupdate(str(os.getgid())) 

278 uupdate(os.path.expanduser("~/")) 

279 return u.hexdigest() 

280 

281 

282try: 

283 from xpra.monotonic_time import _monotonic_time #@UnresolvedImport 

284 assert _monotonic_time()>0 

285 monotonic_time = _monotonic_time 

286except (ImportError, AssertionError): 

287 monotonic_time = time.time 

288 

289 

290def is_X11() -> bool: 

291 if OSX or WIN32: 

292 return False 

293 try: 

294 from xpra.x11.gtk3.gdk_bindings import is_X11_Display #@UnresolvedImport 

295 return is_X11_Display() 

296 except ImportError: 

297 get_util_logger().debug("failed to load x11 bindings", exc_info=True) 

298 return True 

299 

300saved_env = os.environ.copy() 

301def is_Wayland() -> bool: 

302 return _is_Wayland(saved_env) 

303 

304def _is_Wayland(env : dict) -> bool: 

305 backend = env.get("GDK_BACKEND", "") 

306 if backend=="wayland": 

307 return True 

308 return backend!="x11" and ( 

309 bool(env.get("WAYLAND_DISPLAY")) or env.get("XDG_SESSION_TYPE")=="wayland" 

310 ) 

311 

312 

313def is_distribution_variant(variant=b"Debian") -> bool: 

314 if not POSIX: 

315 return False 

316 try: 

317 v = load_os_release_file() 

318 return any(l.find(variant)>=0 for l in v.splitlines() if l.startswith(b"NAME=")) 

319 except Exception: 

320 pass 

321 try: 

322 if variant==b"RedHat" and get_linux_distribution()[0].startswith(variant): 

323 return True 

324 if get_linux_distribution()[0]==variant: 

325 return True 

326 except Exception: 

327 pass 

328 return False 

329 

330def get_distribution_version_id() -> bool: 

331 if not POSIX: 

332 return "" 

333 try: 

334 v = load_os_release_file() 

335 for line in v.splitlines(): 

336 l = line.decode() 

337 if l.startswith("VERSION_ID="): 

338 return l.split("=", 1)[1].strip('"') 

339 except Exception: 

340 pass 

341 return "" 

342 

343os_release_file_data = False 

344def load_os_release_file() -> bytes: 

345 global os_release_file_data 

346 if os_release_file_data is False: 

347 try: 

348 os_release_file_data = load_binary_file("/etc/os-release") 

349 except OSError: # pragma: no cover 

350 os_release_file_data = None 

351 return os_release_file_data 

352 

353def is_Ubuntu() -> bool: 

354 return is_distribution_variant(b"Ubuntu") 

355 

356def is_Debian() -> bool: 

357 return is_distribution_variant(b"Debian") 

358 

359def is_Raspbian() -> bool: 

360 return is_distribution_variant(b"Raspbian") 

361 

362def is_Fedora() -> bool: 

363 return is_distribution_variant(b"Fedora") 

364 

365def is_Arch() -> bool: 

366 return is_distribution_variant(b"Arch") 

367 

368def is_CentOS() -> bool: 

369 return is_distribution_variant(b"CentOS") 

370 

371def is_RedHat() -> bool: 

372 return is_distribution_variant(b"RedHat") 

373 

374 

375def is_arm() -> bool: 

376 import platform 

377 return platform.uname()[4].startswith("arm") 

378 

379 

380_linux_distribution = None 

381def get_linux_distribution(): 

382 global _linux_distribution 

383 if LINUX and not _linux_distribution: 

384 #linux_distribution is deprecated in Python 3.5 and it causes warnings, 

385 #so use our own code first: 

386 import subprocess 

387 cmd = ["lsb_release", "-a"] 

388 try: 

389 p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 

390 out = p.communicate()[0] 

391 assert p.returncode==0 and out 

392 except Exception: 

393 try: 

394 import platform 

395 _linux_distribution = platform.linux_distribution() #@UndefinedVariable, pylint: disable=deprecated-method, no-member 

396 except Exception: 

397 _linux_distribution = ("unknown", "unknown", "unknown") 

398 else: 

399 d = {} 

400 for line in strtobytes(out).splitlines(): 

401 line = bytestostr(line) 

402 parts = line.rstrip("\n\r").split(":", 1) 

403 if len(parts)==2: 

404 d[parts[0].lower().replace(" ", "_")] = parts[1].strip() 

405 v = [d.get(x) for x in ("distributor_id", "release", "codename")] 

406 if None not in v: 

407 return tuple([bytestostr(x) for x in v]) 

408 return _linux_distribution 

409 

410def is_unity() -> bool: 

411 d = os.environ.get("XDG_CURRENT_DESKTOP", "").lower() 

412 return d.find("unity")>=0 or d.find("ubuntu")>=0 

413 

414def is_gnome() -> bool: 

415 if os.environ.get("XDG_SESSION_DESKTOP", "").split("-", 1)[0] in ("i3", "ubuntu", ): 

416 #"i3-gnome" is not really gnome... ie: the systray does work! 

417 return False 

418 return os.environ.get("XDG_CURRENT_DESKTOP", "").lower().find("gnome")>=0 

419 

420def is_kde() -> bool: 

421 return os.environ.get("XDG_CURRENT_DESKTOP", "").lower().find("kde")>=0 

422 

423 

424def get_loaded_kernel_modules(*modlist): 

425 loaded = [] 

426 if LINUX and os.path.exists("/sys/module"): 

427 for mod in modlist: 

428 if os.path.exists("/sys/module/%s" % mod): # pragma: no cover 

429 loaded.append(mod) 

430 return loaded 

431 

432 

433def is_WSL() -> bool: 

434 if not POSIX: 

435 return False 

436 r = None 

437 for f in ("/proc/sys/kernel/osrelease", "/proc/version"): 

438 r = load_binary_file(f) 

439 if r: 

440 break 

441 return r is not None and r.find(b"Microsoft")>=0 

442 

443 

444def get_generic_os_name() -> str: 

445 return do_get_generic_os_name().lower() 

446 

447def do_get_generic_os_name() -> str: 

448 for k,v in { 

449 "linux" : "Linux", 

450 "darwin" : "MacOS", 

451 "win" : "Win32", 

452 "freebsd" : "FreeBSD", 

453 }.items(): 

454 if sys.platform.startswith(k): 

455 return v 

456 return sys.platform # pragma: no cover 

457 

458 

459def filedata_nocrlf(filename) -> str: 

460 v = load_binary_file(filename) 

461 if v is None: 

462 get_util_logger().error("failed to load '%s'", filename) 

463 return None 

464 return v.strip(b"\n\r") 

465 

466def load_binary_file(filename) -> bytes: 

467 if not os.path.exists(filename): 

468 return None 

469 try: 

470 with open(filename, "rb") as f: 

471 return f.read() 

472 except Exception as e: # pragma: no cover 

473 get_util_logger().warn("Warning: failed to load '%s':", filename) 

474 get_util_logger().warn(" %s", e) 

475 return None 

476 

477def parse_encoded_bin_data(data): 

478 if not data: 

479 return None 

480 header = bytestostr(data).lower()[:10] 

481 if header.startswith("0x"): 

482 return binascii.unhexlify(data[2:]) 

483 import base64 

484 if header.startswith("b64:"): 

485 return base64.b64decode(data[4:]) 

486 if header.startswith("base64:"): 

487 return base64.b64decode(data[7:]) 

488 try: 

489 return binascii.unhexlify(data) 

490 except (TypeError, binascii.Error): 

491 try: 

492 return base64.b64decode(data) 

493 except Exception: 

494 pass 

495 return None 

496 

497 

498#here so we can override it when needed 

499def force_quit(status=1): 

500 os._exit(status) #pylint: disable=protected-access 

501 

502 

503def no_idle(fn, *args, **kwargs): 

504 fn(*args, **kwargs) 

505def register_SIGUSR_signals(idle_add=no_idle): 

506 if not os.name=="posix": 

507 return 

508 from xpra.util import dump_all_frames, dump_gc_frames 

509 def sigusr1(*_args): 

510 log = get_util_logger().info 

511 log("SIGUSR1") 

512 idle_add(dump_all_frames, log) 

513 def sigusr2(*_args): 

514 log = get_util_logger().info 

515 log("SIGUSR2") 

516 idle_add(dump_gc_frames, log) 

517 signal.signal(signal.SIGUSR1, sigusr1) 

518 signal.signal(signal.SIGUSR2, sigusr2) 

519 

520 

521def livefds(): 

522 live = set() 

523 try: 

524 MAXFD = os.sysconf("SC_OPEN_MAX") 

525 except (ValueError, AttributeError): 

526 MAXFD = 256 

527 for fd in range(0, MAXFD): 

528 try: 

529 s = os.fstat(fd) 

530 except Exception: 

531 continue 

532 else: 

533 if s: 

534 live.add(fd) 

535 return live 

536 

537def get_all_fds(): 

538 fd_dirs = ["/dev/fd", "/proc/self/fd"] 

539 fds = [] 

540 for fd_dir in fd_dirs: 

541 if os.path.exists(fd_dir): 

542 for fd_str in os.listdir(fd_dir): 

543 try: 

544 fd = int(fd_str) 

545 fds.append(fd) 

546 except OSError: 

547 # This exception happens inevitably, because the fd used 

548 # by listdir() is already closed. 

549 pass 

550 return fds 

551 sys.stderr.write("Uh-oh, can't close fds, please port me to your system...\n") 

552 return fds 

553 

554def close_all_fds(exceptions=()): 

555 for fd in get_all_fds(): 

556 try: 

557 if fd not in exceptions: 

558 os.close(fd) 

559 except OSError: 

560 # This exception happens inevitably, because the fd used 

561 # by listdir() is already closed. 

562 pass 

563 

564def use_tty(): 

565 from xpra.util import envbool 

566 if envbool("XPRA_NOTTY", False): 

567 return False 

568 from xpra.platform.gui import use_stdin 

569 return use_stdin() 

570 

571 

572def shellsub(s, subs=None): 

573 """ shell style string substitution using the dictionary given """ 

574 if subs: 

575 for var,value in subs.items(): 

576 try: 

577 if isinstance(s, bytes): 

578 s = s.replace(("$%s" % var).encode(), str(value).encode()) 

579 s = s.replace(("${%s}" % var).encode(), str(value).encode()) 

580 else: 

581 s = s.replace("$%s" % var, str(value)) 

582 s = s.replace("${%s}" % var, str(value)) 

583 except (TypeError, ValueError): 

584 raise Exception("failed to substitute '%s' with value '%s' (%s) in '%s'" % ( 

585 var, value, type(value), s)) from None 

586 return s 

587 

588 

589def osexpand(s, actual_username="", uid=0, gid=0, subs=None): 

590 if not s: 

591 return s 

592 def expanduser(s): 

593 if actual_username and s.startswith("~/"): 

594 #replace "~/" with "~$actual_username/" 

595 return os.path.expanduser("~%s/%s" % (actual_username, s[2:])) 

596 return os.path.expanduser(s) 

597 d = dict(subs or {}) 

598 d.update({ 

599 "PID" : os.getpid(), 

600 "HOME" : expanduser("~/"), 

601 }) 

602 if os.name=="posix": 

603 d.update({ 

604 "UID" : uid or os.geteuid(), 

605 "GID" : gid or os.getegid(), 

606 }) 

607 if not OSX: 

608 from xpra.platform.xposix.paths import get_runtime_dir 

609 rd = get_runtime_dir() 

610 if rd and "XDG_RUNTIME_DIR" not in os.environ: 

611 d["XDG_RUNTIME_DIR"] = rd 

612 if actual_username: 

613 d["USERNAME"] = actual_username 

614 d["USER"] = actual_username 

615 #first, expand the substitutions themselves, 

616 #as they may contain references to other variables: 

617 ssub = {} 

618 for k,v in d.items(): 

619 ssub[k] = expanduser(shellsub(str(v), d)) 

620 return os.path.expandvars(expanduser(shellsub(expanduser(s), ssub))) 

621 

622 

623def path_permission_info(filename, ftype=None): 

624 if not POSIX: 

625 return [] 

626 info = [] 

627 try: 

628 import stat 

629 stat_info = os.stat(filename) 

630 if not ftype: 

631 ftype = "file" 

632 if os.path.isdir(filename): 

633 ftype = "directory" 

634 info.append("permissions on %s %s: %s" % (ftype, filename, oct(stat.S_IMODE(stat_info.st_mode)))) 

635 import pwd 

636 import grp #@UnresolvedImport 

637 user = pwd.getpwuid(stat_info.st_uid)[0] 

638 group = grp.getgrgid(stat_info.st_gid)[0] 

639 info.append("ownership %s:%s" % (user, group)) 

640 except Exception as e: 

641 info.append("failed to query path information for '%s': %s" % (filename, e)) 

642 return info 

643 

644 

645#code to temporarily redirect stderr and restore it afterwards, adapted from: 

646#http://stackoverflow.com/questions/5081657/how-do-i-prevent-a-c-shared-library-to-print-on-stdout-in-python 

647#used by the sound code to get rid of the stupid gst warning below: 

648#"** Message: pygobject_register_sinkfunc is deprecated (GstObject)" 

649#ideally we would redirect to a buffer so we could still capture and show these messages in debug out 

650class HideStdErr: 

651 

652 def __init__(self, *_args): 

653 self.savedstderr = None 

654 

655 def __enter__(self): 

656 if POSIX and os.getppid()==1: 

657 #this interferes with server daemonizing? 

658 return 

659 sys.stderr.flush() # <--- important when redirecting to files 

660 self.savedstderr = os.dup(2) 

661 devnull = os.open(os.devnull, os.O_WRONLY) 

662 os.dup2(devnull, 2) 

663 os.close(devnull) 

664 sys.stderr = os.fdopen(self.savedstderr, 'w') 

665 

666 def __exit__(self, *_args): 

667 if self.savedstderr is not None: 

668 os.dup2(self.savedstderr, 2) 

669 

670class HideSysArgv: 

671 

672 def __init__(self, *_args): 

673 self.savedsysargv = None 

674 

675 def __enter__(self): 

676 self.savedsysargv = sys.argv 

677 sys.argv = sys.argv[:1] 

678 

679 def __exit__(self, *_args): 

680 if self.savedsysargv is not None: 

681 sys.argv = self.savedsysargv 

682 

683 

684class OSEnvContext: 

685 

686 def __init__(self): 

687 self.env = os.environ.copy() 

688 def __enter__(self): 

689 pass 

690 def __exit__(self, *_args): 

691 os.environ.clear() 

692 os.environ.update(self.env) 

693 def __repr__(self): 

694 return "OSEnvContext" 

695 

696 

697class FDChangeCaptureContext: 

698 

699 def __init__(self): 

700 self.enter_fds = [] 

701 self.exit_fds = [] 

702 def __enter__(self): 

703 self.enter_fds = get_all_fds() 

704 def __exit__(self, *_args): 

705 self.exit_fds = get_all_fds() 

706 def __repr__(self): 

707 return "FDChangeCaptureContext" 

708 def get_new_fds(self): 

709 return sorted(tuple(set(self.exit_fds)-set(self.enter_fds))) 

710 def get_lost_fds(self): 

711 return sorted(tuple(set(self.enter_fds)-set(self.exit_fds))) 

712 

713class DummyContextManager: 

714 

715 def __enter__(self): 

716 pass 

717 def __exit__(self, *_args): 

718 pass 

719 def __repr__(self): 

720 return "DummyContextManager" 

721 

722 

723#workaround incompatibility between paramiko and gssapi: 

724class nomodule_context: 

725 

726 def __init__(self, module_name): 

727 self.module_name = module_name 

728 def __enter__(self): 

729 self.saved_module = sys.modules.get(self.module_name) 

730 sys.modules[self.module_name] = None 

731 def __exit__(self, *_args): 

732 if sys.modules.get(self.module_name) is None: 

733 if self.saved_module is None: 

734 sys.modules.pop(self.module_name, None) 

735 else: 

736 sys.modules[self.module_name] = self.saved_module 

737 def __repr__(self): 

738 return "nomodule_context(%s)" % self.module_name 

739 

740class umask_context: 

741 

742 def __init__(self, umask): 

743 self.umask = umask 

744 def __enter__(self): 

745 self.orig_umask = os.umask(self.umask) 

746 def __exit__(self, *_args): 

747 os.umask(self.orig_umask) 

748 def __repr__(self): 

749 return "umask_context(%s)" % self.umask 

750 

751 

752def disable_stdout_buffering(): 

753 import gc 

754 # Appending to gc.garbage is a way to stop an object from being 

755 # destroyed. If the old sys.stdout is ever collected, it will 

756 # close() stdout, which is not good. 

757 gc.garbage.append(sys.stdout) 

758 sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0) 

759 

760def setbinarymode(fd): 

761 if WIN32: 

762 #turn on binary mode: 

763 try: 

764 import msvcrt 

765 msvcrt.setmode(fd, os.O_BINARY) #@UndefinedVariable pylint: disable=no-member 

766 except OSError: 

767 get_util_logger().error("setting stdin to binary mode failed", exc_info=True) 

768 

769def find_lib_ldconfig(libname): 

770 libname = re.escape(libname) 

771 

772 arch_map = {"x86_64": "libc6,x86-64"} 

773 arch = arch_map.get(os.uname()[4], "libc6") 

774 

775 pattern = r'^\s+lib%s\.[^\s]+ \(%s(?:,.*?)?\) => (.*lib%s[^\s]+)' % (libname, arch, libname) 

776 

777 #try to find ldconfig first, which may not be on the $PATH 

778 #(it isn't on Debian..) 

779 ldconfig = "ldconfig" 

780 for d in ("/sbin", "/usr/sbin"): 

781 t = os.path.join(d, "ldconfig") 

782 if os.path.exists(t): 

783 ldconfig = t 

784 break 

785 import subprocess 

786 p = subprocess.Popen([ldconfig, "-p"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) 

787 data = bytestostr(p.communicate()[0]) 

788 

789 libpath = re.search(pattern, data, re.MULTILINE) #@UndefinedVariable 

790 if libpath: 

791 libpath = libpath.group(1) 

792 return libpath 

793 

794def find_lib(libname): 

795 #it would be better to rely on dlopen to find the paths 

796 #but I cannot find a way of getting ctypes to tell us the path 

797 #it found the library in 

798 assert POSIX 

799 libpaths = os.environ.get("LD_LIBRARY_PATH", "").split(":") 

800 libpaths.append("/usr/lib64") 

801 libpaths.append("/usr/lib") 

802 for libpath in libpaths: 

803 if not libpath or not os.path.exists(libpath): 

804 continue 

805 libname_so = os.path.join(libpath, libname) 

806 if os.path.exists(libname_so): 

807 return libname_so 

808 return None 

809 

810 

811def pollwait(process, timeout=5): 

812 start = monotonic_time() 

813 v = None 

814 while monotonic_time()-start<timeout: 

815 v = process.poll() 

816 if v is not None: 

817 break 

818 time.sleep(0.1) 

819 return v 

820 

821def which(command): 

822 from distutils.spawn import find_executable 

823 try: 

824 return find_executable(command) 

825 except Exception: 

826 get_util_logger().debug("find_executable(%s)", command, exc_info=True) 

827 return None 

828 

829def get_status_output(*args, **kwargs): 

830 import subprocess 

831 kwargs["stdout"] = subprocess.PIPE 

832 kwargs["stderr"] = subprocess.PIPE 

833 try: 

834 p = subprocess.Popen(*args, **kwargs) 

835 except Exception as e: 

836 print("error running %s,%s: %s" % (args, kwargs, e)) 

837 return -1, "", "" 

838 stdout, stderr = p.communicate() 

839 return p.returncode, stdout.decode("utf-8"), stderr.decode("utf-8") 

840 

841 

842def is_systemd_pid1() -> bool: 

843 if not POSIX: 

844 return False 

845 d = load_binary_file("/proc/1/cmdline") 

846 return d and d.find(b"/systemd")>=0 

847 

848 

849def get_ssh_port() -> int: 

850 #on Linux we can run "ssh -T | grep port" 

851 #but this usually requires root permissions to access /etc/ssh/sshd_config 

852 if WIN32: 

853 return 0 

854 return 22 

855 

856 

857def setuidgid(uid, gid): 

858 if not POSIX: 

859 return 

860 log = get_util_logger() 

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

862 #find the username for the given uid: 

863 from pwd import getpwuid 

864 try: 

865 username = getpwuid(uid).pw_name 

866 except KeyError: 

867 raise Exception("uid %i not found" % uid) from None 

868 #set the groups: 

869 if hasattr(os, "initgroups"): # python >= 2.7 

870 os.initgroups(username, gid) 

871 else: 

872 import grp #@UnresolvedImport 

873 groups = [gr.gr_gid for gr in grp.getgrall() if username in gr.gr_mem] 

874 os.setgroups(groups) 

875 #change uid and gid: 

876 try: 

877 if os.getgid()!=gid: 

878 os.setgid(gid) 

879 except OSError as e: 

880 log.error("Error: cannot change gid to %i:", gid) 

881 if os.getgid()==0: 

882 #don't run as root! 

883 raise 

884 log.error(" %s", e) 

885 log.error(" continuing with gid=%i", os.getgid()) 

886 try: 

887 if os.getuid()!=uid: 

888 os.setuid(uid) 

889 except OSError as e: 

890 log.error("Error: cannot change uid to %i:", uid) 

891 if os.getuid()==0: 

892 #don't run as root! 

893 raise 

894 log.error(" %s", e) 

895 log.error(" continuing with uid=%i", os.getuid()) 

896 log("new uid=%s, gid=%s", os.getuid(), os.getgid()) 

897 

898def get_peercred(sock): 

899 if LINUX: 

900 SO_PEERCRED = 17 

901 log = get_util_logger() 

902 try: 

903 import socket 

904 creds = sock.getsockopt(socket.SOL_SOCKET, SO_PEERCRED, struct.calcsize(b'3i')) 

905 pid, uid, gid = struct.unpack(b'3i',creds) 

906 log("peer: %s", (pid, uid, gid)) 

907 return pid, uid, gid 

908 except IOError as e: 

909 log("getsockopt", exc_info=True) 

910 log.error("Error getting peer credentials: %s", e) 

911 return None 

912 elif FREEBSD: 

913 log.warn("Warning: peercred is not yet implemented for FreeBSD") 

914 #use getpeereid 

915 #then pwd to get the gid? 

916 return None