From d46259560030851e9a0ea73375779b9bf56e3b92 Mon Sep 17 00:00:00 2001 From: Michishige Kaito Date: Mon, 26 Mar 2018 12:39:00 +0100 Subject: [PATCH 1/3] Add support for tarsnap options -H and -L A new option `explicitSymlinks` will set `-H` when creating an archive. This option makes tarsnap follow any symlinks specified explicitly on the commandline, but not any found inside the file tree. A new option `followSymlinks` will set `-L` when creating an archive. This option makes tarsnap follow any symlinks found anywhere in the file tree instead of storing them as-is. --- nixos/modules/services/backup/tarsnap.nix | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/nixos/modules/services/backup/tarsnap.nix b/nixos/modules/services/backup/tarsnap.nix index 59e9d122fb5..c33aeb76cb0 100644 --- a/nixos/modules/services/backup/tarsnap.nix +++ b/nixos/modules/services/backup/tarsnap.nix @@ -238,6 +238,20 @@ in Whether to produce verbose logging output. ''; }; + explicitSymlinks = mkOption { + type = types.bool; + default = false; + description = '' + Whether to follow symlinks specified as archives. + ''; + }; + followSymlinks = mkOption { + type = types.bool; + default = false; + description = '' + Whether to follow all symlinks in archive trees. + ''; + }; }; } )); @@ -304,6 +318,8 @@ in let run = ''tarsnap --configfile "/etc/tarsnap/${name}.conf" \ -c -f "${name}-$(date +"%Y%m%d%H%M%S")" \ ${optionalString cfg.verbose "-v"} \ + ${optionalString cfg.explicitSymlinks "-H"} \ + ${optionalString cfg.followSymlinks "-L"} \ ${concatStringsSep " " cfg.directories}''; in if (cfg.cachedir != null) then '' mkdir -p ${cfg.cachedir} From bde525aaaff5cd99e79a21e30b9fdfdf2cd616d2 Mon Sep 17 00:00:00 2001 From: Michishige Kaito Date: Tue, 27 Mar 2018 01:12:26 +0100 Subject: [PATCH 2/3] Add restore service for tarsnap archives This service will never run automatically, but it encapsulates the necessary logic and configuration to run a restore of the latest archive, and allows to hook more specific logic, such as loading a database dump, via `postStart`. --- nixos/modules/services/backup/tarsnap.nix | 47 ++++++++++++++++++++++- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/nixos/modules/services/backup/tarsnap.nix b/nixos/modules/services/backup/tarsnap.nix index c33aeb76cb0..88c847ed92c 100644 --- a/nixos/modules/services/backup/tarsnap.nix +++ b/nixos/modules/services/backup/tarsnap.nix @@ -299,7 +299,7 @@ in }) gcfg.archives); systemd.services = - mapAttrs' (name: cfg: nameValuePair "tarsnap-${name}" { + (mapAttrs' (name: cfg: nameValuePair "tarsnap-${name}" { description = "Tarsnap archive '${name}'"; requires = [ "network-online.target" ]; after = [ "network-online.target" ]; @@ -345,7 +345,50 @@ in CapabilityBoundingSet = [ "CAP_DAC_READ_SEARCH" ]; PermissionsStartOnly = "true"; }; - }) gcfg.archives; + }) gcfg.archives) // + + (mapAttrs' (name: cfg: nameValuePair "tarsnap-restore-${name}"{ + description = "Tarsnap restore '${name}'"; + requires = [ "network-online.target" ]; + + path = [ pkgs.iputils pkgs.tarsnap pkgs.utillinux ]; + + ## + preStart = '' + while ! ping -q -c 1 v1-0-0-server.tarsnap.com &> /dev/null; do sleep 3; done + ''; + + script = + let + tarsnap = ''tarsnap --configfile "/etc/tarsnap/${name}.conf"''; + lastArchive = ''$(${tarsnap} --list-archives | sort | tail -1)''; + run = ''${tarsnap} -x -f "${lastArchive}" ${optionalString cfg.verbose "-v"}''; + + in if (cfg.cachedir != null) then '' + mkdir -p ${cfg.cachedir} + chmod 0700 ${cfg.cachedir} + + ( flock 9 + if [ ! -e ${cfg.cachedir}/firstrun ]; then + ( flock 10 + flock -u 9 + ${tarsnap} --fsck + flock 9 + ) 10>${cfg.cachedir}/firstrun + fi + ) 9>${cfg.cachedir}/lockf + + exec flock ${cfg.cachedir}/firstrun ${run} + '' else "exec ${run}"; + + serviceConfig = { + Type = "oneshot"; + IOSchedulingClass = "idle"; + NoNewPrivileges = "true"; + CapabilityBoundingSet = [ "CAP_DAC_READ_SEARCH" ]; + PermissionsStartOnly = "true"; + }; + }) gcfg.archives); # Note: the timer must be Persistent=true, so that systemd will start it even # if e.g. your laptop was asleep while the latest interval occurred. From c515f7036ecd42456c01411bfd79bfc99c6cfbc4 Mon Sep 17 00:00:00 2001 From: Michishige Kaito Date: Tue, 27 Mar 2018 16:35:54 +0100 Subject: [PATCH 3/3] Address @yegortimoshenko review --- nixos/modules/services/backup/tarsnap.nix | 48 ++++++++++------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/nixos/modules/services/backup/tarsnap.nix b/nixos/modules/services/backup/tarsnap.nix index 88c847ed92c..4fc7c24813a 100644 --- a/nixos/modules/services/backup/tarsnap.nix +++ b/nixos/modules/services/backup/tarsnap.nix @@ -304,7 +304,7 @@ in requires = [ "network-online.target" ]; after = [ "network-online.target" ]; - path = [ pkgs.iputils pkgs.tarsnap pkgs.utillinux ]; + path = with pkgs; [ iputils tarsnap utillinux ]; # In order for the persistent tarsnap timer to work reliably, we have to # make sure that the tarsnap server is reachable after systemd starts up @@ -314,9 +314,9 @@ in while ! ping -q -c 1 v1-0-0-server.tarsnap.com &> /dev/null; do sleep 3; done ''; - script = - let run = ''tarsnap --configfile "/etc/tarsnap/${name}.conf" \ - -c -f "${name}-$(date +"%Y%m%d%H%M%S")" \ + script = let + tarsnap = ''tarsnap --configfile "/etc/tarsnap/${name}.conf"''; + run = ''${tarsnap} -c -f "${name}-$(date +"%Y%m%d%H%M%S")" \ ${optionalString cfg.verbose "-v"} \ ${optionalString cfg.explicitSymlinks "-H"} \ ${optionalString cfg.followSymlinks "-L"} \ @@ -329,7 +329,7 @@ in if [ ! -e ${cfg.cachedir}/firstrun ]; then ( flock 10 flock -u 9 - tarsnap --configfile "/etc/tarsnap/${name}.conf" --fsck + ${tarsnap} --fsck flock 9 ) 10>${cfg.cachedir}/firstrun fi @@ -351,35 +351,29 @@ in description = "Tarsnap restore '${name}'"; requires = [ "network-online.target" ]; - path = [ pkgs.iputils pkgs.tarsnap pkgs.utillinux ]; + path = with pkgs; [ iputils tarsnap utillinux ]; - ## - preStart = '' - while ! ping -q -c 1 v1-0-0-server.tarsnap.com &> /dev/null; do sleep 3; done - ''; - - script = - let + script = let tarsnap = ''tarsnap --configfile "/etc/tarsnap/${name}.conf"''; lastArchive = ''$(${tarsnap} --list-archives | sort | tail -1)''; run = ''${tarsnap} -x -f "${lastArchive}" ${optionalString cfg.verbose "-v"}''; - in if (cfg.cachedir != null) then '' - mkdir -p ${cfg.cachedir} - chmod 0700 ${cfg.cachedir} + in if (cfg.cachedir != null) then '' + mkdir -p ${cfg.cachedir} + chmod 0700 ${cfg.cachedir} - ( flock 9 - if [ ! -e ${cfg.cachedir}/firstrun ]; then - ( flock 10 - flock -u 9 - ${tarsnap} --fsck - flock 9 - ) 10>${cfg.cachedir}/firstrun - fi - ) 9>${cfg.cachedir}/lockf + ( flock 9 + if [ ! -e ${cfg.cachedir}/firstrun ]; then + ( flock 10 + flock -u 9 + ${tarsnap} --fsck + flock 9 + ) 10>${cfg.cachedir}/firstrun + fi + ) 9>${cfg.cachedir}/lockf - exec flock ${cfg.cachedir}/firstrun ${run} - '' else "exec ${run}"; + exec flock ${cfg.cachedir}/firstrun ${run} + '' else "exec ${run}"; serviceConfig = { Type = "oneshot";