/****************************************************************************** * Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved. * lcr licensed under the Mulan PSL v1. * You can use this software according to the terms and conditions of the Mulan PSL v1. * You may obtain a copy of Mulan PSL v1 at: * http://license.coscl.org.cn/MulanPSL * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR * PURPOSE. * See the Mulan PSL v1 for more details. * Author: wujing * Create: 2018-11-08 * Description: provide container definition ******************************************************************************/ /* * liblcrapi */ #define _GNU_SOURCE #include #include #include #include #include #include #include "constants.h" #include "error.h" #include "lcrcontainer.h" #include "lcrcontainer_extend.h" #include "utils.h" #include "log.h" #include "conf.h" #define COMMON_CONFIG_ITEMS \ { .name = "lxc.tty.dir", .value = "lxc" /* Setup the LXC devices in /dev/lxc */ }, \ { .name = "lxc.pty.max", .value = "1024" /* Allow for 1024 pseudo terminals */ }, \ { .name = "lxc.tty.max", .value = "4" /* Setup 4 tty devices */ }, /* Drop some harmful capabilities */ \ { .name = "lxc.cap.drop", .value = "mac_admin mac_override sys_time sys_module sys_rawio" }, \ { .name = "lxc.cgroup.devices.deny", \ .value = "a" /* CGroup whitelist */ }, /* Allow any mknod (but not reading/writing the node */ \ { .name = "lxc.cgroup.devices.allow", \ .value = "c *:* m" }, /* Allow any mknod (but not reading/writing the node */ \ { .name = "lxc.cgroup.devices.allow", .value = "b *:* m" }, \ { .name = "lxc.cgroup.devices.allow", .value = "c 1:3 rwm" /* Allow /dev/null */ }, \ { .name = "lxc.cgroup.devices.allow", .value = "c 1:5 rwm" /* Allow /dev/zero */ }, \ { .name = "lxc.cgroup.devices.allow", .value = "c 1:7 rwm" /* Allow /dev/full */ }, \ { .name = "lxc.cgroup.devices.allow", .value = "c 5:0 rwm" /* Allow /dev/tty */ }, \ { .name = "lxc.cgroup.devices.allow", .value = "c 5:1 rwm" /* Allow /dev/console */ }, \ { .name = "lxc.cgroup.devices.allow", .value = "c 5:2 rwm" /* Allow /dev/ptmx */ }, \ { .name = "lxc.cgroup.devices.allow", .value = "c 1:8 rwm" /* Allow /dev/random */ }, \ { .name = "lxc.cgroup.devices.allow", .value = "c 1:9 rwm" /* Allow /dev/urandom */ }, \ { .name = "lxc.cgroup.devices.allow", .value = "c 136:* rwm" /* Allow /dev/pts */ }, \ { .name = "lxc.cgroup.devices.allow", .value = "c 10:229 rwm" /* Allow fuse */ }, \ { .name = "lxc.mount.auto", .value = "cgroup:mixed proc:mixed sys:mixed" /* default mounts */ }, \ { .name = "lxc.mount.entry", \ .value = "/sys/fs/fuse/connections sys/fs/fuse/connections non bind,optional 0 0" }, \ { NULL, NULL } static const lcr_config_item_t g_app_config_items[] = { /* Container specific configuration */ { .name = "lxc.arch", .value = "x86_64", }, { .name = "lxc.uts.name", .value = "app", }, /* Extra mount entries */ { .name = "lxc.mount.entry", .value = "/dev dev none ro,bind 0 0", }, { .name = "lxc.mount.entry", .value = "/lib lib none ro,bind 0 0", }, { .name = "lxc.mount.entry", .value = "/bin bin none ro,bind 0 0", }, { .name = "lxc.mount.entry", .value = "/usr usr none ro,bind 0 0", }, { .name = "lxc.mount.entry", .value = "/sbin sbin none ro,bind 0 0", }, { .name = "lxc.mount.entry", .value = "tmpfs var/run tmpfs mode=0644 0 0", }, { .name = "lxc.mount.entry", .value = "proc proc proc nodev,noexec,nosuid 0 0", }, { .name = "lxc.mount.entry", .value = "sysfs sys sysfs ro,bind 0 0", }, { .name = "lxc.mount.entry", .value = "/lib64 lib64 none ro,bind 0 0", }, /* Include common config */ COMMON_CONFIG_ITEMS }; static const lcr_config_item_t g_ubuntu_config_items[] = { /* Container specific configuration */ { .name = "lxc.arch", .value = "x86_64" }, { .name = "lxc.uts.name", .value = "ubuntu", }, /* Extra mount entries */ { .name = "lxc.mount.entry", .value = "/sys/kernel/debug sys/kernel/debug none bind,optional 0 0", }, { .name = "lxc.mount.entry", .value = "/sys/kernel/security sys/kernel/security none bind,optional 0 0", }, { .name = "lxc.mount.entry", .value = "/sys/fs/pstore sys/fs/pstore none bind,optional 0 0", }, { .name = "lxc.mount.entry", .value = "mqueue dev/mqueue mqueue rw,realtime,create=dir,optional 0 0", }, /* Extra cgroup device access */ { .name = "lxc.cgroup.devices.allow", .value = "c 254:0 rm", }, /* rtc */ { .name = "lxc.cgroup.devices.allow", .value = "c 10:200 rwm", }, /* tun */ { .name = "lxc.cgroup.devices.allow", .value = "c 10:228 rwm", }, /* hpet */ { .name = "lxc.cgroup.devices.allow", .value = "c 10:232 rwm", }, /* kvm */ /* Include common config */ COMMON_CONFIG_ITEMS }; static const char g_common_seccomp[] = "\ 2\n\ blacklist\n\ reject_force_umount # comment this to allow umount -f; not recommended\n\ [all]\n\ kexec_load errno 1\n\ open_by_handle_at errno 1\n\ init_module errno 1\n\ finit_module errno 1\n\ delete_module errno 1\n\ "; static struct lxc_container *lcr_new_container(const char *name, const char *path) { struct lxc_container *c = lxc_container_new(name, path); if (c == NULL) { ERROR("Failed to delete container."); return NULL; } if (!c->is_defined(c)) { ERROR("No such container"); lcr_set_error_message(LCR_ERR_RUNTIME, "No such container:%s or the configuration files has been corrupted", name); goto out_put; } if (!c->may_control(c)) { ERROR("Insufficent privileges to control"); goto out_put; } return c; out_put: lxc_container_put(c); return NULL; } static int realloc_annotations(oci_runtime_spec *oci_spec, size_t new_lens) { size_t new_size = 0; unsigned long old_size = 0; char **fkey = NULL; char **fval = NULL; int nret = 0; if (new_lens < 1) { return 0; } if (!oci_spec->annotations) { oci_spec->annotations = util_common_calloc_s(sizeof(json_map_string_string)); if (!oci_spec->annotations) { ERROR("Out of memory"); nret = -1; goto out; } } if (new_lens > SIZE_MAX / sizeof(char *) - oci_spec->annotations->len) { ERROR("Too many annotations!"); nret = -1; goto out; } new_size = (oci_spec->annotations->len + new_lens) * sizeof(char *); old_size = oci_spec->annotations->len * sizeof(char *); nret = mem_realloc((void **)&fkey, new_size, oci_spec->annotations->keys, old_size); if (nret) { ERROR("Failed to realloc memory for files limit variables"); nret = -1; goto out; } oci_spec->annotations->keys = fkey; nret = mem_realloc((void **)&fval, new_size, oci_spec->annotations->values, old_size); if (nret) { ERROR("Failed to realloc memory for files limit variables"); nret = -1; goto out; } oci_spec->annotations->values = fval; oci_spec->annotations->len += new_lens; out: return nret; } static int make_annotations(oci_runtime_spec *container, const struct lxc_container *c, int fpos) { int ret = -1; int nret; char default_path[PATH_MAX] = { 0 }; char *realpath = NULL; json_map_string_string *anno = container->annotations; if (fpos == -1) { if (realloc_annotations(container, 1)) { ERROR("Realloc annotations failed"); goto out; } fpos = (int)(anno->len - 1); anno->keys[fpos] = util_strdup_s("log.console.file"); anno->values[fpos] = NULL; } if (!anno->values[fpos]) { nret = sprintf_s(default_path, PATH_MAX, "%s/%s/%s", c->config_path, c->name, "console.log"); if (nret < 0) { ERROR("create default path: %s failed", default_path); goto out; } if (anno->values[fpos]) { free(anno->values[fpos]); } anno->values[fpos] = util_strdup_s(default_path); } if (strcmp("none", anno->values[fpos]) == 0) { DEBUG("Disable console log."); ret = 0; goto out; } if (util_ensure_path(&realpath, anno->values[fpos])) { ERROR("Invalid log path: %s, error: %s.", anno->values[fpos], strerror(errno)); goto out; } ret = 0; out: free(realpath); return ret; } static inline bool is_annotations_key_console_file(const char *key) { return strcmp(key, "log.console.file") == 0; } static int check_annotations(oci_runtime_spec *container, const struct lxc_container *c) { size_t i; int fpos = -1; bool ret = false; if (c == NULL) { return 0; } if (!container->annotations) { SAFE_MALLOC(container->annotations, sizeof(json_map_string_string), ret); if (!ret) { goto out; } } else { for (i = 0; i < container->annotations->len; i++) { if (is_annotations_key_console_file(container->annotations->keys[i])) { fpos = (int)i; } } } if (make_annotations(container, c, fpos)) { goto out; } ret = true; out: return ret ? 0 : -1; } static int trans_rootfs_linux(struct lcr_list *lcr_conf, const char *container_rootfs, oci_runtime_spec *container, char **seccomp) { int ret = -1; struct lcr_list *node = NULL; /*merge the rootfs config*/ if (container_rootfs != NULL) { if (!container->root) { container->root = util_common_calloc_s(sizeof(oci_runtime_spec_root)); if (!container->root) { ERROR("Out of memory"); goto out; } } if (container->root->path) { free(container->root->path); } container->root->path = util_strdup_s(container_rootfs); } /* lxc.rootfs * lxc.rootfs.options */ if (container->root || container->linux) { node = trans_oci_root(container->root, container->linux); if (node == NULL) { ERROR("Failed to translate rootfs configure"); goto out; } lcr_list_merge(lcr_conf, node); } /* lxc.idmap */ if (container->linux) { node = trans_oci_linux(container->linux, seccomp); if (node == NULL) { ERROR("Failed to translate linux configure"); goto out; } lcr_list_merge(lcr_conf, node); } ret = 0; out: return ret; } static int trans_hostname_hooks_process_mounts(struct lcr_list *lcr_conf, const oci_runtime_spec *container) { int ret = -1; /* lxc.uts.name */ struct lcr_list *node = trans_oci_hostname(container->hostname); if (node == NULL) { ERROR("Failed to translate hostname"); goto out; } lcr_list_add_tail(lcr_conf, node); /* lxc.init_{u|g}id * lxc.init_cmd * lxc.environment * lxc.cap.{drop/keep} * lxc.limit.* * lxc.aa_profile * lxc.se_context */ node = trans_oci_process(container->process); if (node == NULL) { ERROR("Failed to translate hooks"); goto out; } lcr_list_merge(lcr_conf, node); /* lxc.mount.entry */ node = trans_oci_mounts(container); if (node == NULL) { ERROR("Failed to translate mount entry configure"); goto out; } lcr_list_merge(lcr_conf, node); ret = 0; out: return ret; } static bool do_stop_and_wait(struct lxc_container *c, long timeout, bool force) { pid_t pid; int sret = 0; bool ret = true; pid = c->init_pid(c); if (pid < 1) { ERROR("Container is not running"); return false; } if (!force) { sret = kill(pid, SIGTERM); if (sret < 0) { if (errno == ESRCH) { return true; } } ret = c->wait(c, "STOPPED", (int)timeout); if (ret) { return true; } } sret = kill(pid, SIGKILL); if (sret < 0) { if (errno == ESRCH) { return true; } } ret = c->wait(c, "STOPPED", -1); if (!ret) { ERROR("Failed to stop container %s", c->name); } return ret; } static bool do_stop(struct lxc_container *c, long timeout, bool force) { bool ret = true; if (force && timeout) { ERROR("-k should not use with -T"); return false; } if (!force && timeout < -1) { ERROR("Timeout should be >= -1"); return false; } if (!c->is_defined(c)) { ERROR("No such container"); ret = false; goto out; } if (!c->may_control(c)) { ERROR("Insufficent privleges to contol"); ret = false; goto out; } if (!c->is_running(c)) { DEBUG("%s is already stopped", c->name); goto out; } ret = do_stop_and_wait(c, timeout, force); out: return ret; } static int lcr_spec_write_seccomp_line(int fd, const char *seccomp) { size_t len; char *line = NULL; int ret = -1; int nret; if (strlen(seccomp) > SIZE_MAX - strlen("lxc.seccomp.profile") - 3 - 1) { ERROR("the length of lxc.seccomp is too long!"); goto cleanup; } len = strlen("lxc.seccomp.profile") + 3 + strlen(seccomp) + 1; line = util_common_calloc_s(len * sizeof(char)); if (line == NULL) { ERROR("Out of memory"); goto cleanup; } nret = sprintf_s(line, len, "%s = %s", "lxc.seccomp.profile", seccomp); if (nret < 0) { ERROR("Sprintf failed"); goto cleanup; } if ((size_t)nret > len - 1) { nret = (int)(len - 1); } line[nret] = '\n'; if (write(fd, line, len) == -1) { SYSERROR("Write failed"); goto cleanup; } ret = 0; cleanup: free(line); return ret; } static char *lcr_save_seccomp_file(const char *bundle, const char *seccomp_conf) { char seccomp[PATH_MAX] = { 0 }; char *real_seccomp = NULL; int fd = -1; int nret; ssize_t written_cnt; nret = sprintf_s(seccomp, sizeof(seccomp), "%s/seccomp", bundle); if (nret < 0) { goto cleanup; } nret = util_ensure_path(&real_seccomp, seccomp); if (nret < 0) { ERROR("Failed to ensure path %s", seccomp); goto cleanup; } fd = util_open(real_seccomp, O_CREAT | O_TRUNC | O_CLOEXEC | O_WRONLY, CONFIG_FILE_MODE); if (fd == -1) { SYSERROR("Create file %s failed", real_seccomp); goto cleanup; } written_cnt = write(fd, seccomp_conf, strlen(seccomp_conf)); close(fd); if (written_cnt == -1) { SYSERROR("write seccomp_conf failed"); goto cleanup; } return real_seccomp; cleanup: free(real_seccomp); return NULL; } static struct lcr_container_info *info_new(struct lcr_container_info **info, size_t *size) { struct lcr_container_info *m = NULL; struct lcr_container_info *n = NULL; size_t length = 0; int nret = 0; if (*size > SIZE_MAX / sizeof(struct lcr_container_info) - 1) { return NULL; } length = (*size + 1) * sizeof(struct lcr_container_info); nret = mem_realloc((void **)&n, length, (void *)(*info), (*size) * sizeof(struct lcr_container_info)); if (nret < 0) { return NULL; } *info = n; m = *info + *size; (*size)++; // *INDENT-OFF* *m = (struct lcr_container_info) { .name = NULL, .state = NULL, .interface = NULL, .ipv4 = NULL, .ipv6 = NULL, .ram = 0.0, .swap = 0.0, .init = -1 }; // *INDENT-ON* return m; } static void free_arr(char **array, size_t size) { size_t i; for (i = 0; i < size; i++) { free(array[i]); array[i] = NULL; } free(array); } /* * Get a complete list of active containers for a given lcrpath. * return Number of containers, or -1 on error. **/ int lcr_list_active_containers(const char *lcrpath, struct lcr_container_info **info_arr) { char **c = NULL; int n = 0; int nret = -1; size_t info_size = 0; const char *path = lcrpath ? lcrpath : LCRPATH; n = list_active_containers(path, &c, NULL); if (n == -1) { n = 0; } nret = lcr_containers_info_get(path, info_arr, &info_size, c, n); if ((info_arr == NULL) && nret == 0) { return -1; } else if ((info_arr == NULL) || nret == -1) { lcr_containers_info_free(info_arr, info_size); return -1; } return (int)info_size; } bool lcr_delete_with_force(const char *name, const char *lcrpath, bool force) { struct lxc_container *c = NULL; const char *path = lcrpath ? lcrpath : LCRPATH; bool ret = false; pid_t pid = 0; clear_error_message(&g_lcr_error); if (name == NULL) { ERROR("Missing container name"); return false; } engine_set_log_prefix(name); c = lcr_new_container(name, path); if (c == NULL) { engine_free_log_prefix(); return false; } if (c->is_running(c)) { if (!force) { ERROR("Container %s is running, Stop the container before remove", name); lcr_set_error_message(LCR_ERR_RUNTIME, "Container %s is running, Stop the container before remove", name); goto out_put; } else { pid = c->init_pid(c); if (pid < 1) { ERROR("Container is not running"); goto clean_delete; } ret = do_stop(c, 0, true); if (!ret) { ERROR("Failed to stop container %s", name); goto out_put; } } } clean_delete: ret = lcr_clean(name, path, NULL, NULL, pid); if (!ret) { ERROR("Failed to clean resource"); } ret = lcr_delete(name, path); if (!ret) { ERROR("Failed to delete container"); } out_put: lxc_container_put(c); engine_free_log_prefix(); return ret; } void lcr_free_config(struct lcr_list *lcr_conf) { struct lcr_list *it = NULL; struct lcr_list *next = NULL; if (lcr_conf == NULL) { return; } lcr_list_for_each_safe(it, lcr_conf, next) { lcr_list_del(it); free_lcr_list_node(it); } } int lcr_containers_info_get(const char *lcrpath, struct lcr_container_info **info, size_t *size, char **containers, int num) { struct lcr_container_info *in = NULL; struct lxc_container *c = NULL; int i; int nret = -1; if ((lcrpath == NULL) || num == 0) { goto err_out; } for (i = 0; i < num; i++) { const char *st = NULL; const char *name = containers[i]; bool run_flag = true; if (name == NULL) { continue; } c = lxc_container_without_config_new(name, lcrpath); if (c == NULL) { continue; } if (!c->is_defined(c)) { goto put_container; } st = c->state(c); if (st == NULL) { st = "UNKNOWN"; } run_flag = (strcmp(st, "STOPPED") != 0); /*Now it makes sense to allocate memory */ in = info_new(info, size); if (in == NULL) { goto put_container; } in->running = run_flag; in->name = util_strdup_s(name); in->state = util_strdup_s(st); if (run_flag) { in->init = c->init_pid(c); } put_container: lxc_container_put(c); } nret = 0; err_out: free_arr(containers, (size_t)num); return nret; } /* *Transform container JSON into oci_runtime_spec struct */ bool container_parse(const char *oci_filename, const char *oci_json_data, oci_runtime_spec **container) { struct parser_context ctx = { OPT_PARSE_STRICT, stderr }; parser_error err = NULL; bool ret = true; if (oci_json_data == NULL) { *container = oci_runtime_spec_parse_file(oci_filename, &ctx, &err); } else { *container = oci_runtime_spec_parse_data(oci_json_data, &ctx, &err); } if (*container == NULL) { ERROR("Failed to get OCI spec: %s", err); ret = false; goto out_free; } out_free: free(err); return ret; } static int merge_annotations(const oci_runtime_spec *container, struct lcr_list *lcr_conf) { int ret = 0; struct lcr_list *tmp = NULL; if (container->annotations != NULL) { tmp = trans_annotations(container->annotations); if (tmp == NULL) { ERROR("Failed to translate annotations configure"); ret = -1; goto out; } lcr_list_merge(lcr_conf, tmp); } out: return ret; } static int merge_needed_lxc_conf(struct lcr_list *lcr_conf) { int ret = 0; struct lcr_list *tmp = get_needed_lxc_conf(); if (tmp == NULL) { ERROR("Failed to append other lxc configure"); ret = -1; goto out; } lcr_list_merge(lcr_conf, tmp); out: return ret; } struct lcr_list *lcr_oci2lcr(const struct lxc_container *c, const char *container_rootfs, oci_runtime_spec *container, char **seccomp) { struct lcr_list *lcr_conf = NULL; lcr_conf = util_common_calloc_s(sizeof(*lcr_conf)); if (lcr_conf == NULL) { goto out_free; } lcr_list_init(lcr_conf); if (check_annotations(container, c)) { ERROR("Check annotations failed"); goto out_free; } if (trans_rootfs_linux(lcr_conf, container_rootfs, container, seccomp)) { goto out_free; } if (trans_hostname_hooks_process_mounts(lcr_conf, container)) { goto out_free; } /* annotations.files.limit */ if (merge_annotations(container, lcr_conf) != 0) { goto out_free; } /* Append other lxc configurations */ if (merge_needed_lxc_conf(lcr_conf) != 0) { goto out_free; } return lcr_conf; out_free: lcr_free_config(lcr_conf); free(lcr_conf); return NULL; } static inline bool is_distribution_ubuntu(const char *distribution) { return strcmp("ubuntu", distribution) == 0; } static inline bool is_distribution_app(const char *distribution) { return strcmp("app", distribution) == 0; } struct lcr_list *lcr_dist2spec(const char *distribution, char **seccomp_conf) { const lcr_config_item_t *items = NULL; const lcr_config_item_t *i = NULL; struct lcr_list *lcr_conf = NULL; struct lcr_list *tmp = NULL; if (seccomp_conf == NULL) { return NULL; } free(*seccomp_conf); *seccomp_conf = NULL; /* get distribution specific configuration */ if (is_distribution_ubuntu(distribution)) { items = g_ubuntu_config_items; *seccomp_conf = util_strdup_s(g_common_seccomp); } else if (is_distribution_app(distribution)) { items = g_app_config_items; } else { ERROR("Distribution \"%s\" is not supported", distribution); return NULL; } /* initialize the config list */ lcr_conf = util_common_calloc_s(sizeof(*lcr_conf)); if (lcr_conf == NULL) { goto out_free; } lcr_list_init(lcr_conf); /* add other config items to `lcr_conf` list */ for (i = items; i->name; i++) { tmp = create_lcr_list_node(i->name, i->value); if (tmp == NULL) { goto out_free; } lcr_list_add_tail(lcr_conf, tmp); } return lcr_conf; out_free: ERROR("Failed translate dist to spec"); free(*seccomp_conf); *seccomp_conf = NULL; lcr_free_config(lcr_conf); free(lcr_conf); return NULL; } static int lcr_open_config_file(const char *bundle) { char config[PATH_MAX] = { 0 }; char *real_config = NULL; int fd = -1; int nret; nret = sprintf_s(config, sizeof(config), "%s/config", bundle); if (nret < 0) { goto out; } nret = util_ensure_path(&real_config, config); if (nret < 0) { ERROR("Failed to ensure path %s", config); goto out; } fd = util_open(real_config, O_CREAT | O_TRUNC | O_CLOEXEC | O_WRONLY, CONFIG_FILE_MODE); if (fd == -1) { ERROR("Create file %s failed, %s", real_config, strerror(errno)); lcr_set_error_message(LCR_ERR_RUNTIME, "Create file %s failed, %s", real_config, strerror(errno)); goto out; } out: free(real_config); return fd; } static int lcr_spec_write_config(int fd, const struct lcr_list *lcr_conf) { struct lcr_list *it = NULL; size_t len; char *line = NULL; int ret = -1; lcr_list_for_each(it, lcr_conf) { lcr_config_item_t *item = it->elem; int nret; if (item != NULL) { if (strlen(item->value) > ((SIZE_MAX - strlen(item->name)) - 4)) { goto cleanup; } len = strlen(item->name) + 3 + strlen(item->value) + 1; line = util_common_calloc_s(len); if (line == NULL) { ERROR("Out of memory"); goto cleanup; } nret = sprintf_s(line, len, "%s = %s", item->name, item->value); if (nret < 0) { ERROR("Sprintf failed"); goto cleanup; } if ((size_t)nret > len - 1) { nret = (int)(len - 1); } line[nret] = '\n'; if (write(fd, line, len) == -1) { SYSERROR("Write failed"); goto cleanup; } free(line); line = NULL; } } ret = 0; cleanup: free(line); return ret; } char *lcr_get_bundle(const char *lcrpath, const char *name) { size_t len = 0; int nret = 0; char *bundle = NULL; struct stat s; if (strlen(name) > ((SIZE_MAX - strlen(lcrpath)) - 2)) { goto cleanup; } /* bundle = lcrpath + '/' + name + '\0' */ len = strlen(lcrpath) + strlen(name) + 2; bundle = util_common_calloc_s(len); if (bundle == NULL) { ERROR("Out of memory"); goto cleanup; } nret = sprintf_s(bundle, len, "%s/%s", lcrpath, name); if (nret < 0) { ERROR("Print bundle string failed"); goto cleanup; } if (stat(bundle, &s) != 0) { switch (errno) { case EACCES: ERROR("You lack permission to access %s", bundle); break; case ENOENT: ERROR("Bundle %s does not exist", bundle); break; default: ERROR("Access %s failed: %s\n", bundle, strerror(errno)); } goto cleanup; } return bundle; cleanup: free(bundle); return NULL; } bool lcr_save_spec(const char *name, const char *lcrpath, const struct lcr_list *lcr_conf, const char *seccomp_conf) { bool bret = false; const char *path = lcrpath ? lcrpath : LCRPATH; char *bundle = NULL; char *seccomp = NULL; int fd = -1; int nret = 0; if (name == NULL) { ERROR("Missing container name"); return bret; } bundle = lcr_get_bundle(path, name); if (bundle == NULL) { goto out_free; } if (seccomp_conf != NULL) { seccomp = lcr_save_seccomp_file(bundle, seccomp_conf); if (seccomp == NULL) { goto out_free; } } fd = lcr_open_config_file(bundle); if (fd == -1) { goto out_free; } if (lcr_spec_write_config(fd, lcr_conf)) { goto out_free; } if (seccomp_conf != NULL) { nret = lcr_spec_write_seccomp_line(fd, seccomp); if (nret) { goto out_free; } } bret = true; out_free: free(bundle); free(seccomp); if (fd != -1) { close(fd); } return bret; }