#pragma once #include #include #include #include #include #include #include #include "idmap.hpp" #include "image.hpp" #include "network.hpp" #include "util.hpp" namespace doos { // What the user asked for. Unset fields mean "default on create, no constraint on existing". struct Options { std::optional net; std::optional image; std::optional ids; std::optional size; std::optional memory; std::optional pids; std::optional milliCpus; // 0 = all cores std::optional relaxed; // --hardening relaxed }; class Container { public: Container(std::filesystem::path dir, std::string name) : dir_(std::move(dir)), name_(std::move(name)) {} static void validateName(const std::string& name); const std::string& name() const { return name_; } const std::filesystem::path& dir() const { return dir_; } std::filesystem::path rootfs() const { return dir_ / "rootfs"; } std::filesystem::path layer(const std::string& hostDir) const; // layers//{upper,work} std::filesystem::path privateDir(const std::string& hostDir) const; std::filesystem::path lockFile() const { return dir_ / "lock"; } std::filesystem::path pidFile() const { return dir_ / "pid"; } std::filesystem::path metaFile() const { return dir_ / "meta"; } // Where /etc lives on disk for this container (resolv.conf and tool config are written there). std::filesystem::path etcDir() const; NetMode net() const { return net_; } long created() const { return created_; } const std::string& image() const { return image_; } bool imageMode() const { return !image_.empty(); } IdMode ids() const { return ids_; } std::uint64_t sizeCap() const { return size_; } void setSizeCap(std::uint64_t bytes); std::uint64_t memoryCap() const { return memory_; } std::uint32_t pidsCap() const { return pids_; } std::uint32_t milliCpus() const { return milliCpus_; } bool relaxed() const { return relaxed_; } // Limits take effect the next time the container starts. void setLimits(std::optional memory, std::optional pids, std::optional milliCpus); void create(const NetMode& net, const std::string& image, IdMode ids, const Options& resolved); void readMeta(); // meta only, no side effects void load(); // readMeta + heal the layout // Process id (outside view) of the container's init, if it is alive. std::optional runningPid() const; void recordPid(pid_t pid) const; void clearPid() const; // Mountpoints, layer dirs, and one-time seeding (skeleton, /etc copy). Idempotent. void prepareLayout() const; // Bytes the container wrote (writable layers and private dirs), as the host user can see them. // Measured from inside the id map, so directories of other container ids are counted too. std::uint64_t usedBytes(std::size_t& skipped) const; private: void writeMeta() const; void seed(const std::string& marker, const std::string& label, const std::function& work) const; std::filesystem::path dir_; std::string name_; NetMode net_ = NetMode::Slirp; long created_ = 0; std::string image_; IdMode ids_ = IdMode::Single; std::uint64_t size_ = 0; std::uint64_t memory_ = 0; std::uint32_t pids_ = 0; std::uint32_t milliCpus_ = 0; bool relaxed_ = false; }; class Store { public: explicit Store(std::filesystem::path root); static Store fromEnvironment(); std::vector list() const; // Returns the container and whether it was created by this call. Pulls a catalog image on demand. std::pair getOrCreate(const std::string& name, const Options& opts); // Dry-run unless confirmed. Returns a human-readable report line. std::string remove(const std::string& name, bool confirmed); ImageStore images() const { return ImageStore(root_ / "images"); } std::vector containersUsing(const std::string& image) const; const std::filesystem::path& root() const { return root_; } private: std::filesystem::path containersDir() const { return root_ / "containers"; } std::filesystem::path root_; }; } // namespace doos