diff -r 57875cf423c9 -r 2372284d9457 mercurial/chgserver.py --- a/mercurial/chgserver.py Sat Oct 05 10:29:34 2019 -0400 +++ b/mercurial/chgserver.py Sun Oct 06 09:45:02 2019 -0400 @@ -67,24 +67,27 @@ stringutil, ) + def _hashlist(items): """return sha1 hexdigest for a list""" return node.hex(hashlib.sha1(stringutil.pprint(items)).digest()) + # sensitive config sections affecting confighash _configsections = [ 'alias', # affects global state commands.table - 'eol', # uses setconfig('eol', ...) + 'eol', # uses setconfig('eol', ...) 'extdiff', # uisetup will register new commands 'extensions', ] _configsectionitems = [ - ('commands', 'show.aliasprefix'), # show.py reads it in extsetup + ('commands', 'show.aliasprefix'), # show.py reads it in extsetup ] # sensitive environment variables affecting confighash -_envre = re.compile(br'''\A(?: +_envre = re.compile( + br'''\A(?: CHGHG |HG(?:DEMANDIMPORT|EMITWARNINGS|MODULEPOLICY|PROF|RCPATH)? |HG(?:ENCODING|PLAIN).* @@ -95,7 +98,10 @@ |PYTHON.* |TERM(?:INFO)? |TZ - )\Z''', re.X) + )\Z''', + re.X, +) + def _confighash(ui): """return a quick hash for detecting config/env changes @@ -119,11 +125,15 @@ ignored = {'HG'} else: ignored = set() - envitems = [(k, v) for k, v in encoding.environ.iteritems() - if _envre.match(k) and k not in ignored] + envitems = [ + (k, v) + for k, v in encoding.environ.iteritems() + if _envre.match(k) and k not in ignored + ] envhash = _hashlist(sorted(envitems)) return sectionhash[:6] + envhash[:6] + def _getmtimepaths(ui): """get a list of paths that should be checked to detect change @@ -135,6 +145,7 @@ modules = [m for n, m in extensions.extensions(ui)] try: from . import __version__ + modules.append(__version__) except ImportError: pass @@ -148,6 +159,7 @@ pass return sorted(set(files)) + def _mtimehash(paths): """return a quick hash for detecting file changes @@ -165,6 +177,7 @@ extensions after importing them (there is imp.find_module but that faces race conditions). We need to calculate confighash without importing. """ + def trystat(path): try: st = os.stat(path) @@ -172,10 +185,13 @@ except OSError: # could be ENOENT, EPERM etc. not fatal in any case pass + return _hashlist(pycompat.maplist(trystat, paths))[:12] + class hashstate(object): """a structure storing confighash, mtimehash, paths used for mtimehash""" + def __init__(self, confighash, mtimehash, mtimepaths): self.confighash = confighash self.mtimehash = mtimehash @@ -187,10 +203,15 @@ mtimepaths = _getmtimepaths(ui) confighash = _confighash(ui) mtimehash = _mtimehash(mtimepaths) - ui.log('cmdserver', 'confighash = %s mtimehash = %s\n', - confighash, mtimehash) + ui.log( + 'cmdserver', + 'confighash = %s mtimehash = %s\n', + confighash, + mtimehash, + ) return hashstate(confighash, mtimehash, mtimepaths) + def _newchgui(srcui, csystem, attachio): class chgui(srcui.__class__): def __init__(self, src=None): @@ -206,21 +227,28 @@ # b. or stdout is redirected by protectfinout(), # because the chg client is not aware of these situations and # will behave differently (i.e. write to stdout). - if (out is not self.fout + if ( + out is not self.fout or not util.safehasattr(self.fout, 'fileno') or self.fout.fileno() != procutil.stdout.fileno() - or self._finoutredirected): + or self._finoutredirected + ): return procutil.system(cmd, environ=environ, cwd=cwd, out=out) self.flush() return self._csystem(cmd, procutil.shellenviron(environ), cwd) def _runpager(self, cmd, env=None): - self._csystem(cmd, procutil.shellenviron(env), type='pager', - cmdtable={'attachio': attachio}) + self._csystem( + cmd, + procutil.shellenviron(env), + type='pager', + cmdtable={'attachio': attachio}, + ) return True return chgui(srcui) + def _loadnewui(srcui, args, cdebug): from . import dispatch # avoid cycle @@ -256,6 +284,7 @@ return (newui, newlui) + class channeledsystem(object): """Propagate ui.system() request in the following format: @@ -276,6 +305,7 @@ and executes it defined by cmdtable, or exits the loop if the command name is empty. """ + def __init__(self, in_, out, channel): self.in_ = in_ self.out = out @@ -291,10 +321,10 @@ if type == 'system': length = self.in_.read(4) - length, = struct.unpack('>I', length) + (length,) = struct.unpack('>I', length) if length != 4: raise error.Abort(_('invalid response')) - rc, = struct.unpack('>i', self.in_.read(4)) + (rc,) = struct.unpack('>i', self.in_.read(4)) return rc elif type == 'pager': while True: @@ -308,6 +338,7 @@ else: raise error.ProgrammingError('invalid S channel type: %s' % type) + _iochannels = [ # server.ch, ui.fp, mode ('cin', 'fin', r'rb'), @@ -315,12 +346,18 @@ ('cerr', 'ferr', r'wb'), ] + class chgcmdserver(commandserver.server): - def __init__(self, ui, repo, fin, fout, sock, prereposetups, - hashstate, baseaddress): + def __init__( + self, ui, repo, fin, fout, sock, prereposetups, hashstate, baseaddress + ): super(chgcmdserver, self).__init__( _newchgui(ui, channeledsystem(fin, fout, 'S'), self.attachio), - repo, fin, fout, prereposetups) + repo, + fin, + fout, + prereposetups, + ) self.clientsock = sock self._ioattached = False self._oldios = [] # original (self.ch, ui.fp, fd) before "attachio" @@ -512,24 +549,32 @@ encoding.environ.update(newenv) capabilities = commandserver.server.capabilities.copy() - capabilities.update({'attachio': attachio, - 'chdir': chdir, - 'runcommand': runcommand, - 'setenv': setenv, - 'setumask': setumask, - 'setumask2': setumask2}) + capabilities.update( + { + 'attachio': attachio, + 'chdir': chdir, + 'runcommand': runcommand, + 'setenv': setenv, + 'setumask': setumask, + 'setumask2': setumask2, + } + ) if util.safehasattr(procutil, 'setprocname'): + def setprocname(self): """Change process title""" name = self._readstr() self.ui.log('chgserver', 'setprocname: %r\n', name) procutil.setprocname(name) + capabilities['setprocname'] = setprocname + def _tempaddress(address): return '%s.%d.tmp' % (address, os.getpid()) + def _hashaddress(address, hashstr): # if the basename of address contains '.', use only the left part. this # makes it possible for the client to pass 'server.tmp$PID' and follow by @@ -538,6 +583,7 @@ basename = basename.split('.', 1)[0] return '%s-%s' % (os.path.join(dirname, basename), hashstr) + class chgunixservicehandler(object): """Set of operations for chg services""" @@ -594,8 +640,10 @@ def _issocketowner(self): try: st = os.stat(self._realaddress) - return (st.st_ino == self._socketstat.st_ino and - st[stat.ST_MTIME] == self._socketstat[stat.ST_MTIME]) + return ( + st.st_ino == self._socketstat.st_ino + and st[stat.ST_MTIME] == self._socketstat[stat.ST_MTIME] + ) except OSError: return False @@ -610,8 +658,9 @@ def shouldexit(self): if not self._issocketowner(): - self.ui.log(b'chgserver', b'%s is not owned, exiting.\n', - self._realaddress) + self.ui.log( + b'chgserver', b'%s is not owned, exiting.\n', self._realaddress + ) return True if time.time() - self._lastactive > self._idletimeout: self.ui.log(b'chgserver', b'being idle too long. exiting.\n') @@ -622,8 +671,17 @@ self._lastactive = time.time() def createcmdserver(self, repo, conn, fin, fout, prereposetups): - return chgcmdserver(self.ui, repo, fin, fout, conn, prereposetups, - self._hashstate, self._baseaddress) + return chgcmdserver( + self.ui, + repo, + fin, + fout, + conn, + prereposetups, + self._hashstate, + self._baseaddress, + ) + def chgunixservice(ui, repo, opts): # CHGINTERNALMARK is set by chg client. It is an indication of things are