Skip to content

Commit

Permalink
Merge pull request #8098 from ThomasWaldmann/new-rc-master
Browse files Browse the repository at this point in the history
optional more specific return codes (master)
  • Loading branch information
ThomasWaldmann committed Feb 18, 2024
2 parents 0e859a6 + ed28eb9 commit ad3f136
Show file tree
Hide file tree
Showing 58 changed files with 963 additions and 437 deletions.
209 changes: 143 additions & 66 deletions docs/internals/frontends.rst
Original file line number Diff line number Diff line change
Expand Up @@ -565,92 +565,169 @@ Message IDs are strings that essentially give a log message or operation a name,
full text, since texts change more frequently. Message IDs are unambiguous and reduce the need to parse
log messages.

Assigned message IDs are:
Assigned message IDs and related error RCs (exit codes) are:

.. See scripts/errorlist.py; this is slightly edited.
Errors
Archive.AlreadyExists
Error rc: 2 traceback: no
Error: {}
ErrorWithTraceback rc: 2 traceback: yes
Error: {}

Buffer.MemoryLimitExceeded rc: 2 traceback: no
Requested buffer size {} is above the limit of {}.
EfficientCollectionQueue.SizeUnderflow rc: 2 traceback: no
Could not pop_front first {} elements, collection only has {} elements..
RTError rc: 2 traceback: no
Runtime Error: {}

CancelledByUser rc: 3 traceback: no
Cancelled by user.

CommandError rc: 4 traceback: no
Command Error: {}
PlaceholderError rc: 5 traceback: no
Formatting Error: "{}".format({}): {}({})
InvalidPlaceholder rc: 6 traceback: no
Invalid placeholder "{}" in string: {}

Repository.AlreadyExists rc: 10 traceback: no
A repository already exists at {}.
Repository.CheckNeeded rc: 12 traceback: yes
Inconsistency detected. Please run "borg check {}".
Repository.DoesNotExist rc: 13 traceback: no
Repository {} does not exist.
Repository.InsufficientFreeSpaceError rc: 14 traceback: no
Insufficient free space to complete transaction (required: {}, available: {}).
Repository.InvalidRepository rc: 15 traceback: no
{} is not a valid repository. Check repo config.
Repository.InvalidRepositoryConfig rc: 16 traceback: no
{} does not have a valid configuration. Check repo config [{}].
Repository.ObjectNotFound rc: 17 traceback: yes
Object with key {} not found in repository {}.
Repository.ParentPathDoesNotExist rc: 18 traceback: no
The parent path of the repo directory [{}] does not exist.
Repository.PathAlreadyExists rc: 19 traceback: no
There is already something at {}.
Repository.StorageQuotaExceeded rc: 20 traceback: no
The storage quota ({}) has been exceeded ({}). Try deleting some archives.

MandatoryFeatureUnsupported rc: 25 traceback: no
Unsupported repository feature(s) {}. A newer version of borg is required to access this repository.
NoManifestError rc: 26 traceback: no
Repository has no manifest.
UnsupportedManifestError rc: 27 traceback: no
Unsupported manifest envelope. A newer version is required to access this repository.

Archive.AlreadyExists rc: 30 traceback: no
Archive {} already exists
Archive.DoesNotExist
Archive.DoesNotExist rc: 31 traceback: no
Archive {} does not exist
Archive.IncompatibleFilesystemEncodingError
Archive.IncompatibleFilesystemEncodingError rc: 32 traceback: no
Failed to encode filename "{}" into file system encoding "{}". Consider configuring the LANG environment variable.
Cache.CacheInitAbortedError
Cache initialization aborted
Cache.EncryptionMethodMismatch
Repository encryption method changed since last access, refusing to continue
Cache.RepositoryAccessAborted
Repository access aborted
Cache.RepositoryIDNotUnique
Cache is newer than repository - do you have multiple, independently updated repos with same ID?
Cache.RepositoryReplay
Cache is newer than repository - this is either an attack or unsafe (multiple repos with same ID)
Buffer.MemoryLimitExceeded
Requested buffer size {} is above the limit of {}.
ExtensionModuleError
The Borg binary extension modules do not seem to be installed properly
IntegrityError
Data integrity error: {}
NoManifestError
Repository has no manifest.
PlaceholderError
Formatting Error: "{}".format({}): {}({})
KeyfileInvalidError

