Packages

otpbp

4.76.2
7.18.0 7.16.1 7.15.0 7.14.0 7.13.0 7.12.0 7.11.0 7.10.2 7.9.3 7.9.2 7.9.1 7.8.7 7.8.6 7.8.5 7.8.4 7.7.1 7.7.0 7.6.3 7.6.2 7.6.1 7.6.0 7.5.1 7.5.0 7.4.0 7.3.4 7.3.3 7.3.2 7.3.1 7.3.0 7.2.0 7.1.1 7.1.0 7.0.4 7.0.3 7.0.2 7.0.1 6.26.0 6.25.0 6.24.0 6.23.0 6.22.2 6.21.3 6.21.2 6.21.1 6.20.2 6.19.1 6.19.0 6.18.3 6.18.2 6.18.1 6.18.0 6.17.1 6.17.0 6.16.0 6.15.4 6.15.3 6.15.2 6.15.1 6.15.0 6.14.0 6.13.5 6.13.4 6.13.3 6.13.2 6.13.1 6.13.0 6.12.0 6.11.2 6.11.1 6.10.0 6.9.2 6.9.1 6.9.0 6.8.0 6.7.1 6.7.0 6.6.4 6.6.3 6.6.2 6.6.1 6.6.0 6.5.0 6.4.0 6.3.3 6.3.2 6.3.1 6.3.0 6.2.1 6.2.0 6.1.0 6.0.0 5.39.0 5.38.0 5.37.0 5.36.0 5.35.2 5.34.3 5.34.2 5.34.1 5.33.1 5.32.1 5.32.0 5.31.3 5.31.2 5.31.1 5.31.0 5.30.1 5.30.0 5.29.0 5.28.4 5.28.3 5.28.2 5.28.1 5.28.0 5.27.0 5.26.5 5.26.4 5.26.3 5.26.2 5.26.1 5.26.0 5.25.0 5.24.2 5.24.1 5.23.0 5.22.2 5.22.1 5.22.0 5.21.0 5.20.1 5.20.0 5.19.4 5.19.3 5.19.2 5.19.1 5.19.0 5.18.0 5.17.0 5.16.3 5.16.1 5.16.0 5.15.1 5.15.0 5.14.0 5.13.1 5.13.0 5.12.1 5.12.0 5.11.2 5.11.1 5.10.1 5.10.0 5.9.0 5.8.0 5.7.0 5.6.1 5.6.0 5.5.0 5.4.2 5.4.1 5.4.0 5.3.0 5.2.2 5.2.1 5.2.0 5.1.2 5.1.1 4.79.0 4.78.0 4.77.0 4.76.2 4.75.3 4.75.2 4.75.1 4.74.0 4.73.1 4.73.0 4.72.3 4.72.2 4.72.1 4.72.0 4.71.1 4.71.0 4.70.0 4.69.4 4.69.3 4.69.2 4.69.1 4.69.0 4.68.0 4.67.5 4.67.4 4.67.3 4.67.2 4.67.0 4.66.0 4.65.2 4.65.1 4.64.0 4.63.2 4.63.1 4.63.0 4.62.0 4.61.1 4.61.0 4.60.4 4.60.3 4.60.2 4.60.1 4.60.0 4.59.0 4.58.0 4.57.2 4.57.1 4.57.0 4.56.2 4.56.1 4.56.0 4.55.0 4.54.0 4.53.0 4.52.1 4.52.0 4.51.3 4.51.2 4.51.0 4.50.0 4.49.3 4.49.2

OTP backports

Current section

Files

Jump to
otpbp src otpbp_os.erl
Raw

src/otpbp_os.erl

