2294 lines
65 KiB
C
2294 lines
65 KiB
C
/******************************************************************************
|
|
* Copyright (c) Huawei Technologies Co., Ltd. 2017-2019. All rights reserved.
|
|
* iSulad licensed under the Mulan PSL v2.
|
|
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
* You may obtain a copy of Mulan PSL v2 at:
|
|
* http://license.coscl.org.cn/MulanPSL2
|
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
* PURPOSE.
|
|
* See the Mulan PSL v2 for more details.
|
|
* Author: lifeng
|
|
* Create: 2017-11-22
|
|
* Description: provide container create functions
|
|
******************************************************************************/
|
|
#include <unistd.h>
|
|
#include <stdio_ext.h>
|
|
#include <regex.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#include "namespace.h"
|
|
#include "error.h"
|
|
#include "arguments.h"
|
|
#include "log.h"
|
|
#include "utils.h"
|
|
#include "console.h"
|
|
#include "create.h"
|
|
#include "commands.h"
|
|
#include "isula_connect.h"
|
|
#include "path.h"
|
|
#include "pull.h"
|
|
#include "libisulad.h"
|
|
|
|
const char g_cmd_create_desc[] = "Create a new container";
|
|
const char g_cmd_create_usage[] = "create [OPTIONS] --external-rootfs=PATH|IMAGE [COMMAND] [ARG...]";
|
|
|
|
struct client_arguments g_cmd_create_args = {
|
|
.runtime = "",
|
|
.restart = "no",
|
|
.log_file_size = "1MB",
|
|
.log_file_rotate = 7,
|
|
.cr.oom_score_adj = 0,
|
|
.custom_conf.health_interval = 0,
|
|
.custom_conf.health_timeout = 0,
|
|
.custom_conf.health_start_period = 0,
|
|
.custom_conf.health_retries = 0,
|
|
};
|
|
|
|
static void request_pack_host_config_limit(const struct client_arguments *args, isula_host_config_t *hostconfig)
|
|
{
|
|
/* pids limit */
|
|
if (args->custom_conf.pids_limit != 0) {
|
|
hostconfig->cr->pids_limit = args->custom_conf.pids_limit;
|
|
}
|
|
/* files limit */
|
|
if (args->custom_conf.files_limit != 0) {
|
|
hostconfig->cr->files_limit = args->custom_conf.files_limit;
|
|
}
|
|
}
|
|
|
|
static int request_pack_host_config_storage_opts(const struct client_arguments *args, isula_host_config_t *hostconfig)
|
|
{
|
|
size_t i = 0;
|
|
size_t len = 0;
|
|
|
|
if (args->custom_conf.storage_opts == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
hostconfig->storage_opts = util_common_calloc_s(sizeof(json_map_string_string));
|
|
if (hostconfig->storage_opts == NULL) {
|
|
COMMAND_ERROR("Out of memory");
|
|
return -1;
|
|
}
|
|
|
|
len = util_array_len((const char **)(args->custom_conf.storage_opts));
|
|
for (i = 0; i < len; i++) {
|
|
char *p = NULL;
|
|
p = strchr(args->custom_conf.storage_opts[i], '=');
|
|
if (p != NULL) {
|
|
*p = '\0';
|
|
if (append_json_map_string_string(hostconfig->storage_opts, args->custom_conf.storage_opts[i], p + 1)) {
|
|
COMMAND_ERROR("Failed to append map");
|
|
*p = '=';
|
|
return -1;
|
|
}
|
|
*p = '=';
|
|
} else {
|
|
COMMAND_ERROR("Invalid storage option '%s'", args->custom_conf.storage_opts[i]);
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int request_pack_host_config_sysctls(const struct client_arguments *args, isula_host_config_t *hostconfig)
|
|
{
|
|
size_t i = 0;
|
|
size_t len = 0;
|
|
|
|
if (args->custom_conf.sysctls == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
hostconfig->sysctls = util_common_calloc_s(sizeof(json_map_string_string));
|
|
if (hostconfig->sysctls == NULL) {
|
|
COMMAND_ERROR("Out of memory");
|
|
return -1;
|
|
}
|
|
|
|
len = util_array_len((const char **)(args->custom_conf.sysctls));
|
|
for (i = 0; i < len; i++) {
|
|
char *p = NULL;
|
|
p = strchr(args->custom_conf.sysctls[i], '=');
|
|
if (p != NULL) {
|
|
*p = '\0';
|
|
if (append_json_map_string_string(hostconfig->sysctls, args->custom_conf.sysctls[i], p + 1)) {
|
|
COMMAND_ERROR("Failed to append map");
|
|
*p = '=';
|
|
return -1;
|
|
}
|
|
*p = '=';
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int request_pack_host_config_cgroup(const struct client_arguments *args, isula_host_config_t *hostconfig)
|
|
{
|
|
if (args == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
/* blkio weight */
|
|
hostconfig->cr->blkio_weight = args->cr.blkio_weight;
|
|
|
|
/* cpu shares */
|
|
hostconfig->cr->cpu_shares = args->cr.cpu_shares;
|
|
|
|
/* cpu period */
|
|
hostconfig->cr->cpu_period = args->cr.cpu_period;
|
|
|
|
/* cpu quota */
|
|
hostconfig->cr->cpu_quota = args->cr.cpu_quota;
|
|
|
|
/* cpu realtime period */
|
|
hostconfig->cr->cpu_realtime_period = args->cr.cpu_rt_period;
|
|
|
|
/* cpu realtime runtime */
|
|
hostconfig->cr->cpu_realtime_runtime = args->cr.cpu_rt_runtime;
|
|
|
|
/* cpuset-cpus */
|
|
hostconfig->cr->cpuset_cpus = args->cr.cpuset_cpus;
|
|
|
|
/* cpuset memory */
|
|
hostconfig->cr->cpuset_mems = args->cr.cpuset_mems;
|
|
|
|
/* oom_score_adj */
|
|
hostconfig->cr->oom_score_adj = args->cr.oom_score_adj;
|
|
|
|
/* Memory limit */
|
|
hostconfig->cr->memory = args->cr.memory_limit;
|
|
|
|
/* memory swap */
|
|
hostconfig->cr->memory_swap = args->cr.memory_swap;
|
|
|
|
/* memory reservation */
|
|
hostconfig->cr->memory_reservation = args->cr.memory_reservation;
|
|
|
|
/* kernel memory limit */
|
|
hostconfig->cr->kernel_memory = args->cr.kernel_memory_limit;
|
|
|
|
hostconfig->cr->swappiness = args->cr.swappiness;
|
|
|
|
request_pack_host_config_limit(args, hostconfig);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int util_env_set_isulad_enable_plugins(char ***penv, const size_t *penv_len, const char *names)
|
|
{
|
|
size_t env_len;
|
|
size_t len = 0;
|
|
char *val = NULL;
|
|
char *kv = NULL;
|
|
char **env = NULL;
|
|
const char *arr[10] = { NULL };
|
|
|
|
if (penv == NULL || penv_len == NULL || names == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
env = *penv;
|
|
env_len = *penv_len;
|
|
|
|
arr[0] = ISULAD_ENABLE_PLUGINS;
|
|
arr[1] = "=";
|
|
arr[2] = names;
|
|
len = 3;
|
|
|
|
val = util_env_get_val(env, env_len, ISULAD_ENABLE_PLUGINS, strlen(ISULAD_ENABLE_PLUGINS));
|
|
if (val != NULL && strlen(val) != 0) {
|
|
arr[3] = ISULAD_ENABLE_PLUGINS_SEPERATOR;
|
|
arr[4] = val;
|
|
len = 5;
|
|
}
|
|
|
|
kv = util_string_join("", arr, len);
|
|
if (kv == NULL) {
|
|
goto failed;
|
|
}
|
|
|
|
if (util_env_set_val(penv, penv_len, ISULAD_ENABLE_PLUGINS, strlen(ISULAD_ENABLE_PLUGINS), kv)) {
|
|
goto failed;
|
|
}
|
|
|
|
free(val);
|
|
free(kv);
|
|
return 0;
|
|
|
|
failed:
|
|
free(val);
|
|
free(kv);
|
|
return -1;
|
|
}
|
|
|
|
static int request_pack_custom_env(struct client_arguments *args, isula_container_config_t *conf)
|
|
{
|
|
int ret = 0;
|
|
char *pe = NULL;
|
|
char *new_env = NULL;
|
|
|
|
if (args->custom_conf.env != NULL) {
|
|
size_t i;
|
|
for (i = 0; i < util_array_len((const char **)(args->custom_conf.env)); i++) {
|
|
if (util_validate_env(args->custom_conf.env[i], &new_env) != 0) {
|
|
COMMAND_ERROR("Invalid environment %s", args->custom_conf.env[i]);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
if (new_env == NULL) {
|
|
continue;
|
|
}
|
|
if (util_array_append(&conf->env, new_env) != 0) {
|
|
COMMAND_ERROR("Failed to append custom config env list %s", new_env);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
free(new_env);
|
|
new_env = NULL;
|
|
}
|
|
conf->env_len = util_array_len((const char **)(conf->env));
|
|
}
|
|
|
|
if (args->custom_conf.accel != NULL) {
|
|
pe = util_env_get_val(conf->env, conf->env_len, ISULAD_ENABLE_PLUGINS, strlen(ISULAD_ENABLE_PLUGINS));
|
|
if (pe == NULL) {
|
|
if (util_array_append(&conf->env, ISULAD_ENABLE_PLUGINS "=")) {
|
|
COMMAND_ERROR("init env ISULAD_ENABLE_PLUGINS failed");
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
}
|
|
conf->env_len = util_array_len((const char **)(conf->env));
|
|
conf->accel = args->custom_conf.accel;
|
|
conf->accel_len = util_array_len((const char **)(args->custom_conf.accel));
|
|
if (util_env_set_isulad_enable_plugins(&conf->env, &conf->env_len, ISULAD_ISULA_ADAPTER)) {
|
|
COMMAND_ERROR("init accel env failed");
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
out:
|
|
free(pe);
|
|
free(new_env);
|
|
return ret;
|
|
}
|
|
|
|
static int read_env_from_file(const char *path, size_t file_size, isula_container_config_t *conf)
|
|
{
|
|
int ret = 0;
|
|
FILE *fp = NULL;
|
|
char *buf = NULL;
|
|
char *new_env = NULL;
|
|
|
|
if (file_size == 0) {
|
|
return 0;
|
|
}
|
|
fp = util_fopen(path, "r");
|
|
if (fp == NULL) {
|
|
ERROR("Failed to open '%s'", path);
|
|
return -1;
|
|
}
|
|
buf = (char *)util_common_calloc_s(file_size + 1);
|
|
if (buf == NULL) {
|
|
ERROR("Out of memory");
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
while (fgets(buf, (int)file_size + 1, fp) != NULL) {
|
|
size_t len = strlen(buf);
|
|
if (len == 1) {
|
|
continue;
|
|
}
|
|
buf[len - 1] = '\0';
|
|
if (util_validate_env(buf, &new_env) != 0) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
if (new_env == NULL) {
|
|
continue;
|
|
}
|
|
if (util_array_append(&conf->env, new_env) != 0) {
|
|
ERROR("Failed to append environment variable");
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
free(new_env);
|
|
new_env = NULL;
|
|
}
|
|
|
|
out:
|
|
fclose(fp);
|
|
free(buf);
|
|
free(new_env);
|
|
return ret;
|
|
}
|
|
|
|
static int append_env_variables_to_conf(const char *env_file, isula_container_config_t *conf)
|
|
{
|
|
int ret = 0;
|
|
size_t file_size;
|
|
|
|
if (!util_file_exists(env_file)) {
|
|
COMMAND_ERROR("env file not exists: %s", env_file);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
file_size = util_file_size(env_file);
|
|
if (file_size > REGULAR_FILE_SIZE) {
|
|
COMMAND_ERROR("env file '%s', size exceed limit: %lld", env_file, REGULAR_FILE_SIZE);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (read_env_from_file(env_file, file_size, conf) != 0) {
|
|
COMMAND_ERROR("failed to read env from file: %s", env_file);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int request_pack_custom_env_file(const struct client_arguments *args, isula_container_config_t *conf)
|
|
{
|
|
int ret = 0;
|
|
size_t i;
|
|
char **env_files = args->custom_conf.env_file;
|
|
size_t env_files_size = util_array_len((const char **)env_files);
|
|
if (env_files_size == 0) {
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < env_files_size; i++) {
|
|
if (append_env_variables_to_conf(env_files[i], conf) != 0) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
}
|
|
conf->env_len = util_array_len((const char **)(conf->env));
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static bool validate_label(const char *label)
|
|
{
|
|
bool ret = true;
|
|
char **arr = util_string_split_n(label, '=', 2);
|
|
if (arr == NULL) {
|
|
ERROR("Failed to split label string");
|
|
ret = false;
|
|
goto out;
|
|
}
|
|
|
|
if (strlen(arr[0]) == 0) {
|
|
ERROR("Invalid label: %s, empty name", label);
|
|
ret = false;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
util_free_array(arr);
|
|
return ret;
|
|
}
|
|
|
|
static int request_pack_custom_label(struct client_arguments *args, isula_container_config_t *conf)
|
|
{
|
|
int ret = 0;
|
|
size_t i;
|
|
|
|
if (args->custom_conf.label == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < util_array_len((const char **)(args->custom_conf.label)); i++) {
|
|
if (!validate_label(args->custom_conf.label[i])) {
|
|
COMMAND_ERROR("Invalid label '%s': empty name", args->custom_conf.label[i]);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
if (util_array_append(&conf->label, args->custom_conf.label[i]) != 0) {
|
|
COMMAND_ERROR("Failed to append custom config label list");
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
}
|
|
util_free_array(args->custom_conf.label);
|
|
args->custom_conf.label = conf->label; /* make sure args->custom_conf.label point to valid memory. */
|
|
conf->label_len = util_array_len((const char **)(conf->label));
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int read_label_from_file(const char *path, size_t file_size, isula_container_config_t *conf)
|
|
{
|
|
int ret = 0;
|
|
FILE *fp = NULL;
|
|
char *buf = NULL;
|
|
size_t len;
|
|
ssize_t num;
|
|
|
|
if (file_size == 0) {
|
|
return 0;
|
|
}
|
|
fp = fopen(path, "re");
|
|
if (fp == NULL) {
|
|
ERROR("Failed to open '%s'", path);
|
|
return -1;
|
|
}
|
|
__fsetlocking(fp, FSETLOCKING_BYCALLER);
|
|
num = getline(&buf, &len, fp);
|
|
while (num != -1) {
|
|
size_t len = strlen(buf);
|
|
if (len == 1) {
|
|
num = getline(&buf, &len, fp);
|
|
continue;
|
|
}
|
|
buf[len - 1] = '\0';
|
|
if (!validate_label(buf)) {
|
|
COMMAND_ERROR("Invalid label '%s': empty name", buf);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
if (util_array_append(&conf->label, buf) != 0) {
|
|
ERROR("Failed to append label");
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
num = getline(&buf, &len, fp);
|
|
}
|
|
|
|
out:
|
|
free(buf);
|
|
fclose(fp);
|
|
return ret;
|
|
}
|
|
|
|
static int append_labels_to_conf(const char *label_file, isula_container_config_t *conf)
|
|
{
|
|
int ret = 0;
|
|
size_t file_size;
|
|
|
|
if (!util_file_exists(label_file)) {
|
|
COMMAND_ERROR("label file not exists: %s", label_file);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
file_size = util_file_size(label_file);
|
|
if (file_size > REGULAR_FILE_SIZE) {
|
|
COMMAND_ERROR("label file '%s', size exceed limit: %lld", label_file, REGULAR_FILE_SIZE);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (read_label_from_file(label_file, file_size, conf) != 0) {
|
|
COMMAND_ERROR("failed to read label from file: %s", label_file);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int request_pack_custom_label_file(const struct client_arguments *args, isula_container_config_t *conf)
|
|
{
|
|
int ret = 0;
|
|
size_t i;
|
|
char **label_files = args->custom_conf.label_file;
|
|
size_t label_files_size = util_array_len((const char **)label_files);
|
|
if (label_files_size == 0) {
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < label_files_size; i++) {
|
|
if (append_labels_to_conf(label_files[i], conf) != 0) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
}
|
|
conf->label_len = util_array_len((const char **)(conf->label));
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static void request_pack_custom_user(const struct client_arguments *args, isula_container_config_t *conf)
|
|
{
|
|
if (args->custom_conf.user != NULL) {
|
|
conf->user = args->custom_conf.user;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void request_pack_custom_hostname(const struct client_arguments *args, isula_container_config_t *conf)
|
|
{
|
|
if (args->custom_conf.hostname != NULL) {
|
|
conf->hostname = args->custom_conf.hostname;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void request_pack_custom_all_devices(const struct client_arguments *args, isula_container_config_t *conf)
|
|
{
|
|
/* alldevices */
|
|
if (args->custom_conf.all_devices) {
|
|
conf->all_devices = true;
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void request_pack_custom_system_container(const struct client_arguments *args, isula_container_config_t *conf)
|
|
{
|
|
if (args->custom_conf.system_container) {
|
|
conf->system_container = true;
|
|
}
|
|
|
|
/* ns change opt */
|
|
if (!args->custom_conf.privileged) {
|
|
if (args->custom_conf.ns_change_opt != NULL) {
|
|
conf->ns_change_opt = args->custom_conf.ns_change_opt;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void request_pack_custom_mounts(const struct client_arguments *args, isula_container_config_t *conf)
|
|
{
|
|
if (args->custom_conf.mounts != NULL) {
|
|
conf->mounts_len = util_array_len((const char **)(args->custom_conf.mounts));
|
|
conf->mounts = args->custom_conf.mounts;
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void request_pack_custom_entrypoint(const struct client_arguments *args, isula_container_config_t *conf)
|
|
{
|
|
if (args->custom_conf.entrypoint != NULL) {
|
|
conf->entrypoint = args->custom_conf.entrypoint;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void request_pack_custom_args(const struct client_arguments *args, isula_container_config_t *conf)
|
|
{
|
|
if (args->argc != 0 && args->argv != NULL) {
|
|
conf->cmd_len = (size_t)(args->argc);
|
|
conf->cmd = (char **)args->argv;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void request_pack_custom_log_options(const struct client_arguments *args, isula_container_config_t *conf)
|
|
{
|
|
if (args->log_file != NULL) {
|
|
conf->log_file = args->log_file;
|
|
}
|
|
if (args->log_file_size != NULL) {
|
|
conf->log_file_size = args->log_file_size;
|
|
}
|
|
conf->log_file_rotate = args->log_file_rotate;
|
|
|
|
return;
|
|
}
|
|
|
|
static int request_pack_custom_log_accel(struct client_arguments *args, isula_container_config_t *conf)
|
|
{
|
|
int ret = 0;
|
|
char *accargs = NULL;
|
|
|
|
if (conf->accel != NULL) {
|
|
accargs = util_string_join(ISULAD_ISULA_ACCEL_ARGS_SEPERATOR, (const char **)conf->accel, conf->accel_len);
|
|
|
|
if (conf->annotations == NULL) {
|
|
conf->annotations = util_common_calloc_s(sizeof(json_map_string_string));
|
|
if (conf->annotations == NULL) {
|
|
COMMAND_ERROR("alloc annotations failed for accel");
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
ret = append_json_map_string_string(conf->annotations, ISULAD_ISULA_ACCEL_ARGS, accargs);
|
|
if (ret != 0) {
|
|
COMMAND_ERROR("init accel annotations failed accel=%s", accargs);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
UTIL_FREE_AND_SET_NULL(accargs);
|
|
}
|
|
|
|
out:
|
|
free(accargs);
|
|
return ret;
|
|
}
|
|
|
|
static void request_pack_custom_work_dir(const struct client_arguments *args, isula_container_config_t *conf)
|
|
{
|
|
/* work dir in container */
|
|
if (args->custom_conf.workdir != NULL) {
|
|
conf->workdir = args->custom_conf.workdir;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static void request_pack_custom_tty(const struct client_arguments *args, isula_container_config_t *conf)
|
|
{
|
|
conf->tty = args->custom_conf.tty;
|
|
conf->open_stdin = args->custom_conf.open_stdin;
|
|
conf->attach_stdin = args->custom_conf.attach_stdin;
|
|
conf->attach_stdout = args->custom_conf.attach_stdout;
|
|
conf->attach_stderr = args->custom_conf.attach_stderr;
|
|
|
|
return;
|
|
}
|
|
|
|
static void request_pack_custom_health_check(const struct client_arguments *args, isula_container_config_t *conf)
|
|
{
|
|
if (args->custom_conf.health_cmd != NULL) {
|
|
conf->health_cmd = args->custom_conf.health_cmd;
|
|
}
|
|
/* health check */
|
|
conf->health_interval = args->custom_conf.health_interval;
|
|
conf->health_timeout = args->custom_conf.health_timeout;
|
|
conf->health_start_period = args->custom_conf.health_start_period;
|
|
conf->health_retries = args->custom_conf.health_retries;
|
|
conf->no_healthcheck = args->custom_conf.no_healthcheck;
|
|
conf->exit_on_unhealthy = args->custom_conf.exit_on_unhealthy;
|
|
|
|
return;
|
|
}
|
|
|
|
static int request_pack_custom_conf(struct client_arguments *args, isula_container_config_t *conf)
|
|
{
|
|
if (args == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
/* append environment variables from env file */
|
|
if (request_pack_custom_env_file(args, conf) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
/* make sure --env has higher priority than --env-file */
|
|
if (request_pack_custom_env(args, conf) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
/* append labels from label file */
|
|
if (request_pack_custom_label_file(args, conf) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
/* make sure --label has higher priority than --label-file */
|
|
if (request_pack_custom_label(args, conf) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
/* user and group */
|
|
request_pack_custom_user(args, conf);
|
|
|
|
request_pack_custom_hostname(args, conf);
|
|
|
|
/* all devices */
|
|
request_pack_custom_all_devices(args, conf);
|
|
|
|
/* system container */
|
|
request_pack_custom_system_container(args, conf);
|
|
|
|
/* mounts to mount filesystem */
|
|
request_pack_custom_mounts(args, conf);
|
|
|
|
/* entrypoint */
|
|
request_pack_custom_entrypoint(args, conf);
|
|
|
|
/* command args */
|
|
request_pack_custom_args(args, conf);
|
|
|
|
/* console log options */
|
|
request_pack_custom_log_options(args, conf);
|
|
|
|
conf->annotations = args->annotations;
|
|
args->annotations = NULL;
|
|
|
|
if (request_pack_custom_log_accel(args, conf) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
/* work dir in container */
|
|
request_pack_custom_work_dir(args, conf);
|
|
|
|
request_pack_custom_tty(args, conf);
|
|
|
|
request_pack_custom_health_check(args, conf);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int request_pack_host_ns_change_files(const struct client_arguments *args, isula_host_config_t *hostconfig)
|
|
{
|
|
int ret = 0;
|
|
size_t i = 0;
|
|
size_t files_len = 0;
|
|
char **files = NULL;
|
|
char *net_files[] = { "/proc/sys/net" };
|
|
char *ipc_files[] = {
|
|
"/proc/sys/kernel/shmmax",
|
|
"/proc/sys/kernel/shmmni",
|
|
"/proc/sys/kernel/shmall",
|
|
"/proc/sys/kernel/shm_rmid_forced",
|
|
"/proc/sys/kernel/msgmax",
|
|
"/proc/sys/kernel/msgmni",
|
|
"/proc/sys/kernel/msgmnb",
|
|
"/proc/sys/kernel/sem",
|
|
"/proc/sys/fs/mqueue"
|
|
};
|
|
char *net_ipc_files[] = {
|
|
"/proc/sys/net",
|
|
"/proc/sys/kernel/shmmax",
|
|
"/proc/sys/kernel/shmmni",
|
|
"/proc/sys/kernel/shmall",
|
|
"/proc/sys/kernel/shm_rmid_forced",
|
|
"/proc/sys/kernel/msgmax",
|
|
"/proc/sys/kernel/msgmni",
|
|
"/proc/sys/kernel/msgmnb",
|
|
"/proc/sys/kernel/sem",
|
|
"/proc/sys/fs/mqueue"
|
|
};
|
|
|
|
if (args->custom_conf.ns_change_opt == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
if (args->custom_conf.privileged) {
|
|
return 0;
|
|
}
|
|
|
|
if (strcmp(args->custom_conf.ns_change_opt, "net") == 0) {
|
|
files = net_files;
|
|
files_len = sizeof(net_files) / sizeof(net_files[0]);
|
|
} else if (strcmp(args->custom_conf.ns_change_opt, "ipc") == 0) {
|
|
files = ipc_files;
|
|
files_len = sizeof(ipc_files) / sizeof(ipc_files[0]);
|
|
} else {
|
|
files = net_ipc_files;
|
|
files_len = sizeof(net_ipc_files) / sizeof(net_ipc_files[0]);
|
|
}
|
|
if (files_len > (SIZE_MAX / sizeof(char *)) - 1) {
|
|
ERROR("Too many files");
|
|
return -1;
|
|
}
|
|
hostconfig->ns_change_files = util_common_calloc_s((files_len + 1) * sizeof(char *));
|
|
if (hostconfig->ns_change_files == NULL) {
|
|
ERROR("Out of memory");
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < files_len; i++) {
|
|
hostconfig->ns_change_files[hostconfig->ns_change_files_len++] = util_strdup_s(files[i]);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void request_pack_host_caps(const struct client_arguments *args, isula_host_config_t *hostconfig)
|
|
{
|
|
/* cap add */
|
|
if (args->custom_conf.cap_adds != NULL) {
|
|
hostconfig->cap_add_len = util_array_len((const char **)(args->custom_conf.cap_adds));
|
|
hostconfig->cap_add = args->custom_conf.cap_adds;
|
|
}
|
|
/* cap drop */
|
|
if (args->custom_conf.cap_drops != NULL) {
|
|
hostconfig->cap_drop_len = util_array_len((const char **)(args->custom_conf.cap_drops));
|
|
hostconfig->cap_drop = args->custom_conf.cap_drops;
|
|
}
|
|
}
|
|
|
|
static void request_pack_host_group_add(const struct client_arguments *args, isula_host_config_t *hostconfig)
|
|
{
|
|
/* group add */
|
|
if (args->custom_conf.group_add != NULL) {
|
|
hostconfig->group_add_len = util_array_len((const char **)(args->custom_conf.group_add));
|
|
hostconfig->group_add = args->custom_conf.group_add;
|
|
}
|
|
}
|
|
|
|
static void request_pack_host_extra_hosts(const struct client_arguments *args, isula_host_config_t *hostconfig)
|
|
{
|
|
/* extra hosts */
|
|
if (args->custom_conf.extra_hosts != NULL) {
|
|
hostconfig->extra_hosts_len = util_array_len((const char **)(args->custom_conf.extra_hosts));
|
|
hostconfig->extra_hosts = args->custom_conf.extra_hosts;
|
|
}
|
|
}
|
|
|
|
static void request_pack_host_dns(const struct client_arguments *args, isula_host_config_t *hostconfig)
|
|
{
|
|
/* dns */
|
|
if (args->custom_conf.dns != NULL) {
|
|
hostconfig->dns_len = util_array_len((const char **)(args->custom_conf.dns));
|
|
hostconfig->dns = args->custom_conf.dns;
|
|
}
|
|
|
|
/* dns options */
|
|
if (args->custom_conf.dns_options != NULL) {
|
|
hostconfig->dns_options_len = util_array_len((const char **)(args->custom_conf.dns_options));
|
|
hostconfig->dns_options = args->custom_conf.dns_options;
|
|
}
|
|
|
|
/* dns search */
|
|
if (args->custom_conf.dns_search != NULL) {
|
|
hostconfig->dns_search_len = util_array_len((const char **)(args->custom_conf.dns_search));
|
|
hostconfig->dns_search = args->custom_conf.dns_search;
|
|
}
|
|
}
|
|
|
|
static void request_pack_host_ulimit(const struct client_arguments *args, isula_host_config_t *hostconfig)
|
|
{
|
|
/* ulimit options */
|
|
if (args->custom_conf.ulimits != NULL) {
|
|
hostconfig->ulimits_len = util_array_len((const char **)(args->custom_conf.ulimits));
|
|
hostconfig->ulimits = args->custom_conf.ulimits;
|
|
}
|
|
}
|
|
|
|
static void request_pack_host_weight_devices(const struct client_arguments *args, isula_host_config_t *hostconfig)
|
|
{
|
|
/* blkio weight devices */
|
|
if (args->custom_conf.weight_devices != NULL) {
|
|
hostconfig->blkio_weight_device_len = util_array_len((const char **)(args->custom_conf.weight_devices));
|
|
hostconfig->blkio_weight_device = args->custom_conf.weight_devices;
|
|
}
|
|
}
|
|
|
|
static void request_pack_host_device_read_bps(const struct client_arguments *args, isula_host_config_t *hostconfig)
|
|
{
|
|
if (args->custom_conf.blkio_throttle_read_bps_device != NULL) {
|
|
hostconfig->blkio_throttle_read_bps_device_len =
|
|
util_array_len((const char **)(args->custom_conf.blkio_throttle_read_bps_device));
|
|
hostconfig->blkio_throttle_read_bps_device = args->custom_conf.blkio_throttle_read_bps_device;
|
|
}
|
|
}
|
|
|
|
static void request_pack_host_device_write_bps(const struct client_arguments *args, isula_host_config_t *hostconfig)
|
|
{
|
|
if (args->custom_conf.blkio_throttle_write_bps_device != NULL) {
|
|
hostconfig->blkio_throttle_write_bps_device_len =
|
|
util_array_len((const char **)(args->custom_conf.blkio_throttle_write_bps_device));
|
|
hostconfig->blkio_throttle_write_bps_device = args->custom_conf.blkio_throttle_write_bps_device;
|
|
}
|
|
}
|
|
|
|
static void request_pack_host_blockio(const struct client_arguments *args, isula_host_config_t *hostconfig)
|
|
{
|
|
request_pack_host_weight_devices(args, hostconfig);
|
|
request_pack_host_device_read_bps(args, hostconfig);
|
|
request_pack_host_device_write_bps(args, hostconfig);
|
|
}
|
|
|
|
static void request_pack_host_devices(const struct client_arguments *args, isula_host_config_t *hostconfig)
|
|
{
|
|
/* devices */
|
|
if (args->custom_conf.devices != NULL) {
|
|
hostconfig->devices_len = util_array_len((const char **)(args->custom_conf.devices));
|
|
hostconfig->devices = args->custom_conf.devices;
|
|
}
|
|
}
|
|
|
|
static void request_pack_host_hugepage_limits(const struct client_arguments *args, isula_host_config_t *hostconfig)
|
|
{
|
|
/* hugepage limits */
|
|
if (args->custom_conf.hugepage_limits != NULL) {
|
|
hostconfig->hugetlbs_len = util_array_len((const char **)(args->custom_conf.hugepage_limits));
|
|
hostconfig->hugetlbs = args->custom_conf.hugepage_limits;
|
|
}
|
|
}
|
|
|
|
static void request_pack_host_binds(const struct client_arguments *args, isula_host_config_t *hostconfig)
|
|
{
|
|
/* volumes to binds */
|
|
if (args->custom_conf.volumes != NULL) {
|
|
hostconfig->binds_len = (size_t)util_array_len((const char **)(args->custom_conf.volumes));
|
|
hostconfig->binds = args->custom_conf.volumes;
|
|
}
|
|
}
|
|
|
|
static void request_pack_host_hook_spec(const struct client_arguments *args, isula_host_config_t *hostconfig)
|
|
{
|
|
/* hook-spec file */
|
|
if (args->custom_conf.hook_spec != NULL) {
|
|
hostconfig->hook_spec = args->custom_conf.hook_spec;
|
|
}
|
|
}
|
|
|
|
static void request_pack_host_restart_policy(const struct client_arguments *args, isula_host_config_t *hostconfig)
|
|
{
|
|
if (args->restart != NULL) {
|
|
hostconfig->restart_policy = args->restart;
|
|
}
|
|
}
|
|
|
|
static void request_pack_host_namespaces(const struct client_arguments *args, isula_host_config_t *hostconfig)
|
|
{
|
|
if (args->host_channel != NULL) {
|
|
hostconfig->host_channel = args->host_channel;
|
|
}
|
|
|
|
if (args->custom_conf.share_ns[NAMESPACE_NET] != NULL) {
|
|
hostconfig->network_mode = args->custom_conf.share_ns[NAMESPACE_NET];
|
|
}
|
|
if (args->custom_conf.share_ns[NAMESPACE_IPC] != NULL) {
|
|
hostconfig->ipc_mode = args->custom_conf.share_ns[NAMESPACE_IPC];
|
|
}
|
|
if (args->custom_conf.share_ns[NAMESPACE_USER] != NULL) {
|
|
hostconfig->userns_mode = args->custom_conf.share_ns[NAMESPACE_USER];
|
|
}
|
|
if (args->custom_conf.share_ns[NAMESPACE_UTS] != NULL) {
|
|
hostconfig->uts_mode = args->custom_conf.share_ns[NAMESPACE_UTS];
|
|
}
|
|
if (args->custom_conf.share_ns[NAMESPACE_PID] != NULL) {
|
|
hostconfig->pid_mode = args->custom_conf.share_ns[NAMESPACE_PID];
|
|
}
|
|
}
|
|
|
|
static void request_pack_host_security(const struct client_arguments *args, isula_host_config_t *hostconfig)
|
|
{
|
|
/* security opt */
|
|
if (args->custom_conf.security != NULL) {
|
|
hostconfig->security_len = util_array_len((const char **)(args->custom_conf.security));
|
|
hostconfig->security = args->custom_conf.security;
|
|
}
|
|
}
|
|
|
|
static int request_pack_host_config(const struct client_arguments *args, isula_host_config_t *hostconfig)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (args == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
/* privileged */
|
|
hostconfig->privileged = args->custom_conf.privileged;
|
|
|
|
/* system container */
|
|
hostconfig->system_container = args->custom_conf.system_container;
|
|
|
|
/* oom kill disable */
|
|
hostconfig->oom_kill_disable = args->custom_conf.oom_kill_disable;
|
|
|
|
/* shm size */
|
|
hostconfig->shm_size = args->custom_conf.shm_size;
|
|
|
|
/* user remap */
|
|
hostconfig->user_remap = args->custom_conf.user_remap;
|
|
|
|
/* auto remove */
|
|
hostconfig->auto_remove = args->custom_conf.auto_remove;
|
|
|
|
/* readonly rootfs */
|
|
hostconfig->readonly_rootfs = args->custom_conf.readonly;
|
|
|
|
/* env target file */
|
|
hostconfig->env_target_file = args->custom_conf.env_target_file;
|
|
|
|
/* cgroup parent */
|
|
hostconfig->cgroup_parent = args->custom_conf.cgroup_parent;
|
|
|
|
ret = request_pack_host_config_cgroup(args, hostconfig);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
/* storage options */
|
|
ret = request_pack_host_config_storage_opts(args, hostconfig);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
/* sysctls */
|
|
ret = request_pack_host_config_sysctls(args, hostconfig);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
ret = request_pack_host_ns_change_files(args, hostconfig);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
|
|
request_pack_host_caps(args, hostconfig);
|
|
|
|
request_pack_host_group_add(args, hostconfig);
|
|
|
|
request_pack_host_extra_hosts(args, hostconfig);
|
|
|
|
request_pack_host_dns(args, hostconfig);
|
|
|
|
request_pack_host_ulimit(args, hostconfig);
|
|
|
|
request_pack_host_blockio(args, hostconfig);
|
|
|
|
request_pack_host_devices(args, hostconfig);
|
|
|
|
request_pack_host_hugepage_limits(args, hostconfig);
|
|
|
|
request_pack_host_binds(args, hostconfig);
|
|
|
|
request_pack_host_hook_spec(args, hostconfig);
|
|
|
|
request_pack_host_restart_policy(args, hostconfig);
|
|
|
|
request_pack_host_namespaces(args, hostconfig);
|
|
|
|
request_pack_host_security(args, hostconfig);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#define IMAGE_NOT_FOUND_ERROR "No such image"
|
|
|
|
static int do_client_create(const struct client_arguments *args, const isula_connect_ops *ops,
|
|
const struct isula_create_request *request, struct isula_create_response *response)
|
|
{
|
|
int ret = 0;
|
|
client_connect_config_t config = get_connect_config(args);
|
|
|
|
ret = ops->container.create(request, response, &config);
|
|
if (ret != 0) {
|
|
if (response->cc == ISULAD_ERR_INPUT) {
|
|
ret = EINVALIDARGS;
|
|
} else if (response->server_errono ||
|
|
(response->errmsg && !strcmp(response->errmsg, errno_to_error_message(ISULAD_ERR_CONNECT)))) {
|
|
ret = ESERVERERROR;
|
|
} else {
|
|
ret = ECOMMON;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int client_try_to_create(const struct client_arguments *args, const struct isula_create_request *request,
|
|
struct isula_create_response **out_response)
|
|
{
|
|
int ret = 0;
|
|
isula_connect_ops *ops = NULL;
|
|
struct isula_create_response *response = NULL;
|
|
|
|
response = util_common_calloc_s(sizeof(struct isula_create_response));
|
|
if (response == NULL) {
|
|
ERROR("Out of memory");
|
|
ret = ECOMMON;
|
|
goto out;
|
|
}
|
|
|
|
ops = get_connect_client_ops();
|
|
if (ops == NULL || ops->container.create == NULL) {
|
|
ERROR("Unimplemented ops");
|
|
ret = ESERVERERROR;
|
|
goto out;
|
|
}
|
|
|
|
ret = do_client_create(args, ops, request, response);
|
|
if (ret != 0) {
|
|
if (response->errmsg == NULL || strstr(response->errmsg, IMAGE_NOT_FOUND_ERROR) == NULL) {
|
|
client_print_error(response->cc, response->server_errono, response->errmsg);
|
|
goto out;
|
|
}
|
|
COMMAND_ERROR("Unable to find image '%s' locally", request->image);
|
|
ret = client_pull(args);
|
|
if (ret != 0) {
|
|
goto out;
|
|
}
|
|
|
|
/* retry create */
|
|
isula_create_response_free(response);
|
|
response = util_common_calloc_s(sizeof(struct isula_create_response));
|
|
if (response == NULL) {
|
|
ERROR("Out of memory");
|
|
ret = ECOMMON;
|
|
goto out;
|
|
}
|
|
ret = do_client_create(args, ops, request, response);
|
|
if (ret != 0) {
|
|
client_print_error(response->cc, response->server_errono, response->errmsg);
|
|
goto out;
|
|
}
|
|
}
|
|
out:
|
|
*out_response = response;
|
|
return ret;
|
|
}
|
|
|
|
static void free_alloced_memory_in_host_config(isula_host_config_t *hostconfig)
|
|
{
|
|
isula_ns_change_files_free(hostconfig);
|
|
isula_host_config_storage_opts_free(hostconfig);
|
|
isula_host_config_sysctl_free(hostconfig);
|
|
}
|
|
|
|
static void free_alloced_memory_in_config(isula_container_config_t *custom_conf)
|
|
{
|
|
if (custom_conf == NULL) {
|
|
return;
|
|
}
|
|
|
|
free_json_map_string_string(custom_conf->annotations);
|
|
custom_conf->annotations = NULL;
|
|
}
|
|
|
|
/*
|
|
* Create a create request message and call RPC
|
|
*/
|
|
int client_create(struct client_arguments *args)
|
|
{
|
|
int ret = 0;
|
|
struct isula_create_request request = { 0 };
|
|
struct isula_create_response *response = NULL;
|
|
isula_container_config_t custom_conf = { 0 };
|
|
isula_host_config_t host_config = { 0 };
|
|
container_cgroup_resources_t cr = { 0 };
|
|
|
|
request.name = args->name;
|
|
request.rootfs = args->create_rootfs;
|
|
request.runtime = args->runtime;
|
|
request.image = args->image_name;
|
|
request.hostconfig = &host_config;
|
|
request.config = &custom_conf;
|
|
host_config.cr = &cr;
|
|
|
|
ret = request_pack_custom_conf(args, request.config);
|
|
if (ret != 0) {
|
|
goto out;
|
|
}
|
|
|
|
ret = request_pack_host_config(args, request.hostconfig);
|
|
if (ret != 0) {
|
|
goto out;
|
|
}
|
|
|
|
ret = client_try_to_create(args, &request, &response);
|
|
if (ret != 0) {
|
|
goto out;
|
|
}
|
|
|
|
if (response->id != NULL) {
|
|
free(args->name);
|
|
args->name = util_strdup_s(response->id);
|
|
} else {
|
|
ERROR("Container id create failed.");
|
|
ret = ESERVERERROR;
|
|
goto out;
|
|
}
|
|
out:
|
|
free_alloced_memory_in_host_config(request.hostconfig);
|
|
free_alloced_memory_in_config(request.config);
|
|
isula_create_response_free(response);
|
|
return ret;
|
|
}
|
|
|
|
static int log_opt_parse_options(struct client_arguments *args, const char *optkey, const char *value)
|
|
{
|
|
int ret = -1;
|
|
|
|
if (strcmp(optkey, "max-size") == 0) {
|
|
args->log_file_size = util_strdup_s(value);
|
|
ret = 0;
|
|
} else if (strcmp(optkey, "max-file") == 0) {
|
|
if (util_safe_uint(value, &args->log_file_rotate)) {
|
|
goto out;
|
|
}
|
|
if (args->log_file_rotate == 0) {
|
|
COMMAND_ERROR("Invalid option 'max-file' key:%s, value:%s", optkey, value);
|
|
goto out;
|
|
}
|
|
ret = 0;
|
|
} else if (strcmp(optkey, "disable-log") == 0) {
|
|
if (strcmp(value, "true") == 0) {
|
|
args->log_file = util_strdup_s("none");
|
|
ret = 0;
|
|
} else if (strcmp(value, "false") == 0) {
|
|
args->log_file = NULL;
|
|
ret = 0;
|
|
} else {
|
|
COMMAND_ERROR("Invalid option 'disable-log' key:%s, value:%s", optkey, value);
|
|
}
|
|
}
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
int log_opt_parser(struct client_arguments *args, const char *option)
|
|
{
|
|
int ret = -1;
|
|
char *optkey = NULL;
|
|
char *value = NULL;
|
|
char *tmp = NULL;
|
|
size_t len;
|
|
size_t total_len;
|
|
|
|
if (option == NULL || args == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
tmp = util_strdup_s(option);
|
|
|
|
// log option format: key=value
|
|
total_len = strlen(tmp);
|
|
if (total_len <= 2) {
|
|
goto out;
|
|
}
|
|
|
|
optkey = tmp;
|
|
value = strchr(tmp, '=');
|
|
// option do not contain '='
|
|
if (value == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
len = (size_t)(value - optkey);
|
|
// if option is 'optkey='
|
|
if (total_len == len + 1) {
|
|
goto out;
|
|
}
|
|
|
|
// if option is '=optkey'
|
|
if (len == 0) {
|
|
goto out;
|
|
}
|
|
|
|
tmp[len] = '\0';
|
|
value += 1;
|
|
|
|
ret = log_opt_parse_options(args, optkey, value);
|
|
|
|
out:
|
|
if (ret < 0) {
|
|
COMMAND_ERROR("Invalid option: %s", option);
|
|
}
|
|
free(tmp);
|
|
|
|
return ret;
|
|
}
|
|
int callback_log_opt(command_option_t *option, const char *value)
|
|
{
|
|
struct client_arguments *args = (struct client_arguments *)option->data;
|
|
return log_opt_parser(args, value);
|
|
}
|
|
|
|
static int annotation_parser(struct client_arguments *args, const char *option)
|
|
{
|
|
int ret = -1;
|
|
char *optkey = NULL;
|
|
char *value = NULL;
|
|
char *tmp = NULL;
|
|
|
|
if (args == NULL || option == NULL) {
|
|
goto out;
|
|
}
|
|
|
|
// annotation format: key[=][value]
|
|
tmp = util_strdup_s(option);
|
|
|
|
optkey = tmp;
|
|
value = strchr(tmp, '=');
|
|
|
|
if (value != NULL) {
|
|
*value = '\0';
|
|
value++;
|
|
} else {
|
|
value = "";
|
|
}
|
|
|
|
if (optkey[0] == '\0') {
|
|
goto out;
|
|
}
|
|
|
|
if (args->annotations == NULL) {
|
|
args->annotations = util_common_calloc_s(sizeof(json_map_string_string));
|
|
if (args->annotations == NULL) {
|
|
COMMAND_ERROR("Out Memory");
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (append_json_map_string_string(args->annotations, optkey, value)) {
|
|
COMMAND_ERROR("Failed to append annotation key:%s, value:%s", optkey, value);
|
|
goto out;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
out:
|
|
if (ret < 0) {
|
|
COMMAND_ERROR("Invalid option: '%s'", option);
|
|
}
|
|
free(tmp);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int callback_annotation(command_option_t *option, const char *value)
|
|
{
|
|
struct client_arguments *args = (struct client_arguments *)option->data;
|
|
return annotation_parser(args, value);
|
|
}
|
|
|
|
int cmd_create_main(int argc, const char **argv)
|
|
{
|
|
int nret = 0;
|
|
int ret = 0;
|
|
command_t cmd = { 0 };
|
|
struct log_config lconf = { 0 };
|
|
|
|
if (client_arguments_init(&g_cmd_create_args)) {
|
|
COMMAND_ERROR("client arguments init failed");
|
|
exit(ECOMMON);
|
|
}
|
|
g_cmd_create_args.progname = argv[0];
|
|
g_cmd_create_args.subcommand = argv[1];
|
|
set_default_command_log_config(argv[0], &lconf);
|
|
struct command_option options[] = {
|
|
LOG_OPTIONS(lconf),
|
|
CREATE_OPTIONS(g_cmd_create_args),
|
|
CREATE_EXTEND_OPTIONS(g_cmd_create_args),
|
|
COMMON_OPTIONS(g_cmd_create_args)
|
|
};
|
|
|
|
command_init(&cmd, options, sizeof(options) / sizeof(options[0]), argc, (const char **)argv, g_cmd_create_desc,
|
|
g_cmd_create_usage);
|
|
if (command_parse_args(&cmd, &g_cmd_create_args.argc, &g_cmd_create_args.argv) ||
|
|
create_checker(&g_cmd_create_args)) {
|
|
nret = EINVALIDARGS;
|
|
goto out;
|
|
}
|
|
if (log_init(&lconf)) {
|
|
COMMAND_ERROR("log init failed");
|
|
exit(ECOMMON);
|
|
}
|
|
|
|
ret = client_create(&g_cmd_create_args);
|
|
if (ret != 0) {
|
|
ERROR("Container \"%s\" create failed", g_cmd_create_args.name);
|
|
nret = ECOMMON;
|
|
goto out;
|
|
}
|
|
printf("%s\n", g_cmd_create_args.name);
|
|
nret = EXIT_SUCCESS;
|
|
out:
|
|
exit(nret);
|
|
}
|
|
|
|
static int check_parsed_devices(const char *devices, const char *cgroup_permissions, const char *path_in_container)
|
|
{
|
|
int ret = 0;
|
|
int nret = 0;
|
|
|
|
/* check valid device mode */
|
|
if (!util_valid_device_mode(cgroup_permissions)) {
|
|
COMMAND_ERROR("Invalid value \"%s\" for flag --device: bad mode specified: %s", devices, cgroup_permissions);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
/* check valid path in container */
|
|
nret = util_validate_absolute_path(path_in_container);
|
|
if (nret != 0) {
|
|
COMMAND_ERROR("Invalid value \"%s\" for flag --device: %s is not an absolute path", devices, path_in_container);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static bool check_devices_conf_valid(const char *devices)
|
|
{
|
|
bool ret = true;
|
|
size_t tmp_str_len = 0;
|
|
char **tmp_str = NULL;
|
|
char *cgroup_permissions = NULL;
|
|
char *path_in_container = NULL;
|
|
|
|
if (devices == NULL || !strcmp(devices, "")) {
|
|
COMMAND_ERROR("Invalid value \"%s\" for flag --device", devices ? devices : "null");
|
|
return false;
|
|
}
|
|
|
|
tmp_str = util_string_split(devices, ':');
|
|
if (tmp_str == NULL) {
|
|
ERROR("Out of memory");
|
|
ret = false;
|
|
goto out;
|
|
}
|
|
tmp_str_len = util_array_len((const char **)tmp_str);
|
|
|
|
// device format: x:x:x or x:x or x
|
|
switch (tmp_str_len) {
|
|
case 3:
|
|
path_in_container = tmp_str[1];
|
|
cgroup_permissions = tmp_str[2];
|
|
break;
|
|
case 2:
|
|
if (util_valid_device_mode(tmp_str[1])) {
|
|
path_in_container = tmp_str[0];
|
|
cgroup_permissions = tmp_str[1];
|
|
} else {
|
|
path_in_container = tmp_str[1];
|
|
cgroup_permissions = "rwm";
|
|
}
|
|
break;
|
|
case 1:
|
|
path_in_container = tmp_str[0];
|
|
cgroup_permissions = "rwm";
|
|
break;
|
|
default:
|
|
COMMAND_ERROR("Invalid value \"%s\" for flag --device\n", devices);
|
|
ret = false;
|
|
break;
|
|
}
|
|
if (!ret) {
|
|
goto out;
|
|
}
|
|
|
|
/* check valid device */
|
|
if (check_parsed_devices(devices, cgroup_permissions, path_in_container) != 0) {
|
|
ret = false;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
util_free_array(tmp_str);
|
|
return ret;
|
|
}
|
|
|
|
static bool check_volumes_valid(const char *volume)
|
|
{
|
|
bool ret = true;
|
|
size_t alen = 0;
|
|
char **array = NULL;
|
|
char **modes = NULL;
|
|
|
|
// split volume to src:dest:mode
|
|
array = util_string_split(volume, ':');
|
|
if (array == NULL) {
|
|
COMMAND_ERROR("Out of memory");
|
|
ret = false;
|
|
goto free_out;
|
|
}
|
|
alen = util_array_len((const char **)array);
|
|
|
|
// volume format: src:dst:mode
|
|
switch (alen) {
|
|
case 1:
|
|
COMMAND_ERROR("Not supported volume format '%s'", volume);
|
|
ret = false;
|
|
goto free_out;
|
|
/* fall-through */
|
|
case 2:
|
|
if (util_valid_mount_mode(array[1])) {
|
|
// Destination + Mode is not a valid volume - volumes
|
|
// cannot include a mode. eg /foo:rw
|
|
COMMAND_ERROR("Invalid volume specification '%s',Invalid mode:%s", volume, array[1]);
|
|
ret = false;
|
|
goto free_out;
|
|
}
|
|
break;
|
|
case 3:
|
|
if (!util_valid_mount_mode(array[2])) {
|
|
COMMAND_ERROR("Invalid volume specification '%s'.Invalid mode:%s", volume, array[2]);
|
|
ret = false;
|
|
goto free_out;
|
|
}
|
|
modes = util_string_split(array[2], ',');
|
|
if (modes == NULL) {
|
|
ERROR("Out of memory");
|
|
ret = false;
|
|
goto free_out;
|
|
}
|
|
break;
|
|
default:
|
|
COMMAND_ERROR("Invalid volume specification '%s'", volume);
|
|
ret = false;
|
|
goto free_out;
|
|
}
|
|
|
|
if (array[0][0] != '/' || array[1][0] != '/' || strcmp(array[1], "/") == 0) {
|
|
COMMAND_ERROR("Invalid volume: path must be absolute, and destination can't be '/'");
|
|
ret = false;
|
|
goto free_out;
|
|
}
|
|
|
|
free_out:
|
|
util_free_array(array);
|
|
util_free_array(modes);
|
|
return ret;
|
|
}
|
|
|
|
static bool check_volumes_conf_valid(const char *volume)
|
|
{
|
|
if (volume == NULL || !strcmp(volume, "")) {
|
|
COMMAND_ERROR("Volume can't be empty");
|
|
return false;
|
|
}
|
|
|
|
if (volume[0] == ':' || volume[strlen(volume) - 1] == ':') {
|
|
COMMAND_ERROR("Delimiter ':' can't be the first or the last character");
|
|
return false;
|
|
}
|
|
|
|
return check_volumes_valid(volume);
|
|
}
|
|
|
|
struct valid_mounts_state {
|
|
char *mount;
|
|
bool has_type;
|
|
bool has_src;
|
|
bool has_dst;
|
|
bool type_squashfs;
|
|
char *source;
|
|
};
|
|
|
|
#define MOUNT_STATE_CHECK_SUCCESS 0
|
|
#define MOUNT_STATE_CHECK_IGNORE 1
|
|
#define MOUNT_STATE_CHECK_INVALID_ARG 2
|
|
|
|
static int parse_mount_item_type(const char *value, struct valid_mounts_state *state)
|
|
{
|
|
/* If value of type is NULL, ignore it */
|
|
if (value == NULL) {
|
|
return MOUNT_STATE_CHECK_IGNORE;
|
|
}
|
|
|
|
if (state->has_type) {
|
|
COMMAND_ERROR("Invalid mount specification '%s'.More than one type found", state->mount);
|
|
return MOUNT_STATE_CHECK_INVALID_ARG;
|
|
}
|
|
|
|
if (strcmp(value, "squashfs") && strcmp(value, "bind")) {
|
|
COMMAND_ERROR("Invalid mount specification '%s'.Type must be squashfs or bind", state->mount);
|
|
return MOUNT_STATE_CHECK_INVALID_ARG;
|
|
}
|
|
|
|
if (strcmp(value, "squashfs") == 0) {
|
|
state->type_squashfs = true;
|
|
}
|
|
|
|
state->has_type = true;
|
|
return MOUNT_STATE_CHECK_SUCCESS;
|
|
}
|
|
|
|
static int parse_mount_item_src(const char *value, struct valid_mounts_state *state)
|
|
{
|
|
/* If value of source is NULL, ignore it */
|
|
if (value == NULL) {
|
|
return MOUNT_STATE_CHECK_IGNORE;
|
|
}
|
|
|
|
if (state->has_src) {
|
|
COMMAND_ERROR("Invalid mount specification '%s'.More than one source found", state->mount);
|
|
return MOUNT_STATE_CHECK_INVALID_ARG;
|
|
}
|
|
|
|
if (value[0] != '/') {
|
|
COMMAND_ERROR("Invalid mount specification '%s'.Source must be absolute path", state->mount);
|
|
return MOUNT_STATE_CHECK_INVALID_ARG;
|
|
}
|
|
|
|
free(state->source);
|
|
state->source = util_strdup_s(value);
|
|
|
|
state->has_src = true;
|
|
return MOUNT_STATE_CHECK_SUCCESS;
|
|
}
|
|
|
|
static int parse_mount_item_dst(const char *value, struct valid_mounts_state *state)
|
|
{
|
|
char dstpath[PATH_MAX] = { 0 };
|
|
|
|
/* If value of destination is NULL, ignore it */
|
|
if (value == NULL) {
|
|
return MOUNT_STATE_CHECK_IGNORE;
|
|
}
|
|
|
|
if (state->has_dst) {
|
|
COMMAND_ERROR("Invalid mount specification '%s'.More than one destination found", state->mount);
|
|
return MOUNT_STATE_CHECK_INVALID_ARG;
|
|
}
|
|
|
|
if (value[0] != '/') {
|
|
COMMAND_ERROR("Invalid mount specification '%s'.Destination must be absolute path", state->mount);
|
|
return MOUNT_STATE_CHECK_INVALID_ARG;
|
|
}
|
|
|
|
if (strcmp(value, "/") == 0) {
|
|
COMMAND_ERROR("Invalid mount specification '%s'.Destination can't be '/'", state->mount);
|
|
return MOUNT_STATE_CHECK_INVALID_ARG;
|
|
}
|
|
|
|
if (!cleanpath(value, dstpath, sizeof(dstpath))) {
|
|
COMMAND_ERROR("Invalid mount specification '%s'.Can't translate destination path to clean path", state->mount);
|
|
return MOUNT_STATE_CHECK_INVALID_ARG;
|
|
}
|
|
|
|
state->has_dst = true;
|
|
return MOUNT_STATE_CHECK_SUCCESS;
|
|
}
|
|
|
|
static int parse_mount_item_ro(const char *value, const struct valid_mounts_state *state)
|
|
{
|
|
if (value != NULL) {
|
|
if (strcmp(value, "1") && strcmp(value, "true") && strcmp(value, "0") && strcmp(value, "false")) {
|
|
COMMAND_ERROR("Invalid mount specification '%s'.Invalid readonly mode:%s", state->mount, value);
|
|
return MOUNT_STATE_CHECK_INVALID_ARG;
|
|
}
|
|
}
|
|
return MOUNT_STATE_CHECK_SUCCESS;
|
|
}
|
|
|
|
static int parse_mount_item_propagation(const char *value, const struct valid_mounts_state *state)
|
|
{
|
|
if (value == NULL) {
|
|
return MOUNT_STATE_CHECK_IGNORE;
|
|
}
|
|
|
|
if (!util_valid_propagation_mode(value)) {
|
|
COMMAND_ERROR("Invalid mount specification '%s'.Invalid propagation mode:%s", state->mount, value);
|
|
return MOUNT_STATE_CHECK_INVALID_ARG;
|
|
}
|
|
return MOUNT_STATE_CHECK_SUCCESS;
|
|
}
|
|
|
|
static int parse_mount_item_selinux(const char *value, const struct valid_mounts_state *state)
|
|
{
|
|
if (value == NULL) {
|
|
return MOUNT_STATE_CHECK_IGNORE;
|
|
}
|
|
|
|
if (!util_valid_label_mode(value)) {
|
|
COMMAND_ERROR("Invalid mount specification '%s'.Invalid bind selinux opts:%s", state->mount, value);
|
|
return MOUNT_STATE_CHECK_INVALID_ARG;
|
|
}
|
|
return MOUNT_STATE_CHECK_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* 0: success
|
|
* 1: ignore this item, continue
|
|
* 2: failed
|
|
*/
|
|
static int valid_mounts_item(const char *mntkey, const char *value, struct valid_mounts_state *state)
|
|
{
|
|
if (strcmp(mntkey, "type") == 0) {
|
|
return parse_mount_item_type(value, state);
|
|
} else if (strcmp(mntkey, "src") == 0 || strcmp(mntkey, "source") == 0) {
|
|
return parse_mount_item_src(value, state);
|
|
} else if (strcmp(mntkey, "dst") == 0 || strcmp(mntkey, "destination") == 0) {
|
|
return parse_mount_item_dst(value, state);
|
|
} else if (strcmp(mntkey, "ro") == 0 || strcmp(mntkey, "readonly") == 0) {
|
|
return parse_mount_item_ro(value, state);
|
|
} else if (strcmp(mntkey, "bind-propagation") == 0) {
|
|
return parse_mount_item_propagation(value, state);
|
|
} else if (strcmp(mntkey, "bind-selinux-opts") == 0) {
|
|
return parse_mount_item_selinux(value, state);
|
|
} else {
|
|
COMMAND_ERROR("Invalid mount specification '%s'.Unsupported item:%s", state->mount, mntkey);
|
|
return MOUNT_STATE_CHECK_INVALID_ARG;
|
|
}
|
|
}
|
|
|
|
static int parse_mounts_conf(const char *mount, struct valid_mounts_state *state)
|
|
{
|
|
int ret = 0;
|
|
size_t i = 0;
|
|
size_t items_len = 0;
|
|
char **items = NULL;
|
|
char **key_val = NULL;
|
|
|
|
items = util_string_split(mount, ',');
|
|
if (items == NULL) {
|
|
ret = EINVALIDARGS;
|
|
COMMAND_ERROR("Invalid mount specification '%s'. unsupported format", mount);
|
|
goto out;
|
|
}
|
|
|
|
items_len = util_array_len((const char **)items);
|
|
|
|
for (i = 0; i < items_len; i++) {
|
|
key_val = util_string_split(items[i], '=');
|
|
if (key_val == NULL) {
|
|
continue;
|
|
}
|
|
|
|
ret = valid_mounts_item(key_val[0], key_val[1], state);
|
|
if (ret == MOUNT_STATE_CHECK_IGNORE) { /* ignore this item */
|
|
ret = 0;
|
|
util_free_array(key_val);
|
|
key_val = NULL;
|
|
continue;
|
|
} else if (ret == MOUNT_STATE_CHECK_INVALID_ARG) { /* invalid args */
|
|
ret = EINVALIDARGS;
|
|
goto out;
|
|
}
|
|
util_free_array(key_val);
|
|
key_val = NULL;
|
|
}
|
|
|
|
out:
|
|
util_free_array(key_val);
|
|
util_free_array(items);
|
|
return ret;
|
|
}
|
|
|
|
static int check_parsed_mounts_conf(const char *mount, const struct valid_mounts_state *state)
|
|
{
|
|
int ret = 0;
|
|
char real_path[PATH_MAX] = { 0 }; /* Init to zero every time loop enter here. */
|
|
|
|
if (!state->has_type) {
|
|
ret = EINVALIDARGS;
|
|
COMMAND_ERROR("Invalid mount specification '%s'.Missing type", mount);
|
|
goto out;
|
|
}
|
|
|
|
if (!state->has_src) {
|
|
ret = EINVALIDARGS;
|
|
COMMAND_ERROR("Invalid mount specification '%s'.Missing source", mount);
|
|
goto out;
|
|
}
|
|
|
|
if (!state->has_dst) {
|
|
ret = EINVALIDARGS;
|
|
COMMAND_ERROR("Invalid mount specification '%s'.Missing destination", mount);
|
|
goto out;
|
|
}
|
|
|
|
if (state->type_squashfs) {
|
|
if (strlen(state->source) > PATH_MAX || realpath(state->source, real_path) == NULL) {
|
|
ret = EINVALIDARGS;
|
|
COMMAND_ERROR("Invalid mount specification '%s'.Source %s not exist", mount, state->source);
|
|
goto out;
|
|
}
|
|
|
|
/* Make sure it's a regular file */
|
|
if (!util_valid_file(real_path, S_IFREG)) {
|
|
ret = EINVALIDARGS;
|
|
COMMAND_ERROR("Invalid mount specification '%s'.Source %s is not a squashfs file", mount, state->source);
|
|
goto out;
|
|
}
|
|
}
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static bool check_mounts_conf_valid(const char *mount)
|
|
{
|
|
int ret = 0;
|
|
struct valid_mounts_state state = { (char *)mount, false, false, false, NULL };
|
|
|
|
if (mount == NULL) {
|
|
COMMAND_ERROR("Invalid mount specification: can't be empty");
|
|
return false;
|
|
}
|
|
if (!mount[0]) {
|
|
COMMAND_ERROR("Invalid mount specification: can't be empty");
|
|
return false;
|
|
}
|
|
|
|
ret = parse_mounts_conf(mount, &state);
|
|
if (ret != 0) {
|
|
goto out;
|
|
}
|
|
|
|
ret = check_parsed_mounts_conf(mount, &state);
|
|
if (ret != 0) {
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
free(state.source);
|
|
return ret ? false : true;
|
|
}
|
|
|
|
static int check_hook_spec_file(const char *hook_spec)
|
|
{
|
|
struct stat hookstat = { 0 };
|
|
|
|
if (hook_spec == NULL) {
|
|
return 0;
|
|
}
|
|
if (util_validate_absolute_path(hook_spec)) {
|
|
COMMAND_ERROR("Hook path \"%s\" must be an absolute path", hook_spec);
|
|
return -1;
|
|
}
|
|
if (stat(hook_spec, &hookstat)) {
|
|
COMMAND_ERROR("Stat hook spec file failed: %s", strerror(errno));
|
|
return -1;
|
|
}
|
|
if ((hookstat.st_mode & S_IFMT) != S_IFREG) {
|
|
COMMAND_ERROR("Hook spec file must be a regular text file");
|
|
return -1;
|
|
}
|
|
|
|
if (hookstat.st_size > REGULAR_FILE_SIZE) {
|
|
COMMAND_ERROR("Hook spec file size %llu exceed limit: %dM", (unsigned long long)hookstat.st_size,
|
|
(int)(REGULAR_FILE_SIZE / SIZE_MB));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int create_check_rootfs(struct client_arguments *args)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (args->external_rootfs != NULL) {
|
|
args->create_rootfs = util_strdup_s(args->external_rootfs);
|
|
} else {
|
|
if (strcmp(args->argv[0], "none:latest") == 0 || strcmp(args->argv[0], "none") == 0) {
|
|
char *rootfs = getenv("IMAGE_NONE_PATH");
|
|
if (rootfs != NULL) {
|
|
args->create_rootfs = util_strdup_s(rootfs);
|
|
} else {
|
|
args->create_rootfs = util_strdup_s(DEFAULT_ROOTFS_PATH);
|
|
}
|
|
}
|
|
}
|
|
|
|
args->image_name = args->argv[0];
|
|
|
|
args->argc--;
|
|
args->argv++;
|
|
|
|
if (args->create_rootfs != NULL) {
|
|
char real_path[PATH_MAX] = { 0 };
|
|
if (realpath(args->create_rootfs, real_path) == NULL) {
|
|
COMMAND_ERROR("Failed to get rootfs '%s': %s", args->create_rootfs, strerror(errno));
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
free(args->create_rootfs);
|
|
args->create_rootfs = util_strdup_s(real_path);
|
|
}
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int create_check_hugetlbs(const struct client_arguments *args)
|
|
{
|
|
int ret = 0;
|
|
size_t len, i;
|
|
|
|
len = util_array_len((const char **)(args->custom_conf.hugepage_limits));
|
|
for (i = 0; i < len; i++) {
|
|
char *limit = NULL;
|
|
int64_t limitvalue;
|
|
char *dup = NULL;
|
|
char *p = NULL;
|
|
char *pdot2 = NULL;
|
|
|
|
dup = util_strdup_s(args->custom_conf.hugepage_limits[i]);
|
|
|
|
p = dup;
|
|
p = strchr(p, ':');
|
|
if (p == NULL) {
|
|
limit = dup;
|
|
} else {
|
|
*p = '\0';
|
|
p++;
|
|
pdot2 = strchr(p, ':');
|
|
if (pdot2 != NULL) {
|
|
COMMAND_ERROR("Invalid arguments \"%s\" for flag --hugetlb-limit: too many colons",
|
|
args->custom_conf.hugepage_limits[i]);
|
|
free(dup);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
limit = p;
|
|
}
|
|
ret = util_parse_byte_size_string(limit, &limitvalue);
|
|
if (ret != 0) {
|
|
COMMAND_ERROR("Invalid hugetlb limit:%s:%s", limit, strerror(-ret));
|
|
free(dup);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
free(dup);
|
|
}
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int create_check_network(const struct client_arguments *args)
|
|
{
|
|
size_t len, i;
|
|
struct sockaddr_in sa;
|
|
|
|
len = util_array_len((const char **)(args->custom_conf.extra_hosts));
|
|
for (i = 0; i < len; i++) {
|
|
char **items = NULL;
|
|
items = util_string_split(args->custom_conf.extra_hosts[i], ':');
|
|
if (items == NULL) {
|
|
COMMAND_ERROR("split extra hosts '%s' failed.", args->custom_conf.extra_hosts[i]);
|
|
return -1;
|
|
}
|
|
if (util_array_len((const char **)items) != 2) {
|
|
util_free_array(items);
|
|
COMMAND_ERROR("Invalid extra hosts specification '%s'. unsupported format",
|
|
args->custom_conf.extra_hosts[i]);
|
|
return EINVALIDARGS;
|
|
}
|
|
if (!inet_pton(AF_INET, items[1], &sa.sin_addr)) {
|
|
COMMAND_ERROR("Invalid host ip address '%s'.", items[1]);
|
|
util_free_array(items);
|
|
return EINVALIDARGS;
|
|
}
|
|
util_free_array(items);
|
|
}
|
|
len = util_array_len((const char **)(args->custom_conf.dns));
|
|
for (i = 0; i < len; i++) {
|
|
if (!inet_pton(AF_INET, args->custom_conf.dns[i], &sa.sin_addr)) {
|
|
COMMAND_ERROR("Invalid dns ip address '%s'.", args->custom_conf.dns[i]);
|
|
return EINVALIDARGS;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int create_hostname_checker(const struct client_arguments *args)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (args->custom_conf.hostname != NULL) {
|
|
if (!util_valid_host_name(args->custom_conf.hostname)) {
|
|
COMMAND_ERROR("Invalid container hostname (%s), only %s and less than 64 bytes are allowed.",
|
|
args->custom_conf.hostname, HOST_NAME_REGEXP);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
}
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int create_name_checker(const struct client_arguments *args)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (args->name != NULL && !util_valid_container_name(args->name)) {
|
|
COMMAND_ERROR("Invalid container name (%s), only [a-zA-Z0-9][a-zA-Z0-9_.-] are allowed.", args->name);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int create_devices_volumes_checker(const struct client_arguments *args)
|
|
{
|
|
int ret = 0;
|
|
size_t i;
|
|
size_t len = 0;
|
|
|
|
len = util_array_len((const char **)(args->custom_conf.devices));
|
|
for (i = 0; i < len; i++) {
|
|
if (!check_devices_conf_valid(args->custom_conf.devices[i])) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
}
|
|
len = util_array_len((const char **)(args->custom_conf.volumes));
|
|
for (i = 0; i < len; i++) {
|
|
if (!check_volumes_conf_valid(args->custom_conf.volumes[i])) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
}
|
|
len = util_array_len((const char **)(args->custom_conf.mounts));
|
|
for (i = 0; i < len; i++) {
|
|
if (!check_mounts_conf_valid(args->custom_conf.mounts[i])) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int create_namespaces_checker(const struct client_arguments *args)
|
|
{
|
|
int ret = 0;
|
|
const char *net_mode = args->custom_conf.share_ns[NAMESPACE_NET];
|
|
|
|
if (args->custom_conf.share_ns[NAMESPACE_NET]) {
|
|
if (!is_host(net_mode) && !is_container(net_mode) && !is_none(net_mode)) {
|
|
COMMAND_ERROR("Unsupported network mode %s", net_mode);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
}
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int create_check_user_remap(const struct client_arguments *args)
|
|
{
|
|
char *user_remap = args->custom_conf.user_remap;
|
|
unsigned int host_uid = 0;
|
|
unsigned int host_gid = 0;
|
|
unsigned int size = 0;
|
|
|
|
if (user_remap == NULL) {
|
|
return 0;
|
|
}
|
|
if (args->custom_conf.privileged || !args->custom_conf.system_container || args->external_rootfs == NULL) {
|
|
COMMAND_ERROR("--user-remap only available for system container");
|
|
return -1;
|
|
}
|
|
return util_parse_user_remap(user_remap, &host_uid, &host_gid, &size);
|
|
}
|
|
|
|
static int create_check_nschangeopt(const struct client_arguments *args)
|
|
{
|
|
size_t array_str_len;
|
|
size_t i;
|
|
char **array_str = NULL;
|
|
|
|
if (args->custom_conf.ns_change_opt == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
if (!args->custom_conf.system_container) {
|
|
COMMAND_ERROR("Unsupported ns-change-opt param in normal container");
|
|
return EINVALIDARGS;
|
|
}
|
|
|
|
array_str = util_string_split(args->custom_conf.ns_change_opt, ',');
|
|
if (array_str == NULL) {
|
|
ERROR("Out of memory");
|
|
return EINVALIDARGS;
|
|
}
|
|
array_str_len = util_array_len((const char **)array_str);
|
|
if (array_str_len != 1 && array_str_len != 2) {
|
|
ERROR("invalid ns-change-opt pararm:%s\n", args->custom_conf.ns_change_opt);
|
|
util_free_array(array_str);
|
|
return EINVALIDARGS;
|
|
}
|
|
|
|
for (i = 0; i < array_str_len; i++) {
|
|
if ((strcmp(array_str[i], "net") != 0) && (strcmp(array_str[i], "ipc") != 0)) {
|
|
ERROR("invalid ns-change-opt pararm:%s\n", args->custom_conf.ns_change_opt);
|
|
util_free_array(array_str);
|
|
return EINVALIDARGS;
|
|
}
|
|
}
|
|
|
|
util_free_array(array_str);
|
|
return 0;
|
|
}
|
|
|
|
static int create_check_oomkilldisable(const struct client_arguments *args)
|
|
{
|
|
if (args->custom_conf.oom_kill_disable && args->cr.memory_limit == 0) {
|
|
COMMAND_ERROR("WARNING: Disabling the OOM killer on containers without " \
|
|
"setting a '-m/--memory' limit may be dangerous.");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void restore_to_equate(char *p)
|
|
{
|
|
*p = '=';
|
|
}
|
|
|
|
static bool do_create_check_sysctl(const char *sysctl)
|
|
{
|
|
char *p = NULL;
|
|
|
|
p = strchr(sysctl, '=');
|
|
if (p != NULL) {
|
|
*p = '\0';
|
|
if (strcmp("kernel.pid_max", sysctl) == 0) {
|
|
if (!pid_max_kernel_namespaced()) {
|
|
COMMAND_ERROR("Sysctl '%s' is not kernel namespaced, it cannot be changed", sysctl);
|
|
restore_to_equate(p);
|
|
return false;
|
|
} else {
|
|
restore_to_equate(p);
|
|
return true;
|
|
}
|
|
}
|
|
if (!check_sysctl_valid(sysctl)) {
|
|
restore_to_equate(p);
|
|
COMMAND_ERROR("Sysctl '%s' is not whitelist", sysctl);
|
|
return false;
|
|
}
|
|
restore_to_equate(p);
|
|
} else {
|
|
COMMAND_ERROR("Invalid sysctl option '%s'", sysctl);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static int create_check_sysctl(const struct client_arguments *args)
|
|
{
|
|
size_t i = 0;
|
|
size_t len = 0;
|
|
|
|
if (args->custom_conf.sysctls == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
len = util_array_len((const char **)(args->custom_conf.sysctls));
|
|
for (i = 0; i < len; i++) {
|
|
if (!do_create_check_sysctl((const char *)args->custom_conf.sysctls[i])) {
|
|
return -1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int create_check_env_target_file(const struct client_arguments *args)
|
|
{
|
|
int ret = 0;
|
|
int64_t file_size = 0;
|
|
char *env_path = NULL;
|
|
char *env_target_file = args->custom_conf.env_target_file;
|
|
|
|
if (env_target_file == NULL) {
|
|
return 0;
|
|
}
|
|
if (env_target_file[0] != '/') {
|
|
COMMAND_ERROR("env target file path must be absolute path");
|
|
return -1;
|
|
}
|
|
if (args->external_rootfs == NULL) {
|
|
COMMAND_ERROR("external rootfs not specified");
|
|
return 0;
|
|
}
|
|
if (realpath_in_scope(args->external_rootfs, env_target_file, &env_path) < 0) {
|
|
COMMAND_ERROR("env target file '%s' real path must be under '%s'", env_target_file, args->external_rootfs);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
if (!util_file_exists(env_path)) {
|
|
goto out;
|
|
}
|
|
file_size = util_file_size(env_path);
|
|
if (file_size > REGULAR_FILE_SIZE) {
|
|
COMMAND_ERROR("env target file '%s', size exceed limit: %lld", env_path, REGULAR_FILE_SIZE);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
free(env_path);
|
|
return ret;
|
|
}
|
|
|
|
int create_checker(struct client_arguments *args)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (args == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
args->custom_conf.attach_stdin = args->custom_conf.open_stdin;
|
|
|
|
if (create_hostname_checker(args) != 0) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (create_name_checker(args) != 0) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (create_devices_volumes_checker(args) != 0) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (args->argc < 1) {
|
|
COMMAND_ERROR("\"%s\" requires a minimum of 1 argument.", args->subcommand);
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (create_check_rootfs(args)) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (create_check_network(args)) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (create_check_user_remap(args)) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (check_hook_spec_file(args->custom_conf.hook_spec)) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (create_check_hugetlbs(args)) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (create_namespaces_checker(args) != 0) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (create_check_nschangeopt(args)) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (create_check_oomkilldisable(args)) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (create_check_sysctl(args)) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (create_check_env_target_file(args)) {
|
|
ret = -1;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
return ret;
|
|
}
|