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/cacheiterators.h>
23 #include <apt-pkg/macros.h>
24 #include <apt-pkg/pkgcache.h>
34 #include <sys/ioctl.h>
35 #include <sys/select.h>
58 APT_PURE
static string
AptHistoryRequestingUser() /*{{{*/
60 const char* EnvKeys
[]{"SUDO_UID", "PKEXEC_UID", "PACKAGEKIT_CALLER_UID"};
62 for (const auto &Key
: EnvKeys
)
64 if (getenv(Key
) != nullptr)
66 int uid
= atoi(getenv(Key
));
69 struct passwd
*result
;
71 if (getpwuid_r(uid
, &pwd
, buf
, sizeof(buf
), &result
) == 0 && result
!= NULL
) {
73 strprintf(res
, "%s (%d)", pwd
.pw_name
, uid
);
82 APT_PURE
static unsigned int EnvironmentSize() /*{{{*/
84 unsigned int size
= 0;
85 char **envp
= environ
;
88 size
+= strlen (*envp
++) + 1;
93 class pkgDPkgPMPrivate
/*{{{*/
96 pkgDPkgPMPrivate() : stdin_is_dev_null(false), dpkgbuf_pos(0),
97 term_out(NULL
), history_out(NULL
),
98 progress(NULL
), tt_is_valid(false), master(-1),
99 slave(NULL
), protect_slave_from_dying(-1),
107 bool stdin_is_dev_null
;
108 // the buffer we use for the dpkg status-fd reading
114 APT::Progress::PackageManager
*progress
;
121 int protect_slave_from_dying
;
125 sigset_t original_sigmask
;
132 // Maps the dpkg "processing" info to human readable names. Entry 0
133 // of each array is the key, entry 1 is the value.
134 const std::pair
<const char *, const char *> PackageProcessingOps
[] = {
135 std::make_pair("install", N_("Installing %s")),
136 std::make_pair("configure", N_("Configuring %s")),
137 std::make_pair("remove", N_("Removing %s")),
138 std::make_pair("purge", N_("Completely removing %s")),
139 std::make_pair("disappear", N_("Noting disappearance of %s")),
140 std::make_pair("trigproc", N_("Running post-installation trigger %s"))
143 const std::pair
<const char *, const char *> * const PackageProcessingOpsBegin
= PackageProcessingOps
;
144 const std::pair
<const char *, const char *> * const PackageProcessingOpsEnd
= PackageProcessingOps
+ sizeof(PackageProcessingOps
) / sizeof(PackageProcessingOps
[0]);
146 // Predicate to test whether an entry in the PackageProcessingOps
147 // array matches a string.
148 class MatchProcessingOp
153 explicit MatchProcessingOp(const char *the_target
)
158 bool operator()(const std::pair
<const char *, const char *> &pair
) const
160 return strcmp(pair
.first
, target
) == 0;
165 // ionice - helper function to ionice the given PID /*{{{*/
166 /* there is no C header for ionice yet - just the syscall interface
167 so we use the binary from util-linux */
168 static bool ionice(int PID
)
170 if (!FileExists("/usr/bin/ionice"))
172 pid_t Process
= ExecFork();
176 snprintf(buf
, sizeof(buf
), "-p%d", PID
);
178 Args
[0] = "/usr/bin/ionice";
182 execv(Args
[0], (char **)Args
);
184 return ExecWait(Process
, "ionice");
187 // FindNowVersion - Helper to find a Version in "now" state /*{{{*/
188 // ---------------------------------------------------------------------
189 /* This is helpful when a package is no longer installed but has residual
193 pkgCache::VerIterator
FindNowVersion(const pkgCache::PkgIterator
&Pkg
)
195 pkgCache::VerIterator Ver
;
196 for (Ver
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
)
197 for (pkgCache::VerFileIterator Vf
= Ver
.FileList(); Vf
.end() == false; ++Vf
)
198 for (pkgCache::PkgFileIterator F
= Vf
.File(); F
.end() == false; ++F
)
200 if (F
.Archive() != 0 && strcmp(F
.Archive(), "now") == 0)
207 // DPkgPM::pkgDPkgPM - Constructor /*{{{*/
208 // ---------------------------------------------------------------------
210 pkgDPkgPM::pkgDPkgPM(pkgDepCache
*Cache
)
211 : pkgPackageManager(Cache
),d(new pkgDPkgPMPrivate()), pkgFailures(0), PackagesDone(0), PackagesTotal(0)
215 // DPkgPM::pkgDPkgPM - Destructor /*{{{*/
216 // ---------------------------------------------------------------------
218 pkgDPkgPM::~pkgDPkgPM()
223 // DPkgPM::Install - Install a package /*{{{*/
224 // ---------------------------------------------------------------------
225 /* Add an install operation to the sequence list */
226 bool pkgDPkgPM::Install(PkgIterator Pkg
,string File
)
228 if (File
.empty() == true || Pkg
.end() == true)
229 return _error
->Error("Internal Error, No file name for %s",Pkg
.FullName().c_str());
231 // If the filename string begins with DPkg::Chroot-Directory, return the
232 // substr that is within the chroot so dpkg can access it.
233 string
const chrootdir
= _config
->FindDir("DPkg::Chroot-Directory","/");
234 if (chrootdir
!= "/" && File
.find(chrootdir
) == 0)
236 size_t len
= chrootdir
.length();
237 if (chrootdir
.at(len
- 1) == '/')
239 List
.push_back(Item(Item::Install
,Pkg
,File
.substr(len
)));
242 List
.push_back(Item(Item::Install
,Pkg
,File
));
247 // DPkgPM::Configure - Configure a package /*{{{*/
248 // ---------------------------------------------------------------------
249 /* Add a configure operation to the sequence list */
250 bool pkgDPkgPM::Configure(PkgIterator Pkg
)
252 if (Pkg
.end() == true)
255 List
.push_back(Item(Item::Configure
, Pkg
));
257 // Use triggers for config calls if we configure "smart"
258 // as otherwise Pre-Depends will not be satisfied, see #526774
259 if (_config
->FindB("DPkg::TriggersPending", false) == true)
260 List
.push_back(Item(Item::TriggersPending
, PkgIterator()));
265 // DPkgPM::Remove - Remove a package /*{{{*/
266 // ---------------------------------------------------------------------
267 /* Add a remove operation to the sequence list */
268 bool pkgDPkgPM::Remove(PkgIterator Pkg
,bool Purge
)
270 if (Pkg
.end() == true)
274 List
.push_back(Item(Item::Purge
,Pkg
));
276 List
.push_back(Item(Item::Remove
,Pkg
));
280 // DPkgPM::SendPkgInfo - Send info for install-pkgs hook /*{{{*/
281 // ---------------------------------------------------------------------
282 /* This is part of the helper script communication interface, it sends
283 very complete information down to the other end of the pipe.*/
284 bool pkgDPkgPM::SendV2Pkgs(FILE *F
)
286 return SendPkgsInfo(F
, 2);
288 bool pkgDPkgPM::SendPkgsInfo(FILE * const F
, unsigned int const &Version
)
290 // This version of APT supports only v3, so don't sent higher versions
292 fprintf(F
,"VERSION %u\n", Version
);
294 fprintf(F
,"VERSION 3\n");
296 /* Write out all of the configuration directives by walking the
297 configuration tree */
298 const Configuration::Item
*Top
= _config
->Tree(0);
301 if (Top
->Value
.empty() == false)
304 QuoteString(Top
->FullTag(),"=\"\n").c_str(),
305 QuoteString(Top
->Value
,"\n").c_str());
314 while (Top
!= 0 && Top
->Next
== 0)
321 // Write out the package actions in order.
322 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
324 if(I
->Pkg
.end() == true)
327 pkgDepCache::StateCache
&S
= Cache
[I
->Pkg
];
329 fprintf(F
,"%s ",I
->Pkg
.Name());
331 // Current version which we are going to replace
332 pkgCache::VerIterator CurVer
= I
->Pkg
.CurrentVer();
333 if (CurVer
.end() == true && (I
->Op
== Item::Remove
|| I
->Op
== Item::Purge
))
334 CurVer
= FindNowVersion(I
->Pkg
);
336 if (CurVer
.end() == true)
341 fprintf(F
, "- - none ");
345 fprintf(F
, "%s ", CurVer
.VerStr());
347 fprintf(F
, "%s %s ", CurVer
.Arch(), CurVer
.MultiArchType());
350 // Show the compare operator between current and install version
351 if (S
.InstallVer
!= 0)
353 pkgCache::VerIterator
const InstVer
= S
.InstVerIter(Cache
);
355 if (CurVer
.end() == false)
356 Comp
= InstVer
.CompareVer(CurVer
);
363 fprintf(F
, "%s ", InstVer
.VerStr());
365 fprintf(F
, "%s %s ", InstVer
.Arch(), InstVer
.MultiArchType());
372 fprintf(F
, "> - - none ");
375 // Show the filename/operation
376 if (I
->Op
== Item::Install
)
379 if (I
->File
[0] != '/')
380 fprintf(F
,"**ERROR**\n");
382 fprintf(F
,"%s\n",I
->File
.c_str());
384 else if (I
->Op
== Item::Configure
)
385 fprintf(F
,"**CONFIGURE**\n");
386 else if (I
->Op
== Item::Remove
||
387 I
->Op
== Item::Purge
)
388 fprintf(F
,"**REMOVE**\n");
396 // DPkgPM::RunScriptsWithPkgs - Run scripts with package names on stdin /*{{{*/
397 // ---------------------------------------------------------------------
398 /* This looks for a list of scripts to run from the configuration file
399 each one is run and is fed on standard input a list of all .deb files
400 that are due to be installed. */
401 bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf
)
405 Configuration::Item
const *Opts
= _config
->Tree(Cnf
);
406 if (Opts
== 0 || Opts
->Child
== 0)
410 sighandler_t old_sigpipe
= signal(SIGPIPE
, SIG_IGN
);
412 unsigned int Count
= 1;
413 for (; Opts
!= 0; Opts
= Opts
->Next
, Count
++)
415 if (Opts
->Value
.empty() == true)
418 if(_config
->FindB("Debug::RunScripts", false) == true)
419 std::clog
<< "Running external script with list of all .deb file: '"
420 << Opts
->Value
<< "'" << std::endl
;
422 // Determine the protocol version
423 string OptSec
= Opts
->Value
;
424 string::size_type Pos
;
425 if ((Pos
= OptSec
.find(' ')) == string::npos
|| Pos
== 0)
426 Pos
= OptSec
.length();
427 OptSec
= "DPkg::Tools::Options::" + string(Opts
->Value
.c_str(),Pos
);
429 unsigned int Version
= _config
->FindI(OptSec
+"::Version",1);
430 unsigned int InfoFD
= _config
->FindI(OptSec
+ "::InfoFD", STDIN_FILENO
);
433 std::set
<int> KeepFDs
;
434 MergeKeepFdsFromConfiguration(KeepFDs
);
436 if (pipe(Pipes
) != 0) {
437 result
= _error
->Errno("pipe","Failed to create IPC pipe to subprocess");
440 if (InfoFD
!= (unsigned)Pipes
[0])
441 SetCloseExec(Pipes
[0],true);
443 KeepFDs
.insert(Pipes
[0]);
446 SetCloseExec(Pipes
[1],true);
448 // Purified Fork for running the script
449 pid_t Process
= ExecFork(KeepFDs
);
453 dup2(Pipes
[0], InfoFD
);
454 SetCloseExec(STDOUT_FILENO
,false);
455 SetCloseExec(STDIN_FILENO
,false);
456 SetCloseExec(STDERR_FILENO
,false);
459 strprintf(hookfd
, "%d", InfoFD
);
460 setenv("APT_HOOK_INFO_FD", hookfd
.c_str(), 1);
462 debSystem::DpkgChrootDirectory();
466 Args
[2] = Opts
->Value
.c_str();
468 execv(Args
[0],(char **)Args
);
472 FILE *F
= fdopen(Pipes
[1],"w");
474 result
= _error
->Errno("fdopen","Failed to open new FD");
478 // Feed it the filenames.
481 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
483 // Only deal with packages to be installed from .deb
484 if (I
->Op
!= Item::Install
)
488 if (I
->File
[0] != '/')
491 /* Feed the filename of each package that is pending install
493 fprintf(F
,"%s\n",I
->File
.c_str());
499 SendPkgsInfo(F
, Version
);
503 // Clean up the sub process
504 if (ExecWait(Process
,Opts
->Value
.c_str()) == false) {
505 result
= _error
->Error("Failure running script %s",Opts
->Value
.c_str());
509 signal(SIGPIPE
, old_sigpipe
);
514 // DPkgPM::DoStdin - Read stdin and pass to master pty /*{{{*/
515 // ---------------------------------------------------------------------
518 void pkgDPkgPM::DoStdin(int master
)
520 unsigned char input_buf
[256] = {0,};
521 ssize_t len
= read(STDIN_FILENO
, input_buf
, sizeof(input_buf
));
523 FileFd::Write(master
, input_buf
, len
);
525 d
->stdin_is_dev_null
= true;
528 // DPkgPM::DoTerminalPty - Read the terminal pty and write log /*{{{*/
529 // ---------------------------------------------------------------------
531 * read the terminal pty and write log
533 void pkgDPkgPM::DoTerminalPty(int master
)
535 unsigned char term_buf
[1024] = {0,0, };
537 ssize_t len
=read(master
, term_buf
, sizeof(term_buf
));
538 if(len
== -1 && errno
== EIO
)
540 // this happens when the child is about to exit, we
541 // give it time to actually exit, otherwise we run
542 // into a race so we sleep for half a second.
543 struct timespec sleepfor
= { 0, 500000000 };
544 nanosleep(&sleepfor
, NULL
);
549 FileFd::Write(1, term_buf
, len
);
551 fwrite(term_buf
, len
, sizeof(char), d
->term_out
);
554 // DPkgPM::ProcessDpkgStatusBuf /*{{{*/
555 void pkgDPkgPM::ProcessDpkgStatusLine(char *line
)
557 bool const Debug
= _config
->FindB("Debug::pkgDPkgProgressReporting",false);
559 std::clog
<< "got from dpkg '" << line
<< "'" << std::endl
;
561 /* dpkg sends strings like this:
562 'status: <pkg>: <pkg qstate>'
563 'status: <pkg>:<arch>: <pkg qstate>'
565 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: pkg'
566 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: trigger'
569 // we need to split on ": " (note the appended space) as the ':' is
570 // part of the pkgname:arch information that dpkg sends
572 // A dpkg error message may contain additional ":" (like
573 // "failed in buffer_write(fd) (10, ret=-1): backend dpkg-deb ..."
574 // so we need to ensure to not split too much
575 std::vector
<std::string
> list
= StringSplit(line
, ": ", 4);
579 std::clog
<< "ignoring line: not enough ':'" << std::endl
;
583 // build the (prefix, pkgname, action) tuple, position of this
584 // is different for "processing" or "status" messages
585 std::string prefix
= APT::String::Strip(list
[0]);
589 // "processing" has the form "processing: action: pkg or trigger"
590 // with action = ["install", "upgrade", "configure", "remove", "purge",
591 // "disappear", "trigproc"]
592 if (prefix
== "processing")
594 pkgname
= APT::String::Strip(list
[2]);
595 action
= APT::String::Strip(list
[1]);
596 // we don't care for the difference (as dpkg doesn't really either)
597 if (action
== "upgrade")
600 // "status" has the form: "status: pkg: state"
601 // with state in ["half-installed", "unpacked", "half-configured",
602 // "installed", "config-files", "not-installed"]
603 else if (prefix
== "status")
605 pkgname
= APT::String::Strip(list
[1]);
606 action
= APT::String::Strip(list
[2]);
609 std::clog
<< "unknown prefix '" << prefix
<< "'" << std::endl
;
614 /* handle the special cases first:
616 errors look like this:
617 '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
618 and conffile-prompt like this
619 'status:/etc/compiz.conf/compiz.conf : conffile-prompt: 'current-conffile' 'new-conffile' useredited distedited
621 if (prefix
== "status")
623 if(action
== "error")
625 d
->progress
->Error(pkgname
, PackagesDone
, PackagesTotal
,
628 WriteApportReport(pkgname
.c_str(), list
[3].c_str());
631 else if(action
== "conffile-prompt")
633 d
->progress
->ConffilePrompt(pkgname
, PackagesDone
, PackagesTotal
,
639 // at this point we know that we should have a valid pkgname, so build all
642 // dpkg does not always send "pkgname:arch" so we add it here if needed
643 if (pkgname
.find(":") == std::string::npos
)
645 // find the package in the group that is touched by dpkg
646 // if there are multiple pkgs dpkg would send us a full pkgname:arch
647 pkgCache::GrpIterator Grp
= Cache
.FindGrp(pkgname
);
648 if (Grp
.end() == false)
650 pkgCache::PkgIterator P
= Grp
.PackageList();
651 for (; P
.end() != true; P
= Grp
.NextPkg(P
))
653 if(Cache
[P
].Keep() == false || Cache
[P
].ReInstall() == true)
655 pkgname
= P
.FullName();
662 const char* const pkg
= pkgname
.c_str();
663 std::string short_pkgname
= StringSplit(pkgname
, ":")[0];
664 std::string arch
= "";
665 if (pkgname
.find(":") != string::npos
)
666 arch
= StringSplit(pkgname
, ":")[1];
667 std::string i18n_pkgname
= pkgname
;
668 if (arch
.size() != 0)
669 strprintf(i18n_pkgname
, "%s (%s)", short_pkgname
.c_str(), arch
.c_str());
671 // 'processing' from dpkg looks like
672 // 'processing: action: pkg'
673 if(prefix
== "processing")
675 const std::pair
<const char *, const char *> * const iter
=
676 std::find_if(PackageProcessingOpsBegin
,
677 PackageProcessingOpsEnd
,
678 MatchProcessingOp(action
.c_str()));
679 if(iter
== PackageProcessingOpsEnd
)
682 std::clog
<< "ignoring unknown action: " << action
<< std::endl
;
686 strprintf(msg
, _(iter
->second
), i18n_pkgname
.c_str());
687 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
689 // FIXME: this needs a muliarch testcase
690 // FIXME2: is "pkgname" here reliable with dpkg only sending us
692 if (action
== "disappear")
693 handleDisappearAction(pkgname
);
697 if (prefix
== "status")
699 vector
<struct DpkgState
> const &states
= PackageOps
[pkg
];
700 if(PackageOpsDone
[pkg
] < states
.size())
702 char const * const next_action
= states
[PackageOpsDone
[pkg
]].state
;
703 if (next_action
&& Debug
== true)
704 std::clog
<< "(parsed from dpkg) pkg: " << short_pkgname
705 << " action: " << action
<< " (expected: '" << next_action
<< "' "
706 << PackageOpsDone
[pkg
] << " of " << states
.size() << ")" << endl
;
708 // check if the package moved to the next dpkg state
709 if(next_action
&& (action
== next_action
))
711 // only read the translation if there is actually a next action
712 char const * const translation
= _(states
[PackageOpsDone
[pkg
]].str
);
714 // we moved from one dpkg state to a new one, report that
715 ++PackageOpsDone
[pkg
];
719 strprintf(msg
, translation
, i18n_pkgname
.c_str());
720 d
->progress
->StatusChanged(pkgname
, PackagesDone
, PackagesTotal
, msg
);
726 // DPkgPM::handleDisappearAction /*{{{*/
727 void pkgDPkgPM::handleDisappearAction(string
const &pkgname
)
729 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
730 if (unlikely(Pkg
.end() == true))
733 // record the package name for display and stuff later
734 disappearedPkgs
.insert(Pkg
.FullName(true));
736 // the disappeared package was auto-installed - nothing to do
737 if ((Cache
[Pkg
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
739 pkgCache::VerIterator PkgVer
= Cache
[Pkg
].InstVerIter(Cache
);
740 if (unlikely(PkgVer
.end() == true))
742 /* search in the list of dependencies for (Pre)Depends,
743 check if this dependency has a Replaces on our package
744 and if so transfer the manual installed flag to it */
745 for (pkgCache::DepIterator Dep
= PkgVer
.DependsList(); Dep
.end() != true; ++Dep
)
747 if (Dep
->Type
!= pkgCache::Dep::Depends
&&
748 Dep
->Type
!= pkgCache::Dep::PreDepends
)
750 pkgCache::PkgIterator Tar
= Dep
.TargetPkg();
751 if (unlikely(Tar
.end() == true))
753 // the package is already marked as manual
754 if ((Cache
[Tar
].Flags
& pkgCache::Flag::Auto
) != pkgCache::Flag::Auto
)
756 pkgCache::VerIterator TarVer
= Cache
[Tar
].InstVerIter(Cache
);
757 if (TarVer
.end() == true)
759 for (pkgCache::DepIterator Rep
= TarVer
.DependsList(); Rep
.end() != true; ++Rep
)
761 if (Rep
->Type
!= pkgCache::Dep::Replaces
)
763 if (Pkg
!= Rep
.TargetPkg())
765 // okay, they are strongly connected - transfer manual-bit
767 std::clog
<< "transfer manual-bit from disappeared »" << pkgname
<< "« to »" << Tar
.FullName() << "«" << std::endl
;
768 Cache
[Tar
].Flags
&= ~Flag::Auto
;
774 // DPkgPM::DoDpkgStatusFd /*{{{*/
775 void pkgDPkgPM::DoDpkgStatusFd(int statusfd
)
777 ssize_t
const len
= read(statusfd
, &d
->dpkgbuf
[d
->dpkgbuf_pos
],
778 (sizeof(d
->dpkgbuf
)/sizeof(d
->dpkgbuf
[0])) - d
->dpkgbuf_pos
);
781 d
->dpkgbuf_pos
+= (len
/ sizeof(d
->dpkgbuf
[0]));
783 // process line by line from the buffer
784 char *p
= d
->dpkgbuf
, *q
= nullptr;
785 while((q
=(char*)memchr(p
, '\n', (d
->dpkgbuf
+ d
->dpkgbuf_pos
) - p
)) != nullptr)
788 ProcessDpkgStatusLine(p
);
789 p
= q
+ 1; // continue with next line
792 // check if we stripped the buffer clean
793 if (p
> (d
->dpkgbuf
+ d
->dpkgbuf_pos
))
799 // otherwise move the unprocessed tail to the start and update pos
800 memmove(d
->dpkgbuf
, p
, (p
- d
->dpkgbuf
));
801 d
->dpkgbuf_pos
= (d
->dpkgbuf
+ d
->dpkgbuf_pos
) - p
;
804 // DPkgPM::WriteHistoryTag /*{{{*/
805 void pkgDPkgPM::WriteHistoryTag(string
const &tag
, string value
)
807 size_t const length
= value
.length();
810 // poor mans rstrip(", ")
811 if (value
[length
-2] == ',' && value
[length
-1] == ' ')
812 value
.erase(length
- 2, 2);
813 fprintf(d
->history_out
, "%s: %s\n", tag
.c_str(), value
.c_str());
815 // DPkgPM::OpenLog /*{{{*/
816 bool pkgDPkgPM::OpenLog()
818 string
const logdir
= _config
->FindDir("Dir::Log");
819 if(CreateAPTDirectoryIfNeeded(logdir
, logdir
) == false)
820 // FIXME: use a better string after freeze
821 return _error
->Error(_("Directory '%s' missing"), logdir
.c_str());
825 time_t const t
= time(NULL
);
827 struct tm
const * const tmp
= localtime_r(&t
, &tm_buf
);
828 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
831 string
const logfile_name
= flCombine(logdir
,
832 _config
->Find("Dir::Log::Terminal"));
833 if (!logfile_name
.empty())
835 d
->term_out
= fopen(logfile_name
.c_str(),"a");
836 if (d
->term_out
== NULL
)
837 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), logfile_name
.c_str());
838 setvbuf(d
->term_out
, NULL
, _IONBF
, 0);
839 SetCloseExec(fileno(d
->term_out
), true);
840 if (getuid() == 0) // if we aren't root, we can't chown a file, so don't try it
842 struct passwd
*pw
= getpwnam("root");
843 struct group
*gr
= getgrnam("adm");
844 if (pw
!= NULL
&& gr
!= NULL
&& chown(logfile_name
.c_str(), pw
->pw_uid
, gr
->gr_gid
) != 0)
845 _error
->WarningE("OpenLog", "chown to root:adm of file %s failed", logfile_name
.c_str());
847 if (chmod(logfile_name
.c_str(), 0640) != 0)
848 _error
->WarningE("OpenLog", "chmod 0640 of file %s failed", logfile_name
.c_str());
849 fprintf(d
->term_out
, "\nLog started: %s\n", timestr
);
852 // write your history
853 string
const history_name
= flCombine(logdir
,
854 _config
->Find("Dir::Log::History"));
855 if (!history_name
.empty())
857 d
->history_out
= fopen(history_name
.c_str(),"a");
858 if (d
->history_out
== NULL
)
859 return _error
->WarningE("OpenLog", _("Could not open file '%s'"), history_name
.c_str());
860 SetCloseExec(fileno(d
->history_out
), true);
861 chmod(history_name
.c_str(), 0644);
862 fprintf(d
->history_out
, "\nStart-Date: %s\n", timestr
);
863 string remove
, purge
, install
, reinstall
, upgrade
, downgrade
;
864 for (pkgCache::PkgIterator I
= Cache
.PkgBegin(); I
.end() == false; ++I
)
866 enum { CANDIDATE
, CANDIDATE_AUTO
, CURRENT_CANDIDATE
, CURRENT
} infostring
;
868 #define HISTORYINFO(X, Y) { line = &X; infostring = Y; }
869 if (Cache
[I
].NewInstall() == true)
870 HISTORYINFO(install
, CANDIDATE_AUTO
)
871 else if (Cache
[I
].ReInstall() == true)
872 HISTORYINFO(reinstall
, CANDIDATE
)
873 else if (Cache
[I
].Upgrade() == true)
874 HISTORYINFO(upgrade
, CURRENT_CANDIDATE
)
875 else if (Cache
[I
].Downgrade() == true)
876 HISTORYINFO(downgrade
, CURRENT_CANDIDATE
)
877 else if (Cache
[I
].Delete() == true)
878 HISTORYINFO((Cache
[I
].Purge() ? purge
: remove
), CURRENT
)
882 line
->append(I
.FullName(false)).append(" (");
883 switch (infostring
) {
884 case CANDIDATE
: line
->append(Cache
[I
].CandVersion
); break;
886 line
->append(Cache
[I
].CandVersion
);
887 if ((Cache
[I
].Flags
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
)
888 line
->append(", automatic");
890 case CURRENT_CANDIDATE
: line
->append(Cache
[I
].CurVersion
).append(", ").append(Cache
[I
].CandVersion
); break;
891 case CURRENT
: line
->append(Cache
[I
].CurVersion
); break;
895 if (_config
->Exists("Commandline::AsString") == true)
896 WriteHistoryTag("Commandline", _config
->Find("Commandline::AsString"));
897 std::string RequestingUser
= AptHistoryRequestingUser();
898 if (RequestingUser
!= "")
899 WriteHistoryTag("Requested-By", RequestingUser
);
900 WriteHistoryTag("Install", install
);
901 WriteHistoryTag("Reinstall", reinstall
);
902 WriteHistoryTag("Upgrade", upgrade
);
903 WriteHistoryTag("Downgrade",downgrade
);
904 WriteHistoryTag("Remove",remove
);
905 WriteHistoryTag("Purge",purge
);
906 fflush(d
->history_out
);
912 // DPkg::CloseLog /*{{{*/
913 bool pkgDPkgPM::CloseLog()
916 time_t t
= time(NULL
);
918 struct tm
*tmp
= localtime_r(&t
, &tm_buf
);
919 strftime(timestr
, sizeof(timestr
), "%F %T", tmp
);
923 fprintf(d
->term_out
, "Log ended: ");
924 fprintf(d
->term_out
, "%s", timestr
);
925 fprintf(d
->term_out
, "\n");
932 if (disappearedPkgs
.empty() == false)
935 for (std::set
<std::string
>::const_iterator d
= disappearedPkgs
.begin();
936 d
!= disappearedPkgs
.end(); ++d
)
938 pkgCache::PkgIterator P
= Cache
.FindPkg(*d
);
939 disappear
.append(*d
);
941 disappear
.append(", ");
943 disappear
.append(" (").append(Cache
[P
].CurVersion
).append("), ");
945 WriteHistoryTag("Disappeared", disappear
);
947 if (d
->dpkg_error
.empty() == false)
948 fprintf(d
->history_out
, "Error: %s\n", d
->dpkg_error
.c_str());
949 fprintf(d
->history_out
, "End-Date: %s\n", timestr
);
950 fclose(d
->history_out
);
952 d
->history_out
= NULL
;
958 // DPkgPM::BuildPackagesProgressMap /*{{{*/
959 void pkgDPkgPM::BuildPackagesProgressMap()
961 // map the dpkg states to the operations that are performed
962 // (this is sorted in the same way as Item::Ops)
963 static const struct DpkgState DpkgStatesOpMap
[][7] = {
966 {"half-installed", N_("Preparing %s")},
967 {"unpacked", N_("Unpacking %s") },
970 // Configure operation
972 {"unpacked",N_("Preparing to configure %s") },
973 {"half-configured", N_("Configuring %s") },
974 { "installed", N_("Installed %s")},
979 {"half-configured", N_("Preparing for removal of %s")},
980 {"half-installed", N_("Removing %s")},
981 {"config-files", N_("Removed %s")},
986 {"config-files", N_("Preparing to completely remove %s")},
987 {"not-installed", N_("Completely removed %s")},
992 // init the PackageOps map, go over the list of packages that
993 // that will be [installed|configured|removed|purged] and add
994 // them to the PackageOps map (the dpkg states it goes through)
995 // and the PackageOpsTranslations (human readable strings)
996 for (vector
<Item
>::const_iterator I
= List
.begin(); I
!= List
.end(); ++I
)
998 if((*I
).Pkg
.end() == true)
1001 string
const name
= (*I
).Pkg
.FullName();
1002 PackageOpsDone
[name
] = 0;
1003 for(int i
=0; (DpkgStatesOpMap
[(*I
).Op
][i
]).state
!= NULL
; ++i
)
1005 PackageOps
[name
].push_back(DpkgStatesOpMap
[(*I
).Op
][i
]);
1009 /* one extra: We don't want the progress bar to reach 100%, especially not
1010 if we call dpkg --configure --pending and process a bunch of triggers
1011 while showing 100%. Also, spindown takes a while, so never reaching 100%
1012 is way more correct than reaching 100% while still doing stuff even if
1013 doing it this way is slightly bending the rules */
1017 bool pkgDPkgPM::Go(int StatusFd
) /*{{{*/
1019 APT::Progress::PackageManager
*progress
= NULL
;
1021 progress
= APT::Progress::PackageManagerProgressFactory();
1023 progress
= new APT::Progress::PackageManagerProgressFd(StatusFd
);
1025 return Go(progress
);
1028 void pkgDPkgPM::StartPtyMagic() /*{{{*/
1030 if (_config
->FindB("Dpkg::Use-Pty", true) == false)
1033 if (d
->slave
!= NULL
)
1039 if (isatty(STDIN_FILENO
) == 0)
1040 d
->direct_stdin
= true;
1042 _error
->PushToStack();
1044 d
->master
= posix_openpt(O_RDWR
| O_NOCTTY
);
1045 if (d
->master
== -1)
1046 _error
->Errno("posix_openpt", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1047 else if (unlockpt(d
->master
) == -1)
1048 _error
->Errno("unlockpt", "Unlocking the slave of master fd %d failed!", d
->master
);
1051 #ifdef HAVE_PTS_NAME_R
1052 char slave_name
[64]; // 64 is used by bionic
1053 if (ptsname_r(d
->master
, slave_name
, sizeof(slave_name
)) != 0)
1055 char const * const slave_name
= ptsname(d
->master
);
1056 if (slave_name
== NULL
)
1058 _error
->Errno("ptsname", "Getting name for slave of master fd %d failed!", d
->master
);
1061 d
->slave
= strdup(slave_name
);
1062 if (d
->slave
== NULL
)
1063 _error
->Errno("strdup", "Copying name %s for slave of master fd %d failed!", slave_name
, d
->master
);
1064 else if (grantpt(d
->master
) == -1)
1065 _error
->Errno("grantpt", "Granting access to slave %s based on master fd %d failed!", slave_name
, d
->master
);
1066 else if (tcgetattr(STDIN_FILENO
, &d
->tt
) == 0)
1068 d
->tt_is_valid
= true;
1069 struct termios raw_tt
;
1070 // copy window size of stdout if its a 'good' terminal
1071 if (tcgetattr(STDOUT_FILENO
, &raw_tt
) == 0)
1074 if (ioctl(STDOUT_FILENO
, TIOCGWINSZ
, &win
) < 0)
1075 _error
->Errno("ioctl", "Getting TIOCGWINSZ from stdout failed!");
1076 if (ioctl(d
->master
, TIOCSWINSZ
, &win
) < 0)
1077 _error
->Errno("ioctl", "Setting TIOCSWINSZ for master fd %d failed!", d
->master
);
1079 if (tcsetattr(d
->master
, TCSANOW
, &d
->tt
) == -1)
1080 _error
->Errno("tcsetattr", "Setting in Start via TCSANOW for master fd %d failed!", d
->master
);
1084 raw_tt
.c_lflag
&= ~ECHO
;
1085 raw_tt
.c_lflag
|= ISIG
;
1086 // block SIGTTOU during tcsetattr to prevent a hang if
1087 // the process is a member of the background process group
1088 // http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html
1089 sigemptyset(&d
->sigmask
);
1090 sigaddset(&d
->sigmask
, SIGTTOU
);
1091 sigprocmask(SIG_BLOCK
,&d
->sigmask
, &d
->original_sigmask
);
1092 if (tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &raw_tt
) == -1)
1093 _error
->Errno("tcsetattr", "Setting in Start via TCSAFLUSH for stdin failed!");
1094 sigprocmask(SIG_SETMASK
, &d
->original_sigmask
, NULL
);
1097 if (d
->slave
!= NULL
)
1099 /* on linux, closing (and later reopening) all references to the slave
1100 makes the slave a death end, so we open it here to have one open all
1101 the time. We could use this fd in SetupSlavePtyMagic() for linux, but
1102 on kfreebsd we get an incorrect ("step like") output then while it has
1103 no problem with closing all references… so to avoid platform specific
1104 code here we combine both and be happy once more */
1105 d
->protect_slave_from_dying
= open(d
->slave
, O_RDWR
| O_CLOEXEC
| O_NOCTTY
);
1110 if (_error
->PendingError() == true)
1112 if (d
->master
!= -1)
1117 if (d
->slave
!= NULL
)
1122 _error
->DumpErrors(std::cerr
, GlobalError::DEBUG
, false);
1124 _error
->RevertToStack();
1127 void pkgDPkgPM::SetupSlavePtyMagic() /*{{{*/
1129 if(d
->master
== -1 || d
->slave
== NULL
)
1132 if (close(d
->master
) == -1)
1133 _error
->FatalE("close", "Closing master %d in child failed!", d
->master
);
1136 _error
->FatalE("setsid", "Starting a new session for child failed!");
1138 int const slaveFd
= open(d
->slave
, O_RDWR
| O_NOCTTY
);
1140 _error
->FatalE("open", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
1141 else if (ioctl(slaveFd
, TIOCSCTTY
, 0) < 0)
1142 _error
->FatalE("ioctl", "Setting TIOCSCTTY for slave fd %d failed!", slaveFd
);
1145 unsigned short i
= 0;
1146 if (d
->direct_stdin
== true)
1149 if (dup2(slaveFd
, i
) == -1)
1150 _error
->FatalE("dup2", "Dupping %d to %d in child failed!", slaveFd
, i
);
1152 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSANOW
, &d
->tt
) < 0)
1153 _error
->FatalE("tcsetattr", "Setting in Setup via TCSANOW for slave fd %d failed!", slaveFd
);
1160 void pkgDPkgPM::StopPtyMagic() /*{{{*/
1162 if (d
->slave
!= NULL
)
1165 if (d
->protect_slave_from_dying
!= -1)
1167 close(d
->protect_slave_from_dying
);
1168 d
->protect_slave_from_dying
= -1;
1172 if (d
->tt_is_valid
== true && tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &d
->tt
) == -1)
1173 _error
->FatalE("tcsetattr", "Setting in Stop via TCSAFLUSH for stdin failed!");
1179 // DPkgPM::Go - Run the sequence /*{{{*/
1180 // ---------------------------------------------------------------------
1181 /* This globs the operations and calls dpkg
1183 * If it is called with a progress object apt will report the install
1184 * progress to this object. It maps the dpkg states a package goes
1185 * through to human readable (and i10n-able)
1186 * names and calculates a percentage for each step.
1188 bool pkgDPkgPM::Go(APT::Progress::PackageManager
*progress
)
1190 pkgPackageManager::SigINTStop
= false;
1191 d
->progress
= progress
;
1193 // Generate the base argument list for dpkg
1194 std::vector
<std::string
> const sArgs
= debSystem::GetDpkgBaseCommand();
1195 std::vector
<const char *> Args(sArgs
.size(), NULL
);
1196 std::transform(sArgs
.begin(), sArgs
.end(), Args
.begin(),
1197 [](std::string
const &s
) { return s
.c_str(); });
1198 unsigned long long const StartSize
= std::accumulate(sArgs
.begin(), sArgs
.end(), 0llu,
1199 [](unsigned long long const i
, std::string
const &s
) { return i
+ s
.length(); });
1200 size_t const BaseArgs
= Args
.size();
1205 // try to figure out the max environment size
1206 int OSArgMax
= sysconf(_SC_ARG_MAX
);
1209 OSArgMax
-= EnvironmentSize() - 2*1024;
1210 unsigned int const MaxArgBytes
= _config
->FindI("Dpkg::MaxArgBytes", OSArgMax
);
1211 bool const NoTriggers
= _config
->FindB("DPkg::NoTriggers", false);
1213 if (RunScripts("DPkg::Pre-Invoke") == false)
1216 if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false)
1219 auto const noopDPkgInvocation
= _config
->FindB("Debug::pkgDPkgPM",false);
1220 // store auto-bits as they are supposed to be after dpkg is run
1221 if (noopDPkgInvocation
== false)
1222 Cache
.writeStateFile(NULL
);
1224 decltype(List
)::const_iterator::difference_type
const notconfidx
=
1225 _config
->FindB("Dpkg::ExplicitLastConfigure", false) ? std::numeric_limits
<decltype(notconfidx
)>::max() :
1226 std::distance(List
.cbegin(), std::find_if_not(List
.crbegin(), List
.crend(), [](Item
const &i
) { return i
.Op
== Item::Configure
; }).base());
1228 // support subpressing of triggers processing for special
1229 // cases like d-i that runs the triggers handling manually
1230 bool const TriggersPending
= _config
->FindB("DPkg::TriggersPending", false);
1231 bool const ConfigurePending
= _config
->FindB("DPkg::ConfigurePending", true);
1232 if (ConfigurePending
)
1233 List
.push_back(Item(Item::ConfigurePending
, PkgIterator()));
1236 BuildPackagesProgressMap();
1238 if (notconfidx
!= std::numeric_limits
<decltype(notconfidx
)>::max())
1240 if (ConfigurePending
)
1241 List
.erase(std::next(List
.begin(), notconfidx
), std::prev(List
.end()));
1243 List
.erase(std::next(List
.begin(), notconfidx
), List
.end());
1246 d
->stdin_is_dev_null
= false;
1251 bool dpkgMultiArch
= debSystem::SupportsMultiArch();
1253 // start pty magic before the loop
1256 // Tell the progress that its starting and fork dpkg
1257 d
->progress
->Start(d
->master
);
1259 // this loop is runs once per dpkg operation
1260 vector
<Item
>::const_iterator I
= List
.begin();
1261 while (I
!= List
.end())
1263 // Do all actions with the same Op in one run
1264 vector
<Item
>::const_iterator J
= I
;
1265 if (TriggersPending
== true)
1266 for (; J
!= List
.end(); ++J
)
1270 if (J
->Op
!= Item::TriggersPending
)
1272 vector
<Item
>::const_iterator T
= J
+ 1;
1273 if (T
!= List
.end() && T
->Op
== I
->Op
)
1278 for (; J
!= List
.end() && J
->Op
== I
->Op
; ++J
)
1281 auto const size
= (J
- I
) + 10;
1283 // start with the baseset of arguments
1284 auto Size
= StartSize
;
1285 Args
.erase(Args
.begin() + BaseArgs
, Args
.end());
1287 // keep track of allocated strings for multiarch package names
1288 std::vector
<char *> Packages(size
, nullptr);
1292 return _error
->Errno("pipe","Failed to create IPC pipe to dpkg");
1294 #define ADDARG(X) Args.push_back(X); Size += strlen(X)
1295 #define ADDARGC(X) Args.push_back(X); Size += sizeof(X) - 1
1297 ADDARGC("--status-fd");
1298 char status_fd_buf
[20];
1299 snprintf(status_fd_buf
,sizeof(status_fd_buf
),"%i", fd
[1]);
1300 ADDARG(status_fd_buf
);
1301 unsigned long const Op
= I
->Op
;
1306 ADDARGC("--force-depends");
1307 ADDARGC("--force-remove-essential");
1308 ADDARGC("--remove");
1312 ADDARGC("--force-depends");
1313 ADDARGC("--force-remove-essential");
1317 case Item::Configure
:
1318 ADDARGC("--configure");
1321 case Item::ConfigurePending
:
1322 ADDARGC("--configure");
1323 ADDARGC("--pending");
1326 case Item::TriggersPending
:
1327 ADDARGC("--triggers-only");
1328 ADDARGC("--pending");
1332 ADDARGC("--unpack");
1333 ADDARGC("--auto-deconfigure");
1337 if (NoTriggers
== true && I
->Op
!= Item::TriggersPending
&&
1338 I
->Op
!= Item::ConfigurePending
)
1340 ADDARGC("--no-triggers");
1344 // Write in the file or package names
1345 if (I
->Op
== Item::Install
)
1347 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1349 if (I
->File
[0] != '/')
1350 return _error
->Error("Internal Error, Pathname to install is not absolute '%s'",I
->File
.c_str());
1351 Args
.push_back(I
->File
.c_str());
1352 Size
+= I
->File
.length();
1357 string
const nativeArch
= _config
->Find("APT::Architecture");
1358 unsigned long const oldSize
= I
->Op
== Item::Configure
? Size
: 0;
1359 for (;I
!= J
&& Size
< MaxArgBytes
; ++I
)
1361 if((*I
).Pkg
.end() == true)
1363 if (I
->Op
== Item::Configure
&& disappearedPkgs
.find(I
->Pkg
.FullName(true)) != disappearedPkgs
.end())
1365 // We keep this here to allow "smooth" transitions from e.g. multiarch dpkg/ubuntu to dpkg/debian
1366 if (dpkgMultiArch
== false && (I
->Pkg
.Arch() == nativeArch
||
1367 strcmp(I
->Pkg
.Arch(), "all") == 0 ||
1368 strcmp(I
->Pkg
.Arch(), "none") == 0))
1370 char const * const name
= I
->Pkg
.Name();
1375 pkgCache::VerIterator PkgVer
;
1376 std::string name
= I
->Pkg
.Name();
1377 if (Op
== Item::Remove
|| Op
== Item::Purge
)
1379 PkgVer
= I
->Pkg
.CurrentVer();
1380 if(PkgVer
.end() == true)
1381 PkgVer
= FindNowVersion(I
->Pkg
);
1384 PkgVer
= Cache
[I
->Pkg
].InstVerIter(Cache
);
1385 if (strcmp(I
->Pkg
.Arch(), "none") == 0)
1386 ; // never arch-qualify a package without an arch
1387 else if (PkgVer
.end() == false)
1388 name
.append(":").append(PkgVer
.Arch());
1390 _error
->Warning("Can not find PkgVer for '%s'", name
.c_str());
1391 char * const fullname
= strdup(name
.c_str());
1392 Packages
.push_back(fullname
);
1396 // skip configure action if all sheduled packages disappeared
1397 if (oldSize
== Size
)
1404 if (noopDPkgInvocation
== true)
1406 for (std::vector
<const char *>::const_iterator a
= Args
.begin();
1407 a
!= Args
.end(); ++a
)
1410 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1411 p
!= Packages
.end(); ++p
)
1418 Args
.push_back(NULL
);
1424 /* Mask off sig int/quit. We do this because dpkg also does when
1425 it forks scripts. What happens is that when you hit ctrl-c it sends
1426 it to all processes in the group. Since dpkg ignores the signal
1427 it doesn't die but we do! So we must also ignore it */
1428 sighandler_t old_SIGQUIT
= signal(SIGQUIT
,SIG_IGN
);
1429 sighandler_t old_SIGINT
= signal(SIGINT
,SigINT
);
1431 // Check here for any SIGINT
1432 if (pkgPackageManager::SigINTStop
&& (Op
== Item::Remove
|| Op
== Item::Purge
|| Op
== Item::Install
))
1435 // ignore SIGHUP as well (debian #463030)
1436 sighandler_t old_SIGHUP
= signal(SIGHUP
,SIG_IGN
);
1439 d
->progress
->StartDpkg();
1440 std::set
<int> KeepFDs
;
1441 KeepFDs
.insert(fd
[1]);
1442 MergeKeepFdsFromConfiguration(KeepFDs
);
1443 pid_t Child
= ExecFork(KeepFDs
);
1446 // This is the child
1447 SetupSlavePtyMagic();
1448 close(fd
[0]); // close the read end of the pipe
1450 debSystem::DpkgChrootDirectory();
1452 if (chdir(_config
->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
1455 if (_config
->FindB("DPkg::FlushSTDIN",true) == true && isatty(STDIN_FILENO
))
1459 if ((Flags
= fcntl(STDIN_FILENO
,F_GETFL
,dummy
)) < 0)
1462 // Discard everything in stdin before forking dpkg
1463 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
| O_NONBLOCK
) < 0)
1466 while (read(STDIN_FILENO
,&dummy
,1) == 1);
1468 if (fcntl(STDIN_FILENO
,F_SETFL
,Flags
& (~(long)O_NONBLOCK
)) < 0)
1472 // if color support isn't enabled/disabled explicitly tell
1473 // dpkg to use the same state apt is using for its color support
1474 if (_config
->FindB("APT::Color", false) == true)
1475 setenv("DPKG_COLORS", "always", 0);
1477 setenv("DPKG_COLORS", "never", 0);
1479 execvp(Args
[0], (char**) &Args
[0]);
1480 cerr
<< "Could not exec dpkg!" << endl
;
1484 // we read from dpkg here
1485 int const _dpkgin
= fd
[0];
1486 close(fd
[1]); // close the write end of the pipe
1489 if (_config
->FindB("DPkg::UseIoNice", false) == true)
1493 sigemptyset(&d
->sigmask
);
1494 sigprocmask(SIG_BLOCK
,&d
->sigmask
,&d
->original_sigmask
);
1496 /* free vectors (and therefore memory) as we don't need the included data anymore */
1497 for (std::vector
<char *>::const_iterator p
= Packages
.begin();
1498 p
!= Packages
.end(); ++p
)
1502 // the result of the waitpid call
1505 bool waitpid_failure
= false;
1506 while ((res
=waitpid(Child
,&Status
, WNOHANG
)) != Child
) {
1508 // error handling, waitpid returned -1
1511 waitpid_failure
= true;
1515 // wait for input or output here
1517 if (d
->master
>= 0 && d
->direct_stdin
== false && d
->stdin_is_dev_null
== false)
1518 FD_SET(STDIN_FILENO
, &rfds
);
1519 FD_SET(_dpkgin
, &rfds
);
1521 FD_SET(d
->master
, &rfds
);
1523 tv
.tv_nsec
= d
->progress
->GetPulseInterval();
1524 auto const select_ret
= pselect(max(d
->master
, _dpkgin
)+1, &rfds
, NULL
, NULL
,
1525 &tv
, &d
->original_sigmask
);
1526 d
->progress
->Pulse();
1527 if (select_ret
== 0)
1529 else if (select_ret
< 0 && errno
== EINTR
)
1531 else if (select_ret
< 0)
1533 perror("select() returned error");
1537 if(d
->master
>= 0 && FD_ISSET(d
->master
, &rfds
))
1538 DoTerminalPty(d
->master
);
1539 if(d
->master
>= 0 && FD_ISSET(0, &rfds
))
1541 if(FD_ISSET(_dpkgin
, &rfds
))
1542 DoDpkgStatusFd(_dpkgin
);
1546 // Restore sig int/quit
1547 signal(SIGQUIT
,old_SIGQUIT
);
1548 signal(SIGINT
,old_SIGINT
);
1549 signal(SIGHUP
,old_SIGHUP
);
1551 if (waitpid_failure
== true)
1553 strprintf(d
->dpkg_error
, "Sub-process %s couldn't be waited for.",Args
[0]);
1554 _error
->Error("%s", d
->dpkg_error
.c_str());
1558 // Check for an error code.
1559 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
1561 // if it was set to "keep-dpkg-running" then we won't return
1562 // here but keep the loop going and just report it as a error
1564 bool const stopOnError
= _config
->FindB("Dpkg::StopOnError",true);
1566 if (WIFSIGNALED(Status
) != 0 && WTERMSIG(Status
) == SIGSEGV
)
1567 strprintf(d
->dpkg_error
, "Sub-process %s received a segmentation fault.",Args
[0]);
1568 else if (WIFEXITED(Status
) != 0)
1569 strprintf(d
->dpkg_error
, "Sub-process %s returned an error code (%u)",Args
[0],WEXITSTATUS(Status
));
1571 strprintf(d
->dpkg_error
, "Sub-process %s exited unexpectedly",Args
[0]);
1572 _error
->Error("%s", d
->dpkg_error
.c_str());
1578 // dpkg is done at this point
1582 if (pkgPackageManager::SigINTStop
)
1583 _error
->Warning(_("Operation was interrupted before it could finish"));
1585 if (noopDPkgInvocation
== false)
1587 std::string
const oldpkgcache
= _config
->FindFile("Dir::cache::pkgcache");
1588 if (oldpkgcache
.empty() == false && RealFileExists(oldpkgcache
) == true &&
1589 RemoveFile("pkgDPkgPM::Go", oldpkgcache
))
1591 std::string
const srcpkgcache
= _config
->FindFile("Dir::cache::srcpkgcache");
1592 if (srcpkgcache
.empty() == false && RealFileExists(srcpkgcache
) == true)
1594 _error
->PushToStack();
1595 pkgCacheFile CacheFile
;
1596 CacheFile
.BuildCaches(NULL
, true);
1597 _error
->RevertToStack();
1602 // disappearing packages can forward their auto-bit
1603 if (disappearedPkgs
.empty() == false)
1604 Cache
.writeStateFile(NULL
);
1606 d
->progress
->Stop();
1608 if (RunScripts("DPkg::Post-Invoke") == false)
1611 return d
->dpkg_error
.empty();
1614 void SigINT(int /*sig*/) {
1615 pkgPackageManager::SigINTStop
= true;
1618 // pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/
1619 // ---------------------------------------------------------------------
1621 void pkgDPkgPM::Reset()
1623 List
.erase(List
.begin(),List
.end());
1626 // pkgDpkgPM::WriteApportReport - write out error report pkg failure /*{{{*/
1627 // ---------------------------------------------------------------------
1629 void pkgDPkgPM::WriteApportReport(const char *pkgpath
, const char *errormsg
)
1631 // If apport doesn't exist or isn't installed do nothing
1632 // This e.g. prevents messages in 'universes' without apport
1633 pkgCache::PkgIterator apportPkg
= Cache
.FindPkg("apport");
1634 if (apportPkg
.end() == true || apportPkg
->CurrentVer
== 0)
1637 string pkgname
, reportfile
, pkgver
, arch
;
1638 string::size_type pos
;
1641 if (_config
->FindB("Dpkg::ApportFailureReport", true) == false)
1643 std::clog
<< "configured to not write apport reports" << std::endl
;
1647 // only report the first errors
1648 if(pkgFailures
> _config
->FindI("APT::Apport::MaxReports", 3))
1650 std::clog
<< _("No apport report written because MaxReports is reached already") << std::endl
;
1654 // check if its not a follow up error
1655 const char *needle
= dgettext("dpkg", "dependency problems - leaving unconfigured");
1656 if(strstr(errormsg
, needle
) != NULL
) {
1657 std::clog
<< _("No apport report written because the error message indicates its a followup error from a previous failure.") << std::endl
;
1661 // do not report disk-full failures
1662 if(strstr(errormsg
, strerror(ENOSPC
)) != NULL
) {
1663 std::clog
<< _("No apport report written because the error message indicates a disk full error") << std::endl
;
1667 // do not report out-of-memory failures
1668 if(strstr(errormsg
, strerror(ENOMEM
)) != NULL
||
1669 strstr(errormsg
, "failed to allocate memory") != NULL
) {
1670 std::clog
<< _("No apport report written because the error message indicates a out of memory error") << std::endl
;
1674 // do not report bugs regarding inaccessible local files
1675 if(strstr(errormsg
, strerror(ENOENT
)) != NULL
||
1676 strstr(errormsg
, "cannot access archive") != NULL
) {
1677 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1681 // do not report errors encountered when decompressing packages
1682 if(strstr(errormsg
, "--fsys-tarfile returned error exit status 2") != NULL
) {
1683 std::clog
<< _("No apport report written because the error message indicates an issue on the local system") << std::endl
;
1687 // do not report dpkg I/O errors, this is a format string, so we compare
1688 // the prefix and the suffix of the error with the dpkg error message
1689 vector
<string
> io_errors
;
1690 io_errors
.push_back(string("failed to read"));
1691 io_errors
.push_back(string("failed to write"));
1692 io_errors
.push_back(string("failed to seek"));
1693 io_errors
.push_back(string("unexpected end of file or stream"));
1695 for (vector
<string
>::iterator I
= io_errors
.begin(); I
!= io_errors
.end(); ++I
)
1697 vector
<string
> list
= VectorizeString(dgettext("dpkg", (*I
).c_str()), '%');
1698 if (list
.size() > 1) {
1699 // we need to split %s, VectorizeString only allows char so we need
1700 // to kill the "s" manually
1701 if (list
[1].size() > 1) {
1702 list
[1].erase(0, 1);
1703 if(strstr(errormsg
, list
[0].c_str()) &&
1704 strstr(errormsg
, list
[1].c_str())) {
1705 std::clog
<< _("No apport report written because the error message indicates a dpkg I/O error") << std::endl
;
1712 // get the pkgname and reportfile
1713 pkgname
= flNotDir(pkgpath
);
1714 pos
= pkgname
.find('_');
1715 if(pos
!= string::npos
)
1716 pkgname
= pkgname
.substr(0, pos
);
1718 // find the package version and source package name
1719 pkgCache::PkgIterator Pkg
= Cache
.FindPkg(pkgname
);
1720 if (Pkg
.end() == true)
1722 pkgCache::VerIterator Ver
= Cache
.GetCandidateVersion(Pkg
);
1723 if (Ver
.end() == true)
1725 pkgver
= Ver
.VerStr() == NULL
? "unknown" : Ver
.VerStr();
1727 // if the file exists already, we check:
1728 // - if it was reported already (touched by apport).
1729 // If not, we do nothing, otherwise
1730 // we overwrite it. This is the same behaviour as apport
1731 // - if we have a report with the same pkgversion already
1733 reportfile
= flCombine("/var/crash",pkgname
+".0.crash");
1734 if(FileExists(reportfile
))
1739 // check atime/mtime
1740 stat(reportfile
.c_str(), &buf
);
1741 if(buf
.st_mtime
> buf
.st_atime
)
1744 // check if the existing report is the same version
1745 report
= fopen(reportfile
.c_str(),"r");
1746 while(fgets(strbuf
, sizeof(strbuf
), report
) != NULL
)
1748 if(strstr(strbuf
,"Package:") == strbuf
)
1750 char pkgname
[255], version
[255];
1751 if(sscanf(strbuf
, "Package: %254s %254s", pkgname
, version
) == 2)
1752 if(strcmp(pkgver
.c_str(), version
) == 0)
1762 // now write the report
1763 arch
= _config
->Find("APT::Architecture");
1764 report
= fopen(reportfile
.c_str(),"w");
1767 if(_config
->FindB("DPkgPM::InitialReportOnly",false) == true)
1768 chmod(reportfile
.c_str(), 0);
1770 chmod(reportfile
.c_str(), 0600);
1771 fprintf(report
, "ProblemType: Package\n");
1772 fprintf(report
, "Architecture: %s\n", arch
.c_str());
1773 time_t now
= time(NULL
);
1774 char ctime_buf
[26]; // need at least 26 bytes according to ctime(3)
1775 fprintf(report
, "Date: %s" , ctime_r(&now
, ctime_buf
));
1776 fprintf(report
, "Package: %s %s\n", pkgname
.c_str(), pkgver
.c_str());
1777 fprintf(report
, "SourcePackage: %s\n", Ver
.SourcePkgName());
1778 fprintf(report
, "ErrorMessage:\n %s\n", errormsg
);
1780 // ensure that the log is flushed
1782 fflush(d
->term_out
);
1784 // attach terminal log it if we have it
1785 string logfile_name
= _config
->FindFile("Dir::Log::Terminal");
1786 if (!logfile_name
.empty())
1790 fprintf(report
, "DpkgTerminalLog:\n");
1791 log
= fopen(logfile_name
.c_str(),"r");
1795 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1796 fprintf(report
, " %s", buf
);
1797 fprintf(report
, " \n");
1802 // attach history log it if we have it
1803 string histfile_name
= _config
->FindFile("Dir::Log::History");
1804 if (!histfile_name
.empty())
1806 fprintf(report
, "DpkgHistoryLog:\n");
1807 FILE* log
= fopen(histfile_name
.c_str(),"r");
1811 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1812 fprintf(report
, " %s", buf
);
1817 // log the ordering, see dpkgpm.h and the "Ops" enum there
1818 const char *ops_str
[] = {
1826 fprintf(report
, "AptOrdering:\n");
1827 for (vector
<Item
>::iterator I
= List
.begin(); I
!= List
.end(); ++I
)
1828 if ((*I
).Pkg
!= NULL
)
1829 fprintf(report
, " %s: %s\n", (*I
).Pkg
.Name(), ops_str
[(*I
).Op
]);
1831 fprintf(report
, " %s: %s\n", "NULL", ops_str
[(*I
).Op
]);
1833 // attach dmesg log (to learn about segfaults)
1834 if (FileExists("/bin/dmesg"))
1836 fprintf(report
, "Dmesg:\n");
1837 FILE *log
= popen("/bin/dmesg","r");
1841 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1842 fprintf(report
, " %s", buf
);
1847 // attach df -l log (to learn about filesystem status)
1848 if (FileExists("/bin/df"))
1851 fprintf(report
, "Df:\n");
1852 FILE *log
= popen("/bin/df -l","r");
1856 while( fgets(buf
, sizeof(buf
), log
) != NULL
)
1857 fprintf(report
, " %s", buf
);