# HG changeset patch # User Gregory Szorc # Date 1519775264 28800 # Node ID 44dc34b8d17b5150d7e5fdc3bdc6df3b357ba6d5 # Parent 7cc4a9b9732ad659a56dc56a24e83da25ec84771 debugcommands: add debugserve command `hg serve --stdio` requires the exact command argument form `hg -R serve --stdio` for security reasons. An upcoming commit will need to start an SSH protocol server process with custom settings. This commit creates a `hg debugserve` command for starting servers with custom options. There are no security restrictions and we can add options here that aren't appropriate for built-in commands. We currently only support starting an SSH protocol server using the process's stdio file descriptors. The server supports logging its I/O activity to a file descriptor number passed as a command argument. Differential Revision: https://phab.mercurial-scm.org/D2464 diff -r 7cc4a9b9732a -r 44dc34b8d17b mercurial/debugcommands.py --- a/mercurial/debugcommands.py Sun Feb 25 11:16:09 2018 -0800 +++ b/mercurial/debugcommands.py Tue Feb 27 15:47:44 2018 -0800 @@ -73,6 +73,7 @@ url as urlmod, util, vfs as vfsmod, + wireprotoserver, ) release = lockmod.release @@ -2230,6 +2231,37 @@ for c in revs: ui.write("%d\n" % c) +@command('debugserve', [ + ('', 'sshstdio', False, _('run an SSH server bound to process handles')), + ('', 'logiofd', '', _('file descriptor to log server I/O to')), + ('', 'logiofile', '', _('file to log server I/O to')), +], '') +def debugserve(ui, repo, **opts): + """run a server with advanced settings + + This command is similar to :hg:`serve`. It exists partially as a + workaround to the fact that ``hg serve --stdio`` must have specific + arguments for security reasons. + """ + opts = pycompat.byteskwargs(opts) + + if not opts['sshstdio']: + raise error.Abort(_('only --sshstdio is currently supported')) + + logfh = None + + if opts['logiofd'] and opts['logiofile']: + raise error.Abort(_('cannot use both --logiofd and --logiofile')) + + if opts['logiofd']: + # Line buffered because output is line based. + logfh = os.fdopen(int(opts['logiofd']), 'ab', 1) + elif opts['logiofile']: + logfh = open(opts['logiofile'], 'ab', 1) + + s = wireprotoserver.sshserver(ui, repo, logfh=logfh) + s.serve_forever() + @command('debugsetparents', [], _('REV1 [REV2]')) def debugsetparents(ui, repo, rev1, rev2=None): """manually set the parents of the current working directory diff -r 7cc4a9b9732a -r 44dc34b8d17b tests/test-completion.t --- a/tests/test-completion.t Sun Feb 25 11:16:09 2018 -0800 +++ b/tests/test-completion.t Tue Feb 27 15:47:44 2018 -0800 @@ -111,6 +111,7 @@ debugrename debugrevlog debugrevspec + debugserve debugsetparents debugssl debugsub @@ -291,6 +292,7 @@ debugrename: rev debugrevlog: changelog, manifest, dir, dump debugrevspec: optimize, show-revs, show-set, show-stage, no-optimized, verify-optimized + debugserve: sshstdio, logiofd, logiofile debugsetparents: debugssl: debugsub: rev diff -r 7cc4a9b9732a -r 44dc34b8d17b tests/test-help.t --- a/tests/test-help.t Sun Feb 25 11:16:09 2018 -0800 +++ b/tests/test-help.t Tue Feb 27 15:47:44 2018 -0800 @@ -967,6 +967,7 @@ debugrename dump rename information debugrevlog show data and statistics about a revlog debugrevspec parse and apply a revision specification + debugserve run a server with advanced settings debugsetparents manually set the parents of the current working directory debugssl test a secure connection to a server diff -r 7cc4a9b9732a -r 44dc34b8d17b tests/test-ssh-proto.t --- a/tests/test-ssh-proto.t Sun Feb 25 11:16:09 2018 -0800 +++ b/tests/test-ssh-proto.t Tue Feb 27 15:47:44 2018 -0800 @@ -39,6 +39,43 @@ 384 capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN +`hg debugserve --sshstdio` works + + $ cd server + $ hg debugserve --sshstdio << EOF + > hello + > EOF + 384 + capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN + +I/O logging works + + $ hg debugserve --sshstdio --logiofd 1 << EOF + > hello + > EOF + o> write(4) -> None: + o> 384\n + o> write(384) -> None: + o> capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN\n + 384 + capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN + o> flush() -> None + + $ hg debugserve --sshstdio --logiofile $TESTTMP/io << EOF + > hello + > EOF + 384 + capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN + + $ cat $TESTTMP/io + o> write(4) -> None: + o> 384\n + o> write(384) -> None: + o> capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN\n + o> flush() -> None + + $ cd .. + >=0.9.1 clients send a "hello" + "between" for the null range as part of handshake. Server should reply with capabilities and should send "1\n\n" as a successful reply with empty response to the "between".