summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/configuration.xml5
-rw-r--r--nixos/doc/manual/installation.xml69
-rw-r--r--nixos/doc/manual/manual.xml7
-rw-r--r--nixos/doc/manual/options-to-docbook.xsl7
-rw-r--r--nixos/doc/manual/release-notes.xml57
-rw-r--r--nixos/lib/test-driver/test-driver.pl7
-rw-r--r--nixos/lib/testing.nix54
-rwxr-xr-xnixos/maintainers/scripts/ec2/create-ebs-amis.py2
-rw-r--r--nixos/modules/config/i18n.nix6
-rw-r--r--nixos/modules/config/pulseaudio.nix2
-rw-r--r--nixos/modules/config/users-groups.nix422
-rw-r--r--nixos/modules/hardware/opengl.nix (renamed from nixos/modules/services/x11/mesa.nix)38
-rw-r--r--nixos/modules/hardware/video/bumblebee.nix41
-rw-r--r--nixos/modules/installer/cd-dvd/installation-cd-base.nix4
-rw-r--r--nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix3
-rw-r--r--nixos/modules/installer/tools/nixos-generate-config.pl14
-rw-r--r--nixos/modules/installer/tools/nixos-option.sh2
-rw-r--r--nixos/modules/installer/virtualbox-demo.nix2
-rw-r--r--nixos/modules/misc/ids.nix19
-rw-r--r--nixos/modules/module-list.nix15
-rw-r--r--nixos/modules/profiles/demo.nix2
-rw-r--r--nixos/modules/programs/environment.nix2
-rw-r--r--nixos/modules/programs/shadow.nix5
-rw-r--r--nixos/modules/programs/shell.nix52
-rw-r--r--nixos/modules/rename.nix23
-rw-r--r--nixos/modules/security/ca.nix5
-rw-r--r--nixos/modules/services/backup/tarsnap.nix203
-rw-r--r--nixos/modules/services/continuous-integration/jenkins/default.nix118
-rw-r--r--nixos/modules/services/continuous-integration/jenkins/slave.nix67
-rw-r--r--nixos/modules/services/databases/couchdb.nix173
-rw-r--r--nixos/modules/services/databases/firebird.nix1
-rw-r--r--nixos/modules/services/databases/mongodb.nix1
-rw-r--r--nixos/modules/services/databases/mysql.nix23
-rw-r--r--nixos/modules/services/databases/mysql55.nix248
-rw-r--r--nixos/modules/services/databases/postgresql.nix2
-rw-r--r--nixos/modules/services/databases/redis.nix1
-rw-r--r--nixos/modules/services/logging/logcheck.nix7
-rw-r--r--nixos/modules/services/mail/opensmtpd.nix5
-rw-r--r--nixos/modules/services/misc/cgminer.nix1
-rw-r--r--nixos/modules/services/misc/dictd.nix2
-rw-r--r--nixos/modules/services/misc/nix-daemon.nix6
-rw-r--r--nixos/modules/services/misc/nix-ssh-serve.nix45
-rw-r--r--nixos/modules/services/misc/nixos-manual.nix2
-rw-r--r--nixos/modules/services/monitoring/apcupsd.nix2
-rw-r--r--nixos/modules/services/monitoring/munin.nix2
-rw-r--r--nixos/modules/services/networking/dhcpcd.nix1
-rw-r--r--nixos/modules/services/networking/firewall.nix54
-rw-r--r--nixos/modules/services/networking/git-daemon.nix2
-rw-r--r--nixos/modules/services/networking/kippo.nix3
-rw-r--r--nixos/modules/services/networking/networkmanager.nix13
-rw-r--r--nixos/modules/services/networking/ntpd.nix5
-rw-r--r--nixos/modules/services/networking/searx.nix75
-rw-r--r--nixos/modules/services/networking/vsftpd.nix8
-rw-r--r--nixos/modules/services/printing/cupsd.nix4
-rw-r--r--nixos/modules/services/search/solr.nix114
-rw-r--r--nixos/modules/services/system/nscd.nix11
-rw-r--r--nixos/modules/services/ttys/agetty.nix105
-rw-r--r--nixos/modules/services/ttys/kmscon.nix13
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/default.nix15
-rw-r--r--nixos/modules/services/web-servers/apache-httpd/mediawiki.nix20
-rw-r--r--nixos/modules/services/web-servers/nginx/default.nix15
-rw-r--r--nixos/modules/services/web-servers/phpfpm.nix77
-rw-r--r--nixos/modules/services/web-servers/winstone.nix129
-rw-r--r--nixos/modules/services/x11/desktop-managers/default.nix8
-rw-r--r--nixos/modules/services/x11/desktop-managers/gnome3.nix68
-rw-r--r--nixos/modules/services/x11/desktop-managers/xbmc.nix31
-rw-r--r--nixos/modules/services/x11/desktop-managers/xfce.nix2
-rw-r--r--nixos/modules/services/x11/display-managers/lightdm.nix4
-rw-r--r--nixos/modules/services/x11/hardware/synaptics.nix27
-rw-r--r--nixos/modules/services/x11/redshift.nix27
-rw-r--r--nixos/modules/services/x11/terminal-server.nix2
-rw-r--r--nixos/modules/services/x11/window-managers/default.nix1
-rw-r--r--nixos/modules/services/x11/window-managers/xbmc.nix31
-rw-r--r--nixos/modules/services/x11/xserver.nix8
-rw-r--r--nixos/modules/system/activation/switch-to-configuration.pl26
-rw-r--r--nixos/modules/system/boot/kernel.nix2
-rw-r--r--nixos/modules/system/boot/loader/grub/memtest.nix72
-rw-r--r--nixos/modules/system/boot/loader/gummiboot/gummiboot-builder.py14
-rw-r--r--nixos/modules/system/boot/luksroot.nix221
-rw-r--r--nixos/modules/system/boot/modprobe.nix24
-rw-r--r--nixos/modules/system/boot/pbkdf2-sha512.c38
-rw-r--r--nixos/modules/system/boot/stage-1-init.sh6
-rw-r--r--nixos/modules/system/boot/stage-2-init.sh10
-rw-r--r--nixos/modules/system/boot/systemd-unit-options.nix7
-rw-r--r--nixos/modules/system/boot/systemd.nix110
-rw-r--r--nixos/modules/tasks/network-interfaces.nix53
-rw-r--r--nixos/modules/virtualisation/amazon-image.nix2
-rw-r--r--nixos/modules/virtualisation/containers.nix4
-rw-r--r--nixos/modules/virtualisation/google-compute-image.nix2
-rw-r--r--nixos/modules/virtualisation/qemu-vm.nix3
-rw-r--r--nixos/modules/virtualisation/virtualbox-guest.nix2
-rw-r--r--nixos/modules/virtualisation/virtualbox-image.nix2
-rw-r--r--nixos/release-combined.nix3
-rw-r--r--nixos/release.nix3
-rw-r--r--nixos/tests/common/user-account.nix1
-rw-r--r--nixos/tests/default.nix2
-rw-r--r--nixos/tests/gnome3.nix31
-rw-r--r--nixos/tests/installer.nix6
-rw-r--r--nixos/tests/jenkins.nix35
-rw-r--r--nixos/tests/mysql-replication.nix31
-rw-r--r--nixos/tests/mysql.nix5
-rw-r--r--nixos/tests/quake3.nix4
-rw-r--r--nixos/tests/subversion.nix2
103 files changed, 2401 insertions, 967 deletions
diff --git a/nixos/doc/manual/configuration.xml b/nixos/doc/manual/configuration.xml
index e6d7dee251af..307719d2cd2e 100644
--- a/nixos/doc/manual/configuration.xml
+++ b/nixos/doc/manual/configuration.xml
@@ -1025,7 +1025,6 @@ users.extraUsers.alice =
     home = "/home/alice";
     description = "Alice Foobar";
     extraGroups = [ "wheel" ];
-    isSystemUser = false;
     useDefaultShell = true;
     openssh.authorizedKeys.keys = [ "ssh-dss AAAAB3Nza... alice@foobar" ];
   };
@@ -1184,7 +1183,7 @@ driver from a set of X.org drivers (such as <literal>vesa</literal>
 and <literal>intel</literal>).  You can also specify a driver
 manually, e.g.
 <programlisting>
-services.xserver.videoDrivers = [ "r128" ];
+hardware.opengl.videoDrivers = [ "r128" ];
 </programlisting>
 to enable X.org’s <literal>xf86-video-r128</literal> driver.</para>
 
@@ -1227,7 +1226,7 @@ $ systemctl start display-manager.service
 has better 3D performance than the X.org drivers.  It is not enabled
 by default because it’s not free software.  You can enable it as follows:
 <programlisting>
-services.xserver.videoDrivers = [ "nvidia" ];
+hardware.opengl.videoDrivers = [ "nvidia" ];
 </programlisting>
 You may need to reboot after enabling this driver to prevent a clash
 with other kernel modules.</para>
diff --git a/nixos/doc/manual/installation.xml b/nixos/doc/manual/installation.xml
index 70001577692e..f6b477ed69bc 100644
--- a/nixos/doc/manual/installation.xml
+++ b/nixos/doc/manual/installation.xml
@@ -1,5 +1,6 @@
 <chapter xmlns="http://docbook.org/ns/docbook"
-         xmlns:xlink="http://www.w3.org/1999/xlink">
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xml:id="installing-nixos">
 
 <title>Installing NixOS</title>
 
@@ -295,8 +296,74 @@ $ reboot</screen>
 }</screen>
 </example>
 
+<section xml:id="sec-uefi-installation">
+
+<title>UEFI Installation</title>
+
+<para>NixOS can also be installed on UEFI systems.  The procedure
+is by and large the same as a BIOS installation, with the following
+changes:
+
+<itemizedlist>
+  <listitem>
+    <para>You should boot the live CD in UEFI mode (consult your
+    specific hardware's documentation for instructions).</para>
+  </listitem>
+  <listitem>
+    <para>Instead of <command>fdisk</command>, you should use
+    <command>gdisk</command> to partition your disks. You will need to
+    have a separate partition for <filename>/boot</filename> with
+    partition code EF00, and it should be formatted as a
+    <literal>vfat</literal> filesystem.</para>
+  </listitem>
+  <listitem>
+    <para>You must set <option>boot.loader.gummiboot.enable</option> to
+    <literal>true</literal>, and <option>boot.loader.grub.enable</option>
+    to <literal>false</literal>. <command>nixos-generate-config</command>
+    should do this automatically for new configurations when booted in
+    UEFI mode.</para>
+  </listitem>
+  <listitem>
+    <para>You may want to look at the options starting with
+    <option>boot.loader.efi</option> and <option>boot.loader.gummiboot</option>
+    as well.</para>
+  </listitem>
+  <listitem>
+    <para>To see console messages during early boot, add <literal>"fbcon"</literal>
+    to your <option>boot.initrd.kernelModules</option>.</para>
+  </listitem>
+</itemizedlist>
+</para>
+
 </section>
 
+<section>
+
+<title>Booting from a USB stick</title>
+
+<para>For systems withoua CD drive, the NixOS livecd can be booted from
+a usb stick. For non-UEFI installations,
+<link xlink:href="http://unetbootin.sourceforge.net/">unetbootin</link>
+will work. For UEFI installations, you should mount the ISO, copy its contents
+verbatim to your drive, then either:
+
+<itemizedlist>
+  <listitem>
+    <para>Change the label of the disk partition to the label of the ISO
+    (visible with the blkid command), or</para>
+  </listitem>
+  <listitem>
+    <para>Edit <filename>loader/entries/nixos-livecd.conf</filename> on the drive
+    and change the <literal>root=</literal> field in the <literal>options</literal>
+    line to point to your drive (see the documentation on <literal>root=</literal>
+    in <link xlink:href="https://www.kernel.org/doc/Documentation/kernel-parameters.txt">
+    the kernel documentation</link> for more details).</para>
+  </listitem>
+</itemizedlist>
+</para>
+</section>
+
+</section>
 
 
 <!--===============================================================-->
diff --git a/nixos/doc/manual/manual.xml b/nixos/doc/manual/manual.xml
index 6e13281cbd94..f9775f4f0170 100644
--- a/nixos/doc/manual/manual.xml
+++ b/nixos/doc/manual/manual.xml
@@ -55,9 +55,12 @@
   <!-- <xi:include href="userconfiguration.xml" /> -->
   <xi:include href="troubleshooting.xml" />
   <xi:include href="development.xml" />
-  <chapter xml:id="ch-options">
+
+  <xi:include href="release-notes.xml" />
+
+  <appendix xml:id="ch-options">
     <title>List of options</title>
     <xi:include href="options-db.xml" />
-  </chapter>
+  </appendix>
 
 </book>
diff --git a/nixos/doc/manual/options-to-docbook.xsl b/nixos/doc/manual/options-to-docbook.xsl
index 6d11ad7a6c4a..9647aae0f2fb 100644
--- a/nixos/doc/manual/options-to-docbook.xsl
+++ b/nixos/doc/manual/options-to-docbook.xsl
@@ -18,15 +18,14 @@
       <variablelist>
 
         <xsl:for-each select="attrs">
-
           <varlistentry>
-             <term>
-               <option>
+            <term xml:id="{generate-id(attr[@name = 'name']/string/@value)}" xlink:href="#{generate-id(attr[@name = 'name']/string/@value)}">
+              <option>
                  <xsl:for-each select="attr[@name = 'name']/string">
                    <xsl:value-of select="@value" />
                    <xsl:if test="position() != last()">.</xsl:if>
                  </xsl:for-each>
-               </option>
+              </option>
              </term>
 
              <listitem>
diff --git a/nixos/doc/manual/release-notes.xml b/nixos/doc/manual/release-notes.xml
new file mode 100644
index 000000000000..8899cbb21498
--- /dev/null
+++ b/nixos/doc/manual/release-notes.xml
@@ -0,0 +1,57 @@
+<appendix xmlns="http://docbook.org/ns/docbook"
+          xml:id="ch-release-notes">
+
+<title>Release notes</title>
+
+<!--==================================================================-->
+
+<section xml:id="sec-release-14.02">
+
+<title>Release 14.02 (“Baboon”, 2014/02/??)</title>
+
+<para>This is the second stable release branch of NixOS.  The main
+enhancements are the following:
+
+<itemizedlist>
+
+  <listitem><para>Installation on UEFI systems is now supported.  See
+  <xref linkend="sec-uefi-installation"/> for
+  details.</para></listitem>
+
+  <listitem><para>NixOS is now based on Glibc 2.18 and GCC
+  4.8.</para></listitem>
+
+  <listitem><para>The mysql55 service has been merged into the
+  mysql service, which no longer sets a default for the 'package
+  option.</para></listitem>
+
+</itemizedlist>
+
+</para>
+
+<para>When upgrading from a previous release, please be aware of the
+following incompatible changes:
+
+<itemizedlist>
+
+  <listitem><para>The option
+  <option>boot.loader.grub.memtest86</option> has been renamed to
+  <option>boot.loader.grub.memtest86.enable</option>.</para></listitem>
+
+</itemizedlist>
+
+</para>
+
+</section>
+
+<!--==================================================================-->
+
+<section xml:id="sec-release-13.10">
+
+<title>Release 13.10 (“Aardvark”, 2013/10/31)</title>
+
+<para>This is the first stable release branch of NixOS.</para>
+
+</section>
+
+</appendix>
diff --git a/nixos/lib/test-driver/test-driver.pl b/nixos/lib/test-driver/test-driver.pl
index c6a707cdf6b9..31f3281cefe0 100644
--- a/nixos/lib/test-driver/test-driver.pl
+++ b/nixos/lib/test-driver/test-driver.pl
@@ -144,6 +144,13 @@ sub runTests {
         }
     });
 
+    $log->nest("syncing", sub {
+        foreach my $vm (values %vms) {
+            next unless $vm->isUp();
+            $vm->execute("sync /tmp/xchg");
+        }
+    });
+
     if ($nrTests != 0) {
         $log->log("$nrSucceeded out of $nrTests tests succeeded",
             ($nrSucceeded < $nrTests ? { error => 1 } : { }));
diff --git a/nixos/lib/testing.nix b/nixos/lib/testing.nix
index 3407229e921a..d5338bc04cac 100644
--- a/nixos/lib/testing.nix
+++ b/nixos/lib/testing.nix
@@ -67,62 +67,16 @@ rec {
     };
 
 
-  # Generate a coverage report from the coverage data produced by
-  # runTests.
-  makeReport = x: runCommand "report" { buildInputs = [rsync]; }
-    ''
-      mkdir -p $TMPDIR/gcov/
-
-      for d in ${x}/coverage-data/*; do
-          echo "doing $d"
-          [ -n "$(ls -A "$d")" ] || continue
-
-          for i in $(cd $d/nix/store && ls); do
-              if ! test -e $TMPDIR/gcov/nix/store/$i; then
-                  echo "copying $i"
-                  mkdir -p $TMPDIR/gcov/$(echo $i | cut -c34-)
-                  rsync -rv /nix/store/$i/.build/* $TMPDIR/gcov/
-              fi
-          done
-
-          chmod -R u+w $TMPDIR/gcov
-
-          find $TMPDIR/gcov -name "*.gcda" -exec rm {} \;
-
-          for i in $(cd $d/nix/store && ls); do
-              rsync -rv $d/nix/store/$i/.build/* $TMPDIR/gcov/
-          done
-
-          find $TMPDIR/gcov -name "*.gcda" -exec chmod 644 {} \;
-
-          echo "producing info..."
-          ${pkgs.lcov}/bin/geninfo --ignore-errors source,gcov $TMPDIR/gcov --output-file $TMPDIR/app.info
-          cat $TMPDIR/app.info >> $TMPDIR/full.info
-      done
-
-      echo "making report..."
-      mkdir -p $out/coverage
-      ${pkgs.lcov}/bin/genhtml --show-details $TMPDIR/full.info -o $out/coverage
-      cp $TMPDIR/full.info $out/coverage/
-
-      mkdir -p $out/nix-support
-      cat ${x}/nix-support/hydra-build-products >> $out/nix-support/hydra-build-products
-      echo "report coverage $out/coverage" >> $out/nix-support/hydra-build-products
-      [ ! -e ${x}/nix-support/failed ] || touch $out/nix-support/failed
-    ''; # */
-
-
   makeTest = testFun: complete (call testFun);
   makeTests = testsFun: lib.mapAttrs (name: complete) (call testsFun);
 
   apply = makeTest; # compatibility
   call = f: f { inherit pkgs system; };
 
-  complete = t: t // rec {
+  complete = { testScript, ... } @ t: t // rec {
+
     nodes = buildVirtualNetwork (
-      if t ? nodes then t.nodes else
-      if t ? machine then { machine = t.machine; }
-      else { } );
+      t.nodes or (if t ? machine then { machine = t.machine; } else { }));
 
     testScript =
       # Call the test script with the computed nodes.
@@ -162,7 +116,7 @@ rec {
 
     test = runTests driver;
 
-    report = makeReport test;
+    report = releaseTools.gcovReport { coverageRuns = [ test ]; };
   };
 
 
diff --git a/nixos/maintainers/scripts/ec2/create-ebs-amis.py b/nixos/maintainers/scripts/ec2/create-ebs-amis.py
index 541eadd7b8c9..eab111a2665b 100755
--- a/nixos/maintainers/scripts/ec2/create-ebs-amis.py
+++ b/nixos/maintainers/scripts/ec2/create-ebs-amis.py
@@ -203,7 +203,7 @@ f = open("{0}.{1}.ami-id".format(args.region, image_type), "w")
 f.write("{0}".format(ami_id))
 f.close()
 
-for dest in [ 'us-east-1', 'us-west-1', 'us-west-2', 'eu-west-1']:
+for dest in [ 'us-east-1', 'us-west-1', 'us-west-2', 'eu-west-1', 'ap-southeast-1', 'ap-southeast-2', 'ap-northeast-1', 'sa-east-1']:
     if args.region != dest:
         print >> sys.stderr, "copying image from region {0} to {1}".format(args.region, dest)
         conn = boto.ec2.connect_to_region(dest)
diff --git a/nixos/modules/config/i18n.nix b/nixos/modules/config/i18n.nix
index 56d541cb9b3b..310739aa1707 100644
--- a/nixos/modules/config/i18n.nix
+++ b/nixos/modules/config/i18n.nix
@@ -53,7 +53,11 @@ in
       };
 
       consoleKeyMap = mkOption {
-        type = types.str;
+        type = mkOptionType {
+          name = "string or path";
+          check = t: (isString t || types.path.check t);
+        };
+
         default = "us";
         example = "fr";
         description = ''
diff --git a/nixos/modules/config/pulseaudio.nix b/nixos/modules/config/pulseaudio.nix
index e7cbe7a28f36..db4f1b43a114 100644
--- a/nixos/modules/config/pulseaudio.nix
+++ b/nixos/modules/config/pulseaudio.nix
@@ -77,7 +77,7 @@ in {
       };
 
       package = mkOption {
-        type = types.path;
+        type = types.package;
         default = pulseaudio;
         example = literalExample "pulseaudio.override { jackaudioSupport = true; }";
         description = ''
diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix
index 714de646eb7a..2d9b941a2cae 100644
--- a/nixos/modules/config/users-groups.nix
+++ b/nixos/modules/config/users-groups.nix
@@ -5,7 +5,25 @@ with pkgs.lib;
 let
 
   ids = config.ids;
-  users = config.users;
+  cfg = config.users;
+
+  passwordDescription = ''
+    The options <literal>hashedPassword</literal>,
+    <literal>password</literal> and <literal>passwordFile</literal>
+    controls what password is set for the user.
+    <literal>hashedPassword</literal> overrides both
+    <literal>password</literal> and <literal>passwordFile</literal>.
+    <literal>password</literal> overrides <literal>passwordFile</literal>.
+    If none of these three options are set, no password is assigned to
+    the user, and the user will not be able to do password logins.
+    If the option <literal>users.mutableUsers</literal> is true, the
+    password defined in one of the three options will only be set when
+    the user is created for the first time. After that, you are free to
+    change the password with the ordinary user management commands. If
+    <literal>users.mutableUsers</literal> is false, you cannot change
+    user passwords, they will always be set according to the password
+    options.
+  '';
 
   userOpts = { name, config, ... }: {
 
@@ -28,9 +46,8 @@ let
       };
 
       uid = mkOption {
-        type = with types; uniq (nullOr int);
-        default = null;
-        description = "The account UID. If undefined, NixOS will select a free UID.";
+        type = with types; uniq int;
+        description = "The account UID.";
       };
 
       group = mkOption {
@@ -60,31 +77,54 @@ let
       createHome = mkOption {
         type = types.bool;
         default = false;
-        description = "If true, the home directory will be created automatically.";
+        description = ''
+          If true, the home directory will be created automatically. If this
+          option is true and the home directory already exists but is not
+          owned by the user, directory owner and group will be changed to
+          match the user.
+        '';
       };
 
       useDefaultShell = mkOption {
         type = types.bool;
         default = false;
-        description = "If true, the user's shell will be set to <literal>users.defaultUserShell</literal>.";
+        description = ''
+          If true, the user's shell will be set to
+          <literal>cfg.defaultUserShell</literal>.
+        '';
+      };
+
+      hashedPassword = mkOption {
+        type = with types; uniq (nullOr str);
+        default = null;
+        description = ''
+          Specifies the (hashed) password for the user.
+          ${passwordDescription}
+        '';
       };
 
       password = mkOption {
         type = with types; uniq (nullOr str);
         default = null;
         description = ''
-          The user's password. If undefined, no password is set for
-          the user.  Warning: do not set confidential information here
-          because it is world-readable in the Nix store.  This option
-          should only be used for public accounts such as
-          <literal>guest</literal>.
+          Specifies the (clear text) password for the user.
+          Warning: do not set confidential information here
+          because it is world-readable in the Nix store. This option
+          should only be used for public accounts.
+          ${passwordDescription}
         '';
       };
 
-      isSystemUser = mkOption {
-        type = types.bool;
-        default = true;
-        description = "Indicates if the user is a system user or not.";
+      passwordFile = mkOption {
+        type = with types; uniq (nullOr string);
+        default = null;
+        description = ''
+          The path to a file that contains the user's password. The password
+          file is read on each system activation. The file should contain
+          exactly one line, which should be the password in an encrypted form
+          that is suitable for the <literal>chpasswd -e</literal> command.
+          ${passwordDescription}
+        '';
       };
 
       createUser = mkOption {
@@ -96,19 +136,11 @@ let
           then not modify any of the basic properties for the user account.
         '';
       };
-
-      isAlias = mkOption {
-        type = types.bool;
-        default = false;
-        description = "If true, the UID of this user is not required to be unique and can thus alias another user.";
-      };
-
     };
 
     config = {
       name = mkDefault name;
-      uid = mkDefault (attrByPath [name] null ids.uids);
-      shell = mkIf config.useDefaultShell (mkDefault users.defaultUserShell);
+      shell = mkIf config.useDefaultShell (mkDefault cfg.defaultUserShell);
     };
 
   };
@@ -123,29 +155,114 @@ let
       };
 
       gid = mkOption {
-        type = with types; uniq (nullOr int);
-        default = null;
-        description = "The GID of the group. If undefined, NixOS will select a free GID.";
+        type = with types; uniq int;
+        description = "The GID of the group.";
+      };
+
+      members = mkOption {
+        type = with types; listOf string;
+        default = [];
+        description = ''
+          The user names of the group members, added to the
+          <literal>/etc/group</literal> file.
+        '';
       };
 
     };
 
     config = {
       name = mkDefault name;
-      gid = mkDefault (attrByPath [name] null ids.gids);
     };
 
   };
 
