1
# Copyright (C) 2006-2010 Canonical Ltd
1
# Copyright (C) 2006, 2007, 2008 Canonical Ltd
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
242
240
# incorrect. It's possible some other servers or filesystems will
243
241
# have a similar bug allowing someone to think they got the lock
244
242
# when it's already held.
246
# See <https://bugs.launchpad.net/bzr/+bug/498378> for one case.
248
# Strictly the check is unnecessary and a waste of time for most
249
# people, but probably worth trapping if something is wrong.
250
243
info = self.peek()
251
244
self._trace("after locking, info=%r", info)
253
raise LockFailed(self, "lock was renamed into place, but "
255
if info.get('nonce') != self.nonce:
245
if info['nonce'] != self.nonce:
256
246
self._trace("rename succeeded, "
257
247
"but lock is still held by someone else")
258
248
raise LockContention(self)
336
325
self._trace("... unlock succeeded after %dms",
337
326
(time.time() - start_time) * 1000)
338
327
result = lock.LockResult(self.transport.abspath(self.path),
340
329
for hook in self.hooks['lock_released']:
390
379
raise LockBreakMismatch(self, broken_info, dead_holder_info)
391
380
self.transport.delete(broken_info_path)
392
381
self.transport.rmdir(tmpname)
393
result = lock.LockResult(self.transport.abspath(self.path),
394
current_info.get('nonce'))
395
for hook in self.hooks['lock_broken']:
398
383
def _check_not_locked(self):
399
384
"""If the lock is held by this instance, raise an error."""
426
411
peek() reads the info file of the lock holder, if any.
428
return self._parse_info(self.transport.get_bytes(path))
413
return self._parse_info(self.transport.get(path))
431
416
"""Check if the lock is held by anyone.
433
If it is held, this returns the lock info structure as a dict
418
If it is held, this returns the lock info structure as a rio Stanza,
434
419
which contains some information about the current lock holder.
435
420
Otherwise returns None.
447
432
# XXX: is creating this here inefficient?
448
433
config = bzrlib.config.GlobalConfig()
435
user = config.user_email()
436
except errors.NoEmailInUsername:
450
437
user = config.username()
451
except errors.NoWhoami:
452
user = osutils.getuser_unicode()
453
438
s = rio.Stanza(hostname=get_host_name(),
454
439
pid=str(os.getpid()),
455
440
start_time=str(int(time.time())),
459
444
return s.to_string()
461
def _parse_info(self, info_bytes):
462
stanza = rio.read_stanza(osutils.split_lines(info_bytes))
464
# see bug 185013; we fairly often end up with the info file being
465
# empty after an interruption; we could log a message here but
466
# there may not be much we can say
469
return stanza.as_dict()
446
def _parse_info(self, info_file):
447
return rio.read_stanza(info_file.readlines()).as_dict()
471
449
def attempt_lock(self):
472
450
"""Take the lock; fail if it's already held.
539
517
if deadline_str is None:
540
518
deadline_str = time.strftime('%H:%M:%S',
541
519
time.localtime(deadline))
542
# As local lock urls are correct we display them.
543
# We avoid displaying remote lock urls.
544
520
lock_url = self.transport.abspath(self.path)
545
if lock_url.startswith('file://'):
546
lock_url = lock_url.split('.bzr/')[0]
549
user, hostname, pid, time_ago = formatted_info
550
msg = ('%s lock %s ' # lock_url
554
'[process #%s], ' # pid
555
'acquired %s.') # time ago
556
msg_args = [start, lock_url, user, hostname, pid, time_ago]
558
msg += ('\nWill continue to try until %s, unless '
560
msg_args.append(deadline_str)
561
msg += '\nSee "bzr help break-lock" for more.'
562
self._report_function(msg, *msg_args)
521
self._report_function('%s %s\n'
523
'%s\n' # locked ... ago
524
'Will continue to try until %s, unless '
526
'If you\'re sure that it\'s not being '
527
'modified, use bzr break-lock %s',
563
535
if (max_attempts is not None) and (attempt_count >= max_attempts):
564
536
self._trace("exceeded %d attempts")
565
537
raise LockContention(self)
567
539
self._trace("waiting %ss", poll)
570
# As timeout is always 0 for remote locks
571
# this block is applicable only for local
573
542
self._trace("timeout after waiting %ss", timeout)
574
raise LockContention('(local)', lock_url)
543
raise LockContention(self)
576
545
def leave_in_place(self):
577
546
self._locked_via_token = True
623
592
def _format_lock_info(self, info):
624
593
"""Turn the contents of peek() into something for the user"""
625
start_time = info.get('start_time')
626
if start_time is None:
627
time_ago = '(unknown)'
629
time_ago = format_delta(time.time() - int(info['start_time']))
630
user = info.get('user', '<unknown>')
631
hostname = info.get('hostname', '<unknown>')
632
pid = info.get('pid', '<unknown>')
594
lock_url = self.transport.abspath(self.path)
595
delta = time.time() - int(info['start_time'])
597
'lock %s' % (lock_url,),
598
'held by %(user)s on host %(hostname)s [process #%(pid)s]' % info,
599
'locked %s' % (format_delta(delta),),
640
602
def validate_token(self, token):