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