From 7af29a1d7cc7f08bb9352c4815964b88b33d86bb Mon Sep 17 00:00:00 2001 From: Yaroslav Maslennikov Date: Tue, 22 Oct 2024 12:52:48 +0200 Subject: [PATCH] Add the os_cmd_shell kernel config parameter to point to system shell for os:cmd/2 Initialize the parameter at startup Fix the test - restore the old shell after the test Add initialization to other tests that use os:cmd/2 Update os:cmd/2 description --- lib/kernel/doc/kernel_app.md | 4 ++ lib/kernel/src/kernel.erl | 1 + lib/kernel/src/os.erl | 69 +++++++++++++-------- lib/kernel/test/application_SUITE.erl | 2 + lib/kernel/test/erl_distribution_SUITE.erl | 1 + lib/kernel/test/file_SUITE.erl | 1 + lib/kernel/test/file_name_SUITE.erl | 1 + lib/kernel/test/gen_tcp_misc_SUITE.erl | 1 + lib/kernel/test/global_SUITE.erl | 1 + lib/kernel/test/inet_SUITE.erl | 1 + lib/kernel/test/init_SUITE.erl | 1 + lib/kernel/test/interactive_shell_SUITE.erl | 1 + lib/kernel/test/kernel_config_SUITE.erl | 1 + lib/kernel/test/kernel_test_lib.erl | 3 + lib/kernel/test/logger_simple_h_SUITE.erl | 1 + lib/kernel/test/os_SUITE.erl | 20 +++++- lib/kernel/test/os_SUITE_data/Makefile.src | 8 ++- lib/kernel/test/os_SUITE_data/sys_shell.c | 6 ++ lib/kernel/test/prim_file_SUITE.erl | 1 + lib/kernel/test/sendfile_SUITE.erl | 1 + lib/kernel/test/seq_trace_SUITE.erl | 1 + lib/kernel/test/socket_SUITE.erl | 1 + lib/kernel/test/socket_api_SUITE.erl | 1 + lib/kernel/test/socket_traffic_SUITE.erl | 1 + 24 files changed, 100 insertions(+), 29 deletions(-) create mode 100644 lib/kernel/test/os_SUITE_data/sys_shell.c diff --git a/lib/kernel/doc/kernel_app.md b/lib/kernel/doc/kernel_app.md index 4984a350ab70..0428b911de30 100644 --- a/lib/kernel/doc/kernel_app.md +++ b/lib/kernel/doc/kernel_app.md @@ -512,6 +512,10 @@ For more information about configuration parameters, see file [Escripts and non-interactive I/O in Unicode Usage in Erlang](`e:stdlib:unicode_usage.md#escripts-and-non-interactive-i-o`) for more details. +- **`os_cmd_shell = string()`{: #os_cmd_shell }** - Specifies which shell to + use when invoking system commands via `os:cmd/2`. By default the shell is detected + automatically. + ## Deprecated Configuration Parameters In Erlang/OTP 21.0, a new API for logging was added. The old `error_logger` diff --git a/lib/kernel/src/kernel.erl b/lib/kernel/src/kernel.erl index 021676c33edc..f53f3b10713a 100644 --- a/lib/kernel/src/kernel.erl +++ b/lib/kernel/src/kernel.erl @@ -33,6 +33,7 @@ start(_, []) -> %% Setup the logger and configure the kernel logger environment ok = logger:internal_init_logger(), + ok = os:internal_init_cmd_shell(), case supervisor:start_link({local, kernel_sup}, kernel, []) of {ok, Pid} -> ok = erl_signal_handler:start(), diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl index adb31bd678f3..570ddb6cf7f5 100644 --- a/lib/kernel/src/os.erl +++ b/lib/kernel/src/os.erl @@ -37,6 +37,8 @@ a program to run on most platforms. -export([type/0, version/0, cmd/1, cmd/2, find_executable/1, find_executable/2]). +-export([internal_init_cmd_shell/0]). + -include("file.hrl"). -export_type([env_var_name/0, env_var_value/0, env_var_name_value/0]). @@ -517,6 +519,9 @@ cmd(Cmd) -> -doc """ Executes `Command` in a command shell of the target OS, captures the standard output and standard error of the command, and returns this result as a string. +The command shell can be set using the +[kernel configuration parameter](kernel_app.md#os_cmd_shell), by default the +shell is detected upon system startup. _Examples:_ @@ -581,35 +586,14 @@ get_option(Opt, Options, Default) -> _ -> throw(badopt) end. -mk_cmd({win32,Wtype}, Cmd) -> - Command = case {os:getenv("COMSPEC"),Wtype} of - {false,windows} -> lists:concat(["command.com /c", Cmd]); - {false,_} -> lists:concat(["cmd /c", Cmd]); - {Cspec,_} -> lists:concat([Cspec," /c",Cmd]) - end, +mk_cmd({win32,_}, Cmd) -> + {ok, Shell} = application:get_env(kernel, os_cmd_shell), + Command = lists:concat([Shell, " /c", Cmd]), {Command, [], [], <<>>}; mk_cmd(_,Cmd) -> %% Have to send command in like this in order to make sh commands like %% cd and ulimit available. - %% - %% We use an absolute path here because we do not want the path to be - %% searched in case a stale NFS handle is somewhere in the path before - %% the sh command. - %% - %% Check if the default shell is located in /bin/sh as expected usually - %% or in /system/bin/sh as implemented on Android. The raw option is - %% used to bypass the file server and speed up the file access. - Shell = case file:read_file_info("/bin/sh",[raw]) of - {ok,#file_info{type=regular}} -> - "/bin/sh"; - _ -> - case file:read_file_info("/system/bin/sh",[raw]) of - {ok,#file_info{type=regular}} -> - "/system/bin/sh"; - _ -> - "/bin/sh" - end - end, + {ok, Shell} = application:get_env(kernel, os_cmd_shell), {Shell ++ " -s unix:cmd", [out], %% We insert a new line after the command, in case the command %% contains a comment character. @@ -628,6 +612,41 @@ mk_cmd(_,Cmd) -> ["(", unicode:characters_to_binary(Cmd), "\n) >}. +-doc false. +internal_init_cmd_shell() -> + case application:get_env(kernel, os_cmd_shell) of + undefined -> + application:set_env(kernel, os_cmd_shell, + internal_init_cmd_shell(os:type())); + _ -> + ok + end. +internal_init_cmd_shell({win32,Wtype}) -> + case {os:getenv("COMSPEC"),Wtype} of + {false,windows} -> "command.com"; + {false,_} -> "cmd"; + {Cspec,_} -> Cspec + end; +internal_init_cmd_shell(_) -> + %% We use an absolute path here because we do not want the path to be + %% searched in case a stale NFS handle is somewhere in the path before + %% the sh command. + %% + %% Check if the default shell is located in /bin/sh as expected usually + %% or in /system/bin/sh as implemented on Android. The raw option is + %% used to bypass the file server. + case file:read_file_info("/bin/sh",[raw]) of + {ok,#file_info{type=regular}} -> + "/bin/sh"; + _ -> + case file:read_file_info("/system/bin/sh",[raw]) of + {ok,#file_info{type=regular}} -> + "/system/bin/sh"; + _ -> + "/bin/sh" + end + end. + validate(Term) -> try validate1(Term) catch error:_ -> throw(badarg) diff --git a/lib/kernel/test/application_SUITE.erl b/lib/kernel/test/application_SUITE.erl index 229c52ecbe6e..577799fe3615 100644 --- a/lib/kernel/test/application_SUITE.erl +++ b/lib/kernel/test/application_SUITE.erl @@ -2060,6 +2060,7 @@ do_configfd_test_bash() -> Command = lists:flatten(io_lib:format("bash -c \"~s\"", [quote_sub_strings(String)])), + os:internal_init_cmd_shell(), Res = os:cmd(Command), io:format("Command:~n"), io:format("~s~n", [Command]), @@ -2255,6 +2256,7 @@ total_memory() -> configfd_bash(Conf) when is_list(Conf) -> case os:type() of {unix,_} -> + os:internal_init_cmd_shell(), case os:cmd("bash -c \"echo -n yes_bash_shell_exists\"") of "yes_bash_shell_exists" -> do_configfd_test_bash(); diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl index dc6f9072a2f9..3a9abd29fe29 100644 --- a/lib/kernel/test/erl_distribution_SUITE.erl +++ b/lib/kernel/test/erl_distribution_SUITE.erl @@ -126,6 +126,7 @@ groups() -> xdg_cookie]}]. init_per_suite(Config) -> + os:internal_init_cmd_shell(), start_gen_tcp_dist_test_type_server(), Config. diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index cdb28b3ee449..cb51a515410d 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -189,6 +189,7 @@ end_per_group(_GroupName, Config) -> init_per_suite(Config) when is_list(Config) -> + os:internal_init_cmd_shell(), SaslConfig = case application:start(sasl) of {error, {already_started, sasl}} -> []; diff --git a/lib/kernel/test/file_name_SUITE.erl b/lib/kernel/test/file_name_SUITE.erl index 6efc54289748..69ce21954821 100644 --- a/lib/kernel/test/file_name_SUITE.erl +++ b/lib/kernel/test/file_name_SUITE.erl @@ -96,6 +96,7 @@ groups() -> []. init_per_suite(Config) -> + os:internal_init_cmd_shell(), Config. end_per_suite(_Config) -> diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl index a3f7f4ebc832..58cc2fffa039 100644 --- a/lib/kernel/test/gen_tcp_misc_SUITE.erl +++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl @@ -358,6 +358,7 @@ socket_monitor_cases() -> ]. init_per_suite(Config0) -> + os:internal_init_cmd_shell(), ?P("init_per_suite -> entry with" "~n Config: ~p" diff --git a/lib/kernel/test/global_SUITE.erl b/lib/kernel/test/global_SUITE.erl index e17122b8b0b0..f2117d2447f0 100644 --- a/lib/kernel/test/global_SUITE.erl +++ b/lib/kernel/test/global_SUITE.erl @@ -159,6 +159,7 @@ end_per_group(_GroupName, Config) -> Config. init_per_suite(Config) -> + os:internal_init_cmd_shell(), Config. end_per_suite(_Config) -> diff --git a/lib/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl index 912e166cc248..9204801748a8 100644 --- a/lib/kernel/test/inet_SUITE.erl +++ b/lib/kernel/test/inet_SUITE.erl @@ -122,6 +122,7 @@ required(hosts) -> init_per_suite(Config0) -> + os:internal_init_cmd_shell(), ?P("init_per_suite -> entry with" "~n Config: ~p" diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl index 4bec6e02a9ea..725c5a71b5dd 100644 --- a/lib/kernel/test/init_SUITE.erl +++ b/lib/kernel/test/init_SUITE.erl @@ -60,6 +60,7 @@ groups() -> [{boot, [], [boot1, boot2]}]. init_per_suite(Config) -> + os:internal_init_cmd_shell(), Config. end_per_suite(_Config) -> diff --git a/lib/kernel/test/interactive_shell_SUITE.erl b/lib/kernel/test/interactive_shell_SUITE.erl index 1f23f05e72a6..6cefc1537a06 100644 --- a/lib/kernel/test/interactive_shell_SUITE.erl +++ b/lib/kernel/test/interactive_shell_SUITE.erl @@ -144,6 +144,7 @@ groups() -> ]. init_per_suite(Config) -> + os:internal_init_cmd_shell(), Term = os:getenv("TERM", "dumb"), os:putenv("TERM", "vt100"), [{term,Term}|Config]. diff --git a/lib/kernel/test/kernel_config_SUITE.erl b/lib/kernel/test/kernel_config_SUITE.erl index e0ed54e57f2a..f04eed8afbd5 100644 --- a/lib/kernel/test/kernel_config_SUITE.erl +++ b/lib/kernel/test/kernel_config_SUITE.erl @@ -44,6 +44,7 @@ end_per_group(_GroupName, Config) -> init_per_suite(Config) when is_list(Config) -> + os:internal_init_cmd_shell(), Config. end_per_suite(Config) when is_list(Config) -> diff --git a/lib/kernel/test/kernel_test_lib.erl b/lib/kernel/test/kernel_test_lib.erl index 079aeefe33a4..0e6d38516e0d 100644 --- a/lib/kernel/test/kernel_test_lib.erl +++ b/lib/kernel/test/kernel_test_lib.erl @@ -72,11 +72,14 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% init_per_suite([{allow_skip, Allow}|Config]) -> + os:internal_init_cmd_shell(), init_per_suite(Allow, Config); init_per_suite(Config) -> + os:internal_init_cmd_shell(), init_per_suite(true, Config). init_per_suite(AllowSkip, Config) when is_boolean(AllowSkip) -> + os:internal_init_cmd_shell(), print("kernel environment: " "~n (kernel) app: ~p" diff --git a/lib/kernel/test/logger_simple_h_SUITE.erl b/lib/kernel/test/logger_simple_h_SUITE.erl index fe224784185c..3b84ae089d2a 100644 --- a/lib/kernel/test/logger_simple_h_SUITE.erl +++ b/lib/kernel/test/logger_simple_h_SUITE.erl @@ -48,6 +48,7 @@ suite() -> [{timetrap,{seconds,30}}]. init_per_suite(Config) -> + os:internal_init_cmd_shell(), Hs0 = logger:get_handler_config(), Hs = lists:keydelete(cth_log_redirect,1,Hs0), [ok = logger:remove_handler(Id) || {Id,_,_} <- Hs], diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl index 6d6ed337a0cd..d5cf6735adb3 100644 --- a/lib/kernel/test/os_SUITE.erl +++ b/lib/kernel/test/os_SUITE.erl @@ -28,7 +28,7 @@ find_executable/1, unix_comment_in_command/1, deep_list_command/1, large_output_command/1, background_command/0, background_command/1, message_leak/1, close_stdin/0, close_stdin/1, max_size_command/1, - perf_counter_api/1, error_info/1]). + perf_counter_api/1, error_info/1, os_cmd_shell/1]). -include_lib("common_test/include/ct.hrl"). @@ -43,12 +43,13 @@ all() -> find_executable, unix_comment_in_command, deep_list_command, large_output_command, background_command, message_leak, close_stdin, max_size_command, perf_counter_api, - error_info]. + error_info, os_cmd_shell]. groups() -> []. init_per_suite(Config) -> + os:internal_init_cmd_shell(), Config. end_per_suite(_Config) -> @@ -61,7 +62,7 @@ end_per_group(_GroupName, Config) -> Config. init_per_testcase(TC, Config) - when TC =:= background_command; TC =:= close_stdin -> + when TC =:= background_command; TC =:= close_stdin; TC =:= os_cmd_shell -> case os:type() of {win32, _} -> {skip,"Should not work on windows"}; @@ -469,6 +470,19 @@ error_info(Config) -> ], error_info_lib:test_error_info(os, L). +os_cmd_shell(Config) -> + DataDir = proplists:get_value(data_dir, Config), + SysShell = filename:join(DataDir, "sys_shell"), + + {ok, OldShell} = application:get_env(kernel, os_cmd_shell), + application:set_env(kernel, os_cmd_shell, SysShell), + + %% os:cmd should not try to detect the shell location rather than use + %% the value from kernel:os_cmd_shell parameter + Ls = os:cmd("ls"), + application:set_env(kernel, os_cmd_shell, OldShell), + comp("sys_shell", Ls). + no_limit_for_opened_files() -> case os:type() of {unix, freebsd} -> diff --git a/lib/kernel/test/os_SUITE_data/Makefile.src b/lib/kernel/test/os_SUITE_data/Makefile.src index f83f781411d9..2141b320bb3d 100644 --- a/lib/kernel/test/os_SUITE_data/Makefile.src +++ b/lib/kernel/test/os_SUITE_data/Makefile.src @@ -3,7 +3,7 @@ LD = @LD@ CFLAGS = @CFLAGS@ -I@erl_include@ @DEFS@ CROSSLDFLAGS = @CROSSLDFLAGS@ -PROGS = my_echo@exe@ my_fds@exe@ +PROGS = my_echo@exe@ my_fds@exe@ sys_shell@exe@ all: $(PROGS) @@ -18,3 +18,9 @@ my_fds@exe@: my_fds@obj@ my_fds@obj@: my_fds.c $(CC) -c -o my_fds@obj@ $(CFLAGS) my_fds.c + +sys_shell@exe@: sys_shell@obj@ + $(LD) $(CROSSLDFLAGS) -o sys_shell sys_shell@obj@ @LIBS@ + +sys_shell@obj@: sys_shell.c + $(CC) -c -o sys_shell@obj@ $(CFLAGS) sys_shell.c diff --git a/lib/kernel/test/os_SUITE_data/sys_shell.c b/lib/kernel/test/os_SUITE_data/sys_shell.c new file mode 100644 index 000000000000..73a8c034501e --- /dev/null +++ b/lib/kernel/test/os_SUITE_data/sys_shell.c @@ -0,0 +1,6 @@ +#include +int main(void) +{ + printf("sys_shell"); + return 0; +} diff --git a/lib/kernel/test/prim_file_SUITE.erl b/lib/kernel/test/prim_file_SUITE.erl index aa96c334b938..763ab0a276bc 100644 --- a/lib/kernel/test/prim_file_SUITE.erl +++ b/lib/kernel/test/prim_file_SUITE.erl @@ -100,6 +100,7 @@ end_per_group(_GroupName, Config) -> init_per_suite(Config) when is_list(Config) -> + os:internal_init_cmd_shell(), case os:type() of {win32, _} -> Priv = proplists:get_value(priv_dir, Config), diff --git a/lib/kernel/test/sendfile_SUITE.erl b/lib/kernel/test/sendfile_SUITE.erl index af409e33db1a..4c249d534bfb 100644 --- a/lib/kernel/test/sendfile_SUITE.erl +++ b/lib/kernel/test/sendfile_SUITE.erl @@ -60,6 +60,7 @@ all() -> t_sendfile_arguments]. init_per_suite(Config) -> + os:internal_init_cmd_shell(), case {os:type(),os:version()} of {{unix,sunos}, {5,8,_}} -> {skip, "Solaris 8 not supported for now"}; diff --git a/lib/kernel/test/seq_trace_SUITE.erl b/lib/kernel/test/seq_trace_SUITE.erl index 6e3cb08af3d3..713d881bde38 100644 --- a/lib/kernel/test/seq_trace_SUITE.erl +++ b/lib/kernel/test/seq_trace_SUITE.erl @@ -61,6 +61,7 @@ groups() -> []. init_per_suite(Config) -> + os:internal_init_cmd_shell(), Config. end_per_suite(_Config) -> diff --git a/lib/kernel/test/socket_SUITE.erl b/lib/kernel/test/socket_SUITE.erl index 34940c69acae..e9309d47d5e1 100644 --- a/lib/kernel/test/socket_SUITE.erl +++ b/lib/kernel/test/socket_SUITE.erl @@ -398,6 +398,7 @@ otp18240_cases() -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% init_per_suite(Config0) -> + os:internal_init_cmd_shell(), ?P("init_per_suite -> entry with" "~n Config: ~p" "~n Nodes: ~p", [Config0, erlang:nodes()]), diff --git a/lib/kernel/test/socket_api_SUITE.erl b/lib/kernel/test/socket_api_SUITE.erl index 92a0507d2c20..70e2a8b7faca 100644 --- a/lib/kernel/test/socket_api_SUITE.erl +++ b/lib/kernel/test/socket_api_SUITE.erl @@ -667,6 +667,7 @@ api_op_with_timeout_cases() -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% init_per_suite(Config0) -> + os:internal_init_cmd_shell(), ?P("init_per_suite -> entry with" "~n Config: ~p" "~n Nodes: ~p", [Config0, erlang:nodes()]), diff --git a/lib/kernel/test/socket_traffic_SUITE.erl b/lib/kernel/test/socket_traffic_SUITE.erl index 28617362909a..8e45d7f3a4fd 100644 --- a/lib/kernel/test/socket_traffic_SUITE.erl +++ b/lib/kernel/test/socket_traffic_SUITE.erl @@ -307,6 +307,7 @@ traffic_pp_sendmsg_recvmsg_cases() -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% init_per_suite(Config0) -> + os:internal_init_cmd_shell(), ?P("init_per_suite -> entry with" "~n Config: ~p" "~n Nodes: ~p", [Config0, erlang:nodes()]),