apt/cmdline/apt-dump-solver.cc

186 lines
5.6 KiB
C++

// -*- mode: cpp; mode: fold -*-
// Description /*{{{*/
/* #####################################################################
dummy solver to get quickly a scenario file out of APT
##################################################################### */
/*}}}*/
// Include Files /*{{{*/
#include <config.h>
#include <apt-pkg/cmndline.h>
#include <apt-pkg/configuration.h>
#include <apt-pkg/edsp.h>
#include <apt-pkg/fileutl.h>
#include <apt-pkg/strutl.h>
#include <apt-private/private-cmndline.h>
#include <cstdio>
#include <iostream>
#include <memory>
#include <sstream>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <unistd.h>
#include <apti18n.h>
/*}}}*/
static bool ShowHelp(CommandLine &) /*{{{*/
{
std::cout <<
_("Usage: apt-dump-solver\n"
"\n"
"apt-dump-solver is an interface to store an EDSP scenario in\n"
"a file and optionally forwards it to another solver.\n");
return true;
}
/*}}}*/
static std::vector<aptDispatchWithHelp> GetCommands() /*{{{*/
{
return {};
}
/*}}}*/
static int WriteError(char const * const uid, std::ostringstream &out, FileFd &stdoutfd, pid_t const &Solver)/*{{{*/
{
_error->DumpErrors(out);
// ensure the solver isn't printing into "our" error message, too
if (Solver != 0)
ExecWait(Solver, "dump", true);
EDSP::WriteError(uid, out.str(), stdoutfd);
return 0;
}
/*}}}*/
int main(int argc,const char *argv[]) /*{{{*/
{
CommandLine CmdL;
ParseCommandLine(CmdL, APT_CMD::APT_DUMP_SOLVER, &_config, nullptr, argc, argv, &ShowHelp, &GetCommands);
_config->Clear("Dir::Log");
bool const is_forwarding_dumper = (CmdL.FileSize() != 0);
FileFd stdoutfd;
if (stdoutfd.OpenDescriptor(STDOUT_FILENO, FileFd::WriteOnly | FileFd::BufferedWrite, true) == false)
return 252;
FileFd dump;
char const * const filename = is_forwarding_dumper ? CmdL.FileList[0] : getenv("APT_EDSP_DUMP_FILENAME");
if (filename == nullptr || strlen(filename) == 0)
{
if (is_forwarding_dumper == false)
{
EDSP::WriteError("ERR_NO_FILENAME", "You have to set the environment variable APT_EDSP_DUMP_FILENAME\n"
"to a valid filename to store the dump of EDSP solver input in.\n"
"For example with: export APT_EDSP_DUMP_FILENAME=/tmp/dump.edsp", stdoutfd);
return 0;
}
}
else
{
// ignore errors here as logging isn't really critical
_error->PushToStack();
if (dump.Open(filename, FileFd::WriteOnly | FileFd::Exclusive | FileFd::Create, FileFd::Extension, 0644) == false &&
is_forwarding_dumper == false)
{
_error->MergeWithStack();
std::ostringstream out;
out << "Writing EDSP solver input to file '" << filename << "' failed as it couldn't be created!\n";
return WriteError("ERR_CREATE_FILE", out, stdoutfd, 0);
}
_error->RevertToStack();
}
pid_t Solver = 0;
FileFd forward;
if (is_forwarding_dumper)
{
signal(SIGPIPE, SIG_IGN);
int external[] = {-1, -1};
if (pipe(external) != 0)
return 250;
for (int i = 0; i < 2; ++i)
SetCloseExec(external[i], true);
Solver = ExecFork();
if (Solver == 0) {
_config->Set("APT::Sandbox::User", _config->Find("APT::Solver::RunAsUser", _config->Find("APT::Sandbox::User")));
DropPrivileges();
dup2(external[0], STDIN_FILENO);
execv(CmdL.FileList[1], const_cast<char**>(CmdL.FileList + 1));
std::cerr << "Failed to execute '" << CmdL.FileList[1] << "'!" << std::endl;
_exit(100);
}
close(external[0]);
if (WaitFd(external[1], true, 5) == false)
return 251;
if (forward.OpenDescriptor(external[1], FileFd::WriteOnly | FileFd::BufferedWrite, true) == false)
return 252;
}
DropPrivileges();
FileFd input;
if (input.OpenDescriptor(STDIN_FILENO, FileFd::ReadOnly) == false)
{
std::ostringstream out;
out << "Writing EDSP solver input to file '" << filename << "' failed as stdin couldn't be opened!\n";
return WriteError("ERR_READ_ERROR", out, stdoutfd, Solver);
}
constexpr size_t BufSize = 64 * 1024;
std::unique_ptr<char[]> Buf(new char[BufSize]);
unsigned long long ToRead = 0;
do {
if (input.Read(Buf.get(),BufSize, &ToRead) == false)
{
std::ostringstream out;
out << "Writing EDSP solver input to file '" << filename << "' failed as reading from stdin failed!\n";
return WriteError("ERR_READ_ERROR", out, stdoutfd, Solver);
}
if (ToRead == 0)
break;
if (forward.IsOpen() && forward.Failed() == false && forward.Write(Buf.get(),ToRead) == false)
forward.Close();
if (dump.IsOpen() && dump.Failed() == false && dump.Write(Buf.get(),ToRead) == false)
dump.Close();
} while (true);
input.Close();
forward.Close();
dump.Close();
if (is_forwarding_dumper)
{
// Wait and collect the error code
int Status;
while (waitpid(Solver, &Status, 0) != Solver)
{
if (errno == EINTR)
continue;
std::ostringstream out;
ioprintf(out, _("Waited for %s but it wasn't there"), CmdL.FileList[1]);
return WriteError("ERR_FORWARD", out, stdoutfd, 0);
}
if (WIFEXITED(Status))
return WEXITSTATUS(Status);
else
return 255;
}
else if (_error->PendingError())
{
std::ostringstream out;
out << "Writing EDSP solver input to file '" << filename << "' failed due to write errors!\n";
return WriteError("ERR_WRITE_ERROR", out, stdoutfd, Solver);
}
else
EDSP::WriteError("ERR_JUST_DUMPING", "I am too dumb, i can just dump!\nPlease use one of my friends instead!", stdoutfd);
return 0;
}