diff options
Diffstat (limited to 'src/libutil/util.hh')
-rw-r--r-- | src/libutil/util.hh | 166 |
1 files changed, 117 insertions, 49 deletions
diff --git a/src/libutil/util.hh b/src/libutil/util.hh index bee77b53f..20591952d 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -11,6 +11,9 @@ #include <unistd.h> #include <signal.h> +#include <boost/lexical_cast.hpp> + +#include <atomic> #include <functional> #include <map> #include <sstream> @@ -46,30 +49,32 @@ void clearEnv(); specified directory, or the current directory otherwise. The path is also canonicalised. */ Path absPath(Path path, - std::optional<Path> dir = {}, + std::optional<PathView> dir = {}, bool resolveSymlinks = false); /* Canonicalise a path by removing all `.' or `..' components and double or trailing slashes. Optionally resolves all symlink components such that each component of the resulting path is *not* a symbolic link. */ -Path canonPath(const Path & path, bool resolveSymlinks = false); +Path canonPath(PathView path, bool resolveSymlinks = false); /* Return the directory part of the given canonical path, i.e., everything before the final `/'. If the path is the root or an immediate child thereof (e.g., `/foo'), this means `/' is returned.*/ -Path dirOf(const Path & path); +Path dirOf(const PathView path); /* Return the base name of the given canonical path, i.e., everything following the final `/' (trailing slashes are removed). */ std::string_view baseNameOf(std::string_view path); -/* Check whether 'path' is a descendant of 'dir'. */ -bool isInDir(const Path & path, const Path & dir); +/* Check whether 'path' is a descendant of 'dir'. Both paths must be + canonicalized. */ +bool isInDir(std::string_view path, std::string_view dir); -/* Check whether 'path' is equal to 'dir' or a descendant of 'dir'. */ -bool isDirOrInDir(const Path & path, const Path & dir); +/* Check whether 'path' is equal to 'dir' or a descendant of + 'dir'. Both paths must be canonicalized. */ +bool isDirOrInDir(std::string_view path, std::string_view dir); /* Get status of `path'. */ struct stat lstat(const Path & path); @@ -87,22 +92,22 @@ bool isLink(const Path & path); removed. */ struct DirEntry { - string name; + std::string name; ino_t ino; unsigned char type; // one of DT_* - DirEntry(const string & name, ino_t ino, unsigned char type) - : name(name), ino(ino), type(type) { } + DirEntry(std::string name, ino_t ino, unsigned char type) + : name(std::move(name)), ino(ino), type(type) { } }; -typedef vector<DirEntry> DirEntries; +typedef std::vector<DirEntry> DirEntries; DirEntries readDirectory(const Path & path); unsigned char getFileType(const Path & path); /* Read the contents of a file into a string. */ -string readFile(int fd); -string readFile(const Path & path); +std::string readFile(int fd); +std::string readFile(const Path & path); void readFile(const Path & path, Sink & sink); /* Write a string to a file. */ @@ -111,10 +116,10 @@ void writeFile(const Path & path, std::string_view s, mode_t mode = 0666); void writeFile(const Path & path, Source & source, mode_t mode = 0666); /* Read a line from a file descriptor. */ -string readLine(int fd); +std::string readLine(int fd); /* Write a line to a file descriptor. */ -void writeLine(int fd, string s); +void writeLine(int fd, std::string s); /* Delete a path; i.e., in the case of a directory, it is deleted recursively. It's not an error if the path does not exist. The @@ -143,6 +148,9 @@ Path getDataDir(); /* Create a directory and all its parents, if necessary. Returns the list of created directories, in order of creation. */ Paths createDirs(const Path & path); +inline Paths createDirs(PathView path) { + return createDirs(Path(path)); +} /* Create a symlink. */ void createSymlink(const Path & target, const Path & link, @@ -162,7 +170,7 @@ MakeError(EndOfFile, Error); /* Read a file descriptor until EOF occurs. */ -string drainFD(int fd, bool block = true, const size_t reserveSize=0); +std::string drainFD(int fd, bool block = true, const size_t reserveSize=0); void drainFD(int fd, Sink & sink, bool block = true); @@ -182,6 +190,7 @@ public: void cancel(); void reset(const Path & p, bool recursive = true); operator Path() const { return path; } + operator PathView() const { return path; } }; @@ -259,10 +268,10 @@ void killUser(uid_t uid); pid to the caller. */ struct ProcessOptions { - string errorPrefix = "error: "; + std::string errorPrefix = ""; bool dieWithParent = true; bool runExitHandlers = false; - bool allowVfork = true; + bool allowVfork = false; }; pid_t startProcess(std::function<void()> fun, const ProcessOptions & options = ProcessOptions()); @@ -270,7 +279,7 @@ pid_t startProcess(std::function<void()> fun, const ProcessOptions & options = P /* Run a program and return its stdout in a string (i.e., like the shell backtick operator). */ -string runProgram(Path program, bool searchPath = false, +std::string runProgram(Path program, bool searchPath = false, const Strings & args = Strings(), const std::optional<std::string> & input = {}); @@ -299,8 +308,21 @@ void setStackSize(size_t stackSize); /* Restore the original inherited Unix process context (such as signal - masks, stack size, CPU affinity). */ -void restoreProcessContext(); + masks, stack size). */ +void restoreProcessContext(bool restoreMounts = true); + +/* Save the current mount namespace. Ignored if called more than + once. */ +void saveMountNamespace(); + +/* Restore the mount namespace saved by saveMountNamespace(). Ignored + if saveMountNamespace() was never called. */ +void restoreMountNamespace(); + +/* Cause this thread to not share any FS attributes with the main + thread, because this causes setns() in restoreMountNamespace() to + fail. */ +void unshareFilesystem(); class ExecError : public Error @@ -321,7 +343,7 @@ std::vector<char *> stringsToCharPtrs(const Strings & ss); /* Close all file descriptors except those listed in the given set. Good practice in child processes. */ -void closeMostFDs(const set<int> & exceptions); +void closeMostFDs(const std::set<int> & exceptions); /* Set the close-on-exec flag for the given file descriptor. */ void closeOnExec(int fd); @@ -329,7 +351,7 @@ void closeOnExec(int fd); /* User interruption. */ -extern bool _isInterrupted; +extern std::atomic<bool> _isInterrupted; extern thread_local std::function<bool()> interruptCheck; @@ -350,15 +372,19 @@ MakeError(FormatError, Error); /* String tokenizer. */ -template<class C> C tokenizeString(std::string_view s, const string & separators = " \t\n\r"); +template<class C> C tokenizeString(std::string_view s, std::string_view separators = " \t\n\r"); /* Concatenate the given strings with a separator between the elements. */ template<class C> -string concatStringsSep(const string & sep, const C & ss) +std::string concatStringsSep(const std::string_view sep, const C & ss) { - string s; + size_t size = 0; + // need a cast to string_view since this is also called with Symbols + for (const auto & s : ss) size += sep.size() + std::string_view(s).size(); + std::string s; + s.reserve(size); for (auto & i : ss) { if (s.size() != 0) s += sep; s += i; @@ -366,6 +392,14 @@ string concatStringsSep(const string & sep, const C & ss) return s; } +template<class ... Parts> +auto concatStrings(Parts && ... parts) + -> std::enable_if_t<(... && std::is_convertible_v<Parts, std::string_view>), std::string> +{ + std::string_view views[sizeof...(parts)] = { parts... }; + return concatStringsSep({}, views); +} + /* Add quotes around a collection of strings. */ template<class C> Strings quoteStrings(const C & c) @@ -379,45 +413,47 @@ template<class C> Strings quoteStrings(const C & c) /* Remove trailing whitespace from a string. FIXME: return std::string_view. */ -string chomp(std::string_view s); +std::string chomp(std::string_view s); /* Remove whitespace from the start and end of a string. */ -string trim(const string & s, const string & whitespace = " \n\r\t"); +std::string trim(std::string_view s, std::string_view whitespace = " \n\r\t"); /* Replace all occurrences of a string inside another string. */ -string replaceStrings(std::string_view s, - const std::string & from, const std::string & to); +std::string replaceStrings( + std::string s, + std::string_view from, + std::string_view to); -std::string rewriteStrings(const std::string & s, const StringMap & rewrites); +std::string rewriteStrings(std::string s, const StringMap & rewrites); /* Convert the exit status of a child as returned by wait() into an error string. */ -string statusToString(int status); +std::string statusToString(int status); bool statusOk(int status); /* Parse a string into an integer. */ template<class N> -std::optional<N> string2Int(const std::string & s) +std::optional<N> string2Int(const std::string_view s) { if (s.substr(0, 1) == "-" && !std::numeric_limits<N>::is_signed) return std::nullopt; - std::istringstream str(s); - N n; - str >> n; - if (str && str.get() == EOF) return n; - return std::nullopt; + try { + return boost::lexical_cast<N>(s.data(), s.size()); + } catch (const boost::bad_lexical_cast &) { + return std::nullopt; + } } /* Like string2Int(), but support an optional suffix 'K', 'M', 'G' or 'T' denoting a binary unit prefix. */ template<class N> -N string2IntWithUnitPrefix(std::string s) +N string2IntWithUnitPrefix(std::string_view s) { N multiplier = 1; if (!s.empty()) { @@ -428,7 +464,7 @@ N string2IntWithUnitPrefix(std::string s) else if (u == 'G') multiplier = 1ULL << 30; else if (u == 'T') multiplier = 1ULL << 40; else throw UsageError("invalid unit specifier '%1%'", u); - s.resize(s.size() - 1); + s.remove_suffix(1); } } if (auto n = string2Int<N>(s)) @@ -438,13 +474,13 @@ N string2IntWithUnitPrefix(std::string s) /* Parse a string into a float. */ template<class N> -std::optional<N> string2Float(const string & s) +std::optional<N> string2Float(const std::string_view s) { - std::istringstream str(s); - N n; - str >> n; - if (str && str.get() == EOF) return n; - return std::nullopt; + try { + return boost::lexical_cast<N>(s.data(), s.size()); + } catch (const boost::bad_lexical_cast &) { + return std::nullopt; + } } @@ -461,7 +497,7 @@ std::string toLower(const std::string & s); /* Escape a string as a shell word. */ -std::string shellEscape(const std::string & s); +std::string shellEscape(const std::string_view s); /* Exception handling in destructors: print an error message, then @@ -491,8 +527,8 @@ std::string filterANSIEscapes(const std::string & s, /* Base64 encoding/decoding. */ -string base64Encode(std::string_view s); -string base64Decode(std::string_view s); +std::string base64Encode(std::string_view s); +std::string base64Decode(std::string_view s); /* Remove common leading whitespace from the lines in the string @@ -511,6 +547,29 @@ std::optional<typename T::mapped_type> get(const T & map, const typename T::key_ } +/* Remove and return the first item from a container. */ +template <class T> +std::optional<typename T::value_type> remove_begin(T & c) +{ + auto i = c.begin(); + if (i == c.end()) return {}; + auto v = std::move(*i); + c.erase(i); + return v; +} + + +/* Remove and return the first item from a container. */ +template <class T> +std::optional<typename T::value_type> pop(T & c) +{ + if (c.empty()) return {}; + auto v = std::move(c.front()); + c.pop(); + return v; +} + + template<typename T> class Callback; @@ -571,9 +630,18 @@ extern PathFilter defaultPathFilter; /* Common initialisation performed in child processes. */ void commonChildInit(Pipe & logPipe); +/* Create a Unix domain socket. */ +AutoCloseFD createUnixDomainSocket(); + /* Create a Unix domain socket in listen mode. */ AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode); +/* Bind a Unix domain socket to a path. */ +void bind(int fd, const std::string & path); + +/* Connect to a Unix domain socket. */ +void connect(int fd, const std::string & path); + // A Rust/Python-like enumerate() iterator adapter. // Borrowed from http://reedbeta.com/blog/python-like-enumerate-in-cpp17. |