You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

477 lines
14 KiB

  1. // -*- mode: cpp; mode: fold -*-
  2. // Description /*{{{*/
  3. /* ######################################################################
  4. System - Abstraction for running on different systems.
  5. Basic general structure..
  6. ##################################################################### */
  7. /*}}}*/
  8. // Include Files /*{{{*/
  9. #include <config.h>
  10. #include <apt-pkg/configuration.h>
  11. #include <apt-pkg/debindexfile.h>
  12. #include <apt-pkg/debsystem.h>
  13. #include <apt-pkg/debversion.h>
  14. #include <apt-pkg/dpkgpm.h>
  15. #include <apt-pkg/error.h>
  16. #include <apt-pkg/fileutl.h>
  17. #include <apt-pkg/pkgcache.h>
  18. #include <algorithm>
  19. #include <string>
  20. #include <vector>
  21. #include <ctype.h>
  22. #include <dirent.h>
  23. #include <errno.h>
  24. #include <fcntl.h>
  25. #include <stdlib.h>
  26. #include <string.h>
  27. #include <sys/stat.h>
  28. #include <sys/types.h>
  29. #include <sys/wait.h>
  30. #include <unistd.h>
  31. #include <apti18n.h>
  32. /*}}}*/
  33. using std::string;
  34. debSystem debSys;
  35. class APT_HIDDEN debSystemPrivate {
  36. public:
  37. debSystemPrivate() : FrontendLockFD(-1), LockFD(-1), LockCount(0), StatusFile(0)
  38. {
  39. }
  40. // For locking support
  41. int FrontendLockFD;
  42. int LockFD;
  43. unsigned LockCount;
  44. debStatusIndex *StatusFile;
  45. };
  46. // System::debSystem - Constructor /*{{{*/
  47. // ---------------------------------------------------------------------
  48. /* */
  49. debSystem::debSystem() : pkgSystem("Debian dpkg interface", &debVS), d(new debSystemPrivate())
  50. {
  51. }
  52. /*}}}*/
  53. // System::~debSystem - Destructor /*{{{*/
  54. // ---------------------------------------------------------------------
  55. /* */
  56. debSystem::~debSystem()
  57. {
  58. delete d->StatusFile;
  59. delete d;
  60. }
  61. /*}}}*/
  62. // System::Lock - Get the lock /*{{{*/
  63. // ---------------------------------------------------------------------
  64. /* This mirrors the operations dpkg does when it starts up. Note the
  65. checking of the updates directory. */
  66. bool debSystem::Lock()
  67. {
  68. // Disable file locking
  69. if (_config->FindB("Debug::NoLocking",false) == true || d->LockCount > 0)
  70. {
  71. d->LockCount++;
  72. return true;
  73. }
  74. // Create the lockfile
  75. string AdminDir = flNotFile(_config->FindFile("Dir::State::status"));
  76. string FrontendLockFile = AdminDir + "lock-frontend";
  77. d->FrontendLockFD = GetLock(FrontendLockFile);
  78. if (d->FrontendLockFD == -1)
  79. {
  80. if (errno == EACCES || errno == EAGAIN)
  81. return _error->Error(_("Unable to acquire the dpkg frontend lock (%s), "
  82. "is another process using it?"),FrontendLockFile.c_str());
  83. else
  84. return _error->Error(_("Unable to acquire the dpkg frontend lock (%s), "
  85. "are you root?"),FrontendLockFile.c_str());
  86. }
  87. if (LockInner() == false)
  88. {
  89. close(d->FrontendLockFD);
  90. return false;
  91. }
  92. // See if we need to abort with a dirty journal
  93. if (CheckUpdates() == true)
  94. {
  95. close(d->LockFD);
  96. close(d->FrontendLockFD);
  97. d->FrontendLockFD = -1;
  98. d->LockFD = -1;
  99. const char *cmd;
  100. if (getenv("SUDO_USER") != NULL)
  101. cmd = "sudo dpkg --configure -a";
  102. else
  103. cmd = "dpkg --configure -a";
  104. // TRANSLATORS: the %s contains the recovery command, usually
  105. // dpkg --configure -a
  106. return _error->Error(_("dpkg was interrupted, you must manually "
  107. "run '%s' to correct the problem. "), cmd);
  108. }
  109. d->LockCount++;
  110. return true;
  111. }
  112. bool debSystem::LockInner() {
  113. string AdminDir = flNotFile(_config->FindFile("Dir::State::status"));
  114. d->LockFD = GetLock(AdminDir + "lock");
  115. if (d->LockFD == -1)
  116. {
  117. if (errno == EACCES || errno == EAGAIN)
  118. return _error->Error(_("Unable to lock the administration directory (%s), "
  119. "is another process using it?"),AdminDir.c_str());
  120. else
  121. return _error->Error(_("Unable to lock the administration directory (%s), "
  122. "are you root?"),AdminDir.c_str());
  123. }
  124. return true;
  125. }
  126. /*}}}*/
  127. // System::UnLock - Drop a lock /*{{{*/
  128. // ---------------------------------------------------------------------
  129. /* */
  130. bool debSystem::UnLock(bool NoErrors)
  131. {
  132. if (d->LockCount == 0 && NoErrors == true)
  133. return false;
  134. if (d->LockCount < 1)
  135. return _error->Error(_("Not locked"));
  136. if (--d->LockCount == 0)
  137. {
  138. close(d->FrontendLockFD);
  139. close(d->LockFD);
  140. d->LockCount = 0;
  141. }
  142. return true;
  143. }
  144. bool debSystem::UnLockInner(bool NoErrors) {
  145. (void) NoErrors;
  146. close(d->LockFD);
  147. return true;
  148. }
  149. /*}}}*/
  150. // System::IsLocked - Check if system is locked /*{{{*/
  151. // ---------------------------------------------------------------------
  152. /* This checks if the frontend lock is hold. The inner lock might be
  153. * released. */
  154. bool debSystem::IsLocked()
  155. {
  156. return d->LockCount > 0;
  157. }
  158. /*}}}*/
  159. // System::CheckUpdates - Check if the updates dir is dirty /*{{{*/
  160. // ---------------------------------------------------------------------
  161. /* This does a check of the updates directory (dpkg journal) to see if it has
  162. any entries in it. */
  163. bool debSystem::CheckUpdates()
  164. {
  165. // Check for updates.. (dirty)
  166. string File = flNotFile(_config->FindFile("Dir::State::status")) + "updates/";
  167. DIR *DirP = opendir(File.c_str());
  168. if (DirP == 0)
  169. return false;
  170. /* We ignore any files that are not all digits, this skips .,.. and
  171. some tmp files dpkg will leave behind.. */
  172. bool Damaged = false;
  173. for (struct dirent *Ent = readdir(DirP); Ent != 0; Ent = readdir(DirP))
  174. {
  175. Damaged = true;
  176. for (unsigned int I = 0; Ent->d_name[I] != 0; I++)
  177. {
  178. // Check if its not a digit..
  179. if (isdigit(Ent->d_name[I]) == 0)
  180. {
  181. Damaged = false;
  182. break;
  183. }
  184. }
  185. if (Damaged == true)
  186. break;
  187. }
  188. closedir(DirP);
  189. return Damaged;
  190. }
  191. /*}}}*/
  192. // System::CreatePM - Create the underlying package manager /*{{{*/
  193. // ---------------------------------------------------------------------
  194. /* */
  195. pkgPackageManager *debSystem::CreatePM(pkgDepCache *Cache) const
  196. {
  197. return new pkgDPkgPM(Cache);
  198. }
  199. /*}}}*/
  200. // System::Initialize - Setup the configuration space.. /*{{{*/
  201. // ---------------------------------------------------------------------
  202. /* These are the Debian specific configuration variables.. */
  203. static std::string getDpkgStatusLocation(Configuration const &Cnf) {
  204. Configuration PathCnf;
  205. PathCnf.Set("Dir", Cnf.Find("Dir", "/"));
  206. PathCnf.Set("Dir::State::status", "status");
  207. auto const cnfstatedir = Cnf.Find("Dir::State", STATE_DIR + 1);
  208. // if the state dir ends in apt, replace it with dpkg -
  209. // for the default this gives us the same as the fallback below.
  210. // This can't be a ../dpkg as that would play bad with symlinks
  211. std::string statedir;
  212. if (APT::String::Endswith(cnfstatedir, "/apt/"))
  213. statedir.assign(cnfstatedir, 0, cnfstatedir.length() - 5);
  214. else if (APT::String::Endswith(cnfstatedir, "/apt"))
  215. statedir.assign(cnfstatedir, 0, cnfstatedir.length() - 4);
  216. if (statedir.empty())
  217. PathCnf.Set("Dir::State", "var/lib/dpkg");
  218. else
  219. PathCnf.Set("Dir::State", flCombine(statedir, "dpkg"));
  220. return PathCnf.FindFile("Dir::State::status");
  221. }
  222. bool debSystem::Initialize(Configuration &Cnf)
  223. {
  224. /* These really should be jammed into a generic 'Local Database' engine
  225. which is yet to be determined. The functions in pkgcachegen should
  226. be the only users of these */
  227. Cnf.CndSet("Dir::State::extended_states", "extended_states");
  228. if (Cnf.Exists("Dir::State::status") == false)
  229. Cnf.Set("Dir::State::status", getDpkgStatusLocation(Cnf));
  230. Cnf.CndSet("Dir::Bin::dpkg",BIN_DIR"/dpkg");
  231. if (d->StatusFile) {
  232. delete d->StatusFile;
  233. d->StatusFile = 0;
  234. }
  235. return true;
  236. }
  237. /*}}}*/
  238. // System::ArchiveSupported - Is a file format supported /*{{{*/
  239. // ---------------------------------------------------------------------
  240. /* The standard name for a deb is 'deb'.. There are no separate versions
  241. of .deb to worry about.. */
  242. APT_PURE bool debSystem::ArchiveSupported(const char *Type)
  243. {
  244. if (strcmp(Type,"deb") == 0)
  245. return true;
  246. return false;
  247. }
  248. /*}}}*/
  249. // System::Score - Determine how 'Debiany' this sys is.. /*{{{*/
  250. // ---------------------------------------------------------------------
  251. /* We check some files that are sure tell signs of this being a Debian
  252. System.. */
  253. signed debSystem::Score(Configuration const &Cnf)
  254. {
  255. signed Score = 0;
  256. if (FileExists(Cnf.FindFile("Dir::State::status",getDpkgStatusLocation(Cnf).c_str())) == true)
  257. Score += 10;
  258. if (FileExists(Cnf.Find("Dir::Bin::dpkg",BIN_DIR"/dpkg")) == true)
  259. Score += 10;
  260. if (FileExists("/etc/debian_version") == true)
  261. Score += 10;
  262. return Score;
  263. }
  264. /*}}}*/
  265. // System::AddStatusFiles - Register the status files /*{{{*/
  266. // ---------------------------------------------------------------------
  267. /* */
  268. bool debSystem::AddStatusFiles(std::vector<pkgIndexFile *> &List)
  269. {
  270. if (d->StatusFile == 0)
  271. d->StatusFile = new debStatusIndex(_config->FindFile("Dir::State::status"));
  272. List.push_back(d->StatusFile);
  273. return true;
  274. }
  275. /*}}}*/
  276. // System::FindIndex - Get an index file for status files /*{{{*/
  277. // ---------------------------------------------------------------------
  278. /* */
  279. bool debSystem::FindIndex(pkgCache::PkgFileIterator File,
  280. pkgIndexFile *&Found) const
  281. {
  282. if (d->StatusFile == 0)
  283. return false;
  284. if (d->StatusFile->FindInCache(*File.Cache()) == File)
  285. {
  286. Found = d->StatusFile;
  287. return true;
  288. }
  289. return false;
  290. }
  291. /*}}}*/
  292. std::string debSystem::GetDpkgExecutable() /*{{{*/
  293. {
  294. string Tmp = _config->Find("Dir::Bin::dpkg","dpkg");
  295. string const dpkgChrootDir = _config->FindDir("DPkg::Chroot-Directory", "/");
  296. size_t dpkgChrootLen = dpkgChrootDir.length();
  297. if (dpkgChrootDir != "/" && Tmp.find(dpkgChrootDir) == 0)
  298. {
  299. if (dpkgChrootDir[dpkgChrootLen - 1] == '/')
  300. --dpkgChrootLen;
  301. Tmp = Tmp.substr(dpkgChrootLen);
  302. }
  303. return Tmp;
  304. }
  305. /*}}}*/
  306. std::vector<std::string> debSystem::GetDpkgBaseCommand() /*{{{*/
  307. {
  308. // Generate the base argument list for dpkg
  309. std::vector<std::string> Args = { GetDpkgExecutable() };
  310. // Stick in any custom dpkg options
  311. Configuration::Item const *Opts = _config->Tree("DPkg::Options");
  312. if (Opts != 0)
  313. {
  314. Opts = Opts->Child;
  315. for (; Opts != 0; Opts = Opts->Next)
  316. {
  317. if (Opts->Value.empty() == true)
  318. continue;
  319. Args.push_back(Opts->Value);
  320. }
  321. }
  322. return Args;
  323. }
  324. /*}}}*/
  325. void debSystem::DpkgChrootDirectory() /*{{{*/
  326. {
  327. std::string const chrootDir = _config->FindDir("DPkg::Chroot-Directory");
  328. if (chrootDir == "/")
  329. return;
  330. std::cerr << "Chrooting into " << chrootDir << std::endl;
  331. if (chroot(chrootDir.c_str()) != 0)
  332. _exit(100);
  333. if (chdir("/") != 0)
  334. _exit(100);
  335. }
  336. /*}}}*/
  337. pid_t debSystem::ExecDpkg(std::vector<std::string> const &sArgs, int * const inputFd, int * const outputFd, bool const DiscardOutput)/*{{{*/
  338. {
  339. std::vector<const char *> Args(sArgs.size(), NULL);
  340. std::transform(sArgs.begin(), sArgs.end(), Args.begin(), [](std::string const &s) { return s.c_str(); });
  341. Args.push_back(NULL);
  342. int external[2] = {-1, -1};
  343. if (inputFd != nullptr || outputFd != nullptr)
  344. if (pipe(external) != 0)
  345. {
  346. _error->WarningE("dpkg", "Can't create IPC pipe for dpkg call");
  347. return -1;
  348. }
  349. pid_t const dpkg = ExecFork();
  350. if (dpkg == 0) {
  351. int const nullfd = open("/dev/null", O_RDONLY);
  352. if (inputFd == nullptr)
  353. dup2(nullfd, STDIN_FILENO);
  354. else
  355. {
  356. close(external[1]);
  357. dup2(external[0], STDIN_FILENO);
  358. }
  359. if (outputFd == nullptr)
  360. dup2(nullfd, STDOUT_FILENO);
  361. else
  362. {
  363. close(external[0]);
  364. dup2(external[1], STDOUT_FILENO);
  365. }
  366. if (DiscardOutput == true)
  367. dup2(nullfd, STDERR_FILENO);
  368. debSystem::DpkgChrootDirectory();
  369. execvp(Args[0], (char**) &Args[0]);
  370. _error->WarningE("dpkg", "Can't execute dpkg!");
  371. _exit(100);
  372. }
  373. if (outputFd != nullptr)
  374. {
  375. close(external[1]);
  376. *outputFd = external[0];
  377. }
  378. else if (inputFd != nullptr)
  379. {
  380. close(external[0]);
  381. *inputFd = external[1];
  382. }
  383. return dpkg;
  384. }
  385. /*}}}*/
  386. bool debSystem::SupportsMultiArch() /*{{{*/
  387. {
  388. std::vector<std::string> Args = GetDpkgBaseCommand();
  389. Args.push_back("--assert-multi-arch");
  390. pid_t const dpkgAssertMultiArch = ExecDpkg(Args, nullptr, nullptr, true);
  391. if (dpkgAssertMultiArch > 0)
  392. {
  393. int Status = 0;
  394. while (waitpid(dpkgAssertMultiArch, &Status, 0) != dpkgAssertMultiArch)
  395. {
  396. if (errno == EINTR)
  397. continue;
  398. _error->WarningE("dpkgGo", _("Waited for %s but it wasn't there"), "dpkg --assert-multi-arch");
  399. break;
  400. }
  401. if (WIFEXITED(Status) == true && WEXITSTATUS(Status) == 0)
  402. return true;
  403. }
  404. return false;
  405. }
  406. /*}}}*/
  407. std::vector<std::string> debSystem::SupportedArchitectures() /*{{{*/
  408. {
  409. std::vector<std::string> archs;
  410. {
  411. string const arch = _config->Find("APT::Architecture");
  412. if (arch.empty() == false)
  413. archs.push_back(std::move(arch));
  414. }
  415. std::vector<std::string> sArgs = GetDpkgBaseCommand();
  416. sArgs.push_back("--print-foreign-architectures");
  417. int outputFd = -1;
  418. pid_t const dpkgMultiArch = ExecDpkg(sArgs, nullptr, &outputFd, true);
  419. if (dpkgMultiArch == -1)
  420. return archs;
  421. FILE *dpkg = fdopen(outputFd, "r");
  422. if(dpkg != NULL) {
  423. char* buf = NULL;
  424. size_t bufsize = 0;
  425. while (getline(&buf, &bufsize, dpkg) != -1)
  426. {
  427. char* tok_saveptr;
  428. char* arch = strtok_r(buf, " ", &tok_saveptr);
  429. while (arch != NULL) {
  430. for (; isspace_ascii(*arch) != 0; ++arch);
  431. if (arch[0] != '\0') {
  432. char const* archend = arch;
  433. for (; isspace_ascii(*archend) == 0 && *archend != '\0'; ++archend);
  434. string a(arch, (archend - arch));
  435. if (std::find(archs.begin(), archs.end(), a) == archs.end())
  436. archs.push_back(a);
  437. }
  438. arch = strtok_r(NULL, " ", &tok_saveptr);
  439. }
  440. }
  441. free(buf);
  442. fclose(dpkg);
  443. }
  444. ExecWait(dpkgMultiArch, "dpkg --print-foreign-architectures", true);
  445. return archs;
  446. }
  447. /*}}}*/