- various "cosmetics" cleanups to shut up gcc complaints on higher warning
[apt.git] / apt-pkg / acquire.cc
1 // -*- mode: c++; 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    if(chdir(StartDir.c_str()) != 0)
442       return false;
443    closedir(D);
444    return true;   
445 }
446                                                                         /*}}}*/
447 // Acquire::TotalNeeded - Number of bytes to fetch                      /*{{{*/
448 // ---------------------------------------------------------------------
449 /* This is the total number of bytes needed */
450 double pkgAcquire::TotalNeeded()
451 {
452    double Total = 0;
453    for (ItemCIterator I = ItemsBegin(); I != ItemsEnd(); I++)
454       Total += (*I)->FileSize;
455    return Total;
456 }
457                                                                         /*}}}*/
458 // Acquire::FetchNeeded - Number of bytes needed to get                 /*{{{*/
459 // ---------------------------------------------------------------------
460 /* This is the number of bytes that is not local */
461 double pkgAcquire::FetchNeeded()
462 {
463    double Total = 0;
464    for (ItemCIterator I = ItemsBegin(); I != ItemsEnd(); I++)
465       if ((*I)->Local == false)
466          Total += (*I)->FileSize;
467    return Total;
468 }
469                                                                         /*}}}*/
470 // Acquire::PartialPresent - Number of partial bytes we already have    /*{{{*/
471 // ---------------------------------------------------------------------
472 /* This is the number of bytes that is not local */
473 double pkgAcquire::PartialPresent()
474 {
475   double Total = 0;
476    for (ItemCIterator I = ItemsBegin(); I != ItemsEnd(); I++)
477       if ((*I)->Local == false)
478          Total += (*I)->PartialSize;
479    return Total;
480 }
481                                                                         /*}}}*/
482 // Acquire::UriBegin - Start iterator for the uri list                  /*{{{*/
483 // ---------------------------------------------------------------------
484 /* */
485 pkgAcquire::UriIterator pkgAcquire::UriBegin()
486 {
487    return UriIterator(Queues);
488 }
489                                                                         /*}}}*/
490 // Acquire::UriEnd - End iterator for the uri list                      /*{{{*/
491 // ---------------------------------------------------------------------
492 /* */
493 pkgAcquire::UriIterator pkgAcquire::UriEnd()
494 {
495    return UriIterator(0);
496 }
497                                                                         /*}}}*/
498
499 // Acquire::MethodConfig::MethodConfig - Constructor                    /*{{{*/
500 // ---------------------------------------------------------------------
501 /* */
502 pkgAcquire::MethodConfig::MethodConfig()
503 {
504    SingleInstance = false;
505    Pipeline = false;
506    SendConfig = false;
507    LocalOnly = false;
508    Removable = false;
509    Next = 0;
510    // CNC:2004-04-27
511    HasPreferredURI = false;
512    DonePreferredURI = false;
513    CheckMethod = "SHA1-Hash";
514 }
515                                                                         /*}}}*/
516
517 // Queue::Queue - Constructor                                           /*{{{*/
518 // ---------------------------------------------------------------------
519 /* */
520 pkgAcquire::Queue::Queue(string Name,pkgAcquire *Owner) : Name(Name), 
521             Owner(Owner)
522 {
523    Items = 0;
524    Next = 0;
525    Workers = 0;
526    MaxPipeDepth = 1;
527    PipeDepth = 0;
528 }
529                                                                         /*}}}*/
530 // Queue::~Queue - Destructor                                           /*{{{*/
531 // ---------------------------------------------------------------------
532 /* */
533 pkgAcquire::Queue::~Queue()
534 {
535    Shutdown(true);
536    
537    while (Items != 0)
538    {
539       QItem *Jnk = Items;
540       Items = Items->Next;
541       delete Jnk;
542    }
543 }
544                                                                         /*}}}*/
545 // Queue::Enqueue - Queue an item to the queue                          /*{{{*/
546 // ---------------------------------------------------------------------
547 /* */
548 void pkgAcquire::Queue::Enqueue(ItemDesc &Item)
549 {
550    QItem **I = &Items;
551    for (; *I != 0; I = &(*I)->Next);
552    
553    // Create a new item
554    QItem *Itm = new QItem;
555    *Itm = Item;
556    Itm->Next = 0;
557    *I = Itm;
558    
559    Item.Owner->QueueCounter++;   
560    if (Items->Next == 0)
561       Cycle();
562 }
563                                                                         /*}}}*/
564 // Queue::Dequeue - Remove an item from the queue                       /*{{{*/
565 // ---------------------------------------------------------------------
566 /* We return true if we hit something */
567 bool pkgAcquire::Queue::Dequeue(Item *Owner)
568 {
569    if (Owner->Status == pkgAcquire::Item::StatFetching)
570       return _error->Error("Tried to dequeue a fetching object");
571        
572    bool Res = false;
573    
574    QItem **I = &Items;
575    for (; *I != 0;)
576    {
577       if ((*I)->Owner == Owner)
578       {
579          QItem *Jnk= *I;
580          *I = (*I)->Next;
581          Owner->QueueCounter--;
582          delete Jnk;
583          Res = true;
584       }
585       else
586          I = &(*I)->Next;
587    }
588    
589    return Res;
590 }
591                                                                         /*}}}*/
592 // Queue::Startup - Start the worker processes                          /*{{{*/
593 // ---------------------------------------------------------------------
594 /* It is possible for this to be called with a pre-existing set of
595    workers. */
596 bool pkgAcquire::Queue::Startup()
597 {
598    if (Workers == 0)
599    {
600       URI U(Name);
601       pkgAcquire::MethodConfig *Cnf = Owner->GetConfig(U.Access);
602       if (Cnf == 0)
603          return false;
604       
605       Workers = new Worker(this,Cnf,Owner->Log);
606       Owner->Add(Workers);
607       if (Workers->Start() == false)
608          return false;
609       
610       /* When pipelining we commit 10 items. This needs to change when we
611          added other source retry to have cycle maintain a pipeline depth
612          on its own. */
613       if (Cnf->Pipeline == true)
614          MaxPipeDepth = 10;
615       else
616          MaxPipeDepth = 1;
617    }
618    
619    return Cycle();
620 }
621                                                                         /*}}}*/
622 // Queue::Shutdown - Shutdown the worker processes                      /*{{{*/
623 // ---------------------------------------------------------------------
624 /* If final is true then all workers are eliminated, otherwise only workers
625    that do not need cleanup are removed */
626 bool pkgAcquire::Queue::Shutdown(bool Final)
627 {
628    // Delete all of the workers
629    pkgAcquire::Worker **Cur = &Workers;
630    while (*Cur != 0)
631    {
632       pkgAcquire::Worker *Jnk = *Cur;
633       if (Final == true || Jnk->GetConf()->NeedsCleanup == false)
634       {
635          *Cur = Jnk->NextQueue;
636          Owner->Remove(Jnk);
637          delete Jnk;
638       }
639       else
640          Cur = &(*Cur)->NextQueue;      
641    }
642    
643    return true;
644 }
645                                                                         /*}}}*/
646 // Queue::FindItem - Find a URI in the item list                        /*{{{*/
647 // ---------------------------------------------------------------------
648 /* */
649 pkgAcquire::Queue::QItem *pkgAcquire::Queue::FindItem(string URI,pkgAcquire::Worker *Owner)
650 {
651    for (QItem *I = Items; I != 0; I = I->Next)
652       if (I->URI == URI && I->Worker == Owner)
653          return I;
654    return 0;
655 }
656                                                                         /*}}}*/
657 // Queue::ItemDone - Item has been completed                            /*{{{*/
658 // ---------------------------------------------------------------------
659 /* The worker signals this which causes the item to be removed from the
660    queue. If this is the last queue instance then it is removed from the
661    main queue too.*/
662 bool pkgAcquire::Queue::ItemDone(QItem *Itm)
663 {
664    PipeDepth--;
665    if (Itm->Owner->Status == pkgAcquire::Item::StatFetching)
666       Itm->Owner->Status = pkgAcquire::Item::StatDone;
667    
668    if (Itm->Owner->QueueCounter <= 1)
669       Owner->Dequeue(Itm->Owner);
670    else
671    {
672       Dequeue(Itm->Owner);
673       Owner->Bump();
674    }
675    
676    return Cycle();
677 }
678                                                                         /*}}}*/
679 // Queue::Cycle - Queue new items into the method                       /*{{{*/
680 // ---------------------------------------------------------------------
681 /* This locates a new idle item and sends it to the worker. If pipelining
682    is enabled then it keeps the pipe full. */
683 bool pkgAcquire::Queue::Cycle()
684 {
685    if (Items == 0 || Workers == 0)
686       return true;
687
688    if (PipeDepth < 0)
689       return _error->Error("Pipedepth failure");
690                            
691    // Look for a queable item
692    // CNC:2004-04-27
693    bool Preferred = (Workers->Config->HasPreferredURI == true &&
694                      Workers->Config->DonePreferredURI == false &&
695                      Workers->Config->PreferredURI.empty() == false);
696    QItem *I = Items;
697    while (PipeDepth < (signed)MaxPipeDepth)
698    {
699       // CNC:2004-04-27
700       if (Preferred) {
701          for (; I != 0; I = I->Next)
702             if (I->Owner->Status == pkgAcquire::Item::StatIdle &&
703                 strncmp(I->URI.c_str(), Workers->Config->PreferredURI.c_str(),
704                         Workers->Config->PreferredURI.length()) == 0)
705                break;
706       } else {
707          for (; I != 0; I = I->Next)
708             if (I->Owner->Status == pkgAcquire::Item::StatIdle)
709                break;
710       }
711       
712       // Nothing to do, queue is idle.
713       if (I == 0) {
714          // CNC:2004-04-27
715          if (Preferred == true) {
716             Preferred = false;
717             Workers->Config->DonePreferredURI = true;
718             I = Items;
719             continue;
720          }
721          return true;
722       }
723       
724       I->Worker = Workers;
725       I->Owner->Status = pkgAcquire::Item::StatFetching;
726       PipeDepth++;
727       if (Workers->QueueItem(I) == false)
728          return false;
729    }
730    
731    return true;
732 }
733                                                                         /*}}}*/
734 // Queue::Bump - Fetch any pending objects if we are idle               /*{{{*/
735 // ---------------------------------------------------------------------
736 /* This is called when an item in multiple queues is dequeued */
737 void pkgAcquire::Queue::Bump()
738 {
739    Cycle();
740 }
741                                                                         /*}}}*/
742
743 // AcquireStatus::pkgAcquireStatus - Constructor                        /*{{{*/
744 // ---------------------------------------------------------------------
745 /* */
746 pkgAcquireStatus::pkgAcquireStatus() : Update(true), MorePulses(false)
747 {
748    Start();
749 }
750                                                                         /*}}}*/
751 // AcquireStatus::Pulse - Called periodically                           /*{{{*/
752 // ---------------------------------------------------------------------
753 /* This computes some internal state variables for the derived classes to
754    use. It generates the current downloaded bytes and total bytes to download
755    as well as the current CPS estimate. */
756 bool pkgAcquireStatus::Pulse(pkgAcquire *Owner)
757 {
758    TotalBytes = 0;
759    CurrentBytes = 0;
760    TotalItems = 0;
761    CurrentItems = 0;
762    
763    // Compute the total number of bytes to fetch
764    unsigned int Unknown = 0;
765    unsigned int Count = 0;
766    for (pkgAcquire::ItemCIterator I = Owner->ItemsBegin(); I != Owner->ItemsEnd();
767         I++, Count++)
768    {
769       TotalItems++;
770       if ((*I)->Status == pkgAcquire::Item::StatDone)
771          CurrentItems++;
772       
773       // Totally ignore local items
774       if ((*I)->Local == true)
775          continue;
776
777       TotalBytes += (*I)->FileSize;
778       if ((*I)->Complete == true)
779          CurrentBytes += (*I)->FileSize;
780       if ((*I)->FileSize == 0 && (*I)->Complete == false)
781          Unknown++;
782    }
783    
784    // Compute the current completion
785    unsigned long ResumeSize = 0;
786    for (pkgAcquire::Worker *I = Owner->WorkersBegin(); I != 0;
787         I = Owner->WorkerStep(I))
788       if (I->CurrentItem != 0 && I->CurrentItem->Owner->Complete == false)
789       {
790          CurrentBytes += I->CurrentSize;
791          ResumeSize += I->ResumePoint;
792          
793          // Files with unknown size always have 100% completion
794          if (I->CurrentItem->Owner->FileSize == 0 && 
795              I->CurrentItem->Owner->Complete == false)
796             TotalBytes += I->CurrentSize;
797       }
798    
799    // Normalize the figures and account for unknown size downloads
800    if (TotalBytes <= 0)
801       TotalBytes = 1;
802    if (Unknown == Count)
803       TotalBytes = Unknown;
804
805    // Wha?! Is not supposed to happen.
806    if (CurrentBytes > TotalBytes)
807       CurrentBytes = TotalBytes;
808    
809    // Compute the CPS
810    struct timeval NewTime;
811    gettimeofday(&NewTime,0);
812    if (NewTime.tv_sec - Time.tv_sec == 6 && NewTime.tv_usec > Time.tv_usec ||
813        NewTime.tv_sec - Time.tv_sec > 6)
814    {    
815       double Delta = NewTime.tv_sec - Time.tv_sec + 
816                      (NewTime.tv_usec - Time.tv_usec)/1000000.0;
817       
818       // Compute the CPS value
819       if (Delta < 0.01)
820          CurrentCPS = 0;
821       else
822          CurrentCPS = ((CurrentBytes - ResumeSize) - LastBytes)/Delta;
823       LastBytes = CurrentBytes - ResumeSize;
824       ElapsedTime = (unsigned long)Delta;
825       Time = NewTime;
826    }
827
828    return true;
829 }
830                                                                         /*}}}*/
831 // AcquireStatus::Start - Called when the download is started           /*{{{*/
832 // ---------------------------------------------------------------------
833 /* We just reset the counters */
834 void pkgAcquireStatus::Start()
835 {
836    gettimeofday(&Time,0);
837    gettimeofday(&StartTime,0);
838    LastBytes = 0;
839    CurrentCPS = 0;
840    CurrentBytes = 0;
841    TotalBytes = 0;
842    FetchedBytes = 0;
843    ElapsedTime = 0;
844    TotalItems = 0;
845    CurrentItems = 0;
846 }
847                                                                         /*}}}*/
848 // AcquireStatus::Stop - Finished downloading                           /*{{{*/
849 // ---------------------------------------------------------------------
850 /* This accurately computes the elapsed time and the total overall CPS. */
851 void pkgAcquireStatus::Stop()
852 {
853    // Compute the CPS and elapsed time
854    struct timeval NewTime;
855    gettimeofday(&NewTime,0);
856    
857    double Delta = NewTime.tv_sec - StartTime.tv_sec + 
858                   (NewTime.tv_usec - StartTime.tv_usec)/1000000.0;
859    
860    // Compute the CPS value
861    if (Delta < 0.01)
862       CurrentCPS = 0;
863    else
864       CurrentCPS = FetchedBytes/Delta;
865    LastBytes = CurrentBytes;
866    ElapsedTime = (unsigned int)Delta;
867 }
868                                                                         /*}}}*/
869 // AcquireStatus::Fetched - Called when a byte set has been fetched     /*{{{*/
870 // ---------------------------------------------------------------------
871 /* This is used to get accurate final transfer rate reporting. */
872 void pkgAcquireStatus::Fetched(unsigned long Size,unsigned long Resume)
873 {   
874    FetchedBytes += Size - Resume;
875 }
876
877
878                                                                         /*}}}*/
879 // AcquireStatus::Authenticate - Called to authenticate                 /*{{{*/
880 // ---------------------------------------------------------------------
881 /* This is used to fetch a username and password from the user */
882 bool pkgAcquireStatus::Authenticate(string Desc,string &User,string &Pass)
883 {
884    /* The default behavior for all clients is to refuse to authenticate
885       interactively; this preserves backwards compatibility. */
886    return false;
887 }
888                                                                         /*}}}*/
889 // vim:sts=3:sw=3