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