/[gentoo-src]/portage/pym/portage_locks.py
Gentoo

Contents of /portage/pym/portage_locks.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.23 - (show annotations) (download) (as text)
Sat Feb 26 06:35:20 2005 UTC (9 years, 6 months ago) by jstubbs
Branch: MAIN
CVS Tags: HEAD
Branch point for: portage_2_1
Changes since 1.22: +3 -2 lines
File MIME type: text/x-python
Brought forward changes from portage_2_0

1 # portage: Lock management code
2 # Copyright 2004 Gentoo Foundation
3 # Distributed under the terms of the GNU General Public License v2
4 # $Header: /var/cvsroot/gentoo-src/portage/pym/portage_locks.py,v 1.22 2004/11/12 17:04:19 ferringb Exp $
5 cvs_id_string="$Id: portage_locks.py,v 1.18.2.2 2005/01/16 02:35:33 carpaski Exp $"[5:-2]
6
7 import atexit
8 import errno
9 import os
10 import stat
11 import string
12 import time
13 import types
14 import portage_exception
15 import portage_file
16 import portage_util
17 import portage_data
18 from portage_localization import _
19
20 HARDLINK_FD = -2
21
22 hardlock_path_list = []
23 def clean_my_hardlocks():
24 for x in hardlock_path_list:
25 hardlock_cleanup(x)
26 def add_hardlock_file_to_cleanup(path):
27 mypath = portage_file.normpath(path)
28 if os.path.isfile(mypath):
29 mypath = os.path.dirname(mypath)
30 if os.path.isdir(mypath):
31 hardlock_path_list = mypath[:]
32
33 atexit.register(clean_my_hardlocks)
34
35 def lockdir(mydir):
36 return lockfile(mydir,wantnewlockfile=1)
37 def unlockdir(mylock):
38 return unlockfile(mylock)
39
40 def lockfile(mypath,wantnewlockfile=0,unlinkfile=0,verbosity=0):
41 """Creates all dirs upto, the given dir. Creates a lockfile
42 for the given directory as the file: directoryname+'.portage_lockfile'."""
43 import fcntl
44
45 if not mypath:
46 raise portage_exception.InvalidData, "Empty path given"
47
48 if type(mypath) == types.StringType and mypath[-1] == '/':
49 mypath = mypath[:-1]
50
51 if type(mypath) == types.FileType:
52 mypath = mypath.fileno()
53 if type(mypath) == types.IntType:
54 lockfilename = mypath
55 wantnewlockfile = 0
56 unlinkfile = 0
57 elif wantnewlockfile:
58 lockfilename = mypath+".portage_lockfile"
59 unlinkfile = 1
60 else:
61 lockfilename = mypath
62
63 if type(mypath) == types.StringType:
64 if not os.path.exists(os.path.dirname(mypath)):
65 raise portage_exception.DirectoryNotFound, os.path.dirname(mypath)
66 if not os.path.exists(lockfilename):
67 old_mask=os.umask(000)
68 myfd = os.open(lockfilename, os.O_CREAT|os.O_RDWR,0660)
69 try:
70 if os.stat(lockfilename).st_gid != portage_data.portage_gid:
71 os.chown(lockfilename,os.getuid(),portage_data.portage_gid)
72 except SystemExit, e:
73 raise
74 except OSError, e:
75 if e[0] == 2: #XXX: No such file or directory
76 return lockfile(mypath,wantnewlockfile,unlinkfile)
77 else:
78 portage_util.writemsg("Cannot chown a lockfile. This could cause inconvenience later.\n",verbosity)
79 os.umask(old_mask)
80 else:
81 myfd = os.open(lockfilename, os.O_CREAT|os.O_RDWR,0660)
82
83 elif type(mypath) == types.IntType:
84 myfd = mypath
85
86 else:
87 raise ValueError, "Unknown type passed in '%s': '%s'" % (type(mypath),mypath)
88
89 # try for a non-blocking lock, if it's held, throw a message
90 # we're waiting on lockfile and use a blocking attempt.
91 locking_method = None
92 link_success=False
93 for locking_method in (fcntl.flock, fcntl.lockf):
94 try:
95 locking_method(myfd,fcntl.LOCK_EX|fcntl.LOCK_NB)
96 link_success=True
97 break
98 except IOError, e:
99 if "errno" not in dir(e):
100 raise
101 if e.errno == errno.EAGAIN:
102 # resource temp unavailable; eg, someone beat us to the lock.
103 if type(mypath) == types.IntType:
104 portage_util.writemsg("waiting for lock on fd %i\n" % myfd,verbosity)
105 else:
106 portage_util.writemsg("waiting for lock on %s\n" % lockfilename,verbosity)
107 # try for the exclusive lock now.
108 locking_method(myfd,fcntl.LOCK_EX)
109 elif e.errno == errno.ENOLCK:
110 pass
111 else:
112 raise
113
114
115 if not link_success:
116 # We're not allowed to lock on this FS.
117 os.close(myfd)
118 link_success = False
119 if lockfilename == str(lockfilename):
120 if wantnewlockfile:
121 try:
122 if os.stat(lockfilename)[stat.ST_NLINK] == 1:
123 os.unlink(lockfilename)
124 except Exception, e:
125 pass
126 link_success = hardlink_lockfile(lockfilename)
127 if not link_success:
128 raise
129 locking_method = None
130 myfd = HARDLINK_FD
131
132
133
134 if type(lockfilename) == types.StringType and not os.path.exists(lockfilename):
135 # The file was deleted on us... Keep trying to make one...
136 os.close(myfd)
137 portage_util.writemsg("lockfile recurse\n",verbosity+1)
138 lockfilename,myfd,unlinkfile,locking_method,verbosity = lockfile(mypath,wantnewlockfile,unlinkfile,verbosity)
139
140 portage_util.writemsg(str((lockfilename,myfd,unlinkfile))+"\n",verbosity+1)
141 return (lockfilename,myfd,unlinkfile,locking_method,verbosity)
142
143 def unlockfile(mytuple):
144 import fcntl
145
146 #XXX: Compatability hack.
147 if len(mytuple) == 3:
148 lockfilename,myfd,unlinkfile = mytuple
149 locking_method = fcntl.flock
150 verbosity=0
151 elif len(mytuple) == 4:
152 lockfilename,myfd,unlinkfile,locking_method = mytuple
153 verbosity=0
154 elif len(mytuple) == 5:
155 lockfilename,myfd,unlinkfile,locking_method,verbosity = mytuple
156 else:
157 raise
158
159 if(myfd == HARDLINK_FD):
160 unhardlink_lockfile(lockfilename)
161 return True
162
163 if type(lockfilename) == types.StringType and not os.path.exists(lockfilename):
164 portage_util.writemsg("lockfile does not exist '%s'\n" % lockfilename,1)
165 if (myfd != None) and type(lockfilename) == types.StringType:
166 os.close(myfd)
167 return False
168
169 try:
170 if myfd == None:
171 myfd = os.open(lockfilename, os.O_WRONLY,0660)
172 unlinkfile = 1
173 locking_method(myfd,fcntl.LOCK_UN)
174 except SystemExit, e:
175 raise
176 except Exception, e:
177 if type(lockfilename) == types.StringType:
178 os.close(myfd)
179 raise IOError, "Failed to unlock file '%s'\n" % lockfilename
180
181 try:
182 # This sleep call was added to allow other processes that are
183 # waiting for a lock to be able to grab it before it is deleted.
184 # lockfile() already accounts for this situation, however, and
185 # the sleep here adds more time than is saved overall, so am
186 # commenting until it is proved necessary.
187 #time.sleep(0.0001)
188 if unlinkfile:
189 locking_method(myfd,fcntl.LOCK_EX|fcntl.LOCK_NB)
190 # We won the lock, so there isn't competition for it.
191 # We can safely delete the file.
192 portage_util.writemsg("Got the lockfile...\n",1)
193 #portage_util.writemsg("Unlinking...\n")
194 os.unlink(lockfilename)
195 portage_util.writemsg("Unlinked lockfile...\n",1)
196 locking_method(myfd,fcntl.LOCK_UN)
197 except SystemExit, e:
198 raise
199 except Exception, e:
200 # We really don't care... Someone else has the lock.
201 # So it is their problem now.
202 portage_util.writemsg("Failed to get lock... someone took it.\n",1)
203 portage_util.writemsg(str(e)+"\n",1)
204
205 # why test lockfilename? because we may have been handed an
206 # fd originally, and the caller might not like having their
207 # open fd closed automatically on them.
208 if type(lockfilename) == types.StringType:
209 os.close(myfd)
210
211 return True
212
213
214
215
216 def hardlock_name(path):
217 return path+".hardlock-"+os.uname()[1]+"-"+str(os.getpid())
218
219 def hardlink_active(lock):
220 if not os.path.exists(lock):
221 return False
222 # XXXXXXXXXXXXXXXXXXXXXXXXXX
223
224 def hardlink_is_mine(link,lock):
225 try:
226 myhls = os.stat(link)
227 mylfs = os.stat(lock)
228 except SystemExit, e:
229 raise
230 except:
231 myhls = None
232 mylfs = None
233
234 if myhls:
235 if myhls[stat.ST_NLINK] == 2:
236 return True
237 if mylfs:
238 if mylfs[stat.ST_INO] == myhls[stat.ST_INO]:
239 return True
240 return False
241
242 def hardlink_lockfile(lockfilename, max_wait=14400):
243 """Does the NFS, hardlink shuffle to ensure locking on the disk.
244 We create a PRIVATE lockfile, that is just a placeholder on the disk.
245 Then we HARDLINK the real lockfile to that private file.
246 If our file can 2 references, then we have the lock. :)
247 Otherwise we lather, rise, and repeat.
248 We default to a 4 hour timeout.
249 """
250
251 add_hardlock_file_to_cleanup(lockfilename)
252
253 start_time = time.time()
254 myhardlock = hardlock_name(lockfilename)
255 reported_waiting = False
256
257 while(time.time() < (start_time + max_wait)):
258 # We only need it to exist.
259 myfd = os.open(myhardlock, os.O_CREAT|os.O_RDWR,0660)
260 os.close(myfd)
261
262 if not os.path.exists(myhardlock):
263 raise portage_exception.FileNotFound, _("Created lockfile is missing: %(filename)s") % {"filename":myhardlock}
264
265 try:
266 res = os.link(myhardlock, lockfilename)
267 except SystemExit, e:
268 raise
269 except Exception, e:
270 #print "lockfile(): Hardlink: Link failed."
271 #print "Exception: ",e
272 pass
273
274 if hardlink_is_mine(myhardlock, lockfilename):
275 # We have the lock.
276 if reported_waiting:
277 print
278 return True
279
280 if reported_waiting:
281 portage_util.writemsg(".")
282 else:
283 reported_waiting = True
284 print
285 print "Waiting on (hardlink) lockfile: (one '.' per 3 seconds)"
286 print "This is a feature to prevent distfiles corruption."
287 print "/usr/lib/portage/bin/clean_locks can fix stuck locks."
288 print "Lockfile: " + lockfilename
289 time.sleep(0.1)
290
291 os.unlink(myhardlock)
292 return False
293
294 def unhardlink_lockfile(lockfilename):
295 myhardlock = hardlock_name(lockfilename)
296 try:
297 if os.path.exists(myhardlock):
298 os.unlink(myhardlock)
299 if os.path.exists(lockfilename):
300 os.unlink(lockfilename)
301 except SystemExit, e:
302 raise
303 except:
304 portage_util.writemsg("Something strange happened to our hardlink locks.\n")
305
306 def hardlock_cleanup(path, remove_all_locks=False):
307 mypid = str(os.getpid())
308 myhost = os.uname()[1]
309 mydl = os.listdir(path)
310
311 results = []
312 mycount = 0
313
314 mylist = {}
315 for x in mydl:
316 if os.path.isfile(path+"/"+x):
317 parts = string.split(x, ".hardlock-")
318 if len(parts) == 2:
319 filename = parts[0]
320 hostpid = string.split(parts[1],"-")
321 host = string.join(hostpid[:-1], "-")
322 pid = hostpid[-1]
323
324 if not mylist.has_key(filename):
325 mylist[filename] = {}
326 if not mylist[filename].has_key(host):
327 mylist[filename][host] = []
328 mylist[filename][host].append(pid)
329
330 mycount += 1
331
332
333 results.append("Found %(count)s locks" % {"count":mycount})
334
335 for x in mylist.keys():
336 if mylist[x].has_key(myhost) or remove_all_locks:
337 mylockname = hardlock_name(path+"/"+x)
338 if hardlink_is_mine(mylockname, path+"/"+x) or \
339 not os.path.exists(path+"/"+x) or \
340 remove_all_locks:
341 for y in mylist[x].keys():
342 for z in mylist[x][y]:
343 filename = path+"/"+x+".hardlock-"+y+"-"+z
344 if filename == mylockname:
345 continue
346 try:
347 # We're sweeping through, unlinking everyone's locks.
348 os.unlink(filename)
349 results.append(_("Unlinked: ") + filename)
350 except SystemExit, e:
351 raise
352 except Exception,e:
353 pass
354 try:
355 os.unlink(path+"/"+x)
356 results.append(_("Unlinked: ") + path+"/"+x)
357 os.unlink(mylockname)
358 results.append(_("Unlinked: ") + mylockname)
359 except SystemExit, e:
360 raise
361 except Exception,e:
362 pass
363 else:
364 try:
365 os.unlink(mylockname)
366 results.append(_("Unlinked: ") + mylockname)
367 except SystemExit, e:
368 raise
369 except Exception,e:
370 pass
371
372 return results

  ViewVC Help
Powered by ViewVC 1.1.20