Skip to content

Commit 7b36e09

Browse files
committed
modules/environment.nix: support etc
Signed-off-by: magic_rb <magic_rb@redalder.org>
1 parent ae75e02 commit 7b36e09

File tree

2 files changed

+152
-0
lines changed

2 files changed

+152
-0
lines changed

modules/environment.hs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{-# LANGUAGE ImportQualifiedPost, DeriveAnyClass, BlockArguments, ViewPatterns #-}
2+
3+
module Main where
4+
5+
import Data.Aeson qualified as A
6+
import GHC.Generics (Generic)
7+
import System.Environment (getArgs)
8+
import Data.HashMap.Strict as HMS
9+
import Data.HashMap.Strict (HashMap)
10+
import System.Exit (ExitCode(..))
11+
import Control.Monad (forM_)
12+
import System.Directory (createDirectoryIfMissing)
13+
import System.Posix.Files (createSymbolicLink)
14+
import System.Exit (exitWith)
15+
import System.FilePath.Posix (takeDirectory, (</>))
16+
17+
data EtcEntry
18+
= EtcEntry
19+
{ enable :: Bool
20+
, target :: String
21+
, source :: String
22+
, mode :: String
23+
, user :: String
24+
, group :: String
25+
, uid :: String
26+
, gid :: String
27+
}
28+
deriving (Show, Generic, A.FromJSON)
29+
30+
etcDirectory :: FilePath
31+
etcDirectory = "/etc"
32+
33+
main :: IO ()
34+
main = do
35+
args <- getArgs
36+
let
37+
etcFile = args !! 0
38+
etcs' :: Maybe (HashMap String EtcEntry) <- A.decodeFileStrict etcFile
39+
40+
case etcs' of
41+
Nothing -> do
42+
putStrLn $ "Unable to deserialize " <> etcFile
43+
exitWith (ExitFailure 1)
44+
Just (HMS.elems -> etcs) -> forM_ etcs \etc ->
45+
let
46+
subdir = takeDirectory (target etc)
47+
exactTarget = etcDirectory </> (target etc)
48+
in
49+
if mode etc == "symlink" then do
50+
createDirectoryIfMissing True (etcDirectory </> subdir)
51+
createSymbolicLink (source etc) exactTarget
52+
else do
53+
putStrLn $ "Other modes than symlink are not supported"

modules/environment.nix

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,83 @@
1515
}:
1616
let
1717
cfg = config.environment;
18+
19+
etcEntry =
20+
{ name, ... }:
21+
{
22+
options = {
23+
enable = lib.mkOption {
24+
type = lib.types.bool;
25+
description = ''
26+
Whether this /etc file should be generated. This option allows specific /etc files to be disabled.
27+
'';
28+
default = true;
29+
};
30+
31+
source = lib.mkOption {
32+
type = lib.types.path;
33+
description = ''
34+
Path of the source file.
35+
'';
36+
};
37+
38+
target = lib.mkOption {
39+
type = lib.types.str;
40+
description = ''
41+
Name of symlink (relative to `/etc`). Defaults to the attribute name.
42+
'';
43+
default = name;
44+
};
45+
46+
mode = lib.mkOption {
47+
type = lib.types.str;
48+
description = ''
49+
If set to something else than symlink, the file is copied instead of symlinked, with the given file mode.
50+
'';
51+
default = "symlink";
52+
};
53+
54+
user = lib.mkOption {
55+
type = lib.types.str;
56+
description = ''
57+
User name of file owner.
58+
59+
Only takes effect when the file is copied (that is, the mode is not symlink).
60+
61+
When services.userborn.enable, this option has no effect. You have to assign a uid instead. Otherwise this option takes precedence over uid.
62+
'';
63+
default = "+0";
64+
};
65+
66+
group = lib.mkOption {
67+
type = lib.types.str;
68+
description = ''
69+
Group name of file owner.
70+
71+
Only takes effect when the file is copied (that is, the mode is not symlink).
72+
73+
When services.userborn.enable, this option has no effect. You have to assign a gid instead. Otherwise this option takes precedence over gid.
74+
'';
75+
default = "+0";
76+
};
77+
78+
uid = lib.mkOption {
79+
type = lib.types.str;
80+
description = ''
81+
UID of created file. Only takes effect when the file is copied (that is, the mode is not 'symlink').
82+
'';
83+
default = "0";
84+
};
85+
86+
gid = lib.mkOption {
87+
type = lib.types.str;
88+
description = ''
89+
GID of created file. Only takes effect when the file is copied (that is, the mode is not ‘symlink’).
90+
'';
91+
default = "0";
92+
};
93+
};
94+
};
1895
in
1996
{
2097
options.environment = {
@@ -64,6 +141,11 @@ in
64141
default = [ ];
65142
type = with lib.types; listOf package;
66143
};
144+
145+
etc = lib.mkOption {
146+
type = lib.types.attrsOf (lib.types.submodule etcEntry);
147+
default = { };
148+
};
67149
};
68150

69151
config = {
@@ -105,5 +187,22 @@ in
105187
mkdir -pm 0555 /var/empty
106188
''
107189
);
190+
191+
init.services.environment-etc = {
192+
enabled = true;
193+
execStart =
194+
let
195+
start = pkgs.writers.writeHaskell "environment-etc-start" {
196+
libraries = with pkgs.haskellPackages; [
197+
unix
198+
directory
199+
filepath
200+
aeson
201+
unordered-containers
202+
];
203+
} (builtins.readFile ./environment.hs);
204+
in
205+
"${start} ${pkgs.writers.writeJSON "environment-etc.json" cfg.etc}";
206+
};
108207
};
109208
}

0 commit comments

Comments
 (0)