/* * Copyright (C) 2018-2022 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 * const argv[], const 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 = -1; 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) { if (fd > 0) close(fd); 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; } /** * 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(const char *outfile, const 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, ret); 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[out] outmem The pointer to command's output. * @param fmt Format string of command. * * @return the length of command's output if successful, or -1 if not. */ ssize_t exec_out2mem(char **outmem, const char *fmt, ...) { va_list args; char *cmd; FILE *pp; char *out = NULL; char *new; char tmp[1024]; size_t memsize = 0; size_t newlen = 0; ssize_t len = 0; int ret; va_start(args, fmt); ret = vasprintf(&cmd, fmt, args); va_end(args); if (ret < 0) return -1; pp = popen(cmd, "r"); if (!pp) { free(cmd); return -1; } while (fgets(tmp, 1024, pp) != NULL) { newlen += strnlen(tmp, 1024); if (newlen + 1 > memsize) { memsize += 1024; new = realloc(out, memsize); if (!new) { if (out) { free(out); out = NULL; } len = -1; goto end; } else { out = new; } } /* fgets read at most 1023 bytes and plus '\0' */ memcpy(out + len, tmp, strnlen(tmp, 1024) + 1); len = newlen; } end: *outmem = out; pclose(pp); free(cmd); return len; }