1# This module defines a global environment configuration and
2# a common configuration for all shells.
3
4{ config, lib, pkgs, ... }:
5
6with lib;
7
8let
9
10 cfg = config.environment;
11
12 exportedEnvVars =
13 let
14 absoluteVariables =
15 mapAttrs (n: toList) cfg.variables;
16
17 suffixedVariables =
18 flip mapAttrs cfg.profileRelativeEnvVars (envVar: listSuffixes:
19 concatMap (profile: map (suffix: "${profile}${suffix}") listSuffixes) cfg.profiles
20 );
21
22 allVariables =
23 zipAttrsWith (n: concatLists) [ absoluteVariables suffixedVariables ];
24
25 exportVariables =
26 mapAttrsToList (n: v: ''export ${n}="${concatStringsSep ":" v}"'') allVariables;
27 in
28 concatStringsSep "\n" exportVariables;
29in
30
31{
32
33 options = {
34
35 environment.variables = mkOption {
36 default = {};
37 description = ''
38 A set of environment variables used in the global environment.
39 These variables will be set on shell initialisation.
40 The value of each variable can be either a string or a list of
41 strings. The latter is concatenated, interspersed with colon
42 characters.
43 '';
44 type = types.attrsOf (mkOptionType {
45 name = "a string or a list of strings";
46 merge = loc: defs:
47 let
48 defs' = filterOverrides defs;
49 res = (head defs').value;
50 in
51 if isList res then concatLists (getValues defs')
52 else if lessThan 1 (length defs') then
53 throw "The option `${showOption loc}' is defined multiple times, in ${showFiles (getFiles defs)}."
54 else if !isString res then
55 throw "The option `${showOption loc}' does not have a string value, in ${showFiles (getFiles defs)}."
56 else res;
57 });
58 apply = mapAttrs (n: v: if isList v then concatStringsSep ":" v else v);
59 };
60
61 environment.profiles = mkOption {
62 default = [];
63 description = ''
64 A list of profiles used to setup the global environment.
65 '';
66 type = types.listOf types.str;
67 };
68
69 environment.profileRelativeEnvVars = mkOption {
70 type = types.attrsOf (types.listOf types.str);
71 example = { PATH = [ "/bin" "/sbin" ]; MANPATH = [ "/man" "/share/man" ]; };
72 description = ''
73 Attribute set of environment variable. Each attribute maps to a list
74 of relative paths. Each relative path is appended to the each profile
75 of <option>environment.profiles</option> to form the content of the
76 corresponding environment variable.
77 '';
78 };
79
80 # !!! isn't there a better way?
81 environment.extraInit = mkOption {
82 default = "";
83 description = ''
84 Shell script code called during global environment initialisation
85 after all variables and profileVariables have been set.
86 This code is asumed to be shell-independent, which means you should
87 stick to pure sh without sh word split.
88 '';
89 type = types.lines;
90 };
91
92 environment.shellInit = mkOption {
93 default = "";
94 description = ''
95 Shell script code called during shell initialisation.
96 This code is asumed to be shell-independent, which means you should
97 stick to pure sh without sh word split.
98 '';
99 type = types.lines;
100 };
101
102 environment.loginShellInit = mkOption {
103 default = "";
104 description = ''
105 Shell script code called during login shell initialisation.
106 This code is asumed to be shell-independent, which means you should
107 stick to pure sh without sh word split.
108 '';
109 type = types.lines;
110 };
111
112 environment.interactiveShellInit = mkOption {
113 default = "";
114 description = ''
115 Shell script code called during interactive shell initialisation.
116 This code is asumed to be shell-independent, which means you should
117 stick to pure sh without sh word split.
118 '';
119 type = types.lines;
120 };
121
122 environment.shellAliases = mkOption {
123 default = {};
124 example = { ll = "ls -l"; };
125 description = ''
126 An attribute set that maps aliases (the top level attribute names in
127 this option) to command strings or directly to build outputs. The
128 aliases are added to all users' shells.
129 '';
130 type = types.attrs; # types.attrsOf types.stringOrPath;
131 };
132
133 environment.binsh = mkOption {
134 default = "${config.system.build.binsh}/bin/sh";
135 example = literalExample ''
136 "''${pkgs.dash}/bin/dash"
137 '';
138 type = types.path;
139 description = ''
140 The shell executable that is linked system-wide to
141 <literal>/bin/sh</literal>. Please note that NixOS assumes all
142 over the place that shell to be Bash, so override the default
143 setting only if you know exactly what you're doing.
144 '';
145 };
146
147 environment.shells = mkOption {
148 default = [];
149 example = [ "/run/current-system/sw/bin/zsh" ];
150 description = ''
151 A list of permissible login shells for user accounts.
152 No need to mention <literal>/bin/sh</literal>
153 here, it is placed into this list implicitly.
154 '';
155 type = types.listOf types.path;
156 };
157
158 };
159
160 config = {
161
162 system.build.binsh = pkgs.bashInteractive;
163
164 # Set session variables in the shell as well. This is usually
165 # unnecessary, but it allows changes to session variables to take
166 # effect without restarting the session (e.g. by opening a new
167 # terminal instead of logging out of X11).
168 environment.variables = config.environment.sessionVariables;
169
170 environment.etc."shells".text =
171 ''
172 ${concatStringsSep "\n" cfg.shells}
173 /bin/sh
174 '';
175
176 system.build.setEnvironment = pkgs.writeText "set-environment"
177 ''
178 ${exportedEnvVars}
179
180 ${cfg.extraInit}
181
182 # The setuid wrappers override other bin directories.
183 export PATH="${config.security.wrapperDir}:$PATH"
184
185 # ~/bin if it exists overrides other bin directories.
186 export PATH="$HOME/bin:$PATH"
187 '';
188
189 system.activationScripts.binsh = stringAfter [ "stdio" ]
190 ''
191 # Create the required /bin/sh symlink; otherwise lots of things
192 # (notably the system() function) won't work.
193 mkdir -m 0755 -p /bin
194 ln -sfn "${cfg.binsh}" /bin/.sh.tmp
195 mv /bin/.sh.tmp /bin/sh # atomically replace /bin/sh
196 '';
197
198 };
199
200}