From f1ca806498bc7f7dad96f2c7e188fdc55e0124cb Mon Sep 17 00:00:00 2001 From: Lukas Larsson Date: Thu, 14 Jul 2016 12:24:49 +0200 Subject: kernel: Use ^D as eot for os:cmd on unix platforms This is needed as doing only an 'exit' will only exit the program, but any children started in the program that have stdout/stderr still open will keep the port open until they terminate. i.e. os:cmd("while true; do echo sleep 1; sleep 1; done&"). would block os:cmd forever because the while loop keeps its copies of stdout/stderr open forever. It could be argued that this is correct behaviour, and it is the way it works on windows, but changing this breaks backwards compatability for os:cmd which is not acceptable. --- lib/kernel/src/os.erl | 48 ++++++++++++++++++++++++++++++++------------ lib/kernel/test/os_SUITE.erl | 22 +++++++++++++++++--- 2 files changed, 54 insertions(+), 16 deletions(-) (limited to 'lib/kernel') diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl index f0ad26b1f2..81b70a7fee 100644 --- a/lib/kernel/src/os.erl +++ b/lib/kernel/src/os.erl @@ -226,11 +226,13 @@ extensions() -> Command :: atom() | io_lib:chars(). cmd(Cmd) -> validate(Cmd), - {SpawnCmd, SpawnOpts, SpawnInput} = mk_cmd(os:type(), Cmd), + {SpawnCmd, SpawnOpts, SpawnInput, Eot} = mk_cmd(os:type(), Cmd), Port = open_port({spawn, SpawnCmd}, [binary, stderr_to_stdout, - stream, in, eof, hide | SpawnOpts]), + stream, in, hide | SpawnOpts]), + MonRef = erlang:monitor(port, Port), true = port_command(Port, SpawnInput), - Bytes = get_data(Port, []), + Bytes = get_data(Port, MonRef, Eot, []), + demonitor(MonRef, [flush]), String = unicode:characters_to_list(Bytes), if %% Convert to unicode list if possible otherwise return bytes is_list(String) -> String; @@ -243,7 +245,7 @@ mk_cmd({win32,Wtype}, Cmd) -> {false,_} -> lists:concat(["cmd /c", Cmd]); {Cspec,_} -> lists:concat([Cspec," /c",Cmd]) end, - {Command, [], []}; + {Command, [], [], <<>>}; mk_cmd(OsType,Cmd) when is_atom(Cmd) -> mk_cmd(OsType, atom_to_list(Cmd)); mk_cmd(_,Cmd) -> @@ -252,7 +254,8 @@ mk_cmd(_,Cmd) -> {"/bin/sh -s unix:cmd", [out], %% We insert a new line after the command, in case the command %% contains a comment character. - ["(", unicode:characters_to_binary(Cmd), "\n); exit\n"]}. + ["(", unicode:characters_to_binary(Cmd), "\n); echo \"\^D\"\n"], + <<$\^D>>}. validate(Atom) when is_atom(Atom) -> ok; @@ -267,16 +270,18 @@ validate1([List|Rest]) when is_list(List) -> validate1([]) -> ok. -get_data(Port, Sofar) -> +get_data(Port, MonRef, Eot, Sofar) -> receive {Port, {data, Bytes}} -> - get_data(Port, [Sofar,Bytes]); - {Port, eof} -> - Port ! {self(), close}, - receive - {Port, closed} -> - true - end, + case eot(Bytes, Eot) of + more -> + get_data(Port, MonRef, Eot, [Sofar,Bytes]); + Last -> + Port ! {self(), close}, + flush_until_closed(Port), + iolist_to_binary([Sofar, Last]) + end; + {'DOWN', MonRef, _, _ , _} -> receive {'EXIT', Port, _} -> ok @@ -285,3 +290,20 @@ get_data(Port, Sofar) -> end, iolist_to_binary(Sofar) end. + +eot(_Bs, <<>>) -> + more; +eot(Bs, Eot) -> + case binary:match(Bs, Eot) of + nomatch -> more; + {Pos, _} -> + binary:part(Bs,{0, Pos}) + end. + +flush_until_closed(Port) -> + receive + {Port, {data, _Bytes}} -> + flush_until_closed(Port); + {Port, closed} -> + true + end. diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl index 2a1e5016ec..8f3e511941 100644 --- a/lib/kernel/test/os_SUITE.erl +++ b/lib/kernel/test/os_SUITE.erl @@ -24,7 +24,8 @@ init_per_testcase/2,end_per_testcase/2]). -export([space_in_cwd/1, quoting/1, cmd_unicode/1, space_in_name/1, bad_command/1, find_executable/1, unix_comment_in_command/1, deep_list_command/1, - large_output_command/1, perf_counter_api/1]). + large_output_command/1, background_command/0, background_command/1, + perf_counter_api/1]). -include_lib("common_test/include/ct.hrl"). @@ -35,7 +36,7 @@ suite() -> all() -> [space_in_cwd, quoting, cmd_unicode, space_in_name, bad_command, find_executable, unix_comment_in_command, deep_list_command, - large_output_command, perf_counter_api]. + large_output_command, background_command, perf_counter_api]. groups() -> []. @@ -52,6 +53,13 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. +init_per_testcase(background_command, Config) -> + case os:type() of + {win32, _} -> + {skip,"Should not work on windows"}; + _ -> + Config + end; init_per_testcase(_TC,Config) -> Config. @@ -261,13 +269,21 @@ deep_list_command(Config) when is_list(Config) -> %% FYI: [$e, $c, "ho"] =:= io_lib:format("ec~s", ["ho"]) ok. -%% Test to take sure that the correct data is +%% Test to make sure that the correct data is %% received when doing large commands. large_output_command(Config) when is_list(Config) -> %% Maximum allowed on windows is 8192, so we test well below that AAA = lists:duplicate(7000, $a), comp(AAA,os:cmd("echo " ++ AAA)). +%% Test that it is possible on unix to start a background task using os:cmd. +background_command() -> + [{timetrap, {seconds, 5}}]. +background_command(_Config) -> + %% This testcase fails when the os:cmd takes + %% longer then the 5 second timeout + os:cmd("sleep 10&"). + %% Test that the os:perf_counter api works as expected perf_counter_api(_Config) -> -- cgit v1.2.3 From 796bf022df397d09494d6198102d203d01efc8f6 Mon Sep 17 00:00:00 2001 From: Erlang/OTP Date: Mon, 8 Aug 2016 16:36:36 +0200 Subject: Prepare release --- lib/kernel/doc/src/notes.xml | 18 ++++++++++++++++++ lib/kernel/vsn.mk | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) (limited to 'lib/kernel') diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index f37433110c..0e3914b083 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -31,6 +31,24 @@

This document describes the changes made to the Kernel application.

+
Kernel 5.0.1 + +
Fixed Bugs and Malfunctions + + +

+ Fix a os:cmd bug where creating a background job using + & would cause os:cmd to hang until the background job + terminated or closed its stdout and stderr file + descriptors. This bug has existed from kernel 5.0.

+

+ Own Id: OTP-13741

+
+
+
+ +
+
Kernel 5.0
Fixed Bugs and Malfunctions diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index e7d422d03c..e88e4f9170 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 5.0 +KERNEL_VSN = 5.0.1 -- cgit v1.2.3