Sync
[ntk/apt.git] / apt-pkg / acquire-worker.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: acquire-worker.cc,v 1.5 1998/10/23 00:49:58 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 #ifdef __GNUG__
16 #pragma implementation "apt-pkg/acquire-worker.h"
17 #endif
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 <strutl.h>
24
25 #include <unistd.h>
26 #include <signal.h>
27 #include <wait.h>
28 /*}}}*/
29
30 // Worker::Worker - Constructor for Queue startup /*{{{*/
31 // ---------------------------------------------------------------------
32 /* */
33 pkgAcquire::Worker::Worker(Queue *Q,MethodConfig *Cnf)
34 {
35 OwnerQ = Q;
36 Config = Cnf;
37 Access = Cnf->Access;
38 CurrentItem = 0;
39
40 Construct();
41 }
42 /*}}}*/
43 // Worker::Worker - Constructor for method config startup /*{{{*/
44 // ---------------------------------------------------------------------
45 /* */
46 pkgAcquire::Worker::Worker(MethodConfig *Cnf)
47 {
48 OwnerQ = 0;
49 Config = Cnf;
50 Access = Cnf->Access;
51 CurrentItem = 0;
52
53 Construct();
54 }
55 /*}}}*/
56 // Worker::Construct - Constructor helper /*{{{*/
57 // ---------------------------------------------------------------------
58 /* */
59 void pkgAcquire::Worker::Construct()
60 {
61 NextQueue = 0;
62 NextAcquire = 0;
63 Process = -1;
64 InFd = -1;
65 OutFd = -1;
66 OutReady = false;
67 InReady = false;
68 Debug = _config->FindB("Debug::pkgAcquire::Worker",false);
69 }
70 /*}}}*/
71 // Worker::~Worker - Destructor /*{{{*/
72 // ---------------------------------------------------------------------
73 /* */
74 pkgAcquire::Worker::~Worker()
75 {
76 close(InFd);
77 close(OutFd);
78
79 if (Process > 0)
80 {
81 kill(Process,SIGINT);
82 if (waitpid(Process,0,0) != Process)
83 _error->Warning("I waited but nothing was there!");
84 }
85 }
86 /*}}}*/
87 // Worker::Start - Start the worker process /*{{{*/
88 // ---------------------------------------------------------------------
89 /* This forks the method and inits the communication channel */
90 bool pkgAcquire::Worker::Start()
91 {
92 // Get the method path
93 string Method = _config->FindDir("Dir::Bin::Methods") + Access;
94 if (FileExists(Method) == false)
95 return _error->Error("The method driver %s could not be found.",Method.c_str());
96
97 if (Debug == true)
98 clog << "Starting method '" << Method << '\'' << endl;
99
100 // Create the pipes
101 int Pipes[4] = {-1,-1,-1,-1};
102 if (pipe(Pipes) != 0 || pipe(Pipes+2) != 0)
103 {
104 _error->Errno("pipe","Failed to create IPC pipe to subprocess");
105 for (int I = 0; I != 4; I++)
106 close(Pipes[I]);
107 return false;
108 }
109
110 // Fork off the process
111 Process = fork();
112 if (Process < 0)
113 {
114 cerr << "FATAL -> Failed to fork." << endl;
115 exit(100);
116 }
117
118 // Spawn the subprocess
119 if (Process == 0)
120 {
121 // Setup the FDs
122 dup2(Pipes[1],STDOUT_FILENO);
123 dup2(Pipes[2],STDIN_FILENO);
124 dup2(((filebuf *)clog.rdbuf())->fd(),STDERR_FILENO);
125 for (int I = 0; I != 4; I++)
126 close(Pipes[I]);
127 SetCloseExec(STDOUT_FILENO,false);
128 SetCloseExec(STDIN_FILENO,false);
129 SetCloseExec(STDERR_FILENO,false);
130
131 const char *Args[2];
132 Args[0] = Method.c_str();
133 Args[1] = 0;
134 execv(Args[0],(char **)Args);
135 cerr << "Failed to exec method " << Args[0] << endl;
136 exit(100);
137 }
138
139 // Fix up our FDs
140 InFd = Pipes[0];
141 OutFd = Pipes[3];
142 SetNonBlock(Pipes[0],true);
143 SetNonBlock(Pipes[3],true);
144 close(Pipes[1]);
145 close(Pipes[2]);
146 OutReady = false;
147 InReady = true;
148
149 // Read the configuration data
150 if (WaitFd(InFd) == false ||
151 ReadMessages() == false)
152 return _error->Error("Method %s did not start correctly",Method.c_str());
153
154 RunMessages();
155 SendConfiguration();
156
157 return true;
158 }
159 /*}}}*/
160 // Worker::ReadMessages - Read all pending messages into the list /*{{{*/
161 // ---------------------------------------------------------------------
162 /* */
163 bool pkgAcquire::Worker::ReadMessages()
164 {
165 if (::ReadMessages(InFd,MessageQueue) == false)
166 return MethodFailure();
167 return true;
168 }
169 /*}}}*/
170 // Worker::RunMessage - Empty the message queue /*{{{*/
171 // ---------------------------------------------------------------------
172 /* This takes the messages from the message queue and runs them through
173 the parsers in order. */
174 bool pkgAcquire::Worker::RunMessages()
175 {
176 while (MessageQueue.empty() == false)
177 {
178 string Message = MessageQueue.front();
179 MessageQueue.erase(MessageQueue.begin());
180
181 if (Debug == true)
182 clog << " <- " << Access << ':' << QuoteString(Message,"\n") << endl;
183
184 // Fetch the message number
185 char *End;
186 int Number = strtol(Message.c_str(),&End,10);
187 if (End == Message.c_str())
188 return _error->Error("Invalid message from method %s: %s",Access.c_str(),Message.c_str());
189
190 // Determine the message number and dispatch
191 switch (Number)
192 {
193 // 100 Capabilities
194 case 100:
195 if (Capabilities(Message) == false)
196 return _error->Error("Unable to process Capabilities message from %s",Access.c_str());
197 break;
198
199 // 101 Log
200 case 101:
201 if (Debug == true)
202 clog << " <- (log) " << LookupTag(Message,"Message") << endl;
203 break;
204
205 // 102 Status
206 case 102:
207 Status = LookupTag(Message,"Message");
208 break;
209
210 // 200 URI Start
211 case 200:
212 break;
213
214 // 201 URI Done
215 case 201:
216 break;
217
218 // 400 URI Failure
219 case 400:
220 break;
221
222 // 401 General Failure
223 case 401:
224 _error->Error("Method %s General failure: %s",LookupTag(Message,"Message").c_str());
225 break;
226 }
227 }
228 return true;
229 }
230 /*}}}*/
231 // Worker::Capabilities - 100 Capabilities handler /*{{{*/
232 // ---------------------------------------------------------------------
233 /* This parses the capabilities message and dumps it into the configuration
234 structure. */
235 bool pkgAcquire::Worker::Capabilities(string Message)
236 {
237 if (Config == 0)
238 return true;
239
240 Config->Version = LookupTag(Message,"Version");
241 Config->SingleInstance = StringToBool(LookupTag(Message,"Single-Instance"),false);
242 Config->PreScan = StringToBool(LookupTag(Message,"Pre-Scan"),false);
243 Config->Pipeline = StringToBool(LookupTag(Message,"Pipeline"),false);
244 Config->SendConfig = StringToBool(LookupTag(Message,"Send-Config"),false);
245
246 // Some debug text
247 if (Debug == true)
248 {
249 clog << "Configured access method " << Config->Access << endl;
250 clog << "Version:" << Config->Version << " SingleInstance:" <<
251 Config->SingleInstance << " PreScan: " << Config->PreScan <<
252 " Pipeline:" << Config->Pipeline << " SendConfig:" <<
253 Config->SendConfig << endl;
254 }
255
256 return true;
257 }
258 /*}}}*/
259 // Worker::SendConfiguration - Send the config to the method /*{{{*/
260 // ---------------------------------------------------------------------
261 /* */
262 bool pkgAcquire::Worker::SendConfiguration()
263 {
264 if (Config->SendConfig == false)
265 return true;
266
267 if (OutFd == -1)
268 return false;
269
270 string Message = "601 Configuration\n";
271 Message.reserve(2000);
272
273 /* Write out all of the configuration directives by walking the
274 configuration tree */
275 const Configuration::Item *Top = _config->Tree(0);
276 for (; Top != 0;)
277 {
278 if (Top->Value.empty() == false)
279 {
280 string Line = "Config-Item: " + Top->FullTag() + "=";
281 Line += QuoteString(Top->Value,"\n") + '\n';
282 Message += Line;
283 }
284
285 if (Top->Child != 0)
286 {
287 Top = Top->Child;
288 continue;
289 }
290
291 while (Top != 0 && Top->Next == 0)
292 Top = Top->Parent;
293 if (Top != 0)
294 Top = Top->Next;
295 }
296 Message += '\n';
297
298 if (Debug == true)
299 clog << " -> " << Access << ':' << QuoteString(Message,"\n") << endl;
300 OutQueue += Message;
301 OutReady = true;
302
303 return true;
304 }
305 /*}}}*/
306 // Worker::QueueItem - Add an item to the outbound queue /*{{{*/
307 // ---------------------------------------------------------------------
308 /* Send a URI Acquire message to the method */
309 bool pkgAcquire::Worker::QueueItem(pkgAcquire::Queue::QItem *Item)
310 {
311 if (OutFd == -1)
312 return false;
313
314 string Message = "600 URI Acquire\n";
315 Message.reserve(300);
316 Message += "URI: " + Item->URI;
317 Message += "\nFilename: " + Item->Owner->DestFile;
318 Message += Item->Owner->Custom600Headers();
319 Message += "\n\n";
320
321 if (Debug == true)
322 clog << " -> " << Access << ':' << QuoteString(Message,"\n") << endl;
323 OutQueue += Message;
324 OutReady = true;
325
326 return true;
327 }
328 /*}}}*/
329 // Worker::OutFdRead - Out bound FD is ready /*{{{*/
330 // ---------------------------------------------------------------------
331 /* */
332 bool pkgAcquire::Worker::OutFdReady()
333 {
334 int Res = write(OutFd,OutQueue.begin(),OutQueue.length());
335 if (Res <= 0)
336 return MethodFailure();
337
338 // Hmm.. this should never happen.
339 if (Res < 0)
340 return true;
341
342 OutQueue.erase(0,Res);
343 if (OutQueue.empty() == true)
344 OutReady = false;
345
346 return true;
347 }
348 /*}}}*/
349 // Worker::InFdRead - In bound FD is ready /*{{{*/
350 // ---------------------------------------------------------------------
351 /* */
352 bool pkgAcquire::Worker::InFdReady()
353 {
354 if (ReadMessages() == false)
355 return false;
356 RunMessages();
357 return true;
358 }
359 /*}}}*/
360 // Worker::MethodFailure - Called when the method fails /*{{{*/
361 // ---------------------------------------------------------------------
362 /* This is called when the method is belived to have failed, probably because
363 read returned -1. */
364 bool pkgAcquire::Worker::MethodFailure()
365 {
366 cerr << "Method " << Access << " has died unexpectedly!" << endl;
367 if (waitpid(Process,0,0) != Process)
368 _error->Warning("I waited but nothing was there!");
369 Process = -1;
370 close(InFd);
371 close(OutFd);
372 InFd = -1;
373 OutFd = -1;
374 OutReady = false;
375 InReady = false;
376 OutQueue = string();
377 MessageQueue.erase(MessageQueue.begin(),MessageQueue.end());
378
379 return false;
380 }
381 /*}}}*/