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