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
125
|
/*
* 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 and split each line of output into whitespace-separated
* columns.
*/
auto columns(exec::executor auto &&executor)
-> std::generator<std::vector<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");
for (auto &&line : words(output, '\n')) {
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 = columns(
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 = columns(exec::execl(path_zpool,
"zpool", "get", "-H", "-o", "name"));
for (auto &&words : lines)
co_yield get_pool(words[0]);
}
auto get_dataset(std::string_view name) -> dataset {
auto ret = dataset{};
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_dataset_names() -> std::generator<std::string> {
auto lines = columns(exec::execl(path_zfs,
"zfs", "get", "-H", "-o", "name"));
for (auto &&words : lines)
co_yield std::string(words[0]);
}
auto get_datasets() -> std::generator<dataset> {
for (auto &&dataset : get_dataset_names())
co_yield get_dataset(dataset);
}
auto dataset_exists(std::string name) -> bool {
return std::ranges::contains(get_dataset_names(), name);
}
} // namespace lfjail::zfs
|