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