X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/e5b7e019232f89a97e8ba3cbffa295595daa0351..ac81c0f9b79351258d3a29212f7fda312e5afeb5:/apt-pkg/deb/dpkgpm.cc diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index e68c2ca34..04a13a86c 100644 --- a/apt-pkg/deb/dpkgpm.cc +++ b/apt-pkg/deb/dpkgpm.cc @@ -59,8 +59,8 @@ class pkgDPkgPMPrivate { public: pkgDPkgPMPrivate() : stdin_is_dev_null(false), dpkgbuf_pos(0), - term_out(NULL), history_out(NULL), - progress(NULL), master(-1), slave(-1) + term_out(NULL), history_out(NULL), + progress(NULL), master(-1), slave(NULL) { dpkgbuf[0] = '\0'; } @@ -77,9 +77,9 @@ public: APT::Progress::PackageManager *progress; // pty stuff - struct termios tt; + struct termios tt; int master; - int slave; + char * slave; // signals sigset_t sigmask; @@ -399,10 +399,14 @@ bool pkgDPkgPM::SendPkgsInfo(FILE * const F, unsigned int const &Version) that are due to be installed. */ bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf) { + bool result = true; + Configuration::Item const *Opts = _config->Tree(Cnf); if (Opts == 0 || Opts->Child == 0) return true; Opts = Opts->Child; + + sighandler_t old_sigpipe = signal(SIGPIPE, SIG_IGN); unsigned int Count = 1; for (; Opts != 0; Opts = Opts->Next, Count++) @@ -428,8 +432,10 @@ bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf) std::set KeepFDs; MergeKeepFdsFromConfiguration(KeepFDs); int Pipes[2]; - if (pipe(Pipes) != 0) - return _error->Errno("pipe","Failed to create IPC pipe to subprocess"); + if (pipe(Pipes) != 0) { + result = _error->Errno("pipe","Failed to create IPC pipe to subprocess"); + break; + } if (InfoFD != (unsigned)Pipes[0]) SetCloseExec(Pipes[0],true); else @@ -463,8 +469,10 @@ bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf) } close(Pipes[0]); FILE *F = fdopen(Pipes[1],"w"); - if (F == 0) - return _error->Errno("fdopen","Faild to open new FD"); + if (F == 0) { + result = _error->Errno("fdopen","Faild to open new FD"); + break; + } // Feed it the filenames. if (Version <= 1) @@ -492,14 +500,17 @@ bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf) fclose(F); // Clean up the sub process - if (ExecWait(Process,Opts->Value.c_str()) == false) - return _error->Error("Failure running script %s",Opts->Value.c_str()); + if (ExecWait(Process,Opts->Value.c_str()) == false) { + result = _error->Error("Failure running script %s",Opts->Value.c_str()); + break; + } } + signal(SIGPIPE, old_sigpipe); - return true; + return result; } /*}}}*/ -// DPkgPM::DoStdin - Read stdin and pass to slave pty /*{{{*/ +// DPkgPM::DoStdin - Read stdin and pass to master pty /*{{{*/ // --------------------------------------------------------------------- /* */ @@ -553,8 +564,8 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line) 'status: : ' 'status: :: ' - 'processing: {install,configure,remove,purge,disappear,trigproc}: pkg' - 'processing: {install,configure,remove,purge,disappear,trigproc}: trigger' + 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: pkg' + 'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: trigger' */ // we need to split on ": " (note the appended space) as the ':' is @@ -578,12 +589,15 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line) std::string action; // "processing" has the form "processing: action: pkg or trigger" - // with action = ["install", "configure", "remove", "purge", "disappear", - // "trigproc"] + // with action = ["install", "upgrade", "configure", "remove", "purge", + // "disappear", "trigproc"] if (prefix == "processing") { pkgname = APT::String::Strip(list[2]); action = APT::String::Strip(list[1]); + // we don't care for the difference (as dpkg doesn't really either) + if (action == "upgrade") + action = "install"; } // "status" has the form: "status: pkg: state" // with state in ["half-installed", "unpacked", "half-configured", @@ -610,15 +624,15 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line) { if(action == "error") { - d->progress->Error(list[1], PackagesDone, PackagesTotal, + d->progress->Error(pkgname, PackagesDone, PackagesTotal, list[3]); pkgFailures++; - WriteApportReport(list[1].c_str(), list[3].c_str()); + WriteApportReport(pkgname.c_str(), list[3].c_str()); return; } else if(action == "conffile-prompt") { - d->progress->ConffilePrompt(list[1], PackagesDone, PackagesTotal, + d->progress->ConffilePrompt(pkgname, PackagesDone, PackagesTotal, list[3]); return; } @@ -627,27 +641,26 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line) // at this point we know that we should have a valid pkgname, so build all // the info from it - // dpkg does not send always send "pkgname:arch" so we add it here - // if needed + // dpkg does not always send "pkgname:arch" so we add it here if needed if (pkgname.find(":") == std::string::npos) { - // find the package in the group that is in a touched by dpkg - // if there are multiple dpkg will send us a full pkgname:arch + // find the package in the group that is touched by dpkg + // if there are multiple pkgs dpkg would send us a full pkgname:arch pkgCache::GrpIterator Grp = Cache.FindGrp(pkgname); - if (Grp.end() == false) + if (Grp.end() == false) { - pkgCache::PkgIterator P = Grp.PackageList(); - for (; P.end() != true; P = Grp.NextPkg(P)) - { - if(Cache[P].Mode != pkgDepCache::ModeKeep) - { - pkgname = P.FullName(); - break; - } - } + pkgCache::PkgIterator P = Grp.PackageList(); + for (; P.end() != true; P = Grp.NextPkg(P)) + { + if(Cache[P].Keep() == false || Cache[P].ReInstall() == true) + { + pkgname = P.FullName(); + break; + } + } } } - + const char* const pkg = pkgname.c_str(); std::string short_pkgname = StringSplit(pkgname, ":")[0]; std::string arch = ""; @@ -686,28 +699,29 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line) if (prefix == "status") { vector const &states = PackageOps[pkg]; - const char *next_action = NULL; if(PackageOpsDone[pkg] < states.size()) - next_action = states[PackageOpsDone[pkg]].state; - // check if the package moved to the next dpkg state - if(next_action && (action == next_action)) { - // only read the translation if there is actually a next - // action - const char *translation = _(states[PackageOpsDone[pkg]].str); - std::string msg; - - // we moved from one dpkg state to a new one, report that - PackageOpsDone[pkg]++; - PackagesDone++; - - strprintf(msg, translation, i18n_pkgname.c_str()); - d->progress->StatusChanged(pkgname, PackagesDone, PackagesTotal, msg); - + char const * const next_action = states[PackageOpsDone[pkg]].state; + if (next_action && Debug == true) + std::clog << "(parsed from dpkg) pkg: " << short_pkgname + << " action: " << action << " (expected: '" << next_action << "' " + << PackageOpsDone[pkg] << " of " << states.size() << ")" << endl; + + // check if the package moved to the next dpkg state + if(next_action && (action == next_action)) + { + // only read the translation if there is actually a next action + char const * const translation = _(states[PackageOpsDone[pkg]].str); + + // we moved from one dpkg state to a new one, report that + ++PackageOpsDone[pkg]; + ++PackagesDone; + + std::string msg; + strprintf(msg, translation, i18n_pkgname.c_str()); + d->progress->StatusChanged(pkgname, PackagesDone, PackagesTotal, msg); + } } - if (Debug == true) - std::clog << "(parsed from dpkg) pkg: " << short_pkgname - << " action: " << action << endl; } } /*}}}*/ @@ -1037,59 +1051,127 @@ void pkgDPkgPM::StartPtyMagic() { if (_config->FindB("Dpkg::Use-Pty", true) == false) { - d->master = d->slave = -1; + d->master = -1; + if (d->slave != NULL) + free(d->slave); + d->slave = NULL; return; } - // setup the pty and stuff - struct winsize win; - - // if tcgetattr does not return zero there was a error - // and we do not do any pty magic _error->PushToStack(); - if (tcgetattr(STDOUT_FILENO, &d->tt) == 0) + // if tcgetattr for both stdin/stdout returns 0 (no error) + // we do the pty magic + if (tcgetattr(STDOUT_FILENO, &d->tt) == 0 && + tcgetattr(STDIN_FILENO, &d->tt) == 0) { - if (ioctl(1, TIOCGWINSZ, (char *)&win) < 0) - { - _error->Errno("ioctl", _("ioctl(TIOCGWINSZ) failed")); - } else if (openpty(&d->master, &d->slave, NULL, &d->tt, &win) < 0) - { - _error->Errno("openpty", _("Can not write log (%s)"), _("Is /dev/pts mounted?")); - d->master = d->slave = -1; - } else { - struct termios rtt; - rtt = d->tt; - cfmakeraw(&rtt); - rtt.c_lflag &= ~ECHO; - rtt.c_lflag |= ISIG; + d->master = posix_openpt(O_RDWR | O_NOCTTY); + if (d->master == -1) + _error->Errno("posix_openpt", _("Can not write log (%s)"), _("Is /dev/pts mounted?")); + else if (unlockpt(d->master) == -1) + { + _error->Errno("unlockpt", "Unlocking the slave of master fd %d failed!", d->master); + close(d->master); + d->master = -1; + } + else + { + char const * const slave_name = ptsname(d->master); + if (slave_name == NULL) + { + _error->Errno("unlockpt", "Getting name for slave of master fd %d failed!", d->master); + close(d->master); + d->master = -1; + } + else + { + d->slave = strdup(slave_name); + if (d->slave == NULL) + { + _error->Errno("strdup", "Copying name %s for slave of master fd %d failed!", slave_name, d->master); + close(d->master); + d->master = -1; + } + struct winsize win; + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) < 0) + _error->Errno("ioctl", "Getting TIOCGWINSZ from stdout failed!"); + if (ioctl(d->master, TIOCSWINSZ, &win) < 0) + _error->Errno("ioctl", "Setting TIOCSWINSZ for master fd %d failed!", d->master); + if (tcsetattr(d->master, TCSANOW, &d->tt) == -1) + _error->Errno("tcsetattr", "Setting in Start via TCSANOW for master fd %d failed!", d->master); + + struct termios raw_tt; + raw_tt = d->tt; + cfmakeraw(&raw_tt); + raw_tt.c_lflag &= ~ECHO; + raw_tt.c_lflag |= ISIG; // block SIGTTOU during tcsetattr to prevent a hang if // the process is a member of the background process group // http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html sigemptyset(&d->sigmask); sigaddset(&d->sigmask, SIGTTOU); sigprocmask(SIG_BLOCK,&d->sigmask, &d->original_sigmask); - tcsetattr(0, TCSAFLUSH, &rtt); - sigprocmask(SIG_SETMASK, &d->original_sigmask, 0); - } + if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw_tt) == -1) + _error->Errno("tcsetattr", "Setting in Start via TCSAFLUSH for stdout failed!"); + sigprocmask(SIG_SETMASK, &d->original_sigmask, NULL); + } } - // complain only if stdout is either a terminal (but still failed) or is an invalid + } + else + { + // complain only if stdout is either a terminal (but still failed) or is an invalid // descriptor otherwise we would complain about redirection to e.g. /dev/null as well. - else if (isatty(STDOUT_FILENO) == 1 || errno == EBADF) - _error->Errno("tcgetattr", _("Can not write log (%s)"), _("Is stdout a terminal?")); + if (isatty(STDOUT_FILENO) == 1 || errno == EBADF) + _error->Errno("tcgetattr", _("Can not write log (%s)"), _("Is stdout a terminal?")); + } - if (_error->PendingError() == true) - _error->DumpErrors(std::cerr); - _error->RevertToStack(); + if (_error->PendingError() == true) + { + if (d->master != -1) + { + close(d->master); + d->master = -1; + } + _error->DumpErrors(std::cerr); + } + _error->RevertToStack(); } +void pkgDPkgPM::SetupSlavePtyMagic() +{ + if(d->master == -1) + return; + + if (close(d->master) == -1) + _error->FatalE("close", "Closing master %d in child failed!", d->master); + if (setsid() == -1) + _error->FatalE("setsid", "Starting a new session for child failed!"); + int const slaveFd = open(d->slave, O_RDWR); + if (slaveFd == -1) + _error->FatalE("open", _("Can not write log (%s)"), _("Is /dev/pts mounted?")); + + if (ioctl(slaveFd, TIOCSCTTY, 0) < 0) + _error->FatalE("ioctl", "Setting TIOCSCTTY for slave fd %d failed!", slaveFd); + else + { + for (unsigned short i = 0; i < 3; ++i) + if (dup2(slaveFd, i) == -1) + _error->FatalE("dup2", "Dupping %d to %d in child failed!", slaveFd, i); + + if (tcsetattr(0, TCSANOW, &d->tt) < 0) + _error->FatalE("tcsetattr", "Setting in Setup via TCSANOW for slave fd %d failed!", slaveFd); + } +} void pkgDPkgPM::StopPtyMagic() { - if(d->slave > 0) - close(d->slave); + if (d->slave != NULL) + free(d->slave); + d->slave = NULL; if(d->master >= 0) { - tcsetattr(0, TCSAFLUSH, &d->tt); + if (tcsetattr(0, TCSAFLUSH, &d->tt) == -1) + _error->FatalE("tcsetattr", "Setting in Stop via TCSAFLUSH for stdin failed!"); close(d->master); + d->master = -1; } } @@ -1401,22 +1483,8 @@ bool pkgDPkgPM::GoNoABIBreak(APT::Progress::PackageManager *progress) pid_t Child = ExecFork(KeepFDs); if (Child == 0) { - // This is the child - if(d->slave >= 0 && d->master >= 0) - { - setsid(); - int res = ioctl(d->slave, TIOCSCTTY, 0); - if (res < 0) { - std::cerr << "ioctl(TIOCSCTTY) failed for fd: " - << d->slave << std::endl; - } else { - close(d->master); - dup2(d->slave, 0); - dup2(d->slave, 1); - dup2(d->slave, 2); - close(d->slave); - } - } + // This is the child + SetupSlavePtyMagic(); close(fd[0]); // close the read end of the pipe dpkgChrootDirectory(); @@ -1426,7 +1494,8 @@ bool pkgDPkgPM::GoNoABIBreak(APT::Progress::PackageManager *progress) if (_config->FindB("DPkg::FlushSTDIN",true) == true && isatty(STDIN_FILENO)) { - int Flags,dummy; + int Flags; + int dummy = 0; if ((Flags = fcntl(STDIN_FILENO,F_GETFL,dummy)) < 0) _exit(100); @@ -1606,7 +1675,7 @@ void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg) string::size_type pos; FILE *report; - if (_config->FindB("Dpkg::ApportFailureReport", false) == false) + if (_config->FindB("Dpkg::ApportFailureReport", true) == false) { std::clog << "configured to not write apport reports" << std::endl; return; @@ -1655,9 +1724,10 @@ void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg) // do not report dpkg I/O errors, this is a format string, so we compare // the prefix and the suffix of the error with the dpkg error message vector io_errors; - io_errors.push_back(string("failed to read on buffer copy for %s")); - io_errors.push_back(string("failed in write on buffer copy for %s")); - io_errors.push_back(string("short read on buffer copy for %s")); + io_errors.push_back(string("failed to read")); + io_errors.push_back(string("failed to write")); + io_errors.push_back(string("failed to seek")); + io_errors.push_back(string("unexpected end of file or stream")); for (vector::iterator I = io_errors.begin(); I != io_errors.end(); ++I) {