#include <apt-pkg/configuration.h>
#include <apt-pkg/depcache.h>
#include <apt-pkg/dpkgpm.h>
+#include <apt-pkg/debsystem.h>
#include <apt-pkg/error.h>
#include <apt-pkg/fileutl.h>
#include <apt-pkg/install-progress.h>
#include <apt-pkg/packagemanager.h>
-#include <apt-pkg/pkgrecords.h>
#include <apt-pkg/strutl.h>
#include <apt-pkg/cacheiterators.h>
#include <apt-pkg/macros.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
-#include <pty.h>
#include <pwd.h>
#include <signal.h>
#include <stddef.h>
#include <string>
#include <utility>
#include <vector>
+#include <sstream>
+#include <numeric>
#include <apti18n.h>
/*}}}*/
using namespace std;
+APT_PURE static string
+AptHistoryRequestingUser()
+{
+ const char* EnvKeys[]{"SUDO_UID", "PKEXEC_UID", "PACKAGEKIT_CALLER_UID"};
+
+ for (const auto &Key: EnvKeys)
+ {
+ if (getenv(Key) != nullptr)
+ {
+ int uid = atoi(getenv(Key));
+ if (uid > 0) {
+ struct passwd pwd;
+ struct passwd *result;
+ char buf[255];
+ if (getpwuid_r(uid, &pwd, buf, sizeof(buf), &result) == 0 && result != NULL) {
+ std::string res;
+ strprintf(res, "%s (%d)", pwd.pw_name, uid);
+ return res;
+ }
+ }
+ }
+ }
+ return "";
+}
+
APT_PURE static unsigned int
EnvironmentSize()
{
pkgDPkgPMPrivate() : stdin_is_dev_null(false), dpkgbuf_pos(0),
term_out(NULL), history_out(NULL),
progress(NULL), tt_is_valid(false), master(-1),
- slave(NULL), protect_slave_from_dying(-1)
+ slave(NULL), protect_slave_from_dying(-1),
+ direct_stdin(false)
{
dpkgbuf[0] = '\0';
}
bool stdin_is_dev_null;
// the buffer we use for the dpkg status-fd reading
char dpkgbuf[1024];
- int dpkgbuf_pos;
+ size_t dpkgbuf_pos;
FILE *term_out;
FILE *history_out;
string dpkg_error;
sigset_t sigmask;
sigset_t original_sigmask;
+ bool direct_stdin;
};
namespace
const char *target;
public:
- MatchProcessingOp(const char *the_target)
+ explicit MatchProcessingOp(const char *the_target)
: target(the_target)
{
}
return ExecWait(Process, "ionice");
}
-static std::string getDpkgExecutable()
-{
- string Tmp = _config->Find("Dir::Bin::dpkg","dpkg");
- string const dpkgChrootDir = _config->FindDir("DPkg::Chroot-Directory", "/");
- size_t dpkgChrootLen = dpkgChrootDir.length();
- if (dpkgChrootDir != "/" && Tmp.find(dpkgChrootDir) == 0)
- {
- if (dpkgChrootDir[dpkgChrootLen - 1] == '/')
- --dpkgChrootLen;
- Tmp = Tmp.substr(dpkgChrootLen);
- }
- return Tmp;
-}
-
-// dpkgChrootDirectory - chrooting for dpkg if needed /*{{{*/
-static void dpkgChrootDirectory()
-{
- std::string const chrootDir = _config->FindDir("DPkg::Chroot-Directory");
- if (chrootDir == "/")
- return;
- std::cerr << "Chrooting into " << chrootDir << std::endl;
- if (chroot(chrootDir.c_str()) != 0)
- _exit(100);
- if (chdir("/") != 0)
- _exit(100);
-}
- /*}}}*/
-
-
// FindNowVersion - Helper to find a Version in "now" state /*{{{*/
// ---------------------------------------------------------------------
/* This is helpful when a package is no longer installed but has residual
{
pkgCache::VerIterator Ver;
for (Ver = Pkg.VersionList(); Ver.end() == false; ++Ver)
- {
- pkgCache::VerFileIterator Vf = Ver.FileList();
- pkgCache::PkgFileIterator F = Vf.File();
- for (F = Vf.File(); F.end() == false; ++F)
- {
- if (F && F.Archive())
- {
- if (strcmp(F.Archive(), "now"))
- return Ver;
- }
- }
- }
+ for (pkgCache::VerFileIterator Vf = Ver.FileList(); Vf.end() == false; ++Vf)
+ for (pkgCache::PkgFileIterator F = Vf.File(); F.end() == false; ++F)
+ {
+ if (F.Archive() != 0 && strcmp(F.Archive(), "now") == 0)
+ return Ver;
+ }
return Ver;
}
/*}}}*/
// DPkgPM::pkgDPkgPM - Constructor /*{{{*/
// ---------------------------------------------------------------------
/* */
-pkgDPkgPM::pkgDPkgPM(pkgDepCache *Cache)
- : pkgPackageManager(Cache), pkgFailures(0), PackagesDone(0), PackagesTotal(0)
+pkgDPkgPM::pkgDPkgPM(pkgDepCache *Cache)
+ : pkgPackageManager(Cache),d(new pkgDPkgPMPrivate()), pkgFailures(0), PackagesDone(0), PackagesTotal(0)
{
- d = new pkgDPkgPMPrivate();
}
/*}}}*/
// DPkgPM::pkgDPkgPM - Destructor /*{{{*/
strprintf(hookfd, "%d", InfoFD);
setenv("APT_HOOK_INFO_FD", hookfd.c_str(), 1);
- dpkgChrootDirectory();
+ debSystem::DpkgChrootDirectory();
const char *Args[4];
Args[0] = "/bin/sh";
Args[1] = "-c";
void pkgDPkgPM::DoStdin(int master)
{
unsigned char input_buf[256] = {0,};
- ssize_t len = read(0, input_buf, sizeof(input_buf));
+ ssize_t len = read(STDIN_FILENO, input_buf, sizeof(input_buf));
if (len)
FileFd::Write(master, input_buf, len);
else
}
/*}}}*/
// DPkgPM::DoDpkgStatusFd /*{{{*/
-// ---------------------------------------------------------------------
-/*
- */
void pkgDPkgPM::DoDpkgStatusFd(int statusfd)
{
- char *p, *q;
- int len;
-
- len=read(statusfd, &d->dpkgbuf[d->dpkgbuf_pos], sizeof(d->dpkgbuf)-d->dpkgbuf_pos);
- d->dpkgbuf_pos += len;
+ ssize_t const len = read(statusfd, &d->dpkgbuf[d->dpkgbuf_pos],
+ (sizeof(d->dpkgbuf)/sizeof(d->dpkgbuf[0])) - d->dpkgbuf_pos);
if(len <= 0)
return;
+ d->dpkgbuf_pos += (len / sizeof(d->dpkgbuf[0]));
- // process line by line if we have a buffer
- p = q = d->dpkgbuf;
- while((q=(char*)memchr(p, '\n', d->dpkgbuf+d->dpkgbuf_pos-p)) != NULL)
+ // process line by line from the buffer
+ char *p = d->dpkgbuf, *q = nullptr;
+ while((q=(char*)memchr(p, '\n', (d->dpkgbuf + d->dpkgbuf_pos) - p)) != nullptr)
{
- *q = 0;
+ *q = '\0';
ProcessDpkgStatusLine(p);
- p=q+1; // continue with next line
+ p = q + 1; // continue with next line
}
- // now move the unprocessed bits (after the final \n that is now a 0x0)
- // to the start and update d->dpkgbuf_pos
- p = (char*)memrchr(d->dpkgbuf, 0, d->dpkgbuf_pos);
- if(p == NULL)
+ // check if we stripped the buffer clean
+ if (p > (d->dpkgbuf + d->dpkgbuf_pos))
+ {
+ d->dpkgbuf_pos = 0;
return;
+ }
- // we are interessted in the first char *after* 0x0
- p++;
-
- // move the unprocessed tail to the start and update pos
- memmove(d->dpkgbuf, p, p-d->dpkgbuf);
- d->dpkgbuf_pos = d->dpkgbuf+d->dpkgbuf_pos-p;
+ // otherwise move the unprocessed tail to the start and update pos
+ memmove(d->dpkgbuf, p, (p - d->dpkgbuf));
+ d->dpkgbuf_pos = (d->dpkgbuf + d->dpkgbuf_pos) - p;
}
/*}}}*/
// DPkgPM::WriteHistoryTag /*{{{*/
// get current time
char timestr[200];
time_t const t = time(NULL);
- struct tm const * const tmp = localtime(&t);
+ struct tm tm_buf;
+ struct tm const * const tmp = localtime_r(&t, &tm_buf);
strftime(timestr, sizeof(timestr), "%F %T", tmp);
// open terminal log
}
if (_config->Exists("Commandline::AsString") == true)
WriteHistoryTag("Commandline", _config->Find("Commandline::AsString"));
+ std::string RequestingUser = AptHistoryRequestingUser();
+ if (RequestingUser != "")
+ WriteHistoryTag("Requested-By", RequestingUser);
WriteHistoryTag("Install", install);
WriteHistoryTag("Reinstall", reinstall);
WriteHistoryTag("Upgrade", upgrade);
{
char timestr[200];
time_t t = time(NULL);
- struct tm *tmp = localtime(&t);
+ struct tm tm_buf;
+ struct tm *tmp = localtime_r(&t, &tm_buf);
strftime(timestr, sizeof(timestr), "%F %T", tmp);
if(d->term_out)
PackagesTotal++;
}
}
+ /* one extra: We don't want the progress bar to reach 100%, especially not
+ if we call dpkg --configure --pending and process a bunch of triggers
+ while showing 100%. Also, spindown takes a while, so never reaching 100%
+ is way more correct than reaching 100% while still doing stuff even if
+ doing it this way is slightly bending the rules */
+ ++PackagesTotal;
}
/*}}}*/
-#if (APT_PKG_MAJOR >= 4 && APT_PKG_MINOR < 13)
bool pkgDPkgPM::Go(int StatusFd)
{
APT::Progress::PackageManager *progress = NULL;
else
progress = new APT::Progress::PackageManagerProgressFd(StatusFd);
- return GoNoABIBreak(progress);
+ return Go(progress);
}
-#endif
void pkgDPkgPM::StartPtyMagic()
{
return;
}
+ if (isatty(STDIN_FILENO) == 0)
+ d->direct_stdin = true;
+
_error->PushToStack();
d->master = posix_openpt(O_RDWR | O_NOCTTY);
_error->Errno("unlockpt", "Unlocking the slave of master fd %d failed!", d->master);
else
{
+#ifdef HAVE_PTS_NAME_R
+ char slave_name[64]; // 64 is used by bionic
+ if (ptsname_r(d->master, slave_name, sizeof(slave_name)) != 0)
+#else
char const * const slave_name = ptsname(d->master);
if (slave_name == NULL)
+#endif
_error->Errno("ptsname", "Getting name for slave of master fd %d failed!", d->master);
else
{
on kfreebsd we get an incorrect ("step like") output then while it has
no problem with closing all references… so to avoid platform specific
code here we combine both and be happy once more */
- d->protect_slave_from_dying = open(d->slave, O_RDWR | O_CLOEXEC);
+ d->protect_slave_from_dying = open(d->slave, O_RDWR | O_CLOEXEC | O_NOCTTY);
}
}
}
free(d->slave);
d->slave = NULL;
}
- _error->DumpErrors(std::cerr);
+ _error->DumpErrors(std::cerr, GlobalError::DEBUG, false);
}
_error->RevertToStack();
}
if (setsid() == -1)
_error->FatalE("setsid", "Starting a new session for child failed!");
- int const slaveFd = open(d->slave, O_RDWR);
+ int const slaveFd = open(d->slave, O_RDWR | O_NOCTTY);
if (slaveFd == -1)
_error->FatalE("open", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
else if (ioctl(slaveFd, TIOCSCTTY, 0) < 0)
_error->FatalE("ioctl", "Setting TIOCSCTTY for slave fd %d failed!", slaveFd);
else
{
- for (unsigned short i = 0; i < 3; ++i)
+ unsigned short i = 0;
+ if (d->direct_stdin == true)
+ ++i;
+ for (; i < 3; ++i)
if (dup2(slaveFd, i) == -1)
_error->FatalE("dup2", "Dupping %d to %d in child failed!", slaveFd, i);
* through to human readable (and i10n-able)
* names and calculates a percentage for each step.
*/
-#if (APT_PKG_MAJOR >= 4 && APT_PKG_MINOR >= 13)
bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
-#else
-bool pkgDPkgPM::GoNoABIBreak(APT::Progress::PackageManager *progress)
-#endif
{
pkgPackageManager::SigINTStop = false;
d->progress = progress;
// Generate the base argument list for dpkg
- unsigned long StartSize = 0;
- std::vector<const char *> Args;
- std::string DpkgExecutable = getDpkgExecutable();
- Args.push_back(DpkgExecutable.c_str());
- StartSize += DpkgExecutable.length();
-
- // Stick in any custom dpkg options
- Configuration::Item const *Opts = _config->Tree("DPkg::Options");
- if (Opts != 0)
- {
- Opts = Opts->Child;
- for (; Opts != 0; Opts = Opts->Next)
- {
- if (Opts->Value.empty() == true)
- continue;
- Args.push_back(Opts->Value.c_str());
- StartSize += Opts->Value.length();
- }
- }
-
+ std::vector<std::string> const sArgs = debSystem::GetDpkgBaseCommand();
+ std::vector<const char *> Args(sArgs.size(), NULL);
+ std::transform(sArgs.begin(), sArgs.end(), Args.begin(),
+ [](std::string const &s) { return s.c_str(); });
+ unsigned long long const StartSize = std::accumulate(sArgs.begin(), sArgs.end(), 0llu,
+ [](unsigned long long const i, std::string const &s) { return i + s.length(); });
size_t const BaseArgs = Args.size();
- // we need to detect if we can qualify packages with the architecture or not
- Args.push_back("--assert-multi-arch");
- Args.push_back(NULL);
-
- pid_t dpkgAssertMultiArch = ExecFork();
- if (dpkgAssertMultiArch == 0)
- {
- dpkgChrootDirectory();
- // redirect everything to the ultimate sink as we only need the exit-status
- int const nullfd = open("/dev/null", O_RDONLY);
- dup2(nullfd, STDIN_FILENO);
- dup2(nullfd, STDOUT_FILENO);
- dup2(nullfd, STDERR_FILENO);
- execvp(Args[0], (char**) &Args[0]);
- _error->WarningE("dpkgGo", "Can't detect if dpkg supports multi-arch!");
- _exit(2);
- }
fd_set rfds;
struct timespec tv;
// support subpressing of triggers processing for special
// cases like d-i that runs the triggers handling manually
- bool const SmartConf = (_config->Find("PackageManager::Configure", "all") != "all");
bool const TriggersPending = _config->FindB("DPkg::TriggersPending", false);
- if (_config->FindB("DPkg::ConfigurePending", SmartConf) == true)
+ if (_config->FindB("DPkg::ConfigurePending", true) == true)
List.push_back(Item(Item::ConfigurePending, PkgIterator()));
// for the progress
// create log
OpenLog();
- bool dpkgMultiArch = false;
- if (dpkgAssertMultiArch > 0)
- {
- int Status = 0;
- while (waitpid(dpkgAssertMultiArch, &Status, 0) != dpkgAssertMultiArch)
- {
- if (errno == EINTR)
- continue;
- _error->WarningE("dpkgGo", _("Waited for %s but it wasn't there"), "dpkg --assert-multi-arch");
- break;
- }
- if (WIFEXITED(Status) == true && WEXITSTATUS(Status) == 0)
- dpkgMultiArch = true;
- }
+ bool dpkgMultiArch = debSystem::SupportsMultiArch();
// start pty magic before the loop
StartPtyMagic();
a != Args.end(); ++a)
clog << *a << ' ';
clog << endl;
+ for (std::vector<char *>::const_iterator p = Packages.begin();
+ p != Packages.end(); ++p)
+ free(*p);
+ Packages.clear();
continue;
}
Args.push_back(NULL);
SetupSlavePtyMagic();
close(fd[0]); // close the read end of the pipe
- dpkgChrootDirectory();
+ debSystem::DpkgChrootDirectory();
if (chdir(_config->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
_exit(100);
_exit(100);
}
- /* No Job Control Stop Env is a magic dpkg var that prevents it
- from using sigstop */
- putenv((char *)"DPKG_NO_TSTP=yes");
execvp(Args[0], (char**) &Args[0]);
cerr << "Could not exec dpkg!" << endl;
_exit(100);
// wait for input or output here
FD_ZERO(&rfds);
- if (d->master >= 0 && !d->stdin_is_dev_null)
- FD_SET(0, &rfds);
+ if (d->master >= 0 && d->direct_stdin == false && d->stdin_is_dev_null == false)
+ FD_SET(STDIN_FILENO, &rfds);
FD_SET(_dpkgin, &rfds);
if(d->master >= 0)
FD_SET(d->master, &rfds);
{
std::string const oldpkgcache = _config->FindFile("Dir::cache::pkgcache");
if (oldpkgcache.empty() == false && RealFileExists(oldpkgcache) == true &&
- unlink(oldpkgcache.c_str()) == 0)
+ RemoveFile("pkgDPkgPM::Go", oldpkgcache))
{
std::string const srcpkgcache = _config->FindFile("Dir::cache::srcpkgcache");
if (srcpkgcache.empty() == false && RealFileExists(srcpkgcache) == true)
if (apportPkg.end() == true || apportPkg->CurrentVer == 0)
return;
- string pkgname, reportfile, srcpkgname, pkgver, arch;
+ string pkgname, reportfile, pkgver, arch;
string::size_type pos;
FILE *report;
if(pos != string::npos)
pkgname = pkgname.substr(0, pos);
- // find the package versin and source package name
+ // find the package version and source package name
pkgCache::PkgIterator Pkg = Cache.FindPkg(pkgname);
if (Pkg.end() == true)
return;
- pkgCache::VerIterator Ver = Cache.GetCandidateVer(Pkg);
+ pkgCache::VerIterator Ver = Cache.GetCandidateVersion(Pkg);
if (Ver.end() == true)
return;
pkgver = Ver.VerStr() == NULL ? "unknown" : Ver.VerStr();
- pkgRecords Recs(Cache);
- pkgRecords::Parser &Parse = Recs.Lookup(Ver.FileList());
- srcpkgname = Parse.SourcePkg();
- if(srcpkgname.empty())
- srcpkgname = pkgname;
// if the file exists already, we check:
// - if it was reported already (touched by apport).
fprintf(report, "ProblemType: Package\n");
fprintf(report, "Architecture: %s\n", arch.c_str());
time_t now = time(NULL);
- fprintf(report, "Date: %s" , ctime(&now));
+ char ctime_buf[26]; // need at least 26 bytes according to ctime(3)
+ fprintf(report, "Date: %s" , ctime_r(&now, ctime_buf));
fprintf(report, "Package: %s %s\n", pkgname.c_str(), pkgver.c_str());
- fprintf(report, "SourcePackage: %s\n", srcpkgname.c_str());
+ fprintf(report, "SourcePackage: %s\n", Ver.SourcePkgName());
fprintf(report, "ErrorMessage:\n %s\n", errormsg);
// ensure that the log is flushed
}
}
- // log the ordering
- const char *ops_str[] = {"Install", "Configure","Remove","Purge"};
+ // log the ordering, see dpkgpm.h and the "Ops" enum there
+ const char *ops_str[] = {
+ "Install",
+ "Configure",
+ "Remove",
+ "Purge",
+ "ConfigurePending",
+ "TriggersPending",
+ };
fprintf(report, "AptOrdering:\n");
for (vector<Item>::iterator I = List.begin(); I != List.end(); ++I)
if ((*I).Pkg != NULL)