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