-  # Note: the 'X' in front of the password is to distinguish between
-  # having an empty password, and not having a password.
-  serializedUser = u: "${u.name}\n${u.description}\n${if u.uid != null then toString u.uid else ""}\n${u.group}\n${toString (concatStringsSep "," u.extraGroups)}\n${u.home}\n${u.shell}\n${toString u.createHome}\n${if u.password != null then "X" + u.password else ""}\n${toString u.isSystemUser}\n${toString u.createUser}\n${toString u.isAlias}\n";
-
-  usersFile = pkgs.writeText "users" (
+  getGroup = gname:
     let
-      p = partition (u: u.isAlias) (attrValues config.users.extraUsers);
-    in concatStrings (map serializedUser p.wrong ++ map serializedUser p.right));
-
+      groups = mapAttrsToList (n: g: g) (
+        filterAttrs (n: g: g.name == gname) cfg.extraGroups
+      );
+    in
+      if length groups == 1 then head groups
+      else if groups == [] then throw "Group ${gname} not defined"
+      else throw "Group ${gname} has multiple definitions";
+
+  getUser = uname:
+    let
+      users = mapAttrsToList (n: u: u) (
+        filterAttrs (n: u: u.name == uname) cfg.extraUsers
+      );
+    in
+      if length users == 1 then head users
+      else if users == [] then throw "User ${uname} not defined"
+      else throw "User ${uname} has multiple definitions";
+
+  mkGroupEntry = gname:
+    let
+      g = getGroup gname;
+      users = mapAttrsToList (n: u: u.name) (
+        filterAttrs (n: u: elem g.name u.extraGroups) cfg.extraUsers
+      );
+    in concatStringsSep ":" [
+      g.name "x" (toString g.gid)
+      (concatStringsSep "," (users ++ (filter (u: !(elem u users)) g.members)))
+    ];
+
+  mkPasswdEntry = uname: let u = getUser uname; in
+    concatStringsSep ":" [
+      u.name "x" (toString u.uid)
+      (toString (getGroup u.group).gid)
+      u.description u.home u.shell
+    ];
+
+  sortOn = a: sort (as1: as2: lessThan (getAttr a as1) (getAttr a as2));
+
+  groupFile = pkgs.writeText "group" (
+    concatStringsSep "\n" (map (g: mkGroupEntry g.name) (
+      sortOn "gid" (attrValues cfg.extraGroups)
+    ))
+  );
+
+  passwdFile = pkgs.writeText "passwd" (
+    concatStringsSep "\n" (map (u: mkPasswdEntry u.name) (
+      sortOn "uid" (filter (u: u.createUser) (attrValues cfg.extraUsers))
+    ))
+  );
+
+  # If mutableUsers is true, this script adds all users/groups defined in
+  # users.extra{Users,Groups} to /etc/{passwd,group} iff there isn't any
+  # existing user/group with the same name in those files.
+  # If mutableUsers is false, the /etc/{passwd,group} files will simply be
+  # replaced with the users/groups defined in the NixOS configuration.
+  # The merging procedure could certainly be improved, and instead of just
+  # keeping the lines as-is from /etc/{passwd,group} they could be combined
+  # in some way with the generated content from the NixOS configuration.
+  merger = src: pkgs.writeScript "merger" ''
+    #!${pkgs.bash}/bin/bash
+
+    PATH=${pkgs.gawk}/bin:${pkgs.gnugrep}/bin:$PATH
+
+    ${if !cfg.mutableUsers
+      then ''cp ${src} $1.tmp''
+      else ''awk -F: '{ print "^"$1":.*" }' $1 | egrep -vf - ${src} | cat $1 - > $1.tmp''
+    }
+
+    # set mtime to +1, otherwise change might go unnoticed (vipw/vigr only looks at mtime)
+    touch -m -t $(date -d @$(($(stat -c %Y $1)+1)) +%Y%m%d%H%M.%S) $1.tmp
+
+    mv -f $1.tmp $1
+  '';
+
+  idsAreUnique = set: idAttr: !(fold (name: args@{ dup, acc }:
+    let
+      id = builtins.toString (builtins.getAttr idAttr (builtins.getAttr name set));
+      exists = builtins.hasAttr id acc;
+      newAcc = acc // (builtins.listToAttrs [ { name = id; value = true; } ]);
+    in if dup then args else if exists
+      then builtins.trace "Duplicate ${idAttr} ${id}" { dup = true; acc = null; }
+      else { dup = false; acc = newAcc; }
+    ) { dup = false; acc = {}; } (builtins.attrNames set)).dup;
+  uidsAreUnique = idsAreUnique cfg.extraUsers "uid";
+  gidsAreUnique = idsAreUnique cfg.extraGroups "gid";
 in
 
 {
@@ -154,6 +271,36 @@ in
 
   options = {
 
+    users.mutableUsers = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        If true, you are free to add new users and groups to the system
+        with the ordinary <literal>useradd</literal> and
+        <literal>groupadd</literal> commands. On system activation, the
+        existing contents of the <literal>/etc/passwd</literal> and
+        <literal>/etc/group</literal> files will be merged with the
+        contents generated from the <literal>users.extraUsers</literal> and
+        <literal>users.extraGroups</literal> options. If
+        <literal>mutableUsers</literal> is false, the contents of the user and
+        group files will simply be replaced on system activation. This also
+        holds for the user passwords; if this option is false, all changed
+        passwords will be reset according to the
+        <literal>users.extraUsers</literal> configuration on activation. If
+        this option is true, the initial password for a user will be set
+        according to <literal>users.extraUsers</literal>, but existing passwords
+        will not be changed.
+      '';
+    };
+
+    users.enforceIdUniqueness = mkOption {
+      type = types.bool;
+      default = true;
+      description = ''
+        Whether to require that no two users/groups share the same uid/gid.
+      '';
+    };
+
     users.extraUsers = mkOption {
       default = {};
       type = types.loaOf types.optionSet;
@@ -194,11 +341,17 @@ in
       example = "!";
       description = ''
         The (hashed) password for the root account set on initial
-        installation.  The empty string denotes that root can login
+        installation. The empty string denotes that root can login
         locally without a password (but not via remote services such
         as SSH, or indirectly via <command>su</command> or
-        <command>sudo</command>).  The string <literal>!</literal>
+        <command>sudo</command>). The string <literal>!</literal>
         prevents root from logging in using a password.
+        Note, setting this option sets
+        <literal>users.extraUsers.root.hashedPassword</literal>.
+        Note, if <literal>users.mutableUsers</literal> is false
+        you cannot change the root password manually, so in that case
+        the name of this option is a bit misleading, since it will define
+        the root password beyond the user initialisation phase.
       '';
     };
 
@@ -211,144 +364,91 @@ in
 
     users.extraUsers = {
       root = {
+        uid = ids.uids.root;
         description = "System administrator";
         home = "/root";
-        shell = config.users.defaultUserShell;
+        shell = cfg.defaultUserShell;
         group = "root";
+        hashedPassword = mkDefault config.security.initialRootPassword;
       };
       nobody = {
+        uid = ids.uids.nobody;
         description = "Unprivileged account (don't use!)";
+        group = "nogroup";
       };
     };
 
     users.extraGroups = {
-      root = { };
-      wheel = { };
-      disk = { };
-      kmem = { };
-      tty = { };
-      floppy = { };
-      uucp = { };
-      lp = { };
-      cdrom = { };
-      tape = { };
-      audio = { };
-      video = { };
-      dialout = { };
-      nogroup = { };
-      users = { };
-      nixbld = { };
-      utmp = { };
-      adm = { }; # expected by journald
+      root.gid = ids.gids.root;
+      wheel.gid = ids.gids.wheel;
+      disk.gid = ids.gids.disk;
+      kmem.gid = ids.gids.kmem;
+      tty.gid = ids.gids.tty;
+      floppy.gid = ids.gids.floppy;
+      uucp.gid = ids.gids.uucp;
+      lp.gid = ids.gids.lp;
+      cdrom.gid = ids.gids.cdrom;
+      tape.gid = ids.gids.tape;
+      audio.gid = ids.gids.audio;
+      video.gid = ids.gids.video;
+      dialout.gid = ids.gids.dialout;
+      nogroup.gid = ids.gids.nogroup;
+      users.gid = ids.gids.users;
+      nixbld.gid = ids.gids.nixbld;
+      utmp.gid = ids.gids.utmp;
+      adm.gid = ids.gids.adm;
     };
 
-    system.activationScripts.rootPasswd = stringAfter [ "etc" ]
-      ''
-        # If there is no password file yet, create a root account with an
-        # empty password.
-        if ! test -e /etc/passwd; then
-            rootHome=/root
-            touch /etc/passwd; chmod 0644 /etc/passwd
-            touch /etc/group; chmod 0644 /etc/group
-            touch /etc/shadow; chmod 0600 /etc/shadow
-            # Can't use useradd, since it complains that it doesn't know us
-            # (bootstrap problem!).
-            echo "root:x:0:0:System administrator:$rootHome:${config.users.defaultUserShell}" >> /etc/passwd
-            echo "root:${config.security.initialRootPassword}:::::::" >> /etc/shadow
-        fi
-      '';
-
-    # Print a reminder for users to set a root password.
-    environment.interactiveShellInit =
-      ''
-        if [ "$UID" = 0 ]; then
-            read _l < /etc/shadow
-            if [ "''${_l:0:6}" = root:: ]; then
-                cat >&2 <<EOF
-        Warning: Your root account has a null password, allowing local users
-        to login as root.  Please set a non-null password using \`passwd', or
-        disable password-based root logins using \`passwd -l'.
-        EOF
-            fi
-            unset _l
-        fi
-      '';
-
-    system.activationScripts.users = stringAfter [ "groups" ]
-      ''
-        echo "updating users..."
-
-        cat ${usersFile} | while true; do
-            read name || break
-            read description
-            read uid
-            read group
-            read extraGroups
-            read home
-            read shell
-            read createHome
-            read password
-            read isSystemUser
-            read createUser
-            read isAlias
-
-            if [ -z "$createUser" ]; then
-                continue
-            fi
-
-            if ! curEnt=$(getent passwd "$name"); then
-                useradd ''${isSystemUser:+--system} \
-                    --comment "$description" \
-                    ''${uid:+--uid $uid} \
-                    --gid "$group" \
-                    --groups "$extraGroups" \
-                    --home "$home" \
-                    --shell "$shell" \
-                    ''${createHome:+--create-home} \
-                    ''${isAlias:+--non-unique} \
-                    "$name"
-                if test "''${password:0:1}" = 'X'; then
-                    (echo "''${password:1}"; echo "''${password:1}") | ${pkgs.shadow}/bin/passwd "$name"
-                fi
-            else
-                #echo "updating user $name..."
-                oldIFS="$IFS"; IFS=:; set -- $curEnt; IFS="$oldIFS"
-                prevUid=$3
-                prevHome=$6
-                # Don't change the home directory if it's the same to prevent
-                # unnecessary warnings about logged in users.
-                if test "$prevHome" = "$home"; then unset home; fi
-                usermod \
-                    --comment "$description" \
-                    --gid "$group" \
-                    --groups "$extraGroups" \
-                    ''${home:+--home "$home"} \
-                    --shell "$shell" \
-                    "$name"
-            fi
-
-        done
+    system.activationScripts.users =
+      let
+        mkhomeUsers = filterAttrs (n: u: u.createHome) cfg.extraUsers;
+        setpwUsers = filterAttrs (n: u: u.createUser) cfg.extraUsers;
+        pwFile = u: if !(isNull u.hashedPassword)
+          then pkgs.writeTextFile { name = "password-file"; text = u.hashedPassword; }
+          else if !(isNull u.password)
+          then pkgs.runCommand "password-file" { pw = u.password; } ''
+            echo -n "$pw" | ${pkgs.mkpasswd}/bin/mkpasswd -s > $out
+          '' else u.passwordFile;
+        setpw = n: u: ''
+          setpw=yes
+          ${optionalString cfg.mutableUsers ''
+            test "$(getent shadow '${u.name}' | cut -d: -f2)" != "x" && setpw=no
+          ''}
+          if [ "$setpw" == "yes" ]; then
+            ${if !(isNull (pwFile u))
+              then ''
+                echo -n "${u.name}:" | cat - "${pwFile u}" | \
+                  ${pkgs.shadow}/sbin/chpasswd -e
+              ''
+              else "passwd -l '${u.name}' &>/dev/null"
+            }
+          fi
+        '';
+        mkhome = n: u:
+         let
+            uid = toString u.uid;
+            gid = toString ((getGroup u.group).gid);
+            h = u.home;
+          in ''
+            test -a "${h}" || mkdir -p "${h}" || true
+            test "$(stat -c %u "${h}")" = ${uid} || chown ${uid} "${h}" || true
+            test "$(stat -c %g "${h}")" = ${gid} || chgrp ${gid} "${h}" || true
+          '';
+      in stringAfter [ "etc" ] ''
+        touch /etc/group
+        touch /etc/passwd
+        VISUAL=${merger groupFile} ${pkgs.shadow}/sbin/vigr &>/dev/null
+        VISUAL=${merger passwdFile} ${pkgs.shadow}/sbin/vipw &>/dev/null
+        ${pkgs.shadow}/sbin/grpconv
+        ${pkgs.shadow}/sbin/pwconv
+        ${concatStrings (mapAttrsToList mkhome mkhomeUsers)}
+        ${concatStrings (mapAttrsToList setpw setpwUsers)}
       '';
 
-    system.activationScripts.groups = stringAfter [ "rootPasswd" "binsh" "etc" "var" ]
-      ''
-        echo "updating groups..."
+    # for backwards compatibility
+    system.activationScripts.groups = stringAfter [ "users" ] "";
 
-        createGroup() {
-            name="$1"
-            gid="$2"
-
-            if ! curEnt=$(getent group "$name"); then
-                groupadd --system \
-                    ''${gid:+--gid $gid} \
-                    "$name"
-            fi
-        }
-
-        ${flip concatMapStrings (attrValues config.users.extraGroups) (g: ''
-          createGroup '${g.name}' '${toString g.gid}'
-        '')}
-      '';
+    assertions = [ { assertion = !cfg.enforceIdUniqueness || (uidsAreUnique && gidsAreUnique); message = "uids and gids must be unique!"; } ];
 
   };
 
diff --git a/nixos/modules/services/x11/mesa.nix b/nixos/modules/hardware/opengl.nix
index f892a1517582..603012cb1092 100644
--- a/nixos/modules/services/x11/mesa.nix
+++ b/nixos/modules/hardware/opengl.nix
@@ -2,19 +2,19 @@
 let
   inherit (pkgs.lib) mkOption types mkIf optional optionals elem optionalString optionalAttrs;
 
-  cfg = config.services.mesa;
+  cfg = config.hardware.opengl;
 
   kernelPackages = config.boot.kernelPackages;
 in {
   options = {
-    services.mesa.enable = mkOption {
-      description = "Whether this configuration requires mesa.";
+    hardware.opengl.enable = mkOption {
+      description = "Whether this configuration requires opengl.";
       type = types.bool;
       default = false;
       internal = true;
     };
 
-    services.mesa.driSupport = mkOption {
+    hardware.opengl.driSupport = mkOption {
       type = types.bool;
       default = true;
       description = ''
@@ -23,18 +23,18 @@ in {
       '';
     };
 
-    services.mesa.driSupport32Bit = mkOption {
+    hardware.opengl.driSupport32Bit = mkOption {
       type = types.bool;
       default = false;
       description = ''
         On 64-bit systems, whether to support Direct Rendering for
         32-bit applications (such as Wine).  This is currently only
         supported for the <literal>nvidia</literal> driver and for
-        <literal>mesa</literal>.
+        <literal>Mesa</literal>.
       '';
     };
 
-    services.mesa.s3tcSupport = mkOption {
+    hardware.opengl.s3tcSupport = mkOption {
       type = types.bool;
       default = false;
       description = ''
@@ -47,27 +47,32 @@ in {
     };
 
 
-    services.mesa.videoDrivers = mkOption {
+    hardware.opengl.videoDrivers = mkOption {
       type = types.listOf types.str;
       # !!! We'd like "nv" here, but it segfaults the X server.
       default = [ "ati" "cirrus" "intel" "vesa" "vmware" ];
       example = [ "vesa" ];
       description = ''
-        The names of the video drivers that the mesa should
-        support.  Mesa will try all of the drivers listed
-        here until it finds one that supports your video card.
+        The names of the opengl video drivers the configuration
+        supports. They will be tried in order until one that
+        supports your card is found.
       '';
     };
   };
 
   config = mkIf cfg.enable {
+    assertions = pkgs.lib.singleton {
+      assertion = cfg.driSupport32Bit -> pkgs.stdenv.isx86_64;
+      message = "Option driSupport32Bit only makes sens on a 64-bit system.";
+    };
+
     system.activationScripts.setup-opengl.deps = [];
     system.activationScripts.setup-opengl.text = ''
       rm -f /run/opengl-driver{,-32}
-      ${optionalString (!cfg.driSupport32Bit) "ln -sf opengl-driver /run/opengl-driver-32"}
-
-      ${# !!! The OpenGL driver depends on what's detected at runtime.
-        if elem "nvidia" cfg.videoDrivers then
+      ${optionalString (pkgs.stdenv.isi686) "ln -sf opengl-driver /run/opengl-driver-32"}
+    ''
+      #TODO:  The OpenGL driver should depend on what's detected at runtime.
+     +( if elem "nvidia" cfg.videoDrivers then
           ''
             ln -sf ${kernelPackages.nvidia_x11} /run/opengl-driver
             ${optionalString cfg.driSupport32Bit
@@ -89,8 +94,7 @@ in {
             ${optionalString cfg.driSupport32Bit
               "ln -sf ${pkgs_i686.mesa_drivers} /run/opengl-driver-32"}
           ''
-      }
-    '';
+      );
 
     environment.variables.LD_LIBRARY_PATH =
       [ "/run/opengl-driver/lib" "/run/opengl-driver-32/lib" ]
diff --git a/nixos/modules/hardware/video/bumblebee.nix b/nixos/modules/hardware/video/bumblebee.nix
new file mode 100644
index 000000000000..504da2cde850
--- /dev/null
+++ b/nixos/modules/hardware/video/bumblebee.nix
@@ -0,0 +1,41 @@
+{ config, pkgs, ... }:
+
+let kernel = config.boot.kernelPackages; in
+with pkgs.lib;
+
+{
+
+  options = {
+    hardware.bumblebee.enable = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Enable the bumblebee daemon to manage Optimus hybrid video cards.
+        This should power off secondary GPU until its use is requested
+        by running an application with optirun.
+
+        Only nvidia driver is supported so far.
+      '';
+    };
+  };
+
+  config = mkIf config.hardware.bumblebee.enable {
+    boot.blacklistedKernelModules = [ "nouveau" "nvidia" ];
+    boot.kernelModules = [ "bbswitch" ];
+    boot.extraModulePackages = [ kernel.bbswitch kernel.nvidia_x11 ];
+
+    environment.systemPackages = [ pkgs.bumblebee ];
+
+    systemd.services.bumblebeed = {
+      description = "Bumblebee Hybrid Graphics Switcher";
+      wantedBy = [ "display-manager.service" ];
+      script = "bumblebeed --use-syslog";
+      path = [ kernel.bbswitch pkgs.bumblebee ];
+      serviceConfig = {
+        Restart = "always";
+        RestartSec = 60;
+        CPUSchedulingPolicy = "idle";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/installer/cd-dvd/installation-cd-base.nix b/nixos/modules/installer/cd-dvd/installation-cd-base.nix
index 1aba67dcd9e9..07c054b391af 100644
--- a/nixos/modules/installer/cd-dvd/installation-cd-base.nix
+++ b/nixos/modules/installer/cd-dvd/installation-cd-base.nix
@@ -19,7 +19,7 @@ with pkgs.lib;
   # ISO naming.
   isoImage.isoName = "${config.isoImage.isoBaseName}-${config.system.nixosVersion}-${pkgs.stdenv.system}.iso";
 
-  isoImage.volumeID = substring 0 32 "NIXOS_${config.system.nixosVersion}";
+  isoImage.volumeID = substring 0 11 "NIXOS_${config.system.nixosVersion}";
 
   # Make the installer more likely to succeed in low memory
   # environments.  The kernel's overcommit heustistics bite us
@@ -36,7 +36,7 @@ with pkgs.lib;
   isoImage.makeEfiBootable = true;
 
   # Add Memtest86+ to the CD.
-  boot.loader.grub.memtest86 = true;
+  boot.loader.grub.memtest86.enable = true;
 
   # Get a console as soon as the initrd loads fbcon on EFI boot
   boot.initrd.kernelModules = [ "fbcon" ];
diff --git a/nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix b/nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix
index 7f253d595dc3..c3ced8490014 100644
--- a/nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix
+++ b/nixos/modules/installer/cd-dvd/system-tarball-sheevaplug.nix
@@ -138,8 +138,7 @@ in
   };
 
   # Setting vesa, we don't get the nvidia driver, which can't work in arm.
-  services.xserver.videoDriver = "vesa";
-  services.xserver.videoDrivers = [];
+  hardware.opengl.videoDrivers = [ "vesa" ];
   services.nixosManual.enable = false;
 
   # Include the firmware for various wireless cards.
diff --git a/nixos/modules/installer/tools/nixos-generate-config.pl b/nixos/modules/installer/tools/nixos-generate-config.pl
index 1365f3b93961..bf7cdcd37119 100644
--- a/nixos/modules/installer/tools/nixos-generate-config.pl
+++ b/nixos/modules/installer/tools/nixos-generate-config.pl
@@ -163,7 +163,7 @@ foreach my $path (glob "/sys/bus/pci/devices/*") {
     pciCheck $path;
 }
 
-push @attrs, "services.xserver.videoDrivers = [ \"$videoDriver\" ];" if $videoDriver;
+push @attrs, "hardware.opengl.videoDrivers = [ \"$videoDriver\" ];" if $videoDriver;
 
 
 # Idem for USB devices.
@@ -256,7 +256,7 @@ foreach my $fs (read_file("/proc/self/mountinfo")) {
     $mountPoint = "/" if $mountPoint eq "";
 
     # Skip special filesystems.
-    next if in($mountPoint, "/proc") || in($mountPoint, "/dev") || in($mountPoint, "/sys") || in($mountPoint, "/run");
+    next if in($mountPoint, "/proc") || in($mountPoint, "/dev") || in($mountPoint, "/sys") || in($mountPoint, "/run") || $mountPoint eq "/var/lib/nfs/rpc_pipefs";
 
     # Skip the optional fields.
     my $n = 6; $n++ while $fields[$n] ne "-"; $n++;
@@ -305,7 +305,15 @@ EOF
   fileSystems.\"$mountPoint\" =
     { device = \"$device\";
       fsType = \"$fsType\";
-      options = \"${\join ",", uniq(@extraOptions, @superOptions, @mountOptions)}\";
+EOF
+
+    if (scalar @extraOptions > 0) {
+      $fileSystems .= <<EOF;
+      options = \"${\join ",", uniq(@extraOptions)}\";
+EOF
+    }
+
+    $fileSystems .= <<EOF;
     };
 
 EOF
diff --git a/nixos/modules/installer/tools/nixos-option.sh b/nixos/modules/installer/tools/nixos-option.sh
index 60cee2519da0..edc94d732084 100644
--- a/nixos/modules/installer/tools/nixos-option.sh
+++ b/nixos/modules/installer/tools/nixos-option.sh
@@ -228,7 +228,7 @@ else
     escapeQuotes () { eval echo "$1"; }
     nixMap escapeQuotes "$names"
   else
-    echo 1>&2 "An error occured while looking for attribute names."
+    echo 1>&2 "An error occurred while looking for attribute names."
     echo $result
   fi
 fi
diff --git a/nixos/modules/installer/virtualbox-demo.nix b/nixos/modules/installer/virtualbox-demo.nix
index 76cc29a1facd..9ef41e470747 100644
--- a/nixos/modules/installer/virtualbox-demo.nix
+++ b/nixos/modules/installer/virtualbox-demo.nix
@@ -15,5 +15,5 @@ with pkgs.lib;
 
   # Add some more video drivers to give X11 a shot at working in
   # VMware and QEMU.
-  services.xserver.videoDrivers = mkOverride 40 [ "virtualbox" "vmware" "cirrus" "vesa" ];
+  hardware.opengl.videoDrivers = mkOverride 40 [ "virtualbox" "vmware" "cirrus" "vesa" ];
 }
diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix
index 0a585139521d..b27739c99ce0 100644
--- a/nixos/modules/misc/ids.nix
+++ b/nixos/modules/misc/ids.nix
@@ -110,7 +110,15 @@
       openldap = 99;
       memcached = 100;
       cgminer = 101;
-      systemd-journal-gateway = 102;
+      munin = 102;
+      logcheck = 103;
+      nix-ssh = 104;
+      dictd = 105;
+      couchdb = 106;
+      searx = 107;
+      kippo = 108;
+      jenkins = 109;
+      systemd-journal-gateway = 110;
 
       # When adding a uid, make sure it doesn't match an existing gid.
 
@@ -200,7 +208,14 @@
       haproxy = 92;
       openldap = 93;
       connman = 94;
-      systemd-journal-gateway = 95;
+      munin = 95;
+      keys = 96;
+      dictd = 105;
+      couchdb = 106;
+      searx = 107;
+      kippo = 108;
+      jenkins = 109;
+      systemd-journal-gateway = 110;
 
       # When adding a gid, make sure it doesn't match an existing uid.
 
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index aa4bada8b28b..b419942057ac 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -29,7 +29,9 @@
   ./hardware/network/intel-3945abg.nix
   ./hardware/network/ralink.nix
   ./hardware/network/rtl8192c.nix
+  ./hardware/opengl.nix
   ./hardware/pcmcia.nix
+  ./hardware/video/bumblebee.nix
   ./installer/tools/nixos-checkout.nix
   ./installer/tools/tools.nix
   ./misc/assertions.nix
@@ -76,16 +78,19 @@
   ./services/backup/bacula.nix
   ./services/backup/mysql-backup.nix
   ./services/backup/postgresql-backup.nix
-  ./services/backup/sitecopy-backup.nix
   ./services/backup/rsnapshot.nix
+  ./services/backup/sitecopy-backup.nix
+  ./services/backup/tarsnap.nix
+  ./services/continuous-integration/jenkins/default.nix
+  ./services/continuous-integration/jenkins/slave.nix
   ./services/databases/4store-endpoint.nix
   ./services/databases/4store.nix
+  ./services/databases/couchdb.nix
   ./services/databases/firebird.nix
   ./services/databases/memcached.nix
   ./services/databases/mongodb.nix
   ./services/databases/redis.nix
   ./services/databases/mysql.nix
-  ./services/databases/mysql55.nix
   ./services/databases/openldap.nix
   ./services/databases/postgresql.nix
   ./services/databases/virtuoso.nix
@@ -123,6 +128,7 @@
   ./services/misc/gpsd.nix
   ./services/misc/nix-daemon.nix
   ./services/misc/nix-gc.nix
+  ./services/misc/nix-ssh-serve.nix
   ./services/misc/nixos-manual.nix
   ./services/misc/rogue.nix
   ./services/misc/svnserve.nix
@@ -185,6 +191,7 @@
   ./services/networking/rdnssd.nix
   ./services/networking/rpcbind.nix
   ./services/networking/sabnzbd.nix
+  ./services/networking/searx.nix
   ./services/networking/supybot.nix
   ./services/networking/ssh/lshd.nix
   ./services/networking/ssh/sshd.nix
@@ -201,6 +208,7 @@
   ./services/scheduling/cron.nix
   ./services/scheduling/fcron.nix
   ./services/search/elasticsearch.nix
+  ./services/search/solr.nix
   ./services/security/clamav.nix
   ./services/security/haveged.nix
   ./services/security/fprot.nix
@@ -223,8 +231,10 @@
   ./services/web-servers/lighttpd/cgit.nix
   ./services/web-servers/lighttpd/gitweb.nix
   ./services/web-servers/nginx/default.nix
+  ./services/web-servers/phpfpm.nix
   ./services/web-servers/tomcat.nix
   ./services/web-servers/varnish/default.nix
+  ./services/web-servers/winstone.nix
   ./services/web-servers/zope2.nix
   ./services/x11/desktop-managers/default.nix
   ./services/x11/display-managers/auto.nix
@@ -235,7 +245,6 @@
   ./services/x11/hardware/multitouch.nix
   ./services/x11/hardware/synaptics.nix
   ./services/x11/hardware/wacom.nix
-  ./services/x11/mesa.nix
   ./services/x11/window-managers/awesome.nix
   #./services/x11/window-managers/compiz.nix
   ./services/x11/window-managers/default.nix
diff --git a/nixos/modules/profiles/demo.nix b/nixos/modules/profiles/demo.nix
index 396dcf6c5d3b..605cc6aad1de 100644
--- a/nixos/modules/profiles/demo.nix
+++ b/nixos/modules/profiles/demo.nix
@@ -11,6 +11,6 @@
       createHome = true;
       useDefaultShell = true;
       password = "demo";
-      isSystemUser = false;
+      uid = 1000;
     };
 }
diff --git a/nixos/modules/programs/environment.nix b/nixos/modules/programs/environment.nix
index 7c1922cdfd89..489a7a4d269a 100644
--- a/nixos/modules/programs/environment.nix
+++ b/nixos/modules/programs/environment.nix
@@ -45,7 +45,7 @@ in
         TERMINFO_DIRS = [ "${i}/share/terminfo" ];
         PERL5LIB = [ "${i}/lib/perl5/site_perl" ];
         ALSA_PLUGIN_DIRS = [ "${i}/lib/alsa-lib" ];
-        GST_PLUGIN_PATH = [ "${i}/lib/gstreamer-0.10" ];
+        GST_PLUGIN_SYSTEM_PATH = [ "${i}/lib/gstreamer-0.10" ];
         KDEDIRS = [ "${i}" ];
         STRIGI_PLUGIN_PATH = [ "${i}/lib/strigi/" ];
         QT_PLUGIN_PATH = [ "${i}/lib/qt4/plugins" "${i}/lib/kde4/plugins" ];
diff --git a/nixos/modules/programs/shadow.nix b/nixos/modules/programs/shadow.nix
index 9e46ab8b298f..15b083b72d28 100644
--- a/nixos/modules/programs/shadow.nix
+++ b/nixos/modules/programs/shadow.nix
@@ -58,7 +58,8 @@ in
 
   config = {
 
-    environment.systemPackages = [ pkgs.shadow ];
+    environment.systemPackages =
+      pkgs.lib.optional config.users.mutableUsers pkgs.shadow;
 
     environment.etc =
       [ { # /etc/login.defs: global configuration for pwdutils.  You
@@ -94,6 +95,8 @@ in
         groupmems = { rootOK = true; };
         groupdel = { rootOK = true; };
         login = { startSession = true; allowNullPassword = true; showMotd = true; updateWtmp = true; };
+        chpasswd = { rootOK = true; };
+        chgpasswd = { rootOK = true; };
       };
 
     security.setuidPrograms = [ "passwd" "chfn" "su" "newgrp" ];
diff --git a/nixos/modules/programs/shell.nix b/nixos/modules/programs/shell.nix
index 8052502c21ea..226105a0c979 100644
--- a/nixos/modules/programs/shell.nix
+++ b/nixos/modules/programs/shell.nix
@@ -28,34 +28,36 @@ in
             echo "WARNING: bad ownership on $NIX_USER_PROFILE_DIR" >&2
         fi
 
-        if ! test -L $HOME/.nix-profile; then
-            if test "$USER" != root; then
-                ln -s $NIX_USER_PROFILE_DIR/profile $HOME/.nix-profile
-            else
-                # Root installs in the system-wide profile by default.
-                ln -s /nix/var/nix/profiles/default $HOME/.nix-profile
-            fi
-        fi
+        if test -w $HOME; then
+          if ! test -L $HOME/.nix-profile; then
+              if test "$USER" != root; then
+                  ln -s $NIX_USER_PROFILE_DIR/profile $HOME/.nix-profile
+              else
+                  # Root installs in the system-wide profile by default.
+                  ln -s /nix/var/nix/profiles/default $HOME/.nix-profile
+              fi
+          fi
 
-        # Subscribe the root user to the NixOS channel by default.
-        if [ "$USER" = root -a ! -e $HOME/.nix-channels ]; then
-            echo "${config.system.defaultChannel} nixos" > $HOME/.nix-channels
-        fi
+          # Subscribe the root user to the NixOS channel by default.
+          if [ "$USER" = root -a ! -e $HOME/.nix-channels ]; then
+              echo "${config.system.defaultChannel} nixos" > $HOME/.nix-channels
+          fi
 
-        # Create the per-user garbage collector roots directory.
-        NIX_USER_GCROOTS_DIR=/nix/var/nix/gcroots/per-user/$USER
-        mkdir -m 0755 -p $NIX_USER_GCROOTS_DIR
-        if test "$(stat --printf '%u' $NIX_USER_GCROOTS_DIR)" != "$(id -u)"; then
-            echo "WARNING: bad ownership on $NIX_USER_GCROOTS_DIR" >&2
-        fi
+          # Create the per-user garbage collector roots directory.
+          NIX_USER_GCROOTS_DIR=/nix/var/nix/gcroots/per-user/$USER
+          mkdir -m 0755 -p $NIX_USER_GCROOTS_DIR
+          if test "$(stat --printf '%u' $NIX_USER_GCROOTS_DIR)" != "$(id -u)"; then
+              echo "WARNING: bad ownership on $NIX_USER_GCROOTS_DIR" >&2
+          fi
 
-        # Set up a default Nix expression from which to install stuff.
-        if [ ! -e $HOME/.nix-defexpr -o -L $HOME/.nix-defexpr ]; then
-            rm -f $HOME/.nix-defexpr
-            mkdir $HOME/.nix-defexpr
-            if [ "$USER" != root ]; then
-                ln -s /nix/var/nix/profiles/per-user/root/channels $HOME/.nix-defexpr/channels_root
-            fi
+          # Set up a default Nix expression from which to install stuff.
+          if [ ! -e $HOME/.nix-defexpr -o -L $HOME/.nix-defexpr ]; then
+              rm -f $HOME/.nix-defexpr
+              mkdir $HOME/.nix-defexpr
+              if [ "$USER" != root ]; then
+                  ln -s /nix/var/nix/profiles/per-user/root/channels $HOME/.nix-defexpr/channels_root
+              fi
+          fi
         fi
       '';
 
diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix
index 6ff5277cf9ca..72093aab5cd7 100644
--- a/nixos/modules/rename.nix
+++ b/nixos/modules/rename.nix
@@ -12,18 +12,20 @@ let
     visible = true;
   };
 
+  # warn option was renamed
   obsolete = from: to: rename {
     inherit from to;
     name = "Obsolete name";
-    use = x: builtins.trace "Obsolete option `${showOption from}' is used instead of `${showOption to}'." x;
-    define = x: builtins.trace "Obsolete option `${showOption from}' is defined instead of `${showOption to}'." x;
+    use = x: builtins.trace "Obsolete option `${showOption from}' is used. It was renamed to `${showOption to}'." x;
+    define = x: builtins.trace "Obsolete option `${showOption from}' is used. It was renamed to  `${showOption to}'." x;
   };
 
