####################################################################
#
#     This file was generated using XDR::Parse version v1.0.1
#                   and LibVirt version v12.0.0
#
#      Don't edit this file, use the source template instead
#
#                 ANY CHANGES HERE WILL BE LOST !
#
####################################################################


use v5.26;
use warnings;
use experimental 'signatures';
use Future::AsyncAwait;
use Object::Pad ':experimental(inherit_field)';

class Sys::Async::Virt::Connection::SSH v0.5.2;

inherit Sys::Async::Virt::Connection::Process;

use Carp qw(croak);
use Log::Any qw($log);

use Protocol::Sys::Virt::UNIXSocket v12.0.8; # imports socket_path
use Protocol::Sys::Virt::URI v12.0.8; # imports parse_url

field $_socket   :reader :param = undef;
field $_readonly :reader :param;

sub shell_escape($val) {
    if ($val !~ m/[\s!"'`$<>#&*?;\\\[\]{}()~|^]/) { # no shell chars
        return $val;
    }

    return (q|'| . ($val =~ s/'/'\\''/gr) . q|'|);
}

my $nc_proxy =
    q{if %1$s -q 2>&1 | grep "requires an argument" >/dev/null 2>&1; } .
    q{then A=-q0; } .
    q{else A=; } .
    q{fi; } .
    q{%1$s $A -U %2$s};

my $native_proxy =
    q{virt-ssh-helper %s};

my $auto_proxy =
    q{if which virt-ssh-helper >/dev/null 2>&1; } .
    q{then %s; } .
    q{else %s; } .
    q{fi};

method _command( $url ) {
    my %c = parse_url( $url );
    my @args =  ('-e', 'none');
    push @args, ('-p', $c{port}) if $c{port};
    push @args, ('-l', $c{username}) if $c{username};
    push @args, ('-i', $c{query}->{keyfile}) if $c{query}->{keyfile};
    push @args, ('-o', 'StrictHostKeyChecking=no') if $c{query}->{no_verify};
    push @args, ('-T') if $c{query}->{no_tty};

    my $remote_cmd;
    my $proxy_mode  = $c{query}->{proxy} // 'auto';
    my $socket_path = $_socket // $c{query}->{socket} //
        socket_path(readonly => $_readonly,
                    hypervisor => $c{hypervisor},
                    mode => $c{query}->{mode},
                    type => $c{type});

    my $nc_command = sprintf($nc_proxy,
                             $c{query}->{netcat} // 'nc',
                             $socket_path);
    my $native_command = 'virt-ssh-helper ';
    $native_command .= '-r ' if $_readonly;

    # $c{proxy} is the URL to use on the proxy
    $native_command .= shell_escape($c{proxy});
    if ($proxy_mode eq 'netcat') {
        $remote_cmd = sprintf(q|sh -c %s|, shell_escape($nc_command));
    }
    elsif ($proxy_mode eq 'native') {
        $remote_cmd = $native_command;
    }
    elsif ($proxy_mode eq 'auto') {
        $remote_cmd = sprintf(q|sh -c %s|,
                              shell_escape(sprintf($auto_proxy,
                                                   $native_command,
                                                   $nc_command)));
    }
    else {
        croak $log->fatal( "Unknown proxy mode '$proxy_mode'" );
    }

    $log->trace("SSH remote command: $remote_cmd");

    my $local_cmd  = $c{query}->{command} // 'ssh';
    my @cmd = ($local_cmd, @args, '--', $c{host}, $remote_cmd);

    return @cmd;
}

method is_secure() {
    return 1;
}

1;


__END__

=head1 NAME

Sys::Async::Virt::Connection::SSH - Connection to LibVirt server over SSH

=head1 VERSION

v0.5.2

=head1 SYNOPSIS

  use v5.26;
  use Future::AsyncAwait;
  use Sys::Async::Virt::Connection::Factory;

  my $factory = Sys::Async::Virt::Connection::Factory->new;
  my $conn    = $factory->create_connection( 'qemu+ssh://localhost/system' );

=head1 DESCRIPTION

This module connects to a local LibVirt server through an ssh binary in
the system PATH, inheriting most of its behaviour from
L<Sys::Async::Virt::Connection::Process>.

B< NOTE > This module requires the C<< Future::IO->waitpid >> call to work,
which the default implementation does not provide. Any of the other backends
listed in L<Future::IO> needs to be active for this module to work.

=head1 URL PARAMETERS

This connection driver supports these parameters in the query string
of the URL, as per L<LibVirt's documentation|https://libvirt.org/uri.html#ssh-transport>:

=over 8

=item * command

=item * keyfile

=item * mode

=item * netcat

=item * no_tty

=item * no_verify

=item * proxy

=item * socket

The path of the socket to be connected to.

=back

=head1 CONSTRUCTOR

=head2 new

Not to be called directly. Instantiated via the connection factory
(L<Sys::Async::Virt::Connection::Factory>).

=head1 METHODS

=head2 is_secure

  my $bool = $conn->is_secure;

Returns C<true>.

=head1 SEE ALSO

L<LibVirt|https://libvirt.org>, L<Sys::Virt>

=head1 LICENSE AND COPYRIGHT


  Copyright (C) 2024-2026 Erik Huelsmann

All rights reserved. This program is free software;
you can redistribute it and/or modify it under the same terms as Perl itself.
