iSulad/src/console/console.c

687 lines
18 KiB
C
Raw Normal View History

2019-09-30 10:53:41 -04:00
/******************************************************************************
* 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: tanyifeng
* Create: 2018-11-08
* Description: provide console definition
******************************************************************************/
#include <unistd.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <limits.h>
#include <termios.h>
#include <pthread.h>
#include <sys/stat.h>
#include "console.h"
#include "securec.h"
#include "mainloop.h"
#include "log.h"
#include "utils.h"
#include "constants.h"
static ssize_t fifo_write_function(void *context, const void *data, size_t len)
{
ssize_t ret;
int fd;
fd = *(int *)context;
ret = util_write_nointr(fd, data, len);
// Ignore EAGAIN to prevent hang, do not report error
if (errno == EAGAIN) {
return (ssize_t)len;
}
if ((ret <= 0) || (ret != (ssize_t)len)) {
ERROR("Failed to write %d: %s", fd, strerror(errno));
return -1;
}
return ret;
}
static ssize_t fd_write_function(void *context, const void *data, size_t len)
{
ssize_t ret;
ret = util_write_nointr(*(int *)context, data, len);
if ((ret <= 0) || (ret != (ssize_t)len)) {
ERROR("Failed to write: %s", strerror(errno));
return -1;
}
return ret;
}
/* console cb tty fifoin */
static int console_cb_tty_stdin_with_escape(int fd, uint32_t events, void *cbdata,
struct epoll_descr *descr)
{
struct tty_state *ts = cbdata;
char c;
int ret = 0;
ssize_t r_ret, w_ret;
if (fd != ts->stdin_reader) {
ret = 1;
goto out;
}
r_ret = util_read_nointr(ts->stdin_reader, &c, 1);
if (r_ret <= 0) {
ret = 1;
goto out;
}
if (ts->tty_exit != -1) {
if (c == ts->tty_exit && !(ts->saw_tty_exit)) {
ts->saw_tty_exit = 1;
goto out;
}
if (c == 'q' && ts->saw_tty_exit) {
ret = 1;
goto out;
}
ts->saw_tty_exit = 0;
}
if (ts->stdin_writer.context && ts->stdin_writer.write_func) {
w_ret = ts->stdin_writer.write_func(ts->stdin_writer.context, &c, 1);
if ((w_ret <= 0) || (w_ret != r_ret)) {
ret = 1;
goto out;
}
}
out:
return ret;
}
static int console_writer_write_data(const struct io_write_wrapper *writer, const char *buf, ssize_t len)
{
ssize_t ret;
if (writer == NULL || writer->context == NULL || writer->write_func == NULL || len <= 0) {
return 0;
}
ret = writer->write_func(writer->context, buf, (size_t)len);
if (ret <= 0 || ret != len) {
ERROR("failed to write, error:%s", strerror(errno));
return -1;
}
return 0;
}
/* console cb tty fifoin */
static int console_cb_stdio_copy(int fd, uint32_t events, void *cbdata, struct epoll_descr *descr)
{
struct tty_state *ts = cbdata;
char *buf = NULL;
size_t buf_len = MAX_MSG_BUFFER_SIZE;
int ret = 0;
ssize_t r_ret;
buf = util_common_calloc_s(buf_len);
if (buf == NULL) {
ERROR("Out of memory");
return -1;
}
if (fd == ts->sync_fd) {
ret = 1;
goto out;
}
if (fd != ts->stdin_reader && fd != ts->stdout_reader && fd != ts->stderr_reader) {
ret = 1;
goto out;
}
r_ret = util_read_nointr(fd, buf, buf_len - 1);
if (r_ret <= 0) {
ret = 1;
goto out;
}
if (fd == ts->stdin_reader) {
if (console_writer_write_data(&ts->stdin_writer, buf, r_ret) != 0) {
ret = 1;
goto out;
}
}
if (fd == ts->stdout_reader) {
if (console_writer_write_data(&ts->stdout_writer, buf, r_ret) != 0) {
ret = 1;
goto out;
}
}
if (fd == ts->stderr_reader) {
if (console_writer_write_data(&ts->stderr_writer, buf, r_ret) != 0) {
ret = 1;
goto out;
}
}
out:
free(buf);
return ret;
}
/* console fifo name */
int console_fifo_name(const char *rundir, const char *subpath,
const char *stdflag,
char *fifo_name, size_t fifo_name_sz,
char *fifo_path, size_t fifo_path_sz, bool do_mkdirp)
{
int ret = 0;
int nret = 0;
nret = sprintf_s(fifo_path, fifo_path_sz, "%s/%s/", rundir, subpath);
if (nret < 0) {
ERROR("FIFO path:%s/%s/ is too long.", rundir, subpath);
ret = -1;
goto out;
}
if (do_mkdirp) {
ret = util_mkdir_p(fifo_path, CONSOLE_FIFO_DIRECTORY_MODE);
if (ret < 0) {
COMMAND_ERROR("Unable to create console fifo directory %s: %s.", fifo_path, strerror(errno));
goto out;
}
}
nret = sprintf_s(fifo_name, fifo_name_sz, "%s/%s/%s-fifo", rundir, subpath, stdflag);
if (nret < 0) {
ERROR("FIFO name %s/%s/%s-fifo is too long.", rundir, subpath, stdflag);
ret = -1;
goto out;
}
out:
return ret;
}
/* console fifo create */
int console_fifo_create(const char *fifo_path)
{
int ret;
ret = mknod(fifo_path, S_IFIFO | S_IRUSR | S_IWUSR, (dev_t)0);
if (ret < 0 && errno != EEXIST) {
ERROR("Failed to mknod monitor fifo %s: %s.", fifo_path, strerror(errno));
return -1;
}
return 0;
}
/* console fifo delete */
int console_fifo_delete(const char *fifo_path)
{
char *ret = NULL;
char real_path[PATH_MAX + 1] = { 0x00 };
if (fifo_path == NULL || strlen(fifo_path) > PATH_MAX) {
ERROR("Invalid input!");
return -1;
}
if (strlen(fifo_path) == 0) {
return 0;
}
ret = realpath(fifo_path, real_path);
if (ret == NULL) {
if (errno != ENOENT) {
ERROR("Failed to get real path: %s", fifo_path);
return -1;
}
return 0;
}
if (unlink(real_path) && errno != ENOENT) {
WARN("Unlink %s failed", real_path);
return -1;
}
return 0;
}
/* console fifo open */
int console_fifo_open(const char *fifo_path, int *fdout, int flags)
{
int fd = 0;
fd = util_open(fifo_path, O_RDONLY | O_NONBLOCK, (mode_t)0);
if (fd < 0) {
ERROR("Failed to open fifo %s to send message: %s.", fifo_path,
strerror(errno));
return -1;
}
*fdout = fd;
return 0;
}
/* console fifo open withlock */
int console_fifo_open_withlock(const char *fifo_path, int *fdout, int flags)
{
int fd = 0;
struct flock lk;
fd = util_open(fifo_path, flags, 0);
if (fd < 0) {
WARN("Failed to open fifo %s to send message: %s.", fifo_path, strerror(errno));
return -1;
}
lk.l_type = F_WRLCK;
lk.l_whence = SEEK_SET;
lk.l_start = 0;
lk.l_len = 0;
if (fcntl(fd, F_SETLK, &lk) != 0) {
/* another console instance is already running, don't start up */
DEBUG("Another console instance already running with path : %s.", fifo_path);
close(fd);
return -1;
}
*fdout = fd;
return 0;
}
/* console fifo close */
void console_fifo_close(int fd)
{
close(fd);
}
/* setup tios */
int setup_tios(int fd, struct termios *curr_tios)
{
struct termios tmptios;
int ret = 0;
if (!isatty(fd)) {
ERROR("Specified fd: '%d' is not a tty", fd);
return -1;
}
if (tcgetattr(fd, curr_tios)) {
ERROR("Failed to get current terminal settings");
ret = -1;
goto out;
}
tmptios = *curr_tios;
cfmakeraw(&tmptios);
tmptios.c_oflag |= OPOST;
if (tcsetattr(fd, TCSAFLUSH, &tmptios)) {
ERROR("Set terminal settings failed");
ret = -1;
goto out;
}
out:
return ret;
}
static void client_console_tty_state_close(struct epoll_descr *descr, const struct tty_state *ts)
{
if (ts->stdin_reader >= 0) {
epoll_loop_del_handler(descr, ts->stdin_reader);
}
if (ts->stdout_reader >= 0) {
epoll_loop_del_handler(descr, ts->stdout_reader);
}
if (ts->stderr_reader >= 0) {
epoll_loop_del_handler(descr, ts->stderr_reader);
}
}
/* console loop */
/* data direction: */
/* read stdinfd, write fifoinfd */
/* read fifooutfd, write stdoutfd */
/* read stderrfd, write stderrfd */
int client_console_loop(int stdinfd, int stdoutfd, int stderrfd,
int fifoinfd, int fifooutfd, int fifoerrfd, int tty_exit, bool tty)
{
int ret;
struct epoll_descr descr;
struct tty_state ts;
ret = epoll_loop_open(&descr);
if (ret) {
ERROR("Create epoll_loop error");
return ret;
}
ts.tty_exit = tty_exit;
ts.saw_tty_exit = 0;
ts.sync_fd = -1;
ts.stdin_reader = -1;
ts.stdout_reader = -1;
ts.stderr_reader = -1;
if (fifoinfd >= 0) {
ts.stdin_reader = stdinfd;
ts.stdin_writer.context = &fifoinfd;
ts.stdin_writer.write_func = fd_write_function;
if (tty) {
ret = epoll_loop_add_handler(&descr, ts.stdin_reader, console_cb_tty_stdin_with_escape, &ts);
if (ret) {
INFO("Add handler for stdinfd faied. with error %s", strerror(errno));
}
} else {
ret = epoll_loop_add_handler(&descr, ts.stdin_reader, console_cb_stdio_copy, &ts);
if (ret) {
INFO("Add handler for stdinfd faied. with error %s", strerror(errno));
}
}
}
if (fifooutfd >= 0) {
ts.stdout_reader = fifooutfd;
ts.stdout_writer.context = &stdoutfd;
ts.stdout_writer.write_func = fd_write_function;
ret = epoll_loop_add_handler(&descr, ts.stdout_reader, console_cb_stdio_copy, &ts);
if (ret) {
ERROR("Add handler for masterfd failed");
goto err_out;
}
}
if (fifoerrfd >= 0) {
ts.stderr_reader = fifoerrfd;
ts.stderr_writer.context = &stderrfd;
ts.stderr_writer.write_func = fd_write_function;
ret = epoll_loop_add_handler(&descr, ts.stderr_reader, console_cb_stdio_copy, &ts);
if (ret) {
ERROR("Add handler for masterfd failed");
goto err_out;
}
}
ret = epoll_loop(&descr, -1);
if (ret) {
ERROR("Epoll_loop error");
goto err_out;
}
ret = 0;
err_out:
client_console_tty_state_close(&descr, &ts);
epoll_loop_close(&descr);
return ret;
}
/* console loop copy */
static int console_loop_io_copy(int sync_fd, const int *srcfds, struct io_write_wrapper *writers, size_t len)
{
int ret = 0;
size_t i = 0;
struct epoll_descr descr;
struct tty_state *ts = NULL;
if (len > (SIZE_MAX / sizeof(struct tty_state)) - 1) {
ERROR("Invalid io size");
return -1;
}
ts = util_common_calloc_s(sizeof(struct tty_state) * (len + 1));
if (ts == NULL) {
ERROR("Out of memory");
return -1;
}
ret = epoll_loop_open(&descr);
if (ret) {
ERROR("Create epoll_loop error");
free(ts);
return ret;
}
for (i = 0; i < len; i++) {
// Reusing ts.stdout_reader and ts.stdout_writer for coping io
ts[i].stdout_reader = srcfds[i];
ts[i].stdout_writer.context = writers[i].context;
ts[i].stdout_writer.write_func = writers[i].write_func;
ts[i].sync_fd = -1;
ret = epoll_loop_add_handler(&descr, ts[i].stdout_reader, console_cb_stdio_copy, &ts[i]);
if (ret != 0) {
ERROR("Add handler for masterfd failed");
goto err_out;
}
}
if (sync_fd >= 0) {
ts[i].sync_fd = sync_fd;
epoll_loop_add_handler(&descr, ts[i].sync_fd, console_cb_stdio_copy, &ts[i]);
if (ret) {
ERROR("Add handler for syncfd failed");
goto err_out;
}
}
ret = epoll_loop(&descr, -1);
if (ret != 0) {
ERROR("Epoll_loop error");
goto err_out;
}
err_out:
for (i = 0; i < (len + 1); i++) {
epoll_loop_del_handler(&descr, ts[i].stdout_reader);
}
epoll_loop_close(&descr);
free(ts);
return ret;
}
struct io_copy_thread_arg {
struct io_copy_arg *copy_arg;
bool detach;
size_t len;
int sync_fd;
sem_t wait_sem;
};
static int io_copy_init_fds(size_t len, int **infds, int **outfds, int **srcfds, struct io_write_wrapper **writers)
{
size_t i;
if (len > SIZE_MAX / sizeof(struct io_write_wrapper)) {
ERROR("Invalid arguments");
return -1;
}
*srcfds = util_common_calloc_s(sizeof(int) * len);
if (*srcfds == NULL) {
ERROR("Out of memory");
return -1;
}
*infds = util_common_calloc_s(sizeof(int) * len);
if (*infds == NULL) {
ERROR("Out of memory");
return -1;
}
for (i = 0; i < len; i++) {
(*infds)[i] = -1;
}
*outfds = util_common_calloc_s(sizeof(int) * len);
if (*outfds == NULL) {
ERROR("Out of memory");
return -1;
}
for (i = 0; i < len; i++) {
(*outfds)[i] = -1;
}
*writers = util_common_calloc_s(sizeof(struct io_write_wrapper) * len);
if (*writers == NULL) {
ERROR("Out of memory");
return -1;
}
return 0;
}
static int io_copy_make_srcfds(size_t len, struct io_copy_arg *copy_arg, int *infds, int *srcfds)
{
size_t i;
for (i = 0; i < len; i++) {
if (copy_arg[i].srctype == IO_FIFO) {
if (console_fifo_open((const char *)copy_arg[i].src, &(infds[i]), O_RDONLY | O_NONBLOCK)) {
ERROR("failed to open console fifo.");
return -1;
}
srcfds[i] = infds[i];
} else if (copy_arg[i].srctype == IO_FD) {
srcfds[i] = *(int *)(copy_arg[i].src);
} else {
ERROR("Got invalid src fd type");
return -1;
}
}
return 0;
}
static int io_copy_make_dstfds(size_t len, struct io_copy_arg *copy_arg, int *outfds,
struct io_write_wrapper *writers)
{
size_t i;
for (i = 0; i < len; i++) {
if (copy_arg[i].dsttype == IO_FIFO) {
if (console_fifo_open_withlock((const char *)copy_arg[i].dst, &outfds[i], O_RDWR | O_NONBLOCK)) {
ERROR("Failed to open console fifo.");
return -1;
}
writers[i].context = &outfds[i];
writers[i].write_func = fifo_write_function;
} else if (copy_arg[i].dsttype == IO_FD) {
writers[i].context = copy_arg[i].dst;
writers[i].write_func = fd_write_function;
} else if (copy_arg[i].dsttype == IO_FUNC) {
struct io_write_wrapper *io_write = copy_arg[i].dst;
writers[i].context = io_write->context;
writers[i].write_func = io_write->write_func;
writers[i].close_func = io_write->close_func;
} else {
ERROR("Got invalid dst fd type");
return -1;
}
}
return 0;
}
static void io_copy_thread_cleanup(struct io_write_wrapper *writers, struct io_copy_thread_arg *thread_arg,
int *infds, int *outfds, int *srcfds, size_t len)
{
size_t i = 0;
for (i = 0; i < len; i++) {
if (writers != NULL && writers[i].close_func != NULL) {
(void)writers[i].close_func(writers[i].context, NULL);
}
}
free(srcfds);
for (i = 0; i < len; i++) {
if ((infds != NULL) && (infds[i] >= 0)) {
console_fifo_close(infds[i]);
}
if ((outfds != NULL) && (outfds[i] >= 0)) {
console_fifo_close(outfds[i]);
}
}
free(infds);
free(outfds);
free(writers);
}
static void *io_copy_thread_main(void *arg)
{
int ret = -1;
struct io_copy_thread_arg *thread_arg = (struct io_copy_thread_arg *)arg;
struct io_copy_arg *copy_arg = thread_arg->copy_arg;
size_t len = 0;
int *infds = NULL;
int *outfds = NULL; // recored fds to close
int *srcfds = NULL;
struct io_write_wrapper *writers = NULL;
int sync_fd = thread_arg->sync_fd;
bool posted = false;
if (thread_arg->detach) {
ret = pthread_detach(pthread_self());
if (ret != 0) {
CRIT("Set thread detach fail");
goto err;
}
}
(void)prctl(PR_SET_NAME, "IoCopy");
len = thread_arg->len;
if (io_copy_init_fds(len, &infds, &outfds, &srcfds, &writers) != 0) {
goto err;
}
if (io_copy_make_srcfds(len, copy_arg, infds, srcfds) != 0) {
goto err;
}
if (io_copy_make_dstfds(len, copy_arg, outfds, writers) != 0) {
goto err;
}
sem_post(&thread_arg->wait_sem);
posted = true;
(void)console_loop_io_copy(sync_fd, srcfds, writers, len);
err:
if (!posted) {
sem_post(&thread_arg->wait_sem);
}
io_copy_thread_cleanup(writers, thread_arg, infds, outfds, srcfds, len);
return NULL;
}
int start_io_copy_thread(int sync_fd, bool detach, struct io_copy_arg *copy_arg, size_t len, pthread_t *tid)
{
int res = 0;
struct io_copy_thread_arg thread_arg;
if (copy_arg == NULL || len == 0) {
return 0;
}
thread_arg.detach = detach;
thread_arg.copy_arg = copy_arg;
thread_arg.len = len;
thread_arg.sync_fd = sync_fd;
if (sem_init(&thread_arg.wait_sem, 0, 0)) {
ERROR("Failed to init start semaphore");
return -1;
}
res = pthread_create(tid, NULL, io_copy_thread_main, (void *)(&thread_arg));
if (res != 0) {
CRIT("Thread creation failed");
return -1;
}
sem_wait(&thread_arg.wait_sem);
sem_destroy(&thread_arg.wait_sem);
return 0;
}
2019-12-25 15:50:34 +08:00