Launcher checks user configuration before running

Bug: 110476897
Test: local
Change-Id: Ifdc26498f8dd3597b49678476f8378edb23bb243
Merged-In: Ifdc26498f8dd3597b49678476f8378edb23bb243
(cherry picked from commit 5020a66d6a8f76845753df717db08a185ee8a9ba)
diff --git a/host/commands/launch/main.cc b/host/commands/launch/main.cc
index 58b43f0..1a077f8 100644
--- a/host/commands/launch/main.cc
+++ b/host/commands/launch/main.cc
@@ -26,6 +26,7 @@
 
 #include <algorithm>
 #include <functional>
+#include <iostream>
 #include <fstream>
 #include <iomanip>
 #include <memory>
@@ -486,7 +487,7 @@
   return true;
 }
 
-bool SetUpGlobalConfiguration(
+bool InitializeCuttlefishConfiguration(
     const cvd::BootImageUnpacker& boot_image_unpacker) {
   auto& memory_layout = *vsoc::VSoCMemoryLayout::Get();
   auto config = vsoc::CuttlefishConfig::Get();
@@ -665,30 +666,43 @@
 int main(int argc, char** argv) {
   ::android::base::InitLogging(argv, android::base::StderrLogger);
   if (!ParseCommandLineFlags(argc, argv)) {
-    return -1;
+    return 1;
   }
 
   auto boot_img_unpacker = cvd::BootImageUnpacker::FromImage(FLAGS_boot_image);
   auto vm_manager = vm_manager::VmManager::Get();
 
+  // Check host configuration
+  std::vector<std::string> config_commands;
+  if (!vm_manager->ValidateHostConfiguration(&config_commands)) {
+    LOG(ERROR) << "Validation of user configuration failed";
+    std::cout << "Execute the following to correctly configure:" << std::endl;
+    for (auto& command : config_commands) {
+      std::cout << "  " << command << std::endl;
+    }
+    std::cout << "You may need to logout for the changes to take effect"
+              << std::endl;
+    return 2;
+  }
+
   // Do this early so that the config object is ready for anything that needs it
-  if (!SetUpGlobalConfiguration(*boot_img_unpacker)) {
-    return -1;
+  if (!InitializeCuttlefishConfiguration(*boot_img_unpacker)) {
+    return 3;
   }
 
   if (!vm_manager->EnsureInstanceDirExists()) {
     LOG(ERROR) << "Failed to create instance directory: " << FLAGS_instance_dir;
-    return -1;
+    return 4;
   }
 
   if (!vm_manager->CleanPriorFiles()) {
     LOG(ERROR) << "Failed to clean prior files";
-    return -1;
+    return 5;
   }
 
   if (!UnpackBootImage(*boot_img_unpacker)) {
     LOG(ERROR) << "Failed to unpack boot image";
-    return -1;
+    return 6;
   }
 
   if (!WriteCuttlefishEnvironment()) {
@@ -698,7 +712,7 @@
   auto config = vsoc::CuttlefishConfig::Get();
   // Save the config object before starting any host process
   if (!config->SaveToFile(GetConfigFile())) {
-    return -1;
+    return 7;
   }
 
   LOG(INFO) << "The following files contain useful debugging information:";
@@ -754,7 +768,7 @@
   // Start the guest VM
   if (!vm_manager->Start()) {
     LOG(FATAL) << "Unable to start vm_manager";
-    return -1;
+    return 8;
   }
 
   LaunchSocketForwardProxyIfEnabled();
diff --git a/host/libs/vm_manager/libvirt_manager.cpp b/host/libs/vm_manager/libvirt_manager.cpp
index 6b499c8..7332834 100644
--- a/host/libs/vm_manager/libvirt_manager.cpp
+++ b/host/libs/vm_manager/libvirt_manager.cpp
@@ -30,6 +30,7 @@
 
 #include "common/libs/utils/files.h"
 #include "common/libs/utils/subprocess.h"
+#include "common/libs/utils/users.h"
 #include "host/libs/config/cuttlefish_config.h"
 
 DEFINE_string(hypervisor_uri, "qemu:///system", "Hypervisor cannonical uri.");
@@ -411,4 +412,9 @@
   }
   return true;
 }
+
+bool LibvirtManager::ValidateHostConfiguration(
+    std::vector<std::string>* config_commands) const {
+  return VmManager::UserInGroup("libvirt", config_commands);
+}
 }  // namespace vm_manager
diff --git a/host/libs/vm_manager/libvirt_manager.h b/host/libs/vm_manager/libvirt_manager.h
index d773eff..2ec81af 100644
--- a/host/libs/vm_manager/libvirt_manager.h
+++ b/host/libs/vm_manager/libvirt_manager.h
@@ -29,6 +29,9 @@
 
   bool EnsureInstanceDirExists() const override;
   bool CleanPriorFiles() const override;
+
+  bool ValidateHostConfiguration(
+      std::vector<std::string>* config_commands) const override;
 };
 
 }  // namespace vm_manager
