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