1 // -*- mode: cpp; mode: fold -*- 
   3 // $Id: dpkgpm.cc,v 1.28 2004/01/27 02:25:01 mdz Exp $ 
   4 /* ###################################################################### 
   6    DPKG Package Manager - Provide an interface to dpkg 
   8    ##################################################################### */ 
  13 #include <apt-pkg/dpkgpm.h> 
  14 #include <apt-pkg/error.h> 
  15 #include <apt-pkg/configuration.h> 
  16 #include <apt-pkg/depcache.h> 
  17 #include <apt-pkg/pkgrecords.h> 
  18 #include <apt-pkg/strutl.h> 
  19 #include <apt-pkg/fileutl.h> 
  20 #include <apt-pkg/cachefile.h> 
  21 #include <apt-pkg/packagemanager.h> 
  26 #include <sys/select.h> 
  28 #include <sys/types.h> 
  44 #include <sys/ioctl.h> 
  52 class pkgDPkgPMPrivate 
 
  55    pkgDPkgPMPrivate() : stdin_is_dev_null(false), dpkgbuf_pos(0), 
  56                         term_out(NULL
), history_out(NULL
),  
  57                         last_reported_progress(0.0), nr_terminal_rows(0), 
  58                         fancy_progress_output(false) 
  61       if(_config
->FindB("Dpkg::Progress-Fancy", false) == true) 
  63          fancy_progress_output 
= true; 
  64          _config
->Set("DpkgPM::Progress", true); 
  67    bool stdin_is_dev_null
; 
  68    // the buffer we use for the dpkg status-fd reading 
  75    float last_reported_progress
; 
  77    bool fancy_progress_output
; 
  82   // Maps the dpkg "processing" info to human readable names.  Entry 0 
  83   // of each array is the key, entry 1 is the value. 
  84   const std::pair
<const char *, const char *> PackageProcessingOps
[] = { 
  85     std::make_pair("install",   N_("Installing %s")), 
  86     std::make_pair("configure", N_("Configuring %s")), 
  87     std::make_pair("remove",    N_("Removing %s")), 
  88     std::make_pair("purge",    N_("Completely removing %s")), 
  89     std::make_pair("disappear", N_("Noting disappearance of %s")), 
  90     std::make_pair("trigproc",  N_("Running post-installation trigger %s")) 
  93   const std::pair
<const char *, const char *> * const PackageProcessingOpsBegin 
= PackageProcessingOps
; 
  94   const std::pair
<const char *, const char *> * const PackageProcessingOpsEnd   
= PackageProcessingOps 
+ sizeof(PackageProcessingOps
) / sizeof(PackageProcessingOps
[0]); 
  96   // Predicate to test whether an entry in the PackageProcessingOps 
  97   // array matches a string. 
  98   class MatchProcessingOp
 
 103     MatchProcessingOp(const char *the_target
) 
 108     bool operator()(const std::pair
<const char *, const char *> &pair
) const 
 110       return strcmp(pair
.first
, target
) == 0; 
 115 /* helper function to ionice the given PID  
 117  there is no C header for ionice yet - just the syscall interface 
 118  so we use the binary from util-linux 
 123    if (!FileExists("/usr/bin/ionice")) 
 125    pid_t Process 
= ExecFork(); 
 129       snprintf(buf
, sizeof(buf
), "-p%d", PID
); 
 131       Args
[0] = "/usr/bin/ionice"; 
 135       execv(Args
[0], (char **)Args
); 
 137    return ExecWait(Process
, "ionice"); 
 140 // dpkgChrootDirectory - chrooting for dpkg if needed                   /*{{{*/ 
 141 static void dpkgChrootDirectory() 
 143    std::string 
const chrootDir 
= _config
->FindDir("DPkg::Chroot-Directory"); 
 144    if (chrootDir 
== "/") 
 146    std::cerr 
<< "Chrooting into " << chrootDir 
<< std::endl
; 
 147    if (chroot(chrootDir
.c_str()) != 0) 
 155 // FindNowVersion - Helper to find a Version in "now" state     /*{{{*/ 
 156 // --------------------------------------------------------------------- 
 157 /* This is helpful when a package is no longer installed but has residual  
 161 pkgCache::VerIterator 
FindNowVersion(const pkgCache::PkgIterator 
&Pkg
) 
 163    pkgCache::VerIterator Ver
; 
 164    for (Ver 
= Pkg
.VersionList(); Ver
.end() == false; ++Ver
) 
 166       pkgCache::VerFileIterator Vf 
= Ver
.FileList(); 
 167       pkgCache::PkgFileIterator F 
= Vf
.File(); 
 168       for (F 
= Vf
.File(); F
.end() == false; ++F
) 
 170          if (F 
&& F
.Archive()) 
 172             if (strcmp(F
.Archive(), "now"))  
 181 // DPkgPM::pkgDPkgPM - Constructor                                      /*{{{*/ 
 182 // --------------------------------------------------------------------- 
 184 pkgDPkgPM::pkgDPkgPM(pkgDepCache 
*Cache
)  
 185    : pkgPackageManager(Cache
), PackagesDone(0), PackagesTotal(0) 
 187    d 
= new pkgDPkgPMPrivate(); 
 190 // DPkgPM::pkgDPkgPM - Destructor                                       /*{{{*/ 
 191 // --------------------------------------------------------------------- 
 193 pkgDPkgPM::~pkgDPkgPM() 
 198 // DPkgPM::Install - Install a package                                  /*{{{*/ 
 199 // --------------------------------------------------------------------- 
 200 /* Add an install operation to the sequence list */ 
 201 bool pkgDPkgPM::Install(PkgIterator Pkg
,string File
) 
 203    if (File
.empty() == true || Pkg
.end() == true) 
 204       return _error
->Error("Internal Error, No file name for %s",Pkg
.FullName().c_str()); 
 206    // If the filename string begins with DPkg::Chroot-Directory, return the 
 207    // substr that is within the chroot so dpkg can access it. 
 208    string 
const chrootdir 
= _config
->FindDir("DPkg::Chroot-Directory","/"); 
 209    if (chrootdir 
!= "/" && File
.find(chrootdir
) == 0) 
 211       size_t len 
= chrootdir
.length(); 
 212       if (chrootdir
.at(len 
- 1) == '/') 
 214       List
.push_back(Item(Item::Install
,Pkg
,File
.substr(len
))); 
 217       List
.push_back(Item(Item::Install
,Pkg
,File
)); 
 222 // DPkgPM::Configure - Configure a package                              /*{{{*/ 
 223 // --------------------------------------------------------------------- 
 224 /* Add a configure operation to the sequence list */ 
 225 bool pkgDPkgPM::Configure(PkgIterator Pkg
) 
 227    if (Pkg
.end() == true) 
 230    List
.push_back(Item(Item::Configure
, Pkg
)); 
 232    // Use triggers for config calls if we configure "smart" 
 233    // as otherwise Pre-Depends will not be satisfied, see #526774 
 234    if (_config
->FindB("DPkg::TriggersPending", false) == true) 
 235       List
.push_back(Item(Item::TriggersPending
, PkgIterator())); 
 240 // DPkgPM::Remove - Remove a package                                    /*{{{*/ 
 241 // --------------------------------------------------------------------- 
 242 /* Add a remove operation to the sequence list */ 
 243 bool pkgDPkgPM::Remove(PkgIterator Pkg
,bool Purge
) 
 245    if (Pkg
.end() == true) 
 249       List
.push_back(Item(Item::Purge
,Pkg
)); 
 251       List
.push_back(Item(Item::Remove
,Pkg
)); 
 255 // DPkgPM::SendPkgInfo - Send info for install-pkgs hook                /*{{{*/ 
 256 // --------------------------------------------------------------------- 
 257 /* This is part of the helper script communication interface, it sends 
 258    very complete information down to the other end of the pipe.*/ 
 259 bool pkgDPkgPM::SendV2Pkgs(FILE *F
) 
 261    return SendPkgsInfo(F
, 2); 
 263 bool pkgDPkgPM::SendPkgsInfo(FILE * const F
, unsigned int const &Version
) 
 265    // This version of APT supports only v3, so don't sent higher versions 
 267       fprintf(F
,"VERSION %u\n", Version
); 
 269       fprintf(F
,"VERSION 3\n"); 
 271    /* Write out all of the configuration directives by walking the 
 272       configuration tree */ 
 273    const Configuration::Item 
*Top 
= _config
->Tree(0); 
 276       if (Top
->Value
.empty() == false) 
 279                  QuoteString(Top
->FullTag(),"=\"\n").c_str(), 
 280                  QuoteString(Top
->Value
,"\n").c_str()); 
 289       while (Top 
!= 0 && Top
->Next 
== 0) 
 296    // Write out the package actions in order. 
 297    for (vector
<Item
>::iterator I 
= List
.begin(); I 
!= List
.end(); ++I
) 
 299       if(I
->Pkg
.end() == true) 
 302       pkgDepCache::StateCache 
