X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/dfa11ecc05d342af77f86120c123a3ea618544b6..955a6ddb8f3bcfadebee5694511661e71980b991:/apt-pkg/deb/dpkgpm.cc diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index 79a8c312b..e78f2dc1c 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 @@ -20,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +26,11 @@ #include #include +#include +#include +#include +#include + #include #include /*}}}*/ @@ -326,7 +329,38 @@ bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf) return true; } + + /*}}}*/ +// DPkgPM::DoStdin - Read stdin and pass to slave pty /*{{{*/ +// --------------------------------------------------------------------- +/* +*/ +void pkgDPkgPM::DoStdin(int master) +{ + char input_buf[256] = {0,}; + int len = read(0, input_buf, sizeof(input_buf)); + write(master, input_buf, len); +} /*}}}*/ +// DPkgPM::DoTerminalPty - Read the terminal pty and write log /*{{{*/ +// --------------------------------------------------------------------- +/* + * read the terminal pty and write log + */ +void pkgDPkgPM::DoTerminalPty(int master, FILE *term_out) +{ + char term_buf[1024] = {0,}; + + int len=read(master, term_buf, sizeof(term_buf)); + if(len <= 0) + return; + fwrite(term_buf, len, sizeof(char), term_out); + write(1, term_buf, len); +} + /*}}}*/ + + + // DPkgPM::Go - Run the sequence /*{{{*/ // --------------------------------------------------------------------- /* This globs the operations and calls dpkg @@ -355,28 +389,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 for remove with config %s")}, - {"not-installed", _("Removed with config %s")}, + {"config-files", N_("Preparing to completely remove %s")}, + {"not-installed", N_("Completely removed %s")}, {NULL, NULL} }, }; @@ -474,6 +508,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 +553,26 @@ 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; + + // FIXME: setup sensible signal handling (*ick*) + 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 +581,15 @@ 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 +608,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"); @@ -572,7 +635,27 @@ bool pkgDPkgPM::Go(int OutStatusFd) // the result of the waitpid call int res; - + close(slave); +#if 0 + fcntl(0, F_SETFL, O_NONBLOCK); + fcntl(master, F_SETFL, O_NONBLOCK); +#endif + // FIXME: make this a apt config option and add a logrotate file + FILE *term_out = fopen("/var/log/dpkg-out.log","a"); + chmod("/var/log/dpkg-out.log", 0600); + // output current time + char outstr[200]; + time_t t = time(NULL); + struct tm *tmp = localtime(&t); + strftime(outstr, sizeof(outstr), "%F %T", tmp); + fprintf(term_out, "Log started: "); + fprintf(term_out, outstr); + fprintf(term_out, "\n"); + + // setups fds + fd_set rfds; + struct timeval tv; + int select_ret; while ((res=waitpid(Child,&Status, WNOHANG)) != Child) { if(res < 0) { // FIXME: move this to a function or something, looks ugly here @@ -586,102 +669,168 @@ bool pkgDPkgPM::Go(int OutStatusFd) signal(SIGINT,old_SIGINT); return _error->Errno("waitpid","Couldn't wait for subprocess"); } - - // read a single char, make sure that the read can't block - // (otherwise we may leave zombies) - int len = read(_dpkgin, buf, 1); - // nothing to read, wait a bit for more - if(len <= 0) - { - usleep(1000); - continue; - } - - // sanity check (should never happen) - if(strlen(line) >= sizeof(line)-10) - { - _error->Error("got a overlong line from dpkg: '%s'",line); - line[0]=0; - } - // append to line, check if we got a complete line - strcat(line, buf); - if(buf[0] != '\n') + // wait for input or output here + FD_ZERO(&rfds); + FD_SET(0, &rfds); + FD_SET(_dpkgin, &rfds); + FD_SET(master, &rfds); + tv.tv_sec = 1; + tv.tv_usec = 0; + select_ret = select(max(master, _dpkgin)+1, &rfds, NULL, NULL, &tv); + if (select_ret < 0) + std::cerr << "Error in select()" << std::endl; + else if (select_ret == 0) continue; - if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true) - std::clog << "got from dpkg '" << line << "'" << std::endl; + if(FD_ISSET(master, &rfds)) + DoTerminalPty(master, term_out); + if(FD_ISSET(0, &rfds)) + DoStdin(master); - // pass "error" and "conffile-prompt" messages from dpkg verbatim - if((strncmp(line,"error",strlen("error")) == 0) || - (strncmp(line,"conffile-prompt",strlen("conffile-prompt")) == 0)) + // FIXME: move this into its own function too + //DoDpkgStatusFd(); + if(FD_ISSET(_dpkgin, &rfds)) + while(true) { - write(OutStatusFd, line, strlen(line)); + if(read(_dpkgin, buf, 1) <= 0) + break; + + // sanity check (should never happen) + if(strlen(line) >= sizeof(line)-10) + { + _error->Error("got a overlong line from dpkg: '%s'",line); line[0]=0; continue; - } - // line contains the dpkg status info now. it has the form: - // 'status: : ' (see dpkg(1) for details) - char* list[5]; - TokSplitString(':', line, list, 5); - char *pkg = list[1]; - char *action = list[2]; - vector &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 && (strcmp(action, next_action) == 0)) - { - // only read the translation if there is actually a next - // action - const char *translation = states[PackageOpsDone[pkg]].str; - char s[200]; - snprintf(s, sizeof(s), translation, pkg); - - // we moved from one dpkg state to a new one, report that - PackageOpsDone[pkg]++; - Done++; - ostringstream status; - // build the status str - status << "pmstatus:" << pkg - << ":" << (Done/float(Total)*100.0) - << ":" << s - << endl; - if(OutStatusFd > 0) - write(OutStatusFd, status.str().c_str(), status.str().size()); + } + // append to line, check if we got a complete line + strcat(line, buf); + if(buf[0] != '\n') + continue; + if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true) + std::clog << "got from dpkg '" << line << "'" << std::endl; + + // the status we output + ostringstream status; + + /* dpkg sends strings like this: + 'status: : ' + errors look like this: + '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 + and conffile-prompt like this + 'status: conffile-prompt: conffile : 'current-conffile' 'new-conffile' useredited distedited + + */ + 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) { - std::clog << "send to fd: '" << status.str() - << "'" << std::endl; - + if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true) + std::clog << "ignoring line: not enough ':'" << std::endl; + // reset the line buffer + line[0]=0; + continue; } - } - if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true) - std::clog << "(parsed from dpkg) pkg: " << pkg - << " action: " << action << endl; + if(strncmp(action,"error",strlen("error")) == 0) + { + status << "pmerror:" << list[1] + << ":" << (Done/float(Total)*100.0) + << ":" << list[3] + << endl; + if(OutStatusFd > 0) + write(OutStatusFd, status.str().c_str(), status.str().size()); + line[0]=0; + if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true) + std::clog << "send: '" << status.str() << "'" << endl; + continue; + } + if(strncmp(action,"conffile",strlen("conffile")) == 0) + { + status << "pmconffile:" << list[1] + << ":" << (Done/float(Total)*100.0) + << ":" << list[3] + << endl; + if(OutStatusFd > 0) + write(OutStatusFd, status.str().c_str(), status.str().size()); + line[0]=0; + if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true) + std::clog << "send: '" << status.str() << "'" << endl; + continue; + } + + vector &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 && (strcmp(action, next_action) == 0)) + { + // only read the translation if there is actually a next + // action + const char *translation = _(states[PackageOpsDone[pkg]].str); + char s[200]; + snprintf(s, sizeof(s), translation, pkg); + + // we moved from one dpkg state to a new one, report that + PackageOpsDone[pkg]++; + Done++; + // build the status str + status << "pmstatus:" << pkg + << ":" << (Done/float(Total)*100.0) + << ":" << s + << endl; + if(OutStatusFd > 0) + write(OutStatusFd, status.str().c_str(), status.str().size()); + if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true) + std::clog << "send: '" << status.str() << "'" << endl; - // reset the line buffer - line[0]=0; + } + if (_config->FindB("Debug::pkgDPkgProgressReporting",false) == true) + std::clog << "(parsed from dpkg) pkg: " << pkg + << " action: " << action << endl; + + // reset the line buffer + 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; } }