1 // -*- mode: cpp; mode: fold -*- 
   3 // $Id: acquire-worker.cc,v 1.34 2001/05/22 04:42:54 jgg Exp $ 
   4 /* ###################################################################### 
   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  
  12    ##################################################################### */ 
  14 // Include Files                                                        /*{{{*/ 
  15 #include <apt-pkg/acquire-worker.h> 
  16 #include <apt-pkg/acquire-item.h> 
  17 #include <apt-pkg/configuration.h> 
  18 #include <apt-pkg/error.h> 
  19 #include <apt-pkg/fileutl.h> 
  20 #include <apt-pkg/strutl.h> 
  38 // Worker::Worker - Constructor for Queue startup                       /*{{{*/ 
  39 // --------------------------------------------------------------------- 
  41 pkgAcquire::Worker::Worker(Queue 
*Q
,MethodConfig 
*Cnf
, 
  42                            pkgAcquireStatus 
*Log
) : Log(Log
) 
  54 // Worker::Worker - Constructor for method config startup               /*{{{*/ 
  55 // --------------------------------------------------------------------- 
  57 pkgAcquire::Worker::Worker(MethodConfig 
*Cnf
) 
  69 // Worker::Construct - Constructor helper                               /*{{{*/ 
  70 // --------------------------------------------------------------------- 
  72 void pkgAcquire::Worker::Construct() 
  81    Debug 
= _config
->FindB("Debug::pkgAcquire::Worker",false); 
  84 // Worker::~Worker - Destructor                                         /*{{{*/ 
  85 // --------------------------------------------------------------------- 
  87 pkgAcquire::Worker::~Worker() 
  94       /* Closing of stdin is the signal to exit and die when the process 
  95          indicates it needs cleanup */ 
  96       if (Config
->NeedsCleanup 
== false) 
  98       ExecWait(Process
,Access
.c_str(),true); 
 102 // Worker::Start - Start the worker process                             /*{{{*/ 
 103 // --------------------------------------------------------------------- 
 104 /* This forks the method and inits the communication channel */ 
 105 bool pkgAcquire::Worker::Start() 
 107    // Get the method path 
 108    string Method 
= _config
->FindDir("Dir::Bin::Methods") + Access
; 
 109    if (FileExists(Method
) == false) 
 110       return _error
->Error(_("The method driver %s could not be found."),Method
.c_str()); 
 113       clog 
<< "Starting method '" << Method 
<< '\'' << endl
; 
 116    int Pipes
[4] = {-1,-1,-1,-1}; 
 117    if (pipe(Pipes
) != 0 || pipe(Pipes
+2) != 0) 
 119       _error
->Errno("pipe","Failed to create IPC pipe to subprocess"); 
 120       for (int I 
= 0; I 
!= 4; I
++) 
 124    for (int I 
= 0; I 
!= 4; I
++) 
 125       SetCloseExec(Pipes
[I
],true); 
 127    // Fork off the process 
 128    Process 
= ExecFork(); 
 132       dup2(Pipes
[1],STDOUT_FILENO
); 
 133       dup2(Pipes
[2],STDIN_FILENO
); 
 134       SetCloseExec(STDOUT_FILENO
,false); 
 135       SetCloseExec(STDIN_FILENO
,false);       
 136       SetCloseExec(STDERR_FILENO
,false); 
 139       Args
[0] = Method
.c_str(); 
 141       execv(Args
[0],(char **)Args
); 
 142       cerr 
<< "Failed to exec method " << Args
[0] << endl
; 
 149    SetNonBlock(Pipes
[0],true); 
 150    SetNonBlock(Pipes
[3],true); 
 156    // Read the configuration data 
 157    if (WaitFd(InFd
) == false || 
 158        ReadMessages() == false) 
 159       return _error
