summaryrefslogtreecommitdiff
path: root/src/generic/apt/download_update_manager.cc
diff options
context:
space:
mode:
authorDaniel Burrows <dburrows@debian.org>2008-01-19 09:55:08 -0800
committerDaniel Burrows <dburrows@debian.org>2008-01-19 09:55:08 -0800
commite770ef4a147b811b133ec787d6118c6408dc8da0 (patch)
tree5483ad6ef8d278c8c6b838972cbb253d19e7f357 /src/generic/apt/download_update_manager.cc
parent55eb8c6ffae4f278de1684923c4ab5e520a6da16 (diff)
downloadaptitude-e770ef4a147b811b133ec787d6118c6408dc8da0.tar.gz
Make the debtags command and its arguments configurable.
Because you know someone is going to ask for this as soon as debtags support is enabled.
Diffstat (limited to 'src/generic/apt/download_update_manager.cc')
-rw-r--r--src/generic/apt/download_update_manager.cc242
1 files changed, 236 insertions, 6 deletions
diff --git a/src/generic/apt/download_update_manager.cc b/src/generic/apt/download_update_manager.cc
index d8158d94..4a4fd63f 100644
--- a/src/generic/apt/download_update_manager.cc
+++ b/src/generic/apt/download_update_manager.cc
@@ -28,6 +28,16 @@
#include <apt-pkg/clean.h>
#include <apt-pkg/error.h>
+#include <cwidget/generic/util/exception.h>
+#include <cwidget/generic/util/ssprintf.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+namespace cw = cwidget;
+
class my_cleaner:public pkgArchiveCleaner
{
protected:
@@ -96,6 +106,166 @@ bool download_update_manager::prepare(OpProgress &progress,
return true;
}
+// TODO: this should be lifted to generic code.
+namespace
+{
+ std::string parse_quoted_string(char quote_character,
+ const std::string &whole_string,
+ std::string::const_iterator &begin,
+ std::string::const_iterator end)
+ {
+ std::string rval;
+
+ while(begin != end)
+ {
+ if(*begin == quote_character)
+ {
+ ++begin;
+ return rval;
+ }
+ else if(*begin == '\\')
+ {
+ ++begin;
+ if(begin != end)
+ rval += *begin;
+ }
+ else
+ rval += *begin;
+
+ ++begin;
+ }
+
+ _error->Warning("Unterminated quoted string in command: %s",
+ whole_string.c_str());
+ return rval;
+ }
+
+ /** \brief Parse whitespace-separated arguments from the given
+ * string, handling both single and double quotes.
+ *
+ * Each argument in the string is pushed onto the end of the
+ * given vector.
+ */
+ void parse_command_arguments(const std::string &command_line,
+ std::vector<std::string> &arguments)
+ {
+ std::string::const_iterator begin = command_line.begin();;
+ const std::string::const_iterator end = command_line.end();
+
+ while(begin != end)
+ {
+ while(begin != end && isspace(*begin))
+ ++begin;
+
+ std::string arg;
+
+ while(begin != end && !isspace(*begin))
+ {
+ if(*begin == '"' || *begin == '\'')
+ {
+ ++begin;
+ arg += parse_quoted_string(*begin, command_line,
+ begin, end);
+ }
+ else
+ {
+ arg.push_back(*begin);
+ ++begin;
+ }
+ }
+
+ arguments.push_back(arg);
+ }
+ }
+
+ /** \brief An exception indicating that a sub-process could not be run.
+ */
+ class SubprocessException : public cwidget::util::Exception
+ {
+ std::string msg;
+
+ public:
+ SubprocessException(const std::string &_msg)
+ : msg(_msg)
+ {
+ }
+
+ std::string errmsg() const { return msg; }
+ };
+
+ /** \brief Run a subprocess and redirect its output to /dev/null.
+ *
+ * $PATH is not searched, and the arguments are passed directly to
+ * the subprocess without shell intervention. The return value is
+ * the status of the subcommand as it would be returned from
+ * waitpid().
+ */
+ int run_in_subprocess_to_devnull(const std::string &command,
+ const std::vector<std::string> &args)
+ {
+ // Merge any new information in the apt cache into the debtags cache
+ // by running 'debtags update'. Be safe here: don't risk running
+ // the wrong thing as root by using system() or scanning the PATH.
+ int pid = fork();
+ if(pid < 0)
+ {
+ int errnum = errno;
+
+ throw SubprocessException(cw::util::ssprintf(_("fork() failed: %s"),
+ cw::util::sstrerror(errnum).c_str()));
+ }
+ else if(pid == 0)
+ {
+ int fdnullin = open("/dev/null", O_RDONLY);
+ if(fdnullin < 0)
+ close(0);
+ else
+ {
+ dup2(fdnullin, 0);
+ close(fdnullin);
+ }
+
+ int fdnullout = open("/dev/null", O_WRONLY);
+ if(fdnullout < 0)
+ {
+ close(1);
+ close(2);
+ }
+ else
+ {
+ dup2(fdnullout, 1);
+ dup2(fdnullout, 2);
+ close(fdnullout);
+ }
+
+ char **argv = new char*[args.size()];
+ for(std::vector<std::string>::size_type i = 0;
+ i < args.size(); ++i)
+ {
+ argv[i] = const_cast<char *>(args[i].c_str());
+ }
+
+ exit(execv(command.c_str(), argv));
+ }
+ else
+ {
+ while(true)
+ {
+ int status = 0;
+ errno = 0;
+ if(waitpid(pid, &status, 0) == pid)
+ return status;
+ else if(errno != EINTR)
+ {
+ int errnum = errno;
+ throw SubprocessException(cw::util::ssprintf(_("waitpid() failed: %s"),
+ cw::util::sstrerror(errnum).c_str()));
+ }
+ }
+ }
+ }
+}
+
download_manager::result
download_update_manager::finish(pkgAcquire::RunResult res,
OpProgress &progress)
@@ -136,14 +306,74 @@ download_update_manager::finish(pkgAcquire::RunResult res,
aptcfg->FindB(PACKAGE "::AutoClean-After-Update", false);
#ifdef HAVE_EPT
- progress.OverallProgress(0, 0, 1, _("Updating debtags database..."));
+ std::string debtags = aptcfg->Find(PACKAGE "::Debtags-Binary", "/usr/bin/debtags");
+
+ if(debtags.size() == 0)
+ _error->Error(_("The debtags command must not be an empty string."));
+ // Keep the user from killing themselves without trying: a relative
+ // path would open a root exploit.
+ else if(debtags[0] != '/')
+ _error->Error(_("The debtags command must be an absolute path."));
+ // Check up-front if we can execute the command. This is not ideal
+ // since there's a race condition (the command could go away before
+ // we try to execute it) but the worst that will happen is that we
+ // display a confusing error message (...exited with code 255).
+ else if(euidaccess(debtags.c_str(), X_OK) != 0)
+ {
+ int errnum = errno;
+ if(errnum == ENOENT)
+ _error->Warning(_("The debtags command (%s) does not exist; perhaps you need to install the debtags package?"),
+ debtags.c_str());
+ else
+ _error->Error(_("The debtags command (%s) cannot be executed: %s"),
+ debtags.c_str(), cw::util::sstrerror(errnum).c_str());
+ }
+ else
+ {
+ progress.OverallProgress(0, 0, 1, _("Updating debtags database..."));
- // Merge any new information in the apt cache into the debtags
- // cache.
- system("/usr/bin/debtags --local update > /dev/null 2>/dev/null");
+ std::string debtags_options = aptcfg->Find(PACKAGE "::Debtags-Update-Options", "--local");
- progress.Progress(1);
- progress.Done();
+ std::vector<std::string> args;
+ args.push_back(debtags);
+ args.push_back("update");
+ parse_command_arguments(debtags_options, args);
+
+ try
+ {
+ int status = run_in_subprocess_to_devnull(debtags, args);
+ if(WIFSIGNALED(status))
+ {
+ std::string coredumpstr;
+#ifdef WCOREDUMP
+ if(WCOREDUMP(status))
+ coredumpstr = std::string(" ") + _("(core dumped)");
+#endif
+ _error->Warning(_("The debtags update process (%s %s) was killed by signal %d%s."),
+ debtags.c_str(), debtags_options.c_str(), WTERMSIG(status),
+ coredumpstr.c_str());
+ }
+ else if(WIFEXITED(status))
+ {
+ if(WEXITSTATUS(status) != 0)
+ _error->Warning(_("The debtags update process (%s %s) exited abnormally (code %d)."),
+ debtags.c_str(), debtags_options.c_str(), WEXITSTATUS(status));
+ }
+ else
+ // This should never happen, but if it does something is
+ // wrong.
+ _error->Warning(_("The debtags update process (%s %s) exited in an unexpected way (status %d)."),
+ debtags.c_str(), debtags_options.c_str(), status);
+ }
+ catch(cw::util::Exception &e)
+ {
+ _error->Warning(_("Updating the debtags database (%s %s) failed (perhaps debtags is not installed?): %s"),
+ debtags.c_str(), debtags_options.c_str(), e.errmsg().c_str());
+ }
+
+ progress.Progress(1);
+ progress.Done();
+ }
#endif
if(need_forget_new || need_autoclean)