diff --git a/host/libs/vm_manager/qemu_manager.cpp b/host/libs/vm_manager/qemu_manager.cpp
index 1630f5e..b1620b2 100644
--- a/host/libs/vm_manager/qemu_manager.cpp
+++ b/host/libs/vm_manager/qemu_manager.cpp
@@ -35,6 +35,7 @@
 
 #include "common/libs/utils/files.h"
 #include "common/libs/utils/subprocess.h"
+#include "common/libs/utils/users.h"
 #include "host/libs/config/cuttlefish_config.h"
 
 DEFINE_string(qemu_binary,
@@ -177,4 +178,13 @@
   }
   return true;
 }
+
+bool QemuManager::ValidateHostConfiguration(
+    std::vector<std::string>* config_commands) const {
+  // the check for cvdnetwork needs to happen even if the user is not in kvm, so
+  // we cant just say UserInGroup("kvm") && UserInGroup("cvdnetwork")
+  auto in_kvm = VmManager::UserInGroup("kvm", config_commands);
+  auto in_cvdnetwork = VmManager::UserInGroup("cvdnetwork", config_commands);
+  return in_kvm && in_cvdnetwork;
+}
 }  // namespace vm_manager
diff --git a/host/libs/vm_manager/qemu_manager.h b/host/libs/vm_manager/qemu_manager.h
index 8180822..2c9ac14 100644
--- a/host/libs/vm_manager/qemu_manager.h
+++ b/host/libs/vm_manager/qemu_manager.h
@@ -31,6 +31,9 @@
 
   bool EnsureInstanceDirExists() const override;
   bool CleanPriorFiles() const override;
+
+  bool ValidateHostConfiguration(
+      std::vector<std::string>* config_commands) const override;
 };
 
 }  // namespace vm_manager
diff --git a/host/libs/vm_manager/vm_manager.cpp b/host/libs/vm_manager/vm_manager.cpp
index 961556c..0fc69c5 100644
--- a/host/libs/vm_manager/vm_manager.cpp
+++ b/host/libs/vm_manager/vm_manager.cpp
@@ -16,6 +16,9 @@
 
 #include "host/libs/vm_manager/vm_manager.h"
 
+#include <glog/logging.h>
+
+#include "common/libs/utils/users.h"
 #include "host/libs/config/cuttlefish_config.h"
 #include "host/libs/vm_manager/libvirt_manager.h"
 #include "host/libs/vm_manager/qemu_manager.h"
@@ -29,4 +32,14 @@
   return vm_manager;
 }
 
+bool VmManager::UserInGroup(const std::string& group,
+                            std::vector<std::string>* config_commands) {
+  if (!cvd::InGroup(group)) {
+    LOG(ERROR) << "User must be a member of " << group;
+    config_commands->push_back("# Add your user to the " + group + " group:");
+    config_commands->push_back("sudo usermod -aG " + group + " $USER");
+    return false;
+  }
+  return true;
+}
 }  // namespace vm_manager
diff --git a/host/libs/vm_manager/vm_manager.h b/host/libs/vm_manager/vm_manager.h
index f2e0122..4dffba7 100644
--- a/host/libs/vm_manager/vm_manager.h
+++ b/host/libs/vm_manager/vm_manager.h
@@ -17,6 +17,7 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 namespace vm_manager {
 
@@ -34,6 +35,13 @@
 
   virtual bool EnsureInstanceDirExists() const = 0;
   virtual bool CleanPriorFiles() const = 0;
+
+  virtual bool ValidateHostConfiguration(
+      std::vector<std::string>* config_commands) const = 0;
+
+ protected:
+  static bool UserInGroup(const std::string& group,
+                          std::vector<std::string>* config_commands);
 };
 
 }  // namespace vm_manager