call dpkg --assert-multi-arch with execvp instead of execv
[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 archs.push_back(string(arch, (archend - arch)));
396 }
397 arch = strtok(NULL, " ");
398 }
399 }
400 fclose(dpkg);
401 }
402 ExecWait(dpkgMultiArch, "dpkg --print-foreign-architectures", true);
403 return archs;
404 }
405
406 if (archs.empty() == true ||
407 std::find(archs.begin(), archs.end(), arch) == archs.end())
408 archs.insert(archs.begin(), arch);
409
410 // erase duplicates and empty strings
411 for (std::vector<string>::reverse_iterator a = archs.rbegin();
412 a != archs.rend(); ++a) {
413 if (a->empty() == true || std::find(a + 1, archs.rend(), *a) != archs.rend())
414 archs.erase(a.base()-1);
415 if (a == archs.rend())
416 break;
417 }
418
419 return archs;
420 }
421 /*}}}*/
422 // checkArchitecture - are we interested in the given Architecture? /*{{{*/
423 bool const Configuration::checkArchitecture(std::string const &Arch) {
424 if (Arch == "all")
425 return true;
426 std::vector<std::string> const archs = getArchitectures(true);
427 return (std::find(archs.begin(), archs.end(), Arch) != archs.end());
428 }
429 /*}}}*/
430 // setDefaultConfigurationForCompressors /*{{{*/
431 void Configuration::setDefaultConfigurationForCompressors() {
432 // Set default application paths to check for optional compression types
433 _config->CndSet("Dir::Bin::lzma", "/usr/bin/lzma");
434 _config->CndSet("Dir::Bin::xz", "/usr/bin/xz");
435 _config->CndSet("Dir::Bin::bzip2", "/bin/bzip2");
436 }
437 /*}}}*/
438 // getCompressors - Return Vector of usbale compressors /*{{{*/
439 // ---------------------------------------------------------------------
440 /* return a vector of compressors used by apt-ftparchive in the
441 multicompress functionality or to detect data.tar files */
442 std::vector<APT::Configuration::Compressor>
443 const Configuration::getCompressors(bool const Cached) {
444 static std::vector<APT::Configuration::Compressor> compressors;
445 if (compressors.empty() == false) {
446 if (Cached == true)
447 return compressors;
448 else
449 compressors.clear();
450 }
451
452 setDefaultConfigurationForCompressors();
453
454 compressors.push_back(Compressor(".", "", "", "", "", 1));
455 if (_config->Exists("Dir::Bin::gzip") == false || FileExists(_config->FindFile("Dir::Bin::gzip")) == true)
456 compressors.push_back(Compressor("gzip",".gz","gzip","-9n","-d",2));
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 if (_config->Exists("Dir::Bin::lzma") == false || FileExists(_config->FindFile("Dir::Bin::lzma")) == true)
460 compressors.push_back(Compressor("lzma",".lzma","lzma","-9","-d",4));
461 if (_config->Exists("Dir::Bin::xz") == false || FileExists(_config->FindFile("Dir::Bin::xz")) == true)
462 compressors.push_back(Compressor("xz",".xz","xz","-6","-d",5));
463
464 std::vector<std::string> const comp = _config->FindVector("APT::Compressor");
465 for (std::vector<std::string>::const_iterator c = comp.begin();
466 c != comp.end(); ++c) {
467 if (*c == "." || *c == "gzip" || *c == "bzip2" || *c == "lzma" || *c == "xz")
468 continue;
469 compressors.push_back(Compressor(c->c_str(), std::string(".").append(*c).c_str(), c->c_str(), "-9", "-d", 100));
470 }
471
472 return compressors;
473 }
474 /*}}}*/
475 // getCompressorExtensions - supported data.tar extensions /*{{{*/
476 // ---------------------------------------------------------------------
477 /* */
478 std::vector<std::string> const Configuration::getCompressorExtensions() {
479 std::vector<APT::Configuration::Compressor> const compressors = getCompressors();
480 std::vector<std::string> ext;
481 for (std::vector<APT::Configuration::Compressor>::const_iterator c = compressors.begin();
482 c != compressors.end(); ++c)
483 if (c->Extension.empty() == false && c->Extension != ".")
484 ext.push_back(c->Extension);
485 return ext;
486 }
487 /*}}}*/
488 // Compressor constructor /*{{{*/
489 // ---------------------------------------------------------------------
490 /* */
491 Configuration::Compressor::Compressor(char const *name, char const *extension,
492 char const *binary,
493 char const *compressArg, char const *uncompressArg,
494 unsigned short const cost) {
495 std::string const config = std::string("APT:Compressor::").append(name).append("::");
496 Name = _config->Find(std::string(config).append("Name"), name);
497 Extension = _config->Find(std::string(config).append("Extension"), extension);
498 Binary = _config->Find(std::string(config).append("Binary"), binary);
499 Cost = _config->FindI(std::string(config).append("Cost"), cost);
500 std::string const compConf = std::string(config).append("CompressArg");
501 if (_config->Exists(compConf) == true)
502 CompressArgs = _config->FindVector(compConf);
503 else if (compressArg != NULL)
504 CompressArgs.push_back(compressArg);
505 std::string const uncompConf = std::string(config).append("UncompressArg");
506 if (_config->Exists(uncompConf) == true)
507 UncompressArgs = _config->FindVector(uncompConf);
508 else if (uncompressArg != NULL)
509 UncompressArgs.push_back(uncompressArg);
510 }
511 /*}}}*/
512 }