540 |
540 |
541 pollinterval = 1 # [sec] |
541 pollinterval = 1 # [sec] |
542 |
542 |
543 def __init__(self, ui): |
543 def __init__(self, ui): |
544 self.ui = ui |
544 self.ui = ui |
545 self.idletimeout = ui.configint('chgserver', 'idletimeout', 3600) |
545 self._idletimeout = ui.configint('chgserver', 'idletimeout', 3600) |
546 self.lastactive = time.time() |
546 self._lastactive = time.time() |
547 |
547 |
548 def bindsocket(self, sock, address): |
548 def bindsocket(self, sock, address): |
549 self._inithashstate(address) |
549 self._inithashstate(address) |
550 self._checkextensions() |
550 self._checkextensions() |
551 self._bind(sock) |
551 self._bind(sock) |
552 self._createsymlink() |
552 self._createsymlink() |
553 |
553 |
554 def _inithashstate(self, address): |
554 def _inithashstate(self, address): |
555 self.baseaddress = address |
555 self._baseaddress = address |
556 if self.ui.configbool('chgserver', 'skiphash', False): |
556 if self.ui.configbool('chgserver', 'skiphash', False): |
557 self.hashstate = None |
557 self._hashstate = None |
558 self.address = address |
558 self._realaddress = address |
559 return |
559 return |
560 self.hashstate = hashstate.fromui(self.ui) |
560 self._hashstate = hashstate.fromui(self.ui) |
561 self.address = _hashaddress(address, self.hashstate.confighash) |
561 self._realaddress = _hashaddress(address, self._hashstate.confighash) |
562 |
562 |
563 def _checkextensions(self): |
563 def _checkextensions(self): |
564 if not self.hashstate: |
564 if not self._hashstate: |
565 return |
565 return |
566 if extensions.notloaded(): |
566 if extensions.notloaded(): |
567 # one or more extensions failed to load. mtimehash becomes |
567 # one or more extensions failed to load. mtimehash becomes |
568 # meaningless because we do not know the paths of those extensions. |
568 # meaningless because we do not know the paths of those extensions. |
569 # set mtimehash to an illegal hash value to invalidate the server. |
569 # set mtimehash to an illegal hash value to invalidate the server. |
570 self.hashstate.mtimehash = '' |
570 self._hashstate.mtimehash = '' |
571 |
571 |
572 def _bind(self, sock): |
572 def _bind(self, sock): |
573 # use a unique temp address so we can stat the file and do ownership |
573 # use a unique temp address so we can stat the file and do ownership |
574 # check later |
574 # check later |
575 tempaddress = _tempaddress(self.address) |
575 tempaddress = _tempaddress(self._realaddress) |
576 util.bindunixsocket(sock, tempaddress) |
576 util.bindunixsocket(sock, tempaddress) |
577 self._socketstat = os.stat(tempaddress) |
577 self._socketstat = os.stat(tempaddress) |
578 # rename will replace the old socket file if exists atomically. the |
578 # rename will replace the old socket file if exists atomically. the |
579 # old server will detect ownership change and exit. |
579 # old server will detect ownership change and exit. |
580 util.rename(tempaddress, self.address) |
580 util.rename(tempaddress, self._realaddress) |
581 |
581 |
582 def _createsymlink(self): |
582 def _createsymlink(self): |
583 if self.baseaddress == self.address: |
583 if self._baseaddress == self._realaddress: |
584 return |
584 return |
585 tempaddress = _tempaddress(self.baseaddress) |
585 tempaddress = _tempaddress(self._baseaddress) |
586 os.symlink(os.path.basename(self.address), tempaddress) |
586 os.symlink(os.path.basename(self._realaddress), tempaddress) |
587 util.rename(tempaddress, self.baseaddress) |
587 util.rename(tempaddress, self._baseaddress) |
588 |
588 |
589 def issocketowner(self): |
589 def _issocketowner(self): |
590 try: |
590 try: |
591 stat = os.stat(self.address) |
591 stat = os.stat(self._realaddress) |
592 return (stat.st_ino == self._socketstat.st_ino and |
592 return (stat.st_ino == self._socketstat.st_ino and |
593 stat.st_mtime == self._socketstat.st_mtime) |
593 stat.st_mtime == self._socketstat.st_mtime) |
594 except OSError: |
594 except OSError: |
595 return False |
595 return False |
596 |
596 |
597 def unlinksocket(self, address): |
597 def unlinksocket(self, address): |
598 if not self.issocketowner(): |
598 if not self._issocketowner(): |
599 return |
599 return |
600 # it is possible to have a race condition here that we may |
600 # it is possible to have a race condition here that we may |
601 # remove another server's socket file. but that's okay |
601 # remove another server's socket file. but that's okay |
602 # since that server will detect and exit automatically and |
602 # since that server will detect and exit automatically and |
603 # the client will start a new server on demand. |
603 # the client will start a new server on demand. |
604 try: |
604 try: |
605 os.unlink(self.address) |
605 os.unlink(self._realaddress) |
606 except OSError as exc: |
606 except OSError as exc: |
607 if exc.errno != errno.ENOENT: |
607 if exc.errno != errno.ENOENT: |
608 raise |
608 raise |
609 |
609 |
610 def printbanner(self, address): |
610 def printbanner(self, address): |
611 # no "listening at" message should be printed to simulate hg behavior |
611 # no "listening at" message should be printed to simulate hg behavior |
612 pass |
612 pass |
613 |
613 |
614 def shouldexit(self): |
614 def shouldexit(self): |
615 if not self.issocketowner(): |
615 if not self._issocketowner(): |
616 self.ui.debug('%s is not owned, exiting.\n' % self.address) |
616 self.ui.debug('%s is not owned, exiting.\n' % self._realaddress) |
617 return True |
617 return True |
618 if time.time() - self.lastactive > self.idletimeout: |
618 if time.time() - self._lastactive > self._idletimeout: |
619 self.ui.debug('being idle too long. exiting.\n') |
619 self.ui.debug('being idle too long. exiting.\n') |
620 return True |
620 return True |
621 return False |
621 return False |
622 |
622 |
623 def newconnection(self): |
623 def newconnection(self): |
624 self.lastactive = time.time() |
624 self._lastactive = time.time() |
625 |
625 |
626 def createcmdserver(self, repo, conn, fin, fout): |
626 def createcmdserver(self, repo, conn, fin, fout): |
627 return chgcmdserver(self.ui, repo, fin, fout, conn, |
627 return chgcmdserver(self.ui, repo, fin, fout, conn, |
628 self.hashstate, self.baseaddress) |
628 self._hashstate, self._baseaddress) |
629 |
629 |
630 def chgunixservice(ui, repo, opts): |
630 def chgunixservice(ui, repo, opts): |
631 if repo: |
631 if repo: |
632 # one chgserver can serve multiple repos. drop repo infomation |
632 # one chgserver can serve multiple repos. drop repo infomation |
633 ui.setconfig('bundle', 'mainreporoot', '', 'repo') |
633 ui.setconfig('bundle', 'mainreporoot', '', 'repo') |