aboutsummaryrefslogtreecommitdiff
path: root/src/libutil/util.hh
diff options
context:
space:
mode:
authorEelco Dolstra <edolstra@gmail.com>2021-09-15 20:33:44 +0200
committerGitHub <noreply@github.com>2021-09-15 20:33:44 +0200
commit79152e307e7eef667c3de9c21571d017654a7c32 (patch)
tree67fd413bcf0b42c5ada7eddc41a04f7bd99df3a8 /src/libutil/util.hh
parent7349f257da8278af9aae35544b15c9a204e2a57b (diff)
parent3b82c1a5fef521ebadea5df12384390c8c24100c (diff)
Merge pull request #5212 from mkenigs/auto-uid-allocation
Merge master into #3600
Diffstat (limited to 'src/libutil/util.hh')
-rw-r--r--src/libutil/util.hh155
1 files changed, 84 insertions, 71 deletions
diff --git a/src/libutil/util.hh b/src/libutil/util.hh
index 3641daaec..bee77b53f 100644
--- a/src/libutil/util.hh
+++ b/src/libutil/util.hh
@@ -12,13 +12,9 @@
#include <signal.h>
#include <functional>
-#include <limits>
-#include <cstdio>
#include <map>
#include <sstream>
#include <optional>
-#include <future>
-#include <iterator>
#ifndef HAVE_STRUCT_DIRENT_D_TYPE
#define DT_UNKNOWN 0
@@ -49,7 +45,9 @@ void clearEnv();
/* Return an absolutized path, resolving paths relative to the
specified directory, or the current directory otherwise. The path
is also canonicalised. */
-Path absPath(Path path, std::optional<Path> dir = {});
+Path absPath(Path path,
+ std::optional<Path> dir = {},
+ bool resolveSymlinks = false);
/* Canonicalise a path by removing all `.' or `..' components and
double or trailing slashes. Optionally resolves all symlink
@@ -108,7 +106,7 @@ string readFile(const Path & path);
void readFile(const Path & path, Sink & sink);
/* Write a string to a file. */
-void writeFile(const Path & path, const string & s, mode_t mode = 0666);
+void writeFile(const Path & path, std::string_view s, mode_t mode = 0666);
void writeFile(const Path & path, Source & source, mode_t mode = 0666);
@@ -123,7 +121,7 @@ void writeLine(int fd, string s);
second variant returns the number of bytes and blocks freed. */
void deletePath(const Path & path);
-void deletePath(const Path & path, unsigned long long & bytesFreed);
+void deletePath(const Path & path, uint64_t & bytesFreed);
std::string getUserName();
@@ -147,17 +145,18 @@ Path getDataDir();
Paths createDirs(const Path & path);
/* Create a symlink. */
-void createSymlink(const Path & target, const Path & link);
+void createSymlink(const Path & target, const Path & link,
+ std::optional<time_t> mtime = {});
/* Atomically create or replace a symlink. */
-void replaceSymlink(const Path & target, const Path & link);
+void replaceSymlink(const Path & target, const Path & link,
+ std::optional<time_t> mtime = {});
/* 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, bool allowInterrupts = true);
-void writeFull(int fd, const string & s, bool allowInterrupts = true);
+void readFull(int fd, char * buf, size_t count);
+void writeFull(int fd, std::string_view s, bool allowInterrupts = true);
MakeError(EndOfFile, Error);
@@ -189,7 +188,6 @@ public:
class AutoCloseFD
{
int fd;
- void close();
public:
AutoCloseFD();
AutoCloseFD(int fd);
@@ -201,6 +199,7 @@ public:
int get() const;
explicit operator bool() const;
int release();
+ void close();
};
@@ -217,6 +216,7 @@ class Pipe
public:
AutoCloseFD readSide, writeSide;
void create();
+ void close();
};
@@ -276,30 +276,33 @@ string runProgram(Path program, bool searchPath = false,
struct RunOptions
{
+ Path program;
+ bool searchPath = true;
+ Strings args;
std::optional<uid_t> uid;
std::optional<uid_t> gid;
std::optional<Path> chdir;
std::optional<std::map<std::string, std::string>> environment;
- Path program;
- bool searchPath = true;
- Strings args;
std::optional<std::string> input;
Source * standardIn = nullptr;
Sink * standardOut = nullptr;
bool mergeStderrToStdout = false;
- bool _killStderr = false;
-
- RunOptions(const Path & program, const Strings & args)
- : program(program), args(args) { };
-
- RunOptions & killStderr(bool v) { _killStderr = true; return *this; }
};
-std::pair<int, std::string> runProgram(const RunOptions & options);
+std::pair<int, std::string> runProgram(RunOptions && options);
void runProgram2(const RunOptions & options);
+/* Change the stack size. */
+void setStackSize(size_t stackSize);
+
+
+/* Restore the original inherited Unix process context (such as signal
+ masks, stack size, CPU affinity). */
+void restoreProcessContext();
+
+
class ExecError : public Error
{
public:
@@ -374,8 +377,9 @@ template<class C> Strings quoteStrings(const C & c)
}
-/* Remove trailing whitespace from a string. */
-string chomp(const string & s);
+/* Remove trailing whitespace from a string. FIXME: return
+ std::string_view. */
+string chomp(std::string_view s);
/* Remove whitespace from the start and end of a string. */
@@ -383,7 +387,7 @@ string trim(const string & s, const string & whitespace = " \n\r\t");
/* Replace all occurrences of a string inside another string. */
-string replaceStrings(const std::string & s,
+string replaceStrings(std::string_view s,
const std::string & from, const std::string & to);
@@ -398,21 +402,49 @@ bool statusOk(int status);
/* Parse a string into an integer. */
-template<class N> bool string2Int(const string & s, N & n)
+template<class N>
+std::optional<N> string2Int(const std::string & s)
{
- if (string(s, 0, 1) == "-" && !std::numeric_limits<N>::is_signed)
- return false;
+ if (s.substr(0, 1) == "-" && !std::numeric_limits<N>::is_signed)
+ return std::nullopt;
std::istringstream str(s);
+ N n;
str >> n;
- return str && str.get() == EOF;
+ if (str && str.get() == EOF) return n;
+ 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 multiplier = 1;
+ if (!s.empty()) {
+ char u = std::toupper(*s.rbegin());
+ if (std::isalpha(u)) {
+ if (u == 'K') multiplier = 1ULL << 10;
+ else if (u == 'M') multiplier = 1ULL << 20;
+ 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);
+ }
+ }
+ if (auto n = string2Int<N>(s))
+ return *n * multiplier;
+ throw UsageError("'%s' is not an integer", s);
}
/* Parse a string into a float. */
-template<class N> bool string2Float(const string & s, N & n)
+template<class N>
+std::optional<N> string2Float(const string & s)
{
std::istringstream str(s);
+ N n;
str >> n;
- return str && str.get() == EOF;
+ if (str && str.get() == EOF) return n;
+ return std::nullopt;
}
@@ -444,6 +476,9 @@ constexpr char treeLast[] = "└───";
constexpr char treeLine[] = "│ ";
constexpr char treeNull[] = " ";
+/* Determine whether ANSI escape sequences are appropriate for the
+ present output. */
+bool shouldANSI();
/* Truncate a string to 'width' printable characters. If 'filterAll'
is true, all ANSI escape sequences are filtered out. Otherwise,
@@ -460,6 +495,12 @@ string base64Encode(std::string_view s);
string base64Decode(std::string_view s);
+/* Remove common leading whitespace from the lines in the string
+ 's'. For example, if every line is indented by at least 3 spaces,
+ then we remove 3 spaces from the start of every line. */
+std::string stripIndentation(std::string_view s);
+
+
/* Get a value for the specified key from an associate container. */
template <class T>
std::optional<typename T::mapped_type> get(const T & map, const typename T::key_type & key)
@@ -470,52 +511,14 @@ std::optional<typename T::mapped_type> get(const T & map, const typename T::key_
}
-/* A callback is a wrapper around a lambda that accepts a valid of
- type T or an exception. (We abuse std::future<T> to pass the value or
- exception.) */
template<typename T>
-class Callback
-{
- std::function<void(std::future<T>)> fun;
- std::atomic_flag done = ATOMIC_FLAG_INIT;
-
-public:
-
- Callback(std::function<void(std::future<T>)> fun) : fun(fun) { }
-
- Callback(Callback && callback) : fun(std::move(callback.fun))
- {
- auto prev = callback.done.test_and_set();
- if (prev) done.test_and_set();
- }
-
- void operator()(T && t) noexcept
- {
- auto prev = done.test_and_set();
- assert(!prev);
- std::promise<T> promise;
- promise.set_value(std::move(t));
- fun(promise.get_future());
- }
-
- void rethrow(const std::exception_ptr & exc = std::current_exception()) noexcept
- {
- auto prev = done.test_and_set();
- assert(!prev);
- std::promise<T> promise;
- promise.set_exception(exc);
- fun(promise.get_future());
- }
-};
+class Callback;
/* Start a thread that handles various signals. Also block those signals
on the current thread (and thus any threads created by it). */
void startSignalHandlerThread();
-/* Restore default signal handling. */
-void restoreSignals();
-
struct InterruptCallback
{
virtual ~InterruptCallback() { };
@@ -565,6 +568,8 @@ typedef std::function<bool(const Path & path)> PathFilter;
extern PathFilter defaultPathFilter;
+/* Common initialisation performed in child processes. */
+void commonChildInit(Pipe & logPipe);
/* Create a Unix domain socket in listen mode. */
AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode);
@@ -597,4 +602,12 @@ constexpr auto enumerate(T && iterable)
}
+// C++17 std::visit boilerplate
+template<class... Ts> struct overloaded : Ts... { using Ts::operator()...; };
+template<class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
+
+
+std::string showBytes(uint64_t bytes);
+
+
}