~siborgium/prelockdpp

5d92301f5cd87276fcf98e16e72cd161ca491943 — Sergey Smirnykh 1 year, 11 months ago f4f2867
Use TOML config
6 files changed, 226 insertions(+), 47 deletions(-)

M CMakeLists.txt
A config.cpp
A config.hpp
A errors.hpp
M main.cpp
A prelockd.toml
M CMakeLists.txt => CMakeLists.txt +4 -3
@@ 13,6 13,7 @@ FetchContent_MakeAvailable(plf_colony)
find_package(Boost 1.70 REQUIRED)
find_package(nlohmann_json 3.10.5 REQUIRED)
find_package(fmt REQUIRED)
find_package(tomlplusplus 3.1 REQUIRED)

find_package(PkgConfig)
if (DEFINED WITH_LIBCAP)


@@ 26,10 27,10 @@ else()
    set(WITH_LIBCAP "${cap_FOUND}")
endif()

add_executable(prelockdpp main.cpp)
add_executable(prelockdpp main.cpp config.cpp)
target_compile_features(prelockdpp PRIVATE cxx_std_20)
target_link_libraries(prelockdpp PRIVATE Boost::boost nlohmann_json::nlohmann_json fmt::fmt)
target_include_directories(prelockdpp PRIVATE "${plf_colony_SOURCE_DIR}")
target_link_libraries(prelockdpp PRIVATE Boost::boost nlohmann_json::nlohmann_json fmt::fmt tomlplusplus::tomlplusplus)
target_include_directories(prelockdpp PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}" "${plf_colony_SOURCE_DIR}")

if (WITH_LIBCAP)
    target_compile_definitions(prelockdpp PRIVATE WITH_LIBCAP)

A config.cpp => config.cpp +64 -0
@@ 0,0 1,64 @@

#include <config.hpp>
#include <errors.hpp>
#include <toml++/toml.h>
#include <fmt/core.h>
#include <sys/mman.h>

template <typename ... Ts>
struct overload: public Ts... {
    using Ts::operator()...;
};

template <typename ... Ts>
overload(Ts && ... ts) -> overload<Ts...>;

config parse_config_file(std::string_view path) {
    auto t = toml::parse_file(path);

    auto cgroup2_regex = t.at_path("critical.cgroup2_regex").value_or(std::string{});
    std::vector<std::string_view> comms;
    auto comms_view = t.at_path("critical.comms").as_array();
    if (!comms_view) {
        throw std::invalid_argument{ "Configuration file section critical.comms is not an array" };
    }
    size_t total_size = 0;
    for (auto&& value: *comms_view) {
        value.visit(overload{
            [&](toml::value<std::string>& comm){
                std::string_view view{ comm.get() };
                comms.push_back(view);
                total_size += view.size();
            },
            [](auto&&) {
                throw std::invalid_argument{ "critical.comms array contains entry of invalid type: expected string" };
            }
        });
    }

    auto pointers_count = comms.size() + 1; // to be able to do string_view{ ptrs[n], ptrs[n + 1] }
    auto map_ptrs_size = pointers_count * sizeof(char*);
    auto map_size = map_ptrs_size + total_size;
    std::unique_ptr<char[]> ptr{ new char[map_size] };

    auto begins = new (ptr.get()) const char* [pointers_count];

    auto cur_ptr = begins;
    auto cur_data = ptr.get() + map_ptrs_size;
    for (auto comm: comms) {
        *cur_ptr = cur_data;
        cur_data = std::copy(comm.begin(), comm.end(), cur_data);
        ++cur_ptr;
    }
    *cur_ptr = cur_data;

    return config {
        .interval_sec = t["poll_interval_sec"].value_or(300),
        .lock_only_critical = t["lock_only_critical"].value_or(true),
        .check_cgroup = !cgroup2_regex.empty(),
        .comms = {
            .begins = { begins, pointers_count },
            .ptr = std::move(ptr),
        }
    };
}

A config.hpp => config.hpp +94 -0
@@ 0,0 1,94 @@
#pragma once

#include <memory>
#include <span>
#include <string_view>
#include <sys/types.h>
#include <sys/mman.h>
#include <cassert>

