blob: afeef1e3ab396c8212d066ab4b15ceea3c0a8288 [file] [log] [blame]
Dylan Reid837c74a2016-01-22 17:25:21 -08001/* 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
6#define _GNU_SOURCE /* For asprintf */
7
8#include <errno.h>
9#include <fcntl.h>
Mike Frysinger05e594e2017-01-10 02:11:08 -050010#if USE_device_mapper
11#include <libdevmapper.h>
12#endif
Dylan Reid837c74a2016-01-22 17:25:21 -080013#include <malloc.h>
14#include <signal.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <sys/mount.h>
19#include <sys/stat.h>
20#include <sys/types.h>
Dylan Reid2bd9ea92016-04-07 20:57:47 -070021#include <sys/wait.h>
Dylan Reid837c74a2016-01-22 17:25:21 -080022#include <unistd.h>
23
Mike Frysinger412dbd22017-01-06 01:50:34 -050024#include <linux/loop.h>
25
Dylan Reid837c74a2016-01-22 17:25:21 -080026#include "container_cgroup.h"
27#include "libcontainer.h"
28#include "libminijail.h"
29
Luis Hector Chavez479b95f2016-06-06 08:01:05 -070030#define FREE_AND_NULL(ptr) \
31do { \
32 free(ptr); \
33 ptr = NULL; \
34} while(0)
35
Yusuke Sato91f11f02016-12-02 16:15:13 -080036#define MAX_NUM_SETFILES_ARGS 128
37
Mike Frysinger412dbd22017-01-06 01:50:34 -050038static const char loopdev_ctl[] = "/dev/loop-control";
Mike Frysinger05e594e2017-01-10 02:11:08 -050039#if USE_device_mapper
40static const char dm_dev_prefix[] = "/dev/mapper/";
41#endif
Mike Frysinger412dbd22017-01-06 01:50:34 -050042
Luis Hector Chavez945af482016-06-03 08:39:34 -070043static int container_teardown(struct container *c);
44
Luis Hector Chavez479b95f2016-06-06 08:01:05 -070045static int strdup_and_free(char **dest, const char *src)
46{
47 char *copy = strdup(src);
48 if (!copy)
49 return -ENOMEM;
50 if (*dest)
51 free(*dest);
52 *dest = copy;
53 return 0;
54}
55
Dylan Reid837c74a2016-01-22 17:25:21 -080056struct container_mount {
57 char *name;
58 char *source;
59 char *destination;
60 char *type;
61 char *data;
Mike Frysinger05e594e2017-01-10 02:11:08 -050062 char *verity;
Dylan Reid837c74a2016-01-22 17:25:21 -080063 int flags;
64 int uid;
65 int gid;
66 int mode;
67 int mount_in_ns; /* True if mount should happen in new vfs ns */
68 int create; /* True if target should be created if it doesn't exist */
Mike Frysinger412dbd22017-01-06 01:50:34 -050069 int loopback; /* True if target should be mounted via loopback */
Dylan Reid837c74a2016-01-22 17:25:21 -080070};
71
72struct container_device {
73 char type; /* 'c' or 'b' for char or block */
74 char *path;
75 int fs_permissions;
76 int major;
77 int minor;
Dylan Reid355d5e42016-04-29 16:53:31 -070078 int copy_minor; /* Copy the minor from existing node, ignores |minor| */
Dylan Reid837c74a2016-01-22 17:25:21 -080079 int uid;
80 int gid;
Dylan Reid4843d6b2017-03-31 18:14:30 -070081};
82
83struct container_cgroup_device {
84 int allow;
85 char type;
86 int major; /* -1 means all */
87 int minor; /* -1 means all */
88 int read;
89 int write;
90 int modify;
Dylan Reid837c74a2016-01-22 17:25:21 -080091};
92
Chinyue Chenfac909e2016-06-24 14:17:42 +080093struct container_cpu_cgroup {
94 int shares;
95 int quota;
96 int period;
97 int rt_runtime;
98 int rt_period;
99};
100
Dylan Reid837c74a2016-01-22 17:25:21 -0800101/*
102 * Structure that configures how the container is run.
103 *
Mike Frysingerb22acdf2017-01-08 02:02:35 -0500104 * config_root - Path to the root of the container itself.
Dylan Reid837c74a2016-01-22 17:25:21 -0800105 * rootfs - Path to the root of the container's filesystem.
Luis Hector Chavezc240e7e2016-09-22 10:33:03 -0700106 * rootfs_mount_flags - Flags that will be passed to mount() for the rootfs.
Keshav Santhanam0e4c3282016-07-14 10:25:16 -0700107 * premounted_runfs - Path to where the container will be run.
108 * pid_file_path - Path to the file where the pid should be written.
Dylan Reid837c74a2016-01-22 17:25:21 -0800109 * program_argv - The program to run and args, e.g. "/sbin/init".
110 * num_args - Number of args in program_argv.
Dylan Reid1874feb2016-06-22 17:53:50 -0700111 * uid - The uid the container will run as.
Dylan Reid837c74a2016-01-22 17:25:21 -0800112 * uid_map - Mapping of UIDs in the container, e.g. "0 100000 1024"
Dylan Reid1874feb2016-06-22 17:53:50 -0700113 * gid - The gid the container will run as.
Dylan Reid837c74a2016-01-22 17:25:21 -0800114 * gid_map - Mapping of GIDs in the container, e.g. "0 100000 1024"
115 * alt_syscall_table - Syscall table to use or NULL if none.
116 * mounts - Filesystems to mount in the new namespace.
117 * num_mounts - Number of above.
118 * devices - Device nodes to create.
119 * num_devices - Number of above.
Dylan Reid4843d6b2017-03-31 18:14:30 -0700120 * cgroup_devices - Device node cgroup permissions.
121 * num_cgroup_devices - Number of above.
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700122 * run_setfiles - Should run setfiles on mounts to enable selinux.
Chinyue Chenfac909e2016-06-24 14:17:42 +0800123 * cpu_cgparams - CPU cgroup params.
Dylan Reid9e724af2016-07-21 09:58:07 -0700124 * cgroup_parent - Parent dir for cgroup creation
125 * cgroup_owner - uid to own the created cgroups
Dmitry Torokhov14eef722016-09-27 16:40:37 -0700126 * cgroup_group - gid to own the created cgroups
Keshav Santhanam1b6bf672016-08-10 18:35:12 -0700127 * share_host_netns - Enable sharing of the host network namespace.
Dylan Reidc4335842016-11-11 10:24:52 -0800128 * keep_fds_open - Allow the child process to keep open FDs (for stdin/out/err).
Dylan Reid837c74a2016-01-22 17:25:21 -0800129 */
130struct container_config {
Mike Frysingerb22acdf2017-01-08 02:02:35 -0500131 char *config_root;
Dylan Reid837c74a2016-01-22 17:25:21 -0800132 char *rootfs;
Luis Hector Chavezc240e7e2016-09-22 10:33:03 -0700133 unsigned long rootfs_mount_flags;
Keshav Santhanam0e4c3282016-07-14 10:25:16 -0700134 char *premounted_runfs;
135 char *pid_file_path;
Dylan Reid837c74a2016-01-22 17:25:21 -0800136 char **program_argv;
137 size_t num_args;
Dylan Reid1874feb2016-06-22 17:53:50 -0700138 uid_t uid;
Dylan Reid837c74a2016-01-22 17:25:21 -0800139 char *uid_map;
Dylan Reid1874feb2016-06-22 17:53:50 -0700140 gid_t gid;
Dylan Reid837c74a2016-01-22 17:25:21 -0800141 char *gid_map;
142 char *alt_syscall_table;
143 struct container_mount *mounts;
144 size_t num_mounts;
145 struct container_device *devices;
146 size_t num_devices;
Dylan Reid4843d6b2017-03-31 18:14:30 -0700147 struct container_cgroup_device *cgroup_devices;
148 size_t num_cgroup_devices;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700149 char *run_setfiles;
Chinyue Chenfac909e2016-06-24 14:17:42 +0800150 struct container_cpu_cgroup cpu_cgparams;
Dylan Reid9e724af2016-07-21 09:58:07 -0700151 char *cgroup_parent;
152 uid_t cgroup_owner;
Dmitry Torokhov14eef722016-09-27 16:40:37 -0700153 gid_t cgroup_group;
Keshav Santhanam1b6bf672016-08-10 18:35:12 -0700154 int share_host_netns;
Dylan Reidc4335842016-11-11 10:24:52 -0800155 int keep_fds_open;
Dylan Reid837c74a2016-01-22 17:25:21 -0800156};
157
158struct container_config *container_config_create()
159{
160 return calloc(1, sizeof(struct container_config));
161}
162
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700163static void container_free_program_args(struct container_config *c)
164{
165 int i;
166
167 if (!c->program_argv)
168 return;
169 for (i = 0; i < c->num_args; ++i) {
170 FREE_AND_NULL(c->program_argv[i]);
171 }
172 FREE_AND_NULL(c->program_argv);
173}
174
175static void container_config_free_mount(struct container_mount *mount)
176{
177 FREE_AND_NULL(mount->name);
178 FREE_AND_NULL(mount->source);
179 FREE_AND_NULL(mount->destination);
180 FREE_AND_NULL(mount->type);
181 FREE_AND_NULL(mount->data);
182}
183
184static void container_config_free_device(struct container_device *device)
185{
186 FREE_AND_NULL(device->path);
187}
188
Dylan Reid837c74a2016-01-22 17:25:21 -0800189void container_config_destroy(struct container_config *c)
190{
191 size_t i;
192
193 if (c == NULL)
194 return;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700195 FREE_AND_NULL(c->rootfs);
196 container_free_program_args(c);
Keshav Santhanam0e4c3282016-07-14 10:25:16 -0700197 FREE_AND_NULL(c->premounted_runfs);
198 FREE_AND_NULL(c->pid_file_path);
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700199 FREE_AND_NULL(c->uid_map);
200 FREE_AND_NULL(c->gid_map);
201 FREE_AND_NULL(c->alt_syscall_table);
Dylan Reid837c74a2016-01-22 17:25:21 -0800202 for (i = 0; i < c->num_mounts; ++i) {
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700203 container_config_free_mount(&c->mounts[i]);
Dylan Reid837c74a2016-01-22 17:25:21 -0800204 }
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700205 FREE_AND_NULL(c->mounts);
Dylan Reid837c74a2016-01-22 17:25:21 -0800206 for (i = 0; i < c->num_devices; ++i) {
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700207 container_config_free_device(&c->devices[i]);
Dylan Reid837c74a2016-01-22 17:25:21 -0800208 }
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700209 FREE_AND_NULL(c->devices);
Dylan Reida34f8162017-05-10 11:33:11 -0700210 FREE_AND_NULL(c->cgroup_devices);
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700211 FREE_AND_NULL(c->run_setfiles);
Dylan Reid9e724af2016-07-21 09:58:07 -0700212 FREE_AND_NULL(c->cgroup_parent);
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700213 FREE_AND_NULL(c);
Dylan Reid837c74a2016-01-22 17:25:21 -0800214}
215
Mike Frysingerb22acdf2017-01-08 02:02:35 -0500216int container_config_config_root(struct container_config *c,
217 const char *config_root)
218{
219 return strdup_and_free(&c->config_root, config_root);
220}
221
222const char *container_config_get_config_root(const struct container_config *c)
223{
224 return c->config_root;
225}
226
Dylan Reid837c74a2016-01-22 17:25:21 -0800227int container_config_rootfs(struct container_config *c, const char *rootfs)
228{
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700229 return strdup_and_free(&c->rootfs, rootfs);
Dylan Reid837c74a2016-01-22 17:25:21 -0800230}
231
Dylan Reid11456722016-05-02 11:24:50 -0700232const char *container_config_get_rootfs(const struct container_config *c)
233{
234 return c->rootfs;
235}
236
Luis Hector Chavezc240e7e2016-09-22 10:33:03 -0700237void container_config_rootfs_mount_flags(struct container_config *c,
238 unsigned long rootfs_mount_flags)
239{
240 /* Since we are going to add MS_REMOUNT anyways, add it here so we can
241 * simply check against zero later. MS_BIND is also added to avoid
242 * re-mounting the original filesystem, since the rootfs is always
243 * bind-mounted.
244 */
245 c->rootfs_mount_flags = MS_REMOUNT | MS_BIND | rootfs_mount_flags;
246}
247
248unsigned long container_config_get_rootfs_mount_flags(
249 const struct container_config *c)
250{
251 return c->rootfs_mount_flags;
252}
253
Keshav Santhanam0e4c3282016-07-14 10:25:16 -0700254int container_config_premounted_runfs(struct container_config *c, const char *runfs)
255{
256 return strdup_and_free(&c->premounted_runfs, runfs);
257}
258
259const char *container_config_get_premounted_runfs(const struct container_config *c)
260{
261 return c->premounted_runfs;
262}
263
264int container_config_pid_file(struct container_config *c, const char *path)
265{
266 return strdup_and_free(&c->pid_file_path, path);
267}
268
269const char *container_config_get_pid_file(const struct container_config *c)
270{
271 return c->pid_file_path;
272}
273
Dylan Reid837c74a2016-01-22 17:25:21 -0800274int container_config_program_argv(struct container_config *c,
Dylan Reid17fd53f2016-11-18 19:14:41 -0800275 const char **argv, size_t num_args)
Dylan Reid837c74a2016-01-22 17:25:21 -0800276{
277 size_t i;
278
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700279 container_free_program_args(c);
Dylan Reid837c74a2016-01-22 17:25:21 -0800280 c->num_args = num_args;
281 c->program_argv = calloc(num_args + 1, sizeof(char *));
282 if (!c->program_argv)
283 return -ENOMEM;
284 for (i = 0; i < num_args; ++i) {
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700285 if (strdup_and_free(&c->program_argv[i], argv[i]))
286 goto error_free_return;
Dylan Reid837c74a2016-01-22 17:25:21 -0800287 }
288 c->program_argv[num_args] = NULL;
289 return 0;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700290
291error_free_return:
292 container_free_program_args(c);
293 return -ENOMEM;
Dylan Reid837c74a2016-01-22 17:25:21 -0800294}
295
Dylan Reid11456722016-05-02 11:24:50 -0700296size_t container_config_get_num_program_args(const struct container_config *c)
297{
298 return c->num_args;
299}
300
301const char *container_config_get_program_arg(const struct container_config *c,
302 size_t index)
303{
304 if (index >= c->num_args)
305 return NULL;
306 return c->program_argv[index];
307}
308
Dylan Reid1874feb2016-06-22 17:53:50 -0700309void container_config_uid(struct container_config *c, uid_t uid)
310{
311 c->uid = uid;
312}
313
314uid_t container_config_get_uid(const struct container_config *c)
315{
316 return c->uid;
317}
318
Dylan Reid837c74a2016-01-22 17:25:21 -0800319int container_config_uid_map(struct container_config *c, const char *uid_map)
320{
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700321 return strdup_and_free(&c->uid_map, uid_map);
Dylan Reid837c74a2016-01-22 17:25:21 -0800322}
323
Dylan Reid1874feb2016-06-22 17:53:50 -0700324void container_config_gid(struct container_config *c, gid_t gid)
325{
326 c->gid = gid;
327}
328
329gid_t container_config_get_gid(const struct container_config *c)
330{
331 return c->gid;
332}
333
Dylan Reid837c74a2016-01-22 17:25:21 -0800334int container_config_gid_map(struct container_config *c, const char *gid_map)
335{
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700336 return strdup_and_free(&c->gid_map, gid_map);
Dylan Reid837c74a2016-01-22 17:25:21 -0800337}
338
339int container_config_alt_syscall_table(struct container_config *c,
340 const char *alt_syscall_table)
341{
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700342 return strdup_and_free(&c->alt_syscall_table, alt_syscall_table);
Dylan Reid837c74a2016-01-22 17:25:21 -0800343}
344
345int container_config_add_mount(struct container_config *c,
346 const char *name,
347 const char *source,
348 const char *destination,
349 const char *type,
350 const char *data,
Mike Frysinger05e594e2017-01-10 02:11:08 -0500351 const char *verity,
Dylan Reid837c74a2016-01-22 17:25:21 -0800352 int flags,
353 int uid,
354 int gid,
355 int mode,
356 int mount_in_ns,
Mike Frysinger412dbd22017-01-06 01:50:34 -0500357 int create,
358 int loopback)
Dylan Reid837c74a2016-01-22 17:25:21 -0800359{
360 struct container_mount *mount_ptr;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700361 struct container_mount *current_mount;
Dylan Reid837c74a2016-01-22 17:25:21 -0800362
363 if (name == NULL || source == NULL ||
364 destination == NULL || type == NULL)
365 return -EINVAL;
366
367 mount_ptr = realloc(c->mounts,
368 sizeof(c->mounts[0]) * (c->num_mounts + 1));
369 if (!mount_ptr)
370 return -ENOMEM;
371 c->mounts = mount_ptr;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700372 current_mount = &c->mounts[c->num_mounts];
373 memset(current_mount, 0, sizeof(struct container_mount));
374
375 if (strdup_and_free(&current_mount->name, name))
376 goto error_free_return;
377 if (strdup_and_free(&current_mount->source, source))
378 goto error_free_return;
379 if (strdup_and_free(&current_mount->destination, destination))
380 goto error_free_return;
381 if (strdup_and_free(&current_mount->type, type))
382 goto error_free_return;
383 if (data && strdup_and_free(&current_mount->data, data))
384 goto error_free_return;
Mike Frysinger05e594e2017-01-10 02:11:08 -0500385 if (verity && strdup_and_free(&current_mount->verity, verity))
386 goto error_free_return;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700387 current_mount->flags = flags;
388 current_mount->uid = uid;
389 current_mount->gid = gid;
390 current_mount->mode = mode;
391 current_mount->mount_in_ns = mount_in_ns;
392 current_mount->create = create;
Mike Frysinger412dbd22017-01-06 01:50:34 -0500393 current_mount->loopback = loopback;
Dylan Reid837c74a2016-01-22 17:25:21 -0800394 ++c->num_mounts;
395 return 0;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700396
397error_free_return:
398 container_config_free_mount(current_mount);
399 return -ENOMEM;
Dylan Reid837c74a2016-01-22 17:25:21 -0800400}
401
Dylan Reid4843d6b2017-03-31 18:14:30 -0700402int container_config_add_cgroup_device(struct container_config *c,
403 int allow,
404 char type,
405 int major,
406 int minor,
407 int read,
408 int write,
409 int modify)
410{
411 struct container_cgroup_device *dev_ptr;
412 struct container_cgroup_device *current_dev;
413
414 dev_ptr = realloc(c->cgroup_devices,
415 sizeof(c->cgroup_devices[0]) *
416 (c->num_cgroup_devices + 1));
417 if (!dev_ptr)
418 return -ENOMEM;
419 c->cgroup_devices = dev_ptr;
420
421 current_dev = &c->cgroup_devices[c->num_cgroup_devices];
422 memset(current_dev, 0, sizeof(struct container_cgroup_device));
423 current_dev->allow = allow;
424 current_dev->type = type;
425 current_dev->major = major;
426 current_dev->minor = minor;
427 current_dev->read = read;
428 current_dev->write = write;
429 current_dev->modify = modify;
430 ++c->num_cgroup_devices;
431
432 return 0;
433}
434
Dylan Reid837c74a2016-01-22 17:25:21 -0800435int container_config_add_device(struct container_config *c,
436 char type,
437 const char *path,
438 int fs_permissions,
439 int major,
440 int minor,
Dylan Reid355d5e42016-04-29 16:53:31 -0700441 int copy_minor,
Dylan Reid837c74a2016-01-22 17:25:21 -0800442 int uid,
443 int gid,
444 int read_allowed,
445 int write_allowed,
446 int modify_allowed)
447{
448 struct container_device *dev_ptr;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700449 struct container_device *current_dev;
Dylan Reid837c74a2016-01-22 17:25:21 -0800450
451 if (path == NULL)
452 return -EINVAL;
Dylan Reid355d5e42016-04-29 16:53:31 -0700453 /* If using a dynamic minor number, ensure that minor is -1. */
454 if (copy_minor && (minor != -1))
455 return -EINVAL;
456
Dylan Reid837c74a2016-01-22 17:25:21 -0800457 dev_ptr = realloc(c->devices,
458 sizeof(c->devices[0]) * (c->num_devices + 1));
459 if (!dev_ptr)
460 return -ENOMEM;
461 c->devices = dev_ptr;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700462 current_dev = &c->devices[c->num_devices];
463 memset(current_dev, 0, sizeof(struct container_device));
464
465 current_dev->type = type;
466 if (strdup_and_free(&current_dev->path, path))
467 goto error_free_return;
468 current_dev->fs_permissions = fs_permissions;
469 current_dev->major = major;
470 current_dev->minor = minor;
471 current_dev->copy_minor = copy_minor;
472 current_dev->uid = uid;
473 current_dev->gid = gid;
Dylan Reid4843d6b2017-03-31 18:14:30 -0700474 if (read_allowed || write_allowed || modify_allowed) {
475 if (container_config_add_cgroup_device(c,
476 1,
477 type,
478 major,
479 minor,
480 read_allowed,
481 write_allowed,
482 modify_allowed))
483 goto error_free_return;
484 }
Dylan Reid837c74a2016-01-22 17:25:21 -0800485 ++c->num_devices;
486 return 0;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700487
488error_free_return:
489 container_config_free_device(current_dev);
490 return -ENOMEM;
Dylan Reid837c74a2016-01-22 17:25:21 -0800491}
492
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700493int container_config_run_setfiles(struct container_config *c,
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700494 const char *setfiles_cmd)
495{
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700496 return strdup_and_free(&c->run_setfiles, setfiles_cmd);
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700497}
Dylan Reid837c74a2016-01-22 17:25:21 -0800498
Dylan Reid11456722016-05-02 11:24:50 -0700499const char *container_config_get_run_setfiles(const struct container_config *c)
500{
501 return c->run_setfiles;
502}
503
Chinyue Chenfac909e2016-06-24 14:17:42 +0800504int container_config_set_cpu_shares(struct container_config *c, int shares)
505{
506 /* CPU shares must be 2 or higher. */
507 if (shares < 2)
508 return -EINVAL;
509
510 c->cpu_cgparams.shares = shares;
511 return 0;
512}
513
514int container_config_set_cpu_cfs_params(struct container_config *c,
515 int quota,
516 int period)
517{
518 /*
519 * quota could be set higher than period to utilize more than one CPU.
520 * quota could also be set as -1 to indicate the cgroup does not adhere
521 * to any CPU time restrictions.
522 */
523 if (quota <= 0 && quota != -1)
524 return -EINVAL;
525 if (period <= 0)
526 return -EINVAL;
527
528 c->cpu_cgparams.quota = quota;
529 c->cpu_cgparams.period = period;
530 return 0;
531}
532
533int container_config_set_cpu_rt_params(struct container_config *c,
534 int rt_runtime,
535 int rt_period)
536{
537 /*
538 * rt_runtime could be set as 0 to prevent the cgroup from using
539 * realtime CPU.
540 */
541 if (rt_runtime < 0 || rt_runtime >= rt_period)
542 return -EINVAL;
543
544 c->cpu_cgparams.rt_runtime = rt_runtime;
545 c->cpu_cgparams.rt_period = rt_period;
546 return 0;
547}
548
Chinyue Chen4f3fd682016-07-01 14:11:42 +0800549int container_config_get_cpu_shares(struct container_config *c)
550{
551 return c->cpu_cgparams.shares;
552}
553
554int container_config_get_cpu_quota(struct container_config *c)
555{
556 return c->cpu_cgparams.quota;
557}
558
559int container_config_get_cpu_period(struct container_config *c)
560{
561 return c->cpu_cgparams.period;
562}
563
564int container_config_get_cpu_rt_runtime(struct container_config *c)
565{
566 return c->cpu_cgparams.rt_runtime;
567}
568
569int container_config_get_cpu_rt_period(struct container_config *c)
570{
571 return c->cpu_cgparams.rt_period;
572}
573
Dylan Reid9e724af2016-07-21 09:58:07 -0700574int container_config_set_cgroup_parent(struct container_config *c,
575 const char *parent,
Dmitry Torokhov14eef722016-09-27 16:40:37 -0700576 uid_t cgroup_owner, gid_t cgroup_group)
Dylan Reid9e724af2016-07-21 09:58:07 -0700577{
578 c->cgroup_owner = cgroup_owner;
Dmitry Torokhov14eef722016-09-27 16:40:37 -0700579 c->cgroup_group = cgroup_group;
Dylan Reid9e724af2016-07-21 09:58:07 -0700580 return strdup_and_free(&c->cgroup_parent, parent);
581}
582
583const char *container_config_get_cgroup_parent(struct container_config *c)
584{
585 return c->cgroup_parent;
586}
587
Keshav Santhanam1b6bf672016-08-10 18:35:12 -0700588void container_config_share_host_netns(struct container_config *c)
589{
590 c->share_host_netns = 1;
591}
592
593int get_container_config_share_host_netns(struct container_config *c)
594{
595 return c->share_host_netns;
596}
597
Dylan Reidc4335842016-11-11 10:24:52 -0800598void container_config_keep_fds_open(struct container_config *c)
599{
600 c->keep_fds_open = 1;
601}
602
Dylan Reid837c74a2016-01-22 17:25:21 -0800603/*
604 * Container manipulation
605 */
606struct container {
Dylan Reid837c74a2016-01-22 17:25:21 -0800607 struct container_cgroup *cgroup;
608 struct minijail *jail;
609 pid_t init_pid;
Mike Frysingerb22acdf2017-01-08 02:02:35 -0500610 char *config_root;
Dylan Reid837c74a2016-01-22 17:25:21 -0800611 char *runfs;
612 char *rundir;
613 char *runfsroot;
614 char *pid_file_path;
Dylan Reide040c6b2016-05-02 18:49:02 -0700615 char **ext_mounts; /* Mounts made outside of the minijail */
616 size_t num_ext_mounts;
Mike Frysinger412dbd22017-01-06 01:50:34 -0500617 char **loopdevs;
618 size_t num_loopdevs;
Mike Frysinger05e594e2017-01-10 02:11:08 -0500619 char **device_mappers;
620 size_t num_device_mappers;
Luis Hector Chavez8e7b6d52016-06-02 20:40:43 -0700621 char *name;
Dylan Reid837c74a2016-01-22 17:25:21 -0800622};
623
624struct container *container_new(const char *name,
Dylan Reide040c6b2016-05-02 18:49:02 -0700625 const char *rundir)
Dylan Reid837c74a2016-01-22 17:25:21 -0800626{
627 struct container *c;
628
Dylan Reid837c74a2016-01-22 17:25:21 -0800629 c = calloc(1, sizeof(*c));
Dylan Reidb435c682016-04-12 04:17:49 -0700630 if (!c)
631 return NULL;
Dylan Reid837c74a2016-01-22 17:25:21 -0800632 c->rundir = strdup(rundir);
Luis Hector Chavez8e7b6d52016-06-02 20:40:43 -0700633 c->name = strdup(name);
Dylan Reida9966422016-07-21 10:11:34 -0700634 if (!c->rundir || !c->name) {
Dylan Reid684975e2016-05-02 15:44:47 -0700635 container_destroy(c);
Dylan Reid837c74a2016-01-22 17:25:21 -0800636 return NULL;
Dylan Reidb435c682016-04-12 04:17:49 -0700637 }
Dylan Reid837c74a2016-01-22 17:25:21 -0800638 return c;
639}
640
641void container_destroy(struct container *c)
642{
Dylan Reid684975e2016-05-02 15:44:47 -0700643 if (c->cgroup)
644 container_cgroup_destroy(c->cgroup);
Luis Hector Chavez8e7b6d52016-06-02 20:40:43 -0700645 if (c->jail)
646 minijail_destroy(c->jail);
Mike Frysingerb22acdf2017-01-08 02:02:35 -0500647 FREE_AND_NULL(c->config_root);
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700648 FREE_AND_NULL(c->name);
649 FREE_AND_NULL(c->rundir);
650 FREE_AND_NULL(c);
Dylan Reid837c74a2016-01-22 17:25:21 -0800651}
652
Stephen Barber1a398c72017-01-23 12:39:44 -0800653/*
654 * Given a uid/gid map of "inside1 outside1 length1, ...", and an id
655 * inside of the user namespace, return the equivalent outside id, or
656 * return < 0 on error.
657 */
658static int get_userns_outside_id(const char *map, int id)
659{
660 char *map_copy, *mapping, *saveptr1, *saveptr2;
661 int inside, outside, length;
662 int result = 0;
663 errno = 0;
664
665 if (asprintf(&map_copy, "%s", map) < 0)
666 return -ENOMEM;
667
668 mapping = strtok_r(map_copy, ",", &saveptr1);
669 while (mapping) {
670 inside = strtol(strtok_r(mapping, " ", &saveptr2), NULL, 10);
671 outside = strtol(strtok_r(NULL, " ", &saveptr2), NULL, 10);
672 length = strtol(strtok_r(NULL, "\0", &saveptr2), NULL, 10);
673 if (errno) {
674 goto error_free_return;
675 } else if (inside < 0 || outside < 0 || length < 0) {
676 errno = EINVAL;
677 goto error_free_return;
678 }
679
680 if (id >= inside && id <= (inside + length)) {
681 result = (id - inside) + outside;
682 goto exit;
683 }
684
685 mapping = strtok_r(NULL, ",", &saveptr1);
686 }
687 errno = EINVAL;
688
689error_free_return:
690 result = -errno;
691exit:
692 free(map_copy);
693 return result;
694}
695
Dylan Reid837c74a2016-01-22 17:25:21 -0800696static int make_dir(const char *path, int uid, int gid, int mode)
697{
698 if (mkdir(path, mode))
699 return -errno;
700 if (chmod(path, mode))
701 return -errno;
702 if (chown(path, uid, gid))
703 return -errno;
704 return 0;
705}
706
707static int touch_file(const char *path, int uid, int gid, int mode)
708{
709 int rc;
710 int fd = open(path, O_RDWR | O_CREAT, mode);
711 if (fd < 0)
712 return -errno;
713 rc = fchown(fd, uid, gid);
714 close(fd);
715
716 if (rc)
717 return -errno;
718 return 0;
719}
720
721/* Make sure the mount target exists in the new rootfs. Create if needed and
722 * possible.
723 */
Stephen Barber1a398c72017-01-23 12:39:44 -0800724static int setup_mount_destination(const struct container_config *config,
725 const struct container_mount *mnt,
Dylan Reid2149be92016-04-28 18:38:57 -0700726 const char *source,
Dylan Reid837c74a2016-01-22 17:25:21 -0800727 const char *dest)
728{
Stephen Barber1a398c72017-01-23 12:39:44 -0800729 int uid_userns, gid_userns;
Dylan Reid837c74a2016-01-22 17:25:21 -0800730 int rc;
731 struct stat st_buf;
732
733 rc = stat(dest, &st_buf);
734 if (rc == 0) /* destination exists */
735 return 0;
736
737 /* Try to create the destination. Either make directory or touch a file
738 * depending on the source type.
739 */
Stephen Barber1a398c72017-01-23 12:39:44 -0800740 uid_userns = get_userns_outside_id(config->uid_map, mnt->uid);
741 if (uid_userns < 0)
742 return uid_userns;
743 gid_userns = get_userns_outside_id(config->gid_map, mnt->gid);
744 if (gid_userns < 0)
745 return gid_userns;
746
Dylan Reid2149be92016-04-28 18:38:57 -0700747 rc = stat(source, &st_buf);
Dylan Reid837c74a2016-01-22 17:25:21 -0800748 if (rc || S_ISDIR(st_buf.st_mode) || S_ISBLK(st_buf.st_mode))
Stephen Barber1a398c72017-01-23 12:39:44 -0800749 return make_dir(dest, uid_userns, gid_userns, mnt->mode);
Dylan Reid837c74a2016-01-22 17:25:21 -0800750
Stephen Barber1a398c72017-01-23 12:39:44 -0800751 return touch_file(dest, uid_userns, gid_userns, mnt->mode);
Dylan Reid837c74a2016-01-22 17:25:21 -0800752}
753
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700754/* Fork and exec the setfiles command to configure the selinux policy. */
Dylan Reide040c6b2016-05-02 18:49:02 -0700755static int run_setfiles_command(const struct container *c,
756 const struct container_config *config,
Yusuke Sato91f11f02016-12-02 16:15:13 -0800757 char *const *destinations, size_t num_destinations)
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700758{
759 int rc;
760 int status;
761 int pid;
762 char *context_path;
763
Dylan Reide040c6b2016-05-02 18:49:02 -0700764 if (!config->run_setfiles)
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700765 return 0;
766
767 if (asprintf(&context_path, "%s/file_contexts",
768 c->runfsroot) < 0)
769 return -errno;
770
771 pid = fork();
772 if (pid == 0) {
Yusuke Sato91f11f02016-12-02 16:15:13 -0800773 size_t i;
774 size_t arg_index = 0;
775 const char *argv[MAX_NUM_SETFILES_ARGS];
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700776 const char *env[] = {
777 NULL,
778 };
779
Yusuke Sato91f11f02016-12-02 16:15:13 -0800780 argv[arg_index++] = config->run_setfiles;
781 argv[arg_index++] = "-r";
782 argv[arg_index++] = c->runfsroot;
783 argv[arg_index++] = context_path;
784 if (arg_index + num_destinations >= MAX_NUM_SETFILES_ARGS)
785 _exit(-E2BIG);
786 for (i = 0; i < num_destinations; ++i) {
787 argv[arg_index++] = destinations[i];
788 }
789 argv[arg_index] = NULL;
790
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700791 execve(argv[0], (char *const*)argv, (char *const*)env);
792
793 /* Command failed to exec if execve returns. */
794 _exit(-errno);
795 }
796 free(context_path);
797 if (pid < 0)
798 return -errno;
799 do {
800 rc = waitpid(pid, &status, 0);
801 } while (rc == -1 && errno == EINTR);
802 if (rc < 0)
803 return -errno;
804 return status;
805}
806
Mike Frysinger412dbd22017-01-06 01:50:34 -0500807/* Find a free loop device and attach it. */
808static int loopdev_setup(char **loopdev_ret, const char *source)
809{
810 int ret = 0;
811 int source_fd = -1;
812 int control_fd = -1;
813 int loop_fd = -1;
814 char *loopdev = NULL;
815
816 source_fd = open(source, O_RDONLY|O_CLOEXEC);
817 if (source_fd < 0)
818 goto error;
819
820 control_fd = open(loopdev_ctl, O_RDWR|O_NOFOLLOW|O_CLOEXEC);
821 if (control_fd < 0)
822 goto error;
823
824 while (1) {
825 int num = ioctl(control_fd, LOOP_CTL_GET_FREE);
826 if (num < 0)
827 goto error;
828
829 if (asprintf(&loopdev, "/dev/loop%i", num) < 0)
830 goto error;
831
832 loop_fd = open(loopdev, O_RDONLY|O_NOFOLLOW|O_CLOEXEC);
833 if (loop_fd < 0)
834 goto error;
835
836 if (ioctl(loop_fd, LOOP_SET_FD, source_fd) == 0)
837 break;
838
839 if (errno != EBUSY)
840 goto error;
841
842 /* Clean up resources for the next pass. */
843 free(loopdev);
844 close(loop_fd);
845 }
846
847 *loopdev_ret = loopdev;
848 goto exit;
849
850error:
851 ret = -errno;
852 free(loopdev);
853exit:
854 if (source_fd != -1)
855 close(source_fd);
856 if (control_fd != -1)
857 close(control_fd);
858 if (loop_fd != -1)
859 close(loop_fd);
860 return ret;
861}
862
863/* Detach the specified loop device. */
864static int loopdev_detach(const char *loopdev)
865{
866 int ret = 0;
867 int fd;
868
869 fd = open(loopdev, O_RDONLY|O_NOFOLLOW|O_CLOEXEC);
870 if (fd < 0)
871 goto error;
872 if (ioctl(fd, LOOP_CLR_FD) < 0)
873 goto error;
874
875 goto exit;
876
877error:
878 ret = -errno;
879exit:
880 if (fd != -1)
881 close(fd);
882 return ret;
883}
884
Mike Frysinger05e594e2017-01-10 02:11:08 -0500885/* Create a new device mapper target for the source. */
886static int dm_setup(char **dm_path_ret, char **dm_name_ret, const char *source,
887 const char *verity_cmdline)
888{
889 int ret = 0;
890#if USE_device_mapper
891 char *p;
892 char *dm_path = NULL;
893 char *dm_name = NULL;
894 char *verity = NULL;
895 struct dm_task *dmt = NULL;
896 uint32_t cookie = 0;
897
898 /* Normalize the name into something unique-esque. */
899 if (asprintf(&dm_name, "cros-containers-%s", source) < 0)
900 goto error;
901 p = dm_name;
902 while ((p = strchr(p, '/')) != NULL)
903 *p++ = '_';
904
905 /* Get the /dev path for the higher levels to mount. */
906 if (asprintf(&dm_path, "%s%s", dm_dev_prefix, dm_name) < 0)
907 goto error;
908
909 /* Insert the source path in the verity command line. */
910 size_t source_len = strlen(source);
911 verity = malloc(strlen(verity_cmdline) + source_len * 2 + 1);
912 strcpy(verity, verity_cmdline);
913 while ((p = strstr(verity, "@DEV@")) != NULL) {
914 memmove(p + source_len, p + 5, strlen(p + 5) + 1);
915 memcpy(p, source, source_len);
916 }
917
918 /* Extract the first three parameters for dm-verity settings. */
919 char ttype[20];
920 unsigned long long start, size;
921 int n;
922 if (sscanf(verity, "%llu %llu %10s %n", &start, &size, ttype, &n) != 3)
923 goto error;
924
925 /* Finally create the device mapper. */
926 dmt = dm_task_create(DM_DEVICE_CREATE);
927 if (dmt == NULL)
928 goto error;
929
930 if (!dm_task_set_name(dmt, dm_name))
931 goto error;
932
933 if (!dm_task_set_ro(dmt))
934 goto error;
935
936 if (!dm_task_add_target(dmt, start, size, ttype, verity + n))
937 goto error;
938
939 if (!dm_task_set_cookie(dmt, &cookie, 0))
940 goto error;
941
942 if (!dm_task_run(dmt))
943 goto error;
944
945 /* Make sure the node exists before we continue. */
946 dm_udev_wait(cookie);
947
948 *dm_path_ret = dm_path;
949 *dm_name_ret = dm_name;
950 goto exit;
951
952error:
953 ret = -errno;
954 free(dm_name);
955 free(dm_path);
956exit:
957 free(verity);
958 if (dmt)
959 dm_task_destroy(dmt);
960#endif
961 return ret;
962}
963
964/* Tear down the device mapper target. */
965static int dm_detach(const char *dm_name)
966{
967 int ret = 0;
968#if USE_device_mapper
969 struct dm_task *dmt;
970
971 dmt = dm_task_create(DM_DEVICE_REMOVE);
972 if (dmt == NULL)
973 goto error;
974
975 if (!dm_task_set_name(dmt, dm_name))
976 goto error;
977
978 if (!dm_task_run(dmt))
979 goto error;
980
981 goto exit;
982
983error:
984 ret = -errno;
985exit:
986 dm_task_destroy(dmt);
987#endif
988 return ret;
989}
990
Dylan Reide040c6b2016-05-02 18:49:02 -0700991/*
992 * Unmounts anything we mounted in this mount namespace in the opposite order
993 * that they were mounted.
994 */
995static int unmount_external_mounts(struct container *c)
996{
997 int ret = 0;
998
999 while (c->num_ext_mounts) {
1000 c->num_ext_mounts--;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001001 if (!c->ext_mounts[c->num_ext_mounts])
1002 continue;
Dylan Reide040c6b2016-05-02 18:49:02 -07001003 if (umount(c->ext_mounts[c->num_ext_mounts]))
1004 ret = -errno;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001005 FREE_AND_NULL(c->ext_mounts[c->num_ext_mounts]);
Dylan Reide040c6b2016-05-02 18:49:02 -07001006 }
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001007 FREE_AND_NULL(c->ext_mounts);
Mike Frysinger412dbd22017-01-06 01:50:34 -05001008
1009 while (c->num_loopdevs) {
1010 c->num_loopdevs--;
1011 if (loopdev_detach(c->loopdevs[c->num_loopdevs]))
1012 ret = -errno;
1013 FREE_AND_NULL(c->loopdevs[c->num_loopdevs]);
1014 }
1015 FREE_AND_NULL(c->loopdevs);
1016
Mike Frysinger05e594e2017-01-10 02:11:08 -05001017 while (c->num_device_mappers) {
1018 c->num_device_mappers--;
1019 if (dm_detach(c->device_mappers[c->num_device_mappers]))
1020 ret = -errno;
1021 FREE_AND_NULL(c->device_mappers[c->num_device_mappers]);
1022 }
1023 FREE_AND_NULL(c->device_mappers);
1024
Dylan Reide040c6b2016-05-02 18:49:02 -07001025 return ret;
1026}
1027
Junichi Uekawa5d272772016-07-21 16:07:19 +09001028/*
1029 * Match mount_one in minijail, mount one mountpoint with
1030 * consideration for combination of MS_BIND/MS_RDONLY flag.
1031 */
1032static int mount_external(const char *src, const char *dest, const char *type,
1033 unsigned long flags, const void *data)
1034{
1035 int remount_ro = 0;
1036
1037 /*
1038 * R/O bind mounts have to be remounted since 'bind' and 'ro'
1039 * can't both be specified in the original bind mount.
1040 * Remount R/O after the initial mount.
1041 */
1042 if ((flags & MS_BIND) && (flags & MS_RDONLY)) {
1043 remount_ro = 1;
1044 flags &= ~MS_RDONLY;
1045 }
1046
1047 if (mount(src, dest, type, flags, data) == -1)
1048 return -1;
1049
1050 if (remount_ro) {
1051 flags |= MS_RDONLY;
1052 if (mount(src, dest, NULL, flags | MS_REMOUNT, data) == -1)
1053 return -1;
1054 }
1055
1056 return 0;
1057}
1058
Luis Hector Chavez3341ed62016-06-06 08:04:04 -07001059static int do_container_mount(struct container *c,
Stephen Barber1a398c72017-01-23 12:39:44 -08001060 const struct container_config *config,
Luis Hector Chavez3341ed62016-06-06 08:04:04 -07001061 const struct container_mount *mnt)
1062{
Mike Frysinger05e594e2017-01-10 02:11:08 -05001063 char *dm_source = NULL;
Mike Frysinger412dbd22017-01-06 01:50:34 -05001064 char *loop_source = NULL;
Luis Hector Chavez3341ed62016-06-06 08:04:04 -07001065 char *source = NULL;
1066 char *dest = NULL;
1067 int rc = 0;
1068
1069 if (asprintf(&dest, "%s%s", c->runfsroot, mnt->destination) < 0)
1070 return -errno;
1071
1072 /*
1073 * If it's a bind mount relative to rootfs, append source to
1074 * rootfs path, otherwise source path is absolute.
1075 */
1076 if ((mnt->flags & MS_BIND) && mnt->source[0] != '/') {
1077 if (asprintf(&source, "%s/%s", c->runfsroot, mnt->source) < 0)
1078 goto error_free_return;
Mike Frysingerb22acdf2017-01-08 02:02:35 -05001079 } else if (mnt->loopback && mnt->source[0] != '/' && c->config_root) {
1080 if (asprintf(&source, "%s/%s", c->config_root, mnt->source) < 0)
1081 goto error_free_return;
Luis Hector Chavez3341ed62016-06-06 08:04:04 -07001082 } else {
1083 if (asprintf(&source, "%s", mnt->source) < 0)
1084 goto error_free_return;
1085 }
1086
Dylan Reidbd5234c2017-06-06 21:20:07 -07001087 // Only create the destinations for external mounts, minijail will take
1088 // care of those mounted in the new namespace.
1089 if (mnt->create && !mnt->mount_in_ns) {
Stephen Barber1a398c72017-01-23 12:39:44 -08001090 rc = setup_mount_destination(config, mnt, source, dest);
Luis Hector Chavez3341ed62016-06-06 08:04:04 -07001091 if (rc)
1092 goto error_free_return;
1093 }
Mike Frysinger412dbd22017-01-06 01:50:34 -05001094 if (mnt->loopback) {
1095 /* Record this loopback file for cleanup later. */
1096 loop_source = source;
1097 source = NULL;
1098 rc = loopdev_setup(&source, loop_source);
1099 if (rc)
1100 goto error_free_return;
1101
Mike Frysinger05e594e2017-01-10 02:11:08 -05001102 /* Save this to cleanup when shutting down. */
Mike Frysinger412dbd22017-01-06 01:50:34 -05001103 rc = strdup_and_free(&c->loopdevs[c->num_loopdevs], source);
1104 if (rc)
1105 goto error_free_return;
1106 c->num_loopdevs++;
1107 }
Mike Frysinger05e594e2017-01-10 02:11:08 -05001108 if (mnt->verity) {
1109 /* Set this device up via dm-verity. */
1110 char *dm_name;
1111 dm_source = source;
1112 source = NULL;
1113 rc = dm_setup(&source, &dm_name, dm_source, mnt->verity);
1114 if (rc)
1115 goto error_free_return;
1116
1117 /* Save this to cleanup when shutting down. */
1118 rc = strdup_and_free(&c->device_mappers[c->num_device_mappers],
1119 dm_name);
1120 free(dm_name);
1121 if (rc)
1122 goto error_free_return;
1123 c->num_device_mappers++;
1124 }
Luis Hector Chavez3341ed62016-06-06 08:04:04 -07001125 if (mnt->mount_in_ns) {
1126 /* We can mount this with minijail. */
Dylan Reid36b9c012016-06-24 18:27:08 -07001127 rc = minijail_mount_with_data(c->jail, source, mnt->destination,
1128 mnt->type, mnt->flags, mnt->data);
Luis Hector Chavez3341ed62016-06-06 08:04:04 -07001129 if (rc)
1130 goto error_free_return;
1131 } else {
1132 /* Mount this externally and unmount it on exit. */
Junichi Uekawa5d272772016-07-21 16:07:19 +09001133 if (mount_external(source, dest, mnt->type, mnt->flags,
1134 mnt->data))
Luis Hector Chavez3341ed62016-06-06 08:04:04 -07001135 goto error_free_return;
1136 /* Save this to unmount when shutting down. */
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001137 rc = strdup_and_free(&c->ext_mounts[c->num_ext_mounts], dest);
1138 if (rc)
Luis Hector Chavez3341ed62016-06-06 08:04:04 -07001139 goto error_free_return;
1140 c->num_ext_mounts++;
1141 }
1142
1143 goto exit;
1144
1145error_free_return:
1146 if (!rc)
1147 rc = -errno;
1148exit:
Mike Frysinger05e594e2017-01-10 02:11:08 -05001149 free(dm_source);
Mike Frysinger412dbd22017-01-06 01:50:34 -05001150 free(loop_source);
Luis Hector Chavez3341ed62016-06-06 08:04:04 -07001151 free(source);
1152 free(dest);
1153 return rc;
1154}
1155
Dylan Reide040c6b2016-05-02 18:49:02 -07001156static int do_container_mounts(struct container *c,
1157 const struct container_config *config)
Dylan Reid7daf9982016-04-28 16:55:42 -07001158{
1159 unsigned int i;
Luis Hector Chavez8e7b6d52016-06-02 20:40:43 -07001160 int rc = 0;
Dylan Reid7daf9982016-04-28 16:55:42 -07001161
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001162 unmount_external_mounts(c);
Dylan Reide040c6b2016-05-02 18:49:02 -07001163 /*
1164 * Allocate space to track anything we mount in our mount namespace.
1165 * This over-allocates as it has space for all mounts.
1166 */
1167 c->ext_mounts = calloc(config->num_mounts, sizeof(*c->ext_mounts));
1168 if (!c->ext_mounts)
1169 return -errno;
Mike Frysinger412dbd22017-01-06 01:50:34 -05001170 c->loopdevs = calloc(config->num_mounts, sizeof(*c->loopdevs));
1171 if (!c->loopdevs)
1172 return -errno;
Mike Frysinger05e594e2017-01-10 02:11:08 -05001173 c->device_mappers = calloc(config->num_mounts, sizeof(*c->device_mappers));
1174 if (!c->device_mappers)
1175 return -errno;
Dylan Reide040c6b2016-05-02 18:49:02 -07001176
1177 for (i = 0; i < config->num_mounts; ++i) {
Stephen Barber1a398c72017-01-23 12:39:44 -08001178 rc = do_container_mount(c, config, &config->mounts[i]);
Luis Hector Chavez3341ed62016-06-06 08:04:04 -07001179 if (rc)
1180 goto error_free_return;
Dylan Reid7daf9982016-04-28 16:55:42 -07001181 }
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001182
Dylan Reid7daf9982016-04-28 16:55:42 -07001183 return 0;
Dylan Reid2149be92016-04-28 18:38:57 -07001184
1185error_free_return:
Dylan Reide040c6b2016-05-02 18:49:02 -07001186 unmount_external_mounts(c);
Luis Hector Chavez8e7b6d52016-06-02 20:40:43 -07001187 return rc;
Dylan Reid7daf9982016-04-28 16:55:42 -07001188}
1189
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001190static int container_create_device(const struct container *c,
Stephen Barber1a398c72017-01-23 12:39:44 -08001191 const struct container_config *config,
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001192 const struct container_device *dev,
1193 int minor)
1194{
1195 char *path = NULL;
1196 int rc = 0;
1197 int mode;
Stephen Barber1a398c72017-01-23 12:39:44 -08001198 int uid_userns, gid_userns;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001199
1200 switch (dev->type) {
1201 case 'b':
1202 mode = S_IFBLK;
1203 break;
1204 case 'c':
1205 mode = S_IFCHR;
1206 break;
1207 default:
1208 return -EINVAL;
1209 }
1210 mode |= dev->fs_permissions;
1211
Stephen Barber1a398c72017-01-23 12:39:44 -08001212 uid_userns = get_userns_outside_id(config->uid_map, dev->uid);
1213 if (uid_userns < 0)
1214 return uid_userns;
1215 gid_userns = get_userns_outside_id(config->gid_map, dev->gid);
1216 if (gid_userns < 0)
1217 return gid_userns;
1218
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001219 if (asprintf(&path, "%s%s", c->runfsroot, dev->path) < 0)
1220 goto error_free_return;
1221 if (mknod(path, mode, makedev(dev->major, minor)) && errno != EEXIST)
1222 goto error_free_return;
Stephen Barber1a398c72017-01-23 12:39:44 -08001223 if (chown(path, uid_userns, gid_userns))
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001224 goto error_free_return;
1225 if (chmod(path, dev->fs_permissions))
1226 goto error_free_return;
1227
1228 goto exit;
1229
1230error_free_return:
1231 rc = -errno;
1232exit:
1233 free(path);
1234 return rc;
1235}
1236
Stephen Barber1a398c72017-01-23 12:39:44 -08001237
Keshav Santhanam0e4c3282016-07-14 10:25:16 -07001238static int mount_runfs(struct container *c, const struct container_config *config)
Dylan Reid837c74a2016-01-22 17:25:21 -08001239{
Dylan Reidb3621832016-03-24 10:24:57 -07001240 static const mode_t root_dir_mode = 0660;
Dylan Reide040c6b2016-05-02 18:49:02 -07001241 const char *rootfs = config->rootfs;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001242 char *runfs_template = NULL;
Stephen Barber1a398c72017-01-23 12:39:44 -08001243 int uid_userns, gid_userns;
Dylan Reid837c74a2016-01-22 17:25:21 -08001244
Keshav Santhanam0e4c3282016-07-14 10:25:16 -07001245 if (asprintf(&runfs_template, "%s/%s_XXXXXX", c->rundir, c->name) < 0)
1246 return -ENOMEM;
1247
1248 c->runfs = mkdtemp(runfs_template);
1249 if (!c->runfs) {
1250 free(runfs_template);
1251 return -errno;
1252 }
1253
Stephen Barber1a398c72017-01-23 12:39:44 -08001254 uid_userns = get_userns_outside_id(config->uid_map, config->uid);
1255 if (uid_userns < 0)
1256 return uid_userns;
1257 gid_userns = get_userns_outside_id(config->gid_map, config->gid);
1258 if (gid_userns < 0)
1259 return gid_userns;
1260
Keshav Santhanam0e4c3282016-07-14 10:25:16 -07001261 /* Make sure the container uid can access the rootfs. */
1262 if (chmod(c->runfs, 0700))
1263 return -errno;
Stephen Barber1a398c72017-01-23 12:39:44 -08001264 if (chown(c->runfs, uid_userns, gid_userns))
Keshav Santhanam0e4c3282016-07-14 10:25:16 -07001265 return -errno;
1266
1267 if (asprintf(&c->runfsroot, "%s/root", c->runfs) < 0)
1268 return -errno;
1269
1270 if (mkdir(c->runfsroot, root_dir_mode))
1271 return -errno;
1272 if (chmod(c->runfsroot, root_dir_mode))
1273 return -errno;
1274
Luis Hector Chavezc240e7e2016-09-22 10:33:03 -07001275 if (mount(rootfs, c->runfsroot, "", MS_BIND, NULL))
Keshav Santhanam0e4c3282016-07-14 10:25:16 -07001276 return -errno;
1277
Luis Hector Chavezc240e7e2016-09-22 10:33:03 -07001278 /* MS_BIND ignores any flags passed to it (except MS_REC). We need a
1279 * second call to mount() to actually set them.
1280 */
1281 if (config->rootfs_mount_flags &&
1282 mount(rootfs, c->runfsroot, "",
1283 config->rootfs_mount_flags, NULL)) {
1284 return -errno;
1285 }
1286
Keshav Santhanam0e4c3282016-07-14 10:25:16 -07001287 return 0;
1288}
1289
Dylan Reidacedff92017-03-31 17:41:40 -07001290static int device_setup(struct container *c,
1291 const struct container_config *config)
1292{
Dylan Reid43d4e5c2017-04-05 09:40:11 -07001293 int rc;
1294 size_t i;
Dylan Reidacedff92017-03-31 17:41:40 -07001295
1296 c->cgroup->ops->deny_all_devices(c->cgroup);
1297
Dylan Reid4843d6b2017-03-31 18:14:30 -07001298 for (i = 0; i < config->num_cgroup_devices; i++) {
1299 const struct container_cgroup_device *dev =
1300 &config->cgroup_devices[i];
1301 rc = c->cgroup->ops->add_device(c->cgroup,
1302 dev->allow,
1303 dev->major,
1304 dev->minor,
1305 dev->read,
1306 dev->write,
1307 dev->modify,
1308 dev->type);
1309 if (rc)
1310 return rc;
1311 }
1312
Dylan Reidacedff92017-03-31 17:41:40 -07001313 for (i = 0; i < config->num_devices; i++) {
1314 const struct container_device *dev = &config->devices[i];
1315 int minor = dev->minor;
1316
1317 if (dev->copy_minor) {
1318 struct stat st_buff;
1319 if (stat(dev->path, &st_buff) < 0)
1320 continue;
1321 minor = minor(st_buff.st_rdev);
1322 }
1323 if (minor >= 0) {
1324 rc = container_create_device(c, config, dev, minor);
1325 if (rc)
1326 return rc;
1327 }
Dylan Reidacedff92017-03-31 17:41:40 -07001328 }
1329
1330 for (i = 0; i < c->num_loopdevs; ++i) {
1331 struct stat st;
1332
Dylan Reid43d4e5c2017-04-05 09:40:11 -07001333 rc = stat(c->loopdevs[i], &st);
1334 if (rc < 0)
1335 return -errno;
Dylan Reid4843d6b2017-03-31 18:14:30 -07001336 rc = c->cgroup->ops->add_device(c->cgroup, 1, major(st.st_rdev),
Dylan Reidacedff92017-03-31 17:41:40 -07001337 minor(st.st_rdev),
1338 1, 0, 0, 'b');
1339 if (rc)
1340 return rc;
1341 }
1342
1343 return 0;
1344}
1345
Keshav Santhanam0e4c3282016-07-14 10:25:16 -07001346int container_start(struct container *c, const struct container_config *config)
1347{
1348 int rc = 0;
1349 unsigned int i;
Stephen Barber1a398c72017-01-23 12:39:44 -08001350 int cgroup_uid, cgroup_gid;
Yusuke Sato91f11f02016-12-02 16:15:13 -08001351 char **destinations;
1352 size_t num_destinations;
Keshav Santhanam0e4c3282016-07-14 10:25:16 -07001353
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001354 if (!c)
1355 return -EINVAL;
Dylan Reide040c6b2016-05-02 18:49:02 -07001356 if (!config)
1357 return -EINVAL;
1358 if (!config->program_argv || !config->program_argv[0])
1359 return -EINVAL;
1360
Mike Frysingerb22acdf2017-01-08 02:02:35 -05001361 if (config->config_root) {
1362 c->config_root = strdup(config->config_root);
1363 if (!c->config_root) {
1364 rc = -ENOMEM;
1365 goto error_rmdir;
1366 }
1367 }
Keshav Santhanam0e4c3282016-07-14 10:25:16 -07001368 if (config->premounted_runfs) {
1369 c->runfs = NULL;
1370 c->runfsroot = strdup(config->premounted_runfs);
1371 if (!c->runfsroot) {
1372 rc = -ENOMEM;
1373 goto error_rmdir;
1374 }
1375 } else {
1376 rc = mount_runfs(c, config);
1377 if (rc)
1378 goto error_rmdir;
Dylan Reid837c74a2016-01-22 17:25:21 -08001379 }
Dylan Reid837c74a2016-01-22 17:25:21 -08001380
1381 c->jail = minijail_new();
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001382 if (!c->jail)
Luis Hector Chavez945af482016-06-03 08:39:34 -07001383 goto error_rmdir;
Dylan Reid837c74a2016-01-22 17:25:21 -08001384
Luis Hector Chavez8e7b6d52016-06-02 20:40:43 -07001385 rc = do_container_mounts(c, config);
1386 if (rc)
Dylan Reid7daf9982016-04-28 16:55:42 -07001387 goto error_rmdir;
Dylan Reid837c74a2016-01-22 17:25:21 -08001388
Stephen Barber1a398c72017-01-23 12:39:44 -08001389 cgroup_uid = get_userns_outside_id(config->uid_map,
1390 config->cgroup_owner);
1391 if (cgroup_uid < 0) {
1392 rc = cgroup_uid;
1393 goto error_rmdir;
1394 }
1395 cgroup_gid = get_userns_outside_id(config->gid_map,
1396 config->cgroup_group);
1397 if (cgroup_gid < 0) {
1398 rc = cgroup_gid;
1399 goto error_rmdir;
1400 }
1401
Dylan Reida9966422016-07-21 10:11:34 -07001402 c->cgroup = container_cgroup_new(c->name,
1403 "/sys/fs/cgroup",
1404 config->cgroup_parent,
Stephen Barber1a398c72017-01-23 12:39:44 -08001405 cgroup_uid,
1406 cgroup_gid);
Dylan Reida9966422016-07-21 10:11:34 -07001407 if (!c->cgroup)
1408 goto error_rmdir;
1409
Keshav Santhanam268fa032016-07-14 09:59:24 -07001410 /* Must be root to modify device cgroup or mknod */
1411 if (getuid() == 0) {
Dylan Reidacedff92017-03-31 17:41:40 -07001412 if (device_setup(c, config))
1413 goto error_rmdir;
Dylan Reid837c74a2016-01-22 17:25:21 -08001414 }
1415
Dylan Reidd7229582016-04-27 17:08:40 -07001416 /* Potentailly run setfiles on mounts configured outside of the jail */
Yusuke Sato91f11f02016-12-02 16:15:13 -08001417 destinations = calloc(config->num_mounts, sizeof(char *));
1418 num_destinations = 0;
Dylan Reide040c6b2016-05-02 18:49:02 -07001419 for (i = 0; i < config->num_mounts; i++) {
1420 const struct container_mount *mnt = &config->mounts[i];
Yusuke Sato91f11f02016-12-02 16:15:13 -08001421 char* dest = mnt->destination;
Dylan Reidd7229582016-04-27 17:08:40 -07001422
1423 if (mnt->mount_in_ns)
1424 continue;
Junichi Uekawa5d272772016-07-21 16:07:19 +09001425 if (mnt->flags & MS_RDONLY)
1426 continue;
Yusuke Sato91f11f02016-12-02 16:15:13 -08001427
Yusuke Satod33db432016-12-05 16:24:37 -08001428 /* A hack to avoid setfiles on /data and /cache. */
1429 if (!strcmp(dest, "/data") || !strcmp(dest, "/cache"))
Yusuke Sato91f11f02016-12-02 16:15:13 -08001430 continue;
1431
1432 if (asprintf(&dest, "%s%s", c->runfsroot, mnt->destination) < 0) {
1433 size_t j;
1434 for (j = 0; j < num_destinations; ++j) {
1435 free(destinations[j]);
1436 }
1437 free(destinations);
Dylan Reidd7229582016-04-27 17:08:40 -07001438 goto error_rmdir;
Yusuke Sato91f11f02016-12-02 16:15:13 -08001439 }
1440
1441 destinations[num_destinations++] = dest;
Dylan Reidd7229582016-04-27 17:08:40 -07001442 }
Yusuke Sato91f11f02016-12-02 16:15:13 -08001443 if (num_destinations) {
1444 size_t i;
1445 rc = run_setfiles_command(c, config, destinations, num_destinations);
1446 for (i = 0; i < num_destinations; ++i) {
1447 free(destinations[i]);
1448 }
1449 }
1450 free(destinations);
1451 if (rc)
1452 goto error_rmdir;
Dylan Reidd7229582016-04-27 17:08:40 -07001453
Chinyue Chenfac909e2016-06-24 14:17:42 +08001454 /* Setup CPU cgroup params. */
1455 if (config->cpu_cgparams.shares) {
1456 rc = c->cgroup->ops->set_cpu_shares(
1457 c->cgroup, config->cpu_cgparams.shares);
1458 if (rc)
1459 goto error_rmdir;
1460 }
1461 if (config->cpu_cgparams.period) {
1462 rc = c->cgroup->ops->set_cpu_quota(
1463 c->cgroup, config->cpu_cgparams.quota);
1464 if (rc)
1465 goto error_rmdir;
1466 rc = c->cgroup->ops->set_cpu_period(
1467 c->cgroup, config->cpu_cgparams.period);
1468 if (rc)
1469 goto error_rmdir;
1470 }
1471 if (config->cpu_cgparams.rt_period) {
1472 rc = c->cgroup->ops->set_cpu_rt_runtime(
1473 c->cgroup, config->cpu_cgparams.rt_runtime);
1474 if (rc)
1475 goto error_rmdir;
1476 rc = c->cgroup->ops->set_cpu_rt_period(
1477 c->cgroup, config->cpu_cgparams.rt_period);
1478 if (rc)
1479 goto error_rmdir;
1480 }
1481
Dylan Reid837c74a2016-01-22 17:25:21 -08001482 /* Setup and start the container with libminijail. */
Keshav Santhanam0e4c3282016-07-14 10:25:16 -07001483 if (config->pid_file_path) {
1484 c->pid_file_path = strdup(config->pid_file_path);
1485 if (!c->pid_file_path) {
1486 rc = -ENOMEM;
1487 goto error_rmdir;
1488 }
1489 } else if (c->runfs) {
1490 if (asprintf(&c->pid_file_path, "%s/container.pid", c->runfs) < 0) {
1491 rc = -ENOMEM;
1492 goto error_rmdir;
1493 }
1494 }
1495
1496 if (c->pid_file_path)
1497 minijail_write_pid_file(c->jail, c->pid_file_path);
Dylan Reid837c74a2016-01-22 17:25:21 -08001498 minijail_reset_signal_mask(c->jail);
1499
1500 /* Setup container namespaces. */
1501 minijail_namespace_ipc(c->jail);
1502 minijail_namespace_vfs(c->jail);
Keshav Santhanam1b6bf672016-08-10 18:35:12 -07001503 if (!config->share_host_netns)
1504 minijail_namespace_net(c->jail);
Dylan Reid837c74a2016-01-22 17:25:21 -08001505 minijail_namespace_pids(c->jail);
Dylan Reid837c74a2016-01-22 17:25:21 -08001506 minijail_namespace_user(c->jail);
Mike Frysingerfbd60552017-01-03 17:28:48 -05001507 if (getuid() != 0)
1508 minijail_namespace_user_disable_setgroups(c->jail);
Dylan Reidc6ca1042016-07-11 15:03:27 -07001509 minijail_namespace_cgroups(c->jail);
Dylan Reide040c6b2016-05-02 18:49:02 -07001510 rc = minijail_uidmap(c->jail, config->uid_map);
Dylan Reid837c74a2016-01-22 17:25:21 -08001511 if (rc)
1512 goto error_rmdir;
Dylan Reide040c6b2016-05-02 18:49:02 -07001513 rc = minijail_gidmap(c->jail, config->gid_map);
Dylan Reid837c74a2016-01-22 17:25:21 -08001514 if (rc)
1515 goto error_rmdir;
Dylan Reid837c74a2016-01-22 17:25:21 -08001516
Keshav Santhanam36485ff2016-08-02 16:21:02 -07001517 /* Set the UID/GID inside the container if not 0. */
Stephen Barber1a398c72017-01-23 12:39:44 -08001518 if (get_userns_outside_id(config->uid_map, config->uid) < 0)
Keshav Santhanam36485ff2016-08-02 16:21:02 -07001519 goto error_rmdir;
Stephen Barber1a398c72017-01-23 12:39:44 -08001520 else if (config->uid > 0)
1521 minijail_change_uid(c->jail, config->uid);
1522 if (get_userns_outside_id(config->gid_map, config->gid) < 0)
Keshav Santhanam36485ff2016-08-02 16:21:02 -07001523 goto error_rmdir;
Stephen Barber1a398c72017-01-23 12:39:44 -08001524 else if (config->gid > 0)
1525 minijail_change_gid(c->jail, config->gid);
Keshav Santhanam36485ff2016-08-02 16:21:02 -07001526
Dylan Reid837c74a2016-01-22 17:25:21 -08001527 rc = minijail_enter_pivot_root(c->jail, c->runfsroot);
1528 if (rc)
1529 goto error_rmdir;
1530
1531 /* Add the cgroups configured above. */
Dmitry Torokhov0d253a62017-01-05 09:41:33 -08001532 for (i = 0; i < NUM_CGROUP_TYPES; i++) {
1533 if (c->cgroup->cgroup_tasks_paths[i]) {
1534 rc = minijail_add_to_cgroup(c->jail,
1535 c->cgroup->cgroup_tasks_paths[i]);
1536 if (rc)
1537 goto error_rmdir;
1538 }
1539 }
Dylan Reid837c74a2016-01-22 17:25:21 -08001540
Dylan Reide040c6b2016-05-02 18:49:02 -07001541 if (config->alt_syscall_table)
1542 minijail_use_alt_syscall(c->jail, config->alt_syscall_table);
Dylan Reid837c74a2016-01-22 17:25:21 -08001543
1544 minijail_run_as_init(c->jail);
1545
Dylan Reid3da683b2016-04-05 03:35:35 -07001546 /* TODO(dgreid) - remove this once shared mounts are cleaned up. */
1547 minijail_skip_remount_private(c->jail);
1548
Dylan Reidc4335842016-11-11 10:24:52 -08001549 if (!config->keep_fds_open)
1550 minijail_close_open_fds(c->jail);
Luis Hector Chaveze18e7d42016-10-12 07:35:32 -07001551
Dylan Reid837c74a2016-01-22 17:25:21 -08001552 rc = minijail_run_pid_pipes_no_preload(c->jail,
Dylan Reide040c6b2016-05-02 18:49:02 -07001553 config->program_argv[0],
1554 config->program_argv,
Dylan Reid837c74a2016-01-22 17:25:21 -08001555 &c->init_pid, NULL, NULL,
1556 NULL);
1557 if (rc)
1558 goto error_rmdir;
1559 return 0;
1560
1561error_rmdir:
Luis Hector Chavez945af482016-06-03 08:39:34 -07001562 if (!rc)
1563 rc = -errno;
1564 container_teardown(c);
Dylan Reid837c74a2016-01-22 17:25:21 -08001565 return rc;
1566}
1567
1568const char *container_root(struct container *c)
1569{
1570 return c->runfs;
1571}
1572
1573int container_pid(struct container *c)
1574{
1575 return c->init_pid;
1576}
1577
1578static int container_teardown(struct container *c)
1579{
Dylan Reid837c74a2016-01-22 17:25:21 -08001580 int ret = 0;
1581
Dylan Reide040c6b2016-05-02 18:49:02 -07001582 unmount_external_mounts(c);
Keshav Santhanam0e4c3282016-07-14 10:25:16 -07001583 if (c->runfsroot && c->runfs) {
Luis Hector Chavez945af482016-06-03 08:39:34 -07001584 if (umount(c->runfsroot))
1585 ret = -errno;
1586 if (rmdir(c->runfsroot))
1587 ret = -errno;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001588 FREE_AND_NULL(c->runfsroot);
Luis Hector Chavez945af482016-06-03 08:39:34 -07001589 }
1590 if (c->pid_file_path) {
1591 if (unlink(c->pid_file_path))
1592 ret = -errno;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001593 FREE_AND_NULL(c->pid_file_path);
Luis Hector Chavez945af482016-06-03 08:39:34 -07001594 }
1595 if (c->runfs) {
1596 if (rmdir(c->runfs))
1597 ret = -errno;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001598 FREE_AND_NULL(c->runfs);
Luis Hector Chavez945af482016-06-03 08:39:34 -07001599 }
Dylan Reid837c74a2016-01-22 17:25:21 -08001600 return ret;
1601}
1602
1603int container_wait(struct container *c)
1604{
Dylan Reidcf745c52016-04-22 10:18:03 -07001605 int rc;
1606
1607 do {
1608 rc = minijail_wait(c->jail);
Luis Hector Chavez4641e852016-06-02 15:40:19 -07001609 } while (rc == -EINTR);
Dylan Reidcf745c52016-04-22 10:18:03 -07001610
Luis Hector Chavez945af482016-06-03 08:39:34 -07001611 // If the process had already been reaped, still perform teardown.
1612 if (rc == -ECHILD || rc >= 0) {
Dylan Reidcf745c52016-04-22 10:18:03 -07001613 rc = container_teardown(c);
Luis Hector Chavez945af482016-06-03 08:39:34 -07001614 }
Dylan Reidcf745c52016-04-22 10:18:03 -07001615 return rc;
Dylan Reid837c74a2016-01-22 17:25:21 -08001616}
1617
1618int container_kill(struct container *c)
1619{
Luis Hector Chavez945af482016-06-03 08:39:34 -07001620 if (kill(c->init_pid, SIGKILL) && errno != ESRCH)
Dylan Reid837c74a2016-01-22 17:25:21 -08001621 return -errno;
1622 return container_wait(c);
1623}