/* * Copyright (c) 2020-2021. Uniontech Software Ltd. All rights reserved. * * Author: Iceyer * * Maintainer: Iceyer * * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef LINGLONG_BOX_SRC_UTIL_OCI_RUNTIME_H_ #define LINGLONG_BOX_SRC_UTIL_OCI_RUNTIME_H_ #include #include "util.h" namespace linglong { #define LLJS_FROM(KEY) (o.KEY = j.at(#KEY).get()) #define LLJS_FROM_OPT(KEY) (o.KEY = optional(j, #KEY)) #define LLJS_TO(KEY) (j[#KEY] = o.KEY) #define LLJS_FROM_OBJ(TYPE) inline void from_json(const nlohmann::json &j, TYPE &o) #define LLJS_TO_OBJ(TYPE) inline void to_json(nlohmann::json &j, const TYPE &o) #undef linux struct Root { std::string path; tl::optional readonly; }; LLJS_FROM_OBJ(Root) { LLJS_FROM(path); LLJS_FROM_OPT(readonly); } LLJS_TO_OBJ(Root) { LLJS_TO(path); LLJS_TO(readonly); } struct Process { util::str_vec args; util::str_vec env; std::string cwd; }; inline void from_json(const nlohmann::json &j, Process &o) { o.args = j.at("args").get(); o.env = j.at("env").get(); o.cwd = j.at("cwd").get(); } inline void to_json(nlohmann::json &j, const Process &o) { j["args"] = o.args; j["env"] = o.env; j["cwd"] = o.cwd; } struct Mount { enum Type { Unknown, Bind, Proc, Sysfs, Devpts, Mqueue, Tmpfs, Cgroup, Cgroup2, }; std::string destination; std::string type; std::string source; util::str_vec data; Type fsType; uint32_t flags = 0u; }; inline void from_json(const nlohmann::json &j, Mount &o) { static std::map fsTypes = { {"bind", Mount::Bind}, {"proc", Mount::Proc}, {"devpts", Mount::Devpts}, {"mqueue", Mount::Mqueue}, {"tmpfs", Mount::Tmpfs}, {"sysfs", Mount::Sysfs}, {"cgroup", Mount::Cgroup}, {"cgroup2", Mount::Cgroup2}, }; struct mountFlag { bool clear; uint32_t flag; }; static std::map optionFlags = { {"acl", {false, MS_POSIXACL}}, {"async", {true, MS_SYNCHRONOUS}}, {"atime", {true, MS_NOATIME}}, {"bind", {false, MS_BIND}}, {"defaults", {false, 0}}, {"dev", {true, MS_NODEV}}, {"diratime", {true, MS_NODIRATIME}}, {"dirsync", {false, MS_DIRSYNC}}, {"exec", {true, MS_NOEXEC}}, {"iversion", {false, MS_I_VERSION}}, {"lazytime", {false, MS_LAZYTIME}}, {"loud", {true, MS_SILENT}}, {"mand", {false, MS_MANDLOCK}}, {"noacl", {true, MS_POSIXACL}}, {"noatime", {false, MS_NOATIME}}, {"nodev", {false, MS_NODEV}}, {"nodiratime", {false, MS_NODIRATIME}}, {"noexec", {false, MS_NOEXEC}}, {"noiversion", {true, MS_I_VERSION}}, {"nolazytime", {true, MS_LAZYTIME}}, {"nomand", {true, MS_MANDLOCK}}, {"norelatime", {true, MS_RELATIME}}, {"nostrictatime", {true, MS_STRICTATIME}}, {"nosuid", {false, MS_NOSUID}}, // {"nosymfollow",{false, MS_NOSYMFOLLOW}}, // since kernel 5.10 {"rbind", {false, MS_BIND | MS_REC}}, {"relatime", {false, MS_RELATIME}}, {"remount", {false, MS_REMOUNT}}, {"ro", {false, MS_RDONLY}}, {"rw", {true, MS_RDONLY}}, {"silent", {false, MS_SILENT}}, {"strictatime", {false, MS_STRICTATIME}}, {"suid", {true, MS_NOSUID}}, {"sync", {false, MS_SYNCHRONOUS}}, // {"symfollow",{true, MS_NOSYMFOLLOW}}, // since kernel 5.10 }; o.destination = j.at("destination").get(); o.type = j.at("type").get(); o.fsType = fsTypes.find(o.type)->second; if (o.fsType == Mount::Bind) { o.flags = MS_BIND; } o.source = j.at("source").get(); o.data = {}; // Parse options to data and flags. // FIXME: support "propagation flags" and "recursive mount attrs" // https://github.com/opencontainers/runc/blob/c83abc503de7e8b3017276e92e7510064eee02a8/libcontainer/specconv/spec_linux.go#L958 auto options = j.value("options", util::str_vec()); for (auto const &opt : options) { auto it = optionFlags.find(opt); if (it != optionFlags.end()) { if (it->second.clear) { o.flags &= ~it->second.flag; } else o.flags |= it->second.flag; } else { o.data.push_back(opt); } } } inline void to_json(nlohmann::json &j, const Mount &o) { j["destination"] = o.destination; j["source"] = o.source; j["type"] = o.type; j["options"] = o.data; // FIXME: this data is not original options, some of them have been prased to flags. } struct Namespace { int type; }; static std::map namespaceType = { {"pid", CLONE_NEWPID}, {"uts", CLONE_NEWUTS}, {"mount", CLONE_NEWNS}, {"cgroup", CLONE_NEWCGROUP}, {"network", CLONE_NEWNET}, {"ipc", CLONE_NEWIPC}, {"user", CLONE_NEWUSER}, }; inline void from_json(const nlohmann::json &j, Namespace &o) { o.type = namespaceType.find(j.at("type").get())->second; } inline void to_json(nlohmann::json &j, const Namespace &o) { auto matchPair = std::find_if(std::begin(namespaceType), std::end(namespaceType), [&](const std::pair &pair) { return pair.second == o.type; }); j["type"] = matchPair->first; } struct IDMap { uint64_t containerID = 0u; uint64_t hostID = 0u; uint64_t size = 0u; }; inline void from_json(const nlohmann::json &j, IDMap &o) { o.hostID = j.value("hostID", 0); o.containerID = j.value("containerID", 0); o.size = j.value("size", 0); } inline void to_json(nlohmann::json &j, const IDMap &o) { j["hostID"] = o.hostID; j["containerID"] = o.containerID; j["size"] = o.size; } typedef std::string SeccompAction; typedef std::string SeccompArch; struct SyscallArg { u_int index; // require u_int64_t value; // require u_int64_t valueTwo; // optional std::string op; // require }; inline void from_json(const nlohmann::json &j, SyscallArg &o) { o.index = j.at("index").get(); o.value = j.at("value").get(); o.valueTwo = j.value("valueTwo", u_int64_t()); o.op = j.at("op").get(); } inline void to_json(nlohmann::json &j, const SyscallArg &o) { j["index"] = o.index; j["value"] = o.value; j["valueTwo"] = o.valueTwo; j["op"] = o.op; } struct Syscall { util::str_vec names; SeccompAction action; std::vector args; }; inline void from_json(const nlohmann::json &j, Syscall &o) { o.names = j.at("names").get(); o.action = j.at("action").get(); o.args = j.value("args", std::vector()); } inline void to_json(nlohmann::json &j, const Syscall &o) { j["names"] = o.names; j["action"] = o.action; j["args"] = o.args; } struct Seccomp { SeccompAction defaultAction = "INVALID_ACTION"; std::vector architectures; std::vector syscalls; }; inline void from_json(const nlohmann::json &j, Seccomp &o) { o.defaultAction = j.at("defaultAction").get(); o.architectures = j.value("architectures", std::vector {}); o.syscalls = j.value("syscalls", std::vector {}); } inline void to_json(nlohmann::json &j, const Seccomp &o) { j["defaultAction"] = o.defaultAction; j["architectures"] = o.architectures; j["syscalls"] = o.syscalls; } // https://github.com/containers/crun/blob/main/crun.1.md#memory-controller struct ResourceMemory { int64_t limit = -1; int64_t reservation = -1; int64_t swap = -1; }; inline void from_json(const nlohmann::json &j, ResourceMemory &o) { o.limit = j.value("limit", -1); o.reservation = j.value("reservation", -1); o.swap = j.value("swap", -1); } inline void to_json(nlohmann::json &j, const ResourceMemory &o) { j["limit"] = o.limit; j["reservation"] = o.reservation; j["swap"] = o.swap; } // https://github.com/containers/crun/blob/main/crun.1.md#cpu-controller // support v1 and v2 with conversion struct ResourceCPU { u_int64_t shares = 1024; int64_t quota = 100000; u_int64_t period = 100000; // int64_t realtimeRuntime; // int64_t realtimePeriod; // std::string cpus; // std::string mems; }; inline void from_json(const nlohmann::json &j, ResourceCPU &o) { o.shares = j.value("shares", 1024); o.quota = j.value("quota", 100000); o.period = j.value("period", 100000); } inline void to_json(nlohmann::json &j, const ResourceCPU &o) { j["shares"] = o.shares; j["quota"] = o.quota; j["period"] = o.period; } struct Resources { ResourceMemory memory; ResourceCPU cpu; }; inline void from_json(const nlohmann::json &j, Resources &o) { o.cpu = j.value("cpu", ResourceCPU()); o.memory = j.value("memory", ResourceMemory()); } inline void to_json(nlohmann::json &j, const Resources &o) { j["cpu"] = o.cpu; j["memory"] = o.memory; } struct Linux { std::vector namespaces; std::vector uidMappings; std::vector gidMappings; tl::optional seccomp; std::string cgroupsPath; Resources resources; }; inline void from_json(const nlohmann::json &j, Linux &o) { o.namespaces = j.at("namespaces").get>(); o.uidMappings = j.value("uidMappings", std::vector {}); o.gidMappings = j.value("gidMappings", std::vector {}); o.seccomp = optional(j, "seccomp"); o.cgroupsPath = j.value("cgroupsPath", ""); o.resources = j.value("resources", Resources()); } inline void to_json(nlohmann::json &j, const Linux &o) { j["namespaces"] = o.namespaces; j["uidMappings"] = o.uidMappings; j["gidMappings"] = o.gidMappings; j["seccomp"] = o.seccomp; j["cgroupsPath"] = o.cgroupsPath; j["resources"] = o.resources; } /* "hooks": { "prestart": [ { "path": "/usr/bin/fix-mounts", "args": ["fix-mounts", "arg1", "arg2"], "env": [ "key1=value1"] }, { "path": "/usr/bin/setup-network" } ], "poststart": [ { "path": "/usr/bin/notify-start", "timeout": 5 } ], "poststop": [ { "path": "/usr/sbin/cleanup.sh", "args": ["cleanup.sh", "-f"] } ] } */ struct Hook { std::string path; tl::optional args; tl::optional> env; }; inline void from_json(const nlohmann::json &j, Hook &o) { LLJS_FROM(path); LLJS_FROM_OPT(args); LLJS_FROM_OPT(env); } inline void to_json(nlohmann::json &j, const Hook &o) { j["path"] = o.path; j["args"] = o.args; j["env"] = o.env; } struct Hooks { tl::optional> prestart; tl::optional> poststart; tl::optional> poststop; }; inline void from_json(const nlohmann::json &j, Hooks &o) { LLJS_FROM_OPT(prestart); LLJS_FROM_OPT(poststart); LLJS_FROM_OPT(poststop); } inline void to_json(nlohmann::json &j, const Hooks &o) { j["poststop"] = o.poststop; j["poststart"] = o.poststart; j["prestart"] = o.prestart; } struct AnnotationsOverlayfs { std::string lower_parent; std::string upper; std::string workdir; std::vector mounts; }; LLJS_FROM_OBJ(AnnotationsOverlayfs) { LLJS_FROM(lower_parent); LLJS_FROM(upper); LLJS_FROM(workdir); LLJS_FROM(mounts); } LLJS_TO_OBJ(AnnotationsOverlayfs) { LLJS_TO(lower_parent); LLJS_TO(upper); LLJS_TO(workdir); LLJS_TO(mounts); } struct AnnotationsNativeRootfs { std::vector mounts; }; LLJS_FROM_OBJ(AnnotationsNativeRootfs) { LLJS_FROM(mounts); } LLJS_TO_OBJ(AnnotationsNativeRootfs) { LLJS_TO(mounts); } struct Annotations { std::string container_root_path; tl::optional overlayfs; tl::optional native; }; LLJS_FROM_OBJ(Annotations) { LLJS_FROM(container_root_path); LLJS_FROM_OPT(overlayfs); LLJS_FROM_OPT(native); } LLJS_TO_OBJ(Annotations) { LLJS_TO(container_root_path); LLJS_TO(overlayfs); LLJS_TO(native); } struct Runtime { std::string version; Root root; Process process; std::string hostname; Linux linux; tl::optional> mounts; tl::optional hooks; tl::optional annotations; }; inline void from_json(const nlohmann::json &j, Runtime &o) { o.version = j.at("ociVersion").get(); o.hostname = j.at("hostname").get(); LLJS_FROM(process); o.mounts = optional(j, "mounts"); LLJS_FROM(linux); // maybe optional LLJS_FROM(root); o.hooks = optional(j, "hooks"); LLJS_FROM_OPT(annotations); } inline void to_json(nlohmann::json &j, const Runtime &o) { j["ociVersion"] = o.version; j["hostname"] = o.hostname; j["process"] = o.process; j["mounts"] = o.mounts; j["linux"] = o.linux; j["root"] = o.root; j["hooks"] = o.hooks; LLJS_TO(annotations); } inline static Runtime fromFile(const std::string &filepath) { return util::json::fromFile(filepath).get(); } inline static Runtime fromString(const std::string &content) { return util::json::fromByteArray(content).get(); } } // namespace linglong #endif /* LINGLONG_BOX_SRC_UTIL_OCI_RUNTIME_H_ */