]> git.saurik.com Git - apt.git/blob - apt-pkg/acquire-method.cc
* basic error reporting from apt in place now (ReportMirrorFailures())
[apt.git] / apt-pkg / acquire-method.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: acquire-method.cc,v 1.27.2.1 2003/12/24 23:09:17 mdz Exp $
4 /* ######################################################################
5
6 Acquire Method
7
8 This is a skeleton class that implements most of the functionality
9 of a method and some useful functions to make method implementation
10 simpler. The methods all derive this and specialize it. The most
11 complex implementation is the http method which needs to provide
12 pipelining, it runs the message engine at the same time it is
13 downloading files..
14
15 ##################################################################### */
16 /*}}}*/
17 // Include Files /*{{{*/
18 #ifdef __GNUG__
19 #pragma implementation "apt-pkg/acquire-method.h"
20 #endif
21 #include <apt-pkg/acquire-method.h>
22 #include <apt-pkg/error.h>
23 #include <apt-pkg/configuration.h>
24 #include <apt-pkg/strutl.h>
25 #include <apt-pkg/fileutl.h>
26 #include <apt-pkg/hashes.h>
27
28 #include <iostream>
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <unistd.h>
32 #include <sys/signal.h>
33 /*}}}*/
34
35 using namespace std;
36
37 // AcqMethod::pkgAcqMethod - Constructor /*{{{*/
38 // ---------------------------------------------------------------------
39 /* This constructs the initialization text */
40 pkgAcqMethod::pkgAcqMethod(const char *Ver,unsigned long Flags)
41 {
42 char S[300] = "";
43 char *End = S;
44 strcat(End,"100 Capabilities\n");
45 sprintf(End+strlen(End),"Version: %s\n",Ver);
46
47 if ((Flags & SingleInstance) == SingleInstance)
48 strcat(End,"Single-Instance: true\n");
49
50 if ((Flags & Pipeline) == Pipeline)
51 strcat(End,"Pipeline: true\n");
52
53 if ((Flags & SendConfig) == SendConfig)
54 strcat(End,"Send-Config: true\n");
55
56 if ((Flags & LocalOnly) == LocalOnly)
57 strcat(End,"Local-Only: true\n");
58
59 if ((Flags & NeedsCleanup) == NeedsCleanup)
60 strcat(End,"Needs-Cleanup: true\n");
61
62 if ((Flags & Removable) == Removable)
63 strcat(End,"Removable: true\n");
64 strcat(End,"\n");
65
66 if (write(STDOUT_FILENO,S,strlen(S)) != (signed)strlen(S))
67 exit(100);
68
69 SetNonBlock(STDIN_FILENO,true);
70
71 Queue = 0;
72 QueueBack = 0;
73 }
74 /*}}}*/
75 // AcqMethod::Fail - A fetch has failed /*{{{*/
76 // ---------------------------------------------------------------------
77 /* */
78 void pkgAcqMethod::Fail(bool Transient)
79 {
80 string Err = "Undetermined Error";
81 if (_error->empty() == false)
82 _error->PopMessage(Err);
83 _error->Discard();
84 Fail(Err,Transient);
85 }
86 /*}}}*/
87 // AcqMethod::Fail - A fetch has failed /*{{{*/
88 // ---------------------------------------------------------------------
89 /* */
90 void pkgAcqMethod::Fail(string Err,bool Transient)
91 {
92 // Strip out junk from the error messages
93 for (string::iterator I = Err.begin(); I != Err.end(); I++)
94 {
95 if (*I == '\r')
96 *I = ' ';
97 if (*I == '\n')
98 *I = ' ';
99 }
100
101 char S[1024];
102 if (Queue != 0)
103 {
104 snprintf(S,sizeof(S)-50,"400 URI Failure\nURI: %s\n"
105 "Message: %s %s\n",Queue->Uri.c_str(),Err.c_str(),
106 FailExtra.c_str());
107
108 // Dequeue
109 FetchItem *Tmp = Queue;
110 Queue = Queue->Next;
111 delete Tmp;
112 if (Tmp == QueueBack)
113 QueueBack = Queue;
114 }
115 else
116 snprintf(S,sizeof(S)-50,"400 URI Failure\nURI: <UNKNOWN>\n"
117 "Message: %s %s\n",Err.c_str(),
118 FailExtra.c_str());
119
120 // Set the transient flag
121 if (Transient == true)
122 strcat(S,"Transient-Failure: true\n\n");
123 else
124 strcat(S,"\n");
125
126 if (write(STDOUT_FILENO,S,strlen(S)) != (signed)strlen(S))
127 exit(100);
128 }
129 /*}}}*/
130 // AcqMethod::URIStart - Indicate a download is starting /*{{{*/
131 // ---------------------------------------------------------------------
132 /* */
133 void pkgAcqMethod::URIStart(FetchResult &Res)
134 {
135 if (Queue == 0)
136 abort();
137
138 char S[1024] = "";
139 char *End = S;
140
141 End += snprintf(S,sizeof(S),"200 URI Start\nURI: %s\n",Queue->Uri.c_str());
142 if (Res.Size != 0)
143 End += snprintf(End,sizeof(S)-4 - (End - S),"Size: %lu\n",Res.Size);
144
145 if (Res.LastModified != 0)
146 End += snprintf(End,sizeof(S)-4 - (End - S),"Last-Modified: %s\n",
147 TimeRFC1123(Res.LastModified).c_str());
148
149 if (Res.ResumePoint != 0)
150 End += snprintf(End,sizeof(S)-4 - (End - S),"Resume-Point: %lu\n",
151 Res.ResumePoint);
152
153 strcat(End,"\n");
154 if (write(STDOUT_FILENO,S,strlen(S)) != (signed)strlen(S))
155 exit(100);
156 }
157 /*}}}*/
158 // AcqMethod::URIDone - A URI is finished /*{{{*/
159 // ---------------------------------------------------------------------
160 /* */
161 void pkgAcqMethod::URIDone(FetchResult &Res, FetchResult *Alt)
162 {
163 if (Queue == 0)
164 abort();
165
166 char S[1024] = "";
167 char *End = S;
168
169 End += snprintf(S,sizeof(S),"201 URI Done\nURI: %s\n",Queue->Uri.c_str());
170
171 if (Res.Filename.empty() == false)
172 End += snprintf(End,sizeof(S)-50 - (End - S),"Filename: %s\n",Res.Filename.c_str());
173
174 if (Res.Size != 0)
175 End += snprintf(End,sizeof(S)-50 - (End - S),"Size: %lu\n",Res.Size);
176
177 if (Res.LastModified != 0)
178 End += snprintf(End,sizeof(S)-50 - (End - S),"Last-Modified: %s\n",
179 TimeRFC1123(Res.LastModified).c_str());
180
181 if (Res.MD5Sum.empty() == false)
182 End += snprintf(End,sizeof(S)-50 - (End - S),"MD5-Hash: %s\n",Res.MD5Sum.c_str());
183 if (Res.SHA1Sum.empty() == false)
184 End += snprintf(End,sizeof(S)-50 - (End - S),"SHA1-Hash: %s\n",Res.SHA1Sum.c_str());
185 if (Res.GPGVOutput.size() > 0)
186 End += snprintf(End,sizeof(S)-50 - (End - S),"GPGVOutput:\n");
187 for (vector<string>::iterator I = Res.GPGVOutput.begin();
188 I != Res.GPGVOutput.end(); I++)
189 End += snprintf(End,sizeof(S)-50 - (End - S), " %s\n", (*I).c_str());
190
191 if (Res.ResumePoint != 0)
192 End += snprintf(End,sizeof(S)-50 - (End - S),"Resume-Point: %lu\n",
193 Res.ResumePoint);
194
195 if (Res.IMSHit == true)
196 strcat(End,"IMS-Hit: true\n");
197 End = S + strlen(S);
198
199 if (Alt != 0)
200 {
201 if (Alt->Filename.empty() == false)
202 End += snprintf(End,sizeof(S)-50 - (End - S),"Alt-Filename: %s\n",Alt->Filename.c_str());
203
204 if (Alt->Size != 0)
205 End += snprintf(End,sizeof(S)-50 - (End - S),"Alt-Size: %lu\n",Alt->Size);
206
207 if (Alt->LastModified != 0)
208 End += snprintf(End,sizeof(S)-50 - (End - S),"Alt-Last-Modified: %s\n",
209 TimeRFC1123(Alt->LastModified).c_str());
210
211 if (Alt->MD5Sum.empty() == false)
212 End += snprintf(End,sizeof(S)-50 - (End - S),"Alt-MD5-Hash: %s\n",
213 Alt->MD5Sum.c_str());
214 if (Alt->SHA1Sum.empty() == false)
215 End += snprintf(End,sizeof(S)-50 - (End - S),"Alt-SHA1-Hash: %s\n",
216 Alt->SHA1Sum.c_str());
217
218 if (Alt->IMSHit == true)
219 strcat(End,"Alt-IMS-Hit: true\n");
220 }
221
222 strcat(End,"\n");
223 if (write(STDOUT_FILENO,S,strlen(S)) != (signed)strlen(S))
224 exit(100);
225
226 // Dequeue
227 FetchItem *Tmp = Queue;
228 Queue = Queue->Next;
229 delete Tmp;
230 if (Tmp == QueueBack)
231 QueueBack = Queue;
232 }
233 /*}}}*/
234 // AcqMethod::MediaFail - Syncronous request for new media /*{{{*/
235 // ---------------------------------------------------------------------
236 /* This sends a 403 Media Failure message to the APT and waits for it
237 to be ackd */
238 bool pkgAcqMethod::MediaFail(string Required,string Drive)
239 {
240 char S[1024];
241 snprintf(S,sizeof(S),"403 Media Failure\nMedia: %s\nDrive: %s\n\n",
242 Required.c_str(),Drive.c_str());
243
244 if (write(STDOUT_FILENO,S,strlen(S)) != (signed)strlen(S))
245 exit(100);
246
247 vector<string> MyMessages;
248
249 /* Here we read messages until we find a 603, each non 603 message is
250 appended to the main message list for later processing */
251 while (1)
252 {
253 if (WaitFd(STDIN_FILENO) == false)
254 return false;
255
256 if (ReadMessages(STDIN_FILENO,MyMessages) == false)
257 return false;
258
259 string Message = MyMessages.front();
260 MyMessages.erase(MyMessages.begin());
261
262 // Fetch the message number
263 char *End;
264 int Number = strtol(Message.c_str(),&End,10);
265 if (End == Message.c_str())
266 {
267 cerr << "Malformed message!" << endl;
268 exit(100);
269 }
270
271 // Change ack
272 if (Number == 603)
273 {
274 while (MyMessages.empty() == false)
275 {
276 Messages.push_back(MyMessages.front());
277 MyMessages.erase(MyMessages.begin());
278 }
279
280 return !StringToBool(LookupTag(Message,"Failed"),false);
281 }
282
283 Messages.push_back(Message);
284 }
285 }
286 /*}}}*/
287 // AcqMethod::Configuration - Handle the configuration message /*{{{*/
288 // ---------------------------------------------------------------------
289 /* This parses each configuration entry and puts it into the _config
290 Configuration class. */
291 bool pkgAcqMethod::Configuration(string Message)
292 {
293 ::Configuration &Cnf = *_config;
294
295 const char *I = Message.c_str();
296 const char *MsgEnd = I + Message.length();
297
298 unsigned int Length = strlen("Config-Item");
299 for (; I + Length < MsgEnd; I++)
300 {
301 // Not a config item
302 if (I[Length] != ':' || stringcasecmp(I,I+Length,"Config-Item") != 0)
303 continue;
304
305 I += Length + 1;
306
307 for (; I < MsgEnd && *I == ' '; I++);
308 const char *Equals = I;
309 for (; Equals < MsgEnd && *Equals != '='; Equals++);
310 const char *End = Equals;
311 for (; End < MsgEnd && *End != '\n'; End++);
312 if (End == Equals)
313 return false;
314
315 Cnf.Set(DeQuoteString(string(I,Equals-I)),
316 DeQuoteString(string(Equals+1,End-Equals-1)));
317 I = End;
318 }
319
320 return true;
321 }
322 /*}}}*/
323 // AcqMethod::Run - Run the message engine /*{{{*/
324 // ---------------------------------------------------------------------
325 /* Fetch any messages and execute them. In single mode it returns 1 if
326 there are no more available messages - any other result is a
327 fatal failure code! */
328 int pkgAcqMethod::Run(bool Single)
329 {
330 while (1)
331 {
332 // Block if the message queue is empty
333 if (Messages.empty() == true)
334 {
335 if (Single == false)
336 if (WaitFd(STDIN_FILENO) == false)
337 break;
338 if (ReadMessages(STDIN_FILENO,Messages) == false)
339 break;
340 }
341
342 // Single mode exits if the message queue is empty
343 if (Single == true && Messages.empty() == true)
344 return -1;
345
346 string Message = Messages.front();
347 Messages.erase(Messages.begin());
348
349 // Fetch the message number
350 char *End;
351 int Number = strtol(Message.c_str(),&End,10);
352 if (End == Message.c_str())
353 {
354 cerr << "Malformed message!" << endl;
355 return 100;
356 }
357
358 switch (Number)
359 {
360 case 601:
361 if (Configuration(Message) == false)
362 return 100;
363 break;
364
365 case 600:
366 {
367 FetchItem *Tmp = new FetchItem;
368
369 Tmp->Uri = LookupTag(Message,"URI");
370 Tmp->DestFile = LookupTag(Message,"FileName");
371 Tmp->ExpectedMD5 = LookupTag(Message,"ExpectedMD5");
372 if (StrToTime(LookupTag(Message,"Last-Modified"),Tmp->LastModified) == false)
373 Tmp->LastModified = 0;
374 Tmp->IndexFile = StringToBool(LookupTag(Message,"Index-File"),false);
375 Tmp->Next = 0;
376
377 // Append it to the list
378 FetchItem **I = &Queue;
379 for (; *I != 0; I = &(*I)->Next);
380 *I = Tmp;
381 if (QueueBack == 0)
382 QueueBack = Tmp;
383
384 // Notify that this item is to be fetched.
385 if (Fetch(Tmp) == false)
386 Fail();
387
388 break;
389 }
390 }
391 }
392
393 Exit();
394 return 0;
395 }
396 /*}}}*/
397 // AcqMethod::Log - Send a log message /*{{{*/
398 // ---------------------------------------------------------------------
399 /* */
400 void pkgAcqMethod::Log(const char *Format,...)
401 {
402 string CurrentURI = "<UNKNOWN>";
403 if (Queue != 0)
404 CurrentURI = Queue->Uri;
405
406 va_list args;
407 va_start(args,Format);
408
409 // sprintf the description
410 char S[1024];
411 unsigned int Len = snprintf(S,sizeof(S)-4,"101 Log\nURI: %s\n"
412 "Message: ",CurrentURI.c_str());
413
414 vsnprintf(S+Len,sizeof(S)-4-Len,Format,args);
415 strcat(S,"\n\n");
416
417 if (write(STDOUT_FILENO,S,strlen(S)) != (signed)strlen(S))
418 exit(100);
419 }
420 /*}}}*/
421 // AcqMethod::Status - Send a status message /*{{{*/
422 // ---------------------------------------------------------------------
423 /* */
424 void pkgAcqMethod::Status(const char *Format,...)
425 {
426 string CurrentURI = "<UNKNOWN>";
427 if (Queue != 0)
428 CurrentURI = Queue->Uri;
429
430 va_list args;
431 va_start(args,Format);
432
433 // sprintf the description
434 char S[1024];
435 unsigned int Len = snprintf(S,sizeof(S)-4,"102 Status\nURI: %s\n"
436 "Message: ",CurrentURI.c_str());
437
438 vsnprintf(S+Len,sizeof(S)-4-Len,Format,args);
439 strcat(S,"\n\n");
440
441 if (write(STDOUT_FILENO,S,strlen(S)) != (signed)strlen(S))
442 exit(100);
443 }
444 /*}}}*/
445
446 // AcqMethod::FetchResult::FetchResult - Constructor /*{{{*/
447 // ---------------------------------------------------------------------
448 /* */
449 pkgAcqMethod::FetchResult::FetchResult() : LastModified(0),
450 IMSHit(false), Size(0), ResumePoint(0)
451 {
452 }
453 /*}}}*/
454 // AcqMethod::FetchResult::TakeHashes - Load hashes /*{{{*/
455 // ---------------------------------------------------------------------
456 /* This hides the number of hashes we are supporting from the caller.
457 It just deals with the hash class. */
458 void pkgAcqMethod::FetchResult::TakeHashes(Hashes &Hash)
459 {
460 MD5Sum = Hash.MD5.Result();
461 SHA1Sum = Hash.SHA1.Result();
462 }
463 /*}}}*/