Skip to content

Commit

Permalink
Merge pull request erlang#7470 from jchristgit/init-dash-capital-s-flag
Browse files Browse the repository at this point in the history
init: Introduce -S flag

OTP-18744
  • Loading branch information
bjorng authored Sep 1, 2023
2 parents 7388d5a + d4e90c4 commit 78d6886
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 22 deletions.
11 changes: 11 additions & 0 deletions erts/doc/src/erl_cmd.xml
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,17 @@ $ <input>erl \
slave node on a remote host; see
<seeerl marker="stdlib:slave"><c>slave(3)</c></seeerl>.</p>
</item>
<tag><c><![CDATA[-S Mod [Func [Arg1, Arg2, ...]]]]></c> (init
flag)</tag>
<item>
<p>Makes <c><![CDATA[init]]></c> call the specified function.
<c><![CDATA[Func]]></c> defaults to <c><![CDATA[start]]></c>.
The function is assumed to be of arity 1, taking the list
<c><![CDATA[[Arg1,Arg2,...]]]></c> as argument, or an empty list
if no arguments are passed. All further arguments occurring after
this option are passed to the specified function as strings.
See <seeerl marker="init"> <c>init(3)</c></seeerl>.</p>
</item>
<tag><c><![CDATA[-run Mod [Func [Arg1, Arg2, ...]]]]></c> (init
flag)</tag>
<item>
Expand Down
32 changes: 32 additions & 0 deletions erts/doc/src/init.xml
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,28 @@ BF</pre>
<seemfa marker="#get_plain_arguments/0">
<c>get_plain_arguments/0</c></seemfa>.</p>
</item>
<tag><c>-S Mod [Func [Arg1, Arg2, ...]]</c></tag>
<item>
<p>Evaluates the specified function call during system
initialization. <c>Func</c> defaults to <c>start</c>. If no
arguments are provided, the function is assumed to be of arity
0. Otherwise it is assumed to be of arity 1, taking the list
<c>[Arg1,Arg2,...]</c> as argument. All arguments are passed
as strings. If an exception is raised, Erlang stops with an
error message.</p>
<p>Example:</p>
<pre>
% <input>erl -S httpd serve --port 8080 /var/www/html</input></pre>
<p>This starts the Erlang runtime system and evaluates
the function <c>httpd:serve(["--port", "8080", "/var/www/html"])</c>.
All arguments up to the end of the command line will be passed
to the called function.</p>
<p>The function is executed sequentially in an initialization
process, which then terminates normally and passes control to
the user. This means that a <c>-S</c> call that does not
return blocks further processing; to avoid this, use
some variant of <c>spawn</c> in such cases.</p>
</item>
<tag><c>-run Mod [Func [Arg1, Arg2, ...]]</c></tag>
<item>
<p>Evaluates the specified function call during system
Expand All @@ -333,6 +355,10 @@ foo:bar(["baz", "1", "2"]).</code>
the user. This means that a <c>-run</c> call that does not
return blocks further processing; to avoid this, use
some variant of <c>spawn</c> in such cases.</p>
<note><p>This flag will not forward arguments beginning with
a hyphen (-) to the specified function, as these will be
interpreted as flags to the runtime. If the function uses
flags in this form, it is advised to use <c>-S</c> instead.</p></note>
</item>
<tag><c>-s Mod [Func [Arg1, Arg2, ...]]</c></tag>
<item>
Expand All @@ -359,6 +385,12 @@ foo:bar([baz, '1', '2']).</code>
some variant of <c>spawn</c> in such cases.</p>
<p>Because of the limited length of atoms, it is recommended to
use <c>-run</c> instead.</p>
<note><p>This flag will not forward arguments beginning with
a hyphen (-) to the specified function, as these will be
interpreted as flags to the runtime. If the function uses
flags in this form, it is advised to use <c>-S</c> instead,
with the additional caveat that arguments are passed as strings
instead of atoms.</p></note>
</item>
</taglist>
</section>
Expand Down
68 changes: 46 additions & 22 deletions erts/preloaded/src/init.erl
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
%% -pz Path+ : Add my own paths last.
%% -run : Start own processes.
%% -s : Start own processes.
%% -S : Start own processes and terminate further option processing.
%%
%% Experimental flags:
%% -profile_boot : Use an 'eprof light' to profile boot sequence
Expand Down Expand Up @@ -256,25 +257,40 @@ boot(BootArgs) ->
register(init, self()),
process_flag(trap_exit, true),

{Start0,Flags,Args} = parse_boot_args(BootArgs),
{Start,Flags,Args} = parse_boot_args(BootArgs),
%% We don't get to profile parsing of BootArgs
case b2a(get_flag(profile_boot, Flags, false)) of
false -> ok;
true -> debug_profile_start()
end,
Start = map(fun prepare_run_args/1, Start0),
boot(Start, Flags, Args).

prepare_run_args({eval, [Expr]}) ->
{eval,Expr};
prepare_run_args({_, L=[]}) ->
bs2as(L);
prepare_run_args({_, L=[_]}) ->
bs2as(L);
prepare_run_args({s, [M,F|Args]}) ->
[b2a(M), b2a(F) | bs2as(Args)];
prepare_run_args({run, [M,F|Args]}) ->
[b2a(M), b2a(F) | bs2ss(Args)].
fold_eval_args([Expr]) -> Expr;
fold_eval_args(Exprs) -> Exprs.