&S 
= Cache
[I
->Pkg
]; 
 304       fprintf(F
,"%s ",I
->Pkg
.Name()); 
 306       // Current version which we are going to replace 
 307       pkgCache::VerIterator CurVer 
= I
->Pkg
.CurrentVer(); 
 308       if (CurVer
.end() == true && (I
->Op 
== Item::Remove 
|| I
->Op 
== Item::Purge
)) 
 309          CurVer 
= FindNowVersion(I
->Pkg
); 
 311       if (CurVer
.end() == true) 
 316             fprintf(F
, "- - none "); 
 320          fprintf(F
, "%s ", CurVer
.VerStr()); 
 322             fprintf(F
, "%s %s ", CurVer
.Arch(), CurVer
.MultiArchType()); 
 325       // Show the compare operator between current and install version 
 326       if (S
.InstallVer 
!= 0) 
 328          pkgCache::VerIterator 
const InstVer 
= S
.InstVerIter(Cache
); 
 330          if (CurVer
.end() == false) 
 331             Comp 
= InstVer
.CompareVer(CurVer
); 
 338          fprintf(F
, "%s ", InstVer
.VerStr()); 
 340             fprintf(F
, "%s %s ", InstVer
.Arch(), InstVer
.MultiArchType()); 
 347             fprintf(F
, "> - - none "); 
 350       // Show the filename/operation 
 351       if (I
->Op 
== Item::Install
) 
 354          if (I
->File
[0] != '/') 
 355             fprintf(F
,"**ERROR**\n"); 
 357             fprintf(F
,"%s\n",I
->File
.c_str()); 
 359       else if (I
->Op 
== Item::Configure
) 
 360          fprintf(F
,"**CONFIGURE**\n"); 
 361       else if (I
->Op 
== Item::Remove 
|| 
 362           I
->Op 
== Item::Purge
) 
 363          fprintf(F
,"**REMOVE**\n"); 
 371 // DPkgPM::RunScriptsWithPkgs - Run scripts with package names on stdin /*{{{*/ 
 372 // --------------------------------------------------------------------- 
 373 /* This looks for a list of scripts to run from the configuration file 
 374    each one is run and is fed on standard input a list of all .deb files 
 375    that are due to be installed. */ 
 376 bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf
) 
 378    Configuration::Item 
const *Opts 
= _config
->Tree(Cnf
); 
 379    if (Opts 
== 0 || Opts
->Child 
== 0) 
 383    unsigned int Count 
= 1; 
 384    for (; Opts 
!= 0; Opts 
= Opts
->Next
, Count
++) 
 386       if (Opts
->Value
.empty() == true) 
 389       // Determine the protocol version 
 390       string OptSec 
= Opts
->Value
; 
 391       string::size_type Pos
; 
 392       if ((Pos 
= OptSec
.find(' ')) == string::npos 
|| Pos 
== 0) 
 393          Pos 
= OptSec
.length(); 
 394       OptSec 
= "DPkg::Tools::Options::" + string(Opts
->Value
.c_str(),Pos
); 
 396       unsigned int Version 
= _config
->FindI(OptSec
+"::Version",1); 
 397       unsigned int InfoFD 
= _config
->FindI(OptSec 
+ "::InfoFD", STDIN_FILENO
); 
 401       if (pipe(Pipes
) != 0) 
 402          return _error
->Errno("pipe","Failed to create IPC pipe to subprocess"); 
 403       if (InfoFD 
!= (unsigned)Pipes
[0]) 
 404          SetCloseExec(Pipes
[0],true); 
 406          _config
->Set("APT::Keep-Fds::", Pipes
[0]); 
 407       SetCloseExec(Pipes
[1],true); 
 409       // Purified Fork for running the script 
 410       pid_t Process 
= ExecFork(); 
 414          dup2(Pipes
[0], InfoFD
); 
 415          SetCloseExec(STDOUT_FILENO
,false); 
 416          SetCloseExec(STDIN_FILENO
,false); 
 417          SetCloseExec(STDERR_FILENO
,false); 
 420          strprintf(hookfd
, "%d", InfoFD
); 
 421          setenv("APT_HOOK_INFO_FD", hookfd
.c_str(), 1); 
 423          dpkgChrootDirectory(); 
 427          Args
[2] = Opts
->Value
.c_str(); 
 429          execv(Args
[0],(char **)Args
); 
 432       if (InfoFD 
== (unsigned)Pipes
[0]) 
 433          _config
->Clear("APT::Keep-Fds", Pipes
[0]); 
 435       FILE *F 
= fdopen(Pipes
[1],"w"); 
 437          return _error
->Errno("fdopen","Faild to open new FD"); 
 439       // Feed it the filenames. 
 442          for (vector
<Item
>::iterator I 
= List
.begin(); I 
!= List
.end(); ++I
) 
 444             // Only deal with packages to be installed from .deb 
 445             if (I
->Op 
!= Item::Install
) 
 449             if (I
->File
[0] != '/') 
 452             /* Feed the filename of each package that is pending install 
 454             fprintf(F
,"%s\n",I
->File
.c_str()); 
 460          SendPkgsInfo(F
, Version
); 
 464       // Clean up the sub process 
 465       if (ExecWait(Process
,Opts
->Value
.c_str()) == false) 
 466          return _error
->Error("Failure running script %s",Opts
->Value
.c_str()); 
 472 // DPkgPM::DoStdin - Read stdin and pass to slave pty                   /*{{{*/ 
 473 // --------------------------------------------------------------------- 
 476 void pkgDPkgPM::DoStdin(int master
) 
 478    unsigned char input_buf
[256] = {0,};  
 479    ssize_t len 
= read(0, input_buf
, sizeof(input_buf
)); 
 481       FileFd::Write(master
, input_buf
, len
); 
 483       d
->stdin_is_dev_null 
= true; 
 486 // DPkgPM::DoTerminalPty - Read the terminal pty and write log          /*{{{*/ 
 487 // --------------------------------------------------------------------- 
 489  * read the terminal pty and write log 
 491 void pkgDPkgPM::DoTerminalPty(int master
) 
 493    unsigned char term_buf
[1024] = {0,0, }; 
 495    ssize_t len
=read(master
, term_buf
, sizeof(term_buf
)); 
 496    if(len 
== -1 && errno 
== EIO
) 
 498       // this happens when the child is about to exit, we 
 499       // give it time to actually exit, otherwise we run 
 500       // into a race so we sleep for half a second. 
 501       struct timespec sleepfor 
= { 0, 500000000 }; 
 502       nanosleep(&sleepfor
, NULL
); 
 507    FileFd::Write(1, term_buf
, len
); 
 509       fwrite(term_buf
, len
, sizeof(char), d
->term_out
); 
 512 // DPkgPM::ProcessDpkgStatusBuf                                         /*{{{*/ 
 513 // --------------------------------------------------------------------- 
 516 void pkgDPkgPM::ProcessDpkgStatusLine(int OutStatusFd
, char *line
) 
 518    bool const Debug 
= _config
->FindB("Debug::pkgDPkgProgressReporting",false); 
 519    // the status we output 
 520    ostringstream status
; 
 523       std::clog 
