apt-cache showsrc
[ntk/apt.git] / apt-pkg / deb / dpkgpm.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: dpkgpm.cc,v 1.14 1999/09/10 02:40:31 jgg Exp $
4 /* ######################################################################
5
6 DPKG Package Manager - Provide an interface to dpkg
7
8 ##################################################################### */
9 /*}}}*/
10 // Includes /*{{{*/
11 #ifdef __GNUG__
12 #pragma implementation "apt-pkg/dpkgpm.h"
13 #endif
14 #include <apt-pkg/dpkgpm.h>
15 #include <apt-pkg/error.h>
16 #include <apt-pkg/configuration.h>
17
18 #include <unistd.h>
19 #include <stdlib.h>
20 #include <fcntl.h>
21 #include <sys/types.h>
22 #include <sys/wait.h>
23 #include <signal.h>
24 #include <errno.h>
25 #include <stdio.h>
26 /*}}}*/
27
28 // DPkgPM::pkgDPkgPM - Constructor /*{{{*/
29 // ---------------------------------------------------------------------
30 /* */
31 pkgDPkgPM::pkgDPkgPM(pkgDepCache &Cache) : pkgPackageManager(Cache)
32 {
33 }
34 /*}}}*/
35 // DPkgPM::pkgDPkgPM - Destructor /*{{{*/
36 // ---------------------------------------------------------------------
37 /* */
38 pkgDPkgPM::~pkgDPkgPM()
39 {
40 }
41 /*}}}*/
42 // DPkgPM::Install - Install a package /*{{{*/
43 // ---------------------------------------------------------------------
44 /* Add an install operation to the sequence list */
45 bool pkgDPkgPM::Install(PkgIterator Pkg,string File)
46 {
47 if (File.empty() == true || Pkg.end() == true)
48 return _error->Error("Internal Error, No file name for %s",Pkg.Name());
49
50 List.push_back(Item(Item::Install,Pkg,File));
51 return true;
52 }
53 /*}}}*/
54 // DPkgPM::Configure - Configure a package /*{{{*/
55 // ---------------------------------------------------------------------
56 /* Add a configure operation to the sequence list */
57 bool pkgDPkgPM::Configure(PkgIterator Pkg)
58 {
59 if (Pkg.end() == true)
60 return false;
61
62 List.push_back(Item(Item::Configure,Pkg));
63 return true;
64 }
65 /*}}}*/
66 // DPkgPM::Remove - Remove a package /*{{{*/
67 // ---------------------------------------------------------------------
68 /* Add a remove operation to the sequence list */
69 bool pkgDPkgPM::Remove(PkgIterator Pkg,bool Purge)
70 {
71 if (Pkg.end() == true)
72 return false;
73
74 if (Purge == true)
75 List.push_back(Item(Item::Purge,Pkg));
76 else
77 List.push_back(Item(Item::Remove,Pkg));
78 return true;
79 }
80 /*}}}*/
81 // DPkgPM::RunScripts - Run a set of scripts /*{{{*/
82 // ---------------------------------------------------------------------
83 /* This looks for a list of script sto run from the configuration file,
84 each one is run with system from a forked child. */
85 bool pkgDPkgPM::RunScripts(const char *Cnf)
86 {
87 Configuration::Item const *Opts = _config->Tree(Cnf);
88 if (Opts == 0 || Opts->Child == 0)
89 return true;
90 Opts = Opts->Child;
91
92 // Fork for running the system calls
93 pid_t Child = ExecFork();
94
95 // This is the child
96 if (Child == 0)
97 {
98 if (chdir("/tmp/") != 0)
99 _exit(100);
100
101 unsigned int Count = 1;
102 for (; Opts != 0; Opts = Opts->Next, Count++)
103 {
104 if (Opts->Value.empty() == true)
105 continue;
106
107 if (system(Opts->Value.c_str()) != 0)
108 _exit(100+Count);
109 }
110 _exit(0);
111 }
112
113 // Wait for the child
114 int Status = 0;
115 while (waitpid(Child,&Status,0) != Child)
116 {
117 if (errno == EINTR)
118 continue;
119 return _error->Errno("waitpid","Couldn't wait for subprocess");
120 }
121
122 // Restore sig int/quit
123 signal(SIGQUIT,SIG_DFL);
124 signal(SIGINT,SIG_DFL);
125
126 // Check for an error code.
127 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
128 {
129 unsigned int Count = WEXITSTATUS(Status);
130 if (Count > 100)
131 {
132 Count -= 100;
133 for (; Opts != 0 && Count != 1; Opts = Opts->Next, Count--);
134 _error->Error("Problem executing scripts %s '%s'",Cnf,Opts->Value.c_str());
135 }
136
137 return _error->Error("Sub-process returned an error code");
138 }
139
140 return true;
141 }
142
143 /*}}}*/
144 // DPkgPM::RunScriptsWithPkgs - Run scripts with package names on stdin /*{{{*/
145 // ---------------------------------------------------------------------
146 /* This looks for a list of scripts to run from the configuration file
147 each one is run and is fed on standard input a list of all .deb files
148 that are due to be installed. */
149 bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf)
150 {
151 Configuration::Item const *Opts = _config->Tree(Cnf);
152 if (Opts == 0 || Opts->Child == 0)
153 return true;
154 Opts = Opts->Child;
155
156 unsigned int Count = 1;
157 for (; Opts != 0; Opts = Opts->Next, Count++)
158 {
159 if (Opts->Value.empty() == true)
160 continue;
161
162 // Create the pipes
163 int Pipes[2];
164 if (pipe(Pipes) != 0)
165 return _error->Errno("pipe","Failed to create IPC pipe to subprocess");
166 SetCloseExec(Pipes[0],true);
167 SetCloseExec(Pipes[1],true);
168
169 // Purified Fork for running the script
170 pid_t Process = ExecFork();
171 if (Process == 0)
172 {
173 // Setup the FDs
174 dup2(Pipes[0],STDIN_FILENO);
175 SetCloseExec(STDOUT_FILENO,false);
176 SetCloseExec(STDIN_FILENO,false);
177 SetCloseExec(STDERR_FILENO,false);
178
179 const char *Args[4];
180 Args[0] = "/bin/sh";
181 Args[1] = "-c";
182 Args[2] = Opts->Value.c_str();
183 Args[3] = 0;
184 execv(Args[0],(char **)Args);
185 _exit(100);
186 }
187 close(Pipes[0]);
188 FileFd Fd(Pipes[1]);
189
190 // Feed it the filenames.
191 for (vector<Item>::iterator I = List.begin(); I != List.end(); I++)
192 {
193 // Only deal with packages to be installed from .deb
194 if (I->Op != Item::Install)
195 continue;
196
197 // No errors here..
198 if (I->File[0] != '/')
199 continue;
200
201 /* Feed the filename of each package that is pending install
202 into the pipe. */
203 if (Fd.Write(I->File.begin(),I->File.length()) == false ||
204 Fd.Write("\n",1) == false)
205 {
206 kill(Process,SIGINT);
207 Fd.Close();
208 ExecWait(Process,Opts->Value.c_str(),true);
209 return _error->Error("Failure running script %s",Opts->Value.c_str());
210 }
211 }
212 Fd.Close();
213
214 // Clean up the sub process
215 if (ExecWait(Process,Opts->Value.c_str()) == false)
216 return _error->Error("Failure running script %s",Opts->Value.c_str());
217 }
218
219 return true;
220 }
221
222 /*}}}*/
223 // DPkgPM::Go - Run the sequence /*{{{*/
224 // ---------------------------------------------------------------------
225 /* This globs the operations and calls dpkg */
226 bool pkgDPkgPM::Go()
227 {
228 if (RunScripts("DPkg::Pre-Invoke") == false)
229 return false;
230
231 if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false)
232 return false;
233
234 for (vector<Item>::iterator I = List.begin(); I != List.end();)
235 {
236 vector<Item>::iterator J = I;
237 for (; J != List.end() && J->Op == I->Op; J++);
238
239 // Generate the argument list
240 const char *Args[400];
241 if (J - I > 350)
242 J = I + 350;
243
244 unsigned int n = 0;
245 unsigned long Size = 0;
246 Args[n++] = _config->Find("Dir::Bin::dpkg","dpkg").c_str();
247 Size += strlen(Args[n-1]);
248
249 // Stick in any custom dpkg options
250 Configuration::Item const *Opts = _config->Tree("DPkg::Options");
251 if (Opts != 0)
252 {
253 Opts = Opts->Child;
254 for (; Opts != 0; Opts = Opts->Next)
255 {
256 if (Opts->Value.empty() == true)
257 continue;
258 Args[n++] = Opts->Value.c_str();
259 Size += Opts->Value.length();
260 }
261 }
262
263 switch (I->Op)
264 {
265 case Item::Remove:
266 Args[n++] = "--force-depends";
267 Size += strlen(Args[n-1]);
268 Args[n++] = "--force-remove-essential";
269 Size += strlen(Args[n-1]);
270 Args[n++] = "--remove";
271 Size += strlen(Args[n-1]);
272 break;
273
274 case Item::Purge:
275 Args[n++] = "--force-depends";
276 Size += strlen(Args[n-1]);
277 Args[n++] = "--force-remove-essential";
278 Size += strlen(Args[n-1]);
279 Args[n++] = "--purge";
280 Size += strlen(Args[n-1]);
281 break;
282
283 case Item::Configure:
284 Args[n++] = "--configure";
285 Size += strlen(Args[n-1]);
286 break;
287
288 case Item::Install:
289 Args[n++] = "--unpack";
290 Size += strlen(Args[n-1]);
291 break;
292 }
293
294 // Write in the file or package names
295 if (I->Op == Item::Install)
296 {
297 for (;I != J && Size < 1024; I++)
298 {
299 if (I->File[0] != '/')
300 return _error->Error("Internal Error, Pathname to install is not absolute '%s'",I->File.c_str());
301 Args[n++] = I->File.c_str();
302 Size += strlen(Args[n-1]);
303 }
304 }
305 else
306 {
307 for (;I != J && Size < 1024; I++)
308 {
309 Args[n++] = I->Pkg.Name();
310 Size += strlen(Args[n-1]);
311 }
312 }
313 Args[n] = 0;
314 J = I;
315
316 if (_config->FindB("Debug::pkgDPkgPM",false) == true)
317 {
318 for (unsigned int k = 0; k != n; k++)
319 clog << Args[k] << ' ';
320 clog << endl;
321 continue;
322 }
323
324 cout << flush;
325 clog << flush;
326 cerr << flush;
327
328 /* Mask off sig int/quit. We do this because dpkg also does when
329 it forks scripts. What happens is that when you hit ctrl-c it sends
330 it to all processes in the group. Since dpkg ignores the signal
331 it doesn't die but we do! So we must also ignore it */
332 signal(SIGQUIT,SIG_IGN);
333 signal(SIGINT,SIG_IGN);
334
335 // Fork dpkg
336 pid_t Child = ExecFork();
337
338 // This is the child
339 if (Child == 0)
340 {
341 if (chdir(_config->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
342 _exit(100);
343
344 int Flags,dummy;
345 if ((Flags = fcntl(STDIN_FILENO,F_GETFL,dummy)) < 0)
346 _exit(100);
347
348 // Discard everything in stdin before forking dpkg
349 if (fcntl(STDIN_FILENO,F_SETFL,Flags | O_NONBLOCK) < 0)
350 _exit(100);
351
352 while (read(STDIN_FILENO,&dummy,1) == 1);
353
354 if (fcntl(STDIN_FILENO,F_SETFL,Flags & (~(long)O_NONBLOCK)) < 0)
355 _exit(100);
356
357 /* No Job Control Stop Env is a magic dpkg var that prevents it
358 from using sigstop */
359 setenv("DPKG_NO_TSTP","yes",1);
360 execvp(Args[0],(char **)Args);
361 cerr << "Could not exec dpkg!" << endl;
362 _exit(100);
363 }
364
365 // Wait for dpkg
366 int Status = 0;
367 while (waitpid(Child,&Status,0) != Child)
368 {
369 if (errno == EINTR)
370 continue;
371 RunScripts("DPkg::Post-Invoke");
372 return _error->Errno("waitpid","Couldn't wait for subprocess");
373 }
374
375 // Restore sig int/quit
376 signal(SIGQUIT,SIG_DFL);
377 signal(SIGINT,SIG_DFL);
378
379 // Check for an error code.
380 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
381 {
382 RunScripts("DPkg::Post-Invoke");
383 if (WIFSIGNALED(Status) != 0 && WTERMSIG(Status) == SIGSEGV)
384 return _error->Error("Sub-process recieved a segmentation fault.");
385
386 if (WIFEXITED(Status) != 0)
387 return _error->Error("Sub-process returned an error code (%u)",WEXITSTATUS(Status));
388
389 return _error->Error("Sub-process exited unexpectedly");
390 }
391 }
392
393 if (RunScripts("DPkg::Post-Invoke") == false)
394 return false;
395 return true;
396 }
397 /*}}}*/
398 // pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/
399 // ---------------------------------------------------------------------
400 /* */
401 void pkgDPkgPM::Reset()
402 {
403 List.erase(List.begin(),List.end());
404 }
405 /*}}}*/