another minor doc update
[Net-SCP.git] / SCP.pm
diff --git a/SCP.pm b/SCP.pm
index f0fd8ea..2eca5de 100644 (file)
--- a/SCP.pm
+++ b/SCP.pm
@@ -1,7 +1,7 @@
 package Net::SCP;
 
 use strict;
-use vars qw($VERSION @ISA @EXPORT_OK $scp);
+use vars qw($VERSION @ISA @EXPORT_OK $scp $DEBUG);
 use Exporter;
 use Carp;
 use File::Basename;
@@ -12,10 +12,12 @@ use IPC::Open3;
 
 @ISA = qw(Exporter);
 @EXPORT_OK = qw( scp iscp );
-$VERSION = '0.02';
+$VERSION = '0.09';
 
 $scp = "scp";
 
+$DEBUG = 0;
+
 =head1 NAME
 
 Net::SCP - Perl extension for secure copy protocol
@@ -32,13 +34,11 @@ Net::SCP - Perl extension for secure copy protocol
   $scp = Net::SCP->new( "hostname", "username" );
   #with named params
   $scp = Net::SCP->new( { "host"=>$hostname, "user"=>$username } );
-  $scp->set(
-    cwd      => "/dir",
-    verbose  => "yes",
-    interact => "yes"
-  );
   $scp->get("filename") or die $scp->{errstr};
   $scp->put("filename") or die $scp->{errstr};
+  #tmtowtdi
+  $scp = new Net::SCP;
+  $scp->scp($source, $destination);
 
   #Net::FTP-style
   $scp = Net::SCP->new("hostname");
@@ -46,7 +46,6 @@ Net::SCP - Perl extension for secure copy protocol
   $scp->cwd("/dir");
   $scp->size("file");
   $scp->get("file");
-  $scp->quit;
 
 =head1 DESCRIPTION
 
