]> git.saurik.com Git - apt.git/blame - apt-pkg/deb/dpkgpm.cc
Added deb-src example
[apt.git] / apt-pkg / deb / dpkgpm.cc
CommitLineData
03e39e59
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
db0c350f 3// $Id: dpkgpm.cc,v 1.13 1999/07/30 06:15:14 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);
178
179 const char *Args[5];
180 Args[0] = "/bin/sh";
181 Args[1] = "/bin/sh";
182 Args[2] = "-c";
183 Args[3] = Opts->Value.c_str();
184 Args[4] = 0;
185 execv(Args[0],(char **)Args);
186 _exit(100);
187 }
188 close(Pipes[0]);
189 FileFd Fd(Pipes[1]);
190
191 // Feed it the filenames.
192 for (vector<Item>::iterator I = List.begin(); I != List.end();)
193 {
194 // Only deal with packages to be installed from .deb
195 if (I->Op != Item::Install)
196 continue;
197
198 // No errors here..
199 if (I->File[0] != '/')
200 continue;
201
202 /* Feed the filename of each package that is pending install
203 into the pipe. */
204 if (Fd.Write(I->File.begin(),I->File.length()) == false ||
205 Fd.Write("\n",1) == false)
206 {
207 kill(Process,SIGINT);
208 Fd.Close();
209 ExecWait(Process,Opts->Value.c_str(),true);
210 return false;
211 }
212 }
213 Fd.Close();
214
215 // Clean up the sub process
216 if (ExecWait(Process,Opts->Value.c_str()) == false)
217 return false;
218 }
219
220 return true;
221}
222
03e39e59
AL
223 /*}}}*/
224// DPkgPM::Go - Run the sequence /*{{{*/
225// ---------------------------------------------------------------------
226/* This globs the operations and calls dpkg */
227bool pkgDPkgPM::Go()
228{
6dd55be7
AL
229 if (RunScripts("DPkg::Pre-Invoke") == false)
230 return false;
db0c350f
AL
231
232 if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false)
233 return false;
234
03e39e59
AL
235 for (vector<Item>::iterator I = List.begin(); I != List.end();)
236 {
237 vector<Item>::iterator J = I;
238 for (; J != List.end() && J->Op == I->Op; J++);
30e1eab5 239
03e39e59
AL
240 // Generate the argument list
241 const char *Args[400];
242 if (J - I > 350)
243 J = I + 350;
244
30e1eab5
AL
245 unsigned int n = 0;
246 unsigned long Size = 0;
247 Args[n++] = _config->Find("Dir::Bin::dpkg","dpkg").c_str();
248 Size += strlen(Args[n-1]);
03e39e59 249
6dd55be7
AL
250 // Stick in any custom dpkg options
251 Configuration::Item const *Opts = _config->Tree("DPkg::Options");
252 if (Opts != 0)
253 {
254 Opts = Opts->Child;
255 for (; Opts != 0; Opts = Opts->Next)
256 {
257 if (Opts->Value.empty() == true)
258 continue;
259 Args[n++] = Opts->Value.c_str();
260 Size += Opts->Value.length();
261 }
262 }
263
03e39e59
AL
264 switch (I->Op)
265 {
266 case Item::Remove:
267 Args[n++] = "--force-depends";
30e1eab5 268 Size += strlen(Args[n-1]);
03e39e59 269 Args[n++] = "--force-remove-essential";
30e1eab5 270 Size += strlen(Args[n-1]);
03e39e59 271 Args[n++] = "--remove";
30e1eab5 272 Size += strlen(Args[n-1]);
03e39e59
AL
273 break;
274
fc4b5c9f
AL
275 case Item::Purge:
276 Args[n++] = "--force-depends";
277 Size += strlen(Args[n-1]);
278 Args[n++] = "--force-remove-essential";
279 Size += strlen(Args[n-1]);
280 Args[n++] = "--purge";
281 Size += strlen(Args[n-1]);
282 break;
283
03e39e59
AL
284 case Item::Configure:
285 Args[n++] = "--configure";
30e1eab5 286 Size += strlen(Args[n-1]);
03e39e59
AL
287 break;
288
289 case Item::Install:
290 Args[n++] = "--unpack";
30e1eab5 291 Size += strlen(Args[n-1]);
03e39e59
AL
292 break;
293 }
294
295 // Write in the file or package names
296 if (I->Op == Item::Install)
30e1eab5
AL
297 {
298 for (;I != J && Size < 1024; I++)
299 {
cf544e14
AL
300 if (I->File[0] != '/')
301 return _error->Error("Internal Error, Pathname to install is not absolute '%s'",I->File.c_str());
03e39e59 302 Args[n++] = I->File.c_str();
30e1eab5
AL
303 Size += strlen(Args[n-1]);
304 }
305 }
03e39e59 306 else
30e1eab5
AL
307 {
308 for (;I != J && Size < 1024; I++)
309 {
03e39e59 310 Args[n++] = I->Pkg.Name();
30e1eab5
AL
311 Size += strlen(Args[n-1]);
312 }
313 }
03e39e59 314 Args[n] = 0;
30e1eab5
AL
315 J = I;
316
317 if (_config->FindB("Debug::pkgDPkgPM",false) == true)
318 {
319 for (unsigned int k = 0; k != n; k++)
320 clog << Args[k] << ' ';
321 clog << endl;
322 continue;
323 }
03e39e59 324
03e39e59
AL
325 cout << flush;
326 clog << flush;
327 cerr << flush;
328
329 /* Mask off sig int/quit. We do this because dpkg also does when
330 it forks scripts. What happens is that when you hit ctrl-c it sends
331 it to all processes in the group. Since dpkg ignores the signal
332 it doesn't die but we do! So we must also ignore it */
333 signal(SIGQUIT,SIG_IGN);
334 signal(SIGINT,SIG_IGN);
6dd55be7 335
03e39e59 336 // Fork dpkg
54676e1a 337 pid_t Child = ExecFork();
6dd55be7 338
03e39e59
AL
339 // This is the child
340 if (Child == 0)
341 {
cf544e14 342 if (chdir(_config->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
0dbb95d8 343 _exit(100);
03e39e59 344
03e39e59
AL
345 int Flags,dummy;
346 if ((Flags = fcntl(STDIN_FILENO,F_GETFL,dummy)) < 0)
0dbb95d8 347 _exit(100);
03e39e59
AL
348
349 // Discard everything in stdin before forking dpkg
350 if (fcntl(STDIN_FILENO,F_SETFL,Flags | O_NONBLOCK) < 0)
0dbb95d8 351 _exit(100);
03e39e59
AL
352
353 while (read(STDIN_FILENO,&dummy,1) == 1);
354
355 if (fcntl(STDIN_FILENO,F_SETFL,Flags & (~(long)O_NONBLOCK)) < 0)
0dbb95d8 356 _exit(100);
03e39e59
AL
357
358 /* No Job Control Stop Env is a magic dpkg var that prevents it
359 from using sigstop */
360 setenv("DPKG_NO_TSTP","yes",1);
d568ed2d 361 execvp(Args[0],(char **)Args);
03e39e59 362 cerr << "Could not exec dpkg!" << endl;
0dbb95d8 363 _exit(100);
03e39e59
AL
364 }
365
366 // Wait for dpkg
367 int Status = 0;
368 while (waitpid(Child,&Status,0) != Child)
369 {
370 if (errno == EINTR)
371 continue;
6dd55be7 372 RunScripts("DPkg::Post-Invoke");
03e39e59
AL
373 return _error->Errno("waitpid","Couldn't wait for subprocess");
374 }
03e39e59
AL
375
376 // Restore sig int/quit
377 signal(SIGQUIT,SIG_DFL);
378 signal(SIGINT,SIG_DFL);
6dd55be7
AL
379
380 // Check for an error code.
381 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
382 {
383 RunScripts("DPkg::Post-Invoke");
f956efb4
AL
384 if (WIFSIGNALED(Status) != 0 && WTERMSIG(Status) == SIGSEGV)
385 return _error->Error("Sub-process recieved a segmentation fault.");
386
387 if (WIFEXITED(Status) != 0)
388 return _error->Error("Sub-process returned an error code (%u)",WEXITSTATUS(Status));
389
390 return _error->Error("Sub-process exited unexpectedly");
6dd55be7 391 }
03e39e59 392 }
6dd55be7
AL
393
394 if (RunScripts("DPkg::Post-Invoke") == false)
395 return false;
03e39e59
AL
396 return true;
397}
398 /*}}}*/
281daf46
AL
399// pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/
400// ---------------------------------------------------------------------
401/* */
402void pkgDPkgPM::Reset()
403{
404 List.erase(List.begin(),List.end());
405}
406 /*}}}*/