From f397687968b5490b85ff269745654a2bbd2bea0e Mon Sep 17 00:00:00 2001 From: Antonio Russo Date: Sun, 1 Nov 2020 10:55:50 -0700 Subject: [PATCH] Do not require user to be specified for syncoid Extends syncoid remote capabilities to match that of ssh as closely as possible: allow a remote dataset to be specified without a username. - Detect if a remote reference is possible by looking for a : before any / characters. - Check if there are any pool names that might conflict with this name. E.g., 'weird:symbol/ds' might refer to the pool "symbol" on host "weird", and dataset ds. OR it might refer to the local pool "weird:symbol" and dataset ds. - Prefer local pools, matching existing behavior. No preexisting functioning configurations will break. - The name of the control socket is changed slightly. - A bug in the handling of remote datasets with colons in the name is addressed. Signed-off-by: Antonio Russo --- syncoid | 49 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/syncoid b/syncoid index a4a5879..68587ce 100755 --- a/syncoid +++ b/syncoid @@ -1420,20 +1420,53 @@ sub targetexists { sub getssh { my $fs = shift; - my $rhost; + my $rhost = ""; my $isroot; my $socket; + my $remoteuser = ""; # if we got passed something with an @ in it, we assume it's an ssh connection, eg root@myotherbox if ($fs =~ /\@/) { $rhost = $fs; - $fs =~ s/^\S*\@\S*://; + $fs =~ s/^[^\@:]*\@[^\@:]*://; $rhost =~ s/:\Q$fs\E$//; - my $remoteuser = $rhost; - $remoteuser =~ s/\@.*$//; + $remoteuser = $rhost; + $remoteuser =~ s/\@.*$//; + # do not require a username to be specified + $rhost =~ s/^@//; + } elsif ($fs =~ m{^[^/]*:}) { + # if we got passed something with an : in it, BEFORE any forward slash + # (i.e., not in a dataset name) it MAY be an ssh connection + # but we need to check if there is a pool with that name + my $pool = $fs; + $pool =~ s%/.*$%%; + my ($pools, $error, $exit) = capture { + system("$zfscmd list -d0 -H -oname"); + }; + $rhost = $fs; + if ($exit != 0) { + warn "Unable to enumerate pools (is zfs available?)"; + } else { + foreach (split(/\n/,$pools)) { + if ($_ eq $pool) { + # there's a pool with this name. + $rhost = ""; + last; + } + } + } + if ($rhost ne "") { + # there's no pool that might conflict with this + $rhost =~ s/:.*$//; + $fs =~ s/\Q$rhost\E://; + } + } + + if ($rhost ne "") { if ($remoteuser eq 'root' || $args{'no-privilege-elevation'}) { $isroot = 1; } else { $isroot = 0; } # now we need to establish a persistent master SSH connection - $socket = "/tmp/syncoid-$remoteuser-$rhost-" . time(); + $socket = "/tmp/syncoid-$rhost-" . time(); + open FH, "$sshcmd -M -S $socket -o ControlPersist=1m $args{'sshport'} $rhost exit |"; close FH; @@ -1882,9 +1915,9 @@ syncoid - ZFS snapshot replication tool =head1 SYNOPSIS syncoid [options]... SOURCE TARGET - or syncoid [options]... SOURCE USER@HOST:TARGET - or syncoid [options]... USER@HOST:SOURCE TARGET - or syncoid [options]... USER@HOST:SOURCE USER@HOST:TARGET + or syncoid [options]... SOURCE [[USER]@]HOST:TARGET + or syncoid [options]... [[USER]@]HOST:SOURCE TARGET + or syncoid [options]... [[USER]@]HOST:SOURCE [[USER]@]HOST:TARGET SOURCE Source ZFS dataset. Can be either local or remote TARGET Target ZFS dataset. Can be either local or remote