Skip to content

Instantly share code, notes, and snippets.

@ryantenney
Forked from eerohele/mosh-cygwin.md
Created August 25, 2012 17:38
Show Gist options
  • Save ryantenney/3468305 to your computer and use it in GitHub Desktop.
Save ryantenney/3468305 to your computer and use it in GitHub Desktop.
Compiling Mosh (http://mosh.mit.edu/) under Cygwin
  1. Download Cygwin.

  2. Run setup.exe and install the following packages in addition to the default ones:

    • make
    • boost
    • libncurses-devel
    • pkg-config
    • perl
  3. Download and compile Protocol Buffers (make sure there are no spaces in the working directory when compiling Protocol Buffers, make will fail if there are):

     $ tar zxvf protobuf-2.4.1.tar.gz
     $ cd protobuf-2.4.1
     $ ./configure
     $ make
     $ make install
    
  4. Download and compile Mosh:

     $ tar zxvf mosh-1.1.3.tar.gz
     $ cd mosh-1.1.3
     $ ./configure CPPFLAGS="-I/usr/include/ncurses" PKG_CONFIG_PATH=/usr/local/lib/pkgconfig/
     $ make
     $ make install
    
  5. Install the IO::Pty Perl module:

     $ perl -MCPAN -e shell
     cpan> install IO::Pty
    
  6. Rock’n’roll.

#!/usr/bin/env perl
# Mosh: the mobile shell
# Copyright 2012 Keith Winstein
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
my $MOSH_VERSION = '1.1.94';
use warnings;
use strict;
use Socket;
use Getopt::Long;
use Errno qw(EINTR);
use IO::Socket::INET;
use POSIX qw(_exit tmpnam);
$| = 1;
my $client = 'mosh-client';
my $server = 'mosh-server';
my $predict = undef;
my $port_request = undef;
my $ssh = 'ssh';
my $help = undef;
my $version = undef;
my @cmdline = @ARGV;
my $usage =
qq{Usage: $0 [options] [--] [user@]host [command...]
--client=PATH mosh client on local machine
(default: "mosh-client")
--server=COMMAND mosh server on remote machine
(default: "mosh-server")
--predict=adaptive local echo for slower links [default]
-a --predict=always use local echo even on fast links
-n --predict=never never use local echo
-p NUM --port=NUM server-side UDP port
--ssh=COMMAND ssh command to run when setting up session
(example: "ssh -p 2222")
(default: "ssh")
--help this message
--version version and copyright information
Please report bugs to mosh-devel\@mit.edu.
Mosh home page: http://mosh.mit.edu\n};
my $version_message = qq{mosh $MOSH_VERSION
Copyright 2012 Keith Winstein <mosh-devel\@mit.edu>
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.\n};
GetOptions( 'client=s' => \$client,
'server=s' => \$server,
'predict=s' => \$predict,
'port=i' => \$port_request,
'a' => sub { $predict = 'always' },
'n' => sub { $predict = 'never' },
'p=i' => \$port_request,
'ssh=s' => \$ssh,
'help' => \$help,
'version' => \$version,
'fake-proxy!' => \my $fake_proxy ) or die $usage;
die $usage if ( defined $help );
die $version_message if ( defined $version );
if ( defined $predict ) {
predict_check( $predict, 0 );
} elsif ( defined $ENV{ 'MOSH_PREDICTION_DISPLAY' } ) {
$predict = $ENV{ 'MOSH_PREDICTION_DISPLAY' };
predict_check( $predict, 1 );
} else {
$predict = 'adaptive';
predict_check( $predict, 0 );
}
if ( defined $port_request ) {
if ( $port_request =~ m{^[0-9]+$}
and $port_request >= 0
and $port_request <= 65535 ) {
# good port
} else {
die "$0: Server-side port ($port_request) must be within valid range [0..65535].\n";
}
}
delete $ENV{ 'MOSH_PREDICTION_DISPLAY' };
# Fake-proxy mode is needed to determine a real host address by its alias.
# One could use his own ssh_config options to make an alias for host,
# e.g. "ssh abcd" may mean "ssh example.com". To find this "example.com"
# name, we use a little hack: call ssh with ProxyCommand equals to
# "ourself --proxy-command %h" option and receive the real hostname (it is
# passed instead of %h placeholder replaced by ssh client). We also dump
# all the auxillary traffic through ourself (e.g. responses from mosh-server
# like "MOSH CONNECT XXX YYY") to make it available from the client.
if ( defined $fake_proxy ) {
my ( $host, $port ) = @ARGV;
# Resolve hostname
my $packed_ip = gethostbyname $host;
if ( not defined $packed_ip ) {
die "$0: Could not resolve hostname $host\n";
}
my $ip = inet_ntoa $packed_ip;
# Pass IP address info via STDERR. It could be catched from the
# client and processed.
print STDERR "MOSH IP $ip\n";
# Act like netcat
my $sock = IO::Socket::INET->new( PeerAddr => $ip,
PeerPort => $port,
Proto => "tcp" )
or die "$0: connect to host $ip port $port: $!\n";
defined( my $pid = fork ) or die "$0: fork: $!\n";
if ( $pid == 0 ) {
cat($sock, \*STDOUT);
$sock->shutdown( 0 );
_exit 0;
}
$SIG{ 'HUP' } = 'IGNORE';
cat(\*STDIN, $sock);
$sock->shutdown( 1 );
waitpid $pid, 0;
exit;
}
if ( scalar @ARGV < 1 ) {
die $usage;
}
my $userhost = shift;
my @command = @ARGV;
# Count colors
open COLORCOUNT, '-|', $client, ('-c') or die "Can't count colors: $!\n";
my $colors = "";
{
local $/ = undef;
$colors = <COLORCOUNT>;
}
close COLORCOUNT or die;
chomp $colors;
if ( (not defined $colors)
or $colors !~ m{^[0-9]+$}
or $colors < 0 ) {
$colors = 0;
}
# Run SSH, read password (if asked) and receive mosh server IP, port, key.
my @server = ( 'new', '-s' );
push @server, ( '-c', $colors );
if ( defined $port_request ) {
push @server, ( '-p', $port_request );
}
for ( locale_vars() ) {
push @server, ( '-l', $_ );
}
if ( @command ) {
push @server, '--', @command;
}
my ( $ip, $port, $key );
my $quoted_self = shell_quote( $0 );
my $tmp = tmpnam();
my $quoted_tmp = shell_quote( $tmp );
# Receive MOSH CONNECT string from mosh-server. Also put IP address
# to $tmp file (thanks to --fake-proxy).
open P, '-|', $ssh, '-S', 'none', '-o', "ProxyCommand=$quoted_self --fake-proxy -- %h %p 2>$quoted_tmp", '-t', $userhost, '--', $server, @server
or die "Cannot exec ssh: $!\n";
while ( <P> ) {
chomp;
if ( m{^MOSH CONNECT } ) {
if ( ( $port, $key ) = m{^MOSH CONNECT (\d+?) ([A-Za-z0-9/+]{22})\s*$} ) {
last;
} else {
die "Bad MOSH CONNECT string: $_\n";
}
} else {
print "$_\n";
}
}
close(P);
# Now read server's IP address.
open P, $tmp or die "Cannot open temporary file $tmp: $!\n";
while ( <P> ) {
chomp;
if ( m{^MOSH IP } ) {
( $ip ) = m{^MOSH IP (\S+)\s*$} or die "Bad MOSH IP string: $_\n";
}
}
close(P);
unlink($tmp);
if ( not defined $ip ) {
die "$0: Did not find remote IP address (is SSH ProxyCommand disabled?).\n";
}
if ( not defined $key or not defined $port ) {
die "$0: Did not find mosh server startup message.\n";
}
# Now start real mosh client.
$ENV{ 'MOSH_KEY' } = $key;
$ENV{ 'MOSH_PREDICTION_DISPLAY' } = $predict;
# Execute the process $client passing "$client @cmdline |" as a displayable
# name (e.g. for "ps" or "top" commands).
exec {$client} "$client @cmdline |", $ip, $port;
sub predict_check {
my ( $predict, $env_set ) = @_;
if ( not exists { adaptive => 0, always => 0, never => 0 }->{ $predict } ) {
my $explanation = $env_set ? " (MOSH_PREDICTION_DISPLAY in environment)" : "";
print STDERR qq{$0: Unknown mode \"$predict\"$explanation.\n\n};
die $usage;
}
}
sub shell_quote {
return join ' ', map { (my $a = $_) =~ s/'/'\\''/g; "'$a'" } @_;
}
sub locale_vars {
my @names = qw[LANG LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION LC_ALL];
my @assignments;
for ( @names ) {
if ( defined $ENV{ $_ } ) {
push @assignments, $_ . q{=} . $ENV{ $_ };
}
}
return @assignments;
}
sub cat {
my ( $from, $to ) = @_;
while ( my $n = $from->sysread( my $buf, 4096 ) ) {
next if ( $n == -1 && $! == EINTR );
$n >= 0 or last;
$to->write( $buf ) or last;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment