Provide a should_create argument to MemoryCGroup

Change-Id: I24ef9f9e062b8237e453b1174031028817e519be
Signed-off-by: James Kuszmaul <james.kuszmaul@bluerivertech.com>
diff --git a/aos/starter/subprocess.cc b/aos/starter/subprocess.cc
index a518c0b..36e780a 100644
--- a/aos/starter/subprocess.cc
+++ b/aos/starter/subprocess.cc
@@ -38,22 +38,25 @@
   gid_t rgid_, egid_, sgid_;
 };
 
-MemoryCGroup::MemoryCGroup(std::string_view name)
-    : cgroup_(absl::StrCat("/sys/fs/cgroup/memory/aos_", name)) {
-  Sudo sudo;
-  int ret = mkdir(cgroup_.c_str(), 0755);
+MemoryCGroup::MemoryCGroup(std::string_view name, Create should_create)
+    : cgroup_(absl::StrCat("/sys/fs/cgroup/memory/aos_", name)),
+      should_create_(should_create) {
+  if (should_create_ == Create::kDoCreate) {
+    Sudo sudo;
+    int ret = mkdir(cgroup_.c_str(), 0755);
 
-  if (ret != 0) {
-    if (errno == EEXIST) {
-      PCHECK(rmdir(cgroup_.c_str()) == 0)
-          << ": Failed to remove previous cgroup " << cgroup_;
-      ret = mkdir(cgroup_.c_str(), 0755);
+    if (ret != 0) {
+      if (errno == EEXIST) {
+        PCHECK(rmdir(cgroup_.c_str()) == 0)
+            << ": Failed to remove previous cgroup " << cgroup_;
+        ret = mkdir(cgroup_.c_str(), 0755);
+      }
     }
-  }
 
-  if (ret != 0) {
-    PLOG(FATAL) << ": Failed to create cgroup aos_" << cgroup_
-                << ", do you have permission?";
+    if (ret != 0) {
+      PLOG(FATAL) << ": Failed to create cgroup aos_" << cgroup_
+                  << ", do you have permission?";
+    }
   }
 }
 
@@ -61,20 +64,32 @@
   if (pid == 0) {
     pid = getpid();
   }
-  Sudo sudo;
-  util::WriteStringToFileOrDie(absl::StrCat(cgroup_, "/tasks").c_str(),
-                               std::to_string(pid));
+  if (should_create_ == Create::kDoCreate) {
+    Sudo sudo;
+    util::WriteStringToFileOrDie(absl::StrCat(cgroup_, "/tasks").c_str(),
+                                 std::to_string(pid));
+  } else {
+    util::WriteStringToFileOrDie(absl::StrCat(cgroup_, "/tasks").c_str(),
+                                 std::to_string(pid));
+  }
 }
 
 void MemoryCGroup::SetLimit(std::string_view limit_name, uint64_t limit_value) {
-  Sudo sudo;
-  util::WriteStringToFileOrDie(absl::StrCat(cgroup_, "/", limit_name).c_str(),
-                               std::to_string(limit_value));
+  if (should_create_ == Create::kDoCreate) {
+    Sudo sudo;
+    util::WriteStringToFileOrDie(absl::StrCat(cgroup_, "/", limit_name).c_str(),
+                                 std::to_string(limit_value));
+  } else {
+    util::WriteStringToFileOrDie(absl::StrCat(cgroup_, "/", limit_name).c_str(),
+                                 std::to_string(limit_value));
+  }
 }
 
 MemoryCGroup::~MemoryCGroup() {
-  Sudo sudo;
-  PCHECK(rmdir(absl::StrCat(cgroup_).c_str()) == 0);
+  if (should_create_ == Create::kDoCreate) {
+    Sudo sudo;
+    PCHECK(rmdir(absl::StrCat(cgroup_).c_str()) == 0);
+  }
 }
 
 SignalListener::SignalListener(aos::ShmEventLoop *loop,
diff --git a/aos/starter/subprocess.h b/aos/starter/subprocess.h
index 1efd56d..ce70644 100644
--- a/aos/starter/subprocess.h
+++ b/aos/starter/subprocess.h
@@ -43,7 +43,14 @@
 // Class to use the V1 cgroup API to limit memory usage.
 class MemoryCGroup {
  public:
-  MemoryCGroup(std::string_view name);
+  // Enum to control if MemoryCGroup should create the cgroup and remove it on
+  // its own, or if it should assume it already exists and just use it.
+  enum class Create {
+    kDoCreate,
+    kDoNotCreate,
+  };
+
+  MemoryCGroup(std::string_view name, Create should_create = Create::kDoCreate);
   ~MemoryCGroup();
 
   // Adds a thread ID to be managed by the cgroup.
@@ -54,6 +61,7 @@
 
  private:
   std::string cgroup_;
+  Create should_create_;
 };
 
 // Manages a running process, allowing starting and stopping, and restarting
@@ -124,6 +132,17 @@
     memory_cgroup_->SetLimit("memory.limit_in_bytes", limit);
   }
 
+  // Sets the cgroup and memory limit to a pre-existing cgroup which is
+  // externally managed.  This lets us configure the cgroup of an application
+  // without root access.
+  void SetExistingCgroupMemoryLimit(std::string_view name, size_t limit) {
+    if (!memory_cgroup_) {
+      memory_cgroup_ = std::make_unique<MemoryCGroup>(
+          name, MemoryCGroup::Create::kDoNotCreate);
+    }
+    memory_cgroup_->SetLimit("memory.limit_in_bytes", limit);
+  }
+
   // Observe a timing report message, and save it if it is relevant to us.
   // It is the responsibility of the caller to manage this, because the lifetime
   // of the Application itself is such that it cannot own Fetchers readily.