summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaroslav Kysela <perex@perex.cz>2021-05-11 14:48:16 +0200
committerJaroslav Kysela <perex@perex.cz>2021-05-12 08:32:45 +0200
commit590df3a5b1d632dbdc86d0efaf418cea8b589686 (patch)
tree0dbe8d41b12d7446c9b26a88c37fc7de4b5532ff
parenta468505c9653545cc709025b85ca2b124f768023 (diff)
downloadalsa-lib-590df3a5b1d632dbdc86d0efaf418cea8b589686.tar.gz
ucm: add exec sequence command
This change renames the original exec command to shell which is more appropriate. Implement a light version of the exec command which calls directly the specified executable without the shell interaction (man 3 system). Signed-off-by: Jaroslav Kysela <perex@perex.cz>
-rw-r--r--src/ucm/Makefile.am2
-rw-r--r--src/ucm/main.c10
-rw-r--r--src/ucm/parser.c6
-rw-r--r--src/ucm/ucm_exec.c285
-rw-r--r--src/ucm/ucm_local.h15
-rw-r--r--src/ucm/utils.c1
6 files changed, 312 insertions, 7 deletions
diff --git a/src/ucm/Makefile.am b/src/ucm/Makefile.am
index feb3c0c1..1f1d8b1f 100644
--- a/src/ucm/Makefile.am
+++ b/src/ucm/Makefile.am
@@ -1,7 +1,7 @@
EXTRA_LTLIBRARIES = libucm.la
libucm_la_SOURCES = utils.c parser.c ucm_cond.c ucm_subs.c ucm_include.c \
- ucm_regex.c main.c
+ ucm_regex.c ucm_exec.c main.c
noinst_HEADERS = ucm_local.h
diff --git a/src/ucm/main.c b/src/ucm/main.c
index 3df9b62a..e6be169f 100644
--- a/src/ucm/main.c
+++ b/src/ucm/main.c
@@ -717,6 +717,14 @@ static int execute_sequence(snd_use_case_mgr_t *uc_mgr,
usleep(s->data.sleep);
break;
case SEQUENCE_ELEMENT_TYPE_EXEC:
+ err = uc_mgr_exec(s->data.exec);
+ if (err != 0) {
+ uc_error("exec '%s' failed (exit code %d)", s->data.exec, err);
+ goto __fail;
+ }
+ break;
+ case SEQUENCE_ELEMENT_TYPE_SHELL:
+shell_retry:
err = system(s->data.exec);
if (WIFSIGNALED(err)) {
err = -EINTR;
@@ -727,6 +735,8 @@ static int execute_sequence(snd_use_case_mgr_t *uc_mgr,
goto __fail;
}
} else if (err < 0) {
+ if (errno == EAGAIN)
+ goto shell_retry;
err = -errno;
goto __fail;
}
diff --git a/src/ucm/parser.c b/src/ucm/parser.c
index c0a9c13a..33754803 100644
--- a/src/ucm/parser.c
+++ b/src/ucm/parser.c
@@ -910,6 +910,7 @@ cset:
if (strcmp(cmd, "exec") == 0) {
curr->type = SEQUENCE_ELEMENT_TYPE_EXEC;
+exec:
err = parse_string_substitute3(uc_mgr, n, &curr->data.exec);
if (err < 0) {
uc_error("error: exec requires a string!");
@@ -918,6 +919,11 @@ cset:
continue;
}
+ if (strcmp(cmd, "shell") == 0) {
+ curr->type = SEQUENCE_ELEMENT_TYPE_SHELL;
+ goto exec;
+ }
+
if (strcmp(cmd, "comment") == 0)
goto skip;
diff --git a/src/ucm/ucm_exec.c b/src/ucm/ucm_exec.c
new file mode 100644
index 00000000..a22df8fe
--- /dev/null
+++ b/src/ucm/ucm_exec.c
@@ -0,0 +1,285 @@
+/*
+ * Exec an external program
+ * Copyright (C) 2021 Jaroslav Kysela
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Support for the verb/device/modifier core logic and API,
+ * command line tool and file parser was kindly sponsored by
+ * Texas Instruments Inc.
+ * Support for multiple active modifiers and devices,
+ * transition sequences, multiple client access and user defined use
+ * cases was kindly sponsored by Wolfson Microelectronics PLC.
+ *
+ * Copyright (C) 2021 Red Hat Inc.
+ * Authors: Jaroslav Kysela <perex@perex.cz>
+ */
+
+#include "ucm_local.h"
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <dirent.h>
+
+static pthread_mutex_t fork_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/*
+ * Search PATH for executable
+ */
+static int find_exec(const char *name, char *out, size_t len)
+{
+ int ret = 0;
+ char bin[PATH_MAX];
+ char *path, *tmp, *tmp2 = NULL;
+ DIR *dir;
+ struct dirent *de;
+ struct stat st;
+ if (name[0] == '/') {
+ if (lstat(name, &st))
+ return 0;
+ if (!S_ISREG(st.st_mode) || !(st.st_mode & S_IEXEC))
+ return 0;
+ snd_strlcpy(out, name, len);
+ return 1;
+ }
+ if (!(tmp = getenv("PATH")))
+ return 0;
+ path = alloca(strlen(tmp) + 1);
+ if (!path)
+ return 0;
+ strcpy(path, tmp);
+ tmp = strtok_r(path, ":", &tmp2);
+ while (tmp && !ret) {
+ if ((dir = opendir(tmp))) {
+ while ((de = readdir(dir))) {
+ if (strstr(de->d_name, name) != de->d_name)
+ continue;
+ snprintf(bin, sizeof(bin), "%s/%s", tmp,
+ de->d_name);
+ if (lstat(bin, &st))
+ continue;
+ if (!S_ISREG(st.st_mode)
+ || !(st.st_mode & S_IEXEC))
+ continue;
+ snd_strlcpy(out, bin, len);
+ return 1;
+ }
+ closedir(dir);
+ }
+ tmp = strtok_r(NULL, ":", &tmp2);
+ }
+ return ret;
+}
+
+static void free_args(char **argv)
+{
+ char **a;
+
+ for (a = argv; *a; a++)
+ free(*a);
+ free(argv);
+}
+
+static int parse_args(char ***argv, int argc, const char *cmd)
+{
+ char *s, *f;
+ int i = 0, l, eow;
+
+ if (!argv || !cmd)
+ return -1;
+
+ s = alloca(strlen(cmd) + 1);
+ if (!s)
+ return -1;
+ strcpy(s, cmd);
+ *argv = calloc(argc, sizeof(char *));
+
+ while (*s && i < argc - 1) {
+ while (*s == ' ')
+ s++;
+ f = s;
+ eow = 0;
+ while (*s) {
+ if (*s == '\\') {
+ l = *(s + 1);
+ if (l == 'b')
+ l = '\b';
+ else if (l == 'f')
+ l = '\f';
+ else if (l == 'n')
+ l = '\n';
+ else if (l == 'r')
+ l = '\r';
+ else if (l == 't')
+ l = '\t';
+ else
+ l = 0;
+ if (l) {
+ *s++ = l;
+ memmove(s, s + 1, strlen(s));
+ } else {
+ memmove(s, s + 1, strlen(s));
+ if (*s)
+ s++;
+ }
+ } else if (eow) {
+ if (*s == eow) {
+ memmove(s, s + 1, strlen(s));
+ eow = 0;
+ } else {
+ s++;
+ }
+ } else if (*s == '\'' || *s == '"') {
+ eow = *s;
+ memmove(s, s + 1, strlen(s));
+ } else if (*s == ' ') {
+ break;
+ } else {
+ s++;
+ }
+ }
+ if (f != s) {
+ if (*s) {
+ *(char *)s = '\0';
+ s++;
+ }
+ (*argv)[i] = strdup(f);
+ if ((*argv)[i] == NULL) {
+ free_args(*argv);
+ return -ENOMEM;
+ }
+ i++;
+ }
+ }
+ (*argv)[i] = NULL;
+ return 0;
+}
+
+/*
+ * execute a binary file
+ *
+ */
+int uc_mgr_exec(const char *prog)
+{
+ pid_t p, f, maxfd;
+ int err = 0, status;
+ char bin[PATH_MAX];
+ struct sigaction sa;
+ struct sigaction intr, quit;
+ sigset_t omask;
+ char **argv;
+
+ if (parse_args(&argv, 32, prog))
+ return -EINVAL;
+
+ prog = argv[0];
+ if (argv[0][0] != '/' && argv[0][0] != '.') {
+ if (!find_exec(argv[0], bin, sizeof(bin))) {
+ err = -ENOEXEC;
+ goto __error;
+ }
+ prog = bin;
+ }
+
+ maxfd = sysconf(_SC_OPEN_MAX);
+
+ /*
+ * block SIGCHLD signal
+ * ignore SIGINT and SIGQUIT in parent
+ */
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = SIG_IGN;
+ sigemptyset(&sa.sa_mask);
+ sigaddset(&sa.sa_mask, SIGCHLD);
+
+ pthread_mutex_lock(&fork_lock);
+
+ sigprocmask(SIG_BLOCK, &sa.sa_mask, &omask);
+
+ sigaction(SIGINT, &sa, &intr);
+ sigaction(SIGQUIT, &sa, &quit);
+
+ p = fork();
+
+ if (p == -1) {
+ err = -errno;
+ pthread_mutex_unlock(&fork_lock);
+ uc_error("Unable to fork() for \"%s\" -- %s", prog,
+ strerror(errno));
+ goto __error;
+ }
+
+ if (p == 0) {
+ f = open("/dev/null", O_RDWR);
+ if (f == -1) {
+ uc_error("pid %d cannot open /dev/null for redirect %s -- %s",
+ getpid(), prog, strerror(errno));
+ exit(1);
+ }
+
+ close(0);
+ close(1);
+ close(2);
+
+ dup2(f, 0);
+ dup2(f, 1);
+ dup2(f, 2);
+
+ close(f);
+
+ for (f = 3; f < maxfd; f++)
+ close(f);
+
+ /* install default handlers for the forked process */
+ signal(SIGINT, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+
+ execve(prog, argv, environ);
+ exit(1);
+ }
+
+ sigaction(SIGINT, &intr, NULL);
+ sigaction(SIGQUIT, &quit, NULL);
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+
+ pthread_mutex_unlock(&fork_lock);
+
+ /* make the spawned process a session leader so killing the
+ process group recursively kills any child process that
+ might have been spawned */
+ setpgid(p, p);
+
+ while (1) {
+ f = waitpid(p, &status, 0);
+ if (f == -1) {
+ if (errno == EAGAIN)
+ continue;
+ err = -errno;
+ goto __error;
+ }
+ if (WIFSIGNALED(status)) {
+ err = -EINTR;
+ break;
+ }
+ if (WIFEXITED(status)) {
+ err = WEXITSTATUS(status);
+ break;
+ }
+ }
+
+ __error:
+ free_args(argv);
+ return err;
+}
diff --git a/src/ucm/ucm_local.h b/src/ucm/ucm_local.h
index 7dfd24b9..c0374148 100644
--- a/src/ucm/ucm_local.h
+++ b/src/ucm/ucm_local.h
@@ -49,12 +49,13 @@
#define SEQUENCE_ELEMENT_TYPE_CSET 2
#define SEQUENCE_ELEMENT_TYPE_SLEEP 3
#define SEQUENCE_ELEMENT_TYPE_EXEC 4
-#define SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE 5
-#define SEQUENCE_ELEMENT_TYPE_CSET_TLV 6
-#define SEQUENCE_ELEMENT_TYPE_CSET_NEW 7
-#define SEQUENCE_ELEMENT_TYPE_CTL_REMOVE 8
-#define SEQUENCE_ELEMENT_TYPE_CMPT_SEQ 9
-#define SEQUENCE_ELEMENT_TYPE_SYSSET 10
+#define SEQUENCE_ELEMENT_TYPE_SHELL 5
+#define SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE 6
+#define SEQUENCE_ELEMENT_TYPE_CSET_TLV 7
+#define SEQUENCE_ELEMENT_TYPE_CSET_NEW 8
+#define SEQUENCE_ELEMENT_TYPE_CTL_REMOVE 9
+#define SEQUENCE_ELEMENT_TYPE_CMPT_SEQ 10
+#define SEQUENCE_ELEMENT_TYPE_SYSSET 11
struct ucm_value {
struct list_head list;
@@ -356,6 +357,8 @@ int uc_mgr_define_regex(snd_use_case_mgr_t *uc_mgr,
const char *name,
snd_config_t *eval);
+int uc_mgr_exec(const char *prog);
+
/** The name of the environment variable containing the UCM directory */
#define ALSA_CONFIG_UCM_VAR "ALSA_CONFIG_UCM"
diff --git a/src/ucm/utils.c b/src/ucm/utils.c
index 560c58dd..0eaf6c3c 100644
--- a/src/ucm/utils.c
+++ b/src/ucm/utils.c
@@ -509,6 +509,7 @@ void uc_mgr_free_sequence_element(struct sequence_element *seq)
free(seq->data.sysw);
break;
case SEQUENCE_ELEMENT_TYPE_EXEC:
+ case SEQUENCE_ELEMENT_TYPE_SHELL:
free(seq->data.exec);
break;
default: