aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Stubbs <jstubbs@gentoo.org>2005-08-28 08:37:44 +0000
committerJason Stubbs <jstubbs@gentoo.org>2005-08-28 08:37:44 +0000
commitd9fc4acc572c6647a4f27b838d35d27d805d190e (patch)
tree262a8de35d8c7567312757da5f1f66efdc8cece5 /pym/portage_locks.py
downloadportage-d9fc4acc572c6647a4f27b838d35d27d805d190e.tar.gz
portage-d9fc4acc572c6647a4f27b838d35d27d805d190e.tar.bz2
portage-d9fc4acc572c6647a4f27b838d35d27d805d190e.zip
Migration (without history) of the current stable line to subversion.
svn path=/main/branches/2.0/; revision=1941
Diffstat (limited to 'pym/portage_locks.py')
-rw-r--r--pym/portage_locks.py360
1 files changed, 360 insertions, 0 deletions
diff --git a/pym/portage_locks.py b/pym/portage_locks.py
new file mode 100644
index 000000000..cf248e4b6
--- /dev/null
+++ b/pym/portage_locks.py
@@ -0,0 +1,360 @@
+# portage: Lock management code
+# Copyright 2004 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Header: /var/cvsroot/gentoo-src/portage/pym/portage_locks.py,v 1.18.2.2 2005/01/16 02:35:33 carpaski Exp $
+cvs_id_string="$Id: portage_locks.py,v 1.18.2.2 2005/01/16 02:35:33 carpaski Exp $"[5:-2]
+
+import atexit
+import errno
+import os
+import stat
+import string
+import time
+import types
+import portage_exception
+import portage_file
+import portage_util
+import portage_data
+from portage_localization import _
+
+HARDLINK_FD = -2
+
+hardlock_path_list = []
+def clean_my_hardlocks():
+ for x in hardlock_path_list:
+ hardlock_cleanup(x)
+def add_hardlock_file_to_cleanup(path):
+ mypath = portage_file.normpath(path)
+ if os.path.isfile(mypath):
+ mypath = os.path.dirname(mypath)
+ if os.path.isdir(mypath):
+ hardlock_path_list = mypath[:]
+
+atexit.register(clean_my_hardlocks)
+
+def lockdir(mydir):
+ return lockfile(mydir,wantnewlockfile=1)
+def unlockdir(mylock):
+ return unlockfile(mylock)
+
+def lockfile(mypath,wantnewlockfile=0,unlinkfile=0):
+ """Creates all dirs upto, the given dir. Creates a lockfile
+ for the given directory as the file: directoryname+'.portage_lockfile'."""
+ import fcntl
+
+ if not mypath:
+ raise portage_exception.InvalidData, "Empty path given"
+
+ if type(mypath) == types.StringType and mypath[-1] == '/':
+ mypath = mypath[:-1]
+
+ if type(mypath) == types.FileType:
+ mypath = mypath.fileno()
+ if type(mypath) == types.IntType:
+ lockfilename = mypath
+ wantnewlockfile = 0
+ unlinkfile = 0
+ elif wantnewlockfile:
+ lockfilename = mypath+".portage_lockfile"
+ unlinkfile = 1
+ else:
+ lockfilename = mypath
+
+ if type(mypath) == types.StringType:
+ if not os.path.exists(os.path.dirname(mypath)):
+ raise portage_exception.DirectoryNotFound, os.path.dirname(mypath)
+ if not os.path.exists(lockfilename):
+ old_mask=os.umask(000)
+ myfd = os.open(lockfilename, os.O_CREAT|os.O_RDWR,0660)
+ try:
+ if os.stat(lockfilename).st_gid != portage_data.portage_gid:
+ os.chown(lockfilename,os.getuid(),portage_data.portage_gid)
+ except SystemExit, e:
+ raise
+ except OSError, e:
+ if e[0] == 2: # No such file or directory
+ return lockfile(mypath,wantnewlockfile,unlinkfile)
+ else:
+ portage_util.writemsg("Cannot chown a lockfile. This could cause inconvenience later.\n");
+ os.umask(old_mask)
+ else:
+ myfd = os.open(lockfilename, os.O_CREAT|os.O_RDWR,0660)
+
+ elif type(mypath) == types.IntType:
+ myfd = mypath
+
+ else:
+ raise ValueError, "Unknown type passed in '%s': '%s'" % (type(mypath),mypath)
+
+ # try for a non-blocking lock, if it's held, throw a message
+ # we're waiting on lockfile and use a blocking attempt.
+ locking_method = fcntl.lockf
+ try:
+ fcntl.lockf(myfd,fcntl.LOCK_EX|fcntl.LOCK_NB)
+ except IOError, e:
+ if "errno" not in dir(e):
+ raise
+ if e.errno == errno.EAGAIN:
+ # resource temp unavailable; eg, someone beat us to the lock.
+ if type(mypath) == types.IntType:
+ print "waiting for lock on fd %i" % myfd
+ else:
+ print "waiting for lock on %s" % lockfilename
+ # try for the exclusive lock now.
+ fcntl.lockf(myfd,fcntl.LOCK_EX)
+ elif e.errno == errno.ENOLCK:
+ # We're not allowed to lock on this FS.
+ os.close(myfd)
+ link_success = False
+ if lockfilename == str(lockfilename):
+ if wantnewlockfile:
+ try:
+ if os.stat(lockfilename)[stat.ST_NLINK] == 1:
+ os.unlink(lockfilename)
+ except Exception, e:
+ pass
+ link_success = hardlink_lockfile(lockfilename)
+ if not link_success:
+ raise
+ locking_method = None
+ myfd = HARDLINK_FD
+ else:
+ raise
+
+
+ if type(lockfilename) == types.StringType and not os.path.exists(lockfilename):
+ # The file was deleted on us... Keep trying to make one...
+ os.close(myfd)
+ portage_util.writemsg("lockfile recurse\n",1)
+ lockfilename,myfd,unlinkfile,locking_method = lockfile(mypath,wantnewlockfile,unlinkfile)
+
+ portage_util.writemsg(str((lockfilename,myfd,unlinkfile))+"\n",1)
+ return (lockfilename,myfd,unlinkfile,locking_method)
+
+def unlockfile(mytuple):
+ import fcntl
+
+ #XXX: Compatability hack.
+ if len(mytuple) == 3:
+ lockfilename,myfd,unlinkfile = mytuple
+ locking_method = fcntl.flock
+ elif len(mytuple) == 4:
+ lockfilename,myfd,unlinkfile,locking_method = mytuple
+ else:
+ raise
+
+ if(myfd == HARDLINK_FD):
+ unhardlink_lockfile(lockfilename)
+ return True
+
+ if type(lockfilename) == types.StringType and not os.path.exists(lockfilename):
+ portage_util.writemsg("lockfile does not exist '%s'\n" % lockfilename,1)
+ if (myfd != None) and type(lockfilename) == types.StringType:
+ os.close(myfd)
+ return False
+
+ try:
+ if myfd == None:
+ myfd = os.open(lockfilename, os.O_WRONLY,0660)
+ unlinkfile = 1
+ locking_method(myfd,fcntl.LOCK_UN)
+ except SystemExit, e:
+ raise
+ except Exception, e:
+ if type(lockfilename) == types.StringType:
+ os.close(myfd)
+ raise IOError, "Failed to unlock file '%s'\n" % lockfilename
+
+ try:
+ # This sleep call was added to allow other processes that are
+ # waiting for a lock to be able to grab it before it is deleted.
+ # lockfile() already accounts for this situation, however, and
+ # the sleep here adds more time than is saved overall, so am
+ # commenting until it is proved necessary.
+ #time.sleep(0.0001)
+ if unlinkfile:
+ locking_method(myfd,fcntl.LOCK_EX|fcntl.LOCK_NB)
+ # We won the lock, so there isn't competition for it.
+ # We can safely delete the file.
+ portage_util.writemsg("Got the lockfile...\n",1)
+ #portage_util.writemsg("Unlinking...\n")
+ os.unlink(lockfilename)
+ portage_util.writemsg("Unlinked lockfile...\n",1)
+ locking_method(myfd,fcntl.LOCK_UN)
+ except SystemExit, e:
+ raise
+ except Exception, e:
+ # We really don't care... Someone else has the lock.
+ # So it is their problem now.
+ portage_util.writemsg("Failed to get lock... someone took it.\n",1)
+ portage_util.writemsg(str(e)+"\n",1)
+
+ # why test lockfilename? because we may have been handed an
+ # fd originally, and the caller might not like having their
+ # open fd closed automatically on them.
+ if type(lockfilename) == types.StringType:
+ os.close(myfd)
+
+ return True
+
+
+
+
+def hardlock_name(path):
+ return path+".hardlock-"+os.uname()[1]+"-"+str(os.getpid())
+
+def hardlink_active(lock):
+ if not os.path.exists(lock):
+ return False
+ # XXXXXXXXXXXXXXXXXXXXXXXXXX
+
+def hardlink_is_mine(link,lock):
+ try:
+ myhls = os.stat(link)
+ mylfs = os.stat(lock)
+ except SystemExit, e:
+ raise
+ except:
+ myhls = None
+ mylfs = None
+
+ if myhls:
+ if myhls[stat.ST_NLINK] == 2:
+ return True
+ if mylfs:
+ if mylfs[stat.ST_INO] == myhls[stat.ST_INO]:
+ return True
+ return False
+
+def hardlink_lockfile(lockfilename, max_wait=14400):
+ """Does the NFS, hardlink shuffle to ensure locking on the disk.
+ We create a PRIVATE lockfile, that is just a placeholder on the disk.
+ Then we HARDLINK the real lockfile to that private file.
+ If our file can 2 references, then we have the lock. :)
+ Otherwise we lather, rise, and repeat.
+ We default to a 4 hour timeout.
+ """
+
+ add_hardlock_file_to_cleanup(lockfilename)
+
+ start_time = time.time()
+ myhardlock = hardlock_name(lockfilename)
+ reported_waiting = False
+
+ while(time.time() < (start_time + max_wait)):
+ # We only need it to exist.
+ myfd = os.open(myhardlock, os.O_CREAT|os.O_RDWR,0660)
+ os.close(myfd)
+
+ if not os.path.exists(myhardlock):
+ raise portage_exception.FileNotFound, _("Created lockfile is missing: %(filename)s") % {"filename":myhardlock}
+
+ try:
+ res = os.link(myhardlock, lockfilename)
+ except SystemExit, e:
+ raise
+ except Exception, e:
+ #print "lockfile(): Hardlink: Link failed."
+ #print "Exception: ",e
+ pass
+
+ if hardlink_is_mine(myhardlock, lockfilename):
+ # We have the lock.
+ if reported_waiting:
+ print
+ return True
+
+ if reported_waiting:
+ portage_util.writemsg(".")
+ else:
+ reported_waiting = True
+ print
+ print "Waiting on (hardlink) lockfile: (one '.' per 3 seconds)"
+ print "This is a feature to prevent distfiles corruption."
+ print "/usr/lib/portage/bin/clean_locks can fix stuck locks."
+ print "Lockfile: " + lockfilename
+ time.sleep(3)
+
+ os.unlink(myhardlock)
+ return False
+
+def unhardlink_lockfile(lockfilename):
+ myhardlock = hardlock_name(lockfilename)
+ try:
+ if os.path.exists(myhardlock):
+ os.unlink(myhardlock)
+ if os.path.exists(lockfilename):
+ os.unlink(lockfilename)
+ except SystemExit, e:
+ raise
+ except:
+ portage_util.writemsg("Something strange happened to our hardlink locks.\n")
+
+def hardlock_cleanup(path, remove_all_locks=False):
+ mypid = str(os.getpid())
+ myhost = os.uname()[1]
+ mydl = os.listdir(path)
+
+ results = []
+ mycount = 0
+
+ mylist = {}
+ for x in mydl:
+ if os.path.isfile(path+"/"+x):
+ parts = string.split(x, ".hardlock-")
+ if len(parts) == 2:
+ filename = parts[0]
+ hostpid = string.split(parts[1],"-")
+ host = string.join(hostpid[:-1], "-")
+ pid = hostpid[-1]
+
+ if not mylist.has_key(filename):
+ mylist[filename] = {}
+ if not mylist[filename].has_key(host):
+ mylist[filename][host] = []
+ mylist[filename][host].append(pid)
+
+ mycount += 1
+
+
+ results.append("Found %(count)s locks" % {"count":mycount})
+
+ for x in mylist.keys():
+ if mylist[x].has_key(myhost) or remove_all_locks:
+ mylockname = hardlock_name(path+"/"+x)
+ if hardlink_is_mine(mylockname, path+"/"+x) or \
+ not os.path.exists(path+"/"+x) or \
+ remove_all_locks:
+ for y in mylist[x].keys():
+ for z in mylist[x][y]:
+ filename = path+"/"+x+".hardlock-"+y+"-"+z
+ if filename == mylockname:
+ continue
+ try:
+ # We're sweeping through, unlinking everyone's locks.
+ os.unlink(filename)
+ results.append(_("Unlinked: ") + filename)
+ except SystemExit, e:
+ raise
+ except Exception,e:
+ pass
+ try:
+ os.unlink(path+"/"+x)
+ results.append(_("Unlinked: ") + path+"/"+x)
+ os.unlink(mylockname)
+ results.append(_("Unlinked: ") + mylockname)
+ except SystemExit, e:
+ raise
+ except Exception,e:
+ pass
+ else:
+ try:
+ os.unlink(mylockname)
+ results.append(_("Unlinked: ") + mylockname)
+ except SystemExit, e:
+ raise
+ except Exception,e:
+ pass
+
+ return results
+