|
@@ -59,8 +59,8 @@ class pkgDPkgPMPrivate
|
|
|
{
|
|
{
|
|
|
public:
|
|
public:
|
|
|
pkgDPkgPMPrivate() : stdin_is_dev_null(false), dpkgbuf_pos(0),
|
|
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';
|
|
dpkgbuf[0] = '\0';
|
|
|
}
|
|
}
|
|
@@ -77,9 +77,9 @@ public:
|
|
|
APT::Progress::PackageManager *progress;
|
|
APT::Progress::PackageManager *progress;
|
|
|
|
|
|
|
|
// pty stuff
|
|
// pty stuff
|
|
|
- struct termios tt;
|
|
|
|
|
|
|
+ struct termios tt;
|
|
|
int master;
|
|
int master;
|
|
|
- int slave;
|
|
|
|
|
|
|
+ char * slave;
|
|
|
|
|
|
|
|
// signals
|
|
// signals
|
|
|
sigset_t sigmask;
|
|
sigset_t sigmask;
|
|
@@ -510,7 +510,7 @@ bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf)
|
|
|
return result;
|
|
return result;
|
|
|
}
|
|
}
|
|
|
/*}}}*/
|
|
/*}}}*/
|
|
|
-// DPkgPM::DoStdin - Read stdin and pass to slave pty /*{{{*/
|
|
|
|
|
|
|
+// DPkgPM::DoStdin - Read stdin and pass to master pty /*{{{*/
|
|
|
// ---------------------------------------------------------------------
|
|
// ---------------------------------------------------------------------
|
|
|
/*
|
|
/*
|
|
|
*/
|
|
*/
|
|
@@ -564,8 +564,8 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line)
|
|
|
'status: <pkg>: <pkg qstate>'
|
|
'status: <pkg>: <pkg qstate>'
|
|
|
'status: <pkg>:<arch>: <pkg qstate>'
|
|
'status: <pkg>:<arch>: <pkg qstate>'
|
|
|
|
|
|
|
|
- '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
|
|
// we need to split on ": " (note the appended space) as the ':' is
|
|
@@ -589,12 +589,15 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line)
|
|
|
std::string action;
|
|
std::string action;
|
|
|
|
|
|
|
|
// "processing" has the form "processing: action: pkg or trigger"
|
|
// "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")
|
|
if (prefix == "processing")
|
|
|
{
|
|
{
|
|
|
pkgname = APT::String::Strip(list[2]);
|
|
pkgname = APT::String::Strip(list[2]);
|
|
|
action = APT::String::Strip(list[1]);
|
|
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"
|
|
// "status" has the form: "status: pkg: state"
|
|
|
// with state in ["half-installed", "unpacked", "half-configured",
|
|
// with state in ["half-installed", "unpacked", "half-configured",
|
|
@@ -638,27 +641,26 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line)
|
|
|
// at this point we know that we should have a valid pkgname, so build all
|
|
// at this point we know that we should have a valid pkgname, so build all
|
|
|
// the info from it
|
|
// 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)
|
|
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);
|
|
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();
|
|
const char* const pkg = pkgname.c_str();
|
|
|
std::string short_pkgname = StringSplit(pkgname, ":")[0];
|
|
std::string short_pkgname = StringSplit(pkgname, ":")[0];
|
|
|
std::string arch = "";
|
|
std::string arch = "";
|
|
@@ -697,28 +699,29 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line)
|
|
|
if (prefix == "status")
|
|
if (prefix == "status")
|
|
|
{
|
|
{
|
|
|
vector<struct DpkgState> const &states = PackageOps[pkg];
|
|
vector<struct DpkgState> const &states = PackageOps[pkg];
|
|
|
- const char *next_action = NULL;
|
|
|
|
|
if(PackageOpsDone[pkg] < states.size())
|
|
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;
|
|
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
/*}}}*/
|
|
/*}}}*/
|
|
@@ -1046,60 +1049,127 @@ void pkgDPkgPM::StartPtyMagic()
|
|
|
{
|
|
{
|
|
|
if (_config->FindB("Dpkg::Use-Pty", true) == false)
|
|
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;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // setup the pty and stuff
|
|
|
|
|
- struct winsize win;
|
|
|
|
|
-
|
|
|
|
|
|
|
+ _error->PushToStack();
|
|
|
// if tcgetattr for both stdin/stdout returns 0 (no error)
|
|
// if tcgetattr for both stdin/stdout returns 0 (no error)
|
|
|
// we do the pty magic
|
|
// we do the pty magic
|
|
|
- _error->PushToStack();
|
|
|
|
|
- if (tcgetattr(STDIN_FILENO, &d->tt) == 0 &&
|
|
|
|
|
- tcgetattr(STDOUT_FILENO, &d->tt) == 0)
|
|
|
|
|
|
|
+ if (tcgetattr(STDOUT_FILENO, &d->tt) == 0 &&
|
|
|
|
|
+ tcgetattr(STDIN_FILENO, &d->tt) == 0)
|
|
|
{
|
|
{
|
|
|
- if (ioctl(STDOUT_FILENO, 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
|
|
// block SIGTTOU during tcsetattr to prevent a hang if
|
|
|
// the process is a member of the background process group
|
|
// the process is a member of the background process group
|
|
|
// http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html
|
|
// http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html
|
|
|
sigemptyset(&d->sigmask);
|
|
sigemptyset(&d->sigmask);
|
|
|
sigaddset(&d->sigmask, SIGTTOU);
|
|
sigaddset(&d->sigmask, SIGTTOU);
|
|
|
sigprocmask(SIG_BLOCK,&d->sigmask, &d->original_sigmask);
|
|
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.
|
|
// 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()
|
|
void pkgDPkgPM::StopPtyMagic()
|
|
|
{
|
|
{
|
|
|
- if(d->slave > 0)
|
|
|
|
|
- close(d->slave);
|
|
|
|
|
|
|
+ if (d->slave != NULL)
|
|
|
|
|
+ free(d->slave);
|
|
|
|
|
+ d->slave = NULL;
|
|
|
if(d->master >= 0)
|
|
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);
|
|
close(d->master);
|
|
|
|
|
+ d->master = -1;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -1407,22 +1477,8 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
|
|
|
pid_t Child = ExecFork(KeepFDs);
|
|
pid_t Child = ExecFork(KeepFDs);
|
|
|
if (Child == 0)
|
|
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
|
|
close(fd[0]); // close the read end of the pipe
|
|
|
|
|
|
|
|
dpkgChrootDirectory();
|
|
dpkgChrootDirectory();
|