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