aboutsummaryrefslogtreecommitdiffstats
path: root/liblfjail/jail.cc
blob: cf5d600bbe110e2a6211069c1e3d1a1b33d1e618 (plain) (blame)
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
/*
 * This source code is released into the public domain.
 */

#include "exec.hh"
#include "jail.hh"
#include "generic_error.hh"
#include "fileutils.hh"
#include "jail_zfs.hh"
#include "spawn.hh"

namespace {

using namespace lfjail;

// Get the jails storage directory.
auto get_jails_dir(context const &ctx) -> std::string {
	return ctx.path("jails");
}

// Get the config file for a jail.
auto jail_config_file(context const &ctx,
		      std::string_view jailname)
	-> std::string
{
	return std::format("{}/{}.ucl", get_jails_dir(ctx), jailname);
}

// Make sure our jails config path exists.
void ensure_jails_dir(context const &ctx) {
	ensure_dir(ctx, get_jails_dir(ctx));
}

} // anonymous namespace

namespace lfjail {

auto get_jail_config(context const &ctx, std::string_view jailname)
	-> std::optional<jail>
{
	ensure_jails_dir(ctx);

	auto file = jail_config_file(ctx, jailname);

	// Load the configuration text.
	std::string config_text;
	try {
		read_file(file, std::back_inserter(config_text));
	} catch (io_error const &exc) {
		if (exc.error == std::errc::no_such_file_or_directory)
			return {};
		throw;
	}

	// Parse the UCL.

	std::string err;
	auto uclconfig = ucl::Ucl::parse(config_text, err);
	jail ret;
	ret.name = jailname;

	if (!uclconfig)
		throw generic_error("{0}: {1}", file, err);

	for (auto const &uclvalue : uclconfig) {
		throw generic_error("{}: unknown option '{}'",
				    file, uclvalue.key());
	}

	return {ret};
}

void set_jail_config(context const &ctx, jail const &jailconf) {
	// The UCL C++ API doesn't seem to support creating new objects
	// from scratch, so we use the C API here.

	auto ucl = ::ucl_object_typed_new(UCL_OBJECT);
	auto ucl_guard = guard([ucl] { ::ucl_object_unref(ucl); });

	// We could add some jail options here...

	// Dump the UCL object to a string.
	auto *ucl_c_text = reinterpret_cast<char *>(
				::ucl_object_emit(ucl, UCL_EMIT_CONFIG));
	//NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
	auto ucl_text_guard = guard([ucl_c_text] { ::free(ucl_c_text); });
	std::string ucl_text(ucl_c_text);

	// Write the object to a file.
	auto file = jail_config_file(ctx, jailconf.name);

	safe_write_file(file, ucl_text);
}

auto jail_exists(context const &ctx, std::string_view name) -> bool {
	return static_cast<bool>(get_jail_config(ctx, name));
}

void jail_destroy(context const &ctx, jail const &jailconf) {
	auto file = jail_config_file(ctx, jailconf.name);

	::unlink(file.c_str());
	zfs::destroy_for_jail(ctx, jailconf);
}

void jail_install(context const &, jail const &jailconf) {
	auto executor = exec::execl("/usr/sbin/pkg",
		"pkg", "-r", jailconf.root_path, "install", "-y",
		"FreeBSD-runtime", "FreeBSD-utilities", "FreeBSD-periodic",
		"FreeBSD-rc", "FreeBSD-syslogd", "FreeBSD-newsyslog",
		"FreeBSD-pkg-bootstrap", "pkg");

	auto output = std::string();
	auto ret = spawn(std::move(executor),
			 capture(STDOUT_FILENO, output)).wait();
	if (!ret)
		throw generic_error("pkg install failed");
}

} // namespace lfjail