~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lockdir.py

  • Committer: Andrew Bennetts
  • Date: 2010-09-17 04:35:23 UTC
  • mfrom: (5050.17.20 2.2)
  • mto: This revision was merged to the branch mainline in revision 5431.
  • Revision ID: andrew.bennetts@canonical.com-20100917043523-c5t63gmvxqxmqh5j
Merge lp:bzr/2.2, including fixes for #625574, #636930, #254278.

Show diffs side-by-side

added added

removed removed

Lines of Context:
120
120
        LockBreakMismatch,
121
121
        LockBroken,
122
122
        LockContention,
 
123
        LockCorrupt,
123
124
        LockFailed,
124
125
        LockNotHeld,
125
126
        NoSuchFile,
151
152
# files/dirs created.
152
153
 
153
154
 
154
 
_DEFAULT_TIMEOUT_SECONDS = 300
 
155
_DEFAULT_TIMEOUT_SECONDS = 30
155
156
_DEFAULT_POLL_SECONDS = 1.0
156
157
 
157
158
 
243
244
        # have a similar bug allowing someone to think they got the lock
244
245
        # when it's already held.
245
246
        #
246
 
        # See <https://bugs.edge.launchpad.net/bzr/+bug/498378> for one case.
 
247
        # See <https://bugs.launchpad.net/bzr/+bug/498378> for one case.
247
248
        #
248
249
        # Strictly the check is unnecessary and a waste of time for most
249
250
        # people, but probably worth trapping if something is wrong.
348
349
        it possibly being still active.
349
350
        """
350
351
        self._check_not_locked()
351
 
        holder_info = self.peek()
 
352
        try:
 
353
            holder_info = self.peek()
 
354
        except LockCorrupt, e:
 
355
            # The lock info is corrupt.
 
356
            if bzrlib.ui.ui_factory.get_boolean("Break (corrupt %r)" % (self,)):
 
357
                self.force_break_corrupt(e.file_data)
 
358
            return
352
359
        if holder_info is not None:
353
360
            lock_info = '\n'.join(self._format_lock_info(holder_info))
354
 
            if bzrlib.ui.ui_factory.get_boolean("Break %s" % lock_info):
 
361
            if bzrlib.ui.ui_factory.confirm_action(
 
362
                "Break %(lock_info)s", 'bzrlib.lockdir.break', 
 
363
                dict(lock_info=lock_info)):
355
364
                self.force_break(holder_info)
356
365
 
357
366
    def force_break(self, dead_holder_info):
395
404
        for hook in self.hooks['lock_broken']:
396
405
            hook(result)
397
406
 
 
407
    def force_break_corrupt(self, corrupt_info_lines):
 
408
        """Release a lock that has been corrupted.
 
409
        
 
410
        This is very similar to force_break, it except it doesn't assume that
 
411
        self.peek() can work.
 
412
        
 
413
        :param corrupt_info_lines: the lines of the corrupted info file, used
 
414
            to check that the lock hasn't changed between reading the (corrupt)
 
415
            info file and calling force_break_corrupt.
 
