summaryrefslogtreecommitdiff
path: root/methods/debdelta.cc
blob: 37b62a5f5985668ec9652965329cb8ee055bc409 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
// Includes									/*{{{*/
#include <apt-pkg/acquire-item.h>
#include <apt-pkg/fileutl.h>
#include <apt-pkg/mmap.h>
#include <apt-pkg/error.h>
#include <apt-pkg/acquire-method.h>
#include <apt-pkg/strutl.h>
#include <apt-pkg/hashes.h>
#include <apt-pkg/init.h>

#include <sys/stat.h>
#include <sys/uio.h>
#include <sys/wait.h>
#include <unistd.h>
#include <utime.h>
#include <stdio.h>
#include <errno.h>
#include <zlib.h>
#include <apti18n.h>
#include <stdlib.h>

#include <iostream>
#include <fstream>
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 << "[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;
   }
}
/*}}}*/