-module(otpbp_os).
-ifndef(HAVE_os__cmd_2).
% OTP 20.2.3
-export([cmd/2]).
-endif.
-ifndef(HAVE_os__env_0).
% OTP 24.0
-export([env/0]).
-endif.
-ifndef(HAVE_os__list_env_vars_0).
% OTP 21.0 - 23.x
-export([list_env_vars/0]).
-endif.
-ifndef(HAVE_os__list_env_vars_0).
-ifdef(HAVE_os__env_0).
-import(os, [env/0]).
-endif.
-endif.
-ifndef(HAVE_os__cmd_2).
cmd(Cmd, Opts) when is_atom(Cmd) -> cmd_(atom_to_list(Cmd), Opts);
cmd(Cmd, Opts) ->
validate(Cmd),
cmd_(Cmd, Opts).
cmd_(Cmd, Opts) ->
{SpawnCmd, SpawnOpts, SpawnInput, Eot} = mk_cmd(Cmd),
Port = open_port({spawn, SpawnCmd}, [binary, stderr_to_stdout, stream, in, hide|SpawnOpts]),
MonRef = monitor(port, Port),
true = port_command(Port, SpawnInput),
Bytes = get_data(Port, MonRef, Eot, [], 0, maps:get(max_size, Opts, infinity)),
demonitor(MonRef, [flush]),
%% Convert to unicode list if possible otherwise return bytes
case unicode:characters_to_list(Bytes) of
String when is_list(String) -> String;
_ -> binary_to_list(Bytes)
end.
mk_cmd(Cmd) ->
%% Have to send command in like this in order to make sh commands like cd and ulimit available
{"/bin/sh -s unix:cmd", [out],
%% We insert a new line after the command, in case the command contains a comment character.
%% The </dev/null closes stdin, which means that programs that use a closed stdin as an termination indicator works.
%% An example of such a program is 'more'.
%% The "echo ^D" is used to indicate that the program has executed and we should return any output we have gotten.
%% We cannot use termination of the child or closing of stdin/stdout as then starting background jobs from os:cmd
%% will block os:cmd.
%% I tried changing this to be "better",
%% but got bombarded with backwards incompatibility bug reports, so leave this as it is.
["(", unicode:characters_to_binary(Cmd), "\n) </dev/null; echo \"\^D\"\n"],
<<$\^D>>}.
validate([C|Rest]) when is_integer(C) -> validate(Rest);
validate([List|Rest]) when is_list(List) ->
validate(List),
validate(Rest);
validate([]) -> ok.
get_data(Port, MonRef, Eot, Sofar, Size, Max) ->
receive
{Port, {data, Bytes}} when Eot =:= <<>> ->
get_data(Port, MonRef, <<>>, [Sofar, Bytes], Size + byte_size(Bytes), Max);
{Port, {data, Bytes}} ->
catch port_close(Port),
flush_until_down(Port, MonRef),
iolist_to_binary([Sofar, eot(Bytes, Eot, Size, Max)]);
{'DOWN', MonRef, _, _, _} ->
flush_exit(Port),
iolist_to_binary(Sofar)
end.
eot(Bs, Eot, Size, Max) ->
MS = Max - Size,
case binary:match(Bs, Eot) of
nomatch when byte_size(Bs) < MS -> more;
{Pos, _} when Pos < MS -> binary:part(Bs, {0, Pos});
_ -> binary:part(Bs, {0, MS})
end.
flush_exit(Port) ->
receive
{'EXIT', Port, _} -> ok
after 0 -> ok
end.
flush_until_down(Port, MonRef) ->
receive
{Port, {data, _Bytes}} -> flush_until_down(Port, MonRef);
{'DOWN', MonRef, _, _, _} -> flush_exit(Port)
end.
-compile({inline, [mk_cmd/1, eot/4]}).
-endif.
-ifndef(HAVE_os__list_env_vars_0).
list_env_vars() -> env().
-endif.
-ifndef(HAVE_os__env_0).
-ifdef(HAVE_os__list_env_vars_0).
env() -> os:list_env_vars().
-else.
env() ->
lists:map(fun(E) ->
{N, [$=|V]} = lists:splitwith(fun(C) -> C =/= $= end, E),
{N, V}
end, os:getenv()).
-endif.
-endif.