summaryrefslogtreecommitdiffstats
path: root/git_config.py
diff options
context:
space:
mode:
Diffstat (limited to 'git_config.py')
-rw-r--r--git_config.py156
1 files changed, 2 insertions, 154 deletions
diff --git a/git_config.py b/git_config.py
index fcd0446c..1d8d1363 100644
--- a/git_config.py
+++ b/git_config.py
@@ -18,25 +18,17 @@ from http.client import HTTPException
18import json 18import json
19import os 19import os
20import re 20import re
21import signal
22import ssl 21import ssl
23import subprocess 22import subprocess
24import sys 23import sys
25try:
26 import threading as _threading
27except ImportError:
28 import dummy_threading as _threading
29import time
30import urllib.error 24import urllib.error
31import urllib.request 25import urllib.request
32 26
33from error import GitError, UploadError 27from error import GitError, UploadError
34import platform_utils 28import platform_utils
35from repo_trace import Trace 29from repo_trace import Trace
36 30import ssh
37from git_command import GitCommand 31from git_command import GitCommand
38from git_command import ssh_sock
39from git_command import terminate_ssh_clients
40from git_refs import R_CHANGES, R_HEADS, R_TAGS 32from git_refs import R_CHANGES, R_HEADS, R_TAGS
41 33
42ID_RE = re.compile(r'^[0-9a-f]{40}$') 34ID_RE = re.compile(r'^[0-9a-f]{40}$')
@@ -440,129 +432,6 @@ class RefSpec(object):
440 return s 432 return s
441 433
442 434
443_master_processes = []
444_master_keys = set()
445_ssh_master = True
446_master_keys_lock = None
447
448
449def init_ssh():
450 """Should be called once at the start of repo to init ssh master handling.
451
452 At the moment, all we do is to create our lock.
453 """
454 global _master_keys_lock
455 assert _master_keys_lock is None, "Should only call init_ssh once"
456 _master_keys_lock = _threading.Lock()
457
458
459def _open_ssh(host, port=None):
460 global _ssh_master
461
462 # Bail before grabbing the lock if we already know that we aren't going to
463 # try creating new masters below.
464 if sys.platform in ('win32', 'cygwin'):
465 return False
466
467 # Acquire the lock. This is needed to prevent opening multiple masters for
468 # the same host when we're running "repo sync -jN" (for N > 1) _and_ the
469 # manifest <remote fetch="ssh://xyz"> specifies a different host from the
470 # one that was passed to repo init.
471 _master_keys_lock.acquire()
472 try:
473
474 # Check to see whether we already think that the master is running; if we
475 # think it's already running, return right away.
476 if port is not None:
477 key = '%s:%s' % (host, port)
478 else:
479 key = host
480
481 if key in _master_keys:
482 return True
483
484 if not _ssh_master or 'GIT_SSH' in os.environ:
485 # Failed earlier, so don't retry.
486 return False
487
488 # We will make two calls to ssh; this is the common part of both calls.
489 command_base = ['ssh',
490 '-o', 'ControlPath %s' % ssh_sock(),
491 host]
492 if port is not None:
493 command_base[1:1] = ['-p', str(port)]
494
495 # Since the key wasn't in _master_keys, we think that master isn't running.
496 # ...but before actually starting a master, we'll double-check. This can
497 # be important because we can't tell that that 'git@myhost.com' is the same
498 # as 'myhost.com' where "User git" is setup in the user's ~/.ssh/config file.
499 check_command = command_base + ['-O', 'check']
500 try:
501 Trace(': %s', ' '.join(check_command))
502 check_process = subprocess.Popen(check_command,
503 stdout=subprocess.PIPE,
504 stderr=subprocess.PIPE)
505 check_process.communicate() # read output, but ignore it...
506 isnt_running = check_process.wait()
507
508 if not isnt_running:
509 # Our double-check found that the master _was_ infact running. Add to
510 # the list of keys.
511 _master_keys.add(key)
512 return True
513 except Exception:
514 # Ignore excpetions. We we will fall back to the normal command and print
515 # to the log there.
516 pass
517
518 command = command_base[:1] + ['-M', '-N'] + command_base[1:]
519 try:
520 Trace(': %s', ' '.join(command))
521 p = subprocess.Popen(command)
522 except Exception as e:
523 _ssh_master = False
524 print('\nwarn: cannot enable ssh control master for %s:%s\n%s'
525 % (host, port, str(e)), file=sys.stderr)
526 return False
527
528 time.sleep(1)
529 ssh_died = (p.poll() is not None)
530 if ssh_died:
531 return False
532
533 _master_processes.append(p)
534 _master_keys.add(key)
535 return True
536 finally:
537 _master_keys_lock.release()
538
539
540def close_ssh():
541 global _master_keys_lock
542
543 terminate_ssh_clients()
544
545 for p in _master_processes:
546 try:
547 os.kill(p.pid, signal.SIGTERM)
548 p.wait()
549 except OSError:
550 pass
551 del _master_processes[:]
552 _master_keys.clear()
553
554 d = ssh_sock(create=False)
555 if d:
556 try:
557 platform_utils.rmdir(os.path.dirname(d))
558 except OSError:
559 pass
560
561 # We're done with the lock, so we can delete it.
562 _master_keys_lock = None
563
564
565URI_SCP = re.compile(r'^([^@:]*@?[^:/]{1,}):')
566URI_ALL = re.compile(r'^([a-z][a-z+-]*)://([^@/]*@?[^/]*)/') 435URI_ALL = re.compile(r'^([a-z][a-z+-]*)://([^@/]*@?[^/]*)/')
567 436
568 437
@@ -614,27 +483,6 @@ def GetUrlCookieFile(url, quiet):
614 yield cookiefile, None 483 yield cookiefile, None
615 484
616 485
617def _preconnect(url):
618 m = URI_ALL.match(url)
619 if m:
620 scheme = m.group(1)
621 host = m.group(2)
622 if ':' in host:
623 host, port = host.split(':')
624 else:
625 port = None
626 if scheme in ('ssh', 'git+ssh', 'ssh+git'):
627 return _open_ssh(host, port)
628 return False
629
630 m = URI_SCP.match(url)
631 if m:
632 host = m.group(1)
633 return _open_ssh(host)
634
635 return False
636
637
638class Remote(object): 486class Remote(object):
639 """Configuration options related to a remote. 487 """Configuration options related to a remote.
640 """ 488 """
@@ -673,7 +521,7 @@ class Remote(object):
673 521
674 def PreConnectFetch(self): 522 def PreConnectFetch(self):
675 connectionUrl = self._InsteadOf() 523 connectionUrl = self._InsteadOf()
676 return _preconnect(connectionUrl) 524 return ssh.preconnect(connectionUrl)
677 525
678 def ReviewUrl(self, userEmail, validate_certs): 526 def ReviewUrl(self, userEmail, validate_certs):
679 if self._review_url is None: 527 if self._review_url is None: