diff --git a/backup.py b/backup.py index d20013a..954b923 100755 --- a/backup.py +++ b/backup.py @@ -10,7 +10,9 @@ import re import sys import stat import time +import select import pathlib +import threading import subprocess import typing @@ -245,13 +247,17 @@ def main(argv: list[str]): args = parser.parse_args() config = Config(args.config) + # Run backup backup = Backup(config, args.dry_run) + captured_output: list[bytes] = [] + if args.dry_run: if args.debug: backup.run(sys.stdout.buffer) else: with open(os.devnull, "wb") as out: backup.run(out) + sys.stdout.flush() else: borg = subprocess.Popen([args.borg, "create", @@ -264,12 +270,33 @@ def main(argv: list[str]): "--paths-from-stdin", "--paths-delimiter", "\\0", "::'{hostname}-{now:%Y%m%d-%H%M%S}'"], - stdin=subprocess.PIPE) + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) if borg.stdin is None: raise Exception("no pipe") + + # Use a thread to capture output + def reader_thread(fh): + os.set_blocking(fh.fileno(), False) + while True: + ready = select.select([fh.fileno()], [], []) + if not len(ready[0]): + break + data = fh.read(8192) + if not len(data): + break + sys.stdout.buffer.write(data) + sys.stdout.flush() + captured_output.append(data) + fh.close() + reader = threading.Thread(target=reader_thread, args=(borg.stdout,)) + reader.daemon = True + reader.start() + try: # Give borg some time to start, just to clean up stdout - time.sleep(2) + time.sleep(1) backup.run(borg.stdin) except BrokenPipeError: sys.stderr.write(f"broken pipe\n") @@ -279,13 +306,12 @@ def main(argv: list[str]): except BrokenPipeError: pass borg.wait() + reader.join() ret = borg.returncode if ret < 0: - sys.stderr.write(f"error: process exited with signal {-ret}\n") - return 1 + backup.log('E', f"borg exited with signal {-ret}") elif ret != 0: - sys.stderr.write(f"error: process exited with return code {ret}\n") - return ret + backup.log('E', f"borg exited with return code {ret}") return 0