1 { config, lib, pkgs, ... }:
3 # openafsMod, openafsBin, mkCellServDB
4 with import ./lib.nix { inherit config lib pkgs; };
7 inherit (lib) getBin literalExpression mkOption mkIf optionalString singleton types;
9 cfg = config.services.openafsClient;
11 cellServDB = pkgs.fetchurl {
12 url = "http://dl.central.org/dl/cellservdb/CellServDB.2018-05-14";
13 sha256 = "1wmjn6mmyy2r8p10nlbdzs4nrqxy8a9pjyrdciy5nmppg4053rk2";
16 clientServDB = pkgs.writeText "client-cellServDB-${cfg.cellName}" (mkCellServDB cfg.cellName cfg.cellServDB);
18 afsConfig = pkgs.runCommand "afsconfig" { preferLocalBuild = true; } ''
20 echo ${cfg.cellName} > $out/ThisCell
21 cat ${cellServDB} ${clientServDB} > $out/CellServDB
22 echo "${cfg.mountPoint}:${cfg.cache.directory}:${toString cfg.cache.blocks}" > $out/cacheinfo
31 services.openafsClient = {
36 description = "Whether to enable the OpenAFS client.";
42 description = "Resolve cells via AFSDB DNS records.";
48 description = "Cell name.";
49 example = "grand.central.org";
52 cellServDB = mkOption {
54 type = with types; listOf (submodule { options = cellServDBConfig; });
56 This cell's database server records, added to the global
57 CellServDB. See CellServDB(5) man page for syntax. Ignored when
58 `afsdb` is set to `true`.
61 { ip = "1.2.3.4"; dnsname = "first.afsdb.server.dns.fqdn.org"; }
62 { ip = "2.3.4.5"; dnsname = "second.afsdb.server.dns.fqdn.org"; }
70 description = "Cache size in 1KB blocks.";
73 chunksize = mkOption {
75 type = types.ints.between 0 30;
77 Size of each cache chunk given in powers of
78 2. `0` resets the chunk size to its default
79 values (13 (8 KB) for memcache, 18-20 (256 KB to 1 MB) for
80 diskcache). Maximum value is 30. Important performance
81 parameter. Set to higher values when dealing with large files.
85 directory = mkOption {
86 default = "/var/cache/openafs";
88 description = "Cache directory.";
95 Use in-memory cache for diskless machines. Has no real
96 performance benefit anymore.
104 description = "Whether to enable (weak) protocol encryption.";
111 Number of daemons to serve user requests. Numbers higher than 6
112 usually do no increase performance. Default is sufficient for up
113 to five concurrent users.
117 fakestat = mkOption {
121 Return fake data on stat() calls. If `true`,
122 always do so. If `false`, only do so for
123 cross-cell mounts (as these are potentially expensive).
127 inumcalc = mkOption {
129 type = types.strMatching "compat|md5";
131 Inode calculation method. `compat` is
132 computationally less expensive, but `md5` greatly
133 reduces the likelihood of inode collisions in larger scenarios
134 involving multiple cells mounted into one AFS space.
138 mountPoint = mkOption {
142 Mountpoint of the AFS file tree, conventionally
143 `/afs`. When set to a different value, only
144 cross-cells that use the same value can be accessed.
150 default = config.boot.kernelPackages.openafs;
151 defaultText = literalExpression "config.boot.kernelPackages.openafs";
152 type = types.package;
153 description = "OpenAFS kernel module package. MUST match the userland package!";
155 programs = mkOption {
156 default = getBin pkgs.openafs;
157 defaultText = literalExpression "getBin pkgs.openafs";
158 type = types.package;
159 description = "OpenAFS programs package. MUST match the kernel module package!";
166 description = "Minimal cell list in /afs.";
169 startDisconnected = mkOption {
173 Start up in disconnected mode. You need to execute
174 `fs disco online` (as root) to switch to
175 connected mode. Useful for roaming devices.
183 ###### implementation
185 config = mkIf cfg.enable {
188 { assertion = cfg.afsdb || cfg.cellServDB != [];
189 message = "You should specify all cell-local database servers in config.services.openafsClient.cellServDB or set config.services.openafsClient.afsdb.";
191 { assertion = cfg.cellName != "";
192 message = "You must specify the local cell name in config.services.openafsClient.cellName.";
196 environment.systemPackages = [ openafsBin ];
200 source = pkgs.runCommand "CellServDB" { preferLocalBuild = true; } ''
201 cat ${cellServDB} ${clientServDB} > $out
203 target = "openafs/CellServDB";
210 target = "openafs/ThisCell";
215 systemd.services.afsd = {
216 description = "AFS client";
217 wantedBy = [ "multi-user.target" ];
218 wants = lib.optional (!cfg.startDisconnected) "network-online.target";
219 after = singleton (if cfg.startDisconnected then "network.target" else "network-online.target");
220 serviceConfig = { RemainAfterExit = true; };
221 restartIfChanged = false;
224 mkdir -p -m 0755 ${cfg.mountPoint}
225 mkdir -m 0700 -p ${cfg.cache.directory}
226 ${pkgs.kmod}/bin/insmod ${openafsMod}/lib/modules/*/extra/openafs/libafs.ko.xz
227 ${openafsBin}/sbin/afsd \
228 -mountdir ${cfg.mountPoint} \
229 -confdir ${afsConfig} \
230 ${optionalString (!cfg.cache.diskless) "-cachedir ${cfg.cache.directory}"} \
231 -blocks ${toString cfg.cache.blocks} \
232 -chunksize ${toString cfg.cache.chunksize} \
233 ${optionalString cfg.cache.diskless "-memcache"} \
234 -inumcalc ${cfg.inumcalc} \
235 ${if cfg.fakestat then "-fakestat-all" else "-fakestat"} \
236 ${if cfg.sparse then "-dynroot-sparse" else "-dynroot"} \
237 ${optionalString cfg.afsdb "-afsdb"}
238 ${openafsBin}/bin/fs setcrypt ${if cfg.crypt then "on" else "off"}
239 ${optionalString cfg.startDisconnected "${openafsBin}/bin/fs discon offline"}
242 # Doing this in preStop, because after these commands AFS is basically
243 # stopped, so systemd has nothing to do, just noticing it. If done in
244 # postStop, then we get a hang + kernel oops, because AFS can't be
245 # stopped simply by sending signals to processes.
247 ${pkgs.util-linux}/bin/umount ${cfg.mountPoint}
248 ${openafsBin}/sbin/afsd -shutdown
249 ${pkgs.kmod}/sbin/rmmod libafs