X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/7d1d3984806f1a9b33638d11d84d7febbacfc91e..d8cb4aa44573aca621110fda47d72bd682c0e3f4:/apt-pkg/deb/dpkgpm.cc?ds=sidebyside diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index 667db8ff2..9ca519acd 100644 --- a/apt-pkg/deb/dpkgpm.cc +++ b/apt-pkg/deb/dpkgpm.cc @@ -8,9 +8,6 @@ ##################################################################### */ /*}}}*/ // Includes /*{{{*/ -#ifdef __GNUG__ -#pragma implementation "apt-pkg/dpkgpm.h" -#endif #include #include #include @@ -28,6 +25,11 @@ #include #include +#include +#include +#include +#include + #include #include /*}}}*/ @@ -355,28 +357,28 @@ bool pkgDPkgPM::Go(int OutStatusFd) static const struct DpkgState DpkgStatesOpMap[][5] = { // Install operation { - {"half-installed", _("Preparing %s")}, - {"unpacked", _("Unpacking %s") }, + {"half-installed", N_("Preparing %s")}, + {"unpacked", N_("Unpacking %s") }, {NULL, NULL} }, // Configure operation { - {"unpacked",_("Preparing to configure %s") }, - {"half-configured", _("Configuring %s") }, - { "installed", _("Installed %s")}, + {"unpacked",N_("Preparing to configure %s") }, + {"half-configured", N_("Configuring %s") }, + { "installed", N_("Installed %s")}, {NULL, NULL} }, // Remove operation { - {"half-configured", _("Preparing for removal of %s")}, - {"half-installed", _("Removing %s")}, - {"config-files", _("Removed %s")}, + {"half-configured", N_("Preparing for removal of %s")}, + {"half-installed", N_("Removing %s")}, + {"config-files", N_("Removed %s")}, {NULL, NULL} }, // Purge operation { - {"config-files", _("Preparing to completely remove %s")}, - {"not-installed", _("Completely removed %s")}, + {"config-files", N_("Preparing to completely remove %s")}, + {"not-installed", N_("Completely removed %s")}, {NULL, NULL} }, }; @@ -474,6 +476,8 @@ bool pkgDPkgPM::Go(int OutStatusFd) case Item::Install: Args[n++] = "--unpack"; Size += strlen(Args[n-1]); + Args[n++] = "--auto-deconfigure"; + Size += strlen(Args[n-1]); break; } @@ -517,7 +521,24 @@ bool pkgDPkgPM::Go(int OutStatusFd) it doesn't die but we do! So we must also ignore it */ sighandler_t old_SIGQUIT = signal(SIGQUIT,SIG_IGN); sighandler_t old_SIGINT = signal(SIGINT,SIG_IGN); - + + struct termios tt; + struct winsize win; + int master; + int slave; + + tcgetattr(0, &tt); + ioctl(0, TIOCGWINSZ, (char *)&win); + if (openpty(&master, &slave, NULL, &tt, &win) < 0) { + fprintf(stderr, _("openpty failed\n")); + } + + struct termios rtt; + rtt = tt; + cfmakeraw(&rtt); + rtt.c_lflag &= ~ECHO; + tcsetattr(0, TCSAFLUSH, &rtt); + // Fork dpkg pid_t Child; _config->Set("APT::Keep-Fds::",fd[1]); @@ -526,8 +547,16 @@ bool pkgDPkgPM::Go(int OutStatusFd) // This is the child if (Child == 0) { + setsid(); + ioctl(slave, TIOCSCTTY, 0); + close(master); + dup2(slave, 0); + dup2(slave, 1); + dup2(slave, 2); + close(slave); + close(fd[0]); // close the read end of the pipe - + if (chdir(_config->FindDir("DPkg::Run-Directory","/").c_str()) != 0) _exit(100); @@ -546,7 +575,8 @@ bool pkgDPkgPM::Go(int OutStatusFd) if (fcntl(STDIN_FILENO,F_SETFL,Flags & (~(long)O_NONBLOCK)) < 0) _exit(100); } - + + /* No Job Control Stop Env is a magic dpkg var that prevents it from using sigstop */ putenv("DPKG_NO_TSTP=yes"); @@ -568,11 +598,19 @@ bool pkgDPkgPM::Go(int OutStatusFd) // the read buffers for the communication with dpkg char line[1024] = {0,}; + char buf[2] = {0,0}; + char term_buf[2] = {0,0}; + char input_buf[2] = {0,0}; // the result of the waitpid call int res; - + close(slave); + fcntl(0, F_SETFL, O_NONBLOCK); + fcntl(master, F_SETFL, O_NONBLOCK); + FILE *term_out = fopen("/var/log/dpkg-out.log","a"); + chmod("/var/log/dpkg-out.log", 0600); + while ((res=waitpid(Child,&Status, WNOHANG)) != Child) { if(res < 0) { // FIXME: move this to a function or something, looks ugly here @@ -586,18 +624,41 @@ bool pkgDPkgPM::Go(int OutStatusFd) signal(SIGINT,old_SIGINT); return _error->Errno("waitpid","Couldn't wait for subprocess"); } + + // wait for input or output here + + // FIXME: use select() instead of the rubish below // read a single char, make sure that the read can't block // (otherwise we may leave zombies) + int term_len = read(master, term_buf, 1); + int input_len = read(0, input_buf, 1); int len = read(_dpkgin, buf, 1); - // nothing to read, wait a bit for more + // see if we have any input that needs to go to the + // master pty + if(input_len > 0) + write(master, input_buf, 1); + + // see if we have any output that needs to be echoed + // and written to the log + if(term_len > 0) + { + do + { + fwrite(term_buf, 1, 1, term_out); + write(1, term_buf, 1); + } while(read(master, term_buf, 1) > 0); + term_buf[0] = 0; + } + + // nothing to read from dpkg , wait a bit for more if(len <= 0) { usleep(1000); continue; } - + // sanity check (should never happen) if(strlen(line) >= sizeof(line)-10) { @@ -624,9 +685,22 @@ bool pkgDPkgPM::Go(int OutStatusFd) */ char* list[5]; + // dpkg sends multiline error messages sometimes (see + // #374195 for a example. we should support this by + // either patching dpkg to not send multiline over the + // statusfd or by rewriting the code here to deal with + // it. for now we just ignore it and not crash TokSplitString(':', line, list, sizeof(list)/sizeof(list[0])); char *pkg = list[1]; char *action = _strstrip(list[2]); + if( pkg == NULL || action == NULL) + { + if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true) + std::clog << "ignoring line: not enough ':'" << std::endl; + // reset the line buffer + line[0]=0; + continue; + } if(strncmp(action,"error",strlen("error")) == 0) { @@ -664,7 +738,7 @@ bool pkgDPkgPM::Go(int OutStatusFd) { // only read the translation if there is actually a next // action - const char *translation = states[PackageOpsDone[pkg]].str; + const char *translation = _(states[PackageOpsDone[pkg]].str); char s[200]; snprintf(s, sizeof(s), translation, pkg); @@ -690,22 +764,34 @@ bool pkgDPkgPM::Go(int OutStatusFd) line[0]=0; } close(_dpkgin); + fclose(term_out); // Restore sig int/quit signal(SIGQUIT,old_SIGQUIT); signal(SIGINT,old_SIGINT); + + tcsetattr(0, TCSAFLUSH, &tt); // Check for an error code. if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0) { - RunScripts("DPkg::Post-Invoke"); - if (WIFSIGNALED(Status) != 0 && WTERMSIG(Status) == SIGSEGV) - return _error->Error("Sub-process %s received a segmentation fault.",Args[0]); - - if (WIFEXITED(Status) != 0) - return _error->Error("Sub-process %s returned an error code (%u)",Args[0],WEXITSTATUS(Status)); + // if it was set to "keep-dpkg-runing" then we won't return + // here but keep the loop going and just report it as a error + // for later + bool stopOnError = _config->FindB("Dpkg::StopOnError",true); - return _error->Error("Sub-process %s exited unexpectedly",Args[0]); + if(stopOnError) + RunScripts("DPkg::Post-Invoke"); + + if (WIFSIGNALED(Status) != 0 && WTERMSIG(Status) == SIGSEGV) + _error->Error("Sub-process %s received a segmentation fault.",Args[0]); + else if (WIFEXITED(Status) != 0) + _error->Error("Sub-process %s returned an error code (%u)",Args[0],WEXITSTATUS(Status)); + else + _error->Error("Sub-process %s exited unexpectedly",Args[0]); + + if(stopOnError) + return false; } }