if we have zlib builtin insert add a dummy gzip compressor for FileFD
[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::bzip2", "/bin/bzip2");
436 _config->CndSet("Dir::Bin::xz", "/usr/bin/xz");
437 if (FileExists(_config->FindFile("Dir::Bin::xz")) == true) {
438 _config->CndSet("Dir::Bin::lzma", _config->Find("Dir::Bin::xz"));
439 _config->Set("APT::Compressor::lzma::Binary", "xz");
440 if (_config->Exists("APT::Compressor::lzma::CompressArg") == false) {
441 _config->Set("APT::Compressor::lzma::CompressArg::", "--format=lzma");
442 _config->Set("APT::Compressor::lzma::CompressArg::", "-9");
443 }
444 if (_config->Exists("APT::Compressor::lzma::UncompressArg") == false) {
445 _config->Set("APT::Compressor::lzma::UncompressArg::", "--format=lzma");
446 _config->Set("APT::Compressor::lzma::UncompressArg::", "-d");
447 }
448 } else {
449 _config->CndSet("Dir::Bin::lzma", "/usr/bin/lzma");
450 if (_config->Exists("APT::Compressor::lzma::CompressArg") == false) {
451 _config->Set("APT::Compressor::lzma::CompressArg::", "--suffix=");
452 _config->Set("APT::Compressor::lzma::CompressArg::", "-9");
453 }
454 if (_config->Exists("APT::Compressor::lzma::UncompressArg") == false) {
455 _config->Set("APT::Compressor::lzma::UncompressArg::", "--suffix=");
456 _config->Set("APT::Compressor::lzma::UncompressArg::", "-d");
457 }
458 }
459 }
460 /*}}}*/
461 // getCompressors - Return Vector of usbale compressors /*{{{*/
462 // ---------------------------------------------------------------------
463 /* return a vector of compressors used by apt-ftparchive in the
464 multicompress functionality or to detect data.tar files */
465 std::vector<APT::Configuration::Compressor>
466 const Configuration::getCompressors(bool const Cached) {
467 static std::vector<APT::Configuration::Compressor> compressors;
468 if (compressors.empty() == false) {
469 if (Cached == true)
470 return compressors;
471 else
472 compressors.clear();
473 }
474
475 setDefaultConfigurationForCompressors();
476
477 compressors.push_back(Compressor(".", "", "", "", "", 1));
478 if (_config->Exists("Dir::Bin::gzip") == false || FileExists(_config->FindFile("Dir::Bin::gzip")) == true)
479 compressors.push_back(Compressor("gzip",".gz","gzip","-9n","-d",2));
480 #ifdef HAVE_ZLIB
481 else
482 compressors.push_back(Compressor("gzip",".gz","/bin/false", "", "", 2));
483 #endif
484 if (_config->Exists("Dir::Bin::bzip2") == false || FileExists(_config->FindFile("Dir::Bin::bzip2")) == true)
485 compressors.push_back(Compressor("bzip2",".bz2","bzip2","-9","-d",3));
486 if (_config->Exists("Dir::Bin::xz") == false || FileExists(_config->FindFile("Dir::Bin::xz")) == true)
487 compressors.push_back(Compressor("xz",".xz","xz","-6","-d",4));
488 if (_config->Exists("Dir::Bin::lzma") == false || FileExists(_config->FindFile("Dir::Bin::lzma")) == true)
489 compressors.push_back(Compressor("lzma",".lzma","lzma","-9","-d",5));
490
491 std::vector<std::string> const comp = _config->FindVector("APT::Compressor");
492 for (std::vector<std::string>::const_iterator c = comp.begin();
493 c != comp.end(); ++c) {
494 if (*c == "." || *c == "gzip" || *c == "bzip2" || *c == "lzma" || *c == "xz")
495 continue;
496 compressors.push_back(Compressor(c->c_str(), std::string(".").append(*c).c_str(), c->c_str(), "-9", "-d", 100));
497 }
498
499 return compressors;
500 }
501 /*}}}*/
502 // getCompressorExtensions - supported data.tar extensions /*{{{*/
503 // ---------------------------------------------------------------------
504 /* */
505 std::vector<std::string> const Configuration::getCompressorExtensions() {
506 std::vector<APT::Configuration::Compressor> const compressors = getCompressors();
507 std::vector<std::string> ext;
508 for (std::vector<APT::Configuration::Compressor>::const_iterator c = compressors.begin();
509 c != compressors.end(); ++c)
510 if (c->Extension.empty() == false && c->Extension != ".")
511 ext.push_back(c->Extension);
512 return ext;
513 }
514 /*}}}*/
515 // Compressor constructor /*{{{*/
516 // ---------------------------------------------------------------------
517 /* */
518 Configuration::Compressor::Compressor(char const *name, char const *extension,
519 char const *binary,
520 char const *compressArg, char const *uncompressArg,
521 unsigned short const cost) {
522 std::string const config = std::string("APT::Compressor::").append(name).append("::");
523 Name = _config->Find(std::string(config).append("Name"), name);
524 Extension = _config->Find(std::string(config).append("Extension"), extension);
525 Binary = _config->Find(std::string(config).append("Binary"), binary);
526 Cost = _config->FindI(std::string(config).append("Cost"), cost);
527 std::string const compConf = std::string(config).append("CompressArg");
528 if (_config->Exists(compConf) == true)
529 CompressArgs = _config->FindVector(compConf);
530 else if (compressArg != NULL)
531 CompressArgs.push_back(compressArg);
532 std::string const uncompConf = std::string(config).append("UncompressArg");
533 if (_config->Exists(uncompConf) == true)
534 UncompressArgs = _config->FindVector(uncompConf);
535 else if (uncompressArg != NULL)
536 UncompressArgs.push_back(uncompressArg);
537 }
538 /*}}}*/
539 }