]> git.saurik.com Git - apt.git/blob - apt-pkg/deb/dpkgpm.cc
* merged from dpkg-log branch
[apt.git] / apt-pkg / deb / dpkgpm.cc
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 /*{{{*/
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>
16 #include <apti18n.h>
17
18 #include <unistd.h>
19 #include <stdlib.h>
20 #include <fcntl.h>
21 #include <sys/select.h>
22 #include <sys/types.h>
23 #include <sys/wait.h>
24 #include <signal.h>
25 #include <errno.h>
26 #include <stdio.h>
27 #include <sstream>
28 #include <map>
29
30 #include <termios.h>
31 #include <unistd.h>
32 #include <sys/ioctl.h>
33 #include <pty.h>
34
35 #include <config.h>
36 #include <apti18n.h>
37 /*}}}*/
38
39 using namespace std;
40
41
42
43 // DPkgPM::pkgDPkgPM - Constructor /*{{{*/
44 // ---------------------------------------------------------------------
45 /* */
46 pkgDPkgPM::pkgDPkgPM(pkgDepCache *Cache)
47 : pkgPackageManager(Cache), dpkgbuf_pos(0), PackagesTotal(0), PackagesDone(0)
48 {
49 }
50 /*}}}*/
51 // DPkgPM::pkgDPkgPM - Destructor /*{{{*/
52 // ---------------------------------------------------------------------
53 /* */
54 pkgDPkgPM::~pkgDPkgPM()
55 {
56 }
57 /*}}}*/
58 // DPkgPM::Install - Install a package /*{{{*/
59 // ---------------------------------------------------------------------
60 /* Add an install operation to the sequence list */
61 bool pkgDPkgPM::Install(PkgIterator Pkg,string File)
62 {
63 if (File.empty() == true || Pkg.end() == true)
64 return _error->Error("Internal Error, No file name for %s",Pkg.Name());
65
66 List.push_back(Item(Item::Install,Pkg,File));
67 return true;
68 }
69 /*}}}*/
70 // DPkgPM::Configure - Configure a package /*{{{*/
71 // ---------------------------------------------------------------------
72 /* Add a configure operation to the sequence list */
73 bool pkgDPkgPM::Configure(PkgIterator Pkg)
74 {
75 if (Pkg.end() == true)
76 return false;
77
78 List.push_back(Item(Item::Configure,Pkg));
79 return true;
80 }
81 /*}}}*/
82 // DPkgPM::Remove - Remove a package /*{{{*/
83 // ---------------------------------------------------------------------
84 /* Add a remove operation to the sequence list */
85 bool pkgDPkgPM::Remove(PkgIterator Pkg,bool Purge)
86 {
87 if (Pkg.end() == true)
88 return false;
89
90 if (Purge == true)
91 List.push_back(Item(Item::Purge,Pkg));
92 else
93 List.push_back(Item(Item::Remove,Pkg));
94 return true;
95 }
96 /*}}}*/
97 // DPkgPM::RunScripts - Run a set of scripts /*{{{*/
98 // ---------------------------------------------------------------------
99 /* This looks for a list of script sto run from the configuration file,
100 each one is run with system from a forked child. */
101 bool pkgDPkgPM::RunScripts(const char *Cnf)
102 {
103 Configuration::Item const *Opts = _config->Tree(Cnf);
104 if (Opts == 0 || Opts->Child == 0)
105 return true;
106 Opts = Opts->Child;
107
108 // Fork for running the system calls
109 pid_t Child = ExecFork();
110
111 // This is the child
112 if (Child == 0)
113 {
114 if (chdir("/tmp/") != 0)
115 _exit(100);
116
117 unsigned int Count = 1;
118 for (; Opts != 0; Opts = Opts->Next, Count++)
119 {
120 if (Opts->Value.empty() == true)
121 continue;
122
123 if (system(Opts->Value.c_str()) != 0)
124 _exit(100+Count);
125 }
126 _exit(0);
127 }
128
129 // Wait for the child
130 int Status = 0;
131 while (waitpid(Child,&Status,0) != Child)
132 {
133 if (errno == EINTR)
134 continue;
135 return _error->Errno("waitpid","Couldn't wait for subprocess");
136 }
137
138 // Restore sig int/quit
139 signal(SIGQUIT,SIG_DFL);
140 signal(SIGINT,SIG_DFL);
141
142 // Check for an error code.
143 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
144 {
145 unsigned int Count = WEXITSTATUS(Status);
146 if (Count > 100)
147 {
148 Count -= 100;
149 for (; Opts != 0 && Count != 1; Opts = Opts->Next, Count--);
150 _error->Error("Problem executing scripts %s '%s'",Cnf,Opts->Value.c_str());
151 }
152
153 return _error->Error("Sub-process returned an error code");
154 }
155
156 return true;
157 }
158 /*}}}*/
159 // DPkgPM::SendV2Pkgs - Send version 2 package info /*{{{*/
160 // ---------------------------------------------------------------------
161 /* This is part of the helper script communication interface, it sends
162 very complete information down to the other end of the pipe.*/
163 bool pkgDPkgPM::SendV2Pkgs(FILE *F)
164 {
165 fprintf(F,"VERSION 2\n");
166
167 /* Write out all of the configuration directives by walking the
168 configuration tree */
169 const Configuration::Item *Top = _config->Tree(0);
170 for (; Top != 0;)
171 {
172 if (Top->Value.empty() == false)
173 {
174 fprintf(F,"%s=%s\n",
175 QuoteString(Top->FullTag(),"=\"\n").c_str(),
176 QuoteString(Top->Value,"\n").c_str());
177 }
178
179 if (Top->Child != 0)
180 {
181 Top = Top->Child;
182 continue;
183 }
184
185 while (Top != 0 && Top->Next == 0)
186 Top = Top->Parent;
187 if (Top != 0)
188 Top = Top->Next;
189 }
190 fprintf(F,"\n");
191
192 // Write out the package actions in order.
193 for (vector<Item>::iterator I = List.begin(); I != List.end(); I++)
194 {
195 pkgDepCache::StateCache &S = Cache[I->Pkg];
196
197 fprintf(F,"%s ",I->Pkg.Name());
198 // Current version
199 if (I->Pkg->CurrentVer == 0)
200 fprintf(F,"- ");
201 else
202 fprintf(F,"%s ",I->Pkg.CurrentVer().VerStr());
203
204 // Show the compare operator
205 // Target version
206 if (S.InstallVer != 0)
207 {
208 int Comp = 2;
209 if (I->Pkg->CurrentVer != 0)
210 Comp = S.InstVerIter(Cache).CompareVer(I->Pkg.CurrentVer());
211 if (Comp < 0)
212 fprintf(F,"> ");
213 if (Comp == 0)
214 fprintf(F,"= ");
215 if (Comp > 0)
216 fprintf(F,"< ");
217 fprintf(F,"%s ",S.InstVerIter(Cache).VerStr());
218 }
219 else
220 fprintf(F,"> - ");
221
222 // Show the filename/operation
223 if (I->Op == Item::Install)
224 {
225 // No errors here..
226 if (I->File[0] != '/')
227 fprintf(F,"**ERROR**\n");
228 else
229 fprintf(F,"%s\n",I->File.c_str());
230 }
231 if (I->Op == Item::Configure)
232 fprintf(F,"**CONFIGURE**\n");
233 if (I->Op == Item::Remove ||
234 I->Op == Item::Purge)
235 fprintf(F,"**REMOVE**\n");
236
237 if (ferror(F) != 0)
238 return false;
239 }
240 return true;
241 }
242 /*}}}*/
243 // DPkgPM::RunScriptsWithPkgs - Run scripts with package names on stdin /*{{{*/
244 // ---------------------------------------------------------------------
245 /* This looks for a list of scripts to run from the configuration file
246 each one is run and is fed on standard input a list of all .deb files
247 that are due to be installed. */
248 bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf)
249 {
250 Configuration::Item const *Opts = _config->Tree(Cnf);
251 if (Opts == 0 || Opts->Child == 0)
252 return true;
253 Opts = Opts->Child;
254
255 unsigned int Count = 1;
256 for (; Opts != 0; Opts = Opts->Next, Count++)
257 {
258 if (Opts->Value.empty() == true)
259 continue;
260
261 // Determine the protocol version
262 string OptSec = Opts->Value;
263 string::size_type Pos;
264 if ((Pos = OptSec.find(' ')) == string::npos || Pos == 0)
265 Pos = OptSec.length();
266 OptSec = "DPkg::Tools::Options::" + string(Opts->Value.c_str(),Pos);
267
268 unsigned int Version = _config->FindI(OptSec+"::Version",1);
269
270 // Create the pipes
271 int Pipes[2];
272 if (pipe(Pipes) != 0)
273 return _error->Errno("pipe","Failed to create IPC pipe to subprocess");
274 SetCloseExec(Pipes[0],true);
275 SetCloseExec(Pipes[1],true);
276
277 // Purified Fork for running the script
278 pid_t Process = ExecFork();
279 if (Process == 0)
280 {
281 // Setup the FDs
282 dup2(Pipes[0],STDIN_FILENO);
283 SetCloseExec(STDOUT_FILENO,false);
284 SetCloseExec(STDIN_FILENO,false);
285 SetCloseExec(STDERR_FILENO,false);
286
287 const char *Args[4];
288 Args[0] = "/bin/sh";
289 Args[1] = "-c";
290 Args[2] = Opts->Value.c_str();
291 Args[3] = 0;
292 execv(Args[0],(char **)Args);
293 _exit(100);
294 }
295 close(Pipes[0]);
296 FILE *F = fdopen(Pipes[1],"w");
297 if (F == 0)
298 return _error->Errno("fdopen","Faild to open new FD");
299
300 // Feed it the filenames.
301 bool Die = false;
302 if (Version <= 1)
303 {
304 for (vector<Item>::iterator I = List.begin(); I != List.end(); I++)
305 {
306 // Only deal with packages to be installed from .deb
307 if (I->Op != Item::Install)
308 continue;
309
310 // No errors here..
311 if (I->File[0] != '/')
312 continue;
313
314 /* Feed the filename of each package that is pending install
315 into the pipe. */
316 fprintf(F,"%s\n",I->File.c_str());
317 if (ferror(F) != 0)
318 {
319 Die = true;
320 break;
321 }
322 }
323 }
324 else
325 Die = !SendV2Pkgs(F);
326
327 fclose(F);
328
329 // Clean up the sub process
330 if (ExecWait(Process,Opts->Value.c_str()) == false)
331 return _error->Error("Failure running script %s",Opts->Value.c_str());
332 }
333
334 return true;
335 }
336
337 /*}}}*/
338 // DPkgPM::DoStdin - Read stdin and pass to slave pty /*{{{*/
339 // ---------------------------------------------------------------------
340 /*
341 */
342 void pkgDPkgPM::DoStdin(int master)
343 {
344 char input_buf[256] = {0,};
345 int len = read(0, input_buf, sizeof(input_buf));
346 write(master, input_buf, len);
347 }
348 /*}}}*/
349 // DPkgPM::DoTerminalPty - Read the terminal pty and write log /*{{{*/
350 // ---------------------------------------------------------------------
351 /*
352 * read the terminal pty and write log
353 */
354 void pkgDPkgPM::DoTerminalPty(int master, FILE *term_out)
355 {
356 char term_buf[1024] = {0,};
357
358 int len=read(master, term_buf, sizeof(term_buf));
359 if(len <= 0)
360 return;
361 write(1, term_buf, len);
362 if(term_out)
363 fwrite(term_buf, len, sizeof(char), term_out);
364 }
365 /*}}}*/
366 // DPkgPM::ProcessDpkgStatusBuf /*{{{*/
367 // ---------------------------------------------------------------------
368 /*
369 */
370 void pkgDPkgPM::ProcessDpkgStatusLine(int OutStatusFd, char *line)
371 {
372 // the status we output
373 ostringstream status;
374
375 if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true)
376 std::clog << "got from dpkg '" << line << "'" << std::endl;
377
378
379 /* dpkg sends strings like this:
380 'status: <pkg>: <pkg qstate>'
381 errors look like this:
382 '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
383 and conffile-prompt like this
384 'status: conffile-prompt: conffile : 'current-conffile' 'new-conffile' useredited distedited
385
386 */
387 char* list[5];
388 // dpkg sends multiline error messages sometimes (see
389 // #374195 for a example. we should support this by
390 // either patching dpkg to not send multiline over the
391 // statusfd or by rewriting the code here to deal with
392 // it. for now we just ignore it and not crash
393 TokSplitString(':', line, list, sizeof(list)/sizeof(list[0]));
394 char *pkg = list[1];
395 char *action = _strstrip(list[2]);
396 if( pkg == NULL || action == NULL)
397 {
398 if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true)
399 std::clog << "ignoring line: not enough ':'" << std::endl;
400 return;
401 }
402
403 if(strncmp(action,"error",strlen("error")) == 0)
404 {
405 status << "pmerror:" << list[1]
406 << ":" << (PackagesDone/float(PackagesTotal)*100.0)
407 << ":" << list[3]
408 << endl;
409 if(OutStatusFd > 0)
410 write(OutStatusFd, status.str().c_str(), status.str().size());
411 if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true)
412 std::clog << "send: '" << status.str() << "'" << endl;
413 return;
414 }
415 if(strncmp(action,"conffile",strlen("conffile")) == 0)
416 {
417 status << "pmconffile:" << list[1]
418 << ":" << (PackagesDone/float(PackagesTotal)*100.0)
419 << ":" << list[3]
420 << endl;
421 if(OutStatusFd > 0)
422 write(OutStatusFd, status.str().c_str(), status.str().size());
423 if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true)
424 std::clog << "send: '" << status.str() << "'" << endl;
425 return;
426 }
427
428 vector<struct DpkgState> &states = PackageOps[pkg];
429 const char *next_action = NULL;
430 if(PackageOpsDone[pkg] < states.size())
431 next_action = states[PackageOpsDone[pkg]].state;
432 // check if the package moved to the next dpkg state
433 if(next_action && (strcmp(action, next_action) == 0))
434 {
435 // only read the translation if there is actually a next
436 // action
437 const char *translation = _(states[PackageOpsDone[pkg]].str);
438 char s[200];
439 snprintf(s, sizeof(s), translation, pkg);
440
441 // we moved from one dpkg state to a new one, report that
442 PackageOpsDone[pkg]++;
443 PackagesDone++;
444 // build the status str
445 status << "pmstatus:" << pkg
446 << ":" << (PackagesDone/float(PackagesTotal)*100.0)
447 << ":" << s
448 << endl;
449 if(OutStatusFd > 0)
450 write(OutStatusFd, status.str().c_str(), status.str().size());
451 if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true)
452 std::clog << "send: '" << status.str() << "'" << endl;
453 }
454 if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true)
455 std::clog << "(parsed from dpkg) pkg: " << pkg
456 << " action: " << action << endl;
457 }
458
459 // DPkgPM::DoDpkgStatusFd /*{{{*/
460 // ---------------------------------------------------------------------
461 /*
462 */
463 void pkgDPkgPM::DoDpkgStatusFd(int statusfd, int OutStatusFd)
464 {
465 char *p, *q;
466 int len;
467
468 len=read(statusfd, &dpkgbuf[dpkgbuf_pos], sizeof(dpkgbuf)-dpkgbuf_pos);
469 dpkgbuf_pos += len;
470 if(len <= 0)
471 return;
472
473 // process line by line if we have a buffer
474 p = q = dpkgbuf;
475 while((q=(char*)memchr(p, '\n', dpkgbuf+dpkgbuf_pos-p)) != NULL)
476 {
477 *q = 0;
478 ProcessDpkgStatusLine(OutStatusFd, p);
479 p=q+1; // continue with next line
480 }
481
482 // now move the unprocessed bits (after the final \n that is now a 0x0)
483 // to the start and update dpkgbuf_pos
484 p = (char*)memrchr(dpkgbuf, 0, dpkgbuf_pos);
485 if(p == NULL)
486 return;
487
488 // we are interessted in the first char *after* 0x0
489 p++;
490
491 // move the unprocessed tail to the start and update pos
492 memmove(dpkgbuf, p, p-dpkgbuf);
493 dpkgbuf_pos = dpkgbuf+dpkgbuf_pos-p;
494 }
495 /*}}}*/
496
497
498 // DPkgPM::Go - Run the sequence /*{{{*/
499 // ---------------------------------------------------------------------
500 /* This globs the operations and calls dpkg
501 *
502 * If it is called with "OutStatusFd" set to a valid file descriptor
503 * apt will report the install progress over this fd. It maps the
504 * dpkg states a package goes through to human readable (and i10n-able)
505 * names and calculates a percentage for each step.
506 */
507 bool pkgDPkgPM::Go(int OutStatusFd)
508 {
509 unsigned int MaxArgs = _config->FindI("Dpkg::MaxArgs",8*1024);
510 unsigned int MaxArgBytes = _config->FindI("Dpkg::MaxArgBytes",32*1024);
511
512 if (RunScripts("DPkg::Pre-Invoke") == false)
513 return false;
514
515 if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false)
516 return false;
517
518 // map the dpkg states to the operations that are performed
519 // (this is sorted in the same way as Item::Ops)
520 static const struct DpkgState DpkgStatesOpMap[][5] = {
521 // Install operation
522 {
523 {"half-installed", N_("Preparing %s")},
524 {"unpacked", N_("Unpacking %s") },
525 {NULL, NULL}
526 },
527 // Configure operation
528 {
529 {"unpacked",N_("Preparing to configure %s") },
530 {"half-configured", N_("Configuring %s") },
531 { "installed", N_("Installed %s")},
532 {NULL, NULL}
533 },
534 // Remove operation
535 {
536 {"half-configured", N_("Preparing for removal of %s")},
537 {"half-installed", N_("Removing %s")},
538 {"config-files", N_("Removed %s")},
539 {NULL, NULL}
540 },
541 // Purge operation
542 {
543 {"config-files", N_("Preparing to completely remove %s")},
544 {"not-installed", N_("Completely removed %s")},
545 {NULL, NULL}
546 },
547 };
548
549 // init the PackageOps map, go over the list of packages that
550 // that will be [installed|configured|removed|purged] and add
551 // them to the PackageOps map (the dpkg states it goes through)
552 // and the PackageOpsTranslations (human readable strings)
553 for (vector<Item>::iterator I = List.begin(); I != List.end();I++)
554 {
555 string name = (*I).Pkg.Name();
556 PackageOpsDone[name] = 0;
557 for(int i=0; (DpkgStatesOpMap[(*I).Op][i]).state != NULL; i++)
558 {
559 PackageOps[name].push_back(DpkgStatesOpMap[(*I).Op][i]);
560 PackagesTotal++;
561 }
562 }
563
564 // create log
565 string logdir = _config->FindDir("Dir::Log");
566 if(not FileExists(logdir))
567 return _error->Error(_("Directory '%s' missing"), logdir.c_str());
568 string logfile_name = flCombine(logdir,
569 _config->Find("Dir::Log::Terminal"));
570 FILE *term_out = NULL;
571 if (!logfile_name.empty())
572 {
573 term_out = fopen(logfile_name.c_str(),"a");
574 chmod(logfile_name.c_str(), 0600);
575 // output current time
576 char outstr[200];
577 time_t t = time(NULL);
578 struct tm *tmp = localtime(&t);
579 strftime(outstr, sizeof(outstr), "%F %T", tmp);
580 fprintf(term_out, "\nLog started: ");
581 fprintf(term_out, outstr);
582 fprintf(term_out, "\n");
583 }
584
585 // this loop is runs once per operation
586 for (vector<Item>::iterator I = List.begin(); I != List.end();)
587 {
588 vector<Item>::iterator J = I;
589 for (; J != List.end() && J->Op == I->Op; J++);
590
591 // Generate the argument list
592 const char *Args[MaxArgs + 50];
593 if (J - I > (signed)MaxArgs)
594 J = I + MaxArgs;
595
596 unsigned int n = 0;
597 unsigned long Size = 0;
598 string Tmp = _config->Find("Dir::Bin::dpkg","dpkg");
599 Args[n++] = Tmp.c_str();
600 Size += strlen(Args[n-1]);
601
602 // Stick in any custom dpkg options
603 Configuration::Item const *Opts = _config->Tree("DPkg::Options");
604 if (Opts != 0)
605 {
606 Opts = Opts->Child;
607 for (; Opts != 0; Opts = Opts->Next)
608 {
609 if (Opts->Value.empty() == true)
610 continue;
611 Args[n++] = Opts->Value.c_str();
612 Size += Opts->Value.length();
613 }
614 }
615
616 char status_fd_buf[20];
617 int fd[2];
618 pipe(fd);
619
620 Args[n++] = "--status-fd";
621 Size += strlen(Args[n-1]);
622 snprintf(status_fd_buf,sizeof(status_fd_buf),"%i", fd[1]);
623 Args[n++] = status_fd_buf;
624 Size += strlen(Args[n-1]);
625
626 switch (I->Op)
627 {
628 case Item::Remove:
629 Args[n++] = "--force-depends";
630 Size += strlen(Args[n-1]);
631 Args[n++] = "--force-remove-essential";
632 Size += strlen(Args[n-1]);
633 Args[n++] = "--remove";
634 Size += strlen(Args[n-1]);
635 break;
636
637 case Item::Purge:
638 Args[n++] = "--force-depends";
639 Size += strlen(Args[n-1]);
640 Args[n++] = "--force-remove-essential";
641 Size += strlen(Args[n-1]);
642 Args[n++] = "--purge";
643 Size += strlen(Args[n-1]);
644 break;
645
646 case Item::Configure:
647 Args[n++] = "--configure";
648 Size += strlen(Args[n-1]);
649 break;
650
651 case Item::Install:
652 Args[n++] = "--unpack";
653 Size += strlen(Args[n-1]);
654 Args[n++] = "--auto-deconfigure";
655 Size += strlen(Args[n-1]);
656 break;
657 }
658
659 // Write in the file or package names
660 if (I->Op == Item::Install)
661 {
662 for (;I != J && Size < MaxArgBytes; I++)
663 {
664 if (I->File[0] != '/')
665 return _error->Error("Internal Error, Pathname to install is not absolute '%s'",I->File.c_str());
666 Args[n++] = I->File.c_str();
667 Size += strlen(Args[n-1]);
668 }
669 }
670 else
671 {
672 for (;I != J && Size < MaxArgBytes; I++)
673 {
674 Args[n++] = I->Pkg.Name();
675 Size += strlen(Args[n-1]);
676 }
677 }
678 Args[n] = 0;
679 J = I;
680
681 if (_config->FindB("Debug::pkgDPkgPM",false) == true)
682 {
683 for (unsigned int k = 0; k != n; k++)
684 clog << Args[k] << ' ';
685 clog << endl;
686 continue;
687 }
688
689 cout << flush;
690 clog << flush;
691 cerr << flush;
692
693 /* Mask off sig int/quit. We do this because dpkg also does when
694 it forks scripts. What happens is that when you hit ctrl-c it sends
695 it to all processes in the group. Since dpkg ignores the signal
696 it doesn't die but we do! So we must also ignore it */
697 sighandler_t old_SIGQUIT = signal(SIGQUIT,SIG_IGN);
698 sighandler_t old_SIGINT = signal(SIGINT,SIG_IGN);
699
700 struct termios tt;
701 struct winsize win;
702 int master;
703 int slave;
704
705 // FIXME: setup sensible signal handling (*ick*)
706 tcgetattr(0, &tt);
707 ioctl(0, TIOCGWINSZ, (char *)&win);
708 if (openpty(&master, &slave, NULL, &tt, &win) < 0)
709 {
710 const char *s = _("Can not write log, openpty() "
711 "failed (/dev/pts not mounted?)\n");
712 fprintf(stderr, "%s",s);
713 fprintf(term_out, "%s",s);
714 master = slave = -1;
715 } else {
716 struct termios rtt;
717 rtt = tt;
718 cfmakeraw(&rtt);
719 rtt.c_lflag &= ~ECHO;
720 tcsetattr(0, TCSAFLUSH, &rtt);
721 }
722
723 // Fork dpkg
724 pid_t Child;
725 _config->Set("APT::Keep-Fds::",fd[1]);
726 Child = ExecFork();
727
728 // This is the child
729 if (Child == 0)
730 {
731 if(slave >= 0 && master >= 0)
732 {
733 setsid();
734 ioctl(slave, TIOCSCTTY, 0);
735 close(master);
736 dup2(slave, 0);
737 dup2(slave, 1);
738 dup2(slave, 2);
739 close(slave);
740 }
741 close(fd[0]); // close the read end of the pipe
742
743 if (chdir(_config->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
744 _exit(100);
745
746 if (_config->FindB("DPkg::FlushSTDIN",true) == true && isatty(STDIN_FILENO))
747 {
748 int Flags,dummy;
749 if ((Flags = fcntl(STDIN_FILENO,F_GETFL,dummy)) < 0)
750 _exit(100);
751
752 // Discard everything in stdin before forking dpkg
753 if (fcntl(STDIN_FILENO,F_SETFL,Flags | O_NONBLOCK) < 0)
754 _exit(100);
755
756 while (read(STDIN_FILENO,&dummy,1) == 1);
757
758 if (fcntl(STDIN_FILENO,F_SETFL,Flags & (~(long)O_NONBLOCK)) < 0)
759 _exit(100);
760 }
761
762
763 /* No Job Control Stop Env is a magic dpkg var that prevents it
764 from using sigstop */
765 putenv("DPKG_NO_TSTP=yes");
766 execvp(Args[0],(char **)Args);
767 cerr << "Could not exec dpkg!" << endl;
768 _exit(100);
769 }
770
771 // clear the Keep-Fd again
772 _config->Clear("APT::Keep-Fds",fd[1]);
773
774 // Wait for dpkg
775 int Status = 0;
776
777 // we read from dpkg here
778 int _dpkgin = fd[0];
779 close(fd[1]); // close the write end of the pipe
780
781 // the read buffers for the communication with dpkg
782 char buf[2] = {0,0};
783
784 // the result of the waitpid call
785 int res;
786 if(slave > 0)
787 close(slave);
788
789 // setups fds
790 fd_set rfds;
791 struct timeval tv;
792 int select_ret;
793 while ((res=waitpid(Child,&Status, WNOHANG)) != Child) {
794 if(res < 0) {
795 // FIXME: move this to a function or something, looks ugly here
796 // error handling, waitpid returned -1
797 if (errno == EINTR)
798 continue;
799 RunScripts("DPkg::Post-Invoke");
800
801 // Restore sig int/quit
802 signal(SIGQUIT,old_SIGQUIT);
803 signal(SIGINT,old_SIGINT);
804 return _error->Errno("waitpid","Couldn't wait for subprocess");
805 }
806
807 // wait for input or output here
808 FD_ZERO(&rfds);
809 FD_SET(0, &rfds);
810 FD_SET(_dpkgin, &rfds);
811 if(master >= 0)
812 FD_SET(master, &rfds);
813 tv.tv_sec = 1;
814 tv.tv_usec = 0;
815 select_ret = select(max(master, _dpkgin)+1, &rfds, NULL, NULL, &tv);
816 if (select_ret < 0) {
817 std::cerr << "Error in select()" << std::endl;
818 continue;
819 } else if (select_ret == 0)
820 continue;
821
822 if(master >= 0 && FD_ISSET(master, &rfds))
823 DoTerminalPty(master, term_out);
824 if(master >= 0 && FD_ISSET(0, &rfds))
825 DoStdin(master);
826 if(FD_ISSET(_dpkgin, &rfds))
827 DoDpkgStatusFd(_dpkgin, OutStatusFd);
828 }
829 close(_dpkgin);
830
831 // Restore sig int/quit
832 signal(SIGQUIT,old_SIGQUIT);
833 signal(SIGINT,old_SIGINT);
834
835 if(master >= 0 && slave >= 0)
836 tcsetattr(0, TCSAFLUSH, &tt);
837
838 // Check for an error code.
839 if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
840 {
841 // if it was set to "keep-dpkg-runing" then we won't return
842 // here but keep the loop going and just report it as a error
843 // for later
844 bool stopOnError = _config->FindB("Dpkg::StopOnError",true);
845
846 if(stopOnError)
847 RunScripts("DPkg::Post-Invoke");
848
849 if (WIFSIGNALED(Status) != 0 && WTERMSIG(Status) == SIGSEGV)
850 _error->Error("Sub-process %s received a segmentation fault.",Args[0]);
851 else if (WIFEXITED(Status) != 0)
852 _error->Error("Sub-process %s returned an error code (%u)",Args[0],WEXITSTATUS(Status));
853 else
854 _error->Error("Sub-process %s exited unexpectedly",Args[0]);
855
856 if(stopOnError)
857 {
858 if(term_out)
859 fclose(term_out);
860 return false;
861 }
862 }
863 }
864 if(term_out)
865 fclose(term_out);
866
867 if (RunScripts("DPkg::Post-Invoke") == false)
868 return false;
869 return true;
870 }
871 /*}}}*/
872 // pkgDpkgPM::Reset - Dump the contents of the command list /*{{{*/
873 // ---------------------------------------------------------------------
874 /* */
875 void pkgDPkgPM::Reset()
876 {
877 List.erase(List.begin(),List.end());
878 }
879 /*}}}*/