->Error(_("Method %s did not start correctly"),Method
.c_str()); 
 168 // Worker::ReadMessages - Read all pending messages into the list       /*{{{*/ 
 169 // --------------------------------------------------------------------- 
 171 bool pkgAcquire::Worker::ReadMessages() 
 173    if (::ReadMessages(InFd
,MessageQueue
) == false) 
 174       return MethodFailure(); 
 178 // Worker::RunMessage - Empty the message queue                         /*{{{*/ 
 179 // --------------------------------------------------------------------- 
 180 /* This takes the messages from the message queue and runs them through 
 181    the parsers in order. */ 
 182 bool pkgAcquire::Worker::RunMessages() 
 184    while (MessageQueue
.empty() == false) 
 186       string Message 
= MessageQueue
.front(); 
 187       MessageQueue
.erase(MessageQueue
.begin()); 
 190          clog 
<< " <- " << Access 
<< ':' << QuoteString(Message
,"\n") << endl
; 
 192       // Fetch the message number 
 194       int Number 
= strtol(Message
.c_str(),&End
,10); 
 195       if (End 
== Message
.c_str()) 
 196          return _error
->Error("Invalid message from method %s: %s",Access
.c_str(),Message
.c_str()); 
 198       string URI 
= LookupTag(Message
,"URI"); 
 199       pkgAcquire::Queue::QItem 
*Itm 
= 0; 
 200       if (URI
.empty() == false) 
 201          Itm 
= OwnerQ
->FindItem(URI
,this); 
 203       // Determine the message number and dispatch 
 208          if (Capabilities(Message
) == false) 
 209             return _error
->Error("Unable to process Capabilities message from %s",Access
.c_str()); 
 215             clog 
<< " <- (log) " << LookupTag(Message
,"Message") << endl
; 
 220          Status 
= LookupTag(Message
,"Message"); 
 228                _error
->Error("Method gave invalid 200 URI Start message"); 
 234             TotalSize 
= atoi(LookupTag(Message
,"Size","0").c_str()); 
 235             ResumePoint 
= atoi(LookupTag(Message
,"Resume-Point","0").c_str()); 
 236             Itm
->Owner
->Start(Message
,atoi(LookupTag(Message
,"Size","0").c_str())); 
 238             // Display update before completion 
 239             if (Log 
!= 0 && Log
->MorePulses 
== true) 
 240                Log
->Pulse(Itm
->Owner
->GetOwner()); 
 253                _error
->Error("Method gave invalid 201 URI Done message"); 
 257             pkgAcquire::Item 
*Owner 
= Itm
->Owner
; 
 258             pkgAcquire::ItemDesc Desc 
= *Itm
; 
 260             // Display update before completion 
 261             if (Log 
!= 0 && Log
->MorePulses 
== true) 
 262                Log
->Pulse(Owner
->GetOwner()); 
 264             OwnerQ
->ItemDone(Itm
); 
 265             if (TotalSize 
!= 0 && 
 266                 (unsigned)atoi(LookupTag(Message
,"Size","0").c_str()) != TotalSize
) 
 267                _error
->Warning("Bizarre Error - File size is not what the server reported %s %lu", 
 268                                LookupTag(Message
,"Size","0").c_str(),TotalSize
); 
 270             // see if there is a hash to verify 
 272             HashString 
expectedHash(Owner
->HashSum()); 
 273             if(!expectedHash
.empty())  
 275                string hashTag 
= expectedHash
.HashType()+"-Hash"; 
 276                string hashSum 
= LookupTag(Message
, hashTag
.c_str()); 
 278                   RecivedHash 
= expectedHash
.HashType() + ":" + hashSum
; 
 279                if(_config
->FindB("Debug::pkgAcquire::Auth", false) == true) 
 281                   clog 
<< "201 URI Done: " << Owner
->DescURI() << endl
 
 282                        << "RecivedHash: " << RecivedHash 
<< endl
 
 283                        << "ExpectedHash: " << expectedHash
.toStr()  
 287             Owner
