blob: 8b83567ffb127b9ecd197f69a72e0747a5f4aae2 [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
Dylan Reidc0c28502016-11-04 10:51:30 -070019// Gets a uint32 from the given dictionary.
20bool ParseUint32FromDict(const base::DictionaryValue& dict, const char *name,
21 uint32_t* val_out) {
22 double double_val;
23 if (!dict.GetDouble(name, &double_val)) {
24 LOG(ERROR) << "Failed to get " << name << " uint32_t value from config";
25 return false;
26 }
27 *val_out = double_val;
28 return true;
29}
30
Dylan Reid6b590e62016-10-27 19:10:53 -070031// Parses basic platform configuration.
32bool ParsePlatformConfig(const base::DictionaryValue& config_root_dict,
33 OciConfigPtr const& config_out) {
34 // |platform_dict| stays owned by |config_root_dict|
35 const base::DictionaryValue* platform_dict = nullptr;
36 if (!config_root_dict.GetDictionary("platform", &platform_dict)) {
37 LOG(ERROR) << "Fail to parse platform dictionary from config";
38 return false;
39 }
40
41 if (!platform_dict->GetString("os", &config_out->platform.os)) {
42 return false;
43 }
44
45 if (!platform_dict->GetString("arch", &config_out->platform.arch)) {
46 return false;
47 }
48
49 return true;
50}
51
52// Parses root fs info.
53bool ParseRootFileSystemConfig(const base::DictionaryValue& config_root_dict,
54 OciConfigPtr const& config_out) {
55 // |rootfs_dict| stays owned by |config_root_dict|
56 const base::DictionaryValue* rootfs_dict = nullptr;
57 if (!config_root_dict.GetDictionary("root", &rootfs_dict)) {
58 LOG(ERROR) << "Fail to parse rootfs dictionary from config";
59 return false;
60 }
61 if (!rootfs_dict->GetString("path", &config_out->root.path)) {
62 LOG(ERROR) << "Fail to get rootfs path from config";
63 return false;
64 }
Dylan Reid6d650742016-11-02 23:10:38 -070065 rootfs_dict->GetBoolean("readonly", &config_out->root.readonly);
Dylan Reid6b590e62016-10-27 19:10:53 -070066 return true;
67}
68
69// Fills |config_out| with information about the main process to run in the
70// container and the user it should be run as.
71bool ParseProcessConfig(const base::DictionaryValue& config_root_dict,
72 OciConfigPtr const& config_out) {
73 // |process_dict| stays owned by |config_root_dict|
74 const base::DictionaryValue* process_dict = nullptr;
75 if (!config_root_dict.GetDictionary("process", &process_dict)) {
76 LOG(ERROR) << "Fail to get main process from config";
77 return false;
78 }
79 process_dict->GetBoolean("terminal", &config_out->process.terminal);
80 // |user_dict| stays owned by |process_dict|
81 const base::DictionaryValue* user_dict = nullptr;
82 if (!process_dict->GetDictionary("user", &user_dict)) {
83 LOG(ERROR) << "Failed to get user info from config";
84 return false;
85 }
Dylan Reidc0c28502016-11-04 10:51:30 -070086 if (!ParseUint32FromDict(*user_dict, "uid", &config_out->process.user.uid))
Dylan Reid6b590e62016-10-27 19:10:53 -070087 return false;
Dylan Reidc0c28502016-11-04 10:51:30 -070088 if (!ParseUint32FromDict(*user_dict, "gid", &config_out->process.user.gid))
Dylan Reid6b590e62016-10-27 19:10:53 -070089 return false;
Dylan Reid6b590e62016-10-27 19:10:53 -070090 // |args_list| stays owned by |process_dict|
91 const base::ListValue* args_list = nullptr;
92 if (!process_dict->GetList("args", &args_list)) {
93 LOG(ERROR) << "Fail to get main process args from config";
94 return false;
95 }
96 size_t num_args = args_list->GetSize();
97 for (size_t i = 0; i < num_args; ++i) {
98 std::string arg;
99 if (!args_list->GetString(i, &arg)) {
100 LOG(ERROR) << "Fail to get process args from config";
101 return false;
102 }
103 config_out->process.args.push_back(arg);
104 }
105 // |env_list| stays owned by |process_dict|
106 const base::ListValue* env_list = nullptr;
107 if (process_dict->GetList("env", &env_list)) {
108 size_t num_env = env_list->GetSize();
109 for (size_t i = 0; i < num_env; ++i) {
110 std::string env;
111 if (!env_list->GetString(i, &env)) {
112 LOG(ERROR) << "Fail to get process env from config";
113 return false;
114 }
115 config_out->process.env.push_back(env);
116 }
117 }
118 if (!process_dict->GetString("cwd", &config_out->process.cwd)) {
119 LOG(ERROR) << "failed to get cwd of process";
120 return false;
121 }
122
123 return true;
124}
125
126// Parses the 'mounts' field. The necessary mounts for running the container
127// are specified here.
128bool ParseMounts(const base::DictionaryValue& config_root_dict,
129 OciConfigPtr const& config_out) {
130 // |config_mounts_list| stays owned by |config_root_dict|
131 const base::ListValue* config_mounts_list = nullptr;
132 if (!config_root_dict.GetList("mounts", &config_mounts_list)) {
133 LOG(ERROR) << "Fail to get mounts from config dictionary";
134 return false;
135 }
136
137 for (size_t i = 0; i < config_mounts_list->GetSize(); ++i) {
138 const base::DictionaryValue* mount_dict;
139 if (!config_mounts_list->GetDictionary(i, &mount_dict)) {
140 LOG(ERROR) << "Fail to get mount item " << i;
141 return false;
142 }
143 OciMount mount;
144 if (!mount_dict->GetString("destination", &mount.destination)) {
145 LOG(ERROR) << "Fail to get mount path for mount " << i;
146 return false;
147 }
Dylan Reid6d650742016-11-02 23:10:38 -0700148 if (!mount_dict->GetString("type", &mount.type)) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700149 LOG(ERROR) << "Fail to get mount type for mount " << i;
150 return false;
151 }
152 if (!mount_dict->GetString("source", &mount.source)) {
153 LOG(ERROR) << "Fail to get mount source for mount " << i;
154 return false;
155 }
156
157 // |options| are owned by |mount_dict|
158 const base::ListValue* options = nullptr;
159 if (mount_dict->GetList("options", &options)) {
160 for (size_t j = 0; j < options->GetSize(); ++j) {
161 std::string this_opt;
162 if (!options->GetString(j, &this_opt)) {
163 LOG(ERROR) << "Fail to get option " << j << " from mount options";
164 return false;
165 }
166 mount.options.push_back(this_opt);
167 }
168 }
169
170 config_out->mounts.push_back(mount);
171 }
172 return true;
173}
174
175// Parse the list of device nodes that the container needs to run.
176bool ParseDeviceList(const base::DictionaryValue& linux_dict,
177 OciConfigPtr const& config_out) {
178 // |device_list| is owned by |linux_dict|
179 const base::ListValue* device_list = nullptr;
180 if (!linux_dict.GetList("devices", &device_list)) {
181 LOG(ERROR) << "Fail to get device list";
182 return false;
183 }
184 size_t num_devices = device_list->GetSize();
185 for (size_t i = 0; i < num_devices; ++i) {
186 OciLinuxDevice device;
187
188 const base::DictionaryValue* dev;
189 if (!device_list->GetDictionary(i, &dev)) {
190 LOG(ERROR) << "Fail to get device " << i;
191 return false;
192 }
193 std::string path;
194 if (!dev->GetString("path", &device.path)) {
195 LOG(ERROR) << "Fail to get path for dev";
196 return false;
197 }
Dylan Reid6d650742016-11-02 23:10:38 -0700198 if (!dev->GetString("type", &device.type)) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700199 LOG(ERROR) << "Fail to get type for " << device.path;
200 return false;
201 }
Dylan Reidc0c28502016-11-04 10:51:30 -0700202 if (!ParseUint32FromDict(*dev, "major", &device.major))
203 return false;
204 if (!ParseUint32FromDict(*dev, "minor", &device.minor))
205 return false;
206 if (!ParseUint32FromDict(*dev, "fileMode", &device.fileMode))
207 return false;
208 if (!ParseUint32FromDict(*dev, "uid", &device.uid))
209 return false;
210 if (!ParseUint32FromDict(*dev, "gid", &device.gid))
211 return false;
Dylan Reid6b590e62016-10-27 19:10:53 -0700212
213 config_out->linux_config.devices.push_back(device);
214 }
215
216 return true;
217}
218
219// Parses the list of ID mappings and fills |mappings_out| with them.
220bool ParseLinuxIdMappings(const base::ListValue* id_map_list,
221 std::vector<OciLinuxNamespaceMapping>& mappings_out) {
222 for (size_t i = 0; i < id_map_list->GetSize(); ++i) {
223 OciLinuxNamespaceMapping new_map;
224 const base::DictionaryValue* map;
225 if (!id_map_list->GetDictionary(i, &map)) {
226 LOG(ERROR) << "Fail to get id map " << i;
227 return false;
228 }
Dylan Reidc0c28502016-11-04 10:51:30 -0700229 if (!ParseUint32FromDict(*map, "hostID", &new_map.hostID))
Dylan Reid6b590e62016-10-27 19:10:53 -0700230 return false;
Dylan Reidc0c28502016-11-04 10:51:30 -0700231 if (!ParseUint32FromDict(*map, "containerID", &new_map.containerID))
Dylan Reid6b590e62016-10-27 19:10:53 -0700232 return false;
Dylan Reidc0c28502016-11-04 10:51:30 -0700233 if (!ParseUint32FromDict(*map, "size", &new_map.size))
Dylan Reid6b590e62016-10-27 19:10:53 -0700234 return false;
Dylan Reid6b590e62016-10-27 19:10:53 -0700235 mappings_out.push_back(new_map);
236 }
237 return true;
238}
239
240// Parses the linux node which has information about setting up a user
241// namespace, and the list of devices for the container.
242bool ParseLinuxConfigDict(const base::DictionaryValue& runtime_root_dict,
243 OciConfigPtr const& config_out) {
244 // |linux_dict| is owned by |runtime_root_dict|
245 const base::DictionaryValue* linux_dict = nullptr;
246 if (!runtime_root_dict.GetDictionary("linux", &linux_dict)) {
247 LOG(ERROR) << "Fail to get linux dictionary from the runtime dictionary";
248 return false;
249 }
250
251 // |uid_map_list| is owned by |linux_dict|
252 const base::ListValue* uid_map_list = nullptr;
253 if (!linux_dict->GetList("uidMappings", &uid_map_list)) {
254 LOG(ERROR) << "Fail to get uid mappings list";
255 return false;
256 }
Dylan Reid6d650742016-11-02 23:10:38 -0700257 ParseLinuxIdMappings(uid_map_list, config_out->linux_config.uidMappings);
Dylan Reid6b590e62016-10-27 19:10:53 -0700258
259 // |gid_map_list| is owned by |linux_dict|
260 const base::ListValue* gid_map_list = nullptr;
261 if (!linux_dict->GetList("gidMappings", &gid_map_list)) {
262 LOG(ERROR) << "Fail to get gid mappings list";
263 return false;
264 }
Dylan Reid6d650742016-11-02 23:10:38 -0700265 ParseLinuxIdMappings(gid_map_list, config_out->linux_config.gidMappings);
Dylan Reid6b590e62016-10-27 19:10:53 -0700266
267 if (!ParseDeviceList(*linux_dict, config_out))
268 return false;
269
270 return true;
271}
272
273// Parses the configuration file for the container. The config file specifies
274// basic filesystem info and details about the process to be run. namespace,
275// cgroup, and syscall configurations are also specified
276bool ParseConfigDict(const base::DictionaryValue& config_root_dict,
277 OciConfigPtr const& config_out) {
Dylan Reid6d650742016-11-02 23:10:38 -0700278 if (!config_root_dict.GetString("ociVersion", &config_out->ociVersion)) {
Dylan Reid6b590e62016-10-27 19:10:53 -0700279 LOG(ERROR) << "Failed to parse ociVersion";
280 return false;
281 }
282 if (!config_root_dict.GetString("hostname", &config_out->hostname)) {
283 LOG(ERROR) << "Failed to parse hostname";
284 return false;
285 }
286
287 // Platform info
288 if (!ParsePlatformConfig(config_root_dict, config_out)) {
289 return false;
290 }
291
292 // Root fs info
293 if (!ParseRootFileSystemConfig(config_root_dict, config_out)) {
294 return false;
295 }
296
297 // Process info
298 if (!ParseProcessConfig(config_root_dict, config_out)) {
299 return false;
300 }
301
302 // Get a list of mount points and mounts.
303 if (!ParseMounts(config_root_dict, config_out)) {
304 LOG(ERROR) << "Failed to parse mounts";
305 return false;
306 }
307
308 // Parse linux node.
309 if (!ParseLinuxConfigDict(config_root_dict, config_out)) {
310 LOG(ERROR) << "Failed to parse the linux node";
311 return false;
312 }
313
314 return true;
315}
316
317} // anonymous namespace
318
319bool ParseContainerConfig(const std::string& config_json_data,
320 OciConfigPtr const& config_out) {
321 std::unique_ptr<const base::Value> config_root_val =
322 base::JSONReader::Read(config_json_data);
323 if (!config_root_val) {
324 LOG(ERROR) << "Fail to parse config.json";
325 return false;
326 }
327 const base::DictionaryValue* config_dict = nullptr;
328 if (!config_root_val->GetAsDictionary(&config_dict)) {
329 LOG(ERROR) << "Fail to parse root dictionary from config.json";
330 return false;
331 }
332 if (!ParseConfigDict(*config_dict, config_out)) {
333 return false;
334 }
335
336 return true;
337}
338
339} // namespace container_utils