• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2016 Google Inc. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import errno
16import fcntl
17import time
18
19from oauth2client.contrib import locked_file
20
21
22class _FcntlOpener(locked_file._Opener):
23    """Open, lock, and unlock a file using fcntl.lockf."""
24
25    def open_and_lock(self, timeout, delay):
26        """Open the file and lock it.
27
28        Args:
29            timeout: float, How long to try to lock for.
30            delay: float, How long to wait between retries
31
32        Raises:
33            AlreadyLockedException: if the lock is already acquired.
34            IOError: if the open fails.
35            CredentialsFileSymbolicLinkError: if the file is a symbolic
36                                              link.
37        """
38        if self._locked:
39            raise locked_file.AlreadyLockedException(
40                'File {0} is already locked'.format(self._filename))
41        start_time = time.time()
42
43        locked_file.validate_file(self._filename)
44        try:
45            self._fh = open(self._filename, self._mode)
46        except IOError as e:
47            # If we can't access with _mode, try _fallback_mode and
48            # don't lock.
49            if e.errno in (errno.EPERM, errno.EACCES):
50                self._fh = open(self._filename, self._fallback_mode)
51                return
52
53        # We opened in _mode, try to lock the file.
54        while True:
55            try:
56                fcntl.lockf(self._fh.fileno(), fcntl.LOCK_EX)
57                self._locked = True
58                return
59            except IOError as e:
60                # If not retrying, then just pass on the error.
61                if timeout == 0:
62                    raise
63                if e.errno != errno.EACCES:
64                    raise
65                # We could not acquire the lock. Try again.
66                if (time.time() - start_time) >= timeout:
67                    locked_file.logger.warn('Could not lock %s in %s seconds',
68                                            self._filename, timeout)
69                    if self._fh:
70                        self._fh.close()
71                    self._fh = open(self._filename, self._fallback_mode)
72                    return
73                time.sleep(delay)
74
75    def unlock_and_close(self):
76        """Close and unlock the file using the fcntl.lockf primitive."""
77        if self._locked:
78            fcntl.lockf(self._fh.fileno(), fcntl.LOCK_UN)
79        self._locked = False
80        if self._fh:
81            self._fh.close()
82