From 1dce19b2ce4d01c12f3f4eda0666457f2fd3c42c Mon Sep 17 00:00:00 2001 From: LiFeng Date: Mon, 13 Apr 2020 18:11:59 +0800 Subject: [PATCH 10/49] IO: refact terminal progress Signed-off-by: LiFeng --- configure.ac | 3 + src/lxc/Makefile.am | 24 +- src/lxc/attach.c | 20 +- src/lxc/attach_options.h | 5 + src/lxc/conf.c | 10 + src/lxc/json/defs.c | 205 +++++++ src/lxc/json/defs.h | 37 ++ src/lxc/json/json_common.c | 1153 ++++++++++++++++++++++++++++++++++++++ src/lxc/json/json_common.h | 185 ++++++ src/lxc/json/logger_json_file.c | 246 ++++++++ src/lxc/json/logger_json_file.h | 45 ++ src/lxc/json/oci_runtime_hooks.c | 52 ++ src/lxc/json/oci_runtime_hooks.h | 15 + src/lxc/json/oci_runtime_spec.c | 195 +++++++ src/lxc/json/oci_runtime_spec.h | 37 ++ src/lxc/json/read-file.c | 95 ++++ src/lxc/json/read-file.h | 11 + src/lxc/lxccontainer.c | 36 ++ src/lxc/lxccontainer.h | 24 + src/lxc/terminal.c | 783 ++++++++++++++++++++++++++ src/lxc/terminal.h | 20 + src/lxc/tools/arguments.h | 1 + src/lxc/tools/lxc_start.c | 12 + 23 files changed, 3208 insertions(+), 6 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/logger_json_file.c create mode 100644 src/lxc/json/logger_json_file.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 5f386d9..56d0cb7 100644 --- a/configure.ac +++ b/configure.ac @@ -119,6 +119,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 d8c2492..23935e5 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -53,7 +53,12 @@ noinst_HEADERS = api_extensions.h \ uuid.h #if HAVE_ISULAD -noinst_HEADERS += isulad_utils.h path.h +noinst_HEADERS += isulad_utils.h path.h \ + json/json_common.h json/defs.h \ + json/oci_runtime_hooks.h \ + json/logger_json_file.h \ + json/oci_runtime_spec.h \ + json/read-file.h #endif if IS_BIONIC @@ -159,10 +164,16 @@ liblxc_la_SOURCES = af_unix.c af_unix.h \ version.h \ $(LSM_SOURCES) -#if HAVE_ISULAD +if HAVE_ISULAD liblxc_la_SOURCES += isulad_utils.c isulad_utils.h \ - path.c path.h -#endif + path.c path.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/logger_json_file.c json/logger_json_file.h \ + json/oci_runtime_spec.c json/oci_runtime_spec.h \ + json/read-file.c json/read-file.h +endif if IS_BIONIC liblxc_la_SOURCES += ../include/fexecve.c ../include/fexecve.h \ @@ -223,6 +234,7 @@ AM_CFLAGS = -DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \ -I $(top_srcdir)/src/lxc/cgroups if HAVE_ISULAD +AM_CFLAGS += -I $(top_srcdir)/src/lxc/json AM_CFLAGS += -DHAVE_ISULAD endif if ENABLE_APPARMOR @@ -271,6 +283,10 @@ liblxc_la_LDFLAGS = -pthread \ -Wl,-soname,liblxc.so.$(firstword $(subst ., ,@LXC_ABI@)) \ -version-info @LXC_ABI_MAJOR@ +if HAVE_ISULAD +liblxc_la_LDFLAGS += @YAJL_LIBS@ +endif + liblxc_la_LIBADD = $(CAP_LIBS) \ $(OPENSSL_LIBS) \ $(SELINUX_LIBS) \ diff --git a/src/lxc/attach.c b/src/lxc/attach.c index 56d62ed..78b4700 100644 --- a/src/lxc/attach.c +++ b/src/lxc/attach.c @@ -898,12 +898,28 @@ on_error: } static int lxc_attach_terminal(struct lxc_conf *conf, - struct lxc_terminal *terminal) + struct lxc_terminal *terminal, lxc_attach_options_t *options) { int ret; lxc_terminal_init(terminal); +#ifdef HAVE_ISULAD + /* isulad: if we pass fifo in option, use them as init fifos */ + if (options->init_fifo[0]) { + free(terminal->init_fifo[0]); + terminal->init_fifo[0] = safe_strdup(options->init_fifo[0]); + } + if (options->init_fifo[1]) { + free(terminal->init_fifo[1]); + terminal->init_fifo[1] = safe_strdup(options->init_fifo[1]); + } + if (options->init_fifo[2]) { + free(terminal->init_fifo[2]); + terminal->init_fifo[2] = safe_strdup(options->init_fifo[2]); + } +#endif + ret = lxc_terminal_create(terminal); if (ret < 0) return log_error(-1, "Failed to create terminal"); @@ -1097,7 +1113,7 @@ int lxc_attach(struct lxc_container *container, lxc_attach_exec_t exec_function, } if (options->attach_flags & LXC_ATTACH_TERMINAL) { - ret = lxc_attach_terminal(conf, &terminal); + ret = lxc_attach_terminal(conf, &terminal, options); if (ret < 0) { ERROR("Failed to setup new terminal"); free(cwd); diff --git a/src/lxc/attach_options.h b/src/lxc/attach_options.h index ec8bea1..3a02ee5 100644 --- a/src/lxc/attach_options.h +++ b/src/lxc/attach_options.h @@ -113,6 +113,11 @@ typedef struct lxc_attach_options_t { /*! File descriptor to log output. */ int log_fd; +#ifdef HAVE_ISULAD + char *init_fifo[3]; /* isulad: default fifos for the start */ + int64_t timeout;/* isulad: Seconds for waiting on a container to attach/exec before it is killed*/ +#endif + } lxc_attach_options_t; /*! Default attach options to use */ diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 1f779b9..1487b73 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -2669,6 +2669,16 @@ struct lxc_conf *lxc_conf_init(void) /* isulad add begin */ lxc_list_init(&new->populate_devs); new->umask = 0027; /*default umask 0027*/ + new->console.init_fifo[0] = NULL; + new->console.init_fifo[1] = NULL; + new->console.init_fifo[2] = NULL; + new->console.pipes[0][0] = -1; + new->console.pipes[0][1] = -1; + new->console.pipes[1][0] = -1; + new->console.pipes[1][1] = -1; + new->console.pipes[2][0] = -1; + new->console.pipes[2][1] = -1; + lxc_list_init(&new->console.fifos); #endif return new; diff --git a/src/lxc/json/defs.c b/src/lxc/json/defs.c new file mode 100644 index 0000000..4bf569a --- /dev/null +++ b/src/lxc/json/defs.c @@ -0,0 +1,205 @@ +// Generated from defs.json. Do not edit! +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include +#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 != NULL) { + char *str = YAJL_GET_STRING(val); + ret->path = safe_strdup(str ? str : ""); + } + } + { + yajl_val tmp = get_val(tree, "args", yajl_t_array); + if (tmp != NULL && YAJL_GET_ARRAY(tmp) != NULL && YAJL_GET_ARRAY(tmp)->len > 0) { + size_t i; + ret->args_len = YAJL_GET_ARRAY(tmp)->len; + if (YAJL_GET_ARRAY(tmp)->len > SIZE_MAX / sizeof(*ret->args) - 1) { + free_defs_hook(ret); + return NULL; + } + 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 != NULL) { + 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 != NULL && YAJL_GET_ARRAY(tmp) != NULL && YAJL_GET_ARRAY(tmp)->len > 0) { + size_t i; + ret->env_len = YAJL_GET_ARRAY(tmp)->len; + if (YAJL_GET_ARRAY(tmp)->len > SIZE_MAX / sizeof(*ret->env) - 1) { + free_defs_hook(ret); + return NULL; + } + 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 != NULL) { + 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 != NULL) { + 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 == NULL) + return; + free(ptr->path); + ptr->path = NULL; + if (ptr->args != NULL) { + size_t i; + for (i = 0; i < ptr->args_len; i++) { + if (ptr->args[i] != NULL) { + free(ptr->args[i]); + ptr->args[i] = NULL; + } + } + free(ptr->args); + ptr->args = NULL; + } + if (ptr->env != NULL) { + size_t i; + for (i = 0; i < ptr->env_len; i++) { + if (ptr->env[i] != NULL) { + 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 != NULL && ptr->path != NULL)) { + 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 != NULL && ptr->path != NULL) { + 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 != NULL && ptr->args != NULL)) { + 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 != NULL && ptr->args != NULL) { + 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 != NULL && ptr->env != NULL)) { + 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 != NULL && ptr->env != NULL) { + 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 != NULL && 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 != NULL && 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 0000000..0bbd8ac --- /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 0000000..ec20c59 --- /dev/null +++ b/src/lxc/json/json_common.c @@ -0,0 +1,1153 @@ +// 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 = snprintf(numstr, MAX_NUM_STR_LEN, "%llu", num); + if (ret < 0 || ret >= MAX_NUM_STR_LEN) { + 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 = snprintf(numstr, MAX_NUM_STR_LEN, "%lld", num); + if (ret < 0 || ret >= MAX_NUM_STR_LEN) { + 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, !(ctx->options & GEN_OPTIONS_NOT_VALIDATE_UTF8)); + 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 == NULL) { + return -EINVAL; + } + + errno = 0; + d = strtod(numstr, &err_str); + if (errno > 0) { + return -errno; + } + + if (err_str == NULL || 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 == NULL) { + return -EINVAL; + } + + errno = 0; + uli = strtoul(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (err == NULL || 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 == NULL) { + return -EINVAL; + } + + errno = 0; + uli = strtoul(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (err == NULL || 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 == NULL) { + return -EINVAL; + } + + errno = 0; + ull = strtoull(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (err == NULL || 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 == NULL) { + return -EINVAL; + } + + errno = 0; + ull = strtoull(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (err == NULL || 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 == NULL) { + return -EINVAL; + } + + errno = 0; + ull = strtoull(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (err == NULL || 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 == NULL) { + return -EINVAL; + } + + errno = 0; + li = strtol(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (err == NULL || 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 == NULL) { + return -EINVAL; + } + + errno = 0; + li = strtol(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (err == NULL || 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 == NULL) { + return -EINVAL; + } + + errno = 0; + lli = strtol(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (err == NULL || 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 == NULL) { + return -EINVAL; + } + + errno = 0; + lli = strtoll(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (err == NULL || 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 == NULL) { + return -EINVAL; + } + + errno = 0; + lli = strtol(numstr, &err, 0); + if (errno > 0) { + return -errno; + } + + if (err == NULL || err == numstr || *err != '\0') { + return -EINVAL; + } + + if (lli > INT_MAX || lli < INT_MIN) { + return -ERANGE; + } + + *converted = (int)lli; + return 0; +} + +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 != NULL) { + 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 = snprintf(numstr, MAX_NUM_STR_LEN, "%lld", (long long int)map->keys[i]); + if (nret < 0 || nret >= MAX_NUM_STR_LEN) { + 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 != NULL) { + 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 != NULL && YAJL_GET_OBJECT(src) != NULL) { + size_t i; + size_t len = YAJL_GET_OBJECT(src)->len; + if (len > SIZE_MAX / sizeof(int) - 1) { + return NULL; + } + 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 != NULL) { + int invalid; + invalid = common_safe_int(srckey, &(ret->keys[i])); + if (invalid) { + if (*err == NULL && 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 != NULL) { + int invalid; + if (!YAJL_IS_NUMBER(srcval)) { + if (*err == NULL && 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 == NULL && 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 = NULL; + int *vals = NULL; + + if (map == NULL) { + 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) { + (void)memcpy(keys, map->keys, map->len * sizeof(int)); + (void)memcpy(vals, map->values, map->len * sizeof(int)); + } + 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 != NULL) { + 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 = snprintf(numstr, MAX_NUM_STR_LEN, "%lld", (long long int)map->keys[i]); + if (nret < 0 || nret >= MAX_NUM_STR_LEN) { + 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 != NULL) { + 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 != NULL && YAJL_GET_OBJECT(src) != NULL) { + size_t i; + size_t len = YAJL_GET_OBJECT(src)->len; + if (len > SIZE_MAX / sizeof(int) - 1) { + return NULL; + } + 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 != NULL) { + int invalid; + invalid = common_safe_int(srckey, &(ret->keys[i])); + if (invalid) { + if (*err == NULL && 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 != NULL) { + if (YAJL_IS_TRUE(srcval)) { + ret->values[i] = true; + } else if (YAJL_IS_FALSE(srcval)) { + ret->values[i] = false; + } else { + if (*err == NULL && 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 = NULL; + bool *vals = NULL; + + if (map == NULL) { + 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) { + (void)memcpy(keys, map->keys, map->len * sizeof(int)); + (void)memcpy(vals, map->values, map->len * sizeof(bool)); + } + 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 != NULL) { + 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 = snprintf(numstr, MAX_NUM_STR_LEN, "%lld", (long long int)map->keys[i]); + if (nret < 0 || nret >= MAX_NUM_STR_LEN) { + 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 != NULL) { + 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 != NULL && YAJL_GET_OBJECT(src) != NULL) { + size_t i; + size_t len = YAJL_GET_OBJECT(src)->len; + if (len > SIZE_MAX / sizeof(char *) - 1) { + return NULL; + } + 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 != NULL) { + int invalid; + invalid = common_safe_int(srckey, &(ret->keys[i])); + if (invalid) { + if (*err == NULL && 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 != NULL) { + if (!YAJL_IS_STRING(srcval)) { + if (*err == NULL && 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 = NULL; + char **vals = NULL; + + if (map == NULL) { + 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) { + (void)memcpy(keys, map->keys, map->len * sizeof(int)); + (void)memcpy(vals, map->values, map->len * sizeof(char *)); + } + 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 != NULL) { + 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 != NULL) { + 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 != NULL && YAJL_GET_OBJECT(src) != NULL) { + size_t i; + size_t len = YAJL_GET_OBJECT(src)->len; + if (len > SIZE_MAX / sizeof(char *) - 1) { + return NULL; + } + 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 != NULL) { + int invalid; + if (!YAJL_IS_NUMBER(srcval)) { + if (*err == NULL && 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 == NULL && 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 = NULL; + int *vals = NULL; + + if (map == NULL) { + 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) { + (void)memcpy(keys, map->keys, map->len * sizeof(char *)); + (void)memcpy(vals, map->values, map->len * sizeof(int)); + } + 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 != NULL) { + 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 != NULL) { + 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 != NULL && YAJL_GET_OBJECT(src) != NULL) { + size_t i; + size_t len = YAJL_GET_OBJECT(src)->len; + if (len > SIZE_MAX / sizeof(char *) - 1) { + return NULL; + } + 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 != NULL) { + if (YAJL_IS_TRUE(srcval)) { + ret->values[i] = true; + } else if (YAJL_IS_FALSE(srcval)) { + ret->values[i] = false; + } else { + if (*err == NULL && 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 = NULL; + bool *vals = NULL; + + if (map == NULL) { + 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) { + (void)memcpy(keys, map->keys, map->len * sizeof(char *)); + (void)memcpy(vals, map->values, map->len * sizeof(bool)); + } + 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 != NULL) { + 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 != NULL) { + 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 != NULL && YAJL_GET_OBJECT(src) != NULL) { + size_t i; + size_t len = YAJL_GET_OBJECT(src)->len; + if (len > SIZE_MAX / sizeof(char *) - 1) { + return NULL; + } + 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 != NULL) { + if (!YAJL_IS_STRING(srcval)) { + if (*err == NULL && 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, i; + char **keys = NULL; + char **vals = NULL; + + if (map == NULL) { + return -1; + } + + for (i = 0; i < map->len; i++) { + if (strcmp(map->keys[i], key) == 0) { + free(map->values[i]); + map->values[i] = safe_strdup(val ? val : ""); + return 0; + } + } + + 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) { + (void)memcpy(keys, map->keys, map->len * sizeof(char *)); + (void)memcpy(vals, map->values, map->len * sizeof(char *)); + } + 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 0000000..60aa5fd --- /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 "utils.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 +//options not to validate utf8 data +# define GEN_OPTIONS_NOT_VALIDATE_UTF8 0x08 + +#define GEN_SET_ERROR_AND_RETURN(stat, err) { \ + if (*(err) == NULL) {\ + 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); + +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/logger_json_file.c b/src/lxc/json/logger_json_file.c new file mode 100644 index 0000000..6abeef4 --- /dev/null +++ b/src/lxc/json/logger_json_file.c @@ -0,0 +1,246 @@ +// Generated from json-file.json. Do not edit! +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include +#include "logger_json_file.h" + +logger_json_file *make_logger_json_file(yajl_val tree, struct parser_context *ctx, parser_error *err) { + logger_json_file *ret = NULL; + *err = 0; + if (tree == NULL) + return ret; + ret = safe_malloc(sizeof(*ret)); + { + yajl_val tmp = get_val(tree, "log", yajl_t_string); + if (tmp != NULL) { + char *str = YAJL_GET_STRING(tmp); + ret->log = (uint8_t *)safe_strdup(str ? str : ""); + ret->log_len = str != NULL ? strlen(str) : 0; + } + } + { + yajl_val val = get_val(tree, "stream", yajl_t_string); + if (val != NULL) { + char *str = YAJL_GET_STRING(val); + ret->stream = safe_strdup(str ? str : ""); + } + } + { + yajl_val val = get_val(tree, "time", yajl_t_string); + if (val != NULL) { + char *str = YAJL_GET_STRING(val); + ret->time = safe_strdup(str ? str : ""); + } + } + { + yajl_val tmp = get_val(tree, "attrs", yajl_t_string); + if (tmp != NULL) { + char *str = YAJL_GET_STRING(tmp); + ret->attrs = (uint8_t *)safe_strdup(str ? str : ""); + ret->attrs_len = str != NULL ? strlen(str) : 0; + } + } + + 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], "log") && + strcmp(tree->u.object.keys[i], "stream") && + strcmp(tree->u.object.keys[i], "time") && + strcmp(tree->u.object.keys[i], "attrs")) { + if (ctx->stderr > 0) + fprintf(ctx->stderr, "WARNING: unknown key found: %s\n", tree->u.object.keys[i]); + } + } + return ret; +} + +void free_logger_json_file(logger_json_file *ptr) { + if (ptr == NULL) + return; + free(ptr->log); + ptr->log = NULL; + free(ptr->stream); + ptr->stream = NULL; + free(ptr->time); + ptr->time = NULL; + free(ptr->attrs); + ptr->attrs = NULL; + free(ptr); +} + +yajl_gen_status gen_logger_json_file(yajl_gen g, logger_json_file *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 != NULL && ptr->log != NULL && ptr->log_len)) { + const char *str = ""; + size_t len = 0; + stat = reformat_map_key(g, "log", strlen("log")); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if (ptr != NULL && ptr->log != NULL) { + str = (const char *)ptr->log; + len = ptr->log_len; + } + stat = reformat_string(g, str, len); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + } + if ((ctx->options & GEN_OPTIONS_ALLKEYVALUE) ||(ptr != NULL && ptr->stream != NULL)) { + char *str = ""; + stat = reformat_map_key(g, "stream", strlen("stream")); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if (ptr != NULL && ptr->stream != NULL) { + str = ptr->stream; + } + 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 != NULL && ptr->time != NULL)) { + char *str = ""; + stat = reformat_map_key(g, "time", strlen("time")); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if (ptr != NULL && ptr->time != NULL) { + str = ptr->time; + } + 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 != NULL && ptr->attrs != NULL && ptr->attrs_len)) { + const char *str = ""; + size_t len = 0; + stat = reformat_map_key(g, "attrs", strlen("attrs")); + if (yajl_gen_status_ok != stat) + GEN_SET_ERROR_AND_RETURN(stat, err); + if (ptr != NULL && ptr->attrs != NULL) { + str = (const char *)ptr->attrs; + len = ptr->attrs_len; + } + stat = reformat_string(g, str, len); + 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; +} + + +logger_json_file *logger_json_file_parse_file(const char *filename, struct parser_context *ctx, parser_error *err) { + logger_json_file *ptr = NULL; + size_t filesize; + char *content = NULL; + + if (filename == NULL || err == NULL) + return NULL; + + *err = NULL; + content = read_file(filename, &filesize); + if (content == NULL) { + if (asprintf(err, "cannot read the file: %s", filename) < 0) + *err = safe_strdup("error allocating memory"); + return NULL; + } + ptr = logger_json_file_parse_data(content, ctx, err); + free(content); + return ptr; +} + +logger_json_file *logger_json_file_parse_file_stream(FILE *stream, struct parser_context *ctx, parser_error *err) { + logger_json_file *ptr = NULL; + size_t filesize; + char *content = NULL ; + + if (stream == NULL || err == NULL) + return NULL; + + *err = NULL; + content = fread_file(stream, &filesize); + if (content == NULL) { + *err = safe_strdup("cannot read the file"); + return NULL; + } + ptr = logger_json_file_parse_data(content, ctx, err); + free(content); + return ptr; +} + +logger_json_file *logger_json_file_parse_data(const char *jsondata, struct parser_context *ctx, parser_error *err) { + logger_json_file *ptr = NULL; + yajl_val tree; + char errbuf[1024]; + struct parser_context tmp_ctx; + + if (jsondata == NULL || err == NULL) + return NULL; + + *err = NULL; + if (ctx == NULL) { + ctx = &tmp_ctx; + memset(&tmp_ctx, 0, sizeof(tmp_ctx)); + } + tree = yajl_tree_parse(jsondata, errbuf, sizeof(errbuf)); + if (tree == NULL) { + if (asprintf(err, "cannot parse the data: %s", errbuf) < 0) + *err = safe_strdup("error allocating memory"); + return NULL; + } + ptr = make_logger_json_file(tree, ctx, err); + yajl_tree_free(tree); + return ptr; +} +char *logger_json_file_generate_json(logger_json_file *ptr, struct parser_context *ctx, parser_error *err) { + yajl_gen g = NULL; + struct parser_context tmp_ctx; + const unsigned char *gen_buf = NULL; + char *json_buf = NULL; + size_t gen_len = 0; + + if (ptr == NULL || err == NULL) + return NULL; + + *err = NULL; + if (ctx == NULL) { + ctx = &tmp_ctx; + memset(&tmp_ctx, 0, sizeof(tmp_ctx)); + } + + if (!json_gen_init(&g, ctx)) { + *err = safe_strdup("Json_gen init failed"); + goto out; + } + if (yajl_gen_status_ok != gen_logger_json_file(g, ptr, ctx, err)) { + if (*err == NULL) + *err = safe_strdup("Failed to generate json"); + goto free_out; + } + yajl_gen_get_buf(g, &gen_buf, &gen_len); + if (gen_buf == NULL) { + *err = safe_strdup("Error to get generated json"); + goto free_out; + } + + if (gen_len == SIZE_MAX) { + *err = safe_strdup("Invalid buffer length"); + goto free_out; + } + json_buf = safe_malloc(gen_len + 1); + (void)memcpy(json_buf, gen_buf, gen_len); + json_buf[gen_len] = '\0'; + +free_out: + yajl_gen_clear(g); + yajl_gen_free(g); +out: + return json_buf; +} diff --git a/src/lxc/json/logger_json_file.h b/src/lxc/json/logger_json_file.h new file mode 100644 index 0000000..ad5af7b --- /dev/null +++ b/src/lxc/json/logger_json_file.h @@ -0,0 +1,45 @@ +// Generated from json-file.json. Do not edit! +#ifndef LOGGER_JSON_FILE_SCHEMA_H +#define LOGGER_JSON_FILE_SCHEMA_H + +#include +#include +#include "json_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint8_t *log; + size_t log_len; + + char *stream; + + char *time; + + uint8_t *attrs; + size_t attrs_len; + +} +logger_json_file; + +void free_logger_json_file(logger_json_file *ptr); + +logger_json_file *make_logger_json_file(yajl_val tree, struct parser_context *ctx, parser_error *err); + +yajl_gen_status gen_logger_json_file(yajl_gen g, logger_json_file *ptr, struct parser_context *ctx, parser_error *err); + +logger_json_file *logger_json_file_parse_file(const char *filename, struct parser_context *ctx, parser_error *err); + +logger_json_file *logger_json_file_parse_file_stream(FILE *stream, struct parser_context *ctx, parser_error *err); + +logger_json_file *logger_json_file_parse_data(const char *jsondata, struct parser_context *ctx, parser_error *err); + +char *logger_json_file_generate_json(logger_json_file *ptr, struct parser_context *ctx, parser_error *err); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/lxc/json/oci_runtime_hooks.c b/src/lxc/json/oci_runtime_hooks.c new file mode 100644 index 0000000..41ddb67 --- /dev/null +++ b/src/lxc/json/oci_runtime_hooks.c @@ -0,0 +1,52 @@ +/****************************************************************************** + * 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 = safe_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 = safe_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 0000000..bf570c9 --- /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 0000000..fd342de --- /dev/null +++ b/src/lxc/json/oci_runtime_spec.c @@ -0,0 +1,195 @@ +// Generated from spec.json. Do not edit! +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include +#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 != NULL && YAJL_GET_ARRAY(tmp) != NULL && YAJL_GET_ARRAY(tmp)->len > 0) { + 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 != NULL && YAJL_GET_ARRAY(tmp) != NULL && YAJL_GET_ARRAY(tmp)->len > 0) { + 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 != NULL && YAJL_GET_ARRAY(tmp) != NULL && YAJL_GET_ARRAY(tmp)->len > 0) { + 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 == NULL) + return; + if (ptr->prestart != NULL) { + size_t i; + for (i = 0; i < ptr->prestart_len; i++) + if (ptr->prestart[i] != NULL) { + free_defs_hook(ptr->prestart[i]); + ptr->prestart[i] = NULL; + } + free(ptr->prestart); + ptr->prestart = NULL; + } + if (ptr->poststart != NULL) { + size_t i; + for (i = 0; i < ptr->poststart_len; i++) + if (ptr->poststart[i] != NULL) { + free_defs_hook(ptr->poststart[i]); + ptr->poststart[i] = NULL; + } + free(ptr->poststart); + ptr->poststart = NULL; + } + if (ptr->poststop != NULL) { + size_t i; + for (i = 0; i < ptr->poststop_len; i++) + if (ptr->poststop[i] != NULL) { + 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 != NULL && ptr->prestart != NULL)) { + 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 != NULL && ptr->prestart != NULL) { + 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 != NULL && ptr->poststart != NULL)) { + 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 != NULL && ptr->poststart != NULL) { + 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 != NULL && ptr->poststop != NULL)) { + 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 != NULL && ptr->poststop != NULL) { + 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 0000000..ef3f161 --- /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 0000000..70e73e5 --- /dev/null +++ b/src/lxc/json/read-file.c @@ -0,0 +1,95 @@ +#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 = -1; + int tmperrno; + FILE *fp = NULL; + + 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 0000000..5d6e0eb --- /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 33bb3ec..891fc62 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -5294,6 +5294,41 @@ static int do_lxcapi_seccomp_notify_fd(struct lxc_container *c) WRAP_API(int, lxcapi_seccomp_notify_fd) #ifdef HAVE_ISULAD +/* isulad add set console fifos*/ +static bool do_lxcapi_set_terminal_default_fifos(struct lxc_container *c, const char *in, const char *out, const char *err) +{ + struct lxc_conf *conf = NULL; + + if (!c || !c->lxc_conf) + return false; + if (container_mem_lock(c)) { + ERROR("Error getting mem lock"); + return false; + } + + conf = c->lxc_conf; + if (in) { + if (conf->console.init_fifo[0]) + free(conf->console.init_fifo[0]); + conf->console.init_fifo[0] = safe_strdup(in); + } + if (out) { + if (conf->console.init_fifo[1]) + free(conf->console.init_fifo[1]); + conf->console.init_fifo[1] = safe_strdup(out); + } + if (err) { + if (conf->console.init_fifo[2]) + free(conf->console.init_fifo[2]); + conf->console.init_fifo[2] = safe_strdup(err); + } + + container_mem_unlock(c); + return true; +} + +WRAP_API_3(bool, lxcapi_set_terminal_default_fifos, const char *, const char *, const char *) + /* isulad add set info file path */ static bool do_lxcapi_set_container_info_file(struct lxc_container *c, const char *info_file) { @@ -5461,6 +5496,7 @@ struct lxc_container *lxc_container_new(const char *name, const char *configpath c->seccomp_notify_fd = lxcapi_seccomp_notify_fd; #ifdef HAVE_ISULAD c->set_container_info_file = lxcapi_set_container_info_file; + c->set_terminal_init_fifos = lxcapi_set_terminal_default_fifos; #endif return c; diff --git a/src/lxc/lxccontainer.h b/src/lxc/lxccontainer.h index edfff32..4a9ba13 100644 --- a/src/lxc/lxccontainer.h +++ b/src/lxc/lxccontainer.h @@ -876,6 +876,30 @@ struct lxc_container { * \return \c true on success, else \c false. */ bool (*set_container_info_file) (struct lxc_container *c, const char *info_file); + + /*! isulad add + * \brief An API call to change the path of the console default fifos + * + * \param c Container. + * \param path Value of the console path. + * + * \return \c true on success, else \c false. + */ + bool (*set_terminal_init_fifos)(struct lxc_container *c, const char *in, const char *out, const char *err); + + /*! isulad add + * \brief An API call to add the path of terminal fifos + * + * \param c Container. + * \param path Value of the console path.. + * + * \return \c true on success, else \c false. + */ + bool (*add_terminal_fifos)(struct lxc_container *c, const char *in, const char *out, const char *err); + + bool (*set_terminal_winch)(struct lxc_container *c, unsigned int height, unsigned int width); + + bool (*set_exec_terminal_winch)(struct lxc_container *c, const char *suffix, unsigned int height, unsigned int width); #endif }; diff --git a/src/lxc/terminal.c b/src/lxc/terminal.c index 1b170ca..c8cd83f 100644 --- a/src/lxc/terminal.c +++ b/src/lxc/terminal.c @@ -28,6 +28,9 @@ #include "syscall_wrappers.h" #include "terminal.h" #include "utils.h" +#ifdef HAVE_ISULAD +#include "logger_json_file.h" +#endif #if HAVE_PTY_H #include @@ -318,6 +321,426 @@ static int lxc_terminal_write_log_file(struct lxc_terminal *terminal, char *buf, return bytes_read; } +#ifdef HAVE_ISULAD +/* get time buffer */ +static bool get_time_buffer(struct timespec *timestamp, char *timebuffer, + size_t maxsize) +{ + struct tm tm_utc = { 0 }; + int32_t nanos = 0; + time_t seconds; + size_t len = 0; + int ret = 0; + + if (!timebuffer || !maxsize) { + return false; + } + + seconds = (time_t)timestamp->tv_sec; + gmtime_r(&seconds, &tm_utc); + strftime(timebuffer, maxsize, "%Y-%m-%dT%H:%M:%S", &tm_utc); + + nanos = (int32_t)timestamp->tv_nsec; + len = strlen(timebuffer); + ret = snprintf(timebuffer + len, (maxsize - len), ".%09dZ", nanos); + if (ret < 0 || ret >= (maxsize - len)) { + return false; + } + + return true; +} + +/* get now time buffer */ +static bool get_now_time_buffer(char *timebuffer, size_t maxsize) +{ + int err = 0; + struct timespec ts; + + err = clock_gettime(CLOCK_REALTIME, &ts); + if (err != 0) { + ERROR("failed to get time"); + return false; + } + + return get_time_buffer(&ts, timebuffer, maxsize); +} + +static int isulad_lxc_terminal_rotate_write_data(struct lxc_terminal *terminal, const char *buf, + int bytes_read) +{ + int ret; + struct stat st; + int64_t space_left = -1; + + if (terminal->log_fd < 0) + return 0; + + /* A log size <= 0 means that there's no limit on the size of the log + * file at which point we simply ignore whether the log is supposed to + * be rotated or not. + */ + if (terminal->log_size <= 0) + return lxc_write_nointr(terminal->log_fd, buf, bytes_read); + + /* Get current size of the log file. */ + ret = fstat(terminal->log_fd, &st); + if (ret < 0) { + SYSERROR("Failed to stat the terminal log file descriptor"); + return -1; + } + + /* handle non-regular files */ + if ((st.st_mode & S_IFMT) != S_IFREG) { + /* This isn't a regular file. so rotating the file seems a + * dangerous thing to do, size limits are also very + * questionable. Let's not risk anything and tell the user that + * he's requesting us to do weird stuff. + */ + if (terminal->log_rotate > 0 || terminal->log_size > 0) + return -EINVAL; + + /* I mean, sure log wherever you want to. */ + return lxc_write_nointr(terminal->log_fd, buf, bytes_read); + } + + space_left = terminal->log_size - st.st_size; + + /* User doesn't want to rotate the log file and there's no more space + * left so simply truncate it. + */ + if (space_left <= 0 && terminal->log_rotate <= 0) { + ret = lxc_terminal_truncate_log_file(terminal); + if (ret < 0) + return ret; + + if (bytes_read <= terminal->log_size) + return lxc_write_nointr(terminal->log_fd, buf, bytes_read); + + /* Write as much as we can into the buffer and loose the rest. */ + return lxc_write_nointr(terminal->log_fd, buf, terminal->log_size); + } + + /* There's enough space left. */ + if (bytes_read <= space_left) + return lxc_write_nointr(terminal->log_fd, buf, bytes_read); + + /* There'd be more to write but we aren't instructed to rotate the log + * file so simply return. There's no error on our side here. + */ + if (terminal->log_rotate > 0) + ret = lxc_terminal_rotate_log_file(terminal); + else + ret = lxc_terminal_truncate_log_file(terminal); + if (ret < 0) + return ret; + + if (terminal->log_size < bytes_read) { + /* Well, this is unfortunate because it means that there is more + * to write than the user has granted us space. There are + * multiple ways to handle this but let's use the simplest one: + * write as much as we can, tell the user that there was more + * stuff to write and move on. + * Note that this scenario shouldn't actually happen with the + * standard pty-based terminal that LXC allocates since it will + * be switched into raw mode. In raw mode only 1 byte at a time + * should be read and written. + */ + WARN("Size of terminal log file is smaller than the bytes to write"); + ret = lxc_write_nointr(terminal->log_fd, buf, terminal->log_size); + if (ret < 0) + return -1; + bytes_read -= ret; + return bytes_read; + } + + /* Yay, we made it. */ + ret = lxc_write_nointr(terminal->log_fd, buf, bytes_read); + if (ret < 0) + return -1; + bytes_read -= ret; + return bytes_read; +} + +static ssize_t isulad_logger_write(struct lxc_terminal *terminal, const char *type, const char *buf, + int bytes_read) +{ + logger_json_file *msg = NULL; + ssize_t ret = -1; + size_t len; + char *json = NULL; + char timebuffer[64] = { 0 }; + parser_error err = NULL; + struct parser_context ctx = { GEN_OPTIONS_SIMPLIFY | GEN_OPTIONS_NOT_VALIDATE_UTF8, stderr }; + + if (bytes_read < 0 || bytes_read >= INT_MAX) { + return -1; + } + msg = calloc(sizeof(logger_json_file), 1); + if (msg == NULL) { + return -errno; + } + msg->log = calloc(bytes_read, 1); + if (!msg->log) { + goto cleanup; + } + memcpy(msg->log, buf, bytes_read); + msg->log_len = bytes_read; + msg->stream = type ? safe_strdup(type) : safe_strdup("stdout"); + + get_now_time_buffer(timebuffer, sizeof(timebuffer)); + msg->time = safe_strdup(timebuffer); + + json = logger_json_file_generate_json(msg, &ctx, &err); + if (!json) { + ERROR("Failed to generate json: %s", err); + goto cleanup; + } + len = strlen(json); + json[len] = '\n'; + ret = isulad_lxc_terminal_rotate_write_data(terminal, json, len + 1); +cleanup: + free(json); + free_logger_json_file(msg); + free(err); + return ret; +} + +static int isulad_lxc_terminal_write_log_file(struct lxc_terminal *terminal, const char *type, char *buf, + int bytes_read) +{ +#define __BUF_CACHE_SIZE (16 * LXC_TERMINAL_BUFFER_SIZE) + static char cache[__BUF_CACHE_SIZE]; + static int size = 0; + int upto, index; + int begin = 0, buf_readed = 0, buf_left = 0; + int ret; + + if (buf != NULL && bytes_read > 0) { + /* Work out how much more data we are okay with reading this time. */ + upto = size + bytes_read; + if (upto > __BUF_CACHE_SIZE) { + upto = __BUF_CACHE_SIZE; + } + + if (upto > size) { + buf_readed = upto - size; + memcpy(cache + size, buf, buf_readed); + buf_left = bytes_read - buf_readed; + size += buf_readed; + } + } + + // If we have no data to log, and there's no more coming, we're done. + if (size == 0) + return 0; + + // Break up the data that we've buffered up into lines, and log each in turn. + for (index = 0; index < size; index++) { + if (cache[index] == '\n') { + ret = isulad_logger_write(terminal, type, cache + begin, index - begin + 1); + if (ret < 0) { + WARN("Failed to log msg"); + } + begin = index + 1; + } + } + /* If there's no more coming, or the buffer is full but + * has no newlines, log whatever we haven't logged yet, + * noting that it's a partial log line. */ + if (buf == NULL || (begin == 0 && size == __BUF_CACHE_SIZE)) { + if (begin < size) { + ret = isulad_logger_write(terminal, type, cache + begin, size - begin); + if (ret < 0) { + WARN("Failed to log msg"); + } + begin = 0; + size = 0; + } + if (buf == NULL) { + return 0; + } + } + /* Move any unlogged data to the front of the buffer in preparation for another read. */ + if (begin > 0) { + memcpy(cache, cache + begin, size - begin); + size -= begin; + } + /* Move left data to cache buffer */ + if (buf_left > 0) { + memcpy(cache + size, buf + buf_readed, buf_left); + size += buf_left; + } + return 0; +} + +/* isulad: forward data to all fifos */ +static void lxc_forward_data_to_fifo(struct lxc_list *list, bool is_err, const char *buf, int r) +{ + struct lxc_list *it = NULL; + struct lxc_list *next = NULL; + struct lxc_fifos_fd *elem = NULL; + + lxc_list_for_each_safe(it, list, next) { + elem = it->elem; + if (is_err) { + if (elem->err_fd >= 0) + lxc_write_nointr(elem->err_fd, buf, r); + } else { + if (elem->out_fd >= 0) + lxc_write_nointr(elem->out_fd, buf, r); + } + } + + return; +} + +/* isulad: judge the fd whether is fifo */ +static bool lxc_terminal_is_fifo(int fd, struct lxc_list *list) +{ + struct lxc_list *it = NULL; + struct lxc_list *next = NULL; + struct lxc_fifos_fd *elem = NULL; + + lxc_list_for_each_safe(it, list, next) { + elem = it->elem; + if (elem->in_fd == fd) + return true; + } + + return false; +} + +/* isulad: if fd == -1, means delete all the fifos*/ +int lxc_terminal_delete_fifo(int fd, struct lxc_list *list) +{ + struct lxc_list *it = NULL; + struct lxc_list *next = NULL; + struct lxc_fifos_fd *elem = NULL; + + lxc_list_for_each_safe(it, list, next) { + elem = it->elem; + if (elem->in_fd == fd || -1 == fd) { + INFO("Delete fifo fd %d", fd); + lxc_list_del(it); + if (elem->in_fifo) + free(elem->in_fifo); + if (elem->out_fifo) + free(elem->out_fifo); + if (elem->err_fifo) + free(elem->err_fifo); + if (elem->in_fd >= 0) + close(elem->in_fd); + if (elem->out_fd >= 0) + close(elem->out_fd); + if (elem->err_fd >= 0) + close(elem->err_fd); + free(elem); + } + } + + return 0; +} + +int lxc_terminal_io_cb(int fd, uint32_t events, void *data, + struct lxc_epoll_descr *descr) +{ + struct lxc_terminal *terminal = data; + char buf[2 * LXC_TERMINAL_BUFFER_SIZE]; + int r, w, w_log, w_rbuf; + + w = r = lxc_read_nointr(fd, buf, sizeof(buf)); + if (r <= 0) { + INFO("Terminal client on fd %d has exited", fd); + lxc_mainloop_del_handler(descr, fd); + + if (fd == terminal->master) { + terminal->master = -EBADF; + /* write remained buffer to terminal log */ + if (terminal->log_fd >= 0) { + w_log = isulad_lxc_terminal_write_log_file(terminal, "stdout", NULL, 0); + if (w_log < 0) + TRACE("Failed to write %d bytes to terminal log", r); + } + /* notes: do not close the master fd due to if we close the fd, the process may + * recive SIGHUP and the exit code will be 129 (128 + 1) + */ + return LXC_MAINLOOP_CLOSE; + } else if (fd == terminal->peer) { + lxc_terminal_signal_fini(terminal); + terminal->peer = -EBADF; + close(fd); + return LXC_MAINLOOP_CONTINUE; /* isulad: do not close mainloop when peer close*/ + } else if (lxc_terminal_is_fifo(fd, &terminal->fifos)) { + /* isulad: delete fifos when the client close */ + lxc_terminal_delete_fifo(fd, &terminal->fifos); + return LXC_MAINLOOP_CONTINUE; + } else if (fd == terminal->pipes[1][0] || fd == terminal->pipes[2][0]) { + if (fd == terminal->pipes[1][0]) { + w_log = isulad_lxc_terminal_write_log_file(terminal, "stdout", NULL, 0); + terminal->pipes[1][0] = -EBADF; + } else if (fd == terminal->pipes[2][0]) { + w_log = isulad_lxc_terminal_write_log_file(terminal, "stderr", NULL, 0); + terminal->pipes[2][0] = -EBADF; + } + if (w_log < 0) + TRACE("Failed to write %d bytes to terminal log", r); + close(fd); + return LXC_MAINLOOP_CONTINUE; + } else if (fd == terminal->pipes[0][1]) { + TRACE("closed stdin pipe of container stdin"); + terminal->pipes[0][1] = -EBADF; + close(fd); + return LXC_MAINLOOP_CONTINUE; + } else { + ERROR("Handler received unexpected file descriptor"); + } + close(fd); + return LXC_MAINLOOP_CLOSE; + } + + if (fd == terminal->peer || lxc_terminal_is_fifo(fd, &terminal->fifos)) { + if (terminal->master > 0) + w = lxc_write_nointr(terminal->master, buf, r); + if (terminal->pipes[0][1] > 0) + w = lxc_write_nointr(terminal->pipes[0][1], buf, r); + } + + w_rbuf = w_log = 0; + if (fd == terminal->master || fd == terminal->pipes[1][0] || fd == terminal->pipes[2][0]) { + /* write to peer first */ + if (terminal->peer >= 0) + w = lxc_write_nointr(terminal->peer, buf, r); + + /* isulad: forward data to fifos */ + lxc_forward_data_to_fifo(&terminal->fifos, fd == terminal->pipes[2][0], buf, r); + + /* write to terminal ringbuffer */ + if (terminal->buffer_size > 0) + w_rbuf = lxc_ringbuf_write(&terminal->ringbuf, buf, r); + + /* write to terminal log */ + if (terminal->log_fd >= 0) { + if (fd == terminal->master || fd == terminal->pipes[1][0]) + w_log = isulad_lxc_terminal_write_log_file(terminal, "stdout", buf, r); + else if (fd == terminal->pipes[2][0]) + w_log = isulad_lxc_terminal_write_log_file(terminal, "stderr", buf, r); + } + } + + if (w != r) + WARN("Short write on terminal r:%d != w:%d", r, w); + + if (w_rbuf < 0) { + errno = -w_rbuf; + SYSTRACE("Failed to write %d bytes to terminal ringbuffer", r); + } + + if (w_log < 0) + TRACE("Failed to write %d bytes to terminal log", r); + + return LXC_MAINLOOP_CONTINUE; +} +#else int lxc_terminal_io_cb(int fd, uint32_t events, void *data, struct lxc_epoll_descr *descr) { @@ -374,6 +797,7 @@ int lxc_terminal_io_cb(int fd, uint32_t events, void *data, return LXC_MAINLOOP_CONTINUE; } +#endif static int lxc_terminal_mainloop_add_peer(struct lxc_terminal *terminal) { @@ -401,6 +825,110 @@ static int lxc_terminal_mainloop_add_peer(struct lxc_terminal *terminal) return 0; } +#ifdef HAVE_ISULAD +/* isulad add pipes to mainloop */ +static int lxc_terminal_mainloop_add_pipes(struct lxc_terminal *terminal) +{ + int ret = 0; + + // parent read data from fifo, and send to stdin of container + if (terminal->pipes[0][1] > 0) { + ret = lxc_mainloop_add_handler(terminal->descr, terminal->pipes[0][1], + lxc_terminal_io_cb, terminal); + if (ret) { + ERROR("pipe fd %d not added to mainloop", terminal->pipes[0][1]); + return -1; + } + } + // parent read data from stdout of container, and send to fifo + if (terminal->pipes[1][0] > 0) { + ret = lxc_mainloop_add_handler(terminal->descr, terminal->pipes[1][0], + lxc_terminal_io_cb, terminal); + if (ret) { + ERROR("pipe fd %d not added to mainloop", terminal->pipes[1][0]); + return -1; + } + } + // parent read data from stderr of container, and send to fifo + if (terminal->pipes[2][0] > 0) { + ret = lxc_mainloop_add_handler(terminal->descr, terminal->pipes[2][0], + lxc_terminal_io_cb, terminal); + if (ret) { + ERROR("pipe fd %d not added to mainloop", terminal->pipes[2][0]); + return -1; + } + } + return ret; +} + +/* isulad add fifo to mainloop */ +static int lxc_terminal_mainloop_add_fifo(struct lxc_terminal *terminal) +{ + int ret = 0; + struct lxc_list *it = NULL; + struct lxc_list *next = NULL; + struct lxc_fifos_fd *elem = NULL; + + lxc_list_for_each_safe(it, &terminal->fifos, next) { + elem = it->elem; + if (elem->in_fd >= 0) { + ret = lxc_mainloop_add_handler(terminal->descr, elem->in_fd, + lxc_terminal_io_cb, terminal); + if (ret) { + ERROR("console fifo %s not added to mainloop", elem->in_fifo); + return -1; + } + } + } + return ret; +} + +int lxc_terminal_mainloop_add(struct lxc_epoll_descr *descr, + struct lxc_terminal *terminal) +{ + int ret; + + /* We cache the descr so that we can add an fd to it when someone + * does attach to it in lxc_terminal_allocate(). + */ + terminal->descr = descr; + + ret = lxc_terminal_mainloop_add_peer(terminal); + if (ret < 0) { + ERROR("Failed to add handler for terminal peer to mainloop"); + return -1; + } + + /* isulad add pipes to mainloop */ + ret = lxc_terminal_mainloop_add_pipes(terminal); + if (ret < 0) { + ERROR("Failed to add handler for terminal fifos to mainloop"); + return -1; + } + + /* isulad add fifo to mainloop */ + ret = lxc_terminal_mainloop_add_fifo(terminal); + if (ret < 0) { + ERROR("Failed to add handler for terminal fifos to mainloop"); + return -1; + } + + if (terminal->master < 0) { + INFO("Terminal is not initialized"); + return 0; + } + + ret = lxc_mainloop_add_handler(descr, terminal->master, + lxc_terminal_io_cb, terminal); + if (ret < 0) { + ERROR("Failed to add handler for terminal master fd %d to " + "mainloop", terminal->master); + return -1; + } + + return 0; +} +#else int lxc_terminal_mainloop_add(struct lxc_epoll_descr *descr, struct lxc_terminal *terminal) { @@ -426,6 +954,7 @@ int lxc_terminal_mainloop_add(struct lxc_epoll_descr *descr, return lxc_terminal_mainloop_add_peer(terminal); } +#endif int lxc_setup_tios(int fd, struct termios *oldtios) { @@ -760,6 +1289,31 @@ void lxc_terminal_delete(struct lxc_terminal *terminal) if (terminal->log_fd >= 0) close(terminal->log_fd); terminal->log_fd = -1; + +#ifdef HAVE_ISULAD + /* isulad: close all pipes */ + if (terminal->pipes[0][0] >= 0) + close(terminal->pipes[0][0]); + terminal->pipes[0][0] = -1; + if (terminal->pipes[0][1] >= 0) + close(terminal->pipes[0][1]); + terminal->pipes[0][1] = -1; + if (terminal->pipes[1][0] >= 0) + close(terminal->pipes[1][0]); + terminal->pipes[1][0] = -1; + if (terminal->pipes[1][1] >= 0) + close(terminal->pipes[1][1]); + terminal->pipes[1][1] = -1; + if (terminal->pipes[2][0] >= 0) + close(terminal->pipes[2][0]); + terminal->pipes[2][0] = -1; + if (terminal->pipes[2][1] >= 0) + close(terminal->pipes[2][1]); + terminal->pipes[2][1] = -1; + + /* isulad: delete all fifos */ + lxc_terminal_delete_fifo(-1, &terminal->fifos); +#endif } /** @@ -828,6 +1382,215 @@ int lxc_terminal_create_log_file(struct lxc_terminal *terminal) return 0; } +#ifdef HAVE_ISULAD +/* isulad: fd_nonblock */ +static int fd_nonblock(int fd) +{ + int flags; + + flags = fcntl(fd, F_GETFL); + + return fcntl(fd, F_SETFL, (int)((unsigned int)flags | O_NONBLOCK)); +} + +static int terminal_fifo_open(const char *fifo_path, int flags) +{ + int fd = -1; + + fd = lxc_open(fifo_path, flags, 0); + if (fd < 0) { + WARN("Failed to open fifo %s to send message: %s.", fifo_path, + strerror(errno)); + return -1; + } + + return fd; +} + +bool fifo_exists(const char *path) +{ + struct stat sb; + int ret; + + ret = stat(path, &sb); + if (ret < 0) + // could be something other than eexist, just say no + return false; + return S_ISFIFO(sb.st_mode); +} + +/* isulad: set terminal fifos */ +static int lxc_terminal_set_fifo(struct lxc_terminal *console, const char *in, const char *out, const char *err, int *input_fd) +{ + int fifofd_in = -1, fifofd_out = -1, fifofd_err = -1; + struct lxc_fifos_fd *fifo_elem = NULL; + + if ((in && !fifo_exists(in)) || (out && !fifo_exists(out)) || (err && !fifo_exists(err))) { + ERROR("File %s or %s or %s does not refer to a FIFO", in, out, err); + return -1; + } + + if (in) { + fifofd_in = terminal_fifo_open(in, O_RDONLY | O_NONBLOCK | O_CLOEXEC); + if (fifofd_in < 0) { + SYSERROR("Failed to open FIFO: %s", in); + return -1; + } + } + + if (out) { + fifofd_out = terminal_fifo_open(out, O_WRONLY | O_NONBLOCK | O_CLOEXEC); + if (fifofd_out < 0) { + SYSERROR("Failed to open FIFO: %s", out); + if (fifofd_in >= 0) + close(fifofd_in); + return -1; + } + } + + if (err) { + fifofd_err = terminal_fifo_open(err, O_WRONLY | O_NONBLOCK | O_CLOEXEC); + if (fifofd_err < 0) { + SYSERROR("Failed to open FIFO: %s", err); + if (fifofd_in >= 0) + close(fifofd_in); + if (fifofd_out >= 0) + close(fifofd_out); + return -1; + } + } + + fifo_elem = malloc(sizeof(*fifo_elem)); + if (fifo_elem == NULL) { + if (fifofd_in >= 0) + close(fifofd_in); + if (fifofd_out >= 0) + close(fifofd_out); + if (fifofd_err >= 0) + close(fifofd_err); + return -1; + } + memset(fifo_elem, 0, sizeof(*fifo_elem)); + + fifo_elem->in_fifo = safe_strdup(in ? in : ""); + fifo_elem->out_fifo = safe_strdup(out ? out : ""); + fifo_elem->err_fifo = safe_strdup(err ? err : ""); + fifo_elem->in_fd = fifofd_in; + fifo_elem->out_fd = fifofd_out; + fifo_elem->err_fd = fifofd_err; + lxc_list_add_elem(&fifo_elem->node, fifo_elem); + lxc_list_add_tail(&console->fifos, &fifo_elem->node); + + if (input_fd) + *input_fd = fifofd_in; + + return 0; +} + +/* isulad: add default fifos */ +static int lxc_terminal_fifo_default(struct lxc_terminal *terminal) +{ + if (terminal->init_fifo[0] || terminal->init_fifo[1] || terminal->init_fifo[2]) + return lxc_terminal_set_fifo(terminal, terminal->init_fifo[0], terminal->init_fifo[1], terminal->init_fifo[2], NULL); + return 0; +} + +static int use_unix_newline(int master_fd) +{ + struct termios oldtios; + int ret; + + ret = tcgetattr(master_fd, &oldtios); + if (ret < 0) + return -1; + oldtios.c_oflag &= ~ONLCR; + ret = tcsetattr(master_fd, TCSAFLUSH, &oldtios); + if (ret < 0) + return -1; + return 0; +} + +int lxc_terminal_create(struct lxc_terminal *terminal) +{ + int ret; + + if (!terminal->disable_pty) { + ret = openpty(&terminal->master, &terminal->slave, NULL, NULL, NULL); + if (ret < 0) { + SYSERROR("Failed to open terminal"); + return -1; + } + + ret = ttyname_r(terminal->slave, terminal->name, sizeof(terminal->name)); + if (ret < 0) { + SYSERROR("Failed to retrieve name of terminal slave"); + goto err; + } + + /* isulad: clear ONLCR flag */ + ret = use_unix_newline(terminal->master); + if (ret < 0) { + SYSERROR("Failed to clear ONLCR flag on terminal master"); + goto err; + } + + ret = fd_cloexec(terminal->master, true); + if (ret < 0) { + SYSERROR("Failed to set FD_CLOEXEC flag on terminal master"); + goto err; + } + + /* isulad: make master NONBLOCK */ + ret = fd_nonblock(terminal->master); + if (ret < 0) { + SYSERROR("Failed to set O_NONBLOCK flag on terminal master"); + goto err; + } + + ret = fd_cloexec(terminal->slave, true); + if (ret < 0) { + SYSERROR("Failed to set FD_CLOEXEC flag on terminal slave"); + goto err; + } + + ret = lxc_terminal_peer_default(terminal); + if (ret < 0) { + ERROR("Failed to allocate proxy terminal"); + goto err; + } + } else { + /* isulad: create 3 pipes */ + /* for stdin */ + if (pipe2(terminal->pipes[0], O_CLOEXEC)) { + ERROR("Failed to create stdin pipe"); + goto err; + } + /* for stdout */ + if (pipe2(terminal->pipes[1], O_NONBLOCK | O_CLOEXEC)) { + ERROR("Failed to create stdout pipe"); + goto err; + } + /* for stderr */ + if (pipe2(terminal->pipes[2], O_NONBLOCK | O_CLOEXEC)) { + ERROR("Failed to create stderr pipe"); + goto err; + } + } + + /* isulad: open fifos */ + ret = lxc_terminal_fifo_default(terminal); + if (ret < 0) { + ERROR("Failed to allocate fifo terminal"); + goto err; + } + + return 0; + +err: + lxc_terminal_delete(terminal); + return -ENODEV; +} +#else int lxc_terminal_create(struct lxc_terminal *terminal) { int ret; @@ -868,6 +1631,7 @@ err: lxc_terminal_delete(terminal); return -ENODEV; } +#endif int lxc_terminal_setup(struct lxc_conf *conf) { @@ -1146,6 +1910,18 @@ void lxc_terminal_init(struct lxc_terminal *terminal) terminal->peer = -EBADF; terminal->log_fd = -EBADF; lxc_terminal_info_init(&terminal->proxy); +#ifdef HAVE_ISULAD + terminal->init_fifo[0] = NULL; + terminal->init_fifo[1] = NULL; + terminal->init_fifo[2] = NULL; + terminal->pipes[0][0] = -1; + terminal->pipes[0][1] = -1; + terminal->pipes[1][0] = -1; + terminal->pipes[1][1] = -1; + terminal->pipes[2][0] = -1; + terminal->pipes[2][1] = -1; + lxc_list_init(&terminal->fifos); +#endif } void lxc_terminal_conf_free(struct lxc_terminal *terminal) @@ -1155,6 +1931,13 @@ void lxc_terminal_conf_free(struct lxc_terminal *terminal) if (terminal->buffer_size > 0 && terminal->ringbuf.addr) lxc_ringbuf_release(&terminal->ringbuf); lxc_terminal_signal_fini(terminal); +#ifdef HAVE_ISULAD + /*isulad: free console fifos */ + free(terminal->init_fifo[0]); + free(terminal->init_fifo[1]); + free(terminal->init_fifo[2]); + lxc_terminal_delete_fifo(-1, &terminal->fifos); +#endif } int lxc_terminal_map_ids(struct lxc_conf *c, struct lxc_terminal *terminal) diff --git a/src/lxc/terminal.h b/src/lxc/terminal.h index 1283cb3..dfc03c6 100644 --- a/src/lxc/terminal.h +++ b/src/lxc/terminal.h @@ -88,8 +88,28 @@ struct lxc_terminal { /* the in-memory ringbuffer */ struct lxc_ringbuf ringbuf; }; +#ifdef HAVE_ISULAD + char *init_fifo[3]; /* isulad: default fifos for the start */ + struct lxc_list fifos; /* isulad: fifos used to forward teminal */ + bool disable_pty; + bool open_stdin; + int pipes[3][2]; /* isulad: pipes for dup to container fds of stdin,stdout,stderr on daemonize mode*/ +#endif }; +#ifdef HAVE_ISULAD +/* isulad: fifo struct */ +struct lxc_fifos_fd { + char *in_fifo; + char *out_fifo; + char *err_fifo; + int in_fd; + int out_fd; + int err_fd; + struct lxc_list node; +}; +#endif + /** * lxc_terminal_allocate: allocate the console or a tty * diff --git a/src/lxc/tools/arguments.h b/src/lxc/tools/arguments.h index 91f4e9a..214949b 100644 --- a/src/lxc/tools/arguments.h +++ b/src/lxc/tools/arguments.h @@ -42,6 +42,7 @@ struct lxc_arguments { const char *share_ns[32]; /* size must be greater than LXC_NS_MAX */ #ifdef HAVE_ISULAD const char *container_info; /* isulad: file used to store pid and ppid info of container */ + char *terminal_fifos[3]; /* isulad add, fifos used to redirct stdin/out/err */ #endif /* for lxc-console */ diff --git a/src/lxc/tools/lxc_start.c b/src/lxc/tools/lxc_start.c index 83ee75a..4c4c820 100644 --- a/src/lxc/tools/lxc_start.c +++ b/src/lxc/tools/lxc_start.c @@ -125,6 +125,15 @@ static int my_parser(struct lxc_arguments *args, int c, char *arg) case OPT_CONTAINER_INFO: args->container_info = arg; break; + case OPT_INPUT_FIFO: + args->terminal_fifos[0] = arg; + break; + case OPT_OUTPUT_FIFO: + args->terminal_fifos[1] = arg; + break; + case OPT_STDERR_FIFO: + args->terminal_fifos[2] = arg; + break; #endif } return 0; @@ -306,6 +315,9 @@ int main(int argc, char *argv[]) goto out; } } + if (my_args.terminal_fifos[0] || my_args.terminal_fifos[1] || my_args.terminal_fifos[2]) { + c->set_terminal_init_fifos(c, my_args.terminal_fifos[0], my_args.terminal_fifos[1], my_args.terminal_fifos[2]); + } #endif if (my_args.console) -- 1.8.3.1