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()
23 def __init__(self,lockdir):
27 self.locking_method=LockDir.locking_method
28 self.set_lockdir(lockdir)
29 self.set_lockfilename(".catalyst_lock")
32 if LockDir.lock_dirs_in_use.count(lockdir)>0:
33 raise "This directory already associated with a lock object"
35 LockDir.lock_dirs_in_use.append(lockdir)
37 self.hardlock_paths={}
41 def delete_lock_from_path_list(self):
44 if LockDir.lock_dirs_in_use:
45 for x in LockDir.lock_dirs_in_use:
46 if LockDir.lock_dirs_in_use[i] == self.lockdir:
47 del LockDir.lock_dirs_in_use[i]
50 except AttributeError:
59 def set_gid(self,gid):
60 if not self.islocked():
61 # if self.settings.has_key("DEBUG"):
62 # print "setting gid to", gid
65 def set_lockdir(self,lockdir):
66 if not os.path.exists(lockdir):
68 if os.path.isdir(lockdir):
69 if not self.islocked():
70 if lockdir[-1] == "/":
72 self.lockdir=normpath(lockdir)
73 # if self.settings.has_key("DEBUG"):
74 # print "setting lockdir to", self.lockdir
76 raise "the lock object needs a path to a dir"
78 def set_lockfilename(self,lockfilename):
79 if not self.islocked():
80 self.lockfilename=lockfilename
81 # if self.settings.has_key("DEBUG"):
82 # print "setting lockfilename to", self.lockfilename
84 def set_lockfile(self):
85 if not self.islocked():
86 self.lockfile=normpath(self.lockdir+'/'+self.lockfilename)
87 # if self.settings.has_key("DEBUG"):
88 # print "setting lockfile to", self.lockfile
91 if not self.locking_method == "HARDLOCK":
92 self.fcntl_lock("read")
94 print "HARDLOCKING doesnt support shared-read locks"
95 print "using exclusive write locks"
99 if not self.locking_method == "HARDLOCK":
100 self.fcntl_lock("write")
105 if not self.locking_method == "HARDLOCK":
110 def fcntl_lock(self,locktype):
112 if not os.path.exists(os.path.dirname(self.lockdir)):
113 raise DirectoryNotFound, os.path.dirname(self.lockdir)
114 if not os.path.exists(self.lockfile):
115 old_mask=os.umask(000)
116 self.myfd = os.open(self.lockfile, os.O_CREAT|os.O_RDWR,0660)
118 if os.stat(self.lockfile).st_gid != self.gid:
119 os.chown(self.lockfile,os.getuid(),self.gid)
120 except SystemExit, e:
123 if e[0] == 2: #XXX: No such file or directory
124 return self.fcntl_locking(locktype)
126 writemsg("Cannot chown a lockfile. This could cause inconvenience later.\n")
130 self.myfd = os.open(self.lockfile, os.O_CREAT|os.O_RDWR,0660)
133 if locktype == "read":
134 self.locking_method(self.myfd,fcntl.LOCK_SH|fcntl.LOCK_NB)
136 self.locking_method(self.myfd,fcntl.LOCK_EX|fcntl.LOCK_NB)
138 if "errno" not in dir(e):
140 if e.errno == errno.EAGAIN:
141 if not LockDir.die_on_failed_lock:
142 # Resource temp unavailable; eg, someone beat us to the lock.
143 writemsg("waiting for lock on %s\n" % self.lockfile)
145 # Try for the exclusive or shared lock again.
146 if locktype == "read":
147 self.locking_method(self.myfd,fcntl.LOCK_SH)
149 self.locking_method(self.myfd,fcntl.LOCK_EX)
151 raise LockInUse,self.lockfile
152 elif e.errno == errno.ENOLCK:
156 if not os.path.exists(self.lockfile):
159 #writemsg("lockfile recurse\n")
160 self.fcntl_lock(locktype)
163 #writemsg("Lockfile obtained\n")
166 def fcntl_unlock(self):
169 if not os.path.exists(self.lockfile):
170 print "lockfile does not exist '%s'" % self.lockfile
171 if (self.myfd != None):
180 if self.myfd == None:
181 self.myfd = os.open(self.lockfile, os.O_WRONLY,0660)
183 self.locking_method(self.myfd,fcntl.LOCK_UN)
184 except SystemExit, e:
189 raise IOError, "Failed to unlock file '%s'\n" % self.lockfile
191 # This sleep call was added to allow other processes that are
192 # waiting for a lock to be able to grab it before it is deleted.
193 # lockfile() already accounts for this situation, however, and
194 # the sleep here adds more time than is saved overall, so am
195 # commenting until it is proved necessary.
200 self.locking_method(self.myfd,fcntl.LOCK_EX|fcntl.LOCK_NB)
202 print "Read lock may be in effect. skipping lockfile delete..."
204 # We won the lock, so there isn't competition for it.
205 # We can safely delete the file.
206 #writemsg("Got the lockfile...\n")
207 #writemsg("Unlinking...\n")
208 self.locking_method(self.myfd,fcntl.LOCK_UN)
210 os.unlink(self.lockfile)
213 # if self.settings.has_key("DEBUG"):
214 # print "Unlinked lockfile..."
215 except SystemExit, e:
218 # We really don't care... Someone else has the lock.
219 # So it is their problem now.
220 print "Failed to get lock... someone took it."
223 # Why test lockfilename? Because we may have been handed an
224 # fd originally, and the caller might not like having their
225 # open fd closed automatically on them.
226 #if type(lockfilename) == types.StringType:
229 if (self.myfd != None):
235 def hard_lock(self,max_wait=14400):
236 """Does the NFS, hardlink shuffle to ensure locking on the disk.
237 We create a PRIVATE lockfile, that is just a placeholder on the disk.
238 Then we HARDLINK the real lockfile to that private file.
239 If our file can 2 references, then we have the lock. :)
240 Otherwise we lather, rise, and repeat.
241 We default to a 4 hour timeout.
244 self.myhardlock = self.hardlock_name(self.lockdir)
246 start_time = time.time()
247 reported_waiting = False
249 while(time.time() < (start_time + max_wait)):
250 # We only need it to exist.
251 self.myfd = os.open(self.myhardlock, os.O_CREAT|os.O_RDWR,0660)
254 self.add_hardlock_file_to_cleanup()
255 if not os.path.exists(self.myhardlock):
256 raise FileNotFound, "Created lockfile is missing: %(filename)s" % {"filename":self.myhardlock}
258 res = os.link(self.myhardlock, self.lockfile)
259 except SystemExit, e:
262 # if self.settings.has_key("DEBUG"):
263 # print "lockfile(): Hardlink: Link failed."
264 # print "Exception: ",e
267 if self.hardlink_is_mine(self.myhardlock, self.lockfile):
276 reported_waiting = True
278 print "Waiting on (hardlink) lockfile: (one '.' per 3 seconds)"
279 print "Lockfile: " + self.lockfile
282 os.unlink(self.myhardlock)
285 def hard_unlock(self):
287 if os.path.exists(self.myhardlock):
288 os.unlink(self.myhardlock)
289 if os.path.exists(self.lockfile):
290 os.unlink(self.lockfile)
291 except SystemExit, e:
294 writemsg("Something strange happened to our hardlink locks.\n")
296 def add_hardlock_file_to_cleanup(self):
297 #mypath = self.normpath(path)
298 if os.path.isdir(self.lockdir) and os.path.isfile(self.myhardlock):
299 self.hardlock_paths[self.lockdir]=self.myhardlock
301 def remove_hardlock_file_from_cleanup(self):
302 if self.hardlock_paths.has_key(self.lockdir):
303 del self.hardlock_paths[self.lockdir]
304 print self.hardlock_paths
306 def hardlock_name(self, path):
307 mypath=path+"/.hardlock-"+os.uname()[1]+"-"+str(os.getpid())
308 newpath = os.path.normpath(mypath)
310 if newpath[1] == "/":
311 newpath = "/"+newpath.lstrip("/")
315 def hardlink_is_mine(self,link,lock):
318 myhls = os.stat(link)
319 mylfs = os.stat(lock)
320 except SystemExit, e:
327 if myhls[stat.ST_NLINK] == 2:
330 if mylfs[stat.ST_INO] == myhls[stat.ST_INO]:
334 def hardlink_active(lock):
335 if not os.path.exists(lock):
338 def clean_my_hardlocks(self):
340 for x in self.hardlock_paths.keys():
341 self.hardlock_cleanup(x)
342 except AttributeError:
345 def hardlock_cleanup(self,path):
346 mypid = str(os.getpid())
347 myhost = os.uname()[1]
348 mydl = os.listdir(path)
355 if os.path.isfile(filepath):
356 parts = filepath.split(".hardlock-")
359 hostpid = parts[1].split("-")
360 host = "-".join(hostpid[:-1])
362 if not mylist.has_key(filename):
363 mylist[filename] = {}
365 if not mylist[filename].has_key(host):
366 mylist[filename][host] = []
367 mylist[filename][host].append(pid)
370 mylist[filename][host].append(pid)
374 results.append("Found %(count)s locks" % {"count":mycount})
375 for x in mylist.keys():
376 if mylist[x].has_key(myhost):
377 mylockname = self.hardlock_name(x)
378 if self.hardlink_is_mine(mylockname, self.lockfile) or \
379 not os.path.exists(self.lockfile):
380 for y in mylist[x].keys():
381 for z in mylist[x][y]:
382 filename = x+".hardlock-"+y+"-"+z
383 if filename == mylockname:
387 # We're sweeping through, unlinking everyone's locks.
389 results.append("Unlinked: " + filename)
390 except SystemExit, e:
396 results.append("Unlinked: " + x)
397 os.unlink(mylockname)
398 results.append("Unlinked: " + mylockname)
399 except SystemExit, e:
405 os.unlink(mylockname)
406 results.append("Unlinked: " + mylockname)
407 except SystemExit, e:
414 if __name__ == "__main__":
422 def normpath(mypath):
423 newpath = os.path.normpath(mypath)
425 if newpath[1] == "/":
426 newpath = "/"+newpath.lstrip("/")
429 print "Lock 5 starting"
431 Lock1=LockDir("/tmp/lock_path")
433 print "Lock1 write lock"
441 print "Lock1 read lock"
449 print "Lock1 read lock"
452 print "Lock1 write lock"
460 print "Lock1 read lock"