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