1 // -*- mode: cpp; mode: fold -*-
3 /* ######################################################################
5 DPKG Package Manager - Provide an interface to dpkg
7 ##################################################################### */
12 #include <apt-pkg/cachefile.h>
13 #include <apt-pkg/configuration.h>
14 #include <apt-pkg/depcache.h>
15 #include <apt-pkg/dpkgpm.h>
16 #include <apt-pkg/debsystem.h>
17 #include <apt-pkg/error.h>
18 #include <apt-pkg/fileutl.h>
19 #include <apt-pkg/install-progress.h>
20 #include <apt-pkg/packagemanager.h>
21 #include <apt-pkg/strutl.h>
22 #include <apt-pkg/statechanges.h>
23 #include <apt-pkg/cacheiterators.h>
24 #include <apt-pkg/macros.h>
25 #include <apt-pkg/pkgcache.h>
26 #include <apt-pkg/version.h>
36 #include <sys/ioctl.h>
37 #include <sys/select.h>
41 #include <sys/types.h>
64 APT_PURE
static string
AptHistoryRequestingUser() /*{{{*/
66 const char* EnvKeys
[]{"SUDO_UID", "PKEXEC_UID", "PACKAGEKIT_CALLER_UID"};
68 for (const auto &Key
: EnvKeys
)
70 if (getenv(Key
) != nullptr)
72 int uid
= atoi(getenv(Key
));
75 struct passwd
*result
;
77 if (getpwuid_r(uid
, &pwd
, buf
, sizeof(buf
), &result
) == 0 && result
!= NULL
) {
79 strprintf(res
, "%s (%d)", pwd
.pw_name
, uid
);
88 APT_PURE
static unsigned int EnvironmentSize() /*{{{*/
90 unsigned int size
= 0;
91 char **envp
= environ
;
94 size
+= strlen (*envp
++) + 1;
99 class pkgDPkgPMPrivate
/*{{{*/
102 pkgDPkgPMPrivate() : stdin_is_dev_null(false), dpkgbuf_pos(0),
103 term_out(NULL
), history_out(NULL
),
104 progress(NULL
), tt_is_valid(false), master(-1),
105 slave(NULL
), protect_slave_from_dying(-1),
113 bool stdin_is_dev_null
;
114 // the buffer we use for the dpkg status-fd reading
120 APT::Progress::PackageManager
*progress
;
127 int protect_slave_from_dying
;
131 sigset_t original_sigmask
;
138 // Maps the dpkg "processing" info to human readable names. Entry 0
139 // of each array is the key, entry 1 is the value.
140 const std::pair
<const char *, const char *> PackageProcessingOps
[] = {
141 std::make_pair("install", N_("Installing %s")),
142 std::make_pair("configure", N_("Configuring %s")),
143 std::make_pair("remove", N_("Removing %s")),
144 std::make_pair("purge", N_("Completely removing %s")),
145 std::make_pair("disappear", N_("Noting disappearance of %s")),
146 std::make_pair("trigproc", N_("Running post-installation trigger %s"))
149 const std::pair
<const char *, const char *> * const PackageProcessingOpsBegin
= PackageProcessingOps
;
150 const std::pair
<const char *, const char *> * const PackageProcessingOpsEnd
= PackageProcessingOps
+ sizeof(PackageProcessingOps
) / sizeof(PackageProcessingOps
[0]);
152 // Predicate to test whether an entry in the PackageProcessingOps
153 // array matches a string.
154 class MatchProcessingOp
159 explicit MatchProcessingOp(const char *the_target
)
164 bool operator()(const std::pair
<const char *, const char *> &pair
) const
166 return strcmp(pair
.first
, target
) == 0;
171 // ionice - helper function to ionice the given PID /*{{{*/
172 /* there is no C header for ionice yet - just the syscall interface
173 so we use the binary from util-linux */
174 static bool ionice(int PID
)
176 if (!FileExists("/usr/bin/ionice"))
178 pid_t Process
= ExecFork();
182 snprintf(buf
, sizeof(buf
), "-p%d", PID
);
184 Args
[0] = "/usr/bin/ionice";
188 execv(Args
[0], (char **)Args
);
190 return ExecWait(Process
, "ionice");
193 // FindNowVersion - Helper to find a Version in "now" state /*{{{*/
194 // ---------------------------------------------------------------------
195 /* This is helpful when a package is no longer installed but has residual
199 pkgCache::VerIterator
FindNowVersion(const pkgCache::PkgIterator
&Pkg
)
201 pkgCache::VerIterator Ver
;
202 for (Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
203 for (pkgCache::VerFileIterator Vf
= Ver
.FileList(); Vf
.end() == false; ++Vf
)
204 for (pkgCache::PkgFileIterator F
= Vf
.File(); F
.end() == false; ++F
)
206 if (F
.Archive() != 0 && strcmp(F
.Archive(), "now") == 0)
212 static pkgCache::VerIterator
FindToBeRemovedVersion(pkgCache::PkgIterator
const &Pkg
)/*{{{*/
214 auto const PV
= Pkg
.CurrentVer();
215 if (PV
.end() == false)
217 return FindNowVersion(Pkg
);
221 // DPkgPM::pkgDPkgPM - Constructor /*{{{*/
222 // ---------------------------------------------------------------------
224 pkgDPkgPM::pkgDPkgPM(pkgDepCache
*Cache
)
225 : pkgPackageManager(Cache
),d(new pkgDPkgPMPrivate()), pkgFailures(0), PackagesDone(0), PackagesTotal(0)
229 // DPkgPM::pkgDPkgPM - Destructor /*{{{*/
230 // ---------------------------------------------------------------------
232 pkgDPkgPM::~pkgDPkgPM()
237 // DPkgPM::Install - Install a package /*{{{*/
238 // ---------------------------------------------------------------------
239 /* Add an install operation to the sequence list */
240 bool pkgDPkgPM::Install(PkgIterator Pkg
,string File
)
242 if (File
.empty() == true || Pkg
.end() == true)
243 return _error
->Error("Internal Error, No file name for %s",Pkg
.FullName().c_str());
245 // If the filename string begins with DPkg::Chroot-Directory, return the
246 // substr that is within the chroot so dpkg can access it.
247 string
const chrootdir
= _config
->FindDir("DPkg::Chroot-Directory","/");
248 if (chrootdir
!= "/" && File
.find(chrootdir
) == 0)
250 size_t len
= chrootdir
.length();
251 if (chrootdir
.at(len
- 1) == '/')
253 List
.push_back(Item(Item::Install
,Pkg
,File
.substr(len
)));
256 List
.push_back(Item(Item::Install
,Pkg
,File
));
261 // DPkgPM::Configure - Configure a package /*{{{*/
262 // ---------------------------------------------------------------------
263 /* Add a configure operation to the sequence list */
264 bool pkgDPkgPM::Configure(PkgIterator Pkg
)
266 if (Pkg
.end() == true)
269 List
.push_back(Item(Item::Configure
, Pkg
));
271 // Use triggers for config calls if we configure "smart"
272 // as otherwise Pre-Depends will not be satisfied, see #526774
273 if (_config
->FindB("DPkg::TriggersPending", false) == true)
274 List
.push_back(Item(Item::TriggersPending
, PkgIterator()));
279 // DPkgPM::Remove - Remove a package /*{{{*/
280 // ---------------------------------------------------------------------
281 /* Add a remove operation to the sequence list */
282 bool pkgDPkgPM::Remove(PkgIterator Pkg
,bool Purge
)
284 if (Pkg
.end() == true)
288 List
.push_back(Item(Item::Purge
,Pkg
));
290 List
.push_back(Item(Item::Remove
,Pkg
));
294 // DPkgPM::SendPkgInfo - Send info for install-pkgs hook /*{{{*/
295 // ---------------------------------------------------------------------
296 /* This is part of the helper script communication interface, it sends
297 very complete information down to the other end of the pipe.*/
298 bool pkgDPkgPM::SendV2Pkgs(FILE *F
)
300 return SendPkgsInfo(F
, 2);
302 bool pkgDPkgPM::SendPkgsInfo(FILE * const F
, unsigned int const &Version
)
304 // This version of APT supports only v3, so don't sent higher versions
306 fprintf(F
,"VERSION %u\n", Version
);
308 fprintf(F
,"VERSION 3\n");
310 /* Write out all of the configuration directives by walking the
311 configuration tree */
312 const Configuration::Item
*Top
= _config
->Tree(0);
315 if (Top
->Value
.empty() == false)
318 QuoteString(Top
->FullTag(),"=\"\n").c_str(),
319 QuoteString(Top
->Value
,"\n").c_str());
328 while (Top
!= 0 && Top
->Next
== 0)
335 // Write out the package actions in order.
336 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
338 if(I
->Pkg
.end() == true)
341 pkgDepCache::StateCache
&S
= Cache
[I
->Pkg
];
343 fprintf(F
,"%s ",I
->Pkg
.Name());
345 // Current version which we are going to replace
346 pkgCache::VerIterator CurVer
= I
->Pkg
.CurrentVer();
347 if (CurVer
.end() == true && (I
->Op
== Item::Remove
|| I
->Op
== Item::Purge
))
348 CurVer
= FindNowVersion(I
->Pkg
);
350 if (CurVer
.end() == true)
355 fprintf(F
, "- - none ");
359 fprintf(F
, "%s ", CurVer
.VerStr());
361 fprintf(F
, "%s %s ", CurVer
.Arch(), CurVer
.MultiArchType());
364 // Show the compare operator between current and install version
365 if (S
.InstallVer
!= 0)
367 pkgCache::VerIterator
const InstVer
= S
.InstVerIter(Cache
);
369 if (CurVer
.end() == false)
370 Comp
= InstVer
.CompareVer(CurVer
);
377 fprintf(F
, "%s ", InstVer
.VerStr());
379 fprintf(F
, "%s %s ", InstVer
.Arch(), InstVer
.MultiArchType());
386 fprintf(F
, "> - - none ");
389 // Show the filename/operation
390 if (I
->Op
== Item::Install
)
393 if (I
->File
[0] != '/')
394 fprintf(F
,"**ERROR**\n");
396 fprintf(F
,"%s\n",I
->File
.c_str());
398 else if (I
->Op
== Item::Configure
)
399 fprintf(F
,"**CONFIGURE**\n");
400 else if (I
->Op
== Item::Remove
||
401 I
->Op
== Item::Purge
)
402 fprintf(F
,"**REMOVE**\n");
410 // DPkgPM::RunScriptsWithPkgs - Run scripts with package names on stdin /*{{{*/
411 // ---------------------------------------------------------------------
412 /* This looks for a list of scripts to run from the configuration file
413 each one is run and is fed on standard input a list of all .deb files
414 that are due to be installed. */
415 bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf
)
418 static bool interrupted
= false;
420 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
421 if (Opts
== 0 || Opts
->Child
== 0)
425 sighandler_t old_sigpipe
= signal(SIGPIPE
, SIG_IGN
);
426 sighandler_t old_sigint
= signal(SIGINT
, [](int signum
){
430 unsigned int Count
= 1;
431 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
433 if (Opts
->Value
.empty() == true)
436 if(_config
->FindB("Debug::RunScripts", false) == true)
437 std::clog
<< "Running external script with list of all .deb file: '"
438 << Opts
->Value
<< "'" << std::endl
;
440 // Determine the protocol version
441 string OptSec
= Opts
->Value
;
442 string::size_type Pos
;
443 if ((Pos
= OptSec
.find(' ')) == string::npos
|| Pos
== 0)
444 Pos
= OptSec
.length();
445 OptSec
= "DPkg::Tools::Options::" + string(Opts
->Value
.c_str(),Pos
);
447 unsigned int Version
= _config
->FindI(OptSec
+"::Version",1);
448 unsigned int InfoFD
= _config
->FindI(OptSec
+ "::InfoFD", STDIN_FILENO
);
451 std::set
<int> KeepFDs
;
452 MergeKeepFdsFromConfiguration(KeepFDs
);
454 if (pipe(Pipes
) != 0) {
455 result
= _error
->Errno("pipe","Failed to create IPC pipe to subprocess");
458 if (InfoFD
!= (unsigned)Pipes
[0])
459 SetCloseExec(Pipes
[0],true);
461 KeepFDs
.insert(Pipes
[0]);
464 SetCloseExec(Pipes
[1],true);
466 // Purified Fork for running the script
467 pid_t Process
= ExecFork(KeepFDs
);
471 dup2(Pipes
[0], InfoFD
);
472 SetCloseExec(STDOUT_FILENO
,false);
473 SetCloseExec(STDIN_FILENO
,false);
474 SetCloseExec(STDERR_FILENO
,false);
477 strprintf(hookfd
, "%d", InfoFD
);
478 setenv("APT_HOOK_INFO_FD", hookfd
.c_str(), 1);
480 debSystem::DpkgChrootDirectory();
484 Args
[2] = Opts
->Value
.c_str();
486 execv(Args
[0],(char **)Args
);
490 FILE *F
= fdopen(Pipes
[1],"w");
492 result
= _error
->Errno("fdopen","Failed to open new FD");
496 // Feed it the filenames.
499 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
501 // Only deal with packages to be installed from .deb
502 if (I
->Op
!= Item::Install
)
506 if (I
->File
[0] != '/')
509 /* Feed the filename of each package that is pending install
511 fprintf(F
,"%s\n",I
->File
.c_str());
517 SendPkgsInfo(F
, Version
);
521 // Clean up the sub process
522 if (ExecWait(Process
,Opts
->Value
.c_str()) == false) {
523 result
= _error
->Error("Failure running script %s",Opts
->Value
.c_str());
527 signal(SIGINT
, old_sigint
);
528 signal(SIGPIPE
, old_sigpipe
);
531 result
= _error
->Error("Interrupted");
536 // DPkgPM::DoStdin - Read stdin and pass to master pty /*{{{*/
537 // ---------------------------------------------------------------------
540 void pkgDPkgPM::DoStdin(int master
)
542 unsigned char input_buf
[256] = {0,};
543 ssize_t len
= read(STDIN_FILENO
, input_buf
, sizeof(input_buf
));
545 FileFd::Write(master
, input_buf
, len
);
547 d
->stdin_is_dev_null
= true;
550 // DPkgPM::DoTerminalPty - Read the terminal pty and write log /*{{{*/
551 // ---------------------------------------------------------------------
553 * read the terminal pty and write log
555 void pkgDPkgPM::DoTerminalPty(int master
)
557 unsigned char term_buf
[1024] = {0,0, };
559 ssize_t len
=read(master
, term_buf
, sizeof(term_buf
));
560 if(len
== -1 && errno
== EIO
)
562 // this happens when the child is about to exit, we
563 // give it time to actually exit, otherwise we run
564 // into a race so we sleep for half a second.
565 struct timespec sleepfor
= { 0, 500000000 };
566 nanosleep(&sleepfor
, NULL
);
571 FileFd::Write(1, term_buf
, len
);
573 fwrite(term_buf
, len
, sizeof(char), d
->term_out
);
576 // DPkgPM::ProcessDpkgStatusBuf /*{{{*/
577 void pkgDPkgPM::ProcessDpkgStatusLine(char *line
)
579 bool const Debug
= _config
->FindB("Debug::pkgDPkgProgressReporting",false);
581 std::clog
<< "got from dpkg '" << line
<< "'" << std::endl
;
583 /* dpkg sends strings like this:
584 'status: <pkg>: <pkg qstate>'
585 'status: <pkg>:<arch>: <pkg qstate>'
587 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: pkg'
588 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: trigger'
591 // we need to split on ": " (note the appended space) as the ':' is
592 // part of the pkgname:arch information that dpkg sends
594 // A dpkg error message may contain additional ":" (like
595 // "failed in buffer_write(fd) (10, ret=-1): backend dpkg-deb ..."
596 // so we need to ensure to not split too much
597 std::vector
<std::string
> list
= StringSplit(line
, ": ", 4);
601 std::clog
<< "ignoring line: not enough ':'" << std::endl
;
605 // build the (prefix, pkgname, action) tuple, position of this
606 // is different for "processing" or "status" messages
607 std::string prefix
= APT::String::Strip(list
[0]);
611 // "processing" has the form "processing: action: pkg or trigger"
612 // with action = ["install", "upgrade", "configure", "remove", "purge",
613 // "disappear", "trigproc"]
614 if (prefix
== "processing")
616 pkgname
= APT::String::Strip(list
[2]);
617 action
= APT::String::Strip(list
[1]);
618 // we don't care for the difference (as dpkg doesn't really either)
619 if (action
== "upgrade")
622 // "status" has the form: "status: pkg: state"
623 // with state in ["half-installed", "unpacked", "half-configured",
624 // "installed", "config-files", "not-installed"]
625 else if (prefix
== "status")
627 pkgname
= APT::String::Strip(list
[1]);
628 action
= APT::String::Strip(list
[2]);
631 std::clog
<< "unknown prefix '" << prefix
<< "'" << std::endl
;
636 /* handle the special cases first:
638 errors look like this:
639 'status: /var/cache/apt/archives/krecipes_0.8.1-0ubuntu1_i386.deb : error : trying to overwrite `/usr/share/doc/kde/HTML/en/krecipes/krectip.png', which is also in package krecipes-data
640 and conffile-prompt like this
641 'status:/etc/compiz.conf/compiz.conf : conffile-prompt: 'current-conffile' 'new-conffile' useredited distedited
643 if (prefix
== "status")
645 if(action
== "error")
647 d
->progress
->Error(pkgname
, PackagesDone
, PackagesTotal
,
650 WriteApportReport(pkgname
.c_str(), list
[3].c_str());
653 else if(action
== "conffile-prompt")
655 d
->progress
->ConffilePrompt(pkgname
, PackagesDone
, PackagesTotal
,
661 // at this point we know that we should have a valid pkgname, so build all
664 // dpkg does not always send "pkgname:arch" so we add it here if needed
665 if (pkgname
.find(":") == std::string::npos
)
667 // find the package in the group that is touched by dpkg
668 // if there are multiple pkgs dpkg would send us a full pkgname:arch
669 pkgCache::GrpIterator Grp
= Cache
.FindGrp(pkgname
);
670 if (Grp
.end() == false)
672 pkgCache::PkgIterator P
= Grp
.PackageList();
673 for (; P
.end() != true; P
= Grp
.NextPkg(P
))
675 if(Cache
[P
].Keep() == false || Cache
[P
].ReInstall() == true)
677 pkgname
= P
.FullName();
684 const char* const pkg
= pkgname
.c_str();
685 std::string short_pkgname
= StringSplit(pkgname
, ":")[0];
686 std::string arch
= "";
687 if (pkgname
.find(":") != string::npos
)
688 arch
= StringSplit(pkgname
, ":")[1];
689 std::string i18n_pkgname
= pkgname
;
690 if (arch
.size() != 0)
691 strprintf(i18n_pkgname
, "%s (%s)", short_pkgname
.c_str(), arch
.c_str());
693 // 'processing' from dpkg looks like
694 // 'processing: action: pkg'
695 if(prefix
== "processing")
697 const std::pair
<const char *, const char *> * const iter
=
698 std::find_if(PackageProcessingOpsBegin
,
699 PackageProcessingOpsEnd
,
700 MatchProcessingOp(action
.c_str()));
701 if(iter
== PackageProcessingOpsEnd
)
704 std::clog
<< "ignoring unknown action: " << action
<< std::endl
;
708 strprintf(msg
, _(iter
->second
), i18n_pkgname
.c_str());
709 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
711 // FIXME: this needs a muliarch testcase
712 // FIXME2: is "pkgname" here reliable with dpkg only sending us
714 if (action
== "disappear")
715 handleDisappearAction(pkgname
);
719 if (prefix
== "status")
721 std::vector
<struct DpkgState
> &states
= PackageOps
[pkg
];
722 if (action
== "triggers-pending")
725 std::clog
<< "(parsed from dpkg) pkg: " << short_pkgname
726 << " action: " << action
<< " (prefix 2 to "
727 << PackageOpsDone
[pkg
] << " of " << states
.size() << ")" << endl
;
729 states
.insert(states
.begin(), {"installed", N_("Installed %s")});
730 states
.insert(states
.begin(), {"half-configured", N_("Configuring %s")});
733 else if(PackageOpsDone
[pkg
] < states
.size())
735 char const * next_action
= states
[PackageOpsDone
[pkg
]].state
;
739 if (action == "half-installed" && strcmp("half-configured", next_action) == 0 &&
740 PackageOpsDone[pkg] + 2 < states.size() && action == states[PackageOpsDone[pkg] + 2].state)
743 std::clog << "(parsed from dpkg) pkg: " << short_pkgname << " action: " << action
744 << " pending trigger defused by unpack" << std::endl;
745 // unpacking a package defuses the pending trigger
746 PackageOpsDone[pkg] += 2;
748 next_action = states[PackageOpsDone[pkg]].state;
752 std::clog
<< "(parsed from dpkg) pkg: " << short_pkgname
753 << " action: " << action
<< " (expected: '" << next_action
<< "' "
754 << PackageOpsDone
[pkg
] << " of " << states
.size() << ")" << endl
;
756 // check if the package moved to the next dpkg state
757 if(action
== next_action
)
759 // only read the translation if there is actually a next action
760 char const * const translation
= _(states
[PackageOpsDone
[pkg
]].str
);
762 // we moved from one dpkg state to a new one, report that
763 ++PackageOpsDone
[pkg
];
767 strprintf(msg
, translation
, i18n_pkgname
.c_str());
768 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
775 // DPkgPM::handleDisappearAction /*{{{*/
776 void pkgDPkgPM::handleDisappearAction(string
const &pkgname
)
778 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
779 if (unlikely(Pkg
.end() == true))
782 // record the package name for display and stuff later
783 disappearedPkgs
.insert(Pkg
.FullName(true));
785 // the disappeared package was auto-installed - nothing to do
786 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
788 pkgCache::VerIterator PkgVer
= Cache
[Pkg
].InstVerIter(Cache
);
789 if (unlikely(PkgVer
.end() == true))
791 /* search in the list of dependencies for (Pre)Depends,
792 check if this dependency has a Replaces on our package
793 and if so transfer the manual installed flag to it */
794 for (pkgCache::DepIterator Dep
= PkgVer
.DependsList(); Dep
.end() != true; ++Dep
)
796 if (Dep
->Type
!= pkgCache::Dep::Depends
&&
797 Dep
->Type
!= pkgCache::Dep::PreDepends
)
799 pkgCache::PkgIterator Tar
= Dep
.TargetPkg();
800 if (unlikely(Tar
.end() == true))
802 // the package is already marked as manual
803 if ((Cache
[Tar
].Flags
& pkgCache::Flag::Auto
) != pkgCache::Flag::Auto
)
805 pkgCache::VerIterator TarVer
= Cache
[Tar
].InstVerIter(Cache
);
806 if (TarVer
.end() == true)
808 for (pkgCache::DepIterator Rep
= TarVer
.DependsList(); Rep
.end() != true; ++Rep
)
810 if (Rep
->Type
!= pkgCache::Dep::Replaces
)
812 if (Pkg
!= Rep
.TargetPkg())
814 // okay, they are strongly connected - transfer manual-bit
816 std::clog
<< "transfer manual-bit from disappeared »" << pkgname
<< "« to »" << Tar
.FullName() << "«" << std::endl
;
817 Cache
[Tar
].Flags
&= ~Flag::Auto
;
823 // DPkgPM::DoDpkgStatusFd /*{{{*/
824 void pkgDPkgPM::DoDpkgStatusFd(int statusfd
)
826 ssize_t
const len
= read(statusfd
, &d
->dpkgbuf
[d
->dpkgbuf_pos
],
827 (sizeof(d
->dpkgbuf
)/sizeof(d
->dpkgbuf
[0])) - d
->dpkgbuf_pos
);
830 d
->dpkgbuf_pos
+= (len
/ sizeof(d
->dpkgbuf
[0]));
832 // process line by line from the buffer
833 char *p
= d
->dpkgbuf
, *q
= nullptr;
834 while((q
=(char*)memchr(p
, '\n', (d
->dpkgbuf
+ d
->dpkgbuf_pos
) - p
)) != nullptr)
837 ProcessDpkgStatusLine(p
);
838 p
= q
+ 1; // continue with next line
841 // check if we stripped the buffer clean
842 if (p
> (d
->dpkgbuf
+ d
->dpkgbuf_pos
))
848 // otherwise move the unprocessed tail to the start and update pos
849 memmove(d
->dpkgbuf
, p
, (p
- d
->dpkgbuf
));
850 d
->dpkgbuf_pos
= (d
->dpkgbuf
+ d
->dpkgbuf_pos
) - p
;
853 // DPkgPM::WriteHistoryTag /*{{{*/
854 void pkgDPkgPM::WriteHistoryTag(string
const &tag
, string value
)
856 size_t const length
= value
.length();
859 // poor mans rstrip(", ")
860 if (value
[length
-2] == ',' && value
[length
-1] == ' ')
861 value
.erase(length
- 2, 2);
862 fprintf(d
->history_out
, "%s: %s\n", tag
.c_str(), value
.c_str());
864 // DPkgPM::OpenLog /*{{{*/
865 bool pkgDPkgPM::OpenLog()
867 string
const logdir
= _config
->FindDir("Dir::Log");
868 if(CreateAPTDirectoryIfNeeded(logdir
, logdir
) == false)
869 // FIXME: use a better string after freeze
870 return _error
->Error(_("Directory '%s' missing"), logdir
.c_str());
874 time_t const t
= time(NULL
);
876 struct tm
const * const tmp
= localtime_r(&t
, &tm_buf
);
877 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
880 string
const logfile_name
= flCombine(logdir
,
881 _config
->Find("Dir::Log::Terminal"));
882 if (!logfile_name
.empty())
884 d
->term_out
= fopen(logfile_name
.c_str(),"a");
885 if (d
->term_out
== NULL
)
886 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), logfile_name
.c_str());
887 setvbuf(d
->term_out
, NULL
, _IONBF
, 0);
888 SetCloseExec(fileno(d
->term_out
), true);
889 if (getuid() == 0) // if we aren't root, we can't chown a file, so don't try it
891 struct passwd
*pw
= getpwnam("root");
892 struct group
*gr
= getgrnam("adm");
893 if (pw
!= NULL
&& gr
!= NULL
&& chown(logfile_name
.c_str(), pw
->pw_uid
, gr
->gr_gid
) != 0)
894 _error
->WarningE("OpenLog", "chown to root:adm of file %s failed", logfile_name
.c_str());
896 if (chmod(logfile_name
.c_str(), 0640) != 0)
897 _error
->WarningE("OpenLog", "chmod 0640 of file %s failed", logfile_name
.c_str());
898 fprintf(d
->term_out
, "\nLog started: %s\n", timestr
);
901 // write your history
902 string
const history_name
= flCombine(logdir
,
903 _config
->Find("Dir::Log::History"));
904 if (!history_name
.empty())
906 d
->history_out
= fopen(history_name
.c_str(),"a");
907 if (d
->history_out
== NULL
)
908 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), history_name
.c_str());
909 SetCloseExec(fileno(d
->history_out
), true);
910 chmod(history_name
.c_str(), 0644);
911 fprintf(d
->history_out
, "\nStart-Date: %s\n", timestr
);
912 string remove
, purge
, install
, reinstall
, upgrade
, downgrade
;
913 for (pkgCache::PkgIterator I
= Cache
.PkgBegin(); I
.end() == false; ++I
)
915 enum { CANDIDATE
, CANDIDATE_AUTO
, CURRENT_CANDIDATE
, CURRENT
} infostring
;
917 #define HISTORYINFO(X, Y) { line = &X; infostring = Y; }
918 if (Cache
[I
].NewInstall() == true)
919 HISTORYINFO(install
, CANDIDATE_AUTO
)
920 else if (Cache
[I
].ReInstall() == true)
921 HISTORYINFO(reinstall
, CANDIDATE
)
922 else if (Cache
[I
].Upgrade() == true)
923 HISTORYINFO(upgrade
, CURRENT_CANDIDATE
)
924 else if (Cache
[I
].Downgrade() == true)
925 HISTORYINFO(downgrade
, CURRENT_CANDIDATE
)
926 else if (Cache
[I
].Delete() == true)
927 HISTORYINFO((Cache
[I
].Purge() ? purge
: remove
), CURRENT
)
931 line
->append(I
.FullName(false)).append(" (");
932 switch (infostring
) {
933 case CANDIDATE
: line
->append(Cache
[I
].CandVersion
); break;
935 line
->append(Cache
[I
].CandVersion
);
936 if ((Cache
[I
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
937 line
->append(", automatic");
939 case CURRENT_CANDIDATE
: line
->append(Cache
[I
].CurVersion
).append(", ").append(Cache
[I
].CandVersion
); break;
940 case CURRENT
: line
->append(Cache
[I
].CurVersion
); break;
944 if (_config
->Exists("Commandline::AsString") == true)
945 WriteHistoryTag("Commandline", _config
->Find("Commandline::AsString"));
946 std::string RequestingUser
= AptHistoryRequestingUser();
947 if (RequestingUser
!= "")
948 WriteHistoryTag("Requested-By", RequestingUser
);
949 WriteHistoryTag("Install", install
);
950 WriteHistoryTag("Reinstall", reinstall
);
951 WriteHistoryTag("Upgrade", upgrade
);
952 WriteHistoryTag("Downgrade",downgrade
);
953 WriteHistoryTag("Remove",remove
);
954 WriteHistoryTag("Purge",purge
);
955 fflush(d
->history_out
);
961 // DPkg::CloseLog /*{{{*/
962 bool pkgDPkgPM::CloseLog()
965 time_t t
= time(NULL
);
967 struct tm
*tmp
= localtime_r(&t
, &tm_buf
);
968 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
972 fprintf(d
->term_out
, "Log ended: ");
973 fprintf(d
->term_out
, "%s", timestr
);
974 fprintf(d
->term_out
, "\n");
981 if (disappearedPkgs
.empty() == false)
984 for (std::set
<std::string
>::const_iterator d
= disappearedPkgs
.begin();
985 d
!= disappearedPkgs
.end(); ++d
)
987 pkgCache::PkgIterator P
= Cache
.FindPkg(*d
);
988 disappear
.append(*d
);
990 disappear
.append(", ");
992 disappear
.append(" (").append(Cache
[P
].CurVersion
).append("), ");
994 WriteHistoryTag("Disappeared", disappear
);
996 if (d
->dpkg_error
.empty() == false)
997 fprintf(d
->history_out
, "Error: %s\n", d
->dpkg_error
.c_str());
998 fprintf(d
->history_out
, "End-Date: %s\n", timestr
);
999 fclose(d
->history_out
);
1001 d
->history_out
= NULL
;
1007 // DPkgPM::BuildPackagesProgressMap /*{{{*/
1008 void pkgDPkgPM::BuildPackagesProgressMap()
1010 // map the dpkg states to the operations that are performed
1011 // (this is sorted in the same way as Item::Ops)
1012 static const std::array
<std::array
<DpkgState
, 3>, 4> DpkgStatesOpMap
= {{
1013 // Install operation
1015 {"half-installed", N_("Preparing %s")},
1016 {"unpacked", N_("Unpacking %s") },
1019 // Configure operation
1021 {"unpacked",N_("Preparing to configure %s") },
1022 {"half-configured", N_("Configuring %s") },
1023 { "installed", N_("Installed %s")},
1027 {"half-configured", N_("Preparing for removal of %s")},
1028 {"half-installed", N_("Removing %s")},
1029 {"config-files", N_("Removed %s")},
1033 {"config-files", N_("Preparing to completely remove %s")},
1034 {"not-installed", N_("Completely removed %s")},
1038 static_assert(Item::Purge
== 3, "Enum item has unexpected index for mapping array");
1040 // init the PackageOps map, go over the list of packages that
1041 // that will be [installed|configured|removed|purged] and add
1042 // them to the PackageOps map (the dpkg states it goes through)
1043 // and the PackageOpsTranslations (human readable strings)
1044 for (auto &&I
: List
)
1046 if(I
.Pkg
.end() == true)
1049 string
const name
= I
.Pkg
.FullName();
1050 PackageOpsDone
[name
] = 0;
1051 auto AddToPackageOps
= std::back_inserter(PackageOps
[name
]);
1052 if (I
.Op
== Item::Purge
&& I
.Pkg
->CurrentVer
!= 0)
1054 // purging a package which is installed first passes through remove states
1055 auto const DpkgOps
= DpkgStatesOpMap
[Item::Remove
];
1056 std::copy(DpkgOps
.begin(), DpkgOps
.end(), AddToPackageOps
);
1057 PackagesTotal
+= DpkgOps
.size();
1059 auto const DpkgOps
= DpkgStatesOpMap
[I
.Op
];
1060 std::copy_if(DpkgOps
.begin(), DpkgOps
.end(), AddToPackageOps
, [&](DpkgState
const &state
) {
1061 if (state
.state
== nullptr)
1067 /* one extra: We don't want the progress bar to reach 100%, especially not
1068 if we call dpkg --configure --pending and process a bunch of triggers
1069 while showing 100%. Also, spindown takes a while, so never reaching 100%
1070 is way more correct than reaching 100% while still doing stuff even if
1071 doing it this way is slightly bending the rules */
1075 bool pkgDPkgPM::Go(int StatusFd
) /*{{{*/
1077 APT::Progress::PackageManager
*progress
= NULL
;
1079 progress
= APT::Progress::PackageManagerProgressFactory();
1081 progress
= new APT::Progress::PackageManagerProgressFd(StatusFd
);
1083 return Go(progress
);
1086 void pkgDPkgPM::StartPtyMagic() /*{{{*/
1088 if (_config
->FindB("Dpkg::Use-Pty", true) == false)
1091 if (d
->slave
!= NULL
)
1097 if (isatty(STDIN_FILENO
) == 0)
1098 d
->direct_stdin
= true;
1100 _error
->PushToStack();
1102 d
->master
= posix_openpt(O_RDWR
| O_NOCTTY
);
1103 if (d
->master
== -1)
1104 _error
->Errno("posix_openpt", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1105 else if (unlockpt(d
->master
) == -1)
1106 _error
->Errno("unlockpt", "Unlocking the slave of master fd %d failed!", d
->master
);
1109 #ifdef HAVE_PTSNAME_R
1110 char slave_name
[64]; // 64 is used by bionic
1111 if (ptsname_r(d
->master
, slave_name
, sizeof(slave_name
)) != 0)
1113 char const * const slave_name
= ptsname(d
->master
);
1114 if (slave_name
== NULL
)
1116 _error
->Errno("ptsname", "Getting name for slave of master fd %d failed!", d
->master
);
1119 d
->slave
= strdup(slave_name
);
1120 if (d
->slave
== NULL
)
1121 _error
->Errno("strdup", "Copying name %s for slave of master fd %d failed!", slave_name
, d
->master
);
1122 else if (grantpt(d
->master
) == -1)
1123 _error
->Errno("grantpt", "Granting access to slave %s based on master fd %d failed!", slave_name
, d
->master
);
1124 else if (tcgetattr(STDIN_FILENO
, &d
->tt
) == 0)
1126 d
->tt_is_valid
= true;
1127 struct termios raw_tt
;
1128 // copy window size of stdout if its a 'good' terminal
1129 if (tcgetattr(STDOUT_FILENO
, &raw_tt
) == 0)
1132 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) < 0)
1133 _error
->Errno("ioctl", "Getting TIOCGWINSZ from stdout failed!");
1134 if (ioctl(d
->master
, TIOCSWINSZ
, &win
) < 0)
1135 _error
->Errno("ioctl", "Setting TIOCSWINSZ for master fd %d failed!", d
->master
);
1137 if (tcsetattr(d
->master
, TCSANOW
, &d
->tt
) == -1)
1138 _error
->Errno("tcsetattr", "Setting in Start via TCSANOW for master fd %d failed!", d
->master
);
1142 raw_tt
.c_lflag
&= ~ECHO
;
1143 raw_tt
.c_lflag
|= ISIG
;
1144 // block SIGTTOU during tcsetattr to prevent a hang if
1145 // the process is a member of the background process group
1146 // http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html
1147 sigemptyset(&d
->sigmask
);
1148 sigaddset(&d
->sigmask
, SIGTTOU
);
1149 sigprocmask(SIG_BLOCK
,&d
->sigmask
, &d
->original_sigmask
);
1150 if (tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &raw_tt
) == -1)
1151 _error
->Errno("tcsetattr", "Setting in Start via TCSAFLUSH for stdin failed!");
1152 sigprocmask(SIG_SETMASK
, &d
->original_sigmask
, NULL
);
1155 if (d
->slave
!= NULL
)
1157 /* on linux, closing (and later reopening) all references to the slave
1158 makes the slave a death end, so we open it here to have one open all
1159 the time. We could use this fd in SetupSlavePtyMagic() for linux, but
1160 on kfreebsd we get an incorrect ("step like") output then while it has
1161 no problem with closing all references… so to avoid platform specific
1162 code here we combine both and be happy once more */
1163 d
->protect_slave_from_dying
= open(d
->slave
, O_RDWR
| O_CLOEXEC
| O_NOCTTY
);
1168 if (_error
->PendingError() == true)
1170 if (d
->master
!= -1)
1175 if (d
->slave
!= NULL
)
1180 _error
->DumpErrors(std::cerr
, GlobalError::DEBUG
, false);
1182 _error
->RevertToStack();
1185 void pkgDPkgPM::SetupSlavePtyMagic() /*{{{*/
1187 if(d
->master
== -1 || d
->slave
== NULL
)
1190 if (close(d
->master
) == -1)
1191 _error
->FatalE("close", "Closing master %d in child failed!", d
->master
);
1194 _error
->FatalE("setsid", "Starting a new session for child failed!");
1196 int const slaveFd
= open(d
->slave
, O_RDWR
| O_NOCTTY
);
1198 _error
->FatalE("open", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1199 else if (ioctl(slaveFd
, TIOCSCTTY
, 0) < 0)
1200 _error
->FatalE("ioctl", "Setting TIOCSCTTY for slave fd %d failed!", slaveFd
);
1203 unsigned short i
= 0;
1204 if (d
->direct_stdin
== true)
1207 if (dup2(slaveFd
, i
) == -1)
1208 _error
->FatalE("dup2", "Dupping %d to %d in child failed!", slaveFd
, i
);
1210 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSANOW
, &d
->tt
) < 0)
1211 _error
->FatalE("tcsetattr", "Setting in Setup via TCSANOW for slave fd %d failed!", slaveFd
);
1218 void pkgDPkgPM::StopPtyMagic() /*{{{*/
1220 if (d
->slave
!= NULL
)
1223 if (d
->protect_slave_from_dying
!= -1)
1225 close(d
->protect_slave_from_dying
);
1226 d
->protect_slave_from_dying
= -1;
1230 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &d
->tt
) == -1)
1231 _error
->FatalE("tcsetattr", "Setting in Stop via TCSAFLUSH for stdin failed!");
1237 static void cleanUpTmpDir(char * const tmpdir
) /*{{{*/
1239 if (tmpdir
== nullptr)
1241 DIR * const D
= opendir(tmpdir
);
1243 _error
->Errno("opendir", _("Unable to read %s"), tmpdir
);
1246 auto const dfd
= dirfd(D
);
1247 for (struct dirent
*Ent
= readdir(D
); Ent
!= nullptr; Ent
= readdir(D
))
1249 if (Ent
->d_name
[0] == '.')
1251 #ifdef _DIRENT_HAVE_D_TYPE
1252 if (unlikely(Ent
->d_type
!= DT_LNK
&& Ent
->d_type
!= DT_UNKNOWN
))
1255 if (unlikely(unlinkat(dfd
, Ent
->d_name
, 0) != 0))
1265 // DPkgPM::Go - Run the sequence /*{{{*/
1266 // ---------------------------------------------------------------------
1267 /* This globs the operations and calls dpkg
1269 * If it is called with a progress object apt will report the install
1270 * progress to this object. It maps the dpkg states a package goes
1271 * through to human readable (and i10n-able)
1272 * names and calculates a percentage for each step.
1274 bool pkgDPkgPM::Go(APT::Progress::PackageManager
*progress
)
1276 auto const ItemIsEssential
= [](pkgDPkgPM::Item
const &I
) {
1277 static auto const cachegen
= _config
->Find("pkgCacheGen::Essential");
1278 if (cachegen
== "none" || cachegen
== "native")
1280 if (unlikely(I
.Pkg
.end()))
1282 return (I
.Pkg
->Flags
& pkgCache::Flag::Essential
) != 0;
1285 pkgPackageManager::SigINTStop
= false;
1286 d
->progress
= progress
;
1288 // Generate the base argument list for dpkg
1289 std::vector
<std::string
> const sArgs
= debSystem::GetDpkgBaseCommand();
1290 std::vector
<const char *> Args(sArgs
.size(), NULL
);
1291 std::transform(sArgs
.begin(), sArgs
.end(), Args
.begin(),
1292 [](std::string
const &s
) { return s
.c_str(); });
1293 unsigned long long const StartSize
= std::accumulate(sArgs
.begin(), sArgs
.end(), 0llu,
1294 [](unsigned long long const i
, std::string
const &s
) { return i
+ s
.length(); });
1295 size_t const BaseArgs
= Args
.size();
1300 // try to figure out the max environment size
1301 int OSArgMax
= sysconf(_SC_ARG_MAX
);
1304 OSArgMax
-= EnvironmentSize() - 2*1024;
1305 unsigned int const MaxArgBytes
= _config
->FindI("Dpkg::MaxArgBytes", OSArgMax
);
1306 bool const NoTriggers
= _config
->FindB("DPkg::NoTriggers", true);
1308 if (RunScripts("DPkg::Pre-Invoke") == false)
1311 if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false)
1314 auto const noopDPkgInvocation
= _config
->FindB("Debug::pkgDPkgPM",false);
1315 // store auto-bits as they are supposed to be after dpkg is run
1316 if (noopDPkgInvocation
== false)
1317 Cache
.writeStateFile(NULL
);
1319 bool dpkg_recursive_install
= _config
->FindB("dpkg::install::recursive", false);
1320 if (_config
->FindB("dpkg::install::recursive::force", false) == false)
1322 // dpkg uses a sorted treewalk since that version which enables the workaround to work
1323 auto const dpkgpkg
= Cache
.FindPkg("dpkg");
1324 if (likely(dpkgpkg
.end() == false && dpkgpkg
->CurrentVer
!= 0))
1325 dpkg_recursive_install
= Cache
.VS().CmpVersion("1.18.5", dpkgpkg
.CurrentVer().VerStr()) <= 0;
1327 // no point in doing this dance for a handful of packages only
1328 unsigned int const dpkg_recursive_install_min
= _config
->FindB("dpkg::install::recursive::minimum", 5);
1329 // FIXME: workaround for dpkg bug, see our ./test-bug-740843-versioned-up-down-breaks test
1330 bool const dpkg_recursive_install_numbered
= _config
->FindB("dpkg::install::recursive::numbered", true);
1332 decltype(List
)::const_iterator::difference_type
const notconfidx
=
1333 _config
->FindB("Dpkg::ExplicitLastConfigure", false) ? std::numeric_limits
<decltype(notconfidx
)>::max() :
1334 std::distance(List
.cbegin(), std::find_if_not(List
.crbegin(), List
.crend(), [](Item
const &i
) { return i
.Op
== Item::Configure
; }).base());
1336 // support subpressing of triggers processing for special
1337 // cases like d-i that runs the triggers handling manually
1338 bool const TriggersPending
= _config
->FindB("DPkg::TriggersPending", false);
1339 bool const ConfigurePending
= _config
->FindB("DPkg::ConfigurePending", true);
1340 if (ConfigurePending
)
1341 List
.push_back(Item(Item::ConfigurePending
, PkgIterator()));
1344 BuildPackagesProgressMap();
1346 if (notconfidx
!= std::numeric_limits
<decltype(notconfidx
)>::max())
1348 if (ConfigurePending
)
1349 List
.erase(std::next(List
.begin(), notconfidx
), std::prev(List
.end()));
1351 List
.erase(std::next(List
.begin(), notconfidx
), List
.end());
1354 APT::StateChanges currentStates
;
1355 if (_config
->FindB("dpkg::selection::current::saveandrestore", true))
1357 for (auto Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
1358 if (Pkg
->CurrentVer
== 0)
1360 else if (Pkg
->SelectedState
== pkgCache::State::Purge
)
1361 currentStates
.Purge(FindToBeRemovedVersion(Pkg
));
1362 else if (Pkg
->SelectedState
== pkgCache::State::DeInstall
)
1363 currentStates
.Remove(FindToBeRemovedVersion(Pkg
));
1364 if (currentStates
.empty() == false)
1366 APT::StateChanges cleanStates
;
1367 for (auto && P
: currentStates
.Remove())
1368 cleanStates
.Install(P
);
1369 for (auto && P
: currentStates
.Purge())
1370 cleanStates
.Install(P
);
1371 if (cleanStates
.Save(false) == false)
1372 return _error
->Error("Couldn't clean the currently selected dpkg states");
1375 APT::StateChanges approvedStates
;
1376 if (_config
->FindB("dpkg::selection::remove::approved", true))
1378 for (auto && I
: List
)
1379 if (I
.Op
== Item::Remove
&& Cache
[I
.Pkg
].Delete())
1380 approvedStates
.Remove(FindToBeRemovedVersion(I
.Pkg
));
1381 else if (I
.Op
== Item::Purge
&& Cache
[I
.Pkg
].Purge())
1382 approvedStates
.Purge(FindToBeRemovedVersion(I
.Pkg
));
1383 if (approvedStates
.Save(false) == false)
1385 _error
->Error("Couldn't record the approved state changes as dpkg selection states");
1386 if (currentStates
.Save(false) == false)
1387 _error
->Error("Couldn't restore dpkg selection states which were present before this interaction!");
1392 std::vector
<bool> toBeRemoved(Cache
.Head().PackageCount
, false);
1393 std::vector
<bool> toBePurged(Cache
.Head().PackageCount
, false);
1394 for (auto Pkg
= Cache
.PkgBegin(); Pkg
.end() == false; ++Pkg
)
1395 if (Cache
[Pkg
].Purge())
1396 toBePurged
[Pkg
->ID
] = true;
1397 else if (Cache
[Pkg
].Delete())
1398 toBeRemoved
[Pkg
->ID
] = true;
1399 for (auto && I
: approvedStates
.Remove())
1400 toBeRemoved
[I
.ParentPkg()->ID
] = false;
1401 for (auto && I
: approvedStates
.Purge())
1402 toBePurged
[I
.ParentPkg()->ID
] = false;
1403 if (std::find(toBeRemoved
.begin(), toBeRemoved
.end(), true) != toBeRemoved
.end())
1405 if (ConfigurePending
)
1406 List
.emplace(std::prev(List
.end()), Item::RemovePending
, pkgCache::PkgIterator());
1408 List
.emplace_back(Item::RemovePending
, pkgCache::PkgIterator());
1410 if (std::find(toBePurged
.begin(), toBePurged
.end(), true) != toBePurged
.end())
1412 if (ConfigurePending
)
1413 List
.emplace(std::prev(List
.end()), Item::PurgePending
, pkgCache::PkgIterator());
1415 List
.emplace_back(Item::PurgePending
, pkgCache::PkgIterator());
1420 d
->stdin_is_dev_null
= false;
1425 bool dpkgMultiArch
= debSystem::SupportsMultiArch();
1427 // start pty magic before the loop
1430 // Tell the progress that its starting and fork dpkg
1431 d
->progress
->Start(d
->master
);
1433 // this loop is runs once per dpkg operation
1434 vector
<Item
>::const_iterator I
= List
.begin();
1435 while (I
!= List
.end())
1437 // Do all actions with the same Op in one run
1438 vector
<Item
>::const_iterator J
= I
;
1439 if (TriggersPending
== true)
1440 for (; J
!= List
.end(); ++J
)
1444 if (J
->Op
!= Item::TriggersPending
)
1446 vector
<Item
>::const_iterator T
= J
+ 1;
1447 if (T
!= List
.end() && T
->Op
== I
->Op
)
1452 for (; J
!= List
.end() && J
->Op
== I
->Op
; ++J
)
1455 auto const size
= (J
- I
) + 10;
1457 // start with the baseset of arguments
1458 auto Size
= StartSize
;
1459 Args
.erase(Args
.begin() + BaseArgs
, Args
.end());
1461 // keep track of allocated strings for multiarch package names
1462 std::vector
<char *> Packages(size
, nullptr);
1466 return _error
->Errno("pipe","Failed to create IPC pipe to dpkg");
1468 #define ADDARG(X) Args.push_back(X); Size += strlen(X)
1469 #define ADDARGC(X) Args.push_back(X); Size += sizeof(X) - 1
1471 ADDARGC("--status-fd");
1472 char status_fd_buf
[20];
1473 snprintf(status_fd_buf
,sizeof(status_fd_buf
),"%i", fd
[1]);
1474 ADDARG(status_fd_buf
);
1475 unsigned long const Op
= I
->Op
;
1477 if (NoTriggers
== true && I
->Op
!= Item::TriggersPending
&&
1478 I
->Op
!= Item::ConfigurePending
)
1480 ADDARGC("--no-triggers");
1486 ADDARGC("--force-depends");
1487 if (std::any_of(I
, J
, ItemIsEssential
))
1488 ADDARGC("--force-remove-essential");
1489 ADDARGC("--remove");
1493 ADDARGC("--force-depends");
1494 if (std::any_of(I
, J
, ItemIsEssential
))
1495 ADDARGC("--force-remove-essential");
1499 case Item::Configure
:
1500 ADDARGC("--configure");
1503 case Item::ConfigurePending
:
1504 ADDARGC("--configure");
1505 ADDARGC("--pending");
1508 case Item::TriggersPending
:
1509 ADDARGC("--triggers-only");
1510 ADDARGC("--pending");
1513 case Item::RemovePending
:
1514 ADDARGC("--remove");
1515 ADDARGC("--pending");
1518 case Item::PurgePending
:
1520 ADDARGC("--pending");
1524 ADDARGC("--unpack");
1525 ADDARGC("--auto-deconfigure");
1529 char * tmpdir_to_free
= nullptr;
1531 // Write in the file or package names
1532 if (I
->Op
== Item::Install
)
1534 auto const installsToDo
= J
- I
;
1535 if (dpkg_recursive_install
== true && dpkg_recursive_install_min
< installsToDo
)
1538 strprintf(tmpdir
, "%s/apt-dpkg-install-XXXXXX", GetTempDir().c_str());
1539 tmpdir_to_free
= strndup(tmpdir
.data(), tmpdir
.length());
1540 if (mkdtemp(tmpdir_to_free
) == nullptr)
1541 return _error
->Errno("DPkg::Go", "mkdtemp of %s failed in preparation of calling dpkg unpack", tmpdir_to_free
);
1544 for (auto c
= installsToDo
- 1; (c
= c
/10) != 0; ++p
);
1545 for (unsigned long n
= 0; I
!= J
; ++n
, ++I
)
1547 if (I
->File
[0] != '/')
1548 return _error
->Error("Internal Error, Pathname to install is not absolute '%s'",I
->File
.c_str());
1549 auto const file
= flNotDir(I
->File
);
1550 std::string linkpath
;
1551 if (dpkg_recursive_install_numbered
)
1552 strprintf(linkpath
, "%s/%.*lu-%s", tmpdir_to_free
, p
, n
, file
.c_str());
1554 strprintf(linkpath
, "%s/%s", tmpdir_to_free
, file
.c_str());
1555 if (symlink(I
->File
.c_str(), linkpath
.c_str()) != 0)
1556 return _error
->Errno("DPkg::Go", "Symlinking %s to %s failed!", I
->File
.c_str(), linkpath
.c_str());
1558 ADDARGC("--recursive");
1559 ADDARG(tmpdir_to_free
);
1563 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1565 if (I
->File
[0] != '/')
1566 return _error
->Error("Internal Error, Pathname to install is not absolute '%s'",I
->File
.c_str());
1567 Args
.push_back(I
->File
.c_str());
1568 Size
+= I
->File
.length();
1574 string
const nativeArch
= _config
->Find("APT::Architecture");
1575 unsigned long const oldSize
= I
->Op
== Item::Configure
? Size
: 0;
1576 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1578 if((*I
).Pkg
.end() == true)
1580 if (I
->Op
== Item::Configure
&& disappearedPkgs
.find(I
->Pkg
.FullName(true)) != disappearedPkgs
.end())
1582 // We keep this here to allow "smooth" transitions from e.g. multiarch dpkg/ubuntu to dpkg/debian
1583 if (dpkgMultiArch
== false && (I
->Pkg
.Arch() == nativeArch
||
1584 strcmp(I
->Pkg
.Arch(), "all") == 0 ||
1585 strcmp(I
->Pkg
.Arch(), "none") == 0))
1587 char const * const name
= I
->Pkg
.Name();
1592 pkgCache::VerIterator PkgVer
;
1593 std::string name
= I
->Pkg
.Name();
1594 if (Op
== Item::Remove
|| Op
== Item::Purge
)
1595 PkgVer
= FindToBeRemovedVersion(I
->Pkg
);
1597 PkgVer
= Cache
[I
->Pkg
].InstVerIter(Cache
);
1598 if (strcmp(I
->Pkg
.Arch(), "none") == 0)
1599 ; // never arch-qualify a package without an arch
1600 else if (PkgVer
.end() == false)
1601 name
.append(":").append(PkgVer
.Arch());
1603 _error
->Warning("Can not find PkgVer for '%s'", name
.c_str());
1604 char * const fullname
= strdup(name
.c_str());
1605 Packages
.push_back(fullname
);
1609 // skip configure action if all sheduled packages disappeared
1610 if (oldSize
== Size
)
1618 if (noopDPkgInvocation
== true)
1620 for (std::vector
<const char *>::const_iterator a
= Args
.begin();
1621 a
!= Args
.end(); ++a
)
1624 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1625 p
!= Packages
.end(); ++p
)
1630 cleanUpTmpDir(tmpdir_to_free
);
1633 Args
.push_back(NULL
);
1639 /* Mask off sig int/quit. We do this because dpkg also does when
1640 it forks scripts. What happens is that when you hit ctrl-c it sends
1641 it to all processes in the group. Since dpkg ignores the signal
1642 it doesn't die but we do! So we must also ignore it */
1643 sighandler_t old_SIGQUIT
= signal(SIGQUIT
,SIG_IGN
);
1644 sighandler_t old_SIGINT
= signal(SIGINT
,SigINT
);
1646 // Check here for any SIGINT
1647 if (pkgPackageManager::SigINTStop
&& (Op
== Item::Remove
|| Op
== Item::Purge
|| Op
== Item::Install
))
1650 // ignore SIGHUP as well (debian #463030)
1651 sighandler_t old_SIGHUP
= signal(SIGHUP
,SIG_IGN
);
1654 d
->progress
->StartDpkg();
1655 std::set
<int> KeepFDs
;
1656 KeepFDs
.insert(fd
[1]);
1657 MergeKeepFdsFromConfiguration(KeepFDs
);
1658 pid_t Child
= ExecFork(KeepFDs
);
1661 // This is the child
1662 SetupSlavePtyMagic();
1663 close(fd
[0]); // close the read end of the pipe
1665 debSystem::DpkgChrootDirectory();
1667 if (chdir(_config
->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
1670 if (_config
->FindB("DPkg::FlushSTDIN",true) == true && isatty(STDIN_FILENO
))
1674 if ((Flags
= fcntl(STDIN_FILENO
,F_GETFL
,dummy
)) < 0)
1677 // Discard everything in stdin before forking dpkg
1678 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
| O_NONBLOCK
) < 0)
1681 while (read(STDIN_FILENO
,&dummy
,1) == 1);
1683 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
& (~(long)O_NONBLOCK
)) < 0)
1687 // if color support isn't enabled/disabled explicitly tell
1688 // dpkg to use the same state apt is using for its color support
1689 if (_config
->FindB("APT::Color", false) == true)
1690 setenv("DPKG_COLORS", "always", 0);
1692 setenv("DPKG_COLORS", "never", 0);
1694 execvp(Args
[0], (char**) &Args
[0]);
1695 cerr
<< "Could not exec dpkg!" << endl
;
1699 // we read from dpkg here
1700 int const _dpkgin
= fd
[0];
1701 close(fd
[1]); // close the write end of the pipe
1704 if (_config
->FindB("DPkg::UseIoNice", false) == true)
1708 sigemptyset(&d
->sigmask
);
1709 sigprocmask(SIG_BLOCK
,&d
->sigmask
,&d
->original_sigmask
);
1711 /* free vectors (and therefore memory) as we don't need the included data anymore */
1712 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1713 p
!= Packages
.end(); ++p
)
1717 // the result of the waitpid call
1720 bool waitpid_failure
= false;
1721 while ((res
=waitpid(Child
,&Status
, WNOHANG
)) != Child
) {
1723 // error handling, waitpid returned -1
1726 waitpid_failure
= true;
1730 // wait for input or output here
1732 if (d
->master
>= 0 && d
->direct_stdin
== false && d
->stdin_is_dev_null
== false)
1733 FD_SET(STDIN_FILENO
, &rfds
);
1734 FD_SET(_dpkgin
, &rfds
);
1736 FD_SET(d
->master
, &rfds
);
1738 tv
.tv_nsec
= d
->progress
->GetPulseInterval();
1739 auto const select_ret
= pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
, NULL
,
1740 &tv
, &d
->original_sigmask
);
1741 d
->progress
->Pulse();
1742 if (select_ret
== 0)
1744 else if (select_ret
< 0 && errno
== EINTR
)
1746 else if (select_ret
< 0)
1748 perror("select() returned error");
1752 if(d
->master
>= 0 && FD_ISSET(d
->master
, &rfds
))
1753 DoTerminalPty(d
->master
);
1754 if(d
->master
>= 0 && FD_ISSET(0, &rfds
))
1756 if(FD_ISSET(_dpkgin
, &rfds
))
1757 DoDpkgStatusFd(_dpkgin
);
1761 // Restore sig int/quit
1762 signal(SIGQUIT
,old_SIGQUIT
);
1763 signal(SIGINT
,old_SIGINT
);
1764 signal(SIGHUP
,old_SIGHUP
);
1766 cleanUpTmpDir(tmpdir_to_free
);
1768 if (waitpid_failure
== true)
1770 strprintf(d
->dpkg_error
, "Sub-process %s couldn't be waited for.",Args
[0]);
1771 _error
->Error("%s", d
->dpkg_error
.c_str());
1775 // Check for an error code.
1776 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
1778 // if it was set to "keep-dpkg-running" then we won't return
1779 // here but keep the loop going and just report it as a error
1781 bool const stopOnError
= _config
->FindB("Dpkg::StopOnError",true);
1783 if (WIFSIGNALED(Status
) != 0 && WTERMSIG(Status
) == SIGSEGV
)
1784 strprintf(d
->dpkg_error
, "Sub-process %s received a segmentation fault.",Args
[0]);
1785 else if (WIFEXITED(Status
) != 0)
1786 strprintf(d
->dpkg_error
, "Sub-process %s returned an error code (%u)",Args
[0],WEXITSTATUS(Status
));
1788 strprintf(d
->dpkg_error
, "Sub-process %s exited unexpectedly",Args
[0]);
1789 _error
->Error("%s", d
->dpkg_error
.c_str());
1795 // dpkg is done at this point
1799 if (d
->dpkg_error
.empty() == false)
1801 APT::StateChanges undo
;
1802 auto && undoRem
= approvedStates
.Remove();
1803 std::move(undoRem
.begin(), undoRem
.end(), std::back_inserter(undo
.Remove()));
1804 auto && undoPur
= approvedStates
.Purge();
1805 std::move(undoPur
.begin(), undoPur
.end(), std::back_inserter(undo
.Purge()));
1806 approvedStates
.clear();
1807 if (undo
.Save(false) == false)
1808 _error
->Error("Couldn't revert dpkg selection for approved remove/purge after an error was encountered!");
1810 if (currentStates
.Save(false) == false)
1811 _error
->Error("Couldn't restore dpkg selection states which were present before this interaction!");
1813 if (pkgPackageManager::SigINTStop
)
1814 _error
->Warning(_("Operation was interrupted before it could finish"));
1816 if (noopDPkgInvocation
== false)
1818 std::string
const oldpkgcache
= _config
->FindFile("Dir::cache::pkgcache");
1819 if (oldpkgcache
.empty() == false && RealFileExists(oldpkgcache
) == true &&
1820 RemoveFile("pkgDPkgPM::Go", oldpkgcache
))
1822 std::string
const srcpkgcache
= _config
->FindFile("Dir::cache::srcpkgcache");
1823 if (srcpkgcache
.empty() == false && RealFileExists(srcpkgcache
) == true)
1825 _error
->PushToStack();
1826 pkgCacheFile CacheFile
;
1827 CacheFile
.BuildCaches(NULL
, true);
1828 _error
->RevertToStack();
1833 // disappearing packages can forward their auto-bit
1834 if (disappearedPkgs
.empty() == false)
1835 Cache
.writeStateFile(NULL
);
1837 d
->progress
->Stop();
1839 if (RunScripts("DPkg::Post-Invoke") == false)
1842 return d
->dpkg_error
.empty();
1845 void SigINT(int /*sig*/) {
1846 pkgPackageManager::SigINTStop
= true;
1849 // pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/
1850 // ---------------------------------------------------------------------
1852 void pkgDPkgPM::Reset()
1854 List
.erase(List
.begin(),List
.end());
1857 // pkgDpkgPM::WriteApportReport - write out error report pkg failure /*{{{*/
1858 // ---------------------------------------------------------------------
1860 void pkgDPkgPM::WriteApportReport(const char *pkgpath
, const char *errormsg
)
1862 // If apport doesn't exist or isn't installed do nothing
1863 // This e.g. prevents messages in 'universes' without apport
1864 pkgCache::PkgIterator apportPkg
= Cache
.FindPkg("apport");
1865 if (apportPkg
.end() == true || apportPkg
->CurrentVer
== 0)
1868 string pkgname
, reportfile
, pkgver
, arch
;
1869 string::size_type pos
;
1872 if (_config
->FindB("Dpkg::ApportFailureReport", true) == false)
1874 std::clog
<< "configured to not write apport reports" << std::endl
;
1878 // only report the first errors
1879 if(pkgFailures
> _config
->FindI("APT::Apport::MaxReports", 3))
1881 std::clog
<< _("No apport report written because MaxReports is reached already") << std::endl
;
1885 // check if its not a follow up error
1886 const char *needle
= dgettext("dpkg", "dependency problems - leaving unconfigured");
1887 if(strstr(errormsg
, needle
) != NULL
) {
1888 std::clog
<< _("No apport report written because the error message indicates its a followup error from a previous failure.") << std::endl
;
1892 // do not report disk-full failures
1893 if(strstr(errormsg
, strerror(ENOSPC
)) != NULL
) {
1894 std::clog
<< _("No apport report written because the error message indicates a disk full error") << std::endl
;
1898 // do not report out-of-memory failures
1899 if(strstr(errormsg
, strerror(ENOMEM
)) != NULL
||
1900 strstr(errormsg
, "failed to allocate memory") != NULL
) {
1901 std::clog
<< _("No apport report written because the error message indicates a out of memory error") << std::endl
;
1905 // do not report bugs regarding inaccessible local files
1906 if(strstr(errormsg
, strerror(ENOENT
)) != NULL
||
1907 strstr(errormsg
, "cannot access archive") != NULL
) {
1908 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1912 // do not report errors encountered when decompressing packages
1913 if(strstr(errormsg
, "--fsys-tarfile returned error exit status 2") != NULL
) {
1914 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1918 // do not report dpkg I/O errors, this is a format string, so we compare
1919 // the prefix and the suffix of the error with the dpkg error message
1920 vector
<string
> io_errors
;
1921 io_errors
.push_back(string("failed to read"));
1922 io_errors
.push_back(string("failed to write"));
1923 io_errors
.push_back(string("failed to seek"));
1924 io_errors
.push_back(string("unexpected end of file or stream"));
1926 for (vector
<string
>::iterator I
= io_errors
.begin(); I
!= io_errors
.end(); ++I
)
1928 vector
<string
> list
= VectorizeString(dgettext("dpkg", (*I
).c_str()), '%');
1929 if (list
.size() > 1) {
1930 // we need to split %s, VectorizeString only allows char so we need
1931 // to kill the "s" manually
1932 if (list
[1].size() > 1) {
1933 list
[1].erase(0, 1);
1934 if(strstr(errormsg
, list
[0].c_str()) &&
1935 strstr(errormsg
, list
[1].c_str())) {
1936 std::clog
<< _("No apport report written because the error message indicates a dpkg I/O error") << std::endl
;
1943 // get the pkgname and reportfile
1944 pkgname
= flNotDir(pkgpath
);
1945 pos
= pkgname
.find('_');
1946 if(pos
!= string::npos
)
1947 pkgname
= pkgname
.substr(0, pos
);
1949 // find the package version and source package name
1950 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
1951 if (Pkg
.end() == true)
1953 if (pos
== std::string::npos
|| _config
->FindB("dpkg::install::recursive::numbered", true) == false)
1955 auto const dash
= pkgname
.find_first_not_of("0123456789");
1956 if (dash
== std::string::npos
|| pkgname
[dash
] != '-')
1958 pkgname
.erase(0, dash
+ 1);
1959 Pkg
= Cache
.FindPkg(pkgname
);
1960 if (Pkg
.end() == true)
1963 pkgCache::VerIterator Ver
= Cache
.GetCandidateVersion(Pkg
);
1964 if (Ver
.end() == true)
1966 pkgver
= Ver
.VerStr() == NULL
? "unknown" : Ver
.VerStr();
1968 // if the file exists already, we check:
1969 // - if it was reported already (touched by apport).
1970 // If not, we do nothing, otherwise
1971 // we overwrite it. This is the same behaviour as apport
1972 // - if we have a report with the same pkgversion already
1974 _config
->CndSet("Dir::Apport", "var/crash");
1975 reportfile
= flCombine(_config
->FindDir("Dir::Apport", "var/crash"), pkgname
+".0.crash");
1976 if(FileExists(reportfile
))
1981 // check atime/mtime
1982 stat(reportfile
.c_str(), &buf
);
1983 if(buf
.st_mtime
> buf
.st_atime
)
1986 // check if the existing report is the same version
1987 report
= fopen(reportfile
.c_str(),"r");
1988 while(fgets(strbuf
, sizeof(strbuf
), report
) != NULL
)
1990 if(strstr(strbuf
,"Package:") == strbuf
)
1992 char pkgname
[255], version
[255];
1993 if(sscanf(strbuf
, "Package: %254s %254s", pkgname
, version
) == 2)
1994 if(strcmp(pkgver
.c_str(), version
) == 0)
2004 // now write the report
2005 arch
= _config
->Find("APT::Architecture");
2006 report
= fopen(reportfile
.c_str(),"w");
2009 if(_config
->FindB("DPkgPM::InitialReportOnly",false) == true)
2010 chmod(reportfile
.c_str(), 0);
2012 chmod(reportfile
.c_str(), 0600);
2013 fprintf(report
, "ProblemType: Package\n");
2014 fprintf(report
, "Architecture: %s\n", arch
.c_str());
2015 time_t now
= time(NULL
);
2016 char ctime_buf
[26]; // need at least 26 bytes according to ctime(3)
2017 fprintf(report
, "Date: %s" , ctime_r(&now
, ctime_buf
));
2018 fprintf(report
, "Package: %s %s\n", pkgname
.c_str(), pkgver
.c_str());
2019 fprintf(report
, "SourcePackage: %s\n", Ver
.SourcePkgName());
2020 fprintf(report
, "ErrorMessage:\n %s\n", errormsg
);
2022 // ensure that the log is flushed
2024 fflush(d
->term_out
);
2026 // attach terminal log it if we have it
2027 string logfile_name
= _config
->FindFile("Dir::Log::Terminal");
2028 if (!logfile_name
.empty())
2032 fprintf(report
, "DpkgTerminalLog:\n");
2033 log
= fopen(logfile_name
.c_str(),"r");
2037 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
2038 fprintf(report
, " %s", buf
);
2039 fprintf(report
, " \n");
2044 // attach history log it if we have it
2045 string histfile_name
= _config
->FindFile("Dir::Log::History");
2046 if (!histfile_name
.empty())
2048 fprintf(report
, "DpkgHistoryLog:\n");
2049 FILE* log
= fopen(histfile_name
.c_str(),"r");
2053 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
2054 fprintf(report
, " %s", buf
);
2059 // log the ordering, see dpkgpm.h and the "Ops" enum there
2060 fprintf(report
, "AptOrdering:\n");
2061 for (auto && I
: List
)
2063 char const * opstr
= nullptr;
2066 case Item::Install
: opstr
= "Install"; break;
2067 case Item::Configure
: opstr
= "Configure"; break;
2068 case Item::Remove
: opstr
= "Remove"; break;
2069 case Item::Purge
: opstr
= "Purge"; break;
2070 case Item::ConfigurePending
: opstr
= "ConfigurePending"; break;
2071 case Item::TriggersPending
: opstr
= "TriggersPending"; break;
2072 case Item::RemovePending
: opstr
= "RemovePending"; break;
2073 case Item::PurgePending
: opstr
= "PurgePending"; break;
2075 auto const pkgname
= I
.Pkg
.end() ? "NULL" : I
.Pkg
.FullName();
2076 fprintf(report
, " %s: %s\n", pkgname
.c_str(), opstr
);
2079 // attach dmesg log (to learn about segfaults)
2080 if (FileExists("/bin/dmesg"))
2082 fprintf(report
, "Dmesg:\n");
2083 FILE *log
= popen("/bin/dmesg","r");
2087 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
2088 fprintf(report
, " %s", buf
);
2093 // attach df -l log (to learn about filesystem status)
2094 if (FileExists("/bin/df"))
2097 fprintf(report
, "Df:\n");
2098 FILE *log
= popen("/bin/df -l","r");
2102 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
2103 fprintf(report
, " %s", buf
);