KeyfileInvalidError rc: 40 traceback: no
Invalid key file for repository {} found in {}.
KeyfileMismatchError
KeyfileMismatchError rc: 41 traceback: no
Mismatch between repository {} and key file {}.
KeyfileNotFoundError
KeyfileNotFoundError rc: 42 traceback: no
No key file for repository {} found in {}.
PassphraseWrong
passphrase supplied in BORG_PASSPHRASE is incorrect
PasswordRetriesExceeded
exceeded the maximum password retries
RepoKeyNotFoundError
No key entry found in the config of repository {}.
UnsupportedManifestError
Unsupported manifest envelope. A newer version is required to access this repository.
UnsupportedPayloadError
Unsupported payload type {}. A newer version is required to access this repository.
NotABorgKeyFile
NotABorgKeyFile rc: 43 traceback: no
This file is not a borg key backup, aborting.
RepoIdMismatch
RepoKeyNotFoundError rc: 44 traceback: no
No key entry found in the config of repository {}.
RepoIdMismatch rc: 45 traceback: no
This key backup seems to be for a different backup repository, aborting.
UnencryptedRepo
Keymanagement not available for unencrypted repositories.
UnknownKeyType
Keytype {0} is unknown.
LockError
UnencryptedRepo rc: 46 traceback: no
Key management not available for unencrypted repositories.
UnknownKeyType rc: 47 traceback: no
Key type {0} is unknown.
UnsupportedPayloadError rc: 48 traceback: no
Unsupported payload type {}. A newer version is required to access this repository.
UnsupportedKeyFormatError rc: 49 traceback:no
Your borg key is stored in an unsupported format. Try using a newer version of borg.


NoPassphraseFailure rc: 50 traceback: no
can not acquire a passphrase: {}
PasscommandFailure rc: 51 traceback: no
passcommand supplied in BORG_PASSCOMMAND failed: {}
PassphraseWrong rc: 52 traceback: no
passphrase supplied in BORG_PASSPHRASE, by BORG_PASSCOMMAND or via BORG_PASSPHRASE_FD is incorrect.
PasswordRetriesExceeded rc: 53 traceback: no
exceeded the maximum password retries

Cache.CacheInitAbortedError rc: 60 traceback: no
Cache initialization aborted
Cache.EncryptionMethodMismatch rc: 61 traceback: no
Repository encryption method changed since last access, refusing to continue
Cache.RepositoryAccessAborted rc: 62 traceback: no
Repository access aborted
Cache.RepositoryIDNotUnique rc: 63 traceback: no
Cache is newer than repository - do you have multiple, independently updated repos with same ID?
Cache.RepositoryReplay rc: 64 traceback: no
Cache, or information obtained from the security directory is newer than repository - this is either an attack or unsafe (multiple repos with same ID)

LockError rc: 70 traceback: no
Failed to acquire the lock {}.
LockErrorT
LockErrorT rc: 71 traceback: yes
Failed to acquire the lock {}.
ConnectionClosed
LockFailed rc: 72 traceback: yes
Failed to create/acquire the lock {} ({}).
LockTimeout rc: 73 traceback: no
Failed to create/acquire the lock {} (timeout).
NotLocked rc: 74 traceback: yes
Failed to release the lock {} (was not locked).
NotMyLock rc: 75 traceback: yes
Failed to release the lock {} (was/is locked, but not by me).

ConnectionClosed rc: 80 traceback: no
Connection closed by remote host
InvalidRPCMethod
ConnectionClosedWithHint rc: 81 traceback: no
Connection closed by remote host. {}
InvalidRPCMethod rc: 82 traceback: no
RPC method {} is not valid
PathNotAllowed
Repository path not allowed
RemoteRepository.RPCServerOutdated
PathNotAllowed rc: 83 traceback: no
Repository path not allowed: {}
RemoteRepository.RPCServerOutdated rc: 84 traceback: no
Borg server is too old for {}. Required version {}
UnexpectedRPCDataFormatFromClient
UnexpectedRPCDataFormatFromClient rc: 85 traceback: no
Borg {}: Got unexpected RPC data format from client.
UnexpectedRPCDataFormatFromServer
UnexpectedRPCDataFormatFromServer rc: 86 traceback: no
Got unexpected RPC data format from server:
{}
Repository.AlreadyExists
Repository {} already exists.
Repository.CheckNeeded
Inconsistency detected. Please run "borg check {}".
Repository.DoesNotExist
Repository {} does not exist.
Repository.InsufficientFreeSpaceError
Insufficient free space to complete transaction (required: {}, available: {}).
Repository.InvalidRepository
{} is not a valid repository. Check repo config.
Repository.AtticRepository
Attic repository detected. Please run "borg upgrade {}".
Repository.ObjectNotFound
Object with key {} not found in repository {}.

IntegrityError rc: 90 traceback: yes
Data integrity error: {}
FileIntegrityError rc: 91 traceback: yes
File failed integrity check: {}
DecompressionError rc: 92 traceback: yes
Decompression error: {}


Warnings
BorgWarning rc: 1
Warning: {}
BackupWarning rc: 1
{}: {}

FileChangedWarning rc: 100
{}: file changed while we backed it up
IncludePatternNeverMatchedWarning rc: 101
Include pattern '{}' never matched.
BackupError rc: 102
{}: backup error
BackupRaceConditionError rc: 103
{}: file type or inode changed while we backed it up (race condition, skipped file)
BackupOSError rc: 104
{}: {}
BackupPermissionError rc: 105
{}: {}
BackupIOError rc: 106
{}: {}
BackupFileNotFoundError rc: 107
{}: {}

