diff --git a/tools/acrn-crashlog/common/Makefile b/tools/acrn-crashlog/common/Makefile index c4a938686..5a062e978 100644 --- a/tools/acrn-crashlog/common/Makefile +++ b/tools/acrn-crashlog/common/Makefile @@ -1,5 +1,6 @@ objects = log_sys.o \ - strutils.o + strutils.o \ + cmdutils.o all: check_obj $(objects) check_obj: diff --git a/tools/acrn-crashlog/common/cmdutils.c b/tools/acrn-crashlog/common/cmdutils.c new file mode 100644 index 000000000..ee4161847 --- /dev/null +++ b/tools/acrn-crashlog/common/cmdutils.c @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2018 Intel Corporation + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cmdutils.h" +#include "strutils.h" +#include "log_sys.h" + +/** + * Execute a command described in an array of pointers to null-terminated + * strings, and redirect the output to specified file. The file will be + * created/truncated if it exists/non-exists. This function will be blocked + * until the child process exits. + * + * @param argv An array of pointers to null-terminated strings that + * represent the argument list available to the new program. + * The array of pointers must be terminated by a null pointer. + * @param outfile File to record command's output, NULL means that this + * function doesn't handle command's output. + * + * @return If a child process could not be created, or its status could not be + * retrieved, or it was killed/stopped by signal, the return value + * is -1. + * If all system calls succeed, then the return value is the + * termination status of the child process used to execute command. + */ +int execv_out2file(char *argv[], char *outfile) +{ + pid_t pid; + + pid = fork(); + if (pid < 0) { + LOGE("fork error (%s)\n", strerror(errno)); + return -1; + } + + if (pid == 0) { + int res; + int fd; + + if (outfile) { + fd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0664); + if (fd < 0) { + LOGE("open error (%s)\n", strerror(errno)); + return -1; + } + res = dup2(fd, STDOUT_FILENO); + if (res < 0) { + close(fd); + LOGE("dup2 error (%s)\n", strerror(errno)); + return -1; + } + } + + res = execvp(argv[0], argv); + if (res == -1) { + LOGE("execvp (%s) failed, error (%s)\n", argv[0], + strerror(errno)); + return -1; + } + } else { + pid_t res; + int status; + int exit_status; + + res = waitpid(pid, &status, 0); + if (res == -1) { + LOGE("waitpid failed, error (%s)\n", strerror(errno)); + return res; + } + + if (WIFEXITED(status)) { + exit_status = WEXITSTATUS(status); + if (!exit_status) + LOGI("%s exited, status=0\n", argv[0]); + else + LOGE("%s exited, status=%d\n", argv[0], + exit_status); + return exit_status; + } else if (WIFSIGNALED(status)) { + LOGE("%s killed by signal %d\n", argv[0], + WTERMSIG(status)); + } else if (WIFSTOPPED(status)) { + LOGE("%s stopped by signal %d\n", argv[0], + WSTOPSIG(status)); + } + + } + + return -1; +} + +int debugfs_cmd(char *loop_dev, char *cmd, char *outfile) +{ + char *argv[5] = {"debugfs", "-R", NULL, NULL, 0}; + + argv[2] = cmd; + argv[3] = loop_dev; + return execv_out2file(argv, outfile); +} + +/** + * Execute a command described in format string, and redirect the output + * to specified file. The file will be created/truncated if it + * exists/non-exists. This function will be blocked until the child process + * exits. + * + * @param outfile File to record command's output, NULL means that this + * function doesn't handle command's output. + * @param fmt Format string of command. + * + * @return If a child process could not be created, or its status could not be + * retrieved, or it was killed/stopped by signal, the return value + * is -1. + * If all system calls succeed, then the return value is the + * termination status of the child process used to execute command. + */ +int exec_out2file(char *outfile, char *fmt, ...) +{ + va_list args; + char *cmd; + char *start; + char *p; + int ret; + int argc; + char **argv; + int i = 0; + + va_start(args, fmt); + ret = vasprintf(&cmd, fmt, args); + va_end(args); + if (ret < 0) + return ret; + + strtrim(cmd); + argc = strcnt(cmd, ' ') + 1; + + argv = (char **)calloc(argc + 1, sizeof(char *)); + if (argv == NULL) { + free(cmd); + LOGE("calloc failed, error (%s)\n", strerror(errno)); + return -1; + } + + /* string to argv[] */ + start = cmd; + argv[i++] = start; + while (start && (p = strchr(start, ' '))) { + argv[i++] = p + 1; + *p = 0; + if (*(p + 1) != '"') + start = p + 1; + else + start = strchr(p + 2, '"'); + } + + ret = execv_out2file(argv, outfile); + + free(argv); + free(cmd); + + return ret; +} + +/** + * Execute a command described in format string, and redirect the output + * to memory. The memory is allocated by this function and needs to be freed + * after return. + * + * @param fmt Format string of command. + * + * @return a pointer to command's output if successful, or NULL if not. + */ +char *exec_out2mem(char *fmt, ...) +{ + va_list args; + char *cmd; + FILE *pp; + char *out = NULL; + char *new; + char tmp[1024]; + int memsize = 0; + int newlen = 0; + int len = 0; + int ret; + + va_start(args, fmt); + ret = vasprintf(&cmd, fmt, args); + va_end(args); + if (ret < 0) + return NULL; + + pp = popen(cmd, "r"); + if (!pp) + goto free_cmd; + + while (fgets(tmp, 1024, pp) != NULL) { + newlen += strlen(tmp); + if (newlen + 1 > memsize) { + memsize += 1024; + new = realloc(out, memsize); + if (!new) { + if (out) + free(out); + goto end; + } else { + out = new; + } + } + memcpy(out + len, tmp, strlen(tmp) + 1); + + len = newlen; + } + +end: + pclose(pp); +free_cmd: + free(cmd); + + return out; +} diff --git a/tools/acrn-crashlog/common/include/cmdutils.h b/tools/acrn-crashlog/common/include/cmdutils.h new file mode 100644 index 000000000..498f1eb29 --- /dev/null +++ b/tools/acrn-crashlog/common/include/cmdutils.h @@ -0,0 +1,9 @@ +/* + * Copyright (C) 2018 Intel Corporation + * SPDX-License-Identifier: BSD-3-Clause + */ + +int execv_out2file(char *argv[], char *outfile); +int debugfs_cmd(char *loop_dev, char *cmd, char *outfile); +int exec_out2file(char *outfile, char *fmt, ...); +char *exec_out2mem(char *fmt, ...);