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