Operations
- cache.begin_transaction
Expand Down
3 changes: 3 additions & 0 deletions docs/usage/general/environment.rst.inc
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ General:
Main usecase for this is to automate fully ``borg change-passphrase``.
BORG_DISPLAY_PASSPHRASE
When set, use the value to answer the "display the passphrase for verification" question when defining a new passphrase for encrypted repositories.
BORG_EXIT_CODES
When set to "modern", the borg process will return more specific exit codes (rc).
Default is "legacy" and returns rc 2 for all errors, 1 for all warnings, 0 for success.
BORG_HOST_ID
Borg usually computes a host id from the FQDN plus the results of ``uuid.getnode()`` (which usually returns
a unique id based on the MAC address of the network interface. Except if that MAC happens to be all-zero - in
Expand Down
6 changes: 4 additions & 2 deletions docs/usage/general/return-codes.rst.inc
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ Borg can exit with the following return codes (rc):
Return code Meaning
=========== =======
0 success (logged as INFO)
1 warning (operation reached its normal end, but there were warnings --
1 generic warning (operation reached its normal end, but there were warnings --
you should check the log, logged as WARNING)
2 error (like a fatal error, a local or remote exception, the operation
2 generic error (like a fatal error, a local or remote exception, the operation
did not reach its normal end, logged as ERROR)
3..99 specific error (enabled by BORG_EXIT_CODES=modern)
100..127 specific warning (enabled by BORG_EXIT_CODES=modern)
128+N killed by signal N (e.g. 137 == kill -9)
=========== =======
Expand Down
63 changes: 56 additions & 7 deletions scripts/errorlist.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,63 @@
#!/usr/bin/env python3
# this script automatically generates the error list for the docs by
# looking at the "Error" class and its subclasses.

from textwrap import indent

import borg.archiver # noqa: F401 - need import to get Error and ErrorWithTraceback subclasses.
from borg.helpers import Error, ErrorWithTraceback
import borg.archiver # noqa: F401 - need import to get Error subclasses.
from borg.constants import * # NOQA
from borg.helpers import Error, BackupError, BorgWarning

classes = Error.__subclasses__() + ErrorWithTraceback.__subclasses__()

for cls in sorted(classes, key=lambda cls: (cls.__module__, cls.__qualname__)):
if cls is ErrorWithTraceback:
continue
print(" ", cls.__qualname__)
def subclasses(cls):
direct_subclasses = cls.__subclasses__()
return set(direct_subclasses) | set(s for c in direct_subclasses for s in subclasses(c))


# 0, 1, 2 are used for success, generic warning, generic error
# 3..99 are available for specific errors
# 100..127 are available for specific warnings
# 128+ are reserved for signals
free_error_rcs = set(range(EXIT_ERROR_BASE, EXIT_WARNING_BASE)) # 3 .. 99
free_warning_rcs = set(range(EXIT_WARNING_BASE, EXIT_SIGNAL_BASE)) # 100 .. 127

# these classes map to rc 2
generic_error_rc_classes = set()
generic_warning_rc_classes = set()

error_classes = {Error} | subclasses(Error)

for cls in sorted(error_classes, key=lambda cls: (cls.__module__, cls.__qualname__)):
traceback = "yes" if cls.traceback else "no"
rc = cls.exit_mcode
print(" ", cls.__qualname__, "rc:", rc, "traceback:", traceback)
print(indent(cls.__doc__, " " * 8))
if rc in free_error_rcs:
free_error_rcs.remove(rc)
elif rc == 2:
generic_error_rc_classes.add(cls.__qualname__)
else: # rc != 2
# if we did not intentionally map this to the generic error rc, this might be an issue:
print(f"ERROR: {rc} is not a free/available RC, but either duplicate or invalid")

print()
print("free error RCs:", sorted(free_error_rcs))
print("generic errors:", sorted(generic_error_rc_classes))

warning_classes = {BorgWarning} | subclasses(BorgWarning) | {BackupError} | subclasses(BackupError)

for cls in sorted(warning_classes, key=lambda cls: (cls.__module__, cls.__qualname__)):
rc = cls.exit_mcode
print(" ", cls.__qualname__, "rc:", rc)
print(indent(cls.__doc__, " " * 8))
if rc in free_warning_rcs:
free_warning_rcs.remove(rc)
elif rc == 1:
generic_warning_rc_classes.add(cls.__qualname__)
else: # rc != 1
# if we did not intentionally map this to the generic warning rc, this might be an issue:
print(f"ERROR: {rc} is not a free/available RC, but either duplicate or invalid")

print("\n")
print("free warning RCs:", sorted(free_warning_rcs))
print("generic warnings:", sorted(generic_warning_rc_classes))

0 comments on commit ad3f136

Please sign in to comment.