btw i use nix
1{
2 pkgs,
3 config,
4 lib,
5 ...
6}:
7
8let
9 cfg = config.custom;
10in
11{
12 options.custom.external-hdd-backup = {
13 enable = lib.mkEnableOption "laptop";
14 disk = lib.mkOption {
15 type = lib.types.str;
16 default = "/dev/disk/by-label/external-hdd";
17 };
18 mountdir = lib.mkOption {
19 type = lib.types.str;
20 default = "/media";
21 };
22 mountname = lib.mkOption {
23 type = lib.types.str;
24 default = "external-hdd";
25 };
26 };
27
28 config = lib.mkIf cfg.external-hdd-backup.enable {
29 systemd.services.backup = {
30 description = "Backup service";
31 # NB udisks isn't viable as non-root due to:
32 # Error creating textual authentication agent: Error opening current controlling terminal for the process (`/dev/tty'): No such device or address (polkit-error-quark, 0)
33 # Error mounting /dev/sda1: GDBus.Error:org.freedesktop.UDisks2.Error.NotAuthorizedCanObtain: Not authorized to perform operation
34 # And in order to communicate with GUI prompts, e.g. yad, we need to run as user
35 # udisks is still use for on-demand mountin, but we'll use the autofs for mounting the backup disk
36 script =
37 let
38 backup = pkgs.writeShellScript "backup.sh" ''
39 # TODO make nixos module with options
40 DISK="${cfg.backup.disk}"
41 LAST_RUN_FILE="$HOME/.cache/last_backup"
42
43 if [ -f "$LAST_RUN_FILE" ] && [ "$(( $(date +%s) - $(date +%s -r "$LAST_RUN_FILE") ))" -lt 86400 ]; then
44 echo "<24hrs"
45 exit 0
46 fi
47
48 # if no external-hdd
49 if [ ! -e $DISK ]; then
50 echo "no $DISK"
51 exit 0
52 fi
53
54 export DISPLAY=:0
55 ${pkgs.xorg.xhost}/bin/xhost +local:${config.custom.username}
56 export GTK_R2_FILES=$HOME/.gtkrc-2.0
57 timeout 60 ${pkgs.yad}/bin/yad --question --title "backup" --text "Backup now? Will autostart in 60s."
58 prompt_status=$?
59 ${pkgs.xorg.xhost}/bin/xhost -local:${config.custom.username}
60 # if not success or timeout
61 if [ ! $prompt_status -eq 0 -a ! $prompt_status -eq 124 ]; then
62 echo "backup cancelled"
63 ${pkgs.libnotify}/bin/notify-send "backup cancelled"
64 exit 0
65 fi
66
67 DIR="${cfg.backup.mountdir}/${cfg.backup.mountname}"
68 cd "$DIR"
69 TEST_DIR=`${pkgs.util-linux}/bin/findmnt -nr -o target -S $DISK`
70 status=$?
71 if [ ! $status -eq 0 ]; then
72 echo "backup failed to find mount"
73 ${pkgs.libnotify}/bin/notify-send "backup failed to find mount"
74 exit $status
75 fi
76 if [ "$DIR" != "$TEST_DIR" ]; then
77 echo "backup disk mounted at unexpected path: $TEST_DIR"
78 ${pkgs.libnotify}/bin/notify-send "backup disk mounted at unexpected path: $TEST_DIR"
79 exit 1
80 fi
81 ${pkgs.libnotify}/bin/notify-send "backup starting"
82 ${pkgs.rsync}/bin/rsync -va --exclude={".cache",".local/share/Steam/"} ~/ $DIR/home/ −−delete−after
83 status=$?
84 if [ $status -eq 0 ]; then
85 touch "$LAST_RUN_FILE"
86 echo "backup finished"
87 ${pkgs.libnotify}/bin/notify-send "backup finished"
88 else
89 echo "backup failed"
90 ${pkgs.libnotify}/bin/notify-send "backup failed"
91 fi
92 exit $status
93 '';
94 in
95 "${backup}";
96 serviceConfig = {
97 Type = "oneshot";
98 User = config.custom.username;
99 };
100 # trigger on wake
101 wantedBy = [
102 "suspend.target"
103 "hibernate.target"
104 "hybrid-sleep.target"
105 "suspend-then-hibernate.target"
106 ];
107 environment.DBUS_SESSION_BUS_ADDRESS = "unix:path=/run/user/1000/bus";
108 };
109 # trigger backup on hard drive connection
110 services.udev.extraRules =
111 # NB could check device label with a trigger script that checks $0 and `RUN+="${trigger} /media/$env{ID_FS_LABEL}"`
112 # but we just assume the Seagate Expansion Desk is the same as /dev/disk/by-label/external-hdd
113 # UDEV has crap support for detecting devices
114 ''
115 ACTION=="add", SUBSYSTEM=="block", KERNEL=="sd[a-z]*[0-9]*", ATTRS{model}=="Expansion Desk ", ATTRS{vendor}=="Seagate ", TAG+="systemd", ENV{SYSTEMD_WANTS}+="backup"
116 '';
117 services.autofs = {
118 enable = true;
119 autoMaster =
120 let
121 map = pkgs.writeText "auto.media" ''
122 ${cfg.backup.mountname} -fstype=auto :${cfg.backup.disk}
123 '';
124 in
125 ''
126 ${cfg.backup.mountdir} file,sun:${map} -t 60
127 '';
128 };
129 };
130}