summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--modules/config/networking.nix2
-rw-r--r--modules/config/system-path.nix7
-rw-r--r--modules/module-list.nix10
-rw-r--r--modules/security/consolekit.nix60
-rw-r--r--modules/security/pam.nix4
-rw-r--r--modules/services/hardware/udev.nix96
-rw-r--r--modules/services/logging/syslogd.nix2
-rw-r--r--modules/services/misc/nix-daemon.nix112
-rw-r--r--modules/services/monitoring/nagios/default.nix7
-rw-r--r--modules/services/networking/ssh/sshd.nix49
-rw-r--r--modules/services/system/dbus.nix37
-rw-r--r--modules/services/system/nscd.nix5
-rw-r--r--modules/services/ttys/agetty.nix (renamed from modules/services/ttys/mingetty.nix)52
-rw-r--r--modules/services/web-servers/apache-httpd/default.nix13
-rw-r--r--modules/services/x11/display-managers/default.nix6
-rw-r--r--modules/services/x11/display-managers/kdm.nix2
-rw-r--r--modules/services/x11/display-managers/slim.nix2
-rw-r--r--modules/services/x11/xserver.nix14
-rw-r--r--modules/system/activation/top-level.nix6
-rw-r--r--modules/system/boot/modprobe.nix11
-rw-r--r--modules/system/boot/shutdown.nix25
-rw-r--r--modules/system/boot/stage-1-init.sh13
-rw-r--r--modules/system/boot/stage-2-init.sh28
-rw-r--r--modules/system/boot/stage-2.nix1
-rw-r--r--modules/system/boot/systemd.nix404
-rw-r--r--modules/system/upstart-events/control-alt-delete.nix26
-rw-r--r--modules/system/upstart-events/runlevel.nix38
-rw-r--r--modules/system/upstart/upstart.nix269
-rw-r--r--modules/tasks/filesystems.nix4
-rw-r--r--modules/tasks/kbd.nix5
-rw-r--r--modules/tasks/lvm.nix2
-rw-r--r--modules/tasks/network-interfaces.nix3
-rw-r--r--modules/testing/test-instrumentation.nix15
34 files changed, 723 insertions, 608 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000000..b25c15b81fae
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+*~
diff --git a/modules/config/networking.nix b/modules/config/networking.nix
index 2b4be69cc51f..a91ce9c59e26 100644
--- a/modules/config/networking.nix
+++ b/modules/config/networking.nix
@@ -67,7 +67,7 @@ in
           '' + optionalString config.services.nscd.enable ''
             # Invalidate the nscd cache whenever resolv.conf is
             # regenerated.
-            libc_restart='${pkgs.upstart}/sbin/start invalidate-nscd'
+            libc_restart='${pkgs.systemd}/bin/systemctl start invalidate-nscd.service'
           '' + optionalString config.services.bind.enable ''
             # This hosts runs a full-blown DNS resolver.
             name_servers='127.0.0.1'
diff --git a/modules/config/system-path.nix b/modules/config/system-path.nix
index 38e5f076bcfd..3969be680980 100644
--- a/modules/config/system-path.nix
+++ b/modules/config/system-path.nix
@@ -16,9 +16,7 @@ let
     '';
 
   requiredPackages =
