aboutsummaryrefslogtreecommitdiffstats
path: root/library
diff options
context:
space:
mode:
authorrobot-piglet <robot-piglet@yandex-team.com>2023-11-12 21:25:31 +0300
committerrobot-piglet <robot-piglet@yandex-team.com>2023-11-12 21:39:54 +0300
commitd28c55ab25cc8cedab8a5f4736c0d66e88b3da95 (patch)
tree73d373709b74fa2baaa4fe02a40a77c0a5baf6b7 /library
parent35b17f4f3b6e0ed855e7e47d3f1eb57470388a2c (diff)
downloadydb-d28c55ab25cc8cedab8a5f4736c0d66e88b3da95.tar.gz
Intermediate changes
Diffstat (limited to 'library')
-rw-r--r--library/cpp/CMakeLists.darwin-x86_64.txt1
-rw-r--r--library/cpp/CMakeLists.linux-aarch64.txt1
-rw-r--r--library/cpp/CMakeLists.linux-x86_64.txt1
-rw-r--r--library/cpp/CMakeLists.windows-x86_64.txt1
-rw-r--r--library/cpp/porto/CMakeLists.darwin-x86_64.txt9
-rw-r--r--library/cpp/porto/CMakeLists.linux-aarch64.txt22
-rw-r--r--library/cpp/porto/CMakeLists.linux-x86_64.txt22
-rw-r--r--library/cpp/porto/CMakeLists.txt17
-rw-r--r--library/cpp/porto/CMakeLists.windows-x86_64.txt9
-rw-r--r--library/cpp/porto/libporto.cpp1547
-rw-r--r--library/cpp/porto/libporto.hpp492
-rw-r--r--library/cpp/porto/libporto_ut.cpp226
-rw-r--r--library/cpp/porto/metrics.cpp183
-rw-r--r--library/cpp/porto/metrics.hpp50
-rw-r--r--library/cpp/porto/proto/CMakeLists.darwin-x86_64.txt43
-rw-r--r--library/cpp/porto/proto/CMakeLists.linux-aarch64.txt44
-rw-r--r--library/cpp/porto/proto/CMakeLists.linux-x86_64.txt44
-rw-r--r--library/cpp/porto/proto/CMakeLists.txt17
-rw-r--r--library/cpp/porto/proto/CMakeLists.windows-x86_64.txt43
-rw-r--r--library/cpp/porto/proto/rpc.proto1607
-rw-r--r--library/cpp/porto/proto/ya.make5
-rw-r--r--library/cpp/porto/ut/ya.make4
-rw-r--r--library/cpp/porto/ya.make17
-rw-r--r--library/cpp/yt/CMakeLists.txt2
-rw-r--r--library/cpp/yt/backtrace/cursors/CMakeLists.darwin-x86_64.txt2
-rw-r--r--library/cpp/yt/backtrace/cursors/CMakeLists.linux-aarch64.txt2
-rw-r--r--library/cpp/yt/backtrace/cursors/CMakeLists.linux-x86_64.txt2
-rw-r--r--library/cpp/yt/backtrace/cursors/CMakeLists.windows-x86_64.txt3
-rw-r--r--library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.darwin-x86_64.txt20
-rw-r--r--library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.linux-aarch64.txt21
-rw-r--r--library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.linux-x86_64.txt21
-rw-r--r--library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.txt17
-rw-r--r--library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.windows-x86_64.txt17
-rw-r--r--library/cpp/yt/backtrace/cursors/interop/CMakeLists.darwin-x86_64.txt22
-rw-r--r--library/cpp/yt/backtrace/cursors/interop/CMakeLists.linux-aarch64.txt23
-rw-r--r--library/cpp/yt/backtrace/cursors/interop/CMakeLists.linux-x86_64.txt23
-rw-r--r--library/cpp/yt/backtrace/cursors/interop/CMakeLists.txt17
-rw-r--r--library/cpp/yt/backtrace/cursors/interop/CMakeLists.windows-x86_64.txt19
-rw-r--r--library/cpp/yt/backtrace/cursors/libunwind/CMakeLists.txt2
-rw-r--r--library/cpp/yt/backtrace/cursors/libunwind/CMakeLists.windows-x86_64.txt18
-rw-r--r--library/cpp/yt/mlock/CMakeLists.darwin-x86_64.txt20
-rw-r--r--library/cpp/yt/mlock/CMakeLists.linux-aarch64.txt21
-rw-r--r--library/cpp/yt/mlock/CMakeLists.linux-x86_64.txt21
-rw-r--r--library/cpp/yt/mlock/CMakeLists.txt17
-rw-r--r--library/cpp/yt/mlock/CMakeLists.windows-x86_64.txt17
-rw-r--r--library/cpp/yt/mlock/README.md11
-rw-r--r--library/cpp/yt/mlock/mlock.h11
-rw-r--r--library/cpp/yt/mlock/mlock_linux.cpp89
-rw-r--r--library/cpp/yt/mlock/mlock_other.cpp14
-rw-r--r--library/cpp/yt/mlock/unittests/mlock_ut.cpp19
-rw-r--r--library/cpp/yt/mlock/unittests/ya.make13
-rw-r--r--library/cpp/yt/mlock/ya.make16
-rw-r--r--library/cpp/yt/stockpile/CMakeLists.darwin-x86_64.txt20
-rw-r--r--library/cpp/yt/stockpile/CMakeLists.linux-aarch64.txt21
-rw-r--r--library/cpp/yt/stockpile/CMakeLists.linux-x86_64.txt21
-rw-r--r--library/cpp/yt/stockpile/CMakeLists.txt17
-rw-r--r--library/cpp/yt/stockpile/CMakeLists.windows-x86_64.txt17
-rw-r--r--library/cpp/yt/stockpile/README.md12
-rw-r--r--library/cpp/yt/stockpile/stockpile.h29
-rw-r--r--library/cpp/yt/stockpile/stockpile_linux.cpp42
-rw-r--r--library/cpp/yt/stockpile/stockpile_other.cpp12
-rw-r--r--library/cpp/yt/stockpile/ya.make11
62 files changed, 5107 insertions, 0 deletions
diff --git a/library/cpp/CMakeLists.darwin-x86_64.txt b/library/cpp/CMakeLists.darwin-x86_64.txt
index 0f393b2039..22b108ee05 100644
--- a/library/cpp/CMakeLists.darwin-x86_64.txt
+++ b/library/cpp/CMakeLists.darwin-x86_64.txt
@@ -63,6 +63,7 @@ add_subdirectory(openssl)
add_subdirectory(packedtypes)
add_subdirectory(packers)
add_subdirectory(pop_count)
+add_subdirectory(porto)
add_subdirectory(presort)
add_subdirectory(protobuf)
add_subdirectory(random_provider)
diff --git a/library/cpp/CMakeLists.linux-aarch64.txt b/library/cpp/CMakeLists.linux-aarch64.txt
index cf47314f07..b42033fd82 100644
--- a/library/cpp/CMakeLists.linux-aarch64.txt
+++ b/library/cpp/CMakeLists.linux-aarch64.txt
@@ -62,6 +62,7 @@ add_subdirectory(openssl)
add_subdirectory(packedtypes)
add_subdirectory(packers)
add_subdirectory(pop_count)
+add_subdirectory(porto)
add_subdirectory(presort)
add_subdirectory(protobuf)
add_subdirectory(random_provider)
diff --git a/library/cpp/CMakeLists.linux-x86_64.txt b/library/cpp/CMakeLists.linux-x86_64.txt
index 0f393b2039..22b108ee05 100644
--- a/library/cpp/CMakeLists.linux-x86_64.txt
+++ b/library/cpp/CMakeLists.linux-x86_64.txt
@@ -63,6 +63,7 @@ add_subdirectory(openssl)
add_subdirectory(packedtypes)
add_subdirectory(packers)
add_subdirectory(pop_count)
+add_subdirectory(porto)
add_subdirectory(presort)
add_subdirectory(protobuf)
add_subdirectory(random_provider)
diff --git a/library/cpp/CMakeLists.windows-x86_64.txt b/library/cpp/CMakeLists.windows-x86_64.txt
index 772027a342..8925d1f2bf 100644
--- a/library/cpp/CMakeLists.windows-x86_64.txt
+++ b/library/cpp/CMakeLists.windows-x86_64.txt
@@ -62,6 +62,7 @@ add_subdirectory(openssl)
add_subdirectory(packedtypes)
add_subdirectory(packers)
add_subdirectory(pop_count)
+add_subdirectory(porto)
add_subdirectory(presort)
add_subdirectory(protobuf)
add_subdirectory(random_provider)
diff --git a/library/cpp/porto/CMakeLists.darwin-x86_64.txt b/library/cpp/porto/CMakeLists.darwin-x86_64.txt
new file mode 100644
index 0000000000..499930c4b0
--- /dev/null
+++ b/library/cpp/porto/CMakeLists.darwin-x86_64.txt
@@ -0,0 +1,9 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+add_subdirectory(proto)
diff --git a/library/cpp/porto/CMakeLists.linux-aarch64.txt b/library/cpp/porto/CMakeLists.linux-aarch64.txt
new file mode 100644
index 0000000000..f61df9eb93
--- /dev/null
+++ b/library/cpp/porto/CMakeLists.linux-aarch64.txt
@@ -0,0 +1,22 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+add_subdirectory(proto)
+
+add_library(library-cpp-porto)
+target_link_libraries(library-cpp-porto PUBLIC
+ contrib-libs-linux-headers
+ contrib-libs-cxxsupp
+ yutil
+ cpp-porto-proto
+ contrib-libs-protobuf
+)
+target_sources(library-cpp-porto PRIVATE
+ ${CMAKE_SOURCE_DIR}/library/cpp/porto/libporto.cpp
+ ${CMAKE_SOURCE_DIR}/library/cpp/porto/metrics.cpp
+)
diff --git a/library/cpp/porto/CMakeLists.linux-x86_64.txt b/library/cpp/porto/CMakeLists.linux-x86_64.txt
new file mode 100644
index 0000000000..f61df9eb93
--- /dev/null
+++ b/library/cpp/porto/CMakeLists.linux-x86_64.txt
@@ -0,0 +1,22 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+add_subdirectory(proto)
+
+add_library(library-cpp-porto)
+target_link_libraries(library-cpp-porto PUBLIC
+ contrib-libs-linux-headers
+ contrib-libs-cxxsupp
+ yutil
+ cpp-porto-proto
+ contrib-libs-protobuf
+)
+target_sources(library-cpp-porto PRIVATE
+ ${CMAKE_SOURCE_DIR}/library/cpp/porto/libporto.cpp
+ ${CMAKE_SOURCE_DIR}/library/cpp/porto/metrics.cpp
+)
diff --git a/library/cpp/porto/CMakeLists.txt b/library/cpp/porto/CMakeLists.txt
new file mode 100644
index 0000000000..f8b31df0c1
--- /dev/null
+++ b/library/cpp/porto/CMakeLists.txt
@@ -0,0 +1,17 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" AND NOT HAVE_CUDA)
+ include(CMakeLists.linux-aarch64.txt)
+elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
+ include(CMakeLists.darwin-x86_64.txt)
+elseif (WIN32 AND CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" AND NOT HAVE_CUDA)
+ include(CMakeLists.windows-x86_64.txt)
+elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND NOT HAVE_CUDA)
+ include(CMakeLists.linux-x86_64.txt)
+endif()
diff --git a/library/cpp/porto/CMakeLists.windows-x86_64.txt b/library/cpp/porto/CMakeLists.windows-x86_64.txt
new file mode 100644
index 0000000000..499930c4b0
--- /dev/null
+++ b/library/cpp/porto/CMakeLists.windows-x86_64.txt
@@ -0,0 +1,9 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+add_subdirectory(proto)
diff --git a/library/cpp/porto/libporto.cpp b/library/cpp/porto/libporto.cpp
new file mode 100644
index 0000000000..8fd8924300
--- /dev/null
+++ b/library/cpp/porto/libporto.cpp
@@ -0,0 +1,1547 @@
+#include "libporto.hpp"
+#include "metrics.hpp"
+
+#include <google/protobuf/text_format.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/io/coded_stream.h>
+
+extern "C" {
+#include <errno.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#ifndef __linux__
+#include <fcntl.h>
+#else
+#include <sys/epoll.h>
+#endif
+}
+
+namespace Porto {
+
+TPortoApi::~TPortoApi() {
+ Disconnect();
+}
+
+EError TPortoApi::SetError(const TString &prefix, int _errno) {
+ LastErrorMsg = prefix + ": " + strerror(_errno);
+
+ switch (_errno) {
+ case ENOENT:
+ LastError = EError::SocketUnavailable;
+ break;
+ case EAGAIN:
+ LastErrorMsg = prefix + ": Timeout exceeded. Timeout value: " + std::to_string(Timeout);
+ LastError = EError::SocketTimeout;
+ break;
+ case EIO:
+ case EPIPE:
+ LastError = EError::SocketError;
+ break;
+ default:
+ LastError = EError::Unknown;
+ break;
+ }
+
+ Disconnect();
+ return LastError;
+}
+
+TString TPortoApi::GetLastError() const {
+ return EError_Name(LastError) + ":(" + LastErrorMsg + ")";
+}
+
+EError TPortoApi::Connect(const char *socket_path) {
+ struct sockaddr_un peer_addr;
+ socklen_t peer_addr_size;
+
+ Disconnect();
+
+#ifdef __linux__
+ Fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ if (Fd < 0)
+ return SetError("socket", errno);
+#else
+ Fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (Fd < 0)
+ return SetError("socket", errno);
+ if (fcntl(Fd, F_SETFD, FD_CLOEXEC) < 0)
+ return SetError("fcntl FD_CLOEXEC", errno);
+#endif
+
+ if (Timeout > 0 && SetSocketTimeout(3, Timeout))
+ return LastError;
+
+ memset(&peer_addr, 0, sizeof(struct sockaddr_un));
+ peer_addr.sun_family = AF_UNIX;
+ strncpy(peer_addr.sun_path, socket_path, strlen(socket_path));
+
+ peer_addr_size = sizeof(struct sockaddr_un);
+ if (connect(Fd, (struct sockaddr *) &peer_addr, peer_addr_size) < 0)
+ return SetError("connect", errno);
+
+ /* Restore async wait state */
+ if (!AsyncWaitNames.empty()) {
+ for (auto &name: AsyncWaitNames)
+ Req.mutable_asyncwait()->add_name(name);
+ for (auto &label: AsyncWaitLabels)
+ Req.mutable_asyncwait()->add_label(label);
+ if (AsyncWaitTimeout >= 0)
+ Req.mutable_asyncwait()->set_timeout_ms(AsyncWaitTimeout * 1000);
+ return Call();
+ }
+
+ return EError::Success;
+}
+
+void TPortoApi::Disconnect() {
+ if (Fd >= 0)
+ close(Fd);
+ Fd = -1;
+}
+
+EError TPortoApi::SetSocketTimeout(int direction, int timeout) {
+ struct timeval tv;
+
+ if (Fd < 0)
+ return EError::Success;
+
+ tv.tv_sec = timeout > 0 ? timeout : 0;
+ tv.tv_usec = 0;
+
+ if ((direction & 1) && setsockopt(Fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv))
+ return SetError("setsockopt SO_SNDTIMEO", errno);
+
+ if ((direction & 2) && setsockopt(Fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv))
+ return SetError("setsockopt SO_RCVTIMEO", errno);
+
+ return EError::Success;
+}
+
+EError TPortoApi::SetTimeout(int timeout) {
+ Timeout = timeout ? timeout : DEFAULT_TIMEOUT;
+ return SetSocketTimeout(3, Timeout);
+}
+
+EError TPortoApi::SetDiskTimeout(int timeout) {
+ DiskTimeout = timeout ? timeout : DEFAULT_DISK_TIMEOUT;
+ return EError::Success;
+}
+
+EError TPortoApi::Send(const TPortoRequest &req) {
+ google::protobuf::io::FileOutputStream raw(Fd);
+
+ if (!req.IsInitialized()) {
+ LastError = EError::InvalidMethod;
+ LastErrorMsg = "Request is not initialized";
+ return EError::InvalidMethod;
+ }
+
+ {
+ google::protobuf::io::CodedOutputStream output(&raw);
+
+ output.WriteVarint32(req.ByteSize());
+ req.SerializeWithCachedSizes(&output);
+ }
+
+ raw.Flush();
+
+ int err = raw.GetErrno();
+ if (err)
+ return SetError("send", err);
+
+ return EError::Success;
+}
+
+EError TPortoApi::Recv(TPortoResponse &rsp) {
+ google::protobuf::io::FileInputStream raw(Fd);
+ google::protobuf::io::CodedInputStream input(&raw);
+
+ while (true) {
+ uint32_t size;
+
+ if (!input.ReadVarint32(&size))
+ return SetError("recv", raw.GetErrno() ?: EIO);
+
+ auto prev_limit = input.PushLimit(size);
+
+ rsp.Clear();
+
+ if (!rsp.ParseFromCodedStream(&input))
+ return SetError("recv", raw.GetErrno() ?: EIO);
+
+ input.PopLimit(prev_limit);
+
+ if (rsp.has_asyncwait()) {
+ if (AsyncWaitCallback)
+ AsyncWaitCallback(rsp.asyncwait());
+
+ if (AsyncWaitOneShot)
+ return EError::Success;
+
+ continue;
+ }
+
+ return EError::Success;
+ }
+}
+
+EError TPortoApi::Call(const TPortoRequest &req,
+ TPortoResponse &rsp,
+ int extra_timeout) {
+ bool reconnect = AutoReconnect;
+ EError err = EError::Success;
+
+ if (Fd < 0) {
+ if (!reconnect)
+ return SetError("Not connected", EIO);
+ err = Connect();
+ reconnect = false;
+ }
+
+ if (!err) {
+ err = Send(req);
+ if (err == EError::SocketError && reconnect) {
+ err = Connect();
+ if (!err)
+ err = Send(req);
+ }
+ }
+
+ if (!err && extra_timeout && Timeout > 0)
+ err = SetSocketTimeout(2, extra_timeout > 0 ? (extra_timeout + Timeout) : -1);
+
+ if (!err)
+ err = Recv(rsp);
+
+ if (extra_timeout && Timeout > 0) {
+ EError err = SetSocketTimeout(2, Timeout);
+ (void)err;
+ }
+
+ if (!err) {
+ err = LastError = rsp.error();
+ LastErrorMsg = rsp.errormsg();
+ }
+
+ return err;
+}
+
+EError TPortoApi::Call(int extra_timeout) {
+ return Call(Req, Rsp, extra_timeout);
+}
+
+EError TPortoApi::Call(const TString &req,
+ TString &rsp,
+ int extra_timeout) {
+ Req.Clear();
+ if (!google::protobuf::TextFormat::ParseFromString(req, &Req)) {
+ LastError = EError::InvalidMethod;
+ LastErrorMsg = "Cannot parse request";
+ rsp = "";
+ return EError::InvalidMethod;
+ }
+
+ EError err = Call(Req, Rsp, extra_timeout);
+
+ rsp = Rsp.DebugString();
+
+ return err;
+}
+
+EError TPortoApi::GetVersion(TString &tag, TString &revision) {
+ Req.Clear();
+ Req.mutable_version();
+
+ if (!Call()) {
+ tag = Rsp.version().tag();
+ revision = Rsp.version().revision();
+ }
+
+ return LastError;
+}
+
+const TGetSystemResponse *TPortoApi::GetSystem() {
+ Req.Clear();
+ Req.mutable_getsystem();
+ if (!Call())
+ return &Rsp.getsystem();
+ return nullptr;
+}
+
+EError TPortoApi::SetSystem(const TString &key, const TString &val) {
+ TString rsp;
+ return Call("SetSystem {" + key + ":" + val + "}", rsp);
+}
+
+/* Container */
+
+EError TPortoApi::Create(const TString &name) {
+ Req.Clear();
+ auto req = Req.mutable_create();
+ req->set_name(name);
+ return Call();
+}
+
+EError TPortoApi::CreateWeakContainer(const TString &name) {
+ Req.Clear();
+ auto req = Req.mutable_createweak();
+ req->set_name(name);
+ return Call();
+}
+
+EError TPortoApi::Destroy(const TString &name) {
+ Req.Clear();
+ auto req = Req.mutable_destroy();
+ req->set_name(name);
+ return Call();
+}
+
+const TListResponse *TPortoApi::List(const TString &mask) {
+ Req.Clear();
+ auto req = Req.mutable_list();
+
+ if(!mask.empty())
+ req->set_mask(mask);
+
+ if (!Call())
+ return &Rsp.list();
+
+ return nullptr;
+}
+
+EError TPortoApi::List(TVector<TString> &list, const TString &mask) {
+ Req.Clear();
+ auto req = Req.mutable_list();
+ if(!mask.empty())
+ req->set_mask(mask);
+ if (!Call())
+ list = TVector<TString>(std::begin(Rsp.list().name()),
+ std::end(Rsp.list().name()));
+ return LastError;
+}
+
+const TListPropertiesResponse *TPortoApi::ListProperties() {
+ Req.Clear();
+ Req.mutable_listproperties();
+
+ if (Call())
+ return nullptr;
+
+ bool has_data = false;
+ for (const auto &prop: Rsp.listproperties().list()) {
+ if (prop.read_only()) {
+ has_data = true;
+ break;
+ }
+ }
+
+ if (!has_data) {
+ TPortoRequest req;
+ TPortoResponse rsp;
+
+ req.mutable_listdataproperties();
+ if (!Call(req, rsp)) {
+ for (const auto &data: rsp.listdataproperties().list()) {
+ auto d = Rsp.mutable_listproperties()->add_list();
+ d->set_name(data.name());
+ d->set_desc(data.desc());
+ d->set_read_only(true);
+ }
+ }
+ }
+
+ return &Rsp.listproperties();
+}
+
+EError TPortoApi::ListProperties(TVector<TString> &properties) {
+ properties.clear();
+ auto rsp = ListProperties();
+ if (rsp) {
+ for (auto &prop: rsp->list())
+ properties.push_back(prop.name());
+ }
+ return LastError;
+}
+
+const TGetResponse *TPortoApi::Get(const TVector<TString> &names,
+ const TVector<TString> &vars,
+ int flags) {
+ Req.Clear();
+ auto get = Req.mutable_get();
+
+ for (const auto &n : names)
+ get->add_name(n);
+
+ for (const auto &v : vars)
+ get->add_variable(v);
+
+ if (flags & GET_NONBLOCK)
+ get->set_nonblock(true);
+ if (flags & GET_SYNC)
+ get->set_sync(true);
+ if (flags & GET_REAL)
+ get->set_real(true);
+
+ if (!Call())
+ return &Rsp.get();
+
+ return nullptr;
+}
+
+EError TPortoApi::GetContainerSpec(const TString &name, TContainer &container) {
+ Req.Clear();
+ TListContainersRequest req;
+ auto filter = req.add_filters();
+ filter->set_name(name);
+
+ TVector<TContainer> containers;
+
+ auto ret = ListContainersBy(req, containers);
+ if (containers.empty())
+ return EError::ContainerDoesNotExist;
+
+ if (!ret)
+ container = containers[0];
+
+ return ret;
+}
+
+EError TPortoApi::ListContainersBy(const TListContainersRequest &listContainersRequest, TVector<TContainer> &containers) {
+ Req.Clear();
+ auto req = Req.mutable_listcontainersby();
+ *req = listContainersRequest;
+
+ auto ret = Call();
+ if (ret)
+ return ret;
+
+ for (auto &ct : Rsp.listcontainersby().containers())
+ containers.push_back(ct);
+
+ return EError::Success;
+}
+
+EError TPortoApi::CreateFromSpec(const TContainerSpec &container, TVector<TVolumeSpec> volumes, bool start) {
+ Req.Clear();
+ auto req = Req.mutable_createfromspec();
+
+ auto ct = req->mutable_container();
+ *ct = container;
+
+ for (auto &volume : volumes) {
+ auto v = req->add_volumes();
+ *v = volume;
+ }
+
+ req->set_start(start);
+
+ return Call();
+}
+
+EError TPortoApi::UpdateFromSpec(const TContainerSpec &container) {
+ Req.Clear();
+ auto req = Req.mutable_updatefromspec();
+
+ auto ct = req->mutable_container();
+ *ct = container;
+
+ return Call();
+}
+
+EError TPortoApi::GetProperty(const TString &name,
+ const TString &property,
+ TString &value,
+ int flags) {
+ Req.Clear();
+ auto req = Req.mutable_getproperty();
+
+ req->set_name(name);
+ req->set_property(property);
+ if (flags & GET_SYNC)
+ req->set_sync(true);
+ if (flags & GET_REAL)
+ req->set_real(true);
+
+ if (!Call())
+ value = Rsp.getproperty().value();
+
+ return LastError;
+}
+
+EError TPortoApi::SetProperty(const TString &name,
+ const TString &property,
+ const TString &value) {
+ Req.Clear();
+ auto req = Req.mutable_setproperty();
+
+ req->set_name(name);
+ req->set_property(property);
+ req->set_value(value);
+
+ return Call();
+}
+
+EError TPortoApi::GetInt(const TString &name,
+ const TString &property,
+ const TString &index,
+ uint64_t &value) {
+ TString key = property, str;
+ if (index.size())
+ key = property + "[" + index + "]";
+ if (!GetProperty(name, key, str)) {
+ const char *ptr = str.c_str();
+ char *end;
+ errno = 0;
+ value = strtoull(ptr, &end, 10);
+ if (errno || end == ptr || *end) {
+ LastError = EError::InvalidValue;
+ LastErrorMsg = " value: " + str;
+ }
+ }
+ return LastError;
+}
+
+EError TPortoApi::SetInt(const TString &name,
+ const TString &property,
+ const TString &index,
+ uint64_t value) {
+ TString key = property;
+ if (index.size())
+ key = property + "[" + index + "]";
+ return SetProperty(name, key, ToString(value));
+}
+
+EError TPortoApi::GetProcMetric(const TVector<TString> &names,
+ const TString &metric,
+ TMap<TString, uint64_t> &values) {
+ auto it = ProcMetrics.find(metric);
+
+ if (it == ProcMetrics.end()) {
+ LastError = EError::InvalidValue;
+ LastErrorMsg = " Unknown metric: " + metric;
+ return LastError;
+ }
+
+ LastError = it->second->GetValues(names, values, *this);
+
+ if (LastError)
+ LastErrorMsg = "Unknown error on Get() method";
+
+ return LastError;
+}
+
+EError TPortoApi::SetLabel(const TString &name,
+ const TString &label,
+ const TString &value,
+ const TString &prev_value) {
+ Req.Clear();
+ auto req = Req.mutable_setlabel();
+
+ req->set_name(name);
+ req->set_label(label);
+ req->set_value(value);
+ if (prev_value != " ")
+ req->set_prev_value(prev_value);
+
+ return Call();
+}
+
+EError TPortoApi::IncLabel(const TString &name,
+ const TString &label,
+ int64_t add,
+ int64_t &result) {
+ Req.Clear();
+ auto req = Req.mutable_inclabel();
+
+ req->set_name(name);
+ req->set_label(label);
+ req->set_add(add);
+
+ EError err = Call();
+
+ if (Rsp.has_inclabel())
+ result = Rsp.inclabel().result();
+
+ return err;
+}
+
+EError TPortoApi::Start(const TString &name) {
+ Req.Clear();
+ auto req = Req.mutable_start();
+
+ req->set_name(name);
+
+ return Call();
+}
+
+EError TPortoApi::Stop(const TString &name, int stop_timeout) {
+ Req.Clear();
+ auto req = Req.mutable_stop();
+
+ req->set_name(name);
+ if (stop_timeout >= 0)
+ req->set_timeout_ms(stop_timeout * 1000);
+
+ return Call(stop_timeout > 0 ? stop_timeout : 0);
+}
+
+EError TPortoApi::Kill(const TString &name, int sig) {
+ Req.Clear();
+ auto req = Req.mutable_kill();
+
+ req->set_name(name);
+ req->set_sig(sig);
+
+ return Call();
+}
+
+EError TPortoApi::Pause(const TString &name) {
+ Req.Clear();
+ auto req = Req.mutable_pause();
+
+ req->set_name(name);
+
+ return Call();
+}
+
+EError TPortoApi::Resume(const TString &name) {
+ Req.Clear();
+ auto req = Req.mutable_resume();
+
+ req->set_name(name);
+
+ return Call();
+}
+
+EError TPortoApi::Respawn(const TString &name) {
+ Req.Clear();
+ auto req = Req.mutable_respawn();
+
+ req->set_name(name);
+
+ return Call();
+}
+
+EError TPortoApi::CallWait(TString &result_state, int wait_timeout) {
+ time_t deadline = 0;
+ time_t last_retry = 0;
+
+ if (wait_timeout >= 0) {
+ deadline = time(nullptr) + wait_timeout;
+ Req.mutable_wait()->set_timeout_ms(wait_timeout * 1000);
+ }
+
+retry:
+ if (!Call(wait_timeout)) {
+ if (Rsp.wait().has_state())
+ result_state = Rsp.wait().state();
+ else if (Rsp.wait().name() == "")
+ result_state = "timeout";
+ else
+ result_state = "dead";
+ } else if (LastError == EError::SocketError && AutoReconnect) {
+ time_t now = time(nullptr);
+
+ if (wait_timeout < 0 || now < deadline) {
+ if (wait_timeout >= 0) {
+ wait_timeout = deadline - now;
+ Req.mutable_wait()->set_timeout_ms(wait_timeout * 1000);
+ }
+ if (last_retry == now)
+ sleep(1);
+ last_retry = now;
+ goto retry;
+ }
+
+ result_state = "timeout";
+ } else
+ result_state = "unknown";
+
+ return LastError;
+}
+
+EError TPortoApi::WaitContainer(const TString &name,
+ TString &result_state,
+ int wait_timeout) {
+ Req.Clear();
+ auto req = Req.mutable_wait();
+
+ req->add_name(name);
+
+ return CallWait(result_state, wait_timeout);
+}
+
+EError TPortoApi::WaitContainers(const TVector<TString> &names,
+ TString &result_name,
+ TString &result_state,
+ int wait_timeout) {
+ Req.Clear();
+ auto req = Req.mutable_wait();
+
+ for (auto &c : names)
+ req->add_name(c);
+
+ EError err = CallWait(result_state, wait_timeout);
+
+ result_name = Rsp.wait().name();
+
+ return err;
+}
+
+const TWaitResponse *TPortoApi::Wait(const TVector<TString> &names,
+ const TVector<TString> &labels,
+ int wait_timeout) {
+ Req.Clear();
+ auto req = Req.mutable_wait();
+ TString result_state;
+
+ for (auto &c : names)
+ req->add_name(c);
+ for (auto &label: labels)
+ req->add_label(label);
+
+ EError err = CallWait(result_state, wait_timeout);
+ (void)err;
+
+ if (Rsp.has_wait())
+ return &Rsp.wait();
+
+ return nullptr;
+}
+
+EError TPortoApi::AsyncWait(const TVector<TString> &names,
+ const TVector<TString> &labels,
+ TWaitCallback callback,
+ int wait_timeout,
+ const TString &targetState) {
+ Req.Clear();
+ auto req = Req.mutable_asyncwait();
+
+ AsyncWaitNames.clear();
+ AsyncWaitLabels.clear();
+ AsyncWaitTimeout = wait_timeout;
+ AsyncWaitCallback = callback;
+
+ for (auto &name: names)
+ req->add_name(name);
+ for (auto &label: labels)
+ req->add_label(label);
+ if (wait_timeout >= 0)
+ req->set_timeout_ms(wait_timeout * 1000);
+ if (!targetState.empty()) {
+ req->set_target_state(targetState);
+ AsyncWaitOneShot = true;
+ } else
+ AsyncWaitOneShot = false;
+
+ if (Call()) {
+ AsyncWaitCallback = nullptr;
+ } else {
+ AsyncWaitNames = names;
+ AsyncWaitLabels = labels;
+ }
+
+ return LastError;
+}
+
+EError TPortoApi::StopAsyncWait(const TVector<TString> &names,
+ const TVector<TString> &labels,
+ const TString &targetState) {
+ Req.Clear();
+ auto req = Req.mutable_stopasyncwait();
+
+ AsyncWaitNames.clear();
+ AsyncWaitLabels.clear();
+
+ for (auto &name: names)
+ req->add_name(name);
+ for (auto &label: labels)
+ req->add_label(label);
+ if (!targetState.empty()) {
+ req->set_target_state(targetState);
+ }
+
+ return Call();
+}
+
+EError TPortoApi::ConvertPath(const TString &path,
+ const TString &src,
+ const TString &dest,
+ TString &res) {
+ Req.Clear();
+ auto req = Req.mutable_convertpath();
+
+ req->set_path(path);
+ req->set_source(src);
+ req->set_destination(dest);
+
+ if (!Call())
+ res = Rsp.convertpath().path();
+
+ return LastError;
+}
+
+EError TPortoApi::AttachProcess(const TString &name, int pid,
+ const TString &comm) {
+ Req.Clear();
+ auto req = Req.mutable_attachprocess();
+
+ req->set_name(name);
+ req->set_pid(pid);
+ req->set_comm(comm);
+
+ return Call();
+}
+
+EError TPortoApi::AttachThread(const TString &name, int pid,
+ const TString &comm) {
+ Req.Clear();
+ auto req = Req.mutable_attachthread();
+
+ req->set_name(name);
+ req->set_pid(pid);
+ req->set_comm(comm);
+
+ return Call();
+}
+
+EError TPortoApi::LocateProcess(int pid, const TString &comm,
+ TString &name) {
+ Req.Clear();
+ auto req = Req.mutable_locateprocess();
+
+ req->set_pid(pid);
+ req->set_comm(comm);
+
+ if (!Call())
+ name = Rsp.locateprocess().name();
+
+ return LastError;
+}
+
+/* Volume */
+
+const TListVolumePropertiesResponse *TPortoApi::ListVolumeProperties() {
+ Req.Clear();
+ Req.mutable_listvolumeproperties();
+
+ if (!Call())
+ return &Rsp.listvolumeproperties();
+
+ return nullptr;
+}
+
+EError TPortoApi::ListVolumeProperties(TVector<TString> &properties) {
+ properties.clear();
+ auto rsp = ListVolumeProperties();
+ if (rsp) {
+ for (auto &prop: rsp->list())
+ properties.push_back(prop.name());
+ }
+ return LastError;
+}
+
+EError TPortoApi::CreateVolume(TString &path,
+ const TMap<TString, TString> &config) {
+ Req.Clear();
+ auto req = Req.mutable_createvolume();
+
+ req->set_path(path);
+
+ *(req->mutable_properties()) =
+ google::protobuf::Map<TString, TString>(config.begin(), config.end());
+
+ if (!Call(DiskTimeout) && path.empty())
+ path = Rsp.createvolume().path();
+
+ return LastError;
+}
+
+EError TPortoApi::TuneVolume(const TString &path,
+ const TMap<TString, TString> &config) {
+ Req.Clear();
+ auto req = Req.mutable_tunevolume();
+
+ req->set_path(path);
+
+ *(req->mutable_properties()) =
+ google::protobuf::Map<TString, TString>(config.begin(), config.end());
+
+ return Call(DiskTimeout);
+}
+
+EError TPortoApi::LinkVolume(const TString &path,
+ const TString &container,
+ const TString &target,
+ bool read_only,
+ bool required) {
+ Req.Clear();
+ auto req = (target.empty() && !required) ? Req.mutable_linkvolume() :
+ Req.mutable_linkvolumetarget();
+
+ req->set_path(path);
+ if (!container.empty())
+ req->set_container(container);
+ if (target != "")
+ req->set_target(target);
+ if (read_only)
+ req->set_read_only(read_only);
+ if (required)
+ req->set_required(required);
+
+ return Call();
+}
+
+EError TPortoApi::UnlinkVolume(const TString &path,
+ const TString &container,
+ const TString &target,
+ bool strict) {
+ Req.Clear();
+ auto req = (target == "***") ? Req.mutable_unlinkvolume() :
+ Req.mutable_unlinkvolumetarget();
+
+ req->set_path(path);
+ if (!container.empty())
+ req->set_container(container);
+ if (target != "***")
+ req->set_target(target);
+ if (strict)
+ req->set_strict(strict);
+
+ return Call(DiskTimeout);
+}
+
+const TListVolumesResponse *
+TPortoApi::ListVolumes(const TString &path,
+ const TString &container) {
+ Req.Clear();
+ auto req = Req.mutable_listvolumes();
+
+ if (!path.empty())
+ req->set_path(path);
+
+ if (!container.empty())
+ req->set_container(container);
+
+ if (Call())
+ return nullptr;
+
+ auto list = Rsp.mutable_listvolumes();
+
+ /* compat */
+ for (auto v: *list->mutable_volumes()) {
+ if (v.links().size())
+ break;
+ for (auto &ct: v.containers())
+ v.add_links()->set_container(ct);
+ }
+
+ return list;
+}
+
+EError TPortoApi::ListVolumes(TVector<TString> &paths) {
+ Req.Clear();
+ auto rsp = ListVolumes();
+ paths.clear();
+ if (rsp) {
+ for (auto &v : rsp->volumes())
+ paths.push_back(v.path());
+ }
+ return LastError;
+}
+
+const TVolumeDescription *TPortoApi::GetVolumeDesc(const TString &path) {
+ Req.Clear();
+ auto rsp = ListVolumes(path);
+
+ if (rsp && rsp->volumes().size())
+ return &rsp->volumes(0);
+
+ return nullptr;
+}
+
+const TVolumeSpec *TPortoApi::GetVolume(const TString &path) {
+ Req.Clear();
+ auto req = Req.mutable_getvolume();
+
+ req->add_path(path);
+
+ if (!Call() && Rsp.getvolume().volume().size())
+ return &Rsp.getvolume().volume(0);
+
+ return nullptr;
+}
+
+const TGetVolumeResponse *TPortoApi::GetVolumes(uint64_t changed_since) {
+ Req.Clear();
+ auto req = Req.mutable_getvolume();
+
+ if (changed_since)
+ req->set_changed_since(changed_since);
+
+ if (!Call() && Rsp.has_getvolume())
+ return &Rsp.getvolume();
+
+ return nullptr;
+}
+
+
+EError TPortoApi::ListVolumesBy(const TGetVolumeRequest &getVolumeRequest, TVector<TVolumeSpec> &volumes) {
+ Req.Clear();
+ auto req = Req.mutable_getvolume();
+ *req = getVolumeRequest;
+
+ auto ret = Call();
+ if (ret)
+ return ret;
+
+ for (auto volume : Rsp.getvolume().volume())
+ volumes.push_back(volume);
+ return EError::Success;
+}
+
+EError TPortoApi::CreateVolumeFromSpec(const TVolumeSpec &volume, TVolumeSpec &resultSpec) {
+ Req.Clear();
+ auto req = Req.mutable_newvolume();
+ auto vol = req->mutable_volume();
+ *vol = volume;
+
+ auto ret = Call();
+ if (ret)
+ return ret;
+
+ resultSpec = Rsp.newvolume().volume();
+
+ return ret;
+}
+
+/* Layer */
+
+EError TPortoApi::ImportLayer(const TString &layer,
+ const TString &tarball,
+ bool merge,
+ const TString &place,
+ const TString &private_value,
+ bool verboseError) {
+ Req.Clear();
+ auto req = Req.mutable_importlayer();
+
+ req->set_layer(layer);
+ req->set_tarball(tarball);
+ req->set_merge(merge);
+ req->set_verbose_error(verboseError);
+ if (place.size())
+ req->set_place(place);
+ if (private_value.size())
+ req->set_private_value(private_value);
+
+ return Call(DiskTimeout);
+}
+
+EError TPortoApi::ExportLayer(const TString &volume,
+ const TString &tarball,
+ const TString &compress) {
+ Req.Clear();
+ auto req = Req.mutable_exportlayer();
+
+ req->set_volume(volume);
+ req->set_tarball(tarball);
+ if (compress.size())
+ req->set_compress(compress);
+
+ return Call(DiskTimeout);
+}
+
+EError TPortoApi::ReExportLayer(const TString &layer,
+ const TString &tarball,
+ const TString &compress) {
+ Req.Clear();
+ auto req = Req.mutable_exportlayer();
+
+ req->set_volume("");
+ req->set_layer(layer);
+ req->set_tarball(tarball);
+ if (compress.size())
+ req->set_compress(compress);
+
+ return Call(DiskTimeout);
+}
+
+EError TPortoApi::RemoveLayer(const TString &layer,
+ const TString &place,
+ bool async) {
+ Req.Clear();
+ auto req = Req.mutable_removelayer();
+
+ req->set_layer(layer);
+ req->set_async(async);
+ if (place.size())
+ req->set_place(place);
+
+ return Call(DiskTimeout);
+}
+
+const TListLayersResponse *TPortoApi::ListLayers(const TString &place,
+ const TString &mask) {
+ Req.Clear();
+ auto req = Req.mutable_listlayers();
+
+ if (place.size())
+ req->set_place(place);
+ if (mask.size())
+ req->set_mask(mask);
+
+ if (Call())
+ return nullptr;
+
+ auto list = Rsp.mutable_listlayers();
+
+ /* compat conversion */
+ if (!list->layers().size() && list->layer().size()) {
+ for (auto &name: list->layer()) {
+ auto l = list->add_layers();
+ l->set_name(name);
+ l->set_owner_user("");
+ l->set_owner_group("");
+ l->set_last_usage(0);
+ l->set_private_value("");
+ }
+ }
+
+ return list;
+}
+
+EError TPortoApi::ListLayers(TVector<TString> &layers,
+ const TString &place,
+ const TString &mask) {
+ Req.Clear();
+ auto req = Req.mutable_listlayers();
+
+ if (place.size())
+ req->set_place(place);
+ if (mask.size())
+ req->set_mask(mask);
+
+ if (!Call())
+ layers = TVector<TString>(std::begin(Rsp.listlayers().layer()),
+ std::end(Rsp.listlayers().layer()));
+
+ return LastError;
+}
+
+EError TPortoApi::GetLayerPrivate(TString &private_value,
+ const TString &layer,
+ const TString &place) {
+ Req.Clear();
+ auto req = Req.mutable_getlayerprivate();
+
+ req->set_layer(layer);
+ if (place.size())
+ req->set_place(place);
+
+ if (!Call())
+ private_value = Rsp.getlayerprivate().private_value();
+
+ return LastError;
+}
+
+EError TPortoApi::SetLayerPrivate(const TString &private_value,
+ const TString &layer,
+ const TString &place) {
+ Req.Clear();
+ auto req = Req.mutable_setlayerprivate();
+
+ req->set_layer(layer);
+ req->set_private_value(private_value);
+ if (place.size())
+ req->set_place(place);
+
+ return Call();
+}
+
+/* Docker images */
+
+DockerImage::DockerImage(const TDockerImage &i) {
+ Id = i.id();
+ for (const auto &tag: i.tags())
+ Tags.emplace_back(tag);
+ for (const auto &digest: i.digests())
+ Digests.emplace_back(digest);
+ for (const auto &layer: i.layers())
+ Layers.emplace_back(layer);
+ if (i.has_size())
+ Size = i.size();
+ if (i.has_config()) {
+ auto &cfg = i.config();
+ for (const auto &cmd: cfg.cmd())
+ Config.Cmd.emplace_back(cmd);
+ for (const auto &env: cfg.env())
+ Config.Env.emplace_back(env);
+ }
+}
+
+EError TPortoApi::DockerImageStatus(DockerImage &image,
+ const TString &name,
+ const TString &place) {
+ auto req = Req.mutable_dockerimagestatus();
+ req->set_name(name);
+ if (!place.empty())
+ req->set_place(place);
+ EError ret = Call();
+ if (!ret && Rsp.dockerimagestatus().has_image())
+ image = DockerImage(Rsp.dockerimagestatus().image());
+ return ret;
+}
+
+EError TPortoApi::ListDockerImages(std::vector<DockerImage> &images,
+ const TString &place,
+ const TString &mask) {
+ auto req = Req.mutable_listdockerimages();
+ if (place.size())
+ req->set_place(place);
+ if (mask.size())
+ req->set_mask(mask);
+ EError ret = Call();
+ if (!ret) {
+ for (const auto &i: Rsp.listdockerimages().images())
+ images.emplace_back(i);
+ }
+ return ret;
+}
+
+EError TPortoApi::PullDockerImage(DockerImage &image,
+ const TString &name,
+ const TString &place,
+ const TString &auth_token,
+ const TString &auth_path,
+ const TString &auth_service) {
+ auto req = Req.mutable_pulldockerimage();
+ req->set_name(name);
+ if (place.size())
+ req->set_place(place);
+ if (auth_token.size())
+ req->set_auth_token(auth_token);
+ if (auth_path.size())
+ req->set_auth_path(auth_path);
+ if (auth_service.size())
+ req->set_auth_service(auth_service);
+ EError ret = Call();
+ if (!ret && Rsp.pulldockerimage().has_image())
+ image = DockerImage(Rsp.pulldockerimage().image());
+ return ret;
+}
+
+EError TPortoApi::RemoveDockerImage(const TString &name,
+ const TString &place) {
+ auto req = Req.mutable_removedockerimage();
+ req->set_name(name);
+ if (place.size())
+ req->set_place(place);
+ return Call();
+}
+
+/* Storage */
+
+const TListStoragesResponse *TPortoApi::ListStorages(const TString &place,
+ const TString &mask) {
+ Req.Clear();
+ auto req = Req.mutable_liststorages();
+
+ if (place.size())
+ req->set_place(place);
+ if (mask.size())
+ req->set_mask(mask);
+
+ if (Call())
+ return nullptr;
+
+ return &Rsp.liststorages();
+}
+
+EError TPortoApi::ListStorages(TVector<TString> &storages,
+ const TString &place,
+ const TString &mask) {
+ Req.Clear();
+ auto req = Req.mutable_liststorages();
+
+ if (place.size())
+ req->set_place(place);
+ if (mask.size())
+ req->set_mask(mask);
+
+ if (!Call()) {
+ storages.clear();
+ for (auto &storage: Rsp.liststorages().storages())
+ storages.push_back(storage.name());
+ }
+
+ return LastError;
+}
+
+EError TPortoApi::RemoveStorage(const TString &storage,
+ const TString &place) {
+ Req.Clear();
+ auto req = Req.mutable_removestorage();
+
+ req->set_name(storage);
+ if (place.size())
+ req->set_place(place);
+
+ return Call(DiskTimeout);
+}
+
+EError TPortoApi::ImportStorage(const TString &storage,
+ const TString &archive,
+ const TString &place,
+ const TString &compression,
+ const TString &private_value) {
+ Req.Clear();
+ auto req = Req.mutable_importstorage();
+
+ req->set_name(storage);
+ req->set_tarball(archive);
+ if (place.size())
+ req->set_place(place);
+ if (compression.size())
+ req->set_compress(compression);
+ if (private_value.size())
+ req->set_private_value(private_value);
+
+ return Call(DiskTimeout);
+}
+
+EError TPortoApi::ExportStorage(const TString &storage,
+ const TString &archive,
+ const TString &place,
+ const TString &compression) {
+ Req.Clear();
+ auto req = Req.mutable_exportstorage();
+
+ req->set_name(storage);
+ req->set_tarball(archive);
+ if (place.size())
+ req->set_place(place);
+ if (compression.size())
+ req->set_compress(compression);
+
+ return Call(DiskTimeout);
+}
+
+#ifdef __linux__
+void TAsyncWaiter::MainCallback(const TWaitResponse &event) {
+ CallbacksCount++;
+
+ auto it = AsyncCallbacks.find(event.name());
+ if (it != AsyncCallbacks.end() && it->second.State == event.state()) {
+ it->second.Callback(event);
+ AsyncCallbacks.erase(it);
+ }
+}
+
+int TAsyncWaiter::Repair() {
+ for (const auto &it : AsyncCallbacks) {
+ int ret = Api.AsyncWait({it.first}, {}, GetMainCallback(), -1, it.second.State);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+void TAsyncWaiter::WatchDog() {
+ int ret;
+ auto apiFd = Api.Fd;
+
+ while (true) {
+ struct epoll_event events[2];
+ int nfds = epoll_wait(EpollFd, events, 2, -1);
+
+ if (nfds < 0) {
+ if (errno == EINTR)
+ continue;
+
+ Fatal("Can not make epoll_wait", errno);
+ return;
+ }
+
+ for (int n = 0; n < nfds; ++n) {
+ if (events[n].data.fd == apiFd) {
+ TPortoResponse rsp;
+ ret = Api.Recv(rsp);
+ // portod reloaded - async_wait must be repaired
+ if (ret == EError::SocketError) {
+ ret = Api.Connect();
+ if (ret) {
+ Fatal("Can not connect to porto api", ret);
+ return;
+ }
+
+ ret = Repair();
+ if (ret) {
+ Fatal("Can not repair", ret);
+ return;
+ }
+
+ apiFd = Api.Fd;
+
+ struct epoll_event portoEv;
+ portoEv.events = EPOLLIN;
+ portoEv.data.fd = apiFd;
+ if (epoll_ctl(EpollFd, EPOLL_CTL_ADD, apiFd, &portoEv)) {
+ Fatal("Can not epoll_ctl", errno);
+ return;
+ }
+ }
+ } else if (events[n].data.fd == Sock) {
+ ERequestType requestType = static_cast<ERequestType>(RecvInt(Sock));
+
+ switch (requestType) {
+ case ERequestType::Add:
+ HandleAddRequest();
+ break;
+ case ERequestType::Del:
+ HandleDelRequest();
+ break;
+ case ERequestType::Stop:
+ return;
+ case ERequestType::None:
+ default:
+ Fatal("Unknown request", static_cast<int>(requestType));
+ }
+ }
+ }
+ }
+}
+
+void TAsyncWaiter::SendInt(int fd, int value) {
+ int ret = write(fd, &value, sizeof(value));
+ if (ret != sizeof(value))
+ Fatal("Can not send int", errno);
+}
+
+int TAsyncWaiter::RecvInt(int fd) {
+ int value;
+ int ret = read(fd, &value, sizeof(value));
+ if (ret != sizeof(value))
+ Fatal("Can not recv int", errno);
+
+ return value;
+}
+
+void TAsyncWaiter::HandleAddRequest() {
+ int ret = 0;
+
+ auto it = AsyncCallbacks.find(ReqCt);
+ if (it != AsyncCallbacks.end()) {
+ ret = Api.StopAsyncWait({ReqCt}, {}, it->second.State);
+ AsyncCallbacks.erase(it);
+ }
+
+ AsyncCallbacks.insert(std::make_pair(ReqCt, TCallbackData({ReqCallback, ReqState})));
+
+ ret = Api.AsyncWait({ReqCt}, {}, GetMainCallback(), -1, ReqState);
+ SendInt(Sock, ret);
+}
+
+void TAsyncWaiter::HandleDelRequest() {
+ int ret = 0;
+
+ auto it = AsyncCallbacks.find(ReqCt);
+ if (it != AsyncCallbacks.end()) {
+ ret = Api.StopAsyncWait({ReqCt}, {}, it->second.State);
+ AsyncCallbacks.erase(it);
+ }
+
+ SendInt(Sock, ret);
+}
+
+TAsyncWaiter::TAsyncWaiter(std::function<void(const TString &error, int ret)> fatalCallback)
+ : CallbacksCount(0ul)
+ , FatalCallback(fatalCallback)
+{
+ int socketPair[2];
+ int ret = socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, socketPair);
+ if (ret) {
+ Fatal("Can not make socketpair", ret);
+ return;
+ }
+
+ MasterSock = socketPair[0];
+ Sock = socketPair[1];
+
+ ret = Api.Connect();
+ if (ret) {
+ Fatal("Can not connect to porto api", ret);
+ return;
+ }
+
+ auto apiFd = Api.Fd;
+
+ EpollFd = epoll_create1(EPOLL_CLOEXEC);
+
+ if (EpollFd == -1) {
+ Fatal("Can not epoll_create", errno);
+ return;
+ }
+
+ struct epoll_event pairEv;
+ pairEv.events = EPOLLIN;
+ pairEv.data.fd = Sock;
+
+ struct epoll_event portoEv;
+ portoEv.events = EPOLLIN;
+ portoEv.data.fd = apiFd;
+
+ if (epoll_ctl(EpollFd, EPOLL_CTL_ADD, Sock, &pairEv)) {
+ Fatal("Can not epoll_ctl", errno);
+ return;
+ }
+
+ if (epoll_ctl(EpollFd, EPOLL_CTL_ADD, apiFd, &portoEv)) {
+ Fatal("Can not epoll_ctl", errno);
+ return;
+ }
+
+ WatchDogThread = std::unique_ptr<std::thread>(new std::thread(&TAsyncWaiter::WatchDog, this));
+}
+
+TAsyncWaiter::~TAsyncWaiter() {
+ SendInt(MasterSock, static_cast<int>(ERequestType::Stop));
+ WatchDogThread->join();
+
+ // pedantic check, that porto api is watching by epoll
+ if (epoll_ctl(EpollFd, EPOLL_CTL_DEL, Api.Fd, nullptr) || epoll_ctl(EpollFd, EPOLL_CTL_DEL, Sock, nullptr)) {
+ Fatal("Can not epoll_ctl_del", errno);
+ }
+
+ close(EpollFd);
+ close(Sock);
+ close(MasterSock);
+}
+
+int TAsyncWaiter::Add(const TString &ct, const TString &state, TWaitCallback callback) {
+ if (FatalError)
+ return -1;
+
+ ReqCt = ct;
+ ReqState = state;
+ ReqCallback = callback;
+
+ SendInt(MasterSock, static_cast<int>(ERequestType::Add));
+ return RecvInt(MasterSock);
+}
+
+int TAsyncWaiter::Remove(const TString &ct) {
+ if (FatalError)
+ return -1;
+
+ ReqCt = ct;
+
+ SendInt(MasterSock, static_cast<int>(ERequestType::Del));
+ return RecvInt(MasterSock);
+}
+#endif
+
+} /* namespace Porto */
diff --git a/library/cpp/porto/libporto.hpp b/library/cpp/porto/libporto.hpp
new file mode 100644
index 0000000000..e30f22a41e
--- /dev/null
+++ b/library/cpp/porto/libporto.hpp
@@ -0,0 +1,492 @@
+#pragma once
+
+#include <atomic>
+#include <thread>
+
+#include <util/string/cast.h>
+#include <util/generic/hash.h>
+#include <util/generic/map.h>
+#include <util/generic/vector.h>
+
+#include <library/cpp/porto/proto/rpc.pb.h>
+
+namespace Porto {
+
+constexpr int INFINITE_TIMEOUT = -1;
+constexpr int DEFAULT_TIMEOUT = 300; // 5min
+constexpr int DEFAULT_DISK_TIMEOUT = 900; // 15min
+
+constexpr char SOCKET_PATH[] = "/run/portod.socket";
+
+typedef std::function<void(const TWaitResponse &event)> TWaitCallback;
+
+enum {
+ GET_NONBLOCK = 1, // try lock container state
+ GET_SYNC = 2, // refresh cached values, cache ttl 5s
+ GET_REAL = 4, // no faked or inherited values
+};
+
+struct DockerImage {
+ std::string Id;
+ std::vector<std::string> Tags;
+ std::vector<std::string> Digests;
+ std::vector<std::string> Layers;
+ uint64_t Size;
+ struct Config {
+ std::vector<std::string> Cmd;
+ std::vector<std::string> Env;
+ } Config;
+
+ DockerImage() = default;
+ DockerImage(const TDockerImage &i);
+
+ DockerImage(const DockerImage &i) = default;
+ DockerImage(DockerImage &&i) = default;
+
+ DockerImage& operator=(const DockerImage &i) = default;
+ DockerImage& operator=(DockerImage &&i) = default;
+};
+
+class TPortoApi {
+#ifdef __linux__
+ friend class TAsyncWaiter;
+#endif
+private:
+ int Fd = -1;
+ int Timeout = DEFAULT_TIMEOUT;
+ int DiskTimeout = DEFAULT_DISK_TIMEOUT;
+ bool AutoReconnect = true;
+
+ EError LastError = EError::Success;
+ TString LastErrorMsg;
+
+ /*
+ * These keep last request and response. Method might return
+ * pointers to Rsp innards -> pointers valid until next call.
+ */
+ TPortoRequest Req;
+ TPortoResponse Rsp;
+
+ std::vector<TString> AsyncWaitNames;
+ std::vector<TString> AsyncWaitLabels;
+ int AsyncWaitTimeout = INFINITE_TIMEOUT;
+ TWaitCallback AsyncWaitCallback;
+ bool AsyncWaitOneShot = false;
+
+ EError SetError(const TString &prefix, int _errno) Y_WARN_UNUSED_RESULT;
+
+ EError SetSocketTimeout(int direction, int timeout) Y_WARN_UNUSED_RESULT;
+
+ EError Send(const TPortoRequest &req) Y_WARN_UNUSED_RESULT;
+
+ EError Recv(TPortoResponse &rsp) Y_WARN_UNUSED_RESULT;
+
+ EError Call(int extra_timeout = 0) Y_WARN_UNUSED_RESULT;
+
+ EError CallWait(TString &result_state, int wait_timeout) Y_WARN_UNUSED_RESULT;
+
+public:
+ TPortoApi() { }
+ ~TPortoApi();
+
+ int GetFd() const {
+ return Fd;
+ }
+
+ bool Connected() const {
+ return Fd >= 0;
+ }
+
+ EError Connect(const char *socket_path = SOCKET_PATH) Y_WARN_UNUSED_RESULT;
+ void Disconnect();
+
+ /* Requires signal(SIGPIPE, SIG_IGN) */
+ void SetAutoReconnect(bool auto_reconnect) {
+ AutoReconnect = auto_reconnect;
+ }
+
+ /* Request and response timeout in seconds */
+ int GetTimeout() const {
+ return Timeout;
+ }
+ EError SetTimeout(int timeout);
+
+ /* Extra timeout for disk operations in seconds */
+ int GetDiskTimeout() const {
+ return DiskTimeout;
+ }
+ EError SetDiskTimeout(int timeout);
+
+ EError Error() const Y_WARN_UNUSED_RESULT {
+ return LastError;
+ }
+
+ EError GetLastError(TString &msg) const Y_WARN_UNUSED_RESULT {
+ msg = LastErrorMsg;
+ return LastError;
+ }
+
+ /* Returns "LastError:(LastErrorMsg)" */
+ TString GetLastError() const Y_WARN_UNUSED_RESULT;
+
+ /* Returns text protobuf */
+ TString GetLastRequest() const {
+ return Req.DebugString();
+ }
+ TString GetLastResponse() const {
+ return Rsp.DebugString();
+ }
+
+ /* To be used for next changed_since */
+ uint64_t ResponseTimestamp() const Y_WARN_UNUSED_RESULT {
+ return Rsp.timestamp();
+ }
+
+ // extra_timeout: 0 - none, -1 - infinite
+ EError Call(const TPortoRequest &req,
+ TPortoResponse &rsp,
+ int extra_timeout = 0) Y_WARN_UNUSED_RESULT;
+
+ EError Call(const TString &req,
+ TString &rsp,
+ int extra_timeout = 0) Y_WARN_UNUSED_RESULT;
+
+ /* System */
+
+ EError GetVersion(TString &tag, TString &revision) Y_WARN_UNUSED_RESULT;
+
+ const TGetSystemResponse *GetSystem();
+
+ EError SetSystem(const TString &key, const TString &val) Y_WARN_UNUSED_RESULT;
+
+ /* Container */
+
+ const TListPropertiesResponse *ListProperties();
+
+ EError ListProperties(TVector<TString> &properties) Y_WARN_UNUSED_RESULT;
+
+ const TListResponse *List(const TString &mask = "");
+
+ EError List(TVector<TString> &names, const TString &mask = "") Y_WARN_UNUSED_RESULT;
+
+ EError Create(const TString &name) Y_WARN_UNUSED_RESULT;
+
+ EError CreateWeakContainer(const TString &name) Y_WARN_UNUSED_RESULT;
+
+ EError Destroy(const TString &name) Y_WARN_UNUSED_RESULT;
+
+ EError Start(const TString &name)Y_WARN_UNUSED_RESULT;
+
+ // stop_timeout: time between SIGTERM and SIGKILL, -1 - default
+ EError Stop(const TString &name, int stop_timeout = -1) Y_WARN_UNUSED_RESULT;
+
+ EError Kill(const TString &name, int sig = 9) Y_WARN_UNUSED_RESULT;
+
+ EError Pause(const TString &name) Y_WARN_UNUSED_RESULT;
+
+ EError Resume(const TString &name) Y_WARN_UNUSED_RESULT;
+
+ EError Respawn(const TString &name) Y_WARN_UNUSED_RESULT;
+
+ // wait_timeout: 0 - nonblock, -1 - infinite
+ EError WaitContainer(const TString &name,
+ TString &result_state,
+ int wait_timeout = INFINITE_TIMEOUT) Y_WARN_UNUSED_RESULT;
+
+ EError WaitContainers(const TVector<TString> &names,
+ TString &result_name,
+ TString &result_state,
+ int wait_timeout = INFINITE_TIMEOUT) Y_WARN_UNUSED_RESULT;
+
+ const TWaitResponse *Wait(const TVector<TString> &names,
+ const TVector<TString> &labels,
+ int wait_timeout = INFINITE_TIMEOUT) Y_WARN_UNUSED_RESULT;
+
+ EError AsyncWait(const TVector<TString> &names,
+ const TVector<TString> &labels,
+ TWaitCallback callbacks,
+ int wait_timeout = INFINITE_TIMEOUT,
+ const TString &targetState = "") Y_WARN_UNUSED_RESULT;
+
+ EError StopAsyncWait(const TVector<TString> &names,
+ const TVector<TString> &labels,
+ const TString &targetState = "") Y_WARN_UNUSED_RESULT;
+
+ const TGetResponse *Get(const TVector<TString> &names,
+ const TVector<TString> &properties,
+ int flags = 0) Y_WARN_UNUSED_RESULT;
+
+ /* Porto v5 api */
+ EError GetContainerSpec(const TString &name, TContainer &container) Y_WARN_UNUSED_RESULT ;
+ EError ListContainersBy(const TListContainersRequest &listContainersRequest, TVector<TContainer> &containers) Y_WARN_UNUSED_RESULT;
+ EError CreateFromSpec(const TContainerSpec &container, TVector<TVolumeSpec> volumes, bool start = false) Y_WARN_UNUSED_RESULT;
+ EError UpdateFromSpec(const TContainerSpec &container) Y_WARN_UNUSED_RESULT;
+
+ EError GetProperty(const TString &name,
+ const TString &property,
+ TString &value,
+ int flags = 0) Y_WARN_UNUSED_RESULT;
+
+ EError GetProperty(const TString &name,
+ const TString &property,
+ const TString &index,
+ TString &value,
+ int flags = 0) Y_WARN_UNUSED_RESULT {
+ return GetProperty(name, property + "[" + index + "]", value, flags);
+ }
+
+ EError SetProperty(const TString &name,
+ const TString &property,
+ const TString &value) Y_WARN_UNUSED_RESULT;
+
+ EError SetProperty(const TString &name,
+ const TString &property,
+ const TString &index,
+ const TString &value) Y_WARN_UNUSED_RESULT {
+ return SetProperty(name, property + "[" + index + "]", value);
+ }
+
+ EError GetInt(const TString &name,
+ const TString &property,
+ const TString &index,
+ uint64_t &value) Y_WARN_UNUSED_RESULT;
+
+ EError GetInt(const TString &name,
+ const TString &property,
+ uint64_t &value) Y_WARN_UNUSED_RESULT {
+ return GetInt(name, property, "", value);
+ }
+
+ EError SetInt(const TString &name,
+ const TString &property,
+ const TString &index,
+ uint64_t value) Y_WARN_UNUSED_RESULT;
+
+ EError SetInt(const TString &name,
+ const TString &property,
+ uint64_t value) Y_WARN_UNUSED_RESULT {
+ return SetInt(name, property, "", value);
+ }
+
+ EError GetProcMetric(const TVector<TString> &names,
+ const TString &metric,
+ TMap<TString, uint64_t> &values);
+
+ EError GetLabel(const TString &name,
+ const TString &label,
+ TString &value) Y_WARN_UNUSED_RESULT {
+ return GetProperty(name, "labels", label, value);
+ }
+
+ EError SetLabel(const TString &name,
+ const TString &label,
+ const TString &value,
+ const TString &prev_value = " ") Y_WARN_UNUSED_RESULT;
+
+ EError IncLabel(const TString &name,
+ const TString &label,
+ int64_t add,
+ int64_t &result) Y_WARN_UNUSED_RESULT;
+
+ EError IncLabel(const TString &name,
+ const TString &label,
+ int64_t add = 1) Y_WARN_UNUSED_RESULT {
+ int64_t result;
+ return IncLabel(name, label, add, result);
+ }
+
+ EError ConvertPath(const TString &path,
+ const TString &src_name,
+ const TString &dst_name,
+ TString &result_path) Y_WARN_UNUSED_RESULT;
+
+ EError AttachProcess(const TString &name, int pid,
+ const TString &comm = "") Y_WARN_UNUSED_RESULT;
+
+ EError AttachThread(const TString &name, int pid,
+ const TString &comm = "") Y_WARN_UNUSED_RESULT;
+
+ EError LocateProcess(int pid,
+ const TString &comm /* = "" */,
+ TString &name) Y_WARN_UNUSED_RESULT;
+
+ /* Volume */
+
+ const TListVolumePropertiesResponse *ListVolumeProperties();
+
+ EError ListVolumeProperties(TVector<TString> &properties) Y_WARN_UNUSED_RESULT;
+
+ const TListVolumesResponse *ListVolumes(const TString &path = "",
+ const TString &container = "");
+
+ EError ListVolumes(TVector<TString> &paths) Y_WARN_UNUSED_RESULT;
+
+ const TVolumeDescription *GetVolumeDesc(const TString &path);
+
+ /* Porto v5 api */
+ EError ListVolumesBy(const TGetVolumeRequest &getVolumeRequest, TVector<TVolumeSpec> &volumes) Y_WARN_UNUSED_RESULT;
+ EError CreateVolumeFromSpec(const TVolumeSpec &volume, TVolumeSpec &resultSpec) Y_WARN_UNUSED_RESULT;
+
+ const TVolumeSpec *GetVolume(const TString &path);
+
+ const TGetVolumeResponse *GetVolumes(uint64_t changed_since = 0);
+
+ EError CreateVolume(TString &path,
+ const TMap<TString, TString> &config) Y_WARN_UNUSED_RESULT;
+
+ EError LinkVolume(const TString &path,
+ const TString &container = "",
+ const TString &target = "",
+ bool read_only = false,
+ bool required = false) Y_WARN_UNUSED_RESULT;
+
+ EError UnlinkVolume(const TString &path,
+ const TString &container = "",
+ const TString &target = "***",
+ bool strict = false) Y_WARN_UNUSED_RESULT;
+
+ EError TuneVolume(const TString &path,
+ const TMap<TString, TString> &config) Y_WARN_UNUSED_RESULT;
+
+ /* Layer */
+
+ const TListLayersResponse *ListLayers(const TString &place = "",
+ const TString &mask = "");
+
+ EError ListLayers(TVector<TString> &layers,
+ const TString &place = "",
+ const TString &mask = "") Y_WARN_UNUSED_RESULT;
+
+ EError ImportLayer(const TString &layer,
+ const TString &tarball,
+ bool merge = false,
+ const TString &place = "",
+ const TString &private_value = "",
+ bool verboseError = false) Y_WARN_UNUSED_RESULT;
+
+ EError ExportLayer(const TString &volume,
+ const TString &tarball,
+ const TString &compress = "") Y_WARN_UNUSED_RESULT;
+
+ EError ReExportLayer(const TString &layer,
+ const TString &tarball,
+ const TString &compress = "") Y_WARN_UNUSED_RESULT;
+
+ EError RemoveLayer(const TString &layer,
+ const TString &place = "",
+ bool async = false) Y_WARN_UNUSED_RESULT;
+
+ EError GetLayerPrivate(TString &private_value,
+ const TString &layer,
+ const TString &place = "") Y_WARN_UNUSED_RESULT;
+
+ EError SetLayerPrivate(const TString &private_value,
+ const TString &layer,
+ const TString &place = "") Y_WARN_UNUSED_RESULT;
+
+ /* Docker images */
+
+ EError DockerImageStatus(DockerImage &image,
+ const TString &name,
+ const TString &place = "") Y_WARN_UNUSED_RESULT;
+
+ EError ListDockerImages(std::vector<DockerImage> &images,
+ const TString &place = "",
+ const TString &mask = "") Y_WARN_UNUSED_RESULT;
+
+ EError PullDockerImage(DockerImage &image,
+ const TString &name,
+ const TString &place = "",
+ const TString &auth_token = "",
+ const TString &auth_host = "",
+ const TString &auth_service = "") Y_WARN_UNUSED_RESULT;
+
+ EError RemoveDockerImage(const TString &name,
+ const TString &place = "");
+
+ /* Storage */
+
+ const TListStoragesResponse *ListStorages(const TString &place = "",
+ const TString &mask = "");
+
+ EError ListStorages(TVector<TString> &storages,
+ const TString &place = "",
+ const TString &mask = "") Y_WARN_UNUSED_RESULT;
+
+ EError RemoveStorage(const TString &storage,
+ const TString &place = "") Y_WARN_UNUSED_RESULT;
+
+ EError ImportStorage(const TString &storage,
+ const TString &archive,
+ const TString &place = "",
+ const TString &compression = "",
+ const TString &private_value = "") Y_WARN_UNUSED_RESULT;
+
+ EError ExportStorage(const TString &storage,
+ const TString &archive,
+ const TString &place = "",
+ const TString &compression = "") Y_WARN_UNUSED_RESULT;
+};
+
+#ifdef __linux__
+class TAsyncWaiter {
+ struct TCallbackData {
+ const TWaitCallback Callback;
+ const TString State;
+ };
+
+ enum class ERequestType {
+ None,
+ Add,
+ Del,
+ Stop,
+ };
+
+ THashMap<TString, TCallbackData> AsyncCallbacks;
+ std::unique_ptr<std::thread> WatchDogThread;
+ std::atomic<uint64_t> CallbacksCount;
+ int EpollFd = -1;
+ TPortoApi Api;
+
+ int Sock, MasterSock;
+ TString ReqCt;
+ TString ReqState;
+ TWaitCallback ReqCallback;
+
+ std::function<void(const TString &error, int ret)> FatalCallback;
+ bool FatalError = false;
+
+ void MainCallback(const TWaitResponse &event);
+ inline TWaitCallback GetMainCallback() {
+ return [this](const TWaitResponse &event) {
+ MainCallback(event);
+ };
+ }
+
+ int Repair();
+ void WatchDog();
+
+ void SendInt(int fd, int value);
+ int RecvInt(int fd);
+
+ void HandleAddRequest();
+ void HandleDelRequest();
+
+ void Fatal(const TString &error, int ret) {
+ FatalError = true;
+ FatalCallback(error, ret);
+ }
+
+ public:
+ TAsyncWaiter(std::function<void(const TString &error, int ret)> fatalCallback);
+ ~TAsyncWaiter();
+
+ int Add(const TString &ct, const TString &state, TWaitCallback callback);
+ int Remove(const TString &ct);
+ uint64_t InvocationCount() const {
+ return CallbacksCount;
+ }
+};
+#endif
+
+} /* namespace Porto */
diff --git a/library/cpp/porto/libporto_ut.cpp b/library/cpp/porto/libporto_ut.cpp
new file mode 100644
index 0000000000..9d78397fb8
--- /dev/null
+++ b/library/cpp/porto/libporto_ut.cpp
@@ -0,0 +1,226 @@
+#include <library/cpp/testing/unittest/registar.h>
+#include <libporto.hpp>
+
+#include <signal.h>
+#include <cassert>
+
+#define Expect(a) assert(a)
+#define ExpectEq(a, b) assert((a) == (b))
+#define ExpectNeq(a, b) assert((a) != (b))
+#define ExpectSuccess(ret) assert((ret) == Porto::EError::Success)
+
+const TString CT_NAME = "test-a";
+
+void test_porto() {
+ TVector<TString> list;
+ TString str, path;
+
+ signal(SIGPIPE, SIG_IGN);
+
+ Porto::TPortoApi api;
+
+ Expect(!api.Connected());
+ Expect(api.GetFd() < 0);
+
+ // Connect
+ ExpectSuccess(api.Connect());
+
+ Expect(api.Connected());
+ Expect(api.GetFd() >= 0);
+
+ // Disconnect
+ api.Disconnect();
+
+ Expect(!api.Connected());
+ Expect(api.GetFd() < 0);
+
+ // Auto connect
+ ExpectSuccess(api.GetVersion(str, str));
+ Expect(api.Connected());
+
+ // Auto reconnect
+ api.Disconnect();
+ ExpectSuccess(api.GetVersion(str, str));
+ Expect(api.Connected());
+
+ // No auto reconnect
+ api.Disconnect();
+ api.SetAutoReconnect(false);
+ ExpectEq(api.GetVersion(str, str), Porto::EError::SocketError);
+ api.SetAutoReconnect(true);
+
+ uint64_t val = api.GetTimeout();
+ ExpectNeq(val, 0);
+ ExpectSuccess(api.SetTimeout(5));
+
+ ExpectSuccess(api.List(list));
+
+ ExpectSuccess(api.ListProperties(list));
+
+ ExpectSuccess(api.ListVolumes(list));
+
+ ExpectSuccess(api.ListVolumeProperties(list));
+
+ ExpectSuccess(api.ListLayers(list));
+
+ ExpectSuccess(api.ListStorages(list));
+
+ ExpectSuccess(api.Call("Version {}", str));
+
+ ExpectSuccess(api.GetProperty("/", "state", str));
+ ExpectEq(str, "meta");
+
+ ExpectSuccess(api.GetProperty("/", "controllers", "memory", str));
+ ExpectEq(str, "true");
+
+ ExpectSuccess(api.GetProperty("/", "memory_usage", str));
+ ExpectNeq(str, "0");
+
+ val = 0;
+ ExpectSuccess(api.GetInt("/", "memory_usage", val));
+ ExpectNeq(val, 0);
+
+ Porto::TContainer ct;
+ ExpectSuccess(api.GetContainerSpec("/", ct));
+ ExpectEq(ct.spec().name(), "/");
+
+ ExpectEq(api.GetInt("/", "__wrong__", val), Porto::EError::InvalidProperty);
+ ExpectEq(api.Error(), Porto::EError::InvalidProperty);
+ ExpectEq(api.GetLastError(str), Porto::EError::InvalidProperty);
+
+ ExpectSuccess(api.GetContainerSpec(CT_NAME, ct));
+ ExpectEq(ct.error().error(), Porto::EError::ContainerDoesNotExist);
+
+ ExpectSuccess(api.CreateWeakContainer(CT_NAME));
+
+ ExpectSuccess(api.SetProperty(CT_NAME, "memory_limit", "20M"));
+ ExpectSuccess(api.GetProperty(CT_NAME, "memory_limit", str));
+ ExpectEq(str, "20971520");
+
+ ExpectSuccess(api.SetInt(CT_NAME, "memory_limit", 10<<20));
+ ExpectSuccess(api.GetInt(CT_NAME, "memory_limit", val));
+ ExpectEq(val, 10485760);
+
+ ExpectSuccess(api.SetLabel(CT_NAME, "TEST.a", "."));
+
+ ExpectSuccess(api.GetContainerSpec(CT_NAME, ct));
+ ExpectEq(ct.status().state(), "stopped");
+ ExpectEq(ct.spec().memory_limit(), 10 << 20);
+
+ ExpectSuccess(api.WaitContainer(CT_NAME, str));
+ ExpectEq(str, "stopped");
+
+ ExpectSuccess(api.CreateVolume(path, {
+ {"containers", CT_NAME},
+ {"backend", "native"},
+ {"space_limit", "1G"}}));
+ ExpectNeq(path, "");
+
+ [[maybe_unused]] auto vd = api.GetVolumeDesc(path);
+ Expect(vd != nullptr);
+ ExpectEq(vd->path(), path);
+
+ [[maybe_unused]] auto vs = api.GetVolume(path);
+ Expect(vs != nullptr);
+ ExpectEq(vs->path(), path);
+
+ ExpectSuccess(api.SetProperty(CT_NAME, "command", "sleep 1000"));
+ ExpectSuccess(api.Start(CT_NAME));
+
+ ExpectSuccess(api.GetProperty(CT_NAME, "state", str));
+ ExpectEq(str, "running");
+
+ ExpectSuccess(api.Destroy(CT_NAME));
+
+ TMap<TString, uint64_t> values;
+ auto CT_NAME_0 = CT_NAME + "abcd";
+ auto CT_NAME_CHILD = CT_NAME + "/b";
+
+ ExpectSuccess(api.Create(CT_NAME_0));
+ ExpectSuccess(api.SetProperty(CT_NAME_0, "command", "sleep 15"));
+ ExpectSuccess(api.Start(CT_NAME_0));
+
+ ExpectSuccess(api.Create(CT_NAME));
+ ExpectSuccess(api.SetProperty(CT_NAME, "command", "sleep 10"));
+ ExpectSuccess(api.GetProcMetric(TVector<TString>{CT_NAME, CT_NAME_0}, "ctxsw", values));
+ ExpectEq(values[CT_NAME], 0);
+ ExpectNeq(values[CT_NAME_0], 0);
+
+ ExpectSuccess(api.Start(CT_NAME));
+ ExpectSuccess(api.GetProcMetric(TVector<TString>{CT_NAME}, "ctxsw", values));
+ ExpectNeq(values[CT_NAME], 0);
+
+ ExpectSuccess(api.Create(CT_NAME_CHILD));
+ ExpectSuccess(api.SetProperty(CT_NAME_CHILD, "command", "sleep 10"));
+ ExpectSuccess(api.GetProcMetric(TVector<TString>{CT_NAME_CHILD}, "ctxsw", values));
+ ExpectEq(values[CT_NAME_CHILD], 0);
+
+ ExpectSuccess(api.Start(CT_NAME_CHILD));
+ ExpectSuccess(api.GetProcMetric(TVector<TString>{CT_NAME, CT_NAME_CHILD}, "ctxsw", values));
+ ExpectNeq(values[CT_NAME_CHILD], 0);
+ Expect(values[CT_NAME] > values[CT_NAME_CHILD]);
+
+ ExpectSuccess(api.Stop(CT_NAME_CHILD));
+ ExpectSuccess(api.GetProcMetric(TVector<TString>{CT_NAME_CHILD}, "ctxsw", values));
+ ExpectEq(values[CT_NAME_CHILD], 0);
+
+ ExpectSuccess(api.Stop(CT_NAME));
+ ExpectSuccess(api.GetProcMetric(TVector<TString>{CT_NAME}, "ctxsw", values));
+ ExpectEq(values[CT_NAME], 0);
+
+ ExpectSuccess(api.Destroy(CT_NAME_CHILD));
+ ExpectSuccess(api.Destroy(CT_NAME));
+ ExpectSuccess(api.Destroy(CT_NAME_0));
+
+#ifdef __linux__
+ // test TAsyncWaiter
+ Porto::TAsyncWaiter waiter([](const TString &error, int ret) {
+ Y_UNUSED(error);
+ Y_UNUSED(ret);
+
+ Expect(false);
+ });
+
+ TString result;
+ waiter.Add("abc", "starting", [&result](const Porto::TWaitResponse &event) {
+ result += event.name() + "-" + event.state();
+ });
+
+ TString name = "abcdef";
+ ExpectSuccess(api.Create(name));
+ ExpectSuccess(api.SetProperty(name, "command", "sleep 1"));
+ ExpectSuccess(api.Start(name));
+ ExpectSuccess(api.Destroy(name));
+ ExpectEq(result, "");
+
+ // callback work only once
+ for (int i = 0; i < 3; i++) {
+ name = "abc";
+ ExpectSuccess(api.Create(name));
+ ExpectSuccess(api.SetProperty(name, "command", "sleep 1"));
+ ExpectSuccess(api.Start(name));
+ ExpectSuccess(api.Destroy(name));
+ ExpectEq(result, "abc-starting");
+ }
+
+ waiter.Add("abc", "starting", [&result](const Porto::TWaitResponse &event) {
+ result += event.name() + "-" + event.state();
+ });
+ waiter.Remove("abc");
+
+ name = "abc";
+ ExpectSuccess(api.Create(name));
+ ExpectSuccess(api.SetProperty(name, "command", "sleep 1"));
+ ExpectSuccess(api.Start(name));
+ ExpectSuccess(api.Destroy(name));
+ ExpectEq(result, "abc-starting");
+#endif
+
+ api.Disconnect();
+}
+
+Y_UNIT_TEST_SUITE(Porto) {
+ Y_UNIT_TEST(All) {
+ test_porto();
+ }
+}
diff --git a/library/cpp/porto/metrics.cpp b/library/cpp/porto/metrics.cpp
new file mode 100644
index 0000000000..7d17d0aee4
--- /dev/null
+++ b/library/cpp/porto/metrics.cpp
@@ -0,0 +1,183 @@
+#include "metrics.hpp"
+
+#include <util/folder/path.h>
+#include <util/generic/maybe.h>
+#include <util/stream/file.h>
+
+namespace Porto {
+
+TMap<TString, TMetric*> ProcMetrics;
+
+TMetric::TMetric(const TString& name, EMetric metric) {
+ Name = name;
+ Metric = metric;
+ ProcMetrics[name] = this;
+}
+
+void TMetric::ClearValues(const TVector<TString>& names, TMap<TString, uint64_t>& values) const {
+ values.clear();
+
+ for (const auto&name : names)
+ values[name] = 0;
+}
+
+EError TMetric::GetValues(const TVector<TString>& names, TMap<TString, uint64_t>& values, TPortoApi& api) const {
+ ClearValues(names, values);
+
+ int procFd = open("/proc", O_RDONLY | O_CLOEXEC | O_DIRECTORY | O_NOCTTY);
+ TFileHandle procFdHandle(procFd);
+ if (procFd == -1)
+ return EError::Unknown;
+
+ TVector<TString> tids;
+ TidSnapshot(tids);
+
+ auto getResponse = api.Get(names, TVector<TString>{"cgroups[freezer]"});
+
+ if (getResponse == nullptr)
+ return EError::Unknown;
+
+ const auto containersCgroups = GetCtFreezerCgroups(getResponse);
+
+ for (const auto& tid : tids) {
+ const TString tidCgroup = GetFreezerCgroup(procFd, tid);
+ if (tidCgroup == "")
+ continue;
+
+ TMaybe<uint64_t> metricValue;
+
+ for (const auto& keyval : containersCgroups) {
+ const TString& containerCgroup = keyval.second;
+ if (MatchCgroups(tidCgroup, containerCgroup)) {
+ if (!metricValue)
+ metricValue = GetMetric(procFd, tid);
+ values[keyval.first] += *metricValue;
+ }
+ }
+ }
+
+ return EError::Success;
+}
+
+uint64_t TMetric::GetTidSchedMetricValue(int procFd, const TString& tid, const TString& metricName) const {
+ const TString schedPath = tid + "/sched";
+ try {
+ int fd = openat(procFd, schedPath.c_str(), O_RDONLY | O_CLOEXEC | O_NOCTTY, 0);
+ TFile file(fd);
+ if (!file.IsOpen())
+ return 0ul;
+
+ TIFStream iStream(file);
+ TString line;
+ while (iStream.ReadLine(line)) {
+ auto metricPos = line.find(metricName);
+
+ if (metricPos != TString::npos) {
+ auto valuePos = metricPos;
+
+ while (valuePos < line.size() && !::isdigit(line[valuePos]))
+ ++valuePos;
+
+ TString value = line.substr(valuePos);
+ if (!value.empty() && IsNumber(value))
+ return IntFromString<uint64_t, 10>(value);
+ }
+ }
+ }
+ catch(...) {}
+
+ return 0ul;
+}
+
+void TMetric::GetPidTasks(const TString& pid, TVector<TString>& tids) const {
+ TFsPath task("/proc/" + pid + "/task");
+ TVector<TString> rawTids;
+
+ try {
+ task.ListNames(rawTids);
+ }
+ catch(...) {}
+
+ for (const auto& tid : rawTids) {
+ tids.push_back(tid);
+ }
+}
+
+void TMetric::TidSnapshot(TVector<TString>& tids) const {
+ TFsPath proc("/proc");
+ TVector<TString> rawPids;
+
+ try {
+ proc.ListNames(rawPids);
+ }
+ catch(...) {}
+
+ for (const auto& pid : rawPids) {
+ if (IsNumber(pid))
+ GetPidTasks(pid, tids);
+ }
+}
+
+TString TMetric::GetFreezerCgroup(int procFd, const TString& tid) const {
+ const TString cgroupPath = tid + "/cgroup";
+ try {
+ int fd = openat(procFd, cgroupPath.c_str(), O_RDONLY | O_CLOEXEC | O_NOCTTY, 0);
+ TFile file(fd);
+ if (!file.IsOpen())
+ return TString();
+
+ TIFStream iStream(file);
+ TString line;
+
+ while (iStream.ReadLine(line)) {
+ static const TString freezer = ":freezer:";
+ auto freezerPos = line.find(freezer);
+
+ if (freezerPos != TString::npos) {
+ line = line.substr(freezerPos + freezer.size());
+ return line;
+ }
+ }
+ }
+ catch(...){}
+
+ return TString();
+}
+
+TMap<TString, TString> TMetric::GetCtFreezerCgroups(const TGetResponse* response) const {
+ TMap<TString, TString> containersProps;
+
+ for (const auto& ctGetListResponse : response->list()) {
+ for (const auto& keyval : ctGetListResponse.keyval()) {
+ if (!keyval.error()) {
+ TString value = keyval.value();
+ static const TString freezerPath = "/sys/fs/cgroup/freezer";
+
+ if (value.find(freezerPath) != TString::npos)
+ value = value.substr(freezerPath.size());
+
+ containersProps[ctGetListResponse.name()] = value;
+ }
+ }
+ }
+
+ return containersProps;
+}
+
+bool TMetric::MatchCgroups(const TString& tidCgroup, const TString& ctCgroup) const {
+ if (tidCgroup.size() <= ctCgroup.size())
+ return tidCgroup == ctCgroup;
+ return ctCgroup == tidCgroup.substr(0, ctCgroup.size()) && tidCgroup[ctCgroup.size()] == '/';
+}
+
+class TCtxsw : public TMetric {
+public:
+ TCtxsw() : TMetric(M_CTXSW, EMetric::CTXSW)
+ {}
+
+ uint64_t GetMetric(int procFd, const TString& tid) const override {
+ return GetTidSchedMetricValue(procFd, tid, "nr_switches");
+ }
+} static Ctxsw;
+
+} /* namespace Porto */
diff --git a/library/cpp/porto/metrics.hpp b/library/cpp/porto/metrics.hpp
new file mode 100644
index 0000000000..5b2ffde8d9
--- /dev/null
+++ b/library/cpp/porto/metrics.hpp
@@ -0,0 +1,50 @@
+#pragma once
+
+#include "libporto.hpp"
+
+#include <util/generic/map.h>
+#include <util/generic/vector.h>
+#include <util/string/cast.h>
+#include <util/string/type.h>
+
+#include <library/cpp/porto/proto/rpc.pb.h>
+namespace Porto {
+
+constexpr const char *M_CTXSW = "ctxsw";
+
+enum class EMetric {
+ NONE,
+ CTXSW,
+};
+
+class TMetric {
+public:
+ TString Name;
+ EMetric Metric;
+
+ TMetric(const TString& name, EMetric metric);
+
+ void ClearValues(const TVector<TString>& names, TMap<TString, uint64_t>& values) const;
+ EError GetValues(const TVector<TString>& names, TMap<TString, uint64_t>& values, TPortoApi& api) const;
+
+ // Returns value of metric from /proc/tid/sched for some tid
+ uint64_t GetTidSchedMetricValue(int procFd, const TString& tid, const TString& metricName) const;
+
+ void TidSnapshot(TVector<TString>& tids) const;
+ void GetPidTasks(const TString& pid, TVector<TString>& tids) const;
+
+ // Returns freezer cgroup from /proc/tid/cgroup
+ TString GetFreezerCgroup(int procFd, const TString& tid) const;
+
+ // Resurns clean cgroup[freezer] for containers names
+ TMap<TString, TString> GetCtFreezerCgroups(const TGetResponse* response) const;
+
+ // Verify inclusion of container cgroup in process cgroup
+ bool MatchCgroups(const TString& tidCgroup, const TString& ctCgroup) const;
+
+private:
+ virtual uint64_t GetMetric(int procFd, const TString& tid) const = 0;
+};
+
+extern TMap<TString, TMetric*> ProcMetrics;
+} /* namespace Porto */
diff --git a/library/cpp/porto/proto/CMakeLists.darwin-x86_64.txt b/library/cpp/porto/proto/CMakeLists.darwin-x86_64.txt
new file mode 100644
index 0000000000..9b8be22fe6
--- /dev/null
+++ b/library/cpp/porto/proto/CMakeLists.darwin-x86_64.txt
@@ -0,0 +1,43 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+get_built_tool_path(
+ TOOL_protoc_bin
+ TOOL_protoc_dependency
+ contrib/tools/protoc/bin
+ protoc
+)
+get_built_tool_path(
+ TOOL_cpp_styleguide_bin
+ TOOL_cpp_styleguide_dependency
+ contrib/tools/protoc/plugins/cpp_styleguide
+ cpp_styleguide
+)
+
+add_library(cpp-porto-proto)
+target_link_libraries(cpp-porto-proto PUBLIC
+ contrib-libs-cxxsupp
+ yutil
+ contrib-libs-protobuf
+)
+target_proto_messages(cpp-porto-proto PRIVATE
+ ${CMAKE_SOURCE_DIR}/library/cpp/porto/proto/rpc.proto
+)
+target_proto_addincls(cpp-porto-proto
+ ./
+ ${CMAKE_SOURCE_DIR}/
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}
+ ${CMAKE_SOURCE_DIR}/contrib/libs/protobuf/src
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/contrib/libs/protobuf/src
+)
+target_proto_outs(cpp-porto-proto
+ --cpp_out=${CMAKE_BINARY_DIR}/
+ --cpp_styleguide_out=${CMAKE_BINARY_DIR}/
+)
diff --git a/library/cpp/porto/proto/CMakeLists.linux-aarch64.txt b/library/cpp/porto/proto/CMakeLists.linux-aarch64.txt
new file mode 100644
index 0000000000..ba0aa7060d
--- /dev/null
+++ b/library/cpp/porto/proto/CMakeLists.linux-aarch64.txt
@@ -0,0 +1,44 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+get_built_tool_path(
+ TOOL_protoc_bin
+ TOOL_protoc_dependency
+ contrib/tools/protoc/bin
+ protoc
+)
+get_built_tool_path(
+ TOOL_cpp_styleguide_bin
+ TOOL_cpp_styleguide_dependency
+ contrib/tools/protoc/plugins/cpp_styleguide
+ cpp_styleguide
+)
+
+add_library(cpp-porto-proto)
+target_link_libraries(cpp-porto-proto PUBLIC
+ contrib-libs-linux-headers
+ contrib-libs-cxxsupp
+ yutil
+ contrib-libs-protobuf
+)
+target_proto_messages(cpp-porto-proto PRIVATE
+ ${CMAKE_SOURCE_DIR}/library/cpp/porto/proto/rpc.proto
+)
+target_proto_addincls(cpp-porto-proto
+ ./
+ ${CMAKE_SOURCE_DIR}/
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}
+ ${CMAKE_SOURCE_DIR}/contrib/libs/protobuf/src
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/contrib/libs/protobuf/src
+)
+target_proto_outs(cpp-porto-proto
+ --cpp_out=${CMAKE_BINARY_DIR}/
+ --cpp_styleguide_out=${CMAKE_BINARY_DIR}/
+)
diff --git a/library/cpp/porto/proto/CMakeLists.linux-x86_64.txt b/library/cpp/porto/proto/CMakeLists.linux-x86_64.txt
new file mode 100644
index 0000000000..ba0aa7060d
--- /dev/null
+++ b/library/cpp/porto/proto/CMakeLists.linux-x86_64.txt
@@ -0,0 +1,44 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+get_built_tool_path(
+ TOOL_protoc_bin
+ TOOL_protoc_dependency
+ contrib/tools/protoc/bin
+ protoc
+)
+get_built_tool_path(
+ TOOL_cpp_styleguide_bin
+ TOOL_cpp_styleguide_dependency
+ contrib/tools/protoc/plugins/cpp_styleguide
+ cpp_styleguide
+)
+
+add_library(cpp-porto-proto)
+target_link_libraries(cpp-porto-proto PUBLIC
+ contrib-libs-linux-headers
+ contrib-libs-cxxsupp
+ yutil
+ contrib-libs-protobuf
+)
+target_proto_messages(cpp-porto-proto PRIVATE
+ ${CMAKE_SOURCE_DIR}/library/cpp/porto/proto/rpc.proto
+)
+target_proto_addincls(cpp-porto-proto
+ ./
+ ${CMAKE_SOURCE_DIR}/
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}
+ ${CMAKE_SOURCE_DIR}/contrib/libs/protobuf/src
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/contrib/libs/protobuf/src
+)
+target_proto_outs(cpp-porto-proto
+ --cpp_out=${CMAKE_BINARY_DIR}/
+ --cpp_styleguide_out=${CMAKE_BINARY_DIR}/
+)
diff --git a/library/cpp/porto/proto/CMakeLists.txt b/library/cpp/porto/proto/CMakeLists.txt
new file mode 100644
index 0000000000..f8b31df0c1
--- /dev/null
+++ b/library/cpp/porto/proto/CMakeLists.txt
@@ -0,0 +1,17 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" AND NOT HAVE_CUDA)
+ include(CMakeLists.linux-aarch64.txt)
+elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
+ include(CMakeLists.darwin-x86_64.txt)
+elseif (WIN32 AND CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" AND NOT HAVE_CUDA)
+ include(CMakeLists.windows-x86_64.txt)
+elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND NOT HAVE_CUDA)
+ include(CMakeLists.linux-x86_64.txt)
+endif()
diff --git a/library/cpp/porto/proto/CMakeLists.windows-x86_64.txt b/library/cpp/porto/proto/CMakeLists.windows-x86_64.txt
new file mode 100644
index 0000000000..9b8be22fe6
--- /dev/null
+++ b/library/cpp/porto/proto/CMakeLists.windows-x86_64.txt
@@ -0,0 +1,43 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+get_built_tool_path(
+ TOOL_protoc_bin
+ TOOL_protoc_dependency
+ contrib/tools/protoc/bin
+ protoc
+)
+get_built_tool_path(
+ TOOL_cpp_styleguide_bin
+ TOOL_cpp_styleguide_dependency
+ contrib/tools/protoc/plugins/cpp_styleguide
+ cpp_styleguide
+)
+
+add_library(cpp-porto-proto)
+target_link_libraries(cpp-porto-proto PUBLIC
+ contrib-libs-cxxsupp
+ yutil
+ contrib-libs-protobuf
+)
+target_proto_messages(cpp-porto-proto PRIVATE
+ ${CMAKE_SOURCE_DIR}/library/cpp/porto/proto/rpc.proto
+)
+target_proto_addincls(cpp-porto-proto
+ ./
+ ${CMAKE_SOURCE_DIR}/
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}
+ ${CMAKE_SOURCE_DIR}/contrib/libs/protobuf/src
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/contrib/libs/protobuf/src
+)
+target_proto_outs(cpp-porto-proto
+ --cpp_out=${CMAKE_BINARY_DIR}/
+ --cpp_styleguide_out=${CMAKE_BINARY_DIR}/
+)
diff --git a/library/cpp/porto/proto/rpc.proto b/library/cpp/porto/proto/rpc.proto
new file mode 100644
index 0000000000..abb8c63905
--- /dev/null
+++ b/library/cpp/porto/proto/rpc.proto
@@ -0,0 +1,1607 @@
+syntax = "proto2";
+
+option go_package = "github.com/ydb-platform/ydb/library/cpp/porto/proto;myapi";
+
+/*
+ Portod daemon listens on /run/portod.socket unix socket.
+
+ Request: Varint32 length, TPortoRequest request
+ Response: Varint32 length, TPortoResponse response
+
+ Command is defined by optional nested message field.
+ Result will be in nested message with the same name.
+
+ Push notification is send as out of order response.
+
+ Access level depends on client container and uid.
+
+ See defails in porto.md or manpage porto
+
+ TContainer, TVolume and related methods are Porto v5 API.
+*/
+
+package Porto;
+
+// List of error codes
+enum EError {
+ // No errors occured.
+ Success = 0;
+
+ // Unclassified error, usually unexpected syscall fail.
+ Unknown = 1;
+
+ // Unknown method or bad request.
+ InvalidMethod = 2;
+
+ // Container with specified name already exists.
+ ContainerAlreadyExists = 3;
+
+ // Container with specified name doesn't exist.
+ ContainerDoesNotExist = 4;
+
+ // Unknown property specified.
+ InvalidProperty = 5;
+
+ // Unknown data specified.
+ InvalidData = 6;
+
+ // Invalid value of property or data.
+ InvalidValue = 7;
+
+ // Can't perform specified operation in current container state.
+ InvalidState = 8;
+
+ // Permanent faulure: old kernel version, missing feature, configuration, etc.
+ NotSupported = 9;
+
+ // Temporary failure: too much objects, not enough memory, etc.
+ ResourceNotAvailable = 10;
+
+ // Insufficient rights for performing requested operation.
+ Permission = 11;
+
+ // Can't create new volume with specified name, because there is already one.
+ VolumeAlreadyExists = 12;
+
+ // Volume with specified name doesn't exist.
+ VolumeNotFound = 13;
+
+ // Not enough disk space.
+ NoSpace = 14;
+
+ // Object in use.
+ Busy = 15;
+
+ // Volume already linked with container.
+ VolumeAlreadyLinked = 16;
+
+ // Volume not linked with container.
+ VolumeNotLinked = 17;
+
+ // Layer with this name already exists.
+ LayerAlreadyExists = 18;
+
+ // Layer with this name not found.
+ LayerNotFound = 19;
+
+ // Property has no value, data source permanently not available.
+ NoValue = 20;
+
+ // Volume under construction or destruction.
+ VolumeNotReady = 21;
+
+ // Cannot parse or execute command.
+ InvalidCommand = 22;
+
+ // Error code is lost or came from future.
+ LostError = 23;
+
+ // Device node not found.
+ DeviceNotFound = 24;
+
+ // Path does not match restricitons or does not exist.
+ InvalidPath = 25;
+
+ // Wrong or unuseable ip address.
+ InvalidNetworkAddress = 26;
+
+ // Porto in system maintenance state.
+ PortoFrozen = 27;
+
+ // Label with this name is not set.
+ LabelNotFound = 28;
+
+ // Label name does not meet restrictions.
+ InvalidLabel = 29;
+
+ // Errors in tar, on archive extraction
+ HelperError = 30;
+ HelperFatalError = 31;
+
+ // Generic object not found.
+ NotFound = 404;
+
+ // Reserved error code for client library.
+ SocketError = 502;
+
+ // Reserved error code for client library.
+ SocketUnavailable = 503;
+
+ // Reserved error code for client library.
+ SocketTimeout = 504;
+
+ // Portod close client connections on reload
+ PortodReloaded = 505;
+
+ // Reserved error code for taints.
+ Taint = 666;
+
+ // Reserved error codes 700-800 to docker
+ Docker = 700;
+ DockerImageNotFound = 701;
+
+ // Internal error code, not for users.
+ Queued = 1000;
+}
+
+
+message TPortoRequest {
+
+ /* System methods */
+
+ // Get portod version
+ optional TVersionRequest Version = 14;
+
+ // Get portod statistics
+ optional TGetSystemRequest GetSystem = 300;
+
+ // Change portod state (for host root user only)
+ optional TSetSystemRequest SetSystem = 301;
+
+ /* Container methods */
+
+ // Create new container
+ optional TCreateRequest Create = 1;
+
+ // Create new contaienr and auto destroy when client disconnects
+ optional TCreateRequest CreateWeak = 17;
+
+ // Force kill all and destroy container and nested containers
+ optional TDestroyRequest Destroy = 2;
+
+ // List container names in current namespace
+ optional TListRequest List = 3;
+
+ // Start contianer and parents if needed
+ optional TStartRequest Start = 7;
+
+ // Kill all and stop container
+ optional TStopRequest Stop = 8;
+
+ // Freeze execution
+ optional TPauseRequest Pause = 9;
+
+ // Resume execution
+ optional TResumeRequest Resume = 10;
+
+ // Send signal to main process
+ optional TKillRequest Kill = 13;
+
+ // Restart dead container
+ optional TRespawnRequest Respawn = 18;
+
+ // Wait for process finish or change of labels
+ optional TWaitRequest Wait = 16;
+
+ // Subscribe to push notifictaions
+ optional TWaitRequest AsyncWait = 19;
+ optional TWaitRequest StopAsyncWait = 128;
+
+ /* Container properties */
+
+ // List supported container properties
+ optional TListPropertiesRequest ListProperties = 11;
+
+ // Get one property
+ optional TGetPropertyRequest GetProperty = 4;
+
+ // Set one property
+ optional TSetPropertyRequest SetProperty = 5;
+
+ // Deprecated, now data properties are also read-only properties
+ optional TListDataPropertiesRequest ListDataProperties = 12;
+ optional TGetDataPropertyRequest GetDataProperty = 6;
+
+ // Get multiple properties for multiple containers
+ optional TGetRequest Get = 15;
+
+ /* Container API based on TContainer (Porto v5 API) */
+
+ // Create, configure and start container with volumes
+ optional TCreateFromSpecRequest CreateFromSpec = 230;
+
+ // Set multiple container properties
+ optional TUpdateFromSpecRequest UpdateFromSpec = 231;
+
+ // Get multiple properties for multiple containers
+ optional TListContainersRequest ListContainersBy = 232;
+
+ // Modify symlink in container
+ optional TSetSymlinkRequest SetSymlink = 125;
+
+ /* Container labels - user defined key-value */
+
+ // Find containers with labels
+ optional TFindLabelRequest FindLabel = 20;
+
+ // Atomic compare and set for label
+ optional TSetLabelRequest SetLabel = 21;
+
+ // Atomic add and return for counter in label
+ optional TIncLabelRequest IncLabel = 22;
+
+ /* Volume methods */
+
+ optional TListVolumePropertiesRequest ListVolumeProperties = 103;
+
+ // List layers and their properties
+ optional TListVolumesRequest ListVolumes = 107;
+
+ // Create, configure and build volume
+ optional TCreateVolumeRequest CreateVolume = 104;
+
+ // Change volume properties - for now only resize
+ optional TTuneVolumeRequest TuneVolume = 108;
+
+ // Volume API based on TVolume (Porto v5 API)
+ optional TNewVolumeRequest NewVolume = 126;
+ optional TGetVolumeRequest GetVolume = 127;
+
+ // Add link between container and volume
+ optional TLinkVolumeRequest LinkVolume = 105;
+
+ // Same as LinkVolume but fails if target is not supported
+ optional TLinkVolumeRequest LinkVolumeTarget = 120;
+
+ // Del link between container and volume
+ optional TUnlinkVolumeRequest UnlinkVolume = 106;
+
+ // Same as UnlinkVolume but fails if target is not supported
+ optional TUnlinkVolumeRequest UnlinkVolumeTarget = 121;
+
+ /* Layer methods */
+
+ // Import layer from tarball
+ optional TImportLayerRequest ImportLayer = 110;
+
+ // Remove layer
+ optional TRemoveLayerRequest RemoveLayer = 111;
+
+ // List layers
+ optional TListLayersRequest ListLayers = 112;
+
+ // Export volume or layer into tarball
+ optional TExportLayerRequest ExportLayer = 113;
+
+ // Get/set layer private (user defined string)
+ optional TGetLayerPrivateRequest GetLayerPrivate = 114;
+ optional TSetLayerPrivateRequest SetLayerPrivate = 115;
+
+ /* Storage methods */
+
+ // Volume creation creates required storage if missing
+
+ // List storages and meta storages
+ optional TListStoragesRequest ListStorages = 116;
+
+ optional TRemoveStorageRequest RemoveStorage = 117;
+
+ // Import storage from tarball
+ optional TImportStorageRequest ImportStorage = 118;
+
+ // Export storage into tarball
+ optional TExportStorageRequest ExportStorage = 119;
+
+ // Meta storage (bundle for storages and layers)
+
+ optional TMetaStorage CreateMetaStorage = 122;
+ optional TMetaStorage ResizeMetaStorage = 123;
+ optional TMetaStorage RemoveMetaStorage = 124;
+
+ // Convert path between containers
+ optional TConvertPathRequest ConvertPath = 200;
+
+ /* Process methods */
+
+ // Attach process to nested container
+ optional TAttachProcessRequest AttachProcess = 201;
+
+ // Find container for process
+ optional TLocateProcessRequest LocateProcess = 202;
+
+ // Attach one thread to nexted container
+ optional TAttachProcessRequest AttachThread = 203;
+
+ /* Docker images API */
+
+ optional TDockerImageStatusRequest dockerImageStatus = 303;
+ optional TDockerImageListRequest listDockerImages = 304;
+ optional TDockerImagePullRequest pullDockerImage = 305;
+ optional TDockerImageRemoveRequest removeDockerImage = 306;
+}
+
+
+message TPortoResponse {
+ // Actually always set, hack for adding new error codes
+ optional EError error = 1 [ default = LostError ];
+
+ // Human readable comment - must be shown to user as is
+ optional string errorMsg = 2;
+
+ optional uint64 timestamp = 1000; // for next changed_since
+
+ /* System methods */
+
+ optional TVersionResponse Version = 8;
+
+ optional TGetSystemResponse GetSystem = 300;
+ optional TSetSystemResponse SetSystem = 301;
+
+ /* Container methods */
+
+ optional TListResponse List = 3;
+
+ optional TWaitResponse Wait = 11;
+
+ optional TWaitResponse AsyncWait = 19;
+
+ /* Container properties */
+
+ optional TListPropertiesResponse ListProperties = 6;
+
+ optional TGetPropertyResponse GetProperty = 4;
+
+
+ // Deprecated
+ optional TListDataPropertiesResponse ListDataProperties = 7;
+ optional TGetDataPropertyResponse GetDataProperty = 5;
+
+ optional TGetResponse Get = 10;
+
+ /* Container API based on TContainer (Porto v5 API) */
+
+ optional TListContainersResponse ListContainersBy = 232;
+
+ /* Container Labels */
+
+ optional TFindLabelResponse FindLabel = 20;
+ optional TSetLabelResponse SetLabel = 21;
+ optional TIncLabelResponse IncLabel = 22;
+
+ /* Volume methods */
+
+ optional TListVolumePropertiesResponse ListVolumeProperties = 12;
+
+ optional TListVolumesResponse ListVolumes = 9;
+
+ optional TVolumeDescription CreateVolume = 13;
+
+ optional TNewVolumeResponse NewVolume = 126;
+
+ optional TGetVolumeResponse GetVolume = 127;
+
+ optional TListLayersResponse ListLayers = 14;
+
+ optional TGetLayerPrivateResponse GetLayerPrivate = 16;
+
+ // List storages and meta storages
+ optional TListStoragesResponse ListStorages = 17;
+
+ optional TConvertPathResponse ConvertPath = 15;
+
+ // Process
+ optional TLocateProcessResponse LocateProcess = 18;
+
+ /* Docker images API */
+
+ optional TDockerImageStatusResponse dockerImageStatus = 302;
+ optional TDockerImageListResponse listDockerImages = 303;
+ optional TDockerImagePullResponse pullDockerImage = 304;
+}
+
+
+// Common objects
+
+
+message TStringMap {
+ message TStringMapEntry {
+ optional string key = 1;
+ optional string val = 2;
+ }
+ // TODO replace with map
+ // map<string, string> map = 1;
+ repeated TStringMapEntry map = 1;
+ optional bool merge = 2; // in, default: replace
+}
+
+
+message TUintMap {
+ message TUintMapEntry {
+ optional string key = 1;
+ optional uint64 val = 2;
+ }
+ // TODO replace with map
+ // map<string, uint64> map = 1;
+ repeated TUintMapEntry map = 1;
+ optional bool merge = 2; // in, default: replace
+}
+
+
+message TError {
+ optional EError error = 1 [ default = LostError ];
+ optional string msg = 2;
+}
+
+
+message TCred {
+ optional string user = 1; // requires user or uid or both
+ optional fixed32 uid = 2;
+ optional string group = 3;
+ optional fixed32 gid = 4;
+ repeated fixed32 grp = 5; // out, supplementary groups
+}
+
+
+message TCapabilities {
+ repeated string cap = 1;
+ optional string hex = 2; // out
+}
+
+
+message TContainerCommandArgv {
+ repeated string argv = 1;
+}
+
+
+// Container
+
+
+message TContainerEnvVar {
+ optional string name = 1; //required
+ optional string value = 2;
+ optional bool unset = 3; // out
+ optional string salt = 4;
+ optional string hash = 5;
+}
+
+message TContainerEnv {
+ repeated TContainerEnvVar var = 1;
+ optional bool merge = 2; // in, default: replace
+}
+
+
+message TContainerUlimit {
+ optional string type = 1; //required
+ optional bool unlimited = 2;
+ optional uint64 soft = 3;
+ optional uint64 hard = 4;
+ optional bool inherited = 5; // out
+}
+
+message TContainerUlimits {
+ repeated TContainerUlimit ulimit = 1;
+ optional bool merge = 2; // in, default: replace
+}
+
+
+message TContainerControllers {
+ repeated string controller = 1;
+}
+
+
+message TContainerCgroup {
+ optional string controller = 1; //required
+ optional string path = 2; //required
+ optional bool inherited = 3;
+}
+
+message TContainerCgroups {
+ repeated TContainerCgroup cgroup = 1;
+}
+
+
+message TContainerCpuSet {
+ optional string policy = 1; // inherit|set|node|reserve|threads|cores
+ optional uint32 arg = 2; // for node|reserve|threads|cores
+ optional string list = 3; // for set
+ repeated uint32 cpu = 4; // for set (used if list isn't set)
+ optional uint32 count = 5; // out
+ optional string mems = 6;
+}
+
+
+message TContainerBindMount {
+ optional string source = 1; //required
+ optional string target = 2; //required
+ repeated string flag = 3;
+}
+
+message TContainerBindMounts {
+ repeated TContainerBindMount bind = 1;
+}
+
+
+message TContainerVolumeLink {
+ optional string volume = 1; //required
+ optional string target = 2;
+ optional bool required = 3;
+ optional bool read_only = 4;
+}
+
+message TContainerVolumeLinks {
+ repeated TContainerVolumeLink link = 1;
+}
+
+
+message TContainerVolumes {
+ repeated string volume = 1;
+}
+
+
+message TContainerPlace {
+ optional string place = 1; //required
+ optional string alias = 2;
+}
+
+message TContainerPlaceConfig {
+ repeated TContainerPlace cfg = 1;
+}
+
+
+message TContainerDevice {
+ optional string device = 1; //required
+ optional string access = 2; //required
+ optional string path = 3;
+ optional string mode = 4;
+ optional string user = 5;
+ optional string group = 6;
+}
+
+message TContainerDevices {
+ repeated TContainerDevice device = 1;
+ optional bool merge = 2; // in, default: replace
+}
+
+
+message TContainerNetOption {
+ optional string opt = 1; //required
+ repeated string arg = 2;
+}
+
+message TContainerNetConfig {
+ repeated TContainerNetOption cfg = 1;
+ optional bool inherited = 2; // out
+}
+
+
+message TContainerIpLimit {
+ optional string policy = 1; //required any|none|some
+ repeated string ip = 2;
+}
+
+
+message TContainerIpConfig {
+ message TContainerIp {
+ optional string dev = 1; //required
+ optional string ip = 2; //required
+ }
+ repeated TContainerIp cfg = 1;
+}
+
+
+message TVmStat {
+ optional uint64 count = 1;
+ optional uint64 size = 2;
+ optional uint64 max_size = 3;
+ optional uint64 used = 4;
+ optional uint64 max_used = 5;
+ optional uint64 anon = 6;
+ optional uint64 file = 7;
+ optional uint64 shmem = 8;
+ optional uint64 huge = 9;
+ optional uint64 swap = 10;
+ optional uint64 data = 11;
+ optional uint64 stack = 12;
+ optional uint64 code = 13;
+ optional uint64 locked = 14;
+ optional uint64 table = 15;
+}
+
+message TContainerStatus {
+ optional string absolute_name = 1; // out, "/porto/..."
+ optional string state = 2; // out
+ optional uint64 id = 3; // out
+ optional uint32 level = 4; // out
+ optional string parent = 5; // out, "/porto/..."
+
+ optional string absolute_namespace = 6; // out
+
+ optional int32 root_pid = 7; // out
+ optional int32 exit_status = 8; // out
+ optional int32 exit_code = 9; // out
+ optional bool core_dumped = 10; // out
+ optional TError start_error = 11; // out
+ optional uint64 time = 12; // out
+ optional uint64 dead_time = 13; // out
+
+ optional TCapabilities capabilities_allowed = 14; // out
+ optional TCapabilities capabilities_ambient_allowed = 15; // out
+ optional string root_path = 16; // out, in client namespace
+ optional uint64 stdout_offset = 17; // out
+ optional uint64 stderr_offset = 18; // out
+ optional string std_err = 69; // out
+ optional string std_out = 70; // out
+
+ optional uint64 creation_time = 19; // out
+ optional uint64 start_time = 20; // out
+ optional uint64 death_time = 21; // out
+ optional uint64 change_time = 22; // out
+ optional bool no_changes = 23; // out, change_time < changed_since
+ optional string extra_properties = 73;
+
+ optional TContainerCgroups cgroups = 24; // out
+ optional TContainerCpuSet cpu_set_affinity = 25; // out
+
+ optional uint64 cpu_usage = 26; // out
+ optional uint64 cpu_usage_system = 27; // out
+ optional uint64 cpu_wait = 28; // out
+ optional uint64 cpu_throttled = 29; // out
+
+ optional uint64 process_count = 30; // out
+ optional uint64 thread_count = 31; // out
+
+ optional TUintMap io_read = 32; // out, bytes
+ optional TUintMap io_write = 33; // out, bytes
+ optional TUintMap io_ops = 34; // out, ops
+ optional TUintMap io_read_ops = 341; // out, ops
+ optional TUintMap io_write_ops = 342; // out, ops
+ optional TUintMap io_time = 35; // out, ns
+ optional TUintMap io_pressure = 351; // out
+
+ optional TUintMap place_usage = 36;
+ optional uint64 memory_usage = 37; // out, bytes
+
+ optional uint64 memory_guarantee_total = 38; // out
+
+ optional uint64 memory_limit_total = 39; // out
+
+ optional uint64 anon_limit_total = 40;
+ optional uint64 anon_usage = 41; // out, bytes
+ optional double cpu_guarantee_total = 42;
+ optional double cpu_guarantee_bound = 421;
+ optional double cpu_limit_total = 422;
+ optional double cpu_limit_bound = 423;
+
+ optional uint64 cache_usage = 43; // out, bytes
+
+ optional uint64 hugetlb_usage = 44; // out, bytes
+ optional uint64 hugetlb_limit = 45;
+
+ optional uint64 minor_faults = 46; // out
+ optional uint64 major_faults = 47; // out
+ optional uint64 memory_reclaimed = 48; // out
+ optional TVmStat virtual_memory = 49; // out
+
+ optional uint64 shmem_usage = 71; // out, bytes
+ optional uint64 mlock_usage = 72; // out, bytes
+
+ optional uint64 oom_kills = 50; // out
+ optional uint64 oom_kills_total = 51; // out
+ optional bool oom_killed = 52; // out
+
+ optional TUintMap net_bytes = 54; // out
+ optional TUintMap net_packets = 55; // out
+ optional TUintMap net_drops = 56; // out
+ optional TUintMap net_overlimits = 57; // out
+ optional TUintMap net_rx_bytes = 58; // out
+ optional TUintMap net_rx_packets = 59; // out
+ optional TUintMap net_rx_drops = 60; // out
+ optional TUintMap net_tx_bytes = 61; // out
+ optional TUintMap net_tx_packets = 62; // out
+ optional TUintMap net_tx_drops = 63; // out
+
+ optional TContainerVolumeLinks volumes_linked = 64; // out
+ optional TContainerVolumes volumes_owned = 65;
+
+ repeated TError error = 66; // out
+ repeated TError warning = 67; // out
+ repeated TError taint = 68; // out
+}
+
+message TContainerSpec {
+ optional string name = 1; // required / in client namespace
+ optional bool weak = 2;
+ optional string private = 3;
+ optional TStringMap labels = 4;
+
+ optional string command = 5;
+ optional TContainerCommandArgv command_argv = 76;
+ optional TContainerEnv env = 6;
+ optional TContainerEnv env_secret = 90; // in, out hides values
+ optional TContainerUlimits ulimit = 7;
+ optional string core_command = 8;
+
+ optional bool isolate = 9;
+ optional string virt_mode = 10;
+ optional string enable_porto = 11;
+ optional string porto_namespace = 12;
+ optional string cgroupfs = 78;
+ optional bool userns = 79;
+
+ optional uint64 aging_time = 13;
+
+ optional TCred task_cred = 14;
+ optional string user = 15;
+ optional string group = 16;
+
+ optional TCred owner_cred = 17;
+ optional string owner_user = 18;
+ optional string owner_group = 19;
+ optional string owner_containers = 77;
+
+ optional TCapabilities capabilities = 20;
+ optional TCapabilities capabilities_ambient = 21;
+
+ optional string root = 22; // in parent namespace
+ optional bool root_readonly = 23;
+ optional TContainerBindMounts bind = 24;
+ optional TStringMap symlink = 25;
+ optional TContainerDevices devices = 26;
+ optional TContainerPlaceConfig place = 27;
+ optional TUintMap place_limit = 28;
+
+ optional string cwd = 29;
+ optional string stdin_path = 30;
+ optional string stdout_path = 31;
+ optional string stderr_path = 32;
+ optional uint64 stdout_limit = 33;
+ optional uint32 umask = 34;
+
+ optional bool respawn = 35;
+ optional uint64 respawn_count = 36;
+ optional int64 max_respawns = 37;
+ optional uint64 respawn_delay = 38;
+
+ optional TContainerControllers controllers = 39;
+
+ optional string cpu_policy = 40; // normal|idle|batch|high|rt
+ optional double cpu_weight = 41; // 0.01 .. 100
+
+ optional double cpu_guarantee = 42; // in cores
+ optional double cpu_limit = 43; // in cores
+ optional double cpu_limit_total = 44; // deprecated (value moved to TContainerStatus)
+ optional uint64 cpu_period = 45; // ns
+
+ optional TContainerCpuSet cpu_set = 46;
+
+ optional uint64 thread_limit = 47;
+
+ optional string io_policy = 48; // none|rt|high|normal|batch|idle
+ optional double io_weight = 49; // 0.01 .. 100
+
+ optional TUintMap io_limit = 50; // bps
+ optional TUintMap io_guarantee = 84; // bps
+ optional TUintMap io_ops_limit = 51; // iops
+ optional TUintMap io_ops_guarantee = 85; // iops
+
+ optional uint64 memory_guarantee = 52; // bytes
+
+ optional uint64 memory_limit = 53; // bytes
+
+ optional uint64 anon_limit = 54;
+ optional uint64 anon_max_usage = 55;
+
+ optional uint64 dirty_limit = 56;
+
+ optional uint64 hugetlb_limit = 57;
+
+ optional bool recharge_on_pgfault = 58;
+ optional bool pressurize_on_death = 59;
+ optional bool anon_only = 60;
+
+ optional int32 oom_score_adj = 61; // -1000 .. +1000
+ optional bool oom_is_fatal = 62;
+
+ optional TContainerNetConfig net = 63;
+ optional TContainerIpLimit ip_limit = 64;
+ optional TContainerIpConfig ip = 65;
+ optional TContainerIpConfig default_gw = 66;
+ optional string hostname = 67;
+ optional string resolv_conf = 68;
+ optional string etc_hosts = 69;
+ optional TStringMap sysctl = 70;
+ optional TUintMap net_guarantee = 71; // bytes per second
+ optional TUintMap net_limit = 72; // bytes per second
+ optional TUintMap net_rx_limit = 73; // bytes per second
+
+ optional TContainerVolumes volumes_required = 75;
+}
+
+message TContainer {
+ optional TContainerSpec spec = 1; //required
+ optional TContainerStatus status = 2;
+ optional TError error = 3;
+}
+
+
+// Volumes
+
+message TVolumeDescription {
+ required string path = 1; // path in client namespace
+ map<string, string> properties = 2;
+ repeated string containers = 3; // linked containers (legacy)
+ repeated TVolumeLink links = 4; // linked containers with details
+
+ optional uint64 change_time = 5; // sec since epoch
+ optional bool no_changes = 6; // change_time < changed_since
+}
+
+
+message TVolumeLink {
+ optional string container = 1;
+ optional string target = 2; // absolute path in container, default: anon
+ optional bool required = 3; // container cannot work without it
+ optional bool read_only = 4;
+ optional string host_target = 5; // out, absolute path in host
+ optional bool container_root = 6; // in, set container root
+ optional bool container_cwd = 7; // in, set container cwd
+}
+
+message TVolumeResource {
+ optional uint64 limit = 1; // bytes or inodes
+ optional uint64 guarantee = 2; // bytes or inodes
+ optional uint64 usage = 3; // out, bytes or inodes
+ optional uint64 available = 4; // out, bytes or inodes
+}
+
+message TVolumeDirectory {
+ optional string path = 1; // relative path in volume
+ optional TCred cred = 2; // default: volume cred
+ optional fixed32 permissions = 3; // default: volume permissions
+}
+
+message TVolumeSymlink {
+ optional string path = 1; // relative path in volume
+ optional string target_path = 2;
+}
+
+message TVolumeShare {
+ optional string path = 1; // relative path in volume
+ optional string origin_path = 2; // absolute path to origin
+ optional bool cow = 3; // default: mutable share
+}
+
+// Structured Volume description (Porto V5 API)
+
+message TVolumeSpec {
+ optional string path = 1; // path in container, default: auto
+ optional string container = 2; // defines root for paths, default: self (client container)
+ repeated TVolumeLink links = 3; // initial links, default: anon link to self
+
+ optional string id = 4; // out
+ optional string state = 5; // out
+
+ optional string private_value = 6; // at most 4096 bytes
+
+ optional string device_name = 7; // out
+
+ optional string backend = 10; // default: auto
+ optional string place = 11; // path in host or alias, default from client container
+ optional string storage = 12; // persistent storage, path or name, default: non-persistent
+ optional string image = 52;
+ repeated string layers = 13; // name or path
+ optional bool read_only = 14;
+
+ // defines root directory user, group and permissions
+ optional TCred cred = 20; // default: self task cred
+ optional fixed32 permissions = 21; // default: 0775
+
+ optional TVolumeResource space = 22;
+ optional TVolumeResource inodes = 23;
+
+ optional TCred owner = 30; // default: self owner
+ optional string owner_container = 31; // default: self
+ optional string place_key = 32; // out, key for place_limit
+ optional string creator = 33; // out
+ optional bool auto_path = 34; // out
+ optional uint32 device_index = 35; // out
+ optional uint64 build_time = 37; // out, sec since epoch
+
+ // customization at creation
+ repeated TVolumeDirectory directories = 40; // in
+ repeated TVolumeSymlink symlinks = 41; // in
+ repeated TVolumeShare shares = 42; // in
+
+ optional uint64 change_time = 50; // out, sec since epoch
+ optional bool no_changes = 51; // out, change_time < changed_since
+}
+
+
+message TLayer {
+ optional string name = 1; // name or meta/name
+ optional string owner_user = 2;
+ optional string owner_group = 3;
+ optional uint64 last_usage = 4; // out, sec since last usage
+ optional string private_value = 5;
+}
+
+
+message TStorage {
+ optional string name = 1; // name or meta/name
+ optional string owner_user = 2;
+ optional string owner_group = 3;
+ optional uint64 last_usage = 4; // out, sec since last usage
+ optional string private_value = 5;
+}
+
+
+message TMetaStorage {
+ optional string name = 1;
+ optional string place = 2;
+ optional string private_value = 3;
+ optional uint64 space_limit = 4; // bytes
+ optional uint64 inode_limit = 5; // inodes
+
+ optional uint64 space_used = 6; // out, bytes
+ optional uint64 space_available = 7; // out, bytes
+ optional uint64 inode_used = 8; // out, inodes
+ optional uint64 inode_available = 9; // out, inodes
+ optional string owner_user = 10; // out
+ optional string owner_group = 11; // out
+ optional uint64 last_usage = 12; // out, sec since last usage
+}
+
+
+// COMMANDS
+
+// System
+
+// Get porto version
+message TVersionRequest {
+}
+
+message TVersionResponse {
+ optional string tag = 1;
+ optional string revision = 2;
+}
+
+
+// Get porto statistics
+message TGetSystemRequest {
+}
+
+message TGetSystemResponse {
+ optional string porto_version = 1;
+ optional string porto_revision = 2;
+ optional string kernel_version = 3;
+
+ optional fixed64 errors = 4;
+ optional fixed64 warnings = 5;
+ optional fixed64 porto_starts = 6;
+ optional fixed64 porto_uptime = 7;
+ optional fixed64 master_uptime = 8;
+ optional fixed64 taints = 9;
+
+ optional bool frozen = 10;
+ optional bool verbose = 100;
+ optional bool debug = 101;
+ optional fixed64 log_lines = 102;
+ optional fixed64 log_bytes = 103;
+
+ optional fixed64 stream_rotate_bytes = 104;
+ optional fixed64 stream_rotate_errors = 105;
+
+ optional fixed64 log_lines_lost = 106;
+ optional fixed64 log_bytes_lost = 107;
+ optional fixed64 log_open = 108;
+
+ optional fixed64 container_count = 200;
+ optional fixed64 container_limit = 201;
+ optional fixed64 container_running = 202;
+ optional fixed64 container_created = 203;
+ optional fixed64 container_started = 204;
+ optional fixed64 container_start_failed = 205;
+ optional fixed64 container_oom = 206;
+ optional fixed64 container_buried = 207;
+ optional fixed64 container_lost = 208;
+ optional fixed64 container_tainted = 209;
+
+ optional fixed64 volume_count = 300;
+ optional fixed64 volume_limit = 301;
+ optional fixed64 volume_created = 303;
+ optional fixed64 volume_failed = 304;
+ optional fixed64 volume_links = 305;
+ optional fixed64 volume_links_mounted = 306;
+ optional fixed64 volume_lost = 307;
+
+ optional fixed64 layer_import = 390;
+ optional fixed64 layer_export = 391;
+ optional fixed64 layer_remove = 392;
+
+ optional fixed64 client_count = 400;
+ optional fixed64 client_max = 401;
+ optional fixed64 client_connected = 402;
+
+ optional fixed64 request_queued = 500;
+ optional fixed64 request_completed = 501;
+ optional fixed64 request_failed = 502;
+ optional fixed64 request_threads = 503;
+ optional fixed64 request_longer_1s = 504;
+ optional fixed64 request_longer_3s = 505;
+ optional fixed64 request_longer_30s = 506;
+ optional fixed64 request_longer_5m = 507;
+
+ optional fixed64 fail_system = 600;
+ optional fixed64 fail_invalid_value = 601;
+ optional fixed64 fail_invalid_command = 602;
+ optional fixed64 fail_memory_guarantee = 603;
+ optional fixed64 fail_invalid_netaddr = 604;
+
+ optional fixed64 porto_crash = 666;
+
+ optional fixed64 network_count = 700;
+ optional fixed64 network_created = 701;
+ optional fixed64 network_problems = 702;
+ optional fixed64 network_repairs = 703;
+}
+
+
+// Change porto state
+message TSetSystemRequest {
+ optional bool frozen = 10;
+ optional bool verbose = 100;
+ optional bool debug = 101;
+}
+
+message TSetSystemResponse {
+}
+
+message TCreateFromSpecRequest {
+ optional TContainerSpec container = 1; //required
+ repeated TVolumeSpec volumes = 2;
+ optional bool start = 3;
+}
+
+message TUpdateFromSpecRequest {
+ optional TContainerSpec container = 1; //required
+ optional bool start = 2;
+}
+
+message TListContainersFilter {
+ optional string name = 1; // name or wildcards, default: all
+ optional TStringMap labels = 2;
+ optional uint64 changed_since = 3; // change_time >= changed_since
+}
+
+message TStreamDumpOptions {
+ optional uint64 stdstream_offset = 2; // default: 0
+ optional uint64 stdstream_limit = 3; // default: 8Mb
+}
+
+message TListContainersFieldOptions {
+ repeated string properties = 1; // property names, default: all
+ optional TStreamDumpOptions stdout_options = 2; // for GetIndexed stdout
+ optional TStreamDumpOptions stderr_options = 3; // for GetIndexed stderr
+}
+
+message TListContainersRequest {
+ repeated TListContainersFilter filters = 1;
+ optional TListContainersFieldOptions field_options = 2;
+}
+
+message TListContainersResponse {
+ repeated TContainer containers = 1;
+}
+
+// List available properties
+message TListPropertiesRequest {
+}
+
+message TListPropertiesResponse {
+ message TContainerPropertyListEntry {
+ optional string name = 1;
+ optional string desc = 2;
+ optional bool read_only = 3;
+ optional bool dynamic = 4;
+ }
+ repeated TContainerPropertyListEntry list = 1;
+}
+
+
+// deprecated, use ListProperties
+message TListDataPropertiesRequest {
+}
+
+message TListDataPropertiesResponse {
+ message TContainerDataListEntry {
+ optional string name = 1;
+ optional string desc = 2;
+ }
+ repeated TContainerDataListEntry list = 1;
+}
+
+
+// Create stopped container
+message TCreateRequest {
+ optional string name = 1;
+}
+
+
+// Stop and destroy container
+message TDestroyRequest {
+ optional string name = 1;
+}
+
+
+// List container names
+message TListRequest {
+ optional string mask = 1;
+ optional uint64 changed_since = 2; // change_time >= changed_since
+}
+
+message TListResponse {
+ repeated string name = 1;
+ optional string absolute_namespace = 2;
+}
+
+
+// Read one property
+message TGetPropertyRequest {
+ optional string name = 1;
+ optional string property = 2;
+ // update cached counters
+ optional bool sync = 3;
+ optional bool real = 4;
+}
+
+message TGetPropertyResponse {
+ optional string value = 1;
+}
+
+
+// Alias for GetProperty, deprecated
+message TGetDataPropertyRequest {
+ optional string name = 1;
+ optional string data = 2;
+ // update cached counters
+ optional bool sync = 3;
+ optional bool real = 4;
+}
+
+message TGetDataPropertyResponse {
+ optional string value = 1;
+}
+
+
+// Change one property
+message TSetPropertyRequest {
+ optional string name = 1;
+ optional string property = 2;
+ optional string value = 3;
+}
+
+
+// Get multiple properties/data of many containers with one request
+message TGetRequest {
+ // list of containers or wildcards, "***" - all
+ repeated string name = 1;
+
+ // list of properties/data
+ repeated string variable = 2;
+
+ // do not wait busy containers
+ optional bool nonblock = 3;
+
+ // update cached counters
+ optional bool sync = 4;
+ optional bool real = 5;
+
+ // change_time >= changed_since
+ optional uint64 changed_since = 6;
+}
+
+message TGetResponse {
+ message TContainerGetValueResponse {
+ optional string variable = 1;
+ optional EError error = 2;
+ optional string errorMsg = 3;
+ optional string value = 4;
+ }
+
+ message TContainerGetListResponse {
+ optional string name = 1;
+ repeated TContainerGetValueResponse keyval = 2;
+
+ optional uint64 change_time = 3;
+ optional bool no_changes = 4; // change_time < changed_since
+ }
+
+ repeated TContainerGetListResponse list = 1;
+}
+
+
+// Start stopped container
+message TStartRequest {
+ optional string name = 1;
+}
+
+
+// Restart dead container
+message TRespawnRequest {
+ optional string name = 1;
+}
+
+
+// Stop dead or running container
+message TStopRequest {
+ optional string name = 1;
+ // Timeout in 1/1000 seconds between SIGTERM and SIGKILL, default 30s
+ optional uint32 timeout_ms = 2;
+}
+
+
+// Freeze running container
+message TPauseRequest {
+ optional string name = 1;
+}
+
+
+// Unfreeze paused container
+message TResumeRequest {
+ optional string name = 1;
+}
+
+
+// Translate filesystem path between containers
+message TConvertPathRequest {
+ optional string path = 1;
+ optional string source = 2;
+ optional string destination = 3;
+}
+
+message TConvertPathResponse {
+ optional string path = 1;
+}
+
+
+// Wait while container(s) is/are in running state
+message TWaitRequest {
+ // list of containers or wildcards, "***" - all
+ repeated string name = 1;
+
+ // timeout in 1/1000 seconds, 0 - nonblock
+ optional uint32 timeout_ms = 2;
+
+ // list of label names or wildcards
+ repeated string label = 3;
+
+ // async wait with target_state works only once
+ optional string target_state = 4;
+}
+
+message TWaitResponse {
+ optional string name = 1; // container name
+ optional string state = 2; // container state or "timeout"
+ optional uint64 when = 3; // unix time stamp in seconds
+ optional string label = 4;
+ optional string value = 5;
+}
+
+
+// Send signal main process in container
+message TKillRequest {
+ optional string name = 1;
+ optional int32 sig = 2;
+}
+
+
+// Move process into container
+message TAttachProcessRequest {
+ optional string name = 1;
+ optional uint32 pid = 2;
+ optional string comm = 3; // ignored if empty
+}
+
+
+// Determine container by pid
+message TLocateProcessRequest {
+ optional uint32 pid = 1;
+ optional string comm = 2; // ignored if empty
+}
+
+message TLocateProcessResponse {
+ optional string name = 1;
+}
+
+
+// Labels
+
+
+message TFindLabelRequest {
+ optional string mask = 1; // containers name or wildcard
+ optional string state = 2; // filter by container state
+ optional string label = 3; // label name or wildcard
+ optional string value = 4; // filter by label value
+}
+
+message TFindLabelResponse {
+ message TFindLabelEntry {
+ optional string name = 1;
+ optional string state = 2;
+ optional string label = 3;
+ optional string value = 4;
+ }
+ repeated TFindLabelEntry list = 1;
+}
+
+
+message TSetLabelRequest {
+ optional string name = 1;
+ optional string label = 2;
+ optional string value = 3;
+ optional string prev_value = 4; // fail with Busy if does not match
+ optional string state = 5; // fail with InvalidState if not match
+}
+
+message TSetLabelResponse {
+ optional string prev_value = 1;
+ optional string state = 2;
+}
+
+
+message TIncLabelRequest {
+ optional string name = 1;
+ optional string label = 2; // missing label starts from 0
+ optional int64 add = 3 [ default = 1];
+}
+
+message TIncLabelResponse {
+ optional int64 result = 1;
+}
+
+
+message TSetSymlinkRequest {
+ optional string container = 1;
+ optional string symlink = 2;
+ optional string target = 3;
+}
+
+
+// Volumes
+
+
+message TNewVolumeRequest {
+ optional TVolumeSpec volume = 1;
+}
+
+message TNewVolumeResponse {
+ optional TVolumeSpec volume = 1;
+}
+
+
+message TGetVolumeRequest {
+ optional string container = 1; // get paths in container, default: self (client container)
+ repeated string path = 2; // volume path in container, default: all
+ optional uint64 changed_since = 3; // change_time >= changed_since
+ repeated string label = 4; // labels or wildcards
+}
+
+message TGetVolumeResponse {
+ repeated TVolumeSpec volume = 1;
+}
+
+
+// List available volume properties
+message TListVolumePropertiesRequest {
+}
+
+message TListVolumePropertiesResponse {
+ message TVolumePropertyDescription {
+ optional string name = 1;
+ optional string desc = 2;
+ }
+ repeated TVolumePropertyDescription list = 1;
+}
+
+
+// Create new volume
+// "createVolume" returns TVolumeDescription in "volume"
+message TCreateVolumeRequest {
+ optional string path = 1;
+ map<string, string> properties = 2;
+}
+
+
+message TLinkVolumeRequest {
+ optional string path = 1;
+ optional string container = 2; // default - self (client container)
+ optional string target = 3; // path in container, "" - anon
+ optional bool required = 4; // stop container at fail
+ optional bool read_only = 5;
+}
+
+
+message TUnlinkVolumeRequest {
+ optional string path = 1;
+ optional string container = 2; // default - self, "***" - all
+ optional bool strict = 3; // non-lazy umount
+ optional string target = 4; // path in container, "" - anon, default - "***" - all
+}
+
+
+message TListVolumesRequest {
+ optional string path = 1; // volume path or wildcard
+ optional string container = 2;
+ optional uint64 changed_since = 3; // change_time >= changed_since
+}
+
+message TListVolumesResponse {
+ repeated TVolumeDescription volumes = 1;
+}
+
+
+message TTuneVolumeRequest {
+ optional string path = 1;
+ map<string, string> properties = 2;
+}
+
+// Layers
+
+
+message TListLayersRequest {
+ optional string place = 1; // default from client container
+ optional string mask = 2;
+}
+
+message TListLayersResponse {
+ repeated string layer = 1; // layer names (legacy)
+ repeated TLayer layers = 2; // layer with description
+}
+
+
+message TImportLayerRequest {
+ optional string layer = 1;
+ optional string tarball = 2;
+ optional bool merge = 3;
+ optional string place = 4;
+ optional string private_value = 5;
+ optional string compress = 6;
+ optional bool verbose_error = 7;
+}
+
+
+message TExportLayerRequest {
+ optional string volume = 1;
+ optional string tarball = 2;
+ optional string layer = 3;
+ optional string place = 4;
+ optional string compress = 5;
+}
+
+
+message TRemoveLayerRequest {
+ optional string layer = 1;
+ optional string place = 2;
+ optional bool async = 3;
+}
+
+
+message TGetLayerPrivateRequest {
+ optional string layer = 1;
+ optional string place = 2;
+}
+
+message TGetLayerPrivateResponse {
+ optional string private_value = 1;
+}
+
+
+message TSetLayerPrivateRequest {
+ optional string layer = 1;
+ optional string place = 2;
+ optional string private_value = 3;
+}
+
+
+// Storages
+
+
+message TListStoragesRequest {
+ optional string place = 1;
+ optional string mask = 2; // "name" - storage, "name/" - meta-storage
+}
+
+message TListStoragesResponse {
+ repeated TStorage storages = 1;
+ repeated TMetaStorage meta_storages = 2;
+}
+
+
+message TRemoveStorageRequest {
+ optional string name = 1;
+ optional string place = 2;
+}
+
+
+message TImportStorageRequest {
+ optional string name = 1;
+ optional string tarball = 2;
+ optional string place = 3;
+ optional string private_value = 5;
+ optional string compress = 6;
+}
+
+
+message TExportStorageRequest {
+ optional string name = 1;
+ optional string tarball = 2;
+ optional string place = 3;
+ optional string compress = 4;
+}
+
+
+// Docker images API
+
+
+message TDockerImageConfig {
+ repeated string cmd = 1;
+ repeated string env = 2;
+}
+
+message TDockerImage {
+ required string id = 1;
+ repeated string tags = 2;
+ repeated string digests = 3;
+ repeated string layers = 4;
+ optional uint64 size = 5;
+ optional TDockerImageConfig config = 6;
+}
+
+
+message TDockerImageStatusRequest {
+ required string name = 1;
+ optional string place = 2;
+}
+
+message TDockerImageStatusResponse {
+ optional TDockerImage image = 1;
+}
+
+
+message TDockerImageListRequest {
+ optional string place = 1;
+ optional string mask = 2;
+}
+
+message TDockerImageListResponse {
+ repeated TDockerImage images = 1;
+}
+
+
+message TDockerImagePullRequest {
+ required string name = 1;
+ optional string place = 2;
+ optional string auth_token = 3;
+ optional string auth_path = 4;
+ optional string auth_service = 5;
+}
+
+message TDockerImagePullResponse {
+ optional TDockerImage image = 1;
+}
+
+
+message TDockerImageRemoveRequest {
+ required string name = 1;
+ optional string place = 2;
+}
diff --git a/library/cpp/porto/proto/ya.make b/library/cpp/porto/proto/ya.make
new file mode 100644
index 0000000000..525a807ee0
--- /dev/null
+++ b/library/cpp/porto/proto/ya.make
@@ -0,0 +1,5 @@
+PROTO_LIBRARY()
+INCLUDE_TAGS(GO_PROTO)
+SRCS(rpc.proto)
+END()
+
diff --git a/library/cpp/porto/ut/ya.make b/library/cpp/porto/ut/ya.make
new file mode 100644
index 0000000000..766a45eb56
--- /dev/null
+++ b/library/cpp/porto/ut/ya.make
@@ -0,0 +1,4 @@
+UNITTEST_FOR(library/cpp/porto)
+TAG(ya:manual sb:portod)
+SRCS(libporto_ut.cpp)
+END()
diff --git a/library/cpp/porto/ya.make b/library/cpp/porto/ya.make
new file mode 100644
index 0000000000..e1ccbac281
--- /dev/null
+++ b/library/cpp/porto/ya.make
@@ -0,0 +1,17 @@
+LIBRARY()
+
+BUILD_ONLY_IF(WARNING WARNING LINUX)
+
+PEERDIR(
+ library/cpp/porto/proto
+ contrib/libs/protobuf
+)
+
+SRCS(
+ libporto.cpp
+ metrics.cpp
+)
+
+END()
+
+RECURSE_FOR_TESTS(ut)
diff --git a/library/cpp/yt/CMakeLists.txt b/library/cpp/yt/CMakeLists.txt
index b1dc1594fc..d05e8fb68e 100644
--- a/library/cpp/yt/CMakeLists.txt
+++ b/library/cpp/yt/CMakeLists.txt
@@ -16,7 +16,9 @@ add_subdirectory(logging)
add_subdirectory(malloc)
add_subdirectory(memory)
add_subdirectory(misc)
+add_subdirectory(mlock)
add_subdirectory(small_containers)
+add_subdirectory(stockpile)
add_subdirectory(string)
add_subdirectory(system)
add_subdirectory(threading)
diff --git a/library/cpp/yt/backtrace/cursors/CMakeLists.darwin-x86_64.txt b/library/cpp/yt/backtrace/cursors/CMakeLists.darwin-x86_64.txt
index 6c6f5d1c50..76c3eda332 100644
--- a/library/cpp/yt/backtrace/cursors/CMakeLists.darwin-x86_64.txt
+++ b/library/cpp/yt/backtrace/cursors/CMakeLists.darwin-x86_64.txt
@@ -6,4 +6,6 @@
# original buildsystem will not be accepted.
+add_subdirectory(frame_pointer)
+add_subdirectory(interop)
add_subdirectory(libunwind)
diff --git a/library/cpp/yt/backtrace/cursors/CMakeLists.linux-aarch64.txt b/library/cpp/yt/backtrace/cursors/CMakeLists.linux-aarch64.txt
index 6c6f5d1c50..76c3eda332 100644
--- a/library/cpp/yt/backtrace/cursors/CMakeLists.linux-aarch64.txt
+++ b/library/cpp/yt/backtrace/cursors/CMakeLists.linux-aarch64.txt
@@ -6,4 +6,6 @@
# original buildsystem will not be accepted.
+add_subdirectory(frame_pointer)
+add_subdirectory(interop)
add_subdirectory(libunwind)
diff --git a/library/cpp/yt/backtrace/cursors/CMakeLists.linux-x86_64.txt b/library/cpp/yt/backtrace/cursors/CMakeLists.linux-x86_64.txt
index 6c6f5d1c50..76c3eda332 100644
--- a/library/cpp/yt/backtrace/cursors/CMakeLists.linux-x86_64.txt
+++ b/library/cpp/yt/backtrace/cursors/CMakeLists.linux-x86_64.txt
@@ -6,4 +6,6 @@
# original buildsystem will not be accepted.
+add_subdirectory(frame_pointer)
+add_subdirectory(interop)
add_subdirectory(libunwind)
diff --git a/library/cpp/yt/backtrace/cursors/CMakeLists.windows-x86_64.txt b/library/cpp/yt/backtrace/cursors/CMakeLists.windows-x86_64.txt
index 961a9a908b..27fb2d8417 100644
--- a/library/cpp/yt/backtrace/cursors/CMakeLists.windows-x86_64.txt
+++ b/library/cpp/yt/backtrace/cursors/CMakeLists.windows-x86_64.txt
@@ -7,3 +7,6 @@
add_subdirectory(dummy)
+add_subdirectory(frame_pointer)
+add_subdirectory(interop)
+add_subdirectory(libunwind)
diff --git a/library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.darwin-x86_64.txt b/library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.darwin-x86_64.txt
new file mode 100644
index 0000000000..9078cd7245
--- /dev/null
+++ b/library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.darwin-x86_64.txt
@@ -0,0 +1,20 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+
+add_library(backtrace-cursors-frame_pointer)
+target_compile_options(backtrace-cursors-frame_pointer PRIVATE
+ -Wdeprecated-this-capture
+)
+target_link_libraries(backtrace-cursors-frame_pointer PUBLIC
+ contrib-libs-cxxsupp
+ yutil
+)
+target_sources(backtrace-cursors-frame_pointer PRIVATE
+ ${CMAKE_SOURCE_DIR}/library/cpp/yt/backtrace/cursors/frame_pointer/frame_pointer_cursor.cpp
+)
diff --git a/library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.linux-aarch64.txt b/library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.linux-aarch64.txt
new file mode 100644
index 0000000000..ce9e059d81
--- /dev/null
+++ b/library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.linux-aarch64.txt
@@ -0,0 +1,21 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+
+add_library(backtrace-cursors-frame_pointer)
+target_compile_options(backtrace-cursors-frame_pointer PRIVATE
+ -Wdeprecated-this-capture
+)
+target_link_libraries(backtrace-cursors-frame_pointer PUBLIC
+ contrib-libs-linux-headers
+ contrib-libs-cxxsupp
+ yutil
+)
+target_sources(backtrace-cursors-frame_pointer PRIVATE
+ ${CMAKE_SOURCE_DIR}/library/cpp/yt/backtrace/cursors/frame_pointer/frame_pointer_cursor.cpp
+)
diff --git a/library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.linux-x86_64.txt b/library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.linux-x86_64.txt
new file mode 100644
index 0000000000..ce9e059d81
--- /dev/null
+++ b/library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.linux-x86_64.txt
@@ -0,0 +1,21 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+
+add_library(backtrace-cursors-frame_pointer)
+target_compile_options(backtrace-cursors-frame_pointer PRIVATE
+ -Wdeprecated-this-capture
+)
+target_link_libraries(backtrace-cursors-frame_pointer PUBLIC
+ contrib-libs-linux-headers
+ contrib-libs-cxxsupp
+ yutil
+)
+target_sources(backtrace-cursors-frame_pointer PRIVATE
+ ${CMAKE_SOURCE_DIR}/library/cpp/yt/backtrace/cursors/frame_pointer/frame_pointer_cursor.cpp
+)
diff --git a/library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.txt b/library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.txt
new file mode 100644
index 0000000000..f8b31df0c1
--- /dev/null
+++ b/library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.txt
@@ -0,0 +1,17 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" AND NOT HAVE_CUDA)
+ include(CMakeLists.linux-aarch64.txt)
+elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
+ include(CMakeLists.darwin-x86_64.txt)
+elseif (WIN32 AND CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" AND NOT HAVE_CUDA)
+ include(CMakeLists.windows-x86_64.txt)
+elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND NOT HAVE_CUDA)
+ include(CMakeLists.linux-x86_64.txt)
+endif()
diff --git a/library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.windows-x86_64.txt b/library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.windows-x86_64.txt
new file mode 100644
index 0000000000..ebfaf2065e
--- /dev/null
+++ b/library/cpp/yt/backtrace/cursors/frame_pointer/CMakeLists.windows-x86_64.txt
@@ -0,0 +1,17 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+
+add_library(backtrace-cursors-frame_pointer)
+target_link_libraries(backtrace-cursors-frame_pointer PUBLIC
+ contrib-libs-cxxsupp
+ yutil
+)
+target_sources(backtrace-cursors-frame_pointer PRIVATE
+ ${CMAKE_SOURCE_DIR}/library/cpp/yt/backtrace/cursors/frame_pointer/frame_pointer_cursor.cpp
+)
diff --git a/library/cpp/yt/backtrace/cursors/interop/CMakeLists.darwin-x86_64.txt b/library/cpp/yt/backtrace/cursors/interop/CMakeLists.darwin-x86_64.txt
new file mode 100644
index 0000000000..8ddc8397b7
--- /dev/null
+++ b/library/cpp/yt/backtrace/cursors/interop/CMakeLists.darwin-x86_64.txt
@@ -0,0 +1,22 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+
+add_library(backtrace-cursors-interop)
+target_compile_options(backtrace-cursors-interop PRIVATE
+ -Wdeprecated-this-capture
+)
+target_link_libraries(backtrace-cursors-interop PUBLIC
+ contrib-libs-cxxsupp
+ yutil
+ backtrace-cursors-frame_pointer
+ contrib-libs-libunwind
+)
+target_sources(backtrace-cursors-interop PRIVATE
+ ${CMAKE_SOURCE_DIR}/library/cpp/yt/backtrace/cursors/interop/interop.cpp
+)
diff --git a/library/cpp/yt/backtrace/cursors/interop/CMakeLists.linux-aarch64.txt b/library/cpp/yt/backtrace/cursors/interop/CMakeLists.linux-aarch64.txt
new file mode 100644
index 0000000000..221213fdae
--- /dev/null
+++ b/library/cpp/yt/backtrace/cursors/interop/CMakeLists.linux-aarch64.txt
@@ -0,0 +1,23 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+
+add_library(backtrace-cursors-interop)
+target_compile_options(backtrace-cursors-interop PRIVATE
+ -Wdeprecated-this-capture
+)
+target_link_libraries(backtrace-cursors-interop PUBLIC
+ contrib-libs-linux-headers
+ contrib-libs-cxxsupp
+ yutil
+ backtrace-cursors-frame_pointer
+ contrib-libs-libunwind
+)
+target_sources(backtrace-cursors-interop PRIVATE
+ ${CMAKE_SOURCE_DIR}/library/cpp/yt/backtrace/cursors/interop/interop.cpp
+)
diff --git a/library/cpp/yt/backtrace/cursors/interop/CMakeLists.linux-x86_64.txt b/library/cpp/yt/backtrace/cursors/interop/CMakeLists.linux-x86_64.txt
new file mode 100644
index 0000000000..221213fdae
--- /dev/null
+++ b/library/cpp/yt/backtrace/cursors/interop/CMakeLists.linux-x86_64.txt
@@ -0,0 +1,23 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+
+add_library(backtrace-cursors-interop)
+target_compile_options(backtrace-cursors-interop PRIVATE
+ -Wdeprecated-this-capture
+)
+target_link_libraries(backtrace-cursors-interop PUBLIC
+ contrib-libs-linux-headers
+ contrib-libs-cxxsupp
+ yutil
+ backtrace-cursors-frame_pointer
+ contrib-libs-libunwind
+)
+target_sources(backtrace-cursors-interop PRIVATE
+ ${CMAKE_SOURCE_DIR}/library/cpp/yt/backtrace/cursors/interop/interop.cpp
+)
diff --git a/library/cpp/yt/backtrace/cursors/interop/CMakeLists.txt b/library/cpp/yt/backtrace/cursors/interop/CMakeLists.txt
new file mode 100644
index 0000000000..f8b31df0c1
--- /dev/null
+++ b/library/cpp/yt/backtrace/cursors/interop/CMakeLists.txt
@@ -0,0 +1,17 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" AND NOT HAVE_CUDA)
+ include(CMakeLists.linux-aarch64.txt)
+elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
+ include(CMakeLists.darwin-x86_64.txt)
+elseif (WIN32 AND CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" AND NOT HAVE_CUDA)
+ include(CMakeLists.windows-x86_64.txt)
+elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND NOT HAVE_CUDA)
+ include(CMakeLists.linux-x86_64.txt)
+endif()
diff --git a/library/cpp/yt/backtrace/cursors/interop/CMakeLists.windows-x86_64.txt b/library/cpp/yt/backtrace/cursors/interop/CMakeLists.windows-x86_64.txt
new file mode 100644
index 0000000000..9a7660f685
--- /dev/null
+++ b/library/cpp/yt/backtrace/cursors/interop/CMakeLists.windows-x86_64.txt
@@ -0,0 +1,19 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+
+add_library(backtrace-cursors-interop)
+target_link_libraries(backtrace-cursors-interop PUBLIC
+ contrib-libs-cxxsupp
+ yutil
+ backtrace-cursors-frame_pointer
+ contrib-libs-libunwind
+)
+target_sources(backtrace-cursors-interop PRIVATE
+ ${CMAKE_SOURCE_DIR}/library/cpp/yt/backtrace/cursors/interop/interop.cpp
+)
diff --git a/library/cpp/yt/backtrace/cursors/libunwind/CMakeLists.txt b/library/cpp/yt/backtrace/cursors/libunwind/CMakeLists.txt
index 606ff46b4b..f8b31df0c1 100644
--- a/library/cpp/yt/backtrace/cursors/libunwind/CMakeLists.txt
+++ b/library/cpp/yt/backtrace/cursors/libunwind/CMakeLists.txt
@@ -10,6 +10,8 @@ if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "aarc
include(CMakeLists.linux-aarch64.txt)
elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
include(CMakeLists.darwin-x86_64.txt)
+elseif (WIN32 AND CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" AND NOT HAVE_CUDA)
+ include(CMakeLists.windows-x86_64.txt)
elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND NOT HAVE_CUDA)
include(CMakeLists.linux-x86_64.txt)
endif()
diff --git a/library/cpp/yt/backtrace/cursors/libunwind/CMakeLists.windows-x86_64.txt b/library/cpp/yt/backtrace/cursors/libunwind/CMakeLists.windows-x86_64.txt
new file mode 100644
index 0000000000..bea2a794c1
--- /dev/null
+++ b/library/cpp/yt/backtrace/cursors/libunwind/CMakeLists.windows-x86_64.txt
@@ -0,0 +1,18 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+
+add_library(backtrace-cursors-libunwind)
+target_link_libraries(backtrace-cursors-libunwind PUBLIC
+ contrib-libs-cxxsupp
+ yutil
+ contrib-libs-libunwind
+)
+target_sources(backtrace-cursors-libunwind PRIVATE
+ ${CMAKE_SOURCE_DIR}/library/cpp/yt/backtrace/cursors/libunwind/libunwind_cursor.cpp
+)
diff --git a/library/cpp/yt/mlock/CMakeLists.darwin-x86_64.txt b/library/cpp/yt/mlock/CMakeLists.darwin-x86_64.txt
new file mode 100644
index 0000000000..ca50021faf
--- /dev/null
+++ b/library/cpp/yt/mlock/CMakeLists.darwin-x86_64.txt
@@ -0,0 +1,20 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+
+add_library(cpp-yt-mlock)
+target_compile_options(cpp-yt-mlock PRIVATE
+ -Wdeprecated-this-capture
+)
+target_link_libraries(cpp-yt-mlock PUBLIC
+ contrib-libs-cxxsupp
+ yutil
+)
+target_sources(cpp-yt-mlock PRIVATE
+ ${CMAKE_SOURCE_DIR}/library/cpp/yt/mlock/mlock_other.cpp
+)
diff --git a/library/cpp/yt/mlock/CMakeLists.linux-aarch64.txt b/library/cpp/yt/mlock/CMakeLists.linux-aarch64.txt
new file mode 100644
index 0000000000..68f500a75a
--- /dev/null
+++ b/library/cpp/yt/mlock/CMakeLists.linux-aarch64.txt
@@ -0,0 +1,21 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+
+add_library(cpp-yt-mlock)
+target_compile_options(cpp-yt-mlock PRIVATE
+ -Wdeprecated-this-capture
+)
+target_link_libraries(cpp-yt-mlock PUBLIC
+ contrib-libs-linux-headers
+ contrib-libs-cxxsupp
+ yutil
+)
+target_sources(cpp-yt-mlock PRIVATE
+ ${CMAKE_SOURCE_DIR}/library/cpp/yt/mlock/mlock_linux.cpp
+)
diff --git a/library/cpp/yt/mlock/CMakeLists.linux-x86_64.txt b/library/cpp/yt/mlock/CMakeLists.linux-x86_64.txt
new file mode 100644
index 0000000000..68f500a75a
--- /dev/null
+++ b/library/cpp/yt/mlock/CMakeLists.linux-x86_64.txt
@@ -0,0 +1,21 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+
+add_library(cpp-yt-mlock)
+target_compile_options(cpp-yt-mlock PRIVATE
+ -Wdeprecated-this-capture
+)
+target_link_libraries(cpp-yt-mlock PUBLIC
+ contrib-libs-linux-headers
+ contrib-libs-cxxsupp
+ yutil
+)
+target_sources(cpp-yt-mlock PRIVATE
+ ${CMAKE_SOURCE_DIR}/library/cpp/yt/mlock/mlock_linux.cpp
+)
diff --git a/library/cpp/yt/mlock/CMakeLists.txt b/library/cpp/yt/mlock/CMakeLists.txt
new file mode 100644
index 0000000000..f8b31df0c1
--- /dev/null
+++ b/library/cpp/yt/mlock/CMakeLists.txt
@@ -0,0 +1,17 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" AND NOT HAVE_CUDA)
+ include(CMakeLists.linux-aarch64.txt)
+elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
+ include(CMakeLists.darwin-x86_64.txt)
+elseif (WIN32 AND CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" AND NOT HAVE_CUDA)
+ include(CMakeLists.windows-x86_64.txt)
+elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND NOT HAVE_CUDA)
+ include(CMakeLists.linux-x86_64.txt)
+endif()
diff --git a/library/cpp/yt/mlock/CMakeLists.windows-x86_64.txt b/library/cpp/yt/mlock/CMakeLists.windows-x86_64.txt
new file mode 100644
index 0000000000..1537ee764b
--- /dev/null
+++ b/library/cpp/yt/mlock/CMakeLists.windows-x86_64.txt
@@ -0,0 +1,17 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+
+add_library(cpp-yt-mlock)
+target_link_libraries(cpp-yt-mlock PUBLIC
+ contrib-libs-cxxsupp
+ yutil
+)
+target_sources(cpp-yt-mlock PRIVATE
+ ${CMAKE_SOURCE_DIR}/library/cpp/yt/mlock/mlock_other.cpp
+)
diff --git a/library/cpp/yt/mlock/README.md b/library/cpp/yt/mlock/README.md
new file mode 100644
index 0000000000..b61b6072c4
--- /dev/null
+++ b/library/cpp/yt/mlock/README.md
@@ -0,0 +1,11 @@
+# mlock
+
+MlockFileMappings подгружает и лочит в память все страницы исполняемого файла.
+
+В отличии от вызова mlockall, функция не лочит другие страницы процесса.
+mlockall явно выделяет физическую память под все vma. Типичный процесс сначала
+стартует и инициализирует аллокатор, а потом уже вызывает функцию для mlock страниц.
+Аллокатор при старте выделяет большие диапазоны через mmap, но реально их не использует.
+Поэтому mlockall приводит в повышенному потреблению памяти.
+
+Также, в отличии от mlockall, функция может подгрузить страницы в память сразу.
diff --git a/library/cpp/yt/mlock/mlock.h b/library/cpp/yt/mlock/mlock.h
new file mode 100644
index 0000000000..035fc47e37
--- /dev/null
+++ b/library/cpp/yt/mlock/mlock.h
@@ -0,0 +1,11 @@
+#pragma once
+
+namespace NYT {
+
+////////////////////////////////////////////////////////////////////////////////
+
+bool MlockFileMappings(bool populate = true);
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace NYT
diff --git a/library/cpp/yt/mlock/mlock_linux.cpp b/library/cpp/yt/mlock/mlock_linux.cpp
new file mode 100644
index 0000000000..8791869f95
--- /dev/null
+++ b/library/cpp/yt/mlock/mlock_linux.cpp
@@ -0,0 +1,89 @@
+#include "mlock.h"
+
+#include <stdio.h>
+#include <sys/mman.h>
+#include <stdint.h>
+#include <inttypes.h>
+
+namespace NYT {
+
+////////////////////////////////////////////////////////////////////////////////
+
+void PopulateFile(void* ptr, size_t size)
+{
+ constexpr size_t PageSize = 4096;
+
+ auto* begin = static_cast<volatile char*>(ptr);
+ for (auto* current = begin; current < begin + size; current += PageSize) {
+ *current;
+ }
+}
+
+bool MlockFileMappings(bool populate)
+{
+ auto* file = ::fopen("/proc/self/maps", "r");
+ if (!file) {
+ return false;
+ }
+
+ // Each line of /proc/<pid>/smaps has the following format:
+ // address perms offset dev inode path
+ // E.g.
+ // 08048000-08056000 r-xp 00000000 03:0c 64593 /usr/sbin/gpm
+
+ bool failed = false;
+ while (true) {
+ char line[1024];
+ if (!fgets(line, sizeof(line), file)) {
+ break;
+ }
+
+ char addressStr[64];
+ char permsStr[64];
+ char offsetStr[64];
+ char devStr[64];
+ int inode;
+ if (sscanf(line, "%s %s %s %s %d",
+ addressStr,
+ permsStr,
+ offsetStr,
+ devStr,
+ &inode) != 5)
+ {
+ continue;
+ }
+
+ if (inode == 0) {
+ continue;
+ }
+
+ if (permsStr[0] != 'r') {
+ continue;
+ }
+
+ uintptr_t startAddress;
+ uintptr_t endAddress;
+ if (sscanf(addressStr, "%" PRIx64 "-%" PRIx64,
+ &startAddress,
+ &endAddress) != 2)
+ {
+ continue;
+ }
+
+ if (::mlock(reinterpret_cast<const void*>(startAddress), endAddress - startAddress) != 0) {
+ failed = true;
+ continue;
+ }
+
+ if (populate) {
+ PopulateFile(reinterpret_cast<void*>(startAddress), endAddress - startAddress);
+ }
+ }
+
+ ::fclose(file);
+ return !failed;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace NYT
diff --git a/library/cpp/yt/mlock/mlock_other.cpp b/library/cpp/yt/mlock/mlock_other.cpp
new file mode 100644
index 0000000000..269c5c3cb9
--- /dev/null
+++ b/library/cpp/yt/mlock/mlock_other.cpp
@@ -0,0 +1,14 @@
+#include "mlock.h"
+
+namespace NYT {
+
+////////////////////////////////////////////////////////////////////////////////
+
+bool MlockFileMappings(bool /* populate */)
+{
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace NYT
diff --git a/library/cpp/yt/mlock/unittests/mlock_ut.cpp b/library/cpp/yt/mlock/unittests/mlock_ut.cpp
new file mode 100644
index 0000000000..98386622e8
--- /dev/null
+++ b/library/cpp/yt/mlock/unittests/mlock_ut.cpp
@@ -0,0 +1,19 @@
+#include <gtest/gtest.h>
+
+#include <library/cpp/yt/mlock/mlock.h>
+
+namespace NYT {
+namespace {
+
+////////////////////////////////////////////////////////////////////////////////
+
+TEST(TMlockTest, Call)
+{
+ ASSERT_TRUE(MlockFileMappings(false));
+ ASSERT_TRUE(MlockFileMappings(true));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace
+} // namespace NYT \ No newline at end of file
diff --git a/library/cpp/yt/mlock/unittests/ya.make b/library/cpp/yt/mlock/unittests/ya.make
new file mode 100644
index 0000000000..f1f956d468
--- /dev/null
+++ b/library/cpp/yt/mlock/unittests/ya.make
@@ -0,0 +1,13 @@
+GTEST()
+
+INCLUDE(${ARCADIA_ROOT}/library/cpp/yt/ya_cpp.make.inc)
+
+SRCS(
+ mlock_ut.cpp
+)
+
+PEERDIR(
+ library/cpp/yt/mlock
+)
+
+END()
diff --git a/library/cpp/yt/mlock/ya.make b/library/cpp/yt/mlock/ya.make
new file mode 100644
index 0000000000..2603d128ed
--- /dev/null
+++ b/library/cpp/yt/mlock/ya.make
@@ -0,0 +1,16 @@
+LIBRARY()
+
+INCLUDE(${ARCADIA_ROOT}/library/cpp/yt/ya_cpp.make.inc)
+
+IF (OS_LINUX AND NOT SANITIZER_TYPE)
+ SRCS(mlock_linux.cpp)
+ELSE()
+ SRCS(mlock_other.cpp)
+ENDIF()
+
+END()
+
+IF (OS_LINUX AND NOT SANITIZER_TYPE)
+ RECURSE(unittests)
+ENDIF()
+
diff --git a/library/cpp/yt/stockpile/CMakeLists.darwin-x86_64.txt b/library/cpp/yt/stockpile/CMakeLists.darwin-x86_64.txt
new file mode 100644
index 0000000000..8036bd5d7e
--- /dev/null
+++ b/library/cpp/yt/stockpile/CMakeLists.darwin-x86_64.txt
@@ -0,0 +1,20 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+
+add_library(cpp-yt-stockpile)
+target_compile_options(cpp-yt-stockpile PRIVATE
+ -Wdeprecated-this-capture
+)
+target_link_libraries(cpp-yt-stockpile PUBLIC
+ contrib-libs-cxxsupp
+ yutil
+)
+target_sources(cpp-yt-stockpile PRIVATE
+ ${CMAKE_SOURCE_DIR}/library/cpp/yt/stockpile/stockpile_other.cpp
+)
diff --git a/library/cpp/yt/stockpile/CMakeLists.linux-aarch64.txt b/library/cpp/yt/stockpile/CMakeLists.linux-aarch64.txt
new file mode 100644
index 0000000000..d023cce4da
--- /dev/null
+++ b/library/cpp/yt/stockpile/CMakeLists.linux-aarch64.txt
@@ -0,0 +1,21 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+
+add_library(cpp-yt-stockpile)
+target_compile_options(cpp-yt-stockpile PRIVATE
+ -Wdeprecated-this-capture
+)
+target_link_libraries(cpp-yt-stockpile PUBLIC
+ contrib-libs-linux-headers
+ contrib-libs-cxxsupp
+ yutil
+)
+target_sources(cpp-yt-stockpile PRIVATE
+ ${CMAKE_SOURCE_DIR}/library/cpp/yt/stockpile/stockpile_linux.cpp
+)
diff --git a/library/cpp/yt/stockpile/CMakeLists.linux-x86_64.txt b/library/cpp/yt/stockpile/CMakeLists.linux-x86_64.txt
new file mode 100644
index 0000000000..d023cce4da
--- /dev/null
+++ b/library/cpp/yt/stockpile/CMakeLists.linux-x86_64.txt
@@ -0,0 +1,21 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+
+add_library(cpp-yt-stockpile)
+target_compile_options(cpp-yt-stockpile PRIVATE
+ -Wdeprecated-this-capture
+)
+target_link_libraries(cpp-yt-stockpile PUBLIC
+ contrib-libs-linux-headers
+ contrib-libs-cxxsupp
+ yutil
+)
+target_sources(cpp-yt-stockpile PRIVATE
+ ${CMAKE_SOURCE_DIR}/library/cpp/yt/stockpile/stockpile_linux.cpp
+)
diff --git a/library/cpp/yt/stockpile/CMakeLists.txt b/library/cpp/yt/stockpile/CMakeLists.txt
new file mode 100644
index 0000000000..f8b31df0c1
--- /dev/null
+++ b/library/cpp/yt/stockpile/CMakeLists.txt
@@ -0,0 +1,17 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" AND NOT HAVE_CUDA)
+ include(CMakeLists.linux-aarch64.txt)
+elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
+ include(CMakeLists.darwin-x86_64.txt)
+elseif (WIN32 AND CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" AND NOT HAVE_CUDA)
+ include(CMakeLists.windows-x86_64.txt)
+elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND NOT HAVE_CUDA)
+ include(CMakeLists.linux-x86_64.txt)
+endif()
diff --git a/library/cpp/yt/stockpile/CMakeLists.windows-x86_64.txt b/library/cpp/yt/stockpile/CMakeLists.windows-x86_64.txt
new file mode 100644
index 0000000000..d60191d7fe
--- /dev/null
+++ b/library/cpp/yt/stockpile/CMakeLists.windows-x86_64.txt
@@ -0,0 +1,17 @@
+
+# This file was generated by the build system used internally in the Yandex monorepo.
+# Only simple modifications are allowed (adding source-files to targets, adding simple properties
+# like target_include_directories). These modifications will be ported to original
+# ya.make files by maintainers. Any complex modifications which can't be ported back to the
+# original buildsystem will not be accepted.
+
+
+
+add_library(cpp-yt-stockpile)
+target_link_libraries(cpp-yt-stockpile PUBLIC
+ contrib-libs-cxxsupp
+ yutil
+)
+target_sources(cpp-yt-stockpile PRIVATE
+ ${CMAKE_SOURCE_DIR}/library/cpp/yt/stockpile/stockpile_other.cpp
+)
diff --git a/library/cpp/yt/stockpile/README.md b/library/cpp/yt/stockpile/README.md
new file mode 100644
index 0000000000..6ee4cd1b1f
--- /dev/null
+++ b/library/cpp/yt/stockpile/README.md
@@ -0,0 +1,12 @@
+# stockpile
+
+При приближении к лимиту памяти в memory cgroup, linux запускает механизм direct reclaim,
+чтобы освободить свободную память. По опыту YT, direct reclaim очень сильно замедляет работу
+всего процесса.
+
+Проблема возникает не только, когда память занята анонимными страницами. 50% памяти контейнера
+может быть занято не dirty страницами page cache, но проблема всёравно будет проявляться. Например,
+если процесс активно читает файлы с диска без O_DIRECT, вся память очень быстро будет забита.
+
+Чтобы бороться с этой проблемой, в яндексовом ядре добавлена ручка `madvise(MADV_STOCKPILE)`.
+Больше подробностей в https://st.yandex-team.ru/KERNEL-186 \ No newline at end of file
diff --git a/library/cpp/yt/stockpile/stockpile.h b/library/cpp/yt/stockpile/stockpile.h
new file mode 100644
index 0000000000..1df9591de4
--- /dev/null
+++ b/library/cpp/yt/stockpile/stockpile.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include <util/system/types.h>
+
+#include <util/generic/size_literals.h>
+
+#include <util/datetime/base.h>
+
+namespace NYT {
+
+////////////////////////////////////////////////////////////////////////////////
+
+struct TStockpileOptions
+{
+ static constexpr i64 DefaultBufferSize = 4_GBs;
+ i64 BufferSize = DefaultBufferSize;
+
+ static constexpr int DefaultThreadCount = 4;
+ int ThreadCount = DefaultThreadCount;
+
+ static constexpr TDuration DefaultPeriod = TDuration::MilliSeconds(10);
+ TDuration Period = DefaultPeriod;
+};
+
+void ConfigureStockpile(const TStockpileOptions& options);
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace NYT
diff --git a/library/cpp/yt/stockpile/stockpile_linux.cpp b/library/cpp/yt/stockpile/stockpile_linux.cpp
new file mode 100644
index 0000000000..3ee83d9334
--- /dev/null
+++ b/library/cpp/yt/stockpile/stockpile_linux.cpp
@@ -0,0 +1,42 @@
+#include "stockpile.h"
+
+#include <thread>
+#include <mutex>
+
+#include <sys/mman.h>
+
+#include <util/system/thread.h>
+
+namespace NYT {
+
+////////////////////////////////////////////////////////////////////////////////
+
+namespace {
+
+void RunStockpile(const TStockpileOptions& options)
+{
+ TThread::SetCurrentThreadName("Stockpile");
+
+ constexpr int MADV_STOCKPILE = 0x59410004;
+
+ while (true) {
+ ::madvise(nullptr, options.BufferSize, MADV_STOCKPILE);
+ Sleep(options.Period);
+ }
+}
+
+} // namespace
+
+void ConfigureStockpile(const TStockpileOptions& options)
+{
+ static std::once_flag OnceFlag;
+ std::call_once(OnceFlag, [options] {
+ for (int i = 0; i < options.ThreadCount; i++) {
+ std::thread(RunStockpile, options).detach();
+ }
+ });
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace NYT
diff --git a/library/cpp/yt/stockpile/stockpile_other.cpp b/library/cpp/yt/stockpile/stockpile_other.cpp
new file mode 100644
index 0000000000..3495d9c1cb
--- /dev/null
+++ b/library/cpp/yt/stockpile/stockpile_other.cpp
@@ -0,0 +1,12 @@
+#include "stockpile.h"
+
+namespace NYT {
+
+////////////////////////////////////////////////////////////////////////////////
+
+void ConfigureStockpile(const TStockpileOptions& /*options*/)
+{ }
+
+////////////////////////////////////////////////////////////////////////////////
+
+} // namespace NYT
diff --git a/library/cpp/yt/stockpile/ya.make b/library/cpp/yt/stockpile/ya.make
new file mode 100644
index 0000000000..39d51aaf97
--- /dev/null
+++ b/library/cpp/yt/stockpile/ya.make
@@ -0,0 +1,11 @@
+LIBRARY()
+
+INCLUDE(${ARCADIA_ROOT}/library/cpp/yt/ya_cpp.make.inc)
+
+IF (OS_LINUX AND NOT SANITIZER_TYPE)
+ SRCS(stockpile_linux.cpp)
+ELSE()
+ SRCS(stockpile_other.cpp)
+ENDIF()
+
+END()