blob: 0454f43d48364b80fc54b8b02c64dd27c18e99a9 [file] [log] [blame]
Yifan Hong25c1eed2017-04-07 13:50:24 -07001/*
2 * Copyright (C) 2017 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
18#define LOG_TAG "libvintf"
19#include <android-base/logging.h>
20
21#include "RuntimeInfo.h"
22
23#include "CompatibilityMatrix.h"
24#include "parse_string.h"
25
Yifan Hong5075f452017-04-20 14:00:24 -070026#include <dirent.h>
Yifan Hong25c1eed2017-04-07 13:50:24 -070027#include <errno.h>
28#include <sys/utsname.h>
29#include <unistd.h>
30
Yifan Hong242eabf2017-04-20 14:06:26 -070031#include <fstream>
32#include <iostream>
33#include <sstream>
34
Yifan Hongf3029302017-04-12 17:23:49 -070035#include <cutils/properties.h>
Yifan Hong25c1eed2017-04-07 13:50:24 -070036#include <selinux/selinux.h>
37#include <zlib.h>
38
39#define PROC_CONFIG "/proc/config.gz"
40#define BUFFER_SIZE sysconf(_SC_PAGESIZE)
41
42namespace android {
43namespace vintf {
44
45static void removeTrailingComments(std::string *s) {
46 size_t sharpPos = s->find('#');
47 if (sharpPos != std::string::npos) {
48 s->erase(sharpPos);
49 }
50}
51static void trim(std::string *s) {
52 auto l = s->begin();
53 for (; l != s->end() && std::isspace(*l); ++l);
54 s->erase(s->begin(), l);
55 auto r = s->rbegin();
56 for (; r != s->rend() && std::isspace(*r); ++r);
57 s->erase(r.base(), s->end());
58}
59
60struct RuntimeInfoFetcher {
61 RuntimeInfoFetcher(RuntimeInfo *ki) : mRuntimeInfo(ki) { }
62 status_t fetchAllInformation();
63private:
64 void streamConfig(const char *buf, size_t len);
65 void parseConfig(std::string *s);
66 status_t fetchVersion();
67 status_t fetchKernelConfigs();
68 status_t fetchCpuInfo();
69 status_t fetchKernelSepolicyVers();
70 status_t fetchSepolicyFiles();
Yifan Hongf3029302017-04-12 17:23:49 -070071 status_t fetchAvb();
Yifan Hong25c1eed2017-04-07 13:50:24 -070072 status_t parseKernelVersion();
73 RuntimeInfo *mRuntimeInfo;
74 std::string mRemaining;
75};
76
77// decompress /proc/config.gz and read its contents.
78status_t RuntimeInfoFetcher::fetchKernelConfigs() {
79 gzFile f = gzopen(PROC_CONFIG, "rb");
80 if (f == NULL) {
81 LOG(ERROR) << "Could not open /proc/config.gz: " << errno;
82 return -errno;
83 }
84
85 char buf[BUFFER_SIZE];
86 int len;
87 while ((len = gzread(f, buf, sizeof buf)) > 0) {
88 streamConfig(buf, len);
89 }
90 status_t err = OK;
91 if (len < 0) {
92 int errnum;
93 const char *errmsg = gzerror(f, &errnum);
94 LOG(ERROR) << "Could not read /proc/config.gz: " << errmsg;
95 err = (errnum == Z_ERRNO ? -errno : errnum);
96 }
97
98 // stream a "\n" to end the stream to finish the last line.
99 streamConfig("\n", 1 /* sizeof "\n" */);
100
101 gzclose(f);
102 return err;
103}
104
105void RuntimeInfoFetcher::parseConfig(std::string *s) {
106 removeTrailingComments(s);
107 trim(s);
108 if (s->empty()) {
109 return;
110 }
111 size_t equalPos = s->find('=');
112 if (equalPos == std::string::npos) {
113 LOG(WARNING) << "Unrecognized line in /proc/config.gz: " << *s;
114 return;
115 }
116 std::string key = s->substr(0, equalPos);
117 std::string value = s->substr(equalPos + 1);
118 if (!mRuntimeInfo->mKernelConfigs.emplace(std::move(key), std::move(value)).second) {
119 LOG(WARNING) << "Duplicated key in /proc/config.gz: " << s->substr(0, equalPos);
120 return;
121 }
122}
123
124void RuntimeInfoFetcher::streamConfig(const char *buf, size_t len) {
125 const char *begin = buf;
126 const char *end = buf;
127 const char *stop = buf + len;
128 while (end < stop) {
129 if (*end == '\n') {
130 mRemaining.insert(mRemaining.size(), begin, end - begin);
131 parseConfig(&mRemaining);
132 mRemaining.clear();
133 begin = end + 1;
134 }
135 end++;
136 }
137 mRemaining.insert(mRemaining.size(), begin, end - begin);
138}
139
140status_t RuntimeInfoFetcher::fetchCpuInfo() {
141 // TODO implement this; 32-bit and 64-bit has different format.
Yifan Hong242eabf2017-04-20 14:06:26 -0700142 std::ifstream in{"/proc/cpuinfo"};
143 if (!in.is_open()) {
144 LOG(WARNING) << "Cannot read /proc/cpuinfo";
145 return UNKNOWN_ERROR;
146 }
147 std::stringstream sstream;
148 sstream << in.rdbuf();
149 mRuntimeInfo->mCpuInfo = sstream.str();
Yifan Hong25c1eed2017-04-07 13:50:24 -0700150 return OK;
151}
152
153status_t RuntimeInfoFetcher::fetchKernelSepolicyVers() {
154 int pv;
155#ifdef LIBVINTF_TARGET
156 pv = security_policyvers();
157#else
158 pv = 0;
159#endif
160 if (pv < 0) {
161 return pv;
162 }
163 mRuntimeInfo->mKernelSepolicyVersion = pv;
164 return OK;
165}
166
167status_t RuntimeInfoFetcher::fetchVersion() {
168 struct utsname buf;
169 if (uname(&buf)) {
170 return -errno;
171 }
172 mRuntimeInfo->mOsName = buf.sysname;
173 mRuntimeInfo->mNodeName = buf.nodename;
174 mRuntimeInfo->mOsRelease = buf.release;
175 mRuntimeInfo->mOsVersion = buf.version;
176 mRuntimeInfo->mHardwareId = buf.machine;
177
178 status_t err = parseKernelVersion();
179 if (err != OK) {
180 LOG(ERROR) << "Could not parse kernel version from \""
181 << mRuntimeInfo->mOsRelease << "\"";
182 }
183 return err;
184}
185
186status_t RuntimeInfoFetcher::parseKernelVersion() {
187 auto pos = mRuntimeInfo->mOsRelease.find('.');
188 if (pos == std::string::npos) {
189 return UNKNOWN_ERROR;
190 }
191 pos = mRuntimeInfo->mOsRelease.find('.', pos + 1);
192 if (pos == std::string::npos) {
193 return UNKNOWN_ERROR;
194 }
195 pos = mRuntimeInfo->mOsRelease.find_first_not_of("0123456789", pos + 1);
196 // no need to check pos == std::string::npos, because substr will handle this
197 if (!parse(mRuntimeInfo->mOsRelease.substr(0, pos), &mRuntimeInfo->mKernelVersion)) {
198 return UNKNOWN_ERROR;
199 }
200 return OK;
201}
202
Yifan Hong5075f452017-04-20 14:00:24 -0700203static const std::string gSepolicyFilesDir{"/vendor/etc/selinux/"};
204
Yifan Hongfa2b18b2017-04-12 19:40:00 -0700205// Grab sepolicy file paths.
Yifan Hong25c1eed2017-04-07 13:50:24 -0700206status_t RuntimeInfoFetcher::fetchSepolicyFiles() {
Yifan Hong5075f452017-04-20 14:00:24 -0700207 mRuntimeInfo->mSepolicyFilePaths.clear();
208 DIR *dir = opendir(gSepolicyFilesDir.c_str());
209 if (dir == NULL) {
210 LOG(ERROR) << "Could not open directory \""
211 << gSepolicyFilesDir << "\"";
212 return -errno;
213 }
214 struct dirent *e;
215 status_t status = OK;
216 errno = 0;
217 while ((e = readdir(dir))) {
218 if (e->d_type == DT_REG || e->d_type == DT_LNK) {
219 mRuntimeInfo->mSepolicyFilePaths.push_back(
220 gSepolicyFilesDir + e->d_name);
221 }
222 }
223 if (errno != 0) {
224 LOG(ERROR) << "Could not read directory \""
225 << gSepolicyFilesDir << "\"";
226 status = -errno;
227 }
228 (void)closedir(dir);
229 return status;
Yifan Hong25c1eed2017-04-07 13:50:24 -0700230}
231
Yifan Hongf3029302017-04-12 17:23:49 -0700232status_t RuntimeInfoFetcher::fetchAvb() {
233 char prop[PROPERTY_VALUE_MAX];
234 property_get("ro.boot.vbmeta.avb_version", prop, "0.0");
Yifan Hong881a9e452017-04-27 19:31:13 -0700235 if (!parse(prop, &mRuntimeInfo->mBootVbmetaAvbVersion)) {
Yifan Hongf3029302017-04-12 17:23:49 -0700236 return UNKNOWN_ERROR;
237 }
238 property_get("ro.boot.avb_version", prop, "0.0");
Yifan Hong881a9e452017-04-27 19:31:13 -0700239 if (!parse(prop, &mRuntimeInfo->mBootAvbVersion)) {
Yifan Hongf3029302017-04-12 17:23:49 -0700240 return UNKNOWN_ERROR;
241 }
242 return OK;
243}
244
Yifan Hong25c1eed2017-04-07 13:50:24 -0700245status_t RuntimeInfoFetcher::fetchAllInformation() {
246 status_t err;
247 if ((err = fetchVersion()) != OK) {
Yifan Hong4e542312017-04-27 19:18:28 -0700248 LOG(ERROR) << "Cannot fetch or parse /proc/version: " << strerror(-err);
Yifan Hong25c1eed2017-04-07 13:50:24 -0700249 }
250 if ((err = fetchKernelConfigs()) != OK) {
Yifan Hong4e542312017-04-27 19:18:28 -0700251 LOG(ERROR) << "Cannot fetch or parse /proc/config.gz: " << strerror(-err);
Yifan Hong25c1eed2017-04-07 13:50:24 -0700252 }
253 if ((err = fetchCpuInfo()) != OK) {
Yifan Hong4e542312017-04-27 19:18:28 -0700254 LOG(ERROR) << "Cannot fetch /proc/cpuinfo: " << strerror(-err);
Yifan Hong25c1eed2017-04-07 13:50:24 -0700255 }
256 if ((err = fetchKernelSepolicyVers()) != OK) {
Yifan Hong4e542312017-04-27 19:18:28 -0700257 LOG(ERROR) << "Cannot fetch kernel sepolicy version: " << strerror(-err);
Yifan Hong25c1eed2017-04-07 13:50:24 -0700258 }
259 if ((err = fetchSepolicyFiles()) != OK) {
Yifan Hong4e542312017-04-27 19:18:28 -0700260 LOG(ERROR) << "Cannot fetch sepolicy file paths: " << strerror(-err);
Yifan Hong25c1eed2017-04-07 13:50:24 -0700261 }
Yifan Hongf3029302017-04-12 17:23:49 -0700262 if ((err = fetchAvb()) != OK) {
Yifan Hong4e542312017-04-27 19:18:28 -0700263 LOG(ERROR) << "Cannot fetch sepolicy avb version: " << strerror(-err);
Yifan Hongf3029302017-04-12 17:23:49 -0700264 }
Yifan Hong25c1eed2017-04-07 13:50:24 -0700265 return OK;
266}
267
268status_t RuntimeInfo::fetchAllInformation() {
269 return RuntimeInfoFetcher(this).fetchAllInformation();
270}
271
272} // namespace vintf
273} // namespace android