]>
Commit | Line | Data |
---|---|---|
7939f6d1 | 1 | /* |
31f0ccce | 2 | * Copyright (c) 2001, 2003 Robert Collins. |
7939f6d1 RC |
3 | * |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License as published by | |
6 | * the Free Software Foundation; either version 2 of the License, or | |
7 | * (at your option) any later version. | |
8 | * | |
9 | * A copy of the GNU General Public License can be found at | |
10 | * http://www.gnu.org/ | |
11 | * | |
12 | * Written by Robert Collins <rbtcollins@hotmail.com> | |
13 | * | |
14 | */ | |
15 | ||
16 | /* this is the package database class. | |
17 | * It lists all known packages, including custom ones, ones from a mirror and | |
18 | * installed ones. | |
19 | */ | |
20 | ||
21 | #if 0 | |
22 | static const char *cvsid = | |
23 | "\n%%% $Id$\n"; | |
24 | #endif | |
7939f6d1 RC |
25 | #include <stdio.h> |
26 | #include <stdlib.h> | |
27 | #include <unistd.h> | |
28 | #include <strings.h> | |
45e01f23 RC |
29 | #if HAVE_ERRNO_H |
30 | #include <errno.h> | |
31 | #endif | |
7939f6d1 RC |
32 | |
33 | #include "io_stream.h" | |
34 | #include "compress.h" | |
35 | ||
36 | #include "filemanip.h" | |
37 | ||
fa0c0d10 | 38 | #include "package_version.h" |
7939f6d1 RC |
39 | #include "cygpackage.h" |
40 | #include "package_db.h" | |
41 | #include "package_meta.h" | |
ad646f43 | 42 | #include "Exception.h" |
2f18f94d | 43 | #include "Generic.h" |
7939f6d1 | 44 | |
6625e635 RC |
45 | using namespace std; |
46 | ||
7939f6d1 RC |
47 | /* static members */ |
48 | ||
7939f6d1 RC |
49 | packagedb::packagedb () |
50 | { | |
df62e023 | 51 | io_stream *db = 0; |
7939f6d1 RC |
52 | if (!installeddbread) |
53 | { | |
54 | /* no parameters. Read in the local installation database. */ | |
55 | db = io_stream::open ("cygfile:///etc/setup/installed.db", "rt"); | |
82573872 | 56 | installeddbread = 1; |
7939f6d1 RC |
57 | if (!db) |
58 | return; | |
59 | /* flush_local_db_package_data */ | |
0ec7cbc9 BD |
60 | char line[1000], pkgname[1000], inst[1000]; |
61 | int instsz; | |
7939f6d1 | 62 | |
24cbae7f RC |
63 | if (db->gets (line, 1000)) |
64 | { | |
65 | int dbver; | |
66 | sscanf (line, "%s %d", pkgname, &instsz); | |
67 | if (!strcasecmp (pkgname, "INSTALLED.DB") && instsz == 2) | |
68 | dbver = 2; | |
69 | else | |
70 | dbver = 1; | |
71 | delete db; | |
fa0c0d10 | 72 | db = 0; |
24cbae7f RC |
73 | /* Later versions may not use installed.db other than to record the version. */ |
74 | if (dbver == 1 || dbver == 2) | |
7939f6d1 | 75 | { |
24cbae7f RC |
76 | db = |
77 | io_stream::open ("cygfile:///etc/setup/installed.db", "rt"); | |
78 | if (dbver == 2) | |
79 | db->gets (line, 1000); | |
80 | while (db->gets (line, 1000)) | |
7939f6d1 | 81 | { |
24cbae7f | 82 | int parseable; |
0ec7cbc9 | 83 | int ign; |
4e868a01 RC |
84 | pkgname[0] = '\0'; |
85 | inst[0] = '\0'; | |
4e868a01 | 86 | |
0ec7cbc9 | 87 | sscanf (line, "%s %s %d", pkgname, inst, &ign); |
4e868a01 RC |
88 | |
89 | if (pkgname[0] == '\0' || inst[0] == '\0') | |
90 | continue; | |
91 | ||
24cbae7f RC |
92 | fileparse f; |
93 | parseable = parse_filename (inst, f); | |
94 | if (!parseable) | |
95 | continue; | |
96 | ||
cfae3b8d | 97 | packagemeta *pkg = findBinary (PackageSpecification(pkgname)); |
24cbae7f RC |
98 | if (!pkg) |
99 | { | |
fa0c0d10 | 100 | pkg = new packagemeta (pkgname, inst); |
cfae3b8d | 101 | packages.push_back (pkg); |
24cbae7f RC |
102 | /* we should install a new handler then not check this... |
103 | */ | |
104 | //if (!pkg) | |
105 | //die badly | |
106 | } | |
107 | ||
3c196821 | 108 | packageversion binary = |
0ec7cbc9 | 109 | cygpackage::createInstance (pkgname, inst, f.ver, |
3c196821 RC |
110 | package_installed, |
111 | package_binary); | |
24cbae7f | 112 | |
3c196821 RC |
113 | pkg->add_version (binary); |
114 | pkg->set_installed (binary); | |
bb849dbd | 115 | pkg->desired = pkg->installed; |
7939f6d1 | 116 | } |
24cbae7f | 117 | delete db; |
fa0c0d10 | 118 | db = 0; |
24cbae7f RC |
119 | } |
120 | else | |
121 | // unknown dbversion | |
122 | exit (1); | |
7939f6d1 | 123 | } |
7939f6d1 | 124 | } |
7939f6d1 RC |
125 | } |
126 | ||
7c7034e8 RC |
127 | int |
128 | packagedb::flush () | |
129 | { | |
130 | /* naive approach - just dump the lot */ | |
131 | char const *odbn = "cygfile:///etc/setup/installed.db"; | |
132 | char const *ndbn = "cygfile:///etc/setup/installed.db.new"; | |
133 | ||
134 | io_stream::mkpath_p (PATH_TO_FILE, ndbn); | |
135 | ||
136 | io_stream *ndb = io_stream::open (ndbn, "wb"); | |
137 | ||
3c054baf | 138 | // XXX if this failed, try removing any existing .new database? |
7c7034e8 RC |
139 | if (!ndb) |
140 | return errno ? errno : 1; | |
141 | ||
142 | ndb->write ("INSTALLED.DB 2\n", strlen ("INSTALLED.DB 2\n")); | |
cfae3b8d RC |
143 | for (vector <packagemeta *>::iterator i = packages.begin (); |
144 | i != packages.end (); ++i) | |
7c7034e8 | 145 | { |
cfae3b8d | 146 | packagemeta & pkgm = **i; |
df62e023 RC |
147 | if (pkgm.installed) |
148 | { | |
df62e023 RC |
149 | /* size here is irrelevant - as we can assume that this install source |
150 | * no longer exists, and it does not correlate to used disk space | |
151 | * also note that we are writing a fictional install source | |
152 | * to keep cygcheck happy. | |
153 | */ | |
d19f12fd | 154 | std::string line; |
3c196821 | 155 | line = pkgm.name + " " + pkgm.name + "-" + |
1eb2461a | 156 | std::string(pkgm.installed.Canonical_version()) + ".tar.bz2 0\n"; |
d2a3615c | 157 | ndb->write (line.c_str(), line.size()); |
df62e023 | 158 | } |
7c7034e8 RC |
159 | } |
160 | ||
161 | delete ndb; | |
162 | ||
163 | io_stream::remove (odbn); | |
164 | ||
165 | if (io_stream::move (ndbn, odbn)) | |
166 | return errno ? errno : 1; | |
167 | return 0; | |
168 | } | |
169 | ||
3c196821 RC |
170 | packagemeta * |
171 | packagedb::findBinary (PackageSpecification const &spec) const | |
172 | { | |
cfae3b8d RC |
173 | for (vector <packagemeta *>::iterator n = packages.begin (); |
174 | n != packages.end (); ++n) | |
3c196821 | 175 | { |
cfae3b8d | 176 | packagemeta & pkgm = **n; |
3c196821 RC |
177 | for (set<packageversion>::iterator i=pkgm.versions.begin(); |
178 | i != pkgm.versions.end(); ++i) | |
179 | if (spec.satisfies (*i)) | |
180 | return &pkgm; | |
181 | } | |
182 | return NULL; | |
183 | } | |
184 | ||
185 | packagemeta * | |
186 | packagedb::findSource (PackageSpecification const &spec) const | |
187 | { | |
188 | for (vector <packagemeta *>::iterator n=sourcePackages.begin(); | |
189 | n != sourcePackages.end(); ++n) | |
190 | { | |
191 | for (set<packageversion>::iterator i = (*n)->versions.begin(); | |
192 | i != (*n)->versions.end(); ++i) | |
193 | if (spec.satisfies (*i)) | |
194 | return *n; | |
195 | } | |
196 | return NULL; | |
197 | } | |
198 | ||
df62e023 RC |
199 | int |
200 | packagedb::installeddbread = | |
201 | 0; | |
cfae3b8d | 202 | vector < packagemeta * > packagedb::packages; |
0cf68afd | 203 | packagedb::categoriesType |
4fe323f9 | 204 | packagedb::categories; |
08cd08c3 | 205 | vector <packagemeta *> packagedb::sourcePackages; |
df62e023 RC |
206 | PackageDBActions |
207 | packagedb::task = | |
208 | PackageDB_Install; | |
ad646f43 RC |
209 | std::vector <packagemeta *> |
210 | packagedb::dependencyOrderedPackages; | |
211 | ||
212 | #include "LogSingleton.h" | |
213 | #include <stack> | |
214 | ||
215 | class | |
216 | ConnectedLoopFinder | |
217 | { | |
218 | public: | |
219 | ConnectedLoopFinder(); | |
220 | void doIt(); | |
221 | packagedb db; | |
222 | size_t visited; | |
223 | std::vector<size_t> visitOrder; | |
224 | size_t visit (size_t const nodeToVisit); | |
225 | std::stack<size_t> nodesInStronglyConnectedComponent; | |
226 | }; | |
227 | ||
228 | ConnectedLoopFinder::ConnectedLoopFinder() : visited(0) | |
229 | { | |
230 | for (size_t counter = 0; counter < db.packages.size(); ++counter) | |
231 | visitOrder.push_back(0); | |
232 | } | |
233 | ||
234 | void | |
235 | ConnectedLoopFinder::doIt() | |
236 | { | |
237 | /* XXX this could be done useing a class to hold both the visitedInIteration and the package | |
238 | * meta reference. Then we could use a range, not an int loop. | |
239 | */ | |
240 | for (size_t i = 0; i < db.packages.size(); ++i) | |
241 | { | |
242 | packagemeta &pkg (*db.packages[i]); | |
243 | if (pkg.installed && ! visitOrder[i]) | |
244 | visit (i); | |
245 | } | |
92ef6cf8 BD |
246 | log (LOG_BABBLE) << "Visited: " << visited << " nodes out of " |
247 | << db.packages.size() << " while creating dependency order." | |
248 | << endLog; | |
ad646f43 RC |
249 | } |
250 | ||
251 | static bool | |
252 | checkForInstalled (PackageSpecification *spec) | |
253 | { | |
254 | packagedb db; | |
255 | packagemeta *required = db.findBinary (*spec); | |
256 | if (!required) | |
257 | return false; | |
258 | if (spec->satisfies (required->installed) | |
259 | && required->desired == required->installed ) | |
260 | /* done, found a satisfactory installed version that will remain | |
261 | installed */ | |
262 | return true; | |
263 | return false; | |
264 | } | |
265 | ||
266 | size_t | |
267 | ConnectedLoopFinder::visit(size_t const nodeToVisit) | |
268 | { | |
269 | if (!db.packages[nodeToVisit]->installed) | |
270 | /* Can't visit this node, and it is not less than any visted node */ | |
271 | return db.packages.size() + 1; | |
272 | ++visited; | |
273 | visitOrder[nodeToVisit] = visited; | |
274 | ||
275 | size_t minimumVisitId = visited; | |
276 | nodesInStronglyConnectedComponent.push(nodeToVisit); | |
277 | ||
278 | vector <vector <PackageSpecification *> *>::iterator dp = db.packages[nodeToVisit]->installed.depends ()->begin(); | |
279 | /* walk through each and clause (a link in the graph) */ | |
280 | while (dp != db.packages[nodeToVisit]->installed.depends ()->end()) | |
281 | { | |
282 | /* check each or clause for an installed match */ | |
283 | vector <PackageSpecification *>::iterator i = | |
284 | find_if ((*dp)->begin(), (*dp)->end(), checkForInstalled); | |
285 | if (i != (*dp)->end()) | |
286 | { | |
287 | /* we found an installed ok package */ | |
288 | /* visit it if needed */ | |
289 | /* UGLY. Need to refactor. iterators in the outer would help as we could simply | |
290 | * vist the iterator | |
291 | */ | |
292 | size_t nodeJustVisited = 0; | |
afa76033 | 293 | while (nodeJustVisited < db.packages.size() && casecompare(db.packages[nodeJustVisited]->name, (*i)->packageName())) |
ad646f43 RC |
294 | ++nodeJustVisited; |
295 | if (nodeJustVisited == db.packages.size()) | |
296 | log (LOG_PLAIN) << "Search for package '" << (*i)->packageName() << "' failed." << endLog; | |
297 | else | |
298 | { | |
299 | if (visitOrder[nodeJustVisited]) | |
300 | minimumVisitId = std::min (minimumVisitId, visitOrder[nodeJustVisited]); | |
301 | else | |
302 | minimumVisitId = std::min (minimumVisitId, visit (nodeJustVisited)); | |
303 | } | |
304 | /* next and clause */ | |
305 | ++dp; | |
306 | continue; | |
307 | } | |
308 | /* not installed or not available we ignore */ | |
309 | ++dp; | |
310 | } | |
311 | ||
312 | if (minimumVisitId == visitOrder[nodeToVisit]) | |
313 | { | |
314 | size_t popped; | |
315 | do { | |
316 | popped = nodesInStronglyConnectedComponent.top(); | |
317 | nodesInStronglyConnectedComponent.pop(); | |
318 | db.dependencyOrderedPackages.push_back(db.packages[popped]); | |
319 | /* mark as displayed in a connected component */ | |
320 | visitOrder[popped] = db.packages.size() + 2; | |
321 | } while (popped != nodeToVisit); | |
322 | } | |
323 | ||
324 | return minimumVisitId; | |
325 | } | |
326 | ||
327 | PackageDBConnectedIterator | |
328 | packagedb::connectedBegin() | |
329 | { | |
330 | if (!dependencyOrderedPackages.size()) | |
ad646f43 | 331 | { |
92ef6cf8 BD |
332 | ConnectedLoopFinder doMe; |
333 | doMe.doIt(); | |
334 | std::string s = "Dependency order of packages: "; | |
335 | ||
336 | for (std::vector<packagemeta *>::iterator i = | |
337 | dependencyOrderedPackages.begin(); | |
338 | i != dependencyOrderedPackages.end(); ++i) | |
339 | s = s + (*i)->name + " "; | |
340 | log (LOG_BABBLE) << s << endLog; | |
ad646f43 | 341 | } |
ad646f43 RC |
342 | return dependencyOrderedPackages.begin(); |
343 | } | |
344 | ||
345 | PackageDBConnectedIterator | |
346 | packagedb::connectedEnd() | |
347 | { | |
348 | return dependencyOrderedPackages.end(); | |
349 | } | |
8c242540 RC |
350 | |
351 | void | |
352 | packagedb::markUnVisited() | |
353 | { | |
354 | for (vector <packagemeta *>::iterator n = packages.begin (); | |
355 | n != packages.end (); ++n) | |
356 | { | |
357 | packagemeta & pkgm = **n; | |
358 | pkgm.visited(false); | |
359 | } | |
360 | } | |
31f0ccce RC |
361 | |
362 | void | |
363 | packagedb::setExistence () | |
364 | { | |
365 | /* binary packages */ | |
366 | /* Remove packages that are in the db, not installed, and have no | |
367 | mirror info and are not cached for both binary and source packages. */ | |
368 | vector <packagemeta *>::iterator i = packages.begin (); | |
369 | while (i != packages.end ()) | |
370 | { | |
371 | packagemeta & pkg = **i; | |
372 | if (!pkg.installed && !pkg.accessible() && | |
373 | !pkg.sourceAccessible() ) | |
374 | { | |
375 | packagemeta *pkgm = *i; | |
376 | delete pkgm; | |
377 | i = packages.erase (i); | |
378 | } | |
379 | else | |
380 | ++i; | |
381 | } | |
382 | #if 0 | |
383 | /* remove any source packages which are not accessible */ | |
384 | vector <packagemeta *>::iterator i = db.sourcePackages.begin(); | |
385 | while (i != db.sourcePackages.end()) | |
386 | { | |
387 | packagemeta & pkg = **i; | |
388 | if (!packageAccessible (pkg)) | |
389 | { | |
390 | packagemeta *pkgm = *i; | |
391 | delete pkgm; | |
392 | i = db.sourcePackages.erase (i); | |
393 | } | |
394 | else | |
395 | ++i; | |
396 | } | |
397 | #endif | |
398 | } | |
2f18f94d RC |
399 | |
400 | void | |
401 | packagedb::fillMissingCategory () | |
402 | { | |
403 | for_each(packages.begin(), packages.end(), visit_if(mem_fun(&packagemeta::setDefaultCategories), mem_fun(&packagemeta::hasNoCategories))); | |
404 | for_each(packages.begin(), packages.end(), mem_fun(&packagemeta::addToCategoryAll)); | |
405 | } | |
406 |