ensure that architectures are not added multiple times
[ntk/apt.git] / apt-pkg / aptconfiguration.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 /* ######################################################################
4
5 Provide access methods to various configuration settings,
6 setup defaults and returns validate settings.
7
8 ##################################################################### */
9 /*}}}*/
10 // Include Files /*{{{*/
11 #include <config.h>
12
13 #include <apt-pkg/aptconfiguration.h>
14 #include <apt-pkg/configuration.h>
15 #include <apt-pkg/error.h>
16 #include <apt-pkg/fileutl.h>
17 #include <apt-pkg/macros.h>
18 #include <apt-pkg/strutl.h>
19
20 #include <sys/types.h>
21 #include <dirent.h>
22 #include <stdio.h>
23 #include <fcntl.h>
24
25 #include <algorithm>
26 #include <string>
27 #include <vector>
28 /*}}}*/
29 namespace APT {
30 // getCompressionTypes - Return Vector of usbale compressiontypes /*{{{*/
31 // ---------------------------------------------------------------------
32 /* return a vector of compression types in the prefered order. */
33 std::vector<std::string>
34 const Configuration::getCompressionTypes(bool const &Cached) {
35 static std::vector<std::string> types;
36 if (types.empty() == false) {
37 if (Cached == true)
38 return types;
39 else
40 types.clear();
41 }
42
43 // setup the defaults for the compressiontypes => method mapping
44 _config->CndSet("Acquire::CompressionTypes::bz2","bzip2");
45 _config->CndSet("Acquire::CompressionTypes::xz","xz");
46 _config->CndSet("Acquire::CompressionTypes::lzma","lzma");
47 _config->CndSet("Acquire::CompressionTypes::gz","gzip");
48
49 setDefaultConfigurationForCompressors();
50
51 // accept non-list order as override setting for config settings on commandline
52 std::string const overrideOrder = _config->Find("Acquire::CompressionTypes::Order","");
53 if (overrideOrder.empty() == false)
54 types.push_back(overrideOrder);
55
56 // load the order setting into our vector
57 std::vector<std::string> const order = _config->FindVector("Acquire::CompressionTypes::Order");
58 for (std::vector<std::string>::const_iterator o = order.begin();
59 o != order.end(); ++o) {
60 if ((*o).empty() == true)
61 continue;
62 // ignore types we have no method ready to use
63 if (_config->Exists(std::string("Acquire::CompressionTypes::").append(*o)) == false)
64 continue;
65 // ignore types we have no app ready to use
66 std::string const appsetting = std::string("Dir::Bin::").append(*o);
67 if (_config->Exists(appsetting) == true) {
68 std::string const app = _config->FindFile(appsetting.c_str(), "");
69 if (app.empty() == false && FileExists(app) == false)
70 continue;
71 }
72 types.push_back(*o);
73 }
74
75 // move again over the option tree to add all missing compression types
76 ::Configuration::Item const *Types = _config->Tree("Acquire::CompressionTypes");
77 if (Types != 0)
78 Types = Types->Child;
79
80 for (; Types != 0; Types = Types->Next) {
81 if (Types->Tag == "Order" || Types->Tag.empty() == true)
82 continue;
83 // ignore types we already have in the vector
84 if (std::find(types.begin(),types.end(),Types->Tag) != types.end())
85 continue;
86 // ignore types we have no app ready to use
87 std::string const appsetting = std::string("Dir::Bin::").append(Types->Value);
88 if (appsetting.empty() == false && _config->Exists(appsetting) == true) {
89 std::string const app = _config->FindFile(appsetting.c_str(), "");
90 if (app.empty() == false && FileExists(app) == false)
91 continue;
92 }
93 types.push_back(Types->Tag);
94 }
95
96 // add the special "uncompressed" type
97 if (std::find(types.begin(), types.end(), "uncompressed") == types.end())
98 {
99 std::string const uncompr = _config->FindFile("Dir::Bin::uncompressed", "");
100 if (uncompr.empty() == true || FileExists(uncompr) == true)
101 types.push_back("uncompressed");
102 }
103
104 return types;
105 }
106 /*}}}*/
107 // GetLanguages - Return Vector of Language Codes /*{{{*/
108 // ---------------------------------------------------------------------
109 /* return a vector of language codes in the prefered order.
110 the special word "environment" will be replaced with the long and the short
111 code of the local settings and it will be insured that this will not add
112 duplicates. So in an german local the setting "environment, de_DE, en, de"
113 will result in "de_DE, de, en".
114 The special word "none" is the stopcode for the not-All code vector */
115 std::vector<std::string> const Configuration::getLanguages(bool const &All,
116 bool const &Cached, char const ** const Locale) {
117 using std::string;
118
119 // The detection is boring and has a lot of cornercases,
120 // so we cache the results to calculated it only once.
121 std::vector<string> static allCodes;
122 std::vector<string> static codes;
123
124 // we have something in the cache
125 if (codes.empty() == false || allCodes.empty() == false) {
126 if (Cached == true) {
127 if(All == true && allCodes.empty() == false)
128 return allCodes;
129 else
130 return codes;
131 } else {
132 allCodes.clear();
133 codes.clear();
134 }
135 }
136
137 // Include all Language codes we have a Translation file for in /var/lib/apt/lists
138 // so they will be all included in the Cache.
139 std::vector<string> builtin;
140 DIR *D = opendir(_config->FindDir("Dir::State::lists").c_str());
141 if (D != 0) {
142 builtin.push_back("none");
143 for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D)) {
144 string const name = Ent->d_name;
145 size_t const foundDash = name.rfind("-");
146 size_t const foundUnderscore = name.rfind("_", foundDash);
147 if (foundDash == string::npos || foundUnderscore == string::npos ||
148 foundDash <= foundUnderscore ||
149 name.substr(foundUnderscore+1, foundDash-(foundUnderscore+1)) != "Translation")
150 continue;
151 string const c = name.substr(foundDash+1);
152 if (unlikely(c.empty() == true) || c == "en")
153 continue;
154 // Skip unusual files, like backups or that alike
155 string::const_iterator s = c.begin();
156 for (;s != c.end(); ++s) {
157 if (isalpha(*s) == 0 && *s != '_')
158 break;
159 }
160 if (s != c.end())
161 continue;
162 if (std::find(builtin.begin(), builtin.end(), c) != builtin.end())
163 continue;
164 builtin.push_back(c);
165 }
166 }
167 closedir(D);
168
169 // FIXME: Remove support for the old APT::Acquire::Translation
170 // it was undocumented and so it should be not very widthly used
171 string const oldAcquire = _config->Find("APT::Acquire::Translation","");
172 if (oldAcquire.empty() == false && oldAcquire != "environment") {
173 // TRANSLATORS: the two %s are APT configuration options
174 _error->Notice("Option '%s' is deprecated. Please use '%s' instead, see 'man 5 apt.conf' for details.",
175 "APT::Acquire::Translation", "Acquire::Languages");
176 if (oldAcquire != "none")
177 codes.push_back(oldAcquire);
178 codes.push_back("en");
179 allCodes = codes;
180 for (std::vector<string>::const_iterator b = builtin.begin();
181 b != builtin.end(); ++b)
182 if (std::find(allCodes.begin(), allCodes.end(), *b) == allCodes.end())
183 allCodes.push_back(*b);
184 if (All == true)
185 return allCodes;
186 else
187 return codes;
188 }
189
190 // get the environment language codes: LC_MESSAGES (and later LANGUAGE)
191 // we extract both, a long and a short code and then we will
192 // check if we actually need both (rare) or if the short is enough
193 string const envMsg = string(Locale == 0 ? std::setlocale(LC_MESSAGES, NULL) : *Locale);
194 size_t const lenShort = (envMsg.find('_') != string::npos) ? envMsg.find('_') : 2;
195 size_t const lenLong = (envMsg.find_first_of(".@") != string::npos) ? envMsg.find_first_of(".@") : (lenShort + 3);
196
197 string const envLong = envMsg.substr(0,lenLong);
198 string const envShort = envLong.substr(0,lenShort);
199
200 // It is very likely we will need the environment codes later,
201 // so let us generate them now from LC_MESSAGES and LANGUAGE
202 std::vector<string> environment;
203 if (envShort != "C") {
204 // take care of LC_MESSAGES
205 if (envLong != envShort)
206 environment.push_back(envLong);
207 environment.push_back(envShort);
208 // take care of LANGUAGE
209 const char *language_env = getenv("LANGUAGE") == 0 ? "" : getenv("LANGUAGE");
210 string envLang = Locale == 0 ? language_env : *(Locale+1);
211 if (envLang.empty() == false) {
212 std::vector<string> env = VectorizeString(envLang,':');
213 short addedLangs = 0; // add a maximum of 3 fallbacks from the environment
214 for (std::vector<string>::const_iterator e = env.begin();
215 e != env.end() && addedLangs < 3; ++e) {
216 if (unlikely(e->empty() == true) || *e == "en")
217 continue;
218 if (*e == envLong || *e == envShort)
219 continue;
220 if (std::find(environment.begin(), environment.end(), *e) != environment.end())
221 continue;
222 ++addedLangs;
223 environment.push_back(*e);
224 }
225 }
226 } else {
227 environment.push_back("en");
228 }
229
230 // Support settings like Acquire::Languages=none on the command line to
231 // override the configuration settings vector of languages.
232 string const forceLang = _config->Find("Acquire::Languages","");
233 if (forceLang.empty() == false) {
234 if (forceLang == "environment") {
235 codes = environment;
236 } else if (forceLang != "none")
237 codes.push_back(forceLang);
238 else //if (forceLang == "none")
239 builtin.clear();
240 allCodes = codes;
241 for (std::vector<string>::const_iterator b = builtin.begin();
242 b != builtin.end(); ++b)
243 if (std::find(allCodes.begin(), allCodes.end(), *b) == allCodes.end())
244 allCodes.push_back(*b);
245 if (All == true)
246 return allCodes;
247 else
248 return codes;
249 }
250
251 // cornercase: LANG=C, so we use only "en" Translation
252 if (envShort == "C") {
253 allCodes = codes = environment;
254 allCodes.insert(allCodes.end(), builtin.begin(), builtin.end());
255 if (All == true)
256 return allCodes;
257 else
258 return codes;
259 }
260
261 std::vector<string> const lang = _config->FindVector("Acquire::Languages");
262 // the default setting -> "environment, en"
263 if (lang.empty() == true) {
264 codes = environment;
265 if (envShort != "en")
266 codes.push_back("en");
267 allCodes = codes;
268 for (std::vector<string>::const_iterator b = builtin.begin();
269 b != builtin.end(); ++b)
270 if (std::find(allCodes.begin(), allCodes.end(), *b) == allCodes.end())
271 allCodes.push_back(*b);
272 if (All == true)
273 return allCodes;
274 else
275 return codes;
276 }
277
278 // the configs define the order, so add the environment
279 // then needed and ensure the codes are not listed twice.
280 bool noneSeen = false;
281 for (std::vector<string>::const_iterator l = lang.begin();
282 l != lang.end(); ++l) {
283 if (*l == "environment") {
284 for (std::vector<string>::const_iterator e = environment.begin();
285 e != environment.end(); ++e) {
286 if (std::find(allCodes.begin(), allCodes.end(), *e) != allCodes.end())
287 continue;
288 if (noneSeen == false)
289 codes.push_back(*e);
290 allCodes.push_back(*e);
291 }
292 continue;
293 } else if (*l == "none") {
294 noneSeen = true;
295 continue;
296 } else if (std::find(allCodes.begin(), allCodes.end(), *l) != allCodes.end())
297 continue;
298
299 if (noneSeen == false)
300 codes.push_back(*l);
301 allCodes.push_back(*l);
302 }
303
304 for (std::vector<string>::const_iterator b = builtin.begin();
305 b != builtin.end(); ++b)
306 if (std::find(allCodes.begin(), allCodes.end(), *b) == allCodes.end())
307 allCodes.push_back(*b);
308
309 if (All == true)
310 return allCodes;
311 else
312 return codes;
313 }
314 /*}}}*/
315 // getArchitectures - Return Vector of prefered Architectures /*{{{*/
316 std::vector<std::string> const Configuration::getArchitectures(bool const &Cached) {
317 using std::string;
318
319 std::vector<string> static archs;
320 if (likely(Cached == true) && archs.empty() == false)
321 return archs;
322
323 string const arch = _config->Find("APT::Architecture");
324 archs = _config->FindVector("APT::Architectures");
325
326 if (unlikely(arch.empty() == true))
327 return archs;
328
329 // FIXME: It is a bit unclean to have debian specific code here…
330 if (archs.empty() == true) {
331 archs.push_back(arch);
332
333 // Generate the base argument list for dpkg
334 std::vector<const char *> Args;
335 string Tmp = _config->Find("Dir::Bin::dpkg","dpkg");
336 {
337 string const dpkgChrootDir = _config->FindDir("DPkg::Chroot-Directory", "/");
338 size_t dpkgChrootLen = dpkgChrootDir.length();
339 if (dpkgChrootDir != "/" && Tmp.find(dpkgChrootDir) == 0) {
340 if (dpkgChrootDir[dpkgChrootLen - 1] == '/')
341 --dpkgChrootLen;
342 Tmp = Tmp.substr(dpkgChrootLen);
343 }
344 }
345 Args.push_back(Tmp.c_str());
346
347 // Stick in any custom dpkg options
348 ::Configuration::Item const *Opts = _config->Tree("DPkg::Options");
349 if (Opts != 0) {
350 Opts = Opts->Child;
351 for (; Opts != 0; Opts = Opts->Next)
352 {
353 if (Opts->Value.empty() == true)
354 continue;
355 Args.push_back(Opts->Value.c_str());
356 }
357 }
358
359 Args.push_back("--print-foreign-architectures");
360 Args.push_back(NULL);
361
362 int external[2] = {-1, -1};
363 if (pipe(external) != 0)
364 {
365 _error->WarningE("getArchitecture", "Can't create IPC pipe for dpkg --print-foreign-architectures");
366 return archs;
367 }
368
369 pid_t dpkgMultiArch = ExecFork();
370 if (dpkgMultiArch == 0) {
371 close(external[0]);
372 std::string const chrootDir = _config->FindDir("DPkg::Chroot-Directory");
373 if (chrootDir != "/" && chroot(chrootDir.c_str()) != 0)
374 _error->WarningE("getArchitecture", "Couldn't chroot into %s for dpkg --print-foreign-architectures", chrootDir.c_str());
375 int const nullfd = open("/dev/null", O_RDONLY);
376 dup2(nullfd, STDIN_FILENO);
377 dup2(external[1], STDOUT_FILENO);
378 dup2(nullfd, STDERR_FILENO);
379 execvp(Args[0], (char**) &Args[0]);
380 _error->WarningE("getArchitecture", "Can't detect foreign architectures supported by dpkg!");
381 _exit(100);
382 }
383 close(external[1]);
384
385 FILE *dpkg = fdopen(external[0], "r");
386 char buf[1024];
387 if(dpkg != NULL) {
388 while (fgets(buf, sizeof(buf), dpkg) != NULL) {
389 char* arch = strtok(buf, " ");
390 while (arch != NULL) {
391 for (; isspace(*arch) != 0; ++arch);
392 if (arch[0] != '\0') {
393 char const* archend = arch;
394 for (; isspace(*archend) == 0 && *archend != '\0'; ++archend);
395 string a(arch, (archend - arch));
396 if (std::find(archs.begin(), archs.end(), a) == archs.end())
397 archs.push_back(a);
398 }
399 arch = strtok(NULL, " ");
400 }
401 }
402 fclose(dpkg);
403 }
404 ExecWait(dpkgMultiArch, "dpkg --print-foreign-architectures", true);
405 return archs;
406 }
407
408 if (archs.empty() == true ||
409 std::find(archs.begin(), archs.end(), arch) == archs.end())
410 archs.insert(archs.begin(), arch);
411
412 // erase duplicates and empty strings
413 for (std::vector<string>::reverse_iterator a = archs.rbegin();
414 a != archs.rend(); ++a) {
415 if (a->empty() == true || std::find(a + 1, archs.rend(), *a) != archs.rend())
416 archs.erase(a.base()-1);
417 if (a == archs.rend())
418 break;
419 }
420
421 return archs;
422 }
423 /*}}}*/
424 // checkArchitecture - are we interested in the given Architecture? /*{{{*/
425 bool const Configuration::checkArchitecture(std::string const &Arch) {
426 if (Arch == "all")
427 return true;
428 std::vector<std::string> const archs = getArchitectures(true);
429 return (std::find(archs.begin(), archs.end(), Arch) != archs.end());
430 }
431 /*}}}*/
432 // setDefaultConfigurationForCompressors /*{{{*/
433 void Configuration::setDefaultConfigurationForCompressors() {
434 // Set default application paths to check for optional compression types
435 _config->CndSet("Dir::Bin::lzma", "/usr/bin/lzma");
436 _config->CndSet("Dir::Bin::xz", "/usr/bin/xz");
437 _config->CndSet("Dir::Bin::bzip2", "/bin/bzip2");
438 }
439 /*}}}*/
440 // getCompressors - Return Vector of usbale compressors /*{{{*/
441 // ---------------------------------------------------------------------
442 /* return a vector of compressors used by apt-ftparchive in the
443 multicompress functionality or to detect data.tar files */
444 std::vector<APT::Configuration::Compressor>
445 const Configuration::getCompressors(bool const Cached) {
446 static std::vector<APT::Configuration::Compressor> compressors;
447 if (compressors.empty() == false) {
448 if (Cached == true)
449 return compressors;
450 else
451 compressors.clear();
452 }
453
454 setDefaultConfigurationForCompressors();
455
456 compressors.push_back(Compressor(".", "", "", "", "", 1));
457 if (_config->Exists("Dir::Bin::gzip") == false || FileExists(_config->FindFile("Dir::Bin::gzip")) == true)
458 compressors.push_back(Compressor("gzip",".gz","gzip","-9n","-d",2));
459 if (_config->Exists("Dir::Bin::bzip2") == false || FileExists(_config->FindFile("Dir::Bin::bzip2")) == true)
460 compressors.push_back(Compressor("bzip2",".bz2","bzip2","-9","-d",3));
461 if (_config->Exists("Dir::Bin::lzma") == false || FileExists(_config->FindFile("Dir::Bin::lzma")) == true)
462 compressors.push_back(Compressor("lzma",".lzma","lzma","-9","-d",4));
463 if (_config->Exists("Dir::Bin::xz") == false || FileExists(_config->FindFile("Dir::Bin::xz")) == true)
464 compressors.push_back(Compressor("xz",".xz","xz","-6","-d",5));
465
466 std::vector<std::string> const comp = _config->FindVector("APT::Compressor");
467 for (std::vector<std::string>::const_iterator c = comp.begin();
468 c != comp.end(); ++c) {
469 if (*c == "." || *c == "gzip" || *c == "bzip2" || *c == "lzma" || *c == "xz")
470 continue;
471 compressors.push_back(Compressor(c->c_str(), std::string(".").append(*c).c_str(), c->c_str(), "-9", "-d", 100));
472 }
473
474 return compressors;
475 }
476 /*}}}*/
477 // getCompressorExtensions - supported data.tar extensions /*{{{*/
478 // ---------------------------------------------------------------------
479 /* */
480 std::vector<std::string> const Configuration::getCompressorExtensions() {
481 std::vector<APT::Configuration::Compressor> const compressors = getCompressors();
482 std::vector<std::string> ext;
483 for (std::vector<APT::Configuration::Compressor>::const_iterator c = compressors.begin();
484 c != compressors.end(); ++c)
485 if (c->Extension.empty() == false && c->Extension != ".")
486 ext.push_back(c->Extension);
487 return ext;
488 }
489 /*}}}*/
490 // Compressor constructor /*{{{*/
491 // ---------------------------------------------------------------------
492 /* */
493 Configuration::Compressor::Compressor(char const *name, char const *extension,
494 char const *binary,
495 char const *compressArg, char const *uncompressArg,
496 unsigned short const cost) {
497 std::string const config = std::string("APT:Compressor::").append(name).append("::");
498 Name = _config->Find(std::string(config).append("Name"), name);
499 Extension = _config->Find(std::string(config).append("Extension"), extension);
500 Binary = _config->Find(std::string(config).append("Binary"), binary);
501 Cost = _config->FindI(std::string(config).append("Cost"), cost);
502 std::string const compConf = std::string(config).append("CompressArg");
503 if (_config->Exists(compConf) == true)
504 CompressArgs = _config->FindVector(compConf);
505 else if (compressArg != NULL)
506 CompressArgs.push_back(compressArg);
507 std::string const uncompConf = std::string(config).append("UncompressArg");
508 if (_config->Exists(uncompConf) == true)
509 UncompressArgs = _config->FindVector(uncompConf);
510 else if (uncompressArg != NULL)
511 UncompressArgs.push_back(uncompressArg);
512 }
513 /*}}}*/
514 }