~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/lockdir.py

  • Committer: Vincent Ladeuil
  • Date: 2009-04-27 16:10:10 UTC
  • mto: (4310.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 4311.
  • Revision ID: v.ladeuil+lp@free.fr-20090427161010-7swfzeagf63cpixd
Fix bug #367726 by reverting some default user handling introduced
while fixing bug #256612.

* bzrlib/transport/ssh.py:
(_paramiko_auth): Explicitly use getpass.getuser() as default
user.

* bzrlib/transport/ftp/_gssapi.py:
(GSSAPIFtpTransport._create_connection): Explicitly use
getpass.getuser() as default user.

* bzrlib/transport/ftp/__init__.py:
(FtpTransport._create_connection): Explicitly use
getpass.getuser() as default user.

* bzrlib/tests/test_sftp_transport.py:
(TestUsesAuthConfig.test_sftp_is_none_if_no_config)
(TestUsesAuthConfig.test_sftp_doesnt_prompt_username): Revert to
None as the default user.

* bzrlib/tests/test_remote.py:
(TestRemoteSSHTransportAuthentication): The really offending one:
revert to None as the default user.

* bzrlib/tests/test_config.py:
(TestAuthenticationConfig.test_username_default_no_prompt): Update
test (and some PEP8).

* bzrlib/smtp_connection.py:
(SMTPConnection._authenticate): Revert to None as the default
user.

* bzrlib/plugins/launchpad/account.py:
(_get_auth_user): Revert default value handling.

* bzrlib/config.py:
(AuthenticationConfig.get_user): Fix doc-string. Leave default
value handling to callers.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Canonical Ltd
 
1
# Copyright (C) 2006, 2007, 2008 Canonical Ltd
2
2
#
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
110
110
    debug,
111
111
    errors,
112
112
    lock,
113
 
    osutils,
114
113
    )
115
114
import bzrlib.config
116
 
from bzrlib.decorators import only_raises
117
115
from bzrlib.errors import (
118
116
        DirectoryNotEmpty,
119
117
        FileExists,
120
118
        LockBreakMismatch,
121
119
        LockBroken,
122
120
        LockContention,
123
 
        LockCorrupt,
124
121
        LockFailed,
125
122
        LockNotHeld,
126
123
        NoSuchFile,
152
149
# files/dirs created.
153
150
 
154
151
 
155
 
_DEFAULT_TIMEOUT_SECONDS = 30
 
152
_DEFAULT_TIMEOUT_SECONDS = 300
156
153
_DEFAULT_POLL_SECONDS = 1.0
157
154
 
158
155
 
243
240
        # incorrect.  It's possible some other servers or filesystems will
244
241
        # have a similar bug allowing someone to think they got the lock
245
242
        # when it's already held.
246
 
        #
247
 
        # See <https://bugs.launchpad.net/bzr/+bug/498378> for one case.
248
 
        #
249
 
        # Strictly the check is unnecessary and a waste of time for most
250
 
        # people, but probably worth trapping if something is wrong.
251
243
        info = self.peek()
252
244
        self._trace("after locking, info=%r", info)
253
 
        if info is None:
254
 
            raise LockFailed(self, "lock was renamed into place, but "
255
 
                "now is missing!")
256
 
        if info.get('nonce') != self.nonce:
 
245
        if info['nonce'] != self.nonce:
257
246
            self._trace("rename succeeded, "
258
247
                "but lock is still held by someone else")
259
248
            raise LockContention(self)
297
286
                                            info_bytes)
298
287
        return tmpname
299
288
 
300
 
    @only_raises(LockNotHeld, LockBroken)
301
289
    def unlock(self):
302
290
        """Release a held lock
303
291
        """
305
293
            self._fake_read_lock = False
306
294
            return
307
295
        if not self._lock_held:
308
 
            return lock.cant_unlock_not_held(self)
 
296
            raise LockNotHeld(self)
309
297
        if self._locked_via_token:
310
298
            self._locked_via_token = False
311
299
            self._lock_held = False
337
325
            self._trace("... unlock succeeded after %dms",
338
326
                    (time.time() - start_time) * 1000)
339
327
            result = lock.LockResult(self.transport.abspath(self.path),
340
 
                                     old_nonce)
 
328
                old_nonce)
341
329
            for hook in self.hooks['lock_released']:
342
330
                hook(result)
343
331
 
349
337
        it possibly being still active.
