]> git.saurik.com Git - apt.git/blob - apt-pkg/acquire-worker.cc
Optimize VersionHash() to not need temporary copy of input
[apt.git] / apt-pkg / acquire-worker.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: acquire-worker.cc,v 1.34 2001/05/22 04:42:54 jgg Exp $
4 /* ######################################################################
5
6 Acquire Worker
7
8 The worker process can startup either as a Configuration prober
9 or as a queue runner. As a configuration prober it only reads the
10 configuration message and
11
12 ##################################################################### */
13 /*}}}*/
14 // Include Files /*{{{*/
15 #include <config.h>
16
17 #include <apt-pkg/acquire.h>
18 #include <apt-pkg/acquire-worker.h>
19 #include <apt-pkg/acquire-item.h>
20 #include <apt-pkg/configuration.h>
21 #include <apt-pkg/error.h>
22 #include <apt-pkg/fileutl.h>
23 #include <apt-pkg/strutl.h>
24 #include <apt-pkg/hashes.h>
25
26 #include <algorithm>
27 #include <string>
28 #include <vector>
29 #include <iostream>
30
31 #include <sys/stat.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <signal.h>
35 #include <stdio.h>
36 #include <errno.h>
37 #include <sstream>
38
39 #include <apti18n.h>
40 /*}}}*/
41
42 using namespace std;
43
44 // Worker::Worker - Constructor for Queue startup /*{{{*/
45 pkgAcquire::Worker::Worker(Queue *Q, MethodConfig *Cnf, pkgAcquireStatus *log) :
46 d(NULL), OwnerQ(Q), Log(log), Config(Cnf), Access(Cnf->Access),
47 CurrentItem(nullptr), CurrentSize(0), TotalSize(0)
48 {
49 Construct();
50 }
51 /*}}}*/
52 // Worker::Worker - Constructor for method config startup /*{{{*/
53 pkgAcquire::Worker::Worker(MethodConfig *Cnf) : Worker(nullptr, Cnf, nullptr)
54 {
55 }
56 /*}}}*/
57 // Worker::Construct - Constructor helper /*{{{*/
58 // ---------------------------------------------------------------------
59 /* */
60 void pkgAcquire::Worker::Construct()
61 {
62 NextQueue = 0;
63 NextAcquire = 0;
64 Process = -1;
65 InFd = -1;
66 OutFd = -1;
67 OutReady = false;
68 InReady = false;
69 Debug = _config->FindB("Debug::pkgAcquire::Worker",false);
70 }
71 /*}}}*/
72 // Worker::~Worker - Destructor /*{{{*/
73 // ---------------------------------------------------------------------
74 /* */
75 pkgAcquire::Worker::~Worker()
76 {
77 close(InFd);
78 close(OutFd);
79
80 if (Process > 0)
81 {
82 /* Closing of stdin is the signal to exit and die when the process
83 indicates it needs cleanup */
84 if (Config->NeedsCleanup == false)
85 kill(Process,SIGINT);
86 ExecWait(Process,Access.c_str(),true);
87 }
88 }
89 /*}}}*/
90 // Worker::Start - Start the worker process /*{{{*/
91 // ---------------------------------------------------------------------
92 /* This forks the method and inits the communication channel */
93 bool pkgAcquire::Worker::Start()
94 {
95 // Get the method path
96 constexpr char const * const methodsDir = "Dir::Bin::Methods";
97 std::string const confItem = std::string(methodsDir) + "::" + Access;
98 std::string Method;
99 if (_config->Exists(confItem))
100 Method = _config->FindFile(confItem.c_str());
101 else
102 Method = _config->FindDir(methodsDir) + Access;
103 if (FileExists(Method) == false)
104 {
105 if (flNotDir(Method) == "false")
106 {
107 _error->Error(_("The method '%s' is explicitly disabled via configuration."), Access.c_str());
108 if (Access == "http" || Access == "https")
109 _error->Notice(_("If you meant to use Tor remember to use %s instead of %s."), ("tor+" + Access).c_str(), Access.c_str());
110 return false;
111 }
112 _error->Error(_("The method driver %s could not be found."),Method.c_str());
113 std::string const A(Access.cbegin(), std::find(Access.cbegin(), Access.cend(), '+'));
114 std::string pkg;
115 strprintf(pkg, "apt-transport-%s", A.c_str());
116 _error->Notice(_("Is the package %s installed?"), pkg.c_str());
117 return false;
118 }
119 std::string const Calling = _config->FindDir(methodsDir) + Access;
120
121 if (Debug == true)
122 {
123 std::clog << "Starting method '" << Calling << "'";
124 if (Calling != Method)
125 std::clog << " ( via " << Method << " )";
126 std::clog << endl;
127 }
128
129 // Create the pipes
130 int Pipes[4] = {-1,-1,-1,-1};
131 if (pipe(Pipes) != 0 || pipe(Pipes+2) != 0)
132 {
133 _error->Errno("pipe","Failed to create IPC pipe to subprocess");
134 for (int I = 0; I != 4; I++)
135 close(Pipes[I]);
136 return false;
137 }
138 for (int I = 0; I != 4; I++)
139 SetCloseExec(Pipes[I],true);
140
141 // Fork off the process
142 Process = ExecFork();
143 if (Process == 0)
144 {
145 // Setup the FDs
146 dup2(Pipes[1],STDOUT_FILENO);
147 dup2(Pipes[2],STDIN_FILENO);
148 SetCloseExec(STDOUT_FILENO,false);
149 SetCloseExec(STDIN_FILENO,false);
150 SetCloseExec(STDERR_FILENO,false);
151
152 const char * const Args[] = { Calling.c_str(), nullptr };
153 execv(Method.c_str() ,const_cast<char **>(Args));
154 std::cerr << "Failed to exec method " << Calling << " ( via " << Method << ")" << endl;
155 _exit(100);
156 }
157
158 // Fix up our FDs
159 InFd = Pipes[0];
160 OutFd = Pipes[3];
161 SetNonBlock(Pipes[0],true);
162 SetNonBlock(Pipes[3],true);
163 close(Pipes[1]);
164 close(Pipes[2]);
165 OutReady = false;
166 InReady = true;
167
168 // Read the configuration data
169 if (WaitFd(InFd) == false ||
170 ReadMessages() == false)
171 return _error->Error(_("Method %s did not start correctly"),Method.c_str());
172
173 RunMessages();
174 if (OwnerQ != 0)
175 SendConfiguration();
176
177 return true;
178 }
179 /*}}}*/
180 // Worker::ReadMessages - Read all pending messages into the list /*{{{*/
181 // ---------------------------------------------------------------------
182 /* */
183 bool pkgAcquire::Worker::ReadMessages()
184 {
185 if (::ReadMessages(InFd,MessageQueue) == false)
186 return MethodFailure();
187 return true;
188 }
189 /*}}}*/
190 // Worker::RunMessage - Empty the message queue /*{{{*/
191 // ---------------------------------------------------------------------
192 /* This takes the messages from the message queue and runs them through
193 the parsers in order. */
194 enum class APT_HIDDEN MessageType {
195 CAPABILITIES = 100,
196 LOG = 101,
197 STATUS = 102,
198 REDIRECT = 103,
199 WARNING = 104,
200 URI_START = 200,
201 URI_DONE = 201,
202 URI_FAILURE = 400,
203 GENERAL_FAILURE = 401,
204 MEDIA_CHANGE = 403
205 };
206 static bool isDoomedItem(pkgAcquire::Item const * const Itm)
207 {
208 auto const TransItm = dynamic_cast<pkgAcqTransactionItem const * const>(Itm);
209 if (TransItm == nullptr)
210 return false;
211 return TransItm->TransactionManager->State != pkgAcqTransactionItem::TransactionStarted;
212 }
213 bool pkgAcquire::Worker::RunMessages()
214 {
215 while (MessageQueue.empty() == false)
216 {
217 string Message = MessageQueue.front();
218 MessageQueue.erase(MessageQueue.begin());
219
220 if (Debug == true)
221 clog << " <- " << Access << ':' << QuoteString(Message,"\n") << endl;
222
223 // Fetch the message number
224 char *End;
225 MessageType const Number = static_cast<MessageType>(strtoul(Message.c_str(),&End,10));
226 if (End == Message.c_str())
227 return _error->Error("Invalid message from method %s: %s",Access.c_str(),Message.c_str());
228
229 string URI = LookupTag(Message,"URI");
230 pkgAcquire::Queue::QItem *Itm = NULL;
231 if (URI.empty() == false)
232 Itm = OwnerQ->FindItem(URI,this);
233
234 if (Itm != NULL)
235 {
236 // update used mirror
237 string UsedMirror = LookupTag(Message,"UsedMirror", "");
238 if (UsedMirror.empty() == false)
239 {
240 for (pkgAcquire::Queue::QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O)
241 (*O)->UsedMirror = UsedMirror;
242
243 if (Itm->Description.find(" ") != string::npos)
244 Itm->Description.replace(0, Itm->Description.find(" "), UsedMirror);
245 }
246 }
247
248 // Determine the message number and dispatch
249 switch (Number)
250 {
251 case MessageType::CAPABILITIES:
252 if (Capabilities(Message) == false)
253 return _error->Error("Unable to process Capabilities message from %s",Access.c_str());
254 break;
255
256 case MessageType::LOG:
257 if (Debug == true)
258 clog << " <- (log) " << LookupTag(Message,"Message") << endl;
259 break;
260
261 case MessageType::STATUS:
262 Status = LookupTag(Message,"Message");
263 break;
264
265 case MessageType::REDIRECT:
266 {
267 if (Itm == nullptr)
268 {
269 _error->Error("Method gave invalid 103 Redirect message");
270 break;
271 }
272
273 std::string const NewURI = LookupTag(Message,"New-URI",URI.c_str());
274 Itm->URI = NewURI;
275
276 ItemDone();
277
278 // Change the status so that it can be dequeued
279 for (auto const &O: Itm->Owners)
280 O->Status = pkgAcquire::Item::StatIdle;
281 // Mark the item as done (taking care of all queues)
282 // and then put it in the main queue again
283 std::vector<Item*> const ItmOwners = Itm->Owners;
284 OwnerQ->ItemDone(Itm);
285 Itm = nullptr;
286 for (auto const &Owner: ItmOwners)
287 {
288 pkgAcquire::ItemDesc &desc = Owner->GetItemDesc();
289 if (Owner->IsRedirectionLoop(NewURI))
290 {
291 std::string msg = Message;
292 msg.append("\nFailReason: RedirectionLoop");
293 Owner->Failed(msg, Config);
294 if (Log != nullptr)
295 Log->Fail(Owner->GetItemDesc());
296 continue;
297 }
298
299 if (Log != nullptr)
300 Log->Done(desc);
301
302 // if we change site, treat it as a mirror change
303 if (URI::SiteOnly(NewURI) != URI::SiteOnly(desc.URI))
304 {
305 auto const firstSpace = desc.Description.find(" ");
306 if (firstSpace != std::string::npos)
307 {
308 std::string const OldSite = desc.Description.substr(0, firstSpace);
309 if (likely(APT::String::Startswith(desc.URI, OldSite)))
310 {
311 std::string const OldExtra = desc.URI.substr(OldSite.length() + 1);
312 if (likely(APT::String::Endswith(NewURI, OldExtra)))
313 {
314 std::string const NewSite = NewURI.substr(0, NewURI.length() - OldExtra.length());
315 Owner->UsedMirror = URI::ArchiveOnly(NewSite);
316 desc.Description.replace(0, firstSpace, Owner->UsedMirror);
317 }
318 }
319 }
320 }
321 desc.URI = NewURI;
322 if (isDoomedItem(Owner) == false)
323 OwnerQ->Owner->Enqueue(desc);
324 }
325 break;
326 }
327
328 case MessageType::WARNING:
329 _error->Warning("%s: %s", Itm->Owner->DescURI().c_str(), LookupTag(Message,"Message").c_str());
330 break;
331
332 case MessageType::URI_START:
333 {
334 if (Itm == nullptr)
335 {
336 _error->Error("Method gave invalid 200 URI Start message");
337 break;
338 }
339
340 CurrentItem = Itm;
341 CurrentSize = 0;
342 TotalSize = strtoull(LookupTag(Message,"Size","0").c_str(), NULL, 10);
343 ResumePoint = strtoull(LookupTag(Message,"Resume-Point","0").c_str(), NULL, 10);
344 for (auto const Owner: Itm->Owners)
345 {
346 Owner->Start(Message, TotalSize);
347 // Display update before completion
348 if (Log != nullptr)
349 {
350 if (Log->MorePulses == true)
351 Log->Pulse(Owner->GetOwner());
352 Log->Fetch(Owner->GetItemDesc());
353 }
354 }
355
356 break;
357 }
358
359 case MessageType::URI_DONE:
360 {
361 if (Itm == nullptr)
362 {
363 _error->Error("Method gave invalid 201 URI Done message");
364 break;
365 }
366
367 PrepareFiles("201::URIDone", Itm);
368
369 // Display update before completion
370 if (Log != 0 && Log->MorePulses == true)
371 for (pkgAcquire::Queue::QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O)
372 Log->Pulse((*O)->GetOwner());
373
374 HashStringList ReceivedHashes;
375 {
376 std::string const givenfilename = LookupTag(Message, "Filename");
377 std::string const filename = givenfilename.empty() ? Itm->Owner->DestFile : givenfilename;
378 // see if we got hashes to verify
379 for (char const * const * type = HashString::SupportedHashes(); *type != NULL; ++type)
380 {
381 std::string const tagname = std::string(*type) + "-Hash";
382 std::string const hashsum = LookupTag(Message, tagname.c_str());
383 if (hashsum.empty() == false)
384 ReceivedHashes.push_back(HashString(*type, hashsum));
385 }
386 // not all methods always sent Hashes our way
387 if (ReceivedHashes.usable() == false)
388 {
389 HashStringList const ExpectedHashes = Itm->GetExpectedHashes();
390 if (ExpectedHashes.usable() == true && RealFileExists(filename))
391 {
392 Hashes calc(ExpectedHashes);
393 FileFd file(filename, FileFd::ReadOnly, FileFd::None);
394 calc.AddFD(file);
395 ReceivedHashes = calc.GetHashStringList();
396 }
397 }
398
399 // only local files can refer other filenames and counting them as fetched would be unfair
400 if (Log != NULL && Itm->Owner->Complete == false && Itm->Owner->Local == false && givenfilename == filename)
401 Log->Fetched(ReceivedHashes.FileSize(),atoi(LookupTag(Message,"Resume-Point","0").c_str()));
402 }
403
404 std::vector<Item*> const ItmOwners = Itm->Owners;
405 OwnerQ->ItemDone(Itm);
406 Itm = NULL;
407
408 bool const isIMSHit = StringToBool(LookupTag(Message,"IMS-Hit"),false) ||
409 StringToBool(LookupTag(Message,"Alt-IMS-Hit"),false);
410 auto const forcedHash = _config->Find("Acquire::ForceHash");
411 for (auto const Owner: ItmOwners)
412 {
413 HashStringList const ExpectedHashes = Owner->GetExpectedHashes();
414 if(_config->FindB("Debug::pkgAcquire::Auth", false) == true)
415 {
416 std::clog << "201 URI Done: " << Owner->DescURI() << endl
417 << "ReceivedHash:" << endl;
418 for (HashStringList::const_iterator hs = ReceivedHashes.begin(); hs != ReceivedHashes.end(); ++hs)
419 std::clog << "\t- " << hs->toStr() << std::endl;
420 std::clog << "ExpectedHash:" << endl;
421 for (HashStringList::const_iterator hs = ExpectedHashes.begin(); hs != ExpectedHashes.end(); ++hs)
422 std::clog << "\t- " << hs->toStr() << std::endl;
423 std::clog << endl;
424 }
425
426 // decide if what we got is what we expected
427 bool consideredOkay = false;
428 if ((forcedHash.empty() && ExpectedHashes.empty() == false) ||
429 (forcedHash.empty() == false && ExpectedHashes.usable()))
430 {
431 if (ReceivedHashes.empty())
432 {
433 /* IMS-Hits can't be checked here as we will have uncompressed file,
434 but the hashes for the compressed file. What we have was good through
435 so all we have to ensure later is that we are not stalled. */
436 consideredOkay = isIMSHit;
437 }
438 else if (ReceivedHashes == ExpectedHashes)
439 consideredOkay = true;
440 else
441 consideredOkay = false;
442
443 }
444 else
445 consideredOkay = !Owner->HashesRequired();
446
447 if (consideredOkay == true)
448 consideredOkay = Owner->VerifyDone(Message, Config);
449 else // hashsum mismatch
450 Owner->Status = pkgAcquire::Item::StatAuthError;
451
452
453 if (consideredOkay == true)
454 {
455 if (isDoomedItem(Owner) == false)
456 Owner->Done(Message, ReceivedHashes, Config);
457 if (Log != nullptr)
458 {
459 if (isIMSHit)
460 Log->IMSHit(Owner->GetItemDesc());
461 else
462 Log->Done(Owner->GetItemDesc());
463 }
464 }
465 else
466 {
467 auto SavedDesc = Owner->GetItemDesc();
468 if (isDoomedItem(Owner) == false)
469 {
470 if (Message.find("\nFailReason:") == std::string::npos)
471 {
472 if (ReceivedHashes != ExpectedHashes)
473 Message.append("\nFailReason: HashSumMismatch");
474 else
475 Message.append("\nFailReason: WeakHashSums");
476 }
477 Owner->Failed(Message,Config);
478 }
479 if (Log != nullptr)
480 Log->Fail(SavedDesc);
481 }
482 }
483 ItemDone();
484 break;
485 }
486
487 case MessageType::URI_FAILURE:
488 {
489 if (Itm == nullptr)
490 {
491 std::string const msg = LookupTag(Message,"Message");
492 _error->Error("Method gave invalid 400 URI Failure message: %s", msg.c_str());
493 break;
494 }
495
496 PrepareFiles("400::URIFailure", Itm);
497
498 // Display update before completion
499 if (Log != nullptr && Log->MorePulses == true)
500 for (pkgAcquire::Queue::QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O)
501 Log->Pulse((*O)->GetOwner());
502
503 std::vector<Item*> const ItmOwners = Itm->Owners;
504 OwnerQ->ItemDone(Itm);
505 Itm = nullptr;
506
507 bool errTransient = false, errAuthErr = false;
508 {
509 std::string const failReason = LookupTag(Message, "FailReason");
510 {
511 auto const reasons = { "Timeout", "ConnectionRefused",
512 "ConnectionTimedOut", "ResolveFailure", "TmpResolveFailure" };
513 errTransient = std::find(std::begin(reasons), std::end(reasons), failReason) != std::end(reasons);
514 }
515 if (errTransient == false)
516 {
517 auto const reasons = { "HashSumMismatch", "WeakHashSums", "MaximumSizeExceeded" };
518 errAuthErr = std::find(std::begin(reasons), std::end(reasons), failReason) != std::end(reasons);
519 }
520 }
521
522 for (auto const Owner: ItmOwners)
523 {
524 if (errAuthErr && Owner->GetExpectedHashes().empty() == false)
525 Owner->Status = pkgAcquire::Item::StatAuthError;
526 else if (errTransient)
527 Owner->Status = pkgAcquire::Item::StatTransientNetworkError;
528 auto SavedDesc = Owner->GetItemDesc();
529 if (isDoomedItem(Owner) == false)
530 Owner->Failed(Message,Config);
531 if (Log != nullptr)
532 Log->Fail(SavedDesc);
533 }
534 ItemDone();
535
536 break;
537 }
538
539 case MessageType::GENERAL_FAILURE:
540 _error->Error("Method %s General failure: %s",Access.c_str(),LookupTag(Message,"Message").c_str());
541 break;
542
543 case MessageType::MEDIA_CHANGE:
544 MediaChange(Message);
545 break;
546 }
547 }
548 return true;
549 }
550 /*}}}*/
551 // Worker::Capabilities - 100 Capabilities handler /*{{{*/
552 // ---------------------------------------------------------------------
553 /* This parses the capabilities message and dumps it into the configuration
554 structure. */
555 bool pkgAcquire::Worker::Capabilities(string Message)
556 {
557 if (Config == 0)
558 return true;
559
560 Config->Version = LookupTag(Message,"Version");
561 Config->SingleInstance = StringToBool(LookupTag(Message,"Single-Instance"),false);
562 Config->Pipeline = StringToBool(LookupTag(Message,"Pipeline"),false);
563 Config->SendConfig = StringToBool(LookupTag(Message,"Send-Config"),false);
564 Config->LocalOnly = StringToBool(LookupTag(Message,"Local-Only"),false);
565 Config->NeedsCleanup = StringToBool(LookupTag(Message,"Needs-Cleanup"),false);
566 Config->Removable = StringToBool(LookupTag(Message,"Removable"),false);
567
568 // Some debug text
569 if (Debug == true)
570 {
571 clog << "Configured access method " << Config->Access << endl;
572 clog << "Version:" << Config->Version <<
573 " SingleInstance:" << Config->SingleInstance <<
574 " Pipeline:" << Config->Pipeline <<
575 " SendConfig:" << Config->SendConfig <<
576 " LocalOnly: " << Config->LocalOnly <<
577 " NeedsCleanup: " << Config->NeedsCleanup <<
578 " Removable: " << Config->Removable << endl;
579 }
580
581 return true;
582 }
583 /*}}}*/
584 // Worker::MediaChange - Request a media change /*{{{*/
585 // ---------------------------------------------------------------------
586 /* */
587 bool pkgAcquire::Worker::MediaChange(string Message)
588 {
589 int status_fd = _config->FindI("APT::Status-Fd",-1);
590 if(status_fd > 0)
591 {
592 string Media = LookupTag(Message,"Media");
593 string Drive = LookupTag(Message,"Drive");
594 ostringstream msg,status;
595 ioprintf(msg,_("Please insert the disc labeled: "
596 "'%s' "
597 "in the drive '%s' and press [Enter]."),
598 Media.c_str(),Drive.c_str());
599 status << "media-change: " // message
600 << Media << ":" // media
601 << Drive << ":" // drive
602 << msg.str() // l10n message
603 << endl;
604
605 std::string const dlstatus = status.str();
606 FileFd::Write(status_fd, dlstatus.c_str(), dlstatus.size());
607 }
608
609 if (Log == 0 || Log->MediaChange(LookupTag(Message,"Media"),
610 LookupTag(Message,"Drive")) == false)
611 {
612 char S[300];
613 snprintf(S,sizeof(S),"603 Media Changed\nFailed: true\n\n");
614 if (Debug == true)
615 clog << " -> " << Access << ':' << QuoteString(S,"\n") << endl;
616 OutQueue += S;
617 OutReady = true;
618 return true;
619 }
620
621 char S[300];
622 snprintf(S,sizeof(S),"603 Media Changed\n\n");
623 if (Debug == true)
624 clog << " -> " << Access << ':' << QuoteString(S,"\n") << endl;
625 OutQueue += S;
626 OutReady = true;
627 return true;
628 }
629 /*}}}*/
630 // Worker::SendConfiguration - Send the config to the method /*{{{*/
631 // ---------------------------------------------------------------------
632 /* */
633 bool pkgAcquire::Worker::SendConfiguration()
634 {
635 if (Config->SendConfig == false)
636 return true;
637
638 if (OutFd == -1)
639 return false;
640
641 /* Write out all of the configuration directives by walking the
642 configuration tree */
643 std::ostringstream Message;
644 Message << "601 Configuration\n";
645 _config->Dump(Message, NULL, "Config-Item: %F=%V\n", false);
646 Message << '\n';
647
648 if (Debug == true)
649 clog << " -> " << Access << ':' << QuoteString(Message.str(),"\n") << endl;
650 OutQueue += Message.str();
651 OutReady = true;
652
653 return true;
654 }
655 /*}}}*/
656 // Worker::QueueItem - Add an item to the outbound queue /*{{{*/
657 // ---------------------------------------------------------------------
658 /* Send a URI Acquire message to the method */
659 bool pkgAcquire::Worker::QueueItem(pkgAcquire::Queue::QItem *Item)
660 {
661 if (OutFd == -1)
662 return false;
663
664 if (isDoomedItem(Item->Owner))
665 return true;
666
667 string Message = "600 URI Acquire\n";
668 Message.reserve(300);
669 Message += "URI: " + Item->URI;
670 Message += "\nFilename: " + Item->Owner->DestFile;
671
672 HashStringList const hsl = Item->GetExpectedHashes();
673 for (HashStringList::const_iterator hs = hsl.begin(); hs != hsl.end(); ++hs)
674 Message += "\nExpected-" + hs->HashType() + ": " + hs->HashValue();
675
676 if (hsl.FileSize() == 0)
677 {
678 unsigned long long FileSize = Item->GetMaximumSize();
679 if(FileSize > 0)
680 {
681 string MaximumSize;
682 strprintf(MaximumSize, "%llu", FileSize);
683 Message += "\nMaximum-Size: " + MaximumSize;
684 }
685 }
686
687 Item->SyncDestinationFiles();
688 Message += Item->Custom600Headers();
689 Message += "\n\n";
690
691 if (RealFileExists(Item->Owner->DestFile))
692 {
693 std::string const SandboxUser = _config->Find("APT::Sandbox::User");
694 ChangeOwnerAndPermissionOfFile("Item::QueueURI", Item->Owner->DestFile.c_str(),
695 SandboxUser.c_str(), ROOT_GROUP, 0600);
696 }
697
698 if (Debug == true)
699 clog << " -> " << Access << ':' << QuoteString(Message,"\n") << endl;
700 OutQueue += Message;
701 OutReady = true;
702
703 return true;
704 }
705 /*}}}*/
706 // Worker::OutFdRead - Out bound FD is ready /*{{{*/
707 // ---------------------------------------------------------------------
708 /* */
709 bool pkgAcquire::Worker::OutFdReady()
710 {
711 int Res;
712 do
713 {
714 Res = write(OutFd,OutQueue.c_str(),OutQueue.length());
715 }
716 while (Res < 0 && errno == EINTR);
717
718 if (Res <= 0)
719 return MethodFailure();
720
721 OutQueue.erase(0,Res);
722 if (OutQueue.empty() == true)
723 OutReady = false;
724
725 return true;
726 }
727 /*}}}*/
728 // Worker::InFdRead - In bound FD is ready /*{{{*/
729 // ---------------------------------------------------------------------
730 /* */
731 bool pkgAcquire::Worker::InFdReady()
732 {
733 if (ReadMessages() == false)
734 return false;
735 RunMessages();
736 return true;
737 }
738 /*}}}*/
739 // Worker::MethodFailure - Called when the method fails /*{{{*/
740 // ---------------------------------------------------------------------
741 /* This is called when the method is believed to have failed, probably because
742 read returned -1. */
743 bool pkgAcquire::Worker::MethodFailure()
744 {
745 _error->Error("Method %s has died unexpectedly!",Access.c_str());
746
747 // do not reap the child here to show meaningfull error to the user
748 ExecWait(Process,Access.c_str(),false);
749 Process = -1;
750 close(InFd);
751 close(OutFd);
752 InFd = -1;
753 OutFd = -1;
754 OutReady = false;
755 InReady = false;
756 OutQueue = string();
757 MessageQueue.erase(MessageQueue.begin(),MessageQueue.end());
758
759 return false;
760 }
761 /*}}}*/
762 // Worker::Pulse - Called periodically /*{{{*/
763 // ---------------------------------------------------------------------
764 /* */
765 void pkgAcquire::Worker::Pulse()
766 {
767 if (CurrentItem == 0)
768 return;
769
770 struct stat Buf;
771 if (stat(CurrentItem->Owner->DestFile.c_str(),&Buf) != 0)
772 return;
773 CurrentSize = Buf.st_size;
774 }
775 /*}}}*/
776 // Worker::ItemDone - Called when the current item is finished /*{{{*/
777 // ---------------------------------------------------------------------
778 /* */
779 void pkgAcquire::Worker::ItemDone()
780 {
781 CurrentItem = 0;
782 CurrentSize = 0;
783 TotalSize = 0;
784 Status = string();
785 }
786 /*}}}*/
787 void pkgAcquire::Worker::PrepareFiles(char const * const caller, pkgAcquire::Queue::QItem const * const Itm)/*{{{*/
788 {
789 if (RealFileExists(Itm->Owner->DestFile))
790 {
791 ChangeOwnerAndPermissionOfFile(caller, Itm->Owner->DestFile.c_str(), "root", ROOT_GROUP, 0644);
792 std::string const filename = Itm->Owner->DestFile;
793 for (pkgAcquire::Queue::QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O)
794 {
795 pkgAcquire::Item const * const Owner = *O;
796 if (Owner->DestFile == filename || filename == "/dev/null")
797 continue;
798 RemoveFile("PrepareFiles", Owner->DestFile);
799 if (link(filename.c_str(), Owner->DestFile.c_str()) != 0)
800 {
801 // different mounts can't happen for us as we download to lists/ by default,
802 // but if the system is reused by others the locations can potentially be on
803 // different disks, so use symlink as poor-men replacement.
804 // FIXME: Real copying as last fallback, but that is costly, so offload to a method preferable
805 if (symlink(filename.c_str(), Owner->DestFile.c_str()) != 0)
806 _error->Error("Can't create (sym)link of file %s to %s", filename.c_str(), Owner->DestFile.c_str());
807 }
808 }
809 }
810 else
811 {
812 for (pkgAcquire::Queue::QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O)
813 RemoveFile("PrepareFiles", (*O)->DestFile);
814 }
815 }
816 /*}}}*/