diff options
Diffstat (limited to 'apt-pkg/deb')
-rw-r--r-- | apt-pkg/deb/debindexfile.cc | 180 | ||||
-rw-r--r-- | apt-pkg/deb/debindexfile.h | 34 | ||||
-rw-r--r-- | apt-pkg/deb/deblistparser.cc | 48 | ||||
-rw-r--r-- | apt-pkg/deb/deblistparser.h | 4 | ||||
-rw-r--r-- | apt-pkg/deb/debmetaindex.cc | 25 | ||||
-rw-r--r-- | apt-pkg/deb/debmetaindex.h | 4 | ||||
-rw-r--r-- | apt-pkg/deb/debrecords.cc | 64 | ||||
-rw-r--r-- | apt-pkg/deb/debrecords.h | 8 | ||||
-rw-r--r-- | apt-pkg/deb/debsrcrecords.cc | 34 | ||||
-rw-r--r-- | apt-pkg/deb/debsrcrecords.h | 13 | ||||
-rw-r--r-- | apt-pkg/deb/debsystem.cc | 4 | ||||
-rw-r--r-- | apt-pkg/deb/debsystem.h | 4 | ||||
-rw-r--r-- | apt-pkg/deb/debversion.cc | 3 | ||||
-rw-r--r-- | apt-pkg/deb/debversion.h | 4 | ||||
-rw-r--r-- | apt-pkg/deb/dpkgpm.cc | 596 | ||||
-rw-r--r-- | apt-pkg/deb/dpkgpm.h | 44 |
16 files changed, 841 insertions, 228 deletions
diff --git a/apt-pkg/deb/debindexfile.cc b/apt-pkg/deb/debindexfile.cc index ff8bce85d..ed7633803 100644 --- a/apt-pkg/deb/debindexfile.cc +++ b/apt-pkg/deb/debindexfile.cc @@ -9,10 +9,6 @@ ##################################################################### */ /*}}}*/ // Include Files /*{{{*/ -#ifdef __GNUG__ -#pragma implementation "apt-pkg/debindexfile.h" -#endif - #include <apt-pkg/debindexfile.h> #include <apt-pkg/debsrcrecords.h> #include <apt-pkg/deblistparser.h> @@ -305,7 +301,7 @@ pkgCache::PkgFileIterator debPackagesIndex::FindInCache(pkgCache &Cache) const pkgCache::PkgFileIterator File = Cache.FileBegin(); for (; File.end() == false; File++) { - if (FileName != File.FileName()) + if (File.FileName() == NULL || FileName != File.FileName()) continue; struct stat St; @@ -320,6 +316,170 @@ pkgCache::PkgFileIterator debPackagesIndex::FindInCache(pkgCache &Cache) const } /*}}}*/ +// TranslationsIndex::debTranslationsIndex - Contructor /*{{{*/ +// --------------------------------------------------------------------- +/* */ +debTranslationsIndex::debTranslationsIndex(string URI,string Dist,string Section) : + pkgIndexFile(true), URI(URI), Dist(Dist), Section(Section) +{ +} + /*}}}*/ +// TranslationIndex::Trans* - Return the URI to the translation files /*{{{*/ +// --------------------------------------------------------------------- +/* */ +inline string debTranslationsIndex::IndexFile(const char *Type) const +{ + return _config->FindDir("Dir::State::lists") + URItoFileName(IndexURI(Type)); +} +string debTranslationsIndex::IndexURI(const char *Type) const +{ + string Res; + if (Dist[Dist.size() - 1] == '/') + { + if (Dist != "/") + Res = URI + Dist; + else + Res = URI; + } + else + Res = URI + "dists/" + Dist + '/' + Section + + "/i18n/Translation-"; + + Res += Type; + return Res; +} + /*}}}*/ +// TranslationsIndex::GetIndexes - Fetch the index files /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool debTranslationsIndex::GetIndexes(pkgAcquire *Owner) const +{ + if (TranslationsAvailable()) { + string TranslationFile = "Translation-" + LanguageCode(); + new pkgAcqIndexTrans(Owner, IndexURI(LanguageCode().c_str()), + Info(TranslationFile.c_str()), + TranslationFile); + } + + return true; +} + /*}}}*/ +// TranslationsIndex::Describe - Give a descriptive path to the index /*{{{*/ +// --------------------------------------------------------------------- +/* This should help the user find the index in the sources.list and + in the filesystem for problem solving */ +string debTranslationsIndex::Describe(bool Short) const +{ + char S[300]; + if (Short == true) + snprintf(S,sizeof(S),"%s",Info(TranslationFile().c_str()).c_str()); + else + snprintf(S,sizeof(S),"%s (%s)",Info(TranslationFile().c_str()).c_str(), + IndexFile(LanguageCode().c_str()).c_str()); + return S; +} + /*}}}*/ +// TranslationsIndex::Info - One liner describing the index URI /*{{{*/ +// --------------------------------------------------------------------- +/* */ +string debTranslationsIndex::Info(const char *Type) const +{ + string Info = ::URI::SiteOnly(URI) + ' '; + if (Dist[Dist.size() - 1] == '/') + { + if (Dist != "/") + Info += Dist; + } + else + Info += Dist + '/' + Section; + Info += " "; + Info += Type; + return Info; +} + /*}}}*/ +bool debTranslationsIndex::HasPackages() const +{ + if(!TranslationsAvailable()) + return false; + + return FileExists(IndexFile(LanguageCode().c_str())); +} + +// TranslationsIndex::Exists - Check if the index is available /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool debTranslationsIndex::Exists() const +{ + return FileExists(IndexFile(LanguageCode().c_str())); +} + /*}}}*/ +// TranslationsIndex::Size - Return the size of the index /*{{{*/ +// --------------------------------------------------------------------- +/* This is really only used for progress reporting. */ +unsigned long debTranslationsIndex::Size() const +{ + struct stat S; + if (stat(IndexFile(LanguageCode().c_str()).c_str(),&S) != 0) + return 0; + return S.st_size; +} + /*}}}*/ +// TranslationsIndex::Merge - Load the index file into a cache /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool debTranslationsIndex::Merge(pkgCacheGenerator &Gen,OpProgress &Prog) const +{ + // Check the translation file, if in use + string TranslationFile = IndexFile(LanguageCode().c_str()); + if (TranslationsAvailable() && FileExists(TranslationFile)) + { + FileFd Trans(TranslationFile,FileFd::ReadOnly); + debListParser TransParser(&Trans); + if (_error->PendingError() == true) + return false; + + Prog.SubProgress(0, Info(TranslationFile.c_str())); + if (Gen.SelectFile(TranslationFile,string(),*this) == false) + return _error->Error("Problem with SelectFile %s",TranslationFile.c_str()); + + // Store the IMS information + pkgCache::PkgFileIterator TransFile = Gen.GetCurFile(); + struct stat TransSt; + if (fstat(Trans.Fd(),&TransSt) != 0) + return _error->Errno("fstat","Failed to stat"); + TransFile->Size = TransSt.st_size; + TransFile->mtime = TransSt.st_mtime; + + if (Gen.MergeList(TransParser) == false) + return _error->Error("Problem with MergeList %s",TranslationFile.c_str()); + } + + return true; +} + /*}}}*/ +// TranslationsIndex::FindInCache - Find this index /*{{{*/ +// --------------------------------------------------------------------- +/* */ +pkgCache::PkgFileIterator debTranslationsIndex::FindInCache(pkgCache &Cache) const +{ + string FileName = IndexFile(LanguageCode().c_str()); + + pkgCache::PkgFileIterator File = Cache.FileBegin(); + for (; File.end() == false; File++) + { + if (FileName != File.FileName()) + continue; + + struct stat St; + if (stat(File.FileName(),&St) != 0) + return pkgCache::PkgFileIterator(Cache); + if ((unsigned)St.st_size != File->Size || St.st_mtime != File->mtime) + return pkgCache::PkgFileIterator(Cache); + return File; + } + return File; +} + /*}}}*/ // StatusIndex::debStatusIndex - Constructor /*{{{*/ // --------------------------------------------------------------------- /* */ @@ -416,6 +576,11 @@ class debIFTypePkg : public pkgIndexFile::Type }; debIFTypePkg() {Label = "Debian Package Index";}; }; +class debIFTypeTrans : public debIFTypePkg +{ + public: + debIFTypeTrans() {Label = "Debian Translation Index";}; +}; class debIFTypeStatus : public pkgIndexFile::Type { public: @@ -428,6 +593,7 @@ class debIFTypeStatus : public pkgIndexFile::Type }; static debIFTypeSrc _apt_Src; static debIFTypePkg _apt_Pkg; +static debIFTypeTrans _apt_Trans; static debIFTypeStatus _apt_Status; const pkgIndexFile::Type *debSourcesIndex::GetType() const @@ -438,6 +604,10 @@ const pkgIndexFile::Type *debPackagesIndex::GetType() const { return &_apt_Pkg; } +const pkgIndexFile::Type *debTranslationsIndex::GetType() const +{ + return &_apt_Trans; +} const pkgIndexFile::Type *debStatusIndex::GetType() const { return &_apt_Status; diff --git a/apt-pkg/deb/debindexfile.h b/apt-pkg/deb/debindexfile.h index a1b9583a4..b0012c96b 100644 --- a/apt-pkg/deb/debindexfile.h +++ b/apt-pkg/deb/debindexfile.h @@ -16,9 +16,7 @@ #ifndef PKGLIB_DEBINDEXFILE_H #define PKGLIB_DEBINDEXFILE_H -#ifdef __GNUG__ -#pragma interface "apt-pkg/debindexfile.h" -#endif + #include <apt-pkg/indexfile.h> @@ -74,6 +72,36 @@ class debPackagesIndex : public pkgIndexFile debPackagesIndex(string URI,string Dist,string Section,bool Trusted); }; +class debTranslationsIndex : public pkgIndexFile +{ + string URI; + string Dist; + string Section; + + string Info(const char *Type) const; + string IndexFile(const char *Type) const; + string IndexURI(const char *Type) const; + + inline string TranslationFile() const {return "Translation-" + LanguageCode();}; + + public: + + virtual const Type *GetType() const; + + // Interface for acquire + virtual string Describe(bool Short) const; + virtual bool GetIndexes(pkgAcquire *Owner) const; + + // Interface for the Cache Generator + virtual bool Exists() const; + virtual bool HasPackages() const; + virtual unsigned long Size() const; + virtual bool Merge(pkgCacheGenerator &Gen,OpProgress &Prog) const; + virtual pkgCache::PkgFileIterator FindInCache(pkgCache &Cache) const; + + debTranslationsIndex(string URI,string Dist,string Section); +}; + class debSourcesIndex : public pkgIndexFile { string URI; diff --git a/apt-pkg/deb/deblistparser.cc b/apt-pkg/deb/deblistparser.cc index d0dc7a260..896d4d6d8 100644 --- a/apt-pkg/deb/deblistparser.cc +++ b/apt-pkg/deb/deblistparser.cc @@ -15,6 +15,7 @@ #include <apt-pkg/configuration.h> #include <apt-pkg/strutl.h> #include <apt-pkg/crc-16.h> +#include <apt-pkg/md5.h> #include <ctype.h> @@ -104,6 +105,8 @@ bool debListParser::NewVersion(pkgCache::VerIterator Ver) return false; if (ParseDepends(Ver,"Conflicts",pkgCache::Dep::Conflicts) == false) return false; + if (ParseDepends(Ver,"Breaks",pkgCache::Dep::DpkgBreaks) == false) + return false; if (ParseDepends(Ver,"Replaces",pkgCache::Dep::Replaces) == false) return false; @@ -117,6 +120,48 @@ bool debListParser::NewVersion(pkgCache::VerIterator Ver) return true; } /*}}}*/ +// ListParser::Description - Return the description string /*{{{*/ +// --------------------------------------------------------------------- +/* This is to return the string describing the package in debian + form. If this returns the blank string then the entry is assumed to + only describe package properties */ +string debListParser::Description() +{ + if (DescriptionLanguage().empty()) + return Section.FindS("Description"); + else + return Section.FindS(("Description-" + pkgIndexFile::LanguageCode()).c_str()); +} + /*}}}*/ +// ListParser::DescriptionLanguage - Return the description lang string /*{{{*/ +// --------------------------------------------------------------------- +/* This is to return the string describing the language of + description. If this returns the blank string then the entry is + assumed to describe original description. */ +string debListParser::DescriptionLanguage() +{ + return Section.FindS("Description").empty() ? pkgIndexFile::LanguageCode() : ""; +} + /*}}}*/ +// ListParser::Description - Return the description_md5 MD5SumValue /*{{{*/ +// --------------------------------------------------------------------- +/* This is to return the md5 string to allow the check if it is the right + description. If no Description-md5 is found in the section it will be + calculated. + */ +MD5SumValue debListParser::Description_md5() +{ + string value = Section.FindS("Description-md5"); + + if (value.empty()) + { + MD5Summation md5; + md5.Add((Description() + "\n").c_str()); + return md5.Result(); + } else + return MD5SumValue(value); +} + /*}}}*/ // ListParser::UsePackage - Update a package structure /*{{{*/ // --------------------------------------------------------------------- /* This is called to update the package with any new information @@ -150,6 +195,7 @@ unsigned short debListParser::VersionHash() // "Suggests", // "Recommends", "Conflicts", + "Breaks", "Replaces",0}; unsigned long Result = INIT_FCS; char S[1024]; @@ -247,6 +293,8 @@ bool debListParser::ParseStatus(pkgCache::PkgIterator Pkg, {"installed",pkgCache::State::Installed}, {"half-installed",pkgCache::State::HalfInstalled}, {"config-files",pkgCache::State::ConfigFiles}, + {"triggers-awaited",pkgCache::State::TriggersAwaited}, + {"triggers-pending",pkgCache::State::TriggersPending}, {"post-inst-failed",pkgCache::State::HalfConfigured}, {"removal-failed",pkgCache::State::HalfInstalled}, {}}; diff --git a/apt-pkg/deb/deblistparser.h b/apt-pkg/deb/deblistparser.h index 3a0e0421b..34bb29c72 100644 --- a/apt-pkg/deb/deblistparser.h +++ b/apt-pkg/deb/deblistparser.h @@ -12,6 +12,7 @@ #define PKGLIB_DEBLISTPARSER_H #include <apt-pkg/pkgcachegen.h> +#include <apt-pkg/indexfile.h> #include <apt-pkg/tagfile.h> class debListParser : public pkgCacheGenerator::ListParser @@ -47,6 +48,9 @@ class debListParser : public pkgCacheGenerator::ListParser virtual string Package(); virtual string Version(); virtual bool NewVersion(pkgCache::VerIterator Ver); + virtual string Description(); + virtual string DescriptionLanguage(); + virtual MD5SumValue Description_md5(); virtual unsigned short VersionHash(); virtual bool UsePackage(pkgCache::PkgIterator Pkg, pkgCache::VerIterator Ver); diff --git a/apt-pkg/deb/debmetaindex.cc b/apt-pkg/deb/debmetaindex.cc index 85e5b16b3..c700f8d58 100644 --- a/apt-pkg/deb/debmetaindex.cc +++ b/apt-pkg/deb/debmetaindex.cc @@ -1,9 +1,5 @@ // ijones, walters -#ifdef __GNUG__ -#pragma implementation "apt-pkg/debmetaindex.h" -#endif - #include <apt-pkg/debmetaindex.h> #include <apt-pkg/debindexfile.h> #include <apt-pkg/strutl.h> @@ -148,7 +144,7 @@ bool debReleaseIndex::GetIndexes(pkgAcquire *Owner, bool GetAll) const vector <struct IndexTarget *> *targets = ComputeIndexTargets(); for (vector <struct IndexTarget*>::const_iterator Target = targets->begin(); Target != targets->end(); Target++) { new pkgAcqIndex(Owner, (*Target)->URI, (*Target)->Description, - (*Target)->ShortDesc, ""); + (*Target)->ShortDesc, HashString()); } } new pkgAcqMetaSig(Owner, MetaIndexURI("Release.gpg"), @@ -157,6 +153,16 @@ bool debReleaseIndex::GetIndexes(pkgAcquire *Owner, bool GetAll) const ComputeIndexTargets(), new indexRecords (Dist)); + // Queue the translations + for (vector<const debSectionEntry *>::const_iterator I = SectionEntries.begin(); + I != SectionEntries.end(); I++) { + + if((*I)->IsSrc) + continue; + debTranslationsIndex i = debTranslationsIndex(URI,Dist,(*I)->Section); + i.GetIndexes(Owner); + } + return true; } @@ -181,11 +187,16 @@ vector <pkgIndexFile *> *debReleaseIndex::GetIndexFiles() Indexes = new vector <pkgIndexFile*>; for (vector<const debSectionEntry *>::const_iterator I = SectionEntries.begin(); - I != SectionEntries.end(); I++) + I != SectionEntries.end(); I++) { if ((*I)->IsSrc) Indexes->push_back(new debSourcesIndex (URI, Dist, (*I)->Section, IsTrusted())); else + { Indexes->push_back(new debPackagesIndex (URI, Dist, (*I)->Section, IsTrusted())); + Indexes->push_back(new debTranslationsIndex(URI, Dist, (*I)->Section)); + } + } + return Indexes; } @@ -213,7 +224,7 @@ class debSLTypeDebian : public pkgSourceList::Type // This check insures that there will be only one Release file // queued for all the Packages files and Sources files it // corresponds to. - if ((*I)->GetType() == "deb") + if (strcmp((*I)->GetType(), "deb") == 0) { debReleaseIndex *Deb = (debReleaseIndex *) (*I); // This check insures that there will be only one Release file diff --git a/apt-pkg/deb/debmetaindex.h b/apt-pkg/deb/debmetaindex.h index 2b9922987..c021a1b5a 100644 --- a/apt-pkg/deb/debmetaindex.h +++ b/apt-pkg/deb/debmetaindex.h @@ -2,10 +2,6 @@ #ifndef PKGLIB_DEBMETAINDEX_H #define PKGLIB_DEBMETAINDEX_H -#ifdef __GNUG__ -#pragma interface "apt-pkg/debmetaindex.h" -#endif - #include <apt-pkg/metaindex.h> #include <apt-pkg/sourcelist.h> diff --git a/apt-pkg/deb/debrecords.cc b/apt-pkg/deb/debrecords.cc index 6652a6ad9..3d3d7de0a 100644 --- a/apt-pkg/deb/debrecords.cc +++ b/apt-pkg/deb/debrecords.cc @@ -8,11 +8,10 @@ ##################################################################### */ /*}}}*/ // Include Files /*{{{*/ -#ifdef __GNUG__ -#pragma implementation "apt-pkg/debrecords.h" -#endif #include <apt-pkg/debrecords.h> +#include <apt-pkg/strutl.h> #include <apt-pkg/error.h> +#include <langinfo.h> /*}}}*/ // RecordParser::debRecordParser - Constructor /*{{{*/ @@ -31,6 +30,10 @@ bool debRecordParser::Jump(pkgCache::VerFileIterator const &Ver) { return Tags.Jump(Section,Ver->Offset); } +bool debRecordParser::Jump(pkgCache::DescFileIterator const &Desc) +{ + return Tags.Jump(Section,Desc->Offset); +} /*}}}*/ // RecordParser::FileName - Return the archive filename on the site /*{{{*/ // --------------------------------------------------------------------- @@ -61,7 +64,15 @@ string debRecordParser::MD5Hash() /* */ string debRecordParser::SHA1Hash() { - return Section.FindS("SHA1Sum"); + return Section.FindS("SHA1"); +} + /*}}}*/ +// RecordParser::SHA1Hash - Return the archive hash /*{{{*/ +// --------------------------------------------------------------------- +/* */ +string debRecordParser::SHA256Hash() +{ + return Section.FindS("SHA256"); } /*}}}*/ // RecordParser::Maintainer - Return the maintainer email /*{{{*/ @@ -77,7 +88,7 @@ string debRecordParser::Maintainer() /* */ string debRecordParser::ShortDesc() { - string Res = Section.FindS("Description"); + string Res = LongDesc(); string::size_type Pos = Res.find('\n'); if (Pos == string::npos) return Res; @@ -89,21 +100,60 @@ string debRecordParser::ShortDesc() /* */ string debRecordParser::LongDesc() { - return Section.FindS("Description"); + string orig, dest; + char *codeset = nl_langinfo(CODESET); + + if (!Section.FindS("Description").empty()) + orig = Section.FindS("Description").c_str(); + else + orig = Section.FindS(("Description-" + pkgIndexFile::LanguageCode()).c_str()).c_str(); + + if (strcmp(codeset,"UTF-8") != 0) { + UTF8ToCodeset(codeset, orig, &dest); + orig = dest; + } + + return orig; } /*}}}*/ + +static const char *SourceVerSeparators = " ()"; + // RecordParser::SourcePkg - Return the source package name if any /*{{{*/ // --------------------------------------------------------------------- /* */ string debRecordParser::SourcePkg() { string Res = Section.FindS("Source"); - string::size_type Pos = Res.find(' '); + string::size_type Pos = Res.find_first_of(SourceVerSeparators); if (Pos == string::npos) return Res; return string(Res,0,Pos); } /*}}}*/ +// RecordParser::SourceVer - Return the source version number if present /*{{{*/ +// --------------------------------------------------------------------- +/* */ +string debRecordParser::SourceVer() +{ + string Pkg = Section.FindS("Source"); + string::size_type Pos = Pkg.find_first_of(SourceVerSeparators); + if (Pos == string::npos) + return ""; + + string::size_type VerStart = Pkg.find_first_not_of(SourceVerSeparators, Pos); + if(VerStart == string::npos) + return ""; + + string::size_type VerEnd = Pkg.find_first_of(SourceVerSeparators, VerStart); + if(VerEnd == string::npos) + // Corresponds to the case of, e.g., "foo (1.2" without a closing + // paren. Be liberal and guess what it means. + return string(Pkg, VerStart); + else + return string(Pkg, VerStart, VerEnd - VerStart); +} + /*}}}*/ // RecordParser::GetRec - Return the whole record /*{{{*/ // --------------------------------------------------------------------- /* */ diff --git a/apt-pkg/deb/debrecords.h b/apt-pkg/deb/debrecords.h index efef2e588..ab244b6dd 100644 --- a/apt-pkg/deb/debrecords.h +++ b/apt-pkg/deb/debrecords.h @@ -14,11 +14,8 @@ #ifndef PKGLIB_DEBRECORDS_H #define PKGLIB_DEBRECORDS_H -#ifdef __GNUG__ -#pragma interface "apt-pkg/debrecords.h" -#endif - #include <apt-pkg/pkgrecords.h> +#include <apt-pkg/indexfile.h> #include <apt-pkg/tagfile.h> class debRecordParser : public pkgRecords::Parser @@ -30,6 +27,7 @@ class debRecordParser : public pkgRecords::Parser protected: virtual bool Jump(pkgCache::VerFileIterator const &Ver); + virtual bool Jump(pkgCache::DescFileIterator const &Desc); public: @@ -37,7 +35,9 @@ class debRecordParser : public pkgRecords::Parser virtual string FileName(); virtual string MD5Hash(); virtual string SHA1Hash(); + virtual string SHA256Hash(); virtual string SourcePkg(); + virtual string SourceVer(); // These are some general stats about the package virtual string Maintainer(); diff --git a/apt-pkg/deb/debsrcrecords.cc b/apt-pkg/deb/debsrcrecords.cc index cac36c2e6..ace4e00b5 100644 --- a/apt-pkg/deb/debsrcrecords.cc +++ b/apt-pkg/deb/debsrcrecords.cc @@ -9,15 +9,13 @@ ##################################################################### */ /*}}}*/ // Include Files /*{{{*/ -#ifdef __GNUG__ -#pragma implementation "apt-pkg/debsrcrecords.h" -#endif - #include <apt-pkg/deblistparser.h> #include <apt-pkg/debsrcrecords.h> #include <apt-pkg/error.h> #include <apt-pkg/strutl.h> #include <apt-pkg/configuration.h> + +using std::max; /*}}}*/ // SrcRecordParser::Binaries - Return the binaries field /*{{{*/ @@ -34,31 +32,19 @@ const char **debSrcRecordParser::Binaries() if (Bins.empty() == true || Bins.length() >= 102400) return 0; - // Workaround for #236688. Only allocate a new buffer if the field - // is large, to avoid a performance penalty - char *BigBuf = NULL; - char *Buf; - if (Bins.length() > sizeof(Buffer)) - { - BigBuf = new char[Bins.length()]; - Buf = BigBuf; - } - else + if (Bins.length() >= BufSize) { - Buf = Buffer; + delete [] Buffer; + // allocate new size based on buffer (but never smaller than 4000) + BufSize = max((unsigned int)4000, max((unsigned int)Bins.length()+1,2*BufSize)); + Buffer = new char[BufSize]; } - strcpy(Buf,Bins.c_str()); - if (TokSplitString(',',Buf,StaticBinList, + strcpy(Buffer,Bins.c_str()); + if (TokSplitString(',',Buffer,StaticBinList, sizeof(StaticBinList)/sizeof(StaticBinList[0])) == false) - { - if (BigBuf != NULL) - delete BigBuf; return 0; - } - if (BigBuf != NULL) - delete BigBuf; return (const char **)StaticBinList; } /*}}}*/ @@ -151,7 +137,7 @@ bool debSrcRecordParser::Files(vector<pkgSrcRecords::File> &List) break; F.Type = string(F.Path,Tmp+1,Pos-Tmp); - if (F.Type == "gz" || F.Type == "bz2") + if (F.Type == "gz" || F.Type == "bz2" || F.Type == "lzma") { Pos = Tmp-1; continue; diff --git a/apt-pkg/deb/debsrcrecords.h b/apt-pkg/deb/debsrcrecords.h index f899993df..8b1237ccd 100644 --- a/apt-pkg/deb/debsrcrecords.h +++ b/apt-pkg/deb/debsrcrecords.h @@ -11,9 +11,6 @@ #ifndef PKGLIB_DEBSRCRECORDS_H #define PKGLIB_DEBSRCRECORDS_H -#ifdef __GNUG__ -#pragma interface "apt-pkg/debsrcrecords.h" -#endif #include <apt-pkg/srcrecords.h> #include <apt-pkg/tagfile.h> @@ -24,9 +21,10 @@ class debSrcRecordParser : public pkgSrcRecords::Parser FileFd Fd; pkgTagFile Tags; pkgTagSection Sect; - char Buffer[10000]; char *StaticBinList[400]; unsigned long iOffset; + char *Buffer; + unsigned int BufSize; public: @@ -49,10 +47,9 @@ class debSrcRecordParser : public pkgSrcRecords::Parser }; virtual bool Files(vector<pkgSrcRecords::File> &F); - debSrcRecordParser(string File,pkgIndexFile const *Index) : - Parser(Index), - Fd(File,FileFd::ReadOnly), - Tags(&Fd,102400) {}; + debSrcRecordParser(string File,pkgIndexFile const *Index) + : Parser(Index), Fd(File,FileFd::ReadOnly), Tags(&Fd,102400), + Buffer(0), BufSize(0) {} }; #endif diff --git a/apt-pkg/deb/debsystem.cc b/apt-pkg/deb/debsystem.cc index 2d805ea6f..11a84f1c6 100644 --- a/apt-pkg/deb/debsystem.cc +++ b/apt-pkg/deb/debsystem.cc @@ -10,10 +10,6 @@ ##################################################################### */ /*}}}*/ // Include Files /*{{{*/ -#ifdef __GNUG__ -#pragma implementation "apt-pkg/debsystem.h" -#endif - #include <apt-pkg/debsystem.h> #include <apt-pkg/debversion.h> #include <apt-pkg/debindexfile.h> diff --git a/apt-pkg/deb/debsystem.h b/apt-pkg/deb/debsystem.h index 84e57e74a..5f9995e5d 100644 --- a/apt-pkg/deb/debsystem.h +++ b/apt-pkg/deb/debsystem.h @@ -10,10 +10,6 @@ #ifndef PKGLIB_DEBSYSTEM_H #define PKGLIB_DEBSYSTEM_H -#ifdef __GNUG__ -#pragma interface "apt-pkg/debsystem.h" -#endif - #include <apt-pkg/pkgsystem.h> class debStatusIndex; diff --git a/apt-pkg/deb/debversion.cc b/apt-pkg/deb/debversion.cc index 064d8fa5b..ad45e9a44 100644 --- a/apt-pkg/deb/debversion.cc +++ b/apt-pkg/deb/debversion.cc @@ -11,9 +11,6 @@ /*}}}*/ // Include Files /*{{{*/ #define APT_COMPATIBILITY 986 -#ifdef __GNUG__ -#pragma implementation "apt-pkg/debversion.h" -#endif #include <apt-pkg/debversion.h> #include <apt-pkg/pkgcache.h> diff --git a/apt-pkg/deb/debversion.h b/apt-pkg/deb/debversion.h index 00a8832a8..56fb67887 100644 --- a/apt-pkg/deb/debversion.h +++ b/apt-pkg/deb/debversion.h @@ -12,9 +12,7 @@ #ifndef PKGLIB_DEBVERSION_H #define PKGLIB_DEBVERSION_H -#ifdef __GNUG__ -#pragma interface "apt-pkg/debversion.h" -#endif + #include <apt-pkg/version.h> diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index c7a6b921f..7a6b222f6 100644 --- a/apt-pkg/deb/dpkgpm.cc +++ b/apt-pkg/deb/dpkgpm.cc @@ -8,18 +8,18 @@ ##################################################################### */ /*}}}*/ // Includes /*{{{*/ -#ifdef __GNUG__ -#pragma implementation "apt-pkg/dpkgpm.h" -#endif #include <apt-pkg/dpkgpm.h> #include <apt-pkg/error.h> #include <apt-pkg/configuration.h> #include <apt-pkg/depcache.h> +#include <apt-pkg/pkgrecords.h> #include <apt-pkg/strutl.h> +#include <apti18n.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> +#include <sys/select.h> #include <sys/types.h> #include <sys/wait.h> #include <signal.h> @@ -28,16 +28,25 @@ #include <sstream> #include <map> +#include <termios.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <pty.h> + #include <config.h> #include <apti18n.h> /*}}}*/ using namespace std; + + // DPkgPM::pkgDPkgPM - Constructor /*{{{*/ // --------------------------------------------------------------------- /* */ -pkgDPkgPM::pkgDPkgPM(pkgDepCache *Cache) : pkgPackageManager(Cache) +pkgDPkgPM::pkgDPkgPM(pkgDepCache *Cache) + : pkgPackageManager(Cache), dpkgbuf_pos(0), PackagesDone(0), + PackagesTotal(0), term_out(NULL) { } /*}}}*/ @@ -326,7 +335,218 @@ bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf) return true; } + + /*}}}*/ +// DPkgPM::DoStdin - Read stdin and pass to slave pty /*{{{*/ +// --------------------------------------------------------------------- +/* +*/ +void pkgDPkgPM::DoStdin(int master) +{ + char input_buf[256] = {0,}; + int len = read(0, input_buf, sizeof(input_buf)); + write(master, input_buf, len); +} + /*}}}*/ +// DPkgPM::DoTerminalPty - Read the terminal pty and write log /*{{{*/ +// --------------------------------------------------------------------- +/* + * read the terminal pty and write log + */ +void pkgDPkgPM::DoTerminalPty(int master) +{ + char term_buf[1024] = {0,}; + + int len=read(master, term_buf, sizeof(term_buf)); + if(len == -1 && errno == EIO) + { + // this happens when the child is about to exit, we + // give it time to actually exit, otherwise we run + // into a race + usleep(500000); + return; + } + if(len <= 0) + return; + write(1, term_buf, len); + if(term_out) + fwrite(term_buf, len, sizeof(char), term_out); +} + /*}}}*/ +// DPkgPM::ProcessDpkgStatusBuf /*{{{*/ +// --------------------------------------------------------------------- +/* + */ +void pkgDPkgPM::ProcessDpkgStatusLine(int OutStatusFd, char *line) +{ + // the status we output + ostringstream status; + + if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true) + std::clog << "got from dpkg '" << line << "'" << std::endl; + + + /* dpkg sends strings like this: + 'status: <pkg>: <pkg qstate>' + errors look like this: + '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 + and conffile-prompt like this + 'status: conffile-prompt: conffile : 'current-conffile' 'new-conffile' useredited distedited + + */ + char* list[5]; + // dpkg sends multiline error messages sometimes (see + // #374195 for a example. we should support this by + // either patching dpkg to not send multiline over the + // statusfd or by rewriting the code here to deal with + // it. for now we just ignore it and not crash + TokSplitString(':', line, list, sizeof(list)/sizeof(list[0])); + if( list[0] == NULL || list[1] == NULL || list[2] == NULL) + { + if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true) + std::clog << "ignoring line: not enough ':'" << std::endl; + return; + } + char *pkg = list[1]; + char *action = _strstrip(list[2]); + + if(strncmp(action,"error",strlen("error")) == 0) + { + status << "pmerror:" << list[1] + << ":" << (PackagesDone/float(PackagesTotal)*100.0) + << ":" << list[3] + << endl; + if(OutStatusFd > 0) + write(OutStatusFd, status.str().c_str(), status.str().size()); + if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true) + std::clog << "send: '" << status.str() << "'" << endl; + pkgFailures++; + WriteApportReport(list[1], list[3]); + return; + } + if(strncmp(action,"conffile",strlen("conffile")) == 0) + { + status << "pmconffile:" << list[1] + << ":" << (PackagesDone/float(PackagesTotal)*100.0) + << ":" << list[3] + << endl; + if(OutStatusFd > 0) + write(OutStatusFd, status.str().c_str(), status.str().size()); + if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true) + std::clog << "send: '" << status.str() << "'" << endl; + return; + } + + vector<struct DpkgState> &states = PackageOps[pkg]; + const char *next_action = NULL; + if(PackageOpsDone[pkg] < states.size()) + next_action = states[PackageOpsDone[pkg]].state; + // check if the package moved to the next dpkg state + if(next_action && (strcmp(action, next_action) == 0)) + { + // only read the translation if there is actually a next + // action + const char *translation = _(states[PackageOpsDone[pkg]].str); + char s[200]; + snprintf(s, sizeof(s), translation, pkg); + + // we moved from one dpkg state to a new one, report that + PackageOpsDone[pkg]++; + PackagesDone++; + // build the status str + status << "pmstatus:" << pkg + << ":" << (PackagesDone/float(PackagesTotal)*100.0) + << ":" << s + << endl; + if(OutStatusFd > 0) + write(OutStatusFd, status.str().c_str(), status.str().size()); + if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true) + std::clog << "send: '" << status.str() << "'" << endl; + } + if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true) + std::clog << "(parsed from dpkg) pkg: " << pkg + << " action: " << action << endl; +} + +// DPkgPM::DoDpkgStatusFd /*{{{*/ +// --------------------------------------------------------------------- +/* + */ +void pkgDPkgPM::DoDpkgStatusFd(int statusfd, int OutStatusFd) +{ + char *p, *q; + int len; + + len=read(statusfd, &dpkgbuf[dpkgbuf_pos], sizeof(dpkgbuf)-dpkgbuf_pos); + dpkgbuf_pos += len; + if(len <= 0) + return; + + // process line by line if we have a buffer + p = q = dpkgbuf; + while((q=(char*)memchr(p, '\n', dpkgbuf+dpkgbuf_pos-p)) != NULL) + { + *q = 0; + ProcessDpkgStatusLine(OutStatusFd, p); + p=q+1; // continue with next line + } + + // now move the unprocessed bits (after the final \n that is now a 0x0) + // to the start and update dpkgbuf_pos + p = (char*)memrchr(dpkgbuf, 0, dpkgbuf_pos); + if(p == NULL) + return; + + // we are interessted in the first char *after* 0x0 + p++; + + // move the unprocessed tail to the start and update pos + memmove(dpkgbuf, p, p-dpkgbuf); + dpkgbuf_pos = dpkgbuf+dpkgbuf_pos-p; +} /*}}}*/ + +bool pkgDPkgPM::OpenLog() +{ + string logdir = _config->FindDir("Dir::Log"); + if(not FileExists(logdir)) + return _error->Error(_("Directory '%s' missing"), logdir.c_str()); + string logfile_name = flCombine(logdir, + _config->Find("Dir::Log::Terminal")); + if (!logfile_name.empty()) + { + term_out = fopen(logfile_name.c_str(),"a"); + chmod(logfile_name.c_str(), 0600); + // output current time + char outstr[200]; + time_t t = time(NULL); + struct tm *tmp = localtime(&t); + strftime(outstr, sizeof(outstr), "%F %T", tmp); + fprintf(term_out, "\nLog started: "); + fprintf(term_out, outstr); + fprintf(term_out, "\n"); + } + return true; +} + +bool pkgDPkgPM::CloseLog() +{ + if(term_out) + { + char outstr[200]; + time_t t = time(NULL); + struct tm *tmp = localtime(&t); + strftime(outstr, sizeof(outstr), "%F %T", tmp); + fprintf(term_out, "Log ended: "); + fprintf(term_out, outstr); + fprintf(term_out, "\n"); + fclose(term_out); + } + term_out = NULL; + return true; +} + + // DPkgPM::Go - Run the sequence /*{{{*/ // --------------------------------------------------------------------- /* This globs the operations and calls dpkg @@ -347,12 +567,9 @@ bool pkgDPkgPM::Go(int OutStatusFd) if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false) return false; - // prepare the progress reporting - int Done = 0; - int Total = 0; // map the dpkg states to the operations that are performed // (this is sorted in the same way as Item::Ops) - static const struct DpkgState DpkgStatesOpMap[][5] = { + static const struct DpkgState DpkgStatesOpMap[][7] = { // Install operation { {"half-installed", N_("Preparing %s")}, @@ -363,12 +580,20 @@ bool pkgDPkgPM::Go(int OutStatusFd) { {"unpacked",N_("Preparing to configure %s") }, {"half-configured", N_("Configuring %s") }, +#if 0 + {"triggers-awaited", N_("Processing triggers for %s") }, + {"triggers-pending", N_("Processing triggers for %s") }, +#endif { "installed", N_("Installed %s")}, {NULL, NULL} }, // Remove operation { {"half-configured", N_("Preparing for removal of %s")}, +#if 0 + {"triggers-awaited", N_("Preparing for removal of %s")}, + {"triggers-pending", N_("Preparing for removal of %s")}, +#endif {"half-installed", N_("Removing %s")}, {"config-files", N_("Removed %s")}, {NULL, NULL} @@ -381,15 +606,6 @@ bool pkgDPkgPM::Go(int OutStatusFd) }, }; - // the dpkg states that the pkg will run through, the string is - // the package, the vector contains the dpkg states that the package - // will go through - map<string,vector<struct DpkgState> > PackageOps; - // the dpkg states that are already done; the string is the package - // the int is the state that is already done (e.g. a package that is - // going to be install is already in state "half-installed") - map<string,int> PackageOpsDone; - // init the PackageOps map, go over the list of packages that // that will be [installed|configured|removed|purged] and add // them to the PackageOps map (the dpkg states it goes through) @@ -401,10 +617,13 @@ bool pkgDPkgPM::Go(int OutStatusFd) for(int i=0; (DpkgStatesOpMap[(*I).Op][i]).state != NULL; i++) { PackageOps[name].push_back(DpkgStatesOpMap[(*I).Op][i]); - Total++; + PackagesTotal++; } } + // create log + OpenLog(); + // this loop is runs once per operation for (vector<Item>::iterator I = List.begin(); I != List.end();) { @@ -474,6 +693,8 @@ bool pkgDPkgPM::Go(int OutStatusFd) case Item::Install: Args[n++] = "--unpack"; Size += strlen(Args[n-1]); + Args[n++] = "--auto-deconfigure"; + Size += strlen(Args[n-1]); break; } @@ -517,7 +738,30 @@ bool pkgDPkgPM::Go(int OutStatusFd) it doesn't die but we do! So we must also ignore it */ sighandler_t old_SIGQUIT = signal(SIGQUIT,SIG_IGN); sighandler_t old_SIGINT = signal(SIGINT,SIG_IGN); - + + struct termios tt; + struct winsize win; + int master; + int slave; + + // FIXME: setup sensible signal handling (*ick*) + tcgetattr(0, &tt); + ioctl(0, TIOCGWINSZ, (char *)&win); + if (openpty(&master, &slave, NULL, &tt, &win) < 0) + { + const char *s = _("Can not write log, openpty() " + "failed (/dev/pts not mounted?)\n"); + fprintf(stderr, "%s",s); + fprintf(term_out, "%s",s); + master = slave = -1; + } else { + struct termios rtt; + rtt = tt; + cfmakeraw(&rtt); + rtt.c_lflag &= ~ECHO; + tcsetattr(0, TCSAFLUSH, &rtt); + } + // Fork dpkg pid_t Child; _config->Set("APT::Keep-Fds::",fd[1]); @@ -526,8 +770,18 @@ bool pkgDPkgPM::Go(int OutStatusFd) // This is the child if (Child == 0) { + if(slave >= 0 && master >= 0) + { + setsid(); + ioctl(slave, TIOCSCTTY, 0); + close(master); + dup2(slave, 0); + dup2(slave, 1); + dup2(slave, 2); + close(slave); + } close(fd[0]); // close the read end of the pipe - + if (chdir(_config->FindDir("DPkg::Run-Directory","/").c_str()) != 0) _exit(100); @@ -546,7 +800,8 @@ bool pkgDPkgPM::Go(int OutStatusFd) if (fcntl(STDIN_FILENO,F_SETFL,Flags & (~(long)O_NONBLOCK)) < 0) _exit(100); } - + + /* No Job Control Stop Env is a magic dpkg var that prevents it from using sigstop */ putenv("DPKG_NO_TSTP=yes"); @@ -563,16 +818,22 @@ bool pkgDPkgPM::Go(int OutStatusFd) // we read from dpkg here int _dpkgin = fd[0]; - fcntl(_dpkgin, F_SETFL, O_NONBLOCK); close(fd[1]); // close the write end of the pipe - // the read buffers for the communication with dpkg - char line[1024] = {0,}; - char buf[2] = {0,0}; - // the result of the waitpid call int res; + if(slave > 0) + close(slave); + + // setups fds + fd_set rfds; + struct timespec tv; + sigset_t sigmask; + sigset_t original_sigmask; + sigemptyset(&sigmask); + sigprocmask(SIG_BLOCK,&sigmask,&original_sigmask); + int select_ret; while ((res=waitpid(Child,&Status, WNOHANG)) != Child) { if(res < 0) { // FIXME: move this to a function or something, looks ugly here @@ -586,141 +847,72 @@ bool pkgDPkgPM::Go(int OutStatusFd) signal(SIGINT,old_SIGINT); return _error->Errno("waitpid","Couldn't wait for subprocess"); } - - // read a single char, make sure that the read can't block - // (otherwise we may leave zombies) - int len = read(_dpkgin, buf, 1); - // nothing to read, wait a bit for more - if(len <= 0) - { - usleep(1000); - continue; - } + // wait for input or output here + FD_ZERO(&rfds); + FD_SET(0, &rfds); + FD_SET(_dpkgin, &rfds); + if(master >= 0) + FD_SET(master, &rfds); + tv.tv_sec = 1; + tv.tv_nsec = 0; + select_ret = pselect(max(master, _dpkgin)+1, &rfds, NULL, NULL, + &tv, &original_sigmask); + if (select_ret == 0) + continue; + else if (select_ret < 0 && errno == EINTR) + continue; + else if (select_ret < 0) + { + perror("select() returned error"); + continue; + } - // sanity check (should never happen) - if(strlen(line) >= sizeof(line)-10) - { - _error->Error("got a overlong line from dpkg: '%s'",line); - line[0]=0; - } - // append to line, check if we got a complete line - strcat(line, buf); - if(buf[0] != '\n') - continue; - - if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true) - std::clog << "got from dpkg '" << line << "'" << std::endl; - - // the status we output - ostringstream status; - - /* dpkg sends strings like this: - 'status: <pkg>: <pkg qstate>' - errors look like this: - '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 - and conffile-prompt like this - 'status: conffile-prompt: conffile : 'current-conffile' 'new-conffile' useredited distedited - - */ - char* list[5]; - // dpkg sends multiline error messages sometimes (see - // #374195 for a example. we should support this by - // either patching dpkg to not send multiline over the - // statusfd or by rewriting the code here to deal with - // it. for now we just ignore it and not crash - TokSplitString(':', line, list, sizeof(list)/sizeof(list[0])); - char *pkg = list[1]; - char *action = _strstrip(list[2]); - if( pkg == NULL || action == NULL) - { - if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true) - std::clog << "ignoring line: not enough ':'" << std::endl; - // reset the line buffer - line[0]=0; - continue; - } - - if(strncmp(action,"error",strlen("error")) == 0) - { - status << "pmerror:" << list[1] - << ":" << (Done/float(Total)*100.0) - << ":" << list[3] - << endl; - if(OutStatusFd > 0) - write(OutStatusFd, status.str().c_str(), status.str().size()); - line[0]=0; - if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true) - std::clog << "send: '" << status.str() << "'" << endl; - continue; - } - if(strncmp(action,"conffile",strlen("conffile")) == 0) - { - status << "pmconffile:" << list[1] - << ":" << (Done/float(Total)*100.0) - << ":" << list[3] - << endl; - if(OutStatusFd > 0) - write(OutStatusFd, status.str().c_str(), status.str().size()); - line[0]=0; - if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true) - std::clog << "send: '" << status.str() << "'" << endl; - continue; - } - - vector<struct DpkgState> &states = PackageOps[pkg]; - const char *next_action = NULL; - if(PackageOpsDone[pkg] < states.size()) - next_action = states[PackageOpsDone[pkg]].state; - // check if the package moved to the next dpkg state - if(next_action && (strcmp(action, next_action) == 0)) - { - // only read the translation if there is actually a next - // action - const char *translation = _(states[PackageOpsDone[pkg]].str); - char s[200]; - snprintf(s, sizeof(s), translation, pkg); - - // we moved from one dpkg state to a new one, report that - PackageOpsDone[pkg]++; - Done++; - // build the status str - status << "pmstatus:" << pkg - << ":" << (Done/float(Total)*100.0) - << ":" << s - << endl; - if(OutStatusFd > 0) - write(OutStatusFd, status.str().c_str(), status.str().size()); - if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true) - std::clog << "send: '" << status.str() << "'" << endl; - - } - if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true) - std::clog << "(parsed from dpkg) pkg: " << pkg - << " action: " << action << endl; - - // reset the line buffer - line[0]=0; + if(master >= 0 && FD_ISSET(master, &rfds)) + DoTerminalPty(master); + if(master >= 0 && FD_ISSET(0, &rfds)) + DoStdin(master); + if(FD_ISSET(_dpkgin, &rfds)) + DoDpkgStatusFd(_dpkgin, OutStatusFd); } close(_dpkgin); // Restore sig int/quit signal(SIGQUIT,old_SIGQUIT); signal(SIGINT,old_SIGINT); + + if(master >= 0) + { + tcsetattr(0, TCSAFLUSH, &tt); + close(master); + } // Check for an error code. if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0) { - RunScripts("DPkg::Post-Invoke"); - if (WIFSIGNALED(Status) != 0 && WTERMSIG(Status) == SIGSEGV) - return _error->Error("Sub-process %s received a segmentation fault.",Args[0]); - - if (WIFEXITED(Status) != 0) - return _error->Error("Sub-process %s returned an error code (%u)",Args[0],WEXITSTATUS(Status)); + // if it was set to "keep-dpkg-runing" then we won't return + // here but keep the loop going and just report it as a error + // for later + bool stopOnError = _config->FindB("Dpkg::StopOnError",true); - return _error->Error("Sub-process %s exited unexpectedly",Args[0]); + if(stopOnError) + RunScripts("DPkg::Post-Invoke"); + + if (WIFSIGNALED(Status) != 0 && WTERMSIG(Status) == SIGSEGV) + _error->Error("Sub-process %s received a segmentation fault.",Args[0]); + else if (WIFEXITED(Status) != 0) + _error->Error("Sub-process %s returned an error code (%u)",Args[0],WEXITSTATUS(Status)); + else + _error->Error("Sub-process %s exited unexpectedly",Args[0]); + + if(stopOnError) + { + CloseLog(); + return false; + } } } + CloseLog(); if (RunScripts("DPkg::Post-Invoke") == false) return false; @@ -735,3 +927,115 @@ void pkgDPkgPM::Reset() List.erase(List.begin(),List.end()); } /*}}}*/ +// pkgDpkgPM::WriteApportReport - write out error report pkg failure /*{{{*/ +// --------------------------------------------------------------------- +/* */ +void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg) +{ + string pkgname, reportfile, srcpkgname, pkgver, arch; + string::size_type pos; + FILE *report; + + if (_config->FindB("Dpkg::ApportFailureReport",true) == false) + return; + + // only report the first error if we are in StopOnError=false mode + // to prevent bogus reports + if((_config->FindB("Dpkg::StopOnError",true) == false) && pkgFailures > 1) + return; + + // get the pkgname and reportfile + pkgname = flNotDir(pkgpath); + pos = pkgname.find('_'); + if(pos != string::npos) + pkgname = pkgname.substr(0, pos); + + // find the package versin and source package name + pkgCache::PkgIterator Pkg = Cache.FindPkg(pkgname); + if (Pkg.end() == true) + return; + pkgCache::VerIterator Ver = Cache.GetCandidateVer(Pkg); + if (Ver.end() == true) + return; + pkgver = Ver.VerStr() == NULL ? "unknown" : Ver.VerStr(); + pkgRecords Recs(Cache); + pkgRecords::Parser &Parse = Recs.Lookup(Ver.FileList()); + srcpkgname = Parse.SourcePkg(); + if(srcpkgname.empty()) + srcpkgname = pkgname; + + // if the file exists already, we check: + // - if it was reported already (touched by apport). + // If not, we do nothing, otherwise + // we overwrite it. This is the same behaviour as apport + // - if we have a report with the same pkgversion already + // then we skip it + reportfile = flCombine("/var/crash",pkgname+".0.crash"); + if(FileExists(reportfile)) + { + struct stat buf; + char strbuf[255]; + + // check atime/mtime + stat(reportfile.c_str(), &buf); + if(buf.st_mtime > buf.st_atime) + return; + + // check if the existing report is the same version + report = fopen(reportfile.c_str(),"r"); + while(fgets(strbuf, sizeof(strbuf), report) != NULL) + { + if(strstr(strbuf,"Package:") == strbuf) + { + char pkgname[255], version[255]; + if(sscanf(strbuf, "Package: %s %s", pkgname, version) == 2) + if(strcmp(pkgver.c_str(), version) == 0) + { + fclose(report); + return; + } + } + } + fclose(report); + } + + // now write the report + arch = _config->Find("APT::Architecture"); + report = fopen(reportfile.c_str(),"w"); + if(report == NULL) + return; + if(_config->FindB("DPkgPM::InitialReportOnly",false) == true) + chmod(reportfile.c_str(), 0); + else + chmod(reportfile.c_str(), 0600); + fprintf(report, "ProblemType: Package\n"); + fprintf(report, "Architecture: %s\n", arch.c_str()); + time_t now = time(NULL); + fprintf(report, "Date: %s" , ctime(&now)); + fprintf(report, "Package: %s %s\n", pkgname.c_str(), pkgver.c_str()); + fprintf(report, "SourcePackage: %s\n", srcpkgname.c_str()); + fprintf(report, "ErrorMessage:\n %s\n", errormsg); + + // ensure that the log is flushed + if(term_out) + fflush(term_out); + + // attach terminal log it if we have it + string logfile_name = _config->FindFile("Dir::Log::Terminal"); + if (!logfile_name.empty()) + { + FILE *log = NULL; + char buf[1024]; + + fprintf(report, "DpkgTerminalLog:\n"); + log = fopen(logfile_name.c_str(),"r"); + if(log != NULL) + { + while( fgets(buf, sizeof(buf), log) != NULL) + fprintf(report, " %s", buf); + fclose(log); + } + } + fclose(report); +} + /*}}}*/ diff --git a/apt-pkg/deb/dpkgpm.h b/apt-pkg/deb/dpkgpm.h index 2ff8a9ac7..e5f197405 100644 --- a/apt-pkg/deb/dpkgpm.h +++ b/apt-pkg/deb/dpkgpm.h @@ -10,27 +10,46 @@ #ifndef PKGLIB_DPKGPM_H #define PKGLIB_DPKGPM_H -#ifdef __GNUG__ -#pragma interface "apt-pkg/dpkgpm.h" -#endif - #include <apt-pkg/packagemanager.h> #include <vector> +#include <map> #include <stdio.h> using std::vector; +using std::map; + class pkgDPkgPM : public pkgPackageManager { + private: + + // the buffer we use for the dpkg status-fd reading + char dpkgbuf[1024]; + int dpkgbuf_pos; + FILE *term_out; + protected: + int pkgFailures; - // used for progress reporting + // progress reporting struct DpkgState { const char *state; // the dpkg state (e.g. "unpack") const char *str; // the human readable translation of the state }; - + + // the dpkg states that the pkg will run through, the string is + // the package, the vector contains the dpkg states that the package + // will go through + map<string,vector<struct DpkgState> > PackageOps; + // the dpkg states that are already done; the string is the package + // the int is the state that is already done (e.g. a package that is + // going to be install is already in state "half-installed") + map<string,unsigned int> PackageOpsDone; + // progress reporting + unsigned int PackagesDone; + unsigned int PackagesTotal; + struct Item { enum Ops {Install, Configure, Remove, Purge} Op; @@ -47,7 +66,20 @@ class pkgDPkgPM : public pkgPackageManager bool RunScripts(const char *Cnf); bool RunScriptsWithPkgs(const char *Cnf); bool SendV2Pkgs(FILE *F); + + // apport integration + void WriteApportReport(const char *pkgpath, const char *errormsg); + + // dpkg log + bool OpenLog(); + bool CloseLog(); + // input processing + void DoStdin(int master); + void DoTerminalPty(int master); + void DoDpkgStatusFd(int statusfd, int OutStatusFd); + void ProcessDpkgStatusLine(int OutStatusFd, char *line); + // The Actuall installation implementation virtual bool Install(PkgIterator Pkg,string File); virtual bool Configure(PkgIterator Pkg); |