11 from catalyst.support import CatalystError, normpath
15 sys.stderr.write(mystr)
19 class LockInUse(Exception):
20 def __init__(self, message):
22 #(type,value)=sys.exc_info()[:2]
25 #kprint traceback.print_exc(file=sys.stdout)
27 print "!!! catalyst lock file in use: "+message
32 locking_method=fcntl.flock
34 die_on_failed_lock=True
36 self.clean_my_hardlocks()
37 self.delete_lock_from_path_list()
41 def __init__(self,lockdir):
45 self.locking_method=LockDir.locking_method
46 self.set_lockdir(lockdir)
47 self.set_lockfilename(".catalyst_lock")
50 if LockDir.lock_dirs_in_use.count(lockdir)>0:
51 raise "This directory already associated with a lock object"
53 LockDir.lock_dirs_in_use.append(lockdir)
55 self.hardlock_paths={}
57 def delete_lock_from_path_list(self):
60 if LockDir.lock_dirs_in_use:
61 for x in LockDir.lock_dirs_in_use:
62 if LockDir.lock_dirs_in_use[i] == self.lockdir:
63 del LockDir.lock_dirs_in_use[i]
66 except AttributeError:
75 def set_gid(self,gid):
76 if not self.islocked():
77 # if "DEBUG" in self.settings:
78 # print "setting gid to", gid
81 def set_lockdir(self,lockdir):
82 if not os.path.exists(lockdir):
84 if os.path.isdir(lockdir):
85 if not self.islocked():
86 if lockdir[-1] == "/":
88 self.lockdir = normpath(lockdir)
89 # if "DEBUG" in self.settings:
90 # print "setting lockdir to", self.lockdir
92 raise "the lock object needs a path to a dir"
94 def set_lockfilename(self,lockfilename):
95 if not self.islocked():
96 self.lockfilename=lockfilename
97 # if "DEBUG" in self.settings:
98 # print "setting lockfilename to", self.lockfilename
100 def set_lockfile(self):
101 if not self.islocked():
102 self.lockfile = normpath(self.lockdir+'/'+self.lockfilename)
103 # if "DEBUG" in self.settings:
104 # print "setting lockfile to", self.lockfile
107 if not self.locking_method == "HARDLOCK":
108 self.fcntl_lock("read")
110 print "HARDLOCKING doesnt support shared-read locks"
111 print "using exclusive write locks"
114 def write_lock(self):
115 if not self.locking_method == "HARDLOCK":
116 self.fcntl_lock("write")
121 if not self.locking_method == "HARDLOCK":
126 def fcntl_lock(self,locktype):
128 if not os.path.exists(os.path.dirname(self.lockdir)):
129 raise CatalystError("DirectoryNotFound: %s"
130 % os.path.dirname(self.lockdir), print_traceback=True)
131 if not os.path.exists(self.lockfile):
132 old_mask=os.umask(000)
133 self.myfd = os.open(self.lockfile, os.O_CREAT|os.O_RDWR,0660)
135 if os.stat(self.lockfile).st_gid != self.gid:
136 os.chown(self.lockfile,os.getuid(),self.gid)
137 except SystemExit, e:
140 if e[0] == 2: #XXX: No such file or directory
141 return self.fcntl_locking(locktype)
143 writemsg("Cannot chown a lockfile. This could cause inconvenience later.\n")
147 self.myfd = os.open(self.lockfile, os.O_CREAT|os.O_RDWR,0660)
150 if locktype == "read":
151 self.locking_method(self.myfd,fcntl.LOCK_SH|fcntl.LOCK_NB)
153 self.locking_method(self.myfd,fcntl.LOCK_EX|fcntl.LOCK_NB)
155 if "errno" not in dir(e):
157 if e.errno == errno.EAGAIN:
158 if not LockDir.die_on_failed_lock:
159 # Resource temp unavailable; eg, someone beat us to the lock.
160 writemsg("waiting for lock on %s\n" % self.lockfile)
162 # Try for the exclusive or shared lock again.
163 if locktype == "read":
164 self.locking_method(self.myfd,fcntl.LOCK_SH)
166 self.locking_method(self.myfd,fcntl.LOCK_EX)
168 raise LockInUse,self.lockfile
169 elif e.errno == errno.ENOLCK:
173 if not os.path.exists(self.lockfile):
176 #writemsg("lockfile recurse\n")
177 self.fcntl_lock(locktype)
180 #writemsg("Lockfile obtained\n")
182 def fcntl_unlock(self):
185 if not os.path.exists(self.lockfile):
186 print "lockfile does not exist '%s'" % self.lockfile
187 if (self.myfd != None):
196 if self.myfd == None:
197 self.myfd = os.open(self.lockfile, os.O_WRONLY,0660)
199 self.locking_method(self.myfd,fcntl.LOCK_UN)
200 except SystemExit, e:
205 raise IOError, "Failed to unlock file '%s'\n" % self.lockfile
207 # This sleep call was added to allow other processes that are
208 # waiting for a lock to be able to grab it before it is deleted.
209 # lockfile() already accounts for this situation, however, and
210 # the sleep here adds more time than is saved overall, so am
211 # commenting until it is proved necessary.
216 self.locking_method(self.myfd,fcntl.LOCK_EX|fcntl.LOCK_NB)
218 print "Read lock may be in effect. skipping lockfile delete..."
220 # We won the lock, so there isn't competition for it.
221 # We can safely delete the file.
222 #writemsg("Got the lockfile...\n")
223 #writemsg("Unlinking...\n")
224 self.locking_method(self.myfd,fcntl.LOCK_UN)
226 os.unlink(self.lockfile)
229 # if "DEBUG" in self.settings:
230 # print "Unlinked lockfile..."
231 except SystemExit, e:
234 # We really don't care... Someone else has the lock.
235 # So it is their problem now.
236 print "Failed to get lock... someone took it."
239 # Why test lockfilename? Because we may have been handed an
240 # fd originally, and the caller might not like having their
241 # open fd closed automatically on them.
242 #if type(lockfilename) == types.StringType:
245 if (self.myfd != None):
251 def hard_lock(self,max_wait=14400):
252 """Does the NFS, hardlink shuffle to ensure locking on the disk.
253 We create a PRIVATE lockfile, that is just a placeholder on the disk.
254 Then we HARDLINK the real lockfile to that private file.
255 If our file can 2 references, then we have the lock. :)
256 Otherwise we lather, rise, and repeat.
257 We default to a 4 hour timeout.
260 self.myhardlock = self.hardlock_name(self.lockdir)
262 start_time = time.time()
263 reported_waiting = False
265 while(time.time() < (start_time + max_wait)):
266 # We only need it to exist.
267 self.myfd = os.open(self.myhardlock, os.O_CREAT|os.O_RDWR,0660)
270 self.add_hardlock_file_to_cleanup()
271 if not os.path.exists(self.myhardlock):
272 raise CatalystError("FileNotFound: Created lockfile is missing: "
273 "%(filename)s" % {"filename":self.myhardlock},
274 print_traceback=True)
276 os.link(self.myhardlock, self.lockfile)
280 # if "DEBUG" in self.settings:
281 # print "lockfile(): Hardlink: Link failed."
282 # print "Exception: ",e
285 if self.hardlink_is_mine(self.myhardlock, self.lockfile):
294 reported_waiting = True
296 print "Waiting on (hardlink) lockfile: (one '.' per 3 seconds)"
297 print "Lockfile: " + self.lockfile
300 os.unlink(self.myhardlock)
303 def hard_unlock(self):
305 if os.path.exists(self.myhardlock):
306 os.unlink(self.myhardlock)
307 if os.path.exists(self.lockfile):
308 os.unlink(self.lockfile)
312 writemsg("Something strange happened to our hardlink locks.\n")
314 def add_hardlock_file_to_cleanup(self):
315 #mypath = self.normpath(path)
316 if os.path.isdir(self.lockdir) and os.path.isfile(self.myhardlock):
317 self.hardlock_paths[self.lockdir]=self.myhardlock
319 def remove_hardlock_file_from_cleanup(self):
320 if self.lockdir in self.hardlock_paths:
321 del self.hardlock_paths[self.lockdir]
322 print self.hardlock_paths
324 def hardlock_name(self, path):
325 mypath=path+"/.hardlock-"+os.uname()[1]+"-"+str(os.getpid())
326 newpath = os.path.normpath(mypath)
328 if newpath[1] == "/":
329 newpath = "/"+newpath.lstrip("/")
332 def hardlink_is_mine(self,link,lock):
335 myhls = os.stat(link)
336 mylfs = os.stat(lock)
344 if myhls[stat.ST_NLINK] == 2:
347 if mylfs[stat.ST_INO] == myhls[stat.ST_INO]:
351 def hardlink_active(lock):
352 if not os.path.exists(lock):
355 def clean_my_hardlocks(self):
357 for x in self.hardlock_paths.keys():
358 self.hardlock_cleanup(x)
359 except AttributeError:
362 def hardlock_cleanup(self,path):
363 #mypid = str(os.getpid())
364 myhost = os.uname()[1]
365 mydl = os.listdir(path)
372 if os.path.isfile(filepath):
373 parts = filepath.split(".hardlock-")
376 hostpid = parts[1].split("-")
377 host = "-".join(hostpid[:-1])
379 if filename not in mylist:
380 mylist[filename] = {}
382 if host not in mylist[filename]:
383 mylist[filename][host] = []
384 mylist[filename][host].append(pid)
387 mylist[filename][host].append(pid)
391 results.append("Found %(count)s locks" % {"count":mycount})
392 for x in mylist.keys():
393 if myhost in mylist[x]:
394 mylockname = self.hardlock_name(x)
395 if self.hardlink_is_mine(mylockname, self.lockfile) or \
396 not os.path.exists(self.lockfile):
397 for y in mylist[x].keys():
398 for z in mylist[x][y]:
399 filename = x+".hardlock-"+y+"-"+z
400 if filename == mylockname:
404 # We're sweeping through, unlinking everyone's locks.
406 results.append("Unlinked: " + filename)
413 results.append("Unlinked: " + x)
414 os.unlink(mylockname)
415 results.append("Unlinked: " + mylockname)
422 os.unlink(mylockname)
423 results.append("Unlinked: " + mylockname)
430 if __name__ == "__main__":
438 #def normpath(mypath):
439 #newpath = os.path.normpath(mypath)
440 #if len(newpath) > 1:
441 #if newpath[1] == "/":
442 #newpath = "/"+newpath.lstrip("/")
445 print "Lock 5 starting"
446 Lock1=LockDir("/tmp/lock_path")
448 print "Lock1 write lock"
456 print "Lock1 read lock"
464 print "Lock1 read lock"
467 print "Lock1 write lock"
475 print "Lock1 read lock"