diff options
| -rw-r--r-- | git_config.py | 148 | ||||
| -rwxr-xr-x | main.py | 3 |
2 files changed, 90 insertions, 61 deletions
diff --git a/git_config.py b/git_config.py index 8e3dfb1b..26fc970b 100644 --- a/git_config.py +++ b/git_config.py | |||
| @@ -18,6 +18,10 @@ import os | |||
| 18 | import re | 18 | import re |
| 19 | import subprocess | 19 | import subprocess |
| 20 | import sys | 20 | import sys |
| 21 | try: | ||
| 22 | import threading as _threading | ||
| 23 | except ImportError: | ||
| 24 | import dummy_threading as _threading | ||
| 21 | import time | 25 | import time |
| 22 | from signal import SIGTERM | 26 | from signal import SIGTERM |
| 23 | from urllib2 import urlopen, HTTPError | 27 | from urllib2 import urlopen, HTTPError |
| @@ -361,76 +365,97 @@ class RefSpec(object): | |||
| 361 | _master_processes = [] | 365 | _master_processes = [] |
| 362 | _master_keys = set() | 366 | _master_keys = set() |
| 363 | _ssh_master = True | 367 | _ssh_master = True |
| 368 | _master_keys_lock = None | ||
| 369 | |||
| 370 | def init_ssh(): | ||
| 371 | """Should be called once at the start of repo to init ssh master handling. | ||
| 372 | |||
| 373 | At the moment, all we do is to create our lock. | ||
| 374 | """ | ||
| 375 | global _master_keys_lock | ||
| 376 | assert _master_keys_lock is None, "Should only call init_ssh once" | ||
| 377 | _master_keys_lock = _threading.Lock() | ||
| 364 | 378 | ||
| 365 | def _open_ssh(host, port=None): | 379 | def _open_ssh(host, port=None): |
| 366 | global _ssh_master | 380 | global _ssh_master |
| 367 | 381 | ||
| 368 | # Check to see whether we already think that the master is running; if we | 382 | # Acquire the lock. This is needed to prevent opening multiple masters for |
| 369 | # think it's already running, return right away. | 383 | # the same host when we're running "repo sync -jN" (for N > 1) _and_ the |
| 370 | if port is not None: | 384 | # manifest <remote fetch="ssh://xyz"> specifies a different host from the |
| 371 | key = '%s:%s' % (host, port) | 385 | # one that was passed to repo init. |
| 372 | else: | 386 | _master_keys_lock.acquire() |
| 373 | key = host | 387 | try: |
| 374 | |||
| 375 | if key in _master_keys: | ||
| 376 | return True | ||
| 377 | 388 | ||
| 378 | if not _ssh_master \ | 389 | # Check to see whether we already think that the master is running; if we |
| 379 | or 'GIT_SSH' in os.environ \ | 390 | # think it's already running, return right away. |
| 380 | or sys.platform in ('win32', 'cygwin'): | 391 | if port is not None: |
| 381 | # failed earlier, or cygwin ssh can't do this | 392 | key = '%s:%s' % (host, port) |
| 382 | # | 393 | else: |
| 383 | return False | 394 | key = host |
| 384 | 395 | ||
| 385 | # We will make two calls to ssh; this is the common part of both calls. | 396 | if key in _master_keys: |
| 386 | command_base = ['ssh', | ||
| 387 | '-o','ControlPath %s' % ssh_sock(), | ||
| 388 | host] | ||
| 389 | if port is not None: | ||
| 390 | command_base[1:1] = ['-p',str(port)] | ||
| 391 | |||
| 392 | # Since the key wasn't in _master_keys, we think that master isn't running. | ||
| 393 | # ...but before actually starting a master, we'll double-check. This can | ||
| 394 | # be important because we can't tell that that 'git@myhost.com' is the same | ||
| 395 | # as 'myhost.com' where "User git" is setup in the user's ~/.ssh/config file. | ||
| 396 | check_command = command_base + ['-O','check'] | ||
| 397 | try: | ||
| 398 | Trace(': %s', ' '.join(check_command)) | ||
| 399 | check_process = subprocess.Popen(check_command, | ||
| 400 | stdout=subprocess.PIPE, | ||
| 401 | stderr=subprocess.PIPE) | ||
| 402 | check_process.communicate() # read output, but ignore it... | ||
| 403 | isnt_running = check_process.wait() | ||
| 404 | |||
| 405 | if not isnt_running: | ||
| 406 | # Our double-check found that the master _was_ infact running. Add to | ||
| 407 | # the list of keys. | ||
| 408 | _master_keys.add(key) | ||
| 409 | return True | 397 | return True |
| 410 | except Exception: | ||
| 411 | # Ignore excpetions. We we will fall back to the normal command and print | ||
| 412 | # to the log there. | ||
| 413 | pass | ||
| 414 | |||
| 415 | command = command_base[:1] + \ | ||
| 416 | ['-M', '-N'] + \ | ||
| 417 | command_base[1:] | ||
| 418 | try: | ||
| 419 | Trace(': %s', ' '.join(command)) | ||
| 420 | p = subprocess.Popen(command) | ||
| 421 | except Exception, e: | ||
| 422 | _ssh_master = False | ||
| 423 | print >>sys.stderr, \ | ||
| 424 | '\nwarn: cannot enable ssh control master for %s:%s\n%s' \ | ||
| 425 | % (host,port, str(e)) | ||
| 426 | return False | ||
| 427 | 398 | ||
| 428 | _master_processes.append(p) | 399 | if not _ssh_master \ |
| 429 | _master_keys.add(key) | 400 | or 'GIT_SSH' in os.environ \ |
| 430 | time.sleep(1) | 401 | or sys.platform in ('win32', 'cygwin'): |
| 431 | return True | 402 | # failed earlier, or cygwin ssh can't do this |
| 403 | # | ||
| 404 | return False | ||
| 405 | |||
| 406 | # We will make two calls to ssh; this is the common part of both calls. | ||
| 407 | command_base = ['ssh', | ||
| 408 | '-o','ControlPath %s' % ssh_sock(), | ||
| 409 | host] | ||
| 410 | if port is not None: | ||
| 411 | command_base[1:1] = ['-p',str(port)] | ||
| 412 | |||
| 413 | # Since the key wasn't in _master_keys, we think that master isn't running. | ||
| 414 | # ...but before actually starting a master, we'll double-check. This can | ||
| 415 | # be important because we can't tell that that 'git@myhost.com' is the same | ||
| 416 | # as 'myhost.com' where "User git" is setup in the user's ~/.ssh/config file. | ||
| 417 | check_command = command_base + ['-O','check'] | ||
| 418 | try: | ||
| 419 | Trace(': %s', ' '.join(check_command)) | ||
| 420 | check_process = subprocess.Popen(check_command, | ||
| 421 | stdout=subprocess.PIPE, | ||
| 422 | stderr=subprocess.PIPE) | ||
| 423 | check_process.communicate() # read output, but ignore it... | ||
| 424 | isnt_running = check_process.wait() | ||
| 425 | |||
| 426 | if not isnt_running: | ||
| 427 | # Our double-check found that the master _was_ infact running. Add to | ||
| 428 | # the list of keys. | ||
| 429 | _master_keys.add(key) | ||
| 430 | return True | ||
| 431 | except Exception: | ||
| 432 | # Ignore excpetions. We we will fall back to the normal command and print | ||
| 433 | # to the log there. | ||
| 434 | pass | ||
| 435 | |||
| 436 | command = command_base[:1] + \ | ||
| 437 | ['-M', '-N'] + \ | ||
| 438 | command_base[1:] | ||
| 439 | try: | ||
| 440 | Trace(': %s', ' '.join(command)) | ||
| 441 | p = subprocess.Popen(command) | ||
| 442 | except Exception, e: | ||
| 443 | _ssh_master = False | ||
| 444 | print >>sys.stderr, \ | ||
| 445 | '\nwarn: cannot enable ssh control master for %s:%s\n%s' \ | ||
| 446 | % (host,port, str(e)) | ||
| 447 | return False | ||
| 448 | |||
| 449 | _master_processes.append(p) | ||
| 450 | _master_keys.add(key) | ||
| 451 | time.sleep(1) | ||
| 452 | return True | ||
| 453 | finally: | ||
| 454 | _master_keys_lock.release() | ||
| 432 | 455 | ||
| 433 | def close_ssh(): | 456 | def close_ssh(): |
| 457 | global _master_keys_lock | ||
| 458 | |||
| 434 | terminate_ssh_clients() | 459 | terminate_ssh_clients() |
| 435 | 460 | ||
| 436 | for p in _master_processes: | 461 | for p in _master_processes: |
| @@ -449,6 +474,9 @@ def close_ssh(): | |||
| 449 | except OSError: | 474 | except OSError: |
| 450 | pass | 475 | pass |
| 451 | 476 | ||
| 477 | # We're done with the lock, so we can delete it. | ||
| 478 | _master_keys_lock = None | ||
| 479 | |||
| 452 | URI_SCP = re.compile(r'^([^@:]*@?[^:/]{1,}):') | 480 | URI_SCP = re.compile(r'^([^@:]*@?[^:/]{1,}):') |
| 453 | URI_ALL = re.compile(r'^([a-z][a-z+]*)://([^@/]*@?[^/]*)/') | 481 | URI_ALL = re.compile(r'^([a-z][a-z+]*)://([^@/]*@?[^/]*)/') |
| 454 | 482 | ||
| @@ -28,7 +28,7 @@ import re | |||
| 28 | import sys | 28 | import sys |
| 29 | 29 | ||
| 30 | from trace import SetTrace | 30 | from trace import SetTrace |
| 31 | from git_config import close_ssh | 31 | from git_config import init_ssh, close_ssh |
| 32 | from command import InteractiveCommand | 32 | from command import InteractiveCommand |
| 33 | from command import MirrorSafeCommand | 33 | from command import MirrorSafeCommand |
| 34 | from command import PagedCommand | 34 | from command import PagedCommand |
| @@ -214,6 +214,7 @@ def _Main(argv): | |||
| 214 | repo = _Repo(opt.repodir) | 214 | repo = _Repo(opt.repodir) |
| 215 | try: | 215 | try: |
| 216 | try: | 216 | try: |
| 217 | init_ssh() | ||
| 217 | repo._Run(argv) | 218 | repo._Run(argv) |
| 218 | finally: | 219 | finally: |
| 219 | close_ssh() | 220 | close_ssh() |
