2527 res1 = repo.debugwireargs(*vals, **args) |
2529 res1 = repo.debugwireargs(*vals, **args) |
2528 res2 = repo.debugwireargs(*vals, **args) |
2530 res2 = repo.debugwireargs(*vals, **args) |
2529 ui.write("%s\n" % res1) |
2531 ui.write("%s\n" % res1) |
2530 if res1 != res2: |
2532 if res1 != res2: |
2531 ui.warn("%s\n" % res2) |
2533 ui.warn("%s\n" % res2) |
|
2534 |
|
2535 def _parsewirelangblocks(fh): |
|
2536 activeaction = None |
|
2537 blocklines = [] |
|
2538 |
|
2539 for line in fh: |
|
2540 line = line.rstrip() |
|
2541 if not line: |
|
2542 continue |
|
2543 |
|
2544 if line.startswith(b'#'): |
|
2545 continue |
|
2546 |
|
2547 if not line.startswith(' '): |
|
2548 # New block. Flush previous one. |
|
2549 if activeaction: |
|
2550 yield activeaction, blocklines |
|
2551 |
|
2552 activeaction = line |
|
2553 blocklines = [] |
|
2554 continue |
|
2555 |
|
2556 # Else we start with an indent. |
|
2557 |
|
2558 if not activeaction: |
|
2559 raise error.Abort(_('indented line outside of block')) |
|
2560 |
|
2561 blocklines.append(line) |
|
2562 |
|
2563 # Flush last block. |
|
2564 if activeaction: |
|
2565 yield activeaction, blocklines |
|
2566 |
|
2567 @command('debugwireproto', |
|
2568 [ |
|
2569 ('', 'localssh', False, _('start an SSH server for this repo')), |
|
2570 ('', 'peer', '', _('construct a specific version of the peer')), |
|
2571 ] + cmdutil.remoteopts, |
|
2572 _('[REPO]'), |
|
2573 optionalrepo=True) |
|
2574 def debugwireproto(ui, repo, **opts): |
|
2575 """send wire protocol commands to a server |
|
2576 |
|
2577 This command can be used to issue wire protocol commands to remote |
|
2578 peers and to debug the raw data being exchanged. |
|
2579 |
|
2580 ``--localssh`` will start an SSH server against the current repository |
|
2581 and connect to that. By default, the connection will perform a handshake |
|
2582 and establish an appropriate peer instance. |
|
2583 |
|
2584 ``--peer`` can be used to bypass the handshake protocol and construct a |
|
2585 peer instance using the specified class type. Valid values are ``raw``, |
|
2586 ``ssh1``, and ``ssh2``. ``raw`` instances only allow sending raw data |
|
2587 payloads and don't support higher-level command actions. |
|
2588 |
|
2589 Commands are issued via a mini language which is specified via stdin. |
|
2590 The language consists of individual actions to perform. An action is |
|
2591 defined by a block. A block is defined as a line with no leading |
|
2592 space followed by 0 or more lines with leading space. Blocks are |
|
2593 effectively a high-level command with additional metadata. |
|
2594 |
|
2595 Lines beginning with ``#`` are ignored. |
|
2596 |
|
2597 The following sections denote available actions. |
|
2598 |
|
2599 raw |
|
2600 --- |
|
2601 |
|
2602 Send raw data to the server. |
|
2603 |
|
2604 The block payload contains the raw data to send as one atomic send |
|
2605 operation. The data may not actually be delivered in a single system |
|
2606 call: it depends on the abilities of the transport being used. |
|
2607 |
|
2608 Each line in the block is de-indented and concatenated. Then, that |
|
2609 value is evaluated as a Python b'' literal. This allows the use of |
|
2610 backslash escaping, etc. |
|
2611 |
|
2612 raw+ |
|
2613 ---- |
|
2614 |
|
2615 Behaves like ``raw`` except flushes output afterwards. |
|
2616 |
|
2617 close |
|
2618 ----- |
|
2619 |
|
2620 Close the connection to the server. |
|
2621 |
|
2622 flush |
|
2623 ----- |
|
2624 |
|
2625 Flush data written to the server. |
|
2626 |
|
2627 readavailable |
|
2628 ------------- |
|
2629 |
|
2630 Read all available data from the server. |
|
2631 |
|
2632 If the connection to the server encompasses multiple pipes, we poll both |
|
2633 pipes and read available data. |
|
2634 |
|
2635 readline |
|
2636 -------- |
|
2637 |
|
2638 Read a line of output from the server. If there are multiple output |
|
2639 pipes, reads only the main pipe. |
|
2640 """ |
|
2641 opts = pycompat.byteskwargs(opts) |
|
2642 |
|
2643 if opts['localssh'] and not repo: |
|
2644 raise error.Abort(_('--localssh requires a repository')) |
|
2645 |
|
2646 if opts['peer'] and opts['peer'] not in ('raw', 'ssh1', 'ssh2'): |
|
2647 raise error.Abort(_('invalid value for --peer'), |
|
2648 hint=_('valid values are "raw", "ssh1", and "ssh2"')) |
|
2649 |
|
2650 if ui.interactive(): |
|
2651 ui.write(_('(waiting for commands on stdin)\n')) |
|
2652 |
|
2653 blocks = list(_parsewirelangblocks(ui.fin)) |
|
2654 |
|
2655 proc = None |
|
2656 |
|
2657 if opts['localssh']: |
|
2658 # We start the SSH server in its own process so there is process |
|
2659 # separation. This prevents a whole class of potential bugs around |
|
2660 # shared state from interfering with server operation. |
|
2661 args = util.hgcmd() + [ |
|
2662 '-R', repo.root, |
|
2663 'debugserve', '--sshstdio', |
|
2664 ] |
|
2665 proc = subprocess.Popen(args, stdin=subprocess.PIPE, |
|
2666 stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
|
2667 bufsize=0) |
|
2668 |
|
2669 stdin = proc.stdin |
|
2670 stdout = proc.stdout |
|
2671 stderr = proc.stderr |
|
2672 |
|
2673 # We turn the pipes into observers so we can log I/O. |
|
2674 if ui.verbose or opts['peer'] == 'raw': |
|
2675 stdin = util.makeloggingfileobject(ui, proc.stdin, b'i', |
|
2676 logdata=True) |
|
2677 stdout = util.makeloggingfileobject(ui, proc.stdout, b'o', |
|
2678 logdata=True) |
|
2679 stderr = util.makeloggingfileobject(ui, proc.stderr, b'e', |
|
2680 logdata=True) |
|
2681 |
|
2682 # --localssh also implies the peer connection settings. |
|
2683 |
|
2684 url = 'ssh://localserver' |
|
2685 |
|
2686 if opts['peer'] == 'ssh1': |
|
2687 ui.write(_('creating ssh peer for wire protocol version 1\n')) |
|
2688 peer = sshpeer.sshv1peer(ui, url, proc, stdin, stdout, stderr, |
|
2689 None) |
|
2690 elif opts['peer'] == 'ssh2': |
|
2691 ui.write(_('creating ssh peer for wire protocol version 2\n')) |
|
2692 peer = sshpeer.sshv2peer(ui, url, proc, stdin, stdout, stderr, |
|
2693 None) |
|
2694 elif opts['peer'] == 'raw': |
|
2695 ui.write(_('using raw connection to peer\n')) |
|
2696 peer = None |
|
2697 else: |
|
2698 ui.write(_('creating ssh peer from handshake results\n')) |
|
2699 peer = sshpeer.makepeer(ui, url, proc, stdin, stdout, stderr) |
|
2700 |
|
2701 else: |
|
2702 raise error.Abort(_('only --localssh is currently supported')) |
|
2703 |
|
2704 # Now perform actions based on the parsed wire language instructions. |
|
2705 for action, lines in blocks: |
|
2706 if action in ('raw', 'raw+'): |
|
2707 # Concatenate the data together. |
|
2708 data = ''.join(l.lstrip() for l in lines) |
|
2709 data = util.unescapestr(data) |
|
2710 stdin.write(data) |
|
2711 |
|
2712 if action == 'raw+': |
|
2713 stdin.flush() |
|
2714 elif action == 'flush': |
|
2715 stdin.flush() |
|
2716 elif action == 'close': |
|
2717 peer.close() |
|
2718 elif action == 'readavailable': |
|
2719 fds = util.poll([stdout.fileno(), stderr.fileno()]) |
|
2720 |
|
2721 if stdout.fileno() in fds: |
|
2722 util.readpipe(stdout) |
|
2723 if stderr.fileno() in fds: |
|
2724 util.readpipe(stderr) |
|
2725 elif action == 'readline': |
|
2726 stdout.readline() |
|
2727 else: |
|
2728 raise error.Abort(_('unknown action: %s') % action) |
|
2729 |
|
2730 if peer: |
|
2731 peer.close() |
|
2732 |
|
2733 if proc: |
|
2734 proc.kill() |