350
338
        """
351
339
        self._check_not_locked()
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
 
340
        holder_info = self.peek()
359
341
        if holder_info is not None:
360
342
            lock_info = '\n'.join(self._format_lock_info(holder_info))
361
 
            if bzrlib.ui.ui_factory.confirm_action(
362
 
                "Break %(lock_info)s", 'bzrlib.lockdir.break', 
363
 
                dict(lock_info=lock_info)):
 
343
            if bzrlib.ui.ui_factory.get_boolean("Break %s" % lock_info):
364
344
                self.force_break(holder_info)
365
345
 
366
346
    def force_break(self, dead_holder_info):
399
379
            raise LockBreakMismatch(self, broken_info, dead_holder_info)
400
380
        self.transport.delete(broken_info_path)
401
381
        self.transport.rmdir(tmpname)
402
 
        result = lock.LockResult(self.transport.abspath(self.path),
403
 
                                 current_info.get('nonce'))
404
 
        for hook in self.hooks['lock_broken']:
405
 
            hook(result)
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
382
 
436
383
    def _check_not_locked(self):
437
384
        """If the lock is held by this instance, raise an error."""
463
410
 
464
411
        peek() reads the info file of the lock holder, if any.
465
412
        """
466
 
        return self._parse_info(self.transport.get_bytes(path))
 
413
        return self._parse_info(self.transport.get(path))
467
414
 
468
415
    def peek(self):
469
416
        """Check if the lock is held by anyone.
470
417
 
471
 
        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,
472
419
        which contains some information about the current lock holder.
473
420
        Otherwise returns None.
474
421
        """
485
432
        # XXX: is creating this here inefficient?
486
433
        config = bzrlib.config.GlobalConfig()
487
434
        try:
 
435
            user = config.user_email()
 
436
        except errors.NoEmailInUsername:
488
437
            user = config.username()
489
 
        except errors.NoWhoami:
490
 
            user = osutils.getuser_unicode()
491
438
        s = rio.Stanza(hostname=get_host_name(),
492
439
                   pid=str(os.getpid()),
493
440
                   start_time=str(int(time.time())),
496
443
                   )
497
444
        return s.to_string()
498
445
 
499
 
    def _parse_info(self, 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)
507
 
        if stanza is None:
508
 
            # see bug 185013; we fairly often end up with the info file being
509
 
            # empty after an interruption; we could log a message here but
510
 
            # there may not be much we can say
511
 
            return {}
512
 
        else:
513
 
            return stanza.as_dict()
 
446
    def _parse_info(self, info_file):
 
447
        return rio.read_stanza(info_file.readlines()).as_dict()
514
448
 
515
449
    def attempt_lock(self):
516
450
        """Take the lock; fail if it's already held.
583
517
                if deadline_str is None:
584
518
                    deadline_str = time.strftime('%H:%M:%S',
585
519
                                                 time.localtime(deadline))
586
 
                # As local lock urls are correct we display them.
587
 
                # We avoid displaying remote lock urls.
588
520
                lock_url = self.transport.abspath(self.path)
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)
 
521
                self._report_function('%s %s\n'
 
522
                                      '%s\n' # held by
 
523
                                      '%s\n' # locked ... ago
 
524
                                      'Will continue to try until %s, unless '
 
525
                                      'you press Ctrl-C\n'
 
526
                                      'If you\'re sure that it\'s not being '
 
527
                                      'modified, use bzr break-lock %s',
 
528
                                      start,
 
529
                                      formatted_info[0],
 
530
                                      formatted_info[1],
 
531
                                      formatted_info[2],
 
532
                                      deadline_str,
 
533
                                      lock_url)
 
534
 
607
535
            if (max_attempts is not None) and (attempt_count >= max_attempts):
608
536
                self._trace("exceeded %d attempts")
609
537
                raise LockContention(self)
611
539
                self._trace("waiting %ss", poll)
612
540
                time.sleep(poll)
613
541
            else:
614
 
                # As timeout is always 0 for remote locks
615
 
                # this block is applicable only for local
616
 
                # lock contention
617
542
                self._trace("timeout after waiting %ss", timeout)
618
 
                raise LockContention('(local)', lock_url)
 
543
                raise LockContention(self)
619
544
 
620
545
    def leave_in_place(self):
621
546
        self._locked_via_token = True
666
591
 
667
592
    def _format_lock_info(self, info):
668
593
        """Turn the contents of peek() into something for the user"""
669
 
        start_time = info.get('start_time')
670
 
        if start_time is None:
671
 
            time_ago = '(unknown)'
672
 
        else:
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>')
 
594
        lock_url = self.transport.abspath(self.path)
 
595
        delta = time.time() - int(info['start_time'])
677
596
        return [
678
 
            user,
679
 
            hostname,
680
 
            pid,
681
 
            time_ago,
 
597
            'lock %s' % (lock_url,),
 
598
            'held by %(user)s on host %(hostname)s [process #%(pid)s]' % info,
 
599
            'locked %s' % (format_delta(delta),),
682
600
            ]
683
601
 
684
602
    def validate_token(self, token):