-    [ config.system.sbin.modprobe # must take precedence over module_init_tools
-      config.system.build.upstart
-      config.environment.nix
+    [ config.environment.nix
       pkgs.acl
       pkgs.attr
       pkgs.bashInteractive # bash with ncurses support
@@ -39,7 +37,6 @@ let
       pkgs.less
       pkgs.libcap
       pkgs.man
-      pkgs.module_init_tools
       pkgs.nano
       pkgs.ncurses
       pkgs.netcat
@@ -50,10 +47,8 @@ let
       pkgs.procps
       pkgs.rsync
       pkgs.strace
-      pkgs.sysklogd
       pkgs.sysvtools
       pkgs.time
-      pkgs.udev
       pkgs.usbutils
       pkgs.utillinux
       extraManpages      
diff --git a/modules/module-list.nix b/modules/module-list.nix
index 1e5ec300df5d..1046233c9839 100644
--- a/modules/module-list.nix
+++ b/modules/module-list.nix
@@ -48,7 +48,6 @@
   ./programs/wvdial.nix
   ./rename.nix
   ./security/ca.nix
-  ./security/consolekit.nix
   ./security/pam.nix
   ./security/pam_usb.nix
   ./security/policykit.nix
@@ -83,7 +82,7 @@
   ./services/hardware/udev.nix
   ./services/hardware/udisks.nix
   ./services/hardware/upower.nix
-  ./services/logging/klogd.nix
+  #./services/logging/klogd.nix
   ./services/logging/logrotate.nix
   ./services/logging/logstash.nix
   ./services/logging/syslogd.nix
@@ -169,7 +168,7 @@
   ./services/system/nscd.nix
   ./services/system/uptimed.nix
   ./services/ttys/gpm.nix
-  ./services/ttys/mingetty.nix
+  ./services/ttys/agetty.nix
   ./services/web-servers/apache-httpd/default.nix
   ./services/web-servers/jboss/default.nix
   ./services/web-servers/tomcat.nix
@@ -198,12 +197,11 @@
   ./system/boot/kernel.nix
   ./system/boot/luksroot.nix
   ./system/boot/modprobe.nix
+  ./system/boot/shutdown.nix
   ./system/boot/stage-1.nix
   ./system/boot/stage-2.nix
+  ./system/boot/systemd.nix
   ./system/etc/etc.nix
-  ./system/upstart-events/control-alt-delete.nix
-  ./system/upstart-events/runlevel.nix
-  ./system/upstart-events/shutdown.nix
   ./system/upstart/upstart.nix
   ./tasks/cpu-freq.nix
   ./tasks/filesystems.nix
diff --git a/modules/security/consolekit.nix b/modules/security/consolekit.nix
deleted file mode 100644
index 28e1fec06010..000000000000
--- a/modules/security/consolekit.nix
+++ /dev/null
@@ -1,60 +0,0 @@
-{ config, pkgs, ... }:
-
-with pkgs.lib;
-
-let
-
-  # `pam_console' maintains the set of locally logged in users in
-  # /var/run/console.  This is obsolete, but D-Bus still uses it for
-  # its `at_console' feature.  So maintain it using a ConsoleKit
-  # session script.  Borrowed from
-  # http://sources.gentoo.org/cgi-bin/viewvc.cgi/gentoo-x86/sys-auth/consolekit/files/pam-foreground-compat.ck
-  updateVarRunConsole = pkgs.writeTextFile {
-    name = "var-run-console.ck";
-    destination = "/etc/ConsoleKit/run-session.d/var-run-console.ck";
-    executable = true;
-
-    text =
-      ''
-        #! ${pkgs.stdenv.shell} -e
-        PATH=${pkgs.coreutils}/bin:${pkgs.gnused}/bin:${pkgs.glibc}/bin
-        TAGDIR=/var/run/console
-
-        [ -n "$CK_SESSION_USER_UID" ] || exit 1
-
-        TAGFILE="$TAGDIR/`getent passwd $CK_SESSION_USER_UID | cut -f 1 -d:`"
-
-        if [ "$1" = "session_added" ]; then
-            mkdir -p "$TAGDIR"
-            echo "$CK_SESSION_ID" >> "$TAGFILE"
-        fi
-
-        if [ "$1" = "session_removed" ] && [ -e "$TAGFILE" ]; then
-            sed -i "\%^$CK_SESSION_ID\$%d" "$TAGFILE"
-            [ -s "$TAGFILE" ] || rm -f "$TAGFILE"
-        fi
-      '';
-  };
-
-in
-
-{
-
-  config = {
-
-    environment.systemPackages = [ pkgs.consolekit ];
-
-    services.dbus.packages = [ pkgs.consolekit ];
-
-    environment.etc = singleton
-      { source = (pkgs.buildEnv {
-          name = "consolekit-config";
-          pathsToLink = [ "/etc/ConsoleKit" ];
-          paths = [ pkgs.consolekit pkgs.udev updateVarRunConsole ];
-        }) + "/etc/ConsoleKit";
-        target = "ConsoleKit";
-      };
-
-  };
-
-}
diff --git a/modules/security/pam.nix b/modules/security/pam.nix
index 4fab7febc710..09803598db90 100644
--- a/modules/security/pam.nix
+++ b/modules/security/pam.nix
@@ -41,7 +41,7 @@ let
       # against the keys in the calling user's ~/.ssh/authorized_keys.
       # This is useful for "sudo" on password-less remote systems.
       sshAgentAuth ? false
-    , # If set, use ConsoleKit's PAM connector module to claim
+    , # If set, use systemd's PAM connector module to claim
       # ownership of audio devices etc.
       ownDevices ? false
     , # Whether to forward XAuth keys between users.  Mostly useful
@@ -104,7 +104,7 @@ let
           ${optionalString config.krb5.enable
               "session optional ${pam_krb5}/lib/security/pam_krb5.so"}
           ${optionalString ownDevices
-              "session optional ${pkgs.consolekit}/lib/security/pam_ck_connector.so"}
+              "session optional ${pkgs.systemd}/lib/security/pam_systemd.so"}
           ${optionalString forwardXAuth
               "session optional pam_xauth.so xauthpath=${pkgs.xorg.xauth}/bin/xauth systemuser=99"}
           ${optionalString (limits != [])
diff --git a/modules/services/hardware/udev.nix b/modules/services/hardware/udev.nix
index 3bbf24bb3791..8d66982fb5cb 100644
--- a/modules/services/hardware/udev.nix
+++ b/modules/services/hardware/udev.nix
@@ -4,7 +4,9 @@ with pkgs.lib;
 
 let
 
-  inherit (pkgs) stdenv writeText udev procps;
+  inherit (pkgs) stdenv writeText procps;
+
+  udev = config.system.build.systemd;
 
   cfg = config.services.udev;
 
@@ -24,20 +26,16 @@ let
   udevRules = stdenv.mkDerivation {
     name = "udev-rules";
     buildCommand = ''
-      ensureDir $out
+      mkdir -p $out
       shopt -s nullglob
 
       # Set a reasonable $PATH for programs called by udev rules.
       echo 'ENV{PATH}="${udevPath}/bin:${udevPath}/sbin"' > $out/00-path.rules
 
-      # Set the firmware search path so that the firmware.sh helper
-      # called by 50-firmware.rules works properly.
-      echo 'ENV{FIRMWARE_DIRS}="/root/test-firmware ${toString config.hardware.firmware}"' >> $out/00-path.rules
-
       # Add the udev rules from other packages.
       for i in ${toString cfg.packages}; do
         echo "Adding rules for package $i"
-        for j in $i/*/udev/rules.d/*; do
+        for j in $i/{etc,lib}/udev/rules.d/*; do
           echo "Copying $j to $out/$(basename $j)"
           echo "# Copied from $j" > $out/$(basename $j)
           cat $j >> $out/$(basename $j)
@@ -53,12 +51,7 @@ let
           --replace \"/bin/mount \"${pkgs.utillinux}/bin/mount
       done
 
-      # If auto-configuration is disabled, then remove
-      # udev's 80-drivers.rules file, which contains rules for
-      # automatically calling modprobe.
-      ${if !config.boot.hardwareScan then "rm $out/80-drivers.rules" else ""}
-
-      echo -n "Checking that all programs called by relative paths in udev rules exist in ${udev}/lib/udev ... "
+      echo -n "Checking that all programs called by relative paths in udev rules exist in ${udev}/lib/udev... "
       import_progs=$(grep 'IMPORT{program}="[^/$]' $out/* |
         sed -e 's/.*IMPORT{program}="\([^ "]*\)[ "].*/\1/' | uniq)
       run_progs=$(grep -v '^[[:space:]]*#' $out/* | grep 'RUN+="[^/$]' |
@@ -72,7 +65,7 @@ let
       done
       echo "OK"
 
-      echo -n "Checking that all programs call by absolute paths in udev rules exist ... "
+      echo -n "Checking that all programs called by absolute paths in udev rules exist... "
       import_progs=$(grep 'IMPORT{program}="\/' $out/* |
         sed -e 's/.*IMPORT{program}="\([^ "]*\)[ "].*/\1/' | uniq)
       run_progs=$(grep -v '^[[:space:]]*#' $out/* | grep 'RUN+="/' |
@@ -90,24 +83,9 @@ let
       for i in ${toString cfg.packages}; do
         grep -l '\(RUN+\|IMPORT{program}\)="\(/usr\)\?/s\?bin' $i/*/udev/rules.d/* || true
       done
-
-      # Use the persistent device rules (naming for CD/DVD and
-      # network devices) stored in
-      # /var/lib/udev/rules.d/70-persistent-{cd,net}.rules.  These are
-      # modified by the write_{cd,net}_rules helpers called from
-      # 75-cd-aliases-generator.rules and
-      # 75-persistent-net-generator.rules.
-      ln -sv /var/lib/udev/rules.d/70-persistent-cd.rules $out/
-      ln -sv /var/lib/udev/rules.d/70-persistent-net.rules $out/
     ''; # */
   };
 
-  # The udev configuration file.
-  conf = writeText "udev.conf" ''
-    udev_rules="${udevRules}"
-    #udev_log="debug"
-  '';
-
   # Udev has a 512-character limit for ENV{PATH}, so create a symlink
   # tree to work around this.
   udevPath = pkgs.buildEnv {
@@ -207,61 +185,15 @@ in
 
     services.udev.extraRules = nixosRules;
 
-    services.udev.packages = [ pkgs.udev extraUdevRules ];
-
-    services.udev.path = [ pkgs.coreutils pkgs.gnused pkgs.gnugrep pkgs.utillinux pkgs.udev ];
-
-    jobs.udev =
-      { startOn = "startup";
-
-        environment = { UDEV_CONFIG_FILE = conf; };
-
-        path = [ udev ];
-
-        preStart =
-          ''
-            echo "" > /proc/sys/kernel/hotplug || true
+    services.udev.packages = [ extraUdevRules ];
 
-            mkdir -p /var/lib/udev/rules.d
-            touch /var/lib/udev/rules.d/70-persistent-cd.rules /var/lib/udev/rules.d/70-persistent-net.rules
+    services.udev.path = [ pkgs.coreutils pkgs.gnused pkgs.gnugrep pkgs.utillinux udev ];
 
-            mkdir -p /dev/.udev # !!! bug in udev?
-          '';
-
-        daemonType = "fork";
-
-        exec = "udevd --daemon";
-
-        postStart =
-          ''
-            # Do the loading of additional stage 2 kernel modules.
-            # This needs to be done while udevd is running, because
-            # the modules may call upon udev's firmware loading rule.
-            for i in ${toString config.boot.kernelModules}; do
-                echo "loading kernel module ‘$i’..."
-                ${config.system.sbin.modprobe}/sbin/modprobe $i || true
-            done
-          '';
-      };
-
-    jobs.udevtrigger =
-      { startOn = "started udev";
-
-        task = true;
-
-        path = [ udev ];
-
-        script =
-          ''
-            # Let udev create device nodes for all modules that have already
-            # been loaded into the kernel (or for which support is built into
-            # the kernel).
-            udevadm trigger --action=add
-            udevadm settle || true # wait for udev to finish
-
-            initctl emit -n new-devices
-          '';
-      };
+    environment.etc =
+      [ { source = udevRules;
+          target = "udev/rules.d";
+        }
+      ];
 
   };
 
diff --git a/modules/services/logging/syslogd.nix b/modules/services/logging/syslogd.nix
index 715cfc0f9e6f..bfe7352122c0 100644
--- a/modules/services/logging/syslogd.nix
+++ b/modules/services/logging/syslogd.nix
@@ -96,7 +96,7 @@ in
     jobs.syslogd =
       { description = "Syslog daemon";
 
-        startOn = "started udev";
+        #startOn = "started udev";
 
         environment = { TZ = config.time.timeZone; };
 
diff --git a/modules/services/misc/nix-daemon.nix b/modules/services/misc/nix-daemon.nix
index 31b81a13e363..ea13def39d60 100644
--- a/modules/services/misc/nix-daemon.nix
+++ b/modules/services/misc/nix-daemon.nix
@@ -4,6 +4,8 @@ with pkgs.lib;
 
 let
 
+  cfg = config.nix;
+
   inherit (config.environment) nix;
 
   makeNixBuildUser = nr:
@@ -74,9 +76,7 @@ in
           gc-keep-outputs = true
           gc-keep-derivations = true
         ";
-        description = "
-          This option allows to append lines to nix.conf.
-        ";
+        description = "Additional text appended to <filename>nix.conf<filename>.";
       };
 
       distributedBuilds = mkOption {
@@ -169,11 +169,9 @@ in
       # actually a shell script.
       envVars = mkOption {
         internal = true;
-        default = "";
-        type = types.string;
-        description = "
-          Environment variables used by Nix.
-        ";
+        default = {};
+        type = types.attrs;
+        description = "Environment variables used by Nix.";
       };
 
       nrBuildUsers = mkOption {
@@ -208,14 +206,14 @@ in
               # /bin/sh won't work.
               binshDeps = pkgs.writeReferencesToFile config.system.build.binsh;
             in
-              pkgs.runCommand "nix.conf" {extraOptions = config.nix.extraOptions; } ''
+              pkgs.runCommand "nix.conf" {extraOptions = cfg.extraOptions; } ''
                 extraPaths=$(for i in $(cat ${binshDeps}); do if test -d $i; then echo $i; fi; done)
                 cat > $out <<END
                 # WARNING: this file is generated.
                 build-users-group = nixbld
-                build-max-jobs = ${toString (config.nix.maxJobs)}
-                build-use-chroot = ${if config.nix.useChroot then "true" else "false"}
-                build-chroot-dirs = ${toString config.nix.chrootDirs} $(echo $extraPaths)
+                build-max-jobs = ${toString (cfg.maxJobs)}
+                build-use-chroot = ${if cfg.useChroot then "true" else "false"}
+                build-chroot-dirs = ${toString cfg.chrootDirs} $(echo $extraPaths)
                 $extraOptions
                 END
               '';
@@ -223,7 +221,7 @@ in
         }
       ]
 
-      ++ optional (config.nix.distributedBuilds && !config.nix.manualNixMachines)
+      ++ optional (cfg.distributedBuilds && !cfg.manualNixMachines)
         { # List of machines for distributed Nix builds in the format expected
           # by build-remote.pl.
           source = pkgs.writeText "nix.machines"
@@ -233,37 +231,69 @@ in
               + " ${machine.sshKey} ${toString machine.maxJobs} "
               + (if machine ? speedFactor then toString machine.speedFactor else "1" )
               + "\n"
-            ) config.nix.buildMachines));
+            ) cfg.buildMachines));
           target = "nix.machines";
         };
 