// names (comms, actually, see comm in proc(5), /proc/[pid]/stat) of
// programs we should lock
struct critical_comms {
    std::span<const char*> begins;

    std::unique_ptr<char[]> ptr;

    auto size() const {
        assert(begins.size() > 0);
        return begins.size() - 1;
    }

    std::string_view operator [](size_t n) {
        return { begins[n], begins[n + 1] };
    }

    struct iterator {
        decltype(begins.data()) ptr;

        using Self = iterator;
        using pointer = decltype(ptr);
        using reference = std::string_view;
        using value_type = reference;
        using difference_type = decltype(ptr - ptr);
        using iterator_category = std::random_access_iterator_tag;

        Self& operator ++ () {
            ++ptr;
            return *this;
        }

        Self operator ++ (int) {
            Self copy{ ptr };
            ++(*this);
            return copy;
        }

        Self& operator -- () {
            --ptr;
            return *this;
        }

        Self& operator += (size_t n) {
            ptr += n;
            return *this;
        }

        difference_type operator - (const Self& other) const{
            return ptr - other.ptr;
        }

        bool operator != (const Self& other) const {
            return ptr != other.ptr;
        }

        bool operator == (const Self& other) const {
            return ptr == other.ptr;
        }

        reference operator * () const {
            return { *ptr, *(ptr + 1) };
        }
    };

    iterator begin() const {
        return { begins.data() };
    }
    iterator end() const {
        return { begins.data() + size() };
    }
};

struct config {
#if 0 // unused for now
    fs::path dump_path{ "/var/lib/prelockdpp/dump.json" };
#endif
    time_t   interval_sec{ 5 };
    bool     lock_only_critical{ true };
    bool     check_cgroup{ true };

    critical_comms comms;
};


config parse_config_file(std::string_view path);

A errors.hpp => errors.hpp +20 -0
@@ 0,0 1,20 @@
#pragma once

#include <stdexcept>
#include <cstring>

auto concat(auto&& ... xs) {
    std::string r; ((r += std::forward<decltype(xs)>(xs)), ...);
    return r;
};

struct errno_exception: public std::runtime_error {
    int err;

    errno_exception(std::string_view reason, int err):
        // FIXME: possible strerror invalidation
        std::runtime_error{ concat(reason, ": ", std::strerror(err)) }, err{ err }
    {
        // intentionally left blank
    }
};

M main.cpp => main.cpp +7 -44
@@ 1,4 1,7 @@

#include <config.hpp>
#include <errors.hpp>

#include <limits>
#include <charconv>
#include <string>


@@ 13,6 16,7 @@
#include <boost/intrusive/set_hook.hpp>
#include <plf_colony.h>
#include <fmt/core.h>
#include <fmt/ranges.h>

#include <dirent.h>
#include <fcntl.h>


@@ 58,22 62,6 @@ constexpr auto to_underlying(auto x) {
    return static_cast<std::underlying_type_t<decltype(x)>>(x);
}

auto concat(auto&& ... xs) {
    std::string r; ((r += std::forward<decltype(xs)>(xs)), ...);
    return r;
};

struct errno_exception: public std::runtime_error {
    int err;

    errno_exception(std::string_view reason, int err):
        // FIXME: possible strerror invalidation
        std::runtime_error{ concat(reason, ": ", std::strerror(err)) }, err{ err }
    {
        // intentionally left blank
    }
};

