diff options
Diffstat (limited to 'src/libutil')
-rw-r--r-- | src/libutil/affinity.cc | 6 | ||||
-rw-r--r-- | src/libutil/archive.cc | 6 | ||||
-rw-r--r-- | src/libutil/args.hh | 2 | ||||
-rw-r--r-- | src/libutil/finally.hh | 2 | ||||
-rw-r--r-- | src/libutil/json.cc | 10 | ||||
-rw-r--r-- | src/libutil/json.hh | 2 | ||||
-rw-r--r-- | src/libutil/logging.cc | 14 | ||||
-rw-r--r-- | src/libutil/logging.hh | 11 | ||||
-rw-r--r-- | src/libutil/regex.cc | 50 | ||||
-rw-r--r-- | src/libutil/regex.hh | 29 | ||||
-rw-r--r-- | src/libutil/serialise.cc | 2 | ||||
-rw-r--r-- | src/libutil/sync.hh | 8 | ||||
-rw-r--r-- | src/libutil/thread-pool.cc | 2 | ||||
-rw-r--r-- | src/libutil/types.hh | 69 | ||||
-rw-r--r-- | src/libutil/util.cc | 125 | ||||
-rw-r--r-- | src/libutil/util.hh | 75 |
16 files changed, 230 insertions, 183 deletions
diff --git a/src/libutil/affinity.cc b/src/libutil/affinity.cc index 3cbdf878617a..98f8287ada67 100644 --- a/src/libutil/affinity.cc +++ b/src/libutil/affinity.cc @@ -20,12 +20,12 @@ void setAffinityTo(int cpu) #if __linux__ if (sched_getaffinity(0, sizeof(cpu_set_t), &savedAffinity) == -1) return; didSaveAffinity = true; - printMsg(lvlDebug, format("locking this thread to CPU %1%") % cpu); + debug(format("locking this thread to CPU %1%") % cpu); cpu_set_t newAffinity; CPU_ZERO(&newAffinity); CPU_SET(cpu, &newAffinity); if (sched_setaffinity(0, sizeof(cpu_set_t), &newAffinity) == -1) - printMsg(lvlError, format("failed to lock thread to CPU %1%") % cpu); + printError(format("failed to lock thread to CPU %1%") % cpu); #endif } @@ -47,7 +47,7 @@ void restoreAffinity() #if __linux__ if (!didSaveAffinity) return; if (sched_setaffinity(0, sizeof(cpu_set_t), &savedAffinity) == -1) - printMsg(lvlError, "failed to restore affinity %1%"); + printError("failed to restore affinity %1%"); #endif } diff --git a/src/libutil/archive.cc b/src/libutil/archive.cc index edd4a881b485..fbba7f853f95 100644 --- a/src/libutil/archive.cc +++ b/src/libutil/archive.cc @@ -1,5 +1,3 @@ -#define _XOPEN_SOURCE 600 - #include "config.h" #include <cerrno> @@ -84,7 +82,7 @@ static void dump(const Path & path, Sink & sink, PathFilter & filter) string name(i.name); size_t pos = i.name.find(caseHackSuffix); if (pos != string::npos) { - printMsg(lvlDebug, format("removing case hack suffix from ‘%1%’") % (path + "/" + i.name)); + debug(format("removing case hack suffix from ‘%1%’") % (path + "/" + i.name)); name.erase(pos); } if (unhacked.find(name) != unhacked.end()) @@ -248,7 +246,7 @@ static void parse(ParseSink & sink, Source & source, const Path & path) if (useCaseHack) { auto i = names.find(name); if (i != names.end()) { - printMsg(lvlDebug, format("case collision between ‘%1%’ and ‘%2%’") % i->first % name); + debug(format("case collision between ‘%1%’ and ‘%2%’") % i->first % name); name += caseHackSuffix; name += std::to_string(++i->second); } else diff --git a/src/libutil/args.hh b/src/libutil/args.hh index 6aa08aacac9e..ac12f8be633a 100644 --- a/src/libutil/args.hh +++ b/src/libutil/args.hh @@ -8,7 +8,7 @@ namespace nix { -MakeError(UsageError, nix::Error); +MakeError(UsageError, Error); enum HashType : char; diff --git a/src/libutil/finally.hh b/src/libutil/finally.hh index 47c64deaecea..7760cfe9a410 100644 --- a/src/libutil/finally.hh +++ b/src/libutil/finally.hh @@ -1,5 +1,7 @@ #pragma once +#include <functional> + /* A trivial class to run a function at the end of a scope. */ class Finally { diff --git a/src/libutil/json.cc b/src/libutil/json.cc index ecc3fdfe514e..6023d1d4fb84 100644 --- a/src/libutil/json.cc +++ b/src/libutil/json.cc @@ -44,6 +44,16 @@ void toJSON(std::ostream & str, long n) str << n; } +void toJSON(std::ostream & str, unsigned int n) +{ + str << n; +} + +void toJSON(std::ostream & str, int n) +{ + str << n; +} + void toJSON(std::ostream & str, double f) { str << f; diff --git a/src/libutil/json.hh b/src/libutil/json.hh index aec456845056..03eecb732586 100644 --- a/src/libutil/json.hh +++ b/src/libutil/json.hh @@ -12,6 +12,8 @@ void toJSON(std::ostream & str, const char * s); void toJSON(std::ostream & str, unsigned long long n); void toJSON(std::ostream & str, unsigned long n); void toJSON(std::ostream & str, long n); +void toJSON(std::ostream & str, unsigned int n); +void toJSON(std::ostream & str, int n); void toJSON(std::ostream & str, double f); void toJSON(std::ostream & str, bool b); diff --git a/src/libutil/logging.cc b/src/libutil/logging.cc index 15bb1e175da6..d9e8d22d7685 100644 --- a/src/libutil/logging.cc +++ b/src/libutil/logging.cc @@ -52,7 +52,7 @@ Verbosity verbosity = lvlInfo; void warnOnce(bool & haveWarned, const FormatOrString & fs) { if (!haveWarned) { - printMsg(lvlError, format("warning: %1%") % fs.s); + printError(format("warning: %1%") % fs.s); haveWarned = true; } } @@ -60,14 +60,12 @@ void warnOnce(bool & haveWarned, const FormatOrString & fs) void writeToStderr(const string & s) { try { - writeFull(STDERR_FILENO, s); + writeFull(STDERR_FILENO, s, false); } catch (SysError & e) { - /* Ignore failing writes to stderr if we're in an exception - handler, otherwise throw an exception. We need to ignore - write errors in exception handlers to ensure that cleanup - code runs to completion if the other side of stderr has - been closed unexpectedly. */ - if (!std::uncaught_exception()) throw; + /* Ignore failing writes to stderr. We need to ignore write + errors to ensure that cleanup code that logs to stderr runs + to completion if the other side of stderr has been closed + unexpectedly. */ } } diff --git a/src/libutil/logging.hh b/src/libutil/logging.hh index 277dff280053..ba99a81c3826 100644 --- a/src/libutil/logging.hh +++ b/src/libutil/logging.hh @@ -66,14 +66,19 @@ Logger * makeDefaultLogger(); extern Verbosity verbosity; /* suppress msgs > this */ -#define printMsg(level, f) \ +/* Print a message if the current log level is at least the specified + level. Note that this has to be implemented as a macro to ensure + that the arguments are evaluated lazily. */ +#define printMsg(level, args...) \ do { \ if (level <= nix::verbosity) { \ - logger->log(level, (f)); \ + logger->log(level, fmt(args)); \ } \ } while (0) -#define debug(f) printMsg(lvlDebug, f) +#define printError(args...) printMsg(lvlError, args) +#define printInfo(args...) printMsg(lvlInfo, args) +#define debug(args...) printMsg(lvlDebug, args) void warnOnce(bool & haveWarned, const FormatOrString & fs); diff --git a/src/libutil/regex.cc b/src/libutil/regex.cc deleted file mode 100644 index 84274b3e1da9..000000000000 --- a/src/libutil/regex.cc +++ /dev/null @@ -1,50 +0,0 @@ -#include "regex.hh" -#include "types.hh" - -#include <algorithm> - -namespace nix { - -Regex::Regex(const string & pattern, bool subs) -{ - /* Patterns must match the entire string. */ - int err = regcomp(&preg, ("^(" + pattern + ")$").c_str(), (subs ? 0 : REG_NOSUB) | REG_EXTENDED); - if (err) throw RegexError(format("compiling pattern ‘%1%’: %2%") % pattern % showError(err)); - nrParens = subs ? std::count(pattern.begin(), pattern.end(), '(') : 0; -} - -Regex::~Regex() -{ - regfree(&preg); -} - -bool Regex::matches(const string & s) -{ - int err = regexec(&preg, s.c_str(), 0, 0, 0); - if (err == 0) return true; - else if (err == REG_NOMATCH) return false; - throw Error(format("matching string ‘%1%’: %2%") % s % showError(err)); -} - -bool Regex::matches(const string & s, Subs & subs) -{ - regmatch_t pmatch[nrParens + 2]; - int err = regexec(&preg, s.c_str(), nrParens + 2, pmatch, 0); - if (err == 0) { - for (unsigned int n = 2; n < nrParens + 2; ++n) - if (pmatch[n].rm_eo != -1) - subs[n - 2] = string(s, pmatch[n].rm_so, pmatch[n].rm_eo - pmatch[n].rm_so); - return true; - } - else if (err == REG_NOMATCH) return false; - throw Error(format("matching string ‘%1%’: %2%") % s % showError(err)); -} - -string Regex::showError(int err) -{ - char buf[256]; - regerror(err, &preg, buf, sizeof(buf)); - return string(buf); -} - -} diff --git a/src/libutil/regex.hh b/src/libutil/regex.hh deleted file mode 100644 index 53e31f4edc4a..000000000000 --- a/src/libutil/regex.hh +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "types.hh" - -#include <sys/types.h> -#include <regex.h> - -#include <map> - -namespace nix { - -MakeError(RegexError, Error) - -class Regex -{ -public: - Regex(const string & pattern, bool subs = false); - ~Regex(); - bool matches(const string & s); - typedef std::map<unsigned int, string> Subs; - bool matches(const string & s, Subs & subs); - -private: - unsigned nrParens; - regex_t preg; - string showError(int err); -}; - -} diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc index 776308cdf321..24c6d107359e 100644 --- a/src/libutil/serialise.cc +++ b/src/libutil/serialise.cc @@ -49,7 +49,7 @@ size_t threshold = 256 * 1024 * 1024; static void warnLargeDump() { - printMsg(lvlError, "warning: dumping very large path (> 256 MiB); this may run out of memory"); + printError("warning: dumping very large path (> 256 MiB); this may run out of memory"); } diff --git a/src/libutil/sync.hh b/src/libutil/sync.hh index ebe64ffbdab7..2aa074299b23 100644 --- a/src/libutil/sync.hh +++ b/src/libutil/sync.hh @@ -54,6 +54,14 @@ public: cv.wait(lk); } + template<class Rep, class Period> + void wait_for(std::condition_variable & cv, + const std::chrono::duration<Rep, Period> & duration) + { + assert(s); + cv.wait_for(lk, duration); + } + template<class Rep, class Period, class Predicate> bool wait_for(std::condition_variable & cv, const std::chrono::duration<Rep, Period> & duration, diff --git a/src/libutil/thread-pool.cc b/src/libutil/thread-pool.cc index 696ecd6c38c8..0a3a407240f7 100644 --- a/src/libutil/thread-pool.cc +++ b/src/libutil/thread-pool.cc @@ -87,7 +87,7 @@ void ThreadPool::workerEntry() if (state->exception) { if (!dynamic_cast<Interrupted*>(&e) && !dynamic_cast<ThreadPoolShutDown*>(&e)) - printMsg(lvlError, format("error: %s") % e.what()); + printError(format("error: %s") % e.what()); } else { state->exception = std::current_exception(); work.notify_all(); diff --git a/src/libutil/types.hh b/src/libutil/types.hh index bd192b8506b2..b9a93d27d2ad 100644 --- a/src/libutil/types.hh +++ b/src/libutil/types.hh @@ -41,6 +41,45 @@ struct FormatOrString }; +/* A helper for formatting strings. ‘fmt(format, a_0, ..., a_n)’ is + equivalent to ‘boost::format(format) % a_0 % ... % + ... a_n’. However, ‘fmt(s)’ is equivalent to ‘s’ (so no %-expansion + takes place). */ + +inline void formatHelper(boost::format & f) +{ +} + +template<typename T, typename... Args> +inline void formatHelper(boost::format & f, T x, Args... args) +{ + formatHelper(f % x, args...); +} + +inline std::string fmt(const std::string & s) +{ + return s; +} + +inline std::string fmt(const char * s) +{ + return s; +} + +inline std::string fmt(const FormatOrString & fs) +{ + return fs.s; +} + +template<typename... Args> +inline std::string fmt(const std::string & fs, Args... args) +{ + boost::format f(fs); + formatHelper(f, args...); + return f.str(); +} + + /* BaseError should generally not be caught, as it has Interrupted as a subclass. Catch Error instead. */ class BaseError : public std::exception @@ -49,14 +88,28 @@ protected: string prefix_; // used for location traces etc. string err; public: - unsigned int status; // exit status - BaseError(const FormatOrString & fs, unsigned int status = 1); + unsigned int status = 1; // exit status + + template<typename... Args> + BaseError(unsigned int status, Args... args) + : err(fmt(args...)) + , status(status) + { + } + + template<typename... Args> + BaseError(Args... args) + : err(fmt(args...)) + { + } + #ifdef EXCEPTION_NEEDS_THROW_SPEC ~BaseError() throw () { }; const char * what() const throw () { return err.c_str(); } #else const char * what() const noexcept { return err.c_str(); } #endif + const string & msg() const { return err; } const string & prefix() const { return prefix_; } BaseError & addPrefix(const FormatOrString & fs); @@ -66,7 +119,7 @@ public: class newClass : public superClass \ { \ public: \ - newClass(const FormatOrString & fs, unsigned int status = 1) : superClass(fs, status) { }; \ + using superClass::superClass; \ }; MakeError(Error, BaseError) @@ -75,7 +128,15 @@ class SysError : public Error { public: int errNo; - SysError(const FormatOrString & fs); + + template<typename... Args> + SysError(Args... args) + : Error(addErrno(fmt(args...))) + { } + +private: + + std::string addErrno(const std::string & s); }; diff --git a/src/libutil/util.cc b/src/libutil/util.cc index f1e714a664a5..ce16cc30a5c7 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -9,6 +9,7 @@ #include <cstdlib> #include <sstream> #include <cstring> +#include <cctype> #include <sys/wait.h> #include <unistd.h> @@ -30,13 +31,6 @@ extern char * * environ; namespace nix { -BaseError::BaseError(const FormatOrString & fs, unsigned int status) - : status(status) -{ - err = fs.s; -} - - BaseError & BaseError::addPrefix(const FormatOrString & fs) { prefix_ = fs.s + prefix_; @@ -44,10 +38,10 @@ BaseError & BaseError::addPrefix(const FormatOrString & fs) } -SysError::SysError(const FormatOrString & fs) - : Error(format("%1%: %2%") % fs.s % strerror(errno)) - , errNo(errno) +std::string SysError::addErrno(const std::string & s) { + errNo = errno; + return s + ": " + strerror(errNo); } @@ -58,6 +52,21 @@ string getEnv(const string & key, const string & def) } +std::map<std::string, std::string> getEnv() +{ + std::map<std::string, std::string> env; + for (size_t i = 0; environ[i]; ++i) { + auto s = environ[i]; + auto eq = strchr(s, '='); + if (!eq) + // invalid env, just keep going + continue; + env.emplace(std::string(s, eq), std::string(eq + 1)); + } + return env; +} + + Path absPath(Path path, Path dir) { if (path[0] != '/') { @@ -330,10 +339,11 @@ static void _deletePath(const Path & path, unsigned long long & bytesFreed) bytesFreed += st.st_blocks * 512; if (S_ISDIR(st.st_mode)) { - /* Make the directory writable. */ - if (!(st.st_mode & S_IWUSR)) { - if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1) - throw SysError(format("making ‘%1%’ writable") % path); + /* Make the directory accessible. */ + const auto PERM_MASK = S_IRUSR | S_IWUSR | S_IXUSR; + if ((st.st_mode & PERM_MASK) != PERM_MASK) { + if (chmod(path.c_str(), st.st_mode | PERM_MASK) == -1) + throw SysError(format("chmod ‘%1%’") % path); } for (auto & i : readDirectory(path)) @@ -473,24 +483,24 @@ void readFull(int fd, unsigned char * buf, size_t count) } -void writeFull(int fd, const unsigned char * buf, size_t count) +void writeFull(int fd, const unsigned char * buf, size_t count, bool allowInterrupts) { while (count) { - checkInterrupt(); ssize_t res = write(fd, (char *) buf, count); - if (res == -1) { - if (errno == EINTR) continue; + if (res == -1 && errno != EINTR) throw SysError("writing to file"); + if (res > 0) { + count -= res; + buf += res; } - count -= res; - buf += res; + if (allowInterrupts) checkInterrupt(); } } -void writeFull(int fd, const string & s) +void writeFull(int fd, const string & s, bool allowInterrupts) { - writeFull(fd, (const unsigned char *) s.data(), s.size()); + writeFull(fd, (const unsigned char *) s.data(), s.size(), allowInterrupts); } @@ -715,20 +725,20 @@ void Pid::kill(bool quiet) if (pid == -1 || pid == 0) return; if (!quiet) - printMsg(lvlError, format("killing process %1%") % pid); + printError(format("killing process %1%") % pid); /* Send the requested signal to the child. If it has its own process group, send the signal to every process in the child process group (which hopefully includes *all* its children). */ if (::kill(separatePG ? -pid : pid, killSignal) != 0) - printMsg(lvlError, (SysError(format("killing process %1%") % pid).msg())); + printError((SysError(format("killing process %1%") % pid).msg())); /* Wait until the child dies, disregarding the exit status. */ int status; while (waitpid(pid, &status, 0) == -1) { checkInterrupt(); if (errno != EINTR) { - printMsg(lvlError, + printError( (SysError(format("waiting for process %1%") % pid).msg())); break; } @@ -768,6 +778,14 @@ void Pid::setKillSignal(int signal) } +pid_t Pid::release() +{ + pid_t p = pid; + pid = -1; + return p; +} + + void killUser(uid_t uid) { debug(format("killing all processes running under uid ‘%1%’") % uid); @@ -918,7 +936,7 @@ string runProgram(Path program, bool searchPath, const Strings & args, /* Wait for the child to finish. */ int status = pid.wait(true); if (!statusOk(status)) - throw ExecError(format("program ‘%1%’ %2%") + throw ExecError(status, format("program ‘%1%’ %2%") % program % statusToString(status)); return result; @@ -1087,44 +1105,12 @@ bool hasSuffix(const string & s, const string & suffix) } -void expect(std::istream & str, const string & s) +std::string toLower(const std::string & s) { - char s2[s.size()]; - str.read(s2, s.size()); - if (string(s2, s.size()) != s) - throw FormatError(format("expected string ‘%1%’") % s); -} - - -string parseString(std::istream & str) -{ - string res; - expect(str, "\""); - int c; - while ((c = str.get()) != '"') - if (c == '\\') { - c = str.get(); - if (c == 'n') res += '\n'; - else if (c == 'r') res += '\r'; - else if (c == 't') res += '\t'; - else res += c; - } - else res += c; - return res; -} - - -bool endOfList(std::istream & str) -{ - if (str.peek() == ',') { - str.get(); - return false; - } - if (str.peek() == ']') { - str.get(); - return true; - } - return false; + std::string r(s); + for (auto & c : r) + c = std::tolower(c); + return r; } @@ -1148,7 +1134,7 @@ void ignoreException() try { throw; } catch (std::exception & e) { - printMsg(lvlError, format("error (ignored): %1%") % e.what()); + printError(format("error (ignored): %1%") % e.what()); } } @@ -1246,4 +1232,15 @@ string base64Decode(const string & s) } +void callFailure(const std::function<void(std::exception_ptr exc)> & failure, std::exception_ptr exc) +{ + try { + failure(exc); + } catch (std::exception & e) { + printError(format("uncaught exception: %s") % e.what()); + abort(); + } +} + + } diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 819921dfff1e..50b96f7ed92c 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -8,9 +8,11 @@ #include <dirent.h> #include <unistd.h> #include <signal.h> + #include <functional> #include <limits> #include <cstdio> +#include <map> #ifndef HAVE_STRUCT_DIRENT_D_TYPE #define DT_UNKNOWN 0 @@ -25,6 +27,9 @@ namespace nix { /* Return an environment variable. */ string getEnv(const string & key, const string & def = ""); +/* Get the entire environment. */ +std::map<std::string, std::string> getEnv(); + /* Return an absolutized path, resolving paths relative to the specified directory, or the current directory otherwise. The path is also canonicalised. */ @@ -120,8 +125,8 @@ void replaceSymlink(const Path & target, const Path & link); /* Wrappers arount read()/write() that read/write exactly the requested number of bytes. */ void readFull(int fd, unsigned char * buf, size_t count); -void writeFull(int fd, const unsigned char * buf, size_t count); -void writeFull(int fd, const string & s); +void writeFull(int fd, const unsigned char * buf, size_t count, bool allowInterrupts = true); +void writeFull(int fd, const string & s, bool allowInterrupts = true); MakeError(EndOfFile, Error) @@ -215,6 +220,7 @@ public: int wait(bool block); void setSeparatePG(bool separatePG); void setKillSignal(int signal); + pid_t release(); }; @@ -242,7 +248,16 @@ pid_t startProcess(std::function<void()> fun, const ProcessOptions & options = P string runProgram(Path program, bool searchPath = false, const Strings & args = Strings(), const string & input = ""); -MakeError(ExecError, Error) +class ExecError : public Error +{ +public: + int status; + + template<typename... Args> + ExecError(int status, Args... args) + : Error(args...), status(status) + { } +}; /* Convert a list of strings to a null-terminated vector of char *'s. The result must not be accessed beyond the lifetime of the @@ -334,18 +349,8 @@ bool hasPrefix(const string & s, const string & prefix); bool hasSuffix(const string & s, const string & suffix); -/* Read string `s' from stream `str'. */ -void expect(std::istream & str, const string & s); - -MakeError(FormatError, Error) - - -/* Read a C-style string from stream `str'. */ -string parseString(std::istream & str); - - -/* Utility function used to parse legacy ATerms. */ -bool endOfList(std::istream & str); +/* Convert a string to lower case. */ +std::string toLower(const std::string & s); /* Escape a string that contains octal-encoded escape codes such as @@ -386,4 +391,44 @@ string get(const T & map, const string & key, const string & def = "") } +/* Call ‘failure’ with the current exception as argument. If ‘failure’ + throws an exception, abort the program. */ +void callFailure(const std::function<void(std::exception_ptr exc)> & failure, + std::exception_ptr exc = std::current_exception()); + + +/* Evaluate the function ‘f’. If it returns a value, call ‘success’ + with that value as its argument. If it or ‘success’ throws an + exception, call ‘failure’. If ‘failure’ throws an exception, abort + the program. */ +template<class T> +void sync2async( + const std::function<void(T)> & success, + const std::function<void(std::exception_ptr exc)> & failure, + const std::function<T()> & f) +{ + try { + success(f()); + } catch (...) { + callFailure(failure); + } +} + + +/* Call the function ‘success’. If it throws an exception, call + ‘failure’. If that throws an exception, abort the program. */ +template<class T> +void callSuccess( + const std::function<void(T)> & success, + const std::function<void(std::exception_ptr exc)> & failure, + T && arg) +{ + try { + success(arg); + } catch (...) { + callFailure(failure); + } +} + + } |