-    jobs.nixDaemon =
-      { name = "nix-daemon";
+    boot.systemd.units."nix-daemon.socket" =
+      { wantedBy = [ "sockets.target" ];
+        text =
+          ''
+            [Unit]
+            Description=Nix Daemon Socket
+            Before=multi-user.target
 
-        startOn = "startup";
+            [Socket]
+            ListenStream=/nix/var/nix/daemon-socket/socket
+          '';
+      };
+     
+    boot.systemd.services."nix-daemon.service" =
+      { description = "Nix Daemon";
 
         path = [ nix pkgs.openssl pkgs.utillinux ]
-          ++ optionals config.nix.distributedBuilds [ pkgs.openssh pkgs.gzip ];
+          ++ optionals cfg.distributedBuilds [ pkgs.openssh pkgs.gzip ];
 
-        script =
-          ''
-            ${config.nix.envVars}
-            exec \
-              nice -n ${builtins.toString config.nix.daemonNiceLevel} \
-              ionice -n ${builtins.toString config.nix.daemonIONiceLevel} \
-              nix-worker --daemon > /dev/null 2>&1
-          '';
+        environment = cfg.envVars;
 
-        extraConfig =
+        serviceConfig =
           ''
-            limit nofile 4096 4096
+            ExecStart=${nix}/bin/nix-worker --daemon
+            KillMode=process
+            PIDFile=/run/sshd.pid
+            Nice=${toString cfg.daemonNiceLevel}
+            IOSchedulingPriority=${toString cfg.daemonIONiceLevel}
+            LimitNOFILE=4096
           '';
       };
+     
+    nix.envVars =
+      { NIX_CONF_DIR = "/etc/nix";
+
+        # Enable the copy-from-other-stores substituter, which allows builds
+        # to be sped up by copying build results from remote Nix stores.  To
+        # do this, mount the remote file system on a subdirectory of
+        # /var/run/nix/remote-stores.
+        NIX_OTHER_STORES = "/var/run/nix/remote-stores/*/nix";
+      }
+
+      // optionalAttrs cfg.distributedBuilds {
+        NIX_BUILD_HOOK = "${config.environment.nix}/libexec/nix/build-remote.pl";
+        NIX_REMOTE_SYSTEMS = "/etc/nix.machines";
+        NIX_CURRENT_LOAD = "/var/run/nix/current-load";
+      }
+
+      # !!! These should not be defined here, but in some general proxy configuration module!
+      // optionalAttrs (cfg.proxy != "") {
+        http_proxy = cfg.proxy;
+        https_proxy = cfg.proxy;
+        ftp_proxy = cfg.proxy;
+      };
 
     environment.shellInit =
       ''
         # Set up the environment variables for running Nix.
-        ${config.nix.envVars}
+        ${concatMapStrings (n: "export ${n}=\"${getAttr n cfg.envVars}\"\n") (attrNames cfg.envVars)}
 
         # Set up secure multi-user builds: non-root users build through the
         # Nix daemon.
@@ -274,29 +304,7 @@ in
         fi
       '';
 
-    nix.envVars =
-      ''
-        export NIX_CONF_DIR=/etc/nix
-
-        # Enable the copy-from-other-stores substituter, which allows builds
-        # to be sped up by copying build results from remote Nix stores.  To
-        # do this, mount the remote file system on a subdirectory of
-        # /var/run/nix/remote-stores.
-        export NIX_OTHER_STORES=/var/run/nix/remote-stores/*/nix
-      '' # */
-      + optionalString config.nix.distributedBuilds ''
-        export NIX_BUILD_HOOK=${config.environment.nix}/libexec/nix/build-remote.pl
-        export NIX_REMOTE_SYSTEMS=/etc/nix.machines
-        export NIX_CURRENT_LOAD=/var/run/nix/current-load
-      ''
-      # !!! These should not be defined here, but in some general proxy configuration module!
-      + optionalString (config.nix.proxy != "") ''
-        export http_proxy=${config.nix.proxy}
-        export https_proxy=${config.nix.proxy}
-        export ftp_proxy=${config.nix.proxy}
-      '';
-
-    users.extraUsers = map makeNixBuildUser (range 1 config.nix.nrBuildUsers);
+    users.extraUsers = map makeNixBuildUser (range 1 cfg.nrBuildUsers);
 
     system.activationScripts.nix = stringAfter [ "etc" "users" ]
       ''
diff --git a/modules/services/monitoring/nagios/default.nix b/modules/services/monitoring/nagios/default.nix
index 6d2fe3f2acec..3c32a3c25ec9 100644
--- a/modules/services/monitoring/nagios/default.nix
+++ b/modules/services/monitoring/nagios/default.nix
@@ -159,12 +159,7 @@ in
     environment.systemPackages = [ pkgs.nagios ];
 
     jobs.nagios =
-      { # Run `nagios -v' to check the validity of the configuration file so
-        # that a nixos-rebuild fails *before* we kill the running Nagios
-        # daemon.
-        buildHook = "${pkgs.nagios}/bin/nagios -v ${nagiosCfgFile}";
-
-        description = "Nagios monitoring daemon";
+      { description = "Nagios monitoring daemon";
 
         startOn = "started network-interfaces";
         stopOn = "stopping network-interfaces";
diff --git a/modules/services/networking/ssh/sshd.nix b/modules/services/networking/ssh/sshd.nix
index ea60e591c14e..d89978a852c0 100644
--- a/modules/services/networking/ssh/sshd.nix
+++ b/modules/services/networking/ssh/sshd.nix
@@ -39,6 +39,7 @@ let
   );
 
   userOptions = {
+  
     openssh.authorizedKeys = {
 
       preserveExistingKeys = mkOption {
@@ -77,6 +78,7 @@ let
       };
 
     };
+    
   };
 
   mkAuthkeyScript =
@@ -136,7 +138,6 @@ let
       ${userLoop}
     '';
 
-
 in
 
 {
@@ -314,24 +315,33 @@ in
       }
     ];
 
-    jobs.sshd = {
+    boot.systemd.services."set-ssh-keys.service" =
+      { description = "Update authorized SSH keys";
 
-        description = "OpenSSH server";
+        wantedBy = [ "multi-user.target" ];
 
-        startOn = "started network-interfaces";
+        script = mkAuthkeyScript;
 
-        environment = {
-          LD_LIBRARY_PATH = nssModulesPath;
-          # Duplicated from bashrc. OpenSSH needs a patch for this.
-          LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive";
-        };
+        serviceConfig =
+          ''
+            Type=oneshot
+            RemainAfterExit=true
+          '';
+      };
+    
+    boot.systemd.services."sshd.service" =
+      { description = "SSH daemon";
+
+        wantedBy = [ "multi-user.target" ];
+        after = [ "set-ssh-keys.service" ];
 
-        path = [ pkgs.openssh pkgs.gnused ];
+        path = [ pkgs.openssh ];
+        
+        environment.LD_LIBRARY_PATH = nssModulesPath;
+        environment.LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive";
 
         preStart =
           ''
-            ${mkAuthkeyScript}
-
             mkdir -m 0755 -p /etc/ssh
 
             if ! test -f ${cfg.hostKeyPath}; then
@@ -339,12 +349,15 @@ in
             fi
           '';
 
-        daemonType = "fork";
-
-        exec =
+        serviceConfig =
           ''
-            ${pkgs.openssh}/sbin/sshd -h ${cfg.hostKeyPath} \
-              -f ${pkgs.writeText "sshd_config" cfg.extraConfig}
+            ExecStart=\
+              ${pkgs.openssh}/sbin/sshd -h ${cfg.hostKeyPath} \
+                -f ${pkgs.writeText "sshd_config" cfg.extraConfig}
+            Restart=always
+            Type=forking
+            KillMode=process
+            PIDFile=/run/sshd.pid
           '';
       };
 
@@ -352,6 +365,8 @@ in
 
     services.openssh.extraConfig =
       ''
+        PidFile /run/sshd.pid
+      
         Protocol 2
 
         UsePAM ${if cfg.usePAM then "yes" else "no"}
diff --git a/modules/services/system/dbus.nix b/modules/services/system/dbus.nix
index f7fbb23d0a78..bd5c290d5826 100644
--- a/modules/services/system/dbus.nix
+++ b/modules/services/system/dbus.nix
@@ -116,6 +116,33 @@ in
         gid = config.ids.gids.messagebus;
       };
 