+  # abort if deprecated option is used
   deprecated = from: to: rename {
     inherit from to;
     name = "Deprecated name";
-    use = x: abort "Deprecated option `${showOption from}' is used instead of `${showOption to}'.";
-    define = x: abort "Deprecated option `${showOption from}' is defined instead of `${showOption to}'.";
+    use = x: abort "Deprecated option `${showOption from}' is used. It was renamed to `${showOption to}'.";
+    define = x: abort "Deprecated option `${showOption from}' is used. It was renamed to  `${showOption to}'.";
   };
 
   showOption = concatStringsSep ".";
@@ -54,7 +56,7 @@ let
             inherit visible;
           });
         }
-        { config = setTo (mkIf (fromOf options).isDefined (define (mkMerge (fromOf options).definitions)));
+        { config = setTo (mkMerge (if (fromOf options).isDefined then [ (define (mkMerge (fromOf options).definitions)) ] else []));
         }
       ];
 
@@ -101,6 +103,7 @@ in zipModules ([]
 ++ obsolete [ "services" "sshd" "gatewayPorts" ] [ "services" "openssh" "gatewayPorts" ]
 ++ obsolete [ "services" "sshd" "permitRootLogin" ] [ "services" "openssh" "permitRootLogin" ]
 ++ obsolete [ "services" "xserver" "startSSHAgent" ] [ "services" "xserver" "startOpenSSHAgent" ]
+++ obsolete [ "services" "xserver" "windowManager" "xbmc" ] [ "services" "xserver" "desktopManager" "xbmc" ]
 
 # KDE
 ++ deprecated [ "kde" "extraPackages" ] [ "environment" "kdePackages" ]
@@ -113,10 +116,12 @@ in zipModules ([]
 # !!! this hardcodes bash, could we detect from config which shell is actually used?
 ++ obsolete [ "environment" "promptInit" ] [ "programs" "bash" "promptInit" ]
 
-++ obsolete [ "services" "xserver" "driSupport" ] [ "services" "mesa" "driSupport" ]
-++ obsolete [ "services" "xserver" "driSupport32Bit" ] [ "services" "mesa" "driSupport32Bit" ]
-++ obsolete [ "services" "xserver" "s3tcSupport" ] [ "services" "mesa" "s3tcSupport" ]
-++ obsolete [ "services" "xserver" "videoDrivers" ] [ "services" "mesa" "videoDrivers" ]
+++ obsolete [ "services" "xserver" "driSupport" ] [ "hardware" "opengl" "driSupport" ]
+++ obsolete [ "services" "xserver" "driSupport32Bit" ] [ "hardware" "opengl" "driSupport32Bit" ]
+++ obsolete [ "services" "xserver" "s3tcSupport" ] [ "hardware" "opengl" "s3tcSupport" ]
+++ obsolete [ "services" "xserver" "videoDrivers" ] [ "hardware" "opengl" "videoDrivers" ]
+
+++ obsolete [ "services" "mysql55" ] [ "services" "mysql" ]
 
 # Options that are obsolete and have no replacement.
 ++ obsolete' [ "boot" "loader" "grub" "bootDevice" ]
diff --git a/nixos/modules/security/ca.nix b/nixos/modules/security/ca.nix
index 2e93fb36b450..05cd1c3ecc17 100644
--- a/nixos/modules/security/ca.nix
+++ b/nixos/modules/security/ca.nix
@@ -10,11 +10,6 @@ with pkgs.lib;
       [ { source = "${pkgs.cacert}/etc/ca-bundle.crt";
           target = "ssl/certs/ca-bundle.crt";
         }
-
-        # Backward compatibility; may remove at some point.
-        { source = "${pkgs.cacert}/etc/ca-bundle.crt";
-          target = "ca-bundle.crt";
-        }
       ];
 
     environment.variables.OPENSSL_X509_CERT_FILE = "/etc/ssl/certs/ca-bundle.crt";
diff --git a/nixos/modules/services/backup/tarsnap.nix b/nixos/modules/services/backup/tarsnap.nix
new file mode 100644
index 000000000000..03fbd29a191d
--- /dev/null
+++ b/nixos/modules/services/backup/tarsnap.nix
@@ -0,0 +1,203 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  cfg = config.services.tarsnap;
+
+  optionalNullStr = e: v: if e == null then "" else v;
+
+  configFile = pkgs.writeText "tarsnap.conf" ''
+    cachedir ${cfg.cachedir}
+    keyfile  ${cfg.keyfile}
+    ${optionalString cfg.nodump "nodump"}
+    ${optionalString cfg.printStats "print-stats"}
+    ${optionalNullStr cfg.checkpointBytes "checkpoint-bytes "+cfg.checkpointBytes}
+    ${optionalString cfg.aggressiveNetworking "aggressive-networking"}
+    ${concatStringsSep "\n" (map (v: "exclude "+v) cfg.excludes)}
+    ${concatStringsSep "\n" (map (v: "include "+v) cfg.includes)}
+    ${optionalString cfg.lowmem "lowmem"}
+    ${optionalString cfg.verylowmem "verylowmem"}
+  '';
+in
+{
+  options = {
+    services.tarsnap = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          If enabled, NixOS will periodically create backups of the
+          specified directories using the <literal>tarsnap</literal>
+          backup service. This installs a <literal>systemd</literal>
+          service called <literal>tarsnap-backup</literal> which is
+          periodically run by cron, or you may run it on-demand.
+
+          See <link xlink:href='http://www.tarsnap.com/gettingstarted.html'>Getting Started</link> 
+          Tarsnap page.
+        '';
+      };
+
+      label = mkOption {
+        type = types.str;
+        default = "nixos";
+        description = ''
+          Specifies the label for archives created by Tarsnap. The
+          full name will be
+          <literal>label-$(date+"%Y%m%d%H%M%S")</literal>. For
+          example, by default your backups will look similar to
+          <literal>nixos-20140301021501</literal>.
+        '';
+      };
+
+      cachedir = mkOption {
+        type    = types.path;
+        default = "/var/cache/tarsnap";
+        description = ''
+          Tarsnap operations use a "cache directory" which allows
+          Tarsnap to identify which blocks of data have been
+          previously stored; this directory is specified via the
+          <literal>cachedir</literal> option. If the cache directory
+          is lost or out of date, tarsnap creation/deletion operations
+          will exit with an error message instructing you to run
+          <literal>tarsnap --fsck</literal> to regenerate the cache
+          directory.
+        '';
+      };
+
+      keyfile = mkOption {
+        type = types.path;
+        default = "/root/tarsnap.key";
+        description = ''
+          Path to the keyfile which identifies the machine associated
+          with your Tarsnap account. This file can be created using
+          the <literal>tarsnap-keygen</literal> utility, and providing
+          your Tarsnap login credentials.
+        '';
+      };
+
+      nodump = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          If set to <literal>true</literal>, then don't archive files
+          which have the <literal>nodump</literal> flag set.
+        '';
+      };
+
+      printStats = mkOption {
+        type = types.bool;
+        default = true;
+        description = "Print statistics when creating archives.";
+      };
+
+      checkpointBytes = mkOption {
+        type = types.nullOr types.str;
+        default = "1G";
+        description = ''
+          Create a checkpoint per a particular amount of uploaded
+          data. By default, Tarsnap will create checkpoints once per
+          GB of data uploaded. At minimum,
+          <literal>checkpointBytes</literal> must be 1GB.
+
+          Can also be set to <literal>null</literal> to disable
+          checkpointing.
+        '';
+      };
+
+      period = mkOption {
+        type = types.str;
+        default = "15 01 * * *";
+        description = ''
+          This option defines (in the format used by cron) when
+          tarsnap is run for backups.  The default is to update at
+          01:15 at night every day.
+        '';
+      };
+
+      aggressiveNetworking = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Aggressive network behaviour: Use multiple TCP connections
+          when writing archives.  Use of this option is recommended
+          only in cases where TCP congestion control is known to be
+          the limiting factor in upload performance.
+        '';
+      };
+
+      directories = mkOption {
+        type = types.listOf types.path;
+        default = [];
+        description = "List of filesystem paths to archive.";
+      };
+
+      excludes = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          Exclude files and directories matching the specified patterns.
+        '';
+      };
+
+      includes = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          Include only files and directories matching the specified patterns.
+
+          Note that exclusions specified via
+          <literal>excludes</literal> take precedence over inclusions.
+        '';
+      };
+
+      lowmem = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Attempt to reduce tarsnap memory consumption.  This option
+          will slow down the process of creating archives, but may
+          help on systems where the average size of files being backed
+          up is less than 1 MB.
+        '';
+      };
+
+      verylowmem = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Try even harder to reduce tarsnap memory consumption.  This
+          can significantly slow down tarsnap, but reduces its memory
+          usage by an additional factor of 2 beyond what the
+          <literal>lowmem</literal> option does.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    assertions =
+      [ { assertion = cfg.directories != [];
+          message = "Must specify directories for Tarsnap to back up";
+        }
+        { assertion = cfg.lowmem -> !cfg.verylowmem && (cfg.verylowmem -> !cfg.lowmem);
+          message = "You cannot set both lowmem and verylowmem";
+        }
+      ];
+
+    systemd.services.tarsnap-backup = {
+      description = "Tarsnap Backup process";
+      path = [ pkgs.tarsnap pkgs.coreutils ];
+      script = ''
+        mkdir -p -m 0755 $(dirname ${cfg.cachedir})
+        mkdir -p -m 0600 ${cfg.cachedir}
+        exec tarsnap --configfile ${configFile} -c -f ${cfg.label}-$(date +"%Y%m%d%H%M%S") ${concatStringsSep " " cfg.directories}
+      '';
+    };
+
+    services.cron.systemCronJobs = optional cfg.enable
+      "${cfg.period} root ${config.systemd.package}/bin/systemctl start tarsnap-backup.service";
+
+    environment.systemPackages = [ pkgs.tarsnap ];
+  };
+}
diff --git a/nixos/modules/services/continuous-integration/jenkins/default.nix b/nixos/modules/services/continuous-integration/jenkins/default.nix
new file mode 100644
index 000000000000..c3dc59a9fbd0
--- /dev/null
+++ b/nixos/modules/services/continuous-integration/jenkins/default.nix
@@ -0,0 +1,118 @@
+{ config, pkgs, ... }:
+with pkgs.lib;
+let
+  cfg = config.services.jenkins;
+in {
+  options = {
+    services.jenkins = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable the jenkins continuous integration server.
+        '';
+      };
+
+      user = mkOption {
+        default = "jenkins";
+        type = with types; string;
+        description = ''
+          User the jenkins server should execute under.
+        '';
+      };
+
+      group = mkOption {
+        default = "jenkins";
+        type = with types; string;
+        description = ''
+          User the jenkins server should execute under.
+        '';
+      };
+
+      home = mkOption {
+        default = "/var/lib/jenkins";
+        type = with types; string;
+        description = ''
+          The path to use as JENKINS_HOME. If the default user "jenkins" is configured then
+          this is the home of the "jenkins" user.
+        '';
+      };
+
+      port = mkOption {
+        default = 8080;
+        type = types.uniq types.int;
+        description = ''
+          Specifies port number on which the jenkins HTTP interface listens. The default is 8080
+        '';
+      };
+
+      packages = mkOption {
+        default = [ pkgs.stdenv pkgs.git pkgs.jdk pkgs.openssh pkgs.nix ];
+        type = types.listOf types.package;
+        description = ''
+          Packages to add to PATH for the jenkins process.
+        '';
+      };
+
+      environment = mkOption {
+        default = { NIX_REMOTE = "daemon"; };
+        type = with types; attrsOf string;
+        description = ''
+          Additional environment variables to be passed to the jenkins process.
+          The environment will always include JENKINS_HOME.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    users.extraGroups = optional (cfg.group == "jenkins") {
+      name = "jenkins";
+      gid = config.ids.gids.jenkins;
+    };
+
+    users.extraUsers = optional (cfg.user == "jenkins") {
+      name = "jenkins";
+      description = "jenkins user";
+      createHome = true;
+      home = cfg.home;
+      group = cfg.group;
+      useDefaultShell = true;
+      uid = config.ids.uids.jenkins;
+    };
+
+    systemd.services.jenkins = {
+      description = "Jenkins Continuous Integration Server";
+      after = [ "network.target" ];
+      wantedBy = [ "multi-user.target" ];
+
+      environment = {
+        JENKINS_HOME = cfg.home;
+      } // cfg.environment;
+
+      path = cfg.packages;
+
+      script = ''
+        ${pkgs.jdk}/bin/java -jar ${pkgs.jenkins} --httpPort=${toString cfg.port}
+      '';
+
+      postStart = ''
+        until ${pkgs.curl}/bin/curl -s -L localhost:${toString cfg.port} ; do
+          sleep 10
+        done
+        while true ; do
+          index=`${pkgs.curl}/bin/curl -s -L localhost:${toString cfg.port}`
+          if [[ !("$index" =~ 'Please wait while Jenkins is restarting' ||
+                  "$index" =~ 'Please wait while Jenkins is getting ready to work') ]]; then
+            exit 0
+          fi
+          sleep 30
+        done
+      '';
+
+      serviceConfig = {
+        User = cfg.user;
+      };
+    };
+  };
+}
diff --git a/nixos/modules/services/continuous-integration/jenkins/slave.nix b/nixos/modules/services/continuous-integration/jenkins/slave.nix
new file mode 100644
index 000000000000..1d31ab830f6c
--- /dev/null
+++ b/nixos/modules/services/continuous-integration/jenkins/slave.nix
@@ -0,0 +1,67 @@
+{ config, pkgs, ... }:
+with pkgs.lib;
+let
+  cfg = config.services.jenkinsSlave;
+  masterCfg = config.services.jenkins;
+in {
+  options = {
+    services.jenkinsSlave = {
+      # todo:
+      # * assure the profile of the jenkins user has a JRE and any specified packages. This would
+      # enable ssh slaves.
+      # * Optionally configure the node as a jenkins ad-hoc slave. This would imply configuration
+      # properties for the master node.
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          If true the system will be configured to work as a jenkins slave.
+          If the system is also configured to work as a jenkins master then this has no effect.
+          In progress: Currently only assures the jenkins user is configured.
+        '';
+      };
+
+      user = mkOption {
+        default = "jenkins";
+        type = with types; string;
+        description = ''
+          User the jenkins slave agent should execute under.
+        '';
+      };
+
+      group = mkOption {
+        default = "jenkins";
+        type = with types; string;
+        description = ''
+          User the jenkins slave agent should execute under.
+        '';
+      };
+
+      home = mkOption {
+        default = "/var/lib/jenkins";
+        type = with types; string;
+        description = ''
+          The path to use as JENKINS_HOME. If the default user "jenkins" is configured then
+          this is the home of the "jenkins" user.
+        '';
+      };
+    };
+  };
+
+  config = mkIf (cfg.enable && !masterCfg.enable) {
+    users.extraGroups = optional (cfg.group == "jenkins") {
+      name = "jenkins";
+      gid = config.ids.gids.jenkins;
+    };
+
+    users.extraUsers = optional (cfg.user == "jenkins") {
+      name = "jenkins";
+      description = "jenkins user";
+      createHome = true;
+      home = cfg.home;
+      group = cfg.group;
+      useDefaultShell = true;
+      uid = config.ids.uids.jenkins;
+    };
+  };
+}
diff --git a/nixos/modules/services/databases/couchdb.nix b/nixos/modules/services/databases/couchdb.nix
new file mode 100644
index 000000000000..b48d3a64767f
--- /dev/null
+++ b/nixos/modules/services/databases/couchdb.nix
@@ -0,0 +1,173 @@
+{ config, pkgs, ... }:
+  with pkgs.lib;
+
+let
+  cfg = config.services.couchdb;
+  configFile = pkgs.writeText "couchdb.ini"
+    ''
+      [couchdb]
+      database_dir = ${cfg.databaseDir}
+      uri_file = ${cfg.uriFile}
+      view_index_dir = ${cfg.viewIndexDir}
+
+      [httpd]
+      port = ${toString cfg.port}
+      bind_address = ${cfg.bindAddress}
+
+      [log]
+      file = ${cfg.logFile}
+    '';
+
+in {
+
+  ###### interface
+
+  options = {
+
+    services.couchdb = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to run CouchDB Server.
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.couchdb;
+        example = literalExample "pkgs.couchdb";
+        description = ''
+          CouchDB package to use.
+        '';
+      };
+
+
+      user = mkOption {
+        type = types.string;
+        default = "couchdb";
+        description = ''
+          User account under which couchdb runs.
+        '';
+      };
+
+      group = mkOption {
+        type = types.string;
+        default = "couchdb";
+        description = ''
+          Group account under which couchdb runs.
+        '';
+      };
+
+      # couchdb options: http://docs.couchdb.org/en/latest/config/index.html
+
+      databaseDir = mkOption {
+        type = types.path;
+        default = "/var/lib/couchdb";
+        description = ''
+          Specifies location of CouchDB database files (*.couch named). This
+          location should be writable and readable for the user the CouchDB
+          service runs as (couchdb by default).
+        '';
+      };
+
+      uriFile = mkOption {
+        type = types.path;
+        default = "/var/run/couchdb/couchdb.uri";
+        description = ''
+          This file contains the full URI that can be used to access this
+          instance of CouchDB. It is used to help discover the port CouchDB is
+          running on (if it was set to 0 (e.g. automatically assigned any free
+          one). This file should be writable and readable for the user that
+          runs the CouchDB service (couchdb by default).
+        '';
+      };
+
+      viewIndexDir = mkOption {
+        type = types.path;
+        default = "/var/lib/couchdb";
+        description = ''
+          Specifies location of CouchDB view index files. This location should
+          be writable and readable for the user that runs the CouchDB service
+          (couchdb by default).
+        '';
+      };
+
+      bindAddress = mkOption {
+        type = types.string;
+        default = "127.0.0.1";
+        description = ''
+          Defines the IP address by which CouchDB will be accessible.
+        '';
+      };
+
+      port = mkOption {
+        type = types.int;
+        default = 5984;
+        description = ''
+          Defined the port number to listen.
+        '';
+      };
+
+      logFile = mkOption {
+        type = types.path;
+        default = "/var/log/couchdb.log";
+        description = ''
+          Specifies the location of file for logging output.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Extra configuration. Overrides any other cofiguration.
+        '';
+      };
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf config.services.couchdb.enable {
+
+    environment.systemPackages = [ cfg.package ];
+
+    systemd.services.couchdb = {
+      description = "CouchDB Server";
+      wantedBy = [ "multi-user.target" ];
+
+      preStart =
+        ''
+        mkdir -p `dirname ${cfg.uriFile}`;
+        mkdir -p `dirname ${cfg.logFile}`;
+        mkdir -p ${cfg.databaseDir};
+        mkdir -p ${cfg.viewIndexDir};
+
+        if [ "$(id -u)" = 0 ]; then
+          chown ${cfg.user}:${cfg.group} `dirname ${cfg.uriFile}`
+          chown ${cfg.user}:${cfg.group} ${cfg.databaseDir}
+          chown ${cfg.user}:${cfg.group} ${cfg.viewIndexDir}
+        fi
+        '';
+
+      serviceConfig = {
+        PermissionsStartOnly = true;
+        User = cfg.user;
+        Group = cfg.group;
+        ExecStart = "${cfg.package}/bin/couchdb -a ${configFile} -a ${pkgs.writeText "couchdb-extra.ini" cfg.extraConfig}";
+      };
+    };
+
+    users.extraUsers.couchdb = {
+      description = "CouchDB Server user";
+      group = "couchdb";
+      uid = config.ids.uids.couchdb;
+    };
+
+    users.extraGroups.couchdb.gid = config.ids.gids.couchdb;
+
+  };
+}
diff --git a/nixos/modules/services/databases/firebird.nix b/nixos/modules/services/databases/firebird.nix
index 75c225bdb67b..f9f7e9d7c518 100644
--- a/nixos/modules/services/databases/firebird.nix
+++ b/nixos/modules/services/databases/firebird.nix
@@ -49,6 +49,7 @@ in
 
       package = mkOption {
         default = pkgs.firebirdSuper;
+        type = types.package;
         /*
           Example: <code>package = pkgs.firebirdSuper.override { icu =
             pkgs.icu; };</code> which is not recommended for compatibility
diff --git a/nixos/modules/services/databases/mongodb.nix b/nixos/modules/services/databases/mongodb.nix
index 213a60687b23..a2f2593e7973 100644
--- a/nixos/modules/services/databases/mongodb.nix
+++ b/nixos/modules/services/databases/mongodb.nix
@@ -39,6 +39,7 @@ in
 
       package = mkOption {
         default = pkgs.mongodb;
+        type = types.package;
         description = "
           Which MongoDB derivation to use.
         ";
diff --git a/nixos/modules/services/databases/mysql.nix b/nixos/modules/services/databases/mysql.nix
index 8be05a27cdcb..7e42438fe6d5 100644
--- a/nixos/modules/services/databases/mysql.nix
+++ b/nixos/modules/services/databases/mysql.nix
@@ -8,10 +8,14 @@ let
 
   mysql = cfg.package;
 
+  is55 = mysql.mysqlVersion == "5.5";
+
+  mysqldDir = if is55 then "${mysql}/bin" else "${mysql}/libexec";
+
   pidFile = "${cfg.pidDir}/mysqld.pid";
 
   mysqldOptions =
-    "--user=${cfg.user} --datadir=${cfg.dataDir} " +
+    "--user=${cfg.user} --datadir=${cfg.dataDir} --basedir=${mysql} " +
     "--pid-file=${pidFile}";
 
   myCnf = pkgs.writeText "my.cnf"
@@ -19,7 +23,7 @@ let
     [mysqld]
     ${optionalString (cfg.replication.role == "master" || cfg.replication.role == "slave") "log-bin=mysql-bin"}
     ${optionalString (cfg.replication.role == "master" || cfg.replication.role == "slave") "server-id = ${toString cfg.replication.serverId}"}
-    ${optionalString (cfg.replication.role == "slave")
+    ${optionalString (cfg.replication.role == "slave" && !is55)
     ''
       master-host = ${cfg.replication.masterHost}
       master-user = ${cfg.replication.masterUser}
@@ -47,7 +51,8 @@ in
       };
 
       package = mkOption {
-        default = pkgs.mysql;
+        type = types.package;
+        example = literalExample "pkgs.mysql";
         description = "
           Which MySQL derivation to use.
         ";
@@ -176,7 +181,7 @@ in
             chown -R ${cfg.user} ${cfg.pidDir}
           '';
 
-        serviceConfig.ExecStart = "${mysql}/libexec/mysqld --defaults-extra-file=${myCnf} ${mysqldOptions}";
+        serviceConfig.ExecStart = "${mysqldDir}/mysqld --defaults-extra-file=${myCnf} ${mysqldOptions}";
 
         postStart =
           ''
@@ -216,6 +221,16 @@ in
                     fi
                   '') cfg.initialDatabases}
 
+                ${optionalString (cfg.replication.role == "slave" && is55)
+                  ''
+                    # Set up the replication master
+
+                    ( echo "stop slave;"
+                      echo "change master to master_host='${cfg.replication.masterHost}', master_user='${cfg.replication.masterUser}', master_password='${cfg.replication.masterPassword}';"
+                      echo "start slave;"
+                    ) | ${mysql}/bin/mysql -u root -N
+                  ''}
+
                 ${optionalString (cfg.initialScript != null)
                   ''
                     # Execute initial script
diff --git a/nixos/modules/services/databases/mysql55.nix b/nixos/modules/services/databases/mysql55.nix
deleted file mode 100644
index fe8b29e3c6b7..000000000000
--- a/nixos/modules/services/databases/mysql55.nix
+++ /dev/null
@@ -1,248 +0,0 @@
-{ config, pkgs, ... }:
-
-with pkgs.lib;
-
-let
-
-  cfg = config.services.mysql55;
-
-  mysql = cfg.package;
-
-  pidFile = "${cfg.pidDir}/mysqld.pid";
-
-  mysqldOptions =
-    "--user=${cfg.user} --datadir=${cfg.dataDir} " +
-    "--pid-file=${pidFile}";
-
-  myCnf = pkgs.writeText "my.cnf"
-  ''
-    [mysqld]
-    ${optionalString (cfg.replication.role == "master" || cfg.replication.role == "slave") "log-bin=mysql-bin"}
-    ${optionalString (cfg.replication.role == "master" || cfg.replication.role == "slave") "server-id = ${toString cfg.replication.serverId}"}
-    ${cfg.extraOptions}
-  '';
-
-in
-
-{
-
-  ###### interface
-
-  options = {
-
-    services.mysql55 = {
-
-      enable = mkOption {
-        default = false;
-        description = "
-          Whether to enable the MySQL server.
-        ";
-      };
-
-      package = mkOption {
-        default = pkgs.mysql55;
-        description = "
-          Which MySQL derivation to use.
-        ";
-      };
-
-      port = mkOption {
-        default = "3306";
-        description = "Port of MySQL";
-      };
-
-      user = mkOption {
-        default = "mysql";
-        description = "User account under which MySQL runs";
-      };
-
-      dataDir = mkOption {
-        default = "/var/mysql"; # !!! should be /var/db/mysql
-        description = "Location where MySQL stores its table files";
-      };
-
-      pidDir = mkOption {
-        default = "/var/run/mysql";
-        description = "Location of the file which stores the PID of the MySQL server";
-      };
-
-      extraOptions = mkOption {
-        default = "";
-        example = ''
-          key_buffer_size = 6G
-          table_cache = 1600
-          log-error = /var/log/mysql_err.log
-        '';
-        description = ''
-          Provide extra options to the MySQL configuration file.
-
-          Please note, that these options are added to the
-          <literal>[mysqld]</literal> section so you don't need to explicitly
-          state it again.
-        '';
-      };
-
-      initialDatabases = mkOption {
-        default = [];
-        description = "List of database names and their initial schemas that should be used to create databases on the first startup of MySQL";
-        example = [
-          { name = "foodatabase"; schema = literalExample "./foodatabase.sql"; }
-          { name = "bardatabase"; schema = literalExample "./bardatabase.sql"; }
-        ];
-      };
-
-      initialScript = mkOption {
-        default = null;
-        description = "A file containing SQL statements to be executed on the first startup. Can be used for granting certain permissions on the database";
-      };
-
-      # FIXME: remove this option; it's a really bad idea.
-      rootPassword = mkOption {
-        default = null;
-        description = "Path to a file containing the root password, modified on the first startup. Not specifying a root password will leave the root password empty.";
-      };
-
-      replication = {
-        role = mkOption {
-          default = "none";
-          description = "Role of the MySQL server instance. Can be either: master, slave or none";
-        };
-
-        serverId = mkOption {
-          default = 1;
-          description = "Id of the MySQL server instance. This number must be unique for each instance";
-        };
-
-        masterHost = mkOption {
-          description = "Hostname of the MySQL master server";
-        };
-
-        masterUser = mkOption {
-          description = "Username of the MySQL replication user";
-        };
-
-        masterPassword = mkOption {
-          description = "Password of the MySQL replication user";
-        };
-
-        masterPort = mkOption {
-          default = 3306;
-          description = "Port number on which the MySQL master server runs";
-        };
-      };
-    };
-
-  };
-
-
-  ###### implementation
-
-  config = mkIf config.services.mysql55.enable {
-
-    users.extraUsers.mysql = {
-      description = "MySQL server user";
-      group = "mysql";
-      uid = config.ids.uids.mysql;
-    };
-
-    users.extraGroups.mysql.gid = config.ids.gids.mysql;
-
-    environment.systemPackages = [mysql];
-
-    systemd.services.mysql =
-      { description = "MySQL Server";
-
-        wantedBy = [ "multi-user.target" ];
-
-        unitConfig.RequiresMountsFor = "${cfg.dataDir}";
-
-        preStart =
-          ''
-            if ! test -e ${cfg.dataDir}/mysql; then
-                mkdir -m 0700 -p ${cfg.dataDir}
-                chown -R ${cfg.user} ${cfg.dataDir}
-                ${mysql}/bin/mysql_install_db ${mysqldOptions}
-                touch /tmp/mysql_init
-            fi
-
-            mkdir -m 0700 -p ${cfg.pidDir}
-            chown -R ${cfg.user} ${cfg.pidDir}
-          '';
-
-        serviceConfig.ExecStart = "${mysql}/bin/mysqld --defaults-extra-file=${myCnf} ${mysqldOptions}";
-
-        postStart =
-          ''
-            # Wait until the MySQL server is available for use
-            count=0
-            while [ ! -e /tmp/mysql.sock ]
-            do
-                if [ $count -eq 30 ]
-                then
-                    echo "Tried 30 times, giving up..."
-                    exit 1
-                fi
-
-                echo "MySQL daemon not yet started. Waiting for 1 second..."
-                count=$((count++))
-                sleep 1
-            done
-
-            if [ -f /tmp/mysql_init ]
-            then
-                ${concatMapStrings (database:
-                  ''
-                    # Create initial databases
-                    if ! test -e "${cfg.dataDir}/${database.name}"; then
-                        echo "Creating initial database: ${database.name}"
-                        ( echo "create database ${database.name};"
-                          echo "use ${database.name};"
-
-                          if [ -f "${database.schema}" ]
-                          then
-                              cat ${database.schema}
-                          elif [ -d "${database.schema}" ]
-                          then
-                              cat ${database.schema}/mysql-databases/*.sql
-                          fi
-                        ) | ${mysql}/bin/mysql -u root -N
-                    fi
-                  '') cfg.initialDatabases}
-                
-                ${optionalString (cfg.replication.role == "slave")
-                  ''
-                    # Set up the replication master
-                    
-                    ( echo "stop slave;"
-                      echo "change master to master_host='${cfg.replication.masterHost}', master_user='${cfg.replication.masterUser}', master_password='${cfg.replication.masterPassword}';"
-                      echo "start slave;"
-                    ) | ${mysql}/bin/mysql -u root -N
-                  ''}
-
-                ${optionalString (cfg.initialScript != null)
-                  ''
-                    # Execute initial script
-                    cat ${cfg.initialScript} | ${mysql}/bin/mysql -u root -N
-                  ''}
-
-                ${optionalString (cfg.rootPassword != null)
-                  ''
-                    # Change root password
-
-                    ( echo "use mysql;"
-                      echo "update user set Password=password('$(cat ${cfg.rootPassword})') where User='root';"
-                      echo "flush privileges;"
-                    ) | ${mysql}/bin/mysql -u root -N
-                  ''}
-
-              rm /tmp/mysql_init
-            fi
-          ''; # */
-
-        serviceConfig.ExecStop =
-          "${mysql}/bin/mysqladmin ${optionalString (cfg.rootPassword != null) "--user=root --password=\"$(cat ${cfg.rootPassword})\""} shutdown";
-      };
-
-  };
-
-}
diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix
index 265d26e8ce98..cc4230d4d6a7 100644
--- a/nixos/modules/services/databases/postgresql.nix
+++ b/nixos/modules/services/databases/postgresql.nix
@@ -55,7 +55,7 @@ in
       };
 
       package = mkOption {
-        type = types.path;
+        type = types.package;
         example = literalExample "pkgs.postgresql92";
         description = ''
           PostgreSQL package to use.
diff --git a/nixos/modules/services/databases/redis.nix b/nixos/modules/services/databases/redis.nix
index ea6399ba4f44..9a4ce36a5cb1 100644
--- a/nixos/modules/services/databases/redis.nix
+++ b/nixos/modules/services/databases/redis.nix
@@ -45,6 +45,7 @@ in
       package = mkOption {
         default = pkgs.redis;
         description = "Which Redis derivation to use.";
+        type = types.package;
       };
 
       user = mkOption {
diff --git a/nixos/modules/services/logging/logcheck.nix b/nixos/modules/services/logging/logcheck.nix
index 2a6a6516f488..ef147f95358c 100644
--- a/nixos/modules/services/logging/logcheck.nix
+++ b/nixos/modules/services/logging/logcheck.nix
@@ -208,12 +208,13 @@ in
         mapAttrsToList writeIgnoreRule cfg.ignore
         ++ mapAttrsToList writeIgnoreCronRule cfg.ignoreCron;
 
-    users.extraUsers = singleton
-      { name = cfg.user;
+    users.extraUsers = optionalAttrs (cfg.user == "logcheck") (singleton
+      { name = "logcheck";
+        uid = config.ids.uids.logcheck;
         shell = "/bin/sh";
         description = "Logcheck user account";
         extraGroups = cfg.extraGroups;
-      };
+      });
 
     system.activationScripts.logcheck = ''
       mkdir -m 700 -p /var/{lib,lock}/logcheck
diff --git a/nixos/modules/services/mail/opensmtpd.nix b/nixos/modules/services/mail/opensmtpd.nix
index 2732fd602004..0c18c464520d 100644
--- a/nixos/modules/services/mail/opensmtpd.nix
+++ b/nixos/modules/services/mail/opensmtpd.nix
@@ -79,5 +79,10 @@ in {
       preStart = "mkdir -p /var/spool";
       serviceConfig.ExecStart = "${opensmtpd}/sbin/smtpd -d -f ${conf} ${args}";
     };
+
+    environment.systemPackages = [ (pkgs.runCommand "opensmtpd-sendmail" {} ''
+      mkdir -p $out/bin
+      ln -s ${opensmtpd}/sbin/smtpctl $out/bin/sendmail
+    '') ];
   };
 }
