aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kernel')
-rw-r--r--lib/kernel/doc/src/notes.xml20
-rw-r--r--lib/kernel/doc/src/os.xml41
-rw-r--r--lib/kernel/src/os.erl41
-rw-r--r--lib/kernel/test/os_SUITE.erl18
-rw-r--r--lib/kernel/vsn.mk2
5 files changed, 98 insertions, 24 deletions
diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml
index d7f224c38e..65fe9b9c07 100644
--- a/lib/kernel/doc/src/notes.xml
+++ b/lib/kernel/doc/src/notes.xml
@@ -31,6 +31,26 @@
</header>
<p>This document describes the changes made to the Kernel application.</p>
+<section><title>Kernel 5.4.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Add <c>os:cmd/2</c> that takes an options map as the
+ second argument.</p>
+ <p>
+ Add <c>max_size</c> as an option to <c>os:cmd/2</c> that
+ control the maximum size of the result that
+ <c>os:cmd/2</c> will return.</p>
+ <p>
+ Own Id: OTP-14823</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Kernel 5.4.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/kernel/doc/src/os.xml b/lib/kernel/doc/src/os.xml
index c27182ff0b..ef416ed233 100644
--- a/lib/kernel/doc/src/os.xml
+++ b/lib/kernel/doc/src/os.xml
@@ -103,7 +103,7 @@
</desc>
</datatype>
<datatype>
- <name name="command_input"/>
+ <name name="os_command"/>
<desc>
<p>All characters needs to be valid characters on the
specific OS using
@@ -116,19 +116,31 @@
</p>
</desc>
</datatype>
+ <datatype>
+ <name name="os_command_opts"/>
+ <desc>
+ <p>Options for <seealso marker="#cmd/2"><c>os:cmd/2</c></seealso></p>
+ <taglist>
+ <tag><c>max_size</c></tag>
+ <item>
+ <p>The maximum size of the data returned by the <c>os:cmd</c> call.
+ See the <seealso marker="#cmd/2"><c>os:cmd/2</c></seealso>
+ documentation for more details.</p>
+ </item>
+ </taglist>
+ </desc>
+ </datatype>
</datatypes>
-
+
<funcs>
<func>
<name name="cmd" arity="1"/>
+ <name name="cmd" arity="2"/>
<fsummary>Execute a command in a shell of the target OS.</fsummary>
<desc>
<p>Executes <c><anno>Command</anno></c> in a command shell of the
- target OS,
- captures the standard output of the command, and returns this
- result as a string. This function is a replacement of
- the previous function <c>unix:cmd/1</c>; they are equivalent on a
- Unix platform.</p>
+ target OS, captures the standard output of the command,
+ and returns this result as a string.</p>
<warning><p>Previous implementation used to allow all characters
as long as they were integer values greater than or equal to zero.
This sometimes lead to unwanted results since null characters
@@ -142,6 +154,21 @@ DirOut = os:cmd("dir"), % on Win32 platform</code>
called from another program (for example, <c>os:cmd/1</c>)
can differ, compared with the standard output of the command
when called directly from an OS command shell.</p>
+ <p><c>os:cmd/2</c> was added in kernel-5.5 (OTP-20.2.1). It makes it
+ possible to pass an options map as the second argument in order to
+ control the behaviour of <c>os:cmd</c>. The possible options are:
+ </p>
+ <taglist>
+ <tag><c>max_size</c></tag>
+ <item>
+ <p>The maximum size of the data returned by the <c>os:cmd</c> call.
+ This option is a safety feature that should be used when the command
+ executed can return a very large, possibly infinite, result.</p>
+ <code type="none">
+> os:cmd("cat /dev/zero", #{ max_size => 20 }).
+[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]</code>
+ </item>
+ </taglist>
</desc>
</func>
diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl
index fbc046c8f9..1e9db80058 100644
--- a/lib/kernel/src/os.erl
+++ b/lib/kernel/src/os.erl
@@ -21,11 +21,11 @@
%% Provides a common operating system interface.
--export([type/0, version/0, cmd/1, find_executable/1, find_executable/2]).
+-export([type/0, version/0, cmd/1, cmd/2, find_executable/1, find_executable/2]).
-include("file.hrl").
--export_type([env_var_name/0, env_var_value/0, env_var_name_value/0, command_input/0]).
+-export_type([env_var_name/0, env_var_value/0, env_var_name_value/0]).
-export([getenv/0, getenv/1, getenv/2, putenv/2, unsetenv/1]).
@@ -35,14 +35,17 @@
perf_counter/1, set_env_var/2, set_signal/2, system_time/0,
system_time/1, timestamp/0, unset_env_var/1]).
+-type os_command() :: atom() | io_lib:chars().
+-type os_command_opts() :: #{ max_size => non_neg_integer() | infinity }.
+
+-export_type([os_command/0, os_command_opts/0]).
+
-type env_var_name() :: nonempty_string().
-type env_var_value() :: string().
-type env_var_name_value() :: nonempty_string().
--type command_input() :: atom() | io_lib:chars().
-
-spec list_env_vars() -> [{env_var_name(), env_var_value()}].
list_env_vars() ->
erlang:nif_error(undef).
@@ -260,14 +263,20 @@ extensions() ->
%% Executes the given command in the default shell for the operating system.
-spec cmd(Command) -> string() when
- Command :: os:command_input().
+ Command :: os_command().
cmd(Cmd) ->
+ cmd(Cmd, #{ }).
+
+-spec cmd(Command, Options) -> string() when
+ Command :: os_command(),
+ Options :: os_command_opts().
+cmd(Cmd, Opts) ->
{SpawnCmd, SpawnOpts, SpawnInput, Eot} = mk_cmd(os:type(), validate(Cmd)),
Port = open_port({spawn, SpawnCmd}, [binary, stderr_to_stdout,
stream, in, hide | SpawnOpts]),
MonRef = erlang:monitor(port, Port),
true = port_command(Port, SpawnInput),
- Bytes = get_data(Port, MonRef, Eot, []),
+ Bytes = get_data(Port, MonRef, Eot, [], 0, maps:get(max_size, Opts, infinity)),
demonitor(MonRef, [flush]),
String = unicode:characters_to_list(Bytes),
if %% Convert to unicode list if possible otherwise return bytes
@@ -332,12 +341,13 @@ validate2([List|Rest]) when is_list(List) ->
validate2(List),
validate2(Rest).
-get_data(Port, MonRef, Eot, Sofar) ->
+get_data(Port, MonRef, Eot, Sofar, Size, Max) ->
receive
{Port, {data, Bytes}} ->
- case eot(Bytes, Eot) of
+ case eot(Bytes, Eot, Size, Max) of
more ->
- get_data(Port, MonRef, Eot, [Sofar,Bytes]);
+ get_data(Port, MonRef, Eot, [Sofar, Bytes],
+ Size + byte_size(Bytes), Max);
Last ->
catch port_close(Port),
flush_until_down(Port, MonRef),
@@ -348,13 +358,16 @@ get_data(Port, MonRef, Eot, Sofar) ->
iolist_to_binary(Sofar)
end.
-eot(_Bs, <<>>) ->
+eot(_Bs, <<>>, _Size, _Max) ->
more;
-eot(Bs, Eot) ->
+eot(Bs, Eot, Size, Max) ->
case binary:match(Bs, Eot) of
- nomatch -> more;
- {Pos, _} ->
- binary:part(Bs,{0, Pos})
+ nomatch when Size + byte_size(Bs) < Max ->
+ more;
+ {Pos, _} when Size + Pos < Max ->
+ binary:part(Bs,{0, Pos});
+ _ ->
+ binary:part(Bs,{0, Max - Size})
end.
%% When port_close returns we know that all the
diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl
index 8056321448..4369b1b0f9 100644
--- a/lib/kernel/test/os_SUITE.erl
+++ b/lib/kernel/test/os_SUITE.erl
@@ -26,7 +26,8 @@
null_in_command/1, space_in_name/1, bad_command/1,
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, perf_counter_api/1]).
+ message_leak/1, close_stdin/0, close_stdin/1, max_size_command/1,
+ perf_counter_api/1]).
-include_lib("common_test/include/ct.hrl").
@@ -39,7 +40,7 @@ all() ->
space_in_name, bad_command,
find_executable, unix_comment_in_command, deep_list_command,
large_output_command, background_command, message_leak,
- close_stdin, perf_counter_api].
+ close_stdin, max_size_command, perf_counter_api].
groups() ->
[].
@@ -322,6 +323,19 @@ close_stdin(Config) ->
"-1" = os:cmd(Fds).
+max_size_command(_Config) ->
+
+ Res20 = os:cmd("cat /dev/zero", #{ max_size => 20 }),
+ 20 = length(Res20),
+
+ Res0 = os:cmd("cat /dev/zero", #{ max_size => 0 }),
+ 0 = length(Res0),
+
+ Res32768 = os:cmd("cat /dev/zero", #{ max_size => 32768 }),
+ 32768 = length(Res32768),
+
+ ResHello = os:cmd("echo hello", #{ max_size => 20 }),
+ 6 = length(ResHello).
%% Test that the os:perf_counter api works as expected
perf_counter_api(_Config) ->
diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk
index 106bda01ca..91261e1d55 100644
--- a/lib/kernel/vsn.mk
+++ b/lib/kernel/vsn.mk
@@ -1 +1 @@
-KERNEL_VSN = 5.4.1
+KERNEL_VSN = 5.4.2