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