passcommand.py 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
  1. import functools
  2. import logging
  3. import shlex
  4. import borgmatic.config.paths
  5. import borgmatic.execute
  6. logger = logging.getLogger(__name__)
  7. @functools.cache
  8. def run_passcommand(passcommand, passphrase_configured, working_directory):
  9. '''
  10. Run the given passcommand using the given working directory and return the passphrase produced
  11. by the command. But bail first if a passphrase is already configured; this mimics Borg's
  12. behavior.
  13. Cache the results so that the passcommand only needs to run—and potentially prompt the user—once
  14. per borgmatic invocation.
  15. '''
  16. if passcommand and passphrase_configured:
  17. logger.warning(
  18. 'Ignoring the "encryption_passcommand" option because "encryption_passphrase" is set'
  19. )
  20. return None
  21. return borgmatic.execute.execute_command_and_capture_output(
  22. shlex.split(passcommand),
  23. working_directory=working_directory,
  24. )
  25. def load_credential(hook_config, config, credential_name):
  26. '''
  27. Given the hook configuration dict, the configuration dict, and a credential name to load, call
  28. the configured passcommand to produce and return an encryption passphrase. In effect, we're
  29. doing an end-run around Borg by invoking its passcommand ourselves. This allows us to pass the
  30. resulting passphrase to multiple different Borg invocations without the user having to be
  31. prompted multiple times.
  32. If no passcommand is configured, then return None.
  33. The credential name must be "encryption_passphrase"; that's the only supported credential with
  34. this particular hook.
  35. '''
  36. if credential_name != 'encryption_passphrase':
  37. raise ValueError(
  38. f'Credential name "{credential_name}" is not supported for the passphrase credential hook'
  39. )
  40. passcommand = config.get('encryption_passcommand')
  41. if not passcommand:
  42. return None
  43. passphrase = config.get('encryption_passphrase')
  44. working_directory = borgmatic.config.paths.get_working_directory(config)
  45. return run_passcommand(passcommand, bool(passphrase), working_directory)