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