# ex:ts=8 sw=4: # $OpenBSD: SCP.pm,v 1.29 2014/08/27 18:40:03 kspillner Exp $ # # Copyright (c) 2003-2006 Marc Espie # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. use strict; use warnings; use OpenBSD::PackageRepository::Persistent; package OpenBSD::PackageRepository::SCP; our @ISA=qw(OpenBSD::PackageRepository::Persistent); use IPC::Open2; use IO::Handle; use OpenBSD::Paths; sub urlscheme { return 'scp'; } # Any SCP repository uses one single connection, reliant on a perl at end. # The connection starts by xfering and firing up the `distant' script. sub initiate { my $self = shift; my ($rdfh, $wrfh); $self->{controller} = open2($rdfh, $wrfh, OpenBSD::Paths->ssh, $self->{host}, 'perl', '-x'); $self->{cmdfh} = $wrfh; $self->{getfh} = $rdfh; $wrfh->autoflush(1); while() { # compress script a bit next if m/^\#/o && !m/^\#!/o; s/^\s*//o; next if m/^$/o; print $wrfh $_; } seek(DATA, 0, 0); } 1; __DATA__ # Distant connection script. #! /usr/bin/perl my $pid; my $token = 0; $|= 1; sub batch(&) { my $code = shift; if (defined $pid) { waitpid($pid, 0); undef $pid; } $token++; $pid = fork(); if (!defined $pid) { print "ERROR: fork failed: $!\n"; } if ($pid == 0) { &$code(); exit(0); } } sub abort_batch() { if (defined $pid) { kill 1, $pid; waitpid($pid, 0); undef $pid; } print "\nABORTED $token\n"; } my $dirs = {}; sub expand_tilde { my $arg = shift; return $dirs->{$arg} //= (getpwnam($arg))[7]."/"; } while () { chomp; if (m/^LIST\s+(.*)$/o) { my $dname = $1; $dname =~ s/^\/\~(.*?)\//expand_tilde($1)/e; batch(sub { my $d; if (opendir($d, $dname)) { print "SUCCESS: directory $dname\n"; } else { print "ERROR: bad directory $dname $!\n"; } while (my $e = readdir($d)) { next if $e eq '.' or $e eq '..'; next unless $e =~ m/(.+)\.tgz$/; next unless -f "$dname/$e"; print "$1\n"; } print "\n"; closedir($d); }); } elsif (m/^GET\s+(.*)$/o) { my $fname = $1; $fname =~ s/^\/\~(.*?)\//expand_tilde($1)/e; batch(sub { if (open(my $fh, '<', $fname)) { my $size = (stat $fh)[7]; print "TRANSFER: $size\n"; my $buffer = ''; while (read($fh, $buffer, 1024 * 1024) > 0) { print $buffer; } close($fh); } else { print "ERROR: bad file $fname $!\n"; } }); } elsif (m/^BYE$/o) { exit(0); } elsif (m/^ABORT$/o) { abort_batch(); } else { print "ERROR: Unknown command\n"; } } __END__