/****************************************************************************** * Copyright (c) Huawei Technologies Co., Ltd. 2017-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: 2017-11-22 * Description: provide container command functions ******************************************************************************/ #include "commands.h" #include #include #include #include #include #include "securec.h" #include "arguments.h" #include "config.h" #include "log.h" #include "utils.h" #include "console.h" #include "constants.h" static void send_msg_to_syslog(int argc, const char **argv) { int nret = 0; int fd = -1; int i = 0; bool found = false; ssize_t len = 0; pid_t ppid = -1; char cmdline_path[PATH_MAX] = { 0 }; char cmdline[MAX_BUFFER_SIZE + 1] = { 0 }; char *msg = NULL; const char *target_command[] = { "kill", "restart", "rm", "stop", NULL }; if (argc < 2) { COMMAND_ERROR("Invalid arguments to send syslog"); return; } for (; target_command[i] != NULL; i++) { if (strcmp(argv[1], target_command[i]) == 0) { found = true; } } if (!found) { return; } ppid = getppid(); // get parent cmdline, "/proc/ppid/cmdline" nret = sprintf_s(cmdline_path, PATH_MAX, "/proc/%d/cmdline", ppid); if (nret < 0) { COMMAND_ERROR("Get parent '%d' cmdline path failed", ppid); return; } fd = util_open(cmdline_path, O_RDONLY, DEFAULT_SECURE_FILE_MODE); if (fd < 0) { COMMAND_ERROR("Open parent '%d' cmdline path failed", ppid); return; } len = util_read_nointr(fd, cmdline, MAX_BUFFER_SIZE); if (len < 0) { COMMAND_ERROR("Read cmdline failed"); goto free_out; } msg = util_string_join(" ", argv, (size_t)argc); if (msg == NULL) { msg = util_strdup_s(argv[1]); } openlog("isulad-client", LOG_PID, LOG_USER); syslog(LOG_DEBUG, "received command [%s] from parent [%d] cmdline [%s]", msg, ppid, cmdline); closelog(); free_out: close(fd); free(msg); } static void print_version() { printf("Version %s, commit %s\n", VERSION, LCRD_GIT_COMMIT); } /* compare commands */ int compare_commands(const void *s1, const void *s2) { return strcmp((*(const struct command *)s1).name, (*(const struct command *)s2).name); } const struct command *command_by_name(const struct command *cmds, const char * const name) { size_t i = 0; if (cmds == NULL) { return NULL; } while (1) { if (cmds[i].name == NULL) { return NULL; } if (strcmp(cmds[i].name, name) == 0) { return cmds + i; } ++i; } } // Default help command if implementation doesn't provide one int command_default_help(const char * const program_name, struct command *commands, int argc, const char **argv) { const struct command *command = NULL; if (commands == NULL) { return 1; } if (argc == 0) { size_t i = 0; size_t max_size = 0; printf("USAGE:\n"); printf("\t%s [args...]\n", program_name); printf("\n"); printf("COMMANDS:\n"); for (i = 0; commands[i].name != NULL; i++) { size_t cmd_size = strlen(commands[i].name); if (cmd_size > max_size) { max_size = cmd_size; } } qsort(commands, i, sizeof(commands[0]), compare_commands); for (i = 0; commands[i].name != NULL; i++) { printf("\t%*s\t%s\n", -(int)max_size, commands[i].name, commands[i].description); } printf("\n"); print_common_help(); return 0; } else if (argc > 1) { printf("%s: unrecognized argument: \"%s\"\n", program_name, argv[1]); return 1; } command = command_by_name(commands, argv[0]); if (command == NULL) { printf("%s: sub-command \"%s\" not found\n", program_name, argv[0]); printf("run `lcrc --help` for a list of sub-commands\n"); return 1; } if (command->longdesc != NULL) { printf("%s\n", command->longdesc); } return 0; } /* run command */ int run_command(struct command *commands, int argc, const char **argv) { const struct command *command = NULL; if (argc == 1) { return command_default_help(argv[0], commands, argc - 1, (const char **)(argv + 1)); } if (strcmp(argv[1], "help") == 0 || strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) { // lcrc help command format: lcrc [-h|help|--help] args return command_default_help(argv[0], commands, argc - 2, (const char **)(argv + 2)); } if (strcmp(argv[1], "--version") == 0) { print_version(); return 0; } command = command_by_name(commands, argv[1]); if (command != NULL) { send_msg_to_syslog(argc, argv); return command->executor(argc, (const char **)argv); } printf("%s: command \"%s\" not found\n", argv[0], argv[1]); printf("run `%s --help` or `run -h` for a list of sub-commands\n", argv[0]); return 1; } /* free command fifo names */ void free_command_fifo_config(struct command_fifo_config *fifos) { if (fifos != NULL) { if (fifos->stdin_path != NULL) { free(fifos->stdin_path); fifos->stdin_path = NULL; } if (fifos->stdout_path != NULL) { free(fifos->stdout_path); fifos->stdout_path = NULL; } if (fifos->stderr_path != NULL) { free(fifos->stderr_path); fifos->stderr_path = NULL; } if (fifos->stdin_name != NULL) { free(fifos->stdin_name); fifos->stdin_name = NULL; } if (fifos->stdout_name != NULL) { free(fifos->stdout_name); fifos->stdout_name = NULL; } if (fifos->stderr_name != NULL) { free(fifos->stderr_name); fifos->stderr_name = NULL; } free(fifos); } } /* delete command fifo */ void delete_command_fifo(struct command_fifo_config *fifos) { int ret; if (fifos == NULL) { return; } ret = console_fifo_delete(fifos->stdin_name); if (ret) { WARN("Delete fifo failed: %s", fifos->stdin_name); } ret = console_fifo_delete(fifos->stdout_name); if (ret) { WARN("Delete fifo failed: %s", fifos->stdout_name); } ret = console_fifo_delete(fifos->stderr_name); if (ret) { WARN("Delete fifo failed: %s", fifos->stderr_name); } ret = util_recursive_rmdir(fifos->stdin_path, 0); if (ret) { WARN("Remove directory failed: %s", fifos->stdin_path); } ret = util_recursive_rmdir(fifos->stdout_path, 0); if (ret) { WARN("Remove directory failed: %s", fifos->stdout_path); } ret = util_recursive_rmdir(fifos->stderr_path, 0); if (ret) { WARN("Remove directory failed: %s", fifos->stderr_path); } free_command_fifo_config(fifos); } static int do_create_console_fifo(const char *subpath, const char *stdflag, char **out_fifo_dir, char **out_fifo_name) { int ret = 0; char fifo_dir[PATH_MAX] = { 0 }; char fifo_name[PATH_MAX] = { 0 }; ret = console_fifo_name(CLIENT_RUNDIR, subpath, stdflag, fifo_name, sizeof(fifo_name), fifo_dir, sizeof(fifo_dir), true); if (ret != 0) { ERROR("Failed to get console fifo name."); ret = -1; goto out; } if (console_fifo_create(fifo_name)) { ERROR("Failed to create console fifo."); ret = -1; goto out; } *out_fifo_dir = util_strdup_s(fifo_dir); *out_fifo_name = util_strdup_s(fifo_name); out: return ret; } int create_console_fifos(bool attach_stdin, bool attach_stdout, bool attach_stderr, const char *name, const char *type, struct command_fifo_config **pconsole_fifos) { int ret = 0; char subpath[PATH_MAX] = { 0 }; struct command_fifo_config *fifos = NULL; fifos = util_common_calloc_s(sizeof(struct command_fifo_config)); if (fifos == NULL) { ERROR("Failed to malloc memory for FIFO names."); return -1; } ret = sprintf_s(subpath, sizeof(subpath), "%s/%s-%u-%u", name, type, (unsigned int)getpid(), (unsigned int)pthread_self()); if (ret < 0) { ERROR("Path is too long"); goto cleanup; } if (attach_stdin) { ret = do_create_console_fifo(subpath, "in", &fifos->stdin_path, &fifos->stdin_name); if (ret != 0) { goto cleanup; } INFO("FIFO:%s create for start success.", fifos->stdin_name); } if (attach_stdout) { ret = do_create_console_fifo(subpath, "out", &fifos->stdout_path, &fifos->stdout_name); if (ret != 0) { goto cleanup; } INFO("FIFO:%s create for start success.", fifos->stdout_name); } if (attach_stderr) { ret = do_create_console_fifo(subpath, "err", &fifos->stderr_path, &fifos->stderr_name); if (ret != 0) { goto cleanup; } INFO("FIFO:%s create for start success.", fifos->stderr_name); } *pconsole_fifos = fifos; return 0; cleanup: console_fifo_delete(fifos->stdin_name); console_fifo_delete(fifos->stdout_name); console_fifo_delete(fifos->stderr_name); free_command_fifo_config(fifos); return -1; } struct console_loop_thread_args { struct command_fifo_config *fifo_config; bool tty; }; static void *client_console_loop_thread(void *arg) { int ret = 0; int fifoinfd = -1; int fifooutfd = -1; int fifoerrfd = -1; const struct console_loop_thread_args *args = arg; bool tty = args->tty; struct command_fifo_config *fifo_config = args->fifo_config; sem_t *wait_open = fifo_config->wait_open; sem_t *wait_exit = fifo_config->wait_exit; ret = pthread_detach(pthread_self()); if (ret != 0) { CRIT("Start: set thread detach fail"); goto err1; } if (fifo_config->stdin_name) { if (console_fifo_open_withlock(fifo_config->stdin_name, &fifoinfd, O_RDWR | O_NONBLOCK)) { ERROR("Start: failed to open console fifo."); goto err2; } INFO("FIFO:%s open success for start.", fifo_config->stdin_name); } if (fifo_config->stdout_name) { if (console_fifo_open(fifo_config->stdout_name, &fifooutfd, O_RDONLY | O_NONBLOCK)) { ERROR("Failed to open console fifo."); goto err2; } INFO("FIFO:%s open success for start.", fifo_config->stdout_name); } if (fifo_config->stderr_name) { if (console_fifo_open(fifo_config->stderr_name, &fifoerrfd, O_RDONLY | O_NONBLOCK)) { ERROR("Start: failed to open console fifo."); goto err2; } INFO("FIFO:%s open success for start.", fifo_config->stderr_name); } sem_post(wait_open); client_console_loop(0, 1, 2, fifoinfd, fifooutfd, fifoerrfd, 1, tty); err2: if (fifoinfd >= 0) { console_fifo_close(fifoinfd); } if (fifooutfd >= 0) { console_fifo_close(fifooutfd); } if (fifoerrfd >= 0) { console_fifo_close(fifoerrfd); } err1: sem_post(wait_open); sem_post(wait_exit); return NULL; } int start_client_console_thread(struct command_fifo_config *console_fifos, bool tty) { int res = 0; pthread_t a_thread; struct console_loop_thread_args args; args.fifo_config = console_fifos; args.tty = tty; res = pthread_create(&a_thread, NULL, client_console_loop_thread, (void *)(&args)); if (res != 0) { CRIT("Thread creation failed"); return -1; } sem_wait(console_fifos->wait_open); return 0; }