Grooooot 7be59a7c67 iSulad: sync openeuler at 3.6
Signed-off-by: Grooooot <isula@huawei.com>
2020-03-06 14:58:03 +08:00

318 lines
8.8 KiB
C

/******************************************************************************
* Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
* iSulad licensed under the Mulan PSL v1.
* You can use this software according to the terms and conditions of the Mulan PSL v1.
* You may obtain a copy of Mulan PSL v1 at:
* http://license.coscl.org.cn/MulanPSL
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
* PURPOSE.
* See the Mulan PSL v1 for more details.
* Author: lifeng
* Create: 2018-11-08
* Description: provide container run functions
******************************************************************************/
#include <sys/ioctl.h>
#include <unistd.h>
#include <pthread.h>
#include <termios.h>
#include "run.h"
#include "arguments.h"
#include "log.h"
#include "utils.h"
#include "isula_connect.h"
#include "console.h"
#include "error.h"
const char g_cmd_run_desc[] = "Run a command in a new container";
const char g_cmd_run_usage[] = "run [OPTIONS] ROOTFS|IMAGE [COMMAND] [ARG...]";
static int run_checker(struct client_arguments *args);
struct client_arguments g_cmd_run_args = {
.runtime = "",
.restart = "no",
.log_file = NULL,
.log_file_size = "1MB",
.log_file_rotate = 7,
};
static int local_cmd_start(struct client_arguments *args, uint32_t *exit_code)
{
int ret = 0;
bool reset_tty = false;
struct termios oldtios;
struct command_fifo_config *console_fifos = NULL;
ret = client_start(&g_cmd_run_args, &reset_tty, &oldtios, &console_fifos);
if (ret != 0) {
goto free_out;
}
if (!g_cmd_run_args.detach) {
ret = client_wait(&g_cmd_run_args, exit_code);
if (ret != 0) {
goto free_out;
}
ret = (int)(*exit_code);
}
client_wait_fifo_exit(&g_cmd_run_args);
free_out:
client_restore_console(reset_tty, &oldtios, console_fifos);
return ret;
}
static int remote_cmd_start_set_tty(const struct client_arguments *args, bool *reset_tty, struct termios *oldtios)
{
int istty = 0;
istty = isatty(0);
if (istty && args->custom_conf.tty && args->custom_conf.attach_stdin) {
if (setup_tios(0, oldtios)) {
ERROR("Failed to setup terminal properties");
return -1;
}
*reset_tty = true;
}
return 0;
}
static int remote_cmd_start(const struct client_arguments *args, uint32_t *exit_code)
{
int ret = 0;
bool reset_tty = false;
isula_connect_ops *ops = NULL;
struct isula_start_request request = { 0 };
struct isula_start_response *response = NULL;
client_connect_config_t config = { 0 };
struct termios oldtios;
ops = get_connect_client_ops();
if (ops == NULL || ops->container.remote_start == NULL) {
ERROR("Unimplemented ops");
ret = ECOMMON;
goto out;
}
if (remote_cmd_start_set_tty(args, &reset_tty, &oldtios) != 0) {
ret = ECOMMON;
goto out;
}
request.name = args->name;
request.attach_stdin = args->custom_conf.attach_stdin;
request.attach_stdout = args->custom_conf.attach_stdout;
request.attach_stderr = args->custom_conf.attach_stderr;
response = util_common_calloc_s(sizeof(struct isula_start_response));
if (response == NULL) {
ERROR("Out of memory");
ret = ECOMMON;
goto out;
}
config = get_connect_config(args);
ret = ops->container.remote_start(&request, response, &config);
if (ret) {
client_print_error(response->cc, response->server_errono, response->errmsg);
ret = ECOMMON;
if (response->server_errono ||
(response->errmsg && !strcmp(response->errmsg, errno_to_error_message(ISULAD_ERR_CONNECT)))) {
ret = ESERVERERROR;
}
goto out;
}
out:
isula_start_response_free(response);
if (reset_tty && tcsetattr(0, TCSAFLUSH, &oldtios) < 0) {
ERROR("Failed to reset terminal properties");
return -1;
}
return ret;
}
static int do_resize_run_console(const struct client_arguments *args, unsigned int height, unsigned int width)
{
int ret = 0;
isula_connect_ops *ops = NULL;
struct isula_resize_request request = { 0 };
struct isula_resize_response *response = NULL;
client_connect_config_t config = { 0 };
ops = get_connect_client_ops();
if (ops == NULL || ops->container.resize == NULL) {
ERROR("Unimplemented ops");
ret = -1;
goto out;
}
request.id = args->name;
request.height = height;
request.width = width;
response = util_common_calloc_s(sizeof(struct isula_resize_response));
if (response == NULL) {
ERROR("Out of memory");
ret = -1;
goto out;
}
config = get_connect_config(args);
ret = ops->container.resize(&request, response, &config);
if (ret != 0) {
ERROR("Failed to call resize");
goto out;
}
out:
isula_resize_response_free(response);
return ret;
}
static void *run_console_resize_thread(void *arg)
{
int ret = 0;
const struct client_arguments *args = arg;
static struct winsize s_pre_wsz;
struct winsize wsz;
if (!isatty(STDIN_FILENO)) {
goto out;
}
ret = pthread_detach(pthread_self());
if (ret != 0) {
CRIT("Start: set thread detach fail");
goto out;
}
while (true) {
sleep(1); // check the windows size per 1s
ret = ioctl(STDIN_FILENO, TIOCGWINSZ, &wsz);
if (ret < 0) {
WARN("Failed to get window size");
continue;
}
if (wsz.ws_row == s_pre_wsz.ws_row && wsz.ws_col == s_pre_wsz.ws_col) {
continue;
}
ret = do_resize_run_console(args, wsz.ws_row, wsz.ws_col);
if (ret != 0) {
continue;
}
s_pre_wsz.ws_row = wsz.ws_row;
s_pre_wsz.ws_col = wsz.ws_col;
}
out:
return NULL;
}
int run_client_console_resize_thread(struct client_arguments *args)
{
int res = 0;
pthread_t a_thread;
res = pthread_create(&a_thread, NULL, run_console_resize_thread, (void *)(args));
if (res != 0) {
CRIT("Thread creation failed");
return -1;
}
return 0;
}
int cmd_run_main(int argc, const char **argv)
{
int ret = 0;
unsigned int exit_code = 0;
command_t cmd = { 0 };
struct log_config lconf = { 0 };
set_default_command_log_config(argv[0], &lconf);
if (client_arguments_init(&g_cmd_run_args)) {
COMMAND_ERROR("client arguments init failed");
exit(ECOMMON);
}
g_cmd_run_args.custom_conf.attach_stdout = true;
g_cmd_run_args.custom_conf.attach_stderr = true;
g_cmd_run_args.progname = argv[0];
g_cmd_run_args.subcommand = argv[1];
struct command_option options[] = {
LOG_OPTIONS(lconf),
COMMON_OPTIONS(g_cmd_run_args),
CREATE_OPTIONS(g_cmd_run_args),
CREATE_EXTEND_OPTIONS(g_cmd_run_args),
RUN_OPTIONS(g_cmd_run_args)
};
command_init(&cmd, options, sizeof(options) / sizeof(options[0]), argc, (const char **)argv, g_cmd_run_desc,
g_cmd_run_usage);
if (command_parse_args(&cmd, &g_cmd_run_args.argc, &g_cmd_run_args.argv) || run_checker(&g_cmd_run_args)) {
exit(EINVALIDARGS);
}
if (log_init(&lconf)) {
COMMAND_ERROR("log init failed");
exit(ECOMMON);
}
ret = client_create(&g_cmd_run_args);
if (ret) {
ERROR("Container \"%s\" create failed", g_cmd_run_args.name);
exit(ret);
}
if (g_cmd_run_args.detach) {
printf("%s\n", g_cmd_run_args.name);
}
if (g_cmd_run_args.custom_conf.tty && isatty(STDIN_FILENO)) {
(void)run_client_console_resize_thread(&g_cmd_run_args);
}
if (strncmp(g_cmd_run_args.socket, "tcp://", strlen("tcp://")) == 0) {
ret = remote_cmd_start(&g_cmd_run_args, &exit_code);
if (ret != 0) {
ERROR("Failed to execute command with remote run");
goto free_out;
}
} else {
ret = local_cmd_start(&g_cmd_run_args, &exit_code);
if (ret != 0) {
ERROR("Failed to execute command with local run");
goto free_out;
}
}
free_out:
exit(ret);
}
static int run_checker(struct client_arguments *args)
{
int ret = 0;
ret = create_checker(args);
if (ret) {
goto out;
}
/* Make detach option a high priority than terminal */
if (args->detach) {
args->custom_conf.attach_stdin = false;
args->custom_conf.attach_stdout = false;
args->custom_conf.attach_stderr = false;
}
if (args->custom_conf.auto_remove && ((args->restart != NULL) && (strcmp("no", args->restart) != 0))) {
COMMAND_ERROR("Conflicting options: --restart and --rm");
ret = -1;
goto out;
}
out:
return ret;
}