diff --git a/doc/manual/configuration.xml b/doc/manual/configuration.xml new file mode 100644 index 000000000000..f1f99fb70ed9 --- /dev/null +++ b/doc/manual/configuration.xml @@ -0,0 +1,173 @@ + + +Configuring NixOS + +This chapter describes how to configure various aspects of a +NixOS machine through the configuration file +/etc/nixos/configuration.nix. As described in +, changes to that file only take +effect after you run nixos-rebuild. + + + + +
Networking + +
Secure shell access + +Secure shell (SSH) access to your machine can be enabled by +setting: + + +services.openssh.enable = true; + + +By default, root logins using a password are disallowed. They can be +disabled entirely by setting +services.openssh.permitRootLogin to +"no". + +You can declaratively specify authorised RSA/DSA public keys for +a user as follows: + + + +users.extraUsers.alice.openssh.authorizedKeys.keys = + [ "ssh-dss AAAAB3NzaC1kc3MAAACBAPIkGWVEt4..." ]; + + + + +
+ + +
IPv4 configuration + +By default, NixOS uses DHCP (specifically, +(dhcpcd)) to automatically configure network +interfaces. However, you can configure an interface manually as +follows: + + +networking.interfaces.eth0 = { ipAddress = "192.168.1.2"; prefixLength = 24; }; + + +(The network prefix can also be specified using the option +subnetMask, +e.g. "255.255.255.0", but this is deprecated.) +Typically you’ll also want to set a default gateway and set of name +servers: + + +networking.defaultGateway = "192.168.1.1"; +networking.nameservers = [ "8.8.8.8" ]; + + + + +Statically configured interfaces are set up by the systemd +service +interface-name-cfg.service. +The default gateway and name server configuration is performed by +network-setup.service. + +The host name is set using : + + +networking.hostName = "cartman"; + + +The default host name is nixos. Set it to the +empty string ("") to allow the DHCP server to +provide the host name. + +
+ + +
IPv6 configuration + +IPv6 is enabled by default. Stateless address autoconfiguration +is used to automatically assign IPv6 addresses to all interfaces. You +can disable IPv6 support globally by setting: + + +networking.enableIPv6 = false; + + + + +
+ + +
Firewall + +NixOS has a simple stateful firewall that blocks incoming +connections and other unexpected packets. The firewall applies to +both IPv4 and IPv6 traffic. It can be enabled as follows: + + +networking.firewall.enable = true; + + +You can open specific TCP ports to the outside world: + + +networking.firewall.allowedTCPPorts = [ 80 443 ]; + + +Note that TCP port 22 (ssh) is opened automatically if the SSH daemon +is enabled (). UDP +ports can be opened through +. Also of +interest is + + +networking.firewall.allowPing = true; + + +to allow the machine to respond to ping requests. (ICMPv6 pings are +always allowed.) + +
+ + +
Wireless networks + +TODO + +
+ + +
Ad-hoc configuration + +You can use to specify +shell commands to be run at the end of +network-setup.service. This is useful for doing +network configuration not covered by the existing NixOS modules. For +instance, to statically configure an IPv6 address: + + +networking.localCommands = + '' + ip -6 addr add 2001:610:685:1::1/64 dev eth0 + ''; + + + + +
+ + + + + +
+ + + + + +
diff --git a/doc/manual/default.nix b/doc/manual/default.nix index d3f554c099ee..e6edb30985c1 100644 --- a/doc/manual/default.nix +++ b/doc/manual/default.nix @@ -59,7 +59,7 @@ in rec { mkdir -p $dst/images/callouts cp ${pkgs.docbook5_xsl}/xml/xsl/docbook/images/callouts/*.gif $dst/images/callouts/ - + cp ${./style.css} $dst/style.css ensureDir $out/nix-support diff --git a/doc/manual/installation.xml b/doc/manual/installation.xml index 8bde2f6e0530..1c00dd37d337 100644 --- a/doc/manual/installation.xml +++ b/doc/manual/installation.xml @@ -1,7 +1,7 @@ -Installation +Installing NixOS @@ -58,7 +58,7 @@ Wiki. For partitioning: fdisk. - + For initialising Ext4 partitions: mkfs.ext4. It is recommended that you assign a unique symbolic label to the file system using the option @@ -70,13 +70,13 @@ Wiki. mkswap. Again it’s recommended to assign a label to the swap partition: . - + For creating LVM volumes, the LVM commands, e.g., $ pvcreate /dev/sda1 /dev/sdb1 $ vgcreate MyVolGroup /dev/sda1 /dev/sdb1 -$ lvcreate --size 2G --name bigdisk MyVolGroup +$ lvcreate --size 2G --name bigdisk MyVolGroup $ lvcreate --size 1G --name smalldisk MyVolGroup @@ -87,7 +87,7 @@ $ lvcreate --size 1G --name smalldisk MyVolGroup - + Mount the target file system on which NixOS should be installed on /mnt. @@ -138,7 +138,7 @@ $ nixos-option --install xlink:href="https://nixos.org/repos/nix/configurations/trunk/"/>. - + If your machine has a limited amount of memory, you may want to activate swap devices now (swapon device). The installer (or @@ -234,7 +234,7 @@ $ reboot swapDevices = [ { device = "/dev/disk/by-label/swap"; } ]; - + services.sshd.enable = true; } @@ -260,7 +260,7 @@ to build the new configuration, make it the default configuration for booting, and try to realise the configuration in the running system (e.g., by restarting system services). -You can also do +You can also do $ nixos-rebuild test @@ -270,7 +270,7 @@ without making it the boot default. So if (say) the configuration locks up your machine, you can just reboot to get back to a working configuration. -There is also +There is also $ nixos-rebuild boot @@ -279,7 +279,7 @@ to build the configuration and make it the boot default, but not switch to it now (so it will only take effect after the next reboot). -Finally, you can do +Finally, you can do $ nixos-rebuild build @@ -329,7 +329,7 @@ You can then upgrade NixOS to the latest version in the channel by running -$ nix-channel --update +$ nix-channel --update nixos and running the nixos-rebuild command as described diff --git a/doc/manual/manual.xml b/doc/manual/manual.xml index b7e4c6315f9e..ce4055a753c9 100644 --- a/doc/manual/manual.xml +++ b/doc/manual/manual.xml @@ -24,16 +24,16 @@ 2007-2012 Eelco Dolstra - + - + Preface This manual describes NixOS, a Linux distribution based on the purely functional package management system Nix. - + NixOS is rather bleeding edge, and this manual is correspondingly sketchy and quite possibly out of date. It gives basic information on how to get NixOS up and running, but since @@ -45,11 +45,13 @@ mailing list or on the #nixos channel on Freenode.. - + - + + + diff --git a/doc/manual/running.xml b/doc/manual/running.xml new file mode 100644 index 000000000000..f9feadbc086e --- /dev/null +++ b/doc/manual/running.xml @@ -0,0 +1,288 @@ + + +Running NixOS + +This chapter describes various aspects of managing a running +NixOS system, such as how to use the systemd +service manager. + + + + +
Service management + +In NixOS, all system services are started and monitored using +the systemd program. Systemd is the “init” process of the system +(i.e. PID 1), the parent of all other processes. It manages a set of +so-called “units”, which can be things like system services +(programs), but also mount points, swap files, devices, targets +(groups of units) and more. Units can have complex dependencies; for +instance, one unit can require that another unit must be succesfully +started before the first unit can be started. When the system boots, +it starts a unit named default.target; the +dependencies of this unit cause all system services to be started, +filesystems to be mounted, swap files to be activated, and so +on. + +The command systemctl is the main way to +interact with systemd. Without any arguments, it +shows the status of active units: + + +$ systemctl +-.mount loaded active mounted / +swapfile.swap loaded active active /swapfile +sshd.service loaded active running SSH Daemon +graphical.target loaded active active Graphical Interface +... + + + + +You can ask for detailed status information about a unit, for +instance, the PostgreSQL database service: + + +$ systemctl status postgresql.service +postgresql.service - PostgreSQL Server + Loaded: loaded (/nix/store/pn3q73mvh75gsrl8w7fdlfk3fq5qm5mw-unit/postgresql.service) + Active: active (running) since Mon, 2013-01-07 15:55:57 CET; 9h ago + Main PID: 2390 (postgres) + CGroup: name=systemd:/system/postgresql.service + ├─2390 postgres + ├─2418 postgres: writer process + ├─2419 postgres: wal writer process + ├─2420 postgres: autovacuum launcher process + ├─2421 postgres: stats collector process + └─2498 postgres: zabbix zabbix [local] idle + +Jan 07 15:55:55 hagbard postgres[2394]: [1-1] LOG: database system was shut down at 2013-01-07 15:55:05 CET +Jan 07 15:55:57 hagbard postgres[2390]: [1-1] LOG: database system is ready to accept connections +Jan 07 15:55:57 hagbard postgres[2420]: [1-1] LOG: autovacuum launcher started +Jan 07 15:55:57 hagbard systemd[1]: Started PostgreSQL Server. + + +Note that this shows the status of the unit (active and running), all +the processes belonging to the service, as well as the most recent log +messages from the service. + + + +Units can be stopped, started or restarted: + + +$ systemctl stop postgresql.service +$ systemctl start postgresql.service +$ systemctl restart postgresql.service + + +These operations are synchronous: they wait until the service has +finished starting or stopping (or has failed). Starting a unit will +cause the dependencies of that unit to be started as well (if +necessary). + + + +
+ + + + +
Rebooting and shutting down + +The system can be shut down (and automatically powered off) by +doing: + + +$ shutdown + + +This is equivalent to running systemctl poweroff. +Likewise, reboot (a.k.a. systemctl +reboot) will reboot the system. + +The machine can be suspended to RAM (if supported) using +systemctl suspend, and suspended to disk using +systemctl hibernate. + +These commands can be run by any user who is logged in locally, +i.e. on a virtual console or in X11; otherwise, the user is asked for +authentication. + +
+ + + + +
User sessions + +Systemd keeps track of all users who are logged into the system +(e.g. on a virtual console or remotely via SSH). The command +loginctl allows quering and manipulating user +sessions. For instance, to list all user sessions: + + +$ loginctl + SESSION UID USER SEAT + c1 500 eelco seat0 + c3 0 root seat0 + c4 500 alice + + +This shows that two users are logged in locally, while another is +logged in remotely. (“Seats” are essentially the combinations of +displays and input devices attached to the system; usually, there is +only one seat.) To get information about a session: + + +$ loginctl session-status c3 +c3 - root (0) + Since: Tue, 2013-01-08 01:17:56 CET; 4min 42s ago + Leader: 2536 (login) + Seat: seat0; vc3 + TTY: /dev/tty3 + Service: login; type tty; class user + State: online + CGroup: name=systemd:/user/root/c3 + ├─ 2536 /nix/store/10mn4xip9n7y9bxqwnsx7xwx2v2g34xn-shadow-4.1.5.1/bin/login -- + ├─10339 -bash + └─10355 w3m nixos.org + + +This shows that the user is logged in on virtual console 3. It also +lists the processes belonging to this session. Since systemd keeps +track of this, you can terminate a session in a way that ensures that +all the session’s processes are gone: + + +$ loginctl terminate-session c3 + + + + +
+ + + + +
Control groups + +To keep track of the processes in a running system, systemd uses +control groups (cgroups). A control group is a +set of processes used to allocate resources such as CPU, memory or I/O +bandwidth. There can be multiple control group hierarchies, allowing +each kind of resource to be managed independently. + +The command systemd-cgls lists all control +groups in the systemd hierarchy, which is what +systemd uses to keep track of the processes belonging to each service +or user session: + + +$ systemd-cgls +├─user +│ └─eelco +│ └─c1 +│ ├─ 2567 -:0 +│ ├─ 2682 kdeinit4: kdeinit4 Running... +│ ├─ ... +│ └─10851 sh -c less -R +└─system + ├─httpd.service + │ ├─2444 httpd -f /nix/store/3pyacby5cpr55a03qwbnndizpciwq161-httpd.conf -DNO_DETACH + │ └─... + ├─dhcpcd.service + │ └─2376 dhcpcd --config /nix/store/f8dif8dsi2yaa70n03xir8r653776ka6-dhcpcd.conf + └─ ... + + +Similarly, systemd-cgls cpu shows the cgroups in +the CPU hierarchy, which allows per-cgroup CPU scheduling priorities. +By default, every systemd service gets its own CPU cgroup, while all +user sessions are in the top-level CPU cgroup. This ensures, for +instance, that a thousand run-away processes in the +httpd.service cgroup cannot starve the CPU for one +process in the postgresql.service cgroup. (By +contrast, it they were in the same cgroup, then the PostgreSQL process +would get 1/1001 of the cgroup’s CPU time.) You can limit a service’s +CPU share in configuration.nix: + + +boot.systemd.services.httpd.serviceConfig.CPUShares = 512; + + +By default, every cgroup has 1024 CPU shares, so this will halve the +CPU allocation of the httpd.service cgroup. + +There also is a memory hierarchy that +controls memory allocation limits; by default, all processes are in +the top-level cgroup, so any service or session can exhaust all +available memory. Per-cgroup memory limits can be specified in +configuration.nix; for instance, to limit +httpd.service to 512 MiB of RAM (excluding swap) +and 640 MiB of RAM (including swap): + + +boot.systemd.services.httpd.serviceConfig.MemoryLimit = "512M"; +boot.systemd.services.httpd.serviceConfig.ControlGroupAttribute = [ "memory.memsw.limit_in_bytes 640M" ]; + + + + +The command systemd-cgtop shows a +continuously updated list of all cgroups with their CPU and memory +usage. + +
+ + + + +
Logging + +System-wide logging is provided by systemd’s +journal, which subsumes traditional logging +daemons such as syslogd and klogd. Log entries are kept in binary +files in /var/log/journal/. The command +journalctl allows you to see the contents of the +journal. For example, + + +$ journalctl -b + + +shows all journal entries since the last reboot. (The output of +journalctl is piped into less by +default.) You can use various options and match operators to restrict +output to messages of interest. For instance, to get all messages +from PostgreSQL: + + +$ journalctl _SYSTEMD_UNIT=postgresql.service +-- Logs begin at Mon, 2013-01-07 13:28:01 CET, end at Tue, 2013-01-08 01:09:57 CET. -- +... +Jan 07 15:44:14 hagbard postgres[2681]: [2-1] LOG: database system is shut down +-- Reboot -- +Jan 07 15:45:10 hagbard postgres[2532]: [1-1] LOG: database system was shut down at 2013-01-07 15:44:14 CET +Jan 07 15:45:13 hagbard postgres[2500]: [1-1] LOG: database system is ready to accept connections + + +Or to get all messages since the last reboot that have at least a +“critical” severity level: + + +$ journalctl -b -p crit +Dec 17 21:08:06 mandark sudo[3673]: pam_unix(sudo:auth): auth could not identify password for [alice] +Dec 29 01:30:22 mandark kernel[6131]: [1053513.909444] CPU6: Core temperature above threshold, cpu clock throttled (total events = 1) + + + + +
+ + +
diff --git a/doc/manual/troubleshooting.xml b/doc/manual/troubleshooting.xml index ff19607844fc..2961f8e1233a 100644 --- a/doc/manual/troubleshooting.xml +++ b/doc/manual/troubleshooting.xml @@ -4,60 +4,81 @@ Troubleshooting -
- -Debugging the boot process +
Boot problems -To get a Stage 1 shell (i.e., a shell in the initial ramdisk), -add debug1 to the kernel command line. The shell -gets started before anything useful has been done. That is, no -modules have been loaded and no file systems have been mounted, except -for /proc and /sys. +If NixOS fails to boot, there are a number of kernel command +line parameters that may help you to identify or fix the issue. You +can add these parameters in the GRUB boot menu by pressing “e” to +modify the selected boot entry and editing the line starting with +linux. The following are some useful kernel command +line parameters that are recognised by the NixOS boot scripts or by +systemd: -To get a Stage 2 shell (i.e., a shell in the actual root file -system), add debug2 to the kernel command -line. This shell is started right after stage 1 calls the stage 2 -init script, so the root file system is there but -no services have been started. + -
+ boot.shell_on_fail + Start a root shell if something goes wrong in + stage 1 of the boot process (the initial ramdisk). This is + disabled by default because there is no authentication for the + root shell. + + boot.debug1 + Start an interactive shell in stage 1 before + anything useful has been done. That is, no modules have been + loaded and no file systems have been mounted, except for + /proc and + /sys. + + boot.trace + Print every shell command executed by the stage 1 + and 2 boot scripts. + -
- -Safe mode + single + Boot into rescue mode (a.k.a. single user mode). + This will cause systemd to start nothing but the unit + rescue.target, which runs + sulogin to prompt for the root password and + start a root login shell. Exiting the shell causes the system to + continue with the normal boot process. + -If the hardware autodetection (in -upstart-jobs/hardware-scan) causes problems, add -safemode to the kernel command line. This will -disable auto-loading of modules for your PCI devices. However, you -will probably need to explicitly add modules to - to get network support etc. + systemd.log_level=debug systemd.log_target=console + Make systemd very verbose and send log messages to + the console instead of the journal. + + + + +For more parameters recognised by systemd, see +systemd1. + +If no login prompts or X11 login screens appear (e.g. due to +hanging dependencies), you can press Alt+ArrowUp. If you’re lucky, +this will start rescue mode (described above). (Also note that since +most units have a 90-second timeout before systemd gives up on them, +the agetty login prompts should appear eventually +unless something is very wrong.)
- + Maintenance mode -You can go to maintenance mode by doing +You can enter rescue mode by running: -$ shutdown now +$ systemctl rescue -This will eventually give you a single-user root shell. - -To get out of maintenance mode, do - - -$ initctl emit startup - - +This will eventually give you a single-user root shell. Systemd will +stop (almost) all system services. To get out of maintenance mode, +just exit from the rescue shell.
- diff --git a/modules/module-list.nix b/modules/module-list.nix index ae5e5d5c49f6..db54f5665a73 100644 --- a/modules/module-list.nix +++ b/modules/module-list.nix @@ -100,6 +100,7 @@ ./services/misc/rogue.nix ./services/misc/svnserve.nix ./services/misc/synergy.nix + ./services/monitoring/dd-agent.nix ./services/monitoring/monit.nix ./services/monitoring/nagios/default.nix ./services/monitoring/smartd.nix diff --git a/modules/profiles/headless.nix b/modules/profiles/headless.nix index 3446654bc6f5..593bd925b006 100644 --- a/modules/profiles/headless.nix +++ b/modules/profiles/headless.nix @@ -16,5 +16,5 @@ with pkgs.lib; boot.systemd.services."serial-getty@hvc0".enable = false; # Since we can't manually respond to a panic, just reboot. - boot.kernelParams = [ "panic=1" "stage1panic=1" ]; + boot.kernelParams = [ "panic=1" "boot.panic_on_fail" ]; } diff --git a/modules/services/misc/nix-daemon.nix b/modules/services/misc/nix-daemon.nix index 49aa8e7931f7..eaea8bd653a3 100644 --- a/modules/services/misc/nix-daemon.nix +++ b/modules/services/misc/nix-daemon.nix @@ -286,7 +286,7 @@ in environment = cfg.envVars; serviceConfig = - { ExecStart = "${nix}/bin/nix-worker --daemon"; + { ExecStart = "@${nix}/bin/nix-daemon nix-daemon"; KillMode = "process"; Nice = cfg.daemonNiceLevel; IOSchedulingPriority = cfg.daemonIONiceLevel; diff --git a/modules/services/misc/nix-gc.nix b/modules/services/misc/nix-gc.nix index 942e7996da02..435326e87fb8 100644 --- a/modules/services/misc/nix-gc.nix +++ b/modules/services/misc/nix-gc.nix @@ -3,7 +3,6 @@ with pkgs.lib; let - nix = config.environment.nix; cfg = config.nix.gc; in @@ -16,7 +15,7 @@ in automatic = mkOption { default = false; - example = true; + type = types.bool; description = " Automatically run the garbage collector at specified dates. "; @@ -24,6 +23,7 @@ in dates = mkOption { default = "15 03 * * *"; + type = types.string; description = " Run the garbage collector at specified dates to avoid full hard-drives. @@ -33,6 +33,7 @@ in options = mkOption { default = ""; example = "--max-freed $((64 * 1024**3))"; + type = types.string; description = " Options given to nix-collect-garbage when the garbage collector is run automatically. @@ -45,10 +46,17 @@ in ###### implementation - config = mkIf cfg.automatic { - services.cron.systemCronJobs = [ - "${cfg.dates} root ${nix}/bin/nix-collect-garbage ${cfg.options} > /var/log/gc.log 2>&1" - ]; + config = { + + services.cron.systemCronJobs = mkIf cfg.automatic (singleton + "${cfg.dates} root ${config.system.build.systemd}/bin/systemctl start nix-gc.service"); + + boot.systemd.services."nix-gc" = + { description = "Nix Garbage Collector"; + path = [ config.environment.nix ]; + script = "exec nix-collect-garbage ${cfg.options}"; + }; + }; } diff --git a/modules/services/misc/nixos-manual.nix b/modules/services/misc/nixos-manual.nix index 2e98d50b0ced..1a172904c454 100644 --- a/modules/services/misc/nixos-manual.nix +++ b/modules/services/misc/nixos-manual.nix @@ -16,6 +16,17 @@ let inherit pkgs options; }; + entry = "${manual.manual}/share/doc/nixos/manual.html"; + + help = pkgs.writeScriptBin "nixos-help" + '' + #! ${pkgs.stdenv.shell} -e + if ! ''${BROWSER:-w3m} ${entry}; then + echo "$0: unable to start a web browser; please set \$BROWSER or install ‘w3m’" + exit 1 + fi + ''; + in { @@ -69,7 +80,7 @@ in system.build.manual = manual; - environment.systemPackages = [ manual.manpages ]; + environment.systemPackages = [ manual.manpages help ]; boot.extraTTYs = mkIf cfg.showManual ["tty${cfg.ttyNumber}"]; @@ -78,7 +89,7 @@ in { description = "NixOS Manual"; wantedBy = [ "multi-user.target" ]; serviceConfig = - { ExecStart = "${cfg.browser} ${manual.manual}/share/doc/nixos/manual.html"; + { ExecStart = "${cfg.browser} ${entry}"; StandardInput = "tty"; StandardOutput = "tty"; TTYPath = "/dev/tty${cfg.ttyNumber}"; diff --git a/modules/services/monitoring/dd-agent.nix b/modules/services/monitoring/dd-agent.nix new file mode 100644 index 000000000000..c0493557d56d --- /dev/null +++ b/modules/services/monitoring/dd-agent.nix @@ -0,0 +1,58 @@ +{ config, pkgs, ... }: + +with pkgs.lib; + +let + cfg = config.services.dd-agent; + + datadog-conf = pkgs.runCommand "datadog.conf" {} '' + sed -e 's|^api_key:|api_key: ${cfg.api_key}|' ${optionalString (cfg.hostname != null) + "-e 's|^#hostname: mymachine.mydomain|hostname: ${cfg.hostname}|'" + } ${pkgs.dd-agent}/etc/dd-agent/datadog.conf.example > $out + ''; +in { + options.services.dd-agent = { + enable = mkOption { + description = "Whether to enable the dd-agent montioring service"; + + default = false; + + type = types.bool; + }; + + # !!! This gets stored in the store (world-readable), wish we had https://github.com/NixOS/nix/issues/8 + api_key = mkOption { + description = "The Datadog API key to associate the agent with your account"; + + example = "ae0aa6a8f08efa988ba0a17578f009ab"; + + type = types.uniq types.string; + }; + + hostname = mkOption { + description = "The hostname to show in the Datadog dashboard (optional)"; + + default = null; + + example = "mymachine.mydomain"; + + type = types.uniq (types.nullOr types.string); + }; + }; + + config = mkIf cfg.enable { + environment.etc = [ { source = datadog-conf; target = "dd-agent/datadog.conf"; } ]; + + boot.systemd.services.dd-agent = { + description = "Datadog agent monitor"; + + path = [ pkgs.sysstat pkgs.procps ]; + + wantedBy = [ "multi-user.target" ]; + + serviceConfig.ExecStart = "${pkgs.dd-agent}/bin/dd-agent foreground"; + + restartTriggers = [ pkgs.dd-agent ]; + }; + }; +} diff --git a/modules/services/monitoring/smartd.nix b/modules/services/monitoring/smartd.nix index 72a4495b1f58..48066856625a 100644 --- a/modules/services/monitoring/smartd.nix +++ b/modules/services/monitoring/smartd.nix @@ -87,7 +87,6 @@ in environment.TZ = config.time.timeZone; wantedBy = [ "multi-user.target" ]; - partOf = [ "multi-user.target" ]; serviceConfig.ExecStart = "${pkgs.smartmontools}/sbin/smartd --no-fork ${smartdFlags}"; }; diff --git a/modules/services/networking/dhcpcd.nix b/modules/services/networking/dhcpcd.nix index 2a0d73f60040..cf7f621a85d5 100644 --- a/modules/services/networking/dhcpcd.nix +++ b/modules/services/networking/dhcpcd.nix @@ -97,6 +97,11 @@ in wantedBy = [ "network.target" ]; + # Stopping dhcpcd during a reconfiguration is undesirable + # because it brings down the network interfaces configured by + # dhcpcd. So do a "systemctl restart" instead. + stopIfChanged = false; + path = [ dhcpcd pkgs.nettools pkgs.openresolv ]; serviceConfig = diff --git a/modules/services/networking/rpcbind.nix b/modules/services/networking/rpcbind.nix index 8e3e86a515c1..f82803e75194 100644 --- a/modules/services/networking/rpcbind.nix +++ b/modules/services/networking/rpcbind.nix @@ -29,6 +29,8 @@ let ''; }; + check = mkAssert (!(config.services.rpcbind.enable && config.services.portmap.enable)) + "Portmap and rpcbind cannot both be enabled."; in @@ -57,7 +59,7 @@ in ###### implementation - config = mkIf config.services.rpcbind.enable { + config = mkIf config.services.rpcbind.enable (check { environment.systemPackages = [ pkgs.rpcbind ]; @@ -77,6 +79,6 @@ in serviceConfig.ExecStart = "@${pkgs.rpcbind}/bin/rpcbind rpcbind"; }; - }; + }); } diff --git a/modules/services/networking/ssh/sshd.nix b/modules/services/networking/ssh/sshd.nix index 21f81152fa57..8f898ce06a18 100644 --- a/modules/services/networking/ssh/sshd.nix +++ b/modules/services/networking/ssh/sshd.nix @@ -267,6 +267,8 @@ in wantedBy = [ "multi-user.target" ]; + stopIfChanged = false; + path = [ pkgs.openssh ]; environment.LD_LIBRARY_PATH = nssModulesPath; diff --git a/modules/services/x11/xserver.nix b/modules/services/x11/xserver.nix index 1c5a3f75039b..6eec6f88b97d 100644 --- a/modules/services/x11/xserver.nix +++ b/modules/services/x11/xserver.nix @@ -389,7 +389,7 @@ in boot.systemd.defaultUnit = mkIf cfg.autorun "graphical.target"; boot.systemd.services."display-manager" = - { after = [ "systemd-udev-settle.service" ]; + { after = [ "systemd-udev-settle.service" "local-fs.target" ]; restartIfChanged = false; diff --git a/modules/system/activation/switch-to-configuration.pl b/modules/system/activation/switch-to-configuration.pl index b085778f0994..032947c3c546 100644 --- a/modules/system/activation/switch-to-configuration.pl +++ b/modules/system/activation/switch-to-configuration.pl @@ -6,6 +6,7 @@ use File::Basename; use File::Slurp; use Cwd 'abs_path'; +my $startListFile = "/run/systemd/start-list"; my $restartListFile = "/run/systemd/restart-list"; my $reloadListFile = "/run/systemd/reload-list"; @@ -116,6 +117,8 @@ while (my ($unit, $state) = each %{$activePrev}) { } elsif ($unit =~ /\.target$/) { + my $unitInfo = parseUnit($newUnitFile); + # Cause all active target units to be restarted below. # This should start most changed units we stop here as # well as any new dependencies (including new mounts and @@ -123,11 +126,25 @@ while (my ($unit, $state) = each %{$activePrev}) { # active after the system has resumed, which probably # should not be the case. Just ignore it. if ($unit ne "suspend.target" && $unit ne "hibernate.target") { - my $unitInfo = parseUnit($newUnitFile); - unless (boolIsTrue($unitInfo->{'RefuseManualStart'} // "false")) { - write_file($restartListFile, { append => 1 }, "$unit\n"); + unless (boolIsTrue($unitInfo->{'RefuseManualStart'} // "no")) { + write_file($startListFile, { append => 1 }, "$unit\n"); } } + + # Stop targets that have X-StopOnReconfiguration set. + # This is necessary to respect dependency orderings + # involving targets: if unit X starts after target Y and + # target Y starts after unit Z, then if X and Z have both + # changed, then X should be restarted after Z. However, + # if target Y is in the "active" state, X and Z will be + # restarted at the same time because X's dependency on Y + # is already satisfied. Thus, we need to stop Y first. + # Stopping a target generally has no effect on other units + # (unless there is a PartOf dependency), so this is just a + # bookkeeping thing to get systemd to do the right thing. + if (boolIsTrue($unitInfo->{'X-StopOnReconfiguration'} // "no")) { + push @unitsToStop, $unit; + } } elsif (abs_path($prevUnitFile) ne abs_path($newUnitFile)) { @@ -140,10 +157,7 @@ while (my ($unit, $state) = each %{$activePrev}) { # FIXME: do something? } else { my $unitInfo = parseUnit($newUnitFile); - if (!boolIsTrue($unitInfo->{'X-RestartIfChanged'} // "true") - || $unit eq "systemd-user-sessions.service" - || $unit eq "systemd-journald.service") - { + if (!boolIsTrue($unitInfo->{'X-RestartIfChanged'} // "yes")) { push @unitsToSkip, $unit; } else { # If this unit is socket-activated, then stop the @@ -158,21 +172,31 @@ while (my ($unit, $state) = each %{$activePrev}) { foreach my $socket (@sockets) { if (defined $activePrev->{$socket}) { push @unitsToStop, $socket; - write_file($restartListFile, { append => 1 }, "$socket\n"); + write_file($startListFile, { append => 1 }, "$socket\n"); $socketActivated = 1; } } } - # Otherwise, record that this unit needs to be - # started below. We write this to a file to - # ensure that the service gets restarted if we're - # interrupted. - if (!$socketActivated) { - write_file($restartListFile, { append => 1 }, "$unit\n"); - } + if (!boolIsTrue($unitInfo->{'X-StopIfChanged'} // "yes")) { - push @unitsToStop, $unit; + # This unit should be restarted instead of + # stopped and started. + write_file($restartListFile, { append => 1 }, "$unit\n"); + + } else { + + # If the unit is not socket-activated, record + # that this unit needs to be started below. + # We write this to a file to ensure that the + # service gets restarted if we're interrupted. + if (!$socketActivated) { + write_file($startListFile, { append => 1 }, "$unit\n"); + } + + push @unitsToStop, $unit; + + } } } } @@ -216,7 +240,7 @@ foreach my $mountPoint (keys %$prevFss) { push @unitsToStop, $unit; } elsif ($prev->{fsType} ne $new->{fsType} || $prev->{device} ne $new->{device}) { # Filesystem type or device changed, so unmount and mount it. - write_file($restartListFile, { append => 1 }, "$unit\n"); + write_file($startListFile, { append => 1 }, "$unit\n"); push @unitsToStop, $unit; } elsif ($prev->{options} ne $new->{options}) { # Mount options changes, so remount it. @@ -266,16 +290,25 @@ system("@systemd@/bin/systemctl", "reset-failed"); # Make systemd reload its units. system("@systemd@/bin/systemctl", "daemon-reload") == 0 or $res = 3; +# Restart changed services (those that have to be restarted rather +# than stopped and started). +my @restart = unique(split('\n', read_file($restartListFile, err_mode => 'quiet') // "")); +if (scalar @restart > 0) { + print STDERR "restarting the following units: ", join(", ", sort(@restart)), "\n"; + system("@systemd@/bin/systemctl", "restart", "--", @restart) == 0 or $res = 4; + unlink($restartListFile); +} + # Start all active targets, as well as changed units we stopped above. # The latter is necessary because some may not be dependencies of the # targets (i.e., they were manually started). FIXME: detect units # that are symlinks to other units. We shouldn't start both at the # same time because we'll get a "Failed to add path to set" error from # systemd. -my @start = unique("default.target", split('\n', read_file($restartListFile, err_mode => 'quiet') // "")); +my @start = unique("default.target", split('\n', read_file($startListFile, err_mode => 'quiet') // "")); print STDERR "starting the following units: ", join(", ", sort(@start)), "\n"; system("@systemd@/bin/systemctl", "start", "--", @start) == 0 or $res = 4; -unlink($restartListFile); +unlink($startListFile); # Reload units that need it. This includes remounting changed mount # units. diff --git a/modules/system/boot/kernel.nix b/modules/system/boot/kernel.nix index 3637378d4d1e..6cf9311e471e 100644 --- a/modules/system/boot/kernel.nix +++ b/modules/system/boot/kernel.nix @@ -50,7 +50,7 @@ in boot.extraKernelParams = mkOption { default = [ ]; - example = [ "debugtrace" ]; + example = [ "boot.trace" ]; description = "Additional user-defined kernel parameters."; }; diff --git a/modules/system/boot/loader/grub/memtest.nix b/modules/system/boot/loader/grub/memtest.nix index 4bd4b69101c9..b18ff0512f2a 100644 --- a/modules/system/boot/loader/grub/memtest.nix +++ b/modules/system/boot/loader/grub/memtest.nix @@ -5,7 +5,7 @@ with pkgs.lib; let isEnabled = config.boot.loader.grub.memtest86; - memtest86 = pkgs.memtest86; + memtest86 = pkgs.memtest86plus; in { options = { diff --git a/modules/system/boot/stage-1-init.sh b/modules/system/boot/stage-1-init.sh index 778e36cfd36e..9bcbe291aad5 100644 --- a/modules/system/boot/stage-1-init.sh +++ b/modules/system/boot/stage-1-init.sh @@ -1,6 +1,7 @@ #! @shell@ targetRoot=/mnt-root +console=tty1 export LD_LIBRARY_PATH=@extraUtils@/lib export PATH=@extraUtils@/bin:@extraUtils@/sbin @@ -17,37 +18,31 @@ An error occured in stage 1 of the boot process, which must mount the root filesystem on \`$targetRoot' and then start stage 2. Press one of the following keys: - i) to launch an interactive shell; +EOF + if [ -n "$allowShell" ]; then cat </dev/$console 2>/dev/$console" ;; - i) - echo "Starting interactive shell..." - setsid @shell@ -c "@shell@ < /dev/$console >/dev/$console 2>/dev/$console" || fail - ;; - *) - echo "Continuing...";; - esac + if [ -n "$allowShell" -a "$reply" = f ]; then + exec setsid @shell@ -c "@shell@ < /dev/$console >/dev/$console 2>/dev/$console" + elif [ -n "$allowShell" -a "$reply" = i ]; then + echo "Starting interactive shell..." + setsid @shell@ -c "@shell@ < /dev/$console >/dev/$console 2>/dev/$console" || fail + elif [ "$reply" = r ]; then + echo "Rebooting..." + reboot -f + else + echo "Continuing..." + fi } trap 'fail' 0 @@ -76,24 +71,36 @@ mount -t securityfs none /sys/kernel/security export stage2Init=/init for o in $(cat /proc/cmdline); do case $o in + console=*) + set -- $(IFS==; echo $o) + params=$2 + set -- $(IFS=,; echo $params) + console=$1 + ;; init=*) set -- $(IFS==; echo $o) stage2Init=$2 ;; - debugtrace) + boot.trace|debugtrace) # Show each command. set -x ;; - debug1) # stop right away + boot.shell_on_fail) + allowShell=1 + ;; + boot.debug1|debug1) # stop right away + allowShell=1 fail ;; - debug1devices) # stop after loading modules and creating device nodes + boot.debug1devices) # stop after loading modules and creating device nodes + allowShell=1 debug1devices=1 ;; - debug1mounts) # stop after mounting file systems + boot.debug1mounts) # stop after mounting file systems + allowShell=1 debug1mounts=1 ;; - stage1panic=1) + boot.panic_on_fail|stage1panic=1) panicOnFail=1 ;; root=*) @@ -180,7 +187,7 @@ onACPower() { checkFS() { local device="$1" local fsType="$2" - + # Only check block devices. if [ ! -b "$device" ]; then return 0; fi @@ -219,7 +226,7 @@ checkFS() { if test $(($fsckResult | 2)) = $fsckResult; then echo "fsck finished, rebooting..." sleep 3 - reboot + reboot -f fi if test $(($fsckResult | 4)) = $fsckResult; then diff --git a/modules/system/boot/stage-1.nix b/modules/system/boot/stage-1.nix index ac8a3ccc2386..b131867966ab 100644 --- a/modules/system/boot/stage-1.nix +++ b/modules/system/boot/stage-1.nix @@ -177,6 +177,7 @@ let cp -v ${pkgs.lvm2}/sbin/dmsetup $out/bin/dmsetup cp -v ${pkgs.lvm2}/sbin/lvm $out/bin/lvm cp -v ${pkgs.lvm2}/lib/libdevmapper.so.*.* $out/lib + cp -v ${pkgs.systemd}/lib/libsystemd-daemon.so.* $out/lib # Add RAID mdadm tool. cp -v ${pkgs.mdadm}/sbin/mdadm $out/bin/mdadm diff --git a/modules/system/boot/stage-2-init.sh b/modules/system/boot/stage-2-init.sh index 67a4e0ed16d0..5447ce0c502e 100644 --- a/modules/system/boot/stage-2-init.sh +++ b/modules/system/boot/stage-2-init.sh @@ -62,20 +62,12 @@ ln -s /proc/mounts /etc/mtab # Process the kernel command line. -debug2= for o in $(cat /proc/cmdline); do case $o in - debugtrace) + boot.debugtrace) # Show each command. set -x ;; - debug2) - debug2=1 - ;; - S|s|single) - # !!! argh, can't pass a startup event to Upstart yet. - exec @shell@ - ;; resume=*) set -- $(IFS==; echo $o) resumeDevice=$2 @@ -168,26 +160,6 @@ ln -sfn /run/booted-system /nix/var/nix/gcroots/booted-system @shell@ @postBootCommands@ -# For debugging Upstart. -if [ -n "$debug2" ]; then - # Get the console from the kernel cmdline - console=tty1 - for o in $(cat /proc/cmdline); do - case $o in - console=*) - set -- $(IFS==; echo $o) - params=$2 - set -- $(IFS=,; echo $params) - console=$1 - ;; - esac - done - - echo "Debug shell called from @out@" - setsid @shellDebug@ < /dev/$console >/dev/$console 2>/dev/$console -fi - - # Start systemd. echo "starting systemd..." PATH=/run/current-system/systemd/lib/systemd \ diff --git a/modules/system/boot/systemd-unit-options.nix b/modules/system/boot/systemd-unit-options.nix index ad9b5da23164..1f8097ada1c3 100644 --- a/modules/system/boot/systemd-unit-options.nix +++ b/modules/system/boot/systemd-unit-options.nix @@ -183,6 +183,21 @@ rec { ''; }; + stopIfChanged = mkOption { + type = types.bool; + default = true; + description = '' + If set, a changed unit is restarted by calling + systemctl stop in the old configuration, + then systemctl start in the new one. + Otherwise, it is restarted in a single step using + systemctl restart in the new configuration. + The latter is less correct because it runs the + ExecStop commands from the new + configuration. + ''; + }; + }; diff --git a/modules/system/boot/systemd.nix b/modules/system/boot/systemd.nix index f4d0655118ea..83911d179fde 100644 --- a/modules/system/boot/systemd.nix +++ b/modules/system/boot/systemd.nix @@ -23,12 +23,11 @@ let upstreamUnits = [ # Targets. "basic.target" - #"sysinit.target" + "sysinit.target" "sockets.target" "graphical.target" "multi-user.target" "getty.target" - "rescue.target" "network.target" "nss-lookup.target" "nss-user-lookup.target" @@ -37,6 +36,12 @@ let #"cryptsetup.target" "sigpwr.target" + # Rescue/emergency. + "rescue.target" + "rescue.service" + "emergency.target" + "emergency.service" + # Udev. "systemd-udevd-control.socket" "systemd-udevd-kernel.socket" @@ -139,33 +144,6 @@ let "shutdown.target.wants" ]; - rescueService = - '' - [Unit] - Description=Rescue Shell - DefaultDependencies=no - Conflicts=shutdown.target - After=sysinit.target - Before=shutdown.target - - [Service] - Environment=HOME=/root - WorkingDirectory=/root - ExecStartPre=-${pkgs.coreutils}/bin/echo 'Welcome to rescue mode. Use "systemctl default" or ^D to enter default mode.' - #ExecStart=-/sbin/sulogin - ExecStart=-${pkgs.bashInteractive}/bin/bash --login - ExecStopPost=-${systemd}/bin/systemctl --fail --no-block default - Type=idle - StandardInput=tty-force - StandardOutput=inherit - StandardError=inherit - KillMode=process - - # Bash ignores SIGTERM, so we send SIGHUP instead, to ensure that bash - # terminates cleanly. - KillSignal=SIGHUP - ''; - makeJobScript = name: text: let x = pkgs.writeTextFile { name = "unit-script"; executable = true; destination = "/bin/${name}"; inherit text; }; in "${x}/bin/${name}"; @@ -246,6 +224,7 @@ let ${let env = cfg.globalEnvironment // def.environment; in concatMapStrings (n: "Environment=${n}=${getAttr n env}\n") (attrNames env)} ${optionalString (!def.restartIfChanged) "X-RestartIfChanged=false"} + ${optionalString (!def.stopIfChanged) "X-StopIfChanged=false"} ${optionalString (def.preStart != "") '' ExecStartPre=${makeJobScript "${name}-pre-start" '' @@ -346,6 +325,8 @@ let ln -s ${cfg.defaultUnit} $out/default.target + ln -s rescue.target $out/kbrequest.target + #ln -s ../getty@tty1.service $out/multi-user.target.wants/ ln -s ../local-fs.target ../remote-fs.target ../network.target ../nss-lookup.target \ ../nss-user-lookup.target ../swap.target $out/multi-user.target.wants/ @@ -523,20 +504,8 @@ in { description = "Security Keys"; }; - # This is like the upstream sysinit.target, except that it doesn't - # depend on local-fs.target and swap.target. If services need to - # be started after some filesystem (local or otherwise) has been - # mounted, they should use the RequiresMountsFor option. - boot.systemd.targets.sysinit = - { description = "System Initialization"; - after = [ "emergency.service" "emergency.target" ]; - unitConfig.Conflicts = "emergency.service emergency.target"; - unitConfig.RefuseManualStart = true; - }; - boot.systemd.units = - { "rescue.service".text = rescueService; } - // mapAttrs' (n: v: nameValuePair "${n}.target" (targetToUnit n v)) cfg.targets + mapAttrs' (n: v: nameValuePair "${n}.target" (targetToUnit n v)) cfg.targets // mapAttrs' (n: v: nameValuePair "${n}.service" (serviceToUnit n v)) cfg.services // mapAttrs' (n: v: nameValuePair "${n}.socket" (socketToUnit n v)) cfg.sockets // listToAttrs (map diff --git a/modules/tasks/network-interfaces.nix b/modules/tasks/network-interfaces.nix index 64cb4a6749eb..a002e2b68b2d 100644 --- a/modules/tasks/network-interfaces.nix +++ b/modules/tasks/network-interfaces.nix @@ -135,6 +135,16 @@ in ''; }; + networking.defaultGatewayWindowSize = mkOption { + default = null; + example = 524288; + type = types.nullOr types.int; + description = '' + The window size of the default gateway. It limits maximal data bursts that TCP peers + are allowed to send to us. + ''; + }; + networking.nameservers = mkOption { default = []; example = ["130.161.158.4" "130.161.33.17"]; @@ -245,6 +255,7 @@ in boot.systemd.targets."network-interfaces" = { description = "All Network Interfaces"; wantedBy = [ "network.target" ]; + unitConfig.X-StopOnReconfiguration = true; }; boot.systemd.services = @@ -282,7 +293,9 @@ in # Set the default gateway. ${optionalString (cfg.defaultGateway != "") '' # FIXME: get rid of "|| true" (necessary to make it idempotent). - ip route add default via "${cfg.defaultGateway}" || true + ip route add default via "${cfg.defaultGateway}" ${ + optionalString (cfg.defaultGatewayWindowSize != null) + "window ${cfg.defaultGatewayWindowSize}"} || true ''} # Turn on forwarding if any interface has enabled proxy_arp. @@ -335,6 +348,9 @@ in echo "configuring interface..." ip -4 addr flush dev "${i.name}" ip -4 addr add "${i.ipAddress}/${mask}" dev "${i.name}" + # Ensure that the default gateway remains set. + # (Flushing this interface may have removed it.) + ${config.system.build.systemd}/bin/systemctl try-restart --no-block network-setup.service else echo "skipping configuring interface" fi diff --git a/modules/testing/test-instrumentation.nix b/modules/testing/test-instrumentation.nix index 0ee3ad65a3d2..108dcb0ab6db 100644 --- a/modules/testing/test-instrumentation.nix +++ b/modules/testing/test-instrumentation.nix @@ -65,7 +65,7 @@ let kernel = config.boot.kernelPackages.kernel; in # Panic if an error occurs in stage 1 (rather than waiting for # user intervention). boot.kernelParams = - [ "console=tty1" "console=ttyS0" "panic=1" "stage1panic=1" ]; + [ "console=tty1" "console=ttyS0" "panic=1" "boot.panic_on_fail" ]; # `xwininfo' is used by the test driver to query open windows. environment.systemPackages = [ pkgs.xorg.xwininfo ];