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