From d0442316761717849deb248b5da45240f7bb1d38 Mon Sep 17 00:00:00 2001 From: liuxu Date: Thu, 11 Apr 2024 11:01:44 +0800 Subject: [PATCH 67/69] cdi:support modules container_edits/parser --- src/daemon/modules/api/specs_api.h | 13 + .../device/cdi/behavior/cdi_container_edits.c | 622 +++++++++++++++++- .../device/cdi/behavior/cdi_container_edits.h | 2 +- .../device/cdi/behavior/parser/cdi_parser.c | 196 +++++- .../device/cdi/behavior/parser/cdi_parser.h | 6 +- src/daemon/modules/spec/specs.c | 230 +++++++ 6 files changed, 1052 insertions(+), 17 deletions(-) diff --git a/src/daemon/modules/api/specs_api.h b/src/daemon/modules/api/specs_api.h index f54c0d31..0999836b 100644 --- a/src/daemon/modules/api/specs_api.h +++ b/src/daemon/modules/api/specs_api.h @@ -55,6 +55,19 @@ const oci_runtime_spec *get_readonly_default_oci_spec(bool system_container); int spec_module_init(void); +#ifdef ENABLE_CDI +int defs_process_add_multiple_env(defs_process *dp, const char **envs, size_t env_len); +int spec_add_multiple_process_env(oci_runtime_spec *oci_spec, const char **envs, size_t env_len); +int spec_add_device(oci_runtime_spec *oci_spec, defs_device *device); +int spec_add_linux_resources_device(oci_runtime_spec *oci_spec, bool allow, const char *dev_type, + int64_t major, int64_t minor, const char *access); +void spec_remove_mount(oci_runtime_spec *oci_spec, const char *dest); +int spec_add_mount(oci_runtime_spec *oci_spec, defs_mount *mnt); +int spec_add_prestart_hook(oci_runtime_spec *oci_spec, defs_hook *prestart_hook); +int spec_add_poststart_hook(oci_runtime_spec *oci_spec, defs_hook *poststart_hook); +int spec_add_poststop_hook(oci_runtime_spec *oci_spec, defs_hook *poststop_hook); +#endif /* ENABLE_CDI */ + #ifdef __cplusplus } #endif diff --git a/src/daemon/modules/device/cdi/behavior/cdi_container_edits.c b/src/daemon/modules/device/cdi/behavior/cdi_container_edits.c index 590118b1..816b9c2d 100644 --- a/src/daemon/modules/device/cdi/behavior/cdi_container_edits.c +++ b/src/daemon/modules/device/cdi/behavior/cdi_container_edits.c @@ -14,36 +14,642 @@ ******************************************************************************/ #include "cdi_container_edits.h" +#include +#include +#include +#include +#include + +#include "error.h" +#include "path.h" +#include "specs_extend.h" +#include "utils.h" +#include "utils_array.h" +#include "specs_api.h" + +/* + * The OCI being used by the iSulad not supportes + * createRuntime/createContainer/startContainer currently. + */ // PRESTART_HOOK is the name of the OCI "prestart" hook. #define PRESTART_HOOK "prestart" -// CREATE_RUNTIME_HOOK is the name of the OCI "createRuntime" hook. -#define CREATE_RUNTIME_HOOK "createRuntime" -// CREATE_CONTAINER_HOOK is the name of the OCI "createContainer" hook. -#define CREATE_CONTAINER_HOOK "createContainer" -// START_CONTAINER_HOOK is the name of the OCI "startContainer" hook. -#define START_CONTAINER_HOOK "startContainer" // POSTSTART_HOOK is the name of the OCI "poststart" hook. #define POSTSTART_HOOK "poststart" // POSTSTOP_HOOK is the name of the OCI "poststop" hook. #define POSTSTOP_HOOK "poststop" +#define VALID_HOOK_NAME_LEN 3 +static const char* g_valid_hook_names[VALID_HOOK_NAME_LEN] = { + PRESTART_HOOK, POSTSTART_HOOK, POSTSTOP_HOOK +}; + +static int cdi_validate_env(char **envs, size_t envs_len); +static int cdi_validate_device_node(cdi_device_node *d); +static int cdi_validate_hook(cdi_hook *h); +static int cdi_validate_mount(cdi_mount *m); + +#define BLOCK_DEVICE "b" +#define CHAR_DEVICE "c" +#define FIFO_DEVICE "p" +static int device_info_from_path(const char *path, char **dev_type, int64_t *major, int64_t *minor) +{ + struct stat stat = { 0 }; + int ret = 0; + + ret = lstat(path, &stat); + if (ret != 0) { + ERROR("Failed to stat %s", path); + return -1; + } + + if (S_ISBLK(stat.st_mode)) { + *dev_type = util_strdup_s(BLOCK_DEVICE); + } else if (S_ISCHR(stat.st_mode)) { + *dev_type = util_strdup_s(CHAR_DEVICE); + } else if (S_ISFIFO(stat.st_mode)) { + *dev_type = util_strdup_s(FIFO_DEVICE); + } else { + *dev_type = NULL; + *major = 0; + *minor = 0; + ERROR("Not a device node"); + return -1; + } + + *major = (int64_t)major(stat.st_rdev); + *minor = (int64_t)minor(stat.st_rdev); + return 0; +} + +static int fill_device_node_info(cdi_device_node *d) +{ + __isula_auto_free char *dev_type = NULL; + int64_t major; + int64_t minor; + + if (d->host_path == NULL) { + d->host_path = util_strdup_s(d->path); + } + + if (d->type != NULL && (d->major != 0 || strcmp(d->type, FIFO_DEVICE) == 0)) { + return 0; + } + + if (device_info_from_path(d->host_path, &dev_type, &major, &minor) != 0) { + ERROR("Failed to stat CDI host device %s", d->host_path); + return -1; + } + + if (d->type == NULL) { + d->type = dev_type; + dev_type = NULL; + } else { + if (strcmp(d->type, dev_type) != 0) { + ERROR("CDI device (%s, %s), host type mismatch (%s, %s)", + d->path, d->host_path, d->type, dev_type); + return -1; + } + } + if (d->major == 0 && strcmp(d->type, FIFO_DEVICE) != 0) { + d->major = major; + d->minor = minor; + } + return 0; +} + +static cdi_device_node *clone_cdi_device_node(cdi_device_node *d) +{ + cdi_device_node *device_node = NULL; + + device_node = util_common_calloc_s(sizeof(*device_node)); + if (device_node == NULL) { + ERROR("Out of memory"); + return NULL; + } + + device_node->path = util_strdup_s(d->path); + device_node->host_path = util_strdup_s(d->host_path); + device_node->type = util_strdup_s(d->type); + device_node->major = d->major; + device_node->minor = d->minor; + device_node->file_mode = d->file_mode; + device_node->permissions = util_strdup_s(d->permissions); + device_node->uid = d->uid; + device_node->gid = d->gid; + return device_node; +} + +static cdi_hook *clone_cdi_hook(cdi_hook *h) +{ + cdi_hook *hook = NULL; + + hook = util_common_calloc_s(sizeof(*hook)); + if (hook == NULL) { + ERROR("Out of memory"); + return NULL; + } + + hook->hook_name = util_strdup_s(h->hook_name); + hook->path = util_strdup_s(h->path); + if (h->args_len != 0) { + hook->args = util_copy_array_by_len(h->args, h->args_len); + if (hook->args == NULL) { + ERROR("Failed to copy args"); + goto error_out; + } + hook->args_len = h->args_len; + } + if (h->env_len != 0) { + hook->env = util_copy_array_by_len(h->env, h->env_len); + if (hook->env == NULL) { + ERROR("Failed to copy env"); + goto error_out; + } + hook->env_len = h->env_len; + } + hook->timeout = h->timeout; + + return hook; + +error_out: + free_cdi_hook(hook); + return NULL; +} + +static cdi_mount *clone_cdi_mount(cdi_mount *m) +{ + cdi_mount *mount = NULL; + + mount = util_common_calloc_s(sizeof(*mount)); + if (mount == NULL) { + ERROR("Out of memory"); + return NULL; + } + + mount->host_path = util_strdup_s(m->host_path); + mount->container_path = util_strdup_s(m->container_path); + if (m->options_len != 0) { + mount->options = util_copy_array_by_len(m->options, m->options_len); + if (mount->options == NULL) { + ERROR("Failed to copy options"); + free_cdi_mount(mount); + return NULL; + } + mount->options_len = m->options_len; + } + mount->type = util_strdup_s(m->type); + + return mount; +} + +static defs_hook *cdi_hook_to_oci(cdi_hook *h) +{ + defs_hook *oci_hook = NULL; + + oci_hook = util_common_calloc_s(sizeof(*oci_hook)); + if (oci_hook == NULL) { + ERROR("Out of memory"); + return NULL; + } + + oci_hook->path = util_strdup_s(h->path); + if (h->args_len != 0) { + oci_hook->args = util_copy_array_by_len(h->args, h->args_len); + if (oci_hook->args == NULL) { + ERROR("Failed to copy args"); + goto error_out; + } + oci_hook->args_len = h->args_len; + } + if (h->env_len != 0) { + oci_hook->env = util_copy_array_by_len(h->env, h->env_len); + if (oci_hook->env == NULL) { + ERROR("Failed to copy env"); + goto error_out; + } + oci_hook->env_len = h->env_len; + } + oci_hook->timeout = h->timeout; + return oci_hook; + +error_out: + free_defs_hook(oci_hook); + return NULL; +} + +static defs_mount *cdi_mount_to_oci(cdi_mount *m) +{ + defs_mount *oci_mount = NULL; + + oci_mount = util_common_calloc_s(sizeof(*oci_mount)); + if (oci_mount == NULL) { + ERROR("Out of memory"); + return NULL; + } + + oci_mount->source = util_strdup_s(m->host_path); + oci_mount->destination = util_strdup_s(m->container_path); + if (m->options_len != 0) { + oci_mount->options = util_copy_array_by_len(m->options, m->options_len); + if (oci_mount->options == NULL) { + ERROR("Failed to copy options"); + free_defs_mount(oci_mount); + return NULL; + } + oci_mount->options_len = m->options_len; + } + oci_mount->type = util_strdup_s(m->type); + + return oci_mount; +} + +static defs_device *cdi_device_node_to_oci(cdi_device_node *d) +{ + defs_device *oci_device = NULL; + + oci_device = util_common_calloc_s(sizeof(*oci_device)); + if (oci_device == NULL) { + ERROR("Out of memory"); + return NULL; + } + + oci_device->path = util_strdup_s(d->path); + oci_device->type = util_strdup_s(d->type); + oci_device->major = d->major; + oci_device->minor = d->minor; + oci_device->file_mode = d->file_mode; + oci_device->uid = d->uid; + oci_device->gid = d->gid; + + return oci_device; +} + +static int apply_cdi_device_nodes(cdi_container_edits *e, oci_runtime_spec *spec) +{ + size_t i; + defs_device *dev = NULL; + cdi_device_node *dn = NULL; + const char *access = NULL; + + for (i = 0; i < e->device_nodes_len; i++) { + dn = clone_cdi_device_node(e->device_nodes[i]); + if (dn == NULL) { + ERROR("Failed to copy device node"); + goto error_out; + } + + if (fill_device_node_info(dn) != 0) { + goto error_out; + } + dev = cdi_device_node_to_oci(dn); + if (dev == NULL) { + ERROR("Failed to generate oci device"); + goto error_out; + } + /* Currently, for uid and gid, isulad cannot distinguish + * 0 and unspecified. Here, 0 is processed as unspecified. + */ + if (dev->uid == 0 && spec->process != NULL) { + if (spec->process->user->uid > 0) { + dev->uid = spec->process->user->uid; + } + } + if (dev->gid == 0 && spec->process != NULL) { + if (spec->process->user->gid > 0) { + dev->gid = spec->process->user->gid; + } + } + + if (spec_add_device(spec, dev) != 0) { + goto error_out; + } + + if (strcmp(dev->type, BLOCK_DEVICE) == 0 || strcmp(dev->type, CHAR_DEVICE) == 0) { + if (e->device_nodes[i]->permissions != NULL) { + access = e->device_nodes[i]->permissions; + } else { + access = "rwm"; + } + if (spec_add_linux_resources_device(spec, true, dev->type, + dev->major, dev->minor, access)) { + dev = NULL; + goto error_out; + } + } + free_cdi_device_node(dn); + dn = NULL; + dev = NULL; + } + + return 0; + +error_out: + free_cdi_device_node(dn); + free_defs_device(dev); + return -1; +} + +static int defs_mount_parts(defs_mount *m) +{ + char cleanpath[PATH_MAX] = { 0 }; + if (util_clean_path(m->destination, cleanpath, sizeof(cleanpath)) == NULL) { + return -1; + } + + return util_strings_count(cleanpath, '/'); +} + +static inline int defs_mount_cmp(defs_mount **first, defs_mount **second) +{ + int first_part = defs_mount_parts(*first); + int second_part = defs_mount_parts(*second); + + if (first_part < second_part) { + return -1; + } + if (first_part > second_part) { + return 1; + } + + return strcmp((*first)->destination, (*second)->destination); +} + +static int apply_cdi_mounts(cdi_container_edits *e, oci_runtime_spec *spec) +{ + size_t i; + defs_mount *mnt = NULL; + + if (e->mounts_len == 0) { + return 0; + } + + for (i = 0; i < e->mounts_len; i++) { + spec_remove_mount(spec, e->mounts[i]->container_path); + mnt = cdi_mount_to_oci(e->mounts[i]); + if (spec_add_mount(spec, mnt) != 0) { + free_defs_mount(mnt); + return -1; + } + } + + qsort(spec->mounts, spec->mounts_len, + sizeof(defs_mount *), (int (*)(const void *, const void *))defs_mount_cmp); + return 0; +} + +static int apply_cdi_hooks(cdi_container_edits *e, oci_runtime_spec *spec) +{ + size_t i; + int ret = 0; + + for (i = 0; i < e->hooks_len; i++) { + defs_hook *oci_hook = cdi_hook_to_oci(e->hooks[i]); + if (strcmp(e->hooks[i]->hook_name, PRESTART_HOOK)) { + ret = spec_add_prestart_hook(spec, oci_hook); + } else if (strcmp(e->hooks[i]->hook_name, POSTSTART_HOOK)) { + ret = spec_add_poststart_hook(spec, oci_hook); + } else if (strcmp(e->hooks[i]->hook_name, POSTSTOP_HOOK)) { + ret = spec_add_poststop_hook(spec, oci_hook); + } else { + /* + * The OCI being used by the iSulad not supportes + * createRuntime/createContainer/startContainer currently. + */ + ERROR("Unknown hook name %s", e->hooks[i]->hook_name); + free_defs_hook(oci_hook); + return -1; + } + if (ret != 0) { + ERROR("Failed add hook %s", e->hooks[i]->hook_name); + free_defs_hook(oci_hook); + return -1; + } + } + return ret; +} + int cdi_container_edits_apply(cdi_container_edits *e, oci_runtime_spec *spec) { + if (spec == NULL) { + ERROR("Can't edit nil OCI Spec"); + return -1; + } + if (e == NULL) { + WARN("Cdi container edits is nil"); + return 0; + } + + if (e->env_len > 0) { + if (spec_add_multiple_process_env(spec, (const char **)e->env, e->env_len) != 0) { + ERROR("Failed to merge envs"); + return -1; + } + } + + if (apply_cdi_device_nodes(e, spec) != 0) { + ERROR("Failed to apply device nodes"); + return -1; + } + + if (apply_cdi_mounts(e, spec) != 0) { + ERROR("Failed to apply mounts"); + return -1; + } + + if (apply_cdi_hooks(e, spec) != 0) { + ERROR("Failed to apply hooks"); + return -1; + } + return 0; } -int cdi_container_edits_validate(cdi_container_edits *e, char **error) +int cdi_container_edits_validate(cdi_container_edits *e) { + size_t i; + + if (e == NULL) { + WARN("Cdi container edits is nil"); + return 0; + } + + if (cdi_validate_env(e->env, e->env_len) != 0) { + ERROR("Invalid container edits"); + return -1; + } + for (i = 0; i < e->device_nodes_len; i++) { + if (cdi_validate_device_node(e->device_nodes[i]) != 0) { + ERROR("Invalid container device node"); + return -1; + } + } + for (i = 0; i < e->hooks_len; i++) { + if (cdi_validate_hook(e->hooks[i]) != 0) { + ERROR("Invalid container hook"); + return -1; + } + } + for (i = 0; i < e->mounts_len; i++) { + if (cdi_validate_mount(e->mounts[i]) != 0) { + ERROR("Invalid container mount"); + return -1; + } + } + return 0; } +#define EDITS_APPEND_ITEM_DEF(item) \ + static int append_##item(cdi_container_edits *e, cdi_container_edits *o, clone_common_array_item_cb cb) \ + { \ + common_array e_array = { \ + .items = (void **)e->item, \ + .len = e->item##_len, \ + .cap = e->item##_len, \ + .free_item_cb = NULL, \ + .clone_item_cb = cb \ + }; \ + common_array o_array = { \ + .items = (void **)o->item, \ + .len = o->item##_len, \ + .cap = o->item##_len, \ + .free_item_cb = NULL, \ + .clone_item_cb = cb \ + }; \ + if (util_merge_common_array(&e_array, &o_array) != 0) { \ + ERROR("Out of memory"); \ + return -1; \ + } \ + e->item = (void *)e_array.items; \ + e->item##_len += o->item##_len; \ + return 0; \ + } + +EDITS_APPEND_ITEM_DEF(env) +EDITS_APPEND_ITEM_DEF(device_nodes) +EDITS_APPEND_ITEM_DEF(hooks) +EDITS_APPEND_ITEM_DEF(mounts) + int cdi_container_edits_append(cdi_container_edits *e, cdi_container_edits *o) { + if (o == NULL) { + return 0; + } + if (e == NULL) { + ERROR("Invalid params"); + return -1; + } + + if (append_env(e, o, (clone_common_array_item_cb)util_strdup_s) != 0) { + return -1; + } + if (append_device_nodes(e, o, (clone_common_array_item_cb)clone_cdi_device_node) != 0) { + return -1; + } + if (append_hooks(e, o, (clone_common_array_item_cb)clone_cdi_hook) != 0) { + return -1; + } + if (append_mounts(e, o, (clone_common_array_item_cb)clone_cdi_mount) != 0) { + return -1; + } + return 0; } bool cdi_container_edits_is_empty(cdi_container_edits *e) { - return true; + if (e == NULL) { + return false; + } + return e->env_len + e->device_nodes_len + e->hooks_len + e->mounts_len == 0; } +static int cdi_validate_env(char **envs, size_t envs_len) +{ + size_t i; + char *ptr = NULL; + + for (i = 0; i < envs_len; i++) { + ptr = strchr(envs[i], '='); + if (ptr == NULL || ptr == envs[i]) { + ERROR("Invalid environment variable %s", envs[i]); + return -1; + } + } + return 0; +} + +static int cdi_validate_device_node(cdi_device_node *d) +{ + char *p = NULL; + + if (d == NULL) { + ERROR("Device node is nil"); + return -1; + } + + if (d->path == NULL) { + ERROR("Invalid (empty) device path"); + return -1; + } + if (d->type != NULL && strcmp(d->type, BLOCK_DEVICE) != 0 && + strcmp(d->type, CHAR_DEVICE) != 0 && strcmp(d->type, FIFO_DEVICE) != 0) { + ERROR("Device %s: invalid type %s", d->path, d->type); + return -1; + } + for (p = d->permissions; p != NULL && *p != '\0'; p++) { + if (*p != 'r' && *p != 'w' && *p != 'm') { + ERROR("Device %s: invalid permissions %s", d->path, d->permissions); + return -1; + } + } + + return 0; +} + +static int cdi_validate_hook(cdi_hook *h) +{ + size_t i; + + if (h == NULL) { + ERROR("Hook is nil"); + return -1; + } + + for (i = 0; i < VALID_HOOK_NAME_LEN; i++) { + if (strcmp(h->hook_name, g_valid_hook_names[i]) == 0) { + break; + } + } + if (i == VALID_HOOK_NAME_LEN) { + ERROR("Invalid hook name %s", h->hook_name); + return -1; + } + if (h->path == NULL) { + ERROR("Invalid hook %s with empty path", h->hook_name); + return -1; + } + if (cdi_validate_env(h->env, h->env_len) != 0) { + ERROR("Invalid hook %s", h->hook_name); + return -1; + } + return 0; +} + +static int cdi_validate_mount(cdi_mount *m) +{ + if (m == NULL) { + ERROR("Mount is nil"); + return -1; + } + + if (m->host_path == NULL) { + ERROR("Invalid mount, empty host path"); + return -1; + } + if (m->container_path == NULL) { + ERROR("Invalid mount, empty container path"); + return -1; + } + return 0; +} diff --git a/src/daemon/modules/device/cdi/behavior/cdi_container_edits.h b/src/daemon/modules/device/cdi/behavior/cdi_container_edits.h index ea921e37..ddceec25 100644 --- a/src/daemon/modules/device/cdi/behavior/cdi_container_edits.h +++ b/src/daemon/modules/device/cdi/behavior/cdi_container_edits.h @@ -28,7 +28,7 @@ extern "C" { #endif int cdi_container_edits_apply(cdi_container_edits *e, oci_runtime_spec *spec); -int cdi_container_edits_validate(cdi_container_edits *e, char **error); +int cdi_container_edits_validate(cdi_container_edits *e); int cdi_container_edits_append(cdi_container_edits *e, cdi_container_edits *o); bool cdi_container_edits_is_empty(cdi_container_edits *e); diff --git a/src/daemon/modules/device/cdi/behavior/parser/cdi_parser.c b/src/daemon/modules/device/cdi/behavior/parser/cdi_parser.c index 14293c72..8824d29c 100644 --- a/src/daemon/modules/device/cdi/behavior/parser/cdi_parser.c +++ b/src/daemon/modules/device/cdi/behavior/parser/cdi_parser.c @@ -14,42 +14,228 @@ ******************************************************************************/ #include "cdi_parser.h" +#include +#include +#include +#include +#include + +#include "error.h" +#include "utils_string.h" + +/* cdi_parser_qualified_name returns the qualified name for a device. + * The syntax for a qualified device names is + * + * "/=". + * + * A valid vendor and class name may contain the following runes: + * + * 'A'-'Z', 'a'-'z', '0'-'9', '.', '-', '_'. + * + * A valid device name may contain the following runes: + * + * 'A'-'Z', 'a'-'z', '0'-'9', '-', '_', '.', ':' + */ char *cdi_parser_qualified_name(const char *vendor, const char *class, const char *name) { - return NULL; + char device_name[PATH_MAX] = { 0 }; + int nret; + + if (vendor == NULL || class == NULL || name == NULL) { + ERROR("Invalid argument"); + return NULL; + } + + nret = snprintf(device_name, sizeof(device_name), "%s/%s=%s", + vendor, class, name); + if (nret < 0 || (size_t)nret >= sizeof(device_name)) { + ERROR("Device name is too long"); + return NULL; + } + return util_strdup_s(device_name); } +// cdi_parser_is_qualified_name tests if a device name is qualified. bool cdi_parser_is_qualified_name(const char *device) { - return true; + __isula_auto_free char *vendor = NULL; + __isula_auto_free char *class = NULL; + __isula_auto_free char *name = NULL; + + return cdi_parser_parse_qualified_name(device, &vendor, &class, &name) == 0; } +// cdi_parser_parse_qualified_name splits a qualified name into device vendor, class, and name. int cdi_parser_parse_qualified_name(const char *device, char **vendor, char **class, char **name) { + int ret = 0; + + ret = cdi_parser_parse_device(device, vendor, class, name); + if (ret != 0) { + if (*vendor == NULL) { + ERROR("Unqualified device %s, missing vendor", device); + return -1; + } + if (*class == NULL) { + ERROR("Unqualified device %s, missing class", device); + return -1; + } + if (*name == NULL) { + ERROR("Unqualified device %s, missing name", device); + return -1; + } + ERROR("Unqualified device %s", device); + return -1; + } + + if (cdi_parser_validate_vendor_name(*vendor) != 0) { + ERROR("Invalid device %s", device); + goto err_out; + } + if (cdi_parser_validate_class_name(*class) != 0) { + ERROR("Invalid device %s", device); + goto err_out; + } + if (cdi_parser_validate_device_name(*name) != 0) { + ERROR("Invalid device %s", device); + goto err_out; + } + return 0; + +err_out: + free(*vendor); + *vendor = NULL; + free(*class); + *class = NULL; + free(*name); + *name = NULL; + return -1; } +// cdi_parser_parse_device tries to split a device name into vendor, class, and name. int cdi_parser_parse_device(const char *device, char **vendor, char **class, char **name) { + __isula_auto_array_t char **parts = NULL; + + if (vendor == NULL || class == NULL || name == NULL || + device == NULL || device[0] == '/') { + ERROR("Invalid argument"); + return -1; + } + + parts = util_string_split_n(device, '=', 2); + if (parts == NULL || util_array_len((const char **)parts) != 2 || parts[0] == NULL || parts[1] == NULL) { + return -1; + } + + *name = parts[1]; + parts[1] = NULL; + (void)cdi_parser_parse_qualifier(parts[0], vendor, class); + if (*vendor == NULL) { + ERROR("Failed to parse device qualifier: %s", parts[0]); + return -1; + } + return 0; } +/* cdi_parser_parse_qualifier splits a device qualifier into vendor and class. + * The syntax for a device qualifier is + * + * "/" + */ int cdi_parser_parse_qualifier(const char *kind, char **vendor, char **class) { + __isula_auto_array_t char **parts = NULL; + + if (kind == NULL || vendor == NULL || class == NULL) { + ERROR("Invalid argument"); + return -1; + } + + parts = util_string_split_n(kind, '/', 2); + if (parts == NULL || util_array_len((const char **)parts) != 2 || parts[0] == NULL || parts[1] == NULL) { + return -1; + } + *vendor = parts[0]; + parts[0] = NULL; + *class = parts[1]; + parts[1] = NULL; + + return 0; +} + +static int validate_vendor_or_class_name(const char *name) +{ + int i = 0; + + if (name == NULL) { + ERROR("Empty name"); + return -1; + } + + if (!isalpha(name[0])) { + ERROR("%s, should start with letter", name); + return -1; + } + for (i = 1; name[i] != '\0'; i++) { + if (!(isalnum(name[i]) || name[i] == '_' || name[i] == '-' || name[i] == '.')) { + ERROR("Invalid character '%c' in name %s", name[i], name); + return -1; + } + } + if (!isalnum(name[i - 1])) { + ERROR("%s, should end with a letter or digit", name); + return -1; + } + return 0; } -int cdi_parser_validate_vendor_name(const char *vendor, char **error) +int cdi_parser_validate_vendor_name(const char *vendor) { + if (validate_vendor_or_class_name(vendor) != 0) { + ERROR("Invalid vendor"); + return -1; + } return 0; } -int cdi_parser_validate_class_name(const char *class, char **error) +int cdi_parser_validate_class_name(const char *class) { + if (validate_vendor_or_class_name(class) != 0) { + ERROR("Invalid class."); + return -1; + } return 0; } -int cdi_parser_validate_device_name(const char *name, char **error) +int cdi_parser_validate_device_name(const char *name) { + size_t i; + + if (name == NULL) { + ERROR("Invalid (empty) device name"); + return -1; + } + if (!isalnum(name[0])) { + ERROR("Invalid class %s, should start with a letter or digit", name); + return -1; + } + if (strlen(name) == 1) { + return 0; + } + for (i = 1; name[i] != '\0'; i++) { + if (!(isalnum(name[i]) || name[i] == '_' || name[i] == '-' || name[i] == '.' || name[i] == ':')) { + ERROR("Invalid character '%c' in device name %s", name[i], name); + return -1; + } + } + if (!isalnum(name[i - 1])) { + ERROR("Invalid name %s, should end with a letter or digit", name); + return -1; + } + return 0; } diff --git a/src/daemon/modules/device/cdi/behavior/parser/cdi_parser.h b/src/daemon/modules/device/cdi/behavior/parser/cdi_parser.h index 467641a1..3658e29b 100644 --- a/src/daemon/modules/device/cdi/behavior/parser/cdi_parser.h +++ b/src/daemon/modules/device/cdi/behavior/parser/cdi_parser.h @@ -27,9 +27,9 @@ bool cdi_parser_is_qualified_name(const char *device); int cdi_parser_parse_qualified_name(const char *device, char **vendor, char **class, char **name); int cdi_parser_parse_device(const char *device, char **vendor, char **class, char **name); int cdi_parser_parse_qualifier(const char *kind, char **vendor, char **class); -int cdi_parser_validate_vendor_name(const char *vendor, char **error); -int cdi_parser_validate_class_name(const char *class, char **error); -int cdi_parser_validate_device_name(const char *name, char **error); +int cdi_parser_validate_vendor_name(const char *vendor); +int cdi_parser_validate_class_name(const char *class); +int cdi_parser_validate_device_name(const char *name); #ifdef __cplusplus } diff --git a/src/daemon/modules/spec/specs.c b/src/daemon/modules/spec/specs.c index b4d2b0f6..77ca70f9 100644 --- a/src/daemon/modules/spec/specs.c +++ b/src/daemon/modules/spec/specs.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "specs_api.h" #include "utils.h" @@ -2595,3 +2596,232 @@ int spec_module_init(void) return 0; } +#ifdef ENABLE_CDI +static int add_env(defs_process *dp, const char *env, const char *key) +{ + size_t i; + char *oci_key = NULL; + char *oci_value = NULL; + char *saveptr = NULL; + __isula_auto_free char *tmp_env = NULL; + + for (i = 0; i < dp->env_len; i++) { + tmp_env = util_strdup_s(dp->env[i]); + oci_key = strtok_r(tmp_env, "=", &saveptr); + oci_value = strtok_r(NULL, "=", &saveptr); + if (oci_key == NULL || oci_value == NULL) { + ERROR("Bad env format"); + return -1; + } + if (strcmp(key, oci_key) == 0) { + free(dp->env[i]); + dp->env[i] = util_strdup_s(env); + return 0; + } + free(tmp_env); + tmp_env = NULL; + } + if (util_mem_realloc((void **)&dp->env, (dp->env_len + 1) * sizeof(char *), + (void *)dp->env, dp->env_len * sizeof(char *)) != 0) { + ERROR("Out of memory"); + return -1; + } + dp->env[dp->env_len] = util_strdup_s(env); + dp->env_len++; + return 0; +} + +int defs_process_add_multiple_env(defs_process *dp, const char **envs, size_t env_len) +{ + size_t i; + char *key = NULL; + char *value = NULL; + char *saveptr = NULL; + __isula_auto_free char *tmp_env = NULL; + + if (envs == NULL || env_len == 0) { + DEBUG("empty envs"); + return 0; + } + if (dp == NULL) { + ERROR("Invalid params"); + return -1; + } + + for (i = 0; i < env_len; i++) { + tmp_env = util_strdup_s(envs[i]); + key = strtok_r(tmp_env, "=", &saveptr); + value = strtok_r(NULL, "=", &saveptr); + if (key == NULL || value == NULL) { + ERROR("Bad env format: %s", tmp_env); + return -1; + } + if (add_env(dp, envs[i], key) != 0) { + return -1; + } + free(tmp_env); + tmp_env = NULL; + } + + return 0; +} + +int spec_add_multiple_process_env(oci_runtime_spec *oci_spec, const char **envs, size_t env_len) +{ + int ret = 0; + + if (envs == NULL || env_len == 0) { + DEBUG("empty envs"); + return 0; + } + if (oci_spec == NULL) { + ERROR("Invalid params"); + return -1; + } + + ret = make_sure_oci_spec_process(oci_spec); + if (ret < 0) { + ERROR("Out of memory"); + return -1; + } + + ret = defs_process_add_multiple_env(oci_spec->process, envs, env_len); + if (ret < 0) { + ERROR("Failed to add envs"); + } + + return ret; +} + +int spec_add_device(oci_runtime_spec *oci_spec, defs_device *device) +{ + int ret = 0; + size_t i; + + if (device == NULL) { + return -1; + } + ret = make_sure_oci_spec_linux(oci_spec); + if (ret < 0) { + return -1; + } + + for (i = 0; i < oci_spec->linux->devices_len; i++) { + if (strcmp(oci_spec->linux->devices[i]->path, device->path) == 0) { + free_defs_device(oci_spec->linux->devices[i]); + oci_spec->linux->devices[i] = device; + return 0; + } + } + + if (util_mem_realloc((void **)&oci_spec->linux->devices, (oci_spec->linux->devices_len + 1) * sizeof(char *), + (void *)oci_spec->linux->devices, oci_spec->linux->devices_len * sizeof(char *)) != 0) { + ERROR("Out of memory"); + return -1; + } + oci_spec->linux->devices[oci_spec->linux->devices_len] = device; + oci_spec->linux->devices_len++; + + return 0; +} + +int spec_add_linux_resources_device(oci_runtime_spec *oci_spec, bool allow, const char *dev_type, + int64_t major, int64_t minor, const char *access) +{ + int ret = 0; + defs_device_cgroup *device = NULL; + + ret = make_sure_oci_spec_linux_resources(oci_spec); + if (ret < 0) { + return -1; + } + + device = util_common_calloc_s(sizeof(*device)); + if (device == NULL) { + ERROR("Out of memory"); + return -1; + } + device->allow = allow; + device->type = util_strdup_s(dev_type); + device->access = util_strdup_s(access); + device->major = major; + device->minor = minor; + + if (util_mem_realloc((void **)&oci_spec->linux->resources->devices, (oci_spec->linux->resources->devices_len + 1) * sizeof(char *), + (void *)oci_spec->linux->resources->devices, oci_spec->linux->resources->devices_len * sizeof(char *)) != 0) { + ERROR("Out of memory"); + free_defs_device_cgroup(device); + return -1; + } + oci_spec->linux->resources->devices[oci_spec->linux->resources->devices_len] = device; + oci_spec->linux->resources->devices_len++; + + return 0; +} + +void spec_remove_mount(oci_runtime_spec *oci_spec, const char *dest) +{ + size_t i; + + if (oci_spec == NULL || oci_spec->mounts == NULL || dest == NULL) { + return; + } + + for (i = 0; i < oci_spec->mounts_len; i++) { + if (strcmp(oci_spec->mounts[i]->destination, dest) == 0) { + free_defs_mount(oci_spec->mounts[i]); + (void)memcpy((void **)&oci_spec->mounts[i], (void **)&oci_spec->mounts[i + 1], + (oci_spec->mounts_len - i - 1) * sizeof(void *)); + oci_spec->mounts_len--; + return; + } + } +} + +int spec_add_mount(oci_runtime_spec *oci_spec, defs_mount *mnt) +{ + if (oci_spec == NULL || mnt == NULL) { + return -1; + } + + if (util_mem_realloc((void **)&oci_spec->mounts, (oci_spec->mounts_len + 1) * sizeof(char *), + (void *)oci_spec->mounts, oci_spec->mounts_len * sizeof(char *)) != 0) { + ERROR("Out of memory"); + return -1; + } + oci_spec->mounts[oci_spec->mounts_len] = mnt; + oci_spec->mounts_len++; + + return 0; +} + +#define SPEC_ADD_HOOKS_ITEM_DEF(hooktype) \ + int spec_add_##hooktype##_hook(oci_runtime_spec *oci_spec, defs_hook *hooktype##_hook) \ + { \ + int ret = 0; \ + if (oci_spec == NULL || hooktype##_hook == NULL) { \ + return -1; \ + } \ + ret = make_sure_oci_spec_hooks(oci_spec); \ + if (ret < 0) { \ + return -1; \ + } \ + if (util_mem_realloc((void **)&oci_spec->hooks->hooktype, (oci_spec->hooks->hooktype##_len + 1) * sizeof(char *), \ + (void *)oci_spec->hooks->hooktype, oci_spec->hooks->hooktype##_len * sizeof(char *)) != 0) { \ + ERROR("Out of memory"); \ + return -1; \ + } \ + oci_spec->hooks->hooktype[oci_spec->hooks->hooktype##_len] = hooktype##_hook; \ + oci_spec->hooks->hooktype##_len++; \ + return 0; \ + } + +/* +* The OCI being used by the iSulad not supportes +* createRuntime/createContainer/startContainer currently. +*/ +SPEC_ADD_HOOKS_ITEM_DEF(prestart) +SPEC_ADD_HOOKS_ITEM_DEF(poststart) +SPEC_ADD_HOOKS_ITEM_DEF(poststop) + +#endif /* ENABLE_CDI */ \ No newline at end of file -- 2.34.1