1# This module defines a global environment configuration and
2# a common configuration for all shells.
3
4{ config, lib, utils, 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 example = { EDITOR = "nvim"; VISUAL = "nvim"; };
38 description = ''
39 A set of environment variables used in the global environment.
40 These variables will be set on shell initialisation (e.g. in /etc/profile).
41 The value of each variable can be either a string or a list of
42 strings. The latter is concatenated, interspersed with colon
43 characters.
44 '';
45 type = with types; attrsOf (either str (listOf str));
46 apply = mapAttrs (n: v: if isList v then concatStringsSep ":" v else v);
47 };
48
49 environment.profiles = mkOption {
50 default = [];
51 description = ''
52 A list of profiles used to setup the global environment.
53 '';
54 type = types.listOf types.str;
55 };
56
57 environment.profileRelativeEnvVars = mkOption {
58 type = types.attrsOf (types.listOf types.str);
59 example = { PATH = [ "/bin" ]; MANPATH = [ "/man" "/share/man" ]; };
60 description = ''
61 Attribute set of environment variable. Each attribute maps to a list
62 of relative paths. Each relative path is appended to the each profile
63 of <option>environment.profiles</option> to form the content of the
64 corresponding environment variable.
65 '';
66 };
67
68 # !!! isn't there a better way?
69 environment.extraInit = mkOption {
70 default = "";
71 description = ''
72 Shell script code called during global environment initialisation
73 after all variables and profileVariables have been set.
74 This code is assumed to be shell-independent, which means you should
75 stick to pure sh without sh word split.
76 '';
77 type = types.lines;
78 };
79
80 environment.shellInit = mkOption {
81 default = "";
82 description = ''
83 Shell script code called during shell initialisation.
84 This code is assumed to be shell-independent, which means you should
85 stick to pure sh without sh word split.
86 '';
87 type = types.lines;
88 };
89
90 environment.loginShellInit = mkOption {
91 default = "";
92 description = ''
93 Shell script code called during login shell initialisation.
94 This code is assumed to be shell-independent, which means you should
95 stick to pure sh without sh word split.
96 '';
97 type = types.lines;
98 };
99
100 environment.interactiveShellInit = mkOption {
101 default = "";
102 description = ''
103 Shell script code called during interactive shell initialisation.
104 This code is assumed to be shell-independent, which means you should
105 stick to pure sh without sh word split.
106 '';
107 type = types.lines;
108 };
109
110 environment.shellAliases = mkOption {
111 default = {};
112 example = { ll = "ls -l"; };
113 description = ''
114 An attribute set that maps aliases (the top level attribute names in
115 this option) to command strings or directly to build outputs. The
116 aliases are added to all users' shells.
117 '';
118 type = types.attrs; # types.attrsOf types.stringOrPath;
119 };
120
121 environment.binsh = mkOption {
122 default = "${config.system.build.binsh}/bin/sh";
123 defaultText = "\${config.system.build.binsh}/bin/sh";
124 example = literalExample ''
125 "''${pkgs.dash}/bin/dash"
126 '';
127 type = types.path;
128 visible = false;
129 description = ''
130 The shell executable that is linked system-wide to
131 <literal>/bin/sh</literal>. Please note that NixOS assumes all
132 over the place that shell to be Bash, so override the default
133 setting only if you know exactly what you're doing.
134 '';
135 };
136
137 environment.shells = mkOption {
138 default = [];
139 example = literalExample "[ pkgs.bashInteractive pkgs.zsh ]";
140 description = ''
141 A list of permissible login shells for user accounts.
142 No need to mention <literal>/bin/sh</literal>
143 here, it is placed into this list implicitly.
144 '';
145 type = types.listOf (types.either types.shellPackage types.path);
146 };
147
148 };
149
150 config = {
151
152 system.build.binsh = pkgs.bashInteractive;
153
154 # Set session variables in the shell as well. This is usually
155 # unnecessary, but it allows changes to session variables to take
156 # effect without restarting the session (e.g. by opening a new
157 # terminal instead of logging out of X11).
158 environment.variables = config.environment.sessionVariables;
159
160 environment.etc."shells".text =
161 ''
162 ${concatStringsSep "\n" (map utils.toShellPath cfg.shells)}
163 /bin/sh
164 '';
165
166 system.build.setEnvironment = pkgs.writeText "set-environment"
167 ''
168 ${exportedEnvVars}
169
170 ${cfg.extraInit}
171
172 # ~/bin if it exists overrides other bin directories.
173 export PATH="$HOME/bin:$PATH"
174 '';
175
176 system.activationScripts.binsh = stringAfter [ "stdio" ]
177 ''
178 # Create the required /bin/sh symlink; otherwise lots of things
179 # (notably the system() function) won't work.
180 mkdir -m 0755 -p /bin
181 ln -sfn "${cfg.binsh}" /bin/.sh.tmp
182 mv /bin/.sh.tmp /bin/sh # atomically replace /bin/sh
183 '';
184
185 };
186
187}