##// END OF EJS Templates
py3: catch PermissionError instead of checking errno == EACCES
Manuel Jacob -
r50202:6f2a57ba default
parent child Browse files
Show More
@@ -1,760 +1,755 b''
1 1 # posix.py - Posix utility function implementations for Mercurial
2 2 #
3 3 # Copyright 2005-2009 Olivia Mackall <olivia@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8
9 9 import errno
10 10 import fcntl
11 11 import getpass
12 12 import grp
13 13 import os
14 14 import pwd
15 15 import re
16 16 import select
17 17 import stat
18 18 import sys
19 19 import tempfile
20 20 import unicodedata
21 21
22 22 from .i18n import _
23 23 from .pycompat import (
24 24 getattr,
25 25 open,
26 26 )
27 27 from . import (
28 28 encoding,
29 29 error,
30 30 policy,
31 31 pycompat,
32 32 )
33 33
34 34 osutil = policy.importmod('osutil')
35 35
36 36 normpath = os.path.normpath
37 37 samestat = os.path.samestat
38 38 abspath = os.path.abspath # re-exports
39 39
40 40 try:
41 41 oslink = os.link
42 42 except AttributeError:
43 43 # Some platforms build Python without os.link on systems that are
44 44 # vaguely unix-like but don't have hardlink support. For those
45 45 # poor souls, just say we tried and that it failed so we fall back
46 46 # to copies.
47 47 def oslink(src, dst):
48 48 raise OSError(
49 49 errno.EINVAL, b'hardlinks not supported: %s to %s' % (src, dst)
50 50 )
51 51
52 52
53 53 readlink = os.readlink
54 54 unlink = os.unlink
55 55 rename = os.rename
56 56 removedirs = os.removedirs
57 57 expandglobs = False
58 58
59 59 umask = os.umask(0)
60 60 os.umask(umask)
61 61
62 62 posixfile = open
63 63
64 64
65 65 def split(p):
66 66 """Same as posixpath.split, but faster
67 67
68 68 >>> import posixpath
69 69 >>> for f in [b'/absolute/path/to/file',
70 70 ... b'relative/path/to/file',
71 71 ... b'file_alone',
72 72 ... b'path/to/directory/',
73 73 ... b'/multiple/path//separators',
74 74 ... b'/file_at_root',
75 75 ... b'///multiple_leading_separators_at_root',
76 76 ... b'']:
77 77 ... assert split(f) == posixpath.split(f), f
78 78 """
79 79 ht = p.rsplit(b'/', 1)
80 80 if len(ht) == 1:
81 81 return b'', p
82 82 nh = ht[0].rstrip(b'/')
83 83 if nh:
84 84 return nh, ht[1]
85 85 return ht[0] + b'/', ht[1]
86 86
87 87
88 88 def openhardlinks():
89 89 '''return true if it is safe to hold open file handles to hardlinks'''
90 90 return True
91 91
92 92
93 93 def nlinks(name):
94 94 '''return number of hardlinks for the given file'''
95 95 return os.lstat(name).st_nlink
96 96
97 97
98 98 def parsepatchoutput(output_line):
99 99 """parses the output produced by patch and returns the filename"""
100 100 pf = output_line[14:]
101 101 if pycompat.sysplatform == b'OpenVMS':
102 102 if pf[0] == b'`':
103 103 pf = pf[1:-1] # Remove the quotes
104 104 else:
105 105 if pf.startswith(b"'") and pf.endswith(b"'") and b" " in pf:
106 106 pf = pf[1:-1] # Remove the quotes
107 107 return pf
108 108
109 109
110 110 def sshargs(sshcmd, host, user, port):
111 111 '''Build argument list for ssh'''
112 112 args = user and (b"%s@%s" % (user, host)) or host
113 113 if b'-' in args[:1]:
114 114 raise error.Abort(
115 115 _(b'illegal ssh hostname or username starting with -: %s') % args
116 116 )
117 117 args = shellquote(args)
118 118 if port:
119 119 args = b'-p %s %s' % (shellquote(port), args)
120 120 return args
121 121
122 122
123 123 def isexec(f):
124 124 """check whether a file is executable"""
125 125 return os.lstat(f).st_mode & 0o100 != 0
126 126
127 127
128 128 def setflags(f, l, x):
129 129 st = os.lstat(f)
130 130 s = st.st_mode
131 131 if l:
132 132 if not stat.S_ISLNK(s):
133 133 # switch file to link
134 134 with open(f, b'rb') as fp:
135 135 data = fp.read()
136 136 unlink(f)
137 137 try:
138 138 os.symlink(data, f)
139 139 except OSError:
140 140 # failed to make a link, rewrite file
141 141 with open(f, b"wb") as fp:
142 142 fp.write(data)
143 143
144 144 # no chmod needed at this point
145 145 return
146 146 if stat.S_ISLNK(s):
147 147 # switch link to file
148 148 data = os.readlink(f)
149 149 unlink(f)
150 150 with open(f, b"wb") as fp:
151 151 fp.write(data)
152 152 s = 0o666 & ~umask # avoid restatting for chmod
153 153
154 154 sx = s & 0o100
155 155 if st.st_nlink > 1 and bool(x) != bool(sx):
156 156 # the file is a hardlink, break it
157 157 with open(f, b"rb") as fp:
158 158 data = fp.read()
159 159 unlink(f)
160 160 with open(f, b"wb") as fp:
161 161 fp.write(data)
162 162
163 163 if x and not sx:
164 164 # Turn on +x for every +r bit when making a file executable
165 165 # and obey umask.
166 166 os.chmod(f, s | (s & 0o444) >> 2 & ~umask)
167 167 elif not x and sx:
168 168 # Turn off all +x bits
169 169 os.chmod(f, s & 0o666)
170 170
171 171
172 172 def copymode(src, dst, mode=None, enforcewritable=False):
173 173 """Copy the file mode from the file at path src to dst.
174 174 If src doesn't exist, we're using mode instead. If mode is None, we're
175 175 using umask."""
176 176 try:
177 177 st_mode = os.lstat(src).st_mode & 0o777
178 178 except FileNotFoundError:
179 179 st_mode = mode
180 180 if st_mode is None:
181 181 st_mode = ~umask
182 182 st_mode &= 0o666
183 183
184 184 new_mode = st_mode
185 185
186 186 if enforcewritable:
187 187 new_mode |= stat.S_IWUSR
188 188
189 189 os.chmod(dst, new_mode)
190 190
191 191
192 192 def checkexec(path):
193 193 """
194 194 Check whether the given path is on a filesystem with UNIX-like exec flags
195 195
196 196 Requires a directory (like /foo/.hg)
197 197 """
198 198
199 199 # VFAT on some Linux versions can flip mode but it doesn't persist
200 200 # a FS remount. Frequently we can detect it if files are created
201 201 # with exec bit on.
202 202
203 203 try:
204 204 EXECFLAGS = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
205 205 basedir = os.path.join(path, b'.hg')
206 206 cachedir = os.path.join(basedir, b'wcache')
207 207 storedir = os.path.join(basedir, b'store')
208 208 if not os.path.exists(cachedir):
209 209 try:
210 210 # we want to create the 'cache' directory, not the '.hg' one.
211 211 # Automatically creating '.hg' directory could silently spawn
212 212 # invalid Mercurial repositories. That seems like a bad idea.
213 213 os.mkdir(cachedir)
214 214 if os.path.exists(storedir):
215 215 copymode(storedir, cachedir)
216 216 else:
217 217 copymode(basedir, cachedir)
218 218 except (IOError, OSError):
219 219 # we other fallback logic triggers
220 220 pass
221 221 if os.path.isdir(cachedir):
222 222 checkisexec = os.path.join(cachedir, b'checkisexec')
223 223 checknoexec = os.path.join(cachedir, b'checknoexec')
224 224
225 225 try:
226 226 m = os.stat(checkisexec).st_mode
227 227 except FileNotFoundError:
228 228 # checkisexec does not exist - fall through ...
229 229 pass
230 230 else:
231 231 # checkisexec exists, check if it actually is exec
232 232 if m & EXECFLAGS != 0:
233 233 # ensure checkisexec exists, check it isn't exec
234 234 try:
235 235 m = os.stat(checknoexec).st_mode
236 236 except FileNotFoundError:
237 237 open(checknoexec, b'w').close() # might fail
238 238 m = os.stat(checknoexec).st_mode
239 239 if m & EXECFLAGS == 0:
240 240 # check-exec is exec and check-no-exec is not exec
241 241 return True
242 242 # checknoexec exists but is exec - delete it
243 243 unlink(checknoexec)
244 244 # checkisexec exists but is not exec - delete it
245 245 unlink(checkisexec)
246 246
247 247 # check using one file, leave it as checkisexec
248 248 checkdir = cachedir
249 249 else:
250 250 # check directly in path and don't leave checkisexec behind
251 251 checkdir = path
252 252 checkisexec = None
253 253 fh, fn = pycompat.mkstemp(dir=checkdir, prefix=b'hg-checkexec-')
254 254 try:
255 255 os.close(fh)
256 256 m = os.stat(fn).st_mode
257 257 if m & EXECFLAGS == 0:
258 258 os.chmod(fn, m & 0o777 | EXECFLAGS)
259 259 if os.stat(fn).st_mode & EXECFLAGS != 0:
260 260 if checkisexec is not None:
261 261 os.rename(fn, checkisexec)
262 262 fn = None
263 263 return True
264 264 finally:
265 265 if fn is not None:
266 266 unlink(fn)
267 267 except (IOError, OSError):
268 268 # we don't care, the user probably won't be able to commit anyway
269 269 return False
270 270
271 271
272 272 def checklink(path):
273 273 """check whether the given path is on a symlink-capable filesystem"""
274 274 # mktemp is not racy because symlink creation will fail if the
275 275 # file already exists
276 276 while True:
277 277 cachedir = os.path.join(path, b'.hg', b'wcache')
278 278 checklink = os.path.join(cachedir, b'checklink')
279 279 # try fast path, read only
280 280 if os.path.islink(checklink):
281 281 return True
282 282 if os.path.isdir(cachedir):
283 283 checkdir = cachedir
284 284 else:
285 285 checkdir = path
286 286 cachedir = None
287 287 name = tempfile.mktemp(
288 288 dir=pycompat.fsdecode(checkdir), prefix=r'checklink-'
289 289 )
290 290 name = pycompat.fsencode(name)
291 291 try:
292 292 fd = None
293 293 if cachedir is None:
294 294 fd = pycompat.namedtempfile(
295 295 dir=checkdir, prefix=b'hg-checklink-'
296 296 )
297 297 target = os.path.basename(fd.name)
298 298 else:
299 299 # create a fixed file to link to; doesn't matter if it
300 300 # already exists.
301 301 target = b'checklink-target'
302 302 try:
303 303 fullpath = os.path.join(cachedir, target)
304 304 open(fullpath, b'w').close()
305 except IOError as inst:
306 # pytype: disable=unsupported-operands
307 if inst[0] == errno.EACCES:
308 # pytype: enable=unsupported-operands
309
305 except PermissionError:
310 306 # If we can't write to cachedir, just pretend
311 307 # that the fs is readonly and by association
312 308 # that the fs won't support symlinks. This
313 309 # seems like the least dangerous way to avoid
314 310 # data loss.
315 311 return False
316 raise
317 312 try:
318 313 os.symlink(target, name)
319 314 if cachedir is None:
320 315 unlink(name)
321 316 else:
322 317 try:
323 318 os.rename(name, checklink)
324 319 except OSError:
325 320 unlink(name)
326 321 return True
327 322 except FileExistsError:
328 323 # link creation might race, try again
329 324 continue
330 325 finally:
331 326 if fd is not None:
332 327 fd.close()
333 328 except AttributeError:
334 329 return False
335 330 except OSError as inst:
336 331 # sshfs might report failure while successfully creating the link
337 332 if inst.errno == errno.EIO and os.path.exists(name):
338 333 unlink(name)
339 334 return False
340 335
341 336
342 337 def checkosfilename(path):
343 338 """Check that the base-relative path is a valid filename on this platform.
344 339 Returns None if the path is ok, or a UI string describing the problem."""
345 340 return None # on posix platforms, every path is ok
346 341
347 342
348 343 def getfsmountpoint(dirpath):
349 344 """Get the filesystem mount point from a directory (best-effort)
350 345
351 346 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
352 347 """
353 348 return getattr(osutil, 'getfsmountpoint', lambda x: None)(dirpath)
354 349
355 350
356 351 def getfstype(dirpath):
357 352 """Get the filesystem type name from a directory (best-effort)
358 353
359 354 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
360 355 """
361 356 return getattr(osutil, 'getfstype', lambda x: None)(dirpath)
362 357
363 358
364 359 def get_password():
365 360 return encoding.strtolocal(getpass.getpass(''))
366 361
367 362
368 363 def setbinary(fd):
369 364 pass
370 365
371 366
372 367 def pconvert(path):
373 368 return path
374 369
375 370
376 371 def localpath(path):
377 372 return path
378 373
379 374
380 375 def samefile(fpath1, fpath2):
381 376 """Returns whether path1 and path2 refer to the same file. This is only
382 377 guaranteed to work for files, not directories."""
383 378 return os.path.samefile(fpath1, fpath2)
384 379
385 380
386 381 def samedevice(fpath1, fpath2):
387 382 """Returns whether fpath1 and fpath2 are on the same device. This is only
388 383 guaranteed to work for files, not directories."""
389 384 st1 = os.lstat(fpath1)
390 385 st2 = os.lstat(fpath2)
391 386 return st1.st_dev == st2.st_dev
392 387
393 388
394 389 # os.path.normcase is a no-op, which doesn't help us on non-native filesystems
395 390 def normcase(path):
396 391 return path.lower()
397 392
398 393
399 394 # what normcase does to ASCII strings
400 395 normcasespec = encoding.normcasespecs.lower
401 396 # fallback normcase function for non-ASCII strings
402 397 normcasefallback = normcase
403 398
404 399 if pycompat.isdarwin:
405 400
406 401 def normcase(path):
407 402 """
408 403 Normalize a filename for OS X-compatible comparison:
409 404 - escape-encode invalid characters
410 405 - decompose to NFD
411 406 - lowercase
412 407 - omit ignored characters [200c-200f, 202a-202e, 206a-206f,feff]
413 408
414 409 >>> normcase(b'UPPER')
415 410 'upper'
416 411 >>> normcase(b'Caf\\xc3\\xa9')
417 412 'cafe\\xcc\\x81'
418 413 >>> normcase(b'\\xc3\\x89')
419 414 'e\\xcc\\x81'
420 415 >>> normcase(b'\\xb8\\xca\\xc3\\xca\\xbe\\xc8.JPG') # issue3918
421 416 '%b8%ca%c3\\xca\\xbe%c8.jpg'
422 417 """
423 418
424 419 try:
425 420 return encoding.asciilower(path) # exception for non-ASCII
426 421 except UnicodeDecodeError:
427 422 return normcasefallback(path)
428 423
429 424 normcasespec = encoding.normcasespecs.lower
430 425
431 426 def normcasefallback(path):
432 427 try:
433 428 u = path.decode('utf-8')
434 429 except UnicodeDecodeError:
435 430 # OS X percent-encodes any bytes that aren't valid utf-8
436 431 s = b''
437 432 pos = 0
438 433 l = len(path)
439 434 while pos < l:
440 435 try:
441 436 c = encoding.getutf8char(path, pos)
442 437 pos += len(c)
443 438 except ValueError:
444 439 c = b'%%%02X' % ord(path[pos : pos + 1])
445 440 pos += 1
446 441 s += c
447 442
448 443 u = s.decode('utf-8')
449 444
450 445 # Decompose then lowercase (HFS+ technote specifies lower)
451 446 enc = unicodedata.normalize('NFD', u).lower().encode('utf-8')
452 447 # drop HFS+ ignored characters
453 448 return encoding.hfsignoreclean(enc)
454 449
455 450
456 451 if pycompat.sysplatform == b'cygwin':
457 452 # workaround for cygwin, in which mount point part of path is
458 453 # treated as case sensitive, even though underlying NTFS is case
459 454 # insensitive.
460 455
461 456 # default mount points
462 457 cygwinmountpoints = sorted(
463 458 [
464 459 b"/usr/bin",
465 460 b"/usr/lib",
466 461 b"/cygdrive",
467 462 ],
468 463 reverse=True,
469 464 )
470 465
471 466 # use upper-ing as normcase as same as NTFS workaround
472 467 def normcase(path):
473 468 pathlen = len(path)
474 469 if (pathlen == 0) or (path[0] != pycompat.ossep):
475 470 # treat as relative
476 471 return encoding.upper(path)
477 472
478 473 # to preserve case of mountpoint part
479 474 for mp in cygwinmountpoints:
480 475 if not path.startswith(mp):
481 476 continue
482 477
483 478 mplen = len(mp)
484 479 if mplen == pathlen: # mount point itself
485 480 return mp
486 481 if path[mplen] == pycompat.ossep:
487 482 return mp + encoding.upper(path[mplen:])
488 483
489 484 return encoding.upper(path)
490 485
491 486 normcasespec = encoding.normcasespecs.other
492 487 normcasefallback = normcase
493 488
494 489 # Cygwin translates native ACLs to POSIX permissions,
495 490 # but these translations are not supported by native
496 491 # tools, so the exec bit tends to be set erroneously.
497 492 # Therefore, disable executable bit access on Cygwin.
498 493 def checkexec(path):
499 494 return False
500 495
501 496 # Similarly, Cygwin's symlink emulation is likely to create
502 497 # problems when Mercurial is used from both Cygwin and native
503 498 # Windows, with other native tools, or on shared volumes
504 499 def checklink(path):
505 500 return False
506 501
507 502
508 503 _needsshellquote = None
509 504
510 505
511 506 def shellquote(s):
512 507 if pycompat.sysplatform == b'OpenVMS':
513 508 return b'"%s"' % s
514 509 global _needsshellquote
515 510 if _needsshellquote is None:
516 511 _needsshellquote = re.compile(br'[^a-zA-Z0-9._/+-]').search
517 512 if s and not _needsshellquote(s):
518 513 # "s" shouldn't have to be quoted
519 514 return s
520 515 else:
521 516 return b"'%s'" % s.replace(b"'", b"'\\''")
522 517
523 518
524 519 def shellsplit(s):
525 520 """Parse a command string in POSIX shell way (best-effort)"""
526 521 return pycompat.shlexsplit(s, posix=True)
527 522
528 523
529 524 def testpid(pid):
530 525 '''return False if pid dead, True if running or not sure'''
531 526 if pycompat.sysplatform == b'OpenVMS':
532 527 return True
533 528 try:
534 529 os.kill(pid, 0)
535 530 return True
536 531 except OSError as inst:
537 532 return inst.errno != errno.ESRCH
538 533
539 534
540 535 def isowner(st):
541 536 """Return True if the stat object st is from the current user."""
542 537 return st.st_uid == os.getuid()
543 538
544 539
545 540 def findexe(command):
546 541 """Find executable for command searching like which does.
547 542 If command is a basename then PATH is searched for command.
548 543 PATH isn't searched if command is an absolute or relative path.
549 544 If command isn't found None is returned."""
550 545 if pycompat.sysplatform == b'OpenVMS':
551 546 return command
552 547
553 548 def findexisting(executable):
554 549 b'Will return executable if existing file'
555 550 if os.path.isfile(executable) and os.access(executable, os.X_OK):
556 551 return executable
557 552 return None
558 553
559 554 if pycompat.ossep in command:
560 555 return findexisting(command)
561 556
562 557 if pycompat.sysplatform == b'plan9':
563 558 return findexisting(os.path.join(b'/bin', command))
564 559
565 560 for path in encoding.environ.get(b'PATH', b'').split(pycompat.ospathsep):
566 561 executable = findexisting(os.path.join(path, command))
567 562 if executable is not None:
568 563 return executable
569 564 return None
570 565
571 566
572 567 def setsignalhandler():
573 568 pass
574 569
575 570
576 571 _wantedkinds = {stat.S_IFREG, stat.S_IFLNK}
577 572
578 573
579 574 def statfiles(files):
580 575 """Stat each file in files. Yield each stat, or None if a file does not
581 576 exist or has a type we don't care about."""
582 577 lstat = os.lstat
583 578 getkind = stat.S_IFMT
584 579 for nf in files:
585 580 try:
586 581 st = lstat(nf)
587 582 if getkind(st.st_mode) not in _wantedkinds:
588 583 st = None
589 584 except OSError as err:
590 585 if err.errno not in (errno.ENOENT, errno.ENOTDIR):
591 586 raise
592 587 st = None
593 588 yield st
594 589
595 590
596 591 def getuser():
597 592 '''return name of current user'''
598 593 return pycompat.fsencode(getpass.getuser())
599 594
600 595
601 596 def username(uid=None):
602 597 """Return the name of the user with the given uid.
603 598
604 599 If uid is None, return the name of the current user."""
605 600
606 601 if uid is None:
607 602 uid = os.getuid()
608 603 try:
609 604 return pycompat.fsencode(pwd.getpwuid(uid)[0])
610 605 except KeyError:
611 606 return b'%d' % uid
612 607
613 608
614 609 def groupname(gid=None):
615 610 """Return the name of the group with the given gid.
616 611
617 612 If gid is None, return the name of the current group."""
618 613
619 614 if gid is None:
620 615 gid = os.getgid()
621 616 try:
622 617 return pycompat.fsencode(grp.getgrgid(gid)[0])
623 618 except KeyError:
624 619 return pycompat.bytestr(gid)
625 620
626 621
627 622 def groupmembers(name):
628 623 """Return the list of members of the group with the given
629 624 name, KeyError if the group does not exist.
630 625 """
631 626 name = pycompat.fsdecode(name)
632 627 return pycompat.rapply(pycompat.fsencode, list(grp.getgrnam(name).gr_mem))
633 628
634 629
635 630 def spawndetached(args):
636 631 return os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0), args[0], args)
637 632
638 633
639 634 def gethgcmd():
640 635 return sys.argv[:1]
641 636
642 637
643 638 def makedir(path, notindexed):
644 639 os.mkdir(path)
645 640
646 641
647 642 def lookupreg(key, name=None, scope=None):
648 643 return None
649 644
650 645
651 646 def hidewindow():
652 647 """Hide current shell window.
653 648
654 649 Used to hide the window opened when starting asynchronous
655 650 child process under Windows, unneeded on other systems.
656 651 """
657 652 pass
658 653
659 654
660 655 class cachestat:
661 656 def __init__(self, path):
662 657 self.stat = os.stat(path)
663 658
664 659 def cacheable(self):
665 660 return bool(self.stat.st_ino)
666 661
667 662 __hash__ = object.__hash__
668 663
669 664 def __eq__(self, other):
670 665 try:
671 666 # Only dev, ino, size, mtime and atime are likely to change. Out
672 667 # of these, we shouldn't compare atime but should compare the
673 668 # rest. However, one of the other fields changing indicates
674 669 # something fishy going on, so return False if anything but atime
675 670 # changes.
676 671 return (
677 672 self.stat.st_mode == other.stat.st_mode
678 673 and self.stat.st_ino == other.stat.st_ino
679 674 and self.stat.st_dev == other.stat.st_dev
680 675 and self.stat.st_nlink == other.stat.st_nlink
681 676 and self.stat.st_uid == other.stat.st_uid
682 677 and self.stat.st_gid == other.stat.st_gid
683 678 and self.stat.st_size == other.stat.st_size
684 679 and self.stat[stat.ST_MTIME] == other.stat[stat.ST_MTIME]
685 680 and self.stat[stat.ST_CTIME] == other.stat[stat.ST_CTIME]
686 681 )
687 682 except AttributeError:
688 683 return False
689 684
690 685 def __ne__(self, other):
691 686 return not self == other
692 687
693 688
694 689 def statislink(st):
695 690 '''check whether a stat result is a symlink'''
696 691 return st and stat.S_ISLNK(st.st_mode)
697 692
698 693
699 694 def statisexec(st):
700 695 '''check whether a stat result is an executable file'''
701 696 return st and (st.st_mode & 0o100 != 0)
702 697
703 698
704 699 def poll(fds):
705 700 """block until something happens on any file descriptor
706 701
707 702 This is a generic helper that will check for any activity
708 703 (read, write. exception) and return the list of touched files.
709 704
710 705 In unsupported cases, it will raise a NotImplementedError"""
711 706 try:
712 707 res = select.select(fds, fds, fds)
713 708 except ValueError: # out of range file descriptor
714 709 raise NotImplementedError()
715 710 return sorted(list(set(sum(res, []))))
716 711
717 712
718 713 def readpipe(pipe):
719 714 """Read all available data from a pipe."""
720 715 # We can't fstat() a pipe because Linux will always report 0.
721 716 # So, we set the pipe to non-blocking mode and read everything
722 717 # that's available.
723 718 flags = fcntl.fcntl(pipe, fcntl.F_GETFL)
724 719 flags |= os.O_NONBLOCK
725 720 oldflags = fcntl.fcntl(pipe, fcntl.F_SETFL, flags)
726 721
727 722 try:
728 723 chunks = []
729 724 while True:
730 725 try:
731 726 s = pipe.read()
732 727 if not s:
733 728 break
734 729 chunks.append(s)
735 730 except IOError:
736 731 break
737 732
738 733 return b''.join(chunks)
739 734 finally:
740 735 fcntl.fcntl(pipe, fcntl.F_SETFL, oldflags)
741 736
742 737
743 738 def bindunixsocket(sock, path):
744 739 """Bind the UNIX domain socket to the specified path"""
745 740 # use relative path instead of full path at bind() if possible, since
746 741 # AF_UNIX path has very small length limit (107 chars) on common
747 742 # platforms (see sys/un.h)
748 743 dirname, basename = os.path.split(path)
749 744 bakwdfd = None
750 745
751 746 try:
752 747 if dirname:
753 748 bakwdfd = os.open(b'.', os.O_DIRECTORY)
754 749 os.chdir(dirname)
755 750 sock.bind(basename)
756 751 if bakwdfd:
757 752 os.fchdir(bakwdfd)
758 753 finally:
759 754 if bakwdfd:
760 755 os.close(bakwdfd)
General Comments 0
You need to be logged in to leave comments. Login now