8 from catalyst.support import *
11 sys.stderr.write(mystr)
15 locking_method=fcntl.flock
17 die_on_failed_lock=True
19 self.clean_my_hardlocks()
20 self.delete_lock_from_path_list()
24 def __init__(self,lockdir):
28 self.locking_method=LockDir.locking_method
29 self.set_lockdir(lockdir)
30 self.set_lockfilename(".catalyst_lock")
33 if LockDir.lock_dirs_in_use.count(lockdir)>0:
34 raise "This directory already associated with a lock object"
36 LockDir.lock_dirs_in_use.append(lockdir)
38 self.hardlock_paths={}
40 def delete_lock_from_path_list(self):
43 if LockDir.lock_dirs_in_use:
44 for x in LockDir.lock_dirs_in_use:
45 if LockDir.lock_dirs_in_use[i] == self.lockdir:
46 del LockDir.lock_dirs_in_use[i]
49 except AttributeError:
58 def set_gid(self,gid):
59 if not self.islocked():
60 # if "DEBUG" in self.settings:
61 # print "setting gid to", gid
64 def set_lockdir(self,lockdir):
65 if not os.path.exists(lockdir):
67 if os.path.isdir(lockdir):
68 if not self.islocked():
69 if lockdir[-1] == "/":
71 self.lockdir=normpath(lockdir)
72 # if "DEBUG" in self.settings:
73 # print "setting lockdir to", self.lockdir
75 raise "the lock object needs a path to a dir"
77 def set_lockfilename(self,lockfilename):
78 if not self.islocked():
79 self.lockfilename=lockfilename
80 # if "DEBUG" in self.settings:
81 # print "setting lockfilename to", self.lockfilename
83 def set_lockfile(self):
84 if not self.islocked():
85 self.lockfile=normpath(self.lockdir+'/'+self.lockfilename)
86 # if "DEBUG" in self.settings:
87 # print "setting lockfile to", self.lockfile
90 if not self.locking_method == "HARDLOCK":
91 self.fcntl_lock("read")
93 print "HARDLOCKING doesnt support shared-read locks"
94 print "using exclusive write locks"
98 if not self.locking_method == "HARDLOCK":
99 self.fcntl_lock("write")
104 if not self.locking_method == "HARDLOCK":
109 def fcntl_lock(self,locktype):
111 if not os.path.exists(os.path.dirname(self.lockdir)):
112 raise DirectoryNotFound, os.path.dirname(self.lockdir)
113 if not os.path.exists(self.lockfile):
114 old_mask=os.umask(000)
115 self.myfd = os.open(self.lockfile, os.O_CREAT|os.O_RDWR,0660)
117 if os.stat(self.lockfile).st_gid != self.gid:
118 os.chown(self.lockfile,os.getuid(),self.gid)
119 except SystemExit, e:
122 if e[0] == 2: #XXX: No such file or directory
123 return self.fcntl_locking(locktype)
125 writemsg("Cannot chown a lockfile. This could cause inconvenience later.\n")
129 self.myfd = os.open(self.lockfile, os.O_CREAT|os.O_RDWR,0660)
132 if locktype == "read":
133 self.locking_method(self.myfd,fcntl.LOCK_SH|fcntl.LOCK_NB)
135 self.locking_method(self.myfd,fcntl.LOCK_EX|fcntl.LOCK_NB)
137 if "errno" not in dir(e):
139 if e.errno == errno.EAGAIN:
140 if not LockDir.die_on_failed_lock:
141 # Resource temp unavailable; eg, someone beat us to the lock.
142 writemsg("waiting for lock on %s\n" % self.lockfile)
144 # Try for the exclusive or shared lock again.
145 if locktype == "read":
146 self.locking_method(self.myfd,fcntl.LOCK_SH)
148 self.locking_method(self.myfd,fcntl.LOCK_EX)
150 raise LockInUse,self.lockfile
151 elif e.errno == errno.ENOLCK:
155 if not os.path.exists(self.lockfile):
158 #writemsg("lockfile recurse\n")
159 self.fcntl_lock(locktype)
162 #writemsg("Lockfile obtained\n")
164 def fcntl_unlock(self):
167 if not os.path.exists(self.lockfile):
168 print "lockfile does not exist '%s'" % self.lockfile
169 if (self.myfd != None):
178 if self.myfd == None:
179 self.myfd = os.open(self.lockfile, os.O_WRONLY,0660)
181 self.locking_method(self.myfd,fcntl.LOCK_UN)
182 except SystemExit, e:
187 raise IOError, "Failed to unlock file '%s'\n" % self.lockfile
189 # This sleep call was added to allow other processes that are
190 # waiting for a lock to be able to grab it before it is deleted.
191 # lockfile() already accounts for this situation, however, and
192 # the sleep here adds more time than is saved overall, so am
193 # commenting until it is proved necessary.
198 self.locking_method(self.myfd,fcntl.LOCK_EX|fcntl.LOCK_NB)
200 print "Read lock may be in effect. skipping lockfile delete..."
202 # We won the lock, so there isn't competition for it.
203 # We can safely delete the file.
204 #writemsg("Got the lockfile...\n")
205 #writemsg("Unlinking...\n")
206 self.locking_method(self.myfd,fcntl.LOCK_UN)
208 os.unlink(self.lockfile)
211 # if "DEBUG" in self.settings:
212 # print "Unlinked lockfile..."
213 except SystemExit, e:
216 # We really don't care... Someone else has the lock.
217 # So it is their problem now.
218 print "Failed to get lock... someone took it."
221 # Why test lockfilename? Because we may have been handed an
222 # fd originally, and the caller might not like having their
223 # open fd closed automatically on them.
224 #if type(lockfilename) == types.StringType:
227 if (self.myfd != None):
233 def hard_lock(self,max_wait=14400):
234 """Does the NFS, hardlink shuffle to ensure locking on the disk.
235 We create a PRIVATE lockfile, that is just a placeholder on the disk.
236 Then we HARDLINK the real lockfile to that private file.
237 If our file can 2 references, then we have the lock. :)
238 Otherwise we lather, rise, and repeat.
239 We default to a 4 hour timeout.
242 self.myhardlock = self.hardlock_name(self.lockdir)
244 start_time = time.time()
245 reported_waiting = False
247 while(time.time() < (start_time + max_wait)):
248 # We only need it to exist.
249 self.myfd = os.open(self.myhardlock, os.O_CREAT|os.O_RDWR,0660)
252 self.add_hardlock_file_to_cleanup()
253 if not os.path.exists(self.myhardlock):
254 raise FileNotFound, "Created lockfile is missing: %(filename)s" % {"filename":self.myhardlock}
256 res = os.link(self.myhardlock, self.lockfile)
257 except SystemExit, e:
260 # if "DEBUG" in self.settings:
261 # print "lockfile(): Hardlink: Link failed."
262 # print "Exception: ",e
265 if self.hardlink_is_mine(self.myhardlock, self.lockfile):
274 reported_waiting = True
276 print "Waiting on (hardlink) lockfile: (one '.' per 3 seconds)"
277 print "Lockfile: " + self.lockfile
280 os.unlink(self.myhardlock)
283 def hard_unlock(self):
285 if os.path.exists(self.myhardlock):
286 os.unlink(self.myhardlock)
287 if os.path.exists(self.lockfile):
288 os.unlink(self.lockfile)
289 except SystemExit, e:
292 writemsg("Something strange happened to our hardlink locks.\n")
294 def add_hardlock_file_to_cleanup(self):
295 #mypath = self.normpath(path)
296 if os.path.isdir(self.lockdir) and os.path.isfile(self.myhardlock):
297 self.hardlock_paths[self.lockdir]=self.myhardlock
299 def remove_hardlock_file_from_cleanup(self):
300 if self.lockdir in self.hardlock_paths:
301 del self.hardlock_paths[self.lockdir]
302 print self.hardlock_paths
304 def hardlock_name(self, path):
305 mypath=path+"/.hardlock-"+os.uname()[1]+"-"+str(os.getpid())
306 newpath = os.path.normpath(mypath)
308 if newpath[1] == "/":
309 newpath = "/"+newpath.lstrip("/")
312 def hardlink_is_mine(self,link,lock):
315 myhls = os.stat(link)
316 mylfs = os.stat(lock)
317 except SystemExit, e:
324 if myhls[stat.ST_NLINK] == 2:
327 if mylfs[stat.ST_INO] == myhls[stat.ST_INO]:
331 def hardlink_active(lock):
332 if not os.path.exists(lock):
335 def clean_my_hardlocks(self):
337 for x in self.hardlock_paths.keys():
338 self.hardlock_cleanup(x)
339 except AttributeError:
342 def hardlock_cleanup(self,path):
343 mypid = str(os.getpid())
344 myhost = os.uname()[1]
345 mydl = os.listdir(path)
352 if os.path.isfile(filepath):
353 parts = filepath.split(".hardlock-")
356 hostpid = parts[1].split("-")
357 host = "-".join(hostpid[:-1])
359 if filename not in mylist:
360 mylist[filename] = {}
362 if host not in mylist[filename]:
363 mylist[filename][host] = []
364 mylist[filename][host].append(pid)
367 mylist[filename][host].append(pid)
371 results.append("Found %(count)s locks" % {"count":mycount})
372 for x in mylist.keys():
373 if myhost in mylist[x]:
374 mylockname = self.hardlock_name(x)
375 if self.hardlink_is_mine(mylockname, self.lockfile) or \
376 not os.path.exists(self.lockfile):
377 for y in mylist[x].keys():
378 for z in mylist[x][y]:
379 filename = x+".hardlock-"+y+"-"+z
380 if filename == mylockname:
384 # We're sweeping through, unlinking everyone's locks.
386 results.append("Unlinked: " + filename)
387 except SystemExit, e:
393 results.append("Unlinked: " + x)
394 os.unlink(mylockname)
395 results.append("Unlinked: " + mylockname)
396 except SystemExit, e:
402 os.unlink(mylockname)
403 results.append("Unlinked: " + mylockname)
404 except SystemExit, e:
410 if __name__ == "__main__":
418 def normpath(mypath):
419 newpath = os.path.normpath(mypath)
421 if newpath[1] == "/":
422 newpath = "/"+newpath.lstrip("/")
425 print "Lock 5 starting"
427 Lock1=LockDir("/tmp/lock_path")
429 print "Lock1 write lock"
437 print "Lock1 read lock"
445 print "Lock1 read lock"
448 print "Lock1 write lock"
456 print "Lock1 read lock"