[PATCH] Only allow one anaconda instance (#1146735)
David Shea
dshea at redhat.com
Tue Oct 7 21:22:46 UTC 2014
If the PID file is stale, remove it and retry. Display a graphical
error dialog if we're in the sort of environment that can display
graphical dialogs early on, otherwise just print a message and bomb out.
---
anaconda | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 85 insertions(+), 4 deletions(-)
diff --git a/anaconda b/anaconda
index beeaabd..d49dea5 100755
--- a/anaconda
+++ b/anaconda
@@ -44,7 +44,7 @@ if ("debug=1" in proc_cmdline) or ("debug" in proc_cmdline):
cov.start()
-import atexit, sys, os, time, signal
+import atexit, sys, os, time, signal, stat, errno
def exitHandler(rebootData, storage):
# Clear the list of watched PIDs.
@@ -81,6 +81,10 @@ def exitHandler(rebootData, storage):
uninhibit_screensaver(anaconda.dbus_session_connection, anaconda.dbus_inhibit_id)
anaconda.dbus_inhibit_id = None
+ # Clean up the PID file
+ if pidfile_created:
+ os.unlink(pidfile_path)
+
if not flags.imageInstall and not flags.livecdInstall \
and not flags.dirInstall:
from pykickstart.constants import KS_SHUTDOWN, KS_WAIT
@@ -98,6 +102,18 @@ def exitHandler(rebootData, storage):
else: # reboot action is KS_REBOOT or None
execWithRedirect("systemctl", ["--no-wall", "reboot"])
+def createPIDFile():
+ # If the PID file already exists, this method will raise OSError with
+ # errno == EEXIST
+ global pidfile_created
+
+ # Set all of the read/write bits and let umask make it make sense
+ pidfile = os.open(pidfile_path, os.O_WRONLY|os.O_CREAT|os.O_EXCL,
+ stat.S_IRUSR|stat.S_IWUSR|stat.S_IRGRP|stat.S_IWGRP|stat.S_IROTH|stat.S_IWOTH)
+ pidfile_created = True
+ os.write(pidfile, "%s\n" % os.getpid())
+ os.close(pidfile)
+
def startSpiceVDAgent():
status = iutil.execWithRedirect("spice-vdagent", [])
@@ -841,9 +857,74 @@ if __name__ == "__main__":
# make sure we have /var/log soon, some programs fail to start without it
iutil.mkdirChain("/var/log")
- pidfile = open("/var/run/anaconda.pid", "w")
- pidfile.write("%s\n" % (os.getpid(),))
- del pidfile
+ # Share these with the exit handler, installed later
+ pidfile_path = '/var/run/anaconda.pid'
+ pidfile_created = False
+
+ # Attempt to create the pidfile
+ try:
+ createPIDFile()
+ except OSError as e:
+ # If the failure was anything other than EEXIST during the open call,
+ # just re-raise the exception
+ if e.errno != errno.EEXIST:
+ raise
+
+ log.error("%s already exists", pidfile_path)
+ retry_pid = False
+
+ # Try to read the PID in the file
+ try:
+ with open(pidfile_path, 'r') as pidfile_read:
+ pid = int(pidfile_read.read())
+ except (IOError, ValueError) as e:
+ log.error("Unable to read PID file: %s", e)
+ else:
+ # Is the PID still running?
+ if not os.path.isdir("/proc/%s" % pid):
+ retry_pid = True
+ # Is the PID anaconda?
+ else:
+ try:
+ with open("/proc/%s/stat" % pid, "r") as pidstat:
+ # The part we care about is "PID (name) ..."
+ procname = pidstat.read().split(' ', 2)[1]
+ if procname == '(anaconda)':
+ log.error("Anaconda instance found with PID %s", pid)
+ else:
+ retry_pid = True
+ except (IOError, IndexError) as e:
+ log.error("Unable to read process information: %s", e)
+
+ # If the PID file appears stale, remove it and try to create a new one
+ if retry_pid:
+ log.info("Removing stale PID file")
+ try:
+ os.unlink(pidfile_path)
+ createPIDFile()
+ except OSError as e:
+ log.info("Error recreating pid file: %s", e)
+
+ if not pidfile_created:
+ # If we have a $DISPLAY and zenity is available, we may be running in
+ # a live environment and we can display an error dialog. Otherwise
+ # just print an error.
+ if "DISPLAY" in os.environ and os.access("/usr/bin/zenity", os.X_OK):
+ # The module-level _() calls are ok here because the language may
+ # be set from the live environment in this case, and anaconda's
+ # language setup hasn't happened yet.
+ # pylint: disable=found-_-in-module-class
+ iutil.execWithRedirect("zenity",
+ ["--error", "--title", _("Unable to create PID file"), "--text",
+ _("Anaconda is unable to create /var/run/anaconda.pid because the file" +
+ " already exists. Anaconda is already running, or a previous instance" +
+ " of anaconda has crashed.")])
+ else:
+ print("/var/run/anaconda.pid already exists, exiting")
+
+ iutil.ipmi_report(constants.IPMI_FAILED)
+ sys.exit(1)
+
# add our own additional signal handlers
signal.signal(signal.SIGHUP, startDebugger)
--
1.9.3
More information about the anaconda-patches
mailing list