contrib/hg-ssh
author Siddharth Agarwal <sid0@fb.com>
Thu, 16 Oct 2014 19:15:51 -0700
changeset 23032 f484be02bd35
parent 16836 1ba3e17186c8
child 25127 2b9cda9040f7
permissions -rwxr-xr-x
lock: while releasing, unlink lockfile even if the release function throws Consider a hypothetical bug in the release function that causes it to raise an exception. Also consider the bisect command, which saves its state in a finally clause. Saving the state requires acquiring the wlock. If we don't unlink the lockfile when the exception is thrown, we'll try to acquire the wlock again. We're going to try and acquire a lock again while our old lockfile is on disk. The PID on disk is our own, and of course we're still running, so we won't take over the lock. Hence we'll be stuck waiting for a lock that we left behind ourselves. To avoid this, always unlink the lockfile. This preserves the invariant that self.held > 0 is equivalent to the lockfile existing on disk.

#!/usr/bin/env python
#
# Copyright 2005-2007 by Intevation GmbH <intevation@intevation.de>
#
# Author(s):
# Thomas Arendsen Hein <thomas@intevation.de>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.

"""
hg-ssh - a wrapper for ssh access to a limited set of mercurial repos

To be used in ~/.ssh/authorized_keys with the "command" option, see sshd(8):
command="hg-ssh path/to/repo1 /path/to/repo2 ~/repo3 ~user/repo4" ssh-dss ...
(probably together with these other useful options:
 no-port-forwarding,no-X11-forwarding,no-agent-forwarding)

This allows pull/push over ssh from/to the repositories given as arguments.

If all your repositories are subdirectories of a common directory, you can
allow shorter paths with:
command="cd path/to/my/repositories && hg-ssh repo1 subdir/repo2"

You can use pattern matching of your normal shell, e.g.:
command="cd repos && hg-ssh user/thomas/* projects/{mercurial,foo}"

You can also add a --read-only flag to allow read-only access to a key, e.g.:
command="hg-ssh --read-only repos/*"
"""

# enable importing on demand to reduce startup time
from mercurial import demandimport; demandimport.enable()

from mercurial import dispatch

import sys, os, shlex

def main():
    cwd = os.getcwd()
    readonly = False
    args = sys.argv[1:]
    while len(args):
        if args[0] == '--read-only':
            readonly = True
            args.pop(0)
        else:
            break
    allowed_paths = [os.path.normpath(os.path.join(cwd,
                                                   os.path.expanduser(path)))
                     for path in args]
    orig_cmd = os.getenv('SSH_ORIGINAL_COMMAND', '?')
    try:
        cmdargv = shlex.split(orig_cmd)
    except ValueError, e:
        sys.stderr.write('Illegal command "%s": %s\n' % (orig_cmd, e))
        sys.exit(255)

    if cmdargv[:2] == ['hg', '-R'] and cmdargv[3:] == ['serve', '--stdio']:
        path = cmdargv[2]
        repo = os.path.normpath(os.path.join(cwd, os.path.expanduser(path)))
        if repo in allowed_paths:
            cmd = ['-R', repo, 'serve', '--stdio']
            if readonly:
                cmd += [
                    '--config',
                    'hooks.prechangegroup.hg-ssh=python:__main__.rejectpush',
                    '--config',
                    'hooks.prepushkey.hg-ssh=python:__main__.rejectpush'
                    ]
            dispatch.dispatch(dispatch.request(cmd))
        else:
            sys.stderr.write('Illegal repository "%s"\n' % repo)
            sys.exit(255)
    else:
        sys.stderr.write('Illegal command "%s"\n' % orig_cmd)
        sys.exit(255)

def rejectpush(ui, **kwargs):
    ui.warn("Permission denied\n")
    # mercurial hooks use unix process conventions for hook return values
    # so a truthy return means failure
    return True

if __name__ == '__main__':
    main()