45namespace fs = std::filesystem;
75 std::vector<fs::path> vec_path_dir_layer;
76 fs::path path_dir_upper;
77 fs::path path_dir_work;
85 fs::path
const path_file_apparmor;
86 Logs(fs::path
const& path_dir_log) : path_file_apparmor(path_dir_log /
"apparmor.log")
88 fs::create_directories(path_file_apparmor.parent_path());
100 std::string
const name;
101 std::filesystem::path
const path_dir_home;
102 std::filesystem::path
const path_file_shell;
103 std::filesystem::path
const path_file_bashrc;
104 std::filesystem::path
const path_file_passwd;
110 inline operator std::string()
112 return std::format(
"{}:x:{}:{}:{}:{}:{}", name,
id.uid,
id.gid, name, path_dir_home.string(), path_file_shell.string());
132 std::ofstream file_passwd{path_file_passwd};
133 return_if(not file_passwd.is_open(), Error(
"E::Failed to open passwd file at {}", path_file_passwd));
134 file_passwd << std::string{data} <<
'\n';
146 std::ofstream file_bashrc{path_file_bashrc};
147 return_if(not file_bashrc.is_open(), Error(
"E::Failed to open bashrc file at {}", path_file_bashrc));
151 file_bashrc << R
"(export PS1="[flatimage-${FIM_DIST,,}] \W > ")";
155 file_bashrc <<
"export PS1=" <<
'"' << ps1 <<
'"';
178 if(not Try(fs::exists(path_dir_work),
"E::Could not check bwrap work directory"))
183 if(::chmod(path_dir_work.c_str(), 0755) < 0)
185 logger(
"D::Error to modify permissions '{}': '{}'", path_dir_work, strerror(errno));
191 Try(fs::remove_all(path_dir_work),
"E::Failed to remove bwrap work directory");
204 std::vector<fs::path> vec_path_dir_layer = fs::directory_iterator(path_dir_layers)
205 | std::views::filter([](
auto&& e){
return fs::is_directory(e.path()); })
206 | std::views::transform([](
auto&& e){
return e.path(); })
207 | std::ranges::to<std::vector<fs::path>>();
208 std::ranges::sort(vec_path_dir_layer);
209 return vec_path_dir_layer;
214using Permission = ns_reserved::ns_permissions::Permission;
216using Unshare = ns_reserved::ns_unshare::Unshare;
233 fs::path m_path_file_program;
234 std::vector<std::string> m_program_args;
235 std::vector<std::string> m_program_env;
237 std::optional<ns_proxy::Overlay> m_overlay;
239 fs::path
const m_path_dir_root;
241 fs::path m_path_dir_xdg_runtime;
243 std::vector<std::string> m_args;
249 void set_xdg_runtime_dir();
252 Bwrap& symlink_nvidia(fs::path
const& path_dir_root_guest, fs::path
const& path_dir_root_host);
257 , fs::path
const& path_dir_root
258 , fs::path
const& path_file_program
259 , std::vector<std::string>
const& program_args
260 , std::vector<std::string>
const& program_env);
281 [[maybe_unused]] [[nodiscard]]
Bwrap&
with_bind_gpu(fs::path
const& path_dir_root_guest, fs::path
const& path_dir_root_host);
282 [[maybe_unused]] [[nodiscard]]
Bwrap&
with_bind(fs::path
const& src, fs::path
const& dst);
283 [[maybe_unused]] [[nodiscard]]
Bwrap&
with_bind_ro(fs::path
const& src, fs::path
const& dst);
286 , Unshares
const& unshares
287 , fs::path
const& path_file_daemon
306 , fs::path
const& path_dir_root
307 , fs::path
const& path_file_program
308 , std::vector<std::string>
const& program_args
309 , std::vector<std::string>
const& program_env)
311 , m_path_file_program(path_file_program)
312 , m_program_args(program_args)
314 , m_overlay(std::nullopt)
315 , m_path_dir_root(path_dir_root)
316 , m_path_dir_xdg_runtime()
318 , m_is_root(user.data.id.uid == 0)
321 std::ranges::for_each(program_env, [&](
auto&& e){
logger(
"I::ENV: {}", e); m_program_env.push_back(e); });
323 m_program_env.push_back(
"TERM=xterm");
327 ,
"--setenv",
"USER", std::string{user.data.name}
329 ,
"--uid", std::to_string(user.data.id.uid),
"--gid", std::to_string(user.data.id.gid)
331 ,
"--setenv",
"HOME", user.data.path_dir_home.string()
333 ,
"--setenv",
"SHELL", user.data.path_file_shell.string()
336 ns_env::set(
"BASHRC_FILE", user.data.path_file_bashrc.c_str(), ns_env::Replace::Y);
341 ,
"--bind",
"/tmp",
"/tmp"
342 ,
"--bind",
"/sys",
"/sys"
343 ,
"--bind-try",
"/etc/group",
"/etc/group"
349 set_xdg_runtime_dir();
357 if(not m_overlay) {
return; }
359 ns_bwrap::bwrap_clean(m_overlay->path_dir_work /
"work").discard(
"E::Could not clean bwrap directory");
371 std::vector<std::string> args;
373 for(fs::path
const& path_dir_layer : overlay.vec_path_dir_layer)
375 logger(
"I::Overlay layer '{}'", path_dir_layer);
379 m_args.insert(m_args.begin(), args.begin(), args.end());
385inline void Bwrap::set_xdg_runtime_dir()
387 m_path_dir_xdg_runtime =
ns_env::get_expected<
"W">(
"XDG_RUNTIME_DIR").value_or(std::format(
"/run/user/{}", getuid()));
388 logger(
"I::XDG_RUNTIME_DIR: {}", m_path_dir_xdg_runtime);
389 m_program_env.push_back(std::format(
"XDG_RUNTIME_DIR={}", m_path_dir_xdg_runtime.string()));
399inline Value<fs::path> Bwrap::test_and_setup(fs::path
const& path_file_bwrap_src)
403 auto ret = ns_subprocess::Subprocess(path_file_bwrap_src)
404 .with_args(
"--bind",
"/",
"/",
"bash",
"-c",
"echo")
407 return_if(ret and *ret == 0, path_file_bwrap_src);
409 fs::path path_file_bwrap_opt =
"/opt/flatimage/bwrap";
410 ret = ns_subprocess::Subprocess(path_file_bwrap_opt)
411 .with_args(
"--bind",
"/",
"/",
"bash",
"-c",
"echo")
414 return_if(ret and *ret == 0, path_file_bwrap_opt);
418 Pop(ns_subprocess::Subprocess(path_file_pkexec)
419 .with_args(path_file_bwrap_apparmor, m_logs.path_file_apparmor, path_file_bwrap_src)
421 return path_file_bwrap_opt;
431inline Bwrap& Bwrap::symlink_nvidia(fs::path
const& path_dir_root_guest, fs::path
const& path_dir_root_host)
433 std::regex regex_exclude(
"gst|icudata|egl-wayland", std::regex_constants::extended);
435 auto f_find_and_bind = [&]<
typename... Args>(fs::path
const& path_dir_search, Args&&... args) ->
void
437 std::vector<std::string_view> keywords{std::forward<Args>(args)...};
438 return_if(not fs::exists(path_dir_search),,
"E::Search path does not exist: '{}'", path_dir_search);
439 auto f_process_entry = [&](fs::path
const& path_file_entry) ->
void
442 return_if(std::regex_search(path_file_entry.c_str(), regex_exclude),);
444 return_if(fs::is_directory(path_file_entry),);
446 return_if(not std::ranges::any_of(keywords, [&](
auto&& f){
return path_file_entry.filename().string().contains(f); }),);
449 auto path_file_entry_realpath = fs::canonical(path_file_entry);
451 fs::path path_link_target = path_dir_root_host / path_file_entry_realpath.relative_path();
452 fs::path path_link_name = path_dir_root_guest / path_file_entry.relative_path();
454 return_if(fs::exists(path_link_name) and not fs::is_symlink(path_link_name),);
456 fs::create_directories(path_link_name.parent_path());
458 fs::remove(path_link_name);
460 fs::create_symlink(path_link_target.c_str(), path_link_name.c_str());
462 logger(
"D::PERM(NVIDIA): {} -> {}", path_link_name, path_link_target);
465 for(
auto&& path_file_entry : fs::directory_iterator(path_dir_search) | std::views::transform([](
auto&& e){
return e.path(); }))
467 Catch(f_process_entry(path_file_entry)).template discard();
472 f_find_and_bind(
"/usr/lib",
"nvidia",
"cuda",
"nvcuvid",
"nvoptix");
473 f_find_and_bind(
"/usr/lib/x86_64-linux-gnu",
"nvidia",
"cuda",
"nvcuvid",
"nvoptix");
474 f_find_and_bind(
"/usr/lib/i386-linux-gnu",
"nvidia",
"cuda",
"nvcuvid",
"nvoptix");
475 f_find_and_bind(
"/usr/bin",
"nvidia");
476 f_find_and_bind(
"/usr/share",
"nvidia");
477 f_find_and_bind(
"/usr/share/vulkan/icd.d",
"nvidia");
478 f_find_and_bind(
"/usr/lib32",
"nvidia",
"cuda");
481 for(
auto&& entry : fs::directory_iterator(
"/dev")
482 | std::views::transform([](
auto&& e){
return e.path(); })
483 | std::views::filter([](
auto&& e){
return e.filename().string().contains(
"nvidia"); }))
499 using TypeBind = ns_db::ns_bind::Type;
501 for(
auto&& bind : binds.
get())
503 std::string src = bind.path_src;
504 std::string dst = bind.path_dst;
505 std::string type = (bind.type == TypeBind::DEV)?
"--dev-bind-try"
506 : (bind.type == TypeBind::RO)?
"--ro-bind-try"
508 m_args.push_back(type);
558 if ( m_is_root ) {
return *
this; }
560 std::string str_dir_home = ({
562 return_if(not ret, *
this,
"E::HOME environment variable is unset");
597 fs::path path_socket_pulse = m_path_dir_xdg_runtime /
"pulse/native";
602 fs::path path_socket_pipewire = m_path_dir_xdg_runtime /
"pipewire-0";
623 logger(
"D::PERM(WAYLAND)");
625 std::string env_wayland_display = ({
627 return_if(not ret, *
this,
"E::WAYLAND_DISPLAY is undefined");
632 fs::path path_socket_wayland = m_path_dir_xdg_runtime / env_wayland_display;
653 std::string env_display = ({
655 return_if(not ret, *
this,
"E::DISPLAY is undefined");
659 std::string env_xauthority = ({
661 return_if(not ret, *
this,
"E::XAUTHORITY is undefined");
680 logger(
"D::PERM(DBUS_USER)");
682 std::string env_dbus_session_bus_address = ({
684 return_if(not ret, *
this,
"E::DBUS_SESSION_BUS_ADDRESS is undefined");
689 std::string str_dbus_session_bus_path = env_dbus_session_bus_address;
693 if (
auto pos = str_dbus_session_bus_path.find(
'/'); pos != std::string::npos )
695 str_dbus_session_bus_path.erase(0, pos);
699 if (
auto pos = str_dbus_session_bus_path.find(
','); pos != std::string::npos )
701 str_dbus_session_bus_path.erase(pos);
705 ns_vector::push_back(m_args,
"--setenv",
"DBUS_SESSION_BUS_ADDRESS", env_dbus_session_bus_address);
706 ns_vector::push_back(m_args,
"--bind-try", str_dbus_session_bus_path, str_dbus_session_bus_path);
720 logger(
"D::PERM(DBUS_SYSTEM)");
721 ns_vector::push_back(m_args,
"--bind-try",
"/run/dbus/system_bus_socket",
"/run/dbus/system_bus_socket");
782 logger(
"D::PERM(NETWORK)");
815 logger(
"D::PERM(OPTICAL)");
816 auto f_bind = [
this](fs::path
const& path_device) ->
bool
819 if(not Try(fs::exists(path_device),
"E::Error to check if file exists: {}", path_device))
828 for(
int i : std::views::iota(0,256))
830 bool sr_exists = f_bind(std::format(
"/dev/sr{}", i));
831 bool sg_exists = f_bind(std::format(
"/dev/sg{}", i));
833 if (!sr_exists && !sg_exists)
break;
864 return symlink_nvidia(path_dir_root_guest, path_dir_root_host);
879 , Unshares
const& unshares
880 , fs::path
const& path_file_daemon
903 if(unshares.
contains(Unshare::USER))
905 logger(
"D::UNSHARE(USER)");
910 logger(
"D::UNSHARE(IPC)");
915 logger(
"D::UNSHARE(PID)");
920 logger(
"D::UNSHARE(NET)");
925 logger(
"D::UNSHARE(UTS)");
928 if(unshares.
contains(Unshare::CGROUP))
930 logger(
"D::UNSHARE(CGROUP)");
942 path_file_bwrap = Pop(test_and_setup(path_file_bwrap));
946 return_if(pipe(pipe_error) == -1, Error(
"E::Could not open bwrap error pipe: {}", strerror(errno)));
949 if(fcntl(pipe_error[0], F_SETFL, fcntl(pipe_error[0], F_GETFL, 0) | O_NONBLOCK) < 0)
951 return Error(
"E::Could not configure bwrap pipe to be non-blocking");
965 return_if(not Try(fs::exists(path_file_daemon)), Error(
"E::Missing portal daemon to run binary file path"));
970 overlay(m_overlay.value());
979 .
with_args(
"-c", std::format(R
"("{}" "$@")", path_file_bwrap.string()), "--")
980 .
with_args(
"--error-fd", std::to_string(pipe_error[1]))
982 .
with_args(path_file_bash,
"-c", std::format(R
"(&>/dev/null nohup "{}" & disown; "{}" "$@")", path_file_daemon.string(), m_path_file_program.string()), "--")
985 .
spawn()->wait().value_or(125);
992 log_if(read(pipe_error[0], &syscall_nr,
sizeof(syscall_nr)) < 0,
"D::Could not read syscall error, success?");
993 log_if(read(pipe_error[0], &errno_nr,
sizeof(errno_nr)) < 0,
"D::Could not read errno number, success?");
996 close(pipe_error[0]);
997 close(pipe_error[1]);
1000 return bwrap_run_ret_t{.code=code, .syscall_nr=syscall_nr, .errno_nr=errno_nr};
Manages bubblewrap (bwrap) containerization.
Bwrap & bind_usb()
Binds the usb devices from the host to the guest.
Bwrap & with_binds(ns_db::ns_bind::Binds const &binds)
Allows to specify custom bindings from a json file.
Bwrap & bind_xorg()
Binds the xorg socket from the host to the guest.
Bwrap & bind_dbus_user()
Binds the user session bus from the host to the guest.
Bwrap & bind_network()
Binds the network configuration from the host to the guest.
Bwrap(ns_proxy::Logs logs, ns_proxy::User user, fs::path const &path_dir_root, fs::path const &path_file_program, std::vector< std::string > const &program_args, std::vector< std::string > const &program_env)
Construct a new Bwrap object.
Bwrap & bind_home()
Includes a binding from the host $HOME to the guest.
Bwrap & bind_shm()
Binds the /dev/shm directory to the containter.
void set_overlay(ns_proxy::Overlay const &overlay)
Enable bwrap's overlay filesystem.
Bwrap & bind_input()
Binds the input devices from the host to the guest.
Bwrap & bind_audio()
Binds the host's audio sockets and devices to the guest.
Bwrap & bind_dev()
Binds the /dev directory to the containter.
Bwrap & bind_media()
Binds the host's media directories to the guest.
Bwrap & with_bind_gpu(fs::path const &path_dir_root_guest, fs::path const &path_dir_root_host)
Binds the gpu device from the host to the guest.
~Bwrap()
Destroy the Bwrap:: Bwrap object.
Bwrap & bind_optical()
Binds optical devices to the container.
Bwrap & bind_udev()
binds the udev folder from the host to the guest
Bwrap & with_bind(fs::path const &src, fs::path const &dst)
Includes a binding from the host to the guest.
Bwrap & bind_dbus_system()
Binds the syst from the host to the guest.
Bwrap & with_bind_ro(fs::path const &src, fs::path const &dst)
Includes a read-only binding from the host to the guest.
Bwrap & bind_wayland()
Binds the wayland socket from the host to the guest.
Value< bwrap_run_ret_t > run(Permissions const &permissions, Unshares const &unshares, fs::path const &path_file_daemon, ns_db::ns_portal::ns_dispatcher::Dispatcher const &arg1_dispatcher, ns_db::ns_portal::ns_daemon::Daemon const &arg1_daemon, ns_db::ns_portal::ns_daemon::ns_log::Logs const &arg2_daemon)
Runs the command in the bubblewrap sandbox.
Manages FlatImage permissions stored in reserved space.
bool contains(Permission const &permission) const noexcept
Checks if a specific permission is enabled.
Manages FlatImage unshare options stored in reserved space.
bool contains(Unshare const &unshare) const noexcept
Checks if a specific unshare option is enabled.
std::unique_ptr< Child > spawn()
Spawns (forks) the child process and begins execution.
Subprocess & with_env(Args &&... args)
Includes environment variables with the format 'NAME=VALUE' in the environment.
Subprocess & with_args(Args &&... args)
Arguments forwarded as the process' arguments.
Defines a class that manages FlatImage's portal configuration.
Used to manage the bindings from the host to the sandbox.
Defines a class that manages FlatImage's portal dispatcher configuration.
Enhanced error handling framework built on std::expected.
constexpr auto __expected_fn
Lambda helper for Pop macro error returns.
A library for manipulating environment variables.
A library for file logging.
#define logger(fmt,...)
Compile-time log level dispatch macro with automatic location capture.
Simplified macros for common control flow patterns with optional logging.
Bubblewrap proxy types and user configuration.
Bubblewrap sandboxing integration.
std::vector< fs::path > get_mounted_layers(fs::path const &path_dir_layers)
Get the mounted layers object.
Value< void > bwrap_clean(fs::path const &path_dir_work)
Cleans up the bwrap work directory.
Value< std::string > serialize(Logs const &logs) noexcept
Serializes a Logs class into a json string.
Value< std::string > serialize(Daemon const &daemon) noexcept
Serializes a Daemon class into a json string.
Value< std::string > serialize(Dispatcher const &dispatcher) noexcept
Serializes a Dispatcher class into a json string.
Value< std::string > expand(ns_concept::StringRepresentable auto &&var)
Performs variable expansion analogous to a POSIX shell.
Value< std::string > get_expected(std::string_view name)
Get the value of an environment variable.
Value< fs::path > search_path(std::string const &query)
Search the directories in the PATH variable for the given input file name.
void set(T &&name, U &&value, Replace replace)
Sets an environment variable.
Stream
Stream redirection modes for child process stdio.
void push_back(R &r, Args &&... args) noexcept
Helper to push back multiple elements into an input iterator.
void push_front(R &r, Args &&... args) noexcept
Helper to prepend multiple elements to the front of a container.
Manages the permissions reserved space.
Manages the unshare namespace options reserved space.
Enhanced expected type with integrated logging capabilities.
A pair of uid and gid mode_t values.
Bwrap's native overlay related options.
The representation of a user in bubblewrap.
Value< void > write_bashrc(fs::path const &path_file_bashrc, std::string_view ps1)
Writes the bashrc file and returns its path.
User(UserData const &data)
Construct a new User object with the provided user data.
Value< void > write_passwd(fs::path const &path_file_passwd)
Writes the passwd entry to the provided file path.
Container for multiple bind mount configurations.
std::vector< Bind > const & get() const
Retrieves the current list of bind mounts.
A library to spawn sub-processes in linux.