1{ lib
2, callPackage
3, nixosTests
4, stdenv
5, fetchurl
6, autoPatchelfHook
7, rpmextract
8, libxcrypt
9, openssl
10, zlib
11, lvm2 # LVM image backup and restore functions (optional)
12, acl # EXT2/EXT3/XFS ACL support (optional)
13, gnugrep
14, procps
15, jdk8 # Java GUI (needed for `enableGui`)
16, buildEnv
17, makeWrapper
18, enableGui ? false # enables Java GUI `dsmj`
19# path to `dsm.sys` configuration files
20, dsmSysCli ? "/etc/tsm-client/cli.dsm.sys"
21, dsmSysApi ? "/etc/tsm-client/api.dsm.sys"
22}:
23
24
25# For an explanation of optional packages
26# (features provided by them, version limits), see
27# https://www.ibm.com/support/pages/node/660813#Version%208.1
28
29
30# IBM Tivoli Storage Manager Client uses a system-wide
31# client system-options file `dsm.sys` and expects it
32# to be located in a directory within the package.
33# Note that the command line client and the API use
34# different "dms.sys" files (located in different directories).
35# Since these files contain settings to be altered by the
36# admin user (e.g. TSM server name), we create symlinks
37# in place of the files that the client attempts to open.
38# Use the arguments `dsmSysCli` and `dsmSysApi` to
39# provide the location of the configuration files for
40# the command-line interface and the API, respectively.
41#
42# While the command-line interface contains wrappers
43# that help the executables find the configuration file,
44# packages that link against the API have to
45# set the environment variable `DSMI_DIR` to
46# point to this derivations `/dsmi_dir` directory symlink.
47# Other environment variables might be necessary,
48# depending on local configuration or usage; see:
49# https://www.ibm.com/docs/en/spectrum-protect/8.1.15?topic=solaris-set-api-environment-variables
50
51
52# The newest version of TSM client should be discoverable by
53# going to the `downloadPage` (see `meta` below).
54# Find the "Backup-archive client" table on that page.
55# Look for "Download Documents" of the latest release.
56# Here, two links must be checked if existing:
57# * "IBM Spectrum Protect Client ... Downloads and READMEs":
58# In the table at the page's bottom,
59# check the date of the "Linux x86_64 client"
60# * "IBM Spectrum Protect BA client ... interim fix downloads"
61# Look for the "Linux x86_64 client ..." rows
62# in the table at the bottom of each page.
63# Follow the "HTTPS" link of the row with the latest date stamp.
64# In the directory listing to show up, pick the big `.tar` file.
65#
66# (as of 2022-09-29)
67
68
69let
70
71 meta = {
72 homepage = "https://www.ibm.com/products/data-protection-and-recovery";
73 downloadPage = "https://www.ibm.com/support/pages/ibm-spectrum-protect-downloads-latest-fix-packs-and-interim-fixes";
74 platforms = [ "x86_64-linux" ];
75 mainProgram = "dsmc";
76 sourceProvenance = with lib.sourceTypes; [ binaryNativeCode ];
77 license = lib.licenses.unfree;
78 maintainers = [ lib.maintainers.yarny ];
79 description = "IBM Spectrum Protect (Tivoli Storage Manager) CLI and API";
80 longDescription = ''
81 IBM Spectrum Protect (Tivoli Storage Manager) provides
82 a single point of control for backup and recovery.
83 This package contains the client software, that is,
84 a command line client and linkable libraries.
85
86 Note that the software requires a system-wide
87 client system-options file (commonly named "dsm.sys").
88 This package allows to use separate files for
89 the command-line interface and for the linkable API.
90 The location of those files can
91 be provided as build parameters.
92 '';
93 };
94
95 passthru.tests = {
96 test-cli = callPackage ./test-cli.nix {};
97 test-gui = nixosTests.tsm-client-gui;
98 };
99
100 mkSrcUrl = version:
101 let
102 major = lib.versions.major version;
103 minor = lib.versions.minor version;
104 patch = lib.versions.patch version;
105 fixup = lib.lists.elemAt (lib.versions.splitVersion version) 3;
106 in
107 "https://public.dhe.ibm.com/storage/tivoli-storage-management/${if fixup=="0" then "maintenance" else "patches"}/client/v${major}r${minor}/Linux/LinuxX86/BA/v${major}${minor}${patch}/${version}-TIV-TSMBAC-LinuxX86.tar";
108
109 unwrapped = stdenv.mkDerivation rec {
110 name = "tsm-client-${version}-unwrapped";
111 version = "8.1.15.2";
112 src = fetchurl {
113 url = mkSrcUrl version;
114 hash = "sha512-ljygVoW7zR+LVHf4LSoBn3qEHISobsxheLxs9NyKWQiwPWpfhSgJO+bX4QRzAmrpSTNrETxHkuXqzGSHaaBlzg==";
115 };
116 inherit meta passthru;
117
118 nativeBuildInputs = [
119 autoPatchelfHook
120 rpmextract
121 ];
122 buildInputs = [
123 libxcrypt
124 openssl
125 stdenv.cc.cc
126 zlib
127 ];
128 runtimeDependencies = [
129 (lib.attrsets.getLib lvm2)
130 ];
131 sourceRoot = ".";
132
133 postUnpack = ''
134 rpmextract TIVsm-API64.x86_64.rpm
135 rpmextract TIVsm-APIcit.x86_64.rpm
136 rpmextract TIVsm-BA.x86_64.rpm
137 rpmextract TIVsm-BAcit.x86_64.rpm
138 rpmextract TIVsm-BAhdw.x86_64.rpm
139 rpmextract TIVsm-JBB.x86_64.rpm
140 # use globbing so that version updates don't break the build:
141 rpmextract gskcrypt64-*.linux.x86_64.rpm
142 rpmextract gskssl64-*.linux.x86_64.rpm
143 '';
144
145 installPhase = ''
146 runHook preInstall
147 mkdir --parents $out
148 mv --target-directory=$out usr/* opt
149 runHook postInstall
150 '';
151
152 # Fix relative symlinks after `/usr` was moved up one level
153 preFixup = ''
154 for link in $out/lib{,64}/* $out/bin/*
155 do
156 target=$(readlink "$link")
157 if [ "$(cut -b -6 <<< "$target")" != "../../" ]
158 then
159 echo "cannot fix this symlink: $link -> $target"
160 exit 1
161 fi
162 ln --symbolic --force --no-target-directory "$out/$(cut -b 7- <<< "$target")" "$link"
163 done
164 '';
165
166 # since 7b9fd5d1c9802131ca0a01ff08a3ff64379d2df4
167 # autopatchelf misses to add $out/lib to rpath;
168 # we have to call autopatchelf manually as it would
169 # run too late and overwrite our rpath otherwise
170 dontAutoPatchelf = true;
171 postFixup = ''
172 autoPatchelf $out
173 patchelf --add-rpath $out/lib $out/lib/*
174 '';
175 };
176
177 binPath = lib.makeBinPath ([ acl gnugrep procps ]
178 ++ lib.optional enableGui jdk8);
179
180in
181
182buildEnv {
183 name = "tsm-client-${unwrapped.version}";
184 meta = meta // lib.attrsets.optionalAttrs enableGui {
185 mainProgram = "dsmj";
186 };
187 passthru = passthru // { inherit unwrapped; };
188 paths = [ unwrapped ];
189 nativeBuildInputs = [ makeWrapper ];
190 pathsToLink = [
191 "/"
192 "/bin"
193 "/opt/tivoli/tsm/client/ba/bin"
194 "/opt/tivoli/tsm/client/api/bin64"
195 ];
196 # * Provide top-level symlinks `dsm_dir` and `dsmi_dir`
197 # to the so-called "installation directories"
198 # * Add symlinks to the "installation directories"
199 # that point to the `dsm.sys` configuration files
200 # * Drop the Java GUI executable unless `enableGui` is set
201 # * Create wrappers for the command-line interface to
202 # prepare `PATH` and `DSM_DIR` environment variables
203 postBuild = ''
204 ln --symbolic --no-target-directory opt/tivoli/tsm/client/ba/bin $out/dsm_dir
205 ln --symbolic --no-target-directory opt/tivoli/tsm/client/api/bin64 $out/dsmi_dir
206 ln --symbolic --no-target-directory "${dsmSysCli}" $out/dsm_dir/dsm.sys
207 ln --symbolic --no-target-directory "${dsmSysApi}" $out/dsmi_dir/dsm.sys
208 ${lib.optionalString (!enableGui) "rm $out/bin/dsmj"}
209 for bin in $out/bin/*
210 do
211 target=$(readlink "$bin")
212 rm "$bin"
213 makeWrapper "$target" "$bin" \
214 --prefix PATH : "$out/dsm_dir:${binPath}" \
215 --set DSM_DIR $out/dsm_dir
216 done
217 '';
218}