]> git.saurik.com Git - apt.git/blame - apt-pkg/deb/dpkgpm.cc
More fixes
[apt.git] / apt-pkg / deb / dpkgpm.cc
CommitLineData
03e39e59
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
0410b3d5 3// $Id: dpkgpm.cc,v 1.16 1999/12/12 03:48:36 jgg Exp $
03e39e59
AL
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>
db0c350f 25#include <stdio.h>
03e39e59
AL
26 /*}}}*/
27
28// DPkgPM::pkgDPkgPM - Constructor /*{{{*/
29// ---------------------------------------------------------------------
30/* */
31pkgDPkgPM::pkgDPkgPM(pkgDepCache &Cache) : pkgPackageManager(Cache)
32{
33}
34 /*}}}*/
35// DPkgPM::pkgDPkgPM - Destructor /*{{{*/
36// ---------------------------------------------------------------------
37/* */
38pkgDPkgPM::~pkgDPkgPM()
39{
40}
41 /*}}}*/
42// DPkgPM::Install - Install a package /*{{{*/
43// ---------------------------------------------------------------------
44/* Add an install operation to the sequence list */
45bool 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 */
57bool 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 */
fc4b5c9f 69bool pkgDPkgPM::Remove(PkgIterator Pkg,bool Purge)
03e39e59
AL
70{
71 if (Pkg.end() == true)
72 return false;
73
fc4b5c9f
AL
74 if (Purge == true)
75 List.push_back(Item(Item::Purge,Pkg));
76 else
77 List.push_back(Item(Item::Remove,Pkg));
6dd55be7
AL
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. */
85bool 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
54676e1a 93 pid_t Child = ExecFork();
6dd55be7
AL
94
95 // This is the child
96 if (Child == 0)
97 {
6dd55be7
AL
98 if (chdir("/tmp/") != 0)
99 _exit(100);
100
6dd55be7
AL
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);
ddc1d8d0 125
6dd55be7
AL
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--);
cf544e14 134 _error->Error("Problem executing scripts %s '%s'",Cnf,Opts->Value.c_str());
6dd55be7
AL
135 }
136
137 return _error->Error("Sub-process returned an error code");
138 }
139
03e39e59
AL
140 return true;
141}
db0c350f
AL
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. */
149bool 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);
90ecbd7d
AL
178
179 const char *Args[4];
db0c350f 180 Args[0] = "/bin/sh";
90ecbd7d
AL
181 Args[1] = "-c";
182 Args[2] = Opts->Value.c_str();
183 Args[3] = 0;
db0c350f
AL
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.
90ecbd7d 191 for (vector<Item>::iterator I = List.begin(); I != List.end(); I++)
db0c350f
AL
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);
90ecbd7d
AL
209 return _error->Error("Failure running script %s",Opts->Value.c_str());
210 }
db0c350f
AL
211 }
212 Fd.Close();
213
214 // Clean up the sub process
215 if (ExecWait(Process,Opts->Value.c_str()) == false)
90ecbd7d 216 return _error->Error("Failure running script %s",Opts->Value.c_str());
db0c350f
AL
217 }
218
219 return true;
220}
221
03e39e59
AL
222 /*}}}*/
223// DPkgPM::Go - Run the sequence /*{{{*/
224// ---------------------------------------------------------------------
225/* This globs the operations and calls dpkg */
226bool pkgDPkgPM::Go()
227{
6dd55be7
AL
228 if (RunScripts("DPkg::Pre-Invoke") == false)
229 return false;
db0c350f
AL
230
231 if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false)
232 return false;
233
03e39e59
AL
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++);
30e1eab5 238
03e39e59
AL
239 // Generate the argument list
240 const char *Args[400];
241 if (J - I > 350)
242 J = I + 350;
243
30e1eab5
AL
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]);
03e39e59 248
6dd55be7
AL
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
03e39e59
AL
263 switch (I->Op)
264 {
265 case Item::Remove:
266 Args[n++] = "--force-depends";
30e1eab5 267 Size += strlen(Args[n-1]);
03e39e59 268 Args[n++] = "--force-remove-essential";
30e1eab5 269 Size += strlen(Args[n-1]);
03e39e59 270 Args[n++] = "--remove";
30e1eab5 271 Size += strlen(Args[n-1]);
03e39e59
AL
272 break;
273
fc4b5c9f
AL
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
03e39e59
AL
283 case Item::Configure:
284 Args[n++] = "--configure";
30e1eab5 285 Size += strlen(Args[n-1]);
03e39e59
AL
286 break;
287
288 case Item::Install:
289 Args[n++] = "--unpack";
30e1eab5 290 Size += strlen(Args[n-1]);
03e39e59
AL
291 break;
292 }
293
294 // Write in the file or package names
295 if (I->Op == Item::Install)
30e1eab5
AL
296 {
297 for (;I != J && Size < 1024; I++)
298 {
cf544e14
AL
299 if (I->File[0] != '/')
300 return _error->Error("Internal Error, Pathname to install is not absolute '%s'",I->File.c_str());
03e39e59 301 Args[n++] = I->File.c_str();
30e1eab5
AL
302 Size += strlen(Args[n-1]);
303 }
304 }
03e39e59 305 else
30e1eab5
AL
306 {
307 for (;I != J && Size < 1024; I++)
308 {
03e39e59 309 Args[n++] = I->Pkg.Name();
30e1eab5
AL
310 Size += strlen(Args[n-1]);
311 }
312 }
03e39e59 313 Args[n] = 0;
30e1eab5
AL
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 }
03e39e59 323
03e39e59
AL
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);
6dd55be7 334
03e39e59 335 // Fork dpkg
54676e1a 336 pid_t Child = ExecFork();
6dd55be7 337
03e39e59
AL
338 // This is the child
339 if (Child == 0)
340 {
cf544e14 341 if (chdir(_config->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
0dbb95d8 342 _exit(100);
03e39e59 343
03e39e59
AL
344 int Flags,dummy;
345 if ((Flags = fcntl(STDIN_FILENO,F_GETFL,dummy)) < 0)
0dbb95d8 346 _exit(100);
03e39e59
AL
347
348 // Discard everything in stdin before forking dpkg
349 if (fcntl(STDIN_FILENO,F_SETFL,Flags | O_NONBLOCK) < 0)
0dbb95d8 350 _exit(100);
03e39e59
AL
351
352 while (read(STDIN_FILENO,&dummy,1) == 1);
353
354 if (fcntl(STDIN_FILENO,F_SETFL,Flags & (~(long)O_NONBLOCK)) < 0)
0dbb95d8 355 _exit(100);
03e39e59
AL
356
357 /* No Job Control Stop Env is a magic dpkg var that prevents it
358 from using sigstop */
101030ab 359 putenv("DPKG_NO_TSTP=yes");
d568ed2d 360 execvp(Args[0],(char **)Args);
03e39e59 361 cerr << "Could not exec dpkg!" << endl;
0dbb95d8 362 _exit(100);
03e39e59
AL
363 }
364
365 // Wait for dpkg
366 int Status = 0;
367 while (waitpid(Child,&Status,0) != Child)
368 {
369 if (errno == EINTR)
370 continue;
6dd55be7 371 RunScripts("DPkg::Post-Invoke");
03e39e59
AL
372 return _error->Errno("waitpid","Couldn't wait for subprocess");
373 }
03e39e59
AL
374
375 // Restore sig int/quit
376 signal(SIGQUIT,SIG_DFL);
377 signal(SIGINT,SIG_DFL);
6dd55be7
AL
378
379 // Check for an error code.
380 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
381 {
382 RunScripts("DPkg::Post-Invoke");
f956efb4 383 if (WIFSIGNALED(Status) != 0 && WTERMSIG(Status) == SIGSEGV)
0410b3d5 384 return _error->Error("Sub-process %s recieved a segmentation fault.",Args[0]);
f956efb4
AL
385
386 if (WIFEXITED(Status) != 0)
0410b3d5 387 return _error->Error("Sub-process %s returned an error code (%u)",Args[0],WEXITSTATUS(Status));
f956efb4 388
0410b3d5 389 return _error->Error("Sub-process %s exited unexpectedly",Args[0]);
6dd55be7 390 }
03e39e59 391 }
6dd55be7
AL
392
393 if (RunScripts("DPkg::Post-Invoke") == false)
394 return false;
03e39e59
AL
395 return true;
396}
397 /*}}}*/
281daf46
AL
398// pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/
399// ---------------------------------------------------------------------
400/* */
401void pkgDPkgPM::Reset()
402{
403 List.erase(List.begin(),List.end());
404}
405 /*}}}*/