// Includes /*{{{*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; /*}}}*/ /** \brief DebdeltaMethod - TODO: say something about debdelta here! * */ class DebdeltaMethod : public pkgAcqMethod { bool Debug; string DebdeltaFile; string FromFile; string ToFile; string DebpatchOutput; protected: // the main(i.e. most important) method of the debdelta method. virtual bool Fetch(FetchItem *Itm); public: DebdeltaMethod() : pkgAcqMethod("1.1", SingleInstance | SendConfig) {}; void MakeToFile(); }; bool DebdeltaMethod::Fetch(FetchItem *Itm) /*{{{*/ { /// Testing only... //FetchResult ResTest; //ResTest.Filename = "/home/ishan/devel/apt/testrepo/binary/gcc-4.6-base_4.6.0-7_amd64.deb"; //URIDone(ResTest); //return true; /// Debug = _config->FindB("Debug::pkgAcquire::Debdelta", false); FromFile = Itm->DestFile; URI U(Itm->Uri); DebdeltaFile = U.Path; if (flExtension(FromFile) != "deb" || !FileExists(FromFile)) FromFile = "/"; if (!FileExists(DebdeltaFile)) return _error->Error("\n[Debdelta] Could not find a debdelta file."); MakeToFile(); if (FileExists(ToFile)) return _error->Error("\n[Debdelta] New .deb already exists."); if (Debug == true) { std::cerr << "\n[Debdelta] FromFile: " << FromFile << "\n ToFile: " << ToFile << "\n DebdelatFile: " << DebdeltaFile << std::endl; } int Fd[2]; if (pipe(Fd) != 0) return _error->Error("[Debdelta] Could not create the pipe."); _config->Set("APT::Keep-Fds", Fd[0]); _config->Set("APT::Keep-Fds", Fd[1]); pid_t Process = ExecFork(); _config->Clear("APT::Keep-Fds", Fd[0]); _config->Clear("APT::Keep-Fds", Fd[1]); if (Process == 0) { // redirect debpatch's stdout,stderr to the pipe close(Fd[0]); close(1); dup(Fd[1]); close(2); dup(Fd[1]); // make the debpatch command and run it. int n = 0; const char* Args[6] = {0}; Args[n++] = "/usr/bin/debpatch"; if (!FileExists(Args[0])) return _error->Error("[Debdelta] Could not find debpatch."); Args[n++] = "-A"; Args[n++] = DebdeltaFile.c_str(); Args[n++] = FromFile.c_str(); Args[n++] = ToFile.c_str(); if (Debug == true) { std::cerr << "\n[Debdelta] Command:" << std::endl; std::cerr << Args[0] << " " << Args[1] << " " << Args[2] << " " << Args[3] << " " << Args[4] << std::endl; } std::cerr << "\n\n[Debdelta] Patching " << ToFile << "..." << std::endl; execv(Args[0], (char **)Args); return _error->Error("[Debdelta] Could not execv debpatch."); } if (ExecWait(Process, "debpatch", false)) { // read the stderr,stdout outputs of debpatch size_t LineSize = 1024; char *Line = (char *)malloc(LineSize + 1); close(Fd[1]); FILE *fp = fdopen(Fd[0], "r"); DebpatchOutput = ""; while (getline(&Line, &LineSize, fp) != EOF) DebpatchOutput += string(Line); fclose(fp); if (!FileExists(ToFile)) return _error->Error("\n[Debdelta] Failed to patch %s", ToFile.c_str()); // move the .deb to Dir::Cache::Archives string FinalFile = _config->FindDir("Dir::Cache::Archives") + flNotDir(ToFile); Rename(ToFile, FinalFile); ToFile = FinalFile; FetchResult Res; Res.Filename = ToFile; if (Queue != 0) URIDone(Res); else std::cout << "Filename: " << Res.Filename << std::endl; return true; } return false; } void DebdeltaMethod::MakeToFile() { string DebdeltaName = flNotDir(DebdeltaFile); int NewBegin = DebdeltaName.find("_", 0); string PkgName = DebdeltaName.substr(0, NewBegin); NewBegin = DebdeltaName.find("_", NewBegin + 1); int NewEnd = DebdeltaName.find("_", NewBegin + 1); string NewVersion = DebdeltaName.substr(NewBegin + 1, NewEnd - NewBegin - 1); string Arch = DebdeltaName.substr(NewEnd + 1, DebdeltaName.find(".", NewEnd + 1) - NewEnd - 1); ToFile = _config->FindDir("Dir::Cache::Archives") + "partial/" + PkgName + "_" + NewVersion + "_" + Arch + ".deb"; } /*}}}*/ /** \brief Wrapper class for testing debdelta */ /*{{{*/ class TestDebdeltaMethod : public DebdeltaMethod { public: /** \brief Run debdelta in debug test mode * * This method can be used to run the debdelta method outside * of the "normal" acquire environment for easier testing. * * \param base basename of all files involved in this debdelta test */ bool Run(char const *DebdeltaFile, char const *FromFile) { if (pkgInitConfig(*_config) == false || pkgInitSystem(*_config,_system) == false) { std::cerr << "[Debdelta] E: Could not initialize the system/configuration." << std::endl; _error->DumpErrors(); return 100; } _config->CndSet("Debug::pkgAcquire::Debdetla", "true"); FetchItem *Test = new FetchItem; Test->DestFile = FromFile; Test->Uri = "debdelta://" + string(DebdeltaFile); Test->FailIgnore = false; Test->IndexFile = false; Test->Next = 0; return Fetch(Test); } }; /*}}}*/ /** \brief Starter for the debdelta method (or its test method) {{{ * * Used without parameters is the normal behavior for methods for * the APT acquire system. While this works great for the acquire system * it is very hard to test the method and therefore the method also * accepts one parameter which will switch it directly to debug test mode: */ int main(int argc, char *argv[]) { if (argc <= 1) { DebdeltaMethod Mth; return Mth.Run(); } else { TestDebdeltaMethod Mth; bool result = Mth.Run(argv[1], argv[2]); _error->DumpErrors(); return result; } } /*}}}*/