summary refs log tree commit diff
diff options
context:
space:
mode:
authorEelco Dolstra <eelco.dolstra@logicblox.com>2012-03-18 01:53:35 +0000
committerEelco Dolstra <eelco.dolstra@logicblox.com>2012-03-18 01:53:35 +0000
commit3495a773f947105b88f375b525e3045b518443f9 (patch)
tree40e4d2d78d2c6198c17d10fcbb710b2ba973cb31
parent5a98d6d51444e9213d8780c59a4e48e806c72641 (diff)
downloadnixlib-3495a773f947105b88f375b525e3045b518443f9.tar
nixlib-3495a773f947105b88f375b525e3045b518443f9.tar.gz
nixlib-3495a773f947105b88f375b525e3045b518443f9.tar.bz2
nixlib-3495a773f947105b88f375b525e3045b518443f9.tar.lz
nixlib-3495a773f947105b88f375b525e3045b518443f9.tar.xz
nixlib-3495a773f947105b88f375b525e3045b518443f9.tar.zst
nixlib-3495a773f947105b88f375b525e3045b518443f9.zip
* Improved Upstart job handling in switch-to-configuration. It no
  longer compares the current configuration to the previous
  configuration, but instead compares the current Upstart state to the
  intended state.  Thus, if the switch script is interrupted, running
  nixos-rebuild again will resume starting/stopping Upstart jobs where
  the previous run left off.

  We determine if an Upstart job has changed by having the pre-start
  script of each Upstart job put a symlink to its .conf file in
  /var/run/upstart-jobs.  So if this symlink differs from the target
  of /etc/init/<job>.conf, then the job has changed.  This also
  prevents multiple restarts of dependent jobs.  E.g., if job B has
  "start on started A" and "stop on stopping A", then restarting A
  will cause B to be restarted, so B shouldn't B restarted a second
  time.

  We only start jobs that are not running if 1) they're tasks that
  have been previously run (like mountall); or 2) they're jobs that
  have a "start on" condition.  This seems a reasonable heuristic.

svn path=/nixos/trunk/; revision=33222
-rw-r--r--modules/system/activation/activation-script.nix5
-rw-r--r--modules/system/activation/switch-to-configuration.sh140
-rw-r--r--modules/system/upstart/upstart.nix11
3 files changed, 91 insertions, 65 deletions
diff --git a/modules/system/activation/activation-script.nix b/modules/system/activation/activation-script.nix
index 9f10e84f5e99..924d6f680fc9 100644
--- a/modules/system/activation/activation-script.nix
+++ b/modules/system/activation/activation-script.nix
@@ -109,6 +109,11 @@ in
         mkdir -m 0755 -p /var/run/nix/current-load # for distributed builds
         mkdir -m 0700 -p /var/run/nix/remote-stores
 
+        # Directory holding symlinks to currently running Upstart
+        # jobs.  Used to determine which jobs need to be restarted
+        # when switching to a new configuration.
+        mkdir -m 0700 -p /var/run/upstart-jobs
+        
         mkdir -m 0755 -p /var/log
         mkdir -m 0755 -p /var/log/upstart
 
diff --git a/modules/system/activation/switch-to-configuration.sh b/modules/system/activation/switch-to-configuration.sh
index c09fa89083bd..07b24d237272 100644
--- a/modules/system/activation/switch-to-configuration.sh
+++ b/modules/system/activation/switch-to-configuration.sh
@@ -62,75 +62,95 @@ if [ "$action" = "switch" -o "$action" = "boot" ]; then
 fi
 
 # Activate the new configuration.
-if [ "$action" = "switch" -o "$action" = "test" ]; then
+if [ "$action" != switch -a "$action" != test ]; then exit 0; fi
 
-    oldVersion=$(cat /var/run/current-system/upstart-interface-version 2> /dev/null || echo 0)
-    newVersion=$(cat @out@/upstart-interface-version 2> /dev/null || echo 0)
+oldVersion=$(cat /var/run/current-system/upstart-interface-version 2> /dev/null || echo 0)
+newVersion=$(cat @out@/upstart-interface-version 2> /dev/null || echo 0)
 
-    if test "$oldVersion" -ne "$newVersion"; then
+if test "$oldVersion" -ne "$newVersion"; then
         cat <<EOF
 Warning: the new NixOS configuration has an Upstart version that is
 incompatible with the current version.  The new configuration won't
 take effect until you reboot the system.
 EOF
         exit 1
-    fi
+fi
 
-    oldJobs=$(readlink -f /etc/static/init)
-    newJobs=$(readlink -f @out@/etc/init)
+newJobs=$(readlink -f @out@/etc/init)
 
