1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "host/libs/vm_manager/host_configuration.h"
18 
19 #include <string>
20 #include <utility>
21 #include <vector>
22 
23 #include <android-base/logging.h>
24 #include <sys/utsname.h>
25 
26 #include "common/libs/utils/users.h"
27 
28 namespace cuttlefish {
29 namespace vm_manager {
30 namespace {
31 
UserInGroup(const std::string & group,std::vector<std::string> * config_commands)32 __attribute__((unused)) bool UserInGroup(
33     const std::string& group, std::vector<std::string>* config_commands) {
34   if (!InGroup(group)) {
35     LOG(ERROR) << "User must be a member of " << group;
36     config_commands->push_back("# Add your user to the " + group + " group:");
37     config_commands->push_back("sudo usermod -aG " + group + " $USER");
38     return false;
39   }
40   return true;
41 }
42 
43 constexpr std::pair<int,int> invalid_linux_version = std::pair<int,int>();
44 
GetLinuxVersion()45 __attribute__((unused)) std::pair<int, int> GetLinuxVersion() {
46   struct utsname info;
47   if (!uname(&info)) {
48     char* digit = strtok(info.release, "+.-");
49     int major = atoi(digit);
50     if (digit) {
51       digit = strtok(NULL, "+.-");
52       int minor = atoi(digit);
53       return std::pair<int,int>{major, minor};
54     }
55   }
56   LOG(ERROR) << "Failed to detect Linux kernel version";
57   return invalid_linux_version;
58 }
59 
LinuxVersionAtLeast(std::vector<std::string> * config_commands,const std::pair<int,int> & version,int major,int minor)60 __attribute__((unused)) bool LinuxVersionAtLeast(
61     std::vector<std::string>* config_commands,
62     const std::pair<int, int>& version, int major, int minor) {
63   if (version.first > major ||
64       (version.first == major && version.second >= minor)) {
65     return true;
66   }
67 
68   LOG(ERROR) << "Kernel version must be >=" << major << "." << minor
69              << ", have " << version.first << "." << version.second;
70   config_commands->push_back("# Please upgrade your kernel to >=" +
71                              std::to_string(major) + "." +
72                              std::to_string(minor));
73   return false;
74 }
75 
76 } // namespace
77 
ValidateHostConfiguration(std::vector<std::string> * config_commands)78 bool ValidateHostConfiguration(std::vector<std::string>* config_commands) {
79 #ifdef __APPLE__
80   (void)config_commands;
81   return true;
82 #else
83   // if we can't detect the kernel version, just fail
84   auto version = GetLinuxVersion();
85   if (version == invalid_linux_version) {
86     return false;
87   }
88 
89   // the check for cvdnetwork needs to happen even if the user is not in kvm, so
90   // we can't just say UserInGroup("kvm") && UserInGroup("cvdnetwork")
91   auto in_cvdnetwork = UserInGroup("cvdnetwork", config_commands);
92 
93   // if we're in the virtaccess group this is likely to be a CrOS environment.
94   auto is_cros = InGroup("virtaccess");
95   if (is_cros) {
96     // relax the minimum kernel requirement slightly, as chromeos-4.4 has the
97     // needed backports to enable vhost_vsock
98     auto linux_ver_4_4 = LinuxVersionAtLeast(config_commands, version, 4, 4);
99     return in_cvdnetwork && linux_ver_4_4;
100   } else {
101     // this is regular Linux, so use the Debian group name and be more
102     // conservative with the kernel version check.
103     auto in_kvm = UserInGroup("kvm", config_commands);
104     auto linux_ver_4_8 = LinuxVersionAtLeast(config_commands, version, 4, 8);
105     return in_cvdnetwork && in_kvm && linux_ver_4_8;
106   }
107 #endif
108 }
109 
110 } // namespace vm_manager
111 } // namespace cuttlefish
112 
113