]> git.saurik.com Git - apt.git/blob - apt-pkg/aptconfiguration.cc
cleanup
[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 /*}}}*/
33 namespace APT {
34 // setDefaultConfigurationForCompressors /*{{{*/
35 static void setDefaultConfigurationForCompressors() {
36 // Set default application paths to check for optional compression types
37 _config->CndSet("Dir::Bin::bzip2", "/bin/bzip2");
38 _config->CndSet("Dir::Bin::xz", "/usr/bin/xz");
39 if (FileExists(_config->FindFile("Dir::Bin::xz")) == true) {
40 _config->Set("Dir::Bin::lzma", _config->FindFile("Dir::Bin::xz"));
41 _config->Set("APT::Compressor::lzma::Binary", "xz");
42 if (_config->Exists("APT::Compressor::lzma::CompressArg") == false) {
43 _config->Set("APT::Compressor::lzma::CompressArg::", "--format=lzma");
44 _config->Set("APT::Compressor::lzma::CompressArg::", "-9");
45 }
46 if (_config->Exists("APT::Compressor::lzma::UncompressArg") == false) {
47 _config->Set("APT::Compressor::lzma::UncompressArg::", "--format=lzma");
48 _config->Set("APT::Compressor::lzma::UncompressArg::", "-d");
49 }
50 } else {
51 _config->CndSet("Dir::Bin::lzma", "/usr/bin/lzma");
52 if (_config->Exists("APT::Compressor::lzma::CompressArg") == false) {
53 _config->Set("APT::Compressor::lzma::CompressArg::", "--suffix=");
54 _config->Set("APT::Compressor::lzma::CompressArg::", "-9");
55 }
56 if (_config->Exists("APT::Compressor::lzma::UncompressArg") == false) {
57 _config->Set("APT::Compressor::lzma::UncompressArg::", "--suffix=");
58 _config->Set("APT::Compressor::lzma::UncompressArg::", "-d");
59 }
60 }
61 }
62 /*}}}*/
63 // getCompressionTypes - Return Vector of usable compressiontypes /*{{{*/
64 // ---------------------------------------------------------------------
65 /* return a vector of compression types in the preferred order. */
66 std::vector<std::string>
67 const Configuration::getCompressionTypes(bool const &Cached) {
68 static std::vector<std::string> types;
69 if (types.empty() == false) {
70 if (Cached == true)
71 return types;
72 else
73 types.clear();
74 }
75
76 // setup the defaults for the compressiontypes => method mapping
77 _config->CndSet("Acquire::CompressionTypes::bz2","bzip2");
78 _config->CndSet("Acquire::CompressionTypes::xz","xz");
79 _config->CndSet("Acquire::CompressionTypes::lzma","lzma");
80 _config->CndSet("Acquire::CompressionTypes::gz","gzip");
81
82 setDefaultConfigurationForCompressors();
83 std::vector<APT::Configuration::Compressor> const compressors = getCompressors();
84
85 // load the order setting into our vector
86 std::vector<std::string> const order = _config->FindVector("Acquire::CompressionTypes::Order");
87 for (std::vector<std::string>::const_iterator o = order.begin();
88 o != order.end(); ++o) {
89 if ((*o).empty() == true)
90 continue;
91 // ignore types we have no method ready to use
92 std::string const method = std::string("Acquire::CompressionTypes::").append(*o);
93 if (_config->Exists(method) == false)
94 continue;
95 // ignore types we have no app ready to use
96 std::string const app = _config->Find(method);
97 std::vector<APT::Configuration::Compressor>::const_iterator c = compressors.begin();
98 for (; c != compressors.end(); ++c)
99 if (c->Name == app)
100 break;
101 if (c == compressors.end())
102 continue;
103 types.push_back(*o);
104 }
105
106 // move again over the option tree to add all missing compression types
107 ::Configuration::Item const *Types = _config->Tree("Acquire::CompressionTypes");
108 if (Types != 0)
109 Types = Types->Child;
110
111 for (; Types != 0; Types = Types->Next) {
112 if (Types->Tag == "Order" || Types->Tag.empty() == true)
113 continue;
114 // ignore types we already have in the vector
115 if (std::find(types.begin(),types.end(),Types->Tag) != types.end())
116 continue;
117 // ignore types we have no app ready to use
118 std::vector<APT::Configuration::Compressor>::const_iterator c = compressors.begin();
119 for (; c != compressors.end(); ++c)
120 if (c->Name == Types->Value)
121 break;
122 if (c == compressors.end())
123 continue;
124 types.push_back(Types->Tag);
125 }
126
127 // add the special "uncompressed" type
128 if (std::find(types.begin(), types.end(), "uncompressed") == types.end())
129 {
130 std::string const uncompr = _config->FindFile("Dir::Bin::uncompressed", "");
131 if (uncompr.empty() == true || FileExists(uncompr) == true)
132 types.push_back("uncompressed");
133 }
134
135 return types;
136 }
137 /*}}}*/
138 // GetLanguages - Return Vector of Language Codes /*{{{*/
139 // ---------------------------------------------------------------------
140 /* return a vector of language codes in the preferred order.
141 the special word "environment" will be replaced with the long and the short
142 code of the local settings and it will be insured that this will not add
143 duplicates. So in an german local the setting "environment, de_DE, en, de"
144 will result in "de_DE, de, en".
145 The special word "none" is the stopcode for the not-All code vector */
146 std::vector<std::string> const Configuration::getLanguages(bool const &All,
147 bool const &Cached, char const ** const Locale) {
148 using std::string;
149
150 // The detection is boring and has a lot of cornercases,
151 // so we cache the results to calculated it only once.
152 std::vector<string> static allCodes;
153 std::vector<string> static codes;
154
155 // we have something in the cache
156 if (codes.empty() == false || allCodes.empty() == false) {
157 if (Cached == true) {
158 if(All == true && allCodes.empty() == false)
159 return allCodes;
160 else
161 return codes;
162 } else {
163 allCodes.clear();
164 codes.clear();
165 }
166 }
167
168 // Include all Language codes we have a Translation file for in /var/lib/apt/lists
169 // so they will be all included in the Cache.
170 std::vector<string> builtin;
171 DIR *D = opendir(_config->FindDir("Dir::State::lists").c_str());
172 if (D != NULL) {
173 builtin.push_back("none");
174 for (struct dirent *Ent = readdir(D); Ent != 0; Ent = readdir(D)) {
175 string const name = SubstVar(Ent->d_name, "%5f", "_");
176 size_t const foundDash = name.rfind("-");
177 size_t const foundUnderscore = name.rfind("_", foundDash);
178 if (foundDash == string::npos || foundUnderscore == string::npos ||
179 foundDash <= foundUnderscore ||
180 name.substr(foundUnderscore+1, foundDash-(foundUnderscore+1)) != "Translation")
181 continue;
182 string const c = name.substr(foundDash+1);
183 if (unlikely(c.empty() == true) || c == "en")
184 continue;
185 // Skip unusual files, like backups or that alike
186 string::const_iterator s = c.begin();
187 for (;s != c.end(); ++s) {
188 if (isalpha(*s) == 0 && *s != '_')
189 break;
190 }
191 if (s != c.end())
192 continue;
193 if (std::find(builtin.begin(), builtin.end(), c) != builtin.end())
194 continue;
195 builtin.push_back(c);
196 }
197 closedir(D);
198 }
199
200 // FIXME: Remove support for the old APT::Acquire::Translation
201 // it was undocumented and so it should be not very widthly used
202 string const oldAcquire = _config->Find("APT::Acquire::Translation","");
203 if (oldAcquire.empty() == false && oldAcquire != "environment") {
204 // TRANSLATORS: the two %s are APT configuration options
205 _error->Notice("Option '%s' is deprecated. Please use '%s' instead, see 'man 5 apt.conf' for details.",
206 "APT::Acquire::Translation", "Acquire::Languages");
207 if (oldAcquire != "none")
208 codes.push_back(oldAcquire);
209 codes.push_back("en");
210 allCodes = codes;
211 for (std::vector<string>::const_iterator b = builtin.begin();
212 b != builtin.end(); ++b)
213 if (std::find(allCodes.begin(), allCodes.end(), *b) == allCodes.end())
214 allCodes.push_back(*b);
215 if (All == true)
216 return allCodes;
217 else
218 return codes;
219 }
220
221 // get the environment language codes: LC_MESSAGES (and later LANGUAGE)
222 // we extract both, a long and a short code and then we will
223 // check if we actually need both (rare) or if the short is enough
224 string const envMsg = string(Locale == 0 ? ::setlocale(LC_MESSAGES, NULL) : *Locale);
225 size_t const lenShort = (envMsg.find('_') != string::npos) ? envMsg.find('_') : 2;
226 size_t const lenLong = (envMsg.find_first_of(".@") != string::npos) ? envMsg.find_first_of(".@") : (lenShort + 3);
227
228 string const envLong = envMsg.substr(0,lenLong);
229 string const envShort = envLong.substr(0,lenShort);
230
231 // It is very likely we will need the environment codes later,
232 // so let us generate them now from LC_MESSAGES and LANGUAGE
233 std::vector<string> environment;
234 if (envShort != "C") {
235 // take care of LC_MESSAGES
236 if (envLong != envShort)
237 environment.push_back(envLong);
238 environment.push_back(envShort);
239 // take care of LANGUAGE
240 const char *language_env = getenv("LANGUAGE") == 0 ? "" : getenv("LANGUAGE");
241 string envLang = Locale == 0 ? language_env : *(Locale+1);
242 if (envLang.empty() == false) {
243 std::vector<string> env = VectorizeString(envLang,':');
244 short addedLangs = 0; // add a maximum of 3 fallbacks from the environment
245 for (std::vector<string>::const_iterator e = env.begin();
246 e != env.end() && addedLangs < 3; ++e) {
247 if (unlikely(e->empty() == true) || *e == "en")
248 continue;
249 if (*e == envLong || *e == envShort)
250 continue;
251 if (std::find(environment.begin(), environment.end(), *e) != environment.end())
252 continue;
253 ++addedLangs;
254 environment.push_back(*e);
255 }
256 }
257 } else {
258 // cornercase: LANG=C, so we use only "en" Translation
259 environment.push_back("en");
260 }
261
262 std::vector<string> const lang = _config->FindVector("Acquire::Languages", "environment,en");
263 // the configs define the order, so add the environment
264 // then needed and ensure the codes are not listed twice.
265 bool noneSeen = false;
266 for (std::vector<string>::const_iterator l = lang.begin();
267 l != lang.end(); ++l) {
268 if (*l == "environment") {
269 for (std::vector<string>::const_iterator e = environment.begin();
270 e != environment.end(); ++e) {
271 if (std::find(allCodes.begin(), allCodes.end(), *e) != allCodes.end())
272 continue;
273 if (noneSeen == false)
274 codes.push_back(*e);
275 allCodes.push_back(*e);
276 }
277 continue;
278 } else if (*l == "none") {
279 noneSeen = true;
280 continue;
281 } else if (std::find(allCodes.begin(), allCodes.end(), *l) != allCodes.end())
282 continue;
283
284 if (noneSeen == false)
285 codes.push_back(*l);
286 allCodes.push_back(*l);
287 }
288
289 if (allCodes.empty() == false) {
290 for (std::vector<string>::const_iterator b = builtin.begin();
291 b != builtin.end(); ++b)
292 if (std::find(allCodes.begin(), allCodes.end(), *b) == allCodes.end())
293 allCodes.push_back(*b);
294 } else {
295 // "none" was forced
296 allCodes.push_back("none");
297 }
298
299 if (All == true)
300 return allCodes;
301 else
302 return codes;
303 }
304 /*}}}*/
305 // checkLanguage - are we interested in the given Language? /*{{{*/
306 bool Configuration::checkLanguage(std::string Lang, bool const All) {
307 // the empty Language is always interesting as it is the original
308 if (Lang.empty() == true)
309 return true;
310 // filenames are encoded, so undo this
311 Lang = SubstVar(Lang, "%5f", "_");
312 std::vector<std::string> const langs = getLanguages(All, true);
313 return (std::find(langs.begin(), langs.end(), Lang) != langs.end());
314 }
315 /*}}}*/
316 // getArchitectures - Return Vector of preferred 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 int const nullfd = open("/dev/null", O_RDONLY);
375 dup2(nullfd, STDIN_FILENO);
376 dup2(external[1], STDOUT_FILENO);
377 dup2(nullfd, STDERR_FILENO);
378 if (chrootDir != "/" && chroot(chrootDir.c_str()) != 0 && chdir("/") != 0)
379 _error->WarningE("getArchitecture", "Couldn't chroot into %s for dpkg --print-foreign-architectures", chrootDir.c_str());
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 if(dpkg != NULL) {
388 char buf[1024];
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 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 // getCompressors - Return Vector of usealbe compressors /*{{{*/
434 // ---------------------------------------------------------------------
435 /* return a vector of compressors used by apt-ftparchive in the
436 multicompress functionality or to detect data.tar files */
437 std::vector<APT::Configuration::Compressor>
438 const Configuration::getCompressors(bool const Cached) {
439 static std::vector<APT::Configuration::Compressor> compressors;
440 if (compressors.empty() == false) {
441 if (Cached == true)
442 return compressors;
443 else
444 compressors.clear();
445 }
446
447 setDefaultConfigurationForCompressors();
448
449 compressors.push_back(Compressor(".", "", "", NULL, NULL, 1));
450 if (_config->Exists("Dir::Bin::gzip") == false || FileExists(_config->FindFile("Dir::Bin::gzip")) == true)
451 compressors.push_back(Compressor("gzip",".gz","gzip","-9n","-d",2));
452 #ifdef HAVE_ZLIB
453 else
454 compressors.push_back(Compressor("gzip",".gz","false", NULL, NULL, 2));
455 #endif
456 if (_config->Exists("Dir::Bin::bzip2") == false || FileExists(_config->FindFile("Dir::Bin::bzip2")) == true)
457 compressors.push_back(Compressor("bzip2",".bz2","bzip2","-9","-d",3));
458 #ifdef HAVE_BZ2
459 else
460 compressors.push_back(Compressor("bzip2",".bz2","false", NULL, NULL, 3));
461 #endif
462 if (_config->Exists("Dir::Bin::xz") == false || FileExists(_config->FindFile("Dir::Bin::xz")) == true)
463 compressors.push_back(Compressor("xz",".xz","xz","-6","-d",4));
464 #ifdef HAVE_LZMA
465 else
466 compressors.push_back(Compressor("xz",".xz","false", NULL, NULL, 4));
467 #endif
468 if (_config->Exists("Dir::Bin::lzma") == false || FileExists(_config->FindFile("Dir::Bin::lzma")) == true)
469 compressors.push_back(Compressor("lzma",".lzma","lzma","-9","-d",5));
470 #ifdef HAVE_LZMA
471 else
472 compressors.push_back(Compressor("lzma",".lzma","false", NULL, NULL, 5));
473 #endif
474
475 std::vector<std::string> const comp = _config->FindVector("APT::Compressor");
476 for (std::vector<std::string>::const_iterator c = comp.begin();
477 c != comp.end(); ++c) {
478 if (c->empty() || *c == "." || *c == "gzip" || *c == "bzip2" || *c == "lzma" || *c == "xz")
479 continue;
480 compressors.push_back(Compressor(c->c_str(), std::string(".").append(*c).c_str(), c->c_str(), "-9", "-d", 100));
481 }
482
483 return compressors;
484 }
485 /*}}}*/
486 // getCompressorExtensions - supported data.tar extensions /*{{{*/
487 // ---------------------------------------------------------------------
488 /* */
489 std::vector<std::string> const Configuration::getCompressorExtensions() {
490 std::vector<APT::Configuration::Compressor> const compressors = getCompressors();
491 std::vector<std::string> ext;
492 for (std::vector<APT::Configuration::Compressor>::const_iterator c = compressors.begin();
493 c != compressors.end(); ++c)
494 if (c->Extension.empty() == false && c->Extension != ".")
495 ext.push_back(c->Extension);
496 return ext;
497 }
498 /*}}}*/
499 // Compressor constructor /*{{{*/
500 // ---------------------------------------------------------------------
501 /* */
502 Configuration::Compressor::Compressor(char const *name, char const *extension,
503 char const *binary,
504 char const *compressArg, char const *uncompressArg,
505 unsigned short const cost) {
506 std::string const config = std::string("APT::Compressor::").append(name).append("::");
507 Name = _config->Find(std::string(config).append("Name"), name);
508 Extension = _config->Find(std::string(config).append("Extension"), extension);
509 Binary = _config->Find(std::string(config).append("Binary"), binary);
510 Cost = _config->FindI(std::string(config).append("Cost"), cost);
511 std::string const compConf = std::string(config).append("CompressArg");
512 if (_config->Exists(compConf) == true)
513 CompressArgs = _config->FindVector(compConf);
514 else if (compressArg != NULL)
515 CompressArgs.push_back(compressArg);
516 std::string const uncompConf = std::string(config).append("UncompressArg");
517 if (_config->Exists(uncompConf) == true)
518 UncompressArgs = _config->FindVector(uncompConf);
519 else if (uncompressArg != NULL)
520 UncompressArgs.push_back(uncompressArg);
521 }
522 /*}}}*/
523 // getBuildProfiles - return a vector of enabled build profiles /*{{{*/
524 std::vector<std::string> const Configuration::getBuildProfiles() {
525 // order is: override value (~= commandline), environment variable, list (~= config file)
526 std::string profiles_env = getenv("DEB_BUILD_PROFILES") == 0 ? "" : getenv("DEB_BUILD_PROFILES");
527 if (profiles_env.empty() == false) {
528 profiles_env = SubstVar(profiles_env, " ", ",");
529 std::string const bp = _config->Find("APT::Build-Profiles");
530 _config->Clear("APT::Build-Profiles");
531 if (bp.empty() == false)
532 _config->Set("APT::Build-Profiles", bp);
533 }
534 return _config->FindVector("APT::Build-Profiles", profiles_env);
535 }
536 std::string const Configuration::getBuildProfilesString() {
537 std::vector<std::string> profiles = getBuildProfiles();
538 if (profiles.empty() == true)
539 return "";
540 std::vector<std::string>::const_iterator p = profiles.begin();
541 std::string list = *p;
542 for (++p; p != profiles.end(); ++p)
543 list.append(",").append(*p);
544 return list;
545 }
546 /*}}}*/
547 }