gpg encryption support

This commit is contained in:
Q
2023-10-22 10:34:45 +03:00
parent 71ce8d182f
commit 08a2e6b28b

View File

@@ -1,12 +1,12 @@
#!/usr/bin/env python3
import datetime
import subprocess
import os
import sys
import shlex
import json
import hashlib
import json
import os
import shlex
import subprocess
import sys
from argparse import ArgumentParser
VERSION = "0.0.9"
@@ -23,13 +23,13 @@ class TB:
self.ssh_args = options.ssh_args
self.delete_older = options.del_older
self.keep_n = options.keep_n
self.do_gpg = options.gpg
self.delete_disk_percentage = options.del_used
self.config_file = os.path.join(self.base_folder, "tar-backup.txt")
self.lock_file = os.path.join(self.base_folder, "tar-backup.lock")
if not self.backup_source.endswith("/"):
self.backup_source += "/"
self.current = os.path.join(self.base_folder, "current")
self.folder_format = "backup-%Y%m%d-%H%M%S"
self.backup_folder = datetime.datetime.now().strftime(self.folder_format)
os.makedirs(self.base_folder, exist_ok=True)
@@ -38,8 +38,8 @@ class TB:
self.write_config()
self.clean_old()
success = self.make_backup()
if success:
self.make_softlinks()
self.make_softlinks(success)
self.lock_clean()
if not success:
@@ -48,9 +48,7 @@ class TB:
def diskused(self):
"""in percents"""
pcent = subprocess.check_output(
["df", "--output=pcent", self.base_folder]
).decode("utf8")
pcent = subprocess.check_output(["df", "--output=pcent", self.base_folder]).decode("utf8")
try:
pcent = int("".join([x for x in pcent if x.isdigit()]))
except Exception as e:
@@ -70,8 +68,7 @@ class TB:
dirs = [
d
for d in sorted(os.listdir(self.base_folder))
if d.startswith("backup-")
and os.path.isdir(os.path.join(self.base_folder, d))
if d.startswith("backup-") and os.path.isdir(os.path.join(self.base_folder, d))
]
for i, d in enumerate(dirs):
if self.delete_older:
@@ -89,23 +86,21 @@ class TB:
else:
not_kept = False
print(
"{}: {} -- old: {}, disk full: {}, keep n: {}".format(
i, d, is_old, is_disk_full, not_kept
)
)
print("{}: {} -- old: {}, disk full: {}, keep n: {}".format(i, d, is_old, is_disk_full, not_kept))
if any((is_old, is_disk_full, not_kept)):
print("Deleting {}".format(d))
cmd = ["rm", "-rf", os.path.join(self.base_folder, d)]
subprocess.call(cmd, shell=False)
def make_softlinks(self):
try:
os.remove(self.current)
except Exception:
pass
if os.path.exists(os.path.join(self.base_folder, self.backup_folder)):
os.symlink(self.backup_folder, self.current)
def make_softlinks(self, success):
target = "success" if success else "failed"
for folder in ("latest", target):
try:
os.remove(os.path.join(self.base_folder, folder))
except Exception:
pass
if os.path.exists(os.path.join(self.base_folder, self.backup_folder)):
os.symlink(self.backup_folder, os.path.join(self.base_folder, folder))
def make_backup(self):
if self.options.no_backup:
@@ -123,13 +118,9 @@ class TB:
else []
)
with open(
os.path.join(self.base_folder, self.backup_folder, self.tar_file), "wb"
) as fp:
with open(os.path.join(self.base_folder, self.backup_folder, self.tar_file), "wb") as fp:
with open(
os.path.join(
self.base_folder, self.backup_folder, self.tar_file + ".log"
),
os.path.join(self.base_folder, self.backup_folder, self.tar_file + ".log"),
"w",
) as fp_log:
command = [
@@ -141,34 +132,43 @@ class TB:
print(command)
md5 = hashlib.md5()
p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=fp_log)
if self.do_gpg:
pwpipe_r, pwpipe_w = os.pipe()
os.write(pwpipe_w, sys.stdin.buffer.read())
os.close(pwpipe_w)
gpg_command = "gpg -o - --passphrase-fd {} --batch --yes --symmetric".format(pwpipe_r)
gnp = subprocess.Popen(
shlex.split(gpg_command), stdin=p.stdout, stdout=subprocess.PIPE, pass_fds=[pwpipe_r]
)
reader = gnp
else:
reader = p
i = 1
while True:
chunk = p.stdout.read(1048576)
chunk = reader.stdout.read(1048576)
if len(chunk) == 0:
break
print(f"{i} Mb", end="\r")
i += 1
md5.update(chunk)
fp.write(chunk)
exitcode = p.wait()
success = exitcode in (0, 2)
print(
f"\nWrote {os.path.join(self.base_folder, self.backup_folder,self.tar_file)}"
)
print(
f"Log file {os.path.join(self.base_folder, self.backup_folder,self.tar_file+'.log')}"
)
if self.do_gpg:
os.close(pwpipe_r)
exitcode = gnp.wait() + p.wait()
else:
exitcode = p.wait()
success = exitcode in (0,)
print(f"\nWrote {os.path.join(self.base_folder, self.backup_folder,self.tar_file)}")
print(f"Log file {os.path.join(self.base_folder, self.backup_folder,self.tar_file+'.log')}")
with open(
os.path.join(self.base_folder, self.backup_folder, self.tar_file + ".md5"),
"w",
) as fp_md5:
fp_md5.write(f"{md5.hexdigest()} {self.tar_file}\n")
if not success:
with open(
os.path.join(
self.base_folder, self.backup_folder, self.tar_file + ".log"
)
) as f:
with open(os.path.join(self.base_folder, self.backup_folder, self.tar_file + ".log")) as f:
print(f.read(), end="")
print(f"Exit code: {exitcode}")
return success
@@ -280,12 +280,17 @@ def get_opts():
default=False,
help="Do not actually backup, only clean up.",
)
parser.add_argument(
"--gpg",
dest="gpg",
action="store_true",
default=False,
help="Encrypt with GPG. Read passphrase from stdin. change --tar-file to match, e.g. .gpg suffix. You might want to remove -vv from tar-args.",
)
parser.add_argument("--version", action="version", version=VERSION)
parser.add_argument(
"backup_source", action="store", help="Source folder to backup with tar+ssh"
)
parser.add_argument("base_folder", action="store", help="Local backup folder")
parser.add_argument("backup_source", action="store", help="Source folder to backup with tar+ssh")
parser.add_argument("base_folder", action="store", help="Local backup folder written to")
options = parser.parse_args()
if "/" in options.tar_file:
parser.error("--tar-file must be a filename")