=head1 NAME

Fugu::SSH - run a command on another machine over SSH

=head1 SYNOPSIS

    use Fugu::SSH;

    my $ssh = Fugu::SSH->new(
        host => '127.0.0.1',
        port => 2222,
        user => 'root',
    );

    $ssh->wait_available(120) or die "the host never answered\n";

    my $result = $ssh->run_command('uname -a');
    print $result->{stdout};

=head1 DESCRIPTION

Fugu::SSH wraps L<Net::SSH2> for the things a provisioning tool does:
run a command and capture its output, write a file, and read a file
back. An interactive session falls back to ssh(1), because
L<Net::SSH2> does not give correct control of a terminal.

L<Net::SSH2> loads at connect time and not at compile time. Thus the
module keeps the core-Perl load contract of Fugu, and an installation
without the library still loads it and fails with a clear message at
the first connect.

Authentication tries the SSH agent first, when C<SSH_AUTH_SOCK> is
set, and falls back to the password when the caller gave one.

=head2 new

C<new(%args)> creates a connection object. The method opens nothing:
every other method connects for itself and disconnects when it is
done.

These are the arguments:

=over 4

=item C<host>

The host to reach. The default is C<127.0.0.1>.

=item C<port>

The port. The default is 22.

=item C<user>

The user to log in as. The default is C<root>.

=item C<password>

The password, for a host that has no key yet.

=item C<timeout>

The connection timeout in seconds. The default is 10.

=item C<known_hosts>

The path of the known_hosts file that the strict mode reads. The
default is absent: each back end then reads F<~/.ssh/known_hosts>,
which is its own default. The value has no effect when C<strict> is
false, and the module never writes the file.

=item C<strict>

Verify the host key of every connection. The default is 0.

The policy belongs to the object, and no method takes a per-call
override, because one object reaches one host. With C<strict> set,
every connecting method verifies the key against the known_hosts
file before it authenticates, and C<interactive> runs ssh(1) with
C<StrictHostKeyChecking=yes>. The name selects
C<LIBSSH2_HOSTKEY_POLICY_STRICT> for the library and
C<StrictHostKeyChecking=yes> for ssh(1).

The strict mode needs L<Net::SSH2> 0.60 or later, which fixed the
argument order of C<check_hostkey>.

=back

=head2 run_command

C<run_command($command)> runs one command and returns a hash reference
with C<stdout>, C<stderr> and C<exit_code>. A connection that fails
reports exit code 1 and a message in C<stderr>.

=head2 write_file

C<write_file($remote_path, $content, $mode)> writes a file over SFTP.
The default mode is 0644.

One SFTP write can accept less than the full buffer, so the method
loops until every byte is accepted, and it reports a write that makes
no progress as a failure. A provisioning script that arrives
half-written is worse than one that never arrived.

=head2 read_file

C<read_file($remote_path, $max_size)> reads a remote file over SFTP
and returns its bytes. The default for C<$max_size> is
C<MAX_READ_SIZE>, 64 MiB.

The method reads the size of the file first, and it refuses a size
above C<$max_size> before it reads one byte. It then compares the
byte count with that size, and it reports a short read as a failure.
A file that arrives half-read is worse than one that never arrived,
because the caller cannot see the difference.

The method returns C<undef> for every failure: a connect that fails,
an SFTP session that fails, a stat that fails, a size above
C<$max_size>, an open that fails, and a byte count that differs from
the size. It reports each reason on the debug level of L<Fugu::Log>.

An empty remote file returns the empty string, which is false. Test
C<defined> on the result, and do not test truth.

=head2 is_available

C<is_available()> reports if the host takes an authenticated
connection now.

=head2 wait_available

C<wait_available($timeout)> polls until the host takes a connection,
or until the timeout ends. The default timeout is 120 seconds.

The wait stops early when a signal arrives, through L<Fugu::Timeout>.
A poll loop must not outlive the interrupt that told the program to
stop.

=head2 interactive

C<interactive()> runs ssh(1) with the terminal of the caller and
returns its exit code.

The code is a value between 0 and 255, and not a raw wait status. A
caller that passed the raw status to C<exit> would turn a remote exit
code of 1 into C<exit(256)>, which the kernel truncates to 0, and a
failed remote command would read as a success.

The method carries the host-key policy as ssh(1) options. In the
strict mode it sets C<StrictHostKeyChecking=yes>, and it names the
known_hosts file with C<UserKnownHostsFile> when the caller gave one.
The method does not die for a host key that does not verify: ssh(1)
refuses the session itself and returns 255. In the permissive mode
the method sets C<StrictHostKeyChecking=no>, sends the known hosts to
F</dev/null>, and lowers the log level to hide the new-key report.

One difference between the back ends stays. ssh(1) also reads
F</etc/ssh/ssh_known_hosts>, and the library reads the named file
only. The method does not set C<GlobalKnownHostsFile>: an entry that
an administrator wrote there is a local trust decision.

=head1 RETURN VALUES

C<run_command()> returns a hash reference.

The two transfer methods report a result in two shapes.
C<write_file()> returns 0 on success and 1 on failure, like a
command. C<read_file()> returns the bytes on success and C<undef> on
failure, like C<Fugu::File-E<gt>read>.

C<is_available()> and C<wait_available()> return 1 or 0.
C<interactive()> returns an exit code.

=head1 ERRORS

Every method dies when L<Net::SSH2> is not installed. The message
names the module, because that failure is an installation problem and
not a connection problem. In the strict mode, every connecting method
dies when L<Net::SSH2> is older than 0.60, and the message names the
version.

No method dies for a connection that fails. One exception exists: in
the strict mode, a host key that does not verify makes the connecting
method die. The one-line message names the host, the port, the file,
and the reason of the back end, which tells a wrong key from a file
that holds no key for the host. A wrong key is permanent, a retry
cannot repair it, and a caller that asked for verification must not
lose the answer to a missing return-value test. C<interactive()> is
the other shape of the same fault: it does not die, and ssh(1)
returns 255.

=head1 SEE ALSO

ssh(1), L<Fugu::Log>, L<Fugu::Process>, L<Fugu::Timeout>, L<Net::SSH2>

=head1 AUTHORS

Dick Olsson E<lt>hi@senzilla.ioE<gt>

=head1 CAVEATS

Every method opens its own connection and closes it at the end. A
caller that runs many commands pays the handshake for each one.

C<run_command()> holds the whole output of the command in memory, and
C<read_file()> holds the whole file. The C<MAX_READ_SIZE> cap of
C<read_file()> bounds the second one at 64 MiB.

The module verifies the host key only in the strict mode, through the
C<strict> argument of C<new>. The default verifies nothing. The
module is a provisioning tool for a machine that the caller just
created, on a port on the loopback address, and such a machine holds
a new key after each install. Set C<strict> to reach a host across a
network you do not trust.

=cut