%% Ensure that when no arguments were explicitly passed on the command line,
%% an empty arguments list will be passed to the function to be applied.
interpolate_empty_mfa_args({M, F, []}) -> {M, F, [[]]};
interpolate_empty_mfa_args({_M, _F, [_Args]} = MFA) -> MFA.

-spec run_args_to_mfa([binary()]) -> {atom(), atom(), [] | [nonempty_list(binary())]} | no_return().
run_args_to_mfa([]) ->
erlang:display_string(
"Error! The -S option must be followed by at least a module to start, such as "
"`-S Module` or `-S Module Function` to start with a function.\r\n\r\n"
),
erlang:error(undef);
run_args_to_mfa([M]) -> {b2a(M), start, []};
run_args_to_mfa([M, F]) -> {b2a(M), b2a(F), []};
run_args_to_mfa([M, F | A]) -> {b2a(M), b2a(F), [A]}.

%% Convert -run / -s / -S arguments to startup instructions, such that
%% no instructions are emitted if no arguments follow the flag, otherwise,
%% an `{apply, M, F, A}' instruction is.
run_args_to_start_instructions([], _Converter) -> [];
run_args_to_start_instructions(Args, Converter) ->
{M, F, A} = run_args_to_mfa(Args),
[{apply, M, F, map(Converter, A)}].

b2a(Bin) when is_binary(Bin) ->
list_to_atom(b2s(Bin));
Expand Down Expand Up @@ -1201,15 +1217,10 @@ start_it({eval,Bin}) ->
erlang:display_string(binary_to_list(iolist_to_binary(Message))),
erlang:raise(E,R,ST)
end;
start_it([M|FA]) ->
start_it({apply,M,F,Args}) ->
case code:ensure_loaded(M) of
{module, M} ->
case FA of
[] -> M:start();
[F] -> M:F();
[F|Args] -> M:F(Args) % Args is a list
end;

apply(M, F, Args);
{error, Reason} ->
Message = [explain_ensure_loaded_error(M, Reason), <<"\r\n\r\n">>],
erlang:display_string(binary_to_list(iolist_to_binary(Message))),
Expand Down Expand Up @@ -1288,6 +1299,7 @@ timer(T) ->
%% --------------------------------------------------------
%% Parse the command line arguments and extract things to start, flags
%% and other arguments. We keep the relative of the groups.
%% Returns a triplet in the form `{Start, Flags, Args}':
%% --------------------------------------------------------

parse_boot_args(Args) ->
Expand All @@ -1299,13 +1311,23 @@ parse_boot_args([B|Bs], Ss, Fs, As) ->
{reverse(Ss),reverse(Fs),lists:reverse(As, Bs)}; % BIF
start_arg ->
{S,Rest} = get_args(Bs, []),
parse_boot_args(Rest, [{s, S}|Ss], Fs, As);
Instructions = run_args_to_start_instructions(S, fun bs2as/1),
parse_boot_args(Rest, Instructions ++ Ss, Fs, As);
start_arg2 ->
{S,Rest} = get_args(Bs, []),
parse_boot_args(Rest, [{run, S}|Ss], Fs, As);
Instructions = run_args_to_start_instructions(S, fun bs2ss/1),
parse_boot_args(Rest, Instructions ++ Ss, Fs, As);
ending_start_arg ->
{S,Rest} = get_args(Bs, []),
%% Forward any additional arguments to the function we are calling,
%% such that no init:get_plain_arguments is needed by it later.
MFA = run_args_to_mfa(S ++ Rest),
{M, F, A} = interpolate_empty_mfa_args(MFA),
StartersWithThis = [{apply, M, F, map(fun bs2ss/1, A)} | Ss],
{reverse(StartersWithThis),reverse(Fs),[]};
eval_arg ->
{Expr,Rest} = get_args(Bs, []),
parse_boot_args(Rest, [{eval, Expr}|Ss], Fs, As);
parse_boot_args(Rest, [{eval, fold_eval_args(Expr)} | Ss], Fs, As);
{flag,A} ->
{F,Rest} = get_args(Bs, []),
Fl = {A,F},
Expand All @@ -1321,6 +1343,7 @@ parse_boot_args([], Start, Flags, Args) ->
check(<<"-extra">>) -> start_extra_arg;
check(<<"-s">>) -> start_arg;
check(<<"-run">>) -> start_arg2;
check(<<"-S">>) -> ending_start_arg;
check(<<"-eval">>) -> eval_arg;
check(<<"--">>) -> end_args;
check(<<"-",Flag/binary>>) -> {flag,b2a(Flag)};
Expand All @@ -1333,6 +1356,7 @@ get_args([B|Bs], As) ->
start_arg2 -> {reverse(As), [B|Bs]};
eval_arg -> {reverse(As), [B|Bs]};
end_args -> {reverse(As), Bs};
ending_start_arg -> {reverse(As), [B|Bs]};
{flag,_} -> {reverse(As), [B|Bs]};
arg ->
get_args(Bs, [B|As])
Expand Down

0 comments on commit 78d6886

Please sign in to comment.