another minor doc update
[Net-SCP.git] / SCP.pm
1 package Net::SCP;
2
3 use strict;
4 use vars qw($VERSION @ISA @EXPORT_OK $scp $DEBUG);
5 use Exporter;
6 use Carp;
7 use File::Basename;
8 use String::ShellQuote;
9 use IO::Handle;
10 use Net::SSH qw(sshopen3);
11 use IPC::Open3;
12
13 @ISA = qw(Exporter);
14 @EXPORT_OK = qw( scp iscp );
15 $VERSION = '0.09';
16
17 $scp = "scp";
18
19 $DEBUG = 0;
20
21 =head1 NAME
22
23 Net::SCP - Perl extension for secure copy protocol
24
25 =head1 SYNOPSIS
26
27   #procedural interface
28   use Net::SCP qw(scp iscp);
29   scp($source, $destination);
30   iscp($source, $destination); #shows command, asks for confirmation, and
31                                #allows user to type a password on tty
32
33   #OO interface
34   $scp = Net::SCP->new( "hostname", "username" );
35   #with named params
36   $scp = Net::SCP->new( { "host"=>$hostname, "user"=>$username } );
37   $scp->get("filename") or die $scp->{errstr};
38   $scp->put("filename") or die $scp->{errstr};
39   #tmtowtdi
40   $scp = new Net::SCP;
41   $scp->scp($source, $destination);
42
43   #Net::FTP-style
44   $scp = Net::SCP->new("hostname");
45   $scp->login("user");
46   $scp->cwd("/dir");
47   $scp->size("file");
48   $scp->get("file");
49
50 =head1 DESCRIPTION
51
52 Simple wrappers around ssh and scp commands.
53
54 =head1 SUBROUTINES
55
56 =over 4
57
58 =item scp SOURCE, DESTINATION
59
60 Can be called either as a subroutine or a method; however, the subroutine
61 interface is depriciated.
62
63 Calls scp in batch mode, with the B<-B> B<-p> B<-q> and B<-r> options.
64 Returns false upon error, with a text error message accessable in
65 $scp->{errstr}.
66
67 Returns false and sets the B<errstr> attribute if there is an error.
68
69 =cut
70
71 sub scp {
72   my $self = ref($_[0]) ? shift : {};
73   my($src, $dest, $interact) = @_;
74   my $flags = '-p';
75   $flags .= 'r' unless &_islocal($src) && ! -d $src;
76   my @cmd;
77   if ( ( defined($interact) && $interact )
78        || ( defined($self->{interactive}) && $self->{interactive} ) ) {
79     @cmd = ( $scp, $flags, $src, $dest );
80     print join(' ', @cmd), "\n";
81     unless ( &_yesno ) {
82       $self->{errstr} = "User declined";
83       return 0;
84     }
85   } else {
86     $flags .= 'qB';
87     @cmd = ( $scp, $flags, $src, $dest );
88   }
89   my($reader, $writer, $error ) =
90     ( new IO::Handle, new IO::Handle, new IO::Handle );
91   $writer->autoflush(1);#  $error->autoflush(1);
92   local $SIG{CHLD} = 'DEFAULT';
93   my $pid = open3($writer, $reader, $error, @cmd );
94   waitpid $pid, 0;
95   if ( $? >> 8 ) {
96     my $errstr = join('', <$error>);
97     #chomp(my $errstr = <$error>);
98     $self->{errstr} = $errstr;
99     0;
100   } else {
101     1;
102   }
103 }
104
105 =item iscp SOURCE, DESTINATION
106
107 Can be called either as a subroutine or a method; however, the subroutine
108 interface is depriciated.
109
110 Prints the scp command to be execute, waits for the user to confirm, and
111 (optionally) executes scp, with the B<-p> and B<-r> flags.
112
113 Returns false and sets the B<errstr> attribute if there is an error.
114
115 =cut
116
117 sub iscp {
118   if ( ref($_[0]) ) {
119     my $self = shift;
120     $self->{'interactive'} = 1;
121     $self->scp(@_);
122   } else {
123     scp(@_, 1);
124   }
125 }
126
127 sub _yesno {
128   print "Proceed [y/N]:";
129   my $x = scalar(<STDIN>);
130   $x =~ /^y/i;
131 }
132
133 sub _islocal {
134   shift !~ /^[^:]+:/
135 }
136
137 =back
138
139 =head1 METHODS
140
141 =over 4
142
143 =item new HOSTNAME [ USER ] | HASHREF
144
145 This is the constructor for a new Net::SCP object.  You must specify a
146 hostname, and may optionally provide a user.  Alternatively, you may pass a
147 hashref of named params, with the following keys:
148
149     host - hostname
150     user - username
151     interactive - bool
152     cwd - current working directory on remote server
153
154 =cut
155
156 sub new {
157   my $proto = shift;
158   my $class = ref($proto) || $proto;
159   my $self;
160   if ( ref($_[0]) ) {
161     $self = shift;
162   } else {
163     $self = {
164               'host'        => shift,
165               'user'        => ( scalar(@_) ? shift : '' ),
166               'interactive' => 0,
167               'cwd'         => '',
168             };
169   }
170   bless($self, $class);
171 }
172
173 =item login [USER]
174
175 Compatibility method.  Optionally sets the user.
176
177 =cut
178
179 sub login {
180   my($self, $user) = @_;
181   $self->{'user'} = $user if $user;
182 }
183
184 =item cwd CWD
185
186 Sets the cwd (used for a subsequent get or put request without a full pathname).
187
188 =cut
189
190 sub cwd {
191   my($self, $cwd) = @_;
192   $self->{'cwd'} = $cwd || '/';
193 }
194
195 =item get REMOTE_FILE [, LOCAL_FILE]
196
197 Uses scp to transfer REMOTE_FILE from the remote host.  If a local filename is
198 omitted, uses the basename of the remote file.
199
200 =cut
201
202 sub get {
203   my($self, $remote, $local) = @_;
204   $remote = $self->{'cwd'}. "/$remote" if $self->{'cwd'} && $remote !~ /^\//;
205   $local ||= basename($remote);
206   my $source = $self->{'host'}. ":$remote";
207   $source = $self->{'user'}. '@'. $source if $self->{'user'};
208   $self->scp($source,$local);
209 }
210
211 =item mkdir DIRECTORY
212
213 Makes a directory on the remote server.  Returns false and sets the B<errstr>
214 attribute on errors.
215
216 (Implementation note: An ssh connection is established to the remote machine
217 and '/bin/mkdir B<-p>' is used to create the directory.)
218
219 =cut
220
221 sub mkdir {
222   my($self, $directory) = @_;
223   $directory = $self->{'cwd'}. "/$directory"
224     if $self->{'cwd'} && $directory !~ /^\//;
225   my $host = $self->{'host'};
226   $host = $self->{'user'}. '@'. $host if $self->{'user'};
227   my($reader, $writer, $error ) =
228     ( new IO::Handle, new IO::Handle, new IO::Handle );
229   $writer->autoflush(1);
230   my $pid = sshopen3( $host, $writer, $reader, $error,
231                       '/bin/mkdir', '-p ', shell_quote($directory) );
232   waitpid $pid, 0;
233   if ( $? >> 8 ) {
234     chomp(my $errstr = <$error> || '');
235     $self->{errstr} = $errstr || "mkdir exited with status ". ($?>>8);
236     return 0;
237   }
238   1;
239 }
240
241 =item size FILE
242
243 Returns the size in bytes for the given file as stored on the remote server.
244 Returns 0 on error, and sets the B<errstr> attribute.  In the case of an actual
245 zero-length file on the remote server, the special value '0e0' is returned,
246 which evaluates to zero when used as a number, but is true.
247
248 (Implementation note: An ssh connection is established to the remote machine
249 and wc is used to determine the file size.)
250
251 =cut
252
253 sub size {
254   my($self, $file) = @_;
255   $file = $self->{'cwd'}. "/$file" if $self->{'cwd'} && $file !~ /^\//;
256   my $host = $self->{'host'};
257   $host = $self->{'user'}. '@'. $host if $self->{'user'};
258   my($reader, $writer, $error ) =
259     ( new IO::Handle, new IO::Handle, new IO::Handle );
260   $writer->autoflush(1);
261   #sshopen2($host, $reader, $writer, 'wc', '-c ', shell_quote($file) );
262   my $pid =
263     sshopen3($host, $writer, $reader, $error, 'wc', '-c ', shell_quote($file) );
264   waitpid $pid, 0;
265   if ( $? >> 8 ) {
266     chomp(my $errstr = <$error>);
267     $self->{errstr} = $errstr || "wc exited with status ". $?>>8;
268     0;
269   } else {
270     chomp( my $size = <$reader> || 0 );
271     if ( $size =~ /^\s*(\d+)/ ) {
272       $1 ? $1 : '0e0';
273     } else {
274       $self->{errstr} = "unparsable output from remote wc: $size";
275       0;
276     }
277   }
278 }
279
280 =item put LOCAL_FILE [, REMOTE_FILE]
281
282 Uses scp to trasnfer LOCAL_FILE to the remote host.  If a remote filename is
283 omitted, uses the basename of the local file.
284
285 =cut
286
287 sub put {
288   my($self, $local, $remote) = @_;
289   $remote ||= basename($local);
290   $remote = $self->{'cwd'}. "/$remote" if $self->{'cwd'} && $remote !~ /^\//;
291   my $dest = $self->{'host'}. ":$remote";
292   $dest = $self->{'user'}. '@'. $dest if $self->{'user'};
293   warn "scp $local $dest\n" if $DEBUG;
294   $self->scp($local, $dest);
295 }
296
297 =item binary
298
299 Compatibility method: does nothing; returns true.
300
301 =cut
302
303 sub binary { 1; }
304
305 =item quit
306
307 Compatibility method: does nothing; returns true.
308
309 =cut
310
311 sub quit { 1; }
312
313 =back
314
315 =head1 FREQUENTLY ASKED QUESTIONS
316
317 Q: How do you supply a password to connect with ssh within a perl script
318 using the Net::SSH module?
319
320 A: You don't (at least not with this module).  Use RSA or DSA keys.  See the
321    quick help in the next section and the ssh-keygen(1) manpage.
322
323 A #2: See L<Net::SCP::Expect> instead.
324
325 Q: My script is "leaking" scp processes.
326
327 A: See L<perlfaq8/"How do I avoid zombies on a Unix system">, L<IPC::Open2>,
328 L<IPC::Open3> and L<perlfunc/waitpid>.
329
330 =head1 GENERATING AND USING SSH KEYS
331
332 =over 4
333
334 =item 1 Generate keys
335
336 Type:
337
338    ssh-keygen -t rsa
339
340 And do not enter a passphrase unless you wanted to be prompted for
341 one during file copying.
342
343 Here is what you will see:
344
345    $ ssh-keygen -t rsa
346    Generating public/private rsa key pair.
347    Enter file in which to save the key (/home/User/.ssh/id_rsa):
348    Enter passphrase (empty for no passphrase):
349
350    Enter same passphrase again:
351
352    Your identification has been saved in /home/User/.ssh/id_rsa.
353    Your public key has been saved in /home/User/.ssh/id_rsa.pub.
354    The key fingerprint is:
355    5a:cd:2b:0a:cd:d9:15:85:26:79:40:0c:55:2a:f4:23 User@JEFF-CPU
356
357
358 =item 2 Copy public to machines you want to upload to
359
360 C<id_rsa.pub> is your public key. Copy it to C<~/.ssh> on target machine.
361
362 Put a copy of the public key file on each machine you want to log into.
363 Name the copy C<authorized_keys> (some implementations name this file
364 C<authorized_keys2>)
365
366 Then type:
367
368      chmod 600 authorized_keys
369
370 Then make sure your home dir on the remote machine is not group or
371 world writeable.
372
373 =back
374
375 =head1 AUTHORS
376
377 Ivan Kohler <ivan-netscp_pod@420.am>
378
379 Assistance wanted - this module could really use a maintainer with enough time
380 to at least review and apply more patches.  Or the module should just be
381 deprecated in favor of Net::SFTP::Expect or Net::SFTP::Foreign and made into a
382 simple compatiblity wrapper.  Please email Ivan if you are interested in
383 helping.
384
385 Major updates Anthony Deaver <bishop@projectmagnus.org>
386
387 Thanks to Jon Gunnip <jon@soundbite.com> for fixing a bug with size().
388
389 Patch for the mkdir method by Anthony Awtrey <tony@awtrey.com>.
390
391 Thanks to terrence brannon <tbone@directsynergy.com> for the documentation in
392 the GENERATING AND USING SSH KEYS section.
393
394 =head1 COPYRIGHT
395
396 Copyright (c) 2000 Ivan Kohler
397 Copyright (c) 2007 Freeside Internet Services, Inc.
398 All rights reserved.
399 This program is free software; you can redistribute it and/or modify it under
400 the same terms as Perl itself.
401
402 =head1 BUGS
403
404 Still has no-OO cruft.
405
406 In order to work around some problems with commercial SSH2, if the source file
407 is on the local system, and is not a directory, the B<-r> flag is omitted.
408 It's probably better just to use OpenSSH <http://www.openssh.com/> which is
409 the de-facto standard these days anyway.
410
411 The Net::FTP-style OO stuff is kinda lame.  And incomplete.
412
413 iscp doesnt expect you to be logging into the box that you are copying to
414 for the first time. so it's completely clueless about how to handle  the
415 whole 'add this file to known hosts' message so it just hangs after the
416 user hits y.  (Thanks to John L. Utz III).  To avoid this, SSH to the box
417 once first.
418
419 =head1 SEE ALSO
420
421 For a perl implementation that does not require the system B<scp> command, see
422 L<Net::SFTP> instead.
423
424 For a wrapper version that allows you to use passwords, see L<Net::SCP::Expect>
425 instead.
426
427 For a wrapper version of the newer SFTP protocol, see L<Net::SFTP::Foreign>
428 instead.
429
430 L<Net::SSH>, L<Net::SSH::Perl>, L<Net::SSH::Expect>, L<Net::SSH2>,
431 L<IPC::PerlSSH>
432
433 scp(1), ssh(1), L<IO::File>, L<IPC::Open2>, L<IPC::Open3>
434
435 =cut
436
437 1;
438
439