]> git.saurik.com Git - apt.git/blame_incremental - apt-pkg/deb/dpkgpm.cc
Added deb-src example
[apt.git] / apt-pkg / deb / dpkgpm.cc
... / ...
CommitLineData
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
3// $Id: dpkgpm.cc,v 1.13 1999/07/30 06:15:14 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/* */
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 */
69bool 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. */
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
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. */
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
223 /*}}}*/
224// DPkgPM::Go - Run the sequence /*{{{*/
225// ---------------------------------------------------------------------
226/* This globs the operations and calls dpkg */
227bool pkgDPkgPM::Go()
228{
229 if (RunScripts("DPkg::Pre-Invoke") == false)
230 return false;
231
232 if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false)
233 return false;
234
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++);
239
240 // Generate the argument list
241 const char *Args[400];
242 if (J - I > 350)
243 J = I + 350;
244
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]);
249
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
264 switch (I->Op)
265 {
266 case Item::Remove:
267 Args[n++] = "--force-depends";
268 Size += strlen(Args[n-1]);
269 Args[n++] = "--force-remove-essential";
270 Size += strlen(Args[n-1]);
271 Args[n++] = "--remove";
272 Size += strlen(Args[n-1]);
273 break;
274
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
284 case Item::Configure:
285 Args[n++] = "--configure";
286 Size += strlen(Args[n-1]);
287 break;
288
289 case Item::Install:
290 Args[n++] = "--unpack";
291 Size += strlen(Args[n-1]);
292 break;
293 }
294
295 // Write in the file or package names
296 if (I->Op == Item::Install)
297 {
298 for (;I != J && Size < 1024; I++)
299 {
300 if (I->File[0] != '/')
301 return _error->Error("Internal Error, Pathname to install is not absolute '%s'",I->File.c_str());
302 Args[n++] = I->File.c_str();
303 Size += strlen(Args[n-1]);
304 }
305 }
306 else
307 {
308 for (;I != J && Size < 1024; I++)
309 {
310 Args[n++] = I->Pkg.Name();
311 Size += strlen(Args[n-1]);
312 }
313 }
314 Args[n] = 0;
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 }
324
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);
335
336 // Fork dpkg
337 pid_t Child = ExecFork();
338
339 // This is the child
340 if (Child == 0)
341 {
342 if (chdir(_config->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
343 _exit(100);
344
345 int Flags,dummy;
346 if ((Flags = fcntl(STDIN_FILENO,F_GETFL,dummy)) < 0)
347 _exit(100);
348
349 // Discard everything in stdin before forking dpkg
350 if (fcntl(STDIN_FILENO,F_SETFL,Flags | O_NONBLOCK) < 0)
351 _exit(100);
352
353 while (read(STDIN_FILENO,&dummy,1) == 1);
354
355 if (fcntl(STDIN_FILENO,F_SETFL,Flags & (~(long)O_NONBLOCK)) < 0)
356 _exit(100);
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);
361 execvp(Args[0],(char **)Args);
362 cerr << "Could not exec dpkg!" << endl;
363 _exit(100);
364 }
365
366 // Wait for dpkg
367 int Status = 0;
368 while (waitpid(Child,&Status,0) != Child)
369 {
370 if (errno == EINTR)
371 continue;
372 RunScripts("DPkg::Post-Invoke");
373 return _error->Errno("waitpid","Couldn't wait for subprocess");
374 }
375
376 // Restore sig int/quit
377 signal(SIGQUIT,SIG_DFL);
378 signal(SIGINT,SIG_DFL);
379
380 // Check for an error code.
381 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
382 {
383 RunScripts("DPkg::Post-Invoke");
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");
391 }
392 }
393
394 if (RunScripts("DPkg::Post-Invoke") == false)
395 return false;
396 return true;
397}
398 /*}}}*/
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 /*}}}*/