blob: 3c547c4b0d62bd490e151f6a329a819e3986a751 [file] [log] [blame]
Dylan Reid6b590e62016-10-27 19:10:53 -07001// Copyright 2016 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "container_utils/container_config_parser.h"
6
7#include <unistd.h>
8
9#include <string>
10#include <vector>
11
12#include <base/json/json_reader.h>
13#include <base/values.h>
14
15namespace container_utils {
16
17namespace {
18
19// Parses basic platform configuration.
20bool ParsePlatformConfig(const base::DictionaryValue& config_root_dict,
21 OciConfigPtr const& config_out) {
22 // |platform_dict| stays owned by |config_root_dict|
23 const base::DictionaryValue* platform_dict = nullptr;
24 if (!config_root_dict.GetDictionary("platform", &platform_dict)) {
25 LOG(ERROR) << "Fail to parse platform dictionary from config";
26 return false;
27 }
28
29 if (!platform_dict->GetString("os", &config_out->platform.os)) {
30 return false;
31 }
32
33 if (!platform_dict->GetString("arch", &config_out->platform.arch)) {
34 return false;
35 }
36
37 return true;
38}
39
40// Parses root fs info.
41bool ParseRootFileSystemConfig(const base::DictionaryValue& config_root_dict,
42 OciConfigPtr const& config_out) {
43 // |rootfs_dict| stays owned by |config_root_dict|
44 const base::DictionaryValue* rootfs_dict = nullptr;
45 if (!config_root_dict.GetDictionary("root", &rootfs_dict)) {
46 LOG(ERROR) << "Fail to parse rootfs dictionary from config";
47 return false;
48 }
49 if (!rootfs_dict->GetString("path", &config_out->root.path)) {
50 LOG(ERROR) << "Fail to get rootfs path from config";
51 return false;
52 }
Dylan Reid6d650742016-11-02 23:10:38 -070053 rootfs_dict->GetBoolean("readonly", &config_out->root.readonly);
Dylan Reid6b590e62016-10-27 19:10:53 -070054 return true;
55}
56
57// Fills |config_out| with information about the main process to run in the
58// container and the user it should be run as.
59bool ParseProcessConfig(const base::DictionaryValue& config_root_dict,
60 OciConfigPtr const& config_out) {
61 // |process_dict| stays owned by |config_root_dict|
62 const base::DictionaryValue* process_dict = nullptr;
63 if (!config_root_dict.GetDictionary("process", &process_dict)) {
64 LOG(ERROR) << "Fail to get main process from config";
65 return false;
66 }
67 process_dict->GetBoolean("terminal", &config_out->process.terminal);
68 // |user_dict| stays owned by |process_dict|
69 const base::DictionaryValue* user_dict = nullptr;
70 if (!process_dict->GetDictionary("user", &user_dict)) {
71 LOG(ERROR) << "Failed to get user info from config";
72 return false;
73 }
74 int uid;
75 if (!user_dict->GetInteger("uid", &uid)) {
76 LOG(ERROR) << "Failed to get uid info from config";
77 return false;
78 }
79 config_out->process.user.uid = uid;
80 int gid;
81 if (!user_dict->GetInteger("gid", &gid)) {
82 LOG(ERROR) << "Failed to get gid info from config";
83 return false;
84 }
85 config_out->process.user.gid = gid;
86 // |args_list| stays owned by |process_dict|
87 const base::ListValue* args_list = nullptr;
88 if (!process_dict->GetList("args", &args_list)) {
89 LOG(ERROR) << "Fail to get main process args from config";
90 return false;
91 }
92 size_t num_args = args_list->GetSize();
93 for (size_t i = 0; i < num_args; ++i) {
94 std::string arg;
95 if (!args_list->GetString(i, &arg)) {
96 LOG(ERROR) << "Fail to get process args from config";
97 return false;
98 }
99 config_out->process.args.push_back(arg);
100 }
101 // |env_list| stays owned by |process_dict|
102 const base::ListValue* env_list = nullptr;
103 if (process_dict->GetList("env", &env_list)) {
104 size_t num_env = env_list->GetSize();
105 for (size_t i = 0; i < num_env; ++i) {
106 std::string env;
107 if (!env_list->GetString(i, &env)) {
108 LOG(ERROR) << "Fail to get process env from config";
109 return false;
110 }
111 config_out->process.env.push_back(env);
112 }
113 }
114 if (!process_dict->GetString("cwd", &config_out->process.cwd)) {
115 LOG(ERROR) << "failed to get cwd of process";
116 return false;
117 }
118
119 return true;
120}
121
122// Parses the 'mounts' field. The necessary mounts for running the container
123// are specified here.
124bool ParseMounts(const base::DictionaryValue& config_root_dict,
125 OciConfigPtr const& config_out) {
126 // |config_mounts_list| stays owned by |config_root_dict|
127 const base::ListValue* config_mounts_list = nullptr;
128 if (!config_root_dict.GetList("mounts", &config_mounts_list)) {
129 LOG(ERROR) << "Fail to get mounts from config dictionary";
130 return false;
131 }
132
133 for (size_t i = 0; i < config_mounts_list->GetSize(); ++i) {
134 const base::DictionaryValue* mount_dict;
135 if (!config_mounts_list->GetDictionary(i, &mount_dict)) {
136 LOG(ERROR) << "Fail to get mount item " << i;
137 return false;
138 }
139 OciMount mount;
140 if (!mount_dict->GetString("destination", &mount.destination)) {
141 LOG(ERROR) << "Fail to get mount path for mount " << i;
142 return false;
143 }
Dylan Reid6d650742016-11-02 23:10:38 -0700144 if (!mount_dict->GetString("type", &mount.type)) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700145 LOG(ERROR) << "Fail to get mount type for mount " << i;
146 return false;
147 }
148 if (!mount_dict->GetString("source", &mount.source)) {
149 LOG(ERROR) << "Fail to get mount source for mount " << i;
150 return false;
151 }
152
153 // |options| are owned by |mount_dict|
154 const base::ListValue* options = nullptr;
155 if (mount_dict->GetList("options", &options)) {
156 for (size_t j = 0; j < options->GetSize(); ++j) {
157 std::string this_opt;
158 if (!options->GetString(j, &this_opt)) {
159 LOG(ERROR) << "Fail to get option " << j << " from mount options";
160 return false;
161 }
162 mount.options.push_back(this_opt);
163 }
164 }
165
166 config_out->mounts.push_back(mount);
167 }
168 return true;
169}
170
171// Parse the list of device nodes that the container needs to run.
172bool ParseDeviceList(const base::DictionaryValue& linux_dict,
173 OciConfigPtr const& config_out) {
174 // |device_list| is owned by |linux_dict|
175 const base::ListValue* device_list = nullptr;
176 if (!linux_dict.GetList("devices", &device_list)) {
177 LOG(ERROR) << "Fail to get device list";
178 return false;
179 }
180 size_t num_devices = device_list->GetSize();
181 for (size_t i = 0; i < num_devices; ++i) {
182 OciLinuxDevice device;
183
184 const base::DictionaryValue* dev;
185 if (!device_list->GetDictionary(i, &dev)) {
186 LOG(ERROR) << "Fail to get device " << i;
187 return false;
188 }
189 std::string path;
190 if (!dev->GetString("path", &device.path)) {
191 LOG(ERROR) << "Fail to get path for dev";
192 return false;
193 }
Dylan Reid6d650742016-11-02 23:10:38 -0700194 if (!dev->GetString("type", &device.type)) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700195 LOG(ERROR) << "Fail to get type for " << device.path;
196 return false;
197 }
198 int major = 0;
199 dev->GetInteger("major", &major);
200 device.major = major;
201 int minor = 0;
202 dev->GetInteger("minor", &minor);
203 device.minor = minor;
Dylan Reid6d650742016-11-02 23:10:38 -0700204 int fileMode = 0;
205 dev->GetInteger("fileMode", &fileMode);
206 device.fileMode = fileMode;
Dylan Reid6b590e62016-10-27 19:10:53 -0700207 int dev_uid = 0;
208 dev->GetInteger("uid", &dev_uid);
209 device.uid = dev_uid;
210 int dev_gid = 0;
211 dev->GetInteger("gid", &dev_gid);
212 device.gid = dev_gid;
213
214 config_out->linux_config.devices.push_back(device);
215 }
216
217 return true;
218}
219
220// Parses the list of ID mappings and fills |mappings_out| with them.
221bool ParseLinuxIdMappings(const base::ListValue* id_map_list,
222 std::vector<OciLinuxNamespaceMapping>& mappings_out) {
223 for (size_t i = 0; i < id_map_list->GetSize(); ++i) {
224 OciLinuxNamespaceMapping new_map;
225 const base::DictionaryValue* map;
226 if (!id_map_list->GetDictionary(i, &map)) {
227 LOG(ERROR) << "Fail to get id map " << i;
228 return false;
229 }
Dylan Reid6d650742016-11-02 23:10:38 -0700230 int hostID = 0;
231 if (!map->GetInteger("hostID", &hostID)) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700232 LOG(ERROR) << "Failed to read hostID from map " << i;
233 return false;
234 }
Dylan Reid6d650742016-11-02 23:10:38 -0700235 new_map.hostID = hostID;
236 int containerID = 0;
237 if (!map->GetInteger("containerID", &containerID)) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700238 LOG(ERROR) << "Failed to read containerID from map " << i;
239 return false;
240 }
Dylan Reid6d650742016-11-02 23:10:38 -0700241 new_map.containerID = containerID;
Dylan Reid6b590e62016-10-27 19:10:53 -0700242 int size = 0;
243 if (!map->GetInteger("size", &size)) {
244 LOG(ERROR) << "Failed to read size from map " << i;
245 return false;
246 }
247 new_map.size = size;
248 mappings_out.push_back(new_map);
249 }
250 return true;
251}
252
253// Parses the linux node which has information about setting up a user
254// namespace, and the list of devices for the container.
255bool ParseLinuxConfigDict(const base::DictionaryValue& runtime_root_dict,
256 OciConfigPtr const& config_out) {
257 // |linux_dict| is owned by |runtime_root_dict|
258 const base::DictionaryValue* linux_dict = nullptr;
259 if (!runtime_root_dict.GetDictionary("linux", &linux_dict)) {
260 LOG(ERROR) << "Fail to get linux dictionary from the runtime dictionary";
261 return false;
262 }
263
264 // |uid_map_list| is owned by |linux_dict|
265 const base::ListValue* uid_map_list = nullptr;
266 if (!linux_dict->GetList("uidMappings", &uid_map_list)) {
267 LOG(ERROR) << "Fail to get uid mappings list";
268 return false;
269 }
Dylan Reid6d650742016-11-02 23:10:38 -0700270 ParseLinuxIdMappings(uid_map_list, config_out->linux_config.uidMappings);
Dylan Reid6b590e62016-10-27 19:10:53 -0700271
272 // |gid_map_list| is owned by |linux_dict|
273 const base::ListValue* gid_map_list = nullptr;
274 if (!linux_dict->GetList("gidMappings", &gid_map_list)) {
275 LOG(ERROR) << "Fail to get gid mappings list";
276 return false;
277 }
Dylan Reid6d650742016-11-02 23:10:38 -0700278 ParseLinuxIdMappings(gid_map_list, config_out->linux_config.gidMappings);
Dylan Reid6b590e62016-10-27 19:10:53 -0700279
280 if (!ParseDeviceList(*linux_dict, config_out))
281 return false;
282
283 return true;
284}
285
286// Parses the configuration file for the container. The config file specifies
287// basic filesystem info and details about the process to be run. namespace,
288// cgroup, and syscall configurations are also specified
289bool ParseConfigDict(const base::DictionaryValue& config_root_dict,
290 OciConfigPtr const& config_out) {
Dylan Reid6d650742016-11-02 23:10:38 -0700291 if (!config_root_dict.GetString("ociVersion", &config_out->ociVersion)) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700292 LOG(ERROR) << "Failed to parse ociVersion";
293 return false;
294 }
295 if (!config_root_dict.GetString("hostname", &config_out->hostname)) {
296 LOG(ERROR) << "Failed to parse hostname";
297 return false;
298 }
299
300 // Platform info
301 if (!ParsePlatformConfig(config_root_dict, config_out)) {
302 return false;
303 }
304
305 // Root fs info
306 if (!ParseRootFileSystemConfig(config_root_dict, config_out)) {
307 return false;
308 }
309
310 // Process info
311 if (!ParseProcessConfig(config_root_dict, config_out)) {
312 return false;
313 }
314
315 // Get a list of mount points and mounts.
316 if (!ParseMounts(config_root_dict, config_out)) {
317 LOG(ERROR) << "Failed to parse mounts";
318 return false;
319 }
320
321 // Parse linux node.
322 if (!ParseLinuxConfigDict(config_root_dict, config_out)) {
323 LOG(ERROR) << "Failed to parse the linux node";
324 return false;
325 }
326
327 return true;
328}
329
330} // anonymous namespace
331
332bool ParseContainerConfig(const std::string& config_json_data,
333 OciConfigPtr const& config_out) {
334 std::unique_ptr<const base::Value> config_root_val =
335 base::JSONReader::Read(config_json_data);
336 if (!config_root_val) {
337 LOG(ERROR) << "Fail to parse config.json";
338 return false;
339 }
340 const base::DictionaryValue* config_dict = nullptr;
341 if (!config_root_val->GetAsDictionary(&config_dict)) {
342 LOG(ERROR) << "Fail to parse root dictionary from config.json";
343 return false;
344 }
345 if (!ParseConfigDict(*config_dict, config_out)) {
346 return false;
347 }
348
349 return true;
350}
351
352} // namespace container_utils