Viewing file: rsync-user-homedir.pl (5.76 KB) -rwxr-xr-x Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
#!/usr/local/cpanel/3rdparty/bin/perl
# cpanel - bin/rsync-user-homedir.pl Copyright 2022 cPanel, L.L.C. # All rights reserved. # copyright@cpanel.net http://cpanel.net # This code is subject to the cPanel license. Unauthorized copying is prohibited
package scripts::rsync_user_homedir;
use strict; use warnings;
use Cpanel::BinCheck::Lite (); use autodie;
=encoding utf-8
=head1 DESCRIPTION
This script exemplifies the client-side functionality of end classes of L<Cpanel::Server::CpXfer::Base::acctxferrsync>.
=head1 USAGE (INTERNAL TO L<rsync(1)>)
B<IMPORTANT:> You aren’t meant to call this script directly; L<rsync(1)> calls it via its C<--rsh> parameter.
For the sake of completeness, though, this script’s arguments are:
For cPanel:
rsync-user-homedir.pl --cpanel [--insecure] <--apitoken-fd=# | --apitoken=...> <hostname> <username> <rsyncargs..>
For WHM:
rsync-user-homedir.pl --whm [--insecure] <--apitoken-fd=# | --apitoken=...> --homedir-user=<username> <hostname> <token_username> <rsyncargs..>
Note that the API token, for security purposes, B<MUST> be passed via file descriptor in production code.
Also note that, in both cases, all positional parameters other than C<E<lt>hostnameE<gt>> will come from rsync.
=head1 REAL-USAGE EXAMPLES
Below are examples of how you’ll actually give this script to L<rsync(1)>.
The following …
rsync --archive --rsh '/usr/local/cpanel/scripts/rsync-user-homedir.pl --cpanel --apitoken=OF24KGWQS9Q8SWI6Y5PNJJHLMBY3UX6Z localhost' theusername: /where/to
… will back up C<theusername>’s home directory contents to F</where/to>. It will authenticate to C<localhost> via the given cPanel API token.
Here is an example of WHM usage:
rsync --archive --rsh '/usr/local/cpanel/scripts/rsync-user-homedir.pl --whm --homedir-user=theuser --apitoken-fd=5 example.com' superman: /where/to
Note the additional C<--homedir-user>; this is the user whose home directory will be backed up. C<superman>’s given WHM API token is how we will authenticate to WHM on the remote server C<example.com>.
The C<--insecure> flag tolerates TLS handshake errors (e.g., from self-signed or invalid certificates). As in L<curl(1)>, you may alias this flag as C<-k>.
=head1 SEE ALSO
F<t/support/rsync_cpsrvd_client.pl> exemplifies how to do this via the WHM endpoint exclusively and assumes that there is a root WHM access hash. (WHM access hashes were a forerunner to API tokens.)
=cut
#----------------------------------------------------------------------
use parent qw( Cpanel::HelpfulScript );
use IO::Socket::SSL (); use Cpanel::JSON::XS ();
use Cpanel::HTTP::QueryString (); use Cpanel::Services::Ports ();
# It’s curious that CPAN doesn’t seem to have anything to do this. use Cpanel::Interconnect ();
use constant _OPTIONS => ( 'cpanel', 'whm|whostmgr', 'insecure|k', 'homedir-user=s', 'apitoken-fd=i', 'apitoken=s', );
use constant _ACCEPT_UNNAMED => 1;
Cpanel::BinCheck::Lite::check_argv();
__PACKAGE__->new(@ARGV)->run() if !caller;
sub run { my ($self) = @_;
my ( $hostname, $username, @rsync_cmd ) = $self->getopt_unnamed();
if ( !$hostname ) { die $self->help('Need a hostname and a username!'); } elsif ( !$username ) { die $self->help('Need a username for the API token!'); } elsif ( !@rsync_cmd ) { die $self->help('Need an rsync command/arguments!'); }
my ( $app, $port, $remote_user );
if ( $self->getopt('cpanel') ) { if ( $self->getopt('whm') ) { die $self->help('Give either --cpanel or --whm, not both.'); }
$app = 'cpanel'; $port = $Cpanel::Services::Ports::SERVICE{'cpanels'}; } elsif ( $self->getopt('whm') ) { $app = 'whm'; $port = $Cpanel::Services::Ports::SERVICE{'whostmgrs'};
$remote_user = $self->getopt('homedir-user') // do { die $self->help('WHM requires a --homedir-user.'); }; } else { die $self->help('Give --cpanel or --whm.'); }
my $token;
if ( my $fd = $self->getopt('apitoken-fd') ) { if ( length $self->getopt('apitoken') ) { die $self->help('Do not give --apitoken with --apitoken-fd.'); }
open my $rfh, '<&=', $fd or die "open(<&=$fd): $!";
local $/; $token = readline $rfh; } else { $token = $self->getopt('apitoken');
if ( length $token ) { warn "XXX SECURITY ALERT: Using API token from the command line.\n"; } }
if ( !length $token ) { die $self->help('Give an API token.'); }
my $query = Cpanel::HTTP::QueryString::make_query_string( ( $remote_user ? ( username => $remote_user ) : () ), rsync_arguments => Cpanel::JSON::XS::encode_json( \@rsync_cmd ), );
my $path_query = "/cpxfer/acctxferrsync?$query";
my $req = join( "\r\n", "GET $path_query HTTP/1.0", "Authorization: $app $username:$token", q<>, q<>, );
my $cl = _get_socket( $hostname, $port, ( $self->getopt('insecure') ? ( SSL_verify_mode => 0 ) : () ), );
syswrite( $cl, $req );
my $response = do { local $/ = "\r\n\r\n"; <$cl> };
if ( $response !~ m{^HTTP.*? 2} ) { print STDERR "*** ERROR ***:\n$response"; local $/; die readline($cl); }
Cpanel::Interconnect->new( 'handles' => [ $cl, [ \*STDIN, \*STDOUT ] ] )->connect();
return; }
sub _get_socket { my ( $hostname, $port, @extra_args ) = @_;
return IO::Socket::SSL->new( PeerHost => $hostname, PeerPort => $port, @extra_args, ) || die "cannot connect: $IO::Socket::SSL::SSL_ERROR: $! ($@)"; }
1;
|