1d5ac70f0Sopenharmony_ci/*
2d5ac70f0Sopenharmony_ci *  Exec an external program
3d5ac70f0Sopenharmony_ci *  Copyright (C) 2021 Jaroslav Kysela
4d5ac70f0Sopenharmony_ci *
5d5ac70f0Sopenharmony_ci *  This library is free software; you can redistribute it and/or
6d5ac70f0Sopenharmony_ci *  modify it under the terms of the GNU Lesser General Public
7d5ac70f0Sopenharmony_ci *  License as published by the Free Software Foundation; either
8d5ac70f0Sopenharmony_ci *  version 2 of the License, or (at your option) any later version.
9d5ac70f0Sopenharmony_ci *
10d5ac70f0Sopenharmony_ci *  This library is distributed in the hope that it will be useful,
11d5ac70f0Sopenharmony_ci *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12d5ac70f0Sopenharmony_ci *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13d5ac70f0Sopenharmony_ci *  Lesser General Public License for more details.
14d5ac70f0Sopenharmony_ci *
15d5ac70f0Sopenharmony_ci *  You should have received a copy of the GNU Lesser General Public
16d5ac70f0Sopenharmony_ci *  License along with this library; if not, write to the Free Software
17d5ac70f0Sopenharmony_ci *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18d5ac70f0Sopenharmony_ci *
19d5ac70f0Sopenharmony_ci *  Support for the verb/device/modifier core logic and API,
20d5ac70f0Sopenharmony_ci *  command line tool and file parser was kindly sponsored by
21d5ac70f0Sopenharmony_ci *  Texas Instruments Inc.
22d5ac70f0Sopenharmony_ci *  Support for multiple active modifiers and devices,
23d5ac70f0Sopenharmony_ci *  transition sequences, multiple client access and user defined use
24d5ac70f0Sopenharmony_ci *  cases was kindly sponsored by Wolfson Microelectronics PLC.
25d5ac70f0Sopenharmony_ci *
26d5ac70f0Sopenharmony_ci *  Copyright (C) 2021 Red Hat Inc.
27d5ac70f0Sopenharmony_ci *  Authors: Jaroslav Kysela <perex@perex.cz>
28d5ac70f0Sopenharmony_ci */
29d5ac70f0Sopenharmony_ci
30d5ac70f0Sopenharmony_ci#include "ucm_local.h"
31d5ac70f0Sopenharmony_ci#include <sys/stat.h>
32d5ac70f0Sopenharmony_ci#include <sys/wait.h>
33d5ac70f0Sopenharmony_ci#include <limits.h>
34d5ac70f0Sopenharmony_ci#include <dirent.h>
35d5ac70f0Sopenharmony_ci
36d5ac70f0Sopenharmony_ci#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__DragonFly__)
37d5ac70f0Sopenharmony_ci#include <signal.h>
38d5ac70f0Sopenharmony_ci#if defined(__DragonFly__)
39d5ac70f0Sopenharmony_ci#define environ NULL /* XXX */
40d5ac70f0Sopenharmony_ci#else
41d5ac70f0Sopenharmony_ciextern char **environ;
42d5ac70f0Sopenharmony_ci#endif
43d5ac70f0Sopenharmony_ci#endif
44d5ac70f0Sopenharmony_ci
45d5ac70f0Sopenharmony_cistatic pthread_mutex_t fork_lock = PTHREAD_MUTEX_INITIALIZER;
46d5ac70f0Sopenharmony_ci
47d5ac70f0Sopenharmony_ci/*
48d5ac70f0Sopenharmony_ci * Search PATH for executable
49d5ac70f0Sopenharmony_ci */
50d5ac70f0Sopenharmony_cistatic int find_exec(const char *name, char *out, size_t len)
51d5ac70f0Sopenharmony_ci{
52d5ac70f0Sopenharmony_ci	int ret = 0;
53d5ac70f0Sopenharmony_ci	char bin[PATH_MAX];
54d5ac70f0Sopenharmony_ci	char *path, *tmp, *tmp2 = NULL;
55d5ac70f0Sopenharmony_ci	DIR *dir;
56d5ac70f0Sopenharmony_ci	struct dirent64 *de;
57d5ac70f0Sopenharmony_ci	struct stat64 st;
58d5ac70f0Sopenharmony_ci	if (name[0] == '/') {
59d5ac70f0Sopenharmony_ci		if (lstat64(name, &st))
60d5ac70f0Sopenharmony_ci			return 0;
61d5ac70f0Sopenharmony_ci		if (!S_ISREG(st.st_mode) || !(st.st_mode & S_IEXEC))
62d5ac70f0Sopenharmony_ci			return 0;
63d5ac70f0Sopenharmony_ci		snd_strlcpy(out, name, len);
64d5ac70f0Sopenharmony_ci		return 1;
65d5ac70f0Sopenharmony_ci	}
66d5ac70f0Sopenharmony_ci	if (!(tmp = getenv("PATH")))
67d5ac70f0Sopenharmony_ci		return 0;
68d5ac70f0Sopenharmony_ci	path = alloca(strlen(tmp) + 1);
69d5ac70f0Sopenharmony_ci	if (!path)
70d5ac70f0Sopenharmony_ci		return 0;
71d5ac70f0Sopenharmony_ci	strcpy(path, tmp);
72d5ac70f0Sopenharmony_ci	tmp = strtok_r(path, ":", &tmp2);
73d5ac70f0Sopenharmony_ci	while (tmp && !ret) {
74d5ac70f0Sopenharmony_ci		if ((dir = opendir(tmp))) {
75d5ac70f0Sopenharmony_ci			while ((de = readdir64(dir))) {
76d5ac70f0Sopenharmony_ci				if (strstr(de->d_name, name) != de->d_name)
77d5ac70f0Sopenharmony_ci					continue;
78d5ac70f0Sopenharmony_ci				snprintf(bin, sizeof(bin), "%s/%s", tmp,
79d5ac70f0Sopenharmony_ci					 de->d_name);
80d5ac70f0Sopenharmony_ci				if (lstat64(bin, &st))
81d5ac70f0Sopenharmony_ci					continue;
82d5ac70f0Sopenharmony_ci				if (!S_ISREG(st.st_mode)
83d5ac70f0Sopenharmony_ci				    || !(st.st_mode & S_IEXEC))
84d5ac70f0Sopenharmony_ci					continue;
85d5ac70f0Sopenharmony_ci				snd_strlcpy(out, bin, len);
86d5ac70f0Sopenharmony_ci				closedir(dir);
87d5ac70f0Sopenharmony_ci				return 1;
88d5ac70f0Sopenharmony_ci			}
89d5ac70f0Sopenharmony_ci			closedir(dir);
90d5ac70f0Sopenharmony_ci		}
91d5ac70f0Sopenharmony_ci		tmp = strtok_r(NULL, ":", &tmp2);
92d5ac70f0Sopenharmony_ci	}
93d5ac70f0Sopenharmony_ci	return ret;
94d5ac70f0Sopenharmony_ci}
95d5ac70f0Sopenharmony_ci
96d5ac70f0Sopenharmony_cistatic void free_args(char **argv)
97d5ac70f0Sopenharmony_ci{
98d5ac70f0Sopenharmony_ci	char **a;
99d5ac70f0Sopenharmony_ci
100d5ac70f0Sopenharmony_ci	for (a = argv; *a; a++)
101d5ac70f0Sopenharmony_ci		free(*a);
102d5ac70f0Sopenharmony_ci	free(argv);
103d5ac70f0Sopenharmony_ci}
104d5ac70f0Sopenharmony_ci
105d5ac70f0Sopenharmony_cistatic int parse_args(char ***argv, int argc, const char *cmd)
106d5ac70f0Sopenharmony_ci{
107d5ac70f0Sopenharmony_ci	char *s, *f;
108d5ac70f0Sopenharmony_ci	int i = 0, l, eow;
109d5ac70f0Sopenharmony_ci
110d5ac70f0Sopenharmony_ci	if (!argv || !cmd)
111d5ac70f0Sopenharmony_ci		return -1;
112d5ac70f0Sopenharmony_ci
113d5ac70f0Sopenharmony_ci	s = alloca(strlen(cmd) + 1);
114d5ac70f0Sopenharmony_ci	if (!s)
115d5ac70f0Sopenharmony_ci		return -1;
116d5ac70f0Sopenharmony_ci	strcpy(s, cmd);
117d5ac70f0Sopenharmony_ci	*argv = calloc(argc, sizeof(char *));
118d5ac70f0Sopenharmony_ci
119d5ac70f0Sopenharmony_ci	while (*s && i < argc - 1) {
120d5ac70f0Sopenharmony_ci		while (*s == ' ')
121d5ac70f0Sopenharmony_ci			s++;
122d5ac70f0Sopenharmony_ci		f = s;
123d5ac70f0Sopenharmony_ci		eow = 0;
124d5ac70f0Sopenharmony_ci		while (*s) {
125d5ac70f0Sopenharmony_ci			if (*s == '\\') {
126d5ac70f0Sopenharmony_ci				l = *(s + 1);
127d5ac70f0Sopenharmony_ci				if (l == 'b')
128d5ac70f0Sopenharmony_ci					l = '\b';
129d5ac70f0Sopenharmony_ci				else if (l == 'f')
130d5ac70f0Sopenharmony_ci					l = '\f';
131d5ac70f0Sopenharmony_ci				else if (l == 'n')
132d5ac70f0Sopenharmony_ci					l = '\n';
133d5ac70f0Sopenharmony_ci				else if (l == 'r')
134d5ac70f0Sopenharmony_ci					l = '\r';
135d5ac70f0Sopenharmony_ci				else if (l == 't')
136d5ac70f0Sopenharmony_ci					l = '\t';
137d5ac70f0Sopenharmony_ci				else
138d5ac70f0Sopenharmony_ci					l = 0;
139d5ac70f0Sopenharmony_ci				if (l) {
140d5ac70f0Sopenharmony_ci					*s++ = l;
141d5ac70f0Sopenharmony_ci					memmove(s, s + 1, strlen(s));
142d5ac70f0Sopenharmony_ci				} else {
143d5ac70f0Sopenharmony_ci					memmove(s, s + 1, strlen(s));
144d5ac70f0Sopenharmony_ci					if (*s)
145d5ac70f0Sopenharmony_ci						s++;
146d5ac70f0Sopenharmony_ci				}
147d5ac70f0Sopenharmony_ci			} else if (eow) {
148d5ac70f0Sopenharmony_ci				if (*s == eow) {
149d5ac70f0Sopenharmony_ci					memmove(s, s + 1, strlen(s));
150d5ac70f0Sopenharmony_ci					eow = 0;
151d5ac70f0Sopenharmony_ci				} else {
152d5ac70f0Sopenharmony_ci					s++;
153d5ac70f0Sopenharmony_ci				}
154d5ac70f0Sopenharmony_ci			} else if (*s == '\'' || *s == '"') {
155d5ac70f0Sopenharmony_ci				eow = *s;
156d5ac70f0Sopenharmony_ci				memmove(s, s + 1, strlen(s));
157d5ac70f0Sopenharmony_ci			} else if (*s == ' ') {
158d5ac70f0Sopenharmony_ci				break;
159d5ac70f0Sopenharmony_ci			} else {
160d5ac70f0Sopenharmony_ci				s++;
161d5ac70f0Sopenharmony_ci			}
162d5ac70f0Sopenharmony_ci		}
163d5ac70f0Sopenharmony_ci		if (f != s) {
164d5ac70f0Sopenharmony_ci			if (*s) {
165d5ac70f0Sopenharmony_ci				*(char *)s = '\0';
166d5ac70f0Sopenharmony_ci				s++;
167d5ac70f0Sopenharmony_ci			}
168d5ac70f0Sopenharmony_ci			(*argv)[i] = strdup(f);
169d5ac70f0Sopenharmony_ci			if ((*argv)[i] == NULL) {
170d5ac70f0Sopenharmony_ci				free_args(*argv);
171d5ac70f0Sopenharmony_ci				return -ENOMEM;
172d5ac70f0Sopenharmony_ci			}
173d5ac70f0Sopenharmony_ci			i++;
174d5ac70f0Sopenharmony_ci		}
175d5ac70f0Sopenharmony_ci	}
176d5ac70f0Sopenharmony_ci	(*argv)[i] = NULL;
177d5ac70f0Sopenharmony_ci	return 0;
178d5ac70f0Sopenharmony_ci}
179d5ac70f0Sopenharmony_ci
180d5ac70f0Sopenharmony_ci/*
181d5ac70f0Sopenharmony_ci * execute a binary file
182d5ac70f0Sopenharmony_ci *
183d5ac70f0Sopenharmony_ci */
184d5ac70f0Sopenharmony_ciint uc_mgr_exec(const char *prog)
185d5ac70f0Sopenharmony_ci{
186d5ac70f0Sopenharmony_ci	pid_t p, f, maxfd;
187d5ac70f0Sopenharmony_ci	int err = 0, status;
188d5ac70f0Sopenharmony_ci	char bin[PATH_MAX];
189d5ac70f0Sopenharmony_ci	struct sigaction sa;
190d5ac70f0Sopenharmony_ci	struct sigaction intr, quit;
191d5ac70f0Sopenharmony_ci	sigset_t omask;
192d5ac70f0Sopenharmony_ci	char **argv;
193d5ac70f0Sopenharmony_ci
194d5ac70f0Sopenharmony_ci	if (parse_args(&argv, 32, prog))
195d5ac70f0Sopenharmony_ci		return -EINVAL;
196d5ac70f0Sopenharmony_ci
197d5ac70f0Sopenharmony_ci	prog = argv[0];
198d5ac70f0Sopenharmony_ci	if (prog == NULL) {
199d5ac70f0Sopenharmony_ci		err = -EINVAL;
200d5ac70f0Sopenharmony_ci		goto __error;
201d5ac70f0Sopenharmony_ci	}
202d5ac70f0Sopenharmony_ci	if (prog[0] != '/' && prog[0] != '.') {
203d5ac70f0Sopenharmony_ci		if (!find_exec(argv[0], bin, sizeof(bin))) {
204d5ac70f0Sopenharmony_ci			err = -ENOEXEC;
205d5ac70f0Sopenharmony_ci			goto __error;
206d5ac70f0Sopenharmony_ci		}
207d5ac70f0Sopenharmony_ci		prog = bin;
208d5ac70f0Sopenharmony_ci	}
209d5ac70f0Sopenharmony_ci
210d5ac70f0Sopenharmony_ci	maxfd = sysconf(_SC_OPEN_MAX);
211d5ac70f0Sopenharmony_ci
212d5ac70f0Sopenharmony_ci	/*
213d5ac70f0Sopenharmony_ci	 * block SIGCHLD signal
214d5ac70f0Sopenharmony_ci	 * ignore SIGINT and SIGQUIT in parent
215d5ac70f0Sopenharmony_ci	 */
216d5ac70f0Sopenharmony_ci
217d5ac70f0Sopenharmony_ci	memset(&sa, 0, sizeof(sa));
218d5ac70f0Sopenharmony_ci	sa.sa_handler = SIG_IGN;
219d5ac70f0Sopenharmony_ci	sigemptyset(&sa.sa_mask);
220d5ac70f0Sopenharmony_ci	sigaddset(&sa.sa_mask, SIGCHLD);
221d5ac70f0Sopenharmony_ci
222d5ac70f0Sopenharmony_ci	pthread_mutex_lock(&fork_lock);
223d5ac70f0Sopenharmony_ci
224d5ac70f0Sopenharmony_ci	sigprocmask(SIG_BLOCK, &sa.sa_mask, &omask);
225d5ac70f0Sopenharmony_ci
226d5ac70f0Sopenharmony_ci	sigaction(SIGINT, &sa, &intr);
227d5ac70f0Sopenharmony_ci	sigaction(SIGQUIT, &sa, &quit);
228d5ac70f0Sopenharmony_ci
229d5ac70f0Sopenharmony_ci	p = fork();
230d5ac70f0Sopenharmony_ci
231d5ac70f0Sopenharmony_ci	if (p == -1) {
232d5ac70f0Sopenharmony_ci		err = -errno;
233d5ac70f0Sopenharmony_ci		pthread_mutex_unlock(&fork_lock);
234d5ac70f0Sopenharmony_ci		uc_error("Unable to fork() for \"%s\" -- %s", prog,
235d5ac70f0Sopenharmony_ci			 strerror(errno));
236d5ac70f0Sopenharmony_ci		goto __error;
237d5ac70f0Sopenharmony_ci	}
238d5ac70f0Sopenharmony_ci
239d5ac70f0Sopenharmony_ci	if (p == 0) {
240d5ac70f0Sopenharmony_ci		f = open("/dev/null", O_RDWR);
241d5ac70f0Sopenharmony_ci		if (f == -1) {
242d5ac70f0Sopenharmony_ci			uc_error("pid %d cannot open /dev/null for redirect %s -- %s",
243d5ac70f0Sopenharmony_ci				 getpid(), prog, strerror(errno));
244d5ac70f0Sopenharmony_ci			exit(1);
245d5ac70f0Sopenharmony_ci		}
246d5ac70f0Sopenharmony_ci
247d5ac70f0Sopenharmony_ci		close(0);
248d5ac70f0Sopenharmony_ci		close(1);
249d5ac70f0Sopenharmony_ci		close(2);
250d5ac70f0Sopenharmony_ci
251d5ac70f0Sopenharmony_ci		dup2(f, 0);
252d5ac70f0Sopenharmony_ci		dup2(f, 1);
253d5ac70f0Sopenharmony_ci		dup2(f, 2);
254d5ac70f0Sopenharmony_ci
255d5ac70f0Sopenharmony_ci		close(f);
256d5ac70f0Sopenharmony_ci
257d5ac70f0Sopenharmony_ci		for (f = 3; f < maxfd; f++)
258d5ac70f0Sopenharmony_ci			close(f);
259d5ac70f0Sopenharmony_ci
260d5ac70f0Sopenharmony_ci		/* install default handlers for the forked process */
261d5ac70f0Sopenharmony_ci		signal(SIGINT, SIG_DFL);
262d5ac70f0Sopenharmony_ci		signal(SIGQUIT, SIG_DFL);
263d5ac70f0Sopenharmony_ci
264d5ac70f0Sopenharmony_ci		execve(prog, argv, environ);
265d5ac70f0Sopenharmony_ci		exit(1);
266d5ac70f0Sopenharmony_ci	}
267d5ac70f0Sopenharmony_ci
268d5ac70f0Sopenharmony_ci	sigaction(SIGINT, &intr, NULL);
269d5ac70f0Sopenharmony_ci	sigaction(SIGQUIT, &quit, NULL);
270d5ac70f0Sopenharmony_ci	sigprocmask(SIG_SETMASK, &omask, NULL);
271d5ac70f0Sopenharmony_ci
272d5ac70f0Sopenharmony_ci	pthread_mutex_unlock(&fork_lock);
273d5ac70f0Sopenharmony_ci
274d5ac70f0Sopenharmony_ci	/* make the spawned process a session leader so killing the
275d5ac70f0Sopenharmony_ci	   process group recursively kills any child process that
276d5ac70f0Sopenharmony_ci	   might have been spawned */
277d5ac70f0Sopenharmony_ci	setpgid(p, p);
278d5ac70f0Sopenharmony_ci
279d5ac70f0Sopenharmony_ci	while (1) {
280d5ac70f0Sopenharmony_ci		f = waitpid(p, &status, 0);
281d5ac70f0Sopenharmony_ci		if (f == -1) {
282d5ac70f0Sopenharmony_ci			if (errno == EAGAIN)
283d5ac70f0Sopenharmony_ci				continue;
284d5ac70f0Sopenharmony_ci			err = -errno;
285d5ac70f0Sopenharmony_ci			goto __error;
286d5ac70f0Sopenharmony_ci		}
287d5ac70f0Sopenharmony_ci		if (WIFSIGNALED(status)) {
288d5ac70f0Sopenharmony_ci			err = -EINTR;
289d5ac70f0Sopenharmony_ci			break;
290d5ac70f0Sopenharmony_ci		}
291d5ac70f0Sopenharmony_ci		if (WIFEXITED(status)) {
292d5ac70f0Sopenharmony_ci			err = WEXITSTATUS(status);
293d5ac70f0Sopenharmony_ci			break;
294d5ac70f0Sopenharmony_ci		}
295d5ac70f0Sopenharmony_ci	}
296d5ac70f0Sopenharmony_ci
297d5ac70f0Sopenharmony_ci __error:
298d5ac70f0Sopenharmony_ci	free_args(argv);
299d5ac70f0Sopenharmony_ci	return err;
300d5ac70f0Sopenharmony_ci}
301