1 // -*- mode: cpp; mode: fold -*-
3 // $Id: debsystem.cc,v 1.4 2004/01/26 17:01:53 mdz Exp $
4 /* ######################################################################
6 System - Abstraction for running on different systems.
8 Basic general structure..
10 ##################################################################### */
12 // Include Files /*{{{*/
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>
35 #include <sys/types.h>
47 class APT_HIDDEN debSystemPrivate
{
49 debSystemPrivate() : LockFD(-1), LockCount(0), StatusFile(0)
52 // For locking support
56 debStatusIndex
*StatusFile
;
59 // System::debSystem - Constructor /*{{{*/
60 // ---------------------------------------------------------------------
62 debSystem::debSystem() : pkgSystem("Debian dpkg interface", &debVS
), d(new debSystemPrivate())
66 // System::~debSystem - Destructor /*{{{*/
67 // ---------------------------------------------------------------------
69 debSystem::~debSystem()
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()
81 // Disable file locking
82 if (_config
->FindB("Debug::NoLocking",false) == true || d
->LockCount
> 1)
88 // Create the lockfile
89 string AdminDir
= flNotFile(_config
->FindFile("Dir::State::status"));
90 d
->LockFD
= GetLock(AdminDir
+ "lock");
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());
97 return _error
->Error(_("Unable to lock the administration directory (%s), "
98 "are you root?"),AdminDir
.c_str());
101 // See if we need to abort with a dirty journal
102 if (CheckUpdates() == true)
107 if (getenv("SUDO_USER") != NULL
)
108 cmd
= "sudo dpkg --configure -a";
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
);
122 // System::UnLock - Drop a lock /*{{{*/
123 // ---------------------------------------------------------------------
125 bool debSystem::UnLock(bool NoErrors
)
127 if (d
->LockCount
== 0 && NoErrors
== true)
130 if (d
->LockCount
< 1)
131 return _error
->Error(_("Not locked"));
132 if (--d
->LockCount
== 0)
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()
147 // Check for updates.. (dirty)
148 string File
= flNotFile(_config
->FindFile("Dir::State::status")) + "updates/";
149 DIR *DirP
= opendir(File
.c_str());
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
))
159 for (unsigned int I
= 0; Ent
->d_name
[I
] != 0; I
++)
161 // Check if its not a digit..
162 if (isdigit(Ent
->d_name
[I
]) == 0)
176 // System::CreatePM - Create the underlying package manager /*{{{*/
177 // ---------------------------------------------------------------------
179 pkgPackageManager
*debSystem::CreatePM(pkgDepCache
*Cache
) const
181 return new pkgDPkgPM(Cache
);
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");
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
);
208 bool debSystem::Initialize(Configuration
&Cnf
)
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");
219 delete d
->StatusFile
;
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
)
232 if (strcmp(Type
,"deb") == 0)
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
241 signed debSystem::Score(Configuration
const &Cnf
)
244 if (FileExists(Cnf
.FindFile("Dir::State::status","/var/lib/dpkg/status")) == true)
246 if (FileExists(Cnf
.Find("Dir::Bin::dpkg","/usr/bin/dpkg")) == true)
248 if (FileExists("/etc/debian_version") == true)
253 // System::AddStatusFiles - Register the status files /*{{{*/
254 // ---------------------------------------------------------------------
256 bool debSystem::AddStatusFiles(std::vector
<pkgIndexFile
*> &List
)
258 if (d
->StatusFile
== 0)
259 d
->StatusFile
= new debStatusIndex(_config
->FindFile("Dir::State::status"));
260 List
.push_back(d
->StatusFile
);
264 // System::FindIndex - Get an index file for status files /*{{{*/
265 // ---------------------------------------------------------------------
267 bool debSystem::FindIndex(pkgCache::PkgFileIterator File
,
268 pkgIndexFile
*&Found
) const
270 if (d
->StatusFile
== 0)
272 if (d
->StatusFile
->FindInCache(*File
.Cache()) == File
)
274 Found
= d
->StatusFile
;
282 std::string
debSystem::GetDpkgExecutable() /*{{{*/
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)
289 if (dpkgChrootDir
[dpkgChrootLen
- 1] == '/')
291 Tmp
= Tmp
.substr(dpkgChrootLen
);
296 std::vector
<std::string
> debSystem::GetDpkgBaseCommand() /*{{{*/
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");
305 for (; Opts
!= 0; Opts
= Opts
->Next
)
307 if (Opts
->Value
.empty() == true)
309 Args
.push_back(Opts
->Value
);
315 void debSystem::DpkgChrootDirectory() /*{{{*/
317 std::string
const chrootDir
= _config
->FindDir("DPkg::Chroot-Directory");
318 if (chrootDir
== "/")
320 std::cerr
<< "Chrooting into " << chrootDir
<< std::endl
;
321 if (chroot(chrootDir
.c_str()) != 0)
327 pid_t
debSystem::ExecDpkg(std::vector
<std::string
> const &sArgs
, int * const inputFd
, int * const outputFd
, bool const DiscardOutput
)/*{{{*/
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
);
333 int external
[2] = {-1, -1};
334 if (inputFd
!= nullptr || outputFd
!= nullptr)
335 if (pipe(external
) != 0)
337 _error
->WarningE("dpkg", "Can't create IPC pipe for dpkg call");
341 pid_t
const dpkg
= ExecFork();
343 int const nullfd
= open("/dev/null", O_RDONLY
);
344 if (inputFd
== nullptr)
345 dup2(nullfd
, STDIN_FILENO
);
349 dup2(external
[0], STDIN_FILENO
);
351 if (outputFd
== nullptr)
352 dup2(nullfd
, STDOUT_FILENO
);
356 dup2(external
[1], STDOUT_FILENO
);
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!");
365 if (outputFd
!= nullptr)
368 *outputFd
= external
[0];
370 else if (inputFd
!= nullptr)
373 *inputFd
= external
[1];
378 bool debSystem::SupportsMultiArch() /*{{{*/
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)
386 while (waitpid(dpkgAssertMultiArch
, &Status
, 0) != dpkgAssertMultiArch
)
390 _error
->WarningE("dpkgGo", _("Waited for %s but it wasn't there"), "dpkg --assert-multi-arch");
393 if (WIFEXITED(Status
) == true && WEXITSTATUS(Status
) == 0)
399 std::vector
<std::string
> debSystem::SupportedArchitectures() /*{{{*/
401 std::vector
<std::string
> archs
;
403 string
const arch
= _config
->Find("APT::Architecture");
404 if (arch
.empty() == false)
405 archs
.push_back(std::move(arch
));
408 std::vector
<std::string
> sArgs
= GetDpkgBaseCommand();
409 sArgs
.push_back("--print-foreign-architectures");
411 pid_t
const dpkgMultiArch
= ExecDpkg(sArgs
, nullptr, &outputFd
, true);
412 if (dpkgMultiArch
== -1)
415 FILE *dpkg
= fdopen(outputFd
, "r");
419 while (getline(&buf
, &bufsize
, dpkg
) != -1)
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())
432 arch
= strtok_r(NULL
, " ", &tok_saveptr
);
438 ExecWait(dpkgMultiArch
, "dpkg --print-foreign-architectures", true);