<< "got from dpkg '" << line 
<< "'" << std::endl
; 
 526    /* dpkg sends strings like this: 
 527       'status:   <pkg>: <pkg  qstate>' 
 528       'status:   <pkg>:<arch>: <pkg  qstate>' 
 529       errors look like this: 
 530       '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  
 531       and conffile-prompt like this 
 532       'status: conffile-prompt: conffile : 'current-conffile' 'new-conffile' useredited distedited 
 534       Newer versions of dpkg sent also: 
 535       'processing: install: pkg' 
 536       'processing: configure: pkg' 
 537       'processing: remove: pkg' 
 538       'processing: purge: pkg' 
 539       'processing: disappear: pkg' 
 540       'processing: trigproc: trigger' 
 544    // we need to split on ": " (note the appended space) as the ':' is 
 545    // part of the pkgname:arch information that dpkg sends 
 547    // A dpkg error message may contain additional ":" (like 
 548    //  "failed in buffer_write(fd) (10, ret=-1): backend dpkg-deb ..." 
 549    // so we need to ensure to not split too much 
 550    std::vector
<std::string
> list 
= StringSplit(line
, ": ", 4); 
 554          std::clog 
<< "ignoring line: not enough ':'" << std::endl
; 
 558    // build the (prefix, pkgname, action) tuple, position of this 
 559    // is different for "processing" or "status" messages 
 560    std::string prefix 
= APT::String::Strip(list
[0]); 
 562    std::string action_str
; 
 563    if (prefix 
== "processing") 
 565       pkgname 
= APT::String::Strip(list
[2]); 
 566       action_str 
= APT::String::Strip(list
[1]); 
 568    else if (prefix 
== "status") 
 570       pkgname 
= APT::String::Strip(list
[1]); 
 571       action_str 
= APT::String::Strip(list
[2]); 
 574          std::clog 
<< "unknown prefix '" << prefix 
<< "'" << std::endl
; 
 578    // FIXME: fix indent once the progress-refactor branch is merged 
 579    if (prefix 
== "status") 
 582    if(action_str 
== "error") 
 584       status 
<< "pmerror:" << list
[1] 
 585              << ":"  << (PackagesDone
/float(PackagesTotal
)*100.0)  
 589          FileFd::Write(OutStatusFd
, status
.str().c_str(), status
.str().size()); 
 591          std::clog 
<< "send: '" << status
.str() << "'" << endl
; 
 593       WriteApportReport(list
[1].c_str(), list
[3].c_str()); 
 596    else if(action_str 
== "conffile") 
 598       status 
<< "pmconffile:" << list
[1] 
 599              << ":"  << (PackagesDone
/float(PackagesTotal
)*100.0)  
 603          FileFd::Write(OutStatusFd
, status
.str().c_str(), status
.str().size()); 
 605          std::clog 
<< "send: '" << status
.str() << "'" << endl
; 
 610    // dpkg does not send always send "pkgname:arch" so we add it here if needed 
 611    if (pkgname
.find(":") == std::string::npos
) 
 613       // find the package in the group that is in a touched by dpkg 
 614       // if there are multiple dpkg will send us a full pkgname:arch 
 615       pkgCache::GrpIterator Grp 
= Cache
.FindGrp(pkgname
); 
 616       if (Grp
.end() == false)  
 618           pkgCache::PkgIterator P 
= Grp
.PackageList(); 
 619           for (; P
.end() != true; P 
= Grp
.NextPkg(P
)) 
 621               if(Cache
[P
].Mode 
!= pkgDepCache::ModeKeep
) 
 623                   pkgname 
= P
.FullName(); 
 629    const char* const pkg 
= pkgname
.c_str(); 
 630    const char* action 
= action_str
.c_str(); 
 631    std::string short_pkgname 
= StringSplit(pkgname
, ":")[0]; 
 632    std::string arch 
= ""; 
 633    if (pkgname
.find(":") != string::npos
) 
 634       arch 
= StringSplit(pkgname
, ":")[1]; 
 635    std::string i18n_pkgname 
= pkgname
; 
 636    if (arch
.size() != 0) 
 637       strprintf(i18n_pkgname
, "%s (%s)", short_pkgname
.c_str(), arch
.c_str()); 
 639    // 'processing' from dpkg looks like 
 640    // 'processing: action: pkg' 
 641    if(prefix 
== "processing") 
 644       const char* const pkg_or_trigger 
= list
[2].c_str(); 
 645       action 
=  list
[1].c_str(); 
 646       const std::pair
<const char *, const char *> * const iter 
= 
 647         std::find_if(PackageProcessingOpsBegin
, 
 648                      PackageProcessingOpsEnd
, 
 649                      MatchProcessingOp(action
)); 
 650       if(iter 
== PackageProcessingOpsEnd
) 
 653             std::clog 
<< "ignoring unknown action: " << action 
<< std::endl
; 
 656       snprintf(s
, sizeof(s
), _(iter
->second
), pkg_or_trigger
); 
 658       status 
<< "pmstatus:" << pkg_or_trigger
 
 659              << ":"  << (PackagesDone
/float(PackagesTotal
)*100.0)  
 663          FileFd::Write(OutStatusFd
, status
.str().c_str(), status
.str().size()); 
 665          std::clog 
<< "send: '" << status
.str() << "'" << endl
; 
 667       if (strncmp(action
, "disappear", strlen("disappear")) == 0) 
 668          handleDisappearAction(pkg_or_trigger
); 
 672    if (prefix 
== "status") 
 675    vector
<struct DpkgState
> const &states 
= PackageOps
[pkg
]; 
 676    const char *next_action 
= NULL
; 
 677    if(PackageOpsDone
[pkg
] < states
.size()) 
 678       next_action 
= states
[PackageOpsDone
[pkg
]].state
; 
 679    // check if the package moved to the next dpkg state 
 680    if(next_action 
&& (strcmp(action
, next_action
) == 0))  
 682       // only read the translation if there is actually a next 
 684       const char *translation 
= _(states
[PackageOpsDone
[pkg
]].str
); 
 686       snprintf(s
, sizeof(s
), translation
, short_pkgname
.c_str()); 
 688       // we moved from one dpkg state to a new one, report that 
 689       PackageOpsDone
[pkg
]++; 
 691       // build the status str 
 692       status 
<< "pmstatus:" << short_pkgname
 
 693              << ":"  << (PackagesDone
/float(PackagesTotal
)*100.0)  
 696       if(_config
->FindB("DPkgPM::Progress", false) == true) 
 697          SendTerminalProgress(PackagesDone
/float(PackagesTotal
)*100.0); 
 700          FileFd::Write(OutStatusFd
, status
.str().c_str(), status
.str().size()); 
 702          std::clog 
<< "send: '" << status
.str() << "'" << endl
; 
 705       std::clog 
<< "(parsed from dpkg) pkg: " << short_pkgname
 
 706                 << " action: " << action 
<< endl
; 
 710 // DPkgPM::handleDisappearAction                                        /*{{{*/ 
 711 void pkgDPkgPM::handleDisappearAction(string 
const &pkgname
) 
 713    // record the package name for display and stuff later 
 714    disappearedPkgs
.insert(pkgname
); 
 716    pkgCache::PkgIterator Pkg 
