1 : /** \file
2 : * High-level front-end to libapt-pkg, as a data provider for the ept framework.
3 : */
4 :
5 : /*
6 : * Copyright (C) 2007,2008 Enrico Zini <enrico@enricozini.org>
7 : *
8 : * This library is free software; you can redistribute it and/or
9 : * modify it under the terms of the GNU Lesser General Public
10 : * License as published by the Free Software Foundation; either
11 : * version 2.1 of the License, or (at your option) any later version.
12 : *
13 : * This library is distributed in the hope that it will be useful,
14 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 : * Lesser General Public License for more details.
17 : *
18 : * You should have received a copy of the GNU Lesser General Public
19 : * License along with this library; if not, write to the Free Software
20 : * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 : */
22 :
23 : #include <ept/apt/apt.h>
24 :
25 : #include <apt-pkg/error.h>
26 : #include <apt-pkg/init.h>
27 : #include <apt-pkg/progress.h>
28 : #include <apt-pkg/sourcelist.h>
29 : #include <apt-pkg/pkgcachegen.h>
30 : #include <apt-pkg/policy.h>
31 : #include <apt-pkg/cachefile.h>
32 :
33 : #include <wibble/sys/fs.h>
34 : #include <sys/stat.h>
35 :
36 : #include <vector>
37 : #include <algorithm>
38 :
39 : #include <iostream>
40 :
41 : using namespace std;
42 :
43 : namespace ept {
44 : namespace apt {
45 : using core::aptTimestamp;
46 :
47 0 : Exception::Exception(const std::string& context) throw ()
48 0 : : Generic(context)
49 : {
50 : // Concatenate all errors and warnings found
51 0 : string err;
52 0 : while (!_error->empty())
53 : {
54 0 : bool type = _error->PopMessage(err);
55 0 : if (type)
56 0 : m_message += "E: " + err + "\n";
57 : else
58 0 : m_message += "W: " + err + "\n";
59 0 : }
60 0 : }
61 :
62 29 : static void aptInit ()
63 : {
64 29 : if (_config->FindB("Initialized"))
65 29 : return;
66 :
67 0 : if (!pkgInitConfig (*_config))
68 0 : throw Exception("initialising apt configuration");
69 :
70 0 : _config->Set("Initialized", 1);
71 :
72 : /*
73 : _config->Set("Dir", CACHE_DIR);
74 : _config->Set("Dir::Cache", "cache");
75 : _config->Set("Dir::State", "state");
76 : _config->Set("Dir::Etc", "etc");
77 : _config->Set("Dir::State::status", CACHE_DIR "dpkg-status");
78 : */
79 0 : if (!pkgInitSystem (*_config, _system))
80 0 : throw Exception("initialising apt system");
81 : }
82 :
83 : struct AptImplementation
84 : {
85 : pkgSourceList* m_list;
86 : MMap *m;
87 : OpProgress progress;
88 : pkgCache* m_cache;
89 : pkgPolicy* m_policy;
90 : pkgCacheFile* m_depcache;
91 : time_t m_open_timestamp;
92 :
93 29 : AptImplementation() : m_list(0), m(0), m_cache(0), m_policy(0), m_depcache(0), m_open_timestamp(0)
94 : {
95 : // Init the apt library if needed
96 29 : aptInit();
97 :
98 29 : m_open_timestamp = aptTimestamp();
99 :
100 29 : m_list = new pkgSourceList;
101 58 : if (!m_list->ReadMainList())
102 0 : throw Exception("reading list of sources");
103 :
104 29 : bool res = pkgMakeStatusCache(*m_list, progress, &m, true);
105 29 : progress.Done();
106 29 : if (!res)
107 0 : throw Exception("Reading the package lists or status file");
108 :
109 29 : m_cache = new pkgCache(m);
110 58 : m_policy = new pkgPolicy(m_cache);
111 58 : if (!ReadPinFile(*m_policy))
112 0 : throw Exception("Reading the policy pin file");
113 29 : }
114 :
115 29 : ~AptImplementation()
116 : {
117 29 : if (m_depcache) delete m_depcache;
118 29 : if (m_policy) delete m_policy;
119 29 : if (m_cache) delete m_cache;
120 29 : if (m) delete m;
121 29 : if (m_list) delete m_list;
122 29 : }
123 :
124 3980 : pkgCache& cache()
125 : {
126 3980 : return *m_cache;
127 : }
128 :
129 16099 : pkgPolicy& policy()
130 : {
131 16099 : return *m_policy;
132 : }
133 :
134 1 : pkgCacheFile& depcache()
135 : {
136 1 : if (!m_depcache)
137 : {
138 1 : m_depcache = new pkgCacheFile;
139 1 : if (!m_depcache->Open(progress, false))
140 0 : throw Exception("Opening the cache file");
141 : }
142 1 : return *m_depcache;
143 : }
144 : };
145 :
146 : // Sort a version list by package file locality
147 39519 : bool localityCompare(const pkgCache::VerFile* a, const pkgCache::VerFile* b)
148 : {
149 39519 : if (a == 0 && b == 0)
150 0 : return false;
151 39519 : if (a == 0)
152 0 : return true;
153 39519 : if (b == 0)
154 0 : return false;
155 :
156 39519 : if (a->File == b->File)
157 39060 : return a->Offset < b->Offset;
158 459 : return a->File < b->File;
159 : }
160 :
161 : // Iterate records using the algorithm used by apt-cache dumpavail
162 : struct RecordIteratorImpl
163 : {
164 : mutable int _ref;
165 : AptImplementation& apt;
166 : vector<pkgCache::VerFile*> vflist;
167 : pkgCache::PkgFileIterator lastFile;
168 : FileFd file;
169 : size_t lastOffset;
170 :
171 9 : RecordIteratorImpl(AptImplementation& apt) : _ref(0), apt(apt), vflist(0)
172 : {
173 : // We already have an estimate of how many versions we're about to find
174 9 : vflist.reserve(apt.cache().HeaderP->PackageCount + 1);
175 :
176 : // Populate the vector of versions to print
177 34002 : for (pkgCache::PkgIterator pi = apt.cache().PkgBegin(); !pi.end(); ++pi)
178 : {
179 33993 : if (pi->VersionList == 0)
180 17901 : continue;
181 :
182 : /* Get the candidate version or fallback on the installed version,
183 : * as usual */
184 16092 : pkgCache::VerIterator vi = apt.policy().GetCandidateVer(pi);
185 16092 : if (vi.end() == true)
186 : {
187 1926 : if (pi->CurrentVer == 0)
188 1926 : continue;
189 0 : vi = pi.CurrentVer();
190 : }
191 :
192 : // Choose a valid file that contains the record for this version
193 14166 : pkgCache::VerFileIterator vfi = vi.FileList();
194 24507 : for ( ; !vfi.end(); ++vfi)
195 14166 : if ((vfi.File()->Flags & pkgCache::Flag::NotSource) == 0)
196 3825 : break;
197 :
198 : // Handle packages whose candidate version is currently installed
199 : // from outside the archives (like from a locally built .deb
200 14166 : if (vfi.end() == true)
201 : {
202 20682 : for (pkgCache::VerIterator cur = pi.VersionList(); cur.end() != true; cur++)
203 : {
204 20763 : for (vfi = cur.FileList(); vfi.end() == false; vfi++)
205 : {
206 10422 : if ((vfi.File()->Flags & pkgCache::Flag::NotSource) == 0)
207 : {
208 81 : vfi = vi.FileList();
209 81 : break;
210 : }
211 : }
212 :
213 10422 : if (vfi.end() == false)
214 81 : break;
215 : }
216 : }
217 14166 : if (!vfi.end())
218 3906 : vflist.push_back(vfi);
219 : }
220 :
221 : //cerr << vflist.size() << " versions found" << endl;
222 :
223 9 : sort(vflist.begin(), vflist.end(), localityCompare);
224 :
225 : //for (size_t i = 0; i < vflist.size(); ++i)
226 : //{
227 : // pkgCache::PkgFileIterator fi(apt.cache(), vflist[i]->File + apt.cache().PkgFileP);
228 : // cerr << i << ": " << fi.FileName() << ":" << vflist[i]->Offset << "-" << vflist[i]->Size << endl;
229 : //}
230 : //cerr << "Done indexing." << endl;
231 9 : }
232 :
233 9 : ~RecordIteratorImpl()
234 : {
235 9 : if (file.IsOpen())
236 9 : file.Close();
237 9 : }
238 :
239 14 : void ref() { ++_ref; }
240 14 : bool unref() { return --_ref == 0; }
241 :
242 3906 : size_t size() { return vflist.size(); }
243 :
244 3906 : string record(size_t idx)
245 : {
246 : //cerr << "Access record " << idx << endl;
247 : //cerr << "lastfile: " << (lastFile.Cache() != 0) << endl;
248 : // We can't reuse the file that was already open: open the new one
249 3906 : if ((lastFile.Cache() == 0) || vflist[idx]->File + apt.cache().PkgFileP != lastFile)
250 : {
251 : //cerr << "Needs open/reopen" << endl;
252 18 : lastFile = pkgCache::PkgFileIterator(apt.cache(), vflist[idx]->File + apt.cache().PkgFileP);
253 18 : if (!lastFile.IsOk())
254 0 : throw Exception(string("Reading the data record for a package from file ") + lastFile.FileName());
255 : //cerr << "Ok for " << lastFile.FileName() << endl;
256 18 : if (file.IsOpen())
257 9 : file.Close();
258 18 : if (!file.Open(lastFile.FileName(), FileFd::ReadOnly))
259 0 : throw Exception(string("Opening file ") + lastFile.FileName());
260 : //cerr << "Opened " << lastFile.FileName() << endl;
261 18 : lastOffset = 0;
262 : }
263 :
264 : //cerr << "Reading from " << lastFile.FileName() << ":" << vflist[idx]->Offset << "-" << vflist[idx]->Size << " (lastOffset: " << lastOffset << ")" << endl;
265 :
266 : // If we start near were we ended, avoid a seek and enlarge the read a bit
267 3906 : size_t slack = vflist[idx]->Offset - lastOffset;
268 : //cerr << "Slack: " << slack << endl;
269 3906 : if (slack > 8)
270 : {
271 : //cerr << "Slack too big: seek to " << vflist[idx]->Offset << endl;
272 171 : slack = 0;
273 171 : if (!file.Seek(vflist[idx]->Offset))
274 0 : throw Exception(string("Cannot seek to package record in file ") + lastFile.FileName());
275 : }
276 :
277 3906 : char buffer[vflist[idx]->Size + slack + 1];
278 3906 : if (!file.Read(buffer, vflist[idx]->Size + slack))
279 0 : throw Exception(string("Cannot read package record in file ") + lastFile.FileName());
280 3906 : buffer[vflist[idx]->Size + slack] = '\n';
281 : //cerr << "Data read (slack: " << slack << ")" << endl;
282 :
283 3906 : lastOffset = vflist[idx]->Offset + vflist[idx]->Size;
284 :
285 3906 : return string(buffer+slack);
286 : }
287 : };
288 :
289 20 : Apt::Iterator::Iterator(const Iterator& i)
290 : {
291 20 : if (i.cur)
292 : {
293 10 : pkgCache::PkgIterator* p = new pkgCache::PkgIterator;
294 10 : *p = *static_cast<pkgCache::PkgIterator*>(i.cur);
295 10 : cur = p;
296 : } else
297 10 : cur = 0;
298 20 : }
299 :
300 0 : Apt::Iterator& Apt::Iterator::operator=(const Iterator& i)
301 : {
302 0 : if (cur != i.cur)
303 : {
304 0 : if (cur) delete static_cast<pkgCache::PkgIterator*>(cur);
305 0 : if (i.cur)
306 : {
307 0 : pkgCache::PkgIterator* p = new pkgCache::PkgIterator;
308 0 : *p = *static_cast<pkgCache::PkgIterator*>(i.cur);
309 0 : cur = p;
310 : } else
311 0 : cur = 0;
312 : }
313 0 : return *this;
314 : }
315 :
316 3605 : Apt::Iterator::~Iterator()
317 : {
318 3605 : if (cur) delete static_cast<pkgCache::PkgIterator*>(cur);
319 3605 : }
320 5364 : std::string Apt::Iterator::operator*()
321 : {
322 5364 : return static_cast<pkgCache::PkgIterator*>(cur)->Name();
323 : }
324 7152 : Apt::Iterator& Apt::Iterator::operator++()
325 : {
326 7152 : pkgCache::PkgIterator* iter = static_cast<pkgCache::PkgIterator*>(cur);
327 7152 : ++*iter;
328 22260 : while (!iter->end() && (*iter)->VersionList == 0)
329 7956 : ++*iter;
330 7152 : if (iter->end())
331 : {
332 4 : delete iter;
333 4 : cur = 0;
334 : }
335 7152 : return *this;
336 : }
337 0 : bool Apt::Iterator::operator==(const Iterator& i) const
338 : {
339 0 : if (cur == 0 && i.cur == 0)
340 0 : return true;
341 0 : if (cur == 0 || i.cur == 0)
342 0 : return false;
343 0 : pkgCache::PkgIterator* iter1 = static_cast<pkgCache::PkgIterator*>(cur);
344 0 : pkgCache::PkgIterator* iter2 = static_cast<pkgCache::PkgIterator*>(i.cur);
345 0 : return *iter1 == *iter2;
346 : }
347 7157 : bool Apt::Iterator::operator!=(const Iterator& i) const
348 : {
349 7157 : if (cur == 0 && i.cur == 0)
350 4 : return false;
351 7153 : if (cur == 0 || i.cur == 0)
352 7153 : return true;
353 0 : pkgCache::PkgIterator* iter1 = static_cast<pkgCache::PkgIterator*>(cur);
354 0 : pkgCache::PkgIterator* iter2 = static_cast<pkgCache::PkgIterator*>(i.cur);
355 0 : return *iter1 != *iter2;
356 : }
357 :
358 :
359 9 : Apt::RecordIterator::RecordIterator(RecordIteratorImpl* impl, size_t pos)
360 9 : : impl(impl), pos(pos), cur_pos(pos)
361 : {
362 9 : if (impl)
363 : {
364 9 : impl->ref();
365 9 : cur = impl->record(pos);
366 9 : cur_pos = pos;
367 : }
368 9 : }
369 10 : Apt::RecordIterator::RecordIterator(const RecordIterator& r)
370 10 : : impl(r.impl), pos(r.pos), cur(r.cur), cur_pos(r.cur_pos)
371 : {
372 10 : if (impl)
373 5 : impl->ref();
374 10 : }
375 3500 : Apt::RecordIterator::~RecordIterator()
376 : {
377 3500 : if (impl && impl->unref())
378 1 : delete impl;
379 3500 : }
380 3906 : std::string Apt::RecordIterator::operator*()
381 : {
382 3906 : if (cur_pos != pos)
383 : {
384 3464 : cur = impl->record(pos);
385 3464 : cur_pos = pos;
386 : }
387 3906 : return cur;
388 : }
389 868 : std::string* Apt::RecordIterator::operator->()
390 : {
391 868 : if (cur_pos != pos)
392 : {
393 433 : cur = impl->record(pos);
394 433 : cur_pos = pos;
395 : }
396 868 : return &cur;
397 : }
398 3906 : Apt::RecordIterator& Apt::RecordIterator::operator++()
399 : {
400 3906 : ++pos;
401 3906 : if (pos >= impl->size())
402 : {
403 : // If we reach the end, we become an end iterator
404 9 : if (impl && impl->unref())
405 8 : delete impl;
406 9 : impl = 0;
407 9 : pos = 0;
408 : }
409 3906 : return *this;
410 : }
411 0 : Apt::RecordIterator& Apt::RecordIterator::operator=(const RecordIterator& r)
412 : {
413 : // Increment first, to avoid it reaching zero on assignment to self
414 0 : if (r.impl) r.impl->ref();
415 0 : if (impl && impl->unref())
416 0 : delete impl;
417 0 : impl = r.impl;
418 0 : pos = r.pos;
419 0 : cur = r.cur;
420 0 : cur_pos = r.cur_pos;
421 0 : return *this;
422 : }
423 0 : bool Apt::RecordIterator::operator==(const RecordIterator& ri) const
424 : {
425 0 : return impl == ri.impl && pos == ri.pos;
426 : }
427 3915 : bool Apt::RecordIterator::operator!=(const RecordIterator& ri) const
428 : {
429 3915 : return impl != ri.impl || pos != ri.pos;
430 : }
431 :
432 :
433 28 : Apt::Apt() : impl(new AptImplementation()) {}
434 28 : Apt::~Apt() { delete impl; }
435 :
436 4 : Apt::iterator Apt::begin() const
437 : {
438 4 : pkgCache::PkgIterator* p = new pkgCache::PkgIterator;
439 4 : *p = impl->cache().PkgBegin();
440 4 : return Apt::Iterator(p);
441 : }
442 :
443 3581 : Apt::iterator Apt::end() const
444 : {
445 3581 : return Apt::Iterator();
446 : }
447 :
448 9 : Apt::record_iterator Apt::recordBegin() const
449 : {
450 9 : return Apt::RecordIterator(new RecordIteratorImpl(*impl));
451 : }
452 :
453 3481 : Apt::record_iterator Apt::recordEnd() const
454 : {
455 3481 : return Apt::RecordIterator();
456 : }
457 :
458 0 : size_t Apt::size() const
459 : {
460 0 : return impl->cache().HeaderP->PackageCount;
461 : }
462 :
463 16 : time_t Apt::timestamp()
464 : {
465 16 : return aptTimestamp();
466 : }
467 :
468 5 : bool Apt::isValid(const std::string& pkg) const
469 : {
470 5 : pkgCache::PkgIterator pi = impl->cache().FindPkg(pkg);
471 5 : return !pi.end();
472 : }
473 :
474 4 : Version Apt::validate(const Version& ver) const
475 : {
476 4 : pkgCache::PkgIterator pi = impl->cache().FindPkg(ver.name());
477 5 : if (pi.end()) return Version();
478 4 : for (pkgCache::VerIterator vi = pi.VersionList(); !vi.end(); vi++)
479 : {
480 3 : const char* v = vi.VerStr();
481 3 : if (v == 0) continue;
482 3 : if (ver.version() == v)
483 2 : return ver;
484 : }
485 1 : return Version();
486 : }
487 :
488 4 : Version Apt::candidateVersion(const std::string& pkg) const
489 : {
490 4 : pkgCache::PkgIterator pi = impl->cache().FindPkg(pkg);
491 4 : if (pi.end()) return Version();
492 3 : pkgCache::VerIterator vi = impl->policy().GetCandidateVer(pi);
493 3 : if (vi.end()) return Version();
494 3 : return Version(pkg, vi.VerStr());
495 : }
496 :
497 2 : Version Apt::installedVersion(const std::string& pkg) const
498 : {
499 2 : pkgCache::PkgIterator pi = impl->cache().FindPkg(pkg);
500 2 : if (pi.end()) return Version();
501 1 : if (pi->CurrentVer == 0) return Version();
502 1 : pkgCache::VerIterator vi = pi.CurrentVer();
503 1 : if (vi.end()) return Version();
504 1 : return Version(pkg, vi.VerStr());
505 : }
506 :
507 4 : Version Apt::anyVersion(const std::string& pkg) const
508 : {
509 4 : pkgCache::PkgIterator pi = impl->cache().FindPkg(pkg);
510 4 : if (pi.end()) return Version();
511 :
512 3 : pkgCache::VerIterator vi = impl->policy().GetCandidateVer(pi);
513 3 : if (vi.end())
514 : {
515 0 : if (pi->CurrentVer == 0) return Version();
516 0 : vi = pi.CurrentVer();
517 0 : if (vi.end()) return Version();
518 : }
519 3 : return Version(pkg, vi.VerStr());
520 : }
521 :
522 2 : PackageState Apt::state(const std::string& pkg) const
523 : {
524 2 : pkgCache::PkgIterator pi = impl->cache().FindPkg(pkg);
525 2 : if (pi.end()) return PackageState();
526 1 : pkgDepCache::StateCache sc = impl->depcache()[pi];
527 :
528 1 : unsigned int flags = PackageState::Valid;
529 :
530 : // Check if the package is installed
531 1 : if (pi->CurrentState != pkgCache::State::ConfigFiles &&
532 : pi->CurrentState != pkgCache::State::NotInstalled &&
533 : pi->CurrentVer != 0)
534 : {
535 : // Try to get a VerIterator to the installed version
536 1 : pkgCache::VerIterator inst = pi.CurrentVer();
537 1 : if (!inst.end())
538 : {
539 : // If we made it so far, it is installed
540 1 : flags |= PackageState::Installed;
541 :
542 : // Now check if it is upgradable
543 1 : pkgCache::VerIterator cand = impl->policy().GetCandidateVer(pi);
544 :
545 : // If the candidate version is different than the installed one, then
546 : // it is installable
547 1 : if (!cand.end() && inst != cand)
548 0 : flags |= PackageState::Upgradable;
549 : }
550 : }
551 1 : if (sc.Install())
552 0 : flags |= PackageState::Install;
553 1 : if ((sc.iFlags & pkgDepCache::ReInstall) == pkgDepCache::ReInstall)
554 0 : flags |= PackageState::ReInstall;
555 1 : if (sc.Keep())
556 1 : flags |= PackageState::Keep;
557 1 : if (sc.Delete())
558 0 : flags |= PackageState::Remove;
559 1 : if ((sc.iFlags & pkgDepCache::Purge) == pkgDepCache::Purge)
560 0 : flags |= PackageState::Purge;
561 1 : if (sc.NowBroken())
562 0 : flags |= PackageState::NowBroken;
563 1 : if (sc.InstBroken())
564 0 : flags |= PackageState::WillBreak;
565 :
566 1 : return PackageState(flags);
567 : }
568 :
569 1 : std::string Apt::rawRecord(const std::string& ver) const
570 : {
571 : // TODO: possibly reimplement using a single lump of apt code, to avoid
572 : // repeating lookups
573 1 : return rawRecord(anyVersion(ver));
574 : }
575 :
576 4 : std::string Apt::rawRecord(const Version& ver) const
577 : {
578 4 : pkgCache::PkgIterator pi = impl->cache().FindPkg(ver.name());
579 4 : if (pi.end()) return std::string();
580 5 : for (pkgCache::VerIterator vi = pi.VersionList(); !vi.end(); vi++)
581 : {
582 4 : const char* v = vi.VerStr();
583 4 : if (v == 0) continue;
584 4 : if (ver.version() == v)
585 : {
586 : // Code taken and adapted from apt-cache's DisplayRecord
587 :
588 : // Find an appropriate file
589 3 : pkgCache::VerFileIterator vfi = vi.FileList();
590 3 : for (; !vfi.end(); vfi++)
591 3 : if ((vfi.File()->Flags & pkgCache::Flag::NotSource) == 0)
592 3 : break;
593 3 : if (vfi.end())
594 0 : vfi = vi.FileList();
595 :
596 : // Check and load the package list file
597 3 : pkgCache::PkgFileIterator pfi = vfi.File();
598 3 : if (!pfi.IsOk())
599 0 : throw Exception(string("Reading the data record for a package version from file ") + pfi.FileName());
600 :
601 3 : FileFd pkgf(pfi.FileName(), FileFd::ReadOnly);
602 6 : if (_error->PendingError() == true)
603 0 : return std::string();
604 :
605 : // Read the record and then write it out again.
606 3 : char* buffer = new char[vfi->Size+1];
607 3 : buffer[vfi->Size] = '\n';
608 3 : if (!pkgf.Seek(vfi->Offset) || !pkgf.Read(buffer, vfi->Size))
609 : {
610 0 : delete[] buffer;
611 0 : return std::string();
612 : }
613 :
614 3 : std::string res(buffer, vfi->Size);
615 6 : delete[] buffer;
616 3 : return res;
617 : }
618 : }
619 1 : return std::string();
620 : }
621 :
622 2 : void Apt::checkCacheUpdates()
623 : {
624 2 : if (impl->m_open_timestamp < timestamp())
625 : {
626 : // Crudely reopen everything
627 1 : delete impl;
628 1 : impl = new AptImplementation;
629 : }
630 2 : }
631 :
632 1 : void Apt::invalidateTimestamp()
633 : {
634 1 : impl->m_open_timestamp = 0;
635 1 : }
636 :
637 : }
638 6 : }
639 :
640 : // vim:set ts=4 sw=4:
|