%-*-Mode:erlang;coding:utf-8;tab-width:4;c-basic-offset:4;indent-tabs-mode:()-*- % ex: set ft=erlang fenc=utf-8 sts=4 ts=4 sw=4 et nomod: %%% %%%------------------------------------------------------------------------ %%% @doc %%% ==CloudI Runtime Environment== %%% @end %%% %%% MIT License %%% %%% Copyright (c) 2014-2021 Michael Truog %%% %%% Permission is hereby granted, free of charge, to any person obtaining a %%% copy of this software and associated documentation files (the "Software"), %%% to deal in the Software without restriction, including without limitation %%% the rights to use, copy, modify, merge, publish, distribute, sublicense, %%% and/or sell copies of the Software, and to permit persons to whom the %%% Software is furnished to do so, subject to the following conditions: %%% %%% The above copyright notice and this permission notice shall be included in %%% all copies or substantial portions of the Software. %%% %%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR %%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, %%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE %%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER %%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING %%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER %%% DEALINGS IN THE SOFTWARE. %%% %%% @author Michael Truog %%% @copyright 2014-2021 Michael Truog %%% @version 2.0.2 {@date} {@time} %%%------------------------------------------------------------------------ -module(cloudi_environment). -author('mjtruog at protonmail dot com'). %% external interface -export([lookup/0, status/0, transform/1, transform/2]). -type lookup() :: trie:trie(). -export_type([lookup/0]). -include("cloudi_environment.hrl"). -include("cloudi_logger.hrl"). -include_lib("kernel/include/file.hrl"). %%%------------------------------------------------------------------------ %%% External interface functions %%%------------------------------------------------------------------------ %%------------------------------------------------------------------------- %% @doc %% ===Get an environment variable lookup.=== %% @end %%------------------------------------------------------------------------- -spec lookup() -> lookup(). lookup() -> trie:new(lists:map(fun(Entry) -> cloudi_string:splitl($=, Entry, input) end, os:getenv())). %%------------------------------------------------------------------------- %% @doc %% ===Get execution environment status.=== %% @end %%------------------------------------------------------------------------- -spec status() -> nonempty_list({atom(), string()}). status() -> RuntimeErtsVersion = erlang:system_info(version), FileErts = filename:join([code:root_dir(), "lib", "erts-" ++ RuntimeErtsVersion, "ebin", "erts.app"]), FileKernel = [_ | _] = code:where_is_file("kernel.app"), FileStdlib = [_ | _] = code:where_is_file("stdlib.app"), FileSasl = [_ | _] = code:where_is_file("sasl.app"), FileCompiler = [_ | _] = code:where_is_file("compiler.app"), FileCloudI = [_ | _] = code:where_is_file("cloudi_core.app"), ApplicationVersions0 = application:loaded_applications(), {value, {kernel, _, RuntimeErlangKernelVersion}, ApplicationVersions1} = lists:keytake(kernel, 1, ApplicationVersions0), {value, {stdlib, _, RuntimeErlangStdlibVersion}, ApplicationVersions2} = lists:keytake(stdlib, 1, ApplicationVersions1), {value, {sasl, _, RuntimeErlangSaslVersion}, ApplicationVersions3} = lists:keytake(sasl, 1, ApplicationVersions2), {value, {compiler, _, RuntimeErlangCompilerVersion}, ApplicationVersions4} = lists:keytake(compiler, 1, ApplicationVersions3), {value, {cloudi_core, _, RuntimeCloudIVersion}, _} = lists:keytake(cloudi_core, 1, ApplicationVersions4), RuntimeErlangCompilation = erlang_compilation(), RuntimeMachineProcessors = erlang:system_info(schedulers), ?CODE_STATUS_STATIC ++ [{build_erlang_erts_c_compiler_version, erts_c_compiler_version()}, {install_erlang_erts_time, status_file_time(FileErts)}, {install_erlang_kernel_time, status_file_time(FileKernel)}, {install_erlang_stdlib_time, status_file_time(FileStdlib)}, {install_erlang_sasl_time, status_file_time(FileSasl)}, {install_erlang_compiler_time, status_file_time(FileCompiler)}, {install_cloudi_time, status_file_time(FileCloudI)}, {runtime_erlang_erts_version, RuntimeErtsVersion}, {runtime_erlang_kernel_version, RuntimeErlangKernelVersion}, {runtime_erlang_stdlib_version, RuntimeErlangStdlibVersion}, {runtime_erlang_sasl_version, RuntimeErlangSaslVersion}, {runtime_erlang_compiler_version, RuntimeErlangCompilerVersion}, {runtime_erlang_compilation, RuntimeErlangCompilation}, {runtime_cloudi_version, RuntimeCloudIVersion}, {runtime_machine_processors, RuntimeMachineProcessors}]. %%------------------------------------------------------------------------- %% @doc %% ===Transform a string, substituting environment variable values from the lookup.=== %% Use ${VARIABLE} or $VARIABLE syntax, where VARIABLE is a name with %% [a-zA-Z0-9_] ASCII characters. %% @end %%------------------------------------------------------------------------- -spec transform(String :: string()) -> string(). transform(String) -> transform(String, lookup()). %%------------------------------------------------------------------------- %% @doc %% ===Transform a string, substituting environment variable values from the lookup.=== %% Use ${VARIABLE} or $VARIABLE syntax, where VARIABLE is a name with %% [a-zA-Z0-9_] ASCII characters. %% @end %%------------------------------------------------------------------------- -spec transform(String :: string(), Lookup :: lookup()) -> string(). transform(String, Lookup) -> transform(String, [], Lookup). %%%------------------------------------------------------------------------ %%% Private functions %%%------------------------------------------------------------------------ status_file_time(FilePath) -> case file:read_file_info(FilePath, [raw, {time, posix}]) of {ok, #file_info{mtime = MTime}} -> cloudi_timestamp:seconds_epoch_to_string(MTime); {error, Reason} -> ?LOG_ERROR("filesystem error (~ts): ~tp", [FilePath, Reason]), "" end. erts_c_compiler_version() -> case erlang:system_info(c_compiler_used) of {undefined, _} -> ""; {Name, undefined} when is_atom(Name) -> erlang:atom_to_list(Name); {Name, V} when is_atom(Name), is_tuple(V) -> [_ | Version] = lists:flatten([[$., io_lib:format("~p", [I])] || I <- erlang:tuple_to_list(V)]), erlang:atom_to_list(Name) ++ [$ | Version]; {Name, Version} -> cloudi_string:format("~tp ~tp", [Name, Version]); Unexpected -> ?LOG_ERROR("erlang:system_info(c_compiler_used) invalid: ~tp", [Unexpected]), "" end. -ifdef(ERLANG_OTP_VERSION_24_FEATURES). erlang_compilation() -> case erlang:system_info(emu_flavor) of jit -> "jit"; _ -> "aot" end. -else. erlang_compilation() -> "aot". -endif. % transform a string using a lookup containing environment variables % (the loop doesn't have error conditions by design) transform([], Output, _) -> lists:reverse(Output); transform([$\\, $$ | String], Output, Lookup) -> transform(String, [$$ | Output], Lookup); transform([$$ | String], Output, Lookup) -> case String of [${ | Rest] -> transform_delimiter(Rest, Output, [], Lookup); _ -> transform_bare(String, Output, [], Lookup) end; transform([C | String], Output, Lookup) -> transform(String, [C | Output], Lookup). transform_delimiter([], Output, _, _) -> lists:reverse(Output); transform_delimiter([$} | String], Output, Key, Lookup) -> transform_value(Key, String, Output, Lookup); transform_delimiter([$= | String], Output, _, Lookup) -> transform(String, Output, Lookup); transform_delimiter([C | String], Output, Key, Lookup) -> transform_delimiter(String, Output, [C | Key], Lookup). transform_bare([] = String, Output, Key, Lookup) -> transform_value(Key, String, Output, Lookup); transform_bare([C | String], Output, _, Lookup) when C == $}; C == $= -> transform(String, Output, Lookup); transform_bare([C | String], Output, Key, Lookup) when C /= $$, C /= $/, C /= $ , C /= $', C /= $", C /= $` -> transform_bare(String, Output, [C | Key], Lookup); transform_bare(String, Output, Key, Lookup) -> transform_value(Key, String, Output, Lookup). transform_value([], String, Output, Lookup) -> transform(String, Output, Lookup); transform_value(Key, String, Output, Lookup) -> case trie:find(lists:reverse(Key), Lookup) of {ok, Value} -> transform(String, lists:reverse(Value, Output), Lookup); error -> transform(String, Output, Lookup) end.