= Cache
.FindPkg(pkgname
); 
 717    if (unlikely(Pkg
.end() == true)) 
 719    // the disappeared package was auto-installed - nothing to do 
 720    if ((Cache
[Pkg
].Flags 
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
) 
 722    pkgCache::VerIterator PkgVer 
= Cache
[Pkg
].InstVerIter(Cache
); 
 723    if (unlikely(PkgVer
.end() == true)) 
 725    /* search in the list of dependencies for (Pre)Depends, 
 726       check if this dependency has a Replaces on our package 
 727       and if so transfer the manual installed flag to it */ 
 728    for (pkgCache::DepIterator Dep 
= PkgVer
.DependsList(); Dep
.end() != true; ++Dep
) 
 730       if (Dep
->Type 
!= pkgCache::Dep::Depends 
&& 
 731           Dep
->Type 
!= pkgCache::Dep::PreDepends
) 
 733       pkgCache::PkgIterator Tar 
= Dep
.TargetPkg(); 
 734       if (unlikely(Tar
.end() == true)) 
 736       // the package is already marked as manual 
 737       if ((Cache
[Tar
].Flags 
& pkgCache::Flag::Auto
) != pkgCache::Flag::Auto
) 
 739       pkgCache::VerIterator TarVer 
=  Cache
[Tar
].InstVerIter(Cache
); 
 740       if (TarVer
.end() == true) 
 742       for (pkgCache::DepIterator Rep 
= TarVer
.DependsList(); Rep
.end() != true; ++Rep
) 
 744          if (Rep
->Type 
!= pkgCache::Dep::Replaces
) 
 746          if (Pkg 
!= Rep
.TargetPkg()) 
 748          // okay, they are strongly connected - transfer manual-bit 
 750             std::clog 
<< "transfer manual-bit from disappeared »" << pkgname 
<< "« to »" << Tar
.FullName() << "«" << std::endl
; 
 751          Cache
[Tar
].Flags 
&= ~Flag::Auto
; 
 757 // DPkgPM::DoDpkgStatusFd                                               /*{{{*/ 
 758 // --------------------------------------------------------------------- 
 761 void pkgDPkgPM::DoDpkgStatusFd(int statusfd
, int OutStatusFd
) 
 766    len
=read(statusfd
, &d
->dpkgbuf
[d
->dpkgbuf_pos
], sizeof(d
->dpkgbuf
)-d
->dpkgbuf_pos
); 
 767    d
->dpkgbuf_pos 
+= len
; 
 771    // process line by line if we have a buffer 
 773    while((q
=(char*)memchr(p
, '\n', d
->dpkgbuf
+d
->dpkgbuf_pos
-p
)) != NULL
) 
 776       ProcessDpkgStatusLine(OutStatusFd
, p
); 
 777       p
=q
+1; // continue with next line 
 780    // now move the unprocessed bits (after the final \n that is now a 0x0)  
 781    // to the start and update d->dpkgbuf_pos 
 782    p 
= (char*)memrchr(d
->dpkgbuf
, 0, d
->dpkgbuf_pos
); 
 786    // we are interessted in the first char *after* 0x0 
 789    // move the unprocessed tail to the start and update pos 
 790    memmove(d
->dpkgbuf
, p
, p
-d
->dpkgbuf
); 
 791    d
->dpkgbuf_pos 
= d
->dpkgbuf
+d
->dpkgbuf_pos
-p
; 
 794 // DPkgPM::WriteHistoryTag                                              /*{{{*/ 
 795 void pkgDPkgPM::WriteHistoryTag(string 
const &tag
, string value
) 
 797    size_t const length 
= value
.length(); 
 800    // poor mans rstrip(", ") 
 801    if (value
[length
-2] == ',' && value
[length
-1] == ' ') 
 802       value
.erase(length 
- 2, 2); 
 803    fprintf(d
->history_out
, "%s: %s\n", tag
.c_str(), value
.c_str()); 
 805 // DPkgPM::OpenLog                                                      /*{{{*/ 
 806 bool pkgDPkgPM::OpenLog() 
 808    string 
const logdir 
= _config
->FindDir("Dir::Log"); 
 809    if(CreateAPTDirectoryIfNeeded(logdir
, logdir
) == false) 
 810       // FIXME: use a better string after freeze 
 811       return _error
->Error(_("Directory '%s' missing"), logdir
.c_str()); 
 815    time_t const t 
= time(NULL
); 
 816    struct tm 
const * const tmp 
= localtime(&t
); 
 817    strftime(timestr
, sizeof(timestr
), "%F  %T", tmp
); 
 820    string 
const logfile_name 
= flCombine(logdir
, 
 821                                    _config
->Find("Dir::Log::Terminal")); 
 822    if (!logfile_name
.empty()) 
 824       d
->term_out 
= fopen(logfile_name
.c_str(),"a"); 
 825       if (d
->term_out 
== NULL
) 
 826          return _error
->WarningE("OpenLog", _("Could not open file '%s'"), logfile_name
.c_str()); 
 827       setvbuf(d
->term_out
, NULL
, _IONBF
, 0); 
 828       SetCloseExec(fileno(d
->term_out
), true); 
 829       if (getuid() == 0) // if we aren't root, we can't chown a file, so don't try it 
 831          struct passwd 
*pw 
= getpwnam("root"); 
 832          struct group 
*gr 
= getgrnam("adm"); 
 833          if (pw 
!= NULL 
&& gr 
!= NULL 
&& chown(logfile_name
.c_str(), pw
->pw_uid
, gr
->gr_gid
) != 0) 
 834             _error
->WarningE("OpenLog", "chown to root:adm of file %s failed", logfile_name
.c_str()); 
 836       if (chmod(logfile_name
.c_str(), 0640) != 0) 
 837          _error
->WarningE("OpenLog", "chmod 0640 of file %s failed", logfile_name
.c_str()); 
 838       fprintf(d
->term_out
, "\nLog started: %s\n", timestr
); 
 841    // write your history 
 842    string 
const history_name 
= flCombine(logdir
, 
 843                                    _config
->Find("Dir::Log::History")); 
 844    if (!history_name
.empty()) 
 846       d
->history_out 
= fopen(history_name
.c_str(),"a"); 
 847       if (d
->history_out 
== NULL
) 
 848          return _error
->WarningE("OpenLog", _("Could not open file '%s'"), history_name
.c_str()); 
 849       SetCloseExec(fileno(d
->history_out
), true); 
 850       chmod(history_name
.c_str(), 0644); 
 851       fprintf(d
->history_out
, "\nStart-Date: %s\n", timestr
); 
 852       string remove
, purge
, install
, reinstall
, upgrade
, downgrade
; 
 853       for (pkgCache::PkgIterator I 
= Cache
.PkgBegin(); I
.end() == false; ++I
) 
 855          enum { CANDIDATE
, CANDIDATE_AUTO
, CURRENT_CANDIDATE
, CURRENT 
} infostring
; 
 857          #define HISTORYINFO(X, Y) { line = &X; infostring = Y; } 
 858          if (Cache
[I
].NewInstall() == true) 
 859             HISTORYINFO(install
, CANDIDATE_AUTO
) 
 860          else if (Cache
[I
].ReInstall() == true) 
 861             HISTORYINFO(reinstall
, CANDIDATE
) 
 862          else if (Cache
[I
].Upgrade() == true) 
 863             HISTORYINFO(upgrade
, CURRENT_CANDIDATE
) 
 864          else if (Cache
[I
].Downgrade() == true) 
 865             HISTORYINFO(downgrade
, CURRENT_CANDIDATE
) 
 866          else if (Cache
[I
].Delete() == true) 
 867             HISTORYINFO((Cache
[I
].Purge() ? purge 
: remove
), CURRENT
) 
 871          line
->append(I
.FullName(false)).append(" ("); 
 872          switch (infostring
) { 
 873          case CANDIDATE
: line
->append(Cache
[I
].CandVersion
); break; 
 875             line
->append(Cache
[I
].CandVersion
); 
 876             if ((Cache
[I
].Flags 
& pkgCache::Flag::Auto
) == pkgCache::Flag::Auto
) 
 877                line
->append(", automatic"); 
 879          case CURRENT_CANDIDATE
: line
->append(Cache
[I
].CurVersion
).append(", ").append(Cache
[I
].CandVersion
); break; 
 880          case CURRENT
: line
->append(Cache
[I
].CurVersion
); break; 
 884       if (_config
->Exists("Commandline::AsString") == true) 
 885          WriteHistoryTag("Commandline", _config
->Find("Commandline::AsString")); 
 886       WriteHistoryTag("Install", install
); 
 887       WriteHistoryTag("Reinstall", reinstall
); 
 888       WriteHistoryTag("Upgrade", upgrade
); 
 889       WriteHistoryTag("Downgrade",downgrade
); 
 890       WriteHistoryTag("Remove",remove
); 
 891       WriteHistoryTag("Purge",purge
); 
 892       fflush(d
->history_out
); 
 898 // DPkg::CloseLog                                                       /*{{{*/ 
 899 bool pkgDPkgPM::CloseLog() 
 902    time_t t 
= time(NULL
); 
 903    struct tm 
*tmp 
= localtime(&t
); 
 904    strftime(timestr
, sizeof(timestr
), "%F  %T", tmp
); 
 908       fprintf(d
->term_out
, "Log ended: "); 
 909       fprintf(d
->term_out
, "%s", timestr
); 
 910       fprintf(d
->term_out
, "\n"); 
 917       if (disappearedPkgs
.empty() == false) 
 920          for (std::set
<std::string
>::const_iterator d 
= disappearedPkgs
.begin(); 
 921               d 
!= disappearedPkgs
.end(); ++d
) 
 923             pkgCache::PkgIterator P 
= Cache
.FindPkg(*d
); 
 924             disappear
.append(*d
); 
 926                disappear
.append(", "); 
 928                disappear
.append(" (").append(Cache
[P
].CurVersion
).append("), "); 
 930          WriteHistoryTag("Disappeared", disappear
); 
 932       if (d
->dpkg_error
.empty() == false) 
 933          fprintf(d
->history_out
, "Error: %s\n", d
->dpkg_error
.c_str()); 
 934       fprintf(d
->history_out
, "End-Date: %s\n", timestr
); 
 935       fclose(d
->history_out
); 
 937    d
->history_out 
= NULL
; 
 942 // DPkgPM::SendTerminalProgress                                         /*{{{*/ 
 943 // --------------------------------------------------------------------- 
 944 /* Send progress info to the terminal 
 946 void pkgDPkgPM::SendTerminalProgress(float percentage
) 
 948    int reporting_steps 
= _config
->FindI("DpkgPM::Reporting-Steps", 1); 
 950    if(percentage 
< (d
->last_reported_progress 
+ reporting_steps
)) 
 953    std::string progress_str
; 
 954    strprintf(progress_str
, _("Progress: [%3i%%]"), (int)percentage
); 
 955    if (d
->fancy_progress_output
) 
 957          int row 
= d
->nr_terminal_rows
; 
 959          static string save_cursor 
= "\033[s"; 
 960          static string restore_cursor 
= "\033[u"; 
 962          static string set_bg_color 
= "\033[42m"; // green 
 963          static string set_fg_color 
= "\033[30m"; // black 
 965          static string restore_bg 
=  "\033[49m"; 
 966          static string restore_fg 
= "\033[39m"; 
 968          std::cout 
<< save_cursor
 
 969             // move cursor position to last row 
 970                    << "\033[" << row 
<< ";0f"  
 980          std::cout 
<< progress_str 
<< "\r\n"; 
 982    std::flush(std::cout
); 
 984    d
->last_reported_progress 
= percentage
; 
 988 // This implements a racy version of pselect for those architectures 
 989 // that don't have a working implementation. 
 990 // FIXME: Probably can be removed on Lenny+1 
 991 static int racy_pselect(int nfds
, fd_set 
*readfds
, fd_set 
*writefds
, 
 992    fd_set 
*exceptfds
, const struct timespec 
*timeout
, 
 993    const sigset_t 
*sigmask
) 
 999    tv
.tv_sec 
= timeout
->tv_sec
; 
1000    tv
.tv_usec 
= timeout
->tv_nsec
/1000; 
1002    sigprocmask(SIG_SETMASK
, sigmask
, &origmask
); 
1003    retval 
= select(nfds
, readfds
, writefds
, exceptfds
, &tv
); 
1004    sigprocmask(SIG_SETMASK
, &origmask
, 0); 
1009 void pkgDPkgPM::SetupTerminalScrollArea(int nr_rows
) 
1011      if(!d
->fancy_progress_output
) 
1014      // scroll down a bit to avoid visual glitch when the screen 
1015      // area shrinks by one row 
1019      std::cout 
<< "\033[s"; 
1021      // set scroll region (this will place the cursor in the top left) 
1022      std::cout 
<< "\033[1;" << nr_rows 
- 1 << "r"; 
1024      // restore cursor but ensure its inside the scrolling area 
1025      std::cout 
<< "\033[u"; 
1026      static const char *move_cursor_up 
= "\033[1A"; 
1027      std::cout 
<< move_cursor_up
; 
1028      std::flush(std::cout
); 
1031 void pkgDPkgPM::CleanupTerminal() 
1033    // reset scroll area 
1034    SetupTerminalScrollArea(d
->nr_terminal_rows 
+ 1); 
1035    if(d
->fancy_progress_output
) 
1037       // override the progress line (sledgehammer) 
1038       static const char* clear_screen_below_cursor 
= "\033[J"; 
1039       std::cout 
<< clear_screen_below_cursor
; 
1040       std::flush(std::cout
); 
1045 // DPkgPM::Go - Run the sequence                                        /*{{{*/ 
1046 // --------------------------------------------------------------------- 
1047 /* This globs the operations and calls dpkg  
1049  * If it is called with "OutStatusFd" set to a valid file descriptor 
1050  * apt will report the install progress over this fd. It maps the 
1051  * dpkg states a package goes through to human readable (and i10n-able) 
1052  * names and calculates a percentage for each step. 
1054 bool pkgDPkgPM::Go(int OutStatusFd
) 
1056    pkgPackageManager::SigINTStop 
= false; 
1058    // Generate the base argument list for dpkg 
1059    std::vector
<const char *> Args
; 
1060    unsigned long StartSize 
= 0; 
1061    string Tmp 
= _config
->Find("Dir::Bin::dpkg","dpkg"); 
1063       string 
const dpkgChrootDir 
= _config
->FindDir("DPkg::Chroot-Directory", "/"); 
1064       size_t dpkgChrootLen 
= dpkgChrootDir
.length(); 
1065       if (dpkgChrootDir 
!= "/" && Tmp
.find(dpkgChrootDir
) == 0) 
1067          if (dpkgChrootDir
[dpkgChrootLen 
- 1] == '/') 
1069          Tmp 
= Tmp
.substr(dpkgChrootLen
); 
1072    Args
.push_back(Tmp
.c_str()); 
1073    StartSize 
+= Tmp
.length(); 
1075    // Stick in any custom dpkg options 
1076    Configuration::Item 
const *Opts 
= _config
->Tree("DPkg::Options"); 
1080       for (; Opts 
!= 0; Opts 
= Opts
->Next
) 
1082          if (Opts
->Value
.empty() == true) 
1084          Args
.push_back(Opts
->Value
.c_str()); 
1085          StartSize 
+= Opts
->Value
.length(); 
1089    size_t const BaseArgs 
= Args
.size(); 
1090    // we need to detect if we can qualify packages with the architecture or not 
1091    Args
.push_back("--assert-multi-arch"); 
1092    Args
.push_back(NULL
); 
1094    pid_t dpkgAssertMultiArch 
= ExecFork(); 
1095    if (dpkgAssertMultiArch 
== 0) 
1097       dpkgChrootDirectory(); 
1098       // redirect everything to the ultimate sink as we only need the exit-status 
1099       int const nullfd 
= open("/dev/null", O_RDONLY
); 
1100       dup2(nullfd
, STDIN_FILENO
); 
1101       dup2(nullfd
, STDOUT_FILENO
); 
1102       dup2(nullfd
, STDERR_FILENO
); 
1103       execvp(Args
[0], (char**) &Args
[0]); 
1104       _error
->WarningE("dpkgGo", "Can't detect if dpkg supports multi-arch!"); 
1111    sigset_t original_sigmask
; 
1113    unsigned int const MaxArgs 
= _config
->FindI("Dpkg::MaxArgs",8*1024); 
1114    unsigned int const MaxArgBytes 
= _config
->FindI("Dpkg::MaxArgBytes",32*1024); 
1115    bool const NoTriggers 
= _config
->FindB("DPkg::NoTriggers", false); 
1117    if (RunScripts("DPkg::Pre-Invoke") == false) 
1120    if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false) 
1123    // support subpressing of triggers processing for special 
1124    // cases like d-i that runs the triggers handling manually 
1125    bool const SmartConf 
= (_config
->Find("PackageManager::Configure", "all") != "all"); 
1126    bool const TriggersPending 
= _config
->FindB("DPkg::TriggersPending", false); 
1127    if (_config
->FindB("DPkg::ConfigurePending", SmartConf
) == true) 
1128       List
.push_back(Item(Item::ConfigurePending
, PkgIterator())); 
1130    // map the dpkg states to the operations that are performed 
1131    // (this is sorted in the same way as Item::Ops) 
1132    static const struct DpkgState DpkgStatesOpMap
[][7] = { 
1133       // Install operation 
1135          {"half-installed", N_("Preparing %s")},  
1136          {"unpacked", N_("Unpacking %s") },  
1139       // Configure operation 
1141          {"unpacked",N_("Preparing to configure %s") }, 
1142          {"half-configured", N_("Configuring %s") }, 
1143          { "installed", N_("Installed %s")}, 
1148          {"half-configured", N_("Preparing for removal of %s")}, 
1149          {"half-installed", N_("Removing %s")}, 
1150          {"config-files",  N_("Removed %s")}, 
1155          {"config-files", N_("Preparing to completely remove %s")}, 
1156          {"not-installed", N_("Completely removed %s")}, 
1161    // init the PackageOps map, go over the list of packages that 
1162    // that will be [installed|configured|removed|purged] and add 
1163    // them to the PackageOps map (the dpkg states it goes through) 
1164    // and the PackageOpsTranslations (human readable strings) 
1165    for (vector
<Item
>::const_iterator I 
= List
.begin(); I 
!= List
.end(); ++I
) 
1167       if((*I
).Pkg
.end() == true) 
1170       string 
const name 
= (*I
).Pkg
.FullName(); 
1171       PackageOpsDone
[name
] = 0; 
1172       for(int i
=0; (DpkgStatesOpMap
[(*I
).Op
][i
]).state 
!= NULL
; ++i
) 
1174          PackageOps
[name
].push_back(DpkgStatesOpMap
[(*I
).Op
][i
]); 
1179    d
->stdin_is_dev_null 
= false; 
1184    bool dpkgMultiArch 
= false; 
1185    if (dpkgAssertMultiArch 
> 0) 
1188       while (waitpid(dpkgAssertMultiArch
, &Status
, 0) != dpkgAssertMultiArch
) 
1192          _error
->WarningE("dpkgGo", _("Waited for %s but it wasn't there"), "dpkg --assert-multi-arch"); 
1195       if (WIFEXITED(Status
) == true && WEXITSTATUS(Status
) == 0) 
1196          dpkgMultiArch 
= true; 
1199    // this loop is runs once per operation 
1200    for (vector
<Item
>::const_iterator I 
= List
.begin(); I 
!= List
.end();) 
1202       // Do all actions with the same Op in one run 
1203       vector
<Item
>::const_iterator J 
= I
; 
1204       if (TriggersPending 
== true) 
1205          for (; J 
!= List
.end(); ++J
) 
1209             if (J
->Op 
!= Item::TriggersPending
) 
1211             vector
<Item
>::const_iterator T 
= J 
+ 1; 
1212             if (T 
!= List
.end() && T
->Op 
== I
->Op
) 
1217          for (; J 
!= List
.end() && J
->Op 
== I
->Op
; ++J
) 
1220       // keep track of allocated strings for multiarch package names 
1221       std::vector
<char *> Packages
; 
1223       // start with the baseset of arguments 
1224       unsigned long Size 
= StartSize
; 
1225       Args
.erase(Args
.begin() + BaseArgs
, Args
.end()); 
1227       // Now check if we are within the MaxArgs limit 
1229       // this code below is problematic, because it may happen that 
1230       // the argument list is split in a way that A depends on B 
1231       // and they are in the same "--configure A B" run 
1232       // - with the split they may now be configured in different 
1233       //   runs, using Immediate-Configure-All can help prevent this. 
1234       if (J 
- I 
> (signed)MaxArgs
) 
1237          unsigned long const size 
= MaxArgs 
+ 10; 
1239          Packages
.reserve(size
); 
1243          unsigned long const size 
= (J 
- I
) + 10; 
1245          Packages
.reserve(size
); 
1250          return _error
->Errno("pipe","Failed to create IPC pipe to dpkg"); 
1252 #define ADDARG(X) Args.push_back(X); Size += strlen(X) 
1253 #define ADDARGC(X) Args.push_back(X); Size += sizeof(X) - 1 
1255       ADDARGC("--status-fd"); 
1256       char status_fd_buf
[20]; 
1257       snprintf(status_fd_buf
,sizeof(status_fd_buf
),"%i", fd
[1]); 
1258       ADDARG(status_fd_buf
); 
1259       unsigned long const Op 
= I
->Op
; 
1264          ADDARGC("--force-depends"); 
1265          ADDARGC("--force-remove-essential"); 
1266          ADDARGC("--remove"); 
1270          ADDARGC("--force-depends"); 
1271          ADDARGC("--force-remove-essential"); 
1275          case Item::Configure
: 
1276          ADDARGC("--configure"); 
1279          case Item::ConfigurePending
: 
1280          ADDARGC("--configure"); 
1281          ADDARGC("--pending"); 
1284          case Item::TriggersPending
: 
1285          ADDARGC("--triggers-only"); 
1286          ADDARGC("--pending"); 
1290          ADDARGC("--unpack"); 
1291          ADDARGC("--auto-deconfigure"); 
1295       if (NoTriggers 
== true && I
->Op 
!= Item::TriggersPending 
&& 
1296           I
->Op 
!= Item::ConfigurePending
) 
1298          ADDARGC("--no-triggers"); 
1302       // Write in the file or package names 
1303       if (I
->Op 
== Item::Install
) 
1305          for (;I 
!= J 
&& Size 
< MaxArgBytes
; ++I
) 
1307             if (I
->File
[0] != '/') 
1308                return _error
->Error("Internal Error, Pathname to install is not absolute '%s'",I
->File
.c_str()); 
1309             Args
.push_back(I
->File
.c_str()); 
1310             Size 
+= I
->File
.length(); 
1315          string 
const nativeArch 
= _config
->Find("APT::Architecture"); 
1316          unsigned long const oldSize 
= I
->Op 
== Item::Configure 
? Size 
: 0; 
1317          for (;I 
!= J 
&& Size 
< MaxArgBytes
; ++I
) 
1319             if((*I
).Pkg
.end() == true) 
1321             if (I
->Op 
== Item::Configure 
&& disappearedPkgs
.find(I
->Pkg
.Name()) != disappearedPkgs
.end()) 
1323             // We keep this here to allow "smooth" transitions from e.g. multiarch dpkg/ubuntu to dpkg/debian 
1324             if (dpkgMultiArch 
== false && (I
->Pkg
.Arch() == nativeArch 
|| 
1325                                            strcmp(I
->Pkg
.Arch(), "all") == 0 || 
1326                                            strcmp(I
->Pkg
.Arch(), "none") == 0)) 
1328                char const * const name 
= I
->Pkg
.Name(); 
1333                pkgCache::VerIterator PkgVer
; 
1334                std::string name 
= I
->Pkg
.Name(); 
1335                if (Op 
== Item::Remove 
|| Op 
== Item::Purge
)  
1337                   PkgVer 
= I
->Pkg
.CurrentVer(); 
1338                   if(PkgVer
.end() == true) 
1339                      PkgVer 
= FindNowVersion(I
->Pkg
); 
1342                   PkgVer 
= Cache
[I
->Pkg
].InstVerIter(Cache
); 
1343                if (strcmp(I
->Pkg
.Arch(), "none") == 0) 
1344                   ; // never arch-qualify a package without an arch 
1345                else if (PkgVer
.end() == false) 
1346                   name
.append(":").append(PkgVer
.Arch()); 
1348                   _error
->Warning("Can not find PkgVer for '%s'", name
.c_str()); 
1349                char * const fullname 
= strdup(name
.c_str()); 
1350                Packages
.push_back(fullname
); 
1354          // skip configure action if all sheduled packages disappeared 
1355          if (oldSize 
== Size
) 
1362       if (_config
->FindB("Debug::pkgDPkgPM",false) == true) 
1364          for (std::vector
<const char *>::const_iterator a 
= Args
.begin(); 
1365               a 
!= Args
.end(); ++a
) 
1370       Args
.push_back(NULL
); 
1376       /* Mask off sig int/quit. We do this because dpkg also does when  
1377          it forks scripts. What happens is that when you hit ctrl-c it sends 
1378          it to all processes in the group. Since dpkg ignores the signal  
1379          it doesn't die but we do! So we must also ignore it */ 
1380       sighandler_t old_SIGQUIT 
= signal(SIGQUIT
,SIG_IGN
); 
1381       sighandler_t old_SIGINT 
= signal(SIGINT
,SigINT
); 
1383       // Check here for any SIGINT 
1384       if (pkgPackageManager::SigINTStop 
&& (Op 
== Item::Remove 
|| Op 
== Item::Purge 
|| Op 
== Item::Install
))  
1388       // ignore SIGHUP as well (debian #463030) 
1389       sighandler_t old_SIGHUP 
= signal(SIGHUP
,SIG_IGN
); 
1396       // if tcgetattr does not return zero there was a error 
1397       // and we do not do any pty magic 
1398       _error
->PushToStack(); 
1399       if (tcgetattr(STDOUT_FILENO
, &tt
) == 0) 
1401          ioctl(1, TIOCGWINSZ
, (char *)&win
); 
1402          d
->nr_terminal_rows 
= win
.ws_row
; 
1403          if (openpty(&master
, &slave
, NULL
, &tt
, &win
) < 0) 
1405             _error
->Errno("openpty", _("Can not write log (%s)"), _("Is /dev/pts mounted?")); 
1406             master 
= slave 
= -1; 
1411             rtt
.c_lflag 
&= ~ECHO
; 
1412             rtt
.c_lflag 
|= ISIG
; 
1413             // block SIGTTOU during tcsetattr to prevent a hang if 
1414             // the process is a member of the background process group 
1415             // http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html 
1416             sigemptyset(&sigmask
); 
1417             sigaddset(&sigmask
, SIGTTOU
); 
1418             sigprocmask(SIG_BLOCK
,&sigmask
, &original_sigmask
); 
1419             tcsetattr(0, TCSAFLUSH
, &rtt
); 
1420             sigprocmask(SIG_SETMASK
, &original_sigmask
, 0); 
1423       // complain only if stdout is either a terminal (but still failed) or is an invalid 
1424       // descriptor otherwise we would complain about redirection to e.g. /dev/null as well. 
1425       else if (isatty(STDOUT_FILENO
) == 1 || errno 
== EBADF
) 
1426          _error
->Errno("tcgetattr", _("Can not write log (%s)"), _("Is stdout a terminal?")); 
1428       if (_error
->PendingError() == true) 
1429          _error
->DumpErrors(std::cerr
); 
1430       _error
->RevertToStack(); 
1434       _config
->Set("APT::Keep-Fds::",fd
[1]); 
1435       // send status information that we are about to fork dpkg 
1436       if(OutStatusFd 
> 0) { 
1437          ostringstream status
; 
1438          status 
<< "pmstatus:dpkg-exec:"  
1439                 << (PackagesDone
/float(PackagesTotal
)*100.0)  
1440                 << ":" << _("Running dpkg") 
1442          FileFd::Write(OutStatusFd
, status
.str().c_str(), status
.str().size()); 
1446       // This is the child 
1450          if(slave 
>= 0 && master 
>= 0)  
1453             ioctl(slave
, TIOCSCTTY
, 0); 
1460          close(fd
[0]); // close the read end of the pipe 
1462          dpkgChrootDirectory(); 
1464          if (chdir(_config
->FindDir("DPkg::Run-Directory","/").c_str()) != 0) 
1467          if (_config
->FindB("DPkg::FlushSTDIN",true) == true && isatty(STDIN_FILENO
)) 
1470             if ((Flags 
= fcntl(STDIN_FILENO
,F_GETFL
,dummy
)) < 0) 
1473             // Discard everything in stdin before forking dpkg 
1474             if (fcntl(STDIN_FILENO
,F_SETFL
,Flags 
| O_NONBLOCK
) < 0) 
1477             while (read(STDIN_FILENO
,&dummy
,1) == 1); 
1479             if (fcntl(STDIN_FILENO
,F_SETFL
,Flags 
& (~(long)O_NONBLOCK
)) < 0) 
1483          SetupTerminalScrollArea(d
->nr_terminal_rows
); 
1484          SendTerminalProgress(PackagesDone
/float(PackagesTotal
)*100.0); 
1486          /* No Job Control Stop Env is a magic dpkg var that prevents it 
1487             from using sigstop */ 
1488          putenv((char *)"DPKG_NO_TSTP=yes"); 
1489          execvp(Args
[0], (char**) &Args
[0]); 
1490          cerr 
<< "Could not exec dpkg!" << endl
; 
1495       if (_config
->FindB("DPkg::UseIoNice", false) == true) 
1498       // clear the Keep-Fd again 
1499       _config
->Clear("APT::Keep-Fds",fd
[1]); 
1504       // we read from dpkg here 
1505       int const _dpkgin 
= fd
[0]; 
1506       close(fd
[1]);                        // close the write end of the pipe 
1512       sigemptyset(&sigmask
); 
1513       sigprocmask(SIG_BLOCK
,&sigmask
,&original_sigmask
); 
1515       /* free vectors (and therefore memory) as we don't need the included data anymore */ 
1516       for (std::vector
<char *>::const_iterator p 
= Packages
.begin(); 
1517            p 
!= Packages
.end(); ++p
) 
1521       // the result of the waitpid call 
1524       while ((res
=waitpid(Child
,&Status
, WNOHANG
)) != Child
) { 
1526             // FIXME: move this to a function or something, looks ugly here 
1527             // error handling, waitpid returned -1 
1530             RunScripts("DPkg::Post-Invoke"); 
1532             // Restore sig int/quit 
1533             signal(SIGQUIT
,old_SIGQUIT
); 
1534             signal(SIGINT
,old_SIGINT
); 
1536             signal(SIGHUP
,old_SIGHUP
); 
1537             return _error
->Errno("waitpid","Couldn't wait for subprocess"); 
1540          // wait for input or output here 
1542          if (master 
>= 0 && !d
->stdin_is_dev_null
) 
1544          FD_SET(_dpkgin
, &rfds
); 
1546             FD_SET(master
, &rfds
); 
1549          select_ret 
= pselect(max(master
, _dpkgin
)+1, &rfds
, NULL
, NULL
,  
1550                               &tv
, &original_sigmask
); 
1551          if (select_ret 
< 0 && (errno 
== EINVAL 
|| errno 
== ENOSYS
)) 
1552             select_ret 
= racy_pselect(max(master
, _dpkgin
)+1, &rfds
, NULL
, 
1553                                       NULL
, &tv
, &original_sigmask
); 
1554          if (select_ret 
== 0)  
1556          else if (select_ret 
< 0 && errno 
== EINTR
) 
1558          else if (select_ret 
< 0)  
1560             perror("select() returned error"); 
1564          if(master 
>= 0 && FD_ISSET(master
, &rfds
)) 
1565             DoTerminalPty(master
); 
1566          if(master 
>= 0 && FD_ISSET(0, &rfds
)) 
1568          if(FD_ISSET(_dpkgin
, &rfds
)) 
1569             DoDpkgStatusFd(_dpkgin
, OutStatusFd
); 
1573       // Restore sig int/quit 
1574       signal(SIGQUIT
,old_SIGQUIT
); 
1575       signal(SIGINT
,old_SIGINT
); 
1577       signal(SIGHUP
,old_SIGHUP
); 
1581          tcsetattr(0, TCSAFLUSH
, &tt
); 
1585       // Check for an error code. 
1586       if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0) 
1588          // if it was set to "keep-dpkg-runing" then we won't return 
1589          // here but keep the loop going and just report it as a error 
1591          bool const stopOnError 
= _config
->FindB("Dpkg::StopOnError",true); 
1594             RunScripts("DPkg::Post-Invoke"); 
1596          if (WIFSIGNALED(Status
) != 0 && WTERMSIG(Status
) == SIGSEGV
)  
1597             strprintf(d
->dpkg_error
, "Sub-process %s received a segmentation fault.",Args
[0]); 
1598          else if (WIFEXITED(Status
) != 0) 
1599             strprintf(d
->dpkg_error
, "Sub-process %s returned an error code (%u)",Args
[0],WEXITSTATUS(Status
)); 
1601             strprintf(d
->dpkg_error
, "Sub-process %s exited unexpectedly",Args
[0]); 
1603          if(d
->dpkg_error
.size() > 0) 
1604             _error
->Error("%s", d
->dpkg_error
.c_str()); 
1616    // dpkg is done at this point 
1617    if(_config
->FindB("DPkgPM::Progress", false) == true) 
1618       SendTerminalProgress(100); 
1622    if (pkgPackageManager::SigINTStop
) 
1623        _error
->Warning(_("Operation was interrupted before it could finish")); 
1625    if (RunScripts("DPkg::Post-Invoke") == false) 
1628    if (_config
->FindB("Debug::pkgDPkgPM",false) == false) 
1630       std::string 
const oldpkgcache 
= _config
->FindFile("Dir::cache::pkgcache"); 
1631       if (oldpkgcache
.empty() == false && RealFileExists(oldpkgcache
) == true && 
1632           unlink(oldpkgcache
.c_str()) == 0) 
1634          std::string 
const srcpkgcache 
= _config
->FindFile("Dir::cache::srcpkgcache"); 
1635          if (srcpkgcache
.empty() == false && RealFileExists(srcpkgcache
) == true) 
1637             _error
->PushToStack(); 
1638             pkgCacheFile CacheFile
; 
1639             CacheFile
.BuildCaches(NULL
, true); 
1640             _error
->RevertToStack(); 
1645    Cache
.writeStateFile(NULL
); 
1649 void SigINT(int sig
) { 
1650    pkgPackageManager::SigINTStop 
= true; 
1653 // pkgDpkgPM::Reset - Dump the contents of the command list             /*{{{*/ 
1654 // --------------------------------------------------------------------- 
1656 void pkgDPkgPM::Reset()  
1658    List
.erase(List
.begin(),List
.end()); 
1661 // pkgDpkgPM::WriteApportReport - write out error report pkg failure    /*{{{*/ 
1662 // --------------------------------------------------------------------- 
1664 void pkgDPkgPM::WriteApportReport(const char *pkgpath
, const char *errormsg
)  
1666    // If apport doesn't exist or isn't installed do nothing 
1667    // This e.g. prevents messages in 'universes' without apport 
1668    pkgCache::PkgIterator apportPkg 
= Cache
.FindPkg("apport"); 
1669    if (apportPkg
.end() == true || apportPkg
->CurrentVer 
== 0) 
1672    string pkgname
, reportfile
, srcpkgname
, pkgver
, arch
; 
1673    string::size_type pos
; 
1676    if (_config
->FindB("Dpkg::ApportFailureReport", false) == false) 
1678       std::clog 
<< "configured to not write apport reports" << std::endl
; 
1682    // only report the first errors 
1683    if(pkgFailures 
> _config
->FindI("APT::Apport::MaxReports", 3)) 
1685       std::clog 
<< _("No apport report written because MaxReports is reached already") << std::endl
; 
1689    // check if its not a follow up error  
1690    const char *needle 
= dgettext("dpkg", "dependency problems - leaving unconfigured"); 
1691    if(strstr(errormsg
, needle
) != NULL
) { 
1692       std::clog 
<< _("No apport report written because the error message indicates its a followup error from a previous failure.") << std::endl
; 
1696    // do not report disk-full failures  
1697    if(strstr(errormsg
, strerror(ENOSPC
)) != NULL
) { 
1698       std::clog 
<< _("No apport report written because the error message indicates a disk full error") << std::endl
; 
1702    // do not report out-of-memory failures  
1703    if(strstr(errormsg
, strerror(ENOMEM
)) != NULL
) { 
1704       std::clog 
<< _("No apport report written because the error message indicates a out of memory error") << std::endl
; 
1708    // do not report dpkg I/O errors 
1709    // XXX - this message is localized, but this only matches the English version.  This is better than nothing. 
1710    if(strstr(errormsg
, "short read in buffer_copy (")) { 
1711       std::clog 
<< _("No apport report written because the error message indicates a dpkg I/O error") << std::endl
; 
1715    // get the pkgname and reportfile 
1716    pkgname 
= flNotDir(pkgpath
); 
1717    pos 
= pkgname
.find('_'); 
1718    if(pos 
!= string::npos
) 
1719       pkgname 
= pkgname
.substr(0, pos
); 
1721    // find the package versin and source package name 
1722    pkgCache::PkgIterator Pkg 
= Cache
.FindPkg(pkgname
); 
1723    if (Pkg
.end() == true) 
1725    pkgCache::VerIterator Ver 
= Cache
.GetCandidateVer(Pkg
); 
1726    if (Ver
.end() == true) 
1728    pkgver 
= Ver
.VerStr() == NULL 
? "unknown" : Ver
.VerStr(); 
1729    pkgRecords 
Recs(Cache
); 
1730    pkgRecords::Parser 
&Parse 
= Recs
.Lookup(Ver
.FileList()); 
1731    srcpkgname 
= Parse
.SourcePkg(); 
1732    if(srcpkgname
.empty()) 
1733       srcpkgname 
= pkgname
; 
1735    // if the file exists already, we check: 
1736    // - if it was reported already (touched by apport).  
1737    //   If not, we do nothing, otherwise 
1738    //    we overwrite it. This is the same behaviour as apport 
1739    // - if we have a report with the same pkgversion already 
1741    reportfile 
= flCombine("/var/crash",pkgname
+".0.crash"); 
1742    if(FileExists(reportfile
)) 
1747       // check atime/mtime 
1748       stat(reportfile
.c_str(), &buf
); 
1749       if(buf
.st_mtime 
> buf
.st_atime
) 
1752       // check if the existing report is the same version 
1753       report 
= fopen(reportfile
.c_str(),"r"); 
1754       while(fgets(strbuf
, sizeof(strbuf
), report
) != NULL
) 
1756          if(strstr(strbuf
,"Package:") == strbuf
) 
1758             char pkgname
[255], version
[255]; 
1759             if(sscanf(strbuf
, "Package: %254s %254s", pkgname
, version
) == 2) 
1760                if(strcmp(pkgver
.c_str(), version
) == 0) 
1770    // now write the report 
1771    arch 
= _config
->Find("APT::Architecture"); 
1772    report 
= fopen(reportfile
.c_str(),"w"); 
1775    if(_config
->FindB("DPkgPM::InitialReportOnly",false) == true) 
1776       chmod(reportfile
.c_str(), 0); 
1778       chmod(reportfile
.c_str(), 0600); 
1779    fprintf(report
, "ProblemType: Package\n"); 
1780    fprintf(report
, "Architecture: %s\n", arch
.c_str()); 
1781    time_t now 
= time(NULL
); 
1782    fprintf(report
, "Date: %s" , ctime(&now
)); 
1783    fprintf(report
, "Package: %s %s\n", pkgname
.c_str(), pkgver
.c_str()); 
1784    fprintf(report
, "SourcePackage: %s\n", srcpkgname
.c_str()); 
1785    fprintf(report
, "ErrorMessage:\n %s\n", errormsg
); 
1787    // ensure that the log is flushed 
1789       fflush(d
->term_out
); 
1791    // attach terminal log it if we have it 
1792    string logfile_name 
= _config
->FindFile("Dir::Log::Terminal"); 
1793    if (!logfile_name
.empty()) 
1797       fprintf(report
, "DpkgTerminalLog:\n"); 
1798       log 
= fopen(logfile_name
.c_str(),"r"); 
1802          while( fgets(buf
, sizeof(buf
), log
) != NULL
) 
1803             fprintf(report
, " %s", buf
); 
1809    const char *ops_str
[] = {"Install", "Configure","Remove","Purge"}; 
1810    fprintf(report
, "AptOrdering:\n"); 
1811    for (vector
<Item
>::iterator I 
= List
.begin(); I 
!= List
.end(); ++I
) 
1812       if ((*I
).Pkg 
!= NULL
) 
1813          fprintf(report
, " %s: %s\n", (*I
).Pkg
.Name(), ops_str
[(*I
).Op
]); 
1815          fprintf(report
, " %s: %s\n", "NULL", ops_str
[(*I
).Op
]); 
1817    // attach dmesg log (to learn about segfaults) 
1818    if (FileExists("/bin/dmesg")) 
1820       fprintf(report
, "Dmesg:\n"); 
1821       FILE *log 
= popen("/bin/dmesg","r"); 
1825          while( fgets(buf
, sizeof(buf
), log
) != NULL
) 
1826             fprintf(report
, " %s", buf
); 
1831    // attach df -l log (to learn about filesystem status) 
1832    if (FileExists("/bin/df")) 
1835       fprintf(report
, "Df:\n"); 
1836       FILE *log 
= popen("/bin/df -l","r"); 
1840          while( fgets(buf
, sizeof(buf
), log
) != NULL
) 
1841             fprintf(report
, " %s", buf
);