[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