+    # FIXME: these are copied verbatim from the dbus source tree.  We
+    # should install and use the originals.
+    boot.systemd.units."dbus.socket".text =
+      ''
+        [Unit]
+        Description=D-Bus System Message Bus Socket
+
+        [Socket]
+        ListenStream=/var/run/dbus/system_bus_socket
+      '';
+      
+    boot.systemd.units."dbus.service".text =
+      ''
+        [Unit]
+        Description=D-Bus System Message Bus
+        Requires=dbus.socket
+        After=syslog.target
+
+        [Service]
+        ExecStartPre=${pkgs.dbus_tools}/bin/dbus-uuidgen --ensure
+        ExecStartPre=-${pkgs.coreutils}/bin/rm -f /var/run/dbus/pid
+        ExecStart=${pkgs.dbus_daemon}/bin/dbus-daemon --system --address=systemd: --nofork --systemd-activation
+        ExecReload=${pkgs.dbus_tools}/dbus-send --print-reply --system --type=method_call --dest=org.freedesktop.DBus / org.freedesktop.DBus.ReloadConfig
+        OOMScoreAdjust=-900
+      '';
+
+    /*
     jobs.dbus =
       { startOn = "started udev and started syslogd";
       
@@ -138,15 +165,6 @@ in
 
         exec = "dbus-daemon --system";
 
-        /*
-        postStart =
-          ''
-            # Signal Upstart to connect to the system bus.  This
-            # allows ‘initctl’ to work for non-root users.
-            kill -USR1 1
-          '';
-        */
-
         postStop =
           ''
             # !!! Hack: doesn't belong here.
@@ -157,6 +175,7 @@ in
             fi
           '';
       };
+    */    
 
     security.setuidOwners = singleton
       { program = "dbus-daemon-launch-helper";
diff --git a/modules/services/system/nscd.nix b/modules/services/system/nscd.nix
index 6f35cd30f582..b2596dbb4139 100644
--- a/modules/services/system/nscd.nix
+++ b/modules/services/system/nscd.nix
@@ -20,15 +20,14 @@ in
 
       enable = mkOption {
         default = true;
-        description = "
-          Whether to enable the Name Service Cache Daemon.
-        ";
+        description = "Whether to enable the Name Service Cache Daemon.";
       };
 
     };
 
   };
 