-    stopJob() {
-        local job=$1
+# Stop all currently running jobs that are not in the new Upstart
+# configuration.  (Here "running" means all jobs that are not in the
+# stop/waiting state.)
+for job in $(initctl list | sed -e '/ stop\/waiting/ d; /^[^a-z]/ d; s/^\([^ ]\+\).*/\1/' | sort); do
+    if ! [ -e "$newJobs/$job.conf" ] ; then
+        echo "stopping obsolete job ‘$job’..."
         initctl stop "$job" || true
-    }
-
-    # Stop all services that are not in the new Upstart
-    # configuration.
-    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
-
-    # Activate the new configuration (i.e., update /etc, make
-    # accounts, and so on).
-    echo "activating the configuration..."
-    @out@/activate @out@
-
-    # Make Upstart reload its jobs.
-    initctl reload-configuration
-
-    # Start all new services and restart all changed services.
-    for job in $(cd $newJobs && ls *.conf); do
-
-        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.
-        # Idem for the emergeny-shell, because its `console owner'
-        # line screws up the X server.
-        # Idem for xendomains because we don't want to save/restore
-	# Xen domains unless we have to.
-        # TODO: Jobs should be able to declare that they should not be
-	# auto-restarted.
-        if echo "$job" | grep -q "^shutdown$\|^control-alt-delete$\|^xserver$\|^dbus$\|^disnix$\|^emergency-shell$\|^xendomains$\|^udevtrigger$\|^drbd-down$"; 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
-
-    # Signal dbus to reload its configuration.
-    dbusPid=$(initctl status dbus 2> /dev/null | sed -e 's/.*process \([0-9]\+\)/\1/;t;d')
-    [ -n "$dbusPid" ] && kill -HUP "$dbusPid"
+    fi
+done
+
+# Activate the new configuration (i.e., update /etc, make accounts,
+# and so on).
+echo "activating the configuration..."
+@out@/activate @out@
+
+# Make Upstart reload its jobs.
+initctl reload-configuration
+
+# Allow Upstart jobs to react intelligently to a config change.
+initctl emit config-changed
+
+# Restart all running jobs that have changed.  (Here "running" means
+# all jobs that don't have a "stop" goal.)  We use the symlinks in
+# /var/run/upstart-jobs (created by each job's pre-start script) to
+# determine if a job has changed.
+for job in $(cd $newJobs && ls *.conf); do
+    job=$(basename $job .conf)
+    status=$(status "$job")
+    if ! [[ "$status" =~ start/ ]]; then continue; fi
+    if [ "$(readlink -f "$newJobs/$job.conf")" = "$(readlink -f "/var/run/upstart-jobs/$job")" ]; then continue; fi
+    # 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.
+    # Idem for xendomains because we don't want to save/restore
+    # Xen domains unless we have to.
+    # TODO: Jobs should be able to declare that they should not be
+    # auto-restarted.
+    if echo "$job" | grep -q "^xserver$\|^dbus$\|^disnix$\|^xendomains$\|^udevtrigger$"; then
+        echo "not restarting changed service ‘$job’"
+        continue
+    fi
+    echo "restarting changed service ‘$job’..."
+    # Note: can't use "restart" here, since that only restarts the
+    # job's main process.
+    stop "$job" || true
+    start "$job" || true
+done
+
+# Start all jobs that are not running but should be.  The "should be"
+# criterion is tricky: the intended semantics is that we end up with
+# the same jobs as after a reboot.  If it's a task, restart it if it
+# differs from the previous instance of the same task; if it wasn't
+# previously run, don't run it.  If it's a service, only start it if
+# it has a "start on" condition.
+for job in $(cd $newJobs && ls *.conf); do
+    job=$(basename $job .conf)
+    status=$(status "$job")
+    if ! [[ "$status" =~ stop/ ]]; then continue; fi
+
+    if grep -q '^task$' "$newJobs/$job.conf"; then
+        if [ ! -e "/var/run/upstart-jobs/$job" -o \
+            "$(readlink -f "$newJobs/$job.conf")" = "$(readlink -f "/var/run/upstart-jobs/$job")" ];
+        then continue; fi
+        echo "starting task ‘$job’..."
+        start "$job" || true
+    else
+        if ! grep -q "^start on" "$newJobs/$job.conf"; then continue; fi
+        echo "starting service ‘$job’..."
+        start "$job" || true
+    fi
     
-fi
+done
+
+# Signal dbus to reload its configuration.
+dbusPid=$(initctl status dbus 2> /dev/null | sed -e 's/.*process \([0-9]\+\)/\1/;t;d')
+[ -n "$dbusPid" ] && kill -HUP "$dbusPid"
diff --git a/modules/system/upstart/upstart.nix b/modules/system/upstart/upstart.nix
index a9ed1d020009..c7203d5672be 100644
--- a/modules/system/upstart/upstart.nix
+++ b/modules/system/upstart/upstart.nix
@@ -42,13 +42,14 @@ let
 
           ${concatMapStrings (n: "env ${n}=\"${getAttr n env}\"\n") (attrNames env)}
 
-          ${optionalString (job.preStart != "") ''
-            pre-start script
-              exec >> ${log} 2>&1
+          pre-start script
+            exec >> ${log} 2>&1
+            ln -sfn "$(readlink -f "/etc/init/${job.name}.conf")" /var/run/upstart-jobs/${job.name}
+            ${optionalString (job.preStart != "") ''
               source ${jobHelpers}
               ${job.preStart}
-            end script
-          ''}
+            ''}
+          end script
 
           ${if job.script != "" && job.exec != "" then
               abort "Job ${job.name} has both a `script' and `exec' attribute."