diff options
author | Daniel Burrows <dburrows@debian.org> | 2008-01-19 09:55:08 -0800 |
---|---|---|
committer | Daniel Burrows <dburrows@debian.org> | 2008-01-19 09:55:08 -0800 |
commit | e770ef4a147b811b133ec787d6118c6408dc8da0 (patch) | |
tree | 5483ad6ef8d278c8c6b838972cbb253d19e7f357 /src/generic/apt/download_update_manager.cc | |
parent | 55eb8c6ffae4f278de1684923c4ab5e520a6da16 (diff) | |
download | aptitude-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.cc | 242 |
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) |