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