##// END OF EJS Templates
dispatch: protect against malicious 'hg serve --stdio' invocations (sec)...
dispatch: protect against malicious 'hg serve --stdio' invocations (sec) Some shared-ssh installations assume that 'hg serve --stdio' is a safe command to run for minimally trusted users. Unfortunately, the messy implementation of argument parsing here meant that trying to access a repo named '--debugger' would give the user a pdb prompt, thereby sidestepping any hoped-for sandboxing. Serving repositories over HTTP(S) is unaffected. We're not currently hardening any subcommands other than 'serve'. If your service exposes other commands to users with arbitrary repository names, it is imperative that you defend against repository names of '--debugger' and anything starting with '--config'. The read-only mode of hg-ssh stopped working because it provided its hook configuration to "hg serve --stdio" via --config parameter. This is banned for security reasons now. This patch switches it to directly call ui.setconfig(). If your custom hosting infrastructure relies on passing --config to "hg serve --stdio", you'll need to find a different way to get that configuration into Mercurial, either by using ui.setconfig() as hg-ssh does in this patch, or by placing an hgrc file someplace where Mercurial will read it. mitrandir@fb.com provided some extra fixes for the dispatch code and for hg-ssh in places that I overlooked.

File last commit:

r29841:d5883fd0 default
r32050:77eaf953 4.1.3 stable
Show More
blackbox.py
250 lines | 7.8 KiB | text/x-python | PythonLexer
Bryan O'Sullivan
blackbox: fix copyright
r18676 # blackbox.py - log repository events to a file for post-mortem debugging
Durham Goode
blackbox: adds a blackbox extension...
r18669 #
Bryan O'Sullivan
blackbox: fix copyright
r18676 # Copyright 2010 Nicolas Dumazet
Durham Goode
blackbox: adds a blackbox extension...
r18669 # Copyright 2013 Facebook, Inc.
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
"""log repository events to a blackbox for debugging
Logs event information to .hg/blackbox.log to help debug and diagnose problems.
The events that get logged can be configured via the blackbox.track config key.
timeless
blackbox: log dirty state...
r28246
Takumi IINO
blackbox: fix literal block syntax
r19162 Examples::
Durham Goode
blackbox: adds a blackbox extension...
r18669
[blackbox]
track = *
timeless
blackbox: rewrite dirty documentation noting it is expensive
r28303 # dirty is *EXPENSIVE* (slow);
# each log entry indicates `+` if the repository is dirty, like :hg:`id`.
timeless
blackbox: log dirty state...
r28246 dirty = True
timeless
blackbox: optionally log event source
r28305 # record the source of log messages
logsource = True
Durham Goode
blackbox: adds a blackbox extension...
r18669
[blackbox]
track = command, commandfinish, commandexception, exthook, pythonhook
[blackbox]
track = incoming
Bryan O'Sullivan
blackbox: automatically rotate log files...
r19066 [blackbox]
# limit the size of a log file
maxsize = 1.5 MB
# rotate up to N log files when the current one gets too big
maxfiles = 3
Durham Goode
blackbox: adds a blackbox extension...
r18669 """
Gregory Szorc
blackbox: use absolute_import
r28090 from __future__ import absolute_import
import errno
import re
Durham Goode
blackbox: adds a blackbox extension...
r18669 from mercurial.i18n import _
timeless
blackbox: log working directory version...
r28245 from mercurial.node import hex
Gregory Szorc
blackbox: use absolute_import
r28090 from mercurial import (
cmdutil,
timeless
blackbox: properly replace ui class...
r28248 ui as uimod,
Gregory Szorc
blackbox: use absolute_import
r28090 util,
)
Durham Goode
blackbox: adds a blackbox extension...
r18669
cmdtable = {}
command = cmdutil.command(cmdtable)
Augie Fackler
extensions: change magic "shipped with hg" string...
r29841 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
Augie Fackler
extensions: document that `testedwith = 'internal'` is special...
r25186 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
# be specifying the version(s) of Mercurial they are tested with, or
# leave the attribute unspecified.
Augie Fackler
extensions: change magic "shipped with hg" string...
r29841 testedwith = 'ships-with-hg-core'
timeless
blackbox: store the blackbox ui object instead of the log file...
r28247 lastui = None
Durham Goode
blackbox: adds a blackbox extension...
r18669
timeless
blackbox: avoid creating multiple file handles for a single log...
r28243 filehandles = {}
def _openlog(vfs):
path = vfs.join('blackbox.log')
if path in filehandles:
return filehandles[path]
filehandles[path] = fp = vfs('blackbox.log', 'a')
return fp
def _closelog(vfs):
path = vfs.join('blackbox.log')
fp = filehandles[path]
del filehandles[path]
fp.close()
Durham Goode
blackbox: adds a blackbox extension...
r18669 def wrapui(ui):
class blackboxui(ui.__class__):
timeless
blackbox: properly replace ui class...
r28248 def __init__(self, src=None):
super(blackboxui, self).__init__(src)
if src is None:
self._partialinit()
else:
Jun Wu
blackbox: do not assume self._bb{vfs,repo,fp} are set in blackboxui.__init__...
r28552 self._bbfp = getattr(src, '_bbfp', None)
timeless
blackbox: guard against recursion from dirty check
r28407 self._bbinlog = False
Jun Wu
blackbox: do not assume self._bb{vfs,repo,fp} are set in blackboxui.__init__...
r28552 self._bbrepo = getattr(src, '_bbrepo', None)
self._bbvfs = getattr(src, '_bbvfs', None)
timeless
blackbox: properly replace ui class...
r28248
def _partialinit(self):
if util.safehasattr(self, '_bbvfs'):
return
self._bbfp = None
timeless
blackbox: guard against recursion from dirty check
r28407 self._bbinlog = False
timeless
blackbox: properly replace ui class...
r28248 self._bbrepo = None
self._bbvfs = None
def copy(self):
self._partialinit()
return self.__class__(self)
Durham Goode
blackbox: adds a blackbox extension...
r18669 @util.propertycache
def track(self):
Bryan O'Sullivan
blackbox: fix a case of name capture
r19052 return self.configlist('blackbox', 'track', ['*'])
Durham Goode
blackbox: adds a blackbox extension...
r18669
Bryan O'Sullivan
blackbox: automatically rotate log files...
r19066 def _openlogfile(self):
def rotate(oldpath, newpath):
try:
timeless
blackbox: refactor use of vfs as _bbvfs
r28026 self._bbvfs.unlink(newpath)
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except OSError as err:
Bryan O'Sullivan
blackbox: automatically rotate log files...
r19066 if err.errno != errno.ENOENT:
self.debug("warning: cannot remove '%s': %s\n" %
(newpath, err.strerror))
try:
if newpath:
timeless
blackbox: refactor use of vfs as _bbvfs
r28026 self._bbvfs.rename(oldpath, newpath)
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except OSError as err:
Bryan O'Sullivan
blackbox: automatically rotate log files...
r19066 if err.errno != errno.ENOENT:
self.debug("warning: cannot rename '%s' to '%s': %s\n" %
(newpath, oldpath, err.strerror))
timeless
blackbox: avoid creating multiple file handles for a single log...
r28243 fp = _openlog(self._bbvfs)
Bryan O'Sullivan
blackbox: automatically rotate log files...
r19066 maxsize = self.configbytes('blackbox', 'maxsize', 1048576)
if maxsize > 0:
timeless
blackbox: refactor use of vfs as _bbvfs
r28026 st = self._bbvfs.fstat(fp)
Bryan O'Sullivan
blackbox: automatically rotate log files...
r19066 if st.st_size >= maxsize:
path = fp.name
timeless
blackbox: avoid creating multiple file handles for a single log...
r28243 _closelog(self._bbvfs)
Bryan O'Sullivan
blackbox: automatically rotate log files...
r19066 maxfiles = self.configint('blackbox', 'maxfiles', 7)
for i in xrange(maxfiles - 1, 1, -1):
rotate(oldpath='%s.%d' % (path, i - 1),
newpath='%s.%d' % (path, i))
rotate(oldpath=path,
newpath=maxfiles > 0 and path + '.1')
timeless
blackbox: avoid creating multiple file handles for a single log...
r28243 fp = _openlog(self._bbvfs)
Bryan O'Sullivan
blackbox: automatically rotate log files...
r19066 return fp
timeless
blackbox: store the blackbox ui object instead of the log file...
r28247 def _bbwrite(self, fmt, *args):
self._bbfp.write(fmt % args)
self._bbfp.flush()
Durham Goode
blackbox: adds a blackbox extension...
r18669 def log(self, event, *msg, **opts):
timeless
blackbox: store the blackbox ui object instead of the log file...
r28247 global lastui
Durham Goode
blackbox: adds a blackbox extension...
r18669 super(blackboxui, self).log(event, *msg, **opts)
timeless
blackbox: properly replace ui class...
r28248 self._partialinit()
Durham Goode
blackbox: adds a blackbox extension...
r18669
if not '*' in self.track and not event in self.track:
return
timeless
blackbox: properly replace ui class...
r28248 if self._bbfp:
timeless
blackbox: store the blackbox ui object instead of the log file...
r28247 ui = self
timeless
blackbox: properly replace ui class...
r28248 elif self._bbvfs:
Bryan O'Sullivan
blackbox: defer opening a log file until needed (issue3869)...
r18831 try:
timeless
blackbox: rename fp variable
r28244 self._bbfp = self._openlogfile()
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except (IOError, OSError) as err:
Bryan O'Sullivan
blackbox: defer opening a log file until needed (issue3869)...
r18831 self.debug('warning: cannot write to blackbox.log: %s\n' %
err.strerror)
timeless
blackbox: refactor use of vfs as _bbvfs
r28026 del self._bbvfs
timeless
blackbox: rename fp variable
r28244 self._bbfp = None
timeless
blackbox: store the blackbox ui object instead of the log file...
r28247 ui = self
Durham Goode
blackbox: adds a blackbox extension...
r18669 else:
# certain ui instances exist outside the context of
# a repo, so just default to the last blackbox that
# was seen.
timeless
blackbox: store the blackbox ui object instead of the log file...
r28247 ui = lastui
Durham Goode
blackbox: adds a blackbox extension...
r18669
timeless
blackbox: guard against recursion from dirty check
r28407 if not ui or not ui._bbfp:
return
if not lastui or ui._bbrepo:
lastui = ui
if ui._bbinlog:
# recursion guard
return
try:
ui._bbinlog = True
Durham Goode
blackbox: adds a blackbox extension...
r18669 date = util.datestr(None, '%Y/%m/%d %H:%M:%S')
Bryan O'Sullivan
blackbox: use util.getuser for portability...
r18787 user = util.getuser()
timeless
util: enable getpid to be replaced...
r28027 pid = str(util.getpid())
Durham Goode
blackbox: adds a blackbox extension...
r18669 formattedmsg = msg[0] % msg[1:]
timeless
blackbox: log working directory version...
r28245 rev = '(unknown)'
timeless
blackbox: log dirty state...
r28246 changed = ''
timeless
blackbox: properly replace ui class...
r28248 if ui._bbrepo:
timeless
blackbox: store the blackbox ui object instead of the log file...
r28247 ctx = ui._bbrepo[None]
timeless
blackbox: remove hexfn...
r28304 parents = ctx.parents()
rev = ('+'.join([hex(p.node()) for p in parents]))
if (ui.configbool('blackbox', 'dirty', False) and (
any(ui._bbrepo.status()) or
any(ctx.sub(s).dirty() for s in ctx.substate)
)):
changed = '+'
timeless
blackbox: optionally log event source
r28305 if ui.configbool('blackbox', 'logsource', False):
src = ' [%s]' % event
else:
src = ''
Bryan O'Sullivan
blackbox: prevent failed I/O from causing hg to abort...
r18786 try:
timeless
blackbox: optionally log event source
r28305 ui._bbwrite('%s %s @%s%s (%s)%s> %s',
date, user, rev, changed, pid, src, formattedmsg)
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except IOError as err:
Bryan O'Sullivan
blackbox: prevent failed I/O from causing hg to abort...
r18786 self.debug('warning: cannot write to blackbox.log: %s\n' %
err.strerror)
timeless
blackbox: guard against recursion from dirty check
r28407 finally:
ui._bbinlog = False
Durham Goode
blackbox: adds a blackbox extension...
r18669
def setrepo(self, repo):
timeless
blackbox: properly replace ui class...
r28248 self._bbfp = None
timeless
blackbox: guard against recursion from dirty check
r28407 self._bbinlog = False
timeless
blackbox: properly replace ui class...
r28248 self._bbrepo = repo
timeless
blackbox: refactor use of vfs as _bbvfs
r28026 self._bbvfs = repo.vfs
Durham Goode
blackbox: adds a blackbox extension...
r18669
ui.__class__ = blackboxui
timeless
blackbox: properly replace ui class...
r28248 uimod.ui = blackboxui
Durham Goode
blackbox: adds a blackbox extension...
r18669
def uisetup(ui):
wrapui(ui)
def reposetup(ui, repo):
# During 'hg pull' a httppeer repo is created to represent the remote repo.
# It doesn't have a .hg directory to put a blackbox in, so we don't do
# the blackbox setup for it.
if not repo.local():
return
Durham Goode
blackbox: fix blackbox causing exceptions in tests...
r19230 if util.safehasattr(ui, 'setrepo'):
ui.setrepo(repo)
Durham Goode
blackbox: adds a 'blackbox' command for viewing recent logs...
r18673
@command('^blackbox',
[('l', 'limit', 10, _('the number of events to show')),
],
_('hg blackbox [OPTION]...'))
def blackbox(ui, repo, *revs, **opts):
'''view the recent repository events
'''
timeless
blackbox: refactor use of vfs as _bbvfs
r28026 if not repo.vfs.exists('blackbox.log'):
Durham Goode
blackbox: adds a 'blackbox' command for viewing recent logs...
r18673 return
limit = opts.get('limit')
timeless
blackbox: rename fp variable
r28244 fp = repo.vfs('blackbox.log', 'r')
lines = fp.read().split('\n')
Durham Goode
blackbox: adds a 'blackbox' command for viewing recent logs...
r18673
count = 0
output = []
for line in reversed(lines):
if count >= limit:
break
# count the commands by matching lines like: 2013/01/23 19:13:36 root>
if re.match('^\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} .*> .*', line):
count += 1
output.append(line)
ui.status('\n'.join(reversed(output)))