]>
Commit | Line | Data |
---|---|---|
da6ee469 JF |
1 | // -*- mode: cpp; mode: fold -*- |
2 | // Description /*{{{*/ | |
3 | // $Id: dpkgpm.cc,v 1.28 2004/01/27 02:25:01 mdz Exp $ | |
4 | /* ###################################################################### | |
5 | ||
6 | DPKG Package Manager - Provide an interface to dpkg | |
7 | ||
8 | ##################################################################### */ | |
9 | /*}}}*/ | |
10 | // Includes /*{{{*/ | |
da6ee469 JF |
11 | #include <apt-pkg/dpkgpm.h> |
12 | #include <apt-pkg/error.h> | |
13 | #include <apt-pkg/configuration.h> | |
14 | #include <apt-pkg/depcache.h> | |
15 | #include <apt-pkg/strutl.h> | |
00ec24d0 JF |
16 | #include <apti18n.h> |
17 | #include <apt-pkg/fileutl.h> | |
da6ee469 JF |
18 | |
19 | #include <unistd.h> | |
20 | #include <stdlib.h> | |
21 | #include <fcntl.h> | |
00ec24d0 | 22 | #include <sys/select.h> |
da6ee469 JF |
23 | #include <sys/types.h> |
24 | #include <sys/wait.h> | |
25 | #include <signal.h> | |
26 | #include <errno.h> | |
27 | #include <stdio.h> | |
00ec24d0 JF |
28 | #include <string.h> |
29 | #include <algorithm> | |
da6ee469 JF |
30 | #include <sstream> |
31 | #include <map> | |
32 | ||
00ec24d0 JF |
33 | #include <termios.h> |
34 | #include <unistd.h> | |
35 | #include <sys/ioctl.h> | |
36 | #include <sys/stat.h> | |
37 | #include <util.h> | |
38 | ||
da6ee469 JF |
39 | #include <config.h> |
40 | #include <apti18n.h> | |
41 | /*}}}*/ | |
42 | ||
43 | using namespace std; | |
44 | ||
00ec24d0 JF |
45 | namespace |
46 | { | |
47 | // Maps the dpkg "processing" info to human readable names. Entry 0 | |
48 | // of each array is the key, entry 1 is the value. | |
49 | const std::pair<const char *, const char *> PackageProcessingOps[] = { | |
50 | std::make_pair("install", N_("Installing %s")), | |
51 | std::make_pair("configure", N_("Configuring %s")), | |
52 | std::make_pair("remove", N_("Removing %s")), | |
0e5943eb | 53 | std::make_pair("purge", N_("Completely removing %s")), |
00ec24d0 JF |
54 | std::make_pair("trigproc", N_("Running post-installation trigger %s")) |
55 | }; | |
56 | ||
57 | const std::pair<const char *, const char *> * const PackageProcessingOpsBegin = PackageProcessingOps; | |
58 | const std::pair<const char *, const char *> * const PackageProcessingOpsEnd = PackageProcessingOps + sizeof(PackageProcessingOps) / sizeof(PackageProcessingOps[0]); | |
59 | ||
60 | // Predicate to test whether an entry in the PackageProcessingOps | |
61 | // array matches a string. | |
62 | class MatchProcessingOp | |
63 | { | |
64 | const char *target; | |
65 | ||
66 | public: | |
67 | MatchProcessingOp(const char *the_target) | |
68 | : target(the_target) | |
69 | { | |
70 | } | |
71 | ||
72 | bool operator()(const std::pair<const char *, const char *> &pair) const | |
73 | { | |
74 | return strcmp(pair.first, target) == 0; | |
75 | } | |
76 | }; | |
77 | } | |
78 | ||
0e5943eb JF |
79 | /* helper function to ionice the given PID |
80 | ||
81 | there is no C header for ionice yet - just the syscall interface | |
82 | so we use the binary from util-linux | |
83 | */ | |
84 | static bool | |
85 | ionice(int PID) | |
86 | { | |
87 | if (!FileExists("/usr/bin/ionice")) | |
88 | return false; | |
89 | pid_t Process = ExecFork(); | |
90 | if (Process == 0) | |
91 | { | |
92 | char buf[32]; | |
93 | snprintf(buf, sizeof(buf), "-p%d", PID); | |
94 | const char *Args[4]; | |
95 | Args[0] = "/usr/bin/ionice"; | |
96 | Args[1] = "-c3"; | |
97 | Args[2] = buf; | |
98 | Args[3] = 0; | |
99 | execv(Args[0], (char **)Args); | |
100 | } | |
101 | return ExecWait(Process, "ionice"); | |
102 | } | |
103 | ||
da6ee469 JF |
104 | // DPkgPM::pkgDPkgPM - Constructor /*{{{*/ |
105 | // --------------------------------------------------------------------- | |
106 | /* */ | |
00ec24d0 JF |
107 | pkgDPkgPM::pkgDPkgPM(pkgDepCache *Cache) |
108 | : pkgPackageManager(Cache), dpkgbuf_pos(0), | |
109 | term_out(NULL), PackagesDone(0), PackagesTotal(0) | |
da6ee469 JF |
110 | { |
111 | } | |
112 | /*}}}*/ | |
113 | // DPkgPM::pkgDPkgPM - Destructor /*{{{*/ | |
114 | // --------------------------------------------------------------------- | |
115 | /* */ | |
116 | pkgDPkgPM::~pkgDPkgPM() | |
117 | { | |
118 | } | |
119 | /*}}}*/ | |
120 | // DPkgPM::Install - Install a package /*{{{*/ | |
121 | // --------------------------------------------------------------------- | |
122 | /* Add an install operation to the sequence list */ | |
123 | bool pkgDPkgPM::Install(PkgIterator Pkg,string File) | |
124 | { | |
125 | if (File.empty() == true || Pkg.end() == true) | |
126 | return _error->Error("Internal Error, No file name for %s",Pkg.Name()); | |
127 | ||
128 | List.push_back(Item(Item::Install,Pkg,File)); | |
129 | return true; | |
130 | } | |
131 | /*}}}*/ | |
132 | // DPkgPM::Configure - Configure a package /*{{{*/ | |
133 | // --------------------------------------------------------------------- | |
134 | /* Add a configure operation to the sequence list */ | |
135 | bool pkgDPkgPM::Configure(PkgIterator Pkg) | |
136 | { | |
137 | if (Pkg.end() == true) | |
138 | return false; | |
0e5943eb JF |
139 | |
140 | List.push_back(Item(Item::Configure, Pkg)); | |
141 | ||
142 | // Use triggers for config calls if we configure "smart" | |
143 | // as otherwise Pre-Depends will not be satisfied, see #526774 | |
144 | if (_config->FindB("DPkg::TriggersPending", false) == true) | |
145 | List.push_back(Item(Item::TriggersPending, PkgIterator())); | |
146 | ||
da6ee469 JF |
147 | return true; |
148 | } | |
149 | /*}}}*/ | |
150 | // DPkgPM::Remove - Remove a package /*{{{*/ | |
151 | // --------------------------------------------------------------------- | |
152 | /* Add a remove operation to the sequence list */ | |
153 | bool pkgDPkgPM::Remove(PkgIterator Pkg,bool Purge) | |
154 | { | |
155 | if (Pkg.end() == true) | |
156 | return false; | |
157 | ||
158 | if (Purge == true) | |
159 | List.push_back(Item(Item::Purge,Pkg)); | |
160 | else | |
161 | List.push_back(Item(Item::Remove,Pkg)); | |
162 | return true; | |
163 | } | |
164 | /*}}}*/ | |
da6ee469 JF |
165 | // DPkgPM::SendV2Pkgs - Send version 2 package info /*{{{*/ |
166 | // --------------------------------------------------------------------- | |
167 | /* This is part of the helper script communication interface, it sends | |
168 | very complete information down to the other end of the pipe.*/ | |
169 | bool pkgDPkgPM::SendV2Pkgs(FILE *F) | |
170 | { | |
171 | fprintf(F,"VERSION 2\n"); | |
172 | ||
173 | /* Write out all of the configuration directives by walking the | |
174 | configuration tree */ | |
175 | const Configuration::Item *Top = _config->Tree(0); | |
176 | for (; Top != 0;) | |
177 | { | |
178 | if (Top->Value.empty() == false) | |
179 | { | |
180 | fprintf(F,"%s=%s\n", | |
181 | QuoteString(Top->FullTag(),"=\"\n").c_str(), | |
182 | QuoteString(Top->Value,"\n").c_str()); | |
183 | } | |
184 | ||
185 | if (Top->Child != 0) | |
186 | { | |
187 | Top = Top->Child; | |
188 | continue; | |
189 | } | |
190 | ||
191 | while (Top != 0 && Top->Next == 0) | |
192 | Top = Top->Parent; | |
193 | if (Top != 0) | |
194 | Top = Top->Next; | |
195 | } | |
196 | fprintf(F,"\n"); | |
197 | ||
198 | // Write out the package actions in order. | |
199 | for (vector<Item>::iterator I = List.begin(); I != List.end(); I++) | |
200 | { | |
0e5943eb JF |
201 | if(I->Pkg.end() == true) |
202 | continue; | |
203 | ||
da6ee469 JF |
204 | pkgDepCache::StateCache &S = Cache[I->Pkg]; |
205 | ||
206 | fprintf(F,"%s ",I->Pkg.Name()); | |
207 | // Current version | |
208 | if (I->Pkg->CurrentVer == 0) | |
209 | fprintf(F,"- "); | |
210 | else | |
211 | fprintf(F,"%s ",I->Pkg.CurrentVer().VerStr()); | |
212 | ||
213 | // Show the compare operator | |
214 | // Target version | |
215 | if (S.InstallVer != 0) | |
216 | { | |
217 | int Comp = 2; | |
218 | if (I->Pkg->CurrentVer != 0) | |
219 | Comp = S.InstVerIter(Cache).CompareVer(I->Pkg.CurrentVer()); | |
220 | if (Comp < 0) | |
221 | fprintf(F,"> "); | |
222 | if (Comp == 0) | |
223 | fprintf(F,"= "); | |
224 | if (Comp > 0) | |
225 | fprintf(F,"< "); | |
226 | fprintf(F,"%s ",S.InstVerIter(Cache).VerStr()); | |
227 | } | |
228 | else | |
229 | fprintf(F,"> - "); | |
230 | ||
231 | // Show the filename/operation | |
232 | if (I->Op == Item::Install) | |
233 | { | |
234 | // No errors here.. | |
235 | if (I->File[0] != '/') | |
236 | fprintf(F,"**ERROR**\n"); | |
237 | else | |
238 | fprintf(F,"%s\n",I->File.c_str()); | |
239 | } | |
240 | if (I->Op == Item::Configure) | |
241 | fprintf(F,"**CONFIGURE**\n"); | |
242 | if (I->Op == Item::Remove || | |
243 | I->Op == Item::Purge) | |
244 | fprintf(F,"**REMOVE**\n"); | |
245 | ||
246 | if (ferror(F) != 0) | |
247 | return false; | |
248 | } | |
249 | return true; | |
250 | } | |
251 | /*}}}*/ | |
252 | // DPkgPM::RunScriptsWithPkgs - Run scripts with package names on stdin /*{{{*/ | |
253 | // --------------------------------------------------------------------- | |
254 | /* This looks for a list of scripts to run from the configuration file | |
255 | each one is run and is fed on standard input a list of all .deb files | |
256 | that are due to be installed. */ | |
257 | bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf) | |
258 | { | |
259 | Configuration::Item const *Opts = _config->Tree(Cnf); | |
260 | if (Opts == 0 || Opts->Child == 0) | |
261 | return true; | |
262 | Opts = Opts->Child; | |
263 | ||
264 | unsigned int Count = 1; | |
265 | for (; Opts != 0; Opts = Opts->Next, Count++) | |
266 | { | |
267 | if (Opts->Value.empty() == true) | |
268 | continue; | |
269 | ||
270 | // Determine the protocol version | |
271 | string OptSec = Opts->Value; | |
272 | string::size_type Pos; | |
273 | if ((Pos = OptSec.find(' ')) == string::npos || Pos == 0) | |
274 | Pos = OptSec.length(); | |
275 | OptSec = "DPkg::Tools::Options::" + string(Opts->Value.c_str(),Pos); | |
276 | ||
277 | unsigned int Version = _config->FindI(OptSec+"::Version",1); | |
278 | ||
279 | // Create the pipes | |
280 | int Pipes[2]; | |
281 | if (pipe(Pipes) != 0) | |
282 | return _error->Errno("pipe","Failed to create IPC pipe to subprocess"); | |
283 | SetCloseExec(Pipes[0],true); | |
284 | SetCloseExec(Pipes[1],true); | |
285 | ||
286 | // Purified Fork for running the script | |
287 | pid_t Process = ExecFork(); | |
288 | if (Process == 0) | |
289 | { | |
290 | // Setup the FDs | |
291 | dup2(Pipes[0],STDIN_FILENO); | |
292 | SetCloseExec(STDOUT_FILENO,false); | |
293 | SetCloseExec(STDIN_FILENO,false); | |
294 | SetCloseExec(STDERR_FILENO,false); | |
295 | ||
296 | const char *Args[4]; | |
297 | Args[0] = "/bin/sh"; | |
298 | Args[1] = "-c"; | |
299 | Args[2] = Opts->Value.c_str(); | |
300 | Args[3] = 0; | |
301 | execv(Args[0],(char **)Args); | |
302 | _exit(100); | |
303 | } | |
304 | close(Pipes[0]); | |
305 | FILE *F = fdopen(Pipes[1],"w"); | |
306 | if (F == 0) | |
307 | return _error->Errno("fdopen","Faild to open new FD"); | |
308 | ||
309 | // Feed it the filenames. | |
310 | bool Die = false; | |
311 | if (Version <= 1) | |
312 | { | |
313 | for (vector<Item>::iterator I = List.begin(); I != List.end(); I++) | |
314 | { | |
315 | // Only deal with packages to be installed from .deb | |
316 | if (I->Op != Item::Install) | |
317 | continue; | |
318 | ||
319 | // No errors here.. | |
320 | if (I->File[0] != '/') | |
321 | continue; | |
322 | ||
323 | /* Feed the filename of each package that is pending install | |
324 | into the pipe. */ | |
325 | fprintf(F,"%s\n",I->File.c_str()); | |
326 | if (ferror(F) != 0) | |
327 | { | |
328 | Die = true; | |
329 | break; | |
330 | } | |
331 | } | |
332 | } | |
333 | else | |
334 | Die = !SendV2Pkgs(F); | |
335 | ||
336 | fclose(F); | |
337 | ||
338 | // Clean up the sub process | |
339 | if (ExecWait(Process,Opts->Value.c_str()) == false) | |
340 | return _error->Error("Failure running script %s",Opts->Value.c_str()); | |
341 | } | |
342 | ||
343 | return true; | |
344 | } | |
00ec24d0 JF |
345 | |
346 | /*}}}*/ | |
347 | // DPkgPM::DoStdin - Read stdin and pass to slave pty /*{{{*/ | |
348 | // --------------------------------------------------------------------- | |
349 | /* | |
350 | */ | |
351 | void pkgDPkgPM::DoStdin(int master) | |
352 | { | |
353 | unsigned char input_buf[256] = {0,}; | |
354 | ssize_t len = read(0, input_buf, sizeof(input_buf)); | |
355 | if (len) | |
356 | write(master, input_buf, len); | |
357 | else | |
358 | stdin_is_dev_null = true; | |
359 | } | |
360 | /*}}}*/ | |
361 | // DPkgPM::DoTerminalPty - Read the terminal pty and write log /*{{{*/ | |
362 | // --------------------------------------------------------------------- | |
363 | /* | |
364 | * read the terminal pty and write log | |
365 | */ | |
366 | void pkgDPkgPM::DoTerminalPty(int master) | |
367 | { | |
368 | unsigned char term_buf[1024] = {0,0, }; | |
369 | ||
370 | ssize_t len=read(master, term_buf, sizeof(term_buf)); | |
371 | if(len == -1 && errno == EIO) | |
372 | { | |
373 | // this happens when the child is about to exit, we | |
374 | // give it time to actually exit, otherwise we run | |
375 | // into a race | |
376 | usleep(500000); | |
377 | return; | |
378 | } | |
379 | if(len <= 0) | |
380 | return; | |
381 | write(1, term_buf, len); | |
382 | if(term_out) | |
383 | fwrite(term_buf, len, sizeof(char), term_out); | |
384 | } | |
da6ee469 | 385 | /*}}}*/ |
00ec24d0 JF |
386 | // DPkgPM::ProcessDpkgStatusBuf /*{{{*/ |
387 | // --------------------------------------------------------------------- | |
388 | /* | |
389 | */ | |
390 | void pkgDPkgPM::ProcessDpkgStatusLine(int OutStatusFd, char *line) | |
391 | { | |
0e5943eb | 392 | bool const Debug = _config->FindB("Debug::pkgDPkgProgressReporting",false); |
00ec24d0 JF |
393 | // the status we output |
394 | ostringstream status; | |
395 | ||
0e5943eb | 396 | if (Debug == true) |
00ec24d0 JF |
397 | std::clog << "got from dpkg '" << line << "'" << std::endl; |
398 | ||
399 | ||
400 | /* dpkg sends strings like this: | |
401 | 'status: <pkg>: <pkg qstate>' | |
402 | errors look like this: | |
403 | 'status: /var/cache/apt/archives/krecipes_0.8.1-0ubuntu1_i386.deb : error : trying to overwrite `/usr/share/doc/kde/HTML/en/krecipes/krectip.png', which is also in package krecipes-data | |
404 | and conffile-prompt like this | |
405 | 'status: conffile-prompt: conffile : 'current-conffile' 'new-conffile' useredited distedited | |
406 | ||
407 | Newer versions of dpkg sent also: | |
408 | 'processing: install: pkg' | |
409 | 'processing: configure: pkg' | |
410 | 'processing: remove: pkg' | |
0e5943eb | 411 | 'processing: purge: pkg' - but for apt is it a ignored "unknown" action |
00ec24d0 JF |
412 | 'processing: trigproc: trigger' |
413 | ||
414 | */ | |
415 | char* list[5]; | |
416 | // dpkg sends multiline error messages sometimes (see | |
417 | // #374195 for a example. we should support this by | |
418 | // either patching dpkg to not send multiline over the | |
419 | // statusfd or by rewriting the code here to deal with | |
420 | // it. for now we just ignore it and not crash | |
421 | TokSplitString(':', line, list, sizeof(list)/sizeof(list[0])); | |
422 | if( list[0] == NULL || list[1] == NULL || list[2] == NULL) | |
423 | { | |
0e5943eb | 424 | if (Debug == true) |
00ec24d0 JF |
425 | std::clog << "ignoring line: not enough ':'" << std::endl; |
426 | return; | |
427 | } | |
0e5943eb JF |
428 | const char* const pkg = list[1]; |
429 | const char* action = _strstrip(list[2]); | |
00ec24d0 JF |
430 | |
431 | // 'processing' from dpkg looks like | |
432 | // 'processing: action: pkg' | |
433 | if(strncmp(list[0], "processing", strlen("processing")) == 0) | |
434 | { | |
435 | char s[200]; | |
0e5943eb JF |
436 | const char* const pkg_or_trigger = _strstrip(list[2]); |
437 | action = _strstrip( list[1]); | |
00ec24d0 JF |
438 | const std::pair<const char *, const char *> * const iter = |
439 | std::find_if(PackageProcessingOpsBegin, | |
440 | PackageProcessingOpsEnd, | |
441 | MatchProcessingOp(action)); | |
442 | if(iter == PackageProcessingOpsEnd) | |
443 | { | |
0e5943eb JF |
444 | if (Debug == true) |
445 | std::clog << "ignoring unknown action: " << action << std::endl; | |
00ec24d0 JF |
446 | return; |
447 | } | |
448 | snprintf(s, sizeof(s), _(iter->second), pkg_or_trigger); | |
449 | ||
450 | status << "pmstatus:" << pkg_or_trigger | |
451 | << ":" << (PackagesDone/float(PackagesTotal)*100.0) | |
452 | << ":" << s | |
453 | << endl; | |
454 | if(OutStatusFd > 0) | |
455 | write(OutStatusFd, status.str().c_str(), status.str().size()); | |
0e5943eb | 456 | if (Debug == true) |
00ec24d0 JF |
457 | std::clog << "send: '" << status.str() << "'" << endl; |
458 | return; | |
459 | } | |
460 | ||
461 | if(strncmp(action,"error",strlen("error")) == 0) | |
462 | { | |
463 | status << "pmerror:" << list[1] | |
464 | << ":" << (PackagesDone/float(PackagesTotal)*100.0) | |
465 | << ":" << list[3] | |
466 | << endl; | |
467 | if(OutStatusFd > 0) | |
468 | write(OutStatusFd, status.str().c_str(), status.str().size()); | |
0e5943eb | 469 | if (Debug == true) |
00ec24d0 JF |
470 | std::clog << "send: '" << status.str() << "'" << endl; |
471 | return; | |
472 | } | |
0e5943eb | 473 | else if(strncmp(action,"conffile",strlen("conffile")) == 0) |
00ec24d0 JF |
474 | { |
475 | status << "pmconffile:" << list[1] | |
476 | << ":" << (PackagesDone/float(PackagesTotal)*100.0) | |
477 | << ":" << list[3] | |
478 | << endl; | |
479 | if(OutStatusFd > 0) | |
480 | write(OutStatusFd, status.str().c_str(), status.str().size()); | |
0e5943eb | 481 | if (Debug == true) |
00ec24d0 JF |
482 | std::clog << "send: '" << status.str() << "'" << endl; |
483 | return; | |
484 | } | |
485 | ||
0e5943eb | 486 | vector<struct DpkgState> const &states = PackageOps[pkg]; |
00ec24d0 JF |
487 | const char *next_action = NULL; |
488 | if(PackageOpsDone[pkg] < states.size()) | |
489 | next_action = states[PackageOpsDone[pkg]].state; | |
490 | // check if the package moved to the next dpkg state | |
491 | if(next_action && (strcmp(action, next_action) == 0)) | |
492 | { | |
493 | // only read the translation if there is actually a next | |
494 | // action | |
495 | const char *translation = _(states[PackageOpsDone[pkg]].str); | |
496 | char s[200]; | |
497 | snprintf(s, sizeof(s), translation, pkg); | |
498 | ||
499 | // we moved from one dpkg state to a new one, report that | |
500 | PackageOpsDone[pkg]++; | |
501 | PackagesDone++; | |
502 | // build the status str | |
503 | status << "pmstatus:" << pkg | |
504 | << ":" << (PackagesDone/float(PackagesTotal)*100.0) | |
505 | << ":" << s | |
506 | << endl; | |
507 | if(OutStatusFd > 0) | |
508 | write(OutStatusFd, status.str().c_str(), status.str().size()); | |
0e5943eb | 509 | if (Debug == true) |
00ec24d0 JF |
510 | std::clog << "send: '" << status.str() << "'" << endl; |
511 | } | |
0e5943eb | 512 | if (Debug == true) |
00ec24d0 JF |
513 | std::clog << "(parsed from dpkg) pkg: " << pkg |
514 | << " action: " << action << endl; | |
515 | } | |
0e5943eb JF |
516 | /*}}}*/ |
517 | // DPkgPM::DoDpkgStatusFd /*{{{*/ | |
00ec24d0 JF |
518 | // --------------------------------------------------------------------- |
519 | /* | |
520 | */ | |
521 | void pkgDPkgPM::DoDpkgStatusFd(int statusfd, int OutStatusFd) | |
522 | { | |
523 | char *p, *q; | |
524 | int len; | |
525 | ||
526 | len=read(statusfd, &dpkgbuf[dpkgbuf_pos], sizeof(dpkgbuf)-dpkgbuf_pos); | |
527 | dpkgbuf_pos += len; | |
528 | if(len <= 0) | |
529 | return; | |
530 | ||
531 | // process line by line if we have a buffer | |
532 | p = q = dpkgbuf; | |
533 | while((q=(char*)memchr(p, '\n', dpkgbuf+dpkgbuf_pos-p)) != NULL) | |
534 | { | |
535 | *q = 0; | |
536 | ProcessDpkgStatusLine(OutStatusFd, p); | |
537 | p=q+1; // continue with next line | |
538 | } | |
539 | ||
540 | // now move the unprocessed bits (after the final \n that is now a 0x0) | |
541 | // to the start and update dpkgbuf_pos | |
542 | void *memrchr(void const *, int, size_t); | |
543 | p = (char*)memrchr(dpkgbuf, 0, dpkgbuf_pos); | |
544 | if(p == NULL) | |
545 | return; | |
546 | ||
547 | // we are interessted in the first char *after* 0x0 | |
548 | p++; | |
549 | ||
550 | // move the unprocessed tail to the start and update pos | |
551 | memmove(dpkgbuf, p, p-dpkgbuf); | |
552 | dpkgbuf_pos = dpkgbuf+dpkgbuf_pos-p; | |
553 | } | |
554 | /*}}}*/ | |
0e5943eb | 555 | // DPkgPM::OpenLog /*{{{*/ |
00ec24d0 JF |
556 | bool pkgDPkgPM::OpenLog() |
557 | { | |
558 | string logdir = _config->FindDir("Dir::Log"); | |
559 | if(not FileExists(logdir)) | |
560 | return _error->Error(_("Directory '%s' missing"), logdir.c_str()); | |
561 | string logfile_name = flCombine(logdir, | |
562 | _config->Find("Dir::Log::Terminal")); | |
563 | if (!logfile_name.empty()) | |
564 | { | |
565 | term_out = fopen(logfile_name.c_str(),"a"); | |
0e5943eb JF |
566 | if (term_out == NULL) |
567 | return _error->WarningE(_("Could not open file '%s'"), logfile_name.c_str()); | |
568 | ||
00ec24d0 JF |
569 | chmod(logfile_name.c_str(), 0600); |
570 | // output current time | |
571 | char outstr[200]; | |
572 | time_t t = time(NULL); | |
573 | struct tm *tmp = localtime(&t); | |
574 | strftime(outstr, sizeof(outstr), "%F %T", tmp); | |
0e5943eb | 575 | fprintf(term_out, "\nLog started: %s\n", outstr); |
00ec24d0 JF |
576 | } |
577 | return true; | |
578 | } | |
0e5943eb JF |
579 | /*}}}*/ |
580 | // DPkg::CloseLog /*{{{*/ | |
00ec24d0 JF |
581 | bool pkgDPkgPM::CloseLog() |
582 | { | |
583 | if(term_out) | |
584 | { | |
585 | char outstr[200]; | |
586 | time_t t = time(NULL); | |
587 | struct tm *tmp = localtime(&t); | |
588 | strftime(outstr, sizeof(outstr), "%F %T", tmp); | |
589 | fprintf(term_out, "Log ended: "); | |
590 | fprintf(term_out, "%s", outstr); | |
591 | fprintf(term_out, "\n"); | |
592 | fclose(term_out); | |
593 | } | |
594 | term_out = NULL; | |
595 | return true; | |
596 | } | |
0e5943eb | 597 | /*}}}*/ |
00ec24d0 JF |
598 | /*{{{*/ |
599 | // This implements a racy version of pselect for those architectures | |
600 | // that don't have a working implementation. | |
601 | // FIXME: Probably can be removed on Lenny+1 | |
602 | static int racy_pselect(int nfds, fd_set *readfds, fd_set *writefds, | |
603 | fd_set *exceptfds, const struct timespec *timeout, | |
604 | const sigset_t *sigmask) | |
605 | { | |
606 | sigset_t origmask; | |
607 | struct timeval tv; | |
608 | int retval; | |
609 | ||
610 | tv.tv_sec = timeout->tv_sec; | |
611 | tv.tv_usec = timeout->tv_nsec/1000; | |
612 | ||
613 | sigprocmask(SIG_SETMASK, sigmask, &origmask); | |
614 | retval = select(nfds, readfds, writefds, exceptfds, &tv); | |
615 | sigprocmask(SIG_SETMASK, &origmask, 0); | |
616 | return retval; | |
617 | } | |
618 | /*}}}*/ | |
da6ee469 JF |
619 | // DPkgPM::Go - Run the sequence /*{{{*/ |
620 | // --------------------------------------------------------------------- | |
621 | /* This globs the operations and calls dpkg | |
622 | * | |
623 | * If it is called with "OutStatusFd" set to a valid file descriptor | |
624 | * apt will report the install progress over this fd. It maps the | |
625 | * dpkg states a package goes through to human readable (and i10n-able) | |
626 | * names and calculates a percentage for each step. | |
627 | */ | |
628 | bool pkgDPkgPM::Go(int OutStatusFd) | |
629 | { | |
0e5943eb JF |
630 | fd_set rfds; |
631 | struct timespec tv; | |
632 | sigset_t sigmask; | |
633 | sigset_t original_sigmask; | |
634 | ||
635 | unsigned int const MaxArgs = _config->FindI("Dpkg::MaxArgs",8*1024); | |
636 | unsigned int const MaxArgBytes = _config->FindI("Dpkg::MaxArgBytes",32*1024); | |
637 | bool const NoTriggers = _config->FindB("DPkg::NoTriggers", false); | |
da6ee469 JF |
638 | |
639 | if (RunScripts("DPkg::Pre-Invoke") == false) | |
640 | return false; | |
641 | ||
642 | if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false) | |
643 | return false; | |
00ec24d0 | 644 | |
0e5943eb JF |
645 | // support subpressing of triggers processing for special |
646 | // cases like d-i that runs the triggers handling manually | |
647 | bool const SmartConf = (_config->Find("PackageManager::Configure", "all") != "all"); | |
648 | bool const TriggersPending = _config->FindB("DPkg::TriggersPending", false); | |
649 | if (_config->FindB("DPkg::ConfigurePending", SmartConf) == true) | |
650 | List.push_back(Item(Item::ConfigurePending, PkgIterator())); | |
651 | ||
da6ee469 JF |
652 | // map the dpkg states to the operations that are performed |
653 | // (this is sorted in the same way as Item::Ops) | |
00ec24d0 | 654 | static const struct DpkgState DpkgStatesOpMap[][7] = { |
da6ee469 JF |
655 | // Install operation |
656 | { | |
657 | {"half-installed", N_("Preparing %s")}, | |
658 | {"unpacked", N_("Unpacking %s") }, | |
659 | {NULL, NULL} | |
660 | }, | |
661 | // Configure operation | |
662 | { | |
663 | {"unpacked",N_("Preparing to configure %s") }, | |
664 | {"half-configured", N_("Configuring %s") }, | |
665 | { "installed", N_("Installed %s")}, | |
666 | {NULL, NULL} | |
667 | }, | |
668 | // Remove operation | |
669 | { | |
670 | {"half-configured", N_("Preparing for removal of %s")}, | |
671 | {"half-installed", N_("Removing %s")}, | |
672 | {"config-files", N_("Removed %s")}, | |
673 | {NULL, NULL} | |
674 | }, | |
675 | // Purge operation | |
676 | { | |
677 | {"config-files", N_("Preparing to completely remove %s")}, | |
678 | {"not-installed", N_("Completely removed %s")}, | |
679 | {NULL, NULL} | |
680 | }, | |
681 | }; | |
682 | ||
da6ee469 JF |
683 | // init the PackageOps map, go over the list of packages that |
684 | // that will be [installed|configured|removed|purged] and add | |
685 | // them to the PackageOps map (the dpkg states it goes through) | |
686 | // and the PackageOpsTranslations (human readable strings) | |
0e5943eb | 687 | for (vector<Item>::const_iterator I = List.begin(); I != List.end();I++) |
da6ee469 | 688 | { |
0e5943eb JF |
689 | if((*I).Pkg.end() == true) |
690 | continue; | |
691 | ||
692 | string const name = (*I).Pkg.Name(); | |
da6ee469 JF |
693 | PackageOpsDone[name] = 0; |
694 | for(int i=0; (DpkgStatesOpMap[(*I).Op][i]).state != NULL; i++) | |
695 | { | |
696 | PackageOps[name].push_back(DpkgStatesOpMap[(*I).Op][i]); | |
00ec24d0 | 697 | PackagesTotal++; |
da6ee469 | 698 | } |
0e5943eb | 699 | } |
da6ee469 | 700 | |
00ec24d0 JF |
701 | stdin_is_dev_null = false; |
702 | ||
703 | // create log | |
704 | OpenLog(); | |
705 | ||
da6ee469 | 706 | // this loop is runs once per operation |
0e5943eb | 707 | for (vector<Item>::const_iterator I = List.begin(); I != List.end();) |
da6ee469 | 708 | { |
0e5943eb JF |
709 | // Do all actions with the same Op in one run |
710 | vector<Item>::const_iterator J = I; | |
711 | if (TriggersPending == true) | |
712 | for (; J != List.end(); J++) | |
713 | { | |
714 | if (J->Op == I->Op) | |
715 | continue; | |
716 | if (J->Op != Item::TriggersPending) | |
717 | break; | |
718 | vector<Item>::const_iterator T = J + 1; | |
719 | if (T != List.end() && T->Op == I->Op) | |
720 | continue; | |
721 | break; | |
722 | } | |
723 | else | |
724 | for (; J != List.end() && J->Op == I->Op; J++) | |
725 | /* nothing */; | |
da6ee469 JF |
726 | |
727 | // Generate the argument list | |
728 | const char *Args[MaxArgs + 50]; | |
0e5943eb JF |
729 | |
730 | // Now check if we are within the MaxArgs limit | |
731 | // | |
732 | // this code below is problematic, because it may happen that | |
733 | // the argument list is split in a way that A depends on B | |
734 | // and they are in the same "--configure A B" run | |
735 | // - with the split they may now be configured in different | |
736 | // runs | |
da6ee469 JF |
737 | if (J - I > (signed)MaxArgs) |
738 | J = I + MaxArgs; | |
739 | ||
740 | unsigned int n = 0; | |
741 | unsigned long Size = 0; | |
0e5943eb | 742 | string const Tmp = _config->Find("Dir::Bin::dpkg","dpkg"); |
da6ee469 JF |
743 | Args[n++] = Tmp.c_str(); |
744 | Size += strlen(Args[n-1]); | |
745 | ||
746 | // Stick in any custom dpkg options | |
747 | Configuration::Item const *Opts = _config->Tree("DPkg::Options"); | |
748 | if (Opts != 0) | |
749 | { | |
750 | Opts = Opts->Child; | |
751 | for (; Opts != 0; Opts = Opts->Next) | |
752 | { | |
753 | if (Opts->Value.empty() == true) | |
754 | continue; | |
755 | Args[n++] = Opts->Value.c_str(); | |
756 | Size += Opts->Value.length(); | |
757 | } | |
758 | } | |
759 | ||
760 | char status_fd_buf[20]; | |
761 | int fd[2]; | |
762 | pipe(fd); | |
763 | ||
764 | Args[n++] = "--status-fd"; | |
765 | Size += strlen(Args[n-1]); | |
766 | snprintf(status_fd_buf,sizeof(status_fd_buf),"%i", fd[1]); | |
767 | Args[n++] = status_fd_buf; | |
768 | Size += strlen(Args[n-1]); | |
769 | ||
770 | switch (I->Op) | |
771 | { | |
772 | case Item::Remove: | |
773 | Args[n++] = "--force-depends"; | |
774 | Size += strlen(Args[n-1]); | |
7ae8af64 JF |
775 | Args[n++] = "--force-remove-reinstreq"; |
776 | Size += strlen(Args[n-1]); | |
da6ee469 JF |
777 | Args[n++] = "--force-remove-essential"; |
778 | Size += strlen(Args[n-1]); | |
779 | Args[n++] = "--remove"; | |
780 | Size += strlen(Args[n-1]); | |
781 | break; | |
782 | ||
783 | case Item::Purge: | |
784 | Args[n++] = "--force-depends"; | |
785 | Size += strlen(Args[n-1]); | |
7ae8af64 JF |
786 | Args[n++] = "--force-remove-reinstreq"; |
787 | Size += strlen(Args[n-1]); | |
da6ee469 JF |
788 | Args[n++] = "--force-remove-essential"; |
789 | Size += strlen(Args[n-1]); | |
790 | Args[n++] = "--purge"; | |
791 | Size += strlen(Args[n-1]); | |
792 | break; | |
793 | ||
794 | case Item::Configure: | |
795 | Args[n++] = "--configure"; | |
796 | Size += strlen(Args[n-1]); | |
797 | break; | |
0e5943eb JF |
798 | |
799 | case Item::ConfigurePending: | |
800 | Args[n++] = "--configure"; | |
801 | Size += strlen(Args[n-1]); | |
802 | Args[n++] = "--pending"; | |
803 | Size += strlen(Args[n-1]); | |
804 | break; | |
805 | ||
806 | case Item::TriggersPending: | |
807 | Args[n++] = "--triggers-only"; | |
808 | Size += strlen(Args[n-1]); | |
809 | Args[n++] = "--pending"; | |
810 | Size += strlen(Args[n-1]); | |
811 | break; | |
812 | ||
da6ee469 JF |
813 | case Item::Install: |
814 | Args[n++] = "--unpack"; | |
815 | Size += strlen(Args[n-1]); | |
00ec24d0 JF |
816 | Args[n++] = "--auto-deconfigure"; |
817 | Size += strlen(Args[n-1]); | |
da6ee469 JF |
818 | break; |
819 | } | |
0e5943eb JF |
820 | |
821 | if (NoTriggers == true && I->Op != Item::TriggersPending && | |
822 | I->Op != Item::ConfigurePending) | |
823 | { | |
824 | Args[n++] = "--no-triggers"; | |
825 | Size += strlen(Args[n-1]); | |
826 | } | |
827 | ||
da6ee469 JF |
828 | // Write in the file or package names |
829 | if (I->Op == Item::Install) | |
830 | { | |
831 | for (;I != J && Size < MaxArgBytes; I++) | |
832 | { | |
833 | if (I->File[0] != '/') | |
834 | return _error->Error("Internal Error, Pathname to install is not absolute '%s'",I->File.c_str()); | |
835 | Args[n++] = I->File.c_str(); | |
836 | Size += strlen(Args[n-1]); | |
837 | } | |
838 | } | |
839 | else | |
840 | { | |
841 | for (;I != J && Size < MaxArgBytes; I++) | |
842 | { | |
0e5943eb JF |
843 | if((*I).Pkg.end() == true) |
844 | continue; | |
da6ee469 JF |
845 | Args[n++] = I->Pkg.Name(); |
846 | Size += strlen(Args[n-1]); | |
847 | } | |
848 | } | |
849 | Args[n] = 0; | |
850 | J = I; | |
851 | ||
852 | if (_config->FindB("Debug::pkgDPkgPM",false) == true) | |
853 | { | |
854 | for (unsigned int k = 0; k != n; k++) | |
855 | clog << Args[k] << ' '; | |
856 | clog << endl; | |
857 | continue; | |
858 | } | |
859 | ||
860 | cout << flush; | |
861 | clog << flush; | |
862 | cerr << flush; | |
863 | ||
864 | typedef void (*sighandler_t)(int); | |
865 | ||
866 | /* Mask off sig int/quit. We do this because dpkg also does when | |
867 | it forks scripts. What happens is that when you hit ctrl-c it sends | |
868 | it to all processes in the group. Since dpkg ignores the signal | |
869 | it doesn't die but we do! So we must also ignore it */ | |
870 | sighandler_t old_SIGQUIT = signal(SIGQUIT,SIG_IGN); | |
871 | sighandler_t old_SIGINT = signal(SIGINT,SIG_IGN); | |
00ec24d0 JF |
872 | |
873 | // ignore SIGHUP as well (debian #463030) | |
874 | sighandler_t old_SIGHUP = signal(SIGHUP,SIG_IGN); | |
875 | ||
876 | struct termios tt; | |
00ec24d0 | 877 | struct winsize win; |
0e5943eb JF |
878 | int master = -1; |
879 | int slave = -1; | |
880 | ||
881 | // if tcgetattr does not return zero there was a error | |
882 | // and we do not do any pty magic | |
883 | if (tcgetattr(0, &tt) == 0) | |
00ec24d0 | 884 | { |
0e5943eb JF |
885 | ioctl(0, TIOCGWINSZ, (char *)&win); |
886 | if (openpty(&master, &slave, NULL, &tt, &win) < 0) | |
887 | { | |
888 | const char *s = _("Can not write log, openpty() " | |
889 | "failed (/dev/pts not mounted?)\n"); | |
890 | fprintf(stderr, "%s",s); | |
891 | if(term_out) | |
892 | fprintf(term_out, "%s",s); | |
893 | master = slave = -1; | |
894 | } else { | |
895 | struct termios rtt; | |
896 | rtt = tt; | |
897 | cfmakeraw(&rtt); | |
898 | rtt.c_lflag &= ~ECHO; | |
899 | // block SIGTTOU during tcsetattr to prevent a hang if | |
900 | // the process is a member of the background process group | |
901 | // http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html | |
902 | sigemptyset(&sigmask); | |
903 | sigaddset(&sigmask, SIGTTOU); | |
904 | sigprocmask(SIG_BLOCK,&sigmask, &original_sigmask); | |
905 | tcsetattr(0, TCSAFLUSH, &rtt); | |
906 | sigprocmask(SIG_SETMASK, &original_sigmask, 0); | |
907 | } | |
00ec24d0 JF |
908 | } |
909 | ||
da6ee469 JF |
910 | // Fork dpkg |
911 | pid_t Child; | |
912 | _config->Set("APT::Keep-Fds::",fd[1]); | |
0e5943eb JF |
913 | // send status information that we are about to fork dpkg |
914 | if(OutStatusFd > 0) { | |
915 | ostringstream status; | |
916 | status << "pmstatus:dpkg-exec:" | |
917 | << (PackagesDone/float(PackagesTotal)*100.0) | |
918 | << ":" << _("Running dpkg") | |
919 | << endl; | |
920 | write(OutStatusFd, status.str().c_str(), status.str().size()); | |
921 | } | |
da6ee469 JF |
922 | Child = ExecFork(); |
923 | ||
924 | // This is the child | |
925 | if (Child == 0) | |
926 | { | |
00ec24d0 JF |
927 | if(slave >= 0 && master >= 0) |
928 | { | |
929 | setsid(); | |
930 | ioctl(slave, TIOCSCTTY, 0); | |
931 | close(master); | |
932 | dup2(slave, 0); | |
933 | dup2(slave, 1); | |
934 | dup2(slave, 2); | |
935 | close(slave); | |
936 | } | |
da6ee469 | 937 | close(fd[0]); // close the read end of the pipe |
00ec24d0 | 938 | |
0e5943eb JF |
939 | if (_config->FindDir("DPkg::Chroot-Directory","/") != "/") |
940 | { | |
941 | std::cerr << "Chrooting into " | |
942 | << _config->FindDir("DPkg::Chroot-Directory") | |
943 | << std::endl; | |
944 | if (chroot(_config->FindDir("DPkg::Chroot-Directory","/").c_str()) != 0) | |
945 | _exit(100); | |
946 | } | |
947 | ||
da6ee469 JF |
948 | if (chdir(_config->FindDir("DPkg::Run-Directory","/").c_str()) != 0) |
949 | _exit(100); | |
950 | ||
951 | if (_config->FindB("DPkg::FlushSTDIN",true) == true && isatty(STDIN_FILENO)) | |
952 | { | |
953 | int Flags,dummy; | |
954 | if ((Flags = fcntl(STDIN_FILENO,F_GETFL,dummy)) < 0) | |
955 | _exit(100); | |
956 | ||
957 | // Discard everything in stdin before forking dpkg | |
958 | if (fcntl(STDIN_FILENO,F_SETFL,Flags | O_NONBLOCK) < 0) | |
959 | _exit(100); | |
960 | ||
961 | while (read(STDIN_FILENO,&dummy,1) == 1); | |
962 | ||
963 | if (fcntl(STDIN_FILENO,F_SETFL,Flags & (~(long)O_NONBLOCK)) < 0) | |
964 | _exit(100); | |
965 | } | |
00ec24d0 | 966 | |
da6ee469 JF |
967 | /* No Job Control Stop Env is a magic dpkg var that prevents it |
968 | from using sigstop */ | |
00ec24d0 | 969 | putenv((char *)"DPKG_NO_TSTP=yes"); |
da6ee469 JF |
970 | execvp(Args[0],(char **)Args); |
971 | cerr << "Could not exec dpkg!" << endl; | |
972 | _exit(100); | |
973 | } | |
974 | ||
0e5943eb JF |
975 | // apply ionice |
976 | if (_config->FindB("DPkg::UseIoNice", false) == true) | |
977 | ionice(Child); | |
978 | ||
da6ee469 JF |
979 | // clear the Keep-Fd again |
980 | _config->Clear("APT::Keep-Fds",fd[1]); | |
981 | ||
982 | // Wait for dpkg | |
983 | int Status = 0; | |
984 | ||
985 | // we read from dpkg here | |
0e5943eb | 986 | int const _dpkgin = fd[0]; |
da6ee469 JF |
987 | close(fd[1]); // close the write end of the pipe |
988 | ||
00ec24d0 JF |
989 | if(slave > 0) |
990 | close(slave); | |
991 | ||
992 | // setups fds | |
00ec24d0 JF |
993 | sigemptyset(&sigmask); |
994 | sigprocmask(SIG_BLOCK,&sigmask,&original_sigmask); | |
da6ee469 | 995 | |
0e5943eb JF |
996 | // the result of the waitpid call |
997 | int res; | |
00ec24d0 | 998 | int select_ret; |
da6ee469 JF |
999 | while ((res=waitpid(Child,&Status, WNOHANG)) != Child) { |
1000 | if(res < 0) { | |
1001 | // FIXME: move this to a function or something, looks ugly here | |
1002 | // error handling, waitpid returned -1 | |
1003 | if (errno == EINTR) | |
1004 | continue; | |
1005 | RunScripts("DPkg::Post-Invoke"); | |
1006 | ||
1007 | // Restore sig int/quit | |
1008 | signal(SIGQUIT,old_SIGQUIT); | |
1009 | signal(SIGINT,old_SIGINT); | |
00ec24d0 | 1010 | signal(SIGHUP,old_SIGHUP); |
da6ee469 JF |
1011 | return _error->Errno("waitpid","Couldn't wait for subprocess"); |
1012 | } | |
da6ee469 | 1013 | |
00ec24d0 JF |
1014 | // wait for input or output here |
1015 | FD_ZERO(&rfds); | |
1016 | if (!stdin_is_dev_null) | |
1017 | FD_SET(0, &rfds); | |
1018 | FD_SET(_dpkgin, &rfds); | |
1019 | if(master >= 0) | |
1020 | FD_SET(master, &rfds); | |
1021 | tv.tv_sec = 1; | |
1022 | tv.tv_nsec = 0; | |
1023 | select_ret = pselect(max(master, _dpkgin)+1, &rfds, NULL, NULL, | |
1024 | &tv, &original_sigmask); | |
1025 | if (select_ret < 0 && (errno == EINVAL || errno == ENOSYS)) | |
1026 | select_ret = racy_pselect(max(master, _dpkgin)+1, &rfds, NULL, | |
1027 | NULL, &tv, &original_sigmask); | |
1028 | if (select_ret == 0) | |
1029 | continue; | |
1030 | else if (select_ret < 0 && errno == EINTR) | |
1031 | continue; | |
1032 | else if (select_ret < 0) | |
1033 | { | |
1034 | perror("select() returned error"); | |
1035 | continue; | |
1036 | } | |
da6ee469 | 1037 | |
00ec24d0 JF |
1038 | if(master >= 0 && FD_ISSET(master, &rfds)) |
1039 | DoTerminalPty(master); | |
1040 | if(master >= 0 && FD_ISSET(0, &rfds)) | |
1041 | DoStdin(master); | |
1042 | if(FD_ISSET(_dpkgin, &rfds)) | |
1043 | DoDpkgStatusFd(_dpkgin, OutStatusFd); | |
da6ee469 JF |
1044 | } |
1045 | close(_dpkgin); | |
1046 | ||
1047 | // Restore sig int/quit | |
1048 | signal(SIGQUIT,old_SIGQUIT); | |
1049 | signal(SIGINT,old_SIGINT); | |
00ec24d0 JF |
1050 | signal(SIGHUP,old_SIGHUP); |
1051 | ||
1052 | if(master >= 0) | |
1053 | { | |
1054 | tcsetattr(0, TCSAFLUSH, &tt); | |
1055 | close(master); | |
1056 | } | |
da6ee469 JF |
1057 | |
1058 | // Check for an error code. | |
1059 | if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0) | |
1060 | { | |
1061 | // if it was set to "keep-dpkg-runing" then we won't return | |
1062 | // here but keep the loop going and just report it as a error | |
1063 | // for later | |
0e5943eb | 1064 | bool const stopOnError = _config->FindB("Dpkg::StopOnError",true); |
da6ee469 JF |
1065 | |
1066 | if(stopOnError) | |
1067 | RunScripts("DPkg::Post-Invoke"); | |
1068 | ||
1069 | if (WIFSIGNALED(Status) != 0 && WTERMSIG(Status) == SIGSEGV) | |
1070 | _error->Error("Sub-process %s received a segmentation fault.",Args[0]); | |
1071 | else if (WIFEXITED(Status) != 0) | |
1072 | _error->Error("Sub-process %s returned an error code (%u)",Args[0],WEXITSTATUS(Status)); | |
1073 | else | |
1074 | _error->Error("Sub-process %s exited unexpectedly",Args[0]); | |
1075 | ||
00ec24d0 JF |
1076 | if(stopOnError) |
1077 | { | |
1078 | CloseLog(); | |
da6ee469 | 1079 | return false; |
00ec24d0 | 1080 | } |
da6ee469 JF |
1081 | } |
1082 | } | |
00ec24d0 | 1083 | CloseLog(); |
da6ee469 JF |
1084 | |
1085 | if (RunScripts("DPkg::Post-Invoke") == false) | |
1086 | return false; | |
00ec24d0 JF |
1087 | |
1088 | Cache.writeStateFile(NULL); | |
da6ee469 JF |
1089 | return true; |
1090 | } | |
1091 | /*}}}*/ | |
1092 | // pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/ | |
1093 | // --------------------------------------------------------------------- | |
1094 | /* */ | |
1095 | void pkgDPkgPM::Reset() | |
1096 | { | |
1097 | List.erase(List.begin(),List.end()); | |
1098 | } | |
1099 | /*}}}*/ | |
00ec24d0 JF |
1100 | |
1101 | /* memrchr -- find the last occurrence of a byte in a memory block | |
1102 | ||
1103 | Copyright (C) 1991, 1993, 1996, 1997, 1999, 2000, 2003, 2004, 2005, | |
1104 | 2006, 2007, 2008 Free Software Foundation, Inc. | |
1105 | ||
1106 | Based on strlen implementation by Torbjorn Granlund (tege@sics.se), | |
1107 | with help from Dan Sahlin (dan@sics.se) and | |
1108 | commentary by Jim Blandy (jimb@ai.mit.edu); | |
1109 | adaptation to memchr suggested by Dick Karpinski (dick@cca.ucsf.edu), | |
1110 | and implemented by Roland McGrath (roland@ai.mit.edu). | |
1111 | ||
1112 | This program is free software: you can redistribute it and/or modify | |
1113 | it under the terms of the GNU General Public License as published by | |
1114 | the Free Software Foundation; either version 3 of the License, or | |
1115 | (at your option) any later version. | |
1116 | ||
1117 | This program is distributed in the hope that it will be useful, | |
1118 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
1119 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
1120 | GNU General Public License for more details. | |
1121 | ||
1122 | You should have received a copy of the GNU General Public License | |
1123 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
1124 | ||
1125 | #if defined _LIBC | |
1126 | # include <memcopy.h> | |
1127 | #else | |
1128 | # include <config.h> | |
1129 | # define reg_char char | |
1130 | #endif | |
1131 | ||
1132 | #include <string.h> | |
1133 | #include <limits.h> | |
1134 | ||
1135 | #undef __memrchr | |
1136 | #ifdef _LIBC | |
1137 | # undef memrchr | |
1138 | #endif | |
1139 | ||
1140 | #ifndef weak_alias | |
1141 | # define __memrchr memrchr | |
1142 | #endif | |
1143 | ||
1144 | /* Search no more than N bytes of S for C. */ | |
1145 | void * | |
1146 | __memrchr (void const *s, int c_in, size_t n) | |
1147 | { | |
1148 | /* On 32-bit hardware, choosing longword to be a 32-bit unsigned | |
1149 | long instead of a 64-bit uintmax_t tends to give better | |
1150 | performance. On 64-bit hardware, unsigned long is generally 64 | |
1151 | bits already. Change this typedef to experiment with | |
1152 | performance. */ | |
1153 | typedef unsigned long int longword; | |
1154 | ||
1155 | const unsigned char *char_ptr; | |
1156 | const longword *longword_ptr; | |
1157 | longword repeated_one; | |
1158 | longword repeated_c; | |
1159 | unsigned reg_char c; | |
1160 | ||
1161 | c = (unsigned char) c_in; | |
1162 | ||
1163 | /* Handle the last few bytes by reading one byte at a time. | |
1164 | Do this until CHAR_PTR is aligned on a longword boundary. */ | |
1165 | for (char_ptr = (const unsigned char *) s + n; | |
1166 | n > 0 && (size_t) char_ptr % sizeof (longword) != 0; | |
1167 | --n) | |
1168 | if (*--char_ptr == c) | |
1169 | return (void *) char_ptr; | |
1170 | ||
1171 | longword_ptr = (const longword *) char_ptr; | |
1172 | ||
1173 | /* All these elucidatory comments refer to 4-byte longwords, | |
1174 | but the theory applies equally well to any size longwords. */ | |
1175 | ||
1176 | /* Compute auxiliary longword values: | |
1177 | repeated_one is a value which has a 1 in every byte. | |
1178 | repeated_c has c in every byte. */ | |
1179 | repeated_one = 0x01010101; | |
1180 | repeated_c = c | (c << 8); | |
1181 | repeated_c |= repeated_c << 16; | |
1182 | if (0xffffffffU < (longword) -1) | |
1183 | { | |
1184 | repeated_one |= repeated_one << 31 << 1; | |
1185 | repeated_c |= repeated_c << 31 << 1; | |
1186 | if (8 < sizeof (longword)) | |
1187 | { | |
1188 | size_t i; | |
1189 | ||
1190 | for (i = 64; i < sizeof (longword) * 8; i *= 2) | |
1191 | { | |
1192 | repeated_one |= repeated_one << i; | |
1193 | repeated_c |= repeated_c << i; | |
1194 | } | |
1195 | } | |
1196 | } | |
1197 | ||
1198 | /* Instead of the traditional loop which tests each byte, we will test a | |
1199 | longword at a time. The tricky part is testing if *any of the four* | |
1200 | bytes in the longword in question are equal to c. We first use an xor | |
1201 | with repeated_c. This reduces the task to testing whether *any of the | |
1202 | four* bytes in longword1 is zero. | |
1203 | ||
1204 | We compute tmp = | |
1205 | ((longword1 - repeated_one) & ~longword1) & (repeated_one << 7). | |
1206 | That is, we perform the following operations: | |
1207 | 1. Subtract repeated_one. | |
1208 | 2. & ~longword1. | |
1209 | 3. & a mask consisting of 0x80 in every byte. | |
1210 | Consider what happens in each byte: | |
1211 | - If a byte of longword1 is zero, step 1 and 2 transform it into 0xff, | |
1212 | and step 3 transforms it into 0x80. A carry can also be propagated | |
1213 | to more significant bytes. | |
1214 | - If a byte of longword1 is nonzero, let its lowest 1 bit be at | |
1215 | position k (0 <= k <= 7); so the lowest k bits are 0. After step 1, | |
1216 | the byte ends in a single bit of value 0 and k bits of value 1. | |
1217 | After step 2, the result is just k bits of value 1: 2^k - 1. After | |
1218 | step 3, the result is 0. And no carry is produced. | |
1219 | So, if longword1 has only non-zero bytes, tmp is zero. | |
1220 | Whereas if longword1 has a zero byte, call j the position of the least | |
1221 | significant zero byte. Then the result has a zero at positions 0, ..., | |
1222 | j-1 and a 0x80 at position j. We cannot predict the result at the more | |
1223 | significant bytes (positions j+1..3), but it does not matter since we | |
1224 | already have a non-zero bit at position 8*j+7. | |
1225 | ||
1226 | So, the test whether any byte in longword1 is zero is equivalent to | |
1227 | testing whether tmp is nonzero. */ | |
1228 | ||
1229 | while (n >= sizeof (longword)) | |
1230 | { | |
1231 | longword longword1 = *--longword_ptr ^ repeated_c; | |
1232 | ||
1233 | if ((((longword1 - repeated_one) & ~longword1) | |
1234 | & (repeated_one << 7)) != 0) | |
1235 | { | |
1236 | longword_ptr++; | |
1237 | break; | |
1238 | } | |
1239 | n -= sizeof (longword); | |
1240 | } | |
1241 | ||
1242 | char_ptr = (const unsigned char *) longword_ptr; | |
1243 | ||
1244 | /* At this point, we know that either n < sizeof (longword), or one of the | |
1245 | sizeof (longword) bytes starting at char_ptr is == c. On little-endian | |
1246 | machines, we could determine the first such byte without any further | |
1247 | memory accesses, just by looking at the tmp result from the last loop | |
1248 | iteration. But this does not work on big-endian machines. Choose code | |
1249 | that works in both cases. */ | |
1250 | ||
1251 | while (n-- > 0) | |
1252 | { | |
1253 | if (*--char_ptr == c) | |
1254 | return (void *) char_ptr; | |
1255 | } | |
1256 | ||
1257 | return NULL; | |
1258 | } | |
1259 | #ifdef weak_alias | |
1260 | weak_alias (__memrchr, memrchr) | |
1261 | #endif |