253 lines
8 KiB
Nix
253 lines
8 KiB
Nix
{ config, lib, pkgs, ... }:
|
|
|
|
# openafsMod, openafsBin, mkCellServDB
|
|
with import ./lib.nix { inherit config lib pkgs; };
|
|
|
|
let
|
|
inherit (lib) getBin mkOption mkIf optionalString singleton types;
|
|
|
|
cfg = config.services.openafsClient;
|
|
|
|
cellServDB = pkgs.fetchurl {
|
|
url = http://dl.central.org/dl/cellservdb/CellServDB.2018-05-14;
|
|
sha256 = "1wmjn6mmyy2r8p10nlbdzs4nrqxy8a9pjyrdciy5nmppg4053rk2";
|
|
};
|
|
|
|
clientServDB = pkgs.writeText "client-cellServDB-${cfg.cellName}" (mkCellServDB cfg.cellName cfg.cellServDB);
|
|
|
|
afsConfig = pkgs.runCommand "afsconfig" {} ''
|
|
mkdir -p $out
|
|
echo ${cfg.cellName} > $out/ThisCell
|
|
cat ${cellServDB} ${clientServDB} > $out/CellServDB
|
|
echo "${cfg.mountPoint}:${cfg.cache.directory}:${toString cfg.cache.blocks}" > $out/cacheinfo
|
|
'';
|
|
|
|
in
|
|
{
|
|
###### interface
|
|
|
|
options = {
|
|
|
|
services.openafsClient = {
|
|
|
|
enable = mkOption {
|
|
default = false;
|
|
type = types.bool;
|
|
description = "Whether to enable the OpenAFS client.";
|
|
};
|
|
|
|
afsdb = mkOption {
|
|
default = true;
|
|
type = types.bool;
|
|
description = "Resolve cells via AFSDB DNS records.";
|
|
};
|
|
|
|
cellName = mkOption {
|
|
default = "";
|
|
type = types.str;
|
|
description = "Cell name.";
|
|
example = "grand.central.org";
|
|
};
|
|
|
|
cellServDB = mkOption {
|
|
default = [];
|
|
type = with types; listOf (submodule { options = cellServDBConfig; });
|
|
description = ''
|
|
This cell's database server records, added to the global
|
|
CellServDB. See CellServDB(5) man page for syntax. Ignored when
|
|
<literal>afsdb</literal> is set to <literal>true</literal>.
|
|
'';
|
|
example = ''
|
|
[ { ip = "1.2.3.4"; dnsname = "first.afsdb.server.dns.fqdn.org"; }
|
|
{ ip = "2.3.4.5"; dnsname = "second.afsdb.server.dns.fqdn.org"; }
|
|
]
|
|
'';
|
|
};
|
|
|
|
cache = {
|
|
blocks = mkOption {
|
|
default = 100000;
|
|
type = types.int;
|
|
description = "Cache size in 1KB blocks.";
|
|
};
|
|
|
|
chunksize = mkOption {
|
|
default = 0;
|
|
type = types.ints.between 0 30;
|
|
description = ''
|
|
Size of each cache chunk given in powers of
|
|
2. <literal>0</literal> resets the chunk size to its default
|
|
values (13 (8 KB) for memcache, 18-20 (256 KB to 1 MB) for
|
|
diskcache). Maximum value is 30. Important performance
|
|
parameter. Set to higher values when dealing with large files.
|
|
'';
|
|
};
|
|
|
|
directory = mkOption {
|
|
default = "/var/cache/openafs";
|
|
type = types.str;
|
|
description = "Cache directory.";
|
|
};
|
|
|
|
diskless = mkOption {
|
|
default = false;
|
|
type = types.bool;
|
|
description = ''
|
|
Use in-memory cache for diskless machines. Has no real
|
|
performance benefit anymore.
|
|
'';
|
|
};
|
|
};
|
|
|
|
crypt = mkOption {
|
|
default = true;
|
|
type = types.bool;
|
|
description = "Whether to enable (weak) protocol encryption.";
|
|
};
|
|
|
|
daemons = mkOption {
|
|
default = 2;
|
|
type = types.int;
|
|
description = ''
|
|
Number of daemons to serve user requests. Numbers higher than 6
|
|
usually do no increase performance. Default is sufficient for up
|
|
to five concurrent users.
|
|
'';
|
|
};
|
|
|
|
fakestat = mkOption {
|
|
default = false;
|
|
type = types.bool;
|
|
description = ''
|
|
Return fake data on stat() calls. If <literal>true</literal>,
|
|
always do so. If <literal>false</literal>, only do so for
|
|
cross-cell mounts (as these are potentially expensive).
|
|
'';
|
|
};
|
|
|
|
inumcalc = mkOption {
|
|
default = "compat";
|
|
type = types.strMatching "compat|md5";
|
|
description = ''
|
|
Inode calculation method. <literal>compat</literal> is
|
|
computationally less expensive, but <literal>md5</literal> greatly
|
|
reduces the likelihood of inode collisions in larger scenarios
|
|
involving multiple cells mounted into one AFS space.
|
|
'';
|
|
};
|
|
|
|
mountPoint = mkOption {
|
|
default = "/afs";
|
|
type = types.str;
|
|
description = ''
|
|
Mountpoint of the AFS file tree, conventionally
|
|
<literal>/afs</literal>. When set to a different value, only
|
|
cross-cells that use the same value can be accessed.
|
|
'';
|
|
};
|
|
|
|
packages = {
|
|
module = mkOption {
|
|
default = config.boot.kernelPackages.openafs;
|
|
defaultText = "config.boot.kernelPackages.openafs";
|
|
type = types.package;
|
|
description = "OpenAFS kernel module package. MUST match the userland package!";
|
|
};
|
|
programs = mkOption {
|
|
default = getBin pkgs.openafs;
|
|
defaultText = "config.boot.kernelPackages.openafs";
|
|
type = types.package;
|
|
description = "OpenAFS programs package. MUST match the kernel module package!";
|
|
};
|
|
};
|
|
|
|
sparse = mkOption {
|
|
default = true;
|
|
type = types.bool;
|
|
description = "Minimal cell list in /afs.";
|
|
};
|
|
|
|
startDisconnected = mkOption {
|
|
default = false;
|
|
type = types.bool;
|
|
description = ''
|
|
Start up in disconnected mode. You need to execute
|
|
<literal>fs disco online</literal> (as root) to switch to
|
|
connected mode. Useful for roaming devices.
|
|
'';
|
|
};
|
|
|
|
};
|
|
};
|
|
|
|
|
|
###### implementation
|
|
|
|
config = mkIf cfg.enable {
|
|
|
|
assertions = [
|
|
{ assertion = cfg.afsdb || cfg.cellServDB != [];
|
|
message = "You should specify all cell-local database servers in config.services.openafsClient.cellServDB or set config.services.openafsClient.afsdb.";
|
|
}
|
|
{ assertion = cfg.cellName != "";
|
|
message = "You must specify the local cell name in config.services.openafsClient.cellName.";
|
|
}
|
|
];
|
|
|
|
environment.systemPackages = [ openafsBin ];
|
|
|
|
environment.etc = {
|
|
clientCellServDB = {
|
|
source = pkgs.runCommand "CellServDB" {} ''
|
|
cat ${cellServDB} ${clientServDB} > $out
|
|
'';
|
|
target = "openafs/CellServDB";
|
|
mode = "0644";
|
|
};
|
|
clientCell = {
|
|
text = ''
|
|
${cfg.cellName}
|
|
'';
|
|
target = "openafs/ThisCell";
|
|
mode = "0644";
|
|
};
|
|
};
|
|
|
|
systemd.services.afsd = {
|
|
description = "AFS client";
|
|
wantedBy = [ "multi-user.target" ];
|
|
after = singleton (if cfg.startDisconnected then "network.target" else "network-online.target");
|
|
serviceConfig = { RemainAfterExit = true; };
|
|
restartIfChanged = false;
|
|
|
|
preStart = ''
|
|
mkdir -p -m 0755 ${cfg.mountPoint}
|
|
mkdir -m 0700 -p ${cfg.cache.directory}
|
|
${pkgs.kmod}/bin/insmod ${openafsMod}/lib/modules/*/extra/openafs/libafs.ko.xz
|
|
${openafsBin}/sbin/afsd \
|
|
-mountdir ${cfg.mountPoint} \
|
|
-confdir ${afsConfig} \
|
|
${optionalString (!cfg.cache.diskless) "-cachedir ${cfg.cache.directory}"} \
|
|
-blocks ${toString cfg.cache.blocks} \
|
|
-chunksize ${toString cfg.cache.chunksize} \
|
|
${optionalString cfg.cache.diskless "-memcache"} \
|
|
-inumcalc ${cfg.inumcalc} \
|
|
${if cfg.fakestat then "-fakestat-all" else "-fakestat"} \
|
|
${if cfg.sparse then "-dynroot-sparse" else "-dynroot"} \
|
|
${optionalString cfg.afsdb "-afsdb"}
|
|
${openafsBin}/bin/fs setcrypt ${if cfg.crypt then "on" else "off"}
|
|
${optionalString cfg.startDisconnected "${openafsBin}/bin/fs discon offline"}
|
|
'';
|
|
|
|
# Doing this in preStop, because after these commands AFS is basically
|
|
# stopped, so systemd has nothing to do, just noticing it. If done in
|
|
# postStop, then we get a hang + kernel oops, because AFS can't be
|
|
# stopped simply by sending signals to processes.
|
|
preStop = ''
|
|
${pkgs.utillinux}/bin/umount ${cfg.mountPoint}
|
|
${openafsBin}/sbin/afsd -shutdown
|
|
${pkgs.kmod}/sbin/rmmod libafs
|
|
'';
|
|
};
|
|
};
|
|
}
|