]> git.saurik.com Git - apt.git/blame_incremental - apt-pkg/acquire-worker.cc
tests: don't do boundless string compares with data()
[apt.git] / apt-pkg / acquire-worker.cc
... / ...
CommitLineData
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
42using namespace std;
43
44// Worker::Worker - Constructor for Queue startup /*{{{*/
45pkgAcquire::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 /*{{{*/
53pkgAcquire::Worker::Worker(MethodConfig *Cnf) : Worker(nullptr, Cnf, nullptr)
54{
55}
56 /*}}}*/
57// Worker::Construct - Constructor helper /*{{{*/
58// ---------------------------------------------------------------------
59/* */
60void 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/* */
75pkgAcquire::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 */
93bool 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/* */
183bool 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. */
194enum 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};
206static 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}
213bool 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 if (isDoomedItem(Owner) == false)
468 {
469 if (Message.find("\nFailReason:") == std::string::npos)
470 {
471 if (ReceivedHashes != ExpectedHashes)
472 Message.append("\nFailReason: HashSumMismatch");
473 else
474 Message.append("\nFailReason: WeakHashSums");
475 }
476 Owner->Failed(Message,Config);
477 }
478 if (Log != nullptr)
479 Log->Fail(Owner->GetItemDesc());
480 }
481 }
482 ItemDone();
483 break;
484 }
485
486 case MessageType::URI_FAILURE:
487 {
488 if (Itm == nullptr)
489 {
490 std::string const msg = LookupTag(Message,"Message");
491 _error->Error("Method gave invalid 400 URI Failure message: %s", msg.c_str());
492 break;
493 }
494
495 PrepareFiles("400::URIFailure", Itm);
496
497 // Display update before completion
498 if (Log != nullptr && Log->MorePulses == true)
499 for (pkgAcquire::Queue::QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O)
500 Log->Pulse((*O)->GetOwner());
501
502 std::vector<Item*> const ItmOwners = Itm->Owners;
503 OwnerQ->ItemDone(Itm);
504 Itm = nullptr;
505
506 bool errTransient = false, errAuthErr = false;
507 {
508 std::string const failReason = LookupTag(Message, "FailReason");
509 {
510 auto const reasons = { "Timeout", "ConnectionRefused",
511 "ConnectionTimedOut", "ResolveFailure", "TmpResolveFailure" };
512 errTransient = std::find(std::begin(reasons), std::end(reasons), failReason) != std::end(reasons);
513 }
514 if (errTransient == false)
515 {
516 auto const reasons = { "HashSumMismatch", "WeakHashSums", "MaximumSizeExceeded" };
517 errAuthErr = std::find(std::begin(reasons), std::end(reasons), failReason) != std::end(reasons);
518 }
519 }
520
521 for (auto const Owner: ItmOwners)
522 {
523 if (errAuthErr && Owner->GetExpectedHashes().empty() == false)
524 Owner->Status = pkgAcquire::Item::StatAuthError;
525 else if (errTransient)
526 Owner->Status = pkgAcquire::Item::StatTransientNetworkError;
527
528 if (isDoomedItem(Owner) == false)
529 Owner->Failed(Message,Config);
530 if (Log != nullptr)
531 Log->Fail(Owner->GetItemDesc());
532 }
533 ItemDone();
534
535 break;
536 }
537
538 case MessageType::GENERAL_FAILURE:
539 _error->Error("Method %s General failure: %s",Access.c_str(),LookupTag(Message,"Message").c_str());
540 break;
541
542 case MessageType::MEDIA_CHANGE:
543 MediaChange(Message);
544 break;
545 }
546 }
547 return true;
548}
549 /*}}}*/
550// Worker::Capabilities - 100 Capabilities handler /*{{{*/
551// ---------------------------------------------------------------------
552/* This parses the capabilities message and dumps it into the configuration
553 structure. */
554bool pkgAcquire::Worker::Capabilities(string Message)
555{
556 if (Config == 0)
557 return true;
558
559 Config->Version = LookupTag(Message,"Version");
560 Config->SingleInstance = StringToBool(LookupTag(Message,"Single-Instance"),false);
561 Config->Pipeline = StringToBool(LookupTag(Message,"Pipeline"),false);
562 Config->SendConfig = StringToBool(LookupTag(Message,"Send-Config"),false);
563 Config->LocalOnly = StringToBool(LookupTag(Message,"Local-Only"),false);
564 Config->NeedsCleanup = StringToBool(LookupTag(Message,"Needs-Cleanup"),false);
565 Config->Removable = StringToBool(LookupTag(Message,"Removable"),false);
566
567 // Some debug text
568 if (Debug == true)
569 {
570 clog << "Configured access method " << Config->Access << endl;
571 clog << "Version:" << Config->Version <<
572 " SingleInstance:" << Config->SingleInstance <<
573 " Pipeline:" << Config->Pipeline <<
574 " SendConfig:" << Config->SendConfig <<
575 " LocalOnly: " << Config->LocalOnly <<
576 " NeedsCleanup: " << Config->NeedsCleanup <<
577 " Removable: " << Config->Removable << endl;
578 }
579
580 return true;
581}
582 /*}}}*/
583// Worker::MediaChange - Request a media change /*{{{*/
584// ---------------------------------------------------------------------
585/* */
586bool pkgAcquire::Worker::MediaChange(string Message)
587{
588 int status_fd = _config->FindI("APT::Status-Fd",-1);
589 if(status_fd > 0)
590 {
591 string Media = LookupTag(Message,"Media");
592 string Drive = LookupTag(Message,"Drive");
593 ostringstream msg,status;
594 ioprintf(msg,_("Please insert the disc labeled: "
595 "'%s' "
596 "in the drive '%s' and press [Enter]."),
597 Media.c_str(),Drive.c_str());
598 status << "media-change: " // message
599 << Media << ":" // media
600 << Drive << ":" // drive
601 << msg.str() // l10n message
602 << endl;
603
604 std::string const dlstatus = status.str();
605 FileFd::Write(status_fd, dlstatus.c_str(), dlstatus.size());
606 }
607
608 if (Log == 0 || Log->MediaChange(LookupTag(Message,"Media"),
609 LookupTag(Message,"Drive")) == false)
610 {
611 char S[300];
612 snprintf(S,sizeof(S),"603 Media Changed\nFailed: true\n\n");
613 if (Debug == true)
614 clog << " -> " << Access << ':' << QuoteString(S,"\n") << endl;
615 OutQueue += S;
616 OutReady = true;
617 return true;
618 }
619
620 char S[300];
621 snprintf(S,sizeof(S),"603 Media Changed\n\n");
622 if (Debug == true)
623 clog << " -> " << Access << ':' << QuoteString(S,"\n") << endl;
624 OutQueue += S;
625 OutReady = true;
626 return true;
627}
628 /*}}}*/
629// Worker::SendConfiguration - Send the config to the method /*{{{*/
630// ---------------------------------------------------------------------
631/* */
632bool pkgAcquire::Worker::SendConfiguration()
633{
634 if (Config->SendConfig == false)
635 return true;
636
637 if (OutFd == -1)
638 return false;
639
640 /* Write out all of the configuration directives by walking the
641 configuration tree */
642 std::ostringstream Message;
643 Message << "601 Configuration\n";
644 _config->Dump(Message, NULL, "Config-Item: %F=%V\n", false);
645 Message << '\n';
646
647 if (Debug == true)
648 clog << " -> " << Access << ':' << QuoteString(Message.str(),"\n") << endl;
649 OutQueue += Message.str();
650 OutReady = true;
651
652 return true;
653}
654 /*}}}*/
655// Worker::QueueItem - Add an item to the outbound queue /*{{{*/
656// ---------------------------------------------------------------------
657/* Send a URI Acquire message to the method */
658bool pkgAcquire::Worker::QueueItem(pkgAcquire::Queue::QItem *Item)
659{
660 if (OutFd == -1)
661 return false;
662
663 HashStringList const hsl = Item->GetExpectedHashes();
664
665 if (isDoomedItem(Item->Owner))
666 return true;
667
668 if (hsl.usable() == false && Item->Owner->HashesRequired() &&
669 _config->Exists("Acquire::ForceHash") == false)
670 {
671 std::string const Message = "400 URI Failure"
672 "\nURI: " + Item->URI +
673 "\nFilename: " + Item->Owner->DestFile +
674 "\nFailReason: WeakHashSums";
675
676 auto const ItmOwners = Item->Owners;
677 for (auto &O: ItmOwners)
678 {
679 O->Status = pkgAcquire::Item::StatAuthError;
680 O->Failed(Message, Config);
681 if (Log != nullptr)
682 Log->Fail(O->GetItemDesc());
683 }
684 // "queued" successfully, the item just instantly failed
685 return true;
686 }
687
688 string Message = "600 URI Acquire\n";
689 Message.reserve(300);
690 Message += "URI: " + Item->URI;
691 Message += "\nFilename: " + Item->Owner->DestFile;
692
693 for (HashStringList::const_iterator hs = hsl.begin(); hs != hsl.end(); ++hs)
694 Message += "\nExpected-" + hs->HashType() + ": " + hs->HashValue();
695
696 if (hsl.FileSize() == 0)
697 {
698 unsigned long long FileSize = Item->GetMaximumSize();
699 if(FileSize > 0)
700 {
701 string MaximumSize;
702 strprintf(MaximumSize, "%llu", FileSize);
703 Message += "\nMaximum-Size: " + MaximumSize;
704 }
705 }
706
707 Item->SyncDestinationFiles();
708 Message += Item->Custom600Headers();
709 Message += "\n\n";
710
711 if (RealFileExists(Item->Owner->DestFile))
712 {
713 std::string const SandboxUser = _config->Find("APT::Sandbox::User");
714 ChangeOwnerAndPermissionOfFile("Item::QueueURI", Item->Owner->DestFile.c_str(),
715 SandboxUser.c_str(), "root", 0600);
716 }
717
718 if (Debug == true)
719 clog << " -> " << Access << ':' << QuoteString(Message,"\n") << endl;
720 OutQueue += Message;
721 OutReady = true;
722
723 return true;
724}
725 /*}}}*/
726// Worker::OutFdRead - Out bound FD is ready /*{{{*/
727// ---------------------------------------------------------------------
728/* */
729bool pkgAcquire::Worker::OutFdReady()
730{
731 int Res;
732 do
733 {
734 Res = write(OutFd,OutQueue.c_str(),OutQueue.length());
735 }
736 while (Res < 0 && errno == EINTR);
737
738 if (Res <= 0)
739 return MethodFailure();
740
741 OutQueue.erase(0,Res);
742 if (OutQueue.empty() == true)
743 OutReady = false;
744
745 return true;
746}
747 /*}}}*/
748// Worker::InFdRead - In bound FD is ready /*{{{*/
749// ---------------------------------------------------------------------
750/* */
751bool pkgAcquire::Worker::InFdReady()
752{
753 if (ReadMessages() == false)
754 return false;
755 RunMessages();
756 return true;
757}
758 /*}}}*/
759// Worker::MethodFailure - Called when the method fails /*{{{*/
760// ---------------------------------------------------------------------
761/* This is called when the method is believed to have failed, probably because
762 read returned -1. */
763bool pkgAcquire::Worker::MethodFailure()
764{
765 _error->Error("Method %s has died unexpectedly!",Access.c_str());
766
767 // do not reap the child here to show meaningfull error to the user
768 ExecWait(Process,Access.c_str(),false);
769 Process = -1;
770 close(InFd);
771 close(OutFd);
772 InFd = -1;
773 OutFd = -1;
774 OutReady = false;
775 InReady = false;
776 OutQueue = string();
777 MessageQueue.erase(MessageQueue.begin(),MessageQueue.end());
778
779 return false;
780}
781 /*}}}*/
782// Worker::Pulse - Called periodically /*{{{*/
783// ---------------------------------------------------------------------
784/* */
785void pkgAcquire::Worker::Pulse()
786{
787 if (CurrentItem == 0)
788 return;
789
790 struct stat Buf;
791 if (stat(CurrentItem->Owner->DestFile.c_str(),&Buf) != 0)
792 return;
793 CurrentSize = Buf.st_size;
794}
795 /*}}}*/
796// Worker::ItemDone - Called when the current item is finished /*{{{*/
797// ---------------------------------------------------------------------
798/* */
799void pkgAcquire::Worker::ItemDone()
800{
801 CurrentItem = 0;
802 CurrentSize = 0;
803 TotalSize = 0;
804 Status = string();
805}
806 /*}}}*/
807void pkgAcquire::Worker::PrepareFiles(char const * const caller, pkgAcquire::Queue::QItem const * const Itm)/*{{{*/
808{
809 if (RealFileExists(Itm->Owner->DestFile))
810 {
811 ChangeOwnerAndPermissionOfFile(caller, Itm->Owner->DestFile.c_str(), "root", "root", 0644);
812 std::string const filename = Itm->Owner->DestFile;
813 for (pkgAcquire::Queue::QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O)
814 {
815 pkgAcquire::Item const * const Owner = *O;
816 if (Owner->DestFile == filename || filename == "/dev/null")
817 continue;
818 RemoveFile("PrepareFiles", Owner->DestFile);
819 if (link(filename.c_str(), Owner->DestFile.c_str()) != 0)
820 {
821 // different mounts can't happen for us as we download to lists/ by default,
822 // but if the system is reused by others the locations can potentially be on
823 // different disks, so use symlink as poor-men replacement.
824 // FIXME: Real copying as last fallback, but that is costly, so offload to a method preferable
825 if (symlink(filename.c_str(), Owner->DestFile.c_str()) != 0)
826 _error->Error("Can't create (sym)link of file %s to %s", filename.c_str(), Owner->DestFile.c_str());
827 }
828 }
829 }
830 else
831 {
832 for (pkgAcquire::Queue::QItem::owner_iterator O = Itm->Owners.begin(); O != Itm->Owners.end(); ++O)
833 RemoveFile("PrepareFiles", (*O)->DestFile);
834 }
835}
836 /*}}}*/