->Done(Message
,atoi(LookupTag(Message
,"Size","0").c_str()), 
 288                         RecivedHash
.c_str(), Config
); 
 291             // Log that we are done 
 294                if (StringToBool(LookupTag(Message
,"IMS-Hit"),false) == true || 
 295                    StringToBool(LookupTag(Message
,"Alt-IMS-Hit"),false) == true) 
 297                   /* Hide 'hits' for local only sources - we also manage to 
 299                   if (Config
->LocalOnly 
== false) 
 313                _error
->Error("Method gave invalid 400 URI Failure message"); 
 317             // Display update before completion 
 318             if (Log 
!= 0 && Log
->MorePulses 
== true) 
 319                Log
->Pulse(Itm
->Owner
->GetOwner()); 
 321             pkgAcquire::Item 
*Owner 
= Itm
->Owner
; 
 322             pkgAcquire::ItemDesc Desc 
= *Itm
; 
 323             OwnerQ
->ItemDone(Itm
); 
 326             if(LookupTag(Message
,"FailReason") == "Timeout" ||  
 327                LookupTag(Message
,"FailReason") == "TmpResolveFailure" || 
 328                LookupTag(Message
,"FailReason") == "ResolveFailure" || 
 329                LookupTag(Message
,"FailReason") == "ConnectionRefused")  
 330                Owner
->Status 
= pkgAcquire::Item::StatTransientNetworkError
; 
 332             Owner
->Failed(Message
,Config
); 
 341          // 401 General Failure 
 343          _error
->Error("Method %s General failure: %s",Access
.c_str(),LookupTag(Message
,"Message").c_str()); 
 348          MediaChange(Message
);  
 355 // Worker::Capabilities - 100 Capabilities handler                      /*{{{*/ 
 356 // --------------------------------------------------------------------- 
 357 /* This parses the capabilities message and dumps it into the configuration 
 359 bool pkgAcquire::Worker::Capabilities(string Message
) 
 364    Config
->Version 
= LookupTag(Message
,"Version"); 
 365    Config
->SingleInstance 
= StringToBool(LookupTag(Message
,"Single-Instance"),false); 
 366    Config
->Pipeline 
= StringToBool(LookupTag(Message
,"Pipeline"),false); 
 367    Config
->SendConfig 
= StringToBool(LookupTag(Message
,"Send-Config"),false); 
 368    Config
->LocalOnly 
= StringToBool(LookupTag(Message
,"Local-Only"),false); 
 369    Config
->NeedsCleanup 
= StringToBool(LookupTag(Message
,"Needs-Cleanup"),false); 
 370    Config
->Removable 
= StringToBool(LookupTag(Message
,"Removable"),false); 
 375       clog 
<< "Configured access method " << Config
->Access 
<< endl
; 
 376       clog 
<< "Version:" << Config
->Version 
<< 
 377               " SingleInstance:" << Config
->SingleInstance 
<< 
 378               " Pipeline:" << Config
->Pipeline 
<<  
 379               " SendConfig:" << Config
->SendConfig 
<<  
 380               " LocalOnly: " << Config
->LocalOnly 
<<  
 381               " NeedsCleanup: " << Config
->NeedsCleanup 
<<  
 382               " Removable: " << Config
->Removable 
<< endl
; 
 388 // Worker::MediaChange - Request a media change                         /*{{{*/ 
 389 // --------------------------------------------------------------------- 
 391 bool pkgAcquire::Worker::MediaChange(string Message
) 
 393    int status_fd 
= _config
->FindI("APT::Status-Fd",-1); 
 396       string Media 
= LookupTag(Message
,"Media"); 
 397       string Drive 
= LookupTag(Message
,"Drive");  
 398       ostringstream msg
,status
; 
 399       ioprintf(msg
,_("Please insert the disc labeled: " 
 401                      "in the drive '%s' and press enter."), 
 402                Media
.c_str(),Drive
.c_str()); 
 403       status 
<< "media-change: "  // message 
 404              << Media  
<< ":"     // media 
 405              << Drive  
<< ":"     // drive 
 406              << msg
.str()         // l10n message 
 408       write(status_fd
, status
.str().c_str(), status
.str().size()); 
 411    if (Log 
== 0 || Log
->MediaChange(LookupTag(Message
,"Media"), 
 412                                     LookupTag(Message
,"Drive")) == false) 
 415       snprintf(S
,sizeof(S
),"603 Media Changed\nFailed: true\n\n"); 
 417          clog 
