|  | @@ -53,60 +53,90 @@ def backup(config, no_print_config, no_print_statistics, tab_dry):
 | 
	
		
			
				|  |  |              if 'encrypt_key' in backup:
 | 
	
		
			
				|  |  |                  backup_command += ['--encrypt-key', backup['encrypt_key']]
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        # selectors
 | 
	
		
			
				|  |  | +        # determine source
 | 
	
		
			
				|  |  |          try:
 | 
	
		
			
				|  |  | -            selectors = backup['selectors']
 | 
	
		
			
				|  |  | +            source_type = backup['source_type']
 | 
	
		
			
				|  |  |          except KeyError:
 | 
	
		
			
				|  |  | -            selectors = []
 | 
	
		
			
				|  |  | -        for selector in selectors:
 | 
	
		
			
				|  |  | -            if selector['option'] in ['include', 'exclude']:
 | 
	
		
			
				|  |  | -                backup_command += ['--{}'.format(selector['option']), selector['shell_pattern']]
 | 
	
		
			
				|  |  | +            source_type = 'local'
 | 
	
		
			
				|  |  | +        source_mount_path = None
 | 
	
		
			
				|  |  | +        try:
 | 
	
		
			
				|  |  | +            if source_type == 'local':
 | 
	
		
			
				|  |  | +                local_source_path = backup['source_path']
 | 
	
		
			
				|  |  | +            elif source_type == 'sshfs':
 | 
	
		
			
				|  |  | +                source_mount_path = tempfile.mkdtemp(prefix = 'duplitab-source-sshfs-')
 | 
	
		
			
				|  |  | +                sshfs_mount(
 | 
	
		
			
				|  |  | +                    url = 'sftp://{}/{}'.format(backup['source_host'], backup['source_path']),
 | 
	
		
			
				|  |  | +                    path = source_mount_path,
 | 
	
		
			
				|  |  | +                    )
 | 
	
		
			
				|  |  | +                local_source_path = source_mount_path
 | 
	
		
			
				|  |  | +                backup_command.append('--allow-source-mismatch')
 | 
	
		
			
				|  |  |              else:
 | 
	
		
			
				|  |  | -                raise Exception("unsupported selector option '{}'".format(selector['option']))
 | 
	
		
			
				|  |  | +                raise Exception("unsupported source type '{}'".format(source_type))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        # statistics
 | 
	
		
			
				|  |  | -        if no_print_statistics:
 | 
	
		
			
				|  |  | -            backup_command.append('--no-print-statistics')
 | 
	
		
			
				|  |  | +            # selectors
 | 
	
		
			
				|  |  | +            try:
 | 
	
		
			
				|  |  | +                selectors = backup['selectors']
 | 
	
		
			
				|  |  | +            except KeyError:
 | 
	
		
			
				|  |  | +                selectors = []
 | 
	
		
			
				|  |  | +            for selector in selectors:
 | 
	
		
			
				|  |  | +                if selector['option'] in ['include', 'exclude']:
 | 
	
		
			
				|  |  | +                    shell_pattern = selector['shell_pattern']
 | 
	
		
			
				|  |  | +                    if shell_pattern.startswith(backup['source_path']):
 | 
	
		
			
				|  |  | +                        shell_pattern = shell_pattern.replace(
 | 
	
		
			
				|  |  | +                                backup['source_path'],
 | 
	
		
			
				|  |  | +                                local_source_path,
 | 
	
		
			
				|  |  | +                                1,
 | 
	
		
			
				|  |  | +                                )
 | 
	
		
			
				|  |  | +                    backup_command += ['--{}'.format(selector['option']), shell_pattern]
 | 
	
		
			
				|  |  | +                else:
 | 
	
		
			
				|  |  | +                    raise Exception("unsupported selector option '{}'".format(selector['option']))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        # source
 | 
	
		
			
				|  |  | -        try:
 | 
	
		
			
				|  |  | -            source_type = backup['source_type']
 | 
	
		
			
				|  |  | -        except KeyError:
 | 
	
		
			
				|  |  | -            source_type = 'local'
 | 
	
		
			
				|  |  | -        if source_type == 'local':
 | 
	
		
			
				|  |  | -            backup_command += [backup['source_path']]
 | 
	
		
			
				|  |  | -        else:
 | 
	
		
			
				|  |  | -            raise Exception("unsupported source type '{}'".format(source_type))
 | 
	
		
			
				|  |  | +            # statistics
 | 
	
		
			
				|  |  | +            if no_print_statistics:
 | 
	
		
			
				|  |  | +                backup_command.append('--no-print-statistics')
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        # target
 | 
	
		
			
				|  |  | -        try:
 | 
	
		
			
				|  |  | +            # source path
 | 
	
		
			
				|  |  | +            backup_command.append(local_source_path)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            # target
 | 
	
		
			
				|  |  |              target_mount_path = None
 | 
	
		
			
				|  |  | -            if 'target_via_sshfs' in backup and backup['target_via_sshfs']:
 | 
	
		
			
				|  |  | -                target_mount_path = tempfile.mkdtemp(prefix = 'duplitab-target-sshfs-')
 | 
	
		
			
				|  |  | -                backup_command += ['file://' + target_mount_path]
 | 
	
		
			
				|  |  | -                sshfs_mount(backup['target_url'], target_mount_path)
 | 
	
		
			
				|  |  | -                # set backup name to make archive dir persistent
 | 
	
		
			
				|  |  | -                # (default name: hash of target url)
 | 
	
		
			
				|  |  | -                backup_command += ['--name', hashlib.sha1(backup['target_url'].encode('utf-8')).hexdigest()]
 | 
	
		
			
				|  |  | -            else:
 | 
	
		
			
				|  |  | -                backup_command += [backup['target_url']]
 | 
	
		
			
				|  |  |              try:
 | 
	
		
			
				|  |  | -                if tab_dry:
 | 
	
		
			
				|  |  | -                    print('* {}'.format(command_join(backup_command)))
 | 
	
		
			
				|  |  | +                if 'target_via_sshfs' in backup and backup['target_via_sshfs']:
 | 
	
		
			
				|  |  | +                    target_mount_path = tempfile.mkdtemp(prefix = 'duplitab-target-sshfs-')
 | 
	
		
			
				|  |  | +                    backup_command += ['file://' + target_mount_path]
 | 
	
		
			
				|  |  | +                    sshfs_mount(backup['target_url'], target_mount_path)
 | 
	
		
			
				|  |  | +                    # set backup name to make archive dir persistent
 | 
	
		
			
				|  |  | +                    # (default name: hash of target url)
 | 
	
		
			
				|  |  | +                    backup_command += ['--name', hashlib.sha1(backup['target_url'].encode('utf-8')).hexdigest()]
 | 
	
		
			
				|  |  |                  else:
 | 
	
		
			
				|  |  | -                    print('+ {}'.format(command_join(backup_command)))
 | 
	
		
			
				|  |  | -                    subprocess.check_call(backup_command)
 | 
	
		
			
				|  |  | +                    backup_command += [backup['target_url']]
 | 
	
		
			
				|  |  | +                try:
 | 
	
		
			
				|  |  | +                    if tab_dry:
 | 
	
		
			
				|  |  | +                        print('* {}'.format(command_join(backup_command)))
 | 
	
		
			
				|  |  | +                    else:
 | 
	
		
			
				|  |  | +                        print('+ {}'.format(command_join(backup_command)))
 | 
	
		
			
				|  |  | +                        subprocess.check_call(backup_command)
 | 
	
		
			
				|  |  | +                finally:
 | 
	
		
			
				|  |  | +                    if target_mount_path:
 | 
	
		
			
				|  |  | +                        try:
 | 
	
		
			
				|  |  | +                            sshfs_unmount(target_mount_path)
 | 
	
		
			
				|  |  | +                        except subprocess.CalledProcessError:
 | 
	
		
			
				|  |  | +                            time.sleep(1)
 | 
	
		
			
				|  |  | +                            sshfs_unmount(target_mount_path)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |              finally:
 | 
	
		
			
				|  |  |                  if target_mount_path:
 | 
	
		
			
				|  |  | +                    os.rmdir(target_mount_path)
 | 
	
		
			
				|  |  | +                if source_mount_path:
 | 
	
		
			
				|  |  |                      try:
 | 
	
		
			
				|  |  | -                        sshfs_unmount(target_mount_path)
 | 
	
		
			
				|  |  | +                        sshfs_unmount(source_mount_path)
 | 
	
		
			
				|  |  |                      except subprocess.CalledProcessError:
 | 
	
		
			
				|  |  |                          time.sleep(1)
 | 
	
		
			
				|  |  | -                        sshfs_unmount(target_mount_path)
 | 
	
		
			
				|  |  | +                        sshfs_unmount(source_mount_path)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          finally:
 | 
	
		
			
				|  |  | -            if target_mount_path:
 | 
	
		
			
				|  |  | -                os.rmdir(target_mount_path)
 | 
	
		
			
				|  |  | +            if source_mount_path:
 | 
	
		
			
				|  |  | +                os.rmdir(source_mount_path)
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  def run(command, config_path, no_print_config, no_print_statistics, tab_dry):
 | 
	
		
			
				|  |  |  
 |