mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-11-01 22:58:49 -04:00
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>
This commit is contained in:
parent
a468505c96
commit
590df3a5b1
6 changed files with 312 additions and 7 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
EXTRA_LTLIBRARIES = libucm.la
|
EXTRA_LTLIBRARIES = libucm.la
|
||||||
|
|
||||||
libucm_la_SOURCES = utils.c parser.c ucm_cond.c ucm_subs.c ucm_include.c \
|
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
|
noinst_HEADERS = ucm_local.h
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -717,6 +717,14 @@ static int execute_sequence(snd_use_case_mgr_t *uc_mgr,
|
||||||
usleep(s->data.sleep);
|
usleep(s->data.sleep);
|
||||||
break;
|
break;
|
||||||
case SEQUENCE_ELEMENT_TYPE_EXEC:
|
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);
|
err = system(s->data.exec);
|
||||||
if (WIFSIGNALED(err)) {
|
if (WIFSIGNALED(err)) {
|
||||||
err = -EINTR;
|
err = -EINTR;
|
||||||
|
|
@ -727,6 +735,8 @@ static int execute_sequence(snd_use_case_mgr_t *uc_mgr,
|
||||||
goto __fail;
|
goto __fail;
|
||||||
}
|
}
|
||||||
} else if (err < 0) {
|
} else if (err < 0) {
|
||||||
|
if (errno == EAGAIN)
|
||||||
|
goto shell_retry;
|
||||||
err = -errno;
|
err = -errno;
|
||||||
goto __fail;
|
goto __fail;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -910,6 +910,7 @@ cset:
|
||||||
|
|
||||||
if (strcmp(cmd, "exec") == 0) {
|
if (strcmp(cmd, "exec") == 0) {
|
||||||
curr->type = SEQUENCE_ELEMENT_TYPE_EXEC;
|
curr->type = SEQUENCE_ELEMENT_TYPE_EXEC;
|
||||||
|
exec:
|
||||||
err = parse_string_substitute3(uc_mgr, n, &curr->data.exec);
|
err = parse_string_substitute3(uc_mgr, n, &curr->data.exec);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
uc_error("error: exec requires a string!");
|
uc_error("error: exec requires a string!");
|
||||||
|
|
@ -918,6 +919,11 @@ cset:
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strcmp(cmd, "shell") == 0) {
|
||||||
|
curr->type = SEQUENCE_ELEMENT_TYPE_SHELL;
|
||||||
|
goto exec;
|
||||||
|
}
|
||||||
|
|
||||||
if (strcmp(cmd, "comment") == 0)
|
if (strcmp(cmd, "comment") == 0)
|
||||||
goto skip;
|
goto skip;
|
||||||
|
|
||||||
|
|
|
||||||
285
src/ucm/ucm_exec.c
Normal file
285
src/ucm/ucm_exec.c
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -49,12 +49,13 @@
|
||||||
#define SEQUENCE_ELEMENT_TYPE_CSET 2
|
#define SEQUENCE_ELEMENT_TYPE_CSET 2
|
||||||
#define SEQUENCE_ELEMENT_TYPE_SLEEP 3
|
#define SEQUENCE_ELEMENT_TYPE_SLEEP 3
|
||||||
#define SEQUENCE_ELEMENT_TYPE_EXEC 4
|
#define SEQUENCE_ELEMENT_TYPE_EXEC 4
|
||||||
#define SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE 5
|
#define SEQUENCE_ELEMENT_TYPE_SHELL 5
|
||||||
#define SEQUENCE_ELEMENT_TYPE_CSET_TLV 6
|
#define SEQUENCE_ELEMENT_TYPE_CSET_BIN_FILE 6
|
||||||
#define SEQUENCE_ELEMENT_TYPE_CSET_NEW 7
|
#define SEQUENCE_ELEMENT_TYPE_CSET_TLV 7
|
||||||
#define SEQUENCE_ELEMENT_TYPE_CTL_REMOVE 8
|
#define SEQUENCE_ELEMENT_TYPE_CSET_NEW 8
|
||||||
#define SEQUENCE_ELEMENT_TYPE_CMPT_SEQ 9
|
#define SEQUENCE_ELEMENT_TYPE_CTL_REMOVE 9
|
||||||
#define SEQUENCE_ELEMENT_TYPE_SYSSET 10
|
#define SEQUENCE_ELEMENT_TYPE_CMPT_SEQ 10
|
||||||
|
#define SEQUENCE_ELEMENT_TYPE_SYSSET 11
|
||||||
|
|
||||||
struct ucm_value {
|
struct ucm_value {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
|
|
@ -356,6 +357,8 @@ int uc_mgr_define_regex(snd_use_case_mgr_t *uc_mgr,
|
||||||
const char *name,
|
const char *name,
|
||||||
snd_config_t *eval);
|
snd_config_t *eval);
|
||||||
|
|
||||||
|
int uc_mgr_exec(const char *prog);
|
||||||
|
|
||||||
/** The name of the environment variable containing the UCM directory */
|
/** The name of the environment variable containing the UCM directory */
|
||||||
#define ALSA_CONFIG_UCM_VAR "ALSA_CONFIG_UCM"
|
#define ALSA_CONFIG_UCM_VAR "ALSA_CONFIG_UCM"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -509,6 +509,7 @@ void uc_mgr_free_sequence_element(struct sequence_element *seq)
|
||||||
free(seq->data.sysw);
|
free(seq->data.sysw);
|
||||||
break;
|
break;
|
||||||
case SEQUENCE_ELEMENT_TYPE_EXEC:
|
case SEQUENCE_ELEMENT_TYPE_EXEC:
|
||||||
|
case SEQUENCE_ELEMENT_TYPE_SHELL:
|
||||||
free(seq->data.exec);
|
free(seq->data.exec);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue