Find rsync in PATH and pass the SSH password only to rsync
A missing /usr/bin/rsync ended in a traceback, possibly after --remove-before-backup had removed old backups, and left SSHPASS in the environment.
This commit is contained in:
@@ -229,7 +229,8 @@ class Backup:
|
||||
self._exclude_path = ''
|
||||
self._remote = False
|
||||
self._ssh: paramiko.client.SSHClient | None = None
|
||||
self._password_auth = False
|
||||
# Passed to sshpass through rsync's environment only
|
||||
self._password: str | None = None
|
||||
self._removed_count = 0
|
||||
|
||||
def check_params(self, homedir: str = '') -> int:
|
||||
@@ -558,8 +559,7 @@ class Backup:
|
||||
|
||||
ssh.connect(**host, username=self.ssh_user, timeout=SSH_TIMEOUT, password=password)
|
||||
|
||||
self._password_auth = True
|
||||
os.environ['SSHPASS'] = password
|
||||
self._password = password
|
||||
|
||||
return ssh
|
||||
except GetPassWarning as e:
|
||||
@@ -690,6 +690,16 @@ class Backup:
|
||||
|
||||
_notify('Starting backup...')
|
||||
|
||||
# Check before anything is removed (--remove-before-backup)
|
||||
rsync = which('rsync')
|
||||
|
||||
if rsync is None:
|
||||
logger.critical('rsync not found. Install rsync to perform the backup')
|
||||
|
||||
_notify('Backup failed (check log for details)')
|
||||
|
||||
return 4
|
||||
|
||||
self.define_backup_dir()
|
||||
|
||||
fd, self._inputs_path = mkstemp(prefix='tmp_inputs', text=True)
|
||||
@@ -729,7 +739,7 @@ class Backup:
|
||||
logger.info('Copying files. This may take a long time...')
|
||||
|
||||
# Build the argument list directly, so paths with spaces or quotes are passed unchanged
|
||||
args = ['/usr/bin/rsync', *self.options.split()]
|
||||
args = [rsync, *self.options.split()]
|
||||
|
||||
if self._last_backup != '':
|
||||
args.append(f'--link-dest={self._last_backup}')
|
||||
@@ -737,11 +747,15 @@ class Backup:
|
||||
args.extend([f'--exclude-from={self._exclude_path}', f'--files-from={self._inputs_path}',
|
||||
'/', f'{self._server}{self._output_dir}'])
|
||||
|
||||
# None inherits our environment
|
||||
env: dict[str, str] | None = None
|
||||
|
||||
# rsync splits the -e command itself, so quote the parts that may contain spaces
|
||||
if self.ssh_keyfile is not None:
|
||||
ssh = f'ssh -i {shlex.quote(self.ssh_keyfile)} -o StrictHostKeyChecking=accept-new'
|
||||
elif self._password_auth and self.ssh_user is not None and which('sshpass'):
|
||||
elif self._password is not None and self.ssh_user is not None and which('sshpass'):
|
||||
ssh = f'sshpass -e ssh -l {shlex.quote(self.ssh_user)} -o StrictHostKeyChecking=accept-new'
|
||||
env = {**os.environ, 'SSHPASS': self._password}
|
||||
else:
|
||||
ssh = 'ssh -o StrictHostKeyChecking=accept-new'
|
||||
|
||||
@@ -756,7 +770,7 @@ class Backup:
|
||||
args.append('--info=progress2')
|
||||
bar = _ProgressBar()
|
||||
|
||||
with Popen(args, stdin=PIPE, stdout=PIPE, stderr=STDOUT, shell=False) as p:
|
||||
with Popen(args, stdin=PIPE, stdout=PIPE, stderr=STDOUT, shell=False, env=env) as p:
|
||||
assert p.stdin is not None and isinstance(p.stdout, io.BufferedIOBase)
|
||||
p.stdin.close()
|
||||
|
||||
@@ -770,11 +784,6 @@ class Backup:
|
||||
if bar is not None:
|
||||
bar.clear()
|
||||
|
||||
try:
|
||||
del os.environ['SSHPASS']
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
returncode = p.wait()
|
||||
|
||||
if returncode == 0:
|
||||
|
||||
Reference in New Issue
Block a user