1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7
8with lib;
9
10let
11 cfg = config.services.getty;
12
13 baseArgs = [
14 "--login-program"
15 "${cfg.loginProgram}"
16 ]
17 ++ optionals (cfg.autologinUser != null && !cfg.autologinOnce) [
18 "--autologin"
19 cfg.autologinUser
20 ]
21 ++ optionals (cfg.loginOptions != null) [
22 "--login-options"
23 cfg.loginOptions
24 ]
25 ++ cfg.extraArgs;
26
27 gettyCmd = args: "${lib.getExe' pkgs.util-linux "agetty"} ${escapeShellArgs baseArgs} ${args}";
28
29 autologinScript = ''
30 otherArgs="--noclear --keep-baud $TTY 115200,38400,9600 $TERM";
31 ${lib.optionalString cfg.autologinOnce ''
32 autologged="/run/agetty.autologged"
33 if test "$TTY" = tty1 && ! test -f "$autologged"; then
34 touch "$autologged"
35 exec ${gettyCmd "$otherArgs --autologin ${cfg.autologinUser}"}
36 fi
37 ''}
38 exec ${gettyCmd "$otherArgs"}
39 '';
40
41in
42
43{
44
45 ###### interface
46
47 imports = [
48 (mkRenamedOptionModule [ "services" "mingetty" ] [ "services" "getty" ])
49 (mkRemovedOptionModule [ "services" "getty" "serialSpeed" ]
50 ''set non-standard baudrates with `boot.kernelParams` i.e. boot.kernelParams = ["console=ttyS2,1500000"];''
51 )
52 ];
53
54 options = {
55
56 services.getty = {
57
58 autologinUser = mkOption {
59 type = types.nullOr types.str;
60 default = null;
61 description = ''
62 Username of the account that will be automatically logged in at the console.
63 If unspecified, a login prompt is shown as usual.
64 '';
65 };
66
67 autologinOnce = mkOption {
68 type = types.bool;
69 default = false;
70 description = ''
71 If enabled the automatic login will only happen in the first tty
72 once per boot. This can be useful to avoid retyping the account
73 password on systems with full disk encrypted.
74 '';
75 };
76
77 loginProgram = mkOption {
78 type = types.path;
79 default = "${pkgs.shadow}/bin/login";
80 defaultText = literalExpression ''"''${pkgs.shadow}/bin/login"'';
81 description = ''
82 Path to the login binary executed by agetty.
83 '';
84 };
85
86 loginOptions = mkOption {
87 type = types.nullOr types.str;
88 default = null;
89 description = ''
90 Template for arguments to be passed to
91 {manpage}`login(1)`.
92
93 See {manpage}`agetty(1)` for details,
94 including security considerations. If unspecified, agetty
95 will not be invoked with a {option}`--login-options`
96 option.
97 '';
98 example = "-h darkstar -- \\u";
99 };
100
101 extraArgs = mkOption {
102 type = types.listOf types.str;
103 default = [ ];
104 description = ''
105 Additional arguments passed to agetty.
106 '';
107 example = [ "--nohostname" ];
108 };
109
110 greetingLine = mkOption {
111 type = types.str;
112 description = ''
113 Welcome line printed by agetty.
114 The default shows current NixOS version label, machine type and tty.
115 '';
116 };
117
118 helpLine = mkOption {
119 type = types.lines;
120 default = "";
121 description = ''
122 Help line printed by agetty below the welcome line.
123 Used by the installation CD to give some hints on
124 how to proceed.
125 '';
126 };
127
128 };
129
130 };
131
132 ###### implementation
133
134 config = mkIf config.console.enable {
135 # Note: this is set here rather than up there so that changing
136 # nixos.label would not rebuild manual pages
137 services.getty.greetingLine = mkDefault ''<<< Welcome to ${config.system.nixos.distroName} ${config.system.nixos.label} (\m) - \l >>>'';
138 services.getty.helpLine = mkIf (
139 config.documentation.nixos.enable && config.documentation.doc.enable
140 ) "\nRun 'nixos-help' for the NixOS manual.";
141
142 systemd.additionalUpstreamSystemUnits = [
143 "getty.target"
144 "getty-pre.target"
145 "getty@.service"
146 "serial-getty@.service"
147 "console-getty.service"
148 "container-getty@.service"
149 ];
150
151 # We can't just rely on 'Conflicts=autovt@tty1.service' because
152 # 'switch-to-configuration switch' will start 'autovt@tty1.service'
153 # and kill the display manager.
154 systemd.targets.getty.wants =
155 lib.mkIf (!(config.systemd.services.display-manager.enable or false))
156 [
157 "autovt@tty1.service"
158 ];
159
160 systemd.services."getty@" = {
161 serviceConfig.ExecStart = [
162 # override upstream default with an empty ExecStart
163 ""
164 (pkgs.writers.writeDash "getty" autologinScript)
165 ];
166 environment.TTY = "%I";
167 restartIfChanged = false;
168 };
169
170 systemd.services."serial-getty@" = {
171 serviceConfig.ExecStart = [
172 "" # override upstream default with an empty ExecStart
173 (gettyCmd "%I --keep-baud $TERM")
174 ];
175 restartIfChanged = false;
176 };
177
178 systemd.services."autovt@" = {
179 serviceConfig.ExecStart = [
180 "" # override upstream default with an empty ExecStart
181 (gettyCmd "--noclear %I $TERM")
182 ];
183 restartIfChanged = false;
184 };
185
186 systemd.services."container-getty@" = {
187 serviceConfig.ExecStart = [
188 "" # override upstream default with an empty ExecStart
189 (gettyCmd "--noclear --keep-baud pts/%I 115200,38400,9600 $TERM")
190 ];
191 restartIfChanged = false;
192 };
193
194 systemd.services.console-getty = {
195 serviceConfig.ExecStart = [
196 "" # override upstream default with an empty ExecStart
197 (gettyCmd "--noclear --keep-baud console 115200,38400,9600 $TERM")
198 ];
199 serviceConfig.Restart = "always";
200 restartIfChanged = false;
201 enable = mkDefault config.boot.isContainer;
202 };
203
204 environment.etc.issue = mkDefault {
205 # Friendly greeting on the virtual consoles.
206 source = pkgs.writeText "issue" ''
207
208 [1;32m${config.services.getty.greetingLine}[0m
209 ${config.services.getty.helpLine}
210
211 '';
212 };
213
214 };
215
216 meta.maintainers = with maintainers; [ RossComputerGuy ];
217}