@@ -74,9 +73,10 @@ sub scp {
   my($src, $dest, $interact) = @_;
   my $flags = '-p';
   $flags .= 'r' unless &_islocal($src) && ! -d $src;
-  my @cmd = ( $scp, $flags, $src, $dest );
+  my @cmd;
   if ( ( defined($interact) && $interact )
-       || ( defined($self->{interact}) && $self->{interact} ) ) {
+       || ( defined($self->{interactive}) && $self->{interactive} ) ) {
+    @cmd = ( $scp, $flags, $src, $dest );
     print join(' ', @cmd), "\n";
     unless ( &_yesno ) {
       $self->{errstr} = "User declined";
@@ -84,14 +84,17 @@ sub scp {
     }
   } else {
     $flags .= 'qB';
+    @cmd = ( $scp, $flags, $src, $dest );
   }
   my($reader, $writer, $error ) =
     ( new IO::Handle, new IO::Handle, new IO::Handle );
   $writer->autoflush(1);#  $error->autoflush(1);
+  local $SIG{CHLD} = 'DEFAULT';
   my $pid = open3($writer, $reader, $error, @cmd );
   waitpid $pid, 0;
   if ( $? >> 8 ) {
-    chomp(my $errstr = <$error>);
+    my $errstr = join('', <$error>);
+    #chomp(my $errstr = <$error>);
     $self->{errstr} = $errstr;
     0;
   } else {
@@ -114,7 +117,7 @@ Returns false and sets the B<errstr> attribute if there is an error.
 sub iscp {
   if ( ref($_[0]) ) {
     my $self = shift;
-    $self->set( 'interact' => 1 );
+    $self->{'interactive'} = 1;
     $self->scp(@_);
   } else {
     scp(@_, 1);
@@ -145,7 +148,6 @@ hashref of named params, with the following keys:
 
     host - hostname
     user - username
-    verbose - bool
     interactive - bool
     cwd - current working directory on remote server
 
@@ -161,7 +163,6 @@ sub new {
     $self = {
               'host'        => shift,
               'user'        => ( scalar(@_) ? shift : '' ),
-              'verbose'     => 0,
               'interactive' => 0,
               'cwd'         => '',
             };
@@ -177,7 +178,7 @@ Compatibility method.  Optionally sets the user.
 
 sub login {
   my($self, $user) = @_;
-  $self->{'user'} = $user;
+  $self->{'user'} = $user if $user;
 }
 
 =item cwd CWD
@@ -204,7 +205,37 @@ sub get {
   $local ||= basename($remote);
   my $source = $self->{'host'}. ":$remote";
   $source = $self->{'user'}. '@'. $source if $self->{'user'};
-  scp($source,$local);
+  $self->scp($source,$local);
+}
+
+=item mkdir DIRECTORY
+
+Makes a directory on the remote server.  Returns false and sets the B<errstr>
+attribute on errors.
+
+(Implementation note: An ssh connection is established to the remote machine
+and '/bin/mkdir B<-p>' is used to create the directory.)
+
+=cut
+
+sub mkdir {
+  my($self, $directory) = @_;
+  $directory = $self->{'cwd'}. "/$directory"
+    if $self->{'cwd'} && $directory !~ /^\//;
+  my $host = $self->{'host'};
+  $host = $self->{'user'}. '@'. $host if $self->{'user'};
+  my($reader, $writer, $error ) =
+    ( new IO::Handle, new IO::Handle, new IO::Handle );
+  $writer->autoflush(1);
+  my $pid = sshopen3( $host, $writer, $reader, $error,
+                      '/bin/mkdir', '-p ', shell_quote($directory) );
+  waitpid $pid, 0;
+  if ( $? >> 8 ) {
+    chomp(my $errstr = <$error> || '');
+    $self->{errstr} = $errstr || "mkdir exited with status ". ($?>>8);
+    return 0;
+  }
+  1;
 }
 
 =item size FILE
@@ -226,7 +257,7 @@ sub size {
   $host = $self->{'user'}. '@'. $host if $self->{'user'};
   my($reader, $writer, $error ) =
     ( new IO::Handle, new IO::Handle, new IO::Handle );
-  $writer->autoflush(1);#  $error->autoflush(1);
+  $writer->autoflush(1);
   #sshopen2($host, $reader, $writer, 'wc', '-c ', shell_quote($file) );
   my $pid =
     sshopen3($host, $writer, $reader, $error, 'wc', '-c ', shell_quote($file) );
@@ -259,8 +290,8 @@ sub put {
   $remote = $self->{'cwd'}. "/$remote" if $self->{'cwd'} && $remote !~ /^\//;
   my $dest = $self->{'host'}. ":$remote";
   $dest = $self->{'user'}. '@'. $dest if $self->{'user'};
-  warn "scp $local $dest\n";
-  scp($local, $dest);
+  warn "scp $local $dest\n" if $DEBUG;
+  $self->scp($local, $dest);
 }
 
 =item binary
@@ -271,29 +302,135 @@ Compatibility method: does nothing; returns true.
 
 sub binary { 1; }
 
+=item quit
+
+Compatibility method: does nothing; returns true.
+
+=cut
+
+sub quit { 1; }
+
+=back
+
+=head1 FREQUENTLY ASKED QUESTIONS
+
+Q: How do you supply a password to connect with ssh within a perl script
+using the Net::SSH module?
+
+A: You don't (at least not with this module).  Use RSA or DSA keys.  See the
+   quick help in the next section and the ssh-keygen(1) manpage.
+
+A #2: See L<Net::SCP::Expect> instead.
+
+Q: My script is "leaking" scp processes.
+
+A: See L<perlfaq8/"How do I avoid zombies on a Unix system">, L<IPC::Open2>,
+L<IPC::Open3> and L<perlfunc/waitpid>.
+
+=head1 GENERATING AND USING SSH KEYS
+
+=over 4
+
+=item 1 Generate keys
+
+Type:
+
+   ssh-keygen -t rsa
+
+And do not enter a passphrase unless you wanted to be prompted for
+one during file copying.
+
+Here is what you will see:
+
+   $ ssh-keygen -t rsa
+   Generating public/private rsa key pair.
+   Enter file in which to save the key (/home/User/.ssh/id_rsa):
+   Enter passphrase (empty for no passphrase):
+
+   Enter same passphrase again:
+
+   Your identification has been saved in /home/User/.ssh/id_rsa.
+   Your public key has been saved in /home/User/.ssh/id_rsa.pub.
+   The key fingerprint is:
+   5a:cd:2b:0a:cd:d9:15:85:26:79:40:0c:55:2a:f4:23 User@JEFF-CPU
+
+
+=item 2 Copy public to machines you want to upload to
+
+C<id_rsa.pub> is your public key. Copy it to C<~/.ssh> on target machine.
+
+Put a copy of the public key file on each machine you want to log into.
+Name the copy C<authorized_keys> (some implementations name this file
+C<authorized_keys2>)
+
+Then type:
+
+     chmod 600 authorized_keys
+
+Then make sure your home dir on the remote machine is not group or
+world writeable.
+
 =back
 
 =head1 AUTHORS
 
 Ivan Kohler <ivan-netscp_pod@420.am>
-Anthony Deaver <bishop@projectmagnus.org>
+
+Assistance wanted - this module could really use a maintainer with enough time
+to at least review and apply more patches.  Or the module should just be
+deprecated in favor of Net::SFTP::Expect or Net::SFTP::Foreign and made into a
+simple compatiblity wrapper.  Please email Ivan if you are interested in
+helping.
+
+Major updates Anthony Deaver <bishop@projectmagnus.org>
 
 Thanks to Jon Gunnip <jon@soundbite.com> for fixing a bug with size().
 
+Patch for the mkdir method by Anthony Awtrey <tony@awtrey.com>.
+
+Thanks to terrence brannon <tbone@directsynergy.com> for the documentation in
+the GENERATING AND USING SSH KEYS section.
+
+=head1 COPYRIGHT
+
+Copyright (c) 2000 Ivan Kohler
+Copyright (c) 2007 Freeside Internet Services, Inc.
+All rights reserved.
+This program is free software; you can redistribute it and/or modify it under
+the same terms as Perl itself.
+
 =head1 BUGS
 
 Still has no-OO cruft.
 
 In order to work around some problems with commercial SSH2, if the source file
 is on the local system, and is not a directory, the B<-r> flag is omitted.
-
-It's probably better just to use SSH1 or OpenSSH <http://www.openssh.com/>
+It's probably better just to use OpenSSH <http://www.openssh.com/> which is
+the de-facto standard these days anyway.
 
 The Net::FTP-style OO stuff is kinda lame.  And incomplete.
 
+iscp doesnt expect you to be logging into the box that you are copying to
+for the first time. so it's completely clueless about how to handle  the
+whole 'add this file to known hosts' message so it just hangs after the
+user hits y.  (Thanks to John L. Utz III).  To avoid this, SSH to the box
+once first.
+
 =head1 SEE ALSO
 
-scp(1), ssh(1)
+For a perl implementation that does not require the system B<scp> command, see
+L<Net::SFTP> instead.
+
+For a wrapper version that allows you to use passwords, see L<Net::SCP::Expect>
+instead.
+
+For a wrapper version of the newer SFTP protocol, see L<Net::SFTP::Foreign>
+instead.
+
+L<Net::SSH>, L<Net::SSH::Perl>, L<Net::SSH::Expect>, L<Net::SSH2>,
+L<IPC::PerlSSH>
+
+scp(1), ssh(1), L<IO::File>, L<IPC::Open2>, L<IPC::Open3>
 
 =cut