blob: 3b094c9839478cbbf9b1fd30d1557f2eba6e34b0 [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 Hongf3029302017-04-12 17:23:49 -070031#include <cutils/properties.h>
Yifan Hong25c1eed2017-04-07 13:50:24 -070032#include <selinux/selinux.h>
33#include <zlib.h>
34
35#define PROC_CONFIG "/proc/config.gz"
36#define BUFFER_SIZE sysconf(_SC_PAGESIZE)
37
38namespace android {
39namespace vintf {
40
41static void removeTrailingComments(std::string *s) {
42 size_t sharpPos = s->find('#');
43 if (sharpPos != std::string::npos) {
44 s->erase(sharpPos);
45 }
46}
47static void trim(std::string *s) {
48 auto l = s->begin();
49 for (; l != s->end() && std::isspace(*l); ++l);
50 s->erase(s->begin(), l);
51 auto r = s->rbegin();
52 for (; r != s->rend() && std::isspace(*r); ++r);
53 s->erase(r.base(), s->end());
54}
55
56struct RuntimeInfoFetcher {
57 RuntimeInfoFetcher(RuntimeInfo *ki) : mRuntimeInfo(ki) { }
58 status_t fetchAllInformation();
59private:
60 void streamConfig(const char *buf, size_t len);
61 void parseConfig(std::string *s);
62 status_t fetchVersion();
63 status_t fetchKernelConfigs();
64 status_t fetchCpuInfo();
65 status_t fetchKernelSepolicyVers();
66 status_t fetchSepolicyFiles();
Yifan Hongf3029302017-04-12 17:23:49 -070067 status_t fetchAvb();
Yifan Hong25c1eed2017-04-07 13:50:24 -070068 status_t parseKernelVersion();
69 RuntimeInfo *mRuntimeInfo;
70 std::string mRemaining;
71};
72
73// decompress /proc/config.gz and read its contents.
74status_t RuntimeInfoFetcher::fetchKernelConfigs() {
75 gzFile f = gzopen(PROC_CONFIG, "rb");
76 if (f == NULL) {
77 LOG(ERROR) << "Could not open /proc/config.gz: " << errno;
78 return -errno;
79 }
80
81 char buf[BUFFER_SIZE];
82 int len;
83 while ((len = gzread(f, buf, sizeof buf)) > 0) {
84 streamConfig(buf, len);
85 }
86 status_t err = OK;
87 if (len < 0) {
88 int errnum;
89 const char *errmsg = gzerror(f, &errnum);
90 LOG(ERROR) << "Could not read /proc/config.gz: " << errmsg;
91 err = (errnum == Z_ERRNO ? -errno : errnum);
92 }
93
94 // stream a "\n" to end the stream to finish the last line.
95 streamConfig("\n", 1 /* sizeof "\n" */);
96
97 gzclose(f);
98 return err;
99}
100
101void RuntimeInfoFetcher::parseConfig(std::string *s) {
102 removeTrailingComments(s);
103 trim(s);
104 if (s->empty()) {
105 return;
106 }
107 size_t equalPos = s->find('=');
108 if (equalPos == std::string::npos) {
109 LOG(WARNING) << "Unrecognized line in /proc/config.gz: " << *s;
110 return;
111 }
112 std::string key = s->substr(0, equalPos);
113 std::string value = s->substr(equalPos + 1);
114 if (!mRuntimeInfo->mKernelConfigs.emplace(std::move(key), std::move(value)).second) {
115 LOG(WARNING) << "Duplicated key in /proc/config.gz: " << s->substr(0, equalPos);
116 return;
117 }
118}
119
120void RuntimeInfoFetcher::streamConfig(const char *buf, size_t len) {
121 const char *begin = buf;
122 const char *end = buf;
123 const char *stop = buf + len;
124 while (end < stop) {
125 if (*end == '\n') {
126 mRemaining.insert(mRemaining.size(), begin, end - begin);
127 parseConfig(&mRemaining);
128 mRemaining.clear();
129 begin = end + 1;
130 }
131 end++;
132 }
133 mRemaining.insert(mRemaining.size(), begin, end - begin);
134}
135
136status_t RuntimeInfoFetcher::fetchCpuInfo() {
137 // TODO implement this; 32-bit and 64-bit has different format.
138 return OK;
139}
140
141status_t RuntimeInfoFetcher::fetchKernelSepolicyVers() {
142 int pv;
143#ifdef LIBVINTF_TARGET
144 pv = security_policyvers();
145#else
146 pv = 0;
147#endif
148 if (pv < 0) {
149 return pv;
150 }
151 mRuntimeInfo->mKernelSepolicyVersion = pv;
152 return OK;
153}
154
155status_t RuntimeInfoFetcher::fetchVersion() {
156 struct utsname buf;
157 if (uname(&buf)) {
158 return -errno;
159 }
160 mRuntimeInfo->mOsName = buf.sysname;
161 mRuntimeInfo->mNodeName = buf.nodename;
162 mRuntimeInfo->mOsRelease = buf.release;
163 mRuntimeInfo->mOsVersion = buf.version;
164 mRuntimeInfo->mHardwareId = buf.machine;
165
166 status_t err = parseKernelVersion();
167 if (err != OK) {
168 LOG(ERROR) << "Could not parse kernel version from \""
169 << mRuntimeInfo->mOsRelease << "\"";
170 }
171 return err;
172}
173
174status_t RuntimeInfoFetcher::parseKernelVersion() {
175 auto pos = mRuntimeInfo->mOsRelease.find('.');
176 if (pos == std::string::npos) {
177 return UNKNOWN_ERROR;
178 }
179 pos = mRuntimeInfo->mOsRelease.find('.', pos + 1);
180 if (pos == std::string::npos) {
181 return UNKNOWN_ERROR;
182 }
183 pos = mRuntimeInfo->mOsRelease.find_first_not_of("0123456789", pos + 1);
184 // no need to check pos == std::string::npos, because substr will handle this
185 if (!parse(mRuntimeInfo->mOsRelease.substr(0, pos), &mRuntimeInfo->mKernelVersion)) {
186 return UNKNOWN_ERROR;
187 }
188 return OK;
189}
190
Yifan Hong5075f452017-04-20 14:00:24 -0700191static const std::string gSepolicyFilesDir{"/vendor/etc/selinux/"};
192
Yifan Hongfa2b18b2017-04-12 19:40:00 -0700193// Grab sepolicy file paths.
Yifan Hong25c1eed2017-04-07 13:50:24 -0700194status_t RuntimeInfoFetcher::fetchSepolicyFiles() {
Yifan Hong5075f452017-04-20 14:00:24 -0700195 mRuntimeInfo->mSepolicyFilePaths.clear();
196 DIR *dir = opendir(gSepolicyFilesDir.c_str());
197 if (dir == NULL) {
198 LOG(ERROR) << "Could not open directory \""
199 << gSepolicyFilesDir << "\"";
200 return -errno;
201 }
202 struct dirent *e;
203 status_t status = OK;
204 errno = 0;
205 while ((e = readdir(dir))) {
206 if (e->d_type == DT_REG || e->d_type == DT_LNK) {
207 mRuntimeInfo->mSepolicyFilePaths.push_back(
208 gSepolicyFilesDir + e->d_name);
209 }
210 }
211 if (errno != 0) {
212 LOG(ERROR) << "Could not read directory \""
213 << gSepolicyFilesDir << "\"";
214 status = -errno;
215 }
216 (void)closedir(dir);
217 return status;
Yifan Hong25c1eed2017-04-07 13:50:24 -0700218}
219
Yifan Hongf3029302017-04-12 17:23:49 -0700220status_t RuntimeInfoFetcher::fetchAvb() {
221 char prop[PROPERTY_VALUE_MAX];
222 property_get("ro.boot.vbmeta.avb_version", prop, "0.0");
223 if (!parse(prop, &mRuntimeInfo->mAvbBootVersion)) {
224 return UNKNOWN_ERROR;
225 }
226 property_get("ro.boot.avb_version", prop, "0.0");
227 if (!parse(prop, &mRuntimeInfo->mAvbInitVersion)) {
228 return UNKNOWN_ERROR;
229 }
230 return OK;
231}
232
Yifan Hong25c1eed2017-04-07 13:50:24 -0700233status_t RuntimeInfoFetcher::fetchAllInformation() {
234 status_t err;
235 if ((err = fetchVersion()) != OK) {
236 return err;
237 }
238 if ((err = fetchKernelConfigs()) != OK) {
239 return err;
240 }
241 if ((err = fetchCpuInfo()) != OK) {
242 return err;
243 }
244 if ((err = fetchKernelSepolicyVers()) != OK) {
245 return err;
246 }
247 if ((err = fetchSepolicyFiles()) != OK) {
248 return err;
249 }
Yifan Hongf3029302017-04-12 17:23:49 -0700250 if ((err = fetchAvb()) != OK) {
251 return err;
252 }
Yifan Hong25c1eed2017-04-07 13:50:24 -0700253 return OK;
254}
255
256status_t RuntimeInfo::fetchAllInformation() {
257 return RuntimeInfoFetcher(this).fetchAllInformation();
258}
259
260} // namespace vintf
261} // namespace android