GNU Make uses /bin/sh by default for running recipe lines and $(shell ) functions. You can change the shell by setting the 'SHELL' variable. Unlike most variables, 'SHELL' is never set from the environment. [1] Currently, Kconfig does not provide any way to change the default shell. /bin/sh is always used for running $(shell,...) because do_shell() is implemented by using popen(3). This commit allows users to change the shell for Kconfig in a similar way to GNU Make; you can set the 'SHELL' variable in a Kconfig file to override the default shell. It is not taken from the environment. The change is effective only for $(shell,...) invocations called after the 'SHELL' assignment. [1]: https://www.gnu.org/software/make/manual/html_node/Choosing-the-Shell.html Signed-off-by: Masahiro Yamada <masahiroy@xxxxxxxxxx> --- .../kbuild/kconfig-macro-language.rst | 4 ++ scripts/kconfig/internal.h | 1 + scripts/kconfig/parser.y | 1 + scripts/kconfig/preprocess.c | 65 +++++++++++++++---- 4 files changed, 57 insertions(+), 14 deletions(-) diff --git a/Documentation/kbuild/kconfig-macro-language.rst b/Documentation/kbuild/kconfig-macro-language.rst index 6163467f6ae4..fe8c6982179e 100644 --- a/Documentation/kbuild/kconfig-macro-language.rst +++ b/Documentation/kbuild/kconfig-macro-language.rst @@ -112,6 +112,10 @@ Kconfig currently supports the following built-in functions. replaced with a space. Any trailing newlines are deleted. The standard error is not returned, nor is any program exit status. + The program used as the shell is taken from the variable SHELL. If it is not + set anywhere in your Kconfig file, /bin/sh is used as the shell. + Unlike most variables, the variable SHELL is never set from the environment. + - $(info,text) The "info" function takes a single argument and prints it to stdout. diff --git a/scripts/kconfig/internal.h b/scripts/kconfig/internal.h index 8e0e6d315b6c..c18017650e54 100644 --- a/scripts/kconfig/internal.h +++ b/scripts/kconfig/internal.h @@ -19,6 +19,7 @@ enum variable_flavor { void env_write_dep(FILE *f, const char *auto_conf_name); void variable_add(const char *name, const char *value, enum variable_flavor flavor); +void variable_init(void); void variable_all_del(void); char *expand_dollar(const char **str); char *expand_one_token(const char **str); diff --git a/scripts/kconfig/parser.y b/scripts/kconfig/parser.y index 2af7ce4e1531..436afaef9228 100644 --- a/scripts/kconfig/parser.y +++ b/scripts/kconfig/parser.y @@ -483,6 +483,7 @@ void conf_parse(const char *name) zconf_initscan(name); _menu_init(); + variable_init(); if (getenv("ZCONF_DEBUG")) yydebug = 1; diff --git a/scripts/kconfig/preprocess.c b/scripts/kconfig/preprocess.c index aeb3fe362c04..608a84e7f5d6 100644 --- a/scripts/kconfig/preprocess.c +++ b/scripts/kconfig/preprocess.c @@ -8,6 +8,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <unistd.h> #include "list.h" #include "lkc.h" @@ -141,24 +142,59 @@ static char *do_lineno(int argc, char *argv[]) static char *do_shell(int argc, char *argv[]) { - FILE *p; + int pipefd[2]; + pid_t pid; char buf[4096]; - char *cmd; - size_t nread; + ssize_t nread; int i; - cmd = argv[0]; - - p = popen(cmd, "r"); - if (!p) { - perror(cmd); + if (pipe(pipefd) < 0) { + perror("pipe"); + exit(1); + } + pid = fork(); + if (pid < -1) { + perror("fork"); exit(1); } - nread = fread(buf, 1, sizeof(buf), p); + if (pid == 0) { /* child */ + char *shell; + + /* duplicate the write end to stdout */ + if (dup2(pipefd[1], STDOUT_FILENO) < 0) { + perror("dup2"); + _exit(1); + } + + /* + * Do not leak file descriptors to the child process + * (including the read end of the pipe). + * Closing up to 15 is enough for us? + */ + for (i = STDERR_FILENO + 1; i < 16; i++) + close(i); + + shell = expand_string("$(SHELL)"); + + execl(shell, shell, "-c", argv[0], NULL); + perror("execl"); + + free(shell); + + _exit(1); + } + + /* parent */ + + close(pipefd[1]); /* the write end is unneeded */ + + nread = read(pipefd[0], buf, sizeof(buf)); if (nread == sizeof(buf)) nread--; + close(pipefd[0]); /* now close the read end */ + /* remove trailing new lines */ while (nread > 0 && buf[nread - 1] == '\n') nread--; @@ -171,11 +207,6 @@ static char *do_shell(int argc, char *argv[]) buf[i] = ' '; } - if (pclose(p) == -1) { - perror(cmd); - exit(1); - } - return xstrdup(buf); } @@ -330,6 +361,12 @@ static void variable_del(struct variable *v) free(v); } +void variable_init(void) +{ + /* Set the default shell */ + variable_add("SHELL", "/bin/sh", VAR_RECURSIVE); +} + void variable_all_del(void) { struct variable *v, *tmp; -- 2.34.1