416
        """
 
417
        # XXX: this copes with unparseable info files, but what about missing
 
418
        # info files?  Or missing lock dirs?
 
419
        self._check_not_locked()
 
420
        tmpname = '%s/broken.%s.tmp' % (self.path, rand_chars(20))
 
421
        self.transport.rename(self._held_dir, tmpname)
 
422
        # check that we actually broke the right lock, not someone else;
 
423
        # there's a small race window between checking it and doing the
 
424
        # rename.
 
425
        broken_info_path = tmpname + self.__INFO_NAME
 
426
        f = self.transport.get(broken_info_path)
 
427
        broken_lines = f.readlines()
 
428
        if broken_lines != corrupt_info_lines:
 
429
            raise LockBreakMismatch(self, broken_lines, corrupt_info_lines)
 
430
        self.transport.delete(broken_info_path)
 
431
        self.transport.rmdir(tmpname)
 
432
        result = lock.LockResult(self.transport.abspath(self.path))
 
433
        for hook in self.hooks['lock_broken']:
 
434
            hook(result)
 
435
 
398
436
    def _check_not_locked(self):
399
437
        """If the lock is held by this instance, raise an error."""
400
438
        if self._lock_held:
447
485
        # XXX: is creating this here inefficient?
448
486
        config = bzrlib.config.GlobalConfig()
449
487
        try:
450
 
            user = config.user_email()
451
 
        except errors.NoEmailInUsername:
452
488
            user = config.username()
 
489
        except errors.NoWhoami:
 
490
            user = osutils.getuser_unicode()
453
491
        s = rio.Stanza(hostname=get_host_name(),
454
492
                   pid=str(os.getpid()),
455
493
                   start_time=str(int(time.time())),
459
497
        return s.to_string()
460
498
 
461
499
    def _parse_info(self, info_bytes):
462
 
        stanza = rio.read_stanza(osutils.split_lines(info_bytes))
 
500
        lines = osutils.split_lines(info_bytes)
 
501
        try:
 
502
            stanza = rio.read_stanza(lines)
 
503
        except ValueError, e:
 
504
            mutter('Corrupt lock info file: %r', lines)
 
505
            raise LockCorrupt("could not parse lock info file: " + str(e),
 
506
                              lines)
463
507
        if stanza is None:
464
508
            # see bug 185013; we fairly often end up with the info file being
465
509
            # empty after an interruption; we could log a message here but
539
583
                if deadline_str is None:
540
584
                    deadline_str = time.strftime('%H:%M:%S',
541
585
                                                 time.localtime(deadline))
 
586
                # As local lock urls are correct we display them.
 
587
                # We avoid displaying remote lock urls.
542
588
                lock_url = self.transport.abspath(self.path)
543
 
                # See <https://bugs.edge.launchpad.net/bzr/+bug/250451>
544
 
                # the URL here is sometimes not one that is useful to the
545
 
                # user, perhaps being wrapped in a lp-%d or chroot decorator,
546
 
                # especially if this error is issued from the server.
547
 
                self._report_function('%s %s\n'
548
 
                    '%s\n' # held by
549
 
                    '%s\n' # locked ... ago
550
 
                    'Will continue to try until %s, unless '
551
 
                    'you press Ctrl-C.\n'
552
 
                    'See "bzr help break-lock" for more.',
553
 
                    start,
554
 
                    formatted_info[0],
555
 
                    formatted_info[1],
556
 
                    formatted_info[2],
557
 
                    deadline_str,
558
 
                    )
559
 
 
 
589
                if lock_url.startswith('file://'):
 
590
                    lock_url = lock_url.split('.bzr/')[0]
 
591
                else:
 
592
                    lock_url = ''
 
593
                user, hostname, pid, time_ago = formatted_info
 
594
                msg = ('%s lock %s '        # lock_url
 
595
                    'held by '              # start
 
596
                    '%s\n'                  # user
 
597
                    'at %s '                # hostname
 
598
                    '[process #%s], '       # pid
 
599
                    'acquired %s.')         # time ago
 
600
                msg_args = [start, lock_url, user, hostname, pid, time_ago]
 
601
                if timeout > 0:
 
602
                    msg += ('\nWill continue to try until %s, unless '
 
603
                        'you press Ctrl-C.')
 
604
                    msg_args.append(deadline_str)
 
605
                msg += '\nSee "bzr help break-lock" for more.'
 
606
                self._report_function(msg, *msg_args)
560
607
            if (max_attempts is not None) and (attempt_count >= max_attempts):
561
608
                self._trace("exceeded %d attempts")
562
609
                raise LockContention(self)
564
611
                self._trace("waiting %ss", poll)
565
612
                time.sleep(poll)
566
613
            else:
 
614
                # As timeout is always 0 for remote locks
 
615
                # this block is applicable only for local
 
616
                # lock contention
567
617
                self._trace("timeout after waiting %ss", timeout)
568
 
                raise LockContention(self)
 
618
                raise LockContention('(local)', lock_url)
569
619
 
570
620
    def leave_in_place(self):
571
621
        self._locked_via_token = True
616
666
 
617
667
    def _format_lock_info(self, info):
618
668
        """Turn the contents of peek() into something for the user"""
619
 
        lock_url = self.transport.abspath(self.path)
620
669
        start_time = info.get('start_time')
621
670
        if start_time is None:
622
671
            time_ago = '(unknown)'
623
672
        else:
624
673
            time_ago = format_delta(time.time() - int(info['start_time']))
 
674
        user = info.get('user', '<unknown>')
 
675
        hostname = info.get('hostname', '<unknown>')
 
676
        pid = info.get('pid', '<unknown>')
625
677
        return [
626
 
            'lock %s' % (lock_url,),
627
 
            'held by %s on host %s [process #%s]' %
628
 
                tuple([info.get(x, '<unknown>') for x in ['user', 'hostname', 'pid']]),
629
 
            'locked %s' % (time_ago,),
 
678
            user,
 
679
            hostname,
 
680
            pid,
 
681
            time_ago,
630
682
            ]
631
683
 
632
684
    def validate_token(self, token):