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