acrn-hypervisor/tools/acrn-crashlog/common/cmdutils.c

233 lines
5.0 KiB
C

/*
* Copyright (C) 2018 Intel Corporation
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <malloc.h>
#include <errno.h>
#include <stdarg.h>
#include <sys/wait.h>
#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;
}