1{ config, lib, ... }:
2
3let
4
5 inherit (lib.attrsets) hasAttr;
6 inherit (lib.meta) getExe';
7 inherit (lib.modules) mkDefault mkIf;
8 inherit (lib.options) mkEnableOption mkOption;
9 inherit (lib.types) nonEmptyStr nullOr;
10
11 options.services.tsmBackup = {
12 enable = mkEnableOption ''
13 automatic backups with the
14 IBM Storage Protect (Tivoli Storage Manager, TSM) client.
15 This also enables
16 {option}`programs.tsmClient.enable`
17 '';
18 command = mkOption {
19 type = nonEmptyStr;
20 default = "backup";
21 example = "incr";
22 description = ''
23 The actual command passed to the
24 `dsmc` executable to start the backup.
25 '';
26 };
27 servername = mkOption {
28 type = nonEmptyStr;
29 example = "mainTsmServer";
30 description = ''
31 Create a systemd system service
32 `tsm-backup.service` that starts
33 a backup based on the given servername's stanza.
34 Note that this server's
35 {option}`passwdDir` will default to
36 {file}`/var/lib/tsm-backup/password`
37 (but may be overridden);
38 also, the service will use
39 {file}`/var/lib/tsm-backup` as
40 `HOME` when calling
41 `dsmc`.
42 '';
43 };
44 autoTime = mkOption {
45 type = nullOr nonEmptyStr;
46 default = null;
47 example = "12:00";
48 description = ''
49 The backup service will be invoked
50 automatically at the given date/time,
51 which must be in the format described in
52 {manpage}`systemd.time(5)`.
53 The default `null`
54 disables automatic backups.
55 '';
56 };
57 };
58
59 cfg = config.services.tsmBackup;
60 cfgPrg = config.programs.tsmClient;
61
62 assertions = [
63 {
64 assertion = hasAttr cfg.servername cfgPrg.servers;
65 message = "TSM service servername not found in list of servers";
66 }
67 {
68 assertion = cfgPrg.servers.${cfg.servername}.genPasswd;
69 message = "TSM service requires automatic password generation";
70 }
71 ];
72
73in
74
75{
76
77 inherit options;
78
79 config = mkIf cfg.enable {
80 inherit assertions;
81 programs.tsmClient.enable = true;
82 programs.tsmClient.servers.${cfg.servername}.passworddir =
83 mkDefault "/var/lib/tsm-backup/password";
84 systemd.services.tsm-backup = {
85 description = "IBM Storage Protect (Tivoli Storage Manager) Backup";
86 # DSM_LOG needs a trailing slash to have it treated as a directory.
87 # `/var/log` would be littered with TSM log files otherwise.
88 environment.DSM_LOG = "/var/log/tsm-backup/";
89 # TSM needs a HOME dir to store certificates.
90 environment.HOME = "/var/lib/tsm-backup";
91 serviceConfig = {
92 # for exit status description see
93 # https://www.ibm.com/docs/en/storage-protect/8.1.22?topic=clients-client-return-codes
94 SuccessExitStatus = "4 8";
95 # The `-se` option must come after the command.
96 # The `-optfile` option suppresses a `dsm.opt`-not-found warning.
97 ExecStart =
98 "${getExe' cfgPrg.wrappedPackage "dsmc"} ${cfg.command} -se='${cfg.servername}' -optfile=/dev/null";
99 LogsDirectory = "tsm-backup";
100 StateDirectory = "tsm-backup";
101 StateDirectoryMode = "0750";
102 # systemd sandboxing
103 LockPersonality = true;
104 NoNewPrivileges = true;
105 PrivateDevices = true;
106 #PrivateTmp = true; # would break backup of {/var,}/tmp
107 #PrivateUsers = true; # would block backup of /home/*
108 ProtectClock = true;
109 ProtectControlGroups = true;
110 ProtectHome = "read-only";
111 ProtectHostname = true;
112 ProtectKernelLogs = true;
113 ProtectKernelModules = true;
114 ProtectKernelTunables = true;
115 ProtectProc = "noaccess";
116 ProtectSystem = "strict";
117 RestrictNamespaces = true;
118 RestrictSUIDSGID = true;
119 };
120 startAt = mkIf (cfg.autoTime!=null) cfg.autoTime;
121 };
122 };
123
124 meta.maintainers = [ lib.maintainers.yarny ];
125
126}