blob: 79bb26f82a0d73ef5a5680a54d3aed7fd761e723 [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>
10#include <malloc.h>
11#include <signal.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <sys/mount.h>
16#include <sys/stat.h>
17#include <sys/types.h>
Dylan Reid2bd9ea92016-04-07 20:57:47 -070018#include <sys/wait.h>
Dylan Reid837c74a2016-01-22 17:25:21 -080019#include <unistd.h>
20
21#include "container_cgroup.h"
22#include "libcontainer.h"
23#include "libminijail.h"
24
Luis Hector Chavez479b95f2016-06-06 08:01:05 -070025#define FREE_AND_NULL(ptr) \
26do { \
27 free(ptr); \
28 ptr = NULL; \
29} while(0)
30
Luis Hector Chavez945af482016-06-03 08:39:34 -070031static int container_teardown(struct container *c);
32
Luis Hector Chavez479b95f2016-06-06 08:01:05 -070033static int strdup_and_free(char **dest, const char *src)
34{
35 char *copy = strdup(src);
36 if (!copy)
37 return -ENOMEM;
38 if (*dest)
39 free(*dest);
40 *dest = copy;
41 return 0;
42}
43
Dylan Reid837c74a2016-01-22 17:25:21 -080044struct container_mount {
45 char *name;
46 char *source;
47 char *destination;
48 char *type;
49 char *data;
50 int flags;
51 int uid;
52 int gid;
53 int mode;
54 int mount_in_ns; /* True if mount should happen in new vfs ns */
55 int create; /* True if target should be created if it doesn't exist */
56};
57
58struct container_device {
59 char type; /* 'c' or 'b' for char or block */
60 char *path;
61 int fs_permissions;
62 int major;
63 int minor;
Dylan Reid355d5e42016-04-29 16:53:31 -070064 int copy_minor; /* Copy the minor from existing node, ignores |minor| */
Dylan Reid837c74a2016-01-22 17:25:21 -080065 int uid;
66 int gid;
67 int read_allowed;
68 int write_allowed;
69 int modify_allowed;
70};
71
Chinyue Chenfac909e2016-06-24 14:17:42 +080072struct container_cpu_cgroup {
73 int shares;
74 int quota;
75 int period;
76 int rt_runtime;
77 int rt_period;
78};
79
Dylan Reid837c74a2016-01-22 17:25:21 -080080/*
81 * Structure that configures how the container is run.
82 *
83 * rootfs - Path to the root of the container's filesystem.
Keshav Santhanam0e4c3282016-07-14 10:25:16 -070084 * premounted_runfs - Path to where the container will be run.
85 * pid_file_path - Path to the file where the pid should be written.
Dylan Reid837c74a2016-01-22 17:25:21 -080086 * program_argv - The program to run and args, e.g. "/sbin/init".
87 * num_args - Number of args in program_argv.
Dylan Reid1874feb2016-06-22 17:53:50 -070088 * uid - The uid the container will run as.
Dylan Reid837c74a2016-01-22 17:25:21 -080089 * uid_map - Mapping of UIDs in the container, e.g. "0 100000 1024"
Dylan Reid1874feb2016-06-22 17:53:50 -070090 * gid - The gid the container will run as.
Dylan Reid837c74a2016-01-22 17:25:21 -080091 * gid_map - Mapping of GIDs in the container, e.g. "0 100000 1024"
92 * alt_syscall_table - Syscall table to use or NULL if none.
93 * mounts - Filesystems to mount in the new namespace.
94 * num_mounts - Number of above.
95 * devices - Device nodes to create.
96 * num_devices - Number of above.
Dylan Reid2bd9ea92016-04-07 20:57:47 -070097 * run_setfiles - Should run setfiles on mounts to enable selinux.
Chinyue Chenfac909e2016-06-24 14:17:42 +080098 * cpu_cgparams - CPU cgroup params.
Dylan Reid9e724af2016-07-21 09:58:07 -070099 * cgroup_parent - Parent dir for cgroup creation
100 * cgroup_owner - uid to own the created cgroups
Dylan Reid837c74a2016-01-22 17:25:21 -0800101 */
102struct container_config {
103 char *rootfs;
Keshav Santhanam0e4c3282016-07-14 10:25:16 -0700104 char *premounted_runfs;
105 char *pid_file_path;
Dylan Reid837c74a2016-01-22 17:25:21 -0800106 char **program_argv;
107 size_t num_args;
Dylan Reid1874feb2016-06-22 17:53:50 -0700108 uid_t uid;
Dylan Reid837c74a2016-01-22 17:25:21 -0800109 char *uid_map;
Dylan Reid1874feb2016-06-22 17:53:50 -0700110 gid_t gid;
Dylan Reid837c74a2016-01-22 17:25:21 -0800111 char *gid_map;
112 char *alt_syscall_table;
113 struct container_mount *mounts;
114 size_t num_mounts;
115 struct container_device *devices;
116 size_t num_devices;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700117 char *run_setfiles;
Chinyue Chenfac909e2016-06-24 14:17:42 +0800118 struct container_cpu_cgroup cpu_cgparams;
Dylan Reid9e724af2016-07-21 09:58:07 -0700119 char *cgroup_parent;
120 uid_t cgroup_owner;
Dylan Reid837c74a2016-01-22 17:25:21 -0800121};
122
123struct container_config *container_config_create()
124{
125 return calloc(1, sizeof(struct container_config));
126}
127
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700128static void container_free_program_args(struct container_config *c)
129{
130 int i;
131
132 if (!c->program_argv)
133 return;
134 for (i = 0; i < c->num_args; ++i) {
135 FREE_AND_NULL(c->program_argv[i]);
136 }
137 FREE_AND_NULL(c->program_argv);
138}
139
140static void container_config_free_mount(struct container_mount *mount)
141{
142 FREE_AND_NULL(mount->name);
143 FREE_AND_NULL(mount->source);
144 FREE_AND_NULL(mount->destination);
145 FREE_AND_NULL(mount->type);
146 FREE_AND_NULL(mount->data);
147}
148
149static void container_config_free_device(struct container_device *device)
150{
151 FREE_AND_NULL(device->path);
152}
153
Dylan Reid837c74a2016-01-22 17:25:21 -0800154void container_config_destroy(struct container_config *c)
155{
156 size_t i;
157
158 if (c == NULL)
159 return;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700160 FREE_AND_NULL(c->rootfs);
161 container_free_program_args(c);
Keshav Santhanam0e4c3282016-07-14 10:25:16 -0700162 FREE_AND_NULL(c->premounted_runfs);
163 FREE_AND_NULL(c->pid_file_path);
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700164 FREE_AND_NULL(c->uid_map);
165 FREE_AND_NULL(c->gid_map);
166 FREE_AND_NULL(c->alt_syscall_table);
Dylan Reid837c74a2016-01-22 17:25:21 -0800167 for (i = 0; i < c->num_mounts; ++i) {
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700168 container_config_free_mount(&c->mounts[i]);
Dylan Reid837c74a2016-01-22 17:25:21 -0800169 }
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700170 FREE_AND_NULL(c->mounts);
Dylan Reid837c74a2016-01-22 17:25:21 -0800171 for (i = 0; i < c->num_devices; ++i) {
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700172 container_config_free_device(&c->devices[i]);
Dylan Reid837c74a2016-01-22 17:25:21 -0800173 }
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700174 FREE_AND_NULL(c->devices);
175 FREE_AND_NULL(c->run_setfiles);
Dylan Reid9e724af2016-07-21 09:58:07 -0700176 FREE_AND_NULL(c->cgroup_parent);
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700177 FREE_AND_NULL(c);
Dylan Reid837c74a2016-01-22 17:25:21 -0800178}
179
180int container_config_rootfs(struct container_config *c, const char *rootfs)
181{
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700182 return strdup_and_free(&c->rootfs, rootfs);
Dylan Reid837c74a2016-01-22 17:25:21 -0800183}
184
Dylan Reid11456722016-05-02 11:24:50 -0700185const char *container_config_get_rootfs(const struct container_config *c)
186{
187 return c->rootfs;
188}
189
Keshav Santhanam0e4c3282016-07-14 10:25:16 -0700190int container_config_premounted_runfs(struct container_config *c, const char *runfs)
191{
192 return strdup_and_free(&c->premounted_runfs, runfs);
193}
194
195const char *container_config_get_premounted_runfs(const struct container_config *c)
196{
197 return c->premounted_runfs;
198}
199
200int container_config_pid_file(struct container_config *c, const char *path)
201{
202 return strdup_and_free(&c->pid_file_path, path);
203}
204
205const char *container_config_get_pid_file(const struct container_config *c)
206{
207 return c->pid_file_path;
208}
209
Dylan Reid837c74a2016-01-22 17:25:21 -0800210int container_config_program_argv(struct container_config *c,
211 char **argv, size_t num_args)
212{
213 size_t i;
214
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700215 container_free_program_args(c);
Dylan Reid837c74a2016-01-22 17:25:21 -0800216 c->num_args = num_args;
217 c->program_argv = calloc(num_args + 1, sizeof(char *));
218 if (!c->program_argv)
219 return -ENOMEM;
220 for (i = 0; i < num_args; ++i) {
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700221 if (strdup_and_free(&c->program_argv[i], argv[i]))
222 goto error_free_return;
Dylan Reid837c74a2016-01-22 17:25:21 -0800223 }
224 c->program_argv[num_args] = NULL;
225 return 0;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700226
227error_free_return:
228 container_free_program_args(c);
229 return -ENOMEM;
Dylan Reid837c74a2016-01-22 17:25:21 -0800230}
231
Dylan Reid11456722016-05-02 11:24:50 -0700232size_t container_config_get_num_program_args(const struct container_config *c)
233{
234 return c->num_args;
235}
236
237const char *container_config_get_program_arg(const struct container_config *c,
238 size_t index)
239{
240 if (index >= c->num_args)
241 return NULL;
242 return c->program_argv[index];
243}
244
Dylan Reid1874feb2016-06-22 17:53:50 -0700245void container_config_uid(struct container_config *c, uid_t uid)
246{
247 c->uid = uid;
248}
249
250uid_t container_config_get_uid(const struct container_config *c)
251{
252 return c->uid;
253}
254
Dylan Reid837c74a2016-01-22 17:25:21 -0800255int container_config_uid_map(struct container_config *c, const char *uid_map)
256{
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700257 return strdup_and_free(&c->uid_map, uid_map);
Dylan Reid837c74a2016-01-22 17:25:21 -0800258}
259
Dylan Reid1874feb2016-06-22 17:53:50 -0700260void container_config_gid(struct container_config *c, gid_t gid)
261{
262 c->gid = gid;
263}
264
265gid_t container_config_get_gid(const struct container_config *c)
266{
267 return c->gid;
268}
269
Dylan Reid837c74a2016-01-22 17:25:21 -0800270int container_config_gid_map(struct container_config *c, const char *gid_map)
271{
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700272 return strdup_and_free(&c->gid_map, gid_map);
Dylan Reid837c74a2016-01-22 17:25:21 -0800273}
274
275int container_config_alt_syscall_table(struct container_config *c,
276 const char *alt_syscall_table)
277{
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700278 return strdup_and_free(&c->alt_syscall_table, alt_syscall_table);
Dylan Reid837c74a2016-01-22 17:25:21 -0800279}
280
281int container_config_add_mount(struct container_config *c,
282 const char *name,
283 const char *source,
284 const char *destination,
285 const char *type,
286 const char *data,
287 int flags,
288 int uid,
289 int gid,
290 int mode,
291 int mount_in_ns,
292 int create)
293{
294 struct container_mount *mount_ptr;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700295 struct container_mount *current_mount;
Dylan Reid837c74a2016-01-22 17:25:21 -0800296
297 if (name == NULL || source == NULL ||
298 destination == NULL || type == NULL)
299 return -EINVAL;
300
301 mount_ptr = realloc(c->mounts,
302 sizeof(c->mounts[0]) * (c->num_mounts + 1));
303 if (!mount_ptr)
304 return -ENOMEM;
305 c->mounts = mount_ptr;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700306 current_mount = &c->mounts[c->num_mounts];
307 memset(current_mount, 0, sizeof(struct container_mount));
308
309 if (strdup_and_free(&current_mount->name, name))
310 goto error_free_return;
311 if (strdup_and_free(&current_mount->source, source))
312 goto error_free_return;
313 if (strdup_and_free(&current_mount->destination, destination))
314 goto error_free_return;
315 if (strdup_and_free(&current_mount->type, type))
316 goto error_free_return;
317 if (data && strdup_and_free(&current_mount->data, data))
318 goto error_free_return;
319 current_mount->flags = flags;
320 current_mount->uid = uid;
321 current_mount->gid = gid;
322 current_mount->mode = mode;
323 current_mount->mount_in_ns = mount_in_ns;
324 current_mount->create = create;
Dylan Reid837c74a2016-01-22 17:25:21 -0800325 ++c->num_mounts;
326 return 0;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700327
328error_free_return:
329 container_config_free_mount(current_mount);
330 return -ENOMEM;
Dylan Reid837c74a2016-01-22 17:25:21 -0800331}
332
333int container_config_add_device(struct container_config *c,
334 char type,
335 const char *path,
336 int fs_permissions,
337 int major,
338 int minor,
Dylan Reid355d5e42016-04-29 16:53:31 -0700339 int copy_minor,
Dylan Reid837c74a2016-01-22 17:25:21 -0800340 int uid,
341 int gid,
342 int read_allowed,
343 int write_allowed,
344 int modify_allowed)
345{
346 struct container_device *dev_ptr;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700347 struct container_device *current_dev;
Dylan Reid837c74a2016-01-22 17:25:21 -0800348
349 if (path == NULL)
350 return -EINVAL;
Dylan Reid355d5e42016-04-29 16:53:31 -0700351 /* If using a dynamic minor number, ensure that minor is -1. */
352 if (copy_minor && (minor != -1))
353 return -EINVAL;
354
Dylan Reid837c74a2016-01-22 17:25:21 -0800355 dev_ptr = realloc(c->devices,
356 sizeof(c->devices[0]) * (c->num_devices + 1));
357 if (!dev_ptr)
358 return -ENOMEM;
359 c->devices = dev_ptr;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700360 current_dev = &c->devices[c->num_devices];
361 memset(current_dev, 0, sizeof(struct container_device));
362
363 current_dev->type = type;
364 if (strdup_and_free(&current_dev->path, path))
365 goto error_free_return;
366 current_dev->fs_permissions = fs_permissions;
367 current_dev->major = major;
368 current_dev->minor = minor;
369 current_dev->copy_minor = copy_minor;
370 current_dev->uid = uid;
371 current_dev->gid = gid;
372 current_dev->read_allowed = read_allowed;
373 current_dev->write_allowed = write_allowed;
374 current_dev->modify_allowed = modify_allowed;
Dylan Reid837c74a2016-01-22 17:25:21 -0800375 ++c->num_devices;
376 return 0;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700377
378error_free_return:
379 container_config_free_device(current_dev);
380 return -ENOMEM;
Dylan Reid837c74a2016-01-22 17:25:21 -0800381}
382
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700383int container_config_run_setfiles(struct container_config *c,
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700384 const char *setfiles_cmd)
385{
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700386 return strdup_and_free(&c->run_setfiles, setfiles_cmd);
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700387}
Dylan Reid837c74a2016-01-22 17:25:21 -0800388
Dylan Reid11456722016-05-02 11:24:50 -0700389const char *container_config_get_run_setfiles(const struct container_config *c)
390{
391 return c->run_setfiles;
392}
393
Chinyue Chenfac909e2016-06-24 14:17:42 +0800394int container_config_set_cpu_shares(struct container_config *c, int shares)
395{
396 /* CPU shares must be 2 or higher. */
397 if (shares < 2)
398 return -EINVAL;
399
400 c->cpu_cgparams.shares = shares;
401 return 0;
402}
403
404int container_config_set_cpu_cfs_params(struct container_config *c,
405 int quota,
406 int period)
407{
408 /*
409 * quota could be set higher than period to utilize more than one CPU.
410 * quota could also be set as -1 to indicate the cgroup does not adhere
411 * to any CPU time restrictions.
412 */
413 if (quota <= 0 && quota != -1)
414 return -EINVAL;
415 if (period <= 0)
416 return -EINVAL;
417
418 c->cpu_cgparams.quota = quota;
419 c->cpu_cgparams.period = period;
420 return 0;
421}
422
423int container_config_set_cpu_rt_params(struct container_config *c,
424 int rt_runtime,
425 int rt_period)
426{
427 /*
428 * rt_runtime could be set as 0 to prevent the cgroup from using
429 * realtime CPU.
430 */
431 if (rt_runtime < 0 || rt_runtime >= rt_period)
432 return -EINVAL;
433
434 c->cpu_cgparams.rt_runtime = rt_runtime;
435 c->cpu_cgparams.rt_period = rt_period;
436 return 0;
437}
438
Chinyue Chen4f3fd682016-07-01 14:11:42 +0800439int container_config_get_cpu_shares(struct container_config *c)
440{
441 return c->cpu_cgparams.shares;
442}
443
444int container_config_get_cpu_quota(struct container_config *c)
445{
446 return c->cpu_cgparams.quota;
447}
448
449int container_config_get_cpu_period(struct container_config *c)
450{
451 return c->cpu_cgparams.period;
452}
453
454int container_config_get_cpu_rt_runtime(struct container_config *c)
455{
456 return c->cpu_cgparams.rt_runtime;
457}
458
459int container_config_get_cpu_rt_period(struct container_config *c)
460{
461 return c->cpu_cgparams.rt_period;
462}
463
Dylan Reid9e724af2016-07-21 09:58:07 -0700464int container_config_set_cgroup_parent(struct container_config *c,
465 const char *parent,
466 uid_t cgroup_owner)
467{
468 c->cgroup_owner = cgroup_owner;
469 return strdup_and_free(&c->cgroup_parent, parent);
470}
471
472const char *container_config_get_cgroup_parent(struct container_config *c)
473{
474 return c->cgroup_parent;
475}
476
Dylan Reid837c74a2016-01-22 17:25:21 -0800477/*
478 * Container manipulation
479 */
480struct container {
Dylan Reid837c74a2016-01-22 17:25:21 -0800481 struct container_cgroup *cgroup;
482 struct minijail *jail;
483 pid_t init_pid;
484 char *runfs;
485 char *rundir;
486 char *runfsroot;
487 char *pid_file_path;
Dylan Reide040c6b2016-05-02 18:49:02 -0700488 char **ext_mounts; /* Mounts made outside of the minijail */
489 size_t num_ext_mounts;
Luis Hector Chavez8e7b6d52016-06-02 20:40:43 -0700490 char *name;
Dylan Reid837c74a2016-01-22 17:25:21 -0800491};
492
493struct container *container_new(const char *name,
Dylan Reide040c6b2016-05-02 18:49:02 -0700494 const char *rundir)
Dylan Reid837c74a2016-01-22 17:25:21 -0800495{
Keshav Santhanam998fd7d2016-07-12 13:33:00 -0700496 return container_new_with_cgroup_parent(name, rundir, NULL);
497}
498
499struct container *container_new_with_cgroup_parent(const char *name,
500 const char *rundir,
501 const char *cgroup_parent)
502{
Dylan Reid837c74a2016-01-22 17:25:21 -0800503 struct container *c;
504
Dylan Reid837c74a2016-01-22 17:25:21 -0800505 c = calloc(1, sizeof(*c));
Dylan Reidb435c682016-04-12 04:17:49 -0700506 if (!c)
507 return NULL;
Keshav Santhanam998fd7d2016-07-12 13:33:00 -0700508 c->cgroup = container_cgroup_new(name, "/sys/fs/cgroup", cgroup_parent);
Dylan Reid837c74a2016-01-22 17:25:21 -0800509 c->rundir = strdup(rundir);
Luis Hector Chavez8e7b6d52016-06-02 20:40:43 -0700510 c->name = strdup(name);
511 if (!c->cgroup || !c->rundir || !c->name) {
Dylan Reid684975e2016-05-02 15:44:47 -0700512 container_destroy(c);
Dylan Reid837c74a2016-01-22 17:25:21 -0800513 return NULL;
Dylan Reidb435c682016-04-12 04:17:49 -0700514 }
Dylan Reid837c74a2016-01-22 17:25:21 -0800515 return c;
516}
517
518void container_destroy(struct container *c)
519{
Dylan Reid684975e2016-05-02 15:44:47 -0700520 if (c->cgroup)
521 container_cgroup_destroy(c->cgroup);
Luis Hector Chavez8e7b6d52016-06-02 20:40:43 -0700522 if (c->jail)
523 minijail_destroy(c->jail);
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700524 FREE_AND_NULL(c->name);
525 FREE_AND_NULL(c->rundir);
526 FREE_AND_NULL(c);
Dylan Reid837c74a2016-01-22 17:25:21 -0800527}
528
529static int make_dir(const char *path, int uid, int gid, int mode)
530{
531 if (mkdir(path, mode))
532 return -errno;
533 if (chmod(path, mode))
534 return -errno;
535 if (chown(path, uid, gid))
536 return -errno;
537 return 0;
538}
539
540static int touch_file(const char *path, int uid, int gid, int mode)
541{
542 int rc;
543 int fd = open(path, O_RDWR | O_CREAT, mode);
544 if (fd < 0)
545 return -errno;
546 rc = fchown(fd, uid, gid);
547 close(fd);
548
549 if (rc)
550 return -errno;
551 return 0;
552}
553
554/* Make sure the mount target exists in the new rootfs. Create if needed and
555 * possible.
556 */
557static int setup_mount_destination(const struct container_mount *mnt,
Dylan Reid2149be92016-04-28 18:38:57 -0700558 const char *source,
Dylan Reid837c74a2016-01-22 17:25:21 -0800559 const char *dest)
560{
561 int rc;
562 struct stat st_buf;
563
564 rc = stat(dest, &st_buf);
565 if (rc == 0) /* destination exists */
566 return 0;
567
568 /* Try to create the destination. Either make directory or touch a file
569 * depending on the source type.
570 */
Dylan Reid2149be92016-04-28 18:38:57 -0700571 rc = stat(source, &st_buf);
Dylan Reid837c74a2016-01-22 17:25:21 -0800572 if (rc || S_ISDIR(st_buf.st_mode) || S_ISBLK(st_buf.st_mode))
573 return make_dir(dest, mnt->uid, mnt->gid, mnt->mode);
574
575 return touch_file(dest, mnt->uid, mnt->gid, mnt->mode);
576}
577
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700578/* Fork and exec the setfiles command to configure the selinux policy. */
Dylan Reide040c6b2016-05-02 18:49:02 -0700579static int run_setfiles_command(const struct container *c,
580 const struct container_config *config,
581 const char *dest)
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700582{
583 int rc;
584 int status;
585 int pid;
586 char *context_path;
587
Dylan Reide040c6b2016-05-02 18:49:02 -0700588 if (!config->run_setfiles)
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700589 return 0;
590
Dylan Reidb3621832016-03-24 10:24:57 -0700591 /* Really gross hack to avoid setfiles on /data, this should be removed
592 * when data isn't under /home/chronos/user where we can't access it as
593 * the android user.
594 * TODO(b/28705740) - Fix permission to the data directory.
595 */
596 if (strlen(dest) >= 5 && !strcmp(&dest[strlen(dest) - 5], "/data"))
597 return 0;
598
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700599 if (asprintf(&context_path, "%s/file_contexts",
600 c->runfsroot) < 0)
601 return -errno;
602
603 pid = fork();
604 if (pid == 0) {
605 const char *argv[] = {
Dylan Reide040c6b2016-05-02 18:49:02 -0700606 config->run_setfiles,
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700607 "-r",
608 c->runfsroot,
609 context_path,
610 dest,
611 NULL,
612 };
613 const char *env[] = {
614 NULL,
615 };
616
617 execve(argv[0], (char *const*)argv, (char *const*)env);
618
619 /* Command failed to exec if execve returns. */
620 _exit(-errno);
621 }
622 free(context_path);
623 if (pid < 0)
624 return -errno;
625 do {
626 rc = waitpid(pid, &status, 0);
627 } while (rc == -1 && errno == EINTR);
628 if (rc < 0)
629 return -errno;
630 return status;
631}
632
Dylan Reide040c6b2016-05-02 18:49:02 -0700633/*
634 * Unmounts anything we mounted in this mount namespace in the opposite order
635 * that they were mounted.
636 */
637static int unmount_external_mounts(struct container *c)
638{
639 int ret = 0;
640
641 while (c->num_ext_mounts) {
642 c->num_ext_mounts--;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700643 if (!c->ext_mounts[c->num_ext_mounts])
644 continue;
Dylan Reide040c6b2016-05-02 18:49:02 -0700645 if (umount(c->ext_mounts[c->num_ext_mounts]))
646 ret = -errno;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700647 FREE_AND_NULL(c->ext_mounts[c->num_ext_mounts]);
Dylan Reide040c6b2016-05-02 18:49:02 -0700648 }
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700649 FREE_AND_NULL(c->ext_mounts);
Dylan Reide040c6b2016-05-02 18:49:02 -0700650 return ret;
651}
652
Junichi Uekawa5d272772016-07-21 16:07:19 +0900653/*
654 * Match mount_one in minijail, mount one mountpoint with
655 * consideration for combination of MS_BIND/MS_RDONLY flag.
656 */
657static int mount_external(const char *src, const char *dest, const char *type,
658 unsigned long flags, const void *data)
659{
660 int remount_ro = 0;
661
662 /*
663 * R/O bind mounts have to be remounted since 'bind' and 'ro'
664 * can't both be specified in the original bind mount.
665 * Remount R/O after the initial mount.
666 */
667 if ((flags & MS_BIND) && (flags & MS_RDONLY)) {
668 remount_ro = 1;
669 flags &= ~MS_RDONLY;
670 }
671
672 if (mount(src, dest, type, flags, data) == -1)
673 return -1;
674
675 if (remount_ro) {
676 flags |= MS_RDONLY;
677 if (mount(src, dest, NULL, flags | MS_REMOUNT, data) == -1)
678 return -1;
679 }
680
681 return 0;
682}
683
Luis Hector Chavez3341ed62016-06-06 08:04:04 -0700684static int do_container_mount(struct container *c,
685 const struct container_mount *mnt)
686{
687 char *source = NULL;
688 char *dest = NULL;
689 int rc = 0;
690
691 if (asprintf(&dest, "%s%s", c->runfsroot, mnt->destination) < 0)
692 return -errno;
693
694 /*
695 * If it's a bind mount relative to rootfs, append source to
696 * rootfs path, otherwise source path is absolute.
697 */
698 if ((mnt->flags & MS_BIND) && mnt->source[0] != '/') {
699 if (asprintf(&source, "%s/%s", c->runfsroot, mnt->source) < 0)
700 goto error_free_return;
701 } else {
702 if (asprintf(&source, "%s", mnt->source) < 0)
703 goto error_free_return;
704 }
705
706 if (mnt->create) {
707 rc = setup_mount_destination(mnt, source, dest);
708 if (rc)
709 goto error_free_return;
710 }
711 if (mnt->mount_in_ns) {
712 /* We can mount this with minijail. */
Dylan Reid36b9c012016-06-24 18:27:08 -0700713 rc = minijail_mount_with_data(c->jail, source, mnt->destination,
714 mnt->type, mnt->flags, mnt->data);
Luis Hector Chavez3341ed62016-06-06 08:04:04 -0700715 if (rc)
716 goto error_free_return;
717 } else {
718 /* Mount this externally and unmount it on exit. */
Junichi Uekawa5d272772016-07-21 16:07:19 +0900719 if (mount_external(source, dest, mnt->type, mnt->flags,
720 mnt->data))
Luis Hector Chavez3341ed62016-06-06 08:04:04 -0700721 goto error_free_return;
722 /* Save this to unmount when shutting down. */
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700723 rc = strdup_and_free(&c->ext_mounts[c->num_ext_mounts], dest);
724 if (rc)
Luis Hector Chavez3341ed62016-06-06 08:04:04 -0700725 goto error_free_return;
726 c->num_ext_mounts++;
727 }
728
729 goto exit;
730
731error_free_return:
732 if (!rc)
733 rc = -errno;
734exit:
735 free(source);
736 free(dest);
737 return rc;
738}
739
Dylan Reide040c6b2016-05-02 18:49:02 -0700740static int do_container_mounts(struct container *c,
741 const struct container_config *config)
Dylan Reid7daf9982016-04-28 16:55:42 -0700742{
743 unsigned int i;
Luis Hector Chavez8e7b6d52016-06-02 20:40:43 -0700744 int rc = 0;
Dylan Reid7daf9982016-04-28 16:55:42 -0700745
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700746 unmount_external_mounts(c);
Dylan Reide040c6b2016-05-02 18:49:02 -0700747 /*
748 * Allocate space to track anything we mount in our mount namespace.
749 * This over-allocates as it has space for all mounts.
750 */
751 c->ext_mounts = calloc(config->num_mounts, sizeof(*c->ext_mounts));
752 if (!c->ext_mounts)
753 return -errno;
754
755 for (i = 0; i < config->num_mounts; ++i) {
Luis Hector Chavez3341ed62016-06-06 08:04:04 -0700756 rc = do_container_mount(c, &config->mounts[i]);
757 if (rc)
758 goto error_free_return;
Dylan Reid7daf9982016-04-28 16:55:42 -0700759 }
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700760
Dylan Reid7daf9982016-04-28 16:55:42 -0700761 return 0;
Dylan Reid2149be92016-04-28 18:38:57 -0700762
763error_free_return:
Dylan Reide040c6b2016-05-02 18:49:02 -0700764 unmount_external_mounts(c);
Luis Hector Chavez8e7b6d52016-06-02 20:40:43 -0700765 return rc;
Dylan Reid7daf9982016-04-28 16:55:42 -0700766}
767
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700768static int container_create_device(const struct container *c,
769 const struct container_device *dev,
770 int minor)
771{
772 char *path = NULL;
773 int rc = 0;
774 int mode;
775
776 switch (dev->type) {
777 case 'b':
778 mode = S_IFBLK;
779 break;
780 case 'c':
781 mode = S_IFCHR;
782 break;
783 default:
784 return -EINVAL;
785 }
786 mode |= dev->fs_permissions;
787
788 if (asprintf(&path, "%s%s", c->runfsroot, dev->path) < 0)
789 goto error_free_return;
790 if (mknod(path, mode, makedev(dev->major, minor)) && errno != EEXIST)
791 goto error_free_return;
792 if (chown(path, dev->uid, dev->gid))
793 goto error_free_return;
794 if (chmod(path, dev->fs_permissions))
795 goto error_free_return;
796
797 goto exit;
798
799error_free_return:
800 rc = -errno;
801exit:
802 free(path);
803 return rc;
804}
805
Keshav Santhanam0e4c3282016-07-14 10:25:16 -0700806static int mount_runfs(struct container *c, const struct container_config *config)
Dylan Reid837c74a2016-01-22 17:25:21 -0800807{
Dylan Reidb3621832016-03-24 10:24:57 -0700808 static const mode_t root_dir_mode = 0660;
Dylan Reide040c6b2016-05-02 18:49:02 -0700809 const char *rootfs = config->rootfs;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700810 char *runfs_template = NULL;
Dylan Reid837c74a2016-01-22 17:25:21 -0800811
Keshav Santhanam0e4c3282016-07-14 10:25:16 -0700812 if (asprintf(&runfs_template, "%s/%s_XXXXXX", c->rundir, c->name) < 0)
813 return -ENOMEM;
814
815 c->runfs = mkdtemp(runfs_template);
816 if (!c->runfs) {
817 free(runfs_template);
818 return -errno;
819 }
820
821 /* Make sure the container uid can access the rootfs. */
822 if (chmod(c->runfs, 0700))
823 return -errno;
824 if (chown(c->runfs, config->uid, config->gid))
825 return -errno;
826
827 if (asprintf(&c->runfsroot, "%s/root", c->runfs) < 0)
828 return -errno;
829
830 if (mkdir(c->runfsroot, root_dir_mode))
831 return -errno;
832 if (chmod(c->runfsroot, root_dir_mode))
833 return -errno;
834
835 if (mount(rootfs, c->runfsroot, "", MS_BIND | MS_RDONLY, NULL))
836 return -errno;
837
838 return 0;
839}
840
Keshav Santhanam36485ff2016-08-02 16:21:02 -0700841static int get_userns_id(const char *map, int id)
842{
843 char *map_copy, *mapping, *saveptr1, *saveptr2;
844 int inside, outside, length;
845 int result = 0;
846 errno = 0;
847
848 if (asprintf(&map_copy, "%s", map) < 0)
849 return -ENOMEM;
850
851 mapping = strtok_r(map_copy, ",", &saveptr1);
852 while (mapping) {
853 inside = strtol(strtok_r(mapping, " ", &saveptr2), NULL, 10);
854 outside = strtol(strtok_r(NULL, " ", &saveptr2), NULL, 10);
855 length = strtol(strtok_r(NULL, "\0", &saveptr2), NULL, 10);
856 if (errno) {
857 goto error_free_return;
858 } else if (inside < 0 || outside < 0 || length < 0) {
859 errno = EINVAL;
860 goto error_free_return;
861 }
862
863 if (id >= outside && id <= (outside + length)) {
864 result = id - (outside - inside);
865 goto exit;
866 }
867
868 mapping = strtok_r(NULL, ",", &saveptr1);
869 }
870 errno = EINVAL;
871
872error_free_return:
873 result = -errno;
874exit:
875 free(map_copy);
876 return result;
877}
878
Keshav Santhanam0e4c3282016-07-14 10:25:16 -0700879int container_start(struct container *c, const struct container_config *config)
880{
881 int rc = 0;
882 unsigned int i;
Keshav Santhanam36485ff2016-08-02 16:21:02 -0700883 int uid_userns, gid_userns;
Keshav Santhanam0e4c3282016-07-14 10:25:16 -0700884
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700885 if (!c)
886 return -EINVAL;
Dylan Reide040c6b2016-05-02 18:49:02 -0700887 if (!config)
888 return -EINVAL;
889 if (!config->program_argv || !config->program_argv[0])
890 return -EINVAL;
891
Keshav Santhanam0e4c3282016-07-14 10:25:16 -0700892 if (config->premounted_runfs) {
893 c->runfs = NULL;
894 c->runfsroot = strdup(config->premounted_runfs);
895 if (!c->runfsroot) {
896 rc = -ENOMEM;
897 goto error_rmdir;
898 }
899 } else {
900 rc = mount_runfs(c, config);
901 if (rc)
902 goto error_rmdir;
Dylan Reid837c74a2016-01-22 17:25:21 -0800903 }
Dylan Reid837c74a2016-01-22 17:25:21 -0800904
905 c->jail = minijail_new();
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700906 if (!c->jail)
Luis Hector Chavez945af482016-06-03 08:39:34 -0700907 goto error_rmdir;
Dylan Reid837c74a2016-01-22 17:25:21 -0800908
Luis Hector Chavez8e7b6d52016-06-02 20:40:43 -0700909 rc = do_container_mounts(c, config);
910 if (rc)
Dylan Reid7daf9982016-04-28 16:55:42 -0700911 goto error_rmdir;
Dylan Reid837c74a2016-01-22 17:25:21 -0800912
Keshav Santhanam268fa032016-07-14 09:59:24 -0700913 /* Must be root to modify device cgroup or mknod */
914 if (getuid() == 0) {
915 c->cgroup->ops->deny_all_devices(c->cgroup);
Dylan Reid837c74a2016-01-22 17:25:21 -0800916
Keshav Santhanam268fa032016-07-14 09:59:24 -0700917 for (i = 0; i < config->num_devices; i++) {
918 const struct container_device *dev = &config->devices[i];
919 int minor = dev->minor;
Dylan Reid837c74a2016-01-22 17:25:21 -0800920
Keshav Santhanam268fa032016-07-14 09:59:24 -0700921 if (dev->copy_minor) {
922 struct stat st_buff;
923 if (stat(dev->path, &st_buff) < 0)
924 continue;
925 /* Use the minor macro to extract the device number. */
926 minor = minor(st_buff.st_rdev);
927 }
928 if (minor >= 0) {
929 rc = container_create_device(c, dev, minor);
930 if (rc)
931 goto error_rmdir;
932 }
933
934 rc = c->cgroup->ops->add_device(c->cgroup, dev->major,
935 minor, dev->read_allowed,
936 dev->write_allowed,
937 dev->modify_allowed, dev->type);
Luis Hector Chavez479b95f2016-06-06 08:01:05 -0700938 if (rc)
Dylan Reid355d5e42016-04-29 16:53:31 -0700939 goto error_rmdir;
Dylan Reid837c74a2016-01-22 17:25:21 -0800940 }
Dylan Reid837c74a2016-01-22 17:25:21 -0800941 }
942
Dylan Reidd7229582016-04-27 17:08:40 -0700943 /* Potentailly run setfiles on mounts configured outside of the jail */
Dylan Reide040c6b2016-05-02 18:49:02 -0700944 for (i = 0; i < config->num_mounts; i++) {
945 const struct container_mount *mnt = &config->mounts[i];
Dylan Reidd7229582016-04-27 17:08:40 -0700946 char *dest;
947
948 if (mnt->mount_in_ns)
949 continue;
Junichi Uekawa5d272772016-07-21 16:07:19 +0900950 if (mnt->flags & MS_RDONLY)
951 continue;
Dylan Reidd7229582016-04-27 17:08:40 -0700952 if (asprintf(&dest, "%s%s", c->runfsroot, mnt->destination) < 0)
953 goto error_rmdir;
Dylan Reide040c6b2016-05-02 18:49:02 -0700954 rc = run_setfiles_command(c, config, dest);
Dylan Reidd7229582016-04-27 17:08:40 -0700955 free(dest);
956 if (rc)
957 goto error_rmdir;
958 }
959
Chinyue Chenfac909e2016-06-24 14:17:42 +0800960 /* Setup CPU cgroup params. */
961 if (config->cpu_cgparams.shares) {
962 rc = c->cgroup->ops->set_cpu_shares(
963 c->cgroup, config->cpu_cgparams.shares);
964 if (rc)
965 goto error_rmdir;
966 }
967 if (config->cpu_cgparams.period) {
968 rc = c->cgroup->ops->set_cpu_quota(
969 c->cgroup, config->cpu_cgparams.quota);
970 if (rc)
971 goto error_rmdir;
972 rc = c->cgroup->ops->set_cpu_period(
973 c->cgroup, config->cpu_cgparams.period);
974 if (rc)
975 goto error_rmdir;
976 }
977 if (config->cpu_cgparams.rt_period) {
978 rc = c->cgroup->ops->set_cpu_rt_runtime(
979 c->cgroup, config->cpu_cgparams.rt_runtime);
980 if (rc)
981 goto error_rmdir;
982 rc = c->cgroup->ops->set_cpu_rt_period(
983 c->cgroup, config->cpu_cgparams.rt_period);
984 if (rc)
985 goto error_rmdir;
986 }
987
Dylan Reid837c74a2016-01-22 17:25:21 -0800988 /* Setup and start the container with libminijail. */
Keshav Santhanam0e4c3282016-07-14 10:25:16 -0700989 if (config->pid_file_path) {
990 c->pid_file_path = strdup(config->pid_file_path);
991 if (!c->pid_file_path) {
992 rc = -ENOMEM;
993 goto error_rmdir;
994 }
995 } else if (c->runfs) {
996 if (asprintf(&c->pid_file_path, "%s/container.pid", c->runfs) < 0) {
997 rc = -ENOMEM;
998 goto error_rmdir;
999 }
1000 }
1001
1002 if (c->pid_file_path)
1003 minijail_write_pid_file(c->jail, c->pid_file_path);
Dylan Reid837c74a2016-01-22 17:25:21 -08001004 minijail_reset_signal_mask(c->jail);
1005
1006 /* Setup container namespaces. */
1007 minijail_namespace_ipc(c->jail);
1008 minijail_namespace_vfs(c->jail);
1009 minijail_namespace_net(c->jail);
1010 minijail_namespace_pids(c->jail);
Dylan Reid837c74a2016-01-22 17:25:21 -08001011 minijail_namespace_user(c->jail);
Dylan Reide040c6b2016-05-02 18:49:02 -07001012 rc = minijail_uidmap(c->jail, config->uid_map);
Dylan Reid837c74a2016-01-22 17:25:21 -08001013 if (rc)
1014 goto error_rmdir;
Dylan Reide040c6b2016-05-02 18:49:02 -07001015 rc = minijail_gidmap(c->jail, config->gid_map);
Dylan Reid837c74a2016-01-22 17:25:21 -08001016 if (rc)
1017 goto error_rmdir;
Dylan Reid837c74a2016-01-22 17:25:21 -08001018
Keshav Santhanam36485ff2016-08-02 16:21:02 -07001019 /* Set the UID/GID inside the container if not 0. */
1020 uid_userns = get_userns_id(config->uid_map, config->uid);
1021 if (uid_userns < 0)
1022 goto error_rmdir;
1023 else if (uid_userns > 0)
1024 minijail_change_uid(c->jail, (uid_t) uid_userns);
1025 gid_userns = get_userns_id(config->gid_map, config->gid);
1026 if (gid_userns < 0)
1027 goto error_rmdir;
1028 else if (gid_userns > 0)
1029 minijail_change_gid(c->jail, (gid_t) gid_userns);
1030
Dylan Reid837c74a2016-01-22 17:25:21 -08001031 rc = minijail_enter_pivot_root(c->jail, c->runfsroot);
1032 if (rc)
1033 goto error_rmdir;
1034
1035 /* Add the cgroups configured above. */
1036 rc = minijail_add_to_cgroup(c->jail, cgroup_cpu_tasks_path(c->cgroup));
1037 if (rc)
1038 goto error_rmdir;
1039 rc = minijail_add_to_cgroup(c->jail,
1040 cgroup_cpuacct_tasks_path(c->cgroup));
1041 if (rc)
1042 goto error_rmdir;
1043 rc = minijail_add_to_cgroup(c->jail,
1044 cgroup_devices_tasks_path(c->cgroup));
1045 if (rc)
1046 goto error_rmdir;
1047 rc = minijail_add_to_cgroup(c->jail,
1048 cgroup_freezer_tasks_path(c->cgroup));
1049 if (rc)
1050 goto error_rmdir;
1051
Dylan Reide040c6b2016-05-02 18:49:02 -07001052 if (config->alt_syscall_table)
1053 minijail_use_alt_syscall(c->jail, config->alt_syscall_table);
Dylan Reid837c74a2016-01-22 17:25:21 -08001054
1055 minijail_run_as_init(c->jail);
1056
Dylan Reid3da683b2016-04-05 03:35:35 -07001057 /* TODO(dgreid) - remove this once shared mounts are cleaned up. */
1058 minijail_skip_remount_private(c->jail);
1059
Dylan Reid837c74a2016-01-22 17:25:21 -08001060 rc = minijail_run_pid_pipes_no_preload(c->jail,
Dylan Reide040c6b2016-05-02 18:49:02 -07001061 config->program_argv[0],
1062 config->program_argv,
Dylan Reid837c74a2016-01-22 17:25:21 -08001063 &c->init_pid, NULL, NULL,
1064 NULL);
1065 if (rc)
1066 goto error_rmdir;
1067 return 0;
1068
1069error_rmdir:
Luis Hector Chavez945af482016-06-03 08:39:34 -07001070 if (!rc)
1071 rc = -errno;
1072 container_teardown(c);
Dylan Reid837c74a2016-01-22 17:25:21 -08001073 return rc;
1074}
1075
1076const char *container_root(struct container *c)
1077{
1078 return c->runfs;
1079}
1080
1081int container_pid(struct container *c)
1082{
1083 return c->init_pid;
1084}
1085
1086static int container_teardown(struct container *c)
1087{
Dylan Reid837c74a2016-01-22 17:25:21 -08001088 int ret = 0;
1089
Dylan Reide040c6b2016-05-02 18:49:02 -07001090 unmount_external_mounts(c);
Keshav Santhanam0e4c3282016-07-14 10:25:16 -07001091 if (c->runfsroot && c->runfs) {
Luis Hector Chavez945af482016-06-03 08:39:34 -07001092 if (umount(c->runfsroot))
1093 ret = -errno;
1094 if (rmdir(c->runfsroot))
1095 ret = -errno;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001096 FREE_AND_NULL(c->runfsroot);
Luis Hector Chavez945af482016-06-03 08:39:34 -07001097 }
1098 if (c->pid_file_path) {
1099 if (unlink(c->pid_file_path))
1100 ret = -errno;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001101 FREE_AND_NULL(c->pid_file_path);
Luis Hector Chavez945af482016-06-03 08:39:34 -07001102 }
1103 if (c->runfs) {
1104 if (rmdir(c->runfs))
1105 ret = -errno;
Luis Hector Chavez479b95f2016-06-06 08:01:05 -07001106 FREE_AND_NULL(c->runfs);
Luis Hector Chavez945af482016-06-03 08:39:34 -07001107 }
Dylan Reid837c74a2016-01-22 17:25:21 -08001108 return ret;
1109}
1110
1111int container_wait(struct container *c)
1112{
Dylan Reidcf745c52016-04-22 10:18:03 -07001113 int rc;
1114
1115 do {
1116 rc = minijail_wait(c->jail);
Luis Hector Chavez4641e852016-06-02 15:40:19 -07001117 } while (rc == -EINTR);
Dylan Reidcf745c52016-04-22 10:18:03 -07001118
Luis Hector Chavez945af482016-06-03 08:39:34 -07001119 // If the process had already been reaped, still perform teardown.
1120 if (rc == -ECHILD || rc >= 0) {
Dylan Reidcf745c52016-04-22 10:18:03 -07001121 rc = container_teardown(c);
Luis Hector Chavez945af482016-06-03 08:39:34 -07001122 }
Dylan Reidcf745c52016-04-22 10:18:03 -07001123 return rc;
Dylan Reid837c74a2016-01-22 17:25:21 -08001124}
1125
1126int container_kill(struct container *c)
1127{
Luis Hector Chavez945af482016-06-03 08:39:34 -07001128 if (kill(c->init_pid, SIGKILL) && errno != ESRCH)
Dylan Reid837c74a2016-01-22 17:25:21 -08001129 return -errno;
1130 return container_wait(c);
1131}