From 5fc4590ce56dda56e02f1e980452838952e10812 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 6 Nov 2009 09:26:36 +0000 Subject: [PATCH 01/44] * Fix indentation. svn path=/nixos/branches/upstart-0.6/; revision=18186 --- modules/system/upstart/upstart.nix | 223 +++++++++++++++-------------- 1 file changed, 113 insertions(+), 110 deletions(-) diff --git a/modules/system/upstart/upstart.nix b/modules/system/upstart/upstart.nix index bb6a4f9a117..42ac9899673 100644 --- a/modules/system/upstart/upstart.nix +++ b/modules/system/upstart/upstart.nix @@ -73,10 +73,12 @@ let echo "$jobText" > $out/etc/event.d/${job.name} ''; + jobs = [pkgs.upstart] # for the built-in logd job ++ map (job: job.upstartPkg) (attrValues config.jobs); + # Create an etc/event.d directory containing symlinks to the # specified list of Upstart job files. jobsDir = pkgs.runCommand "upstart-jobs" {inherit jobs;} @@ -93,132 +95,133 @@ let done ''; # */ - # !! remove extra indentations. + jobOptions = { - name = mkOption { - # !!! The type should ensure that this could be a filename. - type = types.string; - example = "sshd"; - description = '' - Name of the Upstart job. - ''; - }; + name = mkOption { + # !!! The type should ensure that this could be a filename. + type = types.string; + example = "sshd"; + description = '' + Name of the Upstart job. + ''; + }; - 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 - httpd.conf. - ''; - }; + 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 + httpd.conf. + ''; + }; - description = mkOption { - type = types.string; - default = "(no description given)"; - description = '' - A short description of this job. - ''; - }; + description = mkOption { + type = types.string; + default = "(no description given)"; + description = '' + A short description of this job. + ''; + }; - startOn = mkOption { - # !!! Re-enable this once we're on Upstart >= 0.6. - #type = types.string; - default = ""; - description = '' - The Upstart event that triggers this job to be started. - If empty, the job will not start automatically. - ''; - }; + startOn = mkOption { + # !!! Re-enable this once we're on Upstart >= 0.6. + #type = types.string; + default = ""; + description = '' + The Upstart event that triggers this job to be started. + If empty, the job will not start automatically. + ''; + }; - stopOn = mkOption { - type = types.string; - default = "shutdown"; - description = '' - The Upstart event that triggers this job to be stopped. - ''; - }; + stopOn = mkOption { + type = types.string; + default = "shutdown"; + description = '' + The Upstart event that triggers this job to be stopped. + ''; + }; - preStart = mkOption { - type = types.string; - default = ""; - description = '' - Shell commands executed before the job is started - (i.e. before the job's main process is started). - ''; - }; + preStart = mkOption { + type = types.string; + default = ""; + description = '' + Shell commands executed before the job is started + (i.e. before the job's main process is started). + ''; + }; - postStop = mkOption { - type = types.string; - default = ""; - description = '' - Shell commands executed after the job has stopped - (i.e. after the job's main process has terminated). - ''; - }; + postStop = mkOption { + type = types.string; + default = ""; + description = '' + Shell commands executed after the job has stopped + (i.e. after the job's main process has terminated). + ''; + }; - exec = mkOption { - type = types.string; - default = ""; - description = '' - Command to start the job's main process. If empty, the - job has no main process, but can still have pre/post-start - and pre/post-stop scripts, and is considered "running" - until it is stopped. - ''; - }; + exec = mkOption { + type = types.string; + default = ""; + description = '' + Command to start the job's main process. If empty, the + job has no main process, but can still have pre/post-start + and pre/post-stop scripts, and is considered "running" + until it is stopped. + ''; + }; - script = mkOption { - type = types.string; - default = ""; - description = '' - Shell commands executed as the job's main process. Can be - specified instead of the exec attribute. - ''; - }; + script = mkOption { + type = types.string; + default = ""; + description = '' + Shell commands executed as the job's main process. Can be + specified instead of the exec attribute. + ''; + }; - respawn = mkOption { - type = types.bool; - default = true; - description = '' - Whether to restart the job automatically if its process - ends unexpectedly. - ''; - }; + respawn = mkOption { + type = types.bool; + default = true; + description = '' + Whether to restart the job automatically if its process + ends unexpectedly. + ''; + }; - task = mkOption { - type = types.bool; - default = false; - description = '' - Whether this job is a task rather than a service. Tasks - are executed only once, while services are restarted when - they exit. - ''; - }; + task = mkOption { + type = types.bool; + default = false; + description = '' + Whether this job is a task rather than a service. Tasks + are executed only once, while services are restarted when + they exit. + ''; + }; - environment = mkOption { - type = types.attrs; - default = {}; - example = { PATH = "/foo/bar/bin"; LANG = "nl_NL.UTF-8"; }; - description = '' - Environment variables passed to the job's processes. - ''; - }; + environment = mkOption { + type = types.attrs; + default = {}; + example = { PATH = "/foo/bar/bin"; LANG = "nl_NL.UTF-8"; }; + description = '' + Environment variables passed to the job's processes. + ''; + }; - extraConfig = mkOption { - type = types.string; - default = ""; - example = "limit nofile 4096 4096"; - description = '' - Additional Upstart stanzas not otherwise supported. - ''; - }; + extraConfig = mkOption { + type = types.string; + default = ""; + example = "limit nofile 4096 4096"; + description = '' + Additional Upstart stanzas not otherwise supported. + ''; + }; - }; + }; + upstartJob = {name, config, ...}: { options = { upstartPkg = mkOption { From b581a56d79834fd7b89341e7db10a03acaa4837a Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 6 Nov 2009 09:36:35 +0000 Subject: [PATCH 02/44] * Define the Upstart package to use in one place. svn path=/nixos/branches/upstart-0.6/; revision=18187 --- modules/config/system-path.nix | 2 +- modules/services/networking/dhclient.nix | 6 +++--- modules/services/networking/gw6c.nix | 4 +++- modules/services/x11/display-managers/kdm.nix | 4 ++-- modules/services/x11/display-managers/slim.nix | 4 ++-- modules/system/activation/top-level.nix | 6 +++--- modules/system/boot/stage-2.nix | 5 +++-- modules/system/upstart/upstart.nix | 7 ++++++- 8 files changed, 23 insertions(+), 15 deletions(-) diff --git a/modules/config/system-path.nix b/modules/config/system-path.nix index c2605d0963e..50617ec91da 100644 --- a/modules/config/system-path.nix +++ b/modules/config/system-path.nix @@ -10,6 +10,7 @@ let requiredPackages = [ config.system.sbin.modprobe # must take precedence over module_init_tools config.system.sbin.mount # must take precedence over util-linux + config.system.build.upstart config.environment.nix pkgs.acl pkgs.attr @@ -53,7 +54,6 @@ let pkgs.sysvtools pkgs.time pkgs.udev - pkgs.upstart pkgs.usbutils pkgs.utillinux ]; diff --git a/modules/services/networking/dhclient.nix b/modules/services/networking/dhclient.nix index 9696c8b14aa..80451ce2a5a 100644 --- a/modules/services/networking/dhclient.nix +++ b/modules/services/networking/dhclient.nix @@ -26,13 +26,13 @@ let # hostnames in its config file, then it will never do # anything ever again ("couldn't resolve ..., giving up on # it"), so we silently lose time synchronisation. - ${pkgs.upstart}/sbin/initctl stop ntpd + ${config.system.build.upstart}/sbin/initctl stop ntpd - ${pkgs.upstart}/sbin/initctl emit ip-up + ${config.system.build.upstart}/sbin/initctl emit ip-up fi if test "$reason" = EXPIRE -o "$reason" = RELEASE; then - ${pkgs.upstart}/sbin/initctl emit ip-down + ${config.system.build.upstart}/sbin/initctl emit ip-down fi ''; diff --git a/modules/services/networking/gw6c.nix b/modules/services/networking/gw6c.nix index 143fe57979a..d723766bb2c 100644 --- a/modules/services/networking/gw6c.nix +++ b/modules/services/networking/gw6c.nix @@ -6,10 +6,12 @@ let cfg = config.services.gw6c; + # !!! Move this from the services tree to the nixos tree. gw6cService = import (servicesPath + /gw6c) { inherit (pkgs) stdenv gw6c coreutils - procps upstart iputils gnused + procps iputils gnused gnugrep seccure writeScript; + upstart = config.system.build.upstart; username = cfg.username; password = cfg.password; server = cfg.server; diff --git a/modules/services/x11/display-managers/kdm.nix b/modules/services/x11/display-managers/kdm.nix index 349fd19adfe..55171035442 100644 --- a/modules/services/x11/display-managers/kdm.nix +++ b/modules/services/x11/display-managers/kdm.nix @@ -12,8 +12,8 @@ let defaultConfig = '' [Shutdown] - HaltCmd=${pkgs.upstart}/sbin/halt - RebootCmd=${pkgs.upstart}/sbin/reboot + HaltCmd=${config.system.build.upstart}/sbin/halt + RebootCmd=${config.system.build.upstart}/sbin/reboot ${optionalString (config.system.boot.loader.id == "grub") '' BootManager=Grub ''} diff --git a/modules/services/x11/display-managers/slim.nix b/modules/services/x11/display-managers/slim.nix index af15e0df5e6..ad767e199a1 100644 --- a/modules/services/x11/display-managers/slim.nix +++ b/modules/services/x11/display-managers/slim.nix @@ -14,8 +14,8 @@ let xserver_arguments ${dmcfg.xserverArgs} sessions ${pkgs.lib.concatStringsSep "," (dmcfg.session.names ++ ["custom"])} login_cmd exec ${pkgs.stdenv.bash}/bin/sh ${dmcfg.session.script} "%session" - halt_cmd ${pkgs.upstart}/sbin/shutdown -h now - reboot_cmd ${pkgs.upstart}/sbin/shutdown -r now + halt_cmd ${config.system.build.upstart}/sbin/shutdown -h now + reboot_cmd ${config.system.build.upstart}/sbin/shutdown -r now ${if cfg.defaultUser != "" then "default_user " + cfg.defaultUser else ""} ${if cfg.hideCursor then "hidecursor true" else ""} ''; diff --git a/modules/system/activation/top-level.nix b/modules/system/activation/top-level.nix index 1e3f22c37ad..1f17702b408 100644 --- a/modules/system/activation/top-level.nix +++ b/modules/system/activation/top-level.nix @@ -75,11 +75,11 @@ let ln -s ${config.system.activationScripts.script} $out/activate ln -s ${config.system.build.etc}/etc $out/etc ln -s ${config.system.path} $out/sw - ln -s ${pkgs.upstart} $out/upstart + ln -s ${config.system.build.upstart} $out/upstart echo "$kernelParams" > $out/kernel-params echo "$configurationName" > $out/configuration-name - echo "${toString pkgs.upstart.interfaceVersion}" > $out/upstart-interface-version + echo "${toString config.system.build.upstart.interfaceVersion}" > $out/upstart-interface-version mkdir $out/fine-tune childCount=0; @@ -113,7 +113,7 @@ let pkgs.gnugrep pkgs.findutils pkgs.diffutils - pkgs.upstart # for initctl + config.system.build.upstart # for initctl ]; # Boot loaders diff --git a/modules/system/boot/stage-2.nix b/modules/system/boot/stage-2.nix index 440ff70965f..41c53c492f4 100644 --- a/modules/system/boot/stage-2.nix +++ b/modules/system/boot/stage-2.nix @@ -15,9 +15,10 @@ let }; - inherit (pkgs) substituteAll writeText coreutils utillinux udev upstart; + inherit (pkgs) substituteAll writeText coreutils utillinux udev; kernel = config.boot.kernelPackages.kernel; activateConfiguration = config.system.activationScripts.script; + upstart = config.system.build.upstart; # Path for Upstart jobs. Should be quite minimal. upstartPath = @@ -25,7 +26,7 @@ let pkgs.findutils pkgs.gnugrep pkgs.gnused - pkgs.upstart + upstart ]; bootStage2 = substituteAll { diff --git a/modules/system/upstart/upstart.nix b/modules/system/upstart/upstart.nix index 42ac9899673..a0f5b5802da 100644 --- a/modules/system/upstart/upstart.nix +++ b/modules/system/upstart/upstart.nix @@ -4,6 +4,9 @@ with pkgs.lib; let + upstart = pkgs.upstart; + + # From a job description, generate an Upstart job file. makeJob = job: @@ -75,7 +78,7 @@ let jobs = - [pkgs.upstart] # for the built-in logd job + [ upstart ] # for the built-in logd job ++ map (job: job.upstartPkg) (attrValues config.jobs); @@ -278,6 +281,8 @@ in config = { + system.build.upstart = upstart; + environment.etc = [ { # The Upstart events defined above. source = "${jobsDir}/etc/event.d"; From 7011a9315fc077e65c400d09b42627221809e000 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 6 Nov 2009 10:43:38 +0000 Subject: [PATCH 03/44] * Use Upstart 0.6. Jobs are now in /etc/init instead of /etc/event.d. svn path=/nixos/branches/upstart-0.6/; revision=18192 --- modules/services/ttys/mingetty.nix | 2 +- modules/system/boot/stage-2-init.sh | 12 +++-- modules/system/boot/stage-2.nix | 14 +----- modules/system/upstart/upstart.nix | 71 ++++++++++++----------------- modules/tasks/tty-backgrounds.nix | 2 +- 5 files changed, 39 insertions(+), 62 deletions(-) diff --git a/modules/services/ttys/mingetty.nix b/modules/services/ttys/mingetty.nix index 6c793ffa112..5f6b1d0f14c 100644 --- a/modules/services/ttys/mingetty.nix +++ b/modules/services/ttys/mingetty.nix @@ -55,7 +55,7 @@ with pkgs.lib; # Generate a separate job for each tty. jobs = listToAttrs (map (tty: nameValuePair tty { - startOn = "udev"; + startOn = "started udev"; exec = "${pkgs.mingetty}/sbin/mingetty --loginprog=${pkgs.pam_login}/bin/login --noclear ${tty}"; diff --git a/modules/system/boot/stage-2-init.sh b/modules/system/boot/stage-2-init.sh index 0cc09caebba..a95ff1873ef 100644 --- a/modules/system/boot/stage-2-init.sh +++ b/modules/system/boot/stage-2-init.sh @@ -106,9 +106,11 @@ if test -n "$safeMode"; then fi -# Create the minimal device nodes needed before we run udev. +# 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 # Clear the resume device. @@ -136,12 +138,12 @@ export MODULE_DIR=@kernel@/lib/modules/ # Run any user-specified commands. @shell@ @postBootCommands@ -echo "starting Upstart..." +# For debugging Upstart. +#@shell@ --login < /dev/console > /dev/console 2>&1 & # Start Upstart's init. We start it through the # /var/run/current-system symlink indirection so that we can upgrade # init in a running system by changing the symlink and sending init a # HUP signal. -export UPSTART_CFG_DIR=/etc/event.d -setPath "@upstartPath@" -exec /var/run/current-system/upstart/sbin/init +echo "starting Upstart..." +exec /var/run/current-system/upstart/sbin/init -v diff --git a/modules/system/boot/stage-2.nix b/modules/system/boot/stage-2.nix index 41c53c492f4..b19dbc39183 100644 --- a/modules/system/boot/stage-2.nix +++ b/modules/system/boot/stage-2.nix @@ -18,26 +18,16 @@ let inherit (pkgs) substituteAll writeText coreutils utillinux udev; kernel = config.boot.kernelPackages.kernel; activateConfiguration = config.system.activationScripts.script; - upstart = config.system.build.upstart; - - # Path for Upstart jobs. Should be quite minimal. - upstartPath = - [ pkgs.coreutils - pkgs.findutils - pkgs.gnugrep - pkgs.gnused - upstart - ]; bootStage2 = substituteAll { src = ./stage-2-init.sh; isExecutable = true; - inherit kernel upstart activateConfiguration upstartPath; + inherit kernel activateConfiguration; + upstart = config.system.build.upstart; path = [ coreutils utillinux udev - upstart ]; postBootCommands = writeText "local-cmds" config.boot.postBootCommands; }; diff --git a/modules/system/upstart/upstart.nix b/modules/system/upstart/upstart.nix index a0f5b5802da..0919fc0d75f 100644 --- a/modules/system/upstart/upstart.nix +++ b/modules/system/upstart/upstart.nix @@ -4,9 +4,19 @@ with pkgs.lib; let - upstart = pkgs.upstart; + upstart = pkgs.upstart06; + # Path for Upstart jobs. Should be quite minimal. + upstartPath = + [ pkgs.coreutils + pkgs.findutils + pkgs.gnugrep + pkgs.gnused + upstart + ]; + + # From a job description, generate an Upstart job file. makeJob = job: @@ -18,6 +28,8 @@ let description "${job.description}" + console output + ${if isList job.startOn then # This is a hack to support or-dependencies on Upstart 0.3. concatMapStrings (x: "start on ${x}\n") job.startOn @@ -28,10 +40,11 @@ let ${if job.stopOn != "" then "stop on ${job.stopOn}" else ""} + env PATH=${makeSearchPath "bin" upstartPath}:${makeSearchPath "sbin" upstartPath} ${concatMapStrings (n: "env ${n}=${getAttr n job.environment}\n") (attrNames job.environment)} ${if job.preStart != "" then '' - start script + pre-start script ${job.preStart} end script '' else ""} @@ -48,18 +61,13 @@ let '' exec ${job.exec} '' - else - # Simulate jobs without a main process (which Upstart 0.3 - # doesn't support) using a semi-infinite sleep. - '' - exec sleep 1e100 - '' + else "" } ${if job.respawn && !job.task then "respawn" else ""} ${if job.postStop != "" then '' - stop script + post-stop script ${job.postStop} end script '' else ""} @@ -68,37 +76,14 @@ let ''; in - pkgs.runCommand ("upstart-" + job.name) + pkgs.runCommand ("upstart-" + job.name + ".conf") { inherit (job) buildHook; inherit jobText; } '' eval "$buildHook" - ensureDir $out/etc/event.d - echo "$jobText" > $out/etc/event.d/${job.name} + echo "$jobText" > $out ''; - jobs = - [ upstart ] # for the built-in logd job - ++ map (job: job.upstartPkg) (attrValues config.jobs); - - - # Create an etc/event.d directory containing symlinks to the - # specified list of Upstart job files. - jobsDir = pkgs.runCommand "upstart-jobs" {inherit jobs;} - '' - ensureDir $out/etc/event.d - for i in $jobs; do - if ln -s $i . ; then - if test -d $i; then - ln -s $i/etc/event.d/* $out/etc/event.d/ - fi - else - echo Duplicate entry: $i; - fi; - done - ''; # */ - - jobOptions = { name = mkOption { @@ -227,13 +212,12 @@ let upstartJob = {name, config, ...}: { options = { - upstartPkg = mkOption { + jobDrv = mkOption { default = makeJob config; type = types.uniq types.package; description = '' - Upstart package which contains upstart events inside - /etc/event.d/. The default value is - generated from other options. + Derivation that builds the Upstart job file. The default + value is generated from other options. ''; }; }; @@ -284,18 +268,19 @@ in system.build.upstart = upstart; environment.etc = - [ { # The Upstart events defined above. - source = "${jobsDir}/etc/event.d"; - target = "event.d"; - } - ]; + flip map (attrValues config.jobs) (job: + { source = job.jobDrv; + target = "init/${job.name}.conf"; + } ); # !!! fix this + /* tests.upstartJobs = { recurseForDerivations = true; } // builtins.listToAttrs (map (job: { name = removePrefix "upstart-" job.name; value = job; }) jobs); + */ }; diff --git a/modules/tasks/tty-backgrounds.nix b/modules/tasks/tty-backgrounds.nix index d15365fab9d..5f557ac7f5c 100644 --- a/modules/tasks/tty-backgrounds.nix +++ b/modules/tasks/tty-backgrounds.nix @@ -105,7 +105,7 @@ in jobs.ttyBackgrounds = { name = "tty-backgrounds"; - startOn = "udev"; + startOn = "started udev"; preStart = '' From 5d240b99d57126ee611f46fb7cd5ea2f15166dba Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 6 Nov 2009 11:31:57 +0000 Subject: [PATCH 04/44] * Work around a bug in HAL (it fails to start if $PATH is too long, since it uses a 512-byte buffer somewhere). svn path=/nixos/branches/upstart-0.6/; revision=18198 --- modules/services/hardware/hal.nix | 12 +++++------- modules/services/system/dbus.nix | 3 +-- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/modules/services/hardware/hal.nix b/modules/services/hardware/hal.nix index 11cda8d4cc4..56bcf1fb4d8 100644 --- a/modules/services/hardware/hal.nix +++ b/modules/services/hardware/hal.nix @@ -69,7 +69,7 @@ in # !!! TODO: make sure that HAL starts after acpid, # otherwise hald-addon-acpi will grab /proc/acpi/event. - startOn = if config.powerManagement.enable then "acpid" else "dbus"; + startOn = if config.powerManagement.enable then "started acpid" else "started dbus"; stopOn = "shutdown"; environment = @@ -100,15 +100,13 @@ in rm -f /var/cache/hald/fdi-cache - # For some weird reason HAL sometimes fails to start at - # boot time, which seems to be timing-dependent. As a - # temporary workaround, sleep for a while here. - sleep 2 - # !!! Hack: start the daemon here to make sure it's # running when the Upstart job reaches the "running" # state. Should be fixable in Upstart 0.6. - ${hal}/sbin/hald --use-syslog # --verbose=yes + # The `PATH=' works around a bug in HAL: it concatenates + # its libexec directory to $PATH, but using a 512-byte + # buffer. So if $PATH is too long it fails. + PATH= ${hal}/sbin/hald --use-syslog # --verbose=yes ''; postStop = diff --git a/modules/services/system/dbus.nix b/modules/services/system/dbus.nix index 8e1d9bd4d10..d91be4eb88b 100644 --- a/modules/services/system/dbus.nix +++ b/modules/services/system/dbus.nix @@ -113,8 +113,7 @@ in }; jobs.dbus = - { startOn = "udev"; - stopOn = "shutdown"; + { startOn = "started udev"; preStart = '' From 903e92bde6508486482c6d2d18e7ac2087fc06c4 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 6 Nov 2009 15:23:16 +0000 Subject: [PATCH 05/44] * Use Upstart's "export fork" feature to properly detect when a daemon is "ready". This prevents ugly race conditions, e.g. HAL failing to start because dbus hasn't finished starting yet. * Support post-start scripts. These are executed after the job's main process has started but before the job's "started" event is emitted. For instance, the udev job uses this to perform "udevadm trigger / settle" to create all devices. Previously this had to be done in the pre-start script, so the daemon had to started in the pre-start script as well. svn path=/nixos/branches/upstart-0.6/; revision=18211 --- modules/services/hardware/hal.nix | 19 ++++------- modules/services/hardware/udev.nix | 27 +++++---------- modules/services/system/dbus.nix | 19 ++++------- modules/system/upstart/upstart.nix | 53 ++++++++++++++++++++++++++---- 4 files changed, 67 insertions(+), 51 deletions(-) diff --git a/modules/services/hardware/hal.nix b/modules/services/hardware/hal.nix index 56bcf1fb4d8..be51968317f 100644 --- a/modules/services/hardware/hal.nix +++ b/modules/services/hardware/hal.nix @@ -99,21 +99,14 @@ in mkdir -m 0755 -p /var/run/hald rm -f /var/cache/hald/fdi-cache - - # !!! Hack: start the daemon here to make sure it's - # running when the Upstart job reaches the "running" - # state. Should be fixable in Upstart 0.6. - # The `PATH=' works around a bug in HAL: it concatenates - # its libexec directory to $PATH, but using a 512-byte - # buffer. So if $PATH is too long it fails. - PATH= ${hal}/sbin/hald --use-syslog # --verbose=yes ''; - postStop = - '' - pid=$(cat /var/run/hald/pid || true) - test -n "$pid" && kill "$pid" - ''; + daemonType = "fork"; + + # The `PATH=' works around a bug in HAL: it concatenates + # its libexec directory to $PATH, but using a 512-byte + # buffer. So if $PATH is too long it fails. + script = "PATH= exec ${hal}/sbin/hald --use-syslog"; }; services.udev.packages = [hal]; diff --git a/modules/services/hardware/udev.nix b/modules/services/hardware/udev.nix index 105f8a2b54f..2baa87e9547 100644 --- a/modules/services/hardware/udev.nix +++ b/modules/services/hardware/udev.nix @@ -169,9 +169,6 @@ in mkdir -p /var/lib/udev/rules.d - # Get rid of possible old udev processes. - ${procps}/bin/pkill -u root "^udevd$" || true - # Do the loading of additional stage 2 kernel modules. # Maybe this isn't the best place... for i in ${toString config.boot.kernelModules}; do @@ -179,32 +176,24 @@ in ${modprobe}/sbin/modprobe $i || true done - # Start udev. mkdir -p /dev/.udev # !!! bug in udev? - ${udev}/sbin/udevd --daemon + ''; + daemonType = "fork"; + + exec = "${udev}/sbin/udevd --daemon"; + + postStart = + '' # 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). ${udev}/sbin/udevadm trigger ${udev}/sbin/udevadm settle # wait for udev to finish - # Kill udev, let Upstart restart and monitor it. (This is nasty, - # but we have to run `udevadm trigger' first. Maybe we can use - # Upstart's `binary' keyword, but it isn't implemented yet.) - if ! ${procps}/bin/pkill -u root "^udevd$"; then - echo "couldn't stop udevd" - fi - - while ${procps}/bin/pgrep -u root "^udevd$"; do - sleep 1 - done - initctl emit new-devices ''; - - exec = "${udev}/sbin/udevd"; - + }; }; diff --git a/modules/services/system/dbus.nix b/modules/services/system/dbus.nix index d91be4eb88b..b0c8dc66ae5 100644 --- a/modules/services/system/dbus.nix +++ b/modules/services/system/dbus.nix @@ -124,24 +124,19 @@ in ${dbus.tools}/bin/dbus-uuidgen --ensure rm -f ${homeDir}/pid - # !!! hack - dbus should be running once this job is - # considered "running"; should be fixable once we have - # Upstart 0.6. - ${dbus}/bin/dbus-daemon --config-file=${configDir}/system.conf ''; + daemonType = "fork"; + + exec = "${dbus}/bin/dbus-daemon --config-file=${configDir}/system.conf"; + postStop = '' - pid=$(cat ${homeDir}/pid) - if test -n "$pid"; then - kill $pid - fi - # !!! Hack: doesn't belong here. - pid=$(cat /var/run/ConsoleKit/pid) + pid=$(cat /var/run/ConsoleKit/pid || true) if test -n "$pid"; then - kill $pid - rm /var/run/ConsoleKit/pid + kill $pid || true + rm -f /var/run/ConsoleKit/pid fi ''; }; diff --git a/modules/system/upstart/upstart.nix b/modules/system/upstart/upstart.nix index 0919fc0d75f..fea8f9e880c 100644 --- a/modules/system/upstart/upstart.nix +++ b/modules/system/upstart/upstart.nix @@ -38,16 +38,16 @@ let else "" } - ${if job.stopOn != "" then "stop on ${job.stopOn}" else ""} + ${optionalString (job.stopOn != "") "stop on ${job.stopOn}"} env PATH=${makeSearchPath "bin" upstartPath}:${makeSearchPath "sbin" upstartPath} ${concatMapStrings (n: "env ${n}=${getAttr n job.environment}\n") (attrNames job.environment)} - ${if job.preStart != "" then '' + ${optionalString (job.preStart != "") '' pre-start script ${job.preStart} end script - '' else ""} + ''} ${if job.script != "" && job.exec != "" then abort "Job ${job.name} has both a `script' and `exec' attribute." @@ -64,13 +64,28 @@ let else "" } - ${if job.respawn && !job.task then "respawn" else ""} + ${optionalString (job.postStart != "") '' + post-start script + ${job.postStart} + end script + ''} + + ${optionalString job.task "task"} + ${optionalString job.respawn "respawn"} - ${if job.postStop != "" then '' + ${optionalString (job.postStop != "") '' post-stop script ${job.postStop} end script - '' else ""} + ''} + + ${optionalString (!job.task) ( + 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}'" + )} ${job.extraConfig} ''; @@ -141,6 +156,16 @@ let ''; }; + postStart = mkOption { + type = types.string; + default = ""; + description = '' + Shell commands executed after the job is started (i.e. after + the job's main process is started), but before the job is + considered “running”. + ''; + }; + postStop = mkOption { type = types.string; default = ""; @@ -156,7 +181,7 @@ let description = '' Command to start the job's main process. If empty, the job has no main process, but can still have pre/post-start - and pre/post-stop scripts, and is considered "running" + and pre/post-stop scripts, and is considered “running” until it is stopped. ''; }; @@ -198,6 +223,20 @@ let ''; }; + daemonType = mkOption { + type = types.string; + default = "none"; + description = '' + Determines how Upstart detects when a daemon should be + considered “running”. The value none means + that the daemon is considered ready immediately. The value + fork means that the daemon will fork once. + The value daemon means that the daemon will + fork twice. The value stop means that the + daemon will raise the SIGSTOP signal to indicate readiness. + ''; + }; + extraConfig = mkOption { type = types.string; default = ""; From 9fa2f12cc254691dd52c7ee38f77576ddf7111a0 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 6 Nov 2009 15:46:56 +0000 Subject: [PATCH 06/44] * Do some more jobs. svn path=/nixos/branches/upstart-0.6/; revision=18212 --- modules/services/logging/klogd.nix | 19 ++++--------------- modules/services/logging/syslogd.nix | 7 ++++--- modules/services/networking/ssh/sshd.nix | 7 ++++--- modules/system/upstart/upstart.nix | 3 +-- modules/tasks/network-interfaces.nix | 2 +- 5 files changed, 14 insertions(+), 24 deletions(-) diff --git a/modules/services/logging/klogd.nix b/modules/services/logging/klogd.nix index 0b2e3d9ddc7..98d9d5eeabf 100644 --- a/modules/services/logging/klogd.nix +++ b/modules/services/logging/klogd.nix @@ -2,28 +2,17 @@ ###### implementation -let - - klogdCmd = "${pkgs.sysklogd}/sbin/klogd -c 1 -2 -k $(dirname $(readlink -f /var/run/booted-system/kernel))/System.map"; - -in - { jobs.klogd = { description = "Kernel log daemon"; - startOn = "syslogd"; + startOn = "started syslogd"; stopOn = "shutdown"; - preStart = - '' - # !!! this hangs for some reason (it blocks reading from - # /proc/kmsg). - #${klogdCmd} -o - ''; - - exec = "${klogdCmd} -n"; + exec = + "${pkgs.sysklogd}/sbin/klogd -c 1 -2 -n " + + "-k $(dirname $(readlink -f /var/run/booted-system/kernel))/System.map"; }; } diff --git a/modules/services/logging/syslogd.nix b/modules/services/logging/syslogd.nix index bc6b6eb4389..7c3e54cadb0 100644 --- a/modules/services/logging/syslogd.nix +++ b/modules/services/logging/syslogd.nix @@ -50,12 +50,13 @@ in jobs.syslogd = { description = "Syslog daemon"; - startOn = "udev"; - stopOn = "shutdown"; + startOn = "started udev"; environment = { TZ = config.time.timeZone; }; - exec = "${pkgs.sysklogd}/sbin/syslogd -n -f ${syslogConf}"; + daemonType = "fork"; + + exec = "${pkgs.sysklogd}/sbin/syslogd -f ${syslogConf}"; }; }; diff --git a/modules/services/networking/ssh/sshd.nix b/modules/services/networking/ssh/sshd.nix index 8c18a560748..87cb942f2f9 100644 --- a/modules/services/networking/ssh/sshd.nix +++ b/modules/services/networking/ssh/sshd.nix @@ -112,8 +112,7 @@ in description = "OpenSSH server"; - startOn = "network-interfaces/started"; - stopOn = "network-interfaces/stop"; + startOn = "started network-interfaces"; environment = { LD_LIBRARY_PATH = nssModulesPath; }; @@ -126,7 +125,9 @@ in fi ''; - exec = "${openssh}/sbin/sshd -D -h /etc/ssh/ssh_host_dsa_key -f ${sshdConfig}"; + daemonType = "fork"; + + exec = "${openssh}/sbin/sshd -h /etc/ssh/ssh_host_dsa_key -f ${sshdConfig}"; }; networking.firewall.allowedTCPPorts = [22]; diff --git a/modules/system/upstart/upstart.nix b/modules/system/upstart/upstart.nix index fea8f9e880c..9edb0153cb2 100644 --- a/modules/system/upstart/upstart.nix +++ b/modules/system/upstart/upstart.nix @@ -31,8 +31,7 @@ let console output ${if isList job.startOn then - # This is a hack to support or-dependencies on Upstart 0.3. - concatMapStrings (x: "start on ${x}\n") job.startOn + "start on ${concatStringsSep " or " job.startOn}" else if job.startOn != "" then "start on ${job.startOn}" else "" diff --git a/modules/tasks/network-interfaces.nix b/modules/tasks/network-interfaces.nix index 4b53da876da..098828469a6 100644 --- a/modules/tasks/network-interfaces.nix +++ b/modules/tasks/network-interfaces.nix @@ -135,7 +135,7 @@ in jobs.networkInterfaces = { name = "network-interfaces"; - startOn = "udev"; + startOn = "started udev"; preStart = '' From b9bfe7ed43e0839d54708de21639feb1173eb0dd Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 6 Nov 2009 15:59:23 +0000 Subject: [PATCH 07/44] * Connect Upstart to the system bus. svn path=/nixos/branches/upstart-0.6/; revision=18213 --- modules/services/system/dbus.nix | 6 ++++++ modules/system/upstart/upstart.nix | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/modules/services/system/dbus.nix b/modules/services/system/dbus.nix index b0c8dc66ae5..6f79865645d 100644 --- a/modules/services/system/dbus.nix +++ b/modules/services/system/dbus.nix @@ -130,6 +130,12 @@ in exec = "${dbus}/bin/dbus-daemon --config-file=${configDir}/system.conf"; + postStart = + '' + # Signal Upstart that it can connect to the system bus. + kill -HUP 1 || true + ''; + postStop = '' # !!! Hack: doesn't belong here. diff --git a/modules/system/upstart/upstart.nix b/modules/system/upstart/upstart.nix index 9edb0153cb2..3d2af1b0382 100644 --- a/modules/system/upstart/upstart.nix +++ b/modules/system/upstart/upstart.nix @@ -311,6 +311,10 @@ in target = "init/${job.name}.conf"; } ); + # Upstart can listen on the system bus, allowing normal users to + # do status queries. + services.dbus.packages = [ upstart ]; + # !!! fix this /* tests.upstartJobs = { recurseForDerivations = true; } // From 3fcb7c2cb532aa1bee435df185b6d6ed578c74b3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 6 Nov 2009 19:19:46 +0000 Subject: [PATCH 08/44] * Set the nameserver. svn path=/nixos/branches/upstart-0.6/; revision=18218 --- modules/virtualisation/qemu-vm.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/virtualisation/qemu-vm.nix b/modules/virtualisation/qemu-vm.nix index 87138ed5c52..6886a0b23d1 100644 --- a/modules/virtualisation/qemu-vm.nix +++ b/modules/virtualisation/qemu-vm.nix @@ -119,6 +119,8 @@ in networking.defaultGateway = "10.0.2.2"; + networking.nameservers = [ "10.0.2.3" ]; + system.build.vm = pkgs.runCommand "nixos-vm" {} '' ensureDir $out/bin From 676da4d87da48bc754add39add3d6de0a567898c Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 6 Nov 2009 19:20:09 +0000 Subject: [PATCH 09/44] * Updated some more jobs. svn path=/nixos/branches/upstart-0.6/; revision=18219 --- modules/module-list.nix | 2 +- .../{ctrl-alt-delete.nix => control-alt-delete.nix} | 6 +++--- modules/tasks/kbd.nix | 6 ++++-- modules/tasks/lvm.nix | 2 +- modules/tasks/swraid.nix | 4 +++- 5 files changed, 12 insertions(+), 8 deletions(-) rename modules/system/upstart-events/{ctrl-alt-delete.nix => control-alt-delete.nix} (62%) diff --git a/modules/module-list.nix b/modules/module-list.nix index 4addbda5760..eab8503f1ee 100644 --- a/modules/module-list.nix +++ b/modules/module-list.nix @@ -125,7 +125,7 @@ ./system/boot/stage-1.nix ./system/boot/stage-2.nix ./system/etc/etc.nix - ./system/upstart-events/ctrl-alt-delete.nix + ./system/upstart-events/control-alt-delete.nix ./system/upstart-events/halt.nix ./system/upstart-events/maintenance-shell.nix ./system/upstart/upstart.nix diff --git a/modules/system/upstart-events/ctrl-alt-delete.nix b/modules/system/upstart-events/control-alt-delete.nix similarity index 62% rename from modules/system/upstart-events/ctrl-alt-delete.nix rename to modules/system/upstart-events/control-alt-delete.nix index ae1ba2e342e..5fa2bd87eb9 100644 --- a/modules/system/upstart-events/ctrl-alt-delete.nix +++ b/modules/system/upstart-events/control-alt-delete.nix @@ -3,10 +3,10 @@ ###### implementation { - jobs.ctrl_alt_delete = - { name = "ctrl-alt-delete"; + jobs.control_alt_delete = + { name = "control-alt-delete"; - startOn = "ctrlaltdel"; + startOn = "control-alt-delete"; task = true; diff --git a/modules/tasks/kbd.nix b/modules/tasks/kbd.nix index 9ba7f973cf3..8ed4b44f91b 100644 --- a/modules/tasks/kbd.nix +++ b/modules/tasks/kbd.nix @@ -59,9 +59,11 @@ in jobs.kbd = { description = "Keyboard / console initialisation"; - startOn = "udev"; + startOn = "started udev"; - preStart = '' + task = true; + + script = '' export LANG=${defaultLocale} export LOCALE_ARCHIVE=/var/run/current-system/sw/lib/locale/locale-archive export PATH=${pkgs.gzip}/bin:$PATH # Needed by setfont diff --git a/modules/tasks/lvm.nix b/modules/tasks/lvm.nix index 4da517deae1..92dfd52aefd 100644 --- a/modules/tasks/lvm.nix +++ b/modules/tasks/lvm.nix @@ -7,7 +7,7 @@ config = { jobs.lvm = - { startOn = " udev"; # !!! or on new-devices + { startOn = "started udev or new-devices"; script = '' diff --git a/modules/tasks/swraid.nix b/modules/tasks/swraid.nix index 6e29062f0a9..74a34ba5689 100644 --- a/modules/tasks/swraid.nix +++ b/modules/tasks/swraid.nix @@ -13,7 +13,7 @@ in { jobs.swraid = - { startOn = "udev"; # !!! or on "new-devices" + { startOn = "started udev or new-devices"; script = '' @@ -25,6 +25,8 @@ in # Scan /proc/partitions for RAID devices. ${mdadm}/sbin/mdadm --examine --brief --scan -c partitions > ${tempConf} + + if ! test -s ${tempConf}; then exit 0; fi # Activate each device found. ${mdadm}/sbin/mdadm --assemble -c ${tempConf} --scan From 82c3e2aa50713cf8669173360edeffb21228d701 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 6 Nov 2009 21:08:06 +0000 Subject: [PATCH 10/44] * Updated poweroff/reboot/maintenance mode for Upstart 0.6. Upstart no longer emits specific events for those. Instead it emits a "runlevel" event. The "runlevel" task starts the "shutdown" task to perform the desired action. * Upstart 0.6 no longer has a "shutdown" event, so "stop on shutdown" no longer works. Therefore the shutdown task explicitly stops all running Upstart jobs, before sending a TERM/KILL signal to all remaining processes. * Do a "chvt 1" at the start of the shutdown task to switch to the console. * Use /dev/console instead of /dev/tty1, since if somebody is logged in on tty1, bad things will happen. svn path=/nixos/branches/upstart-0.6/; revision=18224 --- modules/module-list.nix | 4 +- modules/system/boot/stage-2-init.sh | 7 +-- .../upstart-events/maintenance-shell.nix | 22 -------- modules/system/upstart-events/runlevel.nix | 25 +++++++++ .../upstart-events/{halt.nix => shutdown.nix} | 53 ++++++++++++------- 5 files changed, 64 insertions(+), 47 deletions(-) delete mode 100644 modules/system/upstart-events/maintenance-shell.nix create mode 100644 modules/system/upstart-events/runlevel.nix rename modules/system/upstart-events/{halt.nix => shutdown.nix} (75%) diff --git a/modules/module-list.nix b/modules/module-list.nix index eab8503f1ee..8c5cdcfd9dd 100644 --- a/modules/module-list.nix +++ b/modules/module-list.nix @@ -126,8 +126,8 @@ ./system/boot/stage-2.nix ./system/etc/etc.nix ./system/upstart-events/control-alt-delete.nix - ./system/upstart-events/halt.nix - ./system/upstart-events/maintenance-shell.nix + ./system/upstart-events/runlevel.nix + ./system/upstart-events/shutdown.nix ./system/upstart/upstart.nix ./tasks/filesystems.nix ./tasks/kbd.nix diff --git a/modules/system/boot/stage-2-init.sh b/modules/system/boot/stage-2-init.sh index a95ff1873ef..5e1a2104ca3 100644 --- a/modules/system/boot/stage-2-init.sh +++ b/modules/system/boot/stage-2-init.sh @@ -141,9 +141,6 @@ export MODULE_DIR=@kernel@/lib/modules/ # For debugging Upstart. #@shell@ --login < /dev/console > /dev/console 2>&1 & -# Start Upstart's init. We start it through the -# /var/run/current-system symlink indirection so that we can upgrade -# init in a running system by changing the symlink and sending init a -# HUP signal. +# Start Upstart's init. echo "starting Upstart..." -exec /var/run/current-system/upstart/sbin/init -v +PATH=/var/run/current-system/upstart/sbin exec init diff --git a/modules/system/upstart-events/maintenance-shell.nix b/modules/system/upstart-events/maintenance-shell.nix deleted file mode 100644 index a8eceb93158..00000000000 --- a/modules/system/upstart-events/maintenance-shell.nix +++ /dev/null @@ -1,22 +0,0 @@ -{ config, pkgs, ... }: - -###### implementation - -{ - jobs.maintenance_shell = - { name = "maintenance-shell"; - - startOn = [ "maintenance" "stalled" ]; - - task = true; - - script = - '' - exec < /dev/tty1 > /dev/tty1 2>&1 - echo \ - echo "<<< MAINTENANCE SHELL >>>" - echo "" - exec ${pkgs.bash}/bin/sh - ''; - }; -} diff --git a/modules/system/upstart-events/runlevel.nix b/modules/system/upstart-events/runlevel.nix new file mode 100644 index 00000000000..750d9a07f50 --- /dev/null +++ b/modules/system/upstart-events/runlevel.nix @@ -0,0 +1,25 @@ +{ config, pkgs, ... }: + +with pkgs.lib; + +{ + + jobs.runlevel = + { name = "runlevel"; + + startOn = "runlevel [0123456S]"; + + task = true; + + script = + '' + case "$RUNLEVEL" in + 0) initctl start shutdown MODE=poweroff;; + 1) initctl start shutdown MODE=maintenance;; + 6) initctl start shutdown MODE=reboot;; + *) echo "Unsupported runlevel: $RUNLEVEL";; + esac + ''; + }; + +} diff --git a/modules/system/upstart-events/halt.nix b/modules/system/upstart-events/shutdown.nix similarity index 75% rename from modules/system/upstart-events/halt.nix rename to modules/system/upstart-events/shutdown.nix index 36efc3fe2cf..a8519edbd84 100644 --- a/modules/system/upstart-events/halt.nix +++ b/modules/system/upstart-events/shutdown.nix @@ -2,29 +2,29 @@ with pkgs.lib; -###### implementation +{ -let - - inherit (pkgs) bash utillinux; - - jobFun = event: - { startOn = event; + jobs.shutdown = + { name = "shutdown"; task = true; + environment = { MODE = "poweroff"; }; + script = '' set +e # continue in case of errors + + ${pkgs.kbd}/bin/chvt 1 - exec < /dev/tty1 > /dev/tty1 2>&1 + exec < /dev/console > /dev/console 2>&1 echo "" echo "<<< SYSTEM SHUTDOWN >>>" echo "" - export PATH=${utillinux}/bin:${utillinux}/sbin:$PATH - + export PATH=${pkgs.utillinux}/bin:${pkgs.utillinux}/sbin:$PATH + # Set the hardware clock to the system time. echo "setting the hardware clock..." hwclock --systohc --utc @@ -32,6 +32,15 @@ let # Do an initial sync just in case. sync + + + # Stop all Upstart jobs. + initctl list | while read jobName rest; do + if test "$jobName" != shutdown; then + echo "stopping $jobName..." + stop "$jobName" + fi + done # Kill all remaining processes except init and this one. @@ -42,7 +51,20 @@ let echo "sending the KILL signal to all processes..." kill -KILL -1 - + + + # If maintenance mode is requested, start a root shell, and + # afterwards emit the "startup" event to bring everything + # back up. + if test "$MODE" = maintenance; then + echo "" + echo "<<< MAINTENANCE SHELL >>>" + echo "" + while ! ${pkgs.bash}/bin/bash --login; do true; done + initctl emit startup + exit 0 + fi + # Unmount helper functions. getMountPoints() { @@ -101,7 +123,7 @@ let # Either reboot or power-off the system. Note that the "halt" # event also does a power-off. - if test ${event} = reboot; then + if test "$MODE" = reboot; then echo "rebooting..." sleep 1 exec reboot -f @@ -113,9 +135,4 @@ let ''; }; -in - -{ - jobs = listToAttrs (map (n: nameValuePair "sys-${n}" (jobFun n)) - [ "reboot" "halt" "system-halt" "power-off" ] ); -} +} \ No newline at end of file From 4b2ff53ec6972f159a9b0343aa6d533e346e0518 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 6 Nov 2009 21:17:11 +0000 Subject: [PATCH 11/44] * Don't stop jobs that are already stopped. svn path=/nixos/branches/upstart-0.6/; revision=18226 --- modules/system/upstart-events/shutdown.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/system/upstart-events/shutdown.nix b/modules/system/upstart-events/shutdown.nix index a8519edbd84..02bce7979d0 100644 --- a/modules/system/upstart-events/shutdown.nix +++ b/modules/system/upstart-events/shutdown.nix @@ -35,8 +35,8 @@ with pkgs.lib; # Stop all Upstart jobs. - initctl list | while read jobName rest; do - if test "$jobName" != shutdown; then + initctl list | while IFS=", " read jobName status rest; do + if test "$jobName" != shutdown -a "$status" != "stop/waiting"; then echo "stopping $jobName..." stop "$jobName" fi From d545d08461edba3a70060afc2cce7431ff8381d6 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 6 Nov 2009 21:38:40 +0000 Subject: [PATCH 12/44] * In the network-interfaces task, emit an ip-up event if there is a statically configured interface (i.e. we're not running dhclient). Otherwise the ntpd job won't be triggered. * Use the "-n" flag of "initctl emit" to send the event asynchronously. svn path=/nixos/branches/upstart-0.6/; revision=18227 --- modules/services/hardware/udev.nix | 2 +- modules/services/networking/dhclient.nix | 4 ++-- modules/services/networking/ntpd.nix | 2 +- modules/system/upstart-events/shutdown.nix | 2 +- modules/tasks/lvm.nix | 2 +- modules/tasks/network-interfaces.nix | 5 +++++ modules/tasks/swraid.nix | 2 +- modules/virtualisation/qemu-vm.nix | 21 ++++++++++++++------- 8 files changed, 26 insertions(+), 14 deletions(-) diff --git a/modules/services/hardware/udev.nix b/modules/services/hardware/udev.nix index 2baa87e9547..eebf9672a84 100644 --- a/modules/services/hardware/udev.nix +++ b/modules/services/hardware/udev.nix @@ -191,7 +191,7 @@ in ${udev}/sbin/udevadm trigger ${udev}/sbin/udevadm settle # wait for udev to finish - initctl emit new-devices + initctl emit -n new-devices ''; }; diff --git a/modules/services/networking/dhclient.nix b/modules/services/networking/dhclient.nix index 80451ce2a5a..5a193e60728 100644 --- a/modules/services/networking/dhclient.nix +++ b/modules/services/networking/dhclient.nix @@ -28,11 +28,11 @@ let # it"), so we silently lose time synchronisation. ${config.system.build.upstart}/sbin/initctl stop ntpd - ${config.system.build.upstart}/sbin/initctl emit ip-up + ${config.system.build.upstart}/sbin/initctl emit -n ip-up fi if test "$reason" = EXPIRE -o "$reason" = RELEASE; then - ${config.system.build.upstart}/sbin/initctl emit ip-down + ${config.system.build.upstart}/sbin/initctl emit -n ip-down fi ''; diff --git a/modules/services/networking/ntpd.nix b/modules/services/networking/ntpd.nix index 159c8022442..895a8b8933b 100644 --- a/modules/services/networking/ntpd.nix +++ b/modules/services/networking/ntpd.nix @@ -82,7 +82,7 @@ in chown ${ntpUser} ${stateDir} # Needed to run ntpd as an unprivileged user. - ${modprobe}/sbin/modprobe capability || true + ${modprobe}/sbin/modprobe --quiet capability # !!! This can hang indefinitely if the network is down or # the servers are unreachable. This is particularly bad diff --git a/modules/system/upstart-events/shutdown.nix b/modules/system/upstart-events/shutdown.nix index 02bce7979d0..9c2359c0ff8 100644 --- a/modules/system/upstart-events/shutdown.nix +++ b/modules/system/upstart-events/shutdown.nix @@ -61,7 +61,7 @@ with pkgs.lib; echo "<<< MAINTENANCE SHELL >>>" echo "" while ! ${pkgs.bash}/bin/bash --login; do true; done - initctl emit startup + initctl emit -n startup exit 0 fi diff --git a/modules/tasks/lvm.nix b/modules/tasks/lvm.nix index 92dfd52aefd..4aebb33767e 100644 --- a/modules/tasks/lvm.nix +++ b/modules/tasks/lvm.nix @@ -23,7 +23,7 @@ # make them appear in /dev. ${pkgs.lvm2}/sbin/vgchange --available y - initctl emit new-devices + initctl emit -n new-devices ''; task = true; diff --git a/modules/tasks/network-interfaces.nix b/modules/tasks/network-interfaces.nix index 098828469a6..2868c251fca 100644 --- a/modules/tasks/network-interfaces.nix +++ b/modules/tasks/network-interfaces.nix @@ -178,6 +178,11 @@ in # Run any user-specified commands. ${pkgs.stdenv.shell} ${pkgs.writeText "local-net-cmds" cfg.localCommands} || true + + # Emit the ip-up event (e.g. to start ntpd). + ${optionalString (cfg.interfaces != []) '' + initctl emit -n ip-up + ''} ''; postStop = diff --git a/modules/tasks/swraid.nix b/modules/tasks/swraid.nix index 74a34ba5689..b9465822601 100644 --- a/modules/tasks/swraid.nix +++ b/modules/tasks/swraid.nix @@ -31,7 +31,7 @@ in # Activate each device found. ${mdadm}/sbin/mdadm --assemble -c ${tempConf} --scan - initctl emit new-devices + initctl emit -n new-devices ''; task = true; diff --git a/modules/virtualisation/qemu-vm.nix b/modules/virtualisation/qemu-vm.nix index 6886a0b23d1..68ba340e6c2 100644 --- a/modules/virtualisation/qemu-vm.nix +++ b/modules/virtualisation/qemu-vm.nix @@ -9,6 +9,8 @@ {config, pkgs, ...}: +with pkgs.lib; + let vmName = config.networking.hostName; @@ -16,7 +18,7 @@ let options = { virtualisation.diskImage = - pkgs.lib.mkOption { + mkOption { default = "./${vmName}.qcow2"; description = '' @@ -90,7 +92,7 @@ in # where the regular value for the `fileSystems' attribute should be # disregarded for the purpose of building a VM test image (since # those filesystems don't exist in the VM). - fileSystems = pkgs.lib.mkOverride 50 {} + fileSystems = mkOverride 50 {} [ { mountPoint = "/"; device = "/dev/vda"; } @@ -121,6 +123,11 @@ in networking.nameservers = [ "10.0.2.3" ]; + networking.interfaces = singleton + { name = "eth0"; + ipAddress = "10.0.2.15"; + }; + system.build.vm = pkgs.runCommand "nixos-vm" {} '' ensureDir $out/bin @@ -137,11 +144,11 @@ in # When building a regular system configuration, override whatever # video driver the host uses. - services.xserver.videoDriver = pkgs.lib.mkOverride 50 {} null; - services.xserver.videoDrivers = pkgs.lib.mkOverride 50 {} [ "cirrus" "vesa" ]; - services.xserver.defaultDepth = pkgs.lib.mkOverride 50 {} 0; - services.xserver.resolutions = pkgs.lib.mkOverride 50 {} []; + services.xserver.videoDriver = mkOverride 50 {} null; + services.xserver.videoDrivers = mkOverride 50 {} [ "cirrus" "vesa" ]; + services.xserver.defaultDepth = mkOverride 50 {} 0; + services.xserver.resolutions = mkOverride 50 {} []; # Wireless won't work in the VM. - networking.enableWLAN = pkgs.lib.mkOverride 50 {} false; + networking.enableWLAN = mkOverride 50 {} false; } From a60d83d3b496e21cda034ca1c33b0dc0ac4c2e98 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 6 Nov 2009 21:39:18 +0000 Subject: [PATCH 13/44] svn path=/nixos/branches/upstart-0.6/; revision=18228 --- modules/services/networking/ntpd.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/services/networking/ntpd.nix b/modules/services/networking/ntpd.nix index 895a8b8933b..eaf5b5532ac 100644 --- a/modules/services/networking/ntpd.nix +++ b/modules/services/networking/ntpd.nix @@ -82,7 +82,7 @@ in chown ${ntpUser} ${stateDir} # Needed to run ntpd as an unprivileged user. - ${modprobe}/sbin/modprobe --quiet capability + ${modprobe}/sbin/modprobe --quiet capability || true # !!! This can hang indefinitely if the network is down or # the servers are unreachable. This is particularly bad From 6b0842ff52e49b907f13d90d52bbb1936f76b3e1 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 6 Nov 2009 21:51:28 +0000 Subject: [PATCH 14/44] svn path=/nixos/branches/upstart-0.6/; revision=18229 --- modules/system/boot/stage-1-init.sh | 2 +- modules/system/boot/stage-2-init.sh | 2 +- modules/system/upstart-events/shutdown.nix | 11 +++++++---- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/modules/system/boot/stage-1-init.sh b/modules/system/boot/stage-1-init.sh index 58bf218428b..182fe7e98e1 100644 --- a/modules/system/boot/stage-1-init.sh +++ b/modules/system/boot/stage-1-init.sh @@ -39,7 +39,7 @@ trap 'fail' ERR # Print a greeting. echo -echo "<<< NixOS Stage 1 >>>" +echo "<<< NixOS Stage 1 >>>" echo diff --git a/modules/system/boot/stage-2-init.sh b/modules/system/boot/stage-2-init.sh index 5e1a2104ca3..30298c3127f 100644 --- a/modules/system/boot/stage-2-init.sh +++ b/modules/system/boot/stage-2-init.sh @@ -5,7 +5,7 @@ # Print a greeting. echo -echo "<<< NixOS Stage 2 >>>" +echo -e "\e[1;32m<<< NixOS Stage 2 >>>\e[0m" echo diff --git a/modules/system/upstart-events/shutdown.nix b/modules/system/upstart-events/shutdown.nix index 9c2359c0ff8..fa264979c6c 100644 --- a/modules/system/upstart-events/shutdown.nix +++ b/modules/system/upstart-events/shutdown.nix @@ -19,7 +19,11 @@ with pkgs.lib; exec < /dev/console > /dev/console 2>&1 echo "" - echo "<<< SYSTEM SHUTDOWN >>>" + if test "$MODE" = maintenance; then + echo "<<< Entering maintenance mode >>>" + else + echo "<<< System shutdown >>>" + fi echo "" export PATH=${pkgs.utillinux}/bin:${pkgs.utillinux}/sbin:$PATH @@ -58,7 +62,7 @@ with pkgs.lib; # back up. if test "$MODE" = maintenance; then echo "" - echo "<<< MAINTENANCE SHELL >>>" + echo "<<< Maintenance shell >>>" echo "" while ! ${pkgs.bash}/bin/bash --login; do true; done initctl emit -n startup @@ -121,8 +125,7 @@ with pkgs.lib; sync - # Either reboot or power-off the system. Note that the "halt" - # event also does a power-off. + # Either reboot or power-off the system. if test "$MODE" = reboot; then echo "rebooting..." sleep 1 From 83a9bf9a6aaac9fb967c34706e6c62150562a149 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 6 Nov 2009 22:19:17 +0000 Subject: [PATCH 15/44] * Change all the startOn / stopOn attributes to the Upstart 0.6 syntax (e.g., startOn = "started foo" instead of startOn = "foo"). svn path=/nixos/branches/upstart-0.6/; revision=18230 --- modules/services/audio/alsa.nix | 2 +- modules/services/audio/pulseaudio.nix | 1 - modules/services/databases/mysql.nix | 3 +-- modules/services/databases/postgresql.nix | 3 +-- modules/services/hardware/acpid.nix | 3 +-- modules/services/hardware/hal.nix | 1 - modules/services/hardware/udev.nix | 1 - modules/services/logging/klogd.nix | 1 - modules/services/mail/dovecot.nix | 2 +- modules/services/mail/postfix.nix | 2 +- modules/services/misc/autofs.nix | 4 ++-- modules/services/misc/disnix.nix | 3 +-- modules/services/misc/gpsd.nix | 4 ++-- modules/services/misc/nixos-manual.nix | 3 +-- modules/services/misc/rogue.nix | 3 +-- modules/services/misc/synergy.nix | 8 ++++---- modules/services/monitoring/nagios/default.nix | 4 ++-- modules/services/monitoring/zabbix-agent.nix | 4 ++-- modules/services/monitoring/zabbix-server.nix | 4 ++-- .../services/network-filesystems/nfs-kernel.nix | 16 ++++++++-------- modules/services/network-filesystems/samba.nix | 8 ++++---- modules/services/networking/avahi-daemon.nix | 4 ++-- modules/services/networking/bitlbee.nix | 4 ++-- modules/services/networking/ddclient.nix | 1 - modules/services/networking/dhclient.nix | 4 ++-- modules/services/networking/dhcpd.nix | 4 ++-- modules/services/networking/ejabberd.nix | 4 ++-- modules/services/networking/firewall.nix | 2 +- modules/services/networking/gnunet.nix | 4 ++-- modules/services/networking/gw6c.nix | 4 ++-- modules/services/networking/ifplugd.nix | 4 ++-- modules/services/networking/ircd-hybrid.nix | 4 ++-- modules/services/networking/openfire.nix | 3 +-- modules/services/networking/portmap.nix | 4 ++-- modules/services/networking/privoxy.nix | 1 - modules/services/networking/ssh/lshd.nix | 4 ++-- modules/services/networking/vsftpd.nix | 4 ++-- modules/services/networking/wpa_supplicant.nix | 4 ++-- modules/services/networking/xinetd.nix | 4 ++-- modules/services/printing/cupsd.nix | 4 ++-- modules/services/scheduling/atd.nix | 9 ++++----- modules/services/scheduling/cron.nix | 1 - modules/services/scheduling/fcron.nix | 1 - modules/services/system/nscd.nix | 1 - modules/services/system/uptimed.nix | 1 - modules/services/ttys/gpm.nix | 3 +-- .../web-servers/apache-httpd/default.nix | 3 +-- modules/services/web-servers/tomcat.nix | 4 ++-- modules/services/x11/xfs.nix | 3 +-- modules/services/x11/xserver.nix | 2 +- 50 files changed, 77 insertions(+), 98 deletions(-) diff --git a/modules/services/audio/alsa.nix b/modules/services/audio/alsa.nix index 5f463f8d8cd..78d6a004418 100644 --- a/modules/services/audio/alsa.nix +++ b/modules/services/audio/alsa.nix @@ -46,7 +46,7 @@ in }; jobs.alsa = - { startOn = "udev"; + { startOn = "started udev"; preStart = '' diff --git a/modules/services/audio/pulseaudio.nix b/modules/services/audio/pulseaudio.nix index 5a8ffbd30a1..f48cdd49c83 100644 --- a/modules/services/audio/pulseaudio.nix +++ b/modules/services/audio/pulseaudio.nix @@ -66,7 +66,6 @@ in { description = "PulseAudio system-wide server"; startOn = "startup"; - stopOn = "shutdown"; preStart = '' diff --git a/modules/services/databases/mysql.nix b/modules/services/databases/mysql.nix index 5d16ebe0af4..81a2c5ea007 100644 --- a/modules/services/databases/mysql.nix +++ b/modules/services/databases/mysql.nix @@ -75,8 +75,7 @@ in jobs.mysql = { description = "MySQL server"; - startOn = "filesystems"; - stopOn = "shutdown"; + startOn = "started filesystems"; preStart = '' diff --git a/modules/services/databases/postgresql.nix b/modules/services/databases/postgresql.nix index b463e39c725..92e6083d948 100644 --- a/modules/services/databases/postgresql.nix +++ b/modules/services/databases/postgresql.nix @@ -116,8 +116,7 @@ in jobs.postgresql = { description = "PostgreSQL server"; - startOn = "${startDependency}/started"; - stopOn = "shutdown"; + startOn = "started ${startDependency}"; preStart = '' diff --git a/modules/services/hardware/acpid.nix b/modules/services/hardware/acpid.nix index ebb2f2a6a05..88bfc4e7258 100644 --- a/modules/services/hardware/acpid.nix +++ b/modules/services/hardware/acpid.nix @@ -79,8 +79,7 @@ in jobs.acpid = { description = "ACPI daemon"; - startOn = "udev"; - stopOn = "shutdown"; + startOn = "started udev"; exec = "${pkgs.acpid}/sbin/acpid --foreground --confdir ${acpiConfDir}"; }; diff --git a/modules/services/hardware/hal.nix b/modules/services/hardware/hal.nix index be51968317f..9ee16cedf87 100644 --- a/modules/services/hardware/hal.nix +++ b/modules/services/hardware/hal.nix @@ -70,7 +70,6 @@ in # !!! TODO: make sure that HAL starts after acpid, # otherwise hald-addon-acpi will grab /proc/acpi/event. startOn = if config.powerManagement.enable then "started acpid" else "started dbus"; - stopOn = "shutdown"; environment = { # !!! HACK? These environment variables manipulated inside diff --git a/modules/services/hardware/udev.nix b/modules/services/hardware/udev.nix index eebf9672a84..798b9a4e381 100644 --- a/modules/services/hardware/udev.nix +++ b/modules/services/hardware/udev.nix @@ -159,7 +159,6 @@ in jobs.udev = { startOn = "startup"; - stopOn = "shutdown"; environment = { UDEV_CONFIG_FILE = conf; }; diff --git a/modules/services/logging/klogd.nix b/modules/services/logging/klogd.nix index 98d9d5eeabf..2b8a6a64d99 100644 --- a/modules/services/logging/klogd.nix +++ b/modules/services/logging/klogd.nix @@ -8,7 +8,6 @@ { description = "Kernel log daemon"; startOn = "started syslogd"; - stopOn = "shutdown"; exec = "${pkgs.sysklogd}/sbin/klogd -c 1 -2 -n " + diff --git a/modules/services/mail/dovecot.nix b/modules/services/mail/dovecot.nix index 8ddbe5bfd7b..a41bd9eaad7 100644 --- a/modules/services/mail/dovecot.nix +++ b/modules/services/mail/dovecot.nix @@ -116,7 +116,7 @@ in jobs.dovecot = { description = "Dovecot IMAP/POP3 server"; - startOn = "${startingDependency}/started"; + startOn = "started ${startingDependency}"; preStart = '' diff --git a/modules/services/mail/postfix.nix b/modules/services/mail/postfix.nix index b5ed10d957b..ae85dd43f23 100644 --- a/modules/services/mail/postfix.nix +++ b/modules/services/mail/postfix.nix @@ -283,7 +283,7 @@ in # accurate way is unlikely to be better. { description = "Postfix mail server"; - startOn = "${startingDependency}/started"; + startOn = "started ${startingDependency}"; script = '' diff --git a/modules/services/misc/autofs.nix b/modules/services/misc/autofs.nix index b9936d0249d..079c684d086 100644 --- a/modules/services/misc/autofs.nix +++ b/modules/services/misc/autofs.nix @@ -75,8 +75,8 @@ in jobs.autofs = { description = "Filesystem automounter"; - startOn = "network-interfaces/started"; - stopOn = "network-interfaces/stop"; + startOn = "started network-interfaces"; + stopOn = "stopping network-interfaces"; environment = { PATH = "${pkgs.nfsUtils}/sbin:${config.system.sbin.modprobe}/sbin"; diff --git a/modules/services/misc/disnix.nix b/modules/services/misc/disnix.nix index 225762813ee..886c86e7fab 100644 --- a/modules/services/misc/disnix.nix +++ b/modules/services/misc/disnix.nix @@ -39,8 +39,7 @@ in jobs.disnix = { description = "Disnix server"; - startOn = "dbus"; - stopOn = "shutdown"; + startOn = "started dbus"; script = '' diff --git a/modules/services/misc/gpsd.nix b/modules/services/misc/gpsd.nix index 98feef68231..83cc6bae1d5 100644 --- a/modules/services/misc/gpsd.nix +++ b/modules/services/misc/gpsd.nix @@ -88,8 +88,8 @@ in jobs.gpsd = { description = "GPSD daemon"; - startOn = "network-interfaces/started"; - stopOn = "network-interfaces/stop"; + startOn = "started network-interfaces"; + stopOn = "stopping network-interfaces"; exec = '' diff --git a/modules/services/misc/nixos-manual.nix b/modules/services/misc/nixos-manual.nix index 04fc34c94fa..ed2972e5b5b 100644 --- a/modules/services/misc/nixos-manual.nix +++ b/modules/services/misc/nixos-manual.nix @@ -78,8 +78,7 @@ in description = "NixOS manual"; - startOn = "udev"; - stopOn = "shutdown"; + startOn = "started udev"; exec = '' diff --git a/modules/services/misc/rogue.nix b/modules/services/misc/rogue.nix index 43f43ae5bad..8760ce12510 100644 --- a/modules/services/misc/rogue.nix +++ b/modules/services/misc/rogue.nix @@ -43,8 +43,7 @@ in jobs.rogue = { description = "Rogue dungeon crawling game"; - startOn = "udev"; - stopOn = "shutdown"; + startOn = "started udev"; extraConfig = "chdir /root"; diff --git a/modules/services/misc/synergy.nix b/modules/services/misc/synergy.nix index 72eaf640ea8..96616fa7a73 100644 --- a/modules/services/misc/synergy.nix +++ b/modules/services/misc/synergy.nix @@ -76,8 +76,8 @@ in description = "Synergy client"; - startOn = "network-interfaces/started"; - stopOn = "network-interfaces/stopped"; + startOn = "started network-interfaces"; + stopOn = "stopping network-interfaces"; exec = "${pkgs.synergy}/bin/synergyc ${if cfgS.screenName == "" then "" else "-n ${cfgS.screenName}" }"; }; @@ -89,8 +89,8 @@ in description = "Synergy server"; - startOn = "network-interfaces/started"; - stopOn = "network-interfaces/stopped"; + startOn = "started network-interfaces"; + stopOn = "stopping network-interfaces"; exec = '' diff --git a/modules/services/monitoring/nagios/default.nix b/modules/services/monitoring/nagios/default.nix index 78e6a276fc8..6b1b3163d26 100644 --- a/modules/services/monitoring/nagios/default.nix +++ b/modules/services/monitoring/nagios/default.nix @@ -166,8 +166,8 @@ in description = "Nagios monitoring daemon"; - startOn = "network-interfaces/started"; - stopOn = "network-interfaces/stop"; + startOn = "started network-interfaces"; + stopOn = "stopping network-interfaces"; preStart = '' diff --git a/modules/services/monitoring/zabbix-agent.nix b/modules/services/monitoring/zabbix-agent.nix index 9fbcad78c68..3691405e1e2 100644 --- a/modules/services/monitoring/zabbix-agent.nix +++ b/modules/services/monitoring/zabbix-agent.nix @@ -69,8 +69,8 @@ in description = "Zabbix agent daemon"; - startOn = "network-interfaces/started"; - stopOn = "network-interfaces/stop"; + startOn = "started network-interfaces"; + stopOn = "stopping network-interfaces"; preStart = '' diff --git a/modules/services/monitoring/zabbix-server.nix b/modules/services/monitoring/zabbix-server.nix index 9b006ed8ec2..d38e955ed4f 100644 --- a/modules/services/monitoring/zabbix-server.nix +++ b/modules/services/monitoring/zabbix-server.nix @@ -56,8 +56,8 @@ in description = "Zabbix server daemon"; - startOn = "postgresql"; - stopOn = "shutdown"; + startOn = "started postgresql"; + stopOn = "stopping postgresql"; preStart = '' diff --git a/modules/services/network-filesystems/nfs-kernel.nix b/modules/services/network-filesystems/nfs-kernel.nix index 1f67d584b89..2f6904457b2 100644 --- a/modules/services/network-filesystems/nfs-kernel.nix +++ b/modules/services/network-filesystems/nfs-kernel.nix @@ -81,8 +81,8 @@ in description = "Kernel NFS server"; - startOn = "network-interfaces/started"; - stopOn = "network-interfaces/stop"; + startOn = "started network-interfaces"; + stopOn = "stopping network-interfaces"; preStart = '' @@ -111,8 +111,8 @@ in description = "Kernel NFS server"; - startOn = "nfs-kernel-exports/started"; - stopOn = "nfs-kernel-exports/stop"; + startOn = "started nfs-kernel-exports"; + stopOn = "stopping nfs-kernel-exports"; exec = "${pkgs.nfsUtils}/sbin/rpc.nfsd ${if cfg.hostName != null then "-H ${cfg.hostName}" else ""} ${builtins.toString cfg.nproc}"; }; @@ -122,8 +122,8 @@ in description = "Kernel NFS server - mount daemon"; - startOn = "nfs-kernel-nfsd/started"; - stopOn = "nfs-kernel-exports/stop"; + startOn = "started nfs-kernel-nfsd"; + stopOn = "stopping nfs-kernel-exports"; exec = "${pkgs.nfsUtils}/sbin/rpc.mountd -F -f ${exports}"; }; @@ -133,8 +133,8 @@ in description = "Kernel NFS server - Network Status Monitor"; - startOn = "nfs-kernel-nfsd/started"; - stopOn = "nfs-kernel-exports/stop"; + startOn = "started nfs-kernel-nfsd"; + stopOn = "stopping nfs-kernel-exports"; preStart = '' diff --git a/modules/services/network-filesystems/samba.nix b/modules/services/network-filesystems/samba.nix index 98d58e9f7f4..6e9cd391597 100644 --- a/modules/services/network-filesystems/samba.nix +++ b/modules/services/network-filesystems/samba.nix @@ -56,8 +56,8 @@ let { name = "samba-${appName}"; description = "Samba Service daemon ${appName}"; - startOn = "samba/started"; - stopOn = "samba-control/stop"; + startOn = "started samba"; + stopOn = "stopping samba-control"; exec = "${samba}/sbin/${appName} ${args}"; }; @@ -168,8 +168,8 @@ in { name = "samba"; description = "Samba server"; - startOn = "network-interfaces/started"; - stopOn = "network-interfaces/stop"; + startOn = "started network-interfaces"; + stopOn = "stopping network-interfaces"; preStart = setupScript; }; diff --git a/modules/services/networking/avahi-daemon.nix b/modules/services/networking/avahi-daemon.nix index f3033e8a408..82693c7399e 100644 --- a/modules/services/networking/avahi-daemon.nix +++ b/modules/services/networking/avahi-daemon.nix @@ -118,8 +118,8 @@ in jobs.avahi_daemon = { name = "avahi-daemon"; - startOn = "network-interfaces/started"; - stopOn = "network-interfaces/stop"; + startOn = "started network-interfaces"; + stopOn = "stopping network-interfaces"; script = '' diff --git a/modules/services/networking/bitlbee.nix b/modules/services/networking/bitlbee.nix index 2aa51603d05..f61227e0984 100644 --- a/modules/services/networking/bitlbee.nix +++ b/modules/services/networking/bitlbee.nix @@ -67,8 +67,8 @@ in jobs.bitlbee = { description = "BitlBee IRC to other chat networks gateway"; - startOn = "network-interfaces/started"; - stopOn = "network-interfaces/stop"; + startOn = "started network-interfaces"; + stopOn = "stopping network-interfaces"; preStart = '' diff --git a/modules/services/networking/ddclient.nix b/modules/services/networking/ddclient.nix index b5bffa83cd7..575386c8579 100644 --- a/modules/services/networking/ddclient.nix +++ b/modules/services/networking/ddclient.nix @@ -114,7 +114,6 @@ in { name = "ddclient"; startOn = "startup"; - stopOn = "shutdown"; preStart = '' diff --git a/modules/services/networking/dhclient.nix b/modules/services/networking/dhclient.nix index 5a193e60728..600b7196aa4 100644 --- a/modules/services/networking/dhclient.nix +++ b/modules/services/networking/dhclient.nix @@ -62,8 +62,8 @@ in config = mkIf config.networking.useDHCP { jobs.dhclient = - { startOn = "network-interfaces/started"; - stopOn = "network-interfaces/stop"; + { startOn = "started network-interfaces"; + stopOn = "stopping network-interfaces"; preStart = '' diff --git a/modules/services/networking/dhcpd.nix b/modules/services/networking/dhcpd.nix index 6daa0c073b8..43e0843cb97 100644 --- a/modules/services/networking/dhcpd.nix +++ b/modules/services/networking/dhcpd.nix @@ -111,8 +111,8 @@ in jobs.dhcpd = { description = "DHCP server"; - startOn = "network-interfaces/started"; - stopOn = "network-interfaces/stop"; + startOn = "started network-interfaces"; + stopOn = "stopping network-interfaces"; script = '' diff --git a/modules/services/networking/ejabberd.nix b/modules/services/networking/ejabberd.nix index 4dce0926364..ae8cf47b2fa 100644 --- a/modules/services/networking/ejabberd.nix +++ b/modules/services/networking/ejabberd.nix @@ -54,8 +54,8 @@ in jobs.ejabberd = { description = "EJabberd server"; - startOn = "network-interface/started"; - stopOn = "network-interfaces/stop"; + startOn = "started network-interface"; + stopOn = "stopping network-interfaces"; preStart = '' diff --git a/modules/services/networking/firewall.nix b/modules/services/networking/firewall.nix index 0b8a8d93061..28c43ef3370 100644 --- a/modules/services/networking/firewall.nix +++ b/modules/services/networking/firewall.nix @@ -79,7 +79,7 @@ in environment.systemPackages = [pkgs.iptables]; jobs.firewall = - { startOn = "network-interfaces/started"; + { startOn = "started network-interfaces"; preStart = '' diff --git a/modules/services/networking/gnunet.nix b/modules/services/networking/gnunet.nix index e35dfbcd1a8..53223f1df22 100644 --- a/modules/services/networking/gnunet.nix +++ b/modules/services/networking/gnunet.nix @@ -194,8 +194,8 @@ in jobs.gnunetd = { description = "The GNUnet Daemon"; - startOn = "network-interfaces/started"; - stopOn = "network-interfaces/stop"; + startOn = "started network-interfaces"; + stopOn = "stopping network-interfaces"; preStart = '' diff --git a/modules/services/networking/gw6c.nix b/modules/services/networking/gw6c.nix index d723766bb2c..eaf84c5f70f 100644 --- a/modules/services/networking/gw6c.nix +++ b/modules/services/networking/gw6c.nix @@ -134,8 +134,8 @@ in jobs.gw6c = { description = "Gateway6 client"; - startOn = if cfg.autorun then "network-interfaces/started" else ""; - stopOn = "network-interfaces/stop"; + startOn = if cfg.autorun then "started network-interfaces" else ""; + stopOn = "stopping network-interfaces"; exec = "${gw6cService}/bin/control start"; }; diff --git a/modules/services/networking/ifplugd.nix b/modules/services/networking/ifplugd.nix index 85eda29f5a2..f1510b1aa4b 100644 --- a/modules/services/networking/ifplugd.nix +++ b/modules/services/networking/ifplugd.nix @@ -58,8 +58,8 @@ in jobs.ifplugd = { description = "Network interface connectivity monitor"; - startOn = "network-interfaces/started"; - stopOn = "network-interfaces/stop"; + startOn = "started network-interfaces"; + stopOn = "stopping network-interfaces"; exec = '' diff --git a/modules/services/networking/ircd-hybrid.nix b/modules/services/networking/ircd-hybrid.nix index aea5fbd6550..34c3b13d82d 100644 --- a/modules/services/networking/ircd-hybrid.nix +++ b/modules/services/networking/ircd-hybrid.nix @@ -122,8 +122,8 @@ in description = "IRCD Hybrid server"; - startOn = "${startingDependency}/started"; - stopOn = "${startingDependency}/stop"; + startOn = "started ${startingDependency}"; + stopOn = "stopping ${startingDependency}"; exec = "${ircdService}/bin/control start"; }; diff --git a/modules/services/networking/openfire.nix b/modules/services/networking/openfire.nix index 67e2558b22a..a46b4fa4294 100644 --- a/modules/services/networking/openfire.nix +++ b/modules/services/networking/openfire.nix @@ -52,8 +52,7 @@ in jobs.openfire = { description = "OpenFire XMPP server"; - startOn = "${startDependency}/started"; - stopOn = "shutdown"; + startOn = "started ${startDependency}"; script = '' diff --git a/modules/services/networking/portmap.nix b/modules/services/networking/portmap.nix index 0fcbf63d799..5a51836efad 100644 --- a/modules/services/networking/portmap.nix +++ b/modules/services/networking/portmap.nix @@ -66,8 +66,8 @@ in jobs.portmap = { description = "ONC RPC portmap"; - startOn = "network-interfaces/started"; - stopOn = "network-interfaces/stop"; + startOn = "started network-interfaces"; + stopOn = "stopping network-interfaces"; exec = '' diff --git a/modules/services/networking/privoxy.nix b/modules/services/networking/privoxy.nix index f2350f4c462..eee020a1f0e 100644 --- a/modules/services/networking/privoxy.nix +++ b/modules/services/networking/privoxy.nix @@ -81,7 +81,6 @@ in { name = "privoxy"; startOn = "startup"; - stopOn = "shutdown"; preStart = '' diff --git a/modules/services/networking/ssh/lshd.nix b/modules/services/networking/ssh/lshd.nix index c3a14ba17e8..f5d52c82101 100644 --- a/modules/services/networking/ssh/lshd.nix +++ b/modules/services/networking/ssh/lshd.nix @@ -119,8 +119,8 @@ in jobs.lshd = { description = "GNU lshd SSH2 daemon"; - startOn = "network-interfaces/started"; - stopOn = "network-interfaces/stop"; + startOn = "started network-interfaces"; + stopOn = "stopping network-interfaces"; environment = { LD_LIBRARY_PATH = config.system.nssModules.path; }; diff --git a/modules/services/networking/vsftpd.nix b/modules/services/networking/vsftpd.nix index b98a9aa3f7d..4c6b6f411cd 100644 --- a/modules/services/networking/vsftpd.nix +++ b/modules/services/networking/vsftpd.nix @@ -102,8 +102,8 @@ in jobs.vsftpd = { description = "vsftpd server"; - startOn = "network-interfaces/started"; - stopOn = "network-interfaces/stop"; + startOn = "started network-interfaces"; + stopOn = "stopping network-interfaces"; preStart = '' diff --git a/modules/services/networking/wpa_supplicant.nix b/modules/services/networking/wpa_supplicant.nix index 37b5923421f..93450f8eac1 100644 --- a/modules/services/networking/wpa_supplicant.nix +++ b/modules/services/networking/wpa_supplicant.nix @@ -38,8 +38,8 @@ in environment.systemPackages = [pkgs.wpa_supplicant]; jobs.wpa_supplicant = - { startOn = "network-interfaces/started"; - stopOn = "network-interfaces/stop"; + { startOn = "started network-interfaces"; + stopOn = "stopping network-interfaces"; preStart = '' diff --git a/modules/services/networking/xinetd.nix b/modules/services/networking/xinetd.nix index be9e5712e7d..4729ba9d2e4 100644 --- a/modules/services/networking/xinetd.nix +++ b/modules/services/networking/xinetd.nix @@ -122,8 +122,8 @@ in jobs.xinetd = { description = "xinetd server"; - startOn = "network-interfaces/started"; - stopOn = "network-interfaces/stop"; + startOn = "started network-interfaces"; + stopOn = "stopping network-interfaces"; exec = "${xinetd}/sbin/xinetd -syslog daemon -dontfork -stayalive -f ${configFile}"; }; diff --git a/modules/services/printing/cupsd.nix b/modules/services/printing/cupsd.nix index 149055d74c5..546eb7be396 100644 --- a/modules/services/printing/cupsd.nix +++ b/modules/services/printing/cupsd.nix @@ -160,8 +160,8 @@ in jobs.cupsd = { description = "CUPS printing daemon"; - startOn = "network-interfaces/started"; - stopOn = "network-interfaces/stop"; + startOn = "started network-interfaces"; + stopOn = "stopping network-interfaces"; preStart = '' diff --git a/modules/services/scheduling/atd.nix b/modules/services/scheduling/atd.nix index fb579dbf0b2..19fda42928a 100644 --- a/modules/services/scheduling/atd.nix +++ b/modules/services/scheduling/atd.nix @@ -67,7 +67,6 @@ in { description = "at daemon (atd)"; startOn = "startup"; - stopOn = "shutdown"; preStart = '' @@ -97,12 +96,12 @@ in chown atd:atd "$jobdir"/.SEQ && \ chmod 600 "$jobdir"/.SEQ fi - - # `atd' doesn't have a no-fork flag, so start it here. !!! - # Fix this once we have Upstart 0.6. - ${at}/sbin/atd ''; + daemonType = "fork"; + + exec = "${at}/sbin/atd"; + postStop = '' test -e /var/run/atd.pid && kill $(cat /var/run/atd.pid) diff --git a/modules/services/scheduling/cron.nix b/modules/services/scheduling/cron.nix index 930162f1c73..bae03f40138 100644 --- a/modules/services/scheduling/cron.nix +++ b/modules/services/scheduling/cron.nix @@ -70,7 +70,6 @@ in { description = "Cron daemon"; startOn = "startup"; - stopOn = "shutdown"; # Needed to interpret times in the local timezone. environment = { TZ = config.time.timeZone; }; diff --git a/modules/services/scheduling/fcron.nix b/modules/services/scheduling/fcron.nix index b38a70b932a..fbd1288a55f 100644 --- a/modules/services/scheduling/fcron.nix +++ b/modules/services/scheduling/fcron.nix @@ -101,7 +101,6 @@ in { description = "fcron daemon"; startOn = "startup"; - stopOn = "shutdown"; environment = { PATH = "/var/run/current-system/sw/bin"; diff --git a/modules/services/system/nscd.nix b/modules/services/system/nscd.nix index a14215c70d1..1e3a4ff75c9 100644 --- a/modules/services/system/nscd.nix +++ b/modules/services/system/nscd.nix @@ -21,7 +21,6 @@ in { description = "Name Service Cache Daemon"; startOn = "startup"; - stopOn = "shutdown"; environment = { LD_LIBRARY_PATH = nssModulesPath; }; diff --git a/modules/services/system/uptimed.nix b/modules/services/system/uptimed.nix index 3749358ac20..fd4c1652bd3 100644 --- a/modules/services/system/uptimed.nix +++ b/modules/services/system/uptimed.nix @@ -53,7 +53,6 @@ in { description = "Uptimed daemon"; startOn = "startup"; - stopOn = "shutdown"; preStart = '' diff --git a/modules/services/ttys/gpm.nix b/modules/services/ttys/gpm.nix index 026ed4a299f..256fe8d2380 100644 --- a/modules/services/ttys/gpm.nix +++ b/modules/services/ttys/gpm.nix @@ -41,8 +41,7 @@ in jobs.gpm = { description = "General purpose mouse"; - startOn = "udev"; - stopOn = "shutdown"; + startOn = "started udev"; exec = "${pkgs.gpm}/sbin/gpm -m /dev/input/mice -t ${cfg.protocol} -D &>/dev/null"; }; diff --git a/modules/services/web-servers/apache-httpd/default.nix b/modules/services/web-servers/apache-httpd/default.nix index 7e3067510d3..c489393f889 100644 --- a/modules/services/web-servers/apache-httpd/default.nix +++ b/modules/services/web-servers/apache-httpd/default.nix @@ -450,8 +450,7 @@ in description = "Apache HTTPD"; - startOn = "${startingDependency}/started"; - stopOn = "shutdown"; + startOn = "started ${startingDependency}"; environment = { # !!! This should be added in test-instrumentation.nix. It diff --git a/modules/services/web-servers/tomcat.nix b/modules/services/web-servers/tomcat.nix index 331653ba929..4eb5b3fc5b5 100644 --- a/modules/services/web-servers/tomcat.nix +++ b/modules/services/web-servers/tomcat.nix @@ -104,8 +104,8 @@ in jobs.tomcat = { description = "Apache Tomcat server"; - startOn = "network-interface/started"; - stopOn = "network-interfaces/stop"; + startOn = "started network-interface"; + stopOn = "stopping network-interfaces"; preStart = '' diff --git a/modules/services/x11/xfs.nix b/modules/services/x11/xfs.nix index 860d5823be3..f2b7108debc 100644 --- a/modules/services/x11/xfs.nix +++ b/modules/services/x11/xfs.nix @@ -43,8 +43,7 @@ in jobs.xfs = { description = "X Font Server"; - startOn = "${startingDependency}/started"; - stopOn = "shutdown"; + startOn = "started ${startingDependency}"; exec = "${pkgs.xorg.xfs}/bin/xfs -config ${configFile}"; }; diff --git a/modules/services/x11/xserver.nix b/modules/services/x11/xserver.nix index 2d432e831f7..ff288f3b1b0 100644 --- a/modules/services/x11/xserver.nix +++ b/modules/services/x11/xserver.nix @@ -360,7 +360,7 @@ in optional (elem "virtualbox" driverNames) kernelPackages.virtualboxGuestAdditions; jobs.xserver = - { startOn = if cfg.autorun then "hal" else "never"; + { startOn = if cfg.autorun then "started hal" else ""; environment = { FONTCONFIG_FILE = "/etc/fonts/fonts.conf"; # !!! cleanup From d7342c78d49ac80c6a1464bbc9c1cdf188eeebe9 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 6 Nov 2009 22:45:19 +0000 Subject: [PATCH 16/44] * Support pre-stop scripts. These are needed to cleanly shutdown daemons such as Apache or PostgreSQL. svn path=/nixos/branches/upstart-0.6/; revision=18234 --- .../web-servers/apache-httpd/default.nix | 9 ++++++++- modules/system/upstart/upstart.nix | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/modules/services/web-servers/apache-httpd/default.nix b/modules/services/web-servers/apache-httpd/default.nix index c489393f889..107340c4586 100644 --- a/modules/services/web-servers/apache-httpd/default.nix +++ b/modules/services/web-servers/apache-httpd/default.nix @@ -480,7 +480,14 @@ in done ''; - exec = "${httpd}/bin/httpd -f ${httpdConf} -DNO_DETACH"; + daemonType = "fork"; + + exec = "${httpd}/bin/httpd -f ${httpdConf}"; + + preStop = + '' + ${httpd}/bin/httpd -f ${httpdConf} -k graceful-stop + ''; }; }; diff --git a/modules/system/upstart/upstart.nix b/modules/system/upstart/upstart.nix index 3d2af1b0382..f7c9eaaa6ef 100644 --- a/modules/system/upstart/upstart.nix +++ b/modules/system/upstart/upstart.nix @@ -72,6 +72,12 @@ let ${optionalString job.task "task"} ${optionalString job.respawn "respawn"} + ${optionalString (job.preStop != "") '' + pre-stop script + ${job.preStop} + end script + ''} + ${optionalString (job.postStop != "") '' post-stop script ${job.postStop} @@ -165,6 +171,16 @@ let ''; }; + preStop = mkOption { + type = types.string; + default = ""; + description = '' + Shell commands executed before the job is stopped + (i.e. before Upstart kills the job's main process). This can + be used to cleanly shut down a daemon. + ''; + }; + postStop = mkOption { type = types.string; default = ""; From 18f2d75275ed6167b250c0513e21e14432e5cfe0 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 6 Nov 2009 22:56:47 +0000 Subject: [PATCH 17/44] * Set a timezone in the PostgreSQL job. Otherwise database initialisation will be incredibly slow (minutes instead of seconds). An strace shows that it's continuously looking up timezone information if TZ is empty. svn path=/nixos/branches/upstart-0.6/; revision=18235 --- modules/services/databases/postgresql.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/services/databases/postgresql.nix b/modules/services/databases/postgresql.nix index 92e6083d948..9f1ca6e2ed1 100644 --- a/modules/services/databases/postgresql.nix +++ b/modules/services/databases/postgresql.nix @@ -118,6 +118,8 @@ in startOn = "started ${startDependency}"; + environment = { TZ = config.time.timeZone; }; + preStart = '' if ! test -e ${cfg.dataDir}; then From 06fcb121fb06031486a9335730e88aca78b5c30b Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 6 Nov 2009 23:37:31 +0000 Subject: [PATCH 18/44] * Shut down PostgreSQL cleanly. svn path=/nixos/branches/upstart-0.6/; revision=18236 --- modules/services/databases/postgresql.nix | 25 ++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/modules/services/databases/postgresql.nix b/modules/services/databases/postgresql.nix index 9f1ca6e2ed1..cd1f9dc8094 100644 --- a/modules/services/databases/postgresql.nix +++ b/modules/services/databases/postgresql.nix @@ -118,19 +118,38 @@ in startOn = "started ${startDependency}"; - environment = { TZ = config.time.timeZone; }; + environment = + { TZ = config.time.timeZone; + PGDATA = cfg.dataDir; + }; preStart = '' + # Initialise the database. if ! test -e ${cfg.dataDir}; then mkdir -m 0700 -p ${cfg.dataDir} chown -R postgres ${cfg.dataDir} - ${run} -c '${postgresql}/bin/initdb -D ${cfg.dataDir} -U root' + ${run} -c '${postgresql}/bin/initdb -U root' fi + cp -f ${pkgs.writeText "pg_hba.conf" cfg.authentication} ${cfg.dataDir}/pg_hba.conf + + # We'd like to use the `-w' flag here to wait until the + # database is up, but it requires a `postgres' user to + # exist. And we can't call `createuser' before the + # database is running. + ${run} -c '${postgresql}/bin/pg_ctl start' + + # So wait until the server is running. + while ! ${run} -c '${postgresql}/bin/pg_ctl status'; do + sleep 1 + done ''; - exec = "${run} -c '${postgresql}/bin/postgres -D ${cfg.dataDir} ${toString flags}'"; + postStop = + '' + ${run} -c '${postgresql}/bin/pg_ctl stop -m fast' + ''; }; }; From b7c519456e534e93649239ffe87a094a7ebb4dc5 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sat, 7 Nov 2009 01:17:56 +0000 Subject: [PATCH 19/44] * Updated switch-to-configuration for Upstart 0.6. svn path=/nixos/branches/upstart-0.6/; revision=18237 --- .../activation/switch-to-configuration.sh | 52 +++++++++---------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/modules/system/activation/switch-to-configuration.sh b/modules/system/activation/switch-to-configuration.sh index 802b57a31af..18a0664daa7 100644 --- a/modules/system/activation/switch-to-configuration.sh +++ b/modules/system/activation/switch-to-configuration.sh @@ -54,27 +54,24 @@ EOF exit 1 fi - oldEvents=$(readlink -f /etc/event.d || true) - newEvents=$(readlink -f @out@/etc/event.d) + oldJobs=$(readlink -f /etc/static/init) + newJobs=$(readlink -f @out@/etc/init) - #echo "old: $oldEvents" - #echo "new: $newEvents" + echo "old: $oldJobs" + echo "new: $newJobs" stopJob() { local job=$1 - initctl stop "$job" - while ! initctl status "$job" 2>&1 | grep -q "(stop) waiting"; do - echo "waiting for $job to stop..." - sleep 1 - done + initctl stop "$job" || true } # Stop all services that are not in the new Upstart # configuration. - for event in $(cd $oldEvents && ls); do - if ! test -e "$newEvents/$event"; then - echo "stopping $event..." - stopJob $event + for job in $(cd $oldJobs && ls *.conf); do + job=$(basename $job .conf) + if ! test -e "$newJobs/$job.conf"; then + echo "stopping $job..." + stopJob $job fi done @@ -83,26 +80,27 @@ EOF echo "activating the configuration..." @out@/activate @out@ - # Make Upstart reload its events. !!! Should wait until it has - # finished processing its stop events. - kill -TERM 1 + # Make Upstart reload its jobs. + initctl reload-configuration # Start all new services and restart all changed services. - for event in $(cd $newEvents && ls); do + for job in $(cd $newJobs && ls *.conf); do - # Hack: skip the sys-* and ctrl-alt-delete events. + job=$(basename $job .conf) + + # Hack: skip the shutdown and control-alt-delete jobs. # Another hack: don't restart the X server (that would kill all the clients). # And don't restart dbus, since that causes ConsoleKit to # forget about current sessions. - if echo "$event" | grep -q "^sys-\|^ctrl-\|^xserver$\|^dbus$"; then continue; fi - - if ! test -e "$oldEvents/$event"; then - echo "starting $event..." - initctl start "$event" - elif test "$(readlink "$oldEvents/$event")" != "$(readlink "$newEvents/$event")"; then - echo "restarting $event..." - stopJob $event - initctl start "$event" + if echo "$job" | grep -q "^shutdown$\|^control-alt-delete$\|^xserver$\|^dbus$"; then continue; fi + + if ! test -e "$oldJobs/$job.conf"; then + echo "starting $job..." + initctl start "$job" || true + elif test "$(readlink "$oldJobs/$job.conf")" != "$(readlink "$newJobs/$job.conf")"; then + echo "restarting $job..." + stopJob $job + initctl start "$job" || true fi done fi From 7d47575a58827ac61bf8c1068f3c81e7324233be Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sat, 7 Nov 2009 11:32:28 +0000 Subject: [PATCH 20/44] * A 15-second timeout to start the X server is too low if there are a billion other daemons starting at the same time during the boot. svn path=/nixos/branches/upstart-0.6/; revision=18250 --- modules/services/x11/display-managers/kdm.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/services/x11/display-managers/kdm.nix b/modules/services/x11/display-managers/kdm.nix index 55171035442..18a6623eb85 100644 --- a/modules/services/x11/display-managers/kdm.nix +++ b/modules/services/x11/display-managers/kdm.nix @@ -26,6 +26,8 @@ let [X-:*-Core] ServerCmd=${dmcfg.xserverBin} ${dmcfg.xserverArgs} + # The default timeout (15) is too short in a heavily loaded boot process. + ServerTimeout=60 # Needed to prevent the X server from dying on logout and not coming back: TerminateServer=true From 7aecd0ca53d28a0398020907df50e4c7e694bb16 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sat, 7 Nov 2009 12:01:12 +0000 Subject: [PATCH 21/44] * hal: depend on both dbus and acpid (if enabled). * dbus: don't signal Upstart, since that seems to make it forget about pending events or something. In any case starting dbus after acpid was running wouldn't trigger hal to be started (but the other way around did work). svn path=/nixos/branches/upstart-0.6/; revision=18251 --- modules/services/hardware/hal.nix | 4 +--- modules/services/system/dbus.nix | 6 ------ 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/modules/services/hardware/hal.nix b/modules/services/hardware/hal.nix index 9ee16cedf87..6c033256633 100644 --- a/modules/services/hardware/hal.nix +++ b/modules/services/hardware/hal.nix @@ -67,9 +67,7 @@ in jobs.hal = { description = "HAL daemon"; - # !!! TODO: make sure that HAL starts after acpid, - # otherwise hald-addon-acpi will grab /proc/acpi/event. - startOn = if config.powerManagement.enable then "started acpid" else "started dbus"; + startOn = "started dbus" + optionalString config.powerManagement.enable " and started acpid"; environment = { # !!! HACK? These environment variables manipulated inside diff --git a/modules/services/system/dbus.nix b/modules/services/system/dbus.nix index 6f79865645d..b0c8dc66ae5 100644 --- a/modules/services/system/dbus.nix +++ b/modules/services/system/dbus.nix @@ -130,12 +130,6 @@ in exec = "${dbus}/bin/dbus-daemon --config-file=${configDir}/system.conf"; - postStart = - '' - # Signal Upstart that it can connect to the system bus. - kill -HUP 1 || true - ''; - postStop = '' # !!! Hack: doesn't belong here. From d9d072a89bcefe7685a0e9709f3937e1e161c59f Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sat, 7 Nov 2009 12:43:32 +0000 Subject: [PATCH 22/44] * Improve the mysql shutdown. svn path=/nixos/branches/upstart-0.6/; revision=18255 --- modules/services/databases/mysql.nix | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/modules/services/databases/mysql.nix b/modules/services/databases/mysql.nix index 81a2c5ea007..230d36dfe91 100644 --- a/modules/services/databases/mysql.nix +++ b/modules/services/databases/mysql.nix @@ -89,14 +89,12 @@ in chown -R ${cfg.user} ${cfg.pidDir} ''; - exec = "${mysql}/bin/mysqld ${mysqldOptions}"; + exec = "${mysql}/libexec/mysqld ${mysqldOptions}"; - postStop = - '' - pid=$(cat ${pidFile}) - kill "$pid" - ${mysql}/bin/mysql_waitpid "$pid" 1000 - ''; + # !!! Need a postStart script to wait until mysqld is ready to + # accept connections. + + extraConfig = "kill timeout 60"; }; }; From 2353e53c5f985c26cd33dd252d0e2ff96397eb59 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sat, 7 Nov 2009 15:24:59 +0000 Subject: [PATCH 23/44] * /var/run/safemode isn't used anywhere. svn path=/nixos/branches/upstart-0.6/; revision=18257 --- modules/system/boot/stage-2-init.sh | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/modules/system/boot/stage-2-init.sh b/modules/system/boot/stage-2-init.sh index 30298c3127f..5343aa9eae9 100644 --- a/modules/system/boot/stage-2-init.sh +++ b/modules/system/boot/stage-2-init.sh @@ -27,7 +27,7 @@ setPath "@path@" # Mount special file systems. mkdir -m 0755 -p /etc test -e /etc/fstab || touch /etc/fstab # to shut up mount -[ -s /etc/mtab ] && rm /etc/mtab # while installing a symlink is created (see man mount), if it's still there for whateever reason remove it +test -s /etc/mtab && rm /etc/mtab # while installing a symlink is created (see man mount), if it's still there for whateever reason remove it rm -f /etc/mtab* # not that we care about stale locks mkdir -m 0755 -p /proc mount -n -t proc none /proc @@ -100,11 +100,6 @@ rm -rf /var/lock # gone, of course. rm -rf /nix/var/nix/chroots # recreated in activate-configuration.sh -if test -n "$safeMode"; then - mkdir -m 0755 -p /var/run - touch /var/run/safemode -fi - # Create the minimal device nodes needed for the activation scripts # and Upstart. From 9bf8801dc6cfba63811f16b2b9cf0b37ad5897d5 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sat, 7 Nov 2009 15:29:46 +0000 Subject: [PATCH 24/44] * Use a tmpfs for /var/run/nscd to ensure that / or /var can be unmounted or at least remounted read-only during shutdown. Upstart 0.6 apparently uses nscd to do some name lookups, resulting in it holding some mmap mappings to deleted files in /var/run/nscd. E.g. lsof shows: init 1 root DEL REG 253,0 1850313 /var/run/nscd/dbyn3Piz init 1 root DEL REG 253,0 1850312 /var/run/nscd/dbt2e8PH See also http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=324900. This is a workaround - it would be better if Upstart didn't do this. svn path=/nixos/branches/upstart-0.6/; revision=18258 --- modules/system/activation/activation-script.nix | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/modules/system/activation/activation-script.nix b/modules/system/activation/activation-script.nix index a62120363f2..c29c82dd7ae 100644 --- a/modules/system/activation/activation-script.nix +++ b/modules/system/activation/activation-script.nix @@ -84,6 +84,14 @@ let mkdir -m 0755 -p /var/run/nix/current-load # for distributed builds mkdir -m 0700 -p /var/run/nix/remote-stores + # Use a tmpfs for /var/run/nscd to ensure that / or /var can be + # unmounted or at least remounted read-only during shutdown. + # (Upstart 0.6 apparently uses nscd to do some name lookups, + # resulting in it holding some mmap mapping to deleted files in + # /var/run/nscd.) + mkdir -p /var/run/nscd + ${pkgs.utillinux}/bin/mount -t tmpfs -o "mode=755" none /var/run/nscd + mkdir -m 0755 -p /var/log touch /var/log/wtmp # must exist From 091631b43352345f67e507291d1b71a2c678adff Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 10 Nov 2009 21:42:38 +0000 Subject: [PATCH 25/44] * Use Grub 2 on the installation CD. No graphics yet. svn path=/nixos/branches/upstart-0.6/; revision=18325 --- .../installer/cd-dvd/installation-cd-base.nix | 28 +++++++++------ modules/installer/cd-dvd/iso-image.nix | 35 ++++++++++++------- modules/installer/cd-dvd/memtest.nix | 5 +-- release.nix | 3 +- 4 files changed, 45 insertions(+), 26 deletions(-) diff --git a/modules/installer/cd-dvd/installation-cd-base.nix b/modules/installer/cd-dvd/installation-cd-base.nix index 3ec4f3a63de..0fb1156dc71 100644 --- a/modules/installer/cd-dvd/installation-cd-base.nix +++ b/modules/installer/cd-dvd/installation-cd-base.nix @@ -1,20 +1,22 @@ # This module contains the basic configuration for building a NixOS # installation CD. -{config, pkgs, ...}: +{ config, pkgs, ... }: + +with pkgs.lib; let options = { - system.nixosVersion = pkgs.lib.mkOption { + system.nixosVersion = mkOption { default = "${builtins.readFile ../../../VERSION}"; description = '' NixOS version number. ''; }; - installer.configModule = pkgs.lib.mkOption { + installer.configModule = mkOption { example = "./nixos/modules/installer/cd-dvd/installation-cd.nix"; description = '' Filename of the configuration module that builds the CD @@ -38,10 +40,12 @@ let ''; # Put the current directory in a tarball. - nixosTarball = makeTarball "nixos.tar.bz2" (pkgs.lib.cleanSource ../../..); + nixosTarball = makeTarball "nixos.tar.bz2" (cleanSource ../../..); # Put Nixpkgs in a tarball. - nixpkgsTarball = makeTarball "nixpkgs.tar.bz2" (pkgs.lib.cleanSource pkgs.path); + nixpkgsTarball = makeTarball "nixpkgs.tar.bz2" (cleanSource pkgs.path); + + includeSources = true; # A dummy /etc/nixos/configuration.nix in the booted CD that @@ -169,12 +173,14 @@ in # Provide the NixOS/Nixpkgs sources in /etc/nixos. This is required # for nixos-install. - echo "unpacking the NixOS/Nixpkgs sources..." - mkdir -p /etc/nixos/nixos - tar xjf ${nixosTarball}/nixos.tar.bz2 -C /etc/nixos/nixos - mkdir -p /etc/nixos/nixpkgs - tar xjf ${nixpkgsTarball}/nixpkgs.tar.bz2 -C /etc/nixos/nixpkgs - chown -R root.root /etc/nixos + ${optionalString includeSources '' + echo "unpacking the NixOS/Nixpkgs sources..." + mkdir -p /etc/nixos/nixos + tar xjf ${nixosTarball}/nixos.tar.bz2 -C /etc/nixos/nixos + mkdir -p /etc/nixos/nixpkgs + tar xjf ${nixpkgsTarball}/nixpkgs.tar.bz2 -C /etc/nixos/nixpkgs + chown -R root.root /etc/nixos + ''} # Provide a configuration for the CD/DVD itself, to allow users # to run nixos-rebuild to change the configuration of the diff --git a/modules/installer/cd-dvd/iso-image.nix b/modules/installer/cd-dvd/iso-image.nix index 2a0ac4f0e28..b8398825057 100644 --- a/modules/installer/cd-dvd/iso-image.nix +++ b/modules/installer/cd-dvd/iso-image.nix @@ -55,12 +55,21 @@ let }; + # The Grub image. + grubImage = pkgs.runCommand "grub_eltorito" {} + '' + ${pkgs.grub2}/bin/grub-mkimage -o tmp biosdisk iso9660 help linux linux16 sh chain + cat ${pkgs.grub2}/lib/grub/*/cdboot.img tmp > $out + ''; # */ + + # The configuration file for Grub. grubCfg = '' - default 0 - timeout 10 - splashimage /boot/background.xpm.gz + set default = 0 + set timeout = 10 + + #splashimage /boot/background.xpm.gz ${config.boot.extraGrubEntries} ''; @@ -112,11 +121,11 @@ in # Individual files to be included on the CD, outside of the Nix # store on the CD. isoImage.contents = - [ { source = "${pkgs.grub}/lib/grub/${if pkgs.stdenv.system == "i686-linux" then "i386-pc" else "x86_64-unknown"}/stage2_eltorito"; - target = "/boot/grub/stage2_eltorito"; + [ { source = grubImage; + target = "/boot/grub/grub_eltorito"; } - { source = pkgs.writeText "menu.lst" grubCfg; - target = "/boot/grub/menu.lst"; + { source = pkgs.writeText "grub.cfg" grubCfg; + target = "/boot/grub/grub.cfg"; } { source = config.boot.kernelPackages.kernel + "/vmlinuz"; target = "/boot/vmlinuz"; @@ -152,13 +161,15 @@ in # The Grub menu. boot.extraGrubEntries = '' - title Boot from hard disk - root (hd0) + menuentry "Boot from hard disk" { + set root=(hd0) chainloader +1 + } - title NixOS Installer / Rescue - kernel /boot/vmlinuz init=/init systemConfig=/system ${toString config.boot.kernelParams} + menuentry "NixOS Installer / Rescue" { + linux /boot/vmlinuz init=/init systemConfig=/system ${toString config.boot.kernelParams} initrd /boot/initrd + } ''; # Create the ISO image. @@ -168,7 +179,7 @@ in inherit (config.isoImage) isoName compressImage volumeID contents; bootable = true; - bootImage = "/boot/grub/stage2_eltorito"; + bootImage = "/boot/grub/grub_eltorito"; }; boot.postBootCommands = diff --git a/modules/installer/cd-dvd/memtest.nix b/modules/installer/cd-dvd/memtest.nix index fb3aa817855..4bf3ee5a53a 100644 --- a/modules/installer/cd-dvd/memtest.nix +++ b/modules/installer/cd-dvd/memtest.nix @@ -12,8 +12,9 @@ in { boot.extraGrubEntries = '' - title Memtest86+ - kernel ${memtestPath} + menuentry "Memtest86+" { + linux16 ${memtestPath} + } ''; isoImage.contents = diff --git a/release.nix b/release.nix index 26708ed3e73..68e77057c9b 100644 --- a/release.nix +++ b/release.nix @@ -30,10 +30,11 @@ let description = "NixOS installation CD (${description}) - ISO image for ${system}"; maintainers = map (x: lib.getAttr x lib.maintainers) maintainers; }; + inherit iso; } '' ensureDir $out/nix-support - echo "file iso" ${iso}/iso/*.iso* >> $out/nix-support/hydra-build-products + echo "file iso" $iso/iso/*.iso* >> $out/nix-support/hydra-build-products ''; # */ From 7621b40d563c064322c5983c131909a254ce9935 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Fri, 13 Nov 2009 16:45:41 +0000 Subject: [PATCH 26/44] * Provide a 640x480 background image for GRUB 2. * Turn on the graphical GRUB boot screen for the installation CD. svn path=/nixos/branches/upstart-0.6/; revision=18340 --- modules/installer/cd-dvd/iso-image.nix | 36 ++++++++++++++---- modules/installer/cd-dvd/memtest.nix | 2 +- modules/installer/grub/grub.nix | 5 +-- .../grub/winkler-gnu-blue-640x480.png | Bin 0 -> 74487 bytes .../installer/grub/winkler-gnu-blue.README | 6 +++ 5 files changed, 36 insertions(+), 13 deletions(-) create mode 100644 modules/installer/grub/winkler-gnu-blue-640x480.png create mode 100644 modules/installer/grub/winkler-gnu-blue.README diff --git a/modules/installer/cd-dvd/iso-image.nix b/modules/installer/cd-dvd/iso-image.nix index b8398825057..c6b28e78ea8 100644 --- a/modules/installer/cd-dvd/iso-image.nix +++ b/modules/installer/cd-dvd/iso-image.nix @@ -58,7 +58,7 @@ let # The Grub image. grubImage = pkgs.runCommand "grub_eltorito" {} '' - ${pkgs.grub2}/bin/grub-mkimage -o tmp biosdisk iso9660 help linux linux16 sh chain + ${pkgs.grub2}/bin/grub-mkimage -o tmp biosdisk iso9660 help linux linux16 sh chain gfxterm vbe png jpeg cat ${pkgs.grub2}/lib/grub/*/cdboot.img tmp > $out ''; # */ @@ -66,12 +66,27 @@ let # The configuration file for Grub. grubCfg = '' - set default = 0 - set timeout = 10 + set default=0 + set timeout=10 - #splashimage /boot/background.xpm.gz + if loadfont /boot/grub/unicode.pf2; then + set gfxmode=640x480 + insmod gfxterm + insmod vbe + terminal_output.gfxterm - ${config.boot.extraGrubEntries} + insmod png + if background_image /boot/grub/splash.png; then + set color_normal=white/black + set color_highlight=black/white + else + set menu_color_normal=cyan/blue + set menu_color_highlight=white/blue + fi + + fi + + ${config.boot.loader.grub.extraEntries} ''; in @@ -79,6 +94,8 @@ in { require = options; + boot.loader.grub.version = 2; + # In stage 1 of the boot, mount the CD/DVD as the root FS by label # so that we don't need to know its device. fileSystems = @@ -133,8 +150,11 @@ in { source = config.system.build.initialRamdisk + "/initrd"; target = "/boot/initrd"; } - { source = config.boot.grubSplashImage; - target = "/boot/background.xpm.gz"; + { source = "${pkgs.grub2}/share/grub/unicode.pf2"; + target = "/boot/grub/unicode.pf2"; + } + { source = config.boot.loader.grub.splashImage; + target = "/boot/grub/splash.png"; } { source = config.system.build.squashfsStore; target = "/nix-store.squashfs"; @@ -159,7 +179,7 @@ in ]; # The Grub menu. - boot.extraGrubEntries = + boot.loader.grub.extraEntries = '' menuentry "Boot from hard disk" { set root=(hd0) diff --git a/modules/installer/cd-dvd/memtest.nix b/modules/installer/cd-dvd/memtest.nix index 4bf3ee5a53a..2ec35dcc8fb 100644 --- a/modules/installer/cd-dvd/memtest.nix +++ b/modules/installer/cd-dvd/memtest.nix @@ -10,7 +10,7 @@ let in { - boot.extraGrubEntries = + boot.loader.grub.extraEntries = '' menuentry "Memtest86+" { linux16 ${memtestPath} diff --git a/modules/installer/grub/grub.nix b/modules/installer/grub/grub.nix index cb716a2a84e..9fe65a7bceb 100644 --- a/modules/installer/grub/grub.nix +++ b/modules/installer/grub/grub.nix @@ -98,10 +98,7 @@ in sha256 = "14kqdx2lfqvh40h6fjjzqgff1mwk74dmbjvmqphi6azzra7z8d59"; } # GRUB 1.97 doesn't support gzipped XPMs. - else pkgs.fetchurl { - url = http://www.gnu.org/graphics/winkler-gnu-blue.png; - sha256 = "0y8fvxalwxyid4k438k7c21bnv728qjsb92rqfapsmpv2bcj7f6k"; - }; + else ./winkler-gnu-blue-640x480.png; example = null; description = '' Background image used for GRUB. It must be a 640x480, diff --git a/modules/installer/grub/winkler-gnu-blue-640x480.png b/modules/installer/grub/winkler-gnu-blue-640x480.png new file mode 100644 index 0000000000000000000000000000000000000000..35bbb57b51ee147cb3dc32747ee31d7fb3cba3bf GIT binary patch literal 74487 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 Date: Sat, 14 Nov 2009 16:12:02 +0000 Subject: [PATCH 27/44] * Don't build the Grub menu builder script on the ISO. Then we don't the /init and /system symlinks on the CD (since it removes the cyclic dependency between building the Grub menu and the system derivation). svn path=/nixos/branches/upstart-0.6/; revision=18344 --- modules/installer/cd-dvd/iso-image.nix | 40 ++++++++++++-------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/modules/installer/cd-dvd/iso-image.nix b/modules/installer/cd-dvd/iso-image.nix index c6b28e78ea8..6c1151ca217 100644 --- a/modules/installer/cd-dvd/iso-image.nix +++ b/modules/installer/cd-dvd/iso-image.nix @@ -2,20 +2,22 @@ # configuration. The derivation for the ISO image will be placed in # config.system.build.isoImage. -{config, pkgs, ...}: +{ config, pkgs, ... }: + +with pkgs.lib; let options = { - isoImage.isoName = pkgs.lib.mkOption { + isoImage.isoName = mkOption { default = "cd.iso"; description = '' Name of the generated ISO image file. ''; }; - isoImage.compressImage = pkgs.lib.mkOption { + isoImage.compressImage = mkOption { default = false; description = '' Whether the ISO image should be compressed using @@ -23,7 +25,7 @@ let ''; }; - isoImage.volumeID = pkgs.lib.mkOption { + isoImage.volumeID = mkOption { default = "NIXOS_BOOT_CD"; description = '' Specifies the label or volume ID of the generated ISO image. @@ -32,7 +34,7 @@ let ''; }; - isoImage.contents = pkgs.lib.mkOption { + isoImage.contents = mkOption { example = [ { source = pkgs.memtest86 + "/memtest.bin"; target = "boot/memtest.bin"; @@ -44,7 +46,7 @@ let ''; }; - isoImage.storeContents = pkgs.lib.mkOption { + isoImage.storeContents = mkOption { example = [pkgs.stdenv]; description = '' This option lists additional derivations to be included in the @@ -96,6 +98,15 @@ in boot.loader.grub.version = 2; + # Don't build the GRUB menu builder script, since we don't need it + # here and it causes a cyclic dependency. + boot.loader.grub.enable = false; + + # !!! Hack - attributes expected by other modules. + system.build.menuBuilder = "true"; + system.boot.loader.kernelFile = "vmlinuz"; + environment.systemPackages = [ pkgs.grub2 ]; + # In stage 1 of the boot, mount the CD/DVD as the root FS by label # so that we don't need to know its device. fileSystems = @@ -112,7 +123,7 @@ in # We need squashfs in the initrd to mount the compressed Nix store, # and aufs to make the root filesystem appear writable. - boot.extraModulePackages = (pkgs.lib.optional + boot.extraModulePackages = (optional (! config.boot.kernelPackages.kernel.features ? aufs) config.boot.kernelPackages.aufs); boot.initrd.extraKernelModules = ["aufs" "squashfs"]; @@ -163,19 +174,6 @@ in source = pkgs.runCommand "empty" {} "ensureDir $out"; target = "/nix/store"; } - { # Another quick hack: the kernel needs a systemConfig - # parameter in menu.lst, but the system config depends on - # menu.lst. Break the cyclic dependency by having a /system - # symlink on the CD, and having menu.lst refer to /system. - source = pkgs.runCommand "system" {} - "ln -s ${config.system.build.toplevel} $out"; - target = "/system"; - } - { # Idem for the stage-2 init script. - source = pkgs.runCommand "system" {} - "ln -s ${config.system.build.bootStage2} $out"; - target = "/init"; - } ]; # The Grub menu. @@ -187,7 +185,7 @@ in } menuentry "NixOS Installer / Rescue" { - linux /boot/vmlinuz init=/init systemConfig=/system ${toString config.boot.kernelParams} + linux /boot/vmlinuz init=${config.system.build.bootStage2} systemConfig=${config.system.build.toplevel} ${toString config.boot.kernelParams} initrd /boot/initrd } ''; From 1affc9168e66ca53c590529597934c1217a908cb Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sun, 15 Nov 2009 12:48:42 +0000 Subject: [PATCH 28/44] * Move power management configuration into a separate module from the acpid service. * Add a pm-utils hook to allow commands to be executed when the system suspends/resumes etc. svn path=/nixos/branches/upstart-0.6/; revision=18353 --- modules/config/power-management.nix | 63 +++++++++++++++++++++++++++++ modules/module-list.nix | 1 + modules/services/hardware/acpid.nix | 6 +-- modules/services/hardware/hal.nix | 9 ++--- 4 files changed, 71 insertions(+), 8 deletions(-) create mode 100644 modules/config/power-management.nix diff --git a/modules/config/power-management.nix b/modules/config/power-management.nix new file mode 100644 index 00000000000..a094caae0e0 --- /dev/null +++ b/modules/config/power-management.nix @@ -0,0 +1,63 @@ +{ config, pkgs, ... }: + +with pkgs.lib; + +let + + cfg = config.powerManagement; + + sleepHook = pkgs.writeScript "sleep-hook.sh" + '' + #! ${pkgs.stdenv.shell} + action="$1" + if [ "$action" = "resume" ]; then + ${cfg.resumeCommands} + fi + ''; + +in + +{ + + ###### interface + + options = { + + powerManagement = { + + enable = mkOption { + default = false; + description = + '' + Whether to enable power management. This includes support + for suspend-to-RAM and powersave features on laptops. + ''; + }; + + resumeCommands = mkOption { + default = ""; + description = "Commands executed after the system resumes from suspend-to-RAM."; + }; + + }; + + }; + + + ###### implementation + + config = mkIf cfg.enable { + + # Enable the ACPI daemon. Not sure whether this is essential. + services.acpid.enable = true; + + environment.systemPackages = [ pkgs.pmutils ]; + + environment.etc = singleton + { source = sleepHook; + target = "pm/sleep.d/00sleep-hook"; + }; + + }; + +} diff --git a/modules/module-list.nix b/modules/module-list.nix index b69012f07e4..e3300242912 100644 --- a/modules/module-list.nix +++ b/modules/module-list.nix @@ -5,6 +5,7 @@ ./config/networking.nix ./config/no-x-libs.nix ./config/nsswitch.nix + ./config/power-management.nix ./config/system-path.nix ./config/timezone.nix ./config/unix-odbc-drivers.nix diff --git a/modules/services/hardware/acpid.nix b/modules/services/hardware/acpid.nix index 88bfc4e7258..e9fd8e46dc9 100644 --- a/modules/services/hardware/acpid.nix +++ b/modules/services/hardware/acpid.nix @@ -60,11 +60,11 @@ in options = { - powerManagement = { + services.acpid = { enable = mkOption { default = false; - description = "Whether to enable power management (ACPI daemon)"; + description = "Whether to enable the ACPI daemon."; }; }; @@ -74,7 +74,7 @@ in ###### implementation - config = mkIf config.powerManagement.enable { + config = mkIf config.services.acpid.enable { jobs.acpid = { description = "ACPI daemon"; diff --git a/modules/services/hardware/hal.nix b/modules/services/hardware/hal.nix index 6c033256633..25a24ce5e34 100644 --- a/modules/services/hardware/hal.nix +++ b/modules/services/hardware/hal.nix @@ -48,10 +48,9 @@ in config = mkIf cfg.enable { - # !!! move pmutils somewhere else - environment.systemPackages = [hal pkgs.pmutils]; + environment.systemPackages = [ hal ]; - services.hal.packages = [hal pkgs.hal_info]; + services.hal.packages = [ hal pkgs.hal_info ]; users.extraUsers = singleton { name = "haldaemon"; @@ -67,7 +66,7 @@ in jobs.hal = { description = "HAL daemon"; - startOn = "started dbus" + optionalString config.powerManagement.enable " and started acpid"; + startOn = "started dbus" + optionalString config.services.acpid.enable " and started acpid"; environment = { # !!! HACK? These environment variables manipulated inside @@ -113,4 +112,4 @@ in }; -} \ No newline at end of file +} From 9174b4ab421029927fef7febc0645480c10748fa Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sun, 15 Nov 2009 12:56:40 +0000 Subject: [PATCH 29/44] * Provide a convience option for commands that must be executed both when the system boots and when it resumes from suspend. svn path=/nixos/branches/upstart-0.6/; revision=18354 --- modules/config/power-management.nix | 14 +++++++++++++- modules/system/boot/stage-2.nix | 6 +++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/modules/config/power-management.nix b/modules/config/power-management.nix index a094caae0e0..dd241e4e815 100644 --- a/modules/config/power-management.nix +++ b/modules/config/power-management.nix @@ -11,7 +11,8 @@ let #! ${pkgs.stdenv.shell} action="$1" if [ "$action" = "resume" ]; then - ${cfg.resumeCommands} + ${cfg.resumeCommands} + ${cfg.powerUpCommands} fi ''; @@ -39,6 +40,17 @@ in description = "Commands executed after the system resumes from suspend-to-RAM."; }; + powerUpCommands = mkOption { + default = ""; + example = "${pkgs.hdparm}/sbin/hdparm -B 255 /dev/sda"; + description = + '' + Commands executed when the machine powers up. That is, + they're executed both when the system first boots and when + it resumes from suspend or hibernation. + ''; + }; + }; }; diff --git a/modules/system/boot/stage-2.nix b/modules/system/boot/stage-2.nix index b19dbc39183..5c3c3560be3 100644 --- a/modules/system/boot/stage-2.nix +++ b/modules/system/boot/stage-2.nix @@ -29,7 +29,11 @@ let utillinux udev ]; - postBootCommands = writeText "local-cmds" config.boot.postBootCommands; + postBootCommands = writeText "local-cmds" + '' + ${config.boot.postBootCommands} + ${config.powerManagement.powerUpCommands} + ''; }; in From 56d817b0b0f198d8102736d511593d42750fd014 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sun, 15 Nov 2009 20:38:19 +0000 Subject: [PATCH 30/44] * X server: remove the udev hack. svn path=/nixos/branches/upstart-0.6/; revision=18358 --- modules/services/x11/xserver.nix | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/modules/services/x11/xserver.nix b/modules/services/x11/xserver.nix index be0fb3bcecf..ea6f0f8cf96 100644 --- a/modules/services/x11/xserver.nix +++ b/modules/services/x11/xserver.nix @@ -363,7 +363,7 @@ in optional (elem "virtualbox" driverNames) kernelPackages.virtualboxGuestAdditions; jobs.xserver = - { startOn = if cfg.autorun then "started hal" else ""; + { startOn = if cfg.autorun then "started udev and started hal" else ""; environment = { FONTCONFIG_FILE = "/etc/fonts/fonts.conf"; # !!! cleanup @@ -379,14 +379,6 @@ in preStart = '' - # Ugly hack: wait until udev has started since the X server - # needs various devices. This would more properly be - # expressed as an Upstart dependency, but AFAIK in "start - # on" we can't express a logical AND. - while ! initctl status udev 2>&1 | grep -q running; do - sleep 1 - done - rm -f /var/run/opengl-driver ${# !!! The OpenGL driver depends on what's detected at runtime. if elem "nvidia" driverNames then '' From 0755e228bfee452957e4c89ef86e47ea1187ecd3 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sun, 15 Nov 2009 20:40:07 +0000 Subject: [PATCH 31/44] * Since "initctl stop" is now synchronous, the sleep is no longer needed. svn path=/nixos/branches/upstart-0.6/; revision=18359 --- modules/services/networking/ifplugd.nix | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/services/networking/ifplugd.nix b/modules/services/networking/ifplugd.nix index f1510b1aa4b..6ec71849ca3 100644 --- a/modules/services/networking/ifplugd.nix +++ b/modules/services/networking/ifplugd.nix @@ -16,7 +16,6 @@ let #! ${pkgs.stdenv.shell} if test "$2" = up; then initctl stop dhclient - sleep 1 initctl start dhclient fi ''; From e9b2ef9fb7ba4582e7a51d2022e5fc30720f8962 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Sun, 15 Nov 2009 20:40:41 +0000 Subject: [PATCH 32/44] * Restart wpa_supplicant and dhclient on resume. svn path=/nixos/branches/upstart-0.6/; revision=18360 --- modules/services/networking/dhclient.nix | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/modules/services/networking/dhclient.nix b/modules/services/networking/dhclient.nix index 600b7196aa4..a714562444e 100644 --- a/modules/services/networking/dhclient.nix +++ b/modules/services/networking/dhclient.nix @@ -103,6 +103,22 @@ in } ]; + powerManagement.resumeCommands = + '' + export PATH=${config.system.build.upstart}/sbin:$PATH + + restart() { + local job="$1" + if initctl status "$job" 2> /dev/null | grep -q 'running'; then + initctl stop "$job" + initctl start "$job" + fi + } + + restart wpa_supplicant + restart dhclient + ''; + }; } From 6d11d63ba313cf2ca839df20edd98d816cecd527 Mon Sep 17 00:00:00 2001 From: Sander van der Burg Date: Wed, 18 Nov 2009 14:50:48 +0000 Subject: [PATCH 33/44] Removed the nasty sleep hack of tomcat, since upstart-0,6 is also capable of only executing start and stop scripts svn path=/nixos/branches/upstart-0.6/; revision=18430 --- modules/services/web-servers/tomcat.nix | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/modules/services/web-servers/tomcat.nix b/modules/services/web-servers/tomcat.nix index 4eb5b3fc5b5..1a2c17dcf1b 100644 --- a/modules/services/web-servers/tomcat.nix +++ b/modules/services/web-servers/tomcat.nix @@ -304,13 +304,10 @@ in done '' else ""} - ''; # */ - exec = - '' - ${pkgs.su}/bin/su -s ${pkgs.bash}/bin/sh ${cfg.user} -c 'CATALINA_BASE=${cfg.baseDir} JAVA_HOME=${pkgs.jdk} JAVA_OPTS="${cfg.javaOpts}" CATALINA_OPTS="${cfg.catalinaOpts}" ${pkgs.tomcat6}/bin/startup.sh; sleep 1000d' + ${pkgs.su}/bin/su -s ${pkgs.bash}/bin/sh ${cfg.user} -c 'CATALINA_BASE=${cfg.baseDir} JAVA_HOME=${pkgs.jdk} JAVA_OPTS="${cfg.javaOpts}" CATALINA_OPTS="${cfg.catalinaOpts}" ${pkgs.tomcat6}/bin/startup.sh' ''; - + postStop = '' echo "Stopping tomcat..." From 27d0d2927eca16ca02acdf78d3c505278845b6d7 Mon Sep 17 00:00:00 2001 From: Sander van der Burg Date: Wed, 18 Nov 2009 16:19:04 +0000 Subject: [PATCH 34/44] Added initialDatabases option to the MySQL service. This is useful for e.g. automatically intialing databases in a test VM svn path=/nixos/branches/upstart-0.6/; revision=18437 --- modules/services/databases/mysql.nix | 40 +++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/modules/services/databases/mysql.nix b/modules/services/databases/mysql.nix index 230d36dfe91..50d00c0e5ba 100644 --- a/modules/services/databases/mysql.nix +++ b/modules/services/databases/mysql.nix @@ -55,7 +55,15 @@ in default = "/var/run/mysql"; description = "Location of the file which stores the PID of the MySQL server"; }; - + + 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 = ./foodatabase.sql; } + { name = "bardatabase"; schema = ./bardatabase.sql; } + ]; + }; }; }; @@ -90,6 +98,36 @@ in ''; exec = "${mysql}/libexec/mysqld ${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 + + # Create initial databases + + ${concatMapStrings (database: + '' + if ! test -e "${cfg.dataDir}/${database.name}"; then + echo "Creating initial database: ${database.name}" + ( echo "create database ${database.name};" + echo "use ${database.name};" + cat ${database.schema} ) | ${mysql}/bin/mysql -u root -N + fi + '') cfg.initialDatabases} + ''; # !!! Need a postStart script to wait until mysqld is ready to # accept connections. From e171b6d86eedc6fedba31db91131a42c497e3934 Mon Sep 17 00:00:00 2001 From: Sander van der Burg Date: Wed, 18 Nov 2009 21:56:47 +0000 Subject: [PATCH 35/44] Added option to specify directories as a path for MySQL database schemas. By using this approach we no longer have to specify schemas like this: schema = "${myDatabaseSchemaDrvFun}/prefix/to/the/sqlfile.sql"; but like this: schema = myDataBaseSchemaDrv; which is in some cases more convenient. svn path=/nixos/branches/upstart-0.6/; revision=18450 --- modules/services/databases/mysql.nix | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/services/databases/mysql.nix b/modules/services/databases/mysql.nix index 50d00c0e5ba..e3c92adee5a 100644 --- a/modules/services/databases/mysql.nix +++ b/modules/services/databases/mysql.nix @@ -124,7 +124,14 @@ in echo "Creating initial database: ${database.name}" ( echo "create database ${database.name};" echo "use ${database.name};" - cat ${database.schema} ) | ${mysql}/bin/mysql -u root -N + 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} ''; From a5cdfcbdb2da9f4ecac892a1a91ebe1d02c8ac11 Mon Sep 17 00:00:00 2001 From: Sander van der Burg Date: Thu, 19 Nov 2009 13:15:15 +0000 Subject: [PATCH 36/44] Removed the dirty sleep hack from the ejabberd service svn path=/nixos/branches/upstart-0.6/; revision=18456 --- modules/services/networking/ejabberd.nix | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/modules/services/networking/ejabberd.nix b/modules/services/networking/ejabberd.nix index ae8cf47b2fa..14476d3e337 100644 --- a/modules/services/networking/ejabberd.nix +++ b/modules/services/networking/ejabberd.nix @@ -56,6 +56,10 @@ in startOn = "started network-interface"; stopOn = "stopping network-interfaces"; + + environment = { + PATH = "$PATH:${pkgs.ejabberd}/sbin:${pkgs.ejabberd}/bin:${pkgs.coreutils}/bin:${pkgs.bash}/bin:${pkgs.gnused}/bin"; + }; preStart = '' @@ -73,13 +77,13 @@ in cp ${pkgs.ejabberd}/etc/ejabberd/* ${cfg.confDir} sed -e 's|{hosts, \["localhost"\]}.|{hosts, \[${cfg.virtualHosts}\]}.|' ${pkgs.ejabberd}/etc/ejabberd/ejabberd.cfg > ${cfg.confDir}/ejabberd.cfg fi + + ejabberdctl --config-dir ${cfg.confDir} --logs ${cfg.logsDir} --spool ${cfg.spoolDir} start ''; - exec = "${pkgs.bash}/bin/sh -c 'export PATH=$PATH:${pkgs.ejabberd}/sbin:${pkgs.coreutils}/bin:${pkgs.bash}/bin; ejabberdctl --config-dir ${cfg.confDir} --logs ${cfg.logsDir} --spool ${cfg.spoolDir} start; sleep 1d'"; - postStop = '' - ${pkgs.ejabberd}/sbin/ejabberdctl stop + ejabberdctl --config-dir ${cfg.confDir} --logs ${cfg.logsDir} --spool ${cfg.spoolDir} stop ''; }; From 9b000b3dcff1cf3636bc7a07cc1ad2055946986a Mon Sep 17 00:00:00 2001 From: Sander van der Burg Date: Thu, 19 Nov 2009 15:12:05 +0000 Subject: [PATCH 37/44] Added loadDumps option, which makes it possible to load a specific ejabberd configuration on the first startup svn path=/nixos/branches/upstart-0.6/; revision=18461 --- modules/services/networking/ejabberd.nix | 33 ++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/modules/services/networking/ejabberd.nix b/modules/services/networking/ejabberd.nix index 14476d3e337..82a51267a80 100644 --- a/modules/services/networking/ejabberd.nix +++ b/modules/services/networking/ejabberd.nix @@ -41,6 +41,11 @@ in description = "Virtualhosts that ejabberd should host. Hostnames are surrounded with doublequotes and separated by commas"; }; + loadDumps = mkOption { + default = []; + description = "Configuration dump that should be loaded on the first startup"; + example = [ ./myejabberd.dump ]; + }; }; }; @@ -68,6 +73,7 @@ in if ! test -d ${cfg.spoolDir} then + initialize=1 cp -av ${pkgs.ejabberd}/var/lib/ejabberd /var/lib fi @@ -79,6 +85,33 @@ in fi ejabberdctl --config-dir ${cfg.confDir} --logs ${cfg.logsDir} --spool ${cfg.spoolDir} start + + ${if cfg.loadDumps == [] then "" else + '' + # Wait until the ejabberd server is available for use + count=0 + while ! ejabberdctl --config-dir ${cfg.confDir} --logs ${cfg.logsDir} --spool ${cfg.spoolDir} status + do + if [ $count -eq 30 ] + then + echo "Tried 30 times, giving up..." + exit 1 + fi + + echo "Ejabberd daemon not yet started. Waiting for 1 second..." + count=$((count++)) + sleep 1 + done + + if [ "$initialize" = "1" ] + then + ${concatMapStrings (dump: + '' + echo "Importing dump: ${dump}" + ejabberdctl --config-dir ${cfg.confDir} --logs ${cfg.logsDir} --spool ${cfg.spoolDir} load ${dump} + '') cfg.loadDumps} + fi + ''} ''; postStop = From dd0f8b36d50bd0840ca6f5cbd34269ee188a702d Mon Sep 17 00:00:00 2001 From: Sander van der Burg Date: Thu, 19 Nov 2009 15:17:04 +0000 Subject: [PATCH 38/44] Moved ejabberd initialization check loop inside the if statement, since we only have to check for it in the first startup svn path=/nixos/branches/upstart-0.6/; revision=18464 --- modules/services/networking/ejabberd.nix | 32 ++++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/modules/services/networking/ejabberd.nix b/modules/services/networking/ejabberd.nix index 82a51267a80..6643d4f9c52 100644 --- a/modules/services/networking/ejabberd.nix +++ b/modules/services/networking/ejabberd.nix @@ -87,24 +87,24 @@ in ejabberdctl --config-dir ${cfg.confDir} --logs ${cfg.logsDir} --spool ${cfg.spoolDir} start ${if cfg.loadDumps == [] then "" else - '' - # Wait until the ejabberd server is available for use - count=0 - while ! ejabberdctl --config-dir ${cfg.confDir} --logs ${cfg.logsDir} --spool ${cfg.spoolDir} status - do - if [ $count -eq 30 ] - then - echo "Tried 30 times, giving up..." - exit 1 - fi - - echo "Ejabberd daemon not yet started. Waiting for 1 second..." - count=$((count++)) - sleep 1 - done - + '' if [ "$initialize" = "1" ] then + # Wait until the ejabberd server is available for use + count=0 + while ! ejabberdctl --config-dir ${cfg.confDir} --logs ${cfg.logsDir} --spool ${cfg.spoolDir} status + do + if [ $count -eq 30 ] + then + echo "Tried 30 times, giving up..." + exit 1 + fi + + echo "Ejabberd daemon not yet started. Waiting for 1 second..." + count=$((count++)) + sleep 1 + done + ${concatMapStrings (dump: '' echo "Importing dump: ${dump}" From ebbde6f8d38e824fcd07d6e97979397bcfc087b2 Mon Sep 17 00:00:00 2001 From: Sander van der Burg Date: Fri, 20 Nov 2009 15:35:01 +0000 Subject: [PATCH 39/44] Added directory option for ejabberd dumps so that we can use derivations on a convenient way, e.g. loadDumps = [ myDumpDrvFun ]; svn path=/nixos/branches/upstart-0.6/; revision=18487 --- modules/services/networking/ejabberd.nix | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/modules/services/networking/ejabberd.nix b/modules/services/networking/ejabberd.nix index 6643d4f9c52..1d714cb0280 100644 --- a/modules/services/networking/ejabberd.nix +++ b/modules/services/networking/ejabberd.nix @@ -108,7 +108,17 @@ in ${concatMapStrings (dump: '' echo "Importing dump: ${dump}" - ejabberdctl --config-dir ${cfg.confDir} --logs ${cfg.logsDir} --spool ${cfg.spoolDir} load ${dump} + + if [ -f ${dump} ] + then + ejabberdctl --config-dir ${cfg.confDir} --logs ${cfg.logsDir} --spool ${cfg.spoolDir} load ${dump} + elif [ -d ${dump} ] + then + for i in ${dump}/ejabberd-dump/* + do + ejabberdctl --config-dir ${cfg.confDir} --logs ${cfg.logsDir} --spool ${cfg.spoolDir} load $i + done + fi '') cfg.loadDumps} fi ''} From 2c8d2c2268ec4325e59faf84e0ffad3012e6f1e7 Mon Sep 17 00:00:00 2001 From: Sander van der Burg Date: Fri, 20 Nov 2009 16:20:57 +0000 Subject: [PATCH 40/44] Small bugfix which automatically creates tomcat configuration dirs. Otherwise the startup would fail svn path=/nixos/branches/upstart-0.6/; revision=18488 --- modules/services/web-servers/tomcat.nix | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/services/web-servers/tomcat.nix b/modules/services/web-servers/tomcat.nix index 1a2c17dcf1b..3d50e42bfae 100644 --- a/modules/services/web-servers/tomcat.nix +++ b/modules/services/web-servers/tomcat.nix @@ -215,6 +215,7 @@ in then for j in $i/conf/Catalina/* do + mkdir -p ${cfg.baseDir}/conf/Catalina/localhost ln -sfn $j ${cfg.baseDir}/conf/Catalina/localhost/`basename $j` done fi @@ -251,6 +252,7 @@ in then for j in $i/conf/Catalina/* do + mkdir -p ${cfg.baseDir}/conf/Catalina/${virtualHost.name} ln -sfn $j ${cfg.baseDir}/conf/Catalina/${virtualHost.name}/`basename $j` done fi From 7fc40ca2f185ddbb0b7f93e164a47966d9fb6a9b Mon Sep 17 00:00:00 2001 From: Sander van der Burg Date: Mon, 23 Nov 2009 13:26:33 +0000 Subject: [PATCH 41/44] Enabled startOn event of the MySQL and Tomcat server so that they are initialised on startup svn path=/nixos/branches/upstart-0.6/; revision=18556 --- modules/services/databases/mysql.nix | 2 +- modules/services/web-servers/tomcat.nix | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/services/databases/mysql.nix b/modules/services/databases/mysql.nix index e3c92adee5a..31d5676b8d7 100644 --- a/modules/services/databases/mysql.nix +++ b/modules/services/databases/mysql.nix @@ -83,7 +83,7 @@ in jobs.mysql = { description = "MySQL server"; - startOn = "started filesystems"; + startOn = "started network-interfaces"; preStart = '' diff --git a/modules/services/web-servers/tomcat.nix b/modules/services/web-servers/tomcat.nix index 3d50e42bfae..ef569a8fedf 100644 --- a/modules/services/web-servers/tomcat.nix +++ b/modules/services/web-servers/tomcat.nix @@ -100,11 +100,11 @@ in description = "Tomcat user"; home = "/homeless-shelter"; }; - + jobs.tomcat = { description = "Apache Tomcat server"; - startOn = "started network-interface"; + startOn = "started network-interfaces"; stopOn = "stopping network-interfaces"; preStart = From ff177a01a707de9a6fcbe0aef1fb793da3fa4f1e Mon Sep 17 00:00:00 2001 From: Sander van der Burg Date: Mon, 23 Nov 2009 13:29:47 +0000 Subject: [PATCH 42/44] Fixed ejabberd service so that it will be started on startup svn path=/nixos/branches/upstart-0.6/; revision=18557 --- modules/services/networking/ejabberd.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/services/networking/ejabberd.nix b/modules/services/networking/ejabberd.nix index 1d714cb0280..b85dc26d2fc 100644 --- a/modules/services/networking/ejabberd.nix +++ b/modules/services/networking/ejabberd.nix @@ -59,7 +59,7 @@ in jobs.ejabberd = { description = "EJabberd server"; - startOn = "started network-interface"; + startOn = "started network-interfaces"; stopOn = "stopping network-interfaces"; environment = { From ed6daf4ea463dfbff391d834b8c5eaab174353ed Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 24 Nov 2009 17:07:23 +0000 Subject: [PATCH 43/44] * Doh! I lost ${flags} somewhere along the way, so Postgres wouldn't get the -i option anymore... svn path=/nixos/branches/upstart-0.6/; revision=18606 --- modules/services/databases/postgresql.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/services/databases/postgresql.nix b/modules/services/databases/postgresql.nix index cd1f9dc8094..1e96483662e 100644 --- a/modules/services/databases/postgresql.nix +++ b/modules/services/databases/postgresql.nix @@ -138,7 +138,7 @@ in # database is up, but it requires a `postgres' user to # exist. And we can't call `createuser' before the # database is running. - ${run} -c '${postgresql}/bin/pg_ctl start' + ${run} -c '${postgresql}/bin/pg_ctl start -o "${toString flags}"' # So wait until the server is running. while ! ${run} -c '${postgresql}/bin/pg_ctl status'; do From 8e8b3f9313e79ef630e5a99a147f43b6ec224bb4 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 24 Nov 2009 17:25:35 +0000 Subject: [PATCH 44/44] * Mount /var/run/nscd only once. svn path=/nixos/branches/upstart-0.6/; revision=18607 --- modules/system/activation/activation-script.nix | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/system/activation/activation-script.nix b/modules/system/activation/activation-script.nix index c29c82dd7ae..df3806b834b 100644 --- a/modules/system/activation/activation-script.nix +++ b/modules/system/activation/activation-script.nix @@ -89,8 +89,10 @@ let # (Upstart 0.6 apparently uses nscd to do some name lookups, # resulting in it holding some mmap mapping to deleted files in # /var/run/nscd.) - mkdir -p /var/run/nscd - ${pkgs.utillinux}/bin/mount -t tmpfs -o "mode=755" none /var/run/nscd + if [ ! -e /var/run/nscd ]; then + mkdir -p /var/run/nscd + ${pkgs.utillinux}/bin/mount -t tmpfs -o "mode=755" none /var/run/nscd + fi mkdir -m 0755 -p /var/log