From 6ebea2ab8e91d294ba2d8d2821937a770f27fcb4 Mon Sep 17 00:00:00 2001 From: liuhao Date: Mon, 14 Jan 2019 17:47:17 +0800 Subject: [PATCH 025/131] support oci hooks support oci hooks Signed-off-by: LiFeng --- configure.ac | 3 + src/lxc/Makefile.am | 13 +- src/lxc/conf.c | 528 ++++++++++++- src/lxc/conf.h | 9 + src/lxc/json/defs.c | 198 +++++ src/lxc/json/defs.h | 37 + src/lxc/json/json_common.c | 1196 ++++++++++++++++++++++++++++++ src/lxc/json/json_common.h | 185 +++++ src/lxc/json/oci_runtime_hooks.c | 53 ++ src/lxc/json/oci_runtime_hooks.h | 15 + src/lxc/json/oci_runtime_spec.c | 196 +++++ src/lxc/json/oci_runtime_spec.h | 37 + src/lxc/json/read-file.c | 94 +++ src/lxc/json/read-file.h | 11 + src/lxc/lxccontainer.c | 66 ++ src/lxc/lxccontainer.h | 7 + src/lxc/start.c | 17 + src/lxc/utils.c | 66 +- src/lxc/utils.h | 2 + 19 files changed, 2716 insertions(+), 17 deletions(-) create mode 100644 src/lxc/json/defs.c create mode 100644 src/lxc/json/defs.h create mode 100755 src/lxc/json/json_common.c create mode 100755 src/lxc/json/json_common.h create mode 100644 src/lxc/json/oci_runtime_hooks.c create mode 100644 src/lxc/json/oci_runtime_hooks.h create mode 100644 src/lxc/json/oci_runtime_spec.c create mode 100644 src/lxc/json/oci_runtime_spec.h create mode 100644 src/lxc/json/read-file.c create mode 100644 src/lxc/json/read-file.h diff --git a/configure.ac b/configure.ac index 950c8dde..4da52a27 100644 --- a/configure.ac +++ b/configure.ac @@ -120,6 +120,9 @@ AM_CONDITIONAL([DISTRO_UBUNTU], [test "x$with_distro" = "xubuntu"]) AC_CONFIG_LINKS([config/etc/default.conf:config/etc/${distroconf}]) +# Check yajl +PKG_CHECK_MODULES([YAJL], [yajl >= 2],[],[AC_MSG_ERROR([You must install yajl >= 2])]) + # Check for init system type AC_MSG_CHECKING([for init system type]) AC_ARG_WITH([init-script], diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index f2928b7d..5678b8da 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -43,6 +43,11 @@ noinst_HEADERS = attach.h \ ../tests/lxctest.h \ tools/arguments.h \ storage/storage_utils.h \ + json/defs.h \ + json/json_common.h \ + json/oci_runtime_hooks.h \ + json/oci_runtime_spec.h \ + json/read-file.h \ utils.h if IS_BIONIC @@ -140,6 +145,11 @@ liblxc_la_SOURCES = af_unix.c af_unix.h \ terminal.c \ utils.c utils.h \ version.h \ + json/json_common.c json/json_common.h \ + json/defs.h json/defs.c \ + json/oci_runtime_hooks.c json/oci_runtime_hooks.h \ + json/oci_runtime_spec.c json/oci_runtime_spec.h \ + json/read-file.c json/read-file.h \ $(LSM_SOURCES) if IS_BIONIC @@ -192,6 +202,7 @@ AM_CFLAGS = -DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \ -I $(top_srcdir)/src \ -I $(top_srcdir)/src/lxc \ -I $(top_srcdir)/src/lxc/storage \ + -I $(top_srcdir)/src/lxc/json \ -I $(top_srcdir)/src/lxc/cgroups if ENABLE_APPARMOR @@ -224,7 +235,7 @@ liblxc_la_CFLAGS = -fPIC \ liblxc_la_LDFLAGS = -pthread \ -Wl,-no-undefined \ -Wl,-soname,liblxc.so.$(firstword $(subst ., ,@LXC_ABI@)) \ - -version-info @LXC_ABI_MAJOR@ + -version-info @LXC_ABI_MAJOR@ @YAJL_LIBS@ liblxc_la_LIBADD = $(CAP_LIBS) \ $(GNUTLS_LIBS) \ diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 800573a8..6a14de1f 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -53,6 +53,7 @@ #include #include #include +#include #include "af_unix.h" #include "caps.h" @@ -137,7 +138,10 @@ char *lxchook_names[NUM_LXC_HOOKS] = { "post-stop", "clone", "destroy", - "start-host" + "start-host", + "oci-prestart", + "oci-poststart", + "oci-poststop" }; struct mount_opt { @@ -4082,13 +4086,530 @@ int lxc_setup(struct lxc_handler *handler) return 0; } +struct oci_hook_conf { + defs_hook *ocihook; + + int errfd; + int which; +}; + +struct wait_conf { + pid_t pid; + unsigned long long startat; + int timeout; + int errfd; + int which; +}; + +static char* generate_json_str(const char *name, const char *lxcpath, const char *rootfs) +{ + char *cpid = NULL; + char *inmsg = NULL; + int rc = 0, ret = 0; + size_t size; + + if (!name || !lxcpath || !rootfs) { + ERROR("Invalid arguments"); + return NULL; + } + cpid = getenv("LXC_PID"); + if (!cpid) { + ERROR("Get container %s pid failed: %s", name, strerror(errno)); + cpid = "-1"; + } + // {"ociVersion":"","id":"xxx","pid":777,"root":"xxx","bundlePath":"xxx"} + size = 1 + 16 + 5 + strlen(name) + 3 + 6 + strlen(cpid) + 1 + + 7 + strlen(rootfs) + 3 + 13 + strlen(lxcpath) + 1 + strlen(name) + 3 + 1; + inmsg = malloc(size); + if (!inmsg) { + ERROR("Out of memory"); + ret = -1; + goto out_free; + } + rc = snprintf(inmsg, size, + "{\"ociVersion\":\"\",\"id\":\"%s\",\"pid\":%s,\"root\":\"%s\",\"bundlePath\":\"%s/%s\"}", + name, cpid, rootfs, lxcpath, name); + if (rc < 0 || rc >= size) { + ERROR("Create json string failed"); + ret = -1; + } + +out_free: + if (ret) { + free(inmsg); + inmsg = NULL; + } + return inmsg; +} + +static char **merge_ocihook_env(char **oldenvs, size_t env_len, size_t *merge_env_len) +{ + char **result; + size_t result_len = env_len; + size_t i, j; + char *tmpenv; + char *lxc_envs[] = {"LXC_CGNS_AWARE", "LXC_PID", "LXC_ROOTFS_MOUNT", + "LXC_CONFIG_FILE", "LXC_CGROUP_PATH", "LXC_ROOTFS_PATH", "LXC_NAME"}; + char *lxcenv_buf; + + result_len += (sizeof(lxc_envs) / sizeof(char *)) + 1; + result = malloc(sizeof(char *) * result_len); + if (!result) + return NULL; + memset(result, 0, sizeof(char *) * result_len); + + for(i = 0; i < env_len; i++) { + if (oldenvs[i]) + result[i] = strdup(oldenvs[i]); + } + + for(j = 0; j < (sizeof(lxc_envs) / sizeof(char *)); j++) { + tmpenv = getenv(lxc_envs[j]); + if (tmpenv && i < (result_len - 1)) { + lxcenv_buf = malloc(strlen(tmpenv) + 1 + strlen(lxc_envs[j]) + 1); + if (!lxcenv_buf) { + lxc_free_array((void **)result, free); + return NULL; + } + if (sprintf(lxcenv_buf, "%s=%s", lxc_envs[j], tmpenv) < 0) { + free(lxcenv_buf); + continue; + } + result[i++] = lxcenv_buf; + lxcenv_buf = NULL; + } + } + + *merge_env_len = i; + return result; +} + +static struct lxc_popen_FILE *lxc_popen_ocihook(char *commandpath, char **args, int args_len, + char **envs, int env_len, const char *instr) +{ + int ret; + struct lxc_popen_FILE *fp = NULL; + int pipe_fds[2] = {-1, -1}; + int pipe_msg[2] = {-1, -1}; + pid_t child_pid; + + ret = pipe2(pipe_fds, O_CLOEXEC | O_NONBLOCK); + if (ret < 0) + return NULL; + + ret = pipe2(pipe_msg, O_CLOEXEC | O_NONBLOCK); + if (ret < 0) { + ERROR("Pipe msg failure"); + close(pipe_fds[0]); + close(pipe_fds[1]); + return NULL; + } + + child_pid = fork(); + if (child_pid < 0) + goto on_error; + + if (child_pid == 0) { + /* child */ + size_t result_capacity; + int r; + char **real_args; + + close(pipe_msg[1]); + if (pipe_msg[0] != STDIN_FILENO) + dup2(pipe_msg[0], STDIN_FILENO); + else { + if (fcntl(pipe_msg[0], F_SETFD, 0) != 0) { + SYSERROR("Failed to remove FD_CLOEXEC from fd."); + exit(127); + } + } + close(pipe_msg[0]); + + close(pipe_fds[0]); + + /* duplicate stdout */ + if (pipe_fds[1] != STDOUT_FILENO) + ret = dup2(pipe_fds[1], STDOUT_FILENO); + else + ret = fcntl(pipe_fds[1], F_SETFD, 0); + if (ret < 0) { + close(pipe_fds[1]); + _exit(EXIT_FAILURE); + } + + /* duplicate stderr */ + if (pipe_fds[1] != STDERR_FILENO) + ret = dup2(pipe_fds[1], STDERR_FILENO); + else + ret = fcntl(pipe_fds[1], F_SETFD, 0); + close(pipe_fds[1]); + if (ret < 0) + _exit(EXIT_FAILURE); + + /* + * Unblock signals. + * This is the main/only reason + * why we do our lousy popen() emulation. + */ + { + sigset_t mask; + sigfillset(&mask); + sigprocmask(SIG_UNBLOCK, &mask, NULL); + } + + result_capacity = args_len; + real_args = malloc(sizeof(char *) * (result_capacity + 2 + 1)); + if (!real_args) + _exit(EXIT_FAILURE); + memset(real_args, 0, sizeof(char *) * (result_capacity + 2 + 1)); + real_args[0] = strdup("sh"); + real_args[1] = strdup(commandpath); + for(r = 2; r < (args_len + 1); r++) + real_args[r] = strdup(args[r-1]); + + if (env_len > 0) + execvpe("/bin/sh", real_args, envs); + else + execvp("/bin/sh", real_args); + exit(127); + } + + /* parent */ + + close(pipe_fds[1]); + pipe_fds[1] = -1; + + close(pipe_msg[0]); + pipe_msg[0]= -1; + if (instr) { + size_t len = strlen(instr); + if (lxc_write_nointr(pipe_msg[1], instr, len) != len) { + WARN("Write instr: %s failed", instr); + } + } + close(pipe_msg[1]); + pipe_msg[1]= -1; + + fp = calloc(1, sizeof(*fp)); + if (!fp) { + ERROR("Failed to allocate memory"); + goto on_error; + } + + fp->child_pid = child_pid; + fp->pipe = pipe_fds[0]; + + return fp; + +on_error: + + if (pipe_fds[0] >= 0) + close(pipe_fds[0]); + + if (pipe_fds[1] >= 0) + close(pipe_fds[1]); + + if (pipe_msg[0] >= 0) + close(pipe_msg[0]); + + if (pipe_msg[1] >= 0) + close(pipe_msg[1]); + + if (fp) + free(fp); + + return NULL; +} + +void* wait_ocihook_timeout(void *arg) +{ + bool alive = false; + struct wait_conf *conf = (struct wait_conf *)arg; + + if (!conf || conf->timeout < 1) + goto out; + + sleep(conf->timeout); + + alive = lxc_process_alive(conf->pid, conf->startat); + + if (alive) { + ERROR("%s:%d: running %s hook caused \"hook ran past specified timeout of %.1fs\"", + __FILE__, __LINE__, + (conf->which == LXCHOOK_START_HOST) ? "prestart" : lxchook_names[conf->which], + (double)conf->timeout); + + if (conf->errfd >= 0) { + lxc_write_error_message(conf->errfd, "%s:%d: running %s hook caused \"hook ran past specified timeout of %.1fs\"", + __FILE__, __LINE__, + (conf->which == LXCHOOK_START_HOST) ? "prestart" : lxchook_names[conf->which], + (double)conf->timeout); + } + + if (kill(conf->pid, SIGKILL) && errno != ESRCH) { + ERROR("Send kill signal failed"); + goto out; + } + } + +out: + free(conf); + return ((void *)0); +} + +static int run_ocihook_buffer(struct oci_hook_conf *oconf, char *inmsg) +{ + struct lxc_popen_FILE *f; + char output[LXC_LOG_BUFFER_SIZE] = {0}; + int ret; + pthread_t ptid; + int err; + struct wait_conf *conf = NULL; + pthread_attr_t attr; + char *buffer = oconf->ocihook->path; + char *err_args_msg = NULL; + char *err_envs_msg = NULL; + char **hookenvs = NULL; + size_t hookenvs_len = 0; + + hookenvs = merge_ocihook_env(oconf->ocihook->env, oconf->ocihook->env_len, &hookenvs_len); + if (!hookenvs) { + ERROR("Out of memory."); + return -1; + } + + f = lxc_popen_ocihook(buffer, oconf->ocihook->args, oconf->ocihook->args_len, hookenvs, hookenvs_len, inmsg); + lxc_free_array((void **)hookenvs, free); + if (!f) { + SYSERROR("Failed to popen() %s.", buffer); + return -1; + } + + conf = malloc(sizeof(struct wait_conf)); + if (!conf) { + SYSERROR("Failed to malloc."); + goto on_error; + } + + memset(conf, 0x00, sizeof(struct wait_conf)); + + conf->pid = f->child_pid; + conf->startat = lxc_get_process_startat(conf->pid); + + INFO("hook_conf timeout %d", oconf->ocihook->timeout); + if(oconf->ocihook->timeout > 0) + conf->timeout = oconf->ocihook->timeout; + else { + conf->timeout = 30; + INFO("Set hook timeout 30s"); + } + conf->errfd = oconf->errfd; + conf->which = oconf->which; + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + err = pthread_create(&ptid, &attr, wait_ocihook_timeout, conf); + if (err != 0) { + ERROR("Create wait timeout thread failed"); + goto on_error; + } + + ret = lxc_wait_for_pid_status(f->child_pid); + + lxc_read_nointr(f->pipe, output, sizeof(output) - 1); + close(f->pipe); + free(f); + + if (ret == -1) { + SYSERROR("Script exited with error."); + goto print_hook; + } else if (WIFEXITED(ret) && WEXITSTATUS(ret) != 0) { + ERROR("Script exited with status %d. output:%s", WEXITSTATUS(ret), output); + if (conf->errfd >= 0) { + lxc_write_error_message(conf->errfd, "%s:%d: running %s hook caused \"error running hook: exit status %d, output:%s\"", + __FILE__, __LINE__, + (conf->which >= NUM_LXC_HOOKS) ? "invalid type" : lxchook_names[conf->which], + WEXITSTATUS(ret), output); + } + + goto print_hook; + } else if (WIFSIGNALED(ret)) { + ERROR("Script terminated by signal %d.", WTERMSIG(ret)); + if (conf->errfd >= 0) { + lxc_write_error_message(conf->errfd, "%s:%d: running %s hook caused \"error running hook: Script terminated by signal %d\"", + __FILE__, __LINE__, + (conf->which >= NUM_LXC_HOOKS) ? "invalid type" : lxchook_names[conf->which], + WTERMSIG(ret)); + } + + goto print_hook; + } + + return 0; + +on_error: + if (f) { + if (f->pipe >= 0) + close(f->pipe); + free(f); + } + +print_hook: + if (oconf->ocihook->args) + err_args_msg = lxc_string_join(" ", (const char **)oconf->ocihook->args, false); + if (oconf->ocihook->env) + err_envs_msg = lxc_string_join(" ", (const char **)oconf->ocihook->env, false); + ERROR("Hook script command: \"%s\", args: \"%s\", envs: \"%s\", timeout: %d.", + buffer, err_args_msg ? err_args_msg : "", + err_envs_msg ? err_envs_msg : "", conf->timeout); + + free(err_args_msg); + free(err_envs_msg); + return -1; +} + +static int run_ocihook_script_argv(const char *name, const char *section, + struct oci_hook_conf *oconf, + const char *lxcpath, const char *rootfs) +{ + int ret; + const char *script = oconf->ocihook->path; + char *inmsg; + + INFO("Executing script \"%s\" for container \"%s\", config section \"%s\".", + script, name, section); + + inmsg = generate_json_str(name, lxcpath, rootfs); + if (!inmsg) { + return -1; + } + + ret = run_ocihook_buffer(oconf, inmsg); + free(inmsg); + inmsg = NULL; + return ret; +} + +static char *get_root_path(const char *path, const char *backend) +{ + char *ret = NULL; + char *tmp = NULL; + + if (!path) { + ret = strdup("/"); + return ret; + } + if (!backend) { + goto default_out; + } + + if (strcmp(backend, "aufs") == 0 || + strcmp(backend, "overlayfs") == 0 || + strcmp(backend, "loop") == 0) { + tmp = strrchr(path, ':'); + tmp++; + ret = strdup(tmp); + if (!ret) { + ERROR("Out of memory"); + return NULL; + } + return ret; + } + +default_out: + ret = strdup(path); + if (!ret) { + ERROR("Out of memory"); + return NULL; + } + return ret; +} + +static int run_oci_hooks(const char *name, const char *lxcpath, struct lxc_conf *lc, int which, int errfd) +{ + struct oci_hook_conf work_conf = {0}; + oci_runtime_spec_hooks *ocihooks = NULL; + size_t i; + int ret = 0; + char *rootpath; + + if (!lc || !lc->ocihooks) { + return -1; + } + + rootpath = get_root_path(lc->rootfs.path, lc->rootfs.bdev_type); + if (!rootpath) { + ERROR("Get container %s rootpath failed.", name); + return -1; + } + + work_conf.errfd = errfd; + work_conf.which = which; + switch (which) { + case OCI_HOOK_PRESTART: + for (i = 0; i < lc->ocihooks->prestart_len; i++) { + work_conf.ocihook = lc->ocihooks->prestart[i]; + ret = run_ocihook_script_argv(name, "lxc", &work_conf, lxcpath, rootpath); + if (ret != 0) + break; + } + break; + case OCI_HOOK_POSTSTART: + for (i = 0; i < lc->ocihooks->poststart_len; i++) { + work_conf.ocihook = lc->ocihooks->poststart[i]; + ret = run_ocihook_script_argv(name, "lxc", &work_conf, lxcpath, rootpath); + if (ret != 0) + break; + } + break; + case OCI_HOOK_POSTSTOP: + for (i = 0; i < lc->ocihooks->poststop_len; i++) { + work_conf.ocihook = lc->ocihooks->poststop[i]; + ret = run_ocihook_script_argv(name, "lxc", &work_conf, lxcpath, rootpath); + if (ret != 0) + break; + } + break; + default: + ret = -1; + } + if (rootpath) + free(rootpath); + return ret; +} + int run_lxc_hooks(const char *name, char *hookname, struct lxc_conf *conf, char *argv[]) { struct lxc_list *it; int which = -1; - if (strcmp(hookname, "pre-start") == 0) + if (strcmp(hookname, "oci-prestart") == 0) { + int ret; + which = OCI_HOOK_PRESTART; + if (!argv || !argv[0]) { + ERROR("oci hook require lxcpath"); + return -1; + } + return run_oci_hooks(name, argv[0], conf, which, conf->errpipe[1]); + } else if (strcmp(hookname, "oci-poststart") == 0) { + int ret; + which = OCI_HOOK_POSTSTART; + if (!argv || !argv[0]) { + ERROR("oci hook require lxcpath"); + return -1; + } + return run_oci_hooks(name, argv[0], conf, which, conf->errpipe[1]); + } else if (strcmp(hookname, "oci-poststop") == 0) { + int ret; + which = OCI_HOOK_POSTSTOP; + if (!argv || !argv[0]) { + ERROR("oci hook require lxcpath"); + return -1; + } + return run_oci_hooks(name, argv[0], conf, which, conf->errpipe[1]); + } else if (strcmp(hookname, "pre-start") == 0) which = LXCHOOK_PRESTART; else if (strcmp(hookname, "start-host") == 0) which = LXCHOOK_START_HOST; @@ -4476,6 +4997,9 @@ void lxc_conf_free(struct lxc_conf *conf) if (current_config == conf) current_config = NULL; + // isulad: free oci hooks + if (conf->ocihooks) + free_oci_runtime_spec_hooks(conf->ocihooks); lxc_terminal_conf_free(&conf->console); free(conf->rootfs.mount); free(conf->rootfs.bdev_type); diff --git a/src/lxc/conf.h b/src/lxc/conf.h index 7393dbfe..2263e47e 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -35,6 +35,7 @@ #include #include +#include "oci_runtime_hooks.h" #include "compiler.h" #include "config.h" #include "list.h" @@ -239,6 +240,9 @@ enum lxchooks { LXCHOOK_CLONE, LXCHOOK_DESTROY, LXCHOOK_START_HOST, + OCI_HOOK_PRESTART, + OCI_HOOK_POSTSTART, + OCI_HOOK_POSTSTOP, NUM_LXC_HOOKS }; @@ -307,6 +311,11 @@ struct lxc_conf { struct lxc_list hooks[NUM_LXC_HOOKS]; }; + /* + * isulad: support oci hook + * */ + oci_runtime_spec_hooks *ocihooks; + char *lsm_aa_profile; unsigned int lsm_aa_allow_incomplete; char *lsm_se_context; diff --git a/src/lxc/json/defs.c b/src/lxc/json/defs.c new file mode 100644 index 00000000..38df2f7a --- /dev/null +++ b/src/lxc/json/defs.c @@ -0,0 +1,198 @@ +// Generated from defs.json. Do not edit! +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include +#include "securec.h" +#include "defs.h" + +defs_hook *make_defs_hook(yajl_val tree, struct parser_context *ctx, parser_error *err) { + defs_hook *ret = NULL; + *err = 0; + if (tree == NULL) + return ret; + ret = safe_malloc(sizeof(*ret)); + { + yajl_val val = get_val(tree, "path", yajl_t_string); + if (val) { + char *str = YAJL_GET_STRING(val); + ret->path = safe_strdup(str ? str : ""); + } + } + { + yajl_val tmp = get_val(tree, "args", yajl_t_array); + if (tmp && YAJL_GET_ARRAY(tmp)) { + size_t i; + ret->args_len = YAJL_GET_ARRAY(tmp)->len; + ret->args = safe_malloc((YAJL_GET_ARRAY(tmp)->len + 1) * sizeof(*ret->args)); + for (i = 0; i < YAJL_GET_ARRAY(tmp)->len; i++) { + yajl_val val = YAJL_GET_ARRAY(tmp)->values[i]; + if (val) { + char *str = YAJL_GET_STRING(val); + ret->args[i] = safe_strdup(str ? str : ""); + } + } + } + } + { + yajl_val tmp = get_val(tree, "env", yajl_t_array); + if (tmp && YAJL_GET_ARRAY(tmp)) { + size_t i; + ret->env_len = YAJL_GET_ARRAY(tmp)->len; + ret->env = safe_malloc((YAJL_GET_ARRAY(tmp)->len + 1) * sizeof(*ret->env)); + for (i = 0; i < YAJL_GET_ARRAY(tmp)->len; i++) { + yajl_val val = YAJL_GET_ARRAY(tmp)->values[i]; + if (val) { + char *str = YAJL_GET_STRING(val); + ret->env[i] = safe_strdup(str ? str : ""); + } + } + } + } + { + yajl_val val = get_val(tree, "timeout", yajl_t_number); + if (val) { + int invalid = common_safe_int(YAJL_GET_NUMBER(val), (int *)&ret->timeout); + if (invalid) { + if (asprintf(err, "Invalid value '%s' with type 'integer' for key 'timeout': %s", YAJL_GET_NUMBER(val), strerror(-invalid)) < 0) + *err = safe_strdup("error allocating memory"); + free_defs_hook(ret); + return NULL; + } + } + } + if (ret->path == NULL) { + if (asprintf(err, "Required field '%s' not present", "path") < 0) + *err = safe_strdup("error allocating memory"); + free_defs_hook(ret); + return NULL; + } + + if (tree->type == yajl_t_object && (ctx->options & PARSE_OPTIONS_STRICT)) { + int i; + for (i = 0; i < tree->u.object.len; i++) + if (strcmp(tree->u.object.keys[i], "path") && + strcmp(tree->u.object.keys[i], "args") && + strcmp(tree->u.object.keys[i], "env") && + strcmp(tree->u.object.keys[i], "timeout")) { + if (ctx->stderr > 0) + fprintf(ctx->stderr, "WARNING: unknown key found: %s\n", tree->u.object.keys[i]); + } + } + return ret; +} + +void free_defs_hook(defs_hook *ptr) { + if (!ptr) + return; + free(ptr->path); + ptr->path = NULL; + if (ptr->args) { + size_t i; + for (i = 0; i < ptr->args_len; i++) { + if (ptr->args[i]) { + free(ptr->args[i]); + ptr->args[i] = NULL; + } + } + free(ptr->args); + ptr->args = NULL; + } + if (ptr->env) { + size_t i; + for (i = 0; i < ptr->env_len; i++) { + if (ptr->env[i]) { + free(ptr->env[i]); + ptr->env[i] = NULL; + } + } + free(ptr->env); + ptr->env = NULL; + } + free(ptr); +} + +yajl_gen_status gen_defs_hook(yajl_gen g, defs_hook *ptr, struct parser_context *ctx, parser_error *err) { + yajl_gen_status stat = yajl_gen_status_ok; + *err = 0; + stat = reformat_start_map(g); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if ((ctx->options & GEN_OPTIONS_ALLKEYVALUE) ||(ptr && ptr->path)) { + char *str = ""; + stat = reformat_map_key(g, "path", strlen("path")); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if (ptr && ptr->path) { + str = ptr->path; + } + stat = reformat_string(g, str, strlen(str)); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + } + if ((ctx->options & GEN_OPTIONS_ALLKEYVALUE) ||(ptr && ptr->args)) { + size_t len = 0, i; + stat = reformat_map_key(g, "args", strlen("args")); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if (ptr && ptr->args) { + len = ptr->args_len; + } + if (!len && !(ctx->options & GEN_OPTIONS_SIMPLIFY)) + yajl_gen_config(g, yajl_gen_beautify, 0); + stat = reformat_start_array(g); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + for (i = 0; i < len; i++) { + stat = reformat_string(g, ptr->args[i], strlen(ptr->args[i])); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = reformat_end_array(g); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if (!len && !(ctx->options & GEN_OPTIONS_SIMPLIFY)) + yajl_gen_config(g, yajl_gen_beautify, 1); + } + if ((ctx->options & GEN_OPTIONS_ALLKEYVALUE) ||(ptr && ptr->env)) { + size_t len = 0, i; + stat = reformat_map_key(g, "env", strlen("env")); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if (ptr && ptr->env) { + len = ptr->env_len; + } + if (!len && !(ctx->options & GEN_OPTIONS_SIMPLIFY)) + yajl_gen_config(g, yajl_gen_beautify, 0); + stat = reformat_start_array(g); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + for (i = 0; i < len; i++) { + stat = reformat_string(g, ptr->env[i], strlen(ptr->env[i])); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = reformat_end_array(g); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if (!len && !(ctx->options & GEN_OPTIONS_SIMPLIFY)) + yajl_gen_config(g, yajl_gen_beautify, 1); + } + if ((ctx->options & GEN_OPTIONS_ALLKEYVALUE) ||(ptr && ptr->timeout)) { + long long int num = 0; + stat = reformat_map_key(g, "timeout", strlen("timeout")); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if (ptr && ptr->timeout) { + num = (long long int)ptr->timeout; + } + stat = reformat_int(g, num); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = reformat_end_map(g); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + return yajl_gen_status_ok; +} diff --git a/src/lxc/json/defs.h b/src/lxc/json/defs.h new file mode 100644 index 00000000..0bbd8ac8 --- /dev/null +++ b/src/lxc/json/defs.h @@ -0,0 +1,37 @@ +// Generated from defs.json. Do not edit! +#ifndef DEFS_SCHEMA_H +#define DEFS_SCHEMA_H + +#include +#include +#include "json_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + char *path; + + char **args; + size_t args_len; + + char **env; + size_t env_len; + + int timeout; + +} +defs_hook; + +void free_defs_hook(defs_hook *ptr); + +defs_hook *make_defs_hook(yajl_val tree, struct parser_context *ctx, parser_error *err); + +yajl_gen_status gen_defs_hook(yajl_gen g, defs_hook *ptr, struct parser_context *ctx, parser_error *err); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/lxc/json/json_common.c b/src/lxc/json/json_common.c new file mode 100755 index 00000000..8b91844d --- /dev/null +++ b/src/lxc/json/json_common.c @@ -0,0 +1,1196 @@ +// Auto generated file. Do not edit! +#define _GNU_SOURCE +#include +#include +#include +#include "json_common.h" + +#define MAX_NUM_STR_LEN 21 + +yajl_gen_status reformat_number(void *ctx, const char *str, size_t len) { + yajl_gen g = (yajl_gen) ctx; + return yajl_gen_number(g, str, len); +} + +yajl_gen_status reformat_uint(void *ctx, long long unsigned int num) { + char numstr[MAX_NUM_STR_LEN]; + int ret; + + ret = sprintf(numstr, "%llu", num); + if (ret < 0) { + return yajl_gen_in_error_state; + } + return reformat_number(ctx, (const char *)numstr, strlen(numstr)); +} + +yajl_gen_status reformat_int(void *ctx, long long int num) { + char numstr[MAX_NUM_STR_LEN]; + int ret; + + ret = sprintf(numstr, "%lld", num); + if (ret < 0) { + return yajl_gen_in_error_state; + } + return reformat_number(ctx, (const char *)numstr, strlen(numstr)); +} + +yajl_gen_status reformat_double(void *ctx, double num) { + yajl_gen g = (yajl_gen) ctx; + return yajl_gen_double(g, num); +} + +yajl_gen_status reformat_string(void *ctx, const char *str, size_t len) { + yajl_gen g = (yajl_gen) ctx; + return yajl_gen_string(g, (const unsigned char *)str, len); +} + +yajl_gen_status reformat_null(void *ctx) { + yajl_gen g = (yajl_gen) ctx; + return yajl_gen_null(g); +} + +yajl_gen_status reformat_bool(void *ctx, int boolean) { + yajl_gen g = (yajl_gen) ctx; + return yajl_gen_bool(g, boolean); +} + +yajl_gen_status reformat_map_key(void *ctx, const char *str, size_t len) { + yajl_gen g = (yajl_gen) ctx; + return yajl_gen_string(g, (const unsigned char *)str, len); +} + +yajl_gen_status reformat_start_map(void *ctx) { + yajl_gen g = (yajl_gen) ctx; + return yajl_gen_map_open(g); +} + +yajl_gen_status reformat_end_map(void *ctx) { + yajl_gen g = (yajl_gen) ctx; + return yajl_gen_map_close(g); +} + +yajl_gen_status reformat_start_array(void *ctx) { + yajl_gen g = (yajl_gen) ctx; + return yajl_gen_array_open(g); +} + +yajl_gen_status reformat_end_array(void *ctx) { + yajl_gen g = (yajl_gen) ctx; + return yajl_gen_array_close(g); +} + +bool json_gen_init(yajl_gen *g, struct parser_context *ctx) { + *g = yajl_gen_alloc(NULL); + if (NULL == *g) { + return false; + + } + yajl_gen_config(*g, yajl_gen_beautify, !(ctx->options & GEN_OPTIONS_SIMPLIFY)); + yajl_gen_config(*g, yajl_gen_validate_utf8, 1); + return true; +} + +yajl_val get_val(yajl_val tree, const char *name, yajl_type type) { + const char *path[] = { name, NULL }; + return yajl_tree_get(tree, path, type); +} + +void *safe_malloc(size_t size) { + void *ret = NULL; + if (size == 0) { + abort(); + } + ret = calloc(1, size); + if (ret == NULL) { + abort(); + } + return ret; +} + +int common_safe_double(const char *numstr, double *converted) { + char *err_str = NULL; + double d; + + if (!numstr) { + return -EINVAL; + } + + errno = 0; + d = strtod(numstr, &err_str); + if (errno > 0) { + return -errno; + } + + if (!err_str || err_str == numstr || *err_str != '\0') { + return -EINVAL; + } + + *converted = d; + return 0; +} + +int common_safe_uint8(const char *numstr, uint8_t *converted) { + char *err = NULL; + unsigned long int uli; + + if (!numstr) { + return -EINVAL; + } + + errno = 0; + uli = strtoul(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (!err || err == numstr || *err != '\0') { + return -EINVAL; + } + + if (uli > UINT8_MAX) { + return -ERANGE; + } + + *converted = (uint8_t)uli; + return 0; +} + +int common_safe_uint16(const char *numstr, uint16_t *converted) { + char *err = NULL; + unsigned long int uli; + + if (!numstr) { + return -EINVAL; + } + + errno = 0; + uli = strtoul(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (!err || err == numstr || *err != '\0') { + return -EINVAL; + } + + if (uli > UINT16_MAX) { + return -ERANGE; + } + + *converted = (uint16_t)uli; + return 0; +} + +int common_safe_uint32(const char *numstr, uint32_t *converted) { + char *err = NULL; + unsigned long long int ull; + + if (!numstr) { + return -EINVAL; + } + + errno = 0; + ull = strtoull(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (!err || err == numstr || *err != '\0') { + return -EINVAL; + } + + if (ull > UINT32_MAX) { + return -ERANGE; + } + + *converted = (uint32_t)ull; + return 0; +} + +int common_safe_uint64(const char *numstr, uint64_t *converted) { + char *err = NULL; + unsigned long long int ull; + + if (!numstr) { + return -EINVAL; + } + + errno = 0; + ull = strtoull(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (!err || err == numstr || *err != '\0') { + return -EINVAL; + } + + *converted = (uint64_t)ull; + return 0; +} + +int common_safe_uint(const char *numstr, unsigned int *converted) { + char *err = NULL; + unsigned long long int ull; + + if (!numstr) { + return -EINVAL; + } + + errno = 0; + ull = strtoull(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (!err || err == numstr || *err != '\0') { + return -EINVAL; + } + + if (ull > UINT_MAX) { + return -ERANGE; + } + + *converted = (unsigned int)ull; + return 0; +} + +int common_safe_int8(const char *numstr, int8_t *converted) { + char *err = NULL; + long int li; + + if (!numstr) { + return -EINVAL; + } + + errno = 0; + li = strtol(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (!err || err == numstr || *err != '\0') { + return -EINVAL; + } + + if (li > INT8_MAX || li < INT8_MIN) { + return -ERANGE; + } + + *converted = (int8_t)li; + return 0; +} + +int common_safe_int16(const char *numstr, int16_t *converted) { + char *err = NULL; + long int li; + + if (!numstr) { + return -EINVAL; + } + + errno = 0; + li = strtol(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (!err || err == numstr || *err != '\0') { + return -EINVAL; + } + + if (li > INT16_MAX || li < INT16_MIN) { + return -ERANGE; + } + + *converted = (int16_t)li; + return 0; +} + +int common_safe_int32(const char *numstr, int32_t *converted) { + char *err = NULL; + long long int lli; + + if (!numstr) { + return -EINVAL; + } + + errno = 0; + lli = strtol(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (!err || err == numstr || *err != '\0') { + return -EINVAL; + } + + if (lli > INT32_MAX || lli < INT32_MIN) { + return -ERANGE; + } + + *converted = (int32_t)lli; + return 0; +} + +int common_safe_int64(const char *numstr, int64_t *converted) { + char *err = NULL; + long long int lli; + + if (!numstr) { + return -EINVAL; + } + + errno = 0; + lli = strtoll(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (!err || err == numstr || *err != '\0') { + return -EINVAL; + } + + *converted = (int64_t)lli; + return 0; +} + +int common_safe_int(const char *numstr, int *converted) { + char *err = NULL; + long long int lli; + + if (!numstr) { + return -EINVAL; + } + + errno = 0; + lli = strtol(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (!err || err == numstr || *err != '\0') { + return -EINVAL; + } + + if (lli > INT_MAX || lli < INT_MIN) { + return -ERANGE; + } + + *converted = (int)lli; + return 0; +} + +char *safe_strdup(const char *src) +{ + char *dst = NULL; + + if (!src) { + return NULL; + } + + dst = strdup(src); + if (!dst) { + abort(); + } + + return dst; +} + + +yajl_gen_status gen_json_map_int_int(void *ctx, json_map_int_int *map, struct parser_context *ptx, parser_error *err) { + yajl_gen_status stat = yajl_gen_status_ok; + yajl_gen g = (yajl_gen) ctx; + size_t len = 0, i = 0; + if (map) { + len = map->len; + } + if (!len && !(ptx->options & GEN_OPTIONS_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 0); + } + stat = reformat_start_map(g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + + } + for (i = 0; i < len; i++) { + char numstr[MAX_NUM_STR_LEN]; + int nret; + nret = sprintf(numstr, "%lld", (long long int)map->keys[i]); + if (nret < 0) { + if (!*err && asprintf(err, "Error to print string") < 0) { + *(err) = safe_strdup("error allocating memory"); + } + return yajl_gen_in_error_state; + } + stat = reformat_string(g, numstr, strlen(numstr)); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = reformat_int(g, map->values[i]); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + } + + stat = reformat_end_map(g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + if (!len && !(ptx->options & GEN_OPTIONS_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 1); + } + return yajl_gen_status_ok; +} + +void free_json_map_int_int(json_map_int_int *map) { + if (map) { + size_t i; + for (i = 0; i < map->len; i++) { + // No need to free key for type int + // No need to free value for type int + } + free(map->keys); + map->keys = NULL; + free(map->values); + map->values = NULL; + free(map); + } +} +json_map_int_int *make_json_map_int_int(yajl_val src, struct parser_context *ctx, parser_error *err) { + json_map_int_int *ret = NULL; + if (src && YAJL_GET_OBJECT(src)) { + size_t i; + size_t len = YAJL_GET_OBJECT(src)->len; + ret = safe_malloc(sizeof(*ret)); + ret->len = len; + ret->keys = safe_malloc((len + 1) * sizeof(int)); + ret->values = safe_malloc((len + 1) * sizeof(int)); + for (i = 0; i < len; i++) { + const char *srckey = YAJL_GET_OBJECT(src)->keys[i]; + yajl_val srcval = YAJL_GET_OBJECT(src)->values[i]; + + if (srckey) { + int invalid; + invalid = common_safe_int(srckey, &(ret->keys[i])); + if (invalid) { + if (!*err && asprintf(err, "Invalid key '%s' with type 'int': %s", srckey, strerror(-invalid)) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_int_int(ret); + return NULL; + } + } + + if (srcval) { + int invalid; + if (!YAJL_IS_NUMBER(srcval)) { + if (!*err && asprintf(err, "Invalid value with type 'int' for key '%s'", srckey) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_int_int(ret); + return NULL; + } + invalid = common_safe_int(YAJL_GET_NUMBER(srcval), &(ret->values[i])); + if (invalid) { + if (!*err && asprintf(err, "Invalid value with type 'int' for key '%s': %s", srckey, strerror(-invalid)) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_int_int(ret); + return NULL; + } + } + } + } + return ret; +} +int append_json_map_int_int(json_map_int_int *map, int key, int val) { + size_t len; + int *keys; + int *vals; + + if (!map) { + return -1; + } + + if ((SIZE_MAX / sizeof(int) - 1) < map->len) { + return -1; + } + + len = map->len + 1; + keys = safe_malloc(len * sizeof(int)); + vals = safe_malloc(len * sizeof(int)); + + if (map->len) { + if (memcpy(keys, map->keys, map->len * sizeof(int)) != EOK) { + free(keys); + free(vals); + return -1; + } + if (memcpy(vals, map->values, map->len * sizeof(int)) != EOK) { + free(keys); + free(vals); + return -1; + } + } + free(map->keys); + map->keys = keys; + free(map->values); + map->values = vals; + map->keys[map->len] = key; + map->values[map->len] = val; + + map->len++; + return 0; +} + +yajl_gen_status gen_json_map_int_bool(void *ctx, json_map_int_bool *map, struct parser_context *ptx, parser_error *err) { + yajl_gen_status stat = yajl_gen_status_ok; + yajl_gen g = (yajl_gen) ctx; + size_t len = 0, i = 0; + if (map) { + len = map->len; + } + if (!len && !(ptx->options & GEN_OPTIONS_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 0); + } + stat = reformat_start_map(g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + + } + for (i = 0; i < len; i++) { + char numstr[MAX_NUM_STR_LEN]; + int nret; + nret = sprintf(numstr, "%lld", (long long int)map->keys[i]); + if (nret < 0) { + if (!*err && asprintf(err, "Error to print string") < 0) { + *(err) = safe_strdup("error allocating memory"); + } + return yajl_gen_in_error_state; + } + stat = reformat_string(g, numstr, strlen(numstr)); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = reformat_bool(g, map->values[i]); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + } + + stat = reformat_end_map(g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + if (!len && !(ptx->options & GEN_OPTIONS_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 1); + } + return yajl_gen_status_ok; +} + +void free_json_map_int_bool(json_map_int_bool *map) { + if (map) { + size_t i; + for (i = 0; i < map->len; i++) { + // No need to free key for type int + // No need to free value for type bool + } + free(map->keys); + map->keys = NULL; + free(map->values); + map->values = NULL; + free(map); + } +} +json_map_int_bool *make_json_map_int_bool(yajl_val src, struct parser_context *ctx, parser_error *err) { + json_map_int_bool *ret = NULL; + if (src && YAJL_GET_OBJECT(src)) { + size_t i; + size_t len = YAJL_GET_OBJECT(src)->len; + ret = safe_malloc(sizeof(*ret)); + ret->len = len; + ret->keys = safe_malloc((len + 1) * sizeof(int)); + ret->values = safe_malloc((len + 1) * sizeof(bool)); + for (i = 0; i < len; i++) { + const char *srckey = YAJL_GET_OBJECT(src)->keys[i]; + yajl_val srcval = YAJL_GET_OBJECT(src)->values[i]; + + if (srckey) { + int invalid; + invalid = common_safe_int(srckey, &(ret->keys[i])); + if (invalid) { + if (!*err && asprintf(err, "Invalid key '%s' with type 'int': %s", srckey, strerror(-invalid)) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_int_bool(ret); + return NULL; + } + } + + if (srcval) { + if (YAJL_IS_TRUE(srcval)) { + ret->values[i] = true; + } else if (YAJL_IS_FALSE(srcval)) { + ret->values[i] = false; + } else { + if (!*err && asprintf(err, "Invalid value with type 'bool' for key '%s'", srckey) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_int_bool(ret); + return NULL; + } + } + } + } + return ret; +} +int append_json_map_int_bool(json_map_int_bool *map, int key, bool val) { + size_t len; + int *keys; + bool *vals; + + if (!map) { + return -1; + } + + if ((SIZE_MAX / sizeof(int) - 1) < map->len || (SIZE_MAX / sizeof(bool) - 1) < map->len) { + return -1; + } + + len = map->len + 1; + keys = safe_malloc(len * sizeof(int)); + vals = safe_malloc(len * sizeof(bool)); + + if (map->len) { + if (memcpy(keys, map->keys, map->len * sizeof(int)) != EOK) { + free(keys); + free(vals); + return -1; + } + if (memcpy(vals, map->values, map->len * sizeof(bool)) != EOK) { + free(keys); + free(vals); + return -1; + } + } + free(map->keys); + map->keys = keys; + free(map->values); + map->values = vals; + map->keys[map->len] = key; + map->values[map->len] = val; + + map->len++; + return 0; +} + +yajl_gen_status gen_json_map_int_string(void *ctx, json_map_int_string *map, struct parser_context *ptx, parser_error *err) { + yajl_gen_status stat = yajl_gen_status_ok; + yajl_gen g = (yajl_gen) ctx; + size_t len = 0, i = 0; + if (map) { + len = map->len; + } + if (!len && !(ptx->options & GEN_OPTIONS_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 0); + } + stat = reformat_start_map(g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + + } + for (i = 0; i < len; i++) { + char numstr[MAX_NUM_STR_LEN]; + int nret; + nret = sprintf(numstr, "%lld", (long long int)map->keys[i]); + if (nret < 0) { + if (!*err && asprintf(err, "Error to print string") < 0) { + *(err) = safe_strdup("error allocating memory"); + } + return yajl_gen_in_error_state; + } + stat = reformat_string(g, numstr, strlen(numstr)); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = reformat_string(g, map->values[i], strlen(map->values[i]));; + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + } + + stat = reformat_end_map(g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + if (!len && !(ptx->options & GEN_OPTIONS_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 1); + } + return yajl_gen_status_ok; +} + +void free_json_map_int_string(json_map_int_string *map) { + if (map) { + size_t i; + for (i = 0; i < map->len; i++) { + // No need to free key for type int + free(map->values[i]); + map->values[i] = NULL; + } + free(map->keys); + map->keys = NULL; + free(map->values); + map->values = NULL; + free(map); + } +} +json_map_int_string *make_json_map_int_string(yajl_val src, struct parser_context *ctx, parser_error *err) { + json_map_int_string *ret = NULL; + if (src && YAJL_GET_OBJECT(src)) { + size_t i; + size_t len = YAJL_GET_OBJECT(src)->len; + ret = safe_malloc(sizeof(*ret)); + ret->len = len; + ret->keys = safe_malloc((len + 1) * sizeof(int)); + ret->values = safe_malloc((len + 1) * sizeof(char *)); + for (i = 0; i < len; i++) { + const char *srckey = YAJL_GET_OBJECT(src)->keys[i]; + yajl_val srcval = YAJL_GET_OBJECT(src)->values[i]; + + if (srckey) { + int invalid; + invalid = common_safe_int(srckey, &(ret->keys[i])); + if (invalid) { + if (!*err && asprintf(err, "Invalid key '%s' with type 'int': %s", srckey, strerror(-invalid)) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_int_string(ret); + return NULL; + } + } + + if (srcval) { + if (!YAJL_IS_STRING(srcval)) { + if (!*err && asprintf(err, "Invalid value with type 'string' for key '%s'", srckey) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_int_string(ret); + return NULL; + } + char *str = YAJL_GET_STRING(srcval); + ret->values[i] = safe_strdup(str ? str : ""); + } + } + } + return ret; +} +int append_json_map_int_string(json_map_int_string *map, int key, const char *val) { + size_t len; + int *keys; + char **vals; + + if (!map) { + return -1; + } + + if ((SIZE_MAX / sizeof(int) - 1) < map->len || (SIZE_MAX / sizeof(char *) - 1) < map->len) { + return -1; + } + + len = map->len + 1; + keys = safe_malloc(len * sizeof(int)); + vals = safe_malloc(len * sizeof(char *)); + + if (map->len) { + if (memcpy(keys, map->keys, map->len * sizeof(int)) != EOK) { + free(keys); + free(vals); + return -1; + } + if (memcpy(vals, map->values, map->len * sizeof(char *)) != EOK) { + free(keys); + free(vals); + return -1; + } + } + free(map->keys); + map->keys = keys; + free(map->values); + map->values = vals; + map->keys[map->len] = key; + map->values[map->len] = safe_strdup(val ? val : ""); + + map->len++; + return 0; +} + +yajl_gen_status gen_json_map_string_int(void *ctx, json_map_string_int *map, struct parser_context *ptx, parser_error *err) { + yajl_gen_status stat = yajl_gen_status_ok; + yajl_gen g = (yajl_gen) ctx; + size_t len = 0, i = 0; + if (map) { + len = map->len; + } + if (!len && !(ptx->options & GEN_OPTIONS_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 0); + } + stat = reformat_start_map(g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + + } + for (i = 0; i < len; i++) { + stat = reformat_string(g, map->keys[i], strlen(map->keys[i])); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = reformat_int(g, map->values[i]); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + } + + stat = reformat_end_map(g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + if (!len && !(ptx->options & GEN_OPTIONS_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 1); + } + return yajl_gen_status_ok; +} + +void free_json_map_string_int(json_map_string_int *map) { + if (map) { + size_t i; + for (i = 0; i < map->len; i++) { + free(map->keys[i]); + map->keys[i] = NULL; + // No need to free value for type int + } + free(map->keys); + map->keys = NULL; + free(map->values); + map->values = NULL; + free(map); + } +} +json_map_string_int *make_json_map_string_int(yajl_val src, struct parser_context *ctx, parser_error *err) { + json_map_string_int *ret = NULL; + if (src && YAJL_GET_OBJECT(src)) { + size_t i; + size_t len = YAJL_GET_OBJECT(src)->len; + ret = safe_malloc(sizeof(*ret)); + ret->len = len; + ret->keys = safe_malloc((len + 1) * sizeof(char *)); + ret->values = safe_malloc((len + 1) * sizeof(int)); + for (i = 0; i < len; i++) { + const char *srckey = YAJL_GET_OBJECT(src)->keys[i]; + yajl_val srcval = YAJL_GET_OBJECT(src)->values[i]; + ret->keys[i] = safe_strdup(srckey ? srckey : ""); + + if (srcval) { + int invalid; + if (!YAJL_IS_NUMBER(srcval)) { + if (!*err && asprintf(err, "Invalid value with type 'int' for key '%s'", srckey) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_string_int(ret); + return NULL; + } + invalid = common_safe_int(YAJL_GET_NUMBER(srcval), &(ret->values[i])); + if (invalid) { + if (!*err && asprintf(err, "Invalid value with type 'int' for key '%s': %s", srckey, strerror(-invalid)) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_string_int(ret); + return NULL; + } + } + } + } + return ret; +} +int append_json_map_string_int(json_map_string_int *map, const char *key, int val) { + size_t len; + char **keys; + int *vals; + + if (!map) { + return -1; + } + + if ((SIZE_MAX / sizeof(char *) - 1) < map->len || (SIZE_MAX / sizeof(int) - 1) < map->len) { + return -1; + } + + len = map->len + 1; + keys = safe_malloc(len * sizeof(char *)); + vals = safe_malloc(len * sizeof(int)); + + if (map->len) { + if (memcpy(keys, map->keys, map->len * sizeof(char *)) != EOK) { + free(keys); + free(vals); + return -1; + } + if (memcpy(vals, map->values, map->len * sizeof(int)) != EOK) { + free(keys); + free(vals); + return -1; + } + } + free(map->keys); + map->keys = keys; + free(map->values); + map->values = vals; + map->keys[map->len] = safe_strdup(key ? key : ""); + map->values[map->len] = val; + + map->len++; + return 0; +} + +yajl_gen_status gen_json_map_string_bool(void *ctx, json_map_string_bool *map, struct parser_context *ptx, parser_error *err) { + yajl_gen_status stat = yajl_gen_status_ok; + yajl_gen g = (yajl_gen) ctx; + size_t len = 0, i = 0; + if (map) { + len = map->len; + } + if (!len && !(ptx->options & GEN_OPTIONS_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 0); + } + stat = reformat_start_map(g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + + } + for (i = 0; i < len; i++) { + stat = reformat_string(g, map->keys[i], strlen(map->keys[i])); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = reformat_bool(g, map->values[i]); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + } + + stat = reformat_end_map(g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + if (!len && !(ptx->options & GEN_OPTIONS_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 1); + } + return yajl_gen_status_ok; +} + +void free_json_map_string_bool(json_map_string_bool *map) { + if (map) { + size_t i; + for (i = 0; i < map->len; i++) { + free(map->keys[i]); + map->keys[i] = NULL; + // No need to free value for type bool + } + free(map->keys); + map->keys = NULL; + free(map->values); + map->values = NULL; + free(map); + } +} +json_map_string_bool *make_json_map_string_bool(yajl_val src, struct parser_context *ctx, parser_error *err) { + json_map_string_bool *ret = NULL; + if (src && YAJL_GET_OBJECT(src)) { + size_t i; + size_t len = YAJL_GET_OBJECT(src)->len; + ret = safe_malloc(sizeof(*ret)); + ret->len = len; + ret->keys = safe_malloc((len + 1) * sizeof(char *)); + ret->values = safe_malloc((len + 1) * sizeof(bool)); + for (i = 0; i < len; i++) { + const char *srckey = YAJL_GET_OBJECT(src)->keys[i]; + yajl_val srcval = YAJL_GET_OBJECT(src)->values[i]; + ret->keys[i] = safe_strdup(srckey ? srckey : ""); + + if (srcval) { + if (YAJL_IS_TRUE(srcval)) { + ret->values[i] = true; + } else if (YAJL_IS_FALSE(srcval)) { + ret->values[i] = false; + } else { + if (!*err && asprintf(err, "Invalid value with type 'bool' for key '%s'", srckey) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_string_bool(ret); + return NULL; + } + } + } + } + return ret; +} +int append_json_map_string_bool(json_map_string_bool *map, const char *key, bool val) { + size_t len; + char **keys; + bool *vals; + + if (!map) { + return -1; + } + + if ((SIZE_MAX / sizeof(char *) - 1) < map->len || (SIZE_MAX / sizeof(bool) - 1) < map->len) { + return -1; + } + + len = map->len + 1; + keys = safe_malloc(len * sizeof(char *)); + vals = safe_malloc(len * sizeof(bool)); + + if (map->len) { + if (memcpy(keys, map->keys, map->len * sizeof(char *)) != EOK) { + free(keys); + free(vals); + return -1; + } + if (memcpy(vals, map->values, map->len * sizeof(bool)) != EOK) { + free(keys); + free(vals); + return -1; + } + } + free(map->keys); + map->keys = keys; + free(map->values); + map->values = vals; + map->keys[map->len] = safe_strdup(key ? key : ""); + map->values[map->len] = val; + + map->len++; + return 0; +} + +yajl_gen_status gen_json_map_string_string(void *ctx, json_map_string_string *map, struct parser_context *ptx, parser_error *err) { + yajl_gen_status stat = yajl_gen_status_ok; + yajl_gen g = (yajl_gen) ctx; + size_t len = 0, i = 0; + if (map) { + len = map->len; + } + if (!len && !(ptx->options & GEN_OPTIONS_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 0); + } + stat = reformat_start_map(g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + + } + for (i = 0; i < len; i++) { + stat = reformat_string(g, map->keys[i], strlen(map->keys[i])); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = reformat_string(g, map->values[i], strlen(map->values[i]));; + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + } + + stat = reformat_end_map(g); + if (yajl_gen_status_ok != stat) { + GEN_SET_ERROR_AND_RETURN(stat, err); + } + if (!len && !(ptx->options & GEN_OPTIONS_SIMPLIFY)) { + yajl_gen_config(g, yajl_gen_beautify, 1); + } + return yajl_gen_status_ok; +} + +void free_json_map_string_string(json_map_string_string *map) { + if (map) { + size_t i; + for (i = 0; i < map->len; i++) { + free(map->keys[i]); + map->keys[i] = NULL; + free(map->values[i]); + map->values[i] = NULL; + } + free(map->keys); + map->keys = NULL; + free(map->values); + map->values = NULL; + free(map); + } +} +json_map_string_string *make_json_map_string_string(yajl_val src, struct parser_context *ctx, parser_error *err) { + json_map_string_string *ret = NULL; + if (src && YAJL_GET_OBJECT(src)) { + size_t i; + size_t len = YAJL_GET_OBJECT(src)->len; + ret = safe_malloc(sizeof(*ret)); + ret->len = len; + ret->keys = safe_malloc((len + 1) * sizeof(char *)); + ret->values = safe_malloc((len + 1) * sizeof(char *)); + for (i = 0; i < len; i++) { + const char *srckey = YAJL_GET_OBJECT(src)->keys[i]; + yajl_val srcval = YAJL_GET_OBJECT(src)->values[i]; + ret->keys[i] = safe_strdup(srckey ? srckey : ""); + + if (srcval) { + if (!YAJL_IS_STRING(srcval)) { + if (!*err && asprintf(err, "Invalid value with type 'string' for key '%s'", srckey) < 0) { + *(err) = safe_strdup("error allocating memory"); + } + free_json_map_string_string(ret); + return NULL; + } + char *str = YAJL_GET_STRING(srcval); + ret->values[i] = safe_strdup(str ? str : ""); + } + } + } + return ret; +} +int append_json_map_string_string(json_map_string_string *map, const char *key, const char *val) { + size_t len; + char **keys; + char **vals; + + if (!map) { + return -1; + } + + if ((SIZE_MAX / sizeof(char *) - 1) < map->len) { + return -1; + } + + len = map->len + 1; + keys = safe_malloc(len * sizeof(char *)); + vals = safe_malloc(len * sizeof(char *)); + + if (map->len) { + if (memcpy(keys, map->keys, map->len * sizeof(char *)) != EOK) { + free(keys); + free(vals); + return -1; + } + if (memcpy(vals, map->values, map->len * sizeof(char *)) != EOK) { + free(keys); + free(vals); + return -1; + } + } + free(map->keys); + map->keys = keys; + free(map->values); + map->values = vals; + map->keys[map->len] = safe_strdup(key ? key : ""); + map->values[map->len] = safe_strdup(val ? val : ""); + + map->len++; + return 0; +} diff --git a/src/lxc/json/json_common.h b/src/lxc/json/json_common.h new file mode 100755 index 00000000..904fe3cd --- /dev/null +++ b/src/lxc/json/json_common.h @@ -0,0 +1,185 @@ +// Auto generated file. Do not edit! +#ifndef _JSON_COMMON_H +#define _JSON_COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include "securec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +# undef linux + +//options to report error if there is unknown key found in json +# define PARSE_OPTIONS_STRICT 0x01 +//options to generate all key and value +# define GEN_OPTIONS_ALLKEYVALUE 0x02 +//options to generate simplify(no indent) json string +# define GEN_OPTIONS_SIMPLIFY 0x04 + +#define GEN_SET_ERROR_AND_RETURN(stat, err) { \ + if (!*(err)) {\ + if (asprintf(err, "%s: %s: %d: error generating json, errcode: %d", __FILE__, __func__, __LINE__, stat) < 0) { \ + *(err) = safe_strdup("error allocating memory"); \ + } \ + }\ + return stat; \ + } + +typedef char *parser_error; + +struct parser_context { + unsigned int options; + FILE *stderr; +}; + +yajl_gen_status reformat_number(void *ctx, const char *str, size_t len); + +yajl_gen_status reformat_uint(void *ctx, long long unsigned int num); + +yajl_gen_status reformat_int(void *ctx, long long int num); + +yajl_gen_status reformat_double(void *ctx, double num); + +yajl_gen_status reformat_string(void *ctx, const char *str, size_t len); + +yajl_gen_status reformat_null(void *ctx); + +yajl_gen_status reformat_bool(void *ctx, int boolean); + +yajl_gen_status reformat_map_key(void *ctx, const char *str, size_t len); + +yajl_gen_status reformat_start_map(void *ctx); + +yajl_gen_status reformat_end_map(void *ctx); + +yajl_gen_status reformat_start_array(void *ctx); + +yajl_gen_status reformat_end_array(void *ctx); + +bool json_gen_init(yajl_gen *g, struct parser_context *ctx); + +yajl_val get_val(yajl_val tree, const char *name, yajl_type type); + +void *safe_malloc(size_t size); + +int common_safe_double(const char *numstr, double *converted); + +int common_safe_uint8(const char *numstr, uint8_t *converted); + +int common_safe_uint16(const char *numstr, uint16_t *converted); + +int common_safe_uint32(const char *numstr, uint32_t *converted); + +int common_safe_uint64(const char *numstr, uint64_t *converted); + +int common_safe_uint(const char *numstr, unsigned int *converted); + +int common_safe_int8(const char *numstr, int8_t *converted); + +int common_safe_int16(const char *numstr, int16_t *converted); + +int common_safe_int32(const char *numstr, int32_t *converted); + +int common_safe_int64(const char *numstr, int64_t *converted); + +int common_safe_int(const char *numstr, int *converted); + +char *safe_strdup(const char *src); + +typedef struct { + int *keys; + int *values; + size_t len; +} json_map_int_int; + +void free_json_map_int_int(json_map_int_int *map); + +json_map_int_int *make_json_map_int_int(yajl_val src, struct parser_context *ctx, parser_error *err); + +yajl_gen_status gen_json_map_int_int(void *ctx, json_map_int_int *map, struct parser_context *ptx, parser_error *err); + +int append_json_map_int_int(json_map_int_int *map, int key, int val); + +typedef struct { + int *keys; + bool *values; + size_t len; +} json_map_int_bool; + +void free_json_map_int_bool(json_map_int_bool *map); + +json_map_int_bool *make_json_map_int_bool(yajl_val src, struct parser_context *ctx, parser_error *err); + +yajl_gen_status gen_json_map_int_bool(void *ctx, json_map_int_bool *map, struct parser_context *ptx, parser_error *err); + +int append_json_map_int_bool(json_map_int_bool *map, int key, bool val); + +typedef struct { + int *keys; + char **values; + size_t len; +} json_map_int_string; + +void free_json_map_int_string(json_map_int_string *map); + +json_map_int_string *make_json_map_int_string(yajl_val src, struct parser_context *ctx, parser_error *err); + +yajl_gen_status gen_json_map_int_string(void *ctx, json_map_int_string *map, struct parser_context *ptx, parser_error *err); + +int append_json_map_int_string(json_map_int_string *map, int key, const char *val); + +typedef struct { + char **keys; + int *values; + size_t len; +} json_map_string_int; + +void free_json_map_string_int(json_map_string_int *map); + +json_map_string_int *make_json_map_string_int(yajl_val src, struct parser_context *ctx, parser_error *err); + +yajl_gen_status gen_json_map_string_int(void *ctx, json_map_string_int *map, struct parser_context *ptx, parser_error *err); + +int append_json_map_string_int(json_map_string_int *map, const char *key, int val); + +typedef struct { + char **keys; + bool *values; + size_t len; +} json_map_string_bool; + +void free_json_map_string_bool(json_map_string_bool *map); + +json_map_string_bool *make_json_map_string_bool(yajl_val src, struct parser_context *ctx, parser_error *err); + +yajl_gen_status gen_json_map_string_bool(void *ctx, json_map_string_bool *map, struct parser_context *ptx, parser_error *err); + +int append_json_map_string_bool(json_map_string_bool *map, const char *key, bool val); + +typedef struct { + char **keys; + char **values; + size_t len; +} json_map_string_string; + +void free_json_map_string_string(json_map_string_string *map); + +json_map_string_string *make_json_map_string_string(yajl_val src, struct parser_context *ctx, parser_error *err); + +yajl_gen_status gen_json_map_string_string(void *ctx, json_map_string_string *map, struct parser_context *ptx, parser_error *err); + +int append_json_map_string_string(json_map_string_string *map, const char *key, const char *val); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/lxc/json/oci_runtime_hooks.c b/src/lxc/json/oci_runtime_hooks.c new file mode 100644 index 00000000..3aa134ef --- /dev/null +++ b/src/lxc/json/oci_runtime_hooks.c @@ -0,0 +1,53 @@ +/****************************************************************************** + * Copyright (C), 1988-1999, Huawei Tech. Co., Ltd. + * FileName: oci_runtime_hooks.c + * Author: maoweiyong Version: 0.1 Date: 2018-11-07 + * Explanation: provide oci runtime hooks functions + ******************************************************************************/ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include "oci_runtime_hooks.h" + +#include "log.h" +#include "utils.h" + +#define PARSE_ERR_BUFFER_SIZE 1024 + +oci_runtime_spec_hooks *oci_runtime_spec_hooks_parse_file(const char *filename, + struct parser_context *ctx, parser_error *err) +{ + yajl_val tree; + size_t filesize; + + if (!filename || !err) { + return NULL; + } + *err = NULL; + struct parser_context tmp_ctx; + if (!ctx) { + ctx = &tmp_ctx; + memset(&tmp_ctx, 0, sizeof(tmp_ctx)); + } + char *content = read_file(filename, &filesize); + char errbuf[PARSE_ERR_BUFFER_SIZE]; + if (content == NULL) { + if (asprintf(err, "cannot read the file: %s", filename) < 0) { + *err = strdup("error allocating memory"); + } + return NULL; + } + tree = yajl_tree_parse(content, errbuf, sizeof(errbuf)); + free(content); + if (tree == NULL) { + if (asprintf(err, "cannot parse the file: %s", errbuf) < 0) { + *err = strdup("error allocating memory"); + } + return NULL; + } + oci_runtime_spec_hooks *ptr = make_oci_runtime_spec_hooks(tree, ctx, + err); + yajl_tree_free(tree); + return ptr; +} diff --git a/src/lxc/json/oci_runtime_hooks.h b/src/lxc/json/oci_runtime_hooks.h new file mode 100644 index 00000000..bf570c9e --- /dev/null +++ b/src/lxc/json/oci_runtime_hooks.h @@ -0,0 +1,15 @@ +/****************************************************************************** + * Copyright (C), 1988-1999, Huawei Tech. Co., Ltd. + * FileName: oci_runtime_hooks.h + * Author: tanyifeng Version: 0.1 Date: 2018-11-08 + * Explanation: provide container oci runtime hooks function definition + ******************************************************************************/ +#ifndef _CONTAINER_HOOKS_H +# define _CONTAINER_HOOKS_H + +# include "oci_runtime_spec.h" + +oci_runtime_spec_hooks *oci_runtime_spec_hooks_parse_file(const char *filename, + struct parser_context *ctx, parser_error *err); + +#endif diff --git a/src/lxc/json/oci_runtime_spec.c b/src/lxc/json/oci_runtime_spec.c new file mode 100644 index 00000000..1f6073c9 --- /dev/null +++ b/src/lxc/json/oci_runtime_spec.c @@ -0,0 +1,196 @@ +// Generated from spec.json. Do not edit! +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include +#include "securec.h" +#include "oci_runtime_spec.h" + +oci_runtime_spec_hooks *make_oci_runtime_spec_hooks(yajl_val tree, struct parser_context *ctx, parser_error *err) { + oci_runtime_spec_hooks *ret = NULL; + *err = 0; + if (tree == NULL) + return ret; + ret = safe_malloc(sizeof(*ret)); + { + yajl_val tmp = get_val(tree, "prestart", yajl_t_array); + if (tmp && YAJL_GET_ARRAY(tmp)) { + size_t i; + ret->prestart_len = YAJL_GET_ARRAY(tmp)->len; + ret->prestart = safe_malloc((YAJL_GET_ARRAY(tmp)->len + 1) * sizeof(*ret->prestart)); + for (i = 0; i < YAJL_GET_ARRAY(tmp)->len; i++) { + yajl_val val = YAJL_GET_ARRAY(tmp)->values[i]; + ret->prestart[i] = make_defs_hook(val, ctx, err); + if (ret->prestart[i] == NULL) { + free_oci_runtime_spec_hooks(ret); + return NULL; + } + } + } + } + { + yajl_val tmp = get_val(tree, "poststart", yajl_t_array); + if (tmp && YAJL_GET_ARRAY(tmp)) { + size_t i; + ret->poststart_len = YAJL_GET_ARRAY(tmp)->len; + ret->poststart = safe_malloc((YAJL_GET_ARRAY(tmp)->len + 1) * sizeof(*ret->poststart)); + for (i = 0; i < YAJL_GET_ARRAY(tmp)->len; i++) { + yajl_val val = YAJL_GET_ARRAY(tmp)->values[i]; + ret->poststart[i] = make_defs_hook(val, ctx, err); + if (ret->poststart[i] == NULL) { + free_oci_runtime_spec_hooks(ret); + return NULL; + } + } + } + } + { + yajl_val tmp = get_val(tree, "poststop", yajl_t_array); + if (tmp && YAJL_GET_ARRAY(tmp)) { + size_t i; + ret->poststop_len = YAJL_GET_ARRAY(tmp)->len; + ret->poststop = safe_malloc((YAJL_GET_ARRAY(tmp)->len + 1) * sizeof(*ret->poststop)); + for (i = 0; i < YAJL_GET_ARRAY(tmp)->len; i++) { + yajl_val val = YAJL_GET_ARRAY(tmp)->values[i]; + ret->poststop[i] = make_defs_hook(val, ctx, err); + if (ret->poststop[i] == NULL) { + free_oci_runtime_spec_hooks(ret); + return NULL; + } + } + } + } + + if (tree->type == yajl_t_object && (ctx->options & PARSE_OPTIONS_STRICT)) { + int i; + for (i = 0; i < tree->u.object.len; i++) + if (strcmp(tree->u.object.keys[i], "prestart") && + strcmp(tree->u.object.keys[i], "poststart") && + strcmp(tree->u.object.keys[i], "poststop")) { + if (ctx->stderr > 0) + fprintf(ctx->stderr, "WARNING: unknown key found: %s\n", tree->u.object.keys[i]); + } + } + return ret; +} + +void free_oci_runtime_spec_hooks(oci_runtime_spec_hooks *ptr) { + if (!ptr) + return; + if (ptr->prestart) { + size_t i; + for (i = 0; i < ptr->prestart_len; i++) + if (ptr->prestart[i]) { + free_defs_hook(ptr->prestart[i]); + ptr->prestart[i] = NULL; + } + free(ptr->prestart); + ptr->prestart = NULL; + } + if (ptr->poststart) { + size_t i; + for (i = 0; i < ptr->poststart_len; i++) + if (ptr->poststart[i]) { + free_defs_hook(ptr->poststart[i]); + ptr->poststart[i] = NULL; + } + free(ptr->poststart); + ptr->poststart = NULL; + } + if (ptr->poststop) { + size_t i; + for (i = 0; i < ptr->poststop_len; i++) + if (ptr->poststop[i]) { + free_defs_hook(ptr->poststop[i]); + ptr->poststop[i] = NULL; + } + free(ptr->poststop); + ptr->poststop = NULL; + } + free(ptr); +} + +yajl_gen_status gen_oci_runtime_spec_hooks(yajl_gen g, oci_runtime_spec_hooks *ptr, struct parser_context *ctx, parser_error *err) { + yajl_gen_status stat = yajl_gen_status_ok; + *err = 0; + stat = reformat_start_map(g); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if ((ctx->options & GEN_OPTIONS_ALLKEYVALUE) ||(ptr && ptr->prestart)) { + size_t len = 0, i; + stat = reformat_map_key(g, "prestart", strlen("prestart")); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if (ptr && ptr->prestart) { + len = ptr->prestart_len; + } + if (!len && !(ctx->options & GEN_OPTIONS_SIMPLIFY)) + yajl_gen_config(g, yajl_gen_beautify, 0); + stat = reformat_start_array(g); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + for (i = 0; i < len; i++) { + stat = gen_defs_hook(g, ptr->prestart[i], ctx, err); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = reformat_end_array(g); + if (!len && !(ctx->options & GEN_OPTIONS_SIMPLIFY)) + yajl_gen_config(g, yajl_gen_beautify, 1); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + } + if ((ctx->options & GEN_OPTIONS_ALLKEYVALUE) ||(ptr && ptr->poststart)) { + size_t len = 0, i; + stat = reformat_map_key(g, "poststart", strlen("poststart")); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if (ptr && ptr->poststart) { + len = ptr->poststart_len; + } + if (!len && !(ctx->options & GEN_OPTIONS_SIMPLIFY)) + yajl_gen_config(g, yajl_gen_beautify, 0); + stat = reformat_start_array(g); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + for (i = 0; i < len; i++) { + stat = gen_defs_hook(g, ptr->poststart[i], ctx, err); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = reformat_end_array(g); + if (!len && !(ctx->options & GEN_OPTIONS_SIMPLIFY)) + yajl_gen_config(g, yajl_gen_beautify, 1); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + } + if ((ctx->options & GEN_OPTIONS_ALLKEYVALUE) ||(ptr && ptr->poststop)) { + size_t len = 0, i; + stat = reformat_map_key(g, "poststop", strlen("poststop")); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if (ptr && ptr->poststop) { + len = ptr->poststop_len; + } + if (!len && !(ctx->options & GEN_OPTIONS_SIMPLIFY)) + yajl_gen_config(g, yajl_gen_beautify, 0); + stat = reformat_start_array(g); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + for (i = 0; i < len; i++) { + stat = gen_defs_hook(g, ptr->poststop[i], ctx, err); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = reformat_end_array(g); + if (!len && !(ctx->options & GEN_OPTIONS_SIMPLIFY)) + yajl_gen_config(g, yajl_gen_beautify, 1); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + } + stat = reformat_end_map(g); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + return yajl_gen_status_ok; +} diff --git a/src/lxc/json/oci_runtime_spec.h b/src/lxc/json/oci_runtime_spec.h new file mode 100644 index 00000000..ef3f1619 --- /dev/null +++ b/src/lxc/json/oci_runtime_spec.h @@ -0,0 +1,37 @@ +// Generated from spec.json. Do not edit! +#ifndef OCI_RUNTIME_SPEC_SCHEMA_H +#define OCI_RUNTIME_SPEC_SCHEMA_H + +#include +#include +#include "json_common.h" +#include "defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + defs_hook **prestart; + size_t prestart_len; + + defs_hook **poststart; + size_t poststart_len; + + defs_hook **poststop; + size_t poststop_len; + +} +oci_runtime_spec_hooks; + +void free_oci_runtime_spec_hooks(oci_runtime_spec_hooks *ptr); + +oci_runtime_spec_hooks *make_oci_runtime_spec_hooks(yajl_val tree, struct parser_context *ctx, parser_error *err); + +yajl_gen_status gen_oci_runtime_spec_hooks(yajl_gen g, oci_runtime_spec_hooks *ptr, struct parser_context *ctx, parser_error *err); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/lxc/json/read-file.c b/src/lxc/json/read-file.c new file mode 100644 index 00000000..ad0eda1e --- /dev/null +++ b/src/lxc/json/read-file.c @@ -0,0 +1,94 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "read-file.h" + +#ifndef O_CLOEXEC +#define O_CLOEXEC 02000000 +#endif + +char *fread_file(FILE *stream, size_t *length) +{ + char *buf = NULL, *tmpbuf = NULL; + size_t off = 0; + + while (1) { + size_t ret, newsize; + + newsize = off + BUFSIZ + 1; + tmpbuf = (char *)calloc(1, newsize); + if (tmpbuf == NULL) { + goto out; + } + + if (buf) { + memcpy(tmpbuf, buf, off); + + memset(buf, 0, off); + + free(buf); + } + + buf = tmpbuf; + ret = fread(buf + off, 1, BUFSIZ, stream); + if (!ret && ferror(stream)) { + tmpbuf = NULL; + goto out; + } + if (ret < BUFSIZ || feof(stream)) { + *length = off + ret + 1; + buf[*length - 1] = '\0'; + return buf; + } + off += BUFSIZ; + } +out: + if (buf) { + free(buf); + } + if (tmpbuf) { + free(tmpbuf); + } + return NULL; + +} + +char *read_file(const char *path, size_t *length) +{ + char *buf = NULL; + char rpath[PATH_MAX + 1] = {0}; + int fd, tmperrno; + FILE *fp; + + if (!path || !length) { + return NULL; + } + + if (strlen(path) > PATH_MAX || NULL == realpath(path, rpath)) { + return NULL; + } + + fd = open(rpath, O_RDONLY | O_CLOEXEC, 0640); + if (fd < 0) { + return NULL; + } + + fp = fdopen(fd, "r"); + tmperrno = errno; + if (!fp) { + close(fd); + errno = tmperrno; + return NULL; + } + + buf = fread_file(fp, length); + fclose(fp); + return buf; +} diff --git a/src/lxc/json/read-file.h b/src/lxc/json/read-file.h new file mode 100644 index 00000000..5d6e0eb6 --- /dev/null +++ b/src/lxc/json/read-file.h @@ -0,0 +1,11 @@ +#ifndef READ_FILE_H +#define READ_FILE_H + +#include +#include + +extern char *fread_file(FILE *stream, size_t *length); + +extern char *read_file(const char *path, size_t *length); + +#endif diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index 31f4819e..68134d87 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -271,6 +271,9 @@ static void lxc_container_free(struct lxc_container *c) free(c->configfile); c->configfile = NULL; + free(c->ocihookfile); + c->ocihookfile = NULL; + free(c->error_string); c->error_string = NULL; @@ -612,6 +615,30 @@ static bool load_config_locked(struct lxc_container *c, const char *fname) return true; } +static bool load_ocihooks_locked(struct lxc_container *c) +{ + parser_error err = NULL; + oci_runtime_spec_hooks *hooks; + + if (!c->lxc_conf) + c->lxc_conf = lxc_conf_init(); + + if (!c->lxc_conf) + return false; + + hooks = oci_runtime_spec_hooks_parse_file(c->ocihookfile, NULL, &err); + if (!hooks) { + fprintf(stderr, "parse oci hooks config failed: %s\n", err); + free(err); + return true; + } + c->lxc_conf->ocihooks = hooks; + + if (err) + free(err); + return true; +} + static bool do_lxcapi_load_config(struct lxc_container *c, const char *alt_file) { int lret; @@ -645,6 +672,9 @@ static bool do_lxcapi_load_config(struct lxc_container *c, const char *alt_file) ret = load_config_locked(c, fname); + if (ret && file_exists(c->ocihookfile)) + ret = load_ocihooks_locked(c); + if (need_disklock) container_disk_unlock(c); else @@ -3242,6 +3272,37 @@ static bool set_config_filename(struct lxc_container *c) return true; } +/* + * isulad: set oci hook file path + * */ +static bool set_oci_hook_config_filename(struct lxc_container *c) +{ +#define OCI_HOOK_JSON_FILE_NAME "ocihooks.json" + char *newpath; + int len, ret; + + if (!c->config_path) + return false; + + /* $lxc_path + "/" + c->name + "/" + "config" + '\0' */ + len = strlen(c->config_path) + strlen(c->name) + strlen(OCI_HOOK_JSON_FILE_NAME) + 3; + newpath = malloc(len); + if (!newpath) + return false; + + ret = snprintf(newpath, len, "%s/%s/%s", c->config_path, c->name, OCI_HOOK_JSON_FILE_NAME); + if (ret < 0 || ret >= len) { + fprintf(stderr, "Error printing out config file name\n"); + free(newpath); + return false; + } + + free(c->ocihookfile); + c->ocihookfile = newpath; + + return true; +} + static bool do_lxcapi_set_config_path(struct lxc_container *c, const char *path) { char *p; @@ -5081,6 +5142,11 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath goto err; } + if (!set_oci_hook_config_filename(c)) { + fprintf(stderr, "Error allocating oci hooks file pathname\n"); + goto err; + } + if (file_exists(c->configfile) && !lxcapi_load_config(c, NULL)) { fprintf(stderr, "Failed to load config for %s\n", name); goto err; diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h index 503038af..5d23cc7e 100644 --- a/src/lxc/lxccontainer.h +++ b/src/lxc/lxccontainer.h @@ -75,6 +75,13 @@ struct lxc_container { */ char *configfile; + /*! + * \private + * isulad: support oci hook from json file + * full path of json file + * */ + char *ocihookfile; + /*! * \private * File to store pid. diff --git a/src/lxc/start.c b/src/lxc/start.c index b13326ce..63f5af88 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -1887,6 +1887,16 @@ static int lxc_spawn(struct lxc_handler *handler) goto out_delete_net; } + /* isulad: Run oci prestart hook at here */ + char* oci_hook_args[1]; + oci_hook_args[0] = alloca(strlen(lxcpath) + 1); + (void)strlcpy(oci_hook_args[0], lxcpath, strlen(lxcpath)); + ret = run_lxc_hooks(name, "oci-prestart", conf, oci_hook_args); + if (ret < 0) { + ERROR("Failed to run oci prestart hooks"); + goto out_delete_net; + } + /* Tell the child to complete its initialization and wait for it to exec * or return an error. (The child will never return * LXC_SYNC_READY_START+1. It will either close the sync pipe, causing @@ -1922,6 +1932,13 @@ static int lxc_spawn(struct lxc_handler *handler) if (ret < 0) goto out_abort; + /* isulad: Run oci prestart hook at here */ + ret = run_lxc_hooks(name, "oci-poststart", conf, oci_hook_args); + if (ret < 0) { + ERROR("Failed to run oci poststart hooks"); + goto out_delete_net; + } + ret = lxc_set_state(name, handler, RUNNING); if (ret < 0) { ERROR("Failed to set state to \"%s\"", lxc_state2str(RUNNING)); diff --git a/src/lxc/utils.c b/src/lxc/utils.c index 8ec9f461..d1a22f73 100644 --- a/src/lxc/utils.c +++ b/src/lxc/utils.c @@ -1864,11 +1864,11 @@ static int lxc_file2str(const char *filename, char ret[], int cap) int fd, num_read; if ((fd = open(filename, O_RDONLY | O_CLOEXEC)) == -1) - return -1;/*lint !e960*/ + return -1; if ((num_read = read(fd, ret, cap - 1)) <= 0) - num_read = -1;/*lint !e960*/ + num_read = -1; else - ret[num_read] = 0;/*lint !e613*//*lint !e960*/ + ret[num_read] = 0; close(fd); return num_read; @@ -1886,16 +1886,16 @@ static proc_t *lxc_stat2proc(char *S) char *tmp = NULL; if (!S) - return NULL;/*lint !e960*/ + return NULL; - tmp = strrchr(S, ')'); /* split into "PID (cmd" and "" *//*lint !e586*/ + tmp = strrchr(S, ')'); /* split into "PID (cmd" and "" */ if (!tmp) - return NULL;/*lint !e960*/ + return NULL; *tmp = '\0'; /* replace trailing ')' with NUL */ P = malloc(sizeof(proc_t)); if (!P) - return NULL;/*lint !e960*/ + return NULL; memset(P, 0x00, sizeof(proc_t)); /* parse these two strings separately, skipping the leading "(". */ @@ -1909,9 +1909,9 @@ static proc_t *lxc_stat2proc(char *S) "%c " "%d %d %d %d %d " "%lu %lu %lu %lu %lu " - "%Lu %Lu %Lu %Lu " /* utime stime cutime cstime *//*lint !e566*/ + "%Lu %Lu %Lu %Lu " /* utime stime cutime cstime */ "%ld %ld %ld %ld " - "%Lu " /* start_time *//*lint !e566*/ + "%Lu " /* start_time */ "%lu " "%ld " "%lu %lu %lu %lu %lu %lu " @@ -1922,9 +1922,9 @@ static proc_t *lxc_stat2proc(char *S) &P->state, &P->ppid, &P->pgrp, &P->session, &P->tty, &P->tpgid, &P->flags, &P->min_flt, &P->cmin_flt, &P->maj_flt, &P->cmaj_flt, - &P->utime, &P->stime, &P->cutime, &P->cstime,/*lint !e561*/ + &P->utime, &P->stime, &P->cutime, &P->cstime, &P->priority, &P->nice, &P->timeout, &P->it_real_value, - &P->start_time,/*lint !e561*/ + &P->start_time, &P->vsize, &P->rss, &P->rss_rlim, &P->start_code, &P->end_code, &P->start_stack, &P->kstk_esp, @@ -1935,7 +1935,7 @@ static proc_t *lxc_stat2proc(char *S) ); if (P->tty == 0) - P->tty = -1; /* the old notty val, update elsewhere bef. moving to 0 *//*lint !e960*/ + P->tty = -1; /* the old notty val, update elsewhere bef. moving to 0 */ return P; } @@ -1949,7 +1949,7 @@ unsigned long long lxc_get_process_startat(pid_t pid) char sbuf[1024] = {0}; /* bufs for stat */ sret = snprintf(filename, sizeof(filename), "/proc/%d/stat", pid); - if (sret < 0 || sret >= sizeof(filename)) {/*lint !e574*/ + if (sret < 0 || sret >= sizeof(filename)) { ERROR("Failed to sprintf filename"); goto out; } @@ -1960,7 +1960,7 @@ unsigned long long lxc_get_process_startat(pid_t pid) } pid_info = lxc_stat2proc(sbuf); - if (!pid_info) {/*lint !e574*/ + if (!pid_info) { ERROR("Failed to get proc stat info"); goto out; } @@ -1992,3 +1992,41 @@ void lxc_write_error_message(int errfd, const char *format, ...) SYSERROR("Write errbuf failed"); } +bool lxc_process_alive(pid_t pid, unsigned long long start_time) +{ + int ret; + int sret = 0; + bool alive = true; + proc_t *pid_info = NULL; + char filename[PATH_MAX] = {0}; + char sbuf[1024] = {0}; /* bufs for stat */ + + sret = kill(pid, 0); + if (sret < 0 && errno == ESRCH) + return false; + + sret = snprintf(filename, sizeof(filename), "/proc/%d/stat", pid); + if (sret < 0 || sret >= sizeof(filename)) { + ERROR("Failed to sprintf filename"); + goto out; + } + + if ((lxc_file2str(filename, sbuf, sizeof(sbuf))) == -1) { + ERROR("Failed to read pidfile %s", filename); + alive = false; + goto out; + } + + pid_info = lxc_stat2proc(sbuf); + if (!pid_info) { + ERROR("Failed to get proc stat info"); + alive = false; + goto out; + } + + if (start_time != pid_info->start_time) + alive = false; +out: + free(pid_info); + return alive; +} diff --git a/src/lxc/utils.h b/src/lxc/utils.h index 3d56fd93..abc88ca5 100644 --- a/src/lxc/utils.h +++ b/src/lxc/utils.h @@ -316,4 +316,6 @@ extern int fd_nonblock(int fd); extern int unsigned long long lxc_get_process_startat(pid_t pid); extern void lxc_write_error_message(int errfd, const char *format, ...); +extern bool lxc_process_alive(pid_t pid, unsigned long long start_time); + #endif /* __LXC_UTILS_H */ -- 2.23.0