1 /*
2 * Copyright (C) 2018-2022 Intel Corporation.
3 * SPDX-License-Identifier: BSD-3-Clause
4 */
5
6 #include <unistd.h>
7 #include <string.h>
8 #include <sys/mman.h>
9 #include <sys/types.h>
10 #include <sys/stat.h>
11 #include <fcntl.h>
12 #include <malloc.h>
13 #include <errno.h>
14 #include <stdarg.h>
15 #include <sys/wait.h>
16 #include "cmdutils.h"
17 #include "strutils.h"
18 #include "log_sys.h"
19
20 /**
21 * Execute a command described in an array of pointers to null-terminated
22 * strings, and redirect the output to specified file. The file will be
23 * created/truncated if it exists/non-exists. This function will be blocked
24 * until the child process exits.
25 *
26 * @param argv An array of pointers to null-terminated strings that
27 * represent the argument list available to the new program.
28 * The array of pointers must be terminated by a null pointer.
29 * @param outfile File to record command's output, NULL means that this
30 * function doesn't handle command's output.
31 *
32 * @return If a child process could not be created, or its status could not be
33 * retrieved, or it was killed/stopped by signal, the return value
34 * is -1.
35 * If all system calls succeed, then the return value is the
36 * termination status of the child process used to execute command.
37 */
execv_out2file(char * const argv[],const char * outfile)38 int execv_out2file(char * const argv[], const char *outfile)
39 {
40 pid_t pid;
41
42 pid = fork();
43 if (pid < 0) {
44 LOGE("fork error (%s)\n", strerror(errno));
45 return -1;
46 }
47
48 if (pid == 0) {
49 int res;
50 int fd = -1;
51
52 if (outfile) {
53 fd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0664);
54 if (fd < 0) {
55 LOGE("open error (%s)\n", strerror(errno));
56 return -1;
57 }
58 res = dup2(fd, STDOUT_FILENO);
59 if (res < 0) {
60 close(fd);
61 LOGE("dup2 error (%s)\n", strerror(errno));
62 return -1;
63 }
64 }
65
66 res = execvp(argv[0], argv);
67 if (res == -1) {
68 if (fd > 0)
69 close(fd);
70 LOGE("execvp (%s) failed, error (%s)\n", argv[0],
71 strerror(errno));
72 return -1;
73 }
74 } else {
75 pid_t res;
76 int status;
77 int exit_status;
78
79 res = waitpid(pid, &status, 0);
80 if (res == -1) {
81 LOGE("waitpid failed, error (%s)\n", strerror(errno));
82 return res;
83 }
84
85 if (WIFEXITED(status)) {
86 exit_status = WEXITSTATUS(status);
87 if (!exit_status)
88 LOGI("%s exited, status=0\n", argv[0]);
89 else
90 LOGE("%s exited, status=%d\n", argv[0],
91 exit_status);
92 return exit_status;
93 } else if (WIFSIGNALED(status)) {
94 LOGE("%s killed by signal %d\n", argv[0],
95 WTERMSIG(status));
96 } else if (WIFSTOPPED(status)) {
97 LOGE("%s stopped by signal %d\n", argv[0],
98 WSTOPSIG(status));
99 }
100
101 }
102
103 return -1;
104 }
105
106 /**
107 * Execute a command described in format string, and redirect the output
108 * to specified file. The file will be created/truncated if it
109 * exists/non-exists. This function will be blocked until the child process
110 * exits.
111 *
112 * @param outfile File to record command's output, NULL means that this
113 * function doesn't handle command's output.
114 * @param fmt Format string of command.
115 *
116 * @return If a child process could not be created, or its status could not be
117 * retrieved, or it was killed/stopped by signal, the return value
118 * is -1.
119 * If all system calls succeed, then the return value is the
120 * termination status of the child process used to execute command.
121 */
exec_out2file(const char * outfile,const char * fmt,...)122 int exec_out2file(const char *outfile, const char *fmt, ...)
123 {
124 va_list args;
125 char *cmd;
126 char *start;
127 char *p;
128 int ret;
129 int argc;
130 char **argv;
131 int i = 0;
132
133 va_start(args, fmt);
134 ret = vasprintf(&cmd, fmt, args);
135 va_end(args);
136 if (ret < 0)
137 return ret;
138
139 strtrim(cmd, ret);
140 argc = strcnt(cmd, ' ') + 1;
141
142 argv = (char **)calloc(argc + 1, sizeof(char *));
143 if (argv == NULL) {
144 free(cmd);
145 LOGE("calloc failed, error (%s)\n", strerror(errno));
146 return -1;
147 }
148
149 /* string to argv[] */
150 start = cmd;
151 argv[i++] = start;
152 while (start && (p = strchr(start, ' '))) {
153 argv[i++] = p + 1;
154 *p = 0;
155 if (*(p + 1) != '"')
156 start = p + 1;
157 else
158 start = strchr(p + 2, '"');
159 }
160
161 ret = execv_out2file(argv, outfile);
162
163 free(argv);
164 free(cmd);
165
166 return ret;
167 }
168
169 /**
170 * Execute a command described in format string, and redirect the output
171 * to memory. The memory is allocated by this function and needs to be freed
172 * after return.
173 *
174 * @param[out] outmem The pointer to command's output.
175 * @param fmt Format string of command.
176 *
177 * @return the length of command's output if successful, or -1 if not.
178 */
exec_out2mem(char ** outmem,const char * fmt,...)179 ssize_t exec_out2mem(char **outmem, const char *fmt, ...)
180 {
181 va_list args;
182 char *cmd;
183 FILE *pp;
184 char *out = NULL;
185 char *new;
186 char tmp[1024];
187 size_t memsize = 0;
188 size_t newlen = 0;
189 ssize_t len = 0;
190 int ret;
191
192 va_start(args, fmt);
193 ret = vasprintf(&cmd, fmt, args);
194 va_end(args);
195 if (ret < 0)
196 return -1;
197
198 pp = popen(cmd, "r");
199 if (!pp) {
200 free(cmd);
201 return -1;
202 }
203
204 while (fgets(tmp, 1024, pp) != NULL) {
205 newlen += strnlen(tmp, 1024);
206 if (newlen + 1 > memsize) {
207 memsize += 1024;
208 new = realloc(out, memsize);
209 if (!new) {
210 if (out) {
211 free(out);
212 out = NULL;
213 }
214 len = -1;
215 goto end;
216 } else {
217 out = new;
218 }
219 }
220 /* fgets read at most 1023 bytes and plus '\0' */
221 memcpy(out + len, tmp, strnlen(tmp, 1024) + 1);
222
223 len = newlen;
224 }
225
226 end:
227 *outmem = out;
228 pclose(pp);
229 free(cmd);
230
231 return len;
232 }
233