blob: d3fc0048103591111f0a0ddcbd8210dc03f321bc [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
25struct container_mount {
26 char *name;
27 char *source;
28 char *destination;
29 char *type;
30 char *data;
31 int flags;
32 int uid;
33 int gid;
34 int mode;
35 int mount_in_ns; /* True if mount should happen in new vfs ns */
36 int create; /* True if target should be created if it doesn't exist */
37};
38
39struct container_device {
40 char type; /* 'c' or 'b' for char or block */
41 char *path;
42 int fs_permissions;
43 int major;
44 int minor;
Dylan Reid355d5e42016-04-29 16:53:31 -070045 int copy_minor; /* Copy the minor from existing node, ignores |minor| */
Dylan Reid837c74a2016-01-22 17:25:21 -080046 int uid;
47 int gid;
48 int read_allowed;
49 int write_allowed;
50 int modify_allowed;
51};
52
53/*
54 * Structure that configures how the container is run.
55 *
56 * rootfs - Path to the root of the container's filesystem.
57 * program_argv - The program to run and args, e.g. "/sbin/init".
58 * num_args - Number of args in program_argv.
59 * uid_map - Mapping of UIDs in the container, e.g. "0 100000 1024"
60 * gid_map - Mapping of GIDs in the container, e.g. "0 100000 1024"
61 * alt_syscall_table - Syscall table to use or NULL if none.
62 * mounts - Filesystems to mount in the new namespace.
63 * num_mounts - Number of above.
64 * devices - Device nodes to create.
65 * num_devices - Number of above.
Dylan Reid2bd9ea92016-04-07 20:57:47 -070066 * run_setfiles - Should run setfiles on mounts to enable selinux.
Dylan Reid837c74a2016-01-22 17:25:21 -080067 */
68struct container_config {
69 char *rootfs;
70 char **program_argv;
71 size_t num_args;
72 char *uid_map;
73 char *gid_map;
74 char *alt_syscall_table;
75 struct container_mount *mounts;
76 size_t num_mounts;
77 struct container_device *devices;
78 size_t num_devices;
Dylan Reid2bd9ea92016-04-07 20:57:47 -070079 const char *run_setfiles;
Dylan Reid837c74a2016-01-22 17:25:21 -080080};
81
82struct container_config *container_config_create()
83{
84 return calloc(1, sizeof(struct container_config));
85}
86
87void container_config_destroy(struct container_config *c)
88{
89 size_t i;
90
91 if (c == NULL)
92 return;
93 free(c->rootfs);
94 for (i = 0; i < c->num_args; ++i)
95 free(c->program_argv[i]);
96 free(c->program_argv);
97 free(c->uid_map);
98 free(c->gid_map);
99 free(c->alt_syscall_table);
100 for (i = 0; i < c->num_mounts; ++i) {
101 free(c->mounts[i].name);
102 free(c->mounts[i].source);
103 free(c->mounts[i].destination);
104 free(c->mounts[i].type);
105 free(c->mounts[i].data);
106 }
107 free(c->mounts);
108 for (i = 0; i < c->num_devices; ++i) {
109 free(c->devices[i].path);
110 }
111 free(c->devices);
112 free(c);
113}
114
115int container_config_rootfs(struct container_config *c, const char *rootfs)
116{
117 c->rootfs = strdup(rootfs);
118 if (!c->rootfs)
119 return -ENOMEM;
120 return 0;
121}
122
123int container_config_program_argv(struct container_config *c,
124 char **argv, size_t num_args)
125{
126 size_t i;
127
128 c->num_args = num_args;
129 c->program_argv = calloc(num_args + 1, sizeof(char *));
130 if (!c->program_argv)
131 return -ENOMEM;
132 for (i = 0; i < num_args; ++i) {
133 c->program_argv[i] = strdup(argv[i]);
134 if (!c->program_argv[i])
135 return -ENOMEM;
136 }
137 c->program_argv[num_args] = NULL;
138 return 0;
139}
140
141int container_config_uid_map(struct container_config *c, const char *uid_map)
142{
143 c->uid_map = strdup(uid_map);
144 if (!c->uid_map)
145 return -ENOMEM;
146 return 0;
147}
148
149int container_config_gid_map(struct container_config *c, const char *gid_map)
150{
151 c->gid_map = strdup(gid_map);
152 if (!c->gid_map)
153 return -ENOMEM;
154 return 0;
155}
156
157int container_config_alt_syscall_table(struct container_config *c,
158 const char *alt_syscall_table)
159{
160 c->alt_syscall_table = strdup(alt_syscall_table);
161 if (!c->alt_syscall_table)
162 return -ENOMEM;
163 return 0;
164}
165
166int container_config_add_mount(struct container_config *c,
167 const char *name,
168 const char *source,
169 const char *destination,
170 const char *type,
171 const char *data,
172 int flags,
173 int uid,
174 int gid,
175 int mode,
176 int mount_in_ns,
177 int create)
178{
179 struct container_mount *mount_ptr;
180
181 if (name == NULL || source == NULL ||
182 destination == NULL || type == NULL)
183 return -EINVAL;
184
185 mount_ptr = realloc(c->mounts,
186 sizeof(c->mounts[0]) * (c->num_mounts + 1));
187 if (!mount_ptr)
188 return -ENOMEM;
189 c->mounts = mount_ptr;
190 c->mounts[c->num_mounts].name = strdup(name);
191 if (!c->mounts[c->num_mounts].name)
192 return -ENOMEM;
193 c->mounts[c->num_mounts].source = strdup(source);
194 if (!c->mounts[c->num_mounts].source)
195 return -ENOMEM;
196 c->mounts[c->num_mounts].destination = strdup(destination);
197 if (!c->mounts[c->num_mounts].destination)
198 return -ENOMEM;
199 c->mounts[c->num_mounts].type = strdup(type);
200 if (!c->mounts[c->num_mounts].type)
201 return -ENOMEM;
202 if (data) {
203 c->mounts[c->num_mounts].data = strdup(data);
204 if (!c->mounts[c->num_mounts].data)
205 return -ENOMEM;
206 } else {
207 c->mounts[c->num_mounts].data = NULL;
208 }
209 c->mounts[c->num_mounts].flags = flags;
210 c->mounts[c->num_mounts].uid = uid;
211 c->mounts[c->num_mounts].gid = gid;
212 c->mounts[c->num_mounts].mode = mode;
213 c->mounts[c->num_mounts].mount_in_ns = mount_in_ns;
214 c->mounts[c->num_mounts].create = create;
215 ++c->num_mounts;
216 return 0;
217}
218
219int container_config_add_device(struct container_config *c,
220 char type,
221 const char *path,
222 int fs_permissions,
223 int major,
224 int minor,
Dylan Reid355d5e42016-04-29 16:53:31 -0700225 int copy_minor,
Dylan Reid837c74a2016-01-22 17:25:21 -0800226 int uid,
227 int gid,
228 int read_allowed,
229 int write_allowed,
230 int modify_allowed)
231{
232 struct container_device *dev_ptr;
233
234 if (path == NULL)
235 return -EINVAL;
Dylan Reid355d5e42016-04-29 16:53:31 -0700236 /* If using a dynamic minor number, ensure that minor is -1. */
237 if (copy_minor && (minor != -1))
238 return -EINVAL;
239
Dylan Reid837c74a2016-01-22 17:25:21 -0800240 dev_ptr = realloc(c->devices,
241 sizeof(c->devices[0]) * (c->num_devices + 1));
242 if (!dev_ptr)
243 return -ENOMEM;
244 c->devices = dev_ptr;
245 c->devices[c->num_devices].type = type;
246 c->devices[c->num_devices].path = strdup(path);
247 if (!c->devices[c->num_devices].path)
248 return -ENOMEM;
249 c->devices[c->num_devices].fs_permissions = fs_permissions;
250 c->devices[c->num_devices].major = major;
251 c->devices[c->num_devices].minor = minor;
Dylan Reid355d5e42016-04-29 16:53:31 -0700252 c->devices[c->num_devices].copy_minor = copy_minor;
Dylan Reid837c74a2016-01-22 17:25:21 -0800253 c->devices[c->num_devices].uid = uid;
254 c->devices[c->num_devices].gid = gid;
255 c->devices[c->num_devices].read_allowed = read_allowed;
256 c->devices[c->num_devices].write_allowed = write_allowed;
257 c->devices[c->num_devices].modify_allowed = modify_allowed;
258 ++c->num_devices;
259 return 0;
260}
261
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700262void container_config_run_setfiles(struct container_config *c,
263 const char *setfiles_cmd)
264{
265 c->run_setfiles = setfiles_cmd;
266}
Dylan Reid837c74a2016-01-22 17:25:21 -0800267
268/*
269 * Container manipulation
270 */
271struct container {
Dylan Reid51fe3872016-04-28 07:32:02 -0700272 const struct container_config *config;
Dylan Reid837c74a2016-01-22 17:25:21 -0800273 struct container_cgroup *cgroup;
274 struct minijail *jail;
275 pid_t init_pid;
276 char *runfs;
277 char *rundir;
278 char *runfsroot;
279 char *pid_file_path;
280 const char *name;
281};
282
283struct container *container_new(const char *name,
284 const char *rundir,
Dylan Reid51fe3872016-04-28 07:32:02 -0700285 const struct container_config *config)
Dylan Reid837c74a2016-01-22 17:25:21 -0800286{
287 struct container *c;
288
289 if (!config)
290 return NULL;
291 if (!config->program_argv || !config->program_argv[0])
292 return NULL;
293
294 c = calloc(1, sizeof(*c));
Dylan Reidb435c682016-04-12 04:17:49 -0700295 if (!c)
296 return NULL;
Dylan Reid837c74a2016-01-22 17:25:21 -0800297 c->name = name;
298 c->config = config;
299 c->cgroup = container_cgroup_new(name, "/sys/fs/cgroup");
300 c->rundir = strdup(rundir);
Dylan Reidb435c682016-04-12 04:17:49 -0700301 if (!c->rundir) {
302 free(c);
Dylan Reid837c74a2016-01-22 17:25:21 -0800303 return NULL;
Dylan Reidb435c682016-04-12 04:17:49 -0700304 }
Dylan Reid837c74a2016-01-22 17:25:21 -0800305 return c;
306}
307
308void container_destroy(struct container *c)
309{
Dylan Reid837c74a2016-01-22 17:25:21 -0800310 container_cgroup_destroy(c->cgroup);
311 free(c->rundir);
312 free(c);
313}
314
315static int make_dir(const char *path, int uid, int gid, int mode)
316{
317 if (mkdir(path, mode))
318 return -errno;
319 if (chmod(path, mode))
320 return -errno;
321 if (chown(path, uid, gid))
322 return -errno;
323 return 0;
324}
325
326static int touch_file(const char *path, int uid, int gid, int mode)
327{
328 int rc;
329 int fd = open(path, O_RDWR | O_CREAT, mode);
330 if (fd < 0)
331 return -errno;
332 rc = fchown(fd, uid, gid);
333 close(fd);
334
335 if (rc)
336 return -errno;
337 return 0;
338}
339
340/* Make sure the mount target exists in the new rootfs. Create if needed and
341 * possible.
342 */
343static int setup_mount_destination(const struct container_mount *mnt,
Dylan Reid2149be92016-04-28 18:38:57 -0700344 const char *source,
Dylan Reid837c74a2016-01-22 17:25:21 -0800345 const char *dest)
346{
347 int rc;
348 struct stat st_buf;
349
350 rc = stat(dest, &st_buf);
351 if (rc == 0) /* destination exists */
352 return 0;
353
354 /* Try to create the destination. Either make directory or touch a file
355 * depending on the source type.
356 */
Dylan Reid2149be92016-04-28 18:38:57 -0700357 rc = stat(source, &st_buf);
Dylan Reid837c74a2016-01-22 17:25:21 -0800358 if (rc || S_ISDIR(st_buf.st_mode) || S_ISBLK(st_buf.st_mode))
359 return make_dir(dest, mnt->uid, mnt->gid, mnt->mode);
360
361 return touch_file(dest, mnt->uid, mnt->gid, mnt->mode);
362}
363
Dylan Reid2bd9ea92016-04-07 20:57:47 -0700364/* Fork and exec the setfiles command to configure the selinux policy. */
365static int run_setfiles_command(const struct container *c, const char *dest)
366{
367 int rc;
368 int status;
369 int pid;
370 char *context_path;
371
372 if (!c->config->run_setfiles)
373 return 0;
374
375 if (asprintf(&context_path, "%s/file_contexts",
376 c->runfsroot) < 0)
377 return -errno;
378
379 pid = fork();
380 if (pid == 0) {
381 const char *argv[] = {
382 c->config->run_setfiles,
383 "-r",
384 c->runfsroot,
385 context_path,
386 dest,
387 NULL,
388 };
389 const char *env[] = {
390 NULL,
391 };
392
393 execve(argv[0], (char *const*)argv, (char *const*)env);
394
395 /* Command failed to exec if execve returns. */
396 _exit(-errno);
397 }
398 free(context_path);
399 if (pid < 0)
400 return -errno;
401 do {
402 rc = waitpid(pid, &status, 0);
403 } while (rc == -1 && errno == EINTR);
404 if (rc < 0)
405 return -errno;
406 return status;
407}
408
Dylan Reid7daf9982016-04-28 16:55:42 -0700409static int do_container_mounts(struct container *c)
410{
411 unsigned int i;
Dylan Reid2149be92016-04-28 18:38:57 -0700412 char *source;
413 char *dest;
Dylan Reid7daf9982016-04-28 16:55:42 -0700414
415 for (i = 0; i < c->config->num_mounts; ++i) {
416 const struct container_mount *mnt = &c->config->mounts[i];
Dylan Reid7daf9982016-04-28 16:55:42 -0700417
Dylan Reid2149be92016-04-28 18:38:57 -0700418 source = NULL;
419 dest = NULL;
Dylan Reid7daf9982016-04-28 16:55:42 -0700420 if (asprintf(&dest, "%s%s", c->runfsroot, mnt->destination) < 0)
421 return -errno;
422
Dylan Reid2149be92016-04-28 18:38:57 -0700423 /*
424 * If it's a bind mount relative to rootfs, append source to
425 * rootfs path, otherwise source path is absolute.
426 */
427 if ((mnt->flags & MS_BIND) && mnt->source[0] != '/') {
428 if (asprintf(&source, "%s/%s", c->runfsroot,
429 mnt->source) < 0)
430 goto error_free_return;
431 } else {
432 if (asprintf(&source, "%s", mnt->source) < 0)
433 goto error_free_return;
434 }
435
Dylan Reid7daf9982016-04-28 16:55:42 -0700436 if (mnt->create) {
Dylan Reid2149be92016-04-28 18:38:57 -0700437 if (setup_mount_destination(mnt, source, dest))
438 goto error_free_return;
Dylan Reid7daf9982016-04-28 16:55:42 -0700439 }
440 if (mnt->mount_in_ns) {
Dylan Reid2149be92016-04-28 18:38:57 -0700441 /* We can mount this with minijail. */
442 if (minijail_mount(c->jail, source, mnt->destination,
443 mnt->type, mnt->flags))
444 goto error_free_return;
Dylan Reid7daf9982016-04-28 16:55:42 -0700445 } else {
446 /*
447 * Mount this externally and unmount it on exit. Don't
448 * allow execution from external mounts.
449 */
Dylan Reid2149be92016-04-28 18:38:57 -0700450 if (mount(source, dest, mnt->type,
451 mnt->flags | MS_NOEXEC, mnt->data))
452 goto error_free_return;
Dylan Reid7daf9982016-04-28 16:55:42 -0700453
454 }
Dylan Reid2149be92016-04-28 18:38:57 -0700455 free(source);
Dylan Reid7daf9982016-04-28 16:55:42 -0700456 free(dest);
457 }
458 return 0;
Dylan Reid2149be92016-04-28 18:38:57 -0700459
460error_free_return:
461 free(dest);
462 free(source);
463 return -errno;
Dylan Reid7daf9982016-04-28 16:55:42 -0700464}
465
Dylan Reid837c74a2016-01-22 17:25:21 -0800466int container_start(struct container *c)
467{
468 int rc;
469 unsigned int i;
470 const char *rootfs = c->config->rootfs;
471 char *runfs_template;
472
473 if (asprintf(&runfs_template, "%s/%s_XXXXXX", c->rundir, c->name) < 0)
474 return -errno;
475
476 c->runfs = mkdtemp(runfs_template);
477 if (!c->runfs) {
478 free(runfs_template);
479 return -errno;
480 }
481 if (asprintf(&c->runfsroot, "%s/root", c->runfs) < 0) {
482 free(runfs_template);
483 return -errno;
484 }
485
486 rc = mkdir(c->runfsroot, 0660);
487 if (rc)
488 goto error_rmdir;
489
490 rc = mount(rootfs, c->runfsroot, "", MS_BIND | MS_RDONLY | MS_NOEXEC,
491 NULL);
492 if (rc)
493 goto error_rmdir;
494
495 c->jail = minijail_new();
496
Dylan Reid7daf9982016-04-28 16:55:42 -0700497 if (do_container_mounts(c))
498 goto error_rmdir;
Dylan Reid837c74a2016-01-22 17:25:21 -0800499
500 c->cgroup->ops->deny_all_devices(c->cgroup);
501
502 for (i = 0; i < c->config->num_devices; i++) {
503 const struct container_device *dev = &c->config->devices[i];
504 int mode;
Dylan Reid355d5e42016-04-29 16:53:31 -0700505 int minor = dev->minor;
Dylan Reid837c74a2016-01-22 17:25:21 -0800506
507 switch (dev->type) {
508 case 'b':
509 mode = S_IFBLK;
510 break;
511 case 'c':
512 mode = S_IFCHR;
513 break;
514 default:
515 goto error_rmdir;
516 }
517 mode |= dev->fs_permissions;
518
Dylan Reid355d5e42016-04-29 16:53:31 -0700519 if (dev->copy_minor) {
520 struct stat st_buff;
521 if (stat(dev->path, &st_buff) < 0)
522 goto error_rmdir;
523 /* Use the minor macro to extract the device number. */
524 minor = minor(st_buff.st_rdev);
525 }
526 if (minor >= 0) {
527 char *path;
528
529 if (asprintf(&path, "%s%s", c->runfsroot, dev->path) < 0)
530 goto error_rmdir;
531 rc = mknod(path, mode, makedev(dev->major, minor));
Dylan Reid837c74a2016-01-22 17:25:21 -0800532 if (rc && errno != EEXIST) {
533 free(path);
534 goto error_rmdir;
535 }
536 rc = chown(path, dev->uid, dev->gid);
537 if (rc) {
538 free(path);
539 goto error_rmdir;
540 }
541 rc = chmod(path, dev->fs_permissions);
542 free(path);
543 if (rc)
544 goto error_rmdir;
545 }
546
547 rc = c->cgroup->ops->add_device(c->cgroup, dev->major,
Dylan Reid355d5e42016-04-29 16:53:31 -0700548 minor, dev->read_allowed,
Dylan Reid837c74a2016-01-22 17:25:21 -0800549 dev->write_allowed,
550 dev->modify_allowed, dev->type);
551 if (rc)
552 goto error_rmdir;
553 }
554
Dylan Reidd7229582016-04-27 17:08:40 -0700555 /* Potentailly run setfiles on mounts configured outside of the jail */
556 for (i = 0; i < c->config->num_mounts; i++) {
557 const struct container_mount *mnt = &c->config->mounts[i];
558 char *dest;
559
560 if (mnt->mount_in_ns)
561 continue;
562 if (asprintf(&dest, "%s%s", c->runfsroot, mnt->destination) < 0)
563 goto error_rmdir;
564 rc = run_setfiles_command(c, dest);
565 free(dest);
566 if (rc)
567 goto error_rmdir;
568 }
569
Dylan Reid837c74a2016-01-22 17:25:21 -0800570 /* Setup and start the container with libminijail. */
571 if (asprintf(&c->pid_file_path, "%s/container.pid", c->runfs) < 0)
572 goto error_rmdir;
573 minijail_write_pid_file(c->jail, c->pid_file_path);
574 minijail_reset_signal_mask(c->jail);
575
576 /* Setup container namespaces. */
577 minijail_namespace_ipc(c->jail);
578 minijail_namespace_vfs(c->jail);
579 minijail_namespace_net(c->jail);
580 minijail_namespace_pids(c->jail);
581/* TODO(dgreid) - Enable user namespaces
582 minijail_namespace_user(c->jail);
583 rc = minijail_uidmap(c->jail, c->config->uid_map);
584 if (rc)
585 goto error_rmdir;
586 rc = minijail_gidmap(c->jail, c->config->gid_map);
587 if (rc)
588 goto error_rmdir;
589*/
590
591 rc = minijail_enter_pivot_root(c->jail, c->runfsroot);
592 if (rc)
593 goto error_rmdir;
594
595 /* Add the cgroups configured above. */
596 rc = minijail_add_to_cgroup(c->jail, cgroup_cpu_tasks_path(c->cgroup));
597 if (rc)
598 goto error_rmdir;
599 rc = minijail_add_to_cgroup(c->jail,
600 cgroup_cpuacct_tasks_path(c->cgroup));
601 if (rc)
602 goto error_rmdir;
603 rc = minijail_add_to_cgroup(c->jail,
604 cgroup_devices_tasks_path(c->cgroup));
605 if (rc)
606 goto error_rmdir;
607 rc = minijail_add_to_cgroup(c->jail,
608 cgroup_freezer_tasks_path(c->cgroup));
609 if (rc)
610 goto error_rmdir;
611
612 if (c->config->alt_syscall_table)
613 minijail_use_alt_syscall(c->jail, c->config->alt_syscall_table);
614
615 minijail_run_as_init(c->jail);
616
617 /* Last mount is to make '/' executable in the container. */
618 rc = minijail_mount(c->jail, rootfs, "/", "",
619 MS_REMOUNT | MS_RDONLY);
620 if (rc)
621 goto error_rmdir;
622
623 rc = minijail_run_pid_pipes_no_preload(c->jail,
624 c->config->program_argv[0],
625 c->config->program_argv,
626 &c->init_pid, NULL, NULL,
627 NULL);
628 if (rc)
629 goto error_rmdir;
630 return 0;
631
632error_rmdir:
633 umount(c->runfsroot);
634 rmdir(c->runfsroot);
635 unlink(c->pid_file_path);
636 free(c->pid_file_path);
637 rmdir(c->runfs);
638 free(c->runfsroot);
639 free(c->runfs);
640 return rc;
641}
642
643const char *container_root(struct container *c)
644{
645 return c->runfs;
646}
647
648int container_pid(struct container *c)
649{
650 return c->init_pid;
651}
652
653static int container_teardown(struct container *c)
654{
655 int i;
656 int ret = 0;
657
658 /*
659 * Unmount anything we mounted in this mount namespace in the opposite
660 * order that they were mounted.
661 */
662 for (i = (int)c->config->num_mounts - 1; i >= 0; --i) {
663 const struct container_mount *mnt = &c->config->mounts[i];
664 char *dest;
665
666 if (mnt->mount_in_ns)
667 continue;
668 if (asprintf(&dest, "%s%s", c->runfsroot, mnt->destination) < 0)
669 continue;
670 if (umount(dest))
671 ret = -errno;
672 free(dest);
673 }
674 if (umount(c->runfsroot))
675 ret = -errno;
676 if (rmdir(c->runfsroot))
677 ret = -errno;
678 if (unlink(c->pid_file_path))
679 ret = -errno;
680 if (rmdir(c->runfs))
681 ret = -errno;
682 free(c->pid_file_path);
683 free(c->runfsroot);
684 free(c->runfs);
685 return ret;
686}
687
688int container_wait(struct container *c)
689{
Dylan Reidcf745c52016-04-22 10:18:03 -0700690 int rc;
691
692 do {
693 rc = minijail_wait(c->jail);
694 } while (rc == -1 && errno == EINTR);
695
696 if (rc == 0)
697 rc = container_teardown(c);
698 return rc;
Dylan Reid837c74a2016-01-22 17:25:21 -0800699}
700
701int container_kill(struct container *c)
702{
703 int rc;
704
705 rc = kill(c->init_pid, SIGKILL);
706 if (rc)
707 return -errno;
708 return container_wait(c);
709}