Early mehods
[ntk/apt.git] / apt-pkg / acquire-worker.cc
CommitLineData
0118833a
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
3b5421b4 3// $Id: acquire-worker.cc,v 1.2 1998/10/20 02:39:13 jgg Exp $
0118833a
AL
4/* ######################################################################
5
6 Acquire Worker
7
3b5421b4
AL
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
0118833a
AL
12 ##################################################################### */
13 /*}}}*/
14// Include Files /*{{{*/
15#ifdef __GNUG__
16#pragma implementation "apt-pkg/acquire-worker.h"
3b5421b4 17#endif
0118833a 18#include <apt-pkg/acquire-worker.h>
3b5421b4
AL
19#include <apt-pkg/configuration.h>
20#include <apt-pkg/error.h>
21#include <apt-pkg/fileutl.h>
22#include <strutl.h>
23
24#include <unistd.h>
25#include <signal.h>
26 /*}}}*/
27
28// Worker::Worker - Constructor for Queue startup /*{{{*/
29// ---------------------------------------------------------------------
30/* */
31pkgAcquire::Worker::Worker(Queue *Q,string Acc)
32{
33 OwnerQ = Q;
34 Config = 0;
35 Access = Acc;
36
37 Construct();
38}
39 /*}}}*/
40// Worker::Worker - Constructor for method config startup /*{{{*/
41// ---------------------------------------------------------------------
42/* */
43pkgAcquire::Worker::Worker(MethodConfig *Cnf)
44{
45 OwnerQ = 0;
46 Config = Cnf;
47 Access = Cnf->Access;
48
49 Construct();
50}
51 /*}}}*/
52// Worker::Construct - Constructor helper /*{{{*/
53// ---------------------------------------------------------------------
54/* */
55void pkgAcquire::Worker::Construct()
56{
57 Next = 0;
58 Process = -1;
59 InFd = -1;
60 OutFd = -1;
61 Debug = _config->FindB("Debug::pkgAcquire::Worker",false);
62}
63 /*}}}*/
64// Worker::~Worker - Destructor /*{{{*/
65// ---------------------------------------------------------------------
66/* */
67pkgAcquire::Worker::~Worker()
68{
69 close(InFd);
70 close(OutFd);
71
72 if (Process > 0)
73 kill(Process,SIGINT);
74}
75 /*}}}*/
76// Worker::Start - Start the worker process /*{{{*/
77// ---------------------------------------------------------------------
78/* This forks the method and inits the communication channel */
79bool pkgAcquire::Worker::Start()
80{
81 // Get the method path
82 string Method = _config->FindDir("Dir::Bin::Methods") + Access;
83 if (FileExists(Method) == false)
84 return _error->Error("The method driver %s could not be found.",Method.c_str());
85
86 if (Debug == true)
87 clog << "Starting method '" << Method << '\'' << endl;
88
89 // Create the pipes
90 int Pipes[4] = {-1,-1,-1,-1};
91 if (pipe(Pipes) != 0 || pipe(Pipes+2) != 0)
92 {
93 _error->Errno("pipe","Failed to create IPC pipe to subprocess");
94 for (int I = 0; I != 4; I++)
95 close(Pipes[I]);
96 return false;
97 }
98
99 // Fork off the process
100 Process = fork();
101 if (Process < 0)
102 {
103 cerr << "FATAL -> Failed to fork." << endl;
104 exit(100);
105 }
106
107 // Spawn the subprocess
108 if (Process == 0)
109 {
110 // Setup the FDs
111 dup2(Pipes[1],STDOUT_FILENO);
112 dup2(Pipes[2],STDIN_FILENO);
113 dup2(((filebuf *)clog.rdbuf())->fd(),STDERR_FILENO);
114 for (int I = 0; I != 4; I++)
115 close(Pipes[I]);
116 SetCloseExec(STDOUT_FILENO,false);
117 SetCloseExec(STDIN_FILENO,false);
118 SetCloseExec(STDERR_FILENO,false);
119
120 const char *Args[2];
121 Args[0] = Method.c_str();
122 Args[1] = 0;
123 execv(Args[0],(char **)Args);
124 cerr << "Failed to exec method " << Args[0] << endl;
125 exit(100);
126 }
127
128 // Fix up our FDs
129 InFd = Pipes[0];
130 OutFd = Pipes[3];
131 SetNonBlock(Pipes[0],true);
132 SetNonBlock(Pipes[3],true);
133 close(Pipes[1]);
134 close(Pipes[2]);
135
136 // Read the configuration data
137 if (WaitFd(InFd) == false ||
138 ReadMessages() == false)
139 return _error->Error("Method %s did not start correctly",Method.c_str());
140
141 RunMessages();
142
143 return true;
144}
145 /*}}}*/
146// Worker::ReadMessages - Read all pending messages into the list /*{{{*/
147// ---------------------------------------------------------------------
148/* This pulls full messages from the input FD into the message buffer.
149 It assumes that messages will not pause during transit so no
150 fancy buffering is used. */
151bool pkgAcquire::Worker::ReadMessages()
152{
153 char Buffer[4000];
154 char *End = Buffer;
155
156 while (1)
157 {
158 int Res = read(InFd,End,sizeof(Buffer) - (End-Buffer));
159
160 // Process is dead, this is kind of bad..
161 if (Res == 0)
162 {
163 if (waitpid(Process,0,0) != Process)
164 _error->Warning("I waited but nothing was there!");
165 Process = -1;
166 close(InFd);
167 close(OutFd);
168 InFd = -1;
169 OutFd = -1;
170 return false;
171 }
172
173 // No data
174 if (Res == -1)
175 return true;
176
177 End += Res;
178
179 // Look for the end of the message
180 for (char *I = Buffer; I < End; I++)
181 {
182 if (I[0] != '\n' || I[1] != '\n')
183 continue;
184
185 // Pull the message out
186 string Message(Buffer,0,I-Buffer);
187
188 // Fix up the buffer
189 for (; I < End && *I == '\n'; I++);
190 End -= I-Buffer;
191 memmove(Buffer,I,End-Buffer);
192 I = Buffer;
193
194 if (Debug == true)
195 clog << "Message " << Access << ':' << QuoteString(Message,"\n") << endl;
196
197 MessageQueue.push_back(Message);
198 }
199 if (End == Buffer)
200 return true;
201
202 if (WaitFd(InFd) == false)
203 return false;
204 }
205
206 return true;
207}
208 /*}}}*/
209
210// Worker::RunMessage - Empty the message queue /*{{{*/
211// ---------------------------------------------------------------------
212/* This takes the messages from the message queue and runs them through
213 the parsers in order. */
214bool pkgAcquire::Worker::RunMessages()
215{
216 while (MessageQueue.empty() == false)
217 {
218 string Message = MessageQueue.front();
219 MessageQueue.erase(MessageQueue.begin());
220
221 // Fetch the message number
222 char *End;
223 int Number = strtol(Message.c_str(),&End,10);
224 if (End == Message.c_str())
225 return _error->Error("Invalid message from method %s: %s",Access.c_str(),Message.c_str());
226
227 // Determine the message number and dispatch
228 switch (Number)
229 {
230 case 100:
231 if (Capabilities(Message) == false)
232 return _error->Error("Unable to process Capabilities message from %s",Access.c_str());
233 break;
234 }
235 }
236 return true;
237}
238 /*}}}*/
239// Worker::Capabilities - 100 Capabilities handler /*{{{*/
240// ---------------------------------------------------------------------
241/* This parses the capabilities message and dumps it into the configuration
242 structure. */
243bool pkgAcquire::Worker::Capabilities(string Message)
244{
245 if (Config == 0)
246 return true;
247
248 Config->Version = LookupTag(Message,"Version");
249 Config->SingleInstance = StringToBool(LookupTag(Message,"Single-Instance"),false);
250 Config->PreScan = StringToBool(LookupTag(Message,"Pre-Scan"),false);
251
252 // Some debug text
253 if (Debug == true)
254 {
255 clog << "Configured access method " << Config->Access << endl;
256 clog << "Version: " << Config->Version << " SingleInstance: " <<
257 Config->SingleInstance << " PreScan: " << Config->PreScan << endl;
258 }
259
260 return true;
261}
0118833a 262 /*}}}*/