- merge repomd branch
[apt.git] / apt-pkg / acquire.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description                                                          /*{{{*/
3 // $Id: acquire.cc,v 1.2 2002/07/25 18:07:18 niemeyer Exp $
4 /* ######################################################################
5
6    Acquire - File Acquiration
7
8    The core element for the schedual system is the concept of a named
9    queue. Each queue is unique and each queue has a name derived from the
10    URI. The degree of paralization can be controled by how the queue
11    name is derived from the URI.
12    
13    ##################################################################### */
14                                                                         /*}}}*/
15 // Include Files                                                        /*{{{*/
16 #ifdef __GNUG__
17 #pragma implementation "apt-pkg/acquire.h"
18 #endif       
19 #include <apt-pkg/acquire.h>
20 #include <apt-pkg/acquire-item.h>
21 #include <apt-pkg/acquire-worker.h>
22 #include <apt-pkg/configuration.h>
23 #include <apt-pkg/error.h>
24 #include <apt-pkg/strutl.h>
25
26 #include <apti18n.h>
27
28 #include <iostream>
29     
30 #include <dirent.h>
31 #include <sys/time.h>
32 #include <errno.h>
33 #include <sys/stat.h>
34                                                                         /*}}}*/
35
36 using namespace std;
37
38 // Acquire::pkgAcquire - Constructor                                    /*{{{*/
39 // ---------------------------------------------------------------------
40 /* We grab some runtime state from the configuration space */
41 pkgAcquire::pkgAcquire(pkgAcquireStatus *Log) : Log(Log)
42 {
43    Queues = 0;
44    Configs = 0;
45    Workers = 0;
46    ToFetch = 0;
47    Running = false;
48    
49    string Mode = _config->Find("Acquire::Queue-Mode","host");
50    if (strcasecmp(Mode.c_str(),"host") == 0)
51       QueueMode = QueueHost;
52    if (strcasecmp(Mode.c_str(),"access") == 0)
53       QueueMode = QueueAccess;   
54
55    Debug = _config->FindB("Debug::pkgAcquire",false);
56    
57    // This is really a stupid place for this
58    struct stat St;
59    if (stat((_config->FindDir("Dir::State::lists") + "partial/").c_str(),&St) != 0 ||
60        S_ISDIR(St.st_mode) == 0)
61       _error->Error(_("Lists directory %spartial is missing."),
62                     _config->FindDir("Dir::State::lists").c_str());
63    if (stat((_config->FindDir("Dir::Cache::Archives") + "partial/").c_str(),&St) != 0 ||
64        S_ISDIR(St.st_mode) == 0)
65       _error->Error(_("Archive directory %spartial is missing."),
66                     _config->FindDir("Dir::Cache::Archives").c_str());
67 }
68                                                                         /*}}}*/
69 // Acquire::~pkgAcquire - Destructor                                    /*{{{*/
70 // ---------------------------------------------------------------------
71 /* Free our memory, clean up the queues (destroy the workers) */
72 pkgAcquire::~pkgAcquire()
73 {
74    Shutdown();
75    
76    while (Configs != 0)
77    {
78       MethodConfig *Jnk = Configs;
79       Configs = Configs->Next;
80       delete Jnk;
81    }   
82 }
83                                                                         /*}}}*/
84 // Acquire::Shutdown - Clean out the acquire object                     /*{{{*/
85 // ---------------------------------------------------------------------
86 /* */
87 void pkgAcquire::Shutdown()
88 {
89    while (Items.size() != 0)
90       delete Items[0];
91
92    while (Queues != 0)
93    {
94       Queue *Jnk = Queues;
95       Queues = Queues->Next;
96       delete Jnk;
97    }   
98 }
99                                                                         /*}}}*/
100 // Acquire::Add - Add a new item                                        /*{{{*/
101 // ---------------------------------------------------------------------
102 /* This puts an item on the acquire list. This list is mainly for tracking
103    item status */
104 void pkgAcquire::Add(Item *Itm)
105 {
106    Items.push_back(Itm);
107 }
108                                                                         /*}}}*/
109 // Acquire::Remove - Remove a item                                      /*{{{*/
110 // ---------------------------------------------------------------------
111 /* Remove an item from the acquire list. This is usually not used.. */
112 void pkgAcquire::Remove(Item *Itm)
113 {
114    Dequeue(Itm);
115    
116    for (ItemIterator I = Items.begin(); I != Items.end();)
117    {
118       if (*I == Itm)
119       {
120          Items.erase(I);
121          I = Items.begin();
122       }      
123       else 
124          I++;
125    }
126 }
127                                                                         /*}}}*/
128 // Acquire::Add - Add a worker                                          /*{{{*/
129 // ---------------------------------------------------------------------
130 /* A list of workers is kept so that the select loop can direct their FD
131    usage. */
132 void pkgAcquire::Add(Worker *Work)
133 {
134    Work->NextAcquire = Workers;
135    Workers = Work;
136 }
137                                                                         /*}}}*/
138 // Acquire::Remove - Remove a worker                                    /*{{{*/
139 // ---------------------------------------------------------------------
140 /* A worker has died. This can not be done while the select loop is running
141    as it would require that RunFds could handling a changing list state and
142    it cant.. */
143 void pkgAcquire::Remove(Worker *Work)
144 {
145    if (Running == true)
146       abort();
147    
148    Worker **I = &Workers;
149    for (; *I != 0;)
150    {
151       if (*I == Work)
152          *I = (*I)->NextAcquire;
153       else
154          I = &(*I)->NextAcquire;
155    }
156 }
157                                                                         /*}}}*/
158 // Acquire::Enqueue - Queue an URI for fetching                         /*{{{*/
159 // ---------------------------------------------------------------------
160 /* This is the entry point for an item. An item calls this function when
161    it is constructed which creates a queue (based on the current queue
162    mode) and puts the item in that queue. If the system is running then
163    the queue might be started. */
164 void pkgAcquire::Enqueue(ItemDesc &Item)
165 {
166    // Determine which queue to put the item in
167    const MethodConfig *Config;
168    string Name = QueueName(Item.URI,Config);
169    if (Name.empty() == true)
170       return;
171
172    // Find the queue structure
173    Queue *I = Queues;
174    for (; I != 0 && I->Name != Name; I = I->Next);
175    if (I == 0)
176    {
177       I = new Queue(Name,this);
178       I->Next = Queues;
179       Queues = I;
180       
181       if (Running == true)
182          I->Startup();
183    }
184
185    // See if this is a local only URI
186    if (Config->LocalOnly == true && Item.Owner->Complete == false)
187       Item.Owner->Local = true;
188    Item.Owner->Status = Item::StatIdle;
189    
190    // Queue it into the named queue
191    I->Enqueue(Item);
192    ToFetch++;
193          
194    // Some trace stuff
195    if (Debug == true)
196    {
197       clog << "Fetching " << Item.URI << endl;
198       clog << " to " << Item.Owner->DestFile << endl;
199       clog << " Queue is: " << Name << endl;
200    }
201 }
202                                                                         /*}}}*/
203 // Acquire::Dequeue - Remove an item from all queues                    /*{{{*/
204 // ---------------------------------------------------------------------
205 /* This is called when an item is finished being fetched. It removes it
206    from all the queues */
207 void pkgAcquire::Dequeue(Item *Itm)
208 {
209    Queue *I = Queues;
210    bool Res = false;
211    for (; I != 0; I = I->Next)
212       Res |= I->Dequeue(Itm);
213    
214    if (Debug == true)
215       clog << "Dequeuing " << Itm->DestFile << endl;
216    if (Res == true)
217       ToFetch--;
218 }
219                                                                         /*}}}*/
220 // Acquire::QueueName - Return the name of the queue for this URI       /*{{{*/
221 // ---------------------------------------------------------------------
222 /* The string returned depends on the configuration settings and the
223    method parameters. Given something like http://foo.org/bar it can
224    return http://foo.org or http */
225 string pkgAcquire::QueueName(string Uri,MethodConfig const *&Config)
226 {
227    URI U(Uri);
228    
229    Config = GetConfig(U.Access);
230    if (Config == 0)
231       return string();
232    
233    /* Single-Instance methods get exactly one queue per URI. This is
234       also used for the Access queue method  */
235    if (Config->SingleInstance == true || QueueMode == QueueAccess)
236        return U.Access;
237
238    return U.Access + ':' + U.Host;
239 }
240                                                                         /*}}}*/
241 // Acquire::GetConfig - Fetch the configuration information             /*{{{*/
242 // ---------------------------------------------------------------------
243 /* This locates the configuration structure for an access method. If 
244    a config structure cannot be found a Worker will be created to
245    retrieve it */
246 pkgAcquire::MethodConfig *pkgAcquire::GetConfig(string Access)
247 {
248    // Search for an existing config
249    MethodConfig *Conf;
250    for (Conf = Configs; Conf != 0; Conf = Conf->Next)
251       if (Conf->Access == Access)
252          return Conf;
253    
254    // Create the new config class
255    Conf = new MethodConfig;
256    Conf->Access = Access;
257    Conf->Next = Configs;
258    Configs = Conf;
259
260    // Create the worker to fetch the configuration
261    Worker Work(Conf);
262    if (Work.Start() == false)
263       return 0;
264    
265    return Conf;
266 }
267                                                                         /*}}}*/
268 // Acquire::SetFds - Deal with readable FDs                             /*{{{*/
269 // ---------------------------------------------------------------------
270 /* Collect FDs that have activity monitors into the fd sets */
271 void pkgAcquire::SetFds(int &Fd,fd_set *RSet,fd_set *WSet)
272 {
273    for (Worker *I = Workers; I != 0; I = I->NextAcquire)
274    {
275       if (I->InReady == true && I->InFd >= 0)
276       {
277          if (Fd < I->InFd)
278             Fd = I->InFd;
279          FD_SET(I->InFd,RSet);
280       }
281       if (I->OutReady == true && I->OutFd >= 0)
282       {
283          if (Fd < I->OutFd)
284             Fd = I->OutFd;
285          FD_SET(I->OutFd,WSet);
286       }
287    }
288 }
289                                                                         /*}}}*/
290 // Acquire::RunFds - Deal with active FDs                               /*{{{*/
291 // ---------------------------------------------------------------------
292 /* Dispatch active FDs over to the proper workers. It is very important
293    that a worker never be erased while this is running! The queue class
294    should never erase a worker except during shutdown processing. */
295 void pkgAcquire::RunFds(fd_set *RSet,fd_set *WSet)
296 {
297    for (Worker *I = Workers; I != 0; I = I->NextAcquire)
298    {
299       if (I->InFd >= 0 && FD_ISSET(I->InFd,RSet) != 0)
300          I->InFdReady();
301       if (I->OutFd >= 0 && FD_ISSET(I->OutFd,WSet) != 0)
302          I->OutFdReady();
303    }
304 }
305                                                                         /*}}}*/
306 // Acquire::Run - Run the fetch sequence                                /*{{{*/
307 // ---------------------------------------------------------------------
308 /* This runs the queues. It manages a select loop for all of the
309    Worker tasks. The workers interact with the queues and items to
310    manage the actual fetch. */
311 pkgAcquire::RunResult pkgAcquire::Run()
312 {
313    Running = true;
314    
315    for (Queue *I = Queues; I != 0; I = I->Next)
316       I->Startup();
317    
318    if (Log != 0)
319       Log->Start();
320    
321    bool WasCancelled = false;
322
323    // Run till all things have been acquired
324    struct timeval tv;
325    tv.tv_sec = 0;
326    tv.tv_usec = 500000; 
327    while (ToFetch > 0)
328    {
329       fd_set RFds;
330       fd_set WFds;
331       int Highest = 0;
332       FD_ZERO(&RFds);
333       FD_ZERO(&WFds);
334       SetFds(Highest,&RFds,&WFds);
335       
336       int Res;
337       do
338       {
339          Res = select(Highest+1,&RFds,&WFds,0,&tv);
340       }
341       while (Res < 0 && errno == EINTR);
342       
343       if (Res < 0)
344       {
345          _error->Errno("select","Select has failed");
346          break;
347       }
348              
349       RunFds(&RFds,&WFds);
350       if (_error->PendingError() == true)
351          break;
352       
353       // Timeout, notify the log class
354       if (Res == 0 || (Log != 0 && Log->Update == true))
355       {
356          tv.tv_usec = 500000;
357          for (Worker *I = Workers; I != 0; I = I->NextAcquire)
358             I->Pulse();
359          if (Log != 0 && Log->Pulse(this) == false)
360          {
361             WasCancelled = true;
362             break;
363          }
364       }      
365    }   
366
367    if (Log != 0)
368       Log->Stop();
369    
370    // Shut down the acquire bits
371    Running = false;
372    for (Queue *I = Queues; I != 0; I = I->Next)
373       I->Shutdown(false);
374
375    // Shut down the items
376    for (ItemIterator I = Items.begin(); I != Items.end(); I++)
377       (*I)->Finished(); 
378    
379    if (_error->PendingError())
380       return Failed;
381    if (WasCancelled)
382       return Cancelled;
383    return Continue;
384 }
385                                                                         /*}}}*/
386 // Acquire::Bump - Called when an item is dequeued                      /*{{{*/
387 // ---------------------------------------------------------------------
388 /* This routine bumps idle queues in hopes that they will be able to fetch
389    the dequeued item */
390 void pkgAcquire::Bump()
391 {
392    for (Queue *I = Queues; I != 0; I = I->Next)
393       I->Bump();
394 }
395                                                                         /*}}}*/
396 // Acquire::WorkerStep - Step to the next worker                        /*{{{*/
397 // ---------------------------------------------------------------------
398 /* Not inlined to advoid including acquire-worker.h */
399 pkgAcquire::Worker *pkgAcquire::WorkerStep(Worker *I)
400 {
401    return I->NextAcquire;
402 };
403                                                                         /*}}}*/
404 // Acquire::Clean - Cleans a directory                                  /*{{{*/
405 // ---------------------------------------------------------------------
406 /* This is a bit simplistic, it looks at every file in the dir and sees
407    if it is part of the download set. */
408 bool pkgAcquire::Clean(string Dir)
409 {
410    DIR *D = opendir(Dir.c_str());   
411    if (D == 0)
412       return _error->Errno("opendir",_("Unable to read %s"),Dir.c_str());
413    
414    string StartDir = SafeGetCWD();
415    if (chdir(Dir.c_str()) != 0)
416    {
417       closedir(D);
418       return _error->Errno("chdir",_("Unable to change to %s"),Dir.c_str());
419    }
420    
421    for (struct dirent *Dir = readdir(D); Dir != 0; Dir = readdir(D))
422    {
423       // Skip some files..
424       if (strcmp(Dir->d_name,"lock") == 0 ||
425           strcmp(Dir->d_name,"partial") == 0 ||
426           strcmp(Dir->d_name,".") == 0 ||
427           strcmp(Dir->d_name,"..") == 0)
428          continue;
429       
430       // Look in the get list
431       ItemCIterator I = Items.begin();
432       for (; I != Items.end(); I++)
433          if (flNotDir((*I)->DestFile) == Dir->d_name)
434             break;
435       
436       // Nothing found, nuke it
437       if (I == Items.end())
438          unlink(Dir->d_name);
439    };
440    
441    chdir(StartDir.c_str());
442    closedir(D);
443    return true;   
444 }
445                                                                         /*}}}*/
446 // Acquire::TotalNeeded - Number of bytes to fetch                      /*{{{*/
447 // ---------------------------------------------------------------------
448 /* This is the total number of bytes needed */
449 double pkgAcquire::TotalNeeded()
450 {
451    double Total = 0;
452    for (ItemCIterator I = ItemsBegin(); I != ItemsEnd(); I++)
453       Total += (*I)->FileSize;
454    return Total;
455 }
456                                                                         /*}}}*/
457 // Acquire::FetchNeeded - Number of bytes needed to get                 /*{{{*/
458 // ---------------------------------------------------------------------
459 /* This is the number of bytes that is not local */
460 double pkgAcquire::FetchNeeded()
461 {
462    double Total = 0;
463    for (ItemCIterator I = ItemsBegin(); I != ItemsEnd(); I++)
464       if ((*I)->Local == false)
465          Total += (*I)->FileSize;
466    return Total;
467 }
468                                                                         /*}}}*/
469 // Acquire::PartialPresent - Number of partial bytes we already have    /*{{{*/
470 // ---------------------------------------------------------------------
471 /* This is the number of bytes that is not local */
472 double pkgAcquire::PartialPresent()
473 {
474   double Total = 0;
475    for (ItemCIterator I = ItemsBegin(); I != ItemsEnd(); I++)
476       if ((*I)->Local == false)
477          Total += (*I)->PartialSize;
478    return Total;
479 }
480                                                                         /*}}}*/
481 // Acquire::UriBegin - Start iterator for the uri list                  /*{{{*/
482 // ---------------------------------------------------------------------
483 /* */
484 pkgAcquire::UriIterator pkgAcquire::UriBegin()
485 {
486    return UriIterator(Queues);
487 }
488                                                                         /*}}}*/
489 // Acquire::UriEnd - End iterator for the uri list                      /*{{{*/
490 // ---------------------------------------------------------------------
491 /* */
492 pkgAcquire::UriIterator pkgAcquire::UriEnd()
493 {
494    return UriIterator(0);
495 }
496                                                                         /*}}}*/
497
498 // Acquire::MethodConfig::MethodConfig - Constructor                    /*{{{*/
499 // ---------------------------------------------------------------------
500 /* */
501 pkgAcquire::MethodConfig::MethodConfig()
502 {
503    SingleInstance = false;
504    Pipeline = false;
505    SendConfig = false;
506    LocalOnly = false;
507    Removable = false;
508    Next = 0;
509    // CNC:2004-04-27
510    HasPreferredURI = false;
511    DonePreferredURI = false;
512    CheckMethod = "SHA1-Hash";
513 }
514                                                                         /*}}}*/
515
516 // Queue::Queue - Constructor                                           /*{{{*/
517 // ---------------------------------------------------------------------
518 /* */
519 pkgAcquire::Queue::Queue(string Name,pkgAcquire *Owner) : Name(Name), 
520             Owner(Owner)
521 {
522    Items = 0;
523    Next = 0;
524    Workers = 0;
525    MaxPipeDepth = 1;
526    PipeDepth = 0;
527 }
528                                                                         /*}}}*/
529 // Queue::~Queue - Destructor                                           /*{{{*/
530 // ---------------------------------------------------------------------
531 /* */
532 pkgAcquire::Queue::~Queue()
533 {
534    Shutdown(true);
535    
536    while (Items != 0)
537    {
538       QItem *Jnk = Items;
539       Items = Items->Next;
540       delete Jnk;
541    }
542 }
543                                                                         /*}}}*/
544 // Queue::Enqueue - Queue an item to the queue                          /*{{{*/
545 // ---------------------------------------------------------------------
546 /* */
547 void pkgAcquire::Queue::Enqueue(ItemDesc &Item)
548 {
549    QItem **I = &Items;
550    for (; *I != 0; I = &(*I)->Next);
551    
552    // Create a new item
553    QItem *Itm = new QItem;
554    *Itm = Item;
555    Itm->Next = 0;
556    *I = Itm;
557    
558    Item.Owner->QueueCounter++;   
559    if (Items->Next == 0)
560       Cycle();
561 }
562                                                                         /*}}}*/
563 // Queue::Dequeue - Remove an item from the queue                       /*{{{*/
564 // ---------------------------------------------------------------------
565 /* We return true if we hit something */
566 bool pkgAcquire::Queue::Dequeue(Item *Owner)
567 {
568    if (Owner->Status == pkgAcquire::Item::StatFetching)
569       return _error->Error("Tried to dequeue a fetching object");
570        
571    bool Res = false;
572    
573    QItem **I = &Items;
574    for (; *I != 0;)
575    {
576       if ((*I)->Owner == Owner)
577       {
578          QItem *Jnk= *I;
579          *I = (*I)->Next;
580          Owner->QueueCounter--;
581          delete Jnk;
582          Res = true;
583       }
584       else
585          I = &(*I)->Next;
586    }
587    
588    return Res;
589 }
590                                                                         /*}}}*/
591 // Queue::Startup - Start the worker processes                          /*{{{*/
592 // ---------------------------------------------------------------------
593 /* It is possible for this to be called with a pre-existing set of
594    workers. */
595 bool pkgAcquire::Queue::Startup()
596 {
597    if (Workers == 0)
598    {
599       URI U(Name);
600       pkgAcquire::MethodConfig *Cnf = Owner->GetConfig(U.Access);
601       if (Cnf == 0)
602          return false;
603       
604       Workers = new Worker(this,Cnf,Owner->Log);
605       Owner->Add(Workers);
606       if (Workers->Start() == false)
607          return false;
608       
609       /* When pipelining we commit 10 items. This needs to change when we
610          added other source retry to have cycle maintain a pipeline depth
611          on its own. */
612       if (Cnf->Pipeline == true)
613          MaxPipeDepth = 10;
614       else
615          MaxPipeDepth = 1;
616    }
617    
618    return Cycle();
619 }
620                                                                         /*}}}*/
621 // Queue::Shutdown - Shutdown the worker processes                      /*{{{*/
622 // ---------------------------------------------------------------------
623 /* If final is true then all workers are eliminated, otherwise only workers
624    that do not need cleanup are removed */
625 bool pkgAcquire::Queue::Shutdown(bool Final)
626 {
627    // Delete all of the workers
628    pkgAcquire::Worker **Cur = &Workers;
629    while (*Cur != 0)
630    {
631       pkgAcquire::Worker *Jnk = *Cur;
632       if (Final == true || Jnk->GetConf()->NeedsCleanup == false)
633       {
634          *Cur = Jnk->NextQueue;
635          Owner->Remove(Jnk);
636          delete Jnk;
637       }
638       else
639          Cur = &(*Cur)->NextQueue;      
640    }
641    
642    return true;
643 }
644                                                                         /*}}}*/
645 // Queue::FindItem - Find a URI in the item list                        /*{{{*/
646 // ---------------------------------------------------------------------
647 /* */
648 pkgAcquire::Queue::QItem *pkgAcquire::Queue::FindItem(string URI,pkgAcquire::Worker *Owner)
649 {
650    for (QItem *I = Items; I != 0; I = I->Next)
651       if (I->URI == URI && I->Worker == Owner)
652          return I;
653    return 0;
654 }
655                                                                         /*}}}*/
656 // Queue::ItemDone - Item has been completed                            /*{{{*/
657 // ---------------------------------------------------------------------
658 /* The worker signals this which causes the item to be removed from the
659    queue. If this is the last queue instance then it is removed from the
660    main queue too.*/
661 bool pkgAcquire::Queue::ItemDone(QItem *Itm)
662 {
663    PipeDepth--;
664    if (Itm->Owner->Status == pkgAcquire::Item::StatFetching)
665       Itm->Owner->Status = pkgAcquire::Item::StatDone;
666    
667    if (Itm->Owner->QueueCounter <= 1)
668       Owner->Dequeue(Itm->Owner);
669    else
670    {
671       Dequeue(Itm->Owner);
672       Owner->Bump();
673    }
674    
675    return Cycle();
676 }
677                                                                         /*}}}*/
678 // Queue::Cycle - Queue new items into the method                       /*{{{*/
679 // ---------------------------------------------------------------------
680 /* This locates a new idle item and sends it to the worker. If pipelining
681    is enabled then it keeps the pipe full. */
682 bool pkgAcquire::Queue::Cycle()
683 {
684    if (Items == 0 || Workers == 0)
685       return true;
686
687    if (PipeDepth < 0)
688       return _error->Error("Pipedepth failure");
689                            
690    // Look for a queable item
691    // CNC:2004-04-27
692    bool Preferred = (Workers->Config->HasPreferredURI == true &&
693                      Workers->Config->DonePreferredURI == false &&
694                      Workers->Config->PreferredURI.empty() == false);
695    QItem *I = Items;
696    while (PipeDepth < (signed)MaxPipeDepth)
697    {
698       // CNC:2004-04-27
699       if (Preferred) {
700          for (; I != 0; I = I->Next)
701             if (I->Owner->Status == pkgAcquire::Item::StatIdle &&
702                 strncmp(I->URI.c_str(), Workers->Config->PreferredURI.c_str(),
703                         Workers->Config->PreferredURI.length()) == 0)
704                break;
705       } else {
706          for (; I != 0; I = I->Next)
707             if (I->Owner->Status == pkgAcquire::Item::StatIdle)
708                break;
709       }
710       
711       // Nothing to do, queue is idle.
712       if (I == 0) {
713          // CNC:2004-04-27
714          if (Preferred == true) {
715             Preferred = false;
716             Workers->Config->DonePreferredURI = true;
717             I = Items;
718             continue;
719          }
720          return true;
721       }
722       
723       I->Worker = Workers;
724       I->Owner->Status = pkgAcquire::Item::StatFetching;
725       PipeDepth++;
726       if (Workers->QueueItem(I) == false)
727          return false;
728    }
729    
730    return true;
731 }
732                                                                         /*}}}*/
733 // Queue::Bump - Fetch any pending objects if we are idle               /*{{{*/
734 // ---------------------------------------------------------------------
735 /* This is called when an item in multiple queues is dequeued */
736 void pkgAcquire::Queue::Bump()
737 {
738    Cycle();
739 }
740                                                                         /*}}}*/
741
742 // AcquireStatus::pkgAcquireStatus - Constructor                        /*{{{*/
743 // ---------------------------------------------------------------------
744 /* */
745 pkgAcquireStatus::pkgAcquireStatus() : Update(true), MorePulses(false)
746 {
747    Start();
748 }
749                                                                         /*}}}*/
750 // AcquireStatus::Pulse - Called periodically                           /*{{{*/
751 // ---------------------------------------------------------------------
752 /* This computes some internal state variables for the derived classes to
753    use. It generates the current downloaded bytes and total bytes to download
754    as well as the current CPS estimate. */
755 bool pkgAcquireStatus::Pulse(pkgAcquire *Owner)
756 {
757    TotalBytes = 0;
758    CurrentBytes = 0;
759    TotalItems = 0;
760    CurrentItems = 0;
761    
762    // Compute the total number of bytes to fetch
763    unsigned int Unknown = 0;
764    unsigned int Count = 0;
765    for (pkgAcquire::ItemCIterator I = Owner->ItemsBegin(); I != Owner->ItemsEnd();
766         I++, Count++)
767    {
768       TotalItems++;
769       if ((*I)->Status == pkgAcquire::Item::StatDone)
770          CurrentItems++;
771       
772       // Totally ignore local items
773       if ((*I)->Local == true)
774          continue;
775
776       TotalBytes += (*I)->FileSize;
777       if ((*I)->Complete == true)
778          CurrentBytes += (*I)->FileSize;
779       if ((*I)->FileSize == 0 && (*I)->Complete == false)
780          Unknown++;
781    }
782    
783    // Compute the current completion
784    unsigned long ResumeSize = 0;
785    for (pkgAcquire::Worker *I = Owner->WorkersBegin(); I != 0;
786         I = Owner->WorkerStep(I))
787       if (I->CurrentItem != 0 && I->CurrentItem->Owner->Complete == false)
788       {
789          CurrentBytes += I->CurrentSize;
790          ResumeSize += I->ResumePoint;
791          
792          // Files with unknown size always have 100% completion
793          if (I->CurrentItem->Owner->FileSize == 0 && 
794              I->CurrentItem->Owner->Complete == false)
795             TotalBytes += I->CurrentSize;
796       }
797    
798    // Normalize the figures and account for unknown size downloads
799    if (TotalBytes <= 0)
800       TotalBytes = 1;
801    if (Unknown == Count)
802       TotalBytes = Unknown;
803
804    // Wha?! Is not supposed to happen.
805    if (CurrentBytes > TotalBytes)
806       CurrentBytes = TotalBytes;
807    
808    // Compute the CPS
809    struct timeval NewTime;
810    gettimeofday(&NewTime,0);
811    if (NewTime.tv_sec - Time.tv_sec == 6 && NewTime.tv_usec > Time.tv_usec ||
812        NewTime.tv_sec - Time.tv_sec > 6)
813    {    
814       double Delta = NewTime.tv_sec - Time.tv_sec + 
815                      (NewTime.tv_usec - Time.tv_usec)/1000000.0;
816       
817       // Compute the CPS value
818       if (Delta < 0.01)
819          CurrentCPS = 0;
820       else
821          CurrentCPS = ((CurrentBytes - ResumeSize) - LastBytes)/Delta;
822       LastBytes = CurrentBytes - ResumeSize;
823       ElapsedTime = (unsigned long)Delta;
824       Time = NewTime;
825    }
826
827    return true;
828 }
829                                                                         /*}}}*/
830 // AcquireStatus::Start - Called when the download is started           /*{{{*/
831 // ---------------------------------------------------------------------
832 /* We just reset the counters */
833 void pkgAcquireStatus::Start()
834 {
835    gettimeofday(&Time,0);
836    gettimeofday(&StartTime,0);
837    LastBytes = 0;
838    CurrentCPS = 0;
839    CurrentBytes = 0;
840    TotalBytes = 0;
841    FetchedBytes = 0;
842    ElapsedTime = 0;
843    TotalItems = 0;
844    CurrentItems = 0;
845 }
846                                                                         /*}}}*/
847 // AcquireStatus::Stop - Finished downloading                           /*{{{*/
848 // ---------------------------------------------------------------------
849 /* This accurately computes the elapsed time and the total overall CPS. */
850 void pkgAcquireStatus::Stop()
851 {
852    // Compute the CPS and elapsed time
853    struct timeval NewTime;
854    gettimeofday(&NewTime,0);
855    
856    double Delta = NewTime.tv_sec - StartTime.tv_sec + 
857                   (NewTime.tv_usec - StartTime.tv_usec)/1000000.0;
858    
859    // Compute the CPS value
860    if (Delta < 0.01)
861       CurrentCPS = 0;
862    else
863       CurrentCPS = FetchedBytes/Delta;
864    LastBytes = CurrentBytes;
865    ElapsedTime = (unsigned int)Delta;
866 }
867                                                                         /*}}}*/
868 // AcquireStatus::Fetched - Called when a byte set has been fetched     /*{{{*/
869 // ---------------------------------------------------------------------
870 /* This is used to get accurate final transfer rate reporting. */
871 void pkgAcquireStatus::Fetched(unsigned long Size,unsigned long Resume)
872 {   
873    FetchedBytes += Size - Resume;
874 }
875
876
877                                                                         /*}}}*/
878 // AcquireStatus::Authenticate - Called to authenticate                 /*{{{*/
879 // ---------------------------------------------------------------------
880 /* This is used to fetch a username and password from the user */
881 bool pkgAcquireStatus::Authenticate(string Desc,string &User,string &Pass)
882 {
883    /* The default behavior for all clients is to refuse to authenticate
884       interactively; this preserves backwards compatibility. */
885    return false;
886 }
887                                                                         /*}}}*/
888 // vim:sts=3:sw=3