254 lines
		
	
	
		
			8.0 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			254 lines
		
	
	
		
			8.0 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" { preferLocalBuild = true; } ''
 | |
|     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 = "getBin pkgs.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" { preferLocalBuild = true; } ''
 | |
|           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.util-linux}/bin/umount ${cfg.mountPoint}
 | |
|         ${openafsBin}/sbin/afsd -shutdown
 | |
|         ${pkgs.kmod}/sbin/rmmod libafs
 | |
|       '';
 | |
|     };
 | |
|   };
 | |
| }
 | 
