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