]>
Commit | Line | Data |
---|---|---|
7939f6d1 RC |
1 | /* |
2 | * Copyright (c) 2001, Robert Collins. | |
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" |
7939f6d1 | 43 | |
6625e635 RC |
44 | using namespace std; |
45 | ||
7939f6d1 RC |
46 | /* static members */ |
47 | ||
7939f6d1 RC |
48 | packagedb::packagedb () |
49 | { | |
df62e023 | 50 | io_stream *db = 0; |
7939f6d1 RC |
51 | if (!installeddbread) |
52 | { | |
53 | /* no parameters. Read in the local installation database. */ | |
54 | db = io_stream::open ("cygfile:///etc/setup/installed.db", "rt"); | |
55 | if (!db) | |
56 | return; | |
57 | /* flush_local_db_package_data */ | |
58 | char line[1000], pkgname[1000], inst[1000], src[1000]; | |
59 | int instsz, srcsz; | |
7939f6d1 | 60 | |
24cbae7f RC |
61 | if (db->gets (line, 1000)) |
62 | { | |
63 | int dbver; | |
64 | sscanf (line, "%s %d", pkgname, &instsz); | |
65 | if (!strcasecmp (pkgname, "INSTALLED.DB") && instsz == 2) | |
66 | dbver = 2; | |
67 | else | |
68 | dbver = 1; | |
69 | delete db; | |
fa0c0d10 | 70 | db = 0; |
24cbae7f RC |
71 | /* Later versions may not use installed.db other than to record the version. */ |
72 | if (dbver == 1 || dbver == 2) | |
7939f6d1 | 73 | { |
24cbae7f RC |
74 | db = |
75 | io_stream::open ("cygfile:///etc/setup/installed.db", "rt"); | |
76 | if (dbver == 2) | |
77 | db->gets (line, 1000); | |
78 | while (db->gets (line, 1000)) | |
7939f6d1 | 79 | { |
24cbae7f | 80 | int parseable; |
4e868a01 RC |
81 | src[0] = '\0'; |
82 | pkgname[0] = '\0'; | |
83 | inst[0] = '\0'; | |
24cbae7f | 84 | srcsz = 0; |
4e868a01 RC |
85 | instsz = 0; |
86 | ||
24cbae7f RC |
87 | sscanf (line, "%s %s %d %s %d", pkgname, inst, &instsz, src, |
88 | &srcsz); | |
4e868a01 RC |
89 | |
90 | if (pkgname[0] == '\0' || inst[0] == '\0') | |
91 | continue; | |
92 | ||
24cbae7f RC |
93 | fileparse f; |
94 | parseable = parse_filename (inst, f); | |
95 | if (!parseable) | |
96 | continue; | |
97 | ||
cfae3b8d | 98 | packagemeta *pkg = findBinary (PackageSpecification(pkgname)); |
24cbae7f RC |
99 | if (!pkg) |
100 | { | |
fa0c0d10 | 101 | pkg = new packagemeta (pkgname, inst); |
cfae3b8d | 102 | packages.push_back (pkg); |
24cbae7f RC |
103 | /* we should install a new handler then not check this... |
104 | */ | |
105 | //if (!pkg) | |
106 | //die badly | |
107 | } | |
108 | ||
3c196821 RC |
109 | packageversion binary = |
110 | cygpackage::createInstance (pkgname, inst, instsz, f.ver, | |
111 | package_installed, | |
112 | package_binary); | |
24cbae7f | 113 | |
3c196821 RC |
114 | pkg->add_version (binary); |
115 | pkg->set_installed (binary); | |
bb849dbd | 116 | pkg->desired = pkg->installed; |
7939f6d1 | 117 | } |
24cbae7f | 118 | delete db; |
fa0c0d10 | 119 | db = 0; |
24cbae7f RC |
120 | } |
121 | else | |
122 | // unknown dbversion | |
123 | exit (1); | |
7939f6d1 | 124 | } |
7b606ae5 | 125 | installeddbread = 1; |
7939f6d1 | 126 | } |
7939f6d1 RC |
127 | } |
128 | ||
7c7034e8 RC |
129 | int |
130 | packagedb::flush () | |
131 | { | |
132 | /* naive approach - just dump the lot */ | |
133 | char const *odbn = "cygfile:///etc/setup/installed.db"; | |
134 | char const *ndbn = "cygfile:///etc/setup/installed.db.new"; | |
135 | ||
136 | io_stream::mkpath_p (PATH_TO_FILE, ndbn); | |
137 | ||
138 | io_stream *ndb = io_stream::open (ndbn, "wb"); | |
139 | ||
3c054baf | 140 | // XXX if this failed, try removing any existing .new database? |
7c7034e8 RC |
141 | if (!ndb) |
142 | return errno ? errno : 1; | |
143 | ||
144 | ndb->write ("INSTALLED.DB 2\n", strlen ("INSTALLED.DB 2\n")); | |
cfae3b8d RC |
145 | for (vector <packagemeta *>::iterator i = packages.begin (); |
146 | i != packages.end (); ++i) | |
7c7034e8 | 147 | { |
cfae3b8d | 148 | packagemeta & pkgm = **i; |
df62e023 RC |
149 | if (pkgm.installed) |
150 | { | |
df62e023 RC |
151 | /* size here is irrelevant - as we can assume that this install source |
152 | * no longer exists, and it does not correlate to used disk space | |
153 | * also note that we are writing a fictional install source | |
154 | * to keep cygcheck happy. | |
155 | */ | |
3c196821 RC |
156 | String line; |
157 | line = pkgm.name + " " + pkgm.name + "-" + | |
158 | pkgm.installed.Canonical_version () + ".tar.bz2 0\n"; | |
159 | ndb->write (line.cstr_oneuse(), line.size()); | |
df62e023 | 160 | } |
7c7034e8 RC |
161 | } |
162 | ||
163 | delete ndb; | |
164 | ||
165 | io_stream::remove (odbn); | |
166 | ||
167 | if (io_stream::move (ndbn, odbn)) | |
168 | return errno ? errno : 1; | |
169 | return 0; | |
170 | } | |
171 | ||
3c196821 RC |
172 | packagemeta * |
173 | packagedb::findBinary (PackageSpecification const &spec) const | |
174 | { | |
cfae3b8d RC |
175 | for (vector <packagemeta *>::iterator n = packages.begin (); |
176 | n != packages.end (); ++n) | |
3c196821 | 177 | { |
cfae3b8d | 178 | packagemeta & pkgm = **n; |
3c196821 RC |
179 | for (set<packageversion>::iterator i=pkgm.versions.begin(); |
180 | i != pkgm.versions.end(); ++i) | |
181 | if (spec.satisfies (*i)) | |
182 | return &pkgm; | |
183 | } | |
184 | return NULL; | |
185 | } | |
186 | ||
187 | packagemeta * | |
188 | packagedb::findSource (PackageSpecification const &spec) const | |
189 | { | |
190 | for (vector <packagemeta *>::iterator n=sourcePackages.begin(); | |
191 | n != sourcePackages.end(); ++n) | |
192 | { | |
193 | for (set<packageversion>::iterator i = (*n)->versions.begin(); | |
194 | i != (*n)->versions.end(); ++i) | |
195 | if (spec.satisfies (*i)) | |
196 | return *n; | |
197 | } | |
198 | return NULL; | |
199 | } | |
200 | ||
df62e023 RC |
201 | int |
202 | packagedb::installeddbread = | |
203 | 0; | |
cfae3b8d | 204 | vector < packagemeta * > packagedb::packages; |
0cf68afd | 205 | packagedb::categoriesType |
4fe323f9 | 206 | packagedb::categories; |
08cd08c3 | 207 | vector <packagemeta *> packagedb::sourcePackages; |
df62e023 RC |
208 | PackageDBActions |
209 | packagedb::task = | |
210 | PackageDB_Install; | |
ad646f43 RC |
211 | std::vector <packagemeta *> |
212 | packagedb::dependencyOrderedPackages; | |
213 | ||
214 | #include "LogSingleton.h" | |
215 | #include <stack> | |
216 | ||
217 | class | |
218 | ConnectedLoopFinder | |
219 | { | |
220 | public: | |
221 | ConnectedLoopFinder(); | |
222 | void doIt(); | |
223 | packagedb db; | |
224 | size_t visited; | |
225 | std::vector<size_t> visitOrder; | |
226 | size_t visit (size_t const nodeToVisit); | |
227 | std::stack<size_t> nodesInStronglyConnectedComponent; | |
228 | }; | |
229 | ||
230 | ConnectedLoopFinder::ConnectedLoopFinder() : visited(0) | |
231 | { | |
232 | for (size_t counter = 0; counter < db.packages.size(); ++counter) | |
233 | visitOrder.push_back(0); | |
234 | } | |
235 | ||
236 | void | |
237 | ConnectedLoopFinder::doIt() | |
238 | { | |
239 | /* XXX this could be done useing a class to hold both the visitedInIteration and the package | |
240 | * meta reference. Then we could use a range, not an int loop. | |
241 | */ | |
242 | for (size_t i = 0; i < db.packages.size(); ++i) | |
243 | { | |
244 | packagemeta &pkg (*db.packages[i]); | |
245 | if (pkg.installed && ! visitOrder[i]) | |
246 | visit (i); | |
247 | } | |
248 | log (LOG_PLAIN) << "Visited: " << visited << " nodes out of " << db.packages.size() << "." << endLog; | |
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; | |
293 | while (nodeJustVisited < db.packages.size() && db.packages[nodeJustVisited]->name.casecompare((*i)->packageName())) | |
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()) | |
331 | { | |
332 | ConnectedLoopFinder doMe; | |
333 | doMe.doIt(); | |
334 | log(LOG_PLAIN) << "Dependency ordered install:" << endLog; | |
335 | for (std::vector<packagemeta *>::iterator i = dependencyOrderedPackages.begin(); | |
336 | i != dependencyOrderedPackages.end(); ++i) | |
337 | { | |
338 | packagemeta &pkg (**i); | |
339 | log(LOG_PLAIN) << pkg.name << endLog; | |
340 | } | |
341 | } | |
342 | ||
343 | return dependencyOrderedPackages.begin(); | |
344 | } | |
345 | ||
346 | PackageDBConnectedIterator | |
347 | packagedb::connectedEnd() | |
348 | { | |
349 | return dependencyOrderedPackages.end(); | |
350 | } |