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