3
0
Fork 0
forked from mirrors/nixpkgs

* 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
This commit is contained in:
Eelco Dolstra 2012-03-18 01:53:35 +00:00
parent 5a98d6d514
commit 3495a773f9
3 changed files with 94 additions and 68 deletions

View file

@ -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

View file

@ -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
oldJobs=$(readlink -f /etc/static/init)
newJobs=$(readlink -f @out@/etc/init)
stopJob() {
local job=$1
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
newJobs=$(readlink -f @out@/etc/init)
# 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
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
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"

View file

@ -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."