diff --git a/nixos/modules/services/misc/cgminer.nix b/nixos/modules/services/misc/cgminer.nix
index f715013b51f3..20b7c34e886e 100644
--- a/nixos/modules/services/misc/cgminer.nix
+++ b/nixos/modules/services/misc/cgminer.nix
@@ -42,6 +42,7 @@ in
       package = mkOption {
         default = pkgs.cgminer;
         description = "Which cgminer derivation to use.";
+        type = types.package;
       };
 
       user = mkOption {
diff --git a/nixos/modules/services/misc/dictd.nix b/nixos/modules/services/misc/dictd.nix
index b84fbb3e1281..fd68f8470102 100644
--- a/nixos/modules/services/misc/dictd.nix
+++ b/nixos/modules/services/misc/dictd.nix
@@ -43,10 +43,12 @@ with pkgs.lib;
         group = "dictd";
         description = "DICT.org dictd server";
         home = "${dictdb}/share/dictd";
+        uid = config.ids.uids.dictd;
       };
 
     users.extraGroups = singleton
       { name = "dictd";
+        gid = config.ids.gids.dictd;
       };
 
     jobs.dictd =
diff --git a/nixos/modules/services/misc/nix-daemon.nix b/nixos/modules/services/misc/nix-daemon.nix
index cca42aa11009..785d43973473 100644
--- a/nixos/modules/services/misc/nix-daemon.nix
+++ b/nixos/modules/services/misc/nix-daemon.nix
@@ -58,7 +58,7 @@ in
     nix = {
 
       package = mkOption {
-        type = types.path;
+        type = types.package;
         default = pkgs.nix;
         description = ''
           This option specifies the Nix package instance to use throughout the system.
@@ -286,8 +286,8 @@ in
     systemd.services."nix-daemon" =
       { description = "Nix Daemon";
 
-        path = [ nix pkgs.openssl pkgs.utillinux ]
-          ++ optionals cfg.distributedBuilds [ pkgs.openssh pkgs.gzip ];
+        path = [ nix pkgs.openssl pkgs.utillinux pkgs.openssh ]
+          ++ optionals cfg.distributedBuilds [ pkgs.gzip ];
 
         environment = cfg.envVars // { CURL_CA_BUNDLE = "/etc/ssl/certs/ca-bundle.crt"; };
 
diff --git a/nixos/modules/services/misc/nix-ssh-serve.nix b/nixos/modules/services/misc/nix-ssh-serve.nix
new file mode 100644
index 000000000000..80e7961b1f82
--- /dev/null
+++ b/nixos/modules/services/misc/nix-ssh-serve.nix
@@ -0,0 +1,45 @@
+{ config, lib, pkgs, ... }:
+
+let
+  serveOnly = pkgs.writeScript "nix-store-serve" ''
+    #!${pkgs.stdenv.shell}
+    if [ "$SSH_ORIGINAL_COMMAND" != "nix-store --serve" ]; then
+      echo 'Error: You are only allowed to run `nix-store --serve'\'''!' >&2
+      exit 1
+    fi
+    exec /run/current-system/sw/bin/nix-store --serve
+  '';
+
+  inherit (lib) mkIf mkOption types;
+in {
+  options = {
+    nix.sshServe = {
+      enable = mkOption {
+        description = "Whether to enable serving the nix store over ssh.";
+        default = false;
+        type = types.bool;
+      };
+    };
+  };
+
+  config = mkIf config.nix.sshServe.enable {
+    users.extraUsers.nix-ssh = {
+      description = "User for running nix-store --serve.";
+      uid = config.ids.uids.nix-ssh;
+      shell = pkgs.stdenv.shell;
+    };
+
+    services.openssh.enable = true;
+
+    services.openssh.extraConfig = ''
+      Match User nix-ssh
+        AllowAgentForwarding no
+        AllowTcpForwarding no
+        PermitTTY no
+        PermitTunnel no
+        X11Forwarding no
+        ForceCommand ${serveOnly}
+      Match All
+    '';
+  };
+}
diff --git a/nixos/modules/services/misc/nixos-manual.nix b/nixos/modules/services/misc/nixos-manual.nix
index 1a8b85db3290..9a5b170d5e66 100644
--- a/nixos/modules/services/misc/nixos-manual.nix
+++ b/nixos/modules/services/misc/nixos-manual.nix
@@ -63,7 +63,7 @@ in
 
     services.nixosManual.showManual = mkOption {
       type = types.bool;
-      default = false;
+      default = true;
       description = ''
         Whether to show the NixOS manual on one of the virtual
         consoles.
diff --git a/nixos/modules/services/monitoring/apcupsd.nix b/nixos/modules/services/monitoring/apcupsd.nix
index 58ec8a49694b..38fd53653d62 100644
--- a/nixos/modules/services/monitoring/apcupsd.nix
+++ b/nixos/modules/services/monitoring/apcupsd.nix
@@ -168,11 +168,11 @@ in
     # shuts off power.) Copied from here:
     # http://forums.opensuse.org/english/get-technical-help-here/applications/479499-apcupsd-systemd-killpower-issues.html
     systemd.services.apcupsd-killpower = {
+      description = "APC UPS Kill Power";
       after = [ "shutdown.target" ]; # append umount.target?
       before = [ "final.target" ];
       wantedBy = [ "shutdown.target" ];
       unitConfig = {
-        Description = "APC UPS Kill Power";
         ConditionPathExists = "/run/apcupsd/powerfail";
         DefaultDependencies = "no";
       };
diff --git a/nixos/modules/services/monitoring/munin.nix b/nixos/modules/services/monitoring/munin.nix
index 153f49429029..39c4fb1aefac 100644
--- a/nixos/modules/services/monitoring/munin.nix
+++ b/nixos/modules/services/monitoring/munin.nix
@@ -173,10 +173,12 @@ in
       name = "munin";
       description = "Munin monitoring user";
       group = "munin";
+      uid = config.ids.uids.munin;
     }];
 
     users.extraGroups = [{
       name = "munin";
+      gid = config.ids.gids.munin;
     }];
 
   }) (mkIf nodeCfg.enable {
diff --git a/nixos/modules/services/networking/dhcpcd.nix b/nixos/modules/services/networking/dhcpcd.nix
index 08a5d6de6a2b..d4ec96a18f6f 100644
--- a/nixos/modules/services/networking/dhcpcd.nix
+++ b/nixos/modules/services/networking/dhcpcd.nix
@@ -106,7 +106,6 @@ in
       { description = "DHCP Client";
 
         wantedBy = [ "network.target" ];
-        after = [ "systemd-udev-settle.service" ];
 
         # Stopping dhcpcd during a reconfiguration is undesirable
         # because it brings down the network interfaces configured by
diff --git a/nixos/modules/services/networking/firewall.nix b/nixos/modules/services/networking/firewall.nix
index 3c0c51e6ec8a..07e05fa6d051 100644
--- a/nixos/modules/services/networking/firewall.nix
+++ b/nixos/modules/services/networking/firewall.nix
@@ -128,6 +128,17 @@ in
         '';
     };
 
+    networking.firewall.allowedTCPPortRanges = mkOption {
+      default = [];
+      example = [ { from = 8999; to = 9003; } ];
+      type = types.listOf (types.attrsOf types.int);
+      description =
+        ''
+          A range of TCP ports on which incoming connections are
+          accepted.
+        '';
+    };
+
     networking.firewall.allowedUDPPorts = mkOption {
       default = [];
       example = [ 53 ];
@@ -138,6 +149,16 @@ in
         '';
     };
 
+    networking.firewall.allowedUDPPortRanges = mkOption {
+      default = [];
+      example = [ { from = 60000; to = 61000; } ];
+      type = types.listOf (types.attrsOf types.int);
+      description =
+        ''
+          Range of open UDP ports.
+        '';
+    };
+
     networking.firewall.allowPing = mkOption {
       default = false;
       type = types.bool;
@@ -150,6 +171,17 @@ in
         '';
     };
 
+    networking.firewall.pingLimit = mkOption {
+      default = null;
+      type = types.nullOr (types.separatedString " ");
+      description =
+        ''
+          If pings are allowed, this allows setting rate limits
+          on them. If non-null, this option should be in the form
+          of flags like "-limit 1/minute -limit-burst 5"
+        '';
+    };
+
     networking.firewall.checkReversePath = mkOption {
       default = kernelHasRPFilter;
       type = types.bool;
@@ -322,6 +354,15 @@ in
               ) cfg.allowedTCPPorts
             }
 
+            # Accept connections to the allowed TCP port ranges.
+            ${concatMapStrings (rangeAttr:
+                let range = toString rangeAttr.from + ":" + toString rangeAttr.to; in
+                ''
+                  ip46tables -A nixos-fw -p tcp --dport ${range} -j nixos-fw-accept
+                ''
+              ) cfg.allowedTCPPortRanges
+            }
+
             # Accept packets on the allowed UDP ports.
             ${concatMapStrings (port:
                 ''
@@ -330,13 +371,24 @@ in
               ) cfg.allowedUDPPorts
             }
 
+            # Accept packets on the allowed UDP port ranges.
+            ${concatMapStrings (rangeAttr:
+                let range = toString rangeAttr.from + ":" + toString rangeAttr.to; in
+                ''
+                  ip46tables -A nixos-fw -p udp --dport ${range} -j nixos-fw-accept
+                ''
+              ) cfg.allowedUDPPortRanges
+            }
+
             # Accept IPv4 multicast.  Not a big security risk since
             # probably nobody is listening anyway.
             #iptables -A nixos-fw -d 224.0.0.0/4 -j nixos-fw-accept
 
             # Optionally respond to ICMPv4 pings.
             ${optionalString cfg.allowPing ''
-              iptables -A nixos-fw -p icmp --icmp-type echo-request -j nixos-fw-accept
+              iptables -A nixos-fw -p icmp --icmp-type echo-request ${optionalString (cfg.pingLimit != null)
+                "-m limit ${cfg.pingLimit} "
+              }-j nixos-fw-accept
             ''}
 
             # Accept all ICMPv6 messages except redirects and node
diff --git a/nixos/modules/services/networking/git-daemon.nix b/nixos/modules/services/networking/git-daemon.nix
index a7c7c206198f..2e7c9c68e2fa 100644
--- a/nixos/modules/services/networking/git-daemon.nix
+++ b/nixos/modules/services/networking/git-daemon.nix
@@ -101,7 +101,7 @@ in
       name = "git-daemon";
       startOn = "ip-up";
       exec = "${pkgs.git}/bin/git daemon --reuseaddr "
-        + (optionalString (cfg.basePath != "") "--basepath=${cfg.basePath} ")
+        + (optionalString (cfg.basePath != "") "--base-path=${cfg.basePath} ")
         + (optionalString (cfg.listenAddress != "") "--listen=${cfg.listenAddress} ")
         + "--port=${toString cfg.port} --user=${gitUser} --group=${gitUser} ${cfg.options} "
         + "--verbose " + (optionalString cfg.exportAll "--export-all")  + concatStringsSep " " cfg.repositories;
diff --git a/nixos/modules/services/networking/kippo.nix b/nixos/modules/services/networking/kippo.nix
index 76dd66013ba7..164c38940f77 100644
--- a/nixos/modules/services/networking/kippo.nix
+++ b/nixos/modules/services/networking/kippo.nix
@@ -76,8 +76,9 @@ rec {
     users.extraUsers = singleton {
       name = "kippo";
       description = "kippo web server privilege separation user";
+      uid = 108; # why does config.ids.uids.kippo give an error?
     };
-    users.extraGroups = singleton { name = "kippo"; };
+    users.extraGroups = singleton { name = "kippo";gid=108; };
 
     systemd.services.kippo = with pkgs; {
       description = "Kippo Web Server";
diff --git a/nixos/modules/services/networking/networkmanager.nix b/nixos/modules/services/networking/networkmanager.nix
index 0b079e3567a1..fd5ff5e44983 100644
--- a/nixos/modules/services/networking/networkmanager.nix
+++ b/nixos/modules/services/networking/networkmanager.nix
@@ -31,7 +31,7 @@ let
 
     [modem-manager]
     Identity=unix-group:networkmanager
-    Action=org.freedesktop.ModemManager.*
+    Action=org.freedesktop.ModemManager*
     ResultAny=yes
     ResultInactive=no
     ResultActive=yes
@@ -42,7 +42,7 @@ let
         subject.isInGroup("networkmanager")
         && subject.active
         && (action.id.indexOf("org.freedesktop.NetworkManager.") == 0
-            || action.id.indexOf("org.freedesktop.ModemManager.")  == 0
+            || action.id.indexOf("org.freedesktop.ModemManager")  == 0
         ))
           { return polkit.Result.YES; }
     });
@@ -89,7 +89,7 @@ in {
           to change network settings to this group.
         '';
       };
-  
+
       packages = mkOption {
         type = types.listOf types.path;
         default = [ ];
@@ -161,6 +161,7 @@ in {
         networkmanager_vpnc
         networkmanager_openconnect
         networkmanager_pptp
+        modemmanager
         ];
 
     users.extraGroups = singleton {
@@ -176,16 +177,13 @@ in {
     systemd.services."networkmanager-init" = {
       description = "NetworkManager initialisation";
       wantedBy = [ "network.target" ];
-      partOf = [ "NetworkManager.service" ];
       wants = [ "NetworkManager.service" ];
       before = [ "NetworkManager.service" ];
       script = ''
         mkdir -m 700 -p /etc/NetworkManager/system-connections
         mkdir -m 755 -p ${stateDirs}
       '';
-      serviceConfig = {
-        Type = "oneshot";
-      };
+      serviceConfig.Type = "oneshot";
     };
 
     # Turn off NixOS' network management
@@ -206,6 +204,7 @@ in {
         networkmanager_vpnc
         networkmanager_openconnect
         networkmanager_pptp
+        modemmanager
         ];
 
     services.udev.packages = cfg.packages;
diff --git a/nixos/modules/services/networking/ntpd.nix b/nixos/modules/services/networking/ntpd.nix
index e5e164021d3a..3d388cb10648 100644
--- a/nixos/modules/services/networking/ntpd.nix
+++ b/nixos/modules/services/networking/ntpd.nix
@@ -15,6 +15,11 @@ let
     # chroot to ${stateDir}, we have to specify it as /ntp.drift.
     driftfile /ntp.drift
 
+    restrict default kod nomodify notrap nopeer noquery
+    restrict -6 default kod nomodify notrap nopeer noquery
+    restrict 127.0.0.1
+    restrict -6 ::1
+
     ${toString (map (server: "server " + server + " iburst\n") config.services.ntp.servers)}
   '';
 
diff --git a/nixos/modules/services/networking/searx.nix b/nixos/modules/services/networking/searx.nix
new file mode 100644
index 000000000000..30f21151a762
--- /dev/null
+++ b/nixos/modules/services/networking/searx.nix
@@ -0,0 +1,75 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.searx;
+
+  configFile = cfg.configFile;
+
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    services.searx = {
+
+      enable = mkOption {
+        default = false;
+        description = "
+          Whether to enable the Searx server. See https://github.com/asciimoo/searx
+        ";
+      };
+
+      configFile = mkOption {
+        default = "";
+        description = "
+          The path of the Searx server configuration file. If no file
+          is specified, a default file is used (default config file has
+          debug mode enabled).
+        ";
+      };
+
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.services.searx.enable {
+
+    users.extraUsers.searx =
+      { uid = config.ids.uids.searx;
+        description = "Searx user";
+        createHome = true;
+        home = "/var/lib/searx";
+      };
+
+    users.extraGroups.searx =
+      { gid = config.ids.gids.searx;
+      };
+
+    systemd.services.searx =
+      {
+        description = "Searx server, the meta search engine.";
+        after = [ "network.target" ];
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig = {
+          User = "searx";
+          ExecStart = "${pkgs.pythonPackages.searx}/bin/searx-run";
+        };
+      } // (optionalAttrs (configFile != "") {
+        environment.SEARX_SETTINGS_PATH = configFile;
+      });
+        
+
+    environment.systemPackages = [ pkgs.pythonPackages.searx ];
+
+  };
+
+}
diff --git a/nixos/modules/services/networking/vsftpd.nix b/nixos/modules/services/networking/vsftpd.nix
index 1c77cc6df4ea..763fa8dc3c42 100644
--- a/nixos/modules/services/networking/vsftpd.nix
+++ b/nixos/modules/services/networking/vsftpd.nix
@@ -91,6 +91,7 @@ let
       ${optionalString (pkgs.stdenv.system == "x86_64-linux") ''
         seccomp_sandbox=NO
       ''}
+      anon_umask=${cfg.anonymousUmask}
     '';
 
 in
@@ -139,6 +140,13 @@ in
         description = "RSA certificate file.";
       };
 
+      anonymousUmask = mkOption {
+        type = types.string;
+        default = "077";
+        example = "002";
+        description = "Anonymous write umask.";
+      };
+
     } // (listToAttrs (catAttrs "nixosOption" optionDescription));
 
   };
diff --git a/nixos/modules/services/printing/cupsd.nix b/nixos/modules/services/printing/cupsd.nix
index 1be3587c3bb9..fb8923e107a8 100644
--- a/nixos/modules/services/printing/cupsd.nix
+++ b/nixos/modules/services/printing/cupsd.nix
@@ -143,7 +143,9 @@ in
       };
 
     services.printing.drivers =
-      [ pkgs.cups pkgs.cups_pdf_filter pkgs.ghostscript additionalBackends pkgs.perl pkgs.coreutils pkgs.gnused ];
+      [ pkgs.cups pkgs.cups_pdf_filter pkgs.ghostscript additionalBackends
+        pkgs.perl pkgs.coreutils pkgs.gnused pkgs.bc pkgs.gawk pkgs.gnugrep
+	];
 
     services.printing.cupsdConf =
       ''
diff --git a/nixos/modules/services/search/solr.nix b/nixos/modules/services/search/solr.nix
new file mode 100644
index 000000000000..eab18c4229b7
--- /dev/null
+++ b/nixos/modules/services/search/solr.nix
@@ -0,0 +1,114 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.solr;
+
+in {
+
+  options = {
+    services.solr = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enables the solr service.
+        '';
+      };
+
+      javaPackage = mkOption {
+        type = types.package;
+        default = pkgs.openjre;
+        description = ''
+          Which Java derivation to use for running solr.
+        '';
+      };
+
+      solrPackage = mkOption {
+        type = types.package;
+        default = pkgs.solr;
+        description = ''
+          Which solr derivation to use for running solr.
+        '';
+      };
+
+      log4jConfiguration = mkOption {
+        type = types.lines;
+        default = ''
+          log4j.rootLogger=INFO, stdout
+          log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+          log4j.appender.stdout.Target=System.out
+          log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+          log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
+        '';
+        description = ''
+          Contents of the <literal>log4j.properties</literal> used. By default,
+          everything is logged to stdout (picked up by systemd) with level INFO.
+        '';
+      };
+
+      user = mkOption {
+        type = types.str;
+        description = ''
+          The user that should run the solr process and.
+          the working directories.
+        '';
+      };
+
+      group = mkOption {
+        type = types.str;
+        description = ''
+          The group that will own the working directory.
+        '';
+      };
+
+      solrHome = mkOption {
+        type = types.str;
+        description = ''
+          The solr home directory. It is your own responsibility to
+          make sure this directory contains a working solr configuration,
+          and is writeable by the the user running the solr service.
+          Failing to do so, the solr will not start properly.
+        '';
+      };
+
+      extraJavaOptions = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          Extra command line options given to the java process running
+          solr.
+        '';
+      };
+
+      extraWinstoneOptions = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          Extra command line options given to the Winstone, which is
+          the servlet container hosting solr.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    services.winstone.solr = {
+      serviceName = "solr";
+      inherit (cfg) user group javaPackage;
+      warFile = "${cfg.solrPackage}/lib/solr.war";
+      extraOptions = [
+        "--commonLibFolder=${cfg.solrPackage}/lib/ext"
+      ] ++ cfg.extraWinstoneOptions;
+      extraJavaOptions = [
+        "-Dsolr.solr.home=${cfg.solrHome}"
+        "-Dlog4j.configuration=file://${pkgs.writeText "log4j.properties" cfg.log4jConfiguration}"
+      ] ++ cfg.extraJavaOptions;
+    };
+
+  };
+
+}
diff --git a/nixos/modules/services/system/nscd.nix b/nixos/modules/services/system/nscd.nix
index b817b1df779f..3259bdbbe128 100644
--- a/nixos/modules/services/system/nscd.nix
+++ b/nixos/modules/services/system/nscd.nix
@@ -5,6 +5,7 @@ with pkgs.lib;
 let
 
   nssModulesPath = config.system.nssModules.path;
+  cfg = config.services.nscd;
 
   inherit (pkgs.lib) singleton;
 
@@ -24,6 +25,12 @@ in
         description = "Whether to enable the Name Service Cache Daemon.";
       };
 
+      config = mkOption {
+        type = types.lines;
+        default = builtins.readFile ./nscd.conf;
+        description = "Configuration to use for Name Service Cache Daemon.";
+      };
+
     };
 
   };
@@ -31,7 +38,7 @@ in
 
   ###### implementation
 
-  config = mkIf config.services.nscd.enable {
+  config = mkIf cfg.enable {
 
     users.extraUsers = singleton
       { name = "nscd";
@@ -56,7 +63,7 @@ in
         restartTriggers = [ config.environment.etc.hosts.source ];
 
         serviceConfig =
-          { ExecStart = "@${pkgs.glibc}/sbin/nscd nscd -f ${./nscd.conf}";
+          { ExecStart = "@${pkgs.glibc}/sbin/nscd nscd -f ${pkgs.writeText "nscd.conf" cfg.config}";
             Type = "forking";
             PIDFile = "/run/nscd/nscd.pid";
             Restart = "always";
diff --git a/nixos/modules/services/ttys/agetty.nix b/nixos/modules/services/ttys/agetty.nix
index ae4fa87d4b7b..ca4fbeb0add3 100644
--- a/nixos/modules/services/ttys/agetty.nix
+++ b/nixos/modules/services/ttys/agetty.nix
@@ -28,6 +28,17 @@ with pkgs.lib;
         '';
       };
 
+      serialSpeed = mkOption {
+        type = types.listOf types.int;
+        default = [ 115200 57600 38400 9600 ];
+        example = [ 38400 9600 ];
+        description = ''
+            Bitrates to allow for agetty's listening on serial ports. Listing more
+            bitrates gives more interoperability but at the cost of long delays
+            for getting a sync on the line.
+        '';
+      };
+
     };
 
   };
@@ -37,81 +48,25 @@ with pkgs.lib;
 
   config = {
 
-    # FIXME: these are mostly copy/pasted from the systemd sources,
-    # which some small modifications, which is annoying.
-
-    # Generate a separate job for each tty.
-    systemd.units."getty@.service".text =
-      ''
-        [Unit]
-        Description=Getty on %I
-        Documentation=man:agetty(8)
-        After=systemd-user-sessions.service plymouth-quit-wait.service
-
-        # If additional gettys are spawned during boot then we should make
-        # sure that this is synchronized before getty.target, even though
-        # getty.target didn't actually pull it in.
-        Before=getty.target
-        IgnoreOnIsolate=yes
-
-        ConditionPathExists=/dev/tty0
-
-        [Service]
-        Environment=TERM=linux
-        Environment=LOCALE_ARCHIVE=/run/current-system/sw/lib/locale/locale-archive
-        ExecStart=@${pkgs.utillinux}/sbin/agetty agetty --noclear --login-program ${pkgs.shadow}/bin/login %I 38400
-        Type=idle
-        Restart=always
-        RestartSec=0
-        UtmpIdentifier=%I
-        TTYPath=/dev/%I
-        TTYReset=yes
-        TTYVHangup=yes
-        TTYVTDisallocate=yes # set to no to prevent clearing the screen
-        KillMode=process
-        IgnoreSIGPIPE=no
-
-        # Some login implementations ignore SIGTERM, so we send SIGHUP
-        # instead, to ensure that login terminates cleanly.
-        KillSignal=SIGHUP
-
-        X-RestartIfChanged=false
-      '';
-    
-    systemd.units."serial-getty@.service".text =
-      ''
-        [Unit]
-        Description=Serial Getty on %I
-        Documentation=man:agetty(8) man:systemd-getty-generator(8)
-        BindsTo=dev-%i.device
-        After=dev-%i.device systemd-user-sessions.service plymouth-quit-wait.service
-
-        # If additional gettys are spawned during boot then we should make
-        # sure that this is synchronized before getty.target, even though
-        # getty.target didn't actually pull it in.
-        Before=getty.target
-        IgnoreOnIsolate=yes
-
-        [Service]
-        Environment=TERM=linux
-        Environment=LOCALE_ARCHIVE=/run/current-system/sw/lib/locale/locale-archive
-        ExecStart=@${pkgs.utillinux}/sbin/agetty agetty --login-program ${pkgs.shadow}/bin/login %I 115200,57600,38400,9600
-        Type=idle
-        Restart=always
-        RestartSec=0
-        UtmpIdentifier=%I
-        TTYPath=/dev/%I
-        TTYReset=yes
-        TTYVHangup=yes
-        KillMode=process
-        IgnoreSIGPIPE=no
-
-        # Some login implementations ignore SIGTERM, so we send SIGHUP
-        # instead, to ensure that login terminates cleanly.
-        KillSignal=SIGHUP
-        
-        X-RestartIfChanged=false
-      '';
+    systemd.services."getty@" =
+      { baseUnit = pkgs.runCommand "getty.service" {}
+          ''
+            sed '/ExecStart/ d' < ${config.systemd.package}/example/systemd/system/getty@.service > $out
+          '';
+        serviceConfig.ExecStart = "@${pkgs.utillinux}/sbin/agetty agetty --noclear --login-program ${pkgs.shadow}/bin/login %I 38400";
+        restartIfChanged = false;
+      };
+
+    systemd.services."serial-getty@" =
+      { baseUnit = pkgs.runCommand "serial-getty.service" {}
+          ''
+            sed '/ExecStart/ d' < ${config.systemd.package}/example/systemd/system/serial-getty@.service > $out
+          '';
+        serviceConfig.ExecStart =
+          let speeds = concatStringsSep "," (map toString config.services.mingetty.serialSpeed);
+          in "@${pkgs.utillinux}/sbin/agetty agetty --login-program ${pkgs.shadow}/bin/login %I ${speeds}";
+        restartIfChanged = false;
+      };
 
     environment.etc = singleton
       { # Friendly greeting on the virtual consoles.
diff --git a/nixos/modules/services/ttys/kmscon.nix b/nixos/modules/services/ttys/kmscon.nix
index 302e660a7bff..70555e5d8825 100644
--- a/nixos/modules/services/ttys/kmscon.nix
+++ b/nixos/modules/services/ttys/kmscon.nix
@@ -44,6 +44,7 @@ in {
       After=systemd-user-sessions.service
       After=plymouth-quit-wait.service
       After=systemd-logind.service
+      After=systemd-vconsole-setup.service
       Requires=systemd-logind.service
       Before=getty.target
       Conflicts=getty@%i.service
@@ -62,17 +63,19 @@ in {
       X-RestartIfChanged=false
     '';
 
-    systemd.units."autovt@.service".linkTarget = "${config.systemd.units."kmsconvt@.service".unit}/kmsconvt@.service";
-
-    systemd.services."systemd-vconsole-setup".restartIfChanged = false;
+    systemd.units."autovt@.service".unit = pkgs.runCommand "unit" { }
+        ''
+          mkdir -p $out
+          ln -s ${config.systemd.units."kmsconvt@.service".unit}/kmsconvt@.service $out/autovt@.service
+        '';
 
-    systemd.units."kmsconvt@tty1.service".extraConfig.wait-for-vconsole-setup = "After=systemd-vconsole-setup.service";
+    systemd.services.systemd-vconsole-setup.restartIfChanged = false;
 
     services.kmscon.extraConfig = mkIf cfg.hwRender ''
       drm
       hwaccel
     '';
 
-    services.mesa.enable = mkIf cfg.hwRender true;
+    hardware.opengl.enable = mkIf cfg.hwRender true;
   };
 }
diff --git a/nixos/modules/services/web-servers/apache-httpd/default.nix b/nixos/modules/services/web-servers/apache-httpd/default.nix
index 2552ec18bb92..a22ef10312d4 100644
--- a/nixos/modules/services/web-servers/apache-httpd/default.nix
+++ b/nixos/modules/services/web-servers/apache-httpd/default.nix
@@ -63,6 +63,7 @@ let
           enablePHP = false;
           phpOptions = "";
           options = {};
+          documentRoot = null;
         };
         res = defaults // svcFunction { inherit config pkgs serverInfo php; };
       in res;
@@ -188,7 +189,11 @@ let
 
     subservices = callSubservices serverInfo cfg.extraSubservices;
 
-    documentRoot = if cfg.documentRoot != null then cfg.documentRoot else
+    maybeDocumentRoot = fold (svc: acc:
+      if acc == null then svc.documentRoot else assert svc.documentRoot == null; acc
+    ) null ([ cfg ] ++ subservices);
+
+    documentRoot = if maybeDocumentRoot != null then maybeDocumentRoot else
       pkgs.runCommand "empty" {} "ensureDir $out";
 
     documentRootConf = ''
@@ -240,7 +245,7 @@ let
 
     ${robotsConf}
 
-    ${if isMainServer || cfg.documentRoot != null then documentRootConf else ""}
+    ${if isMainServer || maybeDocumentRoot != null then documentRootConf else ""}
 
     ${if cfg.enableUserDir then ''
 
@@ -414,7 +419,7 @@ in
       };
 
       package = mkOption {
-        type = types.path;
+        type = types.package;
         default = pkgs.apacheHttpd.override { mpm = mainCfg.multiProcessingModule; };
         example = "pkgs.apacheHttpd_2_4";
         description = ''
@@ -628,8 +633,8 @@ in
           ++ concatMap (svc: svc.extraServerPath) allSubservices;
 
         environment =
-          { PHPRC = if enablePHP then phpIni else "";
-          } // (listToAttrs (concatMap (svc: svc.globalEnvVars) allSubservices));
+          optionalAttrs enablePHP { PHPRC = phpIni; }
+          // (listToAttrs (concatMap (svc: svc.globalEnvVars) allSubservices));
 
         preStart =
           ''
diff --git a/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix b/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix
index 423087991e13..f5669faebc97 100644
--- a/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix
+++ b/nixos/modules/services/web-servers/apache-httpd/mediawiki.nix
@@ -93,6 +93,10 @@ let
         ensureDir $out
         cp -r * $out
         cp ${mediawikiConfig} $out/LocalSettings.php
+        sed -i 's|/bin/bash|${pkgs.stdenv.shell}|' \
+          $out/maintenance/fuzz-tester.php \
+          $out/bin/ulimit.sh \
+          $out/includes/GlobalFunctions.php
       '';
   };
 
@@ -122,7 +126,18 @@ in
         </Directory>
       ''}
 
-      Alias ${config.urlPrefix} ${mediawikiRoot}
+      ${if config.urlPrefix != "" then "Alias ${config.urlPrefix} ${mediawikiRoot}" else ''
+        RewriteEngine On
+        RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f
+        RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-d
+        RewriteRule ${if config.enableUploads
+          then "!^/images"
+          else "^.*\$"
+        } %{DOCUMENT_ROOT}/${if config.articleUrlPrefix == ""
+          then ""
+          else "${config.articleUrlPrefix}/"
+        }index.php [L]
+      ''}
 
       <Directory ${mediawikiRoot}>
           Order allow,deny
@@ -135,6 +150,8 @@ in
       ''}
     '';
 
+  documentRoot = if config.urlPrefix == "" then mediawikiRoot else null;
+
   enablePHP = true;
 
   options = {
@@ -290,6 +307,7 @@ in
             echo COMMIT
           ) | ${pkgs.postgresql}/bin/psql -U "${config.dbUser}" "${config.dbName}"
       fi
+      ${php}/bin/php ${mediawikiRoot}/maintenance/update.php
     '');
 
   robotsEntries = optionalString (config.articleUrlPrefix != "")
diff --git a/nixos/modules/services/web-servers/nginx/default.nix b/nixos/modules/services/web-servers/nginx/default.nix
index 4a1b6de2873f..1a39fe43bbee 100644
--- a/nixos/modules/services/web-servers/nginx/default.nix
+++ b/nixos/modules/services/web-servers/nginx/default.nix
@@ -9,6 +9,7 @@ let
     user ${cfg.user} ${cfg.group};
     daemon off;
     ${cfg.config}
+    ${cfg.appendConfig}
   '';
 in
 
@@ -24,6 +25,7 @@ in
 
       package = mkOption {
         default = pkgs.nginx;
+        type = types.package;
         description = "
           Nginx package to use.
         ";
@@ -36,6 +38,19 @@ in
         ";
       };
 
+      appendConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Configuration lines appended to the generated Nginx
+          configuration file. Commonly used by different modules
+          providing http snippets. <option>appendConfig</option>
+          can be specified more than once and it's value will be
+          concatenated (contrary to <option>config</option> which
+          can be set only once).
+        '';
+      };
+
       stateDir = mkOption {
         default = "/var/spool/nginx";
         description = "
diff --git a/nixos/modules/services/web-servers/phpfpm.nix b/nixos/modules/services/web-servers/phpfpm.nix
new file mode 100644
index 000000000000..76ec41244627
--- /dev/null
+++ b/nixos/modules/services/web-servers/phpfpm.nix
@@ -0,0 +1,77 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  cfg = config.services.phpfpm;
+
+  stateDir = "/run/phpfpm";
+
+  pidFile = "${stateDir}/phpfpm.pid";
+
+  cfgFile = pkgs.writeText "phpfpm.conf" ''
+    [global]
+    pid = ${pidFile}
+    error_log = syslog
+    daemonize = yes
+    ${cfg.extraConfig}
+
+    ${concatStringsSep "\n" (mapAttrsToList (n: v: "[${n}]\n${v}") cfg.poolConfigs)}
+  '';
+
+in {
+
+  options = {
+    services.phpfpm = {
+      extraConfig = mkOption {
+        type = types.lines;
+        default = "";
+        description = ''
+          Extra configuration that should be put in the global section of
+          the PHP FPM configuration file. Do not specify the options
+          <literal>pid</literal>, <literal>error_log</literal> or
+          <literal>daemonize</literal> here, since they are generated by
+          NixOS.
+        '';
+      };
+
+      poolConfigs = mkOption {
+        type = types.attrsOf types.lines;
+        default = {};
+        example = {
+          mypool = ''
+            listen = /run/phpfpm/mypool
+            user = nobody
+            pm = dynamic
+            pm.max_children = 75
+            pm.start_servers = 10
+            pm.min_spare_servers = 5
+            pm.max_spare_servers = 20
+            pm.max_requests = 500
+          '';
+        };
+        description = ''
+          A mapping between PHP FPM pool names and their configurations.
+          See the documentation on <literal>php-fpm.conf</literal> for
+          details on configuration directives. If no pools are defined,
+          the phpfpm service is disabled.
+        '';
+      };
+    };
+  };
+
+  config = mkIf (cfg.poolConfigs != {}) {
+
+    systemd.services.phpfpm = {
+      wantedBy = [ "multi-user.target" ];
+      preStart = ''
+        mkdir -p "${stateDir}"
+      '';
+      serviceConfig = {
+        ExecStart = "${pkgs.php54}/sbin/php-fpm -y ${cfgFile}";
+        PIDFile = pidFile;
+      };
+    };
+
+  };
+}
diff --git a/nixos/modules/services/web-servers/winstone.nix b/nixos/modules/services/web-servers/winstone.nix
new file mode 100644
index 000000000000..33c7e7301182
--- /dev/null
+++ b/nixos/modules/services/web-servers/winstone.nix
@@ -0,0 +1,129 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  cfg = config.services.winstone;
+
+  winstoneOpts = { name, ... }: {
+    options = {
+      name = mkOption {
+        default = name;
+        internal = true;
+      };
+
+      serviceName = mkOption {
+        type = types.str;
+        description = ''
+          The name of the systemd service. By default, it is
+          derived from the winstone instance name.
+        '';
+      };
+
+      warFile = mkOption {
+        type = types.str;
+        description = ''
+          The WAR file that Winstone should serve.
+        '';
+      };
+
+      javaPackage = mkOption {
+        type = types.package;
+        default = pkgs.openjre;
+        description = ''
+          Which Java derivation to use for running Winstone.
+        '';
+      };
+
+      user = mkOption {
+        type = types.str;
+        description = ''
+          The user that should run this Winstone process and
+          own the working directory.
+        '';
+      };
+
+      group = mkOption {
+        type = types.str;
+        description = ''
+          The group that will own the working directory.
+        '';
+      };
+
+      workDir = mkOption {
+        type = types.str;
+        description = ''
+          The working directory for this Winstone instance. Will
+          contain extracted webapps etc. The directory will be
+          created if it doesn't exist.
+        '';
+      };
+
+      extraJavaOptions = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          Extra command line options given to the java process running
+          Winstone.
+        '';
+      };
+
+      extraOptions = mkOption {
+        type = types.listOf types.str;
+        default = [];
+        description = ''
+          Extra command line options given to the Winstone process.
+        '';
+      };
+    };
+
+    config = {
+      workDir = mkDefault "/run/winstone/${name}";
+      serviceName = mkDefault "winstone-${name}";
+    };
+  };
+
+  mkService = cfg: let
+    opts = concatStringsSep " " (cfg.extraOptions ++ [
+      "--warfile ${cfg.warFile}"
+    ]);
+
+    javaOpts = concatStringsSep " " (cfg.extraJavaOptions ++ [
+      "-Djava.io.tmpdir=${cfg.workDir}"
+      "-jar ${pkgs.winstone}/lib/winstone.jar"
+    ]);
+  in {
+    wantedBy = [ "multi-user.target" ];
+    description = "winstone service for ${cfg.name}";
+    preStart = ''
+      mkdir -p "${cfg.workDir}"
+      chown ${cfg.user}:${cfg.group} "${cfg.workDir}"
+    '';
+    serviceConfig = {
+      ExecStart = "${cfg.javaPackage}/bin/java ${javaOpts} ${opts}";
+      User = cfg.user;
+      PermissionsStartOnly = true;
+    };
+  };
+
+in {
+
+  options = {
+    services.winstone = mkOption {
+      default = {};
+      type = types.attrsOf types.optionSet;
+      options = [ winstoneOpts ];
+      description = ''
+        Defines independent Winstone services, each serving one WAR-file.
+      '';
+    };
+  };
+
+  config = mkIf (cfg != {}) {
+
+    systemd.services = mapAttrs' (n: c: nameValuePair c.serviceName (mkService c)) cfg;
+
+  };
+
+}
diff --git a/nixos/modules/services/x11/desktop-managers/default.nix b/nixos/modules/services/x11/desktop-managers/default.nix
index 035b23b4e1bb..753babf6a225 100644
--- a/nixos/modules/services/x11/desktop-managers/default.nix
+++ b/nixos/modules/services/x11/desktop-managers/default.nix
@@ -7,9 +7,9 @@ let
   xcfg = config.services.xserver;
   cfg = xcfg.desktopManager;
 
-  # Whether desktop manager `d' is capable of setting a background.
-  # If it isn't, the `feh' program is used as a fallback.
-  needBGCond = d: ! (d ? bgSupport && d.bgSupport);
+  # If desktop manager `d' isn't capable of setting a background and
+  # the xserver is enabled, the `feh' program is used as a fallback.
+  needBGCond = d: ! (d ? bgSupport && d.bgSupport) && xcfg.enable;
 
 in
 
@@ -17,7 +17,7 @@ in
   # Note: the order in which desktop manager modules are imported here
   # determines the default: later modules (if enabled) are preferred.
   # E.g., if KDE is enabled, it supersedes xterm.
-  imports = [ ./none.nix ./xterm.nix ./xfce.nix ./kde4.nix ./e17.nix ];
+  imports = [ ./none.nix ./xterm.nix ./xfce.nix ./kde4.nix ./e17.nix ./gnome3.nix ./xbmc.nix ];
 
   options = {
 
diff --git a/nixos/modules/services/x11/desktop-managers/gnome3.nix b/nixos/modules/services/x11/desktop-managers/gnome3.nix
new file mode 100644
index 000000000000..456975015a22
--- /dev/null
+++ b/nixos/modules/services/x11/desktop-managers/gnome3.nix
@@ -0,0 +1,68 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+  cfg = config.services.xserver.desktopManager.gnome3;
+  gnome3 = pkgs.gnome3;
+in {
+
+  options = {
+
+    services.xserver.desktopManager.gnome3.enable = mkOption {
+      default = false;
+      example = true;
+      description = "Enable Gnome 3 desktop manager.";
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    # Enable helpful DBus services.
+    security.polkit.enable = true;
+    services.udisks2.enable = true;
+    networking.networkmanager.enable = true;
+    services.upower.enable = config.powerManagement.enable;
+
+    fonts.extraFonts = [ pkgs.dejavu_fonts ];
+
+    services.xserver.desktopManager.session = singleton
+      { name = "gnome3";
+        start = ''
+          # Set GTK_DATA_PREFIX so that GTK+ can find the themes
+          export GTK_DATA_PREFIX=${config.system.path}
+
+          # find theme engines
+          export GTK_PATH=${config.system.path}/lib/gtk-3.0:${config.system.path}/lib/gtk-2.0
+
+          export XDG_MENU_PREFIX=gnome
+
+          ${gnome3.gnome_session}/bin/gnome-session&
+          waitPID=$!
+        '';
+      };
+
+    environment.variables.GIO_EXTRA_MODULES = "${gnome3.dconf}/lib/gio/modules";
+    environment.systemPackages =
+      [ gnome3.evince
+        gnome3.eog
+        gnome3.dconf
+        gnome3.vino
+        gnome3.epiphany
+        gnome3.baobab
+        gnome3.gucharmap
+        gnome3.nautilus
+        gnome3.yelp
+        pkgs.ibus
+        gnome3.gnome_shell
+        gnome3.gnome_settings_daemon
+        gnome3.gnome_terminal
+        gnome3.gnome_icon_theme
+        gnome3.gnome_themes_standard
+        gnome3.gnome_control_center
+      ];
+  };
+
+
+}
diff --git a/nixos/modules/services/x11/desktop-managers/xbmc.nix b/nixos/modules/services/x11/desktop-managers/xbmc.nix
new file mode 100644
index 000000000000..51278c5fadb6
--- /dev/null
+++ b/nixos/modules/services/x11/desktop-managers/xbmc.nix
@@ -0,0 +1,31 @@
+{ pkgs, config, ... }:
+
+with pkgs.lib;
+
+let
+  cfg = config.services.xserver.desktopManager.xbmc;
+in
+
+{
+  options = {
+    services.xserver.desktopManager.xbmc = {
+      enable = mkOption {
+        default = false;
+        example = true;
+        description = "Enable the xbmc multimedia center.";
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.xserver.desktopManager.session = [{
+      name = "xbmc";
+      start = ''
+        ${pkgs.xbmc}/bin/xbmc --lircdev /var/run/lirc/lircd --standalone &
+        waitPID=$!
+      '';
+    }];
+    
+    environment.systemPackages = [ pkgs.xbmc ];
+  };
+}
\ No newline at end of file
diff --git a/nixos/modules/services/x11/desktop-managers/xfce.nix b/nixos/modules/services/x11/desktop-managers/xfce.nix
index 5e5fab3ed2bb..3c67571ffd5b 100644
--- a/nixos/modules/services/x11/desktop-managers/xfce.nix
+++ b/nixos/modules/services/x11/desktop-managers/xfce.nix
@@ -29,7 +29,7 @@ in
         start =
           ''
             # Set GTK_PATH so that GTK+ can find the theme engines.
-            export GTK_PATH=${config.system.path}/lib/gtk-2.0
+            export GTK_PATH="${config.system.path}/lib/gtk-2.0:${config.system.path}/lib/gtk-3.0"
 
             # Set GTK_DATA_PREFIX so that GTK+ can find the Xfce themes.
             export GTK_DATA_PREFIX=${config.system.path}
diff --git a/nixos/modules/services/x11/display-managers/lightdm.nix b/nixos/modules/services/x11/display-managers/lightdm.nix
index e4125891e6cb..0fa16a77c1b9 100644
--- a/nixos/modules/services/x11/display-managers/lightdm.nix
+++ b/nixos/modules/services/x11/display-managers/lightdm.nix
@@ -55,8 +55,8 @@ let
     ''
       [LightDM]
       greeter-user = ${config.users.extraUsers.lightdm.name}
-      xgreeters-directory = ${cfg.greeter.package}
-      xsessions-directory = ${dmcfg.session.desktops}
+      greeters-directory = ${cfg.greeter.package}
+      sessions-directory = ${dmcfg.session.desktops}
 
       [SeatDefaults]
       xserver-command = ${xserverWrapper}
diff --git a/nixos/modules/services/x11/hardware/synaptics.nix b/nixos/modules/services/x11/hardware/synaptics.nix
index 91e01f2e30b1..fe9dcd51a7a6 100644
--- a/nixos/modules/services/x11/hardware/synaptics.nix
+++ b/nixos/modules/services/x11/hardware/synaptics.nix
@@ -2,9 +2,23 @@
 
 with pkgs.lib;
 
-let cfg = config.services.xserver.synaptics; in
-
-{
+let cfg = config.services.xserver.synaptics;
+    tapConfig = if cfg.tapButtons then enabledTapConfig else disabledTapConfig;
+    enabledTapConfig = ''
+      Option "MaxTapTime" "180"
+      Option "MaxTapMove" "220"
+      Option "TapButton1" "${builtins.elemAt cfg.buttonsMap 0}"
+      Option "TapButton2" "${builtins.elemAt cfg.buttonsMap 1}"
+      Option "TapButton3" "${builtins.elemAt cfg.buttonsMap 2}"
+    '';
+    disabledTapConfig = ''
+      Option "MaxTapTime" "0"
+      Option "MaxTapMove" "0"
+      Option "TapButton1" "0"
+      Option "TapButton2" "0"
+      Option "TapButton3" "0"
+    '';
+in {
 
   options = {
 
@@ -106,15 +120,10 @@ let cfg = config.services.xserver.synaptics; in
           MatchIsTouchpad "on"
           ${optionalString (cfg.dev != null) ''MatchDevicePath "${cfg.dev}"''}
           Driver "synaptics"
-          Option "MaxTapTime" "180"
-          Option "MaxTapMove" "220"
           Option "MinSpeed" "${cfg.minSpeed}"
           Option "MaxSpeed" "${cfg.maxSpeed}"
           Option "AccelFactor" "${cfg.accelFactor}"
-          ${if cfg.tapButtons then "" else ''Option "MaxTapTime" "0"''}
-          Option "TapButton1" "${builtins.elemAt cfg.buttonsMap 0}"
-          Option "TapButton2" "${builtins.elemAt cfg.buttonsMap 1}"
-          Option "TapButton3" "${builtins.elemAt cfg.buttonsMap 2}"
+          ${optionalString cfg.tapButtons tapConfig}
           Option "ClickFinger1" "${builtins.elemAt cfg.buttonsMap 0}"
           Option "ClickFinger2" "${builtins.elemAt cfg.buttonsMap 1}"
           Option "ClickFinger3" "${builtins.elemAt cfg.buttonsMap 2}"
diff --git a/nixos/modules/services/x11/redshift.nix b/nixos/modules/services/x11/redshift.nix
index b9ad962d8e46..02416f5ef129 100644
--- a/nixos/modules/services/x11/redshift.nix
+++ b/nixos/modules/services/x11/redshift.nix
@@ -14,24 +14,37 @@ in {
 
     services.redshift.latitude = mkOption {
       description = "Your current latitude";
-      type = types.string;
+      type = types.uniq types.string;
     };
 
     services.redshift.longitude = mkOption {
       description = "Your current longitude";
-      type = types.string;
+      type = types.uniq types.string;
     };
 
     services.redshift.temperature = {
       day = mkOption {
         description = "Colour temperature to use during day time";
         default = 5500;
-        type = types.int;
+        type = types.uniq types.int;
       };
       night = mkOption {
         description = "Colour temperature to use during night time";
         default = 3700;
-        type = types.int;
+        type = types.uniq types.int;
+      };
+    };
+
+    services.redshift.brightness = {
+      day = mkOption {
+        description = "Screen brightness to apply during the day (between 0.1 and 1.0)";
+        default = "1";
+        type = types.uniq types.string;
+      };
+      night = mkOption {
+        description = "Screen brightness to apply during the night (between 0.1 and 1.0)";
+        default = "1";
+        type = types.uniq types.string;
       };
     };
   };
@@ -41,10 +54,12 @@ in {
       description = "Redshift colour temperature adjuster";
       requires = [ "display-manager.service" ];
       after = [ "display-manager.service" ];
-      script = ''
+      wantedBy = [ "graphical.target" ];
+      serviceConfig.ExecStart = ''
         ${pkgs.redshift}/bin/redshift \
           -l ${cfg.latitude}:${cfg.longitude} \
-          -t ${toString cfg.temperature.day}:${toString cfg.temperature.night}
+          -t ${toString cfg.temperature.day}:${toString cfg.temperature.night} \
+          -b ${toString cfg.brightness.day}:${toString cfg.brightness.night}
       '';
       environment = { DISPLAY = ":0"; };
       serviceConfig.Restart = "always";
diff --git a/nixos/modules/services/x11/terminal-server.nix b/nixos/modules/services/x11/terminal-server.nix
index bf9c3435503d..f16a424b4573 100644
--- a/nixos/modules/services/x11/terminal-server.nix
+++ b/nixos/modules/services/x11/terminal-server.nix
@@ -27,7 +27,7 @@ in
   config = {
 
     services.xserver.enable = true;
-    services.xserver.videoDrivers = [];
+    hardware.opengl.videoDrivers = [];
 
     # Enable KDM.  Any display manager will do as long as it supports XDMCP.
     services.xserver.displayManager.kdm.enable = true;
diff --git a/nixos/modules/services/x11/window-managers/default.nix b/nixos/modules/services/x11/window-managers/default.nix
index 4d52e398b477..1c10333462c8 100644
--- a/nixos/modules/services/x11/window-managers/default.nix
+++ b/nixos/modules/services/x11/window-managers/default.nix
@@ -16,7 +16,6 @@ in
       ./wmii.nix
       ./xmonad.nix
       ./i3.nix
-      ./xbmc.nix
       ./herbstluftwm.nix
     ];
 
diff --git a/nixos/modules/services/x11/window-managers/xbmc.nix b/nixos/modules/services/x11/window-managers/xbmc.nix
deleted file mode 100644
index 46494202b404..000000000000
--- a/nixos/modules/services/x11/window-managers/xbmc.nix
+++ /dev/null
@@ -1,31 +0,0 @@
-{pkgs, config, ...}:
-
-let
-  inherit (pkgs.lib) mkOption mkIf;
-  cfg = config.services.xserver.windowManager.xbmc;
-in
-
-{
-  options = {
-    services.xserver.windowManager.xbmc = {
-      enable = mkOption {
-        default = false;
-        example = true;
-        description = "Enable the xbmc multimedia center.";
-      };
-    };
-  };
-
-  config = mkIf cfg.enable {
-    services.xserver.windowManager = {
-      session = [{
-        name = "xbmc";
-        start = "
-          ${pkgs.xbmc}/bin/xbmc --lircdev /var/run/lirc/lircd --standalone &
-          waitPID=$!
-        ";
-      }];
-    };
-    environment.systemPackages = [ pkgs.xbmc ];
-  };
-}
diff --git a/nixos/modules/services/x11/xserver.nix b/nixos/modules/services/x11/xserver.nix
index 5600ce7fac13..2677f7584565 100644
--- a/nixos/modules/services/x11/xserver.nix
+++ b/nixos/modules/services/x11/xserver.nix
@@ -22,7 +22,7 @@ let
     virtualbox   = { modules = [ kernelPackages.virtualboxGuestAdditions ]; driverName = "vboxvideo"; };
   };
 
-  driverNames = config.services.mesa.videoDrivers;
+  driverNames = config.hardware.opengl.videoDrivers;
 
   drivers = flip map driverNames
     (name: { inherit name; driverName = name; } //
@@ -181,7 +181,7 @@ in
         description = ''
           The name of the video driver for your graphics card.  This
           option is obsolete; please set the
-          <option>services.mesa.videoDrivers</option> instead.
+          <option>hardware.opengl.videoDrivers</option> instead.
         '';
       };
 
@@ -381,8 +381,8 @@ in
   ###### implementation
 
   config = mkIf cfg.enable {
-    services.mesa.enable = true;
-    services.mesa.videoDrivers = mkIf (cfg.videoDriver != null) [ cfg.videoDriver ];
+    hardware.opengl.enable = true;
+    hardware.opengl.videoDrivers = mkIf (cfg.videoDriver != null) [ cfg.videoDriver ];
 
     assertions =
       [ { assertion = !(cfg.startOpenSSHAgent && cfg.startGnuPGAgent);
diff --git a/nixos/modules/system/activation/switch-to-configuration.pl b/nixos/modules/system/activation/switch-to-configuration.pl
index 33ae3aef9fca..e0649448c834 100644
--- a/nixos/modules/system/activation/switch-to-configuration.pl
+++ b/nixos/modules/system/activation/switch-to-configuration.pl
@@ -93,8 +93,13 @@ sub parseFstab {
 
 sub parseUnit {
     my ($filename) = @_;
+    parseKeyValues(read_file($filename));
+}
+
+sub parseKeyValues {
+    my @lines = @_;
     my $info = {};
-    foreach my $line (read_file($filename)) {
+    foreach my $line (@_) {
         # FIXME: not quite correct.
         $line =~ /^([^=]+)=(.*)$/ or next;
         $info->{$1} = $2;
@@ -123,7 +128,7 @@ while (my ($unit, $state) = each %{$activePrev}) {
     $baseName =~ s/\.[a-z]*$//;
 
     if (-e $prevUnitFile && ($state->{state} eq "active" || $state->{state} eq "activating")) {
-        if (! -e $newUnitFile) {
+        if (! -e $newUnitFile || abs_path($newUnitFile) eq "/dev/null") {
             push @unitsToStop, $unit;
         }
 
@@ -337,8 +342,21 @@ system("@systemd@/bin/systemctl", "reload", "dbus.service");
 my (@failed, @new, @restarting);
 my $activeNew = getActiveUnits;
 while (my ($unit, $state) = each %{$activeNew}) {
-    push @failed, $unit if $state->{state} eq "failed" || $state->{substate} eq "auto-restart";
-    push @new, $unit if $state->{state} ne "failed" && !defined $activePrev->{$unit};
+    if ($state->{state} eq "failed") {
+        push @failed, $unit;
+    }
+    elsif ($state->{state} eq "auto-restart") {
+        # A unit in auto-restart state is a failure *if* it previously failed to start
+        my $lines = `@systemd@/bin/systemctl show '$unit'`;
+        my $info = parseKeyValues(split "\n", $lines);
+
+        if ($info->{ExecMainStatus} ne '0') {
+            push @failed, $unit;
+        }
+    }
+    elsif ($state->{state} ne "failed" && !defined $activePrev->{$unit}) {
+        push @new, $unit;
+    }
 }
 
 print STDERR "the following new units were started: ", join(", ", sort(@new)), "\n"
diff --git a/nixos/modules/system/boot/kernel.nix b/nixos/modules/system/boot/kernel.nix
index 2b075bf6a6d2..327f3b7e1128 100644
--- a/nixos/modules/system/boot/kernel.nix
+++ b/nixos/modules/system/boot/kernel.nix
@@ -159,7 +159,7 @@ in
 
     boot.kernel.sysctl."kernel.printk" = config.boot.consoleLogLevel;
 
-    boot.kernelModules = [ "loop" ];
+    boot.kernelModules = [ "loop" "configs" ];
 
     boot.initrd.availableKernelModules =
       [ # Note: most of these (especially the SATA/PATA modules)
diff --git a/nixos/modules/system/boot/loader/grub/memtest.nix b/nixos/modules/system/boot/loader/grub/memtest.nix
index 80c1a160cfde..afe194e672ae 100644
--- a/nixos/modules/system/boot/loader/grub/memtest.nix
+++ b/nixos/modules/system/boot/loader/grub/memtest.nix
@@ -6,28 +6,82 @@ with pkgs.lib;
 
 let
   memtest86 = pkgs.memtest86plus;
+  cfg = config.boot.loader.grub.memtest86;
 in
 
 {
   options = {
 
-    boot.loader.grub.memtest86 = mkOption {
-      default = false;
-      type = types.bool;
-      description = ''
-        Make Memtest86+, a memory testing program, available from the
-        GRUB boot menu.
-      '';
+    boot.loader.grub.memtest86 = {
+
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Make Memtest86+, a memory testing program, available from the
+          GRUB boot menu.
+        '';
+      };
+
+      params = mkOption {
+        default = [];
+        example = [ "console=ttyS0,115200" ];
+        type = types.listOf types.str;
+        description = ''
+          Parameters added to the Memtest86+ command line. As of memtest86+ 5.01
+          the following list of (apparently undocumented) parameters are
+          accepted:
+
+          <itemizedlist>
+
+          <listitem>
+            <para><literal>console=...</literal>, set up a serial console.
+            Examples:
+            <literal>console=ttyS0</literal>,
+            <literal>console=ttyS0,9600</literal> or
+            <literal>console=ttyS0,115200n8</literal>.</para>
+          </listitem>
+
+          <listitem>
+            <para><literal>btrace</literal>, enable boot trace.</para>
+          </listitem>
+
+          <listitem>
+            <para><literal>maxcpus=N</literal>, limit number of CPUs.</para>
+          </listitem>
+
+          <listitem>
+            <para><literal>onepass</literal>, run one pass and exit if there
+            are no errors.</para>
+          </listitem>
+
+          <listitem>
+            <para><literal>tstlist=...</literal>, list of tests to run.
+            Example: <literal>0,1,2</literal>.</para>
+          </listitem>
+
+          <listitem>
+            <para><literal>cpumask=...</literal>, set a CPU mask, to select CPUs
+            to use for testing.</para>
+          </listitem>
+
+          </itemizedlist>
+
+          This list of command line options was obtained by reading the
+          Memtest86+ source code.
+        '';
+      };
+
     };
   };
 
-  config = mkIf config.boot.loader.grub.memtest86 {
+  config = mkIf cfg.enable {
 
     boot.loader.grub.extraEntries =
       if config.boot.loader.grub.version == 2 then
         ''
           menuentry "Memtest86+" {
-            linux16 @bootRoot@/memtest.bin
+            linux16 @bootRoot@/memtest.bin ${toString cfg.params}
           }
         ''
       else
diff --git a/nixos/modules/system/boot/loader/gummiboot/gummiboot-builder.py b/nixos/modules/system/boot/loader/gummiboot/gummiboot-builder.py
index 9ea224b51f63..db73544181b6 100644
--- a/nixos/modules/system/boot/loader/gummiboot/gummiboot-builder.py
+++ b/nixos/modules/system/boot/loader/gummiboot/gummiboot-builder.py
@@ -9,7 +9,6 @@ import tempfile
 import errno
 
 def copy_if_not_exists(source, dest):
-    known_paths.append(dest)
     if not os.path.exists(dest):
         shutil.copyfile(source, dest)
 
@@ -38,12 +37,13 @@ def write_loader_conf(generation):
         print >> f, "default nixos-generation-%d" % (generation)
     os.rename("@efiSysMountPoint@/loader/loader.conf.tmp", "@efiSysMountPoint@/loader/loader.conf")
 
-def copy_from_profile(generation, name):
+def copy_from_profile(generation, name, dry_run=False):
     store_file_path = os.readlink("%s/%s" % (system_dir(generation), name))
     suffix = os.path.basename(store_file_path)
     store_dir = os.path.basename(os.path.dirname(store_file_path))
     efi_file_path = "/efi/nixos/%s-%s.efi" % (store_dir, suffix)
-    copy_if_not_exists(store_file_path, "@efiSysMountPoint@%s" % (efi_file_path))
+    if not dry_run:
+        copy_if_not_exists(store_file_path, "@efiSysMountPoint@%s" % (efi_file_path))
     return efi_file_path
 
 def add_entry(generation):
@@ -72,6 +72,10 @@ def get_generations(profile):
 def remove_old_entries(gens):
     slice_start = len("@efiSysMountPoint@/loader/entries/nixos-generation-")
     slice_end = -1 * len(".conf")
+    known_paths = []
+    for gen in gens:
+        known_paths.append(copy_from_profile(gen, "kernel", True))
+        known_paths.append(copy_from_profile(gen, "initrd", True))
     for path in glob.iglob("@efiSysMountPoint@/loader/entries/nixos-generation-[1-9]*.conf"):
         try:
             gen = int(path[slice_start:slice_end])
@@ -94,7 +98,6 @@ if os.getenv("NIXOS_INSTALL_GRUB") == "1":
     else:
         subprocess.check_call(["@gummiboot@/bin/gummiboot", "--path=@efiSysMountPoint@", "--no-variables", "install"])
 
-known_paths = []
 mkdir_p("@efiSysMountPoint@/efi/nixos")
 mkdir_p("@efiSysMountPoint@/loader/entries")
 try:
@@ -106,9 +109,8 @@ except IOError as e:
     machine_id = None
 
 gens = get_generations("system")
+remove_old_entries(gens)
 for gen in gens:
     add_entry(gen)
     if os.readlink(system_dir(gen)) == args.default_config:
         write_loader_conf(gen)
-
-remove_old_entries(gens)
diff --git a/nixos/modules/system/boot/luksroot.nix b/nixos/modules/system/boot/luksroot.nix
index 8547682284f7..117c526fcd38 100644
--- a/nixos/modules/system/boot/luksroot.nix
+++ b/nixos/modules/system/boot/luksroot.nix
@@ -39,153 +39,123 @@ let
     ${optionalString (luks.yubikeySupport && (yubikey != null)) ''
 
     rbtohex() {
-        od -An -vtx1 | tr -d ' \n'
+        ( od -An -vtx1 | tr -d ' \n' )
     }
 
     hextorb() {
-        tr '[:lower:]' '[:upper:]' | sed -e 's|\([0-9A-F]\{2\}\)|\\\\\\x\1|gI' | xargs printf
-    }
-
-    take() {
-        local c="$1"
-        shift
-        head -c $c "$@"
-    }
-
-    drop() {
-        local c="$1"
-        shift
-        if [ -e "$1" ]; then
-            cat "$1" | ( dd of=/dev/null bs="$c" count=1 2>/dev/null ; dd 2>/dev/null )
-        else
-            ( dd of=/dev/null bs="$c" count=1 2>/dev/null ; dd 2>/dev/null )
-        fi
+        ( tr '[:lower:]' '[:upper:]' | sed -e 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/gI' | xargs printf )
     }
 
     open_yubikey() {
 
+        # Make all of these local to this function
+        # to prevent their values being leaked
+        local salt
+        local iterations
+        local k_user
+        local challenge
+        local response
+        local k_luks
+        local opened
+        local new_salt
+        local new_iterations
+        local new_challenge
+        local new_response
+        local new_k_luks
+
         mkdir -p ${yubikey.storage.mountPoint}
         mount -t ${yubikey.storage.fsType} ${toString yubikey.storage.device} ${yubikey.storage.mountPoint}
 
-        local uuid_r
-        local k_user
-        local challenge
-        local k_blob
-        local aes_blob_decrypted
-        local checksum_correct
-        local checksum
-        local uuid_luks
-        local user_record
-
-        uuid_luks="$(cryptsetup luksUUID ${device} | take 36 | tr -d '-')"
-
-        ${optionalString (!yubikey.multiUser) ''
-        user_record="$(cat ${yubikey.storage.mountPoint}${yubikey.storage.path})"
-        uuid_r="$(echo -n $user_record | take 32)"
-        ''}
+        salt="$(cat ${yubikey.storage.mountPoint}${yubikey.storage.path} | sed -n 1p | tr -d '\n')"
+        iterations="$(cat ${yubikey.storage.mountPoint}${yubikey.storage.path} | sed -n 2p | tr -d '\n')"
+        challenge="$(echo -n $salt | openssl-wrap dgst -binary -sha512 | rbtohex)"
+        response="$(ykchalresp -${toString yubikey.slot} -x $challenge 2>/dev/null)"
 
         for try in $(seq 3); do
 
-            ${optionalString yubikey.multiUser ''
-            local user_id
-            echo -n "Enter user id: "
-            read -s user_id
-            echo
-            ''}
-
             ${optionalString yubikey.twoFactor ''
             echo -n "Enter two-factor passphrase: "
             read -s k_user
             echo
             ''}
 
-            ${optionalString yubikey.multiUser ''
-            local user_id_hash
-            user_id_hash="$(echo -n $user_id | openssl-wrap dgst -binary -sha512 | rbtohex)"
-
-            user_record="$(sed -n -e /^$user_id_hash[^$]*$/p ${yubikey.storage.mountPoint}${yubikey.storage.path} | tr -d '\n')"
-
-            if [ ! -z "$user_record" ]; then
-                user_record="$(echo -n $user_record | drop 128)"
-                uuid_r="$(echo -n $user_record | take 32)"
-            ''}
-
-                challenge="$(echo -n $k_user$uuid_r$uuid_luks | openssl-wrap dgst -binary -sha1 | rbtohex)"
-
-                k_blob="$(ykchalresp -${toString yubikey.slot} -x $challenge 2>/dev/null)"
-
-                aes_blob_decrypted="$(echo -n $user_record | drop 32 | hextorb | openssl-wrap enc -d -aes-256-ctr -K $k_blob -iv $uuid_r | rbtohex)"
+            if [ ! -z "$k_user" ]; then
+                k_luks="$(echo -n $k_user | pbkdf2-sha512 ${toString yubikey.keyLength} $iterations $response | rbtohex)"
+            else
+                k_luks="$(echo | pbkdf2-sha512 ${toString yubikey.keyLength} $iterations $response | rbtohex)"
+            fi
 
-                checksum="$(echo -n $aes_blob_decrypted | drop 168)"
-                if [ "$(echo -n $aes_blob_decrypted | hextorb | take 84 | openssl-wrap dgst -binary -sha512 | rbtohex)" == "$checksum" ]; then
-                    checksum_correct=1
-                    break
-                else
-                    checksum_correct=0
-                    echo "Authentication failed!"
-                fi
+            echo -n "$k_luks" | hextorb | cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} --key-file=-
 
-            ${optionalString yubikey.multiUser ''
+            if [ $? == "0" ]; then
+                opened=true
+                break
             else
-                checksum_correct=0
+                opened=false
                 echo "Authentication failed!"
             fi
-            ''}
         done
 
-        if [ "$checksum_correct" != "1" ]; then
+        if [ "$opened" == false ]; then
             umount ${yubikey.storage.mountPoint}
             echo "Maximum authentication errors reached"
             exit 1
         fi
 
-        local k_yubi
-        k_yubi="$(echo -n $aes_blob_decrypted | take 40)"
+        echo -n "Gathering entropy for new salt (please enter random keys to generate entropy if this blocks for long)..."
+        for i in $(seq ${toString yubikey.saltLength}); do
+            byte="$(dd if=/dev/random bs=1 count=1 2>/dev/null | rbtohex)";
+            new_salt="$new_salt$byte";
+            echo -n .
+        done;
+        echo "ok"
 
-        local k_luks
-        k_luks="$(echo -n $aes_blob_decrypted | drop 40 | take 128)"
+        new_iterations="$iterations"
+        ${optionalString (yubikey.iterationStep > 0) ''
+        new_iterations="$(($new_iterations + ${toString yubikey.iterationStep}))"
+        ''}
 
-        echo -n "$k_luks" | hextorb | cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} --key-file=-
+        new_challenge="$(echo -n $new_salt | openssl-wrap dgst -binary -sha512 | rbtohex)"
 
-        update_failed=false
+        new_response="$(ykchalresp -${toString yubikey.slot} -x $new_challenge 2>/dev/null)"
 
-        local new_uuid_r
-        new_uuid_r="$(uuidgen)"
-        if [ $? != "0" ]; then
-            for try in $(seq 10); do
-                sleep 1
-                new_uuid_r="$(uuidgen)"
-                if [ $? == "0" ]; then break; fi
-                if [ $try -eq 10 ]; then update_failed=true; fi
-            done
+        if [ ! -z "$k_user" ]; then
+            new_k_luks="$(echo -n $k_user | pbkdf2-sha512 ${toString yubikey.keyLength} $new_iterations $new_response | rbtohex)"
+        else
+            new_k_luks="$(echo | pbkdf2-sha512 ${toString yubikey.keyLength} $new_iterations $new_response | rbtohex)"
         fi
 
-        if [ "$update_failed" == false ]; then
-            new_uuid_r="$(echo -n $new_uuid_r | take 36 | tr -d '-')"
-
-            local new_challenge
-            new_challenge="$(echo -n $k_user$new_uuid_r$uuid_luks | openssl-wrap dgst -binary -sha1 | rbtohex)"
-
-            local new_k_blob
-            new_k_blob="$(echo -n $new_challenge | hextorb | openssl-wrap dgst -binary -sha1 -mac HMAC -macopt hexkey:$k_yubi | rbtohex)"
+        mkdir -p ${yubikey.ramfsMountPoint}
+        # A ramfs is used here to ensure that the file used to update
+        # the key slot with cryptsetup will never get swapped out.
+        # Warning: Do NOT replace with tmpfs!
+        mount -t ramfs none ${yubikey.ramfsMountPoint}
 
-            local new_aes_blob
-            new_aes_blob=$(echo -n "$k_yubi$k_luks$checksum" | hextorb | openssl-wrap enc -e -aes-256-ctr -K "$new_k_blob" -iv "$new_uuid_r" | rbtohex)
+        echo -n "$new_k_luks" | hextorb > ${yubikey.ramfsMountPoint}/new_key
+        echo -n "$k_luks" | hextorb | cryptsetup luksChangeKey ${device} --key-file=- ${yubikey.ramfsMountPoint}/new_key
 
-            ${optionalString yubikey.multiUser ''
-            sed -i -e "s|^$user_id_hash$user_record|$user_id_hash$new_uuid_r$new_aes_blob|1"
-            ''}
-
-            ${optionalString (!yubikey.multiUser) ''
-            echo -n "$new_uuid_r$new_aes_blob" > ${yubikey.storage.mountPoint}${yubikey.storage.path}
-            ''}
+        if [ $? == "0" ]; then
+            echo -ne "$new_salt\n$new_iterations" > ${yubikey.storage.mountPoint}${yubikey.storage.path}
         else
-            echo "Warning: Could not obtain new UUID, current challenge persists!"
+            echo "Warning: Could not update LUKS key, current challenge persists!"
         fi
 
+        rm -f ${yubikey.ramfsMountPoint}/new_key
+        umount ${yubikey.ramfsMountPoint}
+        rm -rf ${yubikey.ramfsMountPoint}
+
         umount ${yubikey.storage.mountPoint}
     }
 
+    ${optionalString (yubikey.gracePeriod > 0) ''
+    echo -n "Waiting ${toString yubikey.gracePeriod} seconds as grace..."
+    for i in $(seq ${toString yubikey.gracePeriod}); do
+        sleep 1
+        echo -n .
+    done
+    echo "ok"
+    ''}
+
     yubikey_missing=true
     ykinfo -v 1>/dev/null 2>&1
     if [ $? != "0" ]; then
@@ -336,21 +306,45 @@ in
               description = "Whether to use a passphrase and a Yubikey (true), or only a Yubikey (false)";
             };
 
-            multiUser = mkOption {
-              default = false;
-              type = types.bool;
-              description = "Whether to allow multiple users to authenticate with a Yubikey";
-            };
-
             slot = mkOption {
               default = 2;
               type = types.int;
               description = "Which slot on the Yubikey to challenge";
             };
 
+            saltLength = mkOption {
+              default = 16;
+              type = types.int;
+              description = "Length of the new salt in byte (64 is the effective maximum)";
+            };
+
+            keyLength = mkOption {
+              default = 64;
+              type = types.int;
+              description = "Length of the LUKS slot key derived with PBKDF2 in byte";
+            };
+
+            iterationStep = mkOption {
+              default = 0;
+              type = types.int;
+              description = "How much the iteration count for PBKDF2 is increased at each successful authentication";
+            };
+
+            gracePeriod = mkOption {
+              default = 2;
+              type = types.int;
+              description = "Time in seconds to wait before attempting to find the Yubikey";
+            };
+
+            ramfsMountPoint = mkOption {
+              default = "/crypt-ramfs";
+              type = types.string;
+              description = "Path where the ramfs used to update the LUKS key will be mounted in stage-1";
+            };
+
             storage = mkOption {
               type = types.optionSet;
-              description = "Options related to the authentication record";
+              description = "Options related to the storing the salt";
 
               options = {
                 device = mkOption {
@@ -358,7 +352,7 @@ in
                   type = types.path;
                   description = ''
                     An unencrypted device that will temporarily be mounted in stage-1.
-                    Must contain the authentication record for this LUKS device.
+                    Must contain the current salt to create the challenge for this LUKS device.
                   '';
                 };
 
@@ -378,7 +372,7 @@ in
                   default = "/crypt-storage/default";
                   type = types.string;
                   description = ''
-                    Absolute path of the authentication record on the unencrypted device with
+                    Absolute path of the salt on the unencrypted device with
                     that device's root directory as "/".
                   '';
                 };
@@ -420,11 +414,13 @@ in
       cp -pdv ${pkgs.popt}/lib/libpopt*.so.* $out/lib
 
       ${optionalString luks.yubikeySupport ''
-      cp -pdv ${pkgs.utillinux}/bin/uuidgen $out/bin
       cp -pdv ${pkgs.ykpers}/bin/ykchalresp $out/bin
       cp -pdv ${pkgs.ykpers}/bin/ykinfo $out/bin
       cp -pdv ${pkgs.openssl}/bin/openssl $out/bin
 
+      cc -O3 -I${pkgs.openssl}/include -L${pkgs.openssl}/lib ${./pbkdf2-sha512.c} -o $out/bin/pbkdf2-sha512 -lcrypto
+      strip -s $out/bin/pbkdf2-sha512
+
       cp -pdv ${pkgs.libusb1}/lib/libusb*.so.* $out/lib
       cp -pdv ${pkgs.ykpers}/lib/libykpers*.so.* $out/lib
       cp -pdv ${pkgs.libyubikey}/lib/libyubikey*.so.* $out/lib
@@ -444,7 +440,6 @@ EOF
     boot.initrd.extraUtilsCommandsTest = ''
       $out/bin/cryptsetup --version
       ${optionalString luks.yubikeySupport ''
-        $out/bin/uuidgen --version
         $out/bin/ykchalresp -V
         $out/bin/ykinfo -V
         cat > $out/bin/openssl-wrap <<EOF
diff --git a/nixos/modules/system/boot/modprobe.nix b/nixos/modules/system/boot/modprobe.nix
index 027a7ac99d51..f694fd29dd04 100644
--- a/nixos/modules/system/boot/modprobe.nix
+++ b/nixos/modules/system/boot/modprobe.nix
@@ -68,7 +68,10 @@ with pkgs.lib;
 
   config = mkIf (!config.boot.isContainer) {
 
-    environment.etc = singleton
+    environment.etc = [
+      { source = "${pkgs.kmod-blacklist-ubuntu}/modprobe.conf";
+        target = "modprobe.d/ubuntu.conf";
+      }
       { source = pkgs.writeText "modprobe.conf"
           ''
             ${flip concatMapStrings config.boot.blacklistedKernelModules (name: ''
@@ -77,26 +80,11 @@ with pkgs.lib;
             ${config.boot.extraModprobeConfig}
           '';
         target = "modprobe.d/nixos.conf";
-      };
+      }
+    ];
 
     environment.systemPackages = [ config.system.sbin.modprobe pkgs.kmod ];
 
-    boot.blacklistedKernelModules =
-      [ # This module is for debugging and generates gigantic amounts
-        # of log output, so it should never be loaded automatically.
-        "evbug"
-
-        # This module causes ALSA to occassionally select the wrong
-        # default sound device, and is little more than an annoyance
-        # on modern machines.
-        "snd_pcsp"
-
-        # The cirrusfb module prevents X11 from starting.  FIXME:
-        # Ubuntu blacklists all framebuffer devices because they're
-        # "buggy" and cause suspend problems.  Maybe we should too?
-        "cirrusfb"
-      ];
-
     system.activationScripts.modprobe =
       ''
         # Allow the kernel to find our wrapped modprobe (which searches
diff --git a/nixos/modules/system/boot/pbkdf2-sha512.c b/nixos/modules/system/boot/pbkdf2-sha512.c
new file mode 100644
index 000000000000..b40c383ac023
--- /dev/null
+++ b/nixos/modules/system/boot/pbkdf2-sha512.c
@@ -0,0 +1,38 @@
+#include <stdint.h>
+#include <string.h>
+#include <stdio.h>
+#include <openssl/evp.h>
+
+void hextorb(uint8_t* hex, uint8_t* rb)
+{
+	while(sscanf(hex, "%2x", rb) == 1)
+	{
+		hex += 2;
+		rb += 1;
+	}
+	*rb = '\0';
+}
+
+int main(int argc, char** argv)
+{
+	uint8_t k_user[2048];
+	uint8_t salt[2048];
+	uint8_t key[4096];
+
+	uint32_t key_length = atoi(argv[1]);
+	uint32_t iteration_count = atoi(argv[2]);
+
+	hextorb(argv[3], salt);
+	uint32_t salt_length = strlen(argv[3]) / 2;
+
+	fgets(k_user, 2048, stdin);
+	uint32_t k_user_length = strlen(k_user);
+	if(k_user[k_user_length - 1] == '\n') {
+			k_user[k_user_length - 1] = '\0';
+	}
+
+	PKCS5_PBKDF2_HMAC(k_user, k_user_length, salt, salt_length, iteration_count, EVP_sha512(), key_length, key);
+	fwrite(key, 1, key_length, stdout);
+
+	return 0;
+}
\ No newline at end of file
diff --git a/nixos/modules/system/boot/stage-1-init.sh b/nixos/modules/system/boot/stage-1-init.sh
index 1f65026b5def..d0f4576f8112 100644
--- a/nixos/modules/system/boot/stage-1-init.sh
+++ b/nixos/modules/system/boot/stage-1-init.sh
@@ -14,7 +14,7 @@ fail() {
     # in an interactive shell.
     cat <<EOF
 
-An error occured in stage 1 of the boot process, which must mount the
+An error occurred 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:
 
@@ -320,6 +320,10 @@ while read -u 3 mountPoint; do
         echo -n "waiting for device $device to appear..."
         for try in $(seq 1 20); do
             sleep 1
+            # also re-try lvm activation now that new block devices might have appeared
+            lvm vgchange -ay
+            # and tell udev to create nodes for the new LVs
+            udevadm trigger --action=add
             if test -e $device; then break; fi
             echo -n "."
         done
diff --git a/nixos/modules/system/boot/stage-2-init.sh b/nixos/modules/system/boot/stage-2-init.sh
index 2fadd3de1f0f..b749172a3ff3 100644
--- a/nixos/modules/system/boot/stage-2-init.sh
+++ b/nixos/modules/system/boot/stage-2-init.sh
@@ -131,6 +131,16 @@ if ! mountpoint -q /run; then
     mount -t tmpfs -o "mode=0755,size=@runSize@" none /run
 fi
 
+# Create a ramfs on /run/keys to hold secrets that shouldn't
+# be written to disk (generally used for nixops, harmless
+# elsehwere)
+if ! mountpoint -q /run/keys; then
+    rm -rf /run/keys
+    mkdir -m 0750 /run/keys
+    chown root:keys /run/keys
+    mount -t ramfs none /run/keys
+fi
+
 mkdir -m 0755 -p /run/lock
 
 
diff --git a/nixos/modules/system/boot/systemd-unit-options.nix b/nixos/modules/system/boot/systemd-unit-options.nix
index c0518599f17a..113990814efa 100644
--- a/nixos/modules/system/boot/systemd-unit-options.nix
+++ b/nixos/modules/system/boot/systemd-unit-options.nix
@@ -41,6 +41,12 @@ in rec {
       '';
     };
 
+    baseUnit = mkOption {
+      type = types.nullOr types.path;
+      default = null;
+      description = "Path to an upstream unit file on which the NixOS unit configuration will be based.";
+    };
+
     description = mkOption {
       default = "";
       type = types.str;
@@ -135,6 +141,7 @@ in rec {
 
     restartTriggers = mkOption {
       default = [];
+      type = types.listOf types.unspecified;
       description = ''
         An arbitrary list of items such as derivations.  If any item
         in the list changes between reconfigurations, the service will
diff --git a/nixos/modules/system/boot/systemd.nix b/nixos/modules/system/boot/systemd.nix
index aaa496955402..49502b3e6851 100644
--- a/nixos/modules/system/boot/systemd.nix
+++ b/nixos/modules/system/boot/systemd.nix
@@ -11,20 +11,18 @@ let
   systemd = cfg.package;
 
   makeUnit = name: unit:
-    pkgs.runCommand "unit" { preferLocalBuild = true; inherit (unit) text; }
-      ((if !unit.enable then  ''
-        mkdir -p $out
-        ln -s /dev/null $out/${name}
-      '' else if unit.linkTarget != null then ''
-        mkdir -p $out
-        ln -s ${unit.linkTarget} $out/${name}
-      '' else if unit.text != null then ''
-        mkdir -p $out
-        echo -n "$text" > $out/${name}
-      '' else "") + optionalString (unit.extraConfig != {}) ''
-        mkdir -p $out/${name}.d
-        ${concatStringsSep "\n" (mapAttrsToList (n: v: "echo -n \"${v}\" > $out/${name}.d/${n}") unit.extraConfig)}
-      '');
+    if unit.enable then
+      pkgs.runCommand "unit" { preferLocalBuild = true; inherit (unit) text; }
+        ''
+          mkdir -p $out
+          echo -n "$text" > $out/${name}
+        ''
+    else
+      pkgs.runCommand "unit" { preferLocalBuild = true; }
+        ''
+          mkdir -p $out
+          ln -s /dev/null $out/${name}
+        '';
 
   upstreamUnits =
     [ # Targets.
@@ -156,15 +154,23 @@ let
   unitConfig = { name, config, ... }: {
     config = {
       unitConfig =
-        { Requires = concatStringsSep " " config.requires;
-          Wants = concatStringsSep " " config.wants;
-          After = concatStringsSep " " config.after;
-          Before = concatStringsSep " " config.before;
-          BindsTo = concatStringsSep " " config.bindsTo;
-          PartOf = concatStringsSep " " config.partOf;
-          Conflicts = concatStringsSep " " config.conflicts;
-          "X-Restart-Triggers" = toString config.restartTriggers;
-        } // optionalAttrs (config.description != "") {
+        optionalAttrs (config.requires != [])
+          { Requires = toString config.requires; }
+        // optionalAttrs (config.wants != [])
+          { Wants = toString config.wants; }
+        // optionalAttrs (config.after != [])
+          { After = toString config.after; }
+        // optionalAttrs (config.before != [])
+          { Before = toString config.before; }
+        // optionalAttrs (config.bindsTo != [])
+          { BindsTo = toString config.bindsTo; }
+        // optionalAttrs (config.partOf != [])
+          { PartOf = toString config.partOf; }
+        // optionalAttrs (config.conflicts != [])
+          { Conflicts = toString config.conflicts; }
+        // optionalAttrs (config.restartTriggers != [])
+          { X-Restart-Triggers = toString config.restartTriggers; }
+        // optionalAttrs (config.description != "") {
           Description = config.description;
         };
     };
@@ -172,7 +178,7 @@ let
 
   serviceConfig = { name, config, ... }: {
     config = mkMerge
-      [ { # Default path for systemd services.  Should be quite minimal.
+      [ (mkIf (config.baseUnit == null) { # Default path for systemd services.  Should be quite minimal.
           path =
             [ pkgs.coreutils
               pkgs.findutils
@@ -181,7 +187,7 @@ let
               systemd
             ];
           environment.PATH = config.path;
-        }
+        })
         (mkIf (config.preStart != "")
           { serviceConfig.ExecStartPre = makeJobScript "${name}-pre-start" ''
               #! ${pkgs.stdenv.shell} -e
@@ -249,6 +255,14 @@ let
         (if isList value then value else [value]))
         as));
 
+  commonUnitText = def:
+    optionalString (def.baseUnit != null) ''
+      .include ${def.baseUnit}
+    '' + ''
+      [Unit]
+      ${attrsToSection def.unitConfig}
+    '';
+
   targetToUnit = name: def:
     { inherit (def) wantedBy requiredBy enable;
       text =
@@ -260,11 +274,8 @@ let
 
   serviceToUnit = name: def:
     { inherit (def) wantedBy requiredBy enable;
-      text =
+      text = commonUnitText def +
         ''
-          [Unit]
-          ${attrsToSection def.unitConfig}
-
           [Service]
           ${let env = cfg.globalEnvironment // def.environment;
             in concatMapStrings (n: "Environment=\"${n}=${getAttr n env}\"\n") (attrNames env)}
@@ -276,11 +287,8 @@ let
 
   socketToUnit = name: def:
     { inherit (def) wantedBy requiredBy enable;
-      text =
+      text = commonUnitText def +
         ''
-          [Unit]
-          ${attrsToSection def.unitConfig}
-
           [Socket]
           ${attrsToSection def.socketConfig}
           ${concatStringsSep "\n" (map (s: "ListenStream=${s}") def.listenStreams)}
@@ -289,11 +297,8 @@ let
 
   timerToUnit = name: def:
     { inherit (def) wantedBy requiredBy enable;
-      text =
+      text = commonUnitText def +
         ''
-          [Unit]
-          ${attrsToSection def.unitConfig}
-
           [Timer]
           ${attrsToSection def.timerConfig}
         '';
@@ -301,11 +306,8 @@ let
 
   mountToUnit = name: def:
     { inherit (def) wantedBy requiredBy enable;
-      text =
+      text = commonUnitText def +
         ''
-          [Unit]
-          ${attrsToSection def.unitConfig}
-
           [Mount]
           ${attrsToSection def.mountConfig}
         '';
@@ -313,11 +315,8 @@ let
 
   automountToUnit = name: def:
     { inherit (def) wantedBy requiredBy enable;
-      text =
+      text = commonUnitText def +
         ''
-          [Unit]
-          ${attrsToSection def.unitConfig}
-
           [Automount]
           ${attrsToSection def.automountConfig}
         '';
@@ -432,25 +431,9 @@ in
               internal = true;
               description = "The generated unit.";
             };
-            linkTarget = mkOption {
-              default = null;
-              description = "The file to symlink this target to.";
-              type = types.nullOr types.path;
-            };
-            extraConfig = mkOption {
-              default = {};
-              example = { "foo@1.conf" = "X-RestartIfChanged=false"; };
-              type = types.attrsOf types.lines;
-              description = ''
-                Extra files to be appended to the configuration for the unit.
-                This can be used to override configuration for a unit provided
-                by systemd or another package, or to override only a single instance
-                of a template unit.
-              '';
-            };
           };
           config = {
-            unit = makeUnit name config;
+            unit = mkDefault (makeUnit name config);
           };
         };
     };
@@ -662,8 +645,11 @@ in
       '';
 
     # Target for ‘charon send-keys’ to hook into.
+    users.extraGroups.keys.gid = config.ids.gids.keys;
+
     systemd.targets.keys =
       { description = "Security Keys";
+        unitConfig.X-StopOnReconfiguration = true;
       };
 
     systemd.units =
diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix
index 9619f0f5ebe7..548685377a90 100644
--- a/nixos/modules/tasks/network-interfaces.nix
+++ b/nixos/modules/tasks/network-interfaces.nix
@@ -50,6 +50,26 @@ let
         '';
       };
 
+      ipv6Address = mkOption {
+        default = null;
+        example = "2001:1470:fffd:2098::e006";
+        type = types.nullOr types.string;
+        description = ''
+          IPv6 address of the interface.  Leave empty to configure the
+          interface using NDP.
+        '';
+      };
+
+      ipv6prefixLength = mkOption {
+        default = 64;
+        example = 64;
+        type = types.int;
+        description = ''
+          Subnet mask of the interface, specified as the number of
+          bits in the prefix (<literal>64</literal>).
+        '';
+      };
+
       macAddress = mkOption {
         default = null;
         example = "00:11:22:33:44:55";
@@ -401,9 +421,11 @@ in
                 EOF
 
                 # Disable or enable IPv6.
-                if [ -e /proc/sys/net/ipv6/conf/all/disable_ipv6 ]; then
-                  echo ${if cfg.enableIPv6 then "0" else "1"} > /proc/sys/net/ipv6/conf/all/disable_ipv6
-                fi
+                ${optionalString (!config.boot.isContainer) ''
+                  if [ -e /proc/sys/net/ipv6/conf/all/disable_ipv6 ]; then
+                    echo ${if cfg.enableIPv6 then "0" else "1"} > /proc/sys/net/ipv6/conf/all/disable_ipv6
+                  fi
+                ''}
 
                 # Set the default gateway.
                 ${optionalString (cfg.defaultGateway != "") ''
@@ -435,6 +457,7 @@ in
           (let mask =
                 if i.prefixLength != null then toString i.prefixLength else
                 if i.subnetMask != "" then i.subnetMask else "32";
+               staticIPv6 = cfg.enableIPv6 && i.ipv6Address != null;
           in
           { description = "Configuration of ${i.name}";
             wantedBy = [ "network-interfaces.target" ];
@@ -468,11 +491,31 @@ in
                     echo "configuring interface..."
                     ip -4 addr flush dev "${i.name}"
                     ip -4 addr add "${i.ipAddress}/${mask}" dev "${i.name}"
+                    restart_network_setup=true
+                  else
+                    echo "skipping configuring interface"
+                  fi
+                ''
+              + optionalString (staticIPv6)
+                ''
+                  # Only do a flush/add if it's necessary.  This is
+                  # useful when the Nix store is accessed via this
+                  # interface (e.g. in a QEMU VM test).
+                  if ! ip -6 -o a show dev "${i.name}" | grep "${i.ipv6Address}/${toString i.ipv6prefixLength}"; then
+                    echo "configuring interface..."
+                    ip -6 addr flush dev "${i.name}"
+                    ip -6 addr add "${i.ipv6Address}/${toString i.ipv6prefixLength}" dev "${i.name}"
+                    restart_network_setup=true
+                  else
+                    echo "skipping configuring interface"
+                  fi
+                ''
+              + optionalString (i.ipAddress != null || staticIPv6)
+                ''
+                  if [ restart_network_setup = true ]; then
                     # Ensure that the default gateway remains set.
                     # (Flushing this interface may have removed it.)
                     ${config.systemd.package}/bin/systemctl try-restart --no-block network-setup.service
-                  else
-                    echo "skipping configuring interface"
                   fi
                   ${config.systemd.package}/bin/systemctl start ip-up.target
                 ''
diff --git a/nixos/modules/virtualisation/amazon-image.nix b/nixos/modules/virtualisation/amazon-image.nix
index abd2a1084bd9..99d6f927ef0b 100644
--- a/nixos/modules/virtualisation/amazon-image.nix
+++ b/nixos/modules/virtualisation/amazon-image.nix
@@ -164,5 +164,5 @@ with pkgs.lib;
   # Prevent logging in as root without a password.  This doesn't really matter,
   # since the only PAM services that allow logging in with a null
   # password are local ones that are inaccessible on EC2 machines.
-  security.initialRootPassword = "!";
+  security.initialRootPassword = mkDefault "!";
 }
diff --git a/nixos/modules/virtualisation/containers.nix b/nixos/modules/virtualisation/containers.nix
index bcbfaacd703f..d87284de4fc1 100644
--- a/nixos/modules/virtualisation/containers.nix
+++ b/nixos/modules/virtualisation/containers.nix
@@ -55,7 +55,7 @@ with pkgs.lib;
                   modules =
                     let extraConfig =
                       { boot.isContainer = true;
-                        security.initialRootPassword = "!";
+                        security.initialRootPassword = mkDefault "!";
                         networking.hostName = mkDefault name;
                       };
                     in [ extraConfig config.config ];
@@ -134,4 +134,4 @@ with pkgs.lib;
       }) config.systemd.containers;
 
   };
-}
\ No newline at end of file
+}
diff --git a/nixos/modules/virtualisation/google-compute-image.nix b/nixos/modules/virtualisation/google-compute-image.nix
index 098c9ede8533..634932ff0077 100644
--- a/nixos/modules/virtualisation/google-compute-image.nix
+++ b/nixos/modules/virtualisation/google-compute-image.nix
@@ -114,7 +114,7 @@ with pkgs.lib;
   # Prevent logging in as root without a password.  This doesn't really matter,
   # since the only PAM services that allow logging in with a null
   # password are local ones that are inaccessible on Google Compute machines.
-  security.initialRootPassword = "!";
+  security.initialRootPassword = mkDefault "!";
 
   # Configure default metadata hostnames
   networking.extraHosts = ''
diff --git a/nixos/modules/virtualisation/qemu-vm.nix b/nixos/modules/virtualisation/qemu-vm.nix
index 2218e1045eb8..a866b513f0f2 100644
--- a/nixos/modules/virtualisation/qemu-vm.nix
+++ b/nixos/modules/virtualisation/qemu-vm.nix
@@ -386,8 +386,7 @@ in
 
     # When building a regular system configuration, override whatever
     # video driver the host uses.
-    services.xserver.videoDriver = mkVMOverride null;
-    services.xserver.videoDrivers = mkVMOverride [ "vesa" ];
+    hardware.opengl.videoDrivers = mkVMOverride [ "vesa" ];
     services.xserver.defaultDepth = mkVMOverride 0;
     services.xserver.resolutions = mkVMOverride [ { x = 1024; y = 768; } ];
     services.xserver.monitorSection =
diff --git a/nixos/modules/virtualisation/virtualbox-guest.nix b/nixos/modules/virtualisation/virtualbox-guest.nix
index 664fd21781cd..e4c00fe4a419 100644
--- a/nixos/modules/virtualisation/virtualbox-guest.nix
+++ b/nixos/modules/virtualisation/virtualbox-guest.nix
@@ -52,7 +52,7 @@ optionalAttrs (pkgs.stdenv.isi686 || pkgs.stdenv.isx86_64) # ugly...
         serviceConfig.ExecStart = "@${kernel.virtualboxGuestAdditions}/sbin/VBoxService VBoxService --foreground";
       };
 
-    services.xserver.videoDrivers = mkOverride 50 [ "virtualbox" ];
+    hardware.opengl.videoDrivers = mkOverride 50 [ "virtualbox" ];
 
     services.xserver.config =
       ''
diff --git a/nixos/modules/virtualisation/virtualbox-image.nix b/nixos/modules/virtualisation/virtualbox-image.nix
index 71bdf31a98d2..06bea2d8acff 100644
--- a/nixos/modules/virtualisation/virtualbox-image.nix
+++ b/nixos/modules/virtualisation/virtualbox-image.nix
@@ -111,5 +111,5 @@ with pkgs.lib;
   # Prevent logging in as root without a password.  For NixOps, we
   # don't need this because the user can login via SSH, and for the
   # demo images, there is a demo user account that can sudo to root.
-  security.initialRootPassword = "!";
+  security.initialRootPassword = mkDefault "!";
 }
diff --git a/nixos/release-combined.nix b/nixos/release-combined.nix
index dccc3acbf464..94bc2f796eb7 100644
--- a/nixos/release-combined.nix
+++ b/nixos/release-combined.nix
@@ -44,7 +44,7 @@ in rec {
         (all nixos.iso_graphical)
         (all nixos.ova)
 
-        #(all nixos.tests.efi-installer.simple)
+        # (all nixos.tests.efi-installer.simple)
         (all nixos.tests.firefox)
         (all nixos.tests.firewall)
         (all nixos.tests.installer.grub1)
@@ -61,6 +61,7 @@ in rec {
         (all nixos.tests.printing)
         (all nixos.tests.proxy)
         (all nixos.tests.xfce)
+        (all nixos.tests.gnome3)
 
         nixpkgs.tarball
         (all nixpkgs.emacs)
diff --git a/nixos/release.nix b/nixos/release.nix
index ff094cce05fa..b98976c2ccea 100644
--- a/nixos/release.nix
+++ b/nixos/release.nix
@@ -213,7 +213,8 @@ in rec {
     with lib;
     let
       testsFor = system:
-        mapAttrsRecursiveCond (x: !x ? test) (n: v: listToAttrs [(nameValuePair system v.test)])
+        mapAttrsRecursiveCond (x: !x ? test)
+          (n: v: listToAttrs [(nameValuePair system (if v.makeCoverageReport or false then v.report else v.test))])
           (import ./tests { inherit nixpkgs system; });
     in fold recursiveUpdate {} (map testsFor systems);
 }
diff --git a/nixos/tests/common/user-account.nix b/nixos/tests/common/user-account.nix
index 8157cf8d263e..0239a3c4d08a 100644
--- a/nixos/tests/common/user-account.nix
+++ b/nixos/tests/common/user-account.nix
@@ -7,5 +7,6 @@
       createHome = true;
       useDefaultShell = true;
       password = "foobar";
+      uid = 1000;
     };
 }
diff --git a/nixos/tests/default.nix b/nixos/tests/default.nix
index 574e1dd2f8b8..5b68862a2cd1 100644
--- a/nixos/tests/default.nix
+++ b/nixos/tests/default.nix
@@ -12,7 +12,9 @@ with import ../lib/testing.nix { inherit system minimal; };
   firewall = makeTest (import ./firewall.nix);
   installer = makeTests (import ./installer.nix);
   efi-installer = makeTests (import ./efi-installer.nix);
+  gnome3 = makeTest (import ./gnome3.nix);
   ipv6 = makeTest (import ./ipv6.nix);
+  jenkins = makeTest (import ./jenkins.nix);
   kde4 = makeTest (import ./kde4.nix);
   #kexec = makeTest (import ./kexec.nix);
   login = makeTest (import ./login.nix {});
diff --git a/nixos/tests/gnome3.nix b/nixos/tests/gnome3.nix
new file mode 100644
index 000000000000..98a76137842e
--- /dev/null
+++ b/nixos/tests/gnome3.nix
@@ -0,0 +1,31 @@
+{ pkgs, ... }:
+
+{
+
+  machine =
+    { config, pkgs, ... }:
+
+    { imports = [ ./common/user-account.nix ];
+
+      services.xserver.enable = true;
+
+      services.xserver.displayManager.auto.enable = true;
+      services.xserver.displayManager.auto.user = "alice";
+      services.xserver.desktopManager.gnome3.enable = true;
+    };
+
+  testScript =
+    ''
+      $machine->waitForX;
+      $machine->sleep(15);
+
+      # Check that logging in has given the user ownership of devices.
+      $machine->succeed("getfacl /dev/snd/timer | grep -q alice");
+
+      $machine->succeed("su - alice -c 'DISPLAY=:0.0 gnome-terminal &'");
+      $machine->waitForWindow(qr/Terminal/);
+      $machine->sleep(10);
+      $machine->screenshot("screen");
+    '';
+
+}
diff --git a/nixos/tests/installer.nix b/nixos/tests/installer.nix
index 7581c10a01d0..b32012ea0347 100644
--- a/nixos/tests/installer.nix
+++ b/nixos/tests/installer.nix
@@ -238,7 +238,7 @@ in {
               "mkfs.ext3 -L nixos /dev/vda3",
               "mount LABEL=nixos /mnt",
               "mkfs.ext3 -L boot /dev/vda1",
-              "mkdir /mnt/boot",
+              "mkdir -p /mnt/boot",
               "mount LABEL=boot /mnt/boot",
           );
         '';
@@ -252,9 +252,9 @@ in {
         ''
           $machine->succeed(
               "parted /dev/vda mklabel msdos",
-              "parted /dev/vda -- mkpart primary 1M 2048M", # first PV
+              "parted /dev/vda -- mkpart primary 1M 2048M", # PV1
               "parted /dev/vda -- set 1 lvm on",
-              "parted /dev/vda -- mkpart primary 2048M -1s", # second PV
+              "parted /dev/vda -- mkpart primary 2048M -1s", # PV2
               "parted /dev/vda -- set 2 lvm on",
               "udevadm settle",
               "pvcreate /dev/vda1 /dev/vda2",
diff --git a/nixos/tests/jenkins.nix b/nixos/tests/jenkins.nix
new file mode 100644
index 000000000000..e6524ec56538
--- /dev/null
+++ b/nixos/tests/jenkins.nix
@@ -0,0 +1,35 @@
+# verifies:
+#   1. jenkins service starts on master node
+#   2. jenkins user can be extended on both master and slave
+#   3. jenkins service not started on slave node
+{ pkgs, ... }:
+{
+  nodes = {
+    master = { pkgs, config, ... }: {
+        services.jenkins.enable = true;
+
+        # should have no effect
+        services.jenkinsSlave.enable = true;
+
+        users.extraUsers.jenkins.extraGroups = [ "users" ];
+      };
+    slave = { pkgs, config, ... }: {
+        services.jenkinsSlave.enable = true;
+
+        users.extraUsers.jenkins.extraGroups = [ "users" ];
+      };
+  };
+
+  testScript = ''
+    startAll;
+
+    $master->waitForUnit("jenkins");
+    print $master->execute("sudo -u jenkins groups");
+    $master->mustSucceed("sudo -u jenkins groups | grep jenkins | grep users");
+
+    print $slave->execute("sudo -u jenkins groups");
+    $slave->mustSucceed("sudo -u jenkins groups | grep jenkins | grep users");
+
+    $slave->mustFail("systemctl status jenkins.service");
+  '';
+}
diff --git a/nixos/tests/mysql-replication.nix b/nixos/tests/mysql-replication.nix
index 28a1187dd184..f8c82f7ce9c8 100644
--- a/nixos/tests/mysql-replication.nix
+++ b/nixos/tests/mysql-replication.nix
@@ -11,11 +11,12 @@ in
 
       {
         services.mysql.enable = true;
-	services.mysql.replication.role = "master";
-	services.mysql.initialDatabases = [ { name = "testdb"; schema = ./testdb.sql; } ];
-	services.mysql.initialScript = pkgs.writeText "initmysql"
+        services.mysql.package = pkgs.mysql;
+        services.mysql.replication.role = "master";
+        services.mysql.initialDatabases = [ { name = "testdb"; schema = ./testdb.sql; } ];
+        services.mysql.initialScript = pkgs.writeText "initmysql"
         ''
-	  create user '${replicateUser}'@'%' identified by '${replicatePassword}';
+          create user '${replicateUser}'@'%' identified by '${replicatePassword}';
           grant replication slave on *.* to '${replicateUser}'@'%';
         '';
       };
@@ -25,11 +26,12 @@ in
 
       {
         services.mysql.enable = true;
-	services.mysql.replication.role = "slave";
-	services.mysql.replication.serverId = 2;
-	services.mysql.replication.masterHost = nodes.master.config.networking.hostName;
-	services.mysql.replication.masterUser = replicateUser;
-	services.mysql.replication.masterPassword = replicatePassword;
+        services.mysql.package = pkgs.mysql;
+        services.mysql.replication.role = "slave";
+        services.mysql.replication.serverId = 2;
+        services.mysql.replication.masterHost = nodes.master.config.networking.hostName;
+        services.mysql.replication.masterUser = replicateUser;
+        services.mysql.replication.masterPassword = replicatePassword;
       };
 
     slave2 =
@@ -37,11 +39,12 @@ in
 
       {
         services.mysql.enable = true;
-	services.mysql.replication.role = "slave";
-	services.mysql.replication.serverId = 3;
-	services.mysql.replication.masterHost = nodes.master.config.networking.hostName;
-	services.mysql.replication.masterUser = replicateUser;
-	services.mysql.replication.masterPassword = replicatePassword;
+        services.mysql.package = pkgs.mysql;
+        services.mysql.replication.role = "slave";
+        services.mysql.replication.serverId = 3;
+        services.mysql.replication.masterHost = nodes.master.config.networking.hostName;
+        services.mysql.replication.masterUser = replicateUser;
+        services.mysql.replication.masterPassword = replicatePassword;
       };
   };
 
diff --git a/nixos/tests/mysql.nix b/nixos/tests/mysql.nix
index b48850738b72..bceeb8beabc2 100644
--- a/nixos/tests/mysql.nix
+++ b/nixos/tests/mysql.nix
@@ -7,8 +7,9 @@
 
       {
         services.mysql.enable = true;
-	services.mysql.replication.role = "master";
-	services.mysql.initialDatabases = [ { name = "testdb"; schema = ./testdb.sql; } ];
+        services.mysql.replication.role = "master";
+        services.mysql.initialDatabases = [ { name = "testdb"; schema = ./testdb.sql; } ];
+        services.mysql.package = pkgs.mysql;
       };
   };
 
diff --git a/nixos/tests/quake3.nix b/nixos/tests/quake3.nix
index 925011077805..fefbd75b4a3c 100644
--- a/nixos/tests/quake3.nix
+++ b/nixos/tests/quake3.nix
@@ -14,11 +14,13 @@ in
 
 rec {
 
+  makeCoverageReport = true;
+
   client =
     { config, pkgs, ... }:
 
     { imports = [ ./common/x11.nix ];
-      services.xserver.driSupport = true;
+      hardware.opengl.driSupport = true;
       services.xserver.defaultDepth = pkgs.lib.mkOverride 0 16;
       environment.systemPackages = [ pkgs.quake3demo ];
       nixpkgs.config.packageOverrides = overrides;
diff --git a/nixos/tests/subversion.nix b/nixos/tests/subversion.nix
index 309da90c5df1..49450c78f3b8 100644
--- a/nixos/tests/subversion.nix
+++ b/nixos/tests/subversion.nix
@@ -20,7 +20,7 @@ let
         # To build the kernel with coverage instrumentation, we need a
         # special patch to make coverage data available under /proc.
         linux = pkgs.linux.override (orig: {
-          stdenv = cleanupBuildTree (keepBuildTree orig.stdenv);
+          stdenv = overrideInStdenv pkgs.stdenv [ pkgs.keepBuildTree ];
           extraConfig =
             ''
               GCOV_KERNEL y