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