110 lines
		
	
	
	
		
			2.3 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
			
		
		
	
	
			110 lines
		
	
	
	
		
			2.3 KiB
		
	
	
	
		
			Perl
		
	
	
	
	
	
package Nix::SSH;
 | 
						||
 | 
						||
use utf8;
 | 
						||
use strict;
 | 
						||
use File::Temp qw(tempdir);
 | 
						||
use IPC::Open2;
 | 
						||
 | 
						||
our @ISA = qw(Exporter);
 | 
						||
our @EXPORT = qw(
 | 
						||
  @globalSshOpts
 | 
						||
  readN readInt readString readStrings
 | 
						||
  writeInt writeString writeStrings
 | 
						||
  connectToRemoteNix
 | 
						||
);
 | 
						||
 | 
						||
 | 
						||
our @globalSshOpts = split ' ', ($ENV{"NIX_SSHOPTS"} or "");
 | 
						||
 | 
						||
 | 
						||
sub readN {
 | 
						||
    my ($bytes, $from) = @_;
 | 
						||
    my $res = "";
 | 
						||
    while ($bytes > 0) {
 | 
						||
        my $s;
 | 
						||
        my $n = sysread($from, $s, $bytes);
 | 
						||
        die "I/O error reading from remote side\n" if !defined $n;
 | 
						||
        die "got EOF while expecting $bytes bytes from remote side\n" if !$n;
 | 
						||
        $bytes -= $n;
 | 
						||
        $res .= $s;
 | 
						||
    }
 | 
						||
    return $res;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
sub readInt {
 | 
						||
    my ($from) = @_;
 | 
						||
    return unpack("L<x4", readN(8, $from));
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
sub readString {
 | 
						||
    my ($from) = @_;
 | 
						||
    my $len = readInt($from);
 | 
						||
    my $s = readN($len, $from);
 | 
						||
    readN(8 - $len % 8, $from) if $len % 8; # skip padding
 | 
						||
    return $s;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
sub readStrings {
 | 
						||
    my ($from) = @_;
 | 
						||
    my $n = readInt($from);
 | 
						||
    my @res;
 | 
						||
    push @res, readString($from) while $n--;
 | 
						||
    return @res;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
sub writeInt {
 | 
						||
    my ($n, $to) = @_;
 | 
						||
    syswrite($to, pack("L<x4", $n)) or die;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
sub writeString {
 | 
						||
    my ($s, $to) = @_;
 | 
						||
    my $len = length $s;
 | 
						||
    my $req .= pack("L<x4", $len);
 | 
						||
    $req .= $s;
 | 
						||
    $req .= "\000" x (8 - $len % 8) if $len % 8;
 | 
						||
    syswrite($to, $req) or die;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
sub writeStrings {
 | 
						||
    my ($ss, $to) = @_;
 | 
						||
    writeInt(scalar(@{$ss}), $to);
 | 
						||
    writeString($_, $to) foreach @{$ss};
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
sub connectToRemoteNix {
 | 
						||
    my ($sshHost, $sshOpts, $extraFlags) = @_;
 | 
						||
 | 
						||
    $extraFlags ||= "";
 | 
						||
 | 
						||
    # Start ‘nix-store --serve’ on the remote host.
 | 
						||
    my ($from, $to);
 | 
						||
    # FIXME: don't start a shell, start ssh directly.
 | 
						||
    my $pid = open2($from, $to, "exec ssh -x -a $sshHost @globalSshOpts @{$sshOpts} nix-store --serve --write $extraFlags");
 | 
						||
 | 
						||
    # Do the handshake.
 | 
						||
    my $magic;
 | 
						||
    eval {
 | 
						||
        my $SERVE_MAGIC_1 = 0x390c9deb; # FIXME
 | 
						||
        my $clientVersion = 0x200;
 | 
						||
        syswrite($to, pack("L<x4L<x4", $SERVE_MAGIC_1, $clientVersion)) or die;
 | 
						||
        $magic = readInt($from);
 | 
						||
    };
 | 
						||
    die "unable to connect to '$sshHost'\n" if $@;
 | 
						||
    die "did not get valid handshake from remote host\n" if $magic  != 0x5452eecb;
 | 
						||
 | 
						||
    my $serverVersion = readInt($from);
 | 
						||
    die "unsupported server version\n" if $serverVersion < 0x200 || $serverVersion >= 0x300;
 | 
						||
 | 
						||
    return ($from, $to, $pid);
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
1;
 |