Support ChromeOS devices in group/kernel check

ChromeOS devices have vhost_vsock backported to support crosvm. This
means we can relax the kernel version check to 4.4. This also allows the
user to just be in virtaccess, rather than requiring them to be in kvm,
which is just the Debian name for the same thing.

Bug: 148628514
Change-Id: I15f82b7622c9082c86a4b17904ef97728398e61a
diff --git a/host/libs/vm_manager/vm_manager.cpp b/host/libs/vm_manager/vm_manager.cpp
index 40fa828..d2810da 100644
--- a/host/libs/vm_manager/vm_manager.cpp
+++ b/host/libs/vm_manager/vm_manager.cpp
@@ -117,7 +117,7 @@
   return true;
 }
 
-bool VmManager::LinuxVersionAtLeast4_8(std::vector<std::string>* config_commands) {
+std::pair<int,int> VmManager::GetLinuxVersion() {
   struct utsname info;
   if (!uname(&info)) {
     char* digit = strtok(info.release, "+.-");
@@ -125,24 +125,57 @@
     if (digit) {
       digit = strtok(NULL, "+.-");
       int minor = atoi(digit);
-      if (major > 4 || (major == 4 && minor >= 8)) {
-        return true;
-      }
+      return std::pair<int,int>{major, minor};
     }
   }
-  LOG(ERROR) << "Kernel version must be >=4.8";
-  config_commands->push_back("# Please upgrade your kernel to >=4.8");
+  LOG(ERROR) << "Failed to detect Linux kernel version";
+  return invalid_linux_version;
+}
+
+bool VmManager::LinuxVersionAtLeast(std::vector<std::string>* config_commands,
+                                    const std::pair<int,int>& version,
+                                    int major, int minor) {
+  if (version.first > major ||
+      (version.first == major && version.second >= minor)) {
+    return true;
+  }
+
+  LOG(ERROR) << "Kernel version must be >=" << major << "." << minor
+             << ", have " << version.first << "." << version.second;
+  config_commands->push_back("# Please upgrade your kernel to >=" +
+                             std::to_string(major) + "." +
+                             std::to_string(minor));
   return false;
 }
 
 bool VmManager::ValidateHostConfiguration(
     std::vector<std::string>* config_commands) const {
+  // if we can't detect the kernel version, just fail
+  auto version = VmManager::GetLinuxVersion();
+  if (version == invalid_linux_version) {
+    return false;
+  }
+
   // the check for cvdnetwork needs to happen even if the user is not in kvm, so
   // we can't just say UserInGroup("kvm") && UserInGroup("cvdnetwork")
-  auto in_kvm = VmManager::UserInGroup("kvm", config_commands);
   auto in_cvdnetwork = VmManager::UserInGroup("cvdnetwork", config_commands);
-  auto linux_ver_4_8 = VmManager::LinuxVersionAtLeast4_8(config_commands);
-  return in_kvm && in_cvdnetwork && linux_ver_4_8;
+
+  // if we're in the virtaccess group this is likely to be a CrOS environment.
+  auto is_cros = cvd::InGroup("virtaccess");
+  if (is_cros) {
+    // relax the minimum kernel requirement slightly, as chromeos-4.4 has the
+    // needed backports to enable vhost_vsock
+    auto linux_ver_4_4 =
+      VmManager::LinuxVersionAtLeast(config_commands, version, 4, 4);
+    return in_cvdnetwork && linux_ver_4_4;
+  } else {
+    // this is regular Linux, so use the Debian group name and be more
+    // conservative with the kernel version check.
+    auto in_kvm = VmManager::UserInGroup("kvm", config_commands);
+    auto linux_ver_4_8 =
+      VmManager::LinuxVersionAtLeast(config_commands, version, 4, 8);
+    return in_cvdnetwork && in_kvm && linux_ver_4_8;
+  }
 }
 
 void VmManager::WithFrontend(bool enabled) {
diff --git a/host/libs/vm_manager/vm_manager.h b/host/libs/vm_manager/vm_manager.h
index a14ad05..a86218c 100644
--- a/host/libs/vm_manager/vm_manager.h
+++ b/host/libs/vm_manager/vm_manager.h
@@ -60,7 +60,12 @@
  protected:
   static bool UserInGroup(const std::string& group,
                           std::vector<std::string>* config_commands);
-  static bool LinuxVersionAtLeast4_8(std::vector<std::string>* config_commands);
+  static constexpr std::pair<int,int> invalid_linux_version =
+    std::pair<int,int>();
+  static std::pair<int,int> GetLinuxVersion();
+  static bool LinuxVersionAtLeast(std::vector<std::string>* config_commands,
+                                  const std::pair<int,int>& version,
+                                  int major, int minor);
 
   const vsoc::CuttlefishConfig* config_;
   VmManager(const vsoc::CuttlefishConfig* config);