OSDN Git Service

ucm: add exec sequence command
authorJaroslav Kysela <perex@perex.cz>
Tue, 11 May 2021 12:48:16 +0000 (14:48 +0200)
committerJaroslav Kysela <perex@perex.cz>
Wed, 12 May 2021 06:32:45 +0000 (08:32 +0200)
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>
src/ucm/Makefile.am
src/ucm/main.c
src/ucm/parser.c
src/ucm/ucm_exec.c [new file with mode: 0644]
src/ucm/ucm_local.h
src/ucm/utils.c

index feb3c0c..1f1d8b1 100644 (file)
@@ -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
 
index 3df9b62..e6be169 100644 (file)
@@ -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;
                        }
index c0a9c13..3375480 100644 (file)
@@ -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 (file)
index 0000000..a22df8f
--- /dev/null
@@ -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;
+}
index 7dfd24b..c037414 100644 (file)
 #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"
 
index 560c58d..0eaf6c3 100644 (file)
@@ -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: