1
2
3
4 import errno
5 import sys
6 from portage.util import writemsg
7 from portage.data import secpass
8 import portage
9 from portage import os
10
11 try:
12 import cPickle as pickle
13 except ImportError:
14 import pickle
15
16 if sys.hexversion >= 0x3000000:
17 basestring = str
18 long = int
19 _unicode = str
20 else:
21 _unicode = unicode
22
24 """This caches blockers of installed packages so that dep_check does not
25 have to be done for every single installed package on every invocation of
26 emerge. The cache is invalidated whenever it is detected that something
27 has changed that might alter the results of dep_check() calls:
28 1) the set of installed packages (including COUNTER) has changed
29 """
30
31
32
33 _cache_threshold = 5
34
36
37 __slots__ = ("__weakref__", "atoms", "counter")
38
42
44 """ myroot is ignored in favour of EROOT """
45 self._vardb = vardb
46 self._cache_filename = os.path.join(vardb.settings['EROOT'],
47 portage.CACHE_PATH, "vdb_blockers.pickle")
48 self._cache_version = "1"
49 self._cache_data = None
50 self._modified = set()
51 self._load()
52
54 try:
55 f = open(self._cache_filename, mode='rb')
56 mypickle = pickle.Unpickler(f)
57 try:
58 mypickle.find_global = None
59 except AttributeError:
60
61 pass
62 self._cache_data = mypickle.load()
63 f.close()
64 del f
65 except (SystemExit, KeyboardInterrupt):
66 raise
67 except Exception as e:
68 if isinstance(e, EnvironmentError) and \
69 getattr(e, 'errno', None) in (errno.ENOENT, errno.EACCES):
70 pass
71 else:
72 writemsg("!!! Error loading '%s': %s\n" % \
73 (self._cache_filename, str(e)), noiselevel=-1)
74 del e
75
76 cache_valid = self._cache_data and \
77 isinstance(self._cache_data, dict) and \
78 self._cache_data.get("version") == self._cache_version and \
79 isinstance(self._cache_data.get("blockers"), dict)
80 if cache_valid:
81
82
83 invalid_items = set()
84 for k, v in self._cache_data["blockers"].items():
85 if not isinstance(k, basestring):
86 invalid_items.add(k)
87 continue
88 try:
89 if portage.catpkgsplit(k) is None:
90 invalid_items.add(k)
91 continue
92 except portage.exception.InvalidData:
93 invalid_items.add(k)
94 continue
95 if not isinstance(v, tuple) or \
96 len(v) != 2:
97 invalid_items.add(k)
98 continue
99 counter, atoms = v
100 if not isinstance(counter, (int, long)):
101 invalid_items.add(k)
102 continue
103 if not isinstance(atoms, (list, tuple)):
104 invalid_items.add(k)
105 continue
106 invalid_atom = False
107 for atom in atoms:
108 if not isinstance(atom, basestring):
109 invalid_atom = True
110 break
111 if atom[:1] != "!" or \
112 not portage.isvalidatom(
113 atom, allow_blockers=True):
114 invalid_atom = True
115 break
116 if invalid_atom:
117 invalid_items.add(k)
118 continue
119
120 for k in invalid_items:
121 del self._cache_data["blockers"][k]
122 if not self._cache_data["blockers"]:
123 cache_valid = False
124
125 if not cache_valid:
126 self._cache_data = {"version":self._cache_version}
127 self._cache_data["blockers"] = {}
128 self._modified.clear()
129
131 """If the current user has permission and the internal blocker cache
132 been updated, save it to disk and mark it unmodified. This is called
133 by emerge after it has proccessed blockers for all installed packages.
134 Currently, the cache is only written if the user has superuser
135 privileges (since that's required to obtain a lock), but all users
136 have read access and benefit from faster blocker lookups (as long as
137 the entire cache is still valid). The cache is stored as a pickled
138 dict object with the following format:
139
140 {
141 version : "1",
142 "blockers" : {cpv1:(counter,(atom1, atom2...)), cpv2...},
143 }
144 """
145 if len(self._modified) >= self._cache_threshold and \
146 secpass >= 2:
147 try:
148 f = portage.util.atomic_ofstream(self._cache_filename, mode='wb')
149 pickle.dump(self._cache_data, f, protocol=2)
150 f.close()
151 portage.util.apply_secpass_permissions(
152 self._cache_filename, gid=portage.portage_gid, mode=0o644)
153 except (IOError, OSError):
154 pass
155 self._modified.clear()
156
158 """
159 Update the cache and mark it as modified for a future call to
160 self.flush().
161
162 @param cpv: Package for which to cache blockers.
163 @type cpv: String
164 @param blocker_data: An object with counter and atoms attributes.
165 @type blocker_data: BlockerData
166 """
167 self._cache_data["blockers"][_unicode(cpv)] = (blocker_data.counter,
168 tuple(_unicode(x) for x in blocker_data.atoms))
169 self._modified.add(cpv)
170
172 if self._cache_data is None:
173
174 return iter([])
175 return iter(self._cache_data["blockers"])
176
178 """This needs to be implemented in order to avoid
179 infinite recursion in some cases."""
180 return len(self._cache_data["blockers"])
181
183 del self._cache_data["blockers"][cpv]
184
186 """
187 @rtype: BlockerData
188 @return: An object with counter and atoms attributes.
189 """
190 return self.BlockerData(*self._cache_data["blockers"][cpv])
191