summaryrefslogtreecommitdiff
path: root/hosts/vidhar/borg
diff options
context:
space:
mode:
Diffstat (limited to 'hosts/vidhar/borg')
-rwxr-xr-xhosts/vidhar/borg/copy.py25
-rw-r--r--hosts/vidhar/borg/default.nix19
2 files changed, 28 insertions, 16 deletions
diff --git a/hosts/vidhar/borg/copy.py b/hosts/vidhar/borg/copy.py
index 0f39b495..1efeaef4 100755
--- a/hosts/vidhar/borg/copy.py
+++ b/hosts/vidhar/borg/copy.py
@@ -21,6 +21,8 @@ from xdg import xdg_runtime_dir
21import pathlib 21import pathlib
22 22
23import unshare 23import unshare
24from pyprctl import cap_permitted, cap_inheritable, cap_effective, cap_ambient, Cap
25from pwd import getpwnam
24 26
25import signal 27import signal
26from time import sleep 28from time import sleep
@@ -41,10 +43,20 @@ halo_args = {
41 'spinner': 'arc' 43 'spinner': 'arc'
42} 44}
43 45
46borg_pwd = getpwnam('borg')
47
48def as_borg(caps=set()):
49 if caps:
50 for capset in [cap_permitted, cap_inheritable, cap_effective, cap_ambient]:
51 capset.add(*caps)
52
53 os.setuid(borg_pwd.pw_uid)
54 os.setgid(borg_pwd.pw_gid)
55
44def read_repo(path): 56def read_repo(path):
45 with Halo(text=f'Listing {path}', **halo_args) as sp: 57 with Halo(text=f'Listing {path}', **halo_args) as sp:
46 res = None 58 res = None
47 with subprocess.Popen(['borg', 'list', '--info', '--lock-wait=600', '--json', path], stdout=subprocess.PIPE) as proc: 59 with subprocess.Popen(['borg', 'list', '--info', '--lock-wait=600', '--json', path], stdout=subprocess.PIPE, preexec_fn=lambda: as_borg()) as proc:
48 res = json.load(proc.stdout)['archives'] 60 res = json.load(proc.stdout)['archives']
49 if sp.enabled: 61 if sp.enabled:
50 sp.succeed(f'{len(res)} archives in {path}') 62 sp.succeed(f'{len(res)} archives in {path}')
@@ -83,7 +95,7 @@ def copy_archive(src_repo_path, dst_repo_path, entry):
83 match = re.compile('^(.*)-[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.(checkpoint|recreate)(\.[0-9]+)?)?').fullmatch(entry['name']) 95 match = re.compile('^(.*)-[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.(checkpoint|recreate)(\.[0-9]+)?)?').fullmatch(entry['name'])
84 if match: 96 if match:
85 repo_id = None 97 repo_id = None
86 with subprocess.Popen(['borg', 'info', '--info', '--lock-wait=600', '--json', src_repo_path], stdout=subprocess.PIPE) as proc: 98 with subprocess.Popen(['borg', 'info', '--info', '--lock-wait=600', '--json', src_repo_path], stdout=subprocess.PIPE, preexec_fn=lambda: as_borg()) as proc:
87 repo_id = json.load(proc.stdout)['repository']['id'] 99 repo_id = json.load(proc.stdout)['repository']['id']
88 if repo_id: 100 if repo_id:
89 cache_suffix = f'{repo_id}_{match.group(1)}' 101 cache_suffix = f'{repo_id}_{match.group(1)}'
@@ -119,11 +131,12 @@ def copy_archive(src_repo_path, dst_repo_path, entry):
119 os.chroot(chroot) 131 os.chroot(chroot)
120 os.chdir('/') 132 os.chdir('/')
121 dir = pathlib.Path('/borg') 133 dir = pathlib.Path('/borg')
122 dir.mkdir(parents=True,exist_ok=True) 134 dir.mkdir(parents=True,exist_ok=True,mode=750)
135 os.chown(dir, borg_pwd.pw_uid, borg_pwd.pw_gid)
123 with Halo(text=f'Determine size', **halo_args) as sp: 136 with Halo(text=f'Determine size', **halo_args) as sp:
124 total_size = None 137 total_size = None
125 total_files = None 138 total_files = None
126 with subprocess.Popen(['borg', 'info', '--info', '--json', '--lock-wait=600', f'{src_repo_path}::{entry["name"]}'], stdout=subprocess.PIPE, text=True) as proc: 139 with subprocess.Popen(['borg', 'info', '--info', '--json', '--lock-wait=600', f'{src_repo_path}::{entry["name"]}'], stdout=subprocess.PIPE, text=True, preexec_fn=lambda: as_borg()) as proc:
127 stats = json.load(proc.stdout)['archives'][0]['stats'] 140 stats = json.load(proc.stdout)['archives'][0]['stats']
128 total_size = stats['original_size'] 141 total_size = stats['original_size']
129 total_files = stats['nfiles'] 142 total_files = stats['nfiles']
@@ -132,7 +145,7 @@ def copy_archive(src_repo_path, dst_repo_path, entry):
132 else: 145 else:
133 print(f'{total_files} files, {naturalsize(total_size, binary=True)}', file=stderr) 146 print(f'{total_files} files, {naturalsize(total_size, binary=True)}', file=stderr)
134 # print(f'Mounting to {dir}', file=stderr) 147 # print(f'Mounting to {dir}', file=stderr)
135 with subprocess.Popen(['borg', 'mount', '--foreground', '--progress', '--lock-wait=600', f'{src_repo_path}::{entry["name"]}', dir]) as mount_proc: 148 with subprocess.Popen(['borg', 'mount', '--foreground', '--progress', '--lock-wait=600', f'{src_repo_path}::{entry["name"]}', dir], preexec_fn=lambda: as_borg()) as mount_proc:
136 with Halo(text='Waiting for mount', **halo_args) as sp: 149 with Halo(text='Waiting for mount', **halo_args) as sp:
137 wait_start = datetime.now() 150 wait_start = datetime.now()
138 while True: 151 while True:
@@ -173,7 +186,7 @@ def copy_archive(src_repo_path, dst_repo_path, entry):
173 else: 186 else:
174 create_args += ['--files-cache=disabled'] 187 create_args += ['--files-cache=disabled']
175 create_args += [f'{dst_repo_path}::{entry["name"]}', '.'] 188 create_args += [f'{dst_repo_path}::{entry["name"]}', '.']
176 with subprocess.Popen(create_args, cwd=dir, stdin=subprocess.DEVNULL, stderr=subprocess.PIPE, text=True, env=env) as proc: 189 with subprocess.Popen(create_args, cwd=dir, stdin=subprocess.DEVNULL, stderr=subprocess.PIPE, text=True, env=env, preexec_fn=lambda: as_borg(caps={Cap.DAC_READ_SEARCH})) as proc:
177 last_list = None 190 last_list = None
178 last_list_time = None 191 last_list_time = None
179 for line in proc.stderr: 192 for line in proc.stderr:
diff --git a/hosts/vidhar/borg/default.nix b/hosts/vidhar/borg/default.nix
index c404001b..37cdba8c 100644
--- a/hosts/vidhar/borg/default.nix
+++ b/hosts/vidhar/borg/default.nix
@@ -45,9 +45,8 @@ let
45 }; 45 };
46 46
47 copyBorg = pkgs.stdenv.mkDerivation (let 47 copyBorg = pkgs.stdenv.mkDerivation (let
48 # packageOverrides = pkgs.callPackage ./pyprctl-packages.nix {}; 48 packageOverrides = pkgs.callPackage ./pyprctl-packages.nix {};
49 # inpPython = pkgs.python39.override { inherit packageOverrides; }; 49 inpPython = pkgs.python39.override { inherit packageOverrides; };
50 inpPython = pkgs.python39;
51 in rec { 50 in rec {
52 name = "copy"; 51 name = "copy";
53 src = ./copy.py; 52 src = ./copy.py;
@@ -56,7 +55,7 @@ let
56 55
57 buildInputs = with pkgs; [makeWrapper]; 56 buildInputs = with pkgs; [makeWrapper];
58 57
59 python = inpPython.withPackages (ps: with ps; [humanize tqdm dateutil xdg python-unshare halo]); 58 python = inpPython.withPackages (ps: with ps; [humanize tqdm dateutil xdg python-unshare pyprctl halo]);
60 59
61 buildPhase = '' 60 buildPhase = ''
62 substitute $src copy \ 61 substitute $src copy \
@@ -111,12 +110,12 @@ in {
111 110
112 systemd.services = listToAttrs (map copyService [{ repo = "/srv/backup/borg/jotnar"; repoEscaped = "srv-backup-borg-jotnar"; }]); 111 systemd.services = listToAttrs (map copyService [{ repo = "/srv/backup/borg/jotnar"; repoEscaped = "srv-backup-borg-jotnar"; }]);
113 112
114 systemd.timers."copy-borg@srv-backup-borg-jotnar" = { 113 # systemd.timers."copy-borg@srv-backup-borg-jotnar" = {
115 wantedBy = ["multi-user.target"]; 114 # wantedBy = ["multi-user.target"];
116 115
117 timerConfig = { 116 # timerConfig = {
118 OnCalendar = "*-*-* 00/4:00:00 Europe/Berlin"; 117 # OnCalendar = "*-*-* 00/4:00:00 Europe/Berlin";
119 }; 118 # };
120 }; 119 # };
121 }; 120 };
122} 121}