<< " -> " << Access 
<< ':' << QuoteString(S
,"\n") << endl
; 
 424    snprintf(S
,sizeof(S
),"603 Media Changed\n\n"); 
 426       clog 
<< " -> " << Access 
<< ':' << QuoteString(S
,"\n") << endl
; 
 432 // Worker::SendConfiguration - Send the config to the method            /*{{{*/ 
 433 // --------------------------------------------------------------------- 
 435 bool pkgAcquire::Worker::SendConfiguration() 
 437    if (Config
->SendConfig 
== false) 
 443    string Message 
= "601 Configuration\n"; 
 444    Message
.reserve(2000); 
 446    /* Write out all of the configuration directives by walking the  
 447       configuration tree */ 
 448    const Configuration::Item 
*Top 
= _config
->Tree(0); 
 451       if (Top
->Value
.empty() == false) 
 453          string Line 
= "Config-Item: " + QuoteString(Top
->FullTag(),"=\"\n") + "="; 
 454          Line 
+= QuoteString(Top
->Value
,"\n") + '\n'; 
 464       while (Top 
!= 0 && Top
->Next 
== 0) 
 472       clog 
<< " -> " << Access 
<< ':' << QuoteString(Message
,"\n") << endl
; 
 479 // Worker::QueueItem - Add an item to the outbound queue                /*{{{*/ 
 480 // --------------------------------------------------------------------- 
 481 /* Send a URI Acquire message to the method */ 
 482 bool pkgAcquire::Worker::QueueItem(pkgAcquire::Queue::QItem 
*Item
) 
 487    string Message 
= "600 URI Acquire\n"; 
 488    Message
.reserve(300); 
 489    Message 
+= "URI: " + Item
->URI
; 
 490    Message 
+= "\nFilename: " + Item
->Owner
->DestFile
; 
 491    Message 
+= Item
->Owner
->Custom600Headers(); 
 495       clog 
<< " -> " << Access 
<< ':' << QuoteString(Message
,"\n") << endl
; 
 502 // Worker::OutFdRead - Out bound FD is ready                            /*{{{*/ 
 503 // --------------------------------------------------------------------- 
 505 bool pkgAcquire::Worker::OutFdReady() 
 510       Res 
= write(OutFd
,OutQueue
.c_str(),OutQueue
.length()); 
 512    while (Res 
< 0 && errno 
== EINTR
); 
 515       return MethodFailure(); 
 517    // Hmm.. this should never happen. 
 521    OutQueue
.erase(0,Res
); 
 522    if (OutQueue
.empty() == true) 
 528 // Worker::InFdRead - In bound FD is ready                              /*{{{*/ 
 529 // --------------------------------------------------------------------- 
 531 bool pkgAcquire::Worker::InFdReady() 
 533    if (ReadMessages() == false) 
 539 // Worker::MethodFailure - Called when the method fails                 /*{{{*/ 
 540 // --------------------------------------------------------------------- 
 541 /* This is called when the method is belived to have failed, probably because 
 543 bool pkgAcquire::Worker::MethodFailure() 
 545    _error
->Error("Method %s has died unexpectedly!",Access
.c_str()); 
 547    ExecWait(Process
,Access
.c_str(),true); 
 556    MessageQueue
.erase(MessageQueue
.begin(),MessageQueue
.end()); 
 561 // Worker::Pulse - Called periodically                                  /*{{{*/ 
 562 // --------------------------------------------------------------------- 
 564 void pkgAcquire::Worker::Pulse() 
 566    if (CurrentItem 
== 0) 
 570    if (stat(CurrentItem
->Owner
->DestFile
.c_str(),&Buf
) != 0) 
 572    CurrentSize 
= Buf
.st_size
; 
 574    // Hmm? Should not happen... 
 575    if (CurrentSize 
> TotalSize 
&& TotalSize 
!= 0) 
 576       TotalSize 
= CurrentSize
; 
 579 // Worker::ItemDone - Called when the current item is finished          /*{{{*/ 
 580 // --------------------------------------------------------------------- 
 582 void pkgAcquire::Worker::ItemDone()