1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
/*
* This source code is released into the public domain.
*/
/*
* Generic (non-jail-related) ZFS handling.
*
* It would be nice to use libzfs_core for this, but it still has some issues
* on FreeBSD, e.g. https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=257222.
*/
#include <unordered_map>
#include <unistd.h>
#include "ctype.hh"
#include "spawn.hh"
#include "words.hh"
#include "zfs.hh"
using namespace std::literals;
namespace lfjail::zfs {
namespace {
// Hardcode the program locations so we don't rely on $PATH.
constexpr std::string path_zpool = "/sbin/zpool";
constexpr std::string path_zfs = "/sbin/zfs";
// Run a command (probably a zfs command), split its output into lines,
// and return each line.
//
// The spawn_how argument is the same as lfjail::spawn().
auto spawn_lines(exec::executor auto &&executor)
-> std::generator<std::string_view>
{
std::string output;
auto result = spawn(std::move(executor),
capture(STDOUT_FILENO, output)).wait();
if (!result)
throw zfs_error("failed to run zfs command");
co_yield std::ranges::elements_of(words(output, '\n'));
}
// Like spawn_lines, but each line is returned as a vector of words.
auto spawn_words(auto &&spawn_how)
-> std::generator<std::vector<std::string_view>>
{
auto lines = spawn_lines(std::forward<decltype(spawn_how)>(spawn_how));
for (auto &&line : lines)
co_yield std::vector(std::from_range, words(line));
}
// Run a command which produces two columns of output, assumed to be
// a name and a value; call the function from the provided map on
// each line.
auto set_values(auto &&map, std::string command, auto &&...args) -> void
{
auto lines = spawn_words(
exec::execl(std::move(command), std::move(args)...));
for (auto &&words : lines) {
if (std::ranges::size(words) != 2)
throw zfs_error("unexpected output from zfs command");
auto property = words[0];
auto value = words[1];
auto handler = map.find(property);
if (handler != map.end())
handler->second(value);
}
}
} // anonymous namespace
auto get_pool(std::string_view name) -> pool {
auto ret = pool{};
auto handlers = std::unordered_map{
std::pair{"name"sv, [&ret](auto &&v) { ret.name = v; }},
};
set_values(handlers, path_zpool, "zpool", "get",
"-H", "-oproperty,value", "all", name);
return ret;
}
auto get_pools() -> std::generator<pool> {
auto lines = spawn_words(
exec::execl(path_zpool, "zpool", "get", "-H", "-o", "name"));
for (auto &&words : lines)
co_yield get_pool(words[0]);
}
auto get_filesystem(std::string_view name) -> filesystem {
auto ret = filesystem{};
auto handlers = std::unordered_map{
std::pair{"name"sv, [&ret](auto &&v) { ret.name = v; }},
};
set_values(handlers, path_zfs, "zfs", "get", "-H",
"-oproperty,value"sv, "all", name);
return ret;
}
auto get_filesystems() -> std::generator<filesystem> {
auto lines = spawn_words(
exec::execl(path_zfs, "zfs", "get", "-H", "-o", "name"));
for (auto &&words : lines)
co_yield get_filesystem(words[0]);
}
} // namespace lfjail::zfs
|