constexpr auto tspec_diff(const timespec& t0, const timespec& t1) {
    timespec t {
        .tv_sec = t1.tv_sec - t0.tv_sec,


@@ 86,15 74,6 @@ constexpr auto tspec_diff(const timespec& t0, const timespec& t1) {
    return t;
}

struct config {
#if 0 // unused for now
    fs::path dump_path{ "/var/lib/prelockdpp/dump.json" };
#endif
    time_t   interval_sec{ 5 };
    bool     lock_only_critical{ true };
    bool     check_cgroup{ true };
};

struct sc_info {
    int sc_page_size;
    int sc_clk_tck;


@@ 365,10 344,6 @@ struct context {
    linebuf line_buffer;
    linebuf stat_buffer;

    // names (comms, actually, see comm in proc(5), /proc/[pid]/stat) of
    // programs we should lock
    std::unordered_set<std::string> name_set;

    // storage for ids
    plf::colony<uniq_id> ids;
    uniq_alive_set       current_uniqs_set;


@@ 483,9 458,7 @@ struct context {
                // FIXME: fsck unordered_set
                //        imagine not being able to lookup strings by string_view key
                //        in 2022
                bool lock_ok = std::find_if(name_set.begin(), name_set.end(), [=](auto&& c){
                    return comm == c;
                }) != name_set.end();
                bool lock_ok = std::ranges::find(cfg.comms, comm, {}) != cfg.comms.end();

                if (!lock_ok && cfg.check_cgroup) {
                    fmt_proc_pid_file(cgroup_path);


@@ 645,10 618,6 @@ struct context {
        return current_uniqs_set;
    }

    void add_critical_name(std::string_view name) {
        name_set.emplace(name.data(), name.size());
    }

    context(config& cfg):
        cfg{ cfg },
        pid_begin{ std::copy(proc_path.begin(), proc_path.end(), path_buf) }


@@ 791,7 760,6 @@ int main(int argc, char **argv) {
        }
#endif

#if 0
        auto arg = [begin = argv + 1, end = argv + argc](std::string_view name) {
            auto iter = std::find_if(begin, end, [=](auto* a){ return name == a; });
            if (iter == end) {


@@ 808,16 776,11 @@ int main(int argc, char **argv) {
        if (conf_path.empty()) {
            conf_path = "/usr/share/prelockdpp/prelockd.conf";
        }
#endif

        config  cfg;
        auto cfg = parse_config_file(conf_path);
        context ctx{ cfg };

        for (int i = 1; i < argc; ++i) {
            ctx.add_critical_name(argv[i]);
        }

        fmt::print("Name set: {}\n", ctx.name_set);
        fmt::print("Comms: {}\n", cfg.comms);

        ctx.mlockall();


A prelockd.toml => prelockd.toml +37 -0
@@ 0,0 1,37 @@

poll_interval_sec = 5
lock_inly_critical = true

[critical]
cgroup2_regex = '''
^/user\.slice/user-\d+\.slice/user@\d+\.service/session\.slice/.+'''
comms = [
# basic
  "sh", "bash", "fish", "zsh", "sshd", "agetty", "getty", "login",
# systemd
  "systemd", "systemd-logind", "dbus-daemon", "dbus-broker",
# X11, audio
  "X", "Xorg", "Xwayland", "pulseaudio", "pipewire",
# GNOME
  "gnome-shell", "gnome-session-b", "gnome-screensav", "gnome-panel", "gnome-flashback", "gnome-screensav", "metacity",
# KDE
  "plasmashell", "plasma-desktop", "kwin_wayland", "kwin_x11", "kwin", "kded4", "knotify4", "kded5", "kdeinit5",
# Cinnamon
  "cinnamon", "cinnamon-sessio", "cinnamon-screen",
# Mate
  "mate-session", "marco", "mate-settings-d", "mate-screensave", "mate-panel",
# LXQt
  "lxqt-panel", "lxqt-notificati", "lxqt-session",
# XFCE
  "xfwm4", "xfdesktop", "xfce4-session", "xfsettingsd", "xfconfd", "xfce4-notifyd", "xfce4-screensav", "xfce4-panel",
# LXDE
  "lxsession", "xscreensaver", "light-locker", "lxpanel",
# Gala
  "gala", "gala-daemon", "notification-da",
# Budgie
  "budgie-wm", "budgie-daemon", "budgie-panel",
# Standalone window managers
  "i3", "icewm", "icewm-session", "openbox", "fluxbox", "awesome", "bspwm", "sway", "wayfire", "compiz",
# gnome-terminal-, konsole, xfce4-terminal, mate-terminal, lxterminal, qterminal,
# nautilus, nautilus-deskto, dolphin, pcmanfm-qt, pcmanfm, caja, nemo-desktop, nemo, Thunar
]