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