+  
   ###### implementation
 
   config = mkIf config.services.nscd.enable {
diff --git a/modules/services/ttys/mingetty.nix b/modules/services/ttys/agetty.nix
index e5dbc07aeb0f..42d276c96664 100644
--- a/modules/services/ttys/mingetty.nix
+++ b/modules/services/ttys/agetty.nix
@@ -10,6 +10,7 @@ with pkgs.lib;
 
     services.mingetty = {
 
+      # FIXME
       ttys = mkOption {
         default =
           if pkgs.stdenv.isArm
@@ -56,23 +57,39 @@ with pkgs.lib;
   config = {
 
     # Generate a separate job for each tty.
-    jobs = listToAttrs (map (tty: nameValuePair tty {
-
-      startOn =
-        # On tty1 we should always wait for mountall, since it may
-        # start an emergency-shell job.
-        if config.services.mingetty.waitOnMounts || tty == "tty1"
-        then "stopped udevtrigger and filesystem"
-        else "stopped udevtrigger"; # !!! should start as soon as the tty device is created
-
-      path = [ pkgs.mingetty ];
-
-      exec = "mingetty --loginprog=${pkgs.shadow}/bin/login --noclear ${tty}";
-
-      environment.LOCALE_ARCHIVE = "/run/current-system/sw/lib/locale/locale-archive";
-
-    }) config.services.mingetty.ttys);
-
+    boot.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
+
+        [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
+        KillMode=process
+        IgnoreSIGPIPE=no
+
+        # Some login implementations ignore SIGTERM, so we send SIGHUP
+        # instead, to ensure that login terminates cleanly.
+        KillSignal=SIGHUP
+      '';
+    
     environment.etc = singleton
       { # Friendly greeting on the virtual consoles.
         source = pkgs.writeText "issue" ''
@@ -83,6 +100,7 @@ with pkgs.lib;
         '';
         target = "issue";
       };
+
   };
 
 }
diff --git a/modules/services/web-servers/apache-httpd/default.nix b/modules/services/web-servers/apache-httpd/default.nix
index ba02c6ead43f..ceb93cd8b016 100644
--- a/modules/services/web-servers/apache-httpd/default.nix
+++ b/modules/services/web-servers/apache-httpd/default.nix
@@ -557,18 +557,7 @@ in
       '';
 
     jobs.httpd =
-      { # Statically verify the syntactic correctness of the generated
-        # httpd.conf.  !!! this is impure!  It doesn't just check for
-        # syntax, but also whether the Apache user/group exist,
-        # whether SSL keys exist, etc.
-        buildHook =
-          ''
-            echo
-            echo '=== Checking the generated Apache configuration file ==='
-            ${httpd}/bin/httpd -f ${httpdConf} -t || true
-          '';
-
-        description = "Apache HTTPD";
+      { description = "Apache HTTPD";
 
         startOn = "started networking and filesystem"
           # Hacky.  Some subservices depend on Postgres
diff --git a/modules/services/x11/display-managers/default.nix b/modules/services/x11/display-managers/default.nix
index cdfd271ee65c..528b3a6f7276 100644
--- a/modules/services/x11/display-managers/default.nix
+++ b/modules/services/x11/display-managers/default.nix
@@ -53,12 +53,6 @@ let
         fi
       ''}
 
-      # Start a ConsoleKit session so that we get ownership of various
-      # devices.
-      if test -z "$XDG_SESSION_COOKIE"; then
-          exec ${pkgs.consolekit}/bin/ck-launch-session "$0" "$sessionType"
-      fi
-
       # Handle being called by kdm.
       if test "''${1:0:1}" = /; then eval exec "$1"; fi
 
diff --git a/modules/services/x11/display-managers/kdm.nix b/modules/services/x11/display-managers/kdm.nix
index 838a4ed90330..ec4d033a597f 100644
--- a/modules/services/x11/display-managers/kdm.nix
+++ b/modules/services/x11/display-managers/kdm.nix
@@ -111,7 +111,7 @@ in
         logsXsession = true;
       };
 
-    security.pam.services = [ { name = "kde"; allowNullPassword = true; } ];
+    security.pam.services = [ { name = "kde"; allowNullPassword = true; ownDevices = true; } ];
 
     users.extraUsers = singleton
       { name = "kdm";
diff --git a/modules/services/x11/display-managers/slim.nix b/modules/services/x11/display-managers/slim.nix
index 97c5f1d1b2b8..fc4df80d810a 100644
--- a/modules/services/x11/display-managers/slim.nix
+++ b/modules/services/x11/display-managers/slim.nix
@@ -115,7 +115,7 @@ in
 
     # Allow null passwords so that the user can login as root on the
     # installation CD.
-    security.pam.services = [ { name = "slim"; allowNullPassword = true; } ];
+    security.pam.services = [ { name = "slim"; allowNullPassword = true; ownDevices = true; } ];
 
   };
 
diff --git a/modules/services/x11/xserver.nix b/modules/services/x11/xserver.nix
index 97424308c273..5d795e5d57dc 100644
--- a/modules/services/x11/xserver.nix
+++ b/modules/services/x11/xserver.nix
@@ -377,15 +377,13 @@ in
     environment.pathsToLink =
       [ "/etc/xdg" "/share/xdg" "/share/applications" "/share/icons" "/share/pixmaps" ];
 
-    jobs."xserver-start-check" =
-      { startOn = if cfg.autorun then "filesystem and stopped udevtrigger" else "";
-        stopOn = "";
-        task = true;
-        script = "grep -qv noX11 /proc/cmdline && start xserver || true";
-      };
+    boot.systemd.defaultUnit = mkIf cfg.autorun "graphical.target";
+
+    boot.systemd.services."xserver.service" =
+      { wantedBy = [ "graphical.target" ];
+        after = [ "systemd-udev-settle.service" ];
 
-    jobs.xserver =
-      { restartIfChanged = false;
+        #restartIfChanged = false;
 
         environment =
           { FONTCONFIG_FILE = "/etc/fonts/fonts.conf"; # !!! cleanup
diff --git a/modules/system/activation/top-level.nix b/modules/system/activation/top-level.nix
index 3edc92d502cc..ef32d991d9d6 100644
--- a/modules/system/activation/top-level.nix
+++ b/modules/system/activation/top-level.nix
@@ -117,12 +117,12 @@ let
 
       ln -s ${config.system.build.etc}/etc $out/etc
       ln -s ${config.system.path} $out/sw
-      ln -s ${config.system.build.upstart} $out/upstart
+      ln -s ${pkgs.systemd} $out/systemd
       ln -s ${config.hardware.firmware} $out/firmware
 
       echo -n "$kernelParams" > $out/kernel-params
       echo -n "$configurationName" > $out/configuration-name
-      echo -n "${toString config.system.build.upstart.interfaceVersion}" > $out/upstart-interface-version
+      #echo -n "${toString config.system.build.upstart.interfaceVersion}" > $out/upstart-interface-version
       echo -n "$nixosVersion" > $out/nixos-version
 
       mkdir $out/fine-tune
@@ -173,7 +173,7 @@ let
         pkgs.gnugrep
         pkgs.findutils
         pkgs.diffutils
-        config.system.build.upstart # for initctl
+        pkgs.systemd
       ];
 
     # Boot loaders
diff --git a/modules/system/boot/modprobe.nix b/modules/system/boot/modprobe.nix
index 41252e2ba0cd..03d9222af217 100644
--- a/modules/system/boot/modprobe.nix
+++ b/modules/system/boot/modprobe.nix
@@ -9,7 +9,6 @@ with pkgs.lib;
   options = {
 
     system.sbin.modprobe = mkOption {
-      # should be moved in module-init-tools
       internal = true;
       default = pkgs.writeTextFile {
         name = "modprobe";
@@ -18,8 +17,8 @@ with pkgs.lib;
         text =
           ''
             #! ${pkgs.stdenv.shell}
-            export MODULE_DIR=${config.system.modulesTree}/lib/modules/
-
+            export MODULE_DIR=/var/run/current-system/kernel-modules/lib/modules
+            
             # Fall back to the kernel modules used at boot time if the
             # modules in the current configuration don't match the
             # running kernel.
@@ -27,7 +26,7 @@ with pkgs.lib;
                 MODULE_DIR=/run/booted-system/kernel-modules/lib/modules/
             fi
 
-            exec ${pkgs.module_init_tools}/sbin/modprobe "$@"
+            exec ${pkgs.kmod}/sbin/modprobe "$@"
           '';
       };
       description = ''
@@ -78,6 +77,8 @@ with pkgs.lib;
         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.
@@ -104,7 +105,7 @@ with pkgs.lib;
 
     environment.shellInit =
       ''
-        export MODULE_DIR=${config.system.modulesTree}/lib/modules/
+        export MODULE_DIR=/var/run/current-system/kernel-modules/lib/modules
       '';    
 
   };
diff --git a/modules/system/boot/shutdown.nix b/modules/system/boot/shutdown.nix
new file mode 100644
index 000000000000..3af8e3e4b295
--- /dev/null
+++ b/modules/system/boot/shutdown.nix
@@ -0,0 +1,25 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+{
+
+  # This unit saves the value of the system clock to the hardware
+  # clock on shutdown.
+  boot.systemd.units."save-hwclock.service" =
+    { wantedBy = [ "shutdown.target" ];
+
+      text =
+        ''
+          [Unit]
+          Description=Save Hardware Clock
+          DefaultDependencies=no
+          Before=shutdown.target
+
+          [Service]
+          Type=oneshot
+          ExecStart=${pkgs.utillinux}/sbin/hwclock --systohc --utc
+        '';
+    };
+  
+}
diff --git a/modules/system/boot/stage-1-init.sh b/modules/system/boot/stage-1-init.sh
index cc12ccaa738f..a232151967fc 100644
--- a/modules/system/boot/stage-1-init.sh
+++ b/modules/system/boot/stage-1-init.sh
@@ -67,17 +67,10 @@ mkdir -p /proc
 mount -t proc none /proc
 mkdir -p /sys
 mount -t sysfs none /sys
-mount -t tmpfs -o "mode=0755,size=@devSize@" none /dev
+mount -t devtmpfs -o "size=@devSize@" none /dev
 mkdir -p /run
 mount -t tmpfs -o "mode=0755,size=@runSize@" none /run
 
-# Some console devices, for the interactivity
-mknod /dev/console c 5 1
-mknod /dev/tty c 5 0
-mknod /dev/tty1 c 4 1
-mknod /dev/ttyS0 c 4 64
-mknod /dev/ttyS1 c 4 65
-
 # Process the kernel command line.
 export stage2Init=/init
 for o in $(cat /proc/cmdline); do
@@ -130,10 +123,6 @@ for i in @kernelModules@; do
 done
 
 
-# Create /dev/null.
-mknod /dev/null c 1 3
-
-
 # Create device nodes in /dev.
 echo "running udev..."
 export UDEV_CONFIG_FILE=@udevConf@
diff --git a/modules/system/boot/stage-2-init.sh b/modules/system/boot/stage-2-init.sh
index 0a42083144df..e8b01788900b 100644
--- a/modules/system/boot/stage-2-init.sh
+++ b/modules/system/boot/stage-2-init.sh
@@ -36,20 +36,8 @@ mount -n -o remount,rw none /
 if [ ! -e /proc/1 ]; then
     mkdir -m 0755 -p /proc
     mount -n -t proc none /proc
-    mkdir -m 0755 -p /sys
-    mount -t sysfs none /sys
     mkdir -m 0755 -p /dev
-    mount -t tmpfs -o "mode=0755" none /dev
-
-    # Create the minimal device nodes needed for the activation scripts
-    # and Upstart.
-    mknod -m 0666 /dev/null c 1 3
-    mknod -m 0644 /dev/urandom c 1 9 # needed for passwd
-    mknod -m 0644 /dev/console c 5 1
-    mknod -m 0644 /dev/ptmx c 5 2 # required by upstart
-    mknod -m 0644 /dev/tty1 c 4 1
-    mknod -m 0644 /dev/ttyS0 c 4 64
-    mknod -m 0644 /dev/ttyS1 c 4 65
+    mount -t devtmpfs none /dev
 fi
 
 
@@ -57,7 +45,7 @@ fi
 mkdir -m 0755 -p /etc
 test -e /etc/fstab || touch /etc/fstab # to shut up mount
 rm -f /etc/mtab* # not that we care about stale locks
-cat /proc/mounts > /etc/mtab
+ln -s /proc/mounts /etc/mtab
 
 
 # Process the kernel command line.
@@ -87,15 +75,15 @@ done
 mkdir -m 0755 /dev/shm
 mount -t tmpfs -o "rw,nosuid,nodev,size=@devShmSize@" tmpfs /dev/shm
 mkdir -m 0755 -p /dev/pts
-mount -t devpts -o mode=0600,gid=@ttyGid@ none /dev/pts
 [ -e /proc/bus/usb ] && mount -t usbfs none /proc/bus/usb # UML doesn't have USB by default
 mkdir -m 01777 -p /tmp
-mkdir -m 0755 -p /var
+mkdir -m 0755 -p /var /var/log
 mkdir -m 0755 -p /nix/var
 mkdir -m 0700 -p /root
 mkdir -m 0755 -p /bin # for the /bin/sh symlink
 mkdir -m 0755 -p /home
 mkdir -m 0755 -p /etc/nixos
+mkdir -m 0700 -p /var/log/journal
 
 
 # Miscellaneous boot time cleanup.
@@ -187,6 +175,8 @@ if [ -n "$debug2" ]; then
 fi
 
 
-# Start Upstart's init.
-echo "starting Upstart..."
-PATH=/run/current-system/upstart/sbin exec init --no-sessions ${debug2:+--verbose}
+# Start systemd.
+echo "starting systemd..."
+PATH=/run/current-system/systemd/lib/systemd \
+    MODULE_DIR=/run/current-system/kernel-modules/lib/modules \
+    exec systemd --log-target journal # --log-level debug --crash-shell
diff --git a/modules/system/boot/stage-2.nix b/modules/system/boot/stage-2.nix
index 02c061dacde2..a5e96cc0f1cf 100644
--- a/modules/system/boot/stage-2.nix
+++ b/modules/system/boot/stage-2.nix
@@ -61,7 +61,6 @@ let
     isExecutable = true;
     inherit (config.boot) devShmSize runSize cleanTmpDir;
     ttyGid = config.ids.gids.tty;
-    upstart = config.system.build.upstart;
     path =
       [ pkgs.coreutils
         pkgs.utillinux
diff --git a/modules/system/boot/systemd.nix b/modules/system/boot/systemd.nix
new file mode 100644
index 000000000000..6976a204e7f2
--- /dev/null
+++ b/modules/system/boot/systemd.nix
@@ -0,0 +1,404 @@
+{ config, pkgs, ... }:
+
+with pkgs.lib;
+
+let
+
+  servicesOptions = {
+
+    description = mkOption {
+      default = "";
+      types = types.uniq types.string;
+      description = "Description of this unit used in systemd messages and progress indicators.";
+    };
+
+    requires = mkOption {
+      default = [];
+      types = types.listOf types.string;
+      description = ''
+        Start the specified units when this unit is started, and stop
+        this unit when the specified units are stopped or fail.
+      '';
+    };
+
+    wants = mkOption {
+      default = [];
+      types = types.listOf types.string;
+      description = ''
+        Start the specified units when this unit is started.
+      '';
+    };
+
+    after = mkOption {
+      default = [];
+      types = types.listOf types.string;
+      description = ''
+        If the specified units are started at the same time as
+        this unit, delay this unit until they have started.
+      '';
+    };
+
+    before = mkOption {
+      default = [];
+      types = types.listOf types.string;
+      description = ''
+        If the specified units are started at the same time as
+        this unit, delay them until this unit has started.
+      '';
+    };
+
+    wantedBy = mkOption {
+      default = [];
+      types = types.listOf types.string;
+      description = "Start this unit when the specified units are started.";
+    };
+
+    environment = mkOption {
+      default = {};
+      type = types.attrs;
+      example = { PATH = "/foo/bar/bin"; LANG = "nl_NL.UTF-8"; };
+      description = "Environment variables passed to the services's processes.";
+    };
+
+    path = mkOption {
+      default = [];
+      apply = ps: "${makeSearchPath "bin" ps}:${makeSearchPath "sbin" ps}";
+      description = ''
+        Packages added to the service's <envar>PATH</envar>
+        environment variable.  Both the <filename>bin</filename>
+        and <filename>sbin</filename> subdirectories of each
+        package are added.
+      '';
+    };
+
+    serviceConfig = mkOption {
+      default = "";
+      type = types.string;
+      description = ''
+        Contents of the <literal>[Service]</literal> section of the unit.
+        See <citerefentry><refentrytitle>systemd.unit</refentrytitle>
+        <manvolnum>5</manvolnum></citerefentry> for details.
+      '';
+    };
+
+    script = mkOption {
+      type = types.uniq types.string;
+      default = "";
+      description = "Shell commands executed as the service's main process.";
+    };
+
+    preStart = mkOption {
+      type = types.string;
+      default = "";
+      description = ''
+        Shell commands executed before the service's main process
+        is started.
+      '';
+    };
+
+  };
+
+
+  servicesConfig = { name, config, ... }: {
+
+    config = {
+
+      # Default path for systemd services.  Should be quite minimal.
+      path = 
+        [ pkgs.coreutils
+          pkgs.findutils
+          pkgs.gnugrep
+          pkgs.gnused
+          systemd
+        ];
+
+    };
+
+  };
+  
+
+  cfg = config.boot.systemd;
+
+  systemd = pkgs.systemd;
+
+  makeUnit = name: unit:
+    pkgs.writeTextFile { name = "unit"; inherit (unit) text; destination = "/${name}"; };
+
+  upstreamUnits =
+    [ # Targets.
+      "basic.target"
+      "sysinit.target"
+      "sockets.target"
+      "graphical.target"
+      "multi-user.target"
+      "getty.target"
+      "rescue.target"
+      "network.target"
+      "nss-lookup.target"
+      "nss-user-lookup.target"
+      "syslog.target"
+      "time-sync.target"
+
+      # Udev.
+      "systemd-udev-control.socket"
+      "systemd-udev-kernel.socket"
+      "systemd-udev.service"
+      "systemd-udev-settle.service"
+      "systemd-udev-trigger.service"
+
+      # Login stuff.
+      "systemd-logind.service"
+      "autovt@.service"
+      "systemd-vconsole-setup.service"
+      "systemd-user-sessions.service"
+      "dbus-org.freedesktop.login1.service"
+      "user@.service"
+
+      # Journal.
+      "systemd-journald.socket"
+      "systemd-journald.service"
+
+      # SysV init compatibility.
+      "systemd-initctl.socket"
+      "systemd-initctl.service"
+      "runlevel0.target"
+      "runlevel1.target"
+      "runlevel2.target"
+      "runlevel3.target"
+      "runlevel4.target"
+      "runlevel5.target"
+      "runlevel6.target"
+
+      # Random seed.
+      "systemd-random-seed-load.service"
+      "systemd-random-seed-save.service"
+
+      # Utmp maintenance.
+      "systemd-update-utmp-runlevel.service"
+      "systemd-update-utmp-shutdown.service"
+      
+      # Filesystems.
+      "fsck@.service"
+      "fsck-root.service"
+      "systemd-remount-fs.service"
+      "local-fs.target"
+      "local-fs-pre.target"
+      "remote-fs.target"
+      "remote-fs-pre.target"
+      "swap.target"
+      "dev-hugepages.mount"
+      "dev-mqueue.mount"
+      "sys-fs-fuse-connections.mount"
+      "sys-kernel-config.mount"
+      "sys-kernel-debug.mount"
+
+      # Hibernate / suspend.
+      "hibernate.target"
+      "hibernate.service"
+      "suspend.target"
+      "suspend.service"
+      "sleep.target"
+
+      # Reboot stuff.
+      "reboot.target"
+      "reboot.service"
+      "poweroff.target"
+      "poweroff.service"
+      "halt.target"
+      "halt.service"
+      "ctrl-alt-del.target"
+      "shutdown.target"
+      "umount.target"
+      "final.target"
+
+      # Password entry.
+      "systemd-ask-password-console.path"
+      "systemd-ask-password-console.service"
+      "systemd-ask-password-wall.path"
+      "systemd-ask-password-wall.service"
+
+      # Misc.
+      "syslog.socket"
+    ];
+
+  upstreamWants =
+    [ "basic.target.wants"
+      "sysinit.target.wants"
+      "sockets.target.wants"
+      "local-fs.target.wants"
+      "multi-user.target.wants"
+      "shutdown.target.wants"
+    ];
+
+  rescueService =
+    ''
+      [Unit]
+      Description=Rescue Shell
+      DefaultDependencies=no
+      Conflicts=shutdown.target
+      After=sysinit.target
+      Before=shutdown.target
+
+      [Service]
+      Environment=HOME=/root
+      WorkingDirectory=/root
+      ExecStartPre=-${pkgs.coreutils}/bin/echo 'Welcome to rescue mode. Use "systemctl default" or ^D to enter default mode.'
+      #ExecStart=-/sbin/sulogin
+      ExecStart=-${pkgs.bashInteractive}/bin/bash --login
+      ExecStopPost=-${systemd}/bin/systemctl --fail --no-block default
+      Type=idle
+      StandardInput=tty-force
+      StandardOutput=inherit
+      StandardError=inherit
+      KillMode=process
+
+      # Bash ignores SIGTERM, so we send SIGHUP instead, to ensure that bash
+      # terminates cleanly.
+      KillSignal=SIGHUP
+    '';
+
+  serviceToUnit = name: def:
+    { inherit (def) wantedBy;
+
+      text =
+        ''
+          [Unit]
+          ${optionalString (def.description != "") ''
+            Description=${def.description}
+          ''}
+          Requires=${concatStringsSep " " def.requires}
+          Wants=${concatStringsSep " " def.wants}
+          Before=${concatStringsSep " " def.before}
+          After=${concatStringsSep " " def.after}
+
+          [Service]
+          Environment=PATH=${def.path}
+          ${concatMapStrings (n: "Environment=${n}=${getAttr n def.environment}\n") (attrNames def.environment)}
+          
+          ${optionalString (def.preStart != "") ''
+            ExecStartPre=${pkgs.writeScript "${name}-prestart.sh" ''
+              #! ${pkgs.stdenv.shell} -e
+              ${def.preStart}
+            ''}
+          ''}
+
+          ${optionalString (def.script != "") ''
+            ExecStart=${pkgs.writeScript "${name}.sh" ''
+              #! ${pkgs.stdenv.shell} -e
+              ${def.script}
+            ''}
+          ''}
+
+          ${def.serviceConfig}
+        '';
+    };
+
+  nixosUnits = mapAttrsToList makeUnit cfg.units;
+    
+  units = pkgs.runCommand "units" { preferLocalBuild = true; }
+    ''
+      mkdir -p $out/system
+      for i in ${toString upstreamUnits}; do
+        fn=${systemd}/example/systemd/system/$i
+        [ -e $fn ]
+        if [ -L $fn ]; then
+          cp -pd $fn $out/system/
+        else
+          ln -s $fn $out/system
+        fi
+      done
+      
+      for i in ${toString upstreamWants}; do
+        fn=${systemd}/example/systemd/system/$i
+        [ -e $fn ]
+        x=$out/system/$(basename $fn)
+        mkdir $x
+        for i in $fn/*; do
+          y=$x/$(basename $i)
+          cp -pd $i $y
+          if ! [ -e $y ]; then rm -v $y; fi
+        done
+      done
+      
+      for i in ${toString nixosUnits}; do
+        cp $i/* $out/system
+      done
+
+      ${concatStrings (mapAttrsToList (name: unit:
+          concatMapStrings (name2: ''
+            mkdir -p $out/system/${name2}.wants
+            ln -sfn ../${name} $out/system/${name2}.wants/
+          '') unit.wantedBy) cfg.units)}
+
+      ln -s ${cfg.defaultUnit} $out/system/default.target
+    ''; # */
+    
+in
+
+{
+
+  ###### interface
+
+  options = {
+
+    boot.systemd.units = mkOption {
+      description = "Definition of systemd units.";
+      default = {};
+      type = types.attrsOf types.optionSet;
+      
+      options = {
+      
+        text = mkOption {
+          types = types.uniq types.string;
+          description = "Text of this systemd unit.";
+        };
+        
+        wantedBy = mkOption {
+          default = [];
+          types = types.listOf types.string;
+          description = "Units that want (i.e. depend on) this unit.";
+        };
+        
+      };
+      
+    };
+
+    boot.systemd.services = mkOption {
+      description = "Definition of systemd services.";
+      default = {};
+      type = types.attrsOf types.optionSet;
+      options = [ servicesOptions servicesConfig ];
+    };
+
+    boot.systemd.defaultUnit = mkOption {
+      default = "multi-user.target";
+      type = types.uniq types.string;
+      description = "Default unit started when the system boots.";
+    };
+    
+  };
+
+  
+  ###### implementation
+
+  config = {
+
+    system.build.systemd = systemd;
+
+    system.build.units = units;
+
+    environment.systemPackages = [ systemd ];
+  
+    environment.etc =
+      [ { source = units;
+          target = "systemd";
+        }
+      ];
+
+    boot.systemd.units =
+      { "rescue.service".text = rescueService; }
+      // mapAttrs serviceToUnit cfg.services;
+
+  };
+
+}
diff --git a/modules/system/upstart-events/control-alt-delete.nix b/modules/system/upstart-events/control-alt-delete.nix
deleted file mode 100644
index f0f146160e58..000000000000
--- a/modules/system/upstart-events/control-alt-delete.nix
+++ /dev/null
@@ -1,26 +0,0 @@
-{ config, pkgs, ... }:
-
-###### implementation
-
-{
-  jobs.control_alt_delete =
-    { name = "control-alt-delete";
-
-      startOn = "control-alt-delete";
-
-      task = true;
-
-      script =
-        ''
-          shutdown -r now 'Ctrl-Alt-Delete pressed'
-        '';
-    };
-
-  system.activationScripts.poweroff =
-    ''
-      # Allow the kernel to find the poweroff command.  This is used
-      # (for instance) by Xen's "xm shutdown" command to signal a
-      # guest to shut down cleanly.
-      echo ${config.system.build.upstart}/sbin/poweroff > /proc/sys/kernel/poweroff_cmd
-    '';
-}
diff --git a/modules/system/upstart-events/runlevel.nix b/modules/system/upstart-events/runlevel.nix
deleted file mode 100644
index 016217ea3a67..000000000000
--- a/modules/system/upstart-events/runlevel.nix
+++ /dev/null
@@ -1,38 +0,0 @@
-{ config, pkgs, ... }:
-
-with pkgs.lib;
-
-{
-
-  # After booting, go to runlevel 2.  (NixOS doesn't really use
-  # runlevels, but this keeps wtmp happy.)
-  jobs.boot =
-    { name = "boot";
-      startOn = "startup";
-      task = true;
-      restartIfChanged = false;
-      script = "telinit 2";
-    };
-
-  jobs.runlevel =
-    { name = "runlevel";
-
-      startOn = "runlevel [0123456S]";
-
-      task = true;
-
-      restartIfChanged = false;
-      
-      script =
-        ''
-          case "$RUNLEVEL" in
-              0) initctl start shutdown --no-wait MODE=poweroff;;
-              1) initctl start shutdown --no-wait MODE=maintenance;;
-              2) true;;
-              6) initctl start shutdown --no-wait MODE=reboot;;
-              *) echo "Unsupported runlevel: $RUNLEVEL";;
-          esac
-        '';
-    };
-
-}
diff --git a/modules/system/upstart/upstart.nix b/modules/system/upstart/upstart.nix
index 1bfde9fc60ee..de60f693e831 100644
--- a/modules/system/upstart/upstart.nix
+++ b/modules/system/upstart/upstart.nix
@@ -12,182 +12,92 @@ let
   groupExists = g:
     (g == "") || any (gg: gg.name == g) (attrValues config.users.extraGroups);
 
-  # From a job description, generate an Upstart job file.
-  makeJob = job:
+  # From a job description, generate an systemd unit file.
+  makeUnit = job:
 
     let
       hasMain = job.script != "" || job.exec != "";
 
       env = config.system.upstartEnvironment // job.environment;
 
-      jobText =
-        let log = "/var/log/upstart/${job.name}"; in
+      preStartScript = pkgs.writeScript "${job.name}-pre-start.sh"
         ''
-          # Upstart job `${job.name}'.  This is a generated file.  Do not edit.
+          #! ${pkgs.stdenv.shell} -e
+          ${job.preStart}
+        '';
 
-          ${optionalString (job.description != "") ''
-            description "${job.description}"
+      startScript = pkgs.writeScript "${job.name}-start.sh"
+        ''
+          #! ${pkgs.stdenv.shell} -e
+          ${if job.script != "" then job.script else ''
+            exec ${job.exec}
           ''}
+        '';
 
-          ${if isList job.startOn then
-              "start on ${concatStringsSep " or " job.startOn}"
-            else if job.startOn != "" then
-              "start on ${job.startOn}"
-            else ""
-          }
-
-          ${optionalString (job.stopOn != "") "stop on ${job.stopOn}"}
-
-          env PATH=${job.path}
-
-          ${concatMapStrings (n: "env ${n}=\"${getAttr n env}\"\n") (attrNames env)}
-
-          ${optionalString (job.console != "") "console ${job.console}"}
-
-          pre-start script
-            ${optionalString (job.console == "") ''
-              exec >> ${log} 2>&1
-            ''}
-            ln -sfn "$(readlink -f "/etc/init/${job.name}.conf")" /var/run/upstart-jobs/${job.name}
-            ${optionalString (job.preStart != "") ''
-              source ${jobHelpers}
-              ${job.preStart}
-            ''}
-          end script
-
-          ${if job.script != "" && job.exec != "" then
-              abort "Job ${job.name} has both a `script' and `exec' attribute."
-            else if job.script != "" then
-              ''
-                script
-                  ${optionalString (job.console == "") ''
-                    exec >> ${log} 2>&1
-                  ''}
-                  source ${jobHelpers}
-                  ${job.script}
-                end script
-              ''
-            else if job.exec != "" && job.console == "" then
-              ''
-                script
-                  exec >> ${log} 2>&1
-                  exec ${job.exec}
-                end script
-              ''
-            else if job.exec != "" then
-              ''
-                exec ${job.exec}
-              ''
-            else ""
-          }
+      postStartScript = pkgs.writeScript "${job.name}-post-start.sh"
+        ''
+          #! ${pkgs.stdenv.shell} -e
+          ${job.postStart}
+        '';
 
-          ${optionalString (job.postStart != "") ''
-            post-start script
-              ${optionalString (job.console == "") ''
-                exec >> ${log} 2>&1
-              ''}
-              source ${jobHelpers}
-              ${job.postStart}
-            end script
-          ''}
+      preStopScript = pkgs.writeScript "${job.name}-pre-stop.sh"
+        ''
+          #! ${pkgs.stdenv.shell} -e
+          ${job.preStop}
+        '';
 
-          ${optionalString job.task "task"}
-          ${optionalString (!job.task && job.respawn) "respawn"}
-
-          ${ # preStop is run only if there is exec or script.
-             # (upstart 0.6.5, job.c:562)
-            optionalString (job.preStop != "") (assert hasMain; ''
-            pre-stop script
-              ${optionalString (job.console == "") ''
-                exec >> ${log} 2>&1
-              ''}
-              source ${jobHelpers}
-              ${job.preStop}
-            end script
-          '')}
+      postStopScript = pkgs.writeScript "${job.name}-post-stop.sh"
+        ''
+          #! ${pkgs.stdenv.shell} -e
+          ${job.postStop}
+        '';
+    in {
 
-          ${optionalString (job.postStop != "") ''
-            post-stop script
-              ${optionalString (job.console == "") ''
-                exec >> ${log} 2>&1
-              ''}
-              source ${jobHelpers}
-              ${job.postStop}
-            end script
+      inherit (job) description path environment;
+
+      after =
+        if job.startOn == "stopped udevtrigger" then [ "systemd-udev-settle.service" ] else
+        if job.startOn == "started udev" then [ "systemd-udev.service" ] else
+        [];
+
+      wantedBy = if job.startOn == "" then [ ] else [ "multi-user.target" ];
+
+      serviceConfig =
+        ''
+          ${optionalString (job.preStart != "" && (job.script != "" || job.exec != "")) ''
+            ExecStartPre=${preStartScript}
           ''}
 
-          ${if job.daemonType == "fork" then "expect fork" else
-            if job.daemonType == "daemon" then "expect daemon" else
-            if job.daemonType == "stop" then "expect stop" else
-            if job.daemonType == "none" then "" else
-            throw "invalid daemon type `${job.daemonType}'"}
+          ${optionalString (job.preStart != "" && job.script == "" && job.exec == "") ''
+            ExecStart=${preStartScript}
+          ''}
 
-          ${optionalString (job.setuid != "") ''
-            setuid ${job.setuid}
+          ${optionalString (job.script != "" || job.exec != "") ''
+            ExecStart=${startScript}
+          ''}
+          
+          ${optionalString (job.postStart != "") ''
+            ExecStartPost=${postStartScript}
           ''}
 
-          ${optionalString (job.setgid != "") ''
-            setuid ${job.setgid}
+          ${optionalString (job.preStop != "") ''
+            ExecStop=${preStopScript}
+          ''}
+          
+          ${optionalString (job.postStop != "") ''
+            ExecStopPost=${postStopScript}
           ''}
 
-          ${job.extraConfig}
-        '';
+          ${if job.script == "" && job.exec == "" then "Type=oneshot\nRemainAfterExit=true" else
+            if job.daemonType == "fork" then "Type=forking\nGuessMainPID=true" else
+            if job.daemonType == "none" then "" else
+            throw "invalid daemon type `${job.daemonType}'"}
 
-    in
-      pkgs.runCommand ("upstart-" + job.name + ".conf")
-        { inherit (job) buildHook; inherit jobText; preferLocalBuild = true; }
-        ''
-          eval "$buildHook"
-          echo "$jobText" > $out
+          ${optionalString (!job.task && job.respawn) "Restart=always"}
         '';
+    };
 
 
-  # Shell functions for use in Upstart jobs.
-  jobHelpers = pkgs.writeText "job-helpers.sh"
-    ''
-      # Ensure that an Upstart service is running.
-      ensure() {
-          local job="$1"
-          local status="$(status "$job")"
-
-          # If it's already running, we're happy.
-          [[ "$status" =~ start/running ]] && return 0
-
-          # If its current goal is to stop, start it.
-          [[ "$status" =~ stop/ ]] && { status="$(start "$job")" || true; }
-
-          # The "start" command is synchronous *if* the job is
-          # not already starting.  So if somebody else started
-          # the job in parallel, the "start" above may return
-          # while the job is still starting.  So wait until it
-          # is up or has failed.
-          while true; do
-              [[ "$status" =~ stop/ ]] && { echo "job $job failed to start"; return 1; }
-              [[ "$status" =~ start/running ]] && return 0
-              echo "waiting for job $job to start..."
-              sleep 1
-              status="$(status "$job")"
-          done
-      }
-
-      # Check whether the current job has been stopped.  Used in
-      # post-start jobs to determine if they should continue.
-      stop_check() {
-          local status="$(status)"
-          if [[ "$status" =~ stop/ ]]; then
-              echo "job asked to stop!"
-              return 1
-          fi
-          if [[ "$status" =~ respawn/ ]]; then
-              echo "job respawning unexpectedly!"
-              stop
-              return 1
-          fi
-          return 0
-      }
-    '';
-        
-
   jobOptions = {
 
     name = mkOption {
@@ -199,17 +109,6 @@ let
       '';
     };
 
-    buildHook = mkOption {
-      type = types.string;
-      default = "true";
-      description = ''
-        Command run while building the Upstart job.  Can be used
-        to perform simple regression tests (e.g., the Apache
-        Upstart job uses it to check the syntax of the generated
-        <filename>httpd.conf</filename>.
-      '';
-    };
-
     description = mkOption {
       type = types.string;
       default = "";
@@ -373,8 +272,7 @@ let
     };
 
     path = mkOption {
-      default = [ ];
-      apply = ps: "${makeSearchPath "bin" ps}:${makeSearchPath "sbin" ps}";
+      default = [];
       description = ''
         Packages added to the job's <envar>PATH</envar> environment variable.
         Both the <filename>bin</filename> and <filename>sbin</filename>
@@ -401,13 +299,9 @@ let
 
     options = {
     
-      jobDrv = mkOption {
-        default = makeJob config;
-        type = types.uniq types.package;
-        description = ''
-          Derivation that builds the Upstart job file.  The default
-          value is generated from other options.
-        '';
+      unit = mkOption {
+        default = makeUnit config;
+        description = "Generated definition of the systemd unit corresponding to this job.";
       };
       
     };
@@ -448,16 +342,6 @@ in
       options = [ jobOptions upstartJob ];
     };
 
-    tests.upstartJobs = mkOption {
-      internal = true;
-      default = {};
-      description = ''
-        Make it easier to build individual Upstart jobs. (e.g.,
-        <command>nix-build /etc/nixos/nixos -A
-        tests.upstartJobs.xserver</command>).
-      '';
-    };
-
     system.upstartEnvironment = mkOption {
       type = types.attrs;
       default = {};
@@ -476,23 +360,10 @@ in
 
     system.build.upstart = upstart;
 
-    environment.etc =
-      flip map (attrValues config.jobs) (job:
-        { source = job.jobDrv;
-          target = "init/${job.name}.conf";
-        } );
-
-    # Upstart can listen on the system bus, allowing normal users to
-    # do status queries.
-    services.dbus.packages = [ upstart ];
-
-    system.activationScripts.chownJobLogs = stringAfter ["var"] 
-    (concatMapStrings (job: ''
-      touch /var/log/upstart/${job.name}
-      ${optionalString (job.setuid != "") "chown ${job.setuid} /var/log/upstart/${job.name}"}
-      ${optionalString (job.setgid != "") "chown :${job.setgid} /var/log/upstart/${job.name}"}
-    '') (attrValues config.jobs));
-
+    boot.systemd.services =
+      flip mapAttrs' config.jobs (name: job:
+        nameValuePair "${job.name}.service" job.unit);
+        
   };
 
 }
diff --git a/modules/tasks/filesystems.nix b/modules/tasks/filesystems.nix
index 9495cac67855..8eaf77330a3d 100644
--- a/modules/tasks/filesystems.nix
+++ b/modules/tasks/filesystems.nix
@@ -174,7 +174,7 @@ in
     system.fsPackages = [ pkgs.dosfstools ];
     
     environment.systemPackages =
-      [ pkgs.ntfs3g pkgs.cifs_utils pkgs.mountall ]
+      [ pkgs.ntfs3g pkgs.cifs_utils ]
       ++ config.system.fsPackages;
 
     environment.etc = singleton
@@ -182,6 +182,7 @@ in
         target = "fstab";
       };
 
+    /*
     jobs.mountall =
       { startOn = "started udev or config-changed";
 
@@ -309,6 +310,7 @@ in
             initctl start --no-wait mountall
           '';
       };
+    */
 
   };
 
diff --git a/modules/tasks/kbd.nix b/modules/tasks/kbd.nix
index d9e820ef9e76..dd4b9f382c09 100644
--- a/modules/tasks/kbd.nix
+++ b/modules/tasks/kbd.nix
@@ -54,8 +54,10 @@ in
 
     inherit requiredTTYs; # pass it to ./modules/tasks/tty-backgrounds.nix
 
-    environment.systemPackages = [pkgs.kbd];
+    environment.systemPackages = [ pkgs.kbd ];
 
+    /* FIXME - remove; this is handled by systemd now.
+    
     jobs.kbd =
       { description = "Keyboard / console initialisation";
 
@@ -120,6 +122,7 @@ in
           ${pkgs.kbd}/bin/loadkeys '${consoleKeyMap}'
         '';
       };
+    */
 
   };
 
diff --git a/modules/tasks/lvm.nix b/modules/tasks/lvm.nix
index 635146ac889b..7773a2c79e31 100644
--- a/modules/tasks/lvm.nix
+++ b/modules/tasks/lvm.nix
@@ -14,8 +14,6 @@
             # Make all logical volumes on all volume groups available, i.e.,
             # make them appear in /dev.
             ${pkgs.lvm2}/sbin/vgchange --available y
-
-            initctl emit -n new-devices
           '';
 
         task = true;
diff --git a/modules/tasks/network-interfaces.nix b/modules/tasks/network-interfaces.nix
index c12f9b28ca91..611de13148a3 100644
--- a/modules/tasks/network-interfaces.nix
+++ b/modules/tasks/network-interfaces.nix
@@ -267,7 +267,8 @@ in
 
             ${optionalString (cfg.interfaces != [] || cfg.localCommands != "") ''
               # Emit the ip-up event (e.g. to start ntpd).
-              initctl emit -n ip-up
+              #FIXME
+              #initctl emit -n ip-up
             ''}
           '';
       };
diff --git a/modules/testing/test-instrumentation.nix b/modules/testing/test-instrumentation.nix
index 830f1744fa04..ce9c1cea7431 100644
--- a/modules/testing/test-instrumentation.nix
+++ b/modules/testing/test-instrumentation.nix
@@ -7,15 +7,12 @@ with pkgs.lib;
 
 {
 
-  config =
-  # Require a patch to the kernel to increase the 15s CIFS timeout.
-  mkAssert (config.boot.kernelPackages.kernel.features ? cifsTimeout) "
-    VM tests require that the kernel has the CIFS timeout patch.
-  " {
+  config = {
 
-    jobs.backdoor =
-      { startOn = "started udev";
-        stopOn = "";
+    boot.systemd.services."backdoor.service" =
+      { wantedBy = [ "multi-user.target" ];
+        requires = [ "dev-hvc0.device" ];
+        after = [ "dev-hvc0.device" ];
 
         script =
           ''
@@ -30,8 +27,6 @@ with pkgs.lib;
             echo
             PS1= /bin/sh
           '';
-
-        respawn = false;
       };
 
     boot.initrd.postDeviceCommands =