headed/headless dev scripts, runlocal works again

This commit is contained in:
Craig Everett 2018-05-30 23:25:20 +09:00
parent 9441c7a879
commit 7062ffe747
8 changed files with 471 additions and 342 deletions

View File

@ -19,7 +19,7 @@
-license("GPL-3.0"). -license("GPL-3.0").
-export([run/0, run/1]). -export([do/0, do/1]).
-export([subscribe/1, unsubscribe/0]). -export([subscribe/1, unsubscribe/0]).
-export([start/2, stop/1, stop/0]). -export([start/2, stop/1, stop/0]).
-export([usage_exit/1]). -export([usage_exit/1]).
@ -69,71 +69,68 @@
%%% Command Dispatch %%% Command Dispatch
-spec run() -> no_return(). -spec do() -> no_return().
run() -> do() ->
run([]). do([]).
-spec run(Args) -> no_return() -spec do(Args) -> no_return()
when Args :: [string()]. when Args :: [string()].
%% Dispatch work functions based on the nature of the input arguments. %% Dispatch work functions based on the nature of the input arguments.
run(["help"]) -> do(["help"]) ->
usage_exit(0); usage_exit(0);
run(["run", PackageString | Args]) -> do(["run", PackageString | Args]) ->
ok = start(), ok = start(),
run(PackageString, Args); run(PackageString, Args);
run(["runlocal" | ArgV]) -> do(["runlocal" | ArgV]) ->
ok = start(), ok = start(),
run_local(ArgV); run_local(ArgV);
run(["init", "app", PackageString]) -> do(["init", "app", PackageString]) ->
ok = compatibility_check([unix]), ok = compatibility_check([unix]),
ok = zx_local:initialize(app, PackageString), ok = zx_local:initialize(app, PackageString),
halt(0); halt(0);
run(["init", "lib", PackageString]) -> do(["init", "lib", PackageString]) ->
ok = compatibility_check([unix]), ok = compatibility_check([unix]),
ok = zx_local:initialize(lib, PackageString), ok = zx_local:initialize(lib, PackageString),
halt(0); halt(0);
run(["install", PackageFile]) -> do(["install", PackageFile]) ->
ok = zx_local:assimilate(PackageFile), ok = zx_local:assimilate(PackageFile),
halt(0); halt(0);
run(["set", "dep", PackageString]) -> do(["set", "dep", PackageString]) ->
ok = zx_local:set_dep(PackageString), ok = zx_local:set_dep(PackageString),
halt(0); halt(0);
run(["set", "version", VersionString]) -> do(["set", "version", VersionString]) ->
ok = compatibility_check([unix]), ok = compatibility_check([unix]),
ok = zx_local:set_version(VersionString), ok = zx_local:set_version(VersionString),
halt(0); halt(0);
run(["verup", Level]) -> do(["verup", Level]) ->
ok = compatibility_check([unix]), ok = compatibility_check([unix]),
ok = zx_local:verup(Level), ok = zx_local:verup(Level),
halt(0); halt(0);
run(["list", "realms"]) -> do(["list", "realms"]) ->
ok = zx_local:list_realms(), ok = zx_local:list_realms(),
halt(0); halt(0);
run(["list", "packages", Realm]) -> do(["list", "packages", Realm]) ->
ok = start(), ok = start(),
ok = zx_local:list_packages(Realm), ok = zx_local:list_packages(Realm),
halt(0); halt(0);
run(["list", "versions", PackageName]) -> do(["list", "versions", PackageName]) ->
ok = start(), ok = start(),
ok = zx_local:list_versions(PackageName), ok = zx_local:list_versions(PackageName),
halt(0); halt(0);
run(["add", "realm", RealmFile]) -> do(["add", "realm", RealmFile]) ->
ok = zx_local:add_realm(RealmFile), ok = zx_local:add_realm(RealmFile),
halt(0); halt(0);
run(["drop", "dep", PackageString]) -> do(["drop", "dep", PackageString]) ->
PackageID = zx_lib:package_id(PackageString), PackageID = zx_lib:package_id(PackageString),
ok = zx_local:drop_dep(PackageID), ok = zx_local:drop_dep(PackageID),
halt(0); halt(0);
run(["drop", "realm", Realm]) -> do(["package"]) ->
ok = zx_local:drop_realm(Realm),
halt(0);
run(["package"]) ->
{ok, TargetDir} = file:get_cwd(), {ok, TargetDir} = file:get_cwd(),
zx_local:package(TargetDir); zx_local:package(TargetDir);
run(["package", TargetDir]) -> do(["package", TargetDir]) ->
case filelib:is_dir(TargetDir) of case filelib:is_dir(TargetDir) of
true -> true ->
ok = zx_local:package(TargetDir), ok = zx_local:package(TargetDir),
@ -142,49 +139,61 @@ run(["package", TargetDir]) ->
ok = log(error, "Target directory ~tp does not exist!", [TargetDir]), ok = log(error, "Target directory ~tp does not exist!", [TargetDir]),
halt(22) halt(22)
end; end;
run(["dialyze"]) -> do(["dialyze"]) ->
ok = zx_local:dialyze(), ok = zx_local:dialyze(),
halt(0); halt(0);
run(["create", "user", Realm, Name]) -> do(["create", "user", Realm, Name]) ->
ok = zx_local:create_user(Realm, Name), ok = zx_local:create_user(Realm, Name),
halt(0); halt(0);
run(["create", "keypair"]) -> do(["create", "keypair"]) ->
ok = zx_local:grow_a_pair(), ok = zx_local:grow_a_pair(),
halt(0); halt(0);
run(["drop", "key", Realm, KeyName]) -> do(["drop", "key", Realm, KeyName]) ->
ok = zx_local:drop_key({Realm, KeyName}), ok = zx_local:drop_key({Realm, KeyName}),
halt(0); halt(0);
run(["create", "plt"]) -> do(["create", "plt"]) ->
zx_local:create_plt(); ok = zx_local:create_plt(),
run(["create", "realm"]) -> halt(0);
zx_local:create_realm(); do(["create", "realm"]) ->
run(["create", "realmfile", Realm]) -> ok = zx_local:create_realm(),
zx_local:create_realmfile(Realm); halt(0);
run(["list", "pending", PackageName]) -> do(["create", "realmfile", Realm]) ->
ok = zx_local:create_realmfile(Realm, "."),
halt(0);
do(["takeover", Realm]) ->
ok = zx_local:takeover(Realm),
halt(0);
do(["abdicate", Realm]) ->
ok = zx_local:abdicate(Realm),
halt(0);
do(["drop", "realm", Realm]) ->
ok = zx_local:drop_realm(Realm),
halt(0);
do(["list", "pending", PackageName]) ->
zx_auth:list_pending(PackageName); zx_auth:list_pending(PackageName);
run(["list", "resigns", Realm]) -> do(["list", "resigns", Realm]) ->
zx_auth:list_resigns(Realm); zx_auth:list_resigns(Realm);
run(["submit", PackageFile]) -> do(["submit", PackageFile]) ->
zx_auth:submit(PackageFile); zx_auth:submit(PackageFile);
run(["review", PackageString]) -> do(["review", PackageString]) ->
zx_auth:review(PackageString); zx_auth:review(PackageString);
run(["approve", PackageString]) -> do(["approve", PackageString]) ->
PackageID = zx_lib:package_id(PackageString), PackageID = zx_lib:package_id(PackageString),
zx_auth:approve(PackageID); zx_auth:approve(PackageID);
run(["reject", PackageString]) -> do(["reject", PackageString]) ->
PackageID = zx_lib:package_id(PackageString), PackageID = zx_lib:package_id(PackageString),
zx_auth:reject(PackageID); zx_auth:reject(PackageID);
run(["accept", PackageString]) -> do(["accept", PackageString]) ->
zx_auth:accept(PackageString); zx_auth:accept(PackageString);
run(["add", "packager", Package, UserName]) -> do(["add", "packager", Package, UserName]) ->
zx_auth:add_packager(Package, UserName); zx_auth:add_packager(Package, UserName);
run(["add", "maintainer", Package, UserName]) -> do(["add", "maintainer", Package, UserName]) ->
zx_auth:add_maintainer(Package, UserName); zx_auth:add_maintainer(Package, UserName);
run(["add", "sysop", Package, UserName]) -> do(["add", "sysop", Package, UserName]) ->
zx_auth:add_sysop(Package, UserName); zx_auth:add_sysop(Package, UserName);
run(["add", "package", PackageName]) -> do(["add", "package", PackageName]) ->
zx_auth:add_package(PackageName); zx_auth:add_package(PackageName);
run(_) -> do(_) ->
usage_exit(22). usage_exit(22).
@ -317,7 +326,6 @@ unsubscribe() ->
run(Identifier, RunArgs) -> run(Identifier, RunArgs) ->
ok = file:set_cwd(zx_lib:zomp_dir()), ok = file:set_cwd(zx_lib:zomp_dir()),
ok = start(),
FuzzyID = FuzzyID =
case zx_lib:package_id(Identifier) of case zx_lib:package_id(Identifier) of
{ok, Fuzzy} -> {ok, Fuzzy} ->
@ -329,12 +337,9 @@ run(Identifier, RunArgs) ->
ok = build(PackageID), ok = build(PackageID),
Dir = zx_lib:package_dir(PackageID), Dir = zx_lib:package_dir(PackageID),
{ok, Meta} = zx_lib:read_project_meta(Dir), {ok, Meta} = zx_lib:read_project_meta(Dir),
execute(PackageID, Meta, Dir, RunArgs). prepare(PackageID, Meta, Dir, RunArgs).
%%% Execution of local project
-spec run_local(RunArgs) -> no_return() -spec run_local(RunArgs) -> no_return()
when RunArgs :: [term()]. when RunArgs :: [term()].
%% @private %% @private
@ -352,11 +357,10 @@ run_local(RunArgs) ->
ok = zx_lib:build(), ok = zx_lib:build(),
{ok, Dir} = file:get_cwd(), {ok, Dir} = file:get_cwd(),
ok = file:set_cwd(zx_lib:zomp_dir()), ok = file:set_cwd(zx_lib:zomp_dir()),
ok = start(), prepare(PackageID, Meta, Dir, RunArgs).
execute(PackageID, Meta, Dir, RunArgs).
-spec execute(PackageID, Meta, Dir, RunArgs) -> no_return() -spec prepare(PackageID, Meta, Dir, RunArgs) -> no_return()
when PackageID :: package_id(), when PackageID :: package_id(),
Meta :: package_meta(), Meta :: package_meta(),
Dir :: file:filename(), Dir :: file:filename(),
@ -364,21 +368,43 @@ run_local(RunArgs) ->
%% @private %% @private
%% Execution prep common to all packages. %% Execution prep common to all packages.
execute(PackageID, Meta, Dir, RunArgs) -> prepare(PackageID, Meta, Dir, RunArgs) ->
PackageString = zx_lib:package_string(PackageID), {ok, PackageString} = zx_lib:package_string(PackageID),
ok = log(info, "Preparing ~ts...", [PackageString]), ok = log(info, "Preparing ~ts...", [PackageString]),
Type = maps:get(type, Meta), Type = maps:get(type, Meta),
Deps = maps:get(deps, Meta), Deps = maps:get(deps, Meta),
case zx_daemon:fetch(Deps) of NotInstalled = fun(P) -> not filelib:is_dir(zx_lib:ppath(lib, P)) end,
{{ok, _}, {error, []}} -> Needed = lists:filter(NotInstalled, Deps),
ok = lists:foreach(fun install/1, Deps), Pending = lists:map(fun zx_daemon:fetch/1, Needed),
ok = lists:foreach(fun build/1, Deps), case await_fetches(Pending) of
execute(Type, PackageID, Dir, Meta, RunArgs); ok ->
{{ok, _}, {error, Errors}} -> ok = lists:foreach(fun install/1, Needed),
ok = lists:foreach(fun build/1, Needed),
execute(Type, PackageID, Meta, Dir, RunArgs);
{error, Errors} ->
error_exit("Failed package fetches: ~tp", [Errors], ?LINE) error_exit("Failed package fetches: ~tp", [Errors], ?LINE)
end. end.
await_fetches([]) -> ok;
await_fetches(Pending) -> await_fetches(Pending, []).
await_fetches([], []) ->
ok;
await_fetches([], Errors) ->
{error, Errors};
await_fetches(Pending, Errors) ->
{NewPending, NewErrors} =
receive
{z_reply, ID, ok} ->
{lists:delete(ID, Pending), Errors};
{z_reply, ID, {error, Package, Reason}} ->
{lists:delete(ID, Pending), [{Package, Reason} | Errors]}
end,
await_fetches(NewPending, NewErrors).
-spec execute(Type, PackageID, Meta, Dir, RunArgs) -> no_return() -spec execute(Type, PackageID, Meta, Dir, RunArgs) -> no_return()
when Type :: app | lib, when Type :: app | lib,
PackageID :: package_id(), PackageID :: package_id(),
@ -390,12 +416,13 @@ execute(PackageID, Meta, Dir, RunArgs) ->
%% the exec_wait/1 loop to wait for any queries from the application. %% the exec_wait/1 loop to wait for any queries from the application.
execute(app, PackageID, Meta, Dir, RunArgs) -> execute(app, PackageID, Meta, Dir, RunArgs) ->
PackageString = zx_lib:package_string(PackageID), {ok, PackageString} = zx_lib:package_string(PackageID),
ok = log(info, "Starting ~ts.", [PackageString]), ok = log(info, "Starting ~ts.", [PackageString]),
Name = element(2, PackageID), Name = element(2, PackageID),
AppMod = list_to_atom(Name), AppTag = list_to_atom(Name),
ok = zx_daemon:pass_meta(Meta, Dir), {AppMod, _} = maps:get(appmod, Meta),
ok = ensure_all_started(AppMod), ok = zx_daemon:pass_meta(Meta, Dir, RunArgs),
ok = ensure_all_started(AppTag),
ok = pass_argv(AppMod, RunArgs), ok = pass_argv(AppMod, RunArgs),
log(info, "Launcher complete."); log(info, "Launcher complete.");
execute(lib, PackageID, _, _, _) -> execute(lib, PackageID, _, _, _) ->

View File

@ -127,14 +127,14 @@ timeout(#d{timeout = Timeout}) ->
Timeout. Timeout.
-spec timeout(Data, Value) -> NewData -spec timeout(Value, Data) -> NewData
when Data :: data(), when Value :: pos_integer(),
Value :: pos_integer(), Data :: data(),
NewData :: data(). NewData :: data().
%% @doc %% @doc
%% Set the timeout attribute to a new value. %% Set the timeout attribute to a new value.
timeout(Data, Value) timeout(Value, Data)
when is_integer(Value) and Value > 0 -> when is_integer(Value) and Value > 0 ->
Data#d{timeout = Value}. Data#d{timeout = Value}.
@ -147,14 +147,14 @@ retries(#d{retries = {_, Retries}}) ->
Retries. Retries.
-spec retries(Data, Value) -> NewData -spec retries(Value, Data) -> NewData
when Data :: data(), when Value :: non_neg_integer(),
Value :: non_neg_integer(), Data :: data(),
NewData :: data(). NewData :: data().
%% @doc %% @doc
%% Set the retries attribute to a new value. %% Set the retries attribute to a new value.
retries(Data = #d{retries = {Remaining, _}}, Value) retries(Value, Data = #d{retries = {Remaining, _}})
when is_integer(Value) and Value >= 0 -> when is_integer(Value) and Value >= 0 ->
Data#d{retries = {Remaining, Value}}. Data#d{retries = {Remaining, Value}}.
@ -192,14 +192,14 @@ maxconn(#d{maxconn = MaxConn}) ->
MaxConn. MaxConn.
-spec maxconn(Data, Value) -> NewData -spec maxconn(Value, Data) -> NewData
when Data :: data(), when Value :: pos_integer(),
Value :: pos_integer(), Data :: data(),
NewData :: data(). NewData :: data().
%% @doc %% @doc
%% Set the value of maxconn. %% Set the value of maxconn.
maxconn(Data, Value) maxconn(Value, Data)
when is_integer(Value) and Value > 0 -> when is_integer(Value) and Value > 0 ->
Data#d{maxconn = Value}. Data#d{maxconn = Value}.
@ -212,24 +212,24 @@ managed(#d{managed = Managed}) ->
sets:to_list(Managed). sets:to_list(Managed).
-spec managed(Data, List) -> NewData -spec managed(List, Data) -> NewData
when Data :: data(), when List :: [zx:realm()],
List :: [zx:realm()], Data :: data(),
NewData :: data(). NewData :: data().
%% @doc %% @doc
%% Reset the set of managed realms entirely. %% Reset the set of managed realms entirely.
%% The realms must be configured on the current realm at a minimum. %% The realms must be configured on the current realm at a minimum.
managed(Data, List) -> managed(List, Data) ->
Desired = sets:from_list(List), Desired = sets:from_list(List),
Configured = sets:from_list(zx_lib:list_realms()), Configured = sets:from_list(zx_lib:list_realms()),
NewManaged = sets:intersection(Desired, Configured), NewManaged = sets:intersection(Desired, Configured),
Data#d{managed = NewManaged}. Data#d{managed = NewManaged}.
-spec add_managed(Data, Realm) -> Result -spec add_managed(Realm, Data) -> Result
when Data :: data(), when Realm :: zx:realm(),
Realm :: zx:realm(), Data :: data(),
Result :: {ok, NewData} Result :: {ok, NewData}
| {error, unconfigured}, | {error, unconfigured},
NewData :: data(). NewData :: data().
@ -238,8 +238,8 @@ managed(Data, List) ->
%% the current node. This node will then behave as the prime node for the realm (whether %% the current node. This node will then behave as the prime node for the realm (whether
%% it is or not). %% it is or not).
add_managed(Data = #d{managed = Managed}, Realm) -> add_managed(Realm, Data = #d{managed = Managed}) ->
case lists:member(Realm, zx_lib:list_realms()) of case zx_lib:realm_exists(Realm) of
true -> true ->
NewData = Data#d{managed = sets:add_element(Realm, Managed)}, NewData = Data#d{managed = sets:add_element(Realm, Managed)},
ok = log(info, "Now managing realm: ~tp", [Realm]), ok = log(info, "Now managing realm: ~tp", [Realm]),
@ -250,16 +250,16 @@ add_managed(Data = #d{managed = Managed}, Realm) ->
end. end.
-spec rem_managed(Data, Realm) -> Result -spec rem_managed(Realm, Data) -> Result
when Data :: data(), when Realm :: zx:realm(),
Realm :: zx:realm(), Data :: data(),
Result :: {ok, NewData} Result :: {ok, NewData}
| {error, unmanaged}, | {error, unmanaged},
NewData :: data(). NewData :: data().
%% @doc %% @doc
%% Stop managing a realm. %% Stop managing a realm.
rem_managed(Data = #d{managed = Managed}, Realm) -> rem_managed(Realm, Data = #d{managed = Managed}) ->
case sets:is_element(Realm, Managed) of case sets:is_element(Realm, Managed) of
true -> true ->
NewData = Data#d{managed = sets:del_element(Realm, Managed)}, NewData = Data#d{managed = sets:del_element(Realm, Managed)},
@ -279,20 +279,20 @@ mirrors(#d{mirrors = Mirrors}) ->
Mirrors. Mirrors.
-spec mirrors(Data, Hosts) -> NewData -spec mirrors(Hosts, Data) -> NewData
when Data :: data(), when Hosts :: [zx:host()],
Hosts :: [zx:host()], Data :: data(),
NewData :: data(). NewData :: data().
%% @private %% @private
%% Reset the mirror configuration. %% Reset the mirror configuration.
mirrors(Data, Hosts) -> mirrors(Hosts, Data) ->
Data#d{mirrors = Hosts}. Data#d{mirrors = Hosts}.
-spec add_mirror(Data, Host) -> NewData -spec add_mirror(Host, Data) -> NewData
when Data :: data(), when Host :: zx:host(),
Host :: zx:host(), Data :: data(),
NewData :: data(). NewData :: data().
%% @doc %% @doc
%% Add a mirror to the permanent configuration. %% Add a mirror to the permanent configuration.
@ -304,14 +304,14 @@ add_mirror(Data = #d{mirrors = Mirrors}, Host) ->
end. end.
-spec rem_mirror(Data, Host) -> NewData -spec rem_mirror(Host, Data) -> NewData
when Data :: data(), when Host :: zx:host(),
Host :: zx:host(), Data :: data(),
NewData :: data(). NewData :: data().
%% @private %% @private
%% Remove a host from the list of permanent mirrors. %% Remove a host from the list of permanent mirrors.
rem_mirror(Data = #d{mirrors = Mirrors}, Host) -> rem_mirror(Host, Data = #d{mirrors = Mirrors}) ->
Data#d{mirrors = lists:delete(Host, Mirrors)}. Data#d{mirrors = lists:delete(Host, Mirrors)}.

View File

@ -147,7 +147,7 @@
-export([pass_meta/3, -export([pass_meta/3,
subscribe/1, unsubscribe/1, subscribe/1, unsubscribe/1,
list/0, list/1, list/2, list/3, latest/1, list/0, list/1, list/2, list/3, latest/1,
fetch_zsp/1, fetch_key/1, fetch/1, verify_key/1,
pending/1, packagers/1, maintainers/1, sysops/1]). pending/1, packagers/1, maintainers/1, sysops/1]).
-export([report/1, result/2, notify/2]). -export([report/1, result/2, notify/2]).
-export([start_link/0, stop/0]). -export([start_link/0, stop/0]).
@ -326,6 +326,9 @@
%% references. %% references.
pass_meta(Meta, Dir, ArgV) -> pass_meta(Meta, Dir, ArgV) ->
ok = log(info, "Meta: ~tp", [Meta]),
ok = log(info, "Dir : ~tp", [Dir]),
ok = log(info, "ArgV: ~tp", [ArgV]),
gen_server:cast(?MODULE, {pass_meta, Meta, Dir, ArgV}). gen_server:cast(?MODULE, {pass_meta, Meta, Dir, ArgV}).
@ -446,7 +449,7 @@ latest({Realm, Name, Version}) ->
request({latest, Realm, Name, Version}). request({latest, Realm, Name, Version}).
-spec fetch_zsp(PackageID) -> {ok, RequestID} -spec fetch(PackageID) -> {ok, RequestID}
when PackageID :: zx:package_id(), when PackageID :: zx:package_id(),
RequestID :: integer(). RequestID :: integer().
%% @doc %% @doc
@ -458,27 +461,28 @@ latest({Realm, Name, Version}) ->
%% Response messages are of the type `result()' where the third element is of the %% Response messages are of the type `result()' where the third element is of the
%% type `fetch_result()'. %% type `fetch_result()'.
fetch_zsp(PackageID = {Realm, Name, Version}) -> fetch({Realm, Name, Version}) ->
true = zx_lib:valid_lower0_9(Realm), true = zx_lib:valid_lower0_9(Realm),
true = zx_lib:valid_lower0_9(Name), true = zx_lib:valid_lower0_9(Name),
true = zx_lib:valid_version(Version), true = zx_lib:valid_version(Version),
request({fetch, zsp, PackageID}). request({fetch, Realm, Name, Version}).
-spec fetch_key(KeyID) -> {ok, RequestID} -spec verify_key(KeyID) -> {ok, RequestID}
when KeyID :: zx:key_id(), when KeyID :: zx:key_id(),
RequestID :: id(). RequestID :: id().
%% @doc %% @doc
%% Request a public key be fetched from its relevant realm. %% Request a public key be fetched from its upstream, and pursue the key validation
%% chain until a key in possession is found or the chain is proven to be broken.
%% Crashes the caller if either component of the KeyID is illegal. %% Crashes the caller if either component of the KeyID is illegal.
%% %%
%% Response messages are of the type `result()' where the third element is of the %% Response messages are of the type `result()' where the third element is of the
%% type `key_result()'. %% type `key_result()'.
fetch_key(KeyID = {Realm, KeyName}) -> verify_key({Realm, KeyName}) ->
true = zx_lib:valid_lower0_9(Realm), true = zx_lib:valid_lower0_9(Realm),
true = zx_lib:valid_lower0_9(KeyName), true = zx_lib:valid_lower0_9(KeyName),
request({fetch, key, KeyID}). request({verify_key, Realm, KeyName}).
-spec pending(Package) -> {ok, RequestID} -spec pending(Package) -> {ok, RequestID}

View File

@ -105,9 +105,7 @@ prompt_keygen() ->
-spec generate_rsa(KeyID) -> Result -spec generate_rsa(KeyID) -> Result
when KeyID :: zx:key_id(), when KeyID :: zx:key_id(),
Result :: ok Result :: ok
| {error, keygen_fail}, | {error, keygen_fail}.
KeyFile :: file:filename(),
PubFile :: file:filename().
%% @private %% @private
%% Generate an RSA keypair and write them in der format to the current directory, using %% Generate an RSA keypair and write them in der format to the current directory, using
%% filenames derived from Prefix. %% filenames derived from Prefix.

View File

@ -15,8 +15,8 @@
-license("GPL-3.0"). -license("GPL-3.0").
-export([zomp_dir/0, find_zomp_dir/0, -export([zomp_dir/0, find_zomp_dir/0,
path/1, path/2, path/3, path/1, path/2, path/3, path/4, ppath/2,
force_dir/1, force_dir/1, mktemp_dir/1,
list_realms/0, list_realms/0,
hosts_cache_file/1, get_prime/1, realm_meta/1, hosts_cache_file/1, get_prime/1, realm_meta/1,
read_project_meta/0, read_project_meta/1, read_package_meta/1, read_project_meta/0, read_project_meta/1, read_package_meta/1,
@ -71,13 +71,13 @@ find_zomp_dir() ->
end. end.
-spec path(Type) -> Result -spec path(Type) -> Path
when Type :: etc when Type :: etc
| var | var
| tmp | tmp
| log | log
| lib, | lib,
Result :: file:filename(). Path :: file:filename().
%% @private %% @private
%% Return the top-level path of the given type in the Zomp/ZX system. %% Return the top-level path of the given type in the Zomp/ZX system.
@ -85,17 +85,19 @@ path(etc) -> filename:join(zomp_dir(), "etc");
path(var) -> filename:join(zomp_dir(), "var"); path(var) -> filename:join(zomp_dir(), "var");
path(tmp) -> filename:join(zomp_dir(), "tmp"); path(tmp) -> filename:join(zomp_dir(), "tmp");
path(log) -> filename:join(zomp_dir(), "log"); path(log) -> filename:join(zomp_dir(), "log");
path(key) -> filename:join(zomp_dir(), "key");
path(zsp) -> filename:join(zomp_dir(), "zsp");
path(lib) -> filename:join(zomp_dir(), "lib"). path(lib) -> filename:join(zomp_dir(), "lib").
-spec path(Type, Realm) -> Result -spec path(Type, Realm) -> Path
when Type :: etc when Type :: etc
| var | var
| tmp | tmp
| log | log
| lib, | lib,
Realm :: zx:realm(), Realm :: zx:realm(),
Result :: file:filename(). Path :: file:filename().
%% @private %% @private
%% Return the realm-level path of the given type in the Zomp/ZX system. %% Return the realm-level path of the given type in the Zomp/ZX system.
@ -103,7 +105,7 @@ path(Type, Realm) ->
filename:join(path(Type), Realm). filename:join(path(Type), Realm).
-spec path(Type, Realm, Name) -> Result -spec path(Type, Realm, Name) -> Path
when Type :: etc when Type :: etc
| var | var
| tmp | tmp
@ -111,7 +113,7 @@ path(Type, Realm) ->
| lib, | lib,
Realm :: zx:realm(), Realm :: zx:realm(),
Name :: zx:name(), Name :: zx:name(),
Result :: file:filename(). Path :: file:filename().
%% @private %% @private
%% Return the package-level path of the given type in the Zomp/ZX system. %% Return the package-level path of the given type in the Zomp/ZX system.
@ -119,6 +121,40 @@ path(Type, Realm, Name) ->
filename:join([path(Type), Realm, Name]). filename:join([path(Type), Realm, Name]).
-spec path(Type, Realm, Name, Version) -> Path
when Type :: etc
| var
| tmp
| log
| lib,
Realm :: zx:realm(),
Name :: zx:name(),
Version :: zx:version(),
Path :: file:filename().
%% @private
%% Return the version-specific level path of the given type in the Zomp/ZX system.
path(Type, Realm, Name, Version) ->
{ok, VersionString} = version_to_string(Version),
filename:join([path(Type), Realm, Name, VersionString]).
-spec ppath(Type, PackageID) -> Path
when Type :: etc
| var
| tmp
| log
| lib,
PackageID :: zx:package_id(),
Path :: file:filename().
%% @private
%% An alias for path/4, but more convenient when needing a path from a closed
%% package_id().
ppath(Type, {Realm, Name, Version}) ->
path(Type, Realm, Name, Version).
-spec force_dir(Path) -> Result -spec force_dir(Path) -> Result
when Path :: file:filename(), when Path :: file:filename(),
Result :: ok Result :: ok
@ -134,6 +170,20 @@ force_dir(Path) ->
end. end.
-spec mktemp_dir(Package) -> Result
when Package :: zx:package(),
Result :: {ok, TempDir :: file:filename()}
| {error, Reason :: file:posix()}.
mktemp_dir({Realm, Name}) ->
Rand = integer_to_list(binary:decode_unsigned(crypto:strong_rand_bytes(8)), 36),
TempDir = filename:join(path(etc, Realm, Name), Rand),
case force_dir(TempDir) of
ok -> {ok, TempDir};
Error -> Error
end.
-spec hosts_cache_file(zx:realm()) -> file:filename(). -spec hosts_cache_file(zx:realm()) -> file:filename().
%% @private %% @private
%% Given a Realm name, construct a realm's .hosts filename and return it. %% Given a Realm name, construct a realm's .hosts filename and return it.

View File

@ -11,11 +11,13 @@
-license("GPL-3.0"). -license("GPL-3.0").
-export([initialize/2, assimilate/1, set_dep/1, set_version/1, -export([initialize/2, assimilate/1, set_dep/1, set_version/1,
list_realms/0, list_packages/1, list_versions/1, add_realm/1, list_realms/0, list_packages/1, list_versions/1,
drop_dep/1, drop_key/1, drop_realm/1, verup/1, package/1, drop_dep/1, verup/1, package/1,
add_realm/1, drop_realm/1,
takeover/1, abdicate/1,
create_plt/0, dialyze/0, create_plt/0, dialyze/0,
grow_a_pair/0, drop_key/1, grow_a_pair/0, drop_key/1,
create_user/2, create_realm/0, create_realmfile/1]). create_user/2, create_realm/0, create_realmfile/2]).
-include("zx_logger.hrl"). -include("zx_logger.hrl").
@ -27,13 +29,8 @@
when Type :: app | lib, when Type :: app | lib,
PackageString :: string(). PackageString :: string().
%% @private %% @private
%% Initialize an application in the local directory based on the PackageID provided. %% Initialize an application in the local directory based on the PackageID provided
%% This function does not care about the name of the current directory and leaves %% and interaction with the user to determine a few details.
%% providing a complete, proper and accurate PackageID.
%% This function will check the current `lib/' directory for zomp-style dependencies.
%% If this is not the intended function or if there are non-compliant directory names
%% in `lib/' then the project will need to be rearranged to become zomp compliant or
%% the `deps' section of the resulting meta file will need to be manually updated.
initialize(Type, RawPackageString) -> initialize(Type, RawPackageString) ->
ok = ok =
@ -41,7 +38,7 @@ initialize(Type, RawPackageString) ->
false -> ok; false -> ok;
true -> error_exit("This project is already Zompified.", ?LINE) true -> error_exit("This project is already Zompified.", ?LINE)
end, end,
PackageID = PackageID = {Realm, Name, _} =
case zx_lib:package_id(RawPackageString) of case zx_lib:package_id(RawPackageString) of
{ok, {R, N, {z, z, z}}} -> {ok, {R, N, {z, z, z}}} ->
{R, N, {0, 1, 0}}; {R, N, {0, 1, 0}};
@ -54,19 +51,58 @@ initialize(Type, RawPackageString) ->
{error, invalid_package_string} -> {error, invalid_package_string} ->
error_exit("Invalid package string: ~tp", [RawPackageString], ?LINE) error_exit("Invalid package string: ~tp", [RawPackageString], ?LINE)
end, end,
{ok, PackageString} = zx_lib:package_string(PackageID), case package_exists(PackageID) of
ok = check_package_conflict(PackageID, PackageString), false ->
ok = log(info, "Initializing ~s...", [PackageString]),
Prefix = solicit_prefix(), Prefix = solicit_prefix(),
initialize(Type, PackageID, Prefix);
true ->
PackageName = zx_lib:package_string({Realm, Name, {z, z, z}}),
Message = "Package ~tp already exists. Try another.",
error_exit(Message, [PackageName], ?LINE)
end.
initialize(app, PackageID, Prefix) ->
Instructions =
"The OTP application controller has to know what module to call start/2 on to "
"start your program.~n"
"If this is not an OTP application or if there is some special setting you "
"need then ignore this by just pressing [ENTER] to bypass this and go back "
"later and edit the 'mod' attribute in your ebin/[AppName].app file by hand.~n"
"this blank.~n"
"Enter the name of your main/start interface module where the application "
"callback start/2 has been defined.~n",
ok = io:format(Instructions),
case zx_tty:get_input() of
"" ->
initialize(lib, PackageID, Prefix, none);
String ->
case zx_lib:valid_lower0_9(String) of
true ->
AppStart = {list_to_atom(String), []},
initialize(app, PackageID, Prefix, AppStart);
false ->
Message = "The name \"~ts\" doesn't seem valid. Try \"[a-z_]*\".~n",
ok = io:format(Message, String),
initialize(app, PackageID, Prefix)
end
end;
initialize(lib, PackageID, Prefix) ->
initialize(lib, PackageID, Prefix, none).
initialize(Type, PackageID, Prefix, AppStart) ->
ok = update_source_vsn(element(3, PackageID)), ok = update_source_vsn(element(3, PackageID)),
ok = update_app_file(PackageID), ok = initialize_app_file(PackageID, AppStart),
MetaList = MetaList =
[{package_id, PackageID}, [{package_id, PackageID},
{deps, []}, {deps, []},
{type, Type}, {type, Type},
{prefix, Prefix}], {prefix, Prefix},
{appmod, AppStart}],
Meta = maps:from_list(MetaList), Meta = maps:from_list(MetaList),
ok = zx_lib:write_project_meta(Meta), ok = zx_lib:write_project_meta(Meta),
{ok, PackageString} = zx_lib:package_string(PackageID),
ok = log(info, "Project ~tp initialized.", [PackageString]), ok = log(info, "Project ~tp initialized.", [PackageString]),
Message = Message =
"~nNOTICE:~n" "~nNOTICE:~n"
@ -76,36 +112,54 @@ initialize(Type, RawPackageString) ->
io:format(Message). io:format(Message).
-spec check_package_conflict(zx:package_id(), string()) -> ok. -spec package_exists(zx:package_id()) -> boolean().
%% @private %% @private
%% Check the realm's upstream for the existence of a package that already has the same %% Check the realm's upstream for the existence of a package that already has the same
%% name, or the name of any modules in src/ and report them to the user. Give the user %% name, or the name of any modules in src/ and report them to the user. Give the user
%% a chance to change their package's name or ignore the conflict, and report all module %% a chance to change their package's name or ignore the conflict, and report all module
%% naming conflicts. %% naming conflicts.
check_package_conflict(_PackageID, _PackageString) -> package_exists(PackageID) ->
log(info, "TODO: This is where the intended realm is checked for conflicts " ok = log(info, "TODO: This is where the intended realm is checked for conflicts "
"and the user is given a chance to rename or ignore the conflict. " "and the user is given a chance to rename or ignore the conflict. "
"This check will have to wait for the network protocol fix."). "This check will have to wait for the network protocol fix."),
ok = log(info, "Pretending that the existence of ~tp was checked...", [PackageID]),
false.
-spec solicit_prefix() -> ok. -spec solicit_prefix() -> string().
%% @private %% @private
%% Most Erlang projects outside of the core distribution evolve a prefix of some sort %% Get a valid module prefix to use as a namespace for new modules.
%% to namespace their work from other projects (or deliberately mimic another project's
%% prefix in the case that a new project is meant to augment, but not alter, an already
%% existing one).
%% A prefix is decided upon or disregarded here and stored in the meta file. The user
%% is given the option to update module names now (to include modifying call sites
%% in project code automatically -- while providing ample warnings about breaking
%% external code).
%% Just like with check_package_conflict/2, this procedure gives the user a chance to
%% ignore all warnings and just go.
solicit_prefix() -> solicit_prefix() ->
ok = log(info, "Will solicit a prefix here. Just returning \"zz\" right now. Hah!"), Instructions =
"zz". "~nPICKING A PREFIX~n"
"Most Erlang applications have a prefix on the front of their modules. This "
"is a way of namespacing modules from different applications so they don't "
"conflict in very large execution environments.~n"
"A common way of coming up with a prefix is to make a lower-case acronym of "
"your application name and add an underscore at the end, unless your app "
"name is so short that it isn't a problem to use it as its own prefix.~n"
"For example: the prefix for all ZX modules is 'zx_', and the prefix for "
"otpr-example_server is 'es_'.~n"
"If you already have a prefix, just type it here (including the trailing "
"underscore if there is one). If you are making up a new one in an existing "
"project you'll need to rename your modules and fix up call sites with sed.~n"
"Enter a blank prefix (just [ENTER]) to ignore this and use no prefix.~n",
ok = io:format(Instructions),
case zx_tty:get_input() of
"" ->
"";
Prefix ->
case zx_lib:valid_lower0_9(Prefix) of
true ->
Prefix;
false ->
Message = "The string \"~tp\" is problematic. Try \"[a-z]*_\".~n",
ok = io:format(Message, [Prefix]),
solicit_prefix()
end
end.
-spec update_source_vsn(zx:version()) -> ok. -spec update_source_vsn(zx:version()) -> ok.
%% @private %% @private
@ -128,17 +182,25 @@ update_source_vsn(Version) ->
log(info, "Source version attributes set"). log(info, "Source version attributes set").
-spec update_app_file(zx:package_id()) -> ok. -spec update_app_vsn(zx:name(), zx:version()) -> ok.
update_app_vsn(Name, Version) ->
{ok, VersionString} = zx_lib:version_to_string(Version),
AppFile = filename:join("ebin", Name ++ ".app"),
{ok, [{application, AppName, OldAppData}]} = file:consult(AppFile),
NewAppData = lists:keystore(vsn, 1, OldAppData, {vsn, VersionString}),
zx_lib:write_terms(AppFile, [{application, AppName, NewAppData}]).
-spec initialize_app_file(PackageID, AppStart) -> ok
when PackageID :: zx:package_id(),
AppStart :: {StartMod :: module(), Args :: term()}
| none.
%% @private %% @private
%% Update the app file or create it if it is missing. %% Update the app file or create it if it is missing.
%% TODO: If the app file is missing, interpret the src/*app.src file correctly. %% TODO: If the app file is missing and an app/*.src exists, interpret that.
%% Should really pull a few pages out of the rebar/erland.mk books on this,
%% as they've done all the hard pioneering work already.
%% TODO: Interactively determine whether the main module is the name, or the prefix
%% is the name, or the name is the prefix, or the name is the description, etc.
%% before writing anything out.
update_app_file({_, Name, Version}) -> initialize_app_file({_, Name, Version}, AppStart) ->
{ok, VersionString} = zx_lib:version_to_string(Version), {ok, VersionString} = zx_lib:version_to_string(Version),
AppName = list_to_atom(Name), AppName = list_to_atom(Name),
AppFile = filename:join("ebin", Name ++ ".app"), AppFile = filename:join("ebin", Name ++ ".app"),
@ -157,13 +219,24 @@ update_app_file({_, Name, Version}) ->
Grep = "grep -oP '^-module\\(\\K[^)]+' src/* | cut -d: -f2", Grep = "grep -oP '^-module\\(\\K[^)]+' src/* | cut -d: -f2",
Modules = [list_to_atom(M) || M <- string:lexemes(os:cmd(Grep), "\n")], Modules = [list_to_atom(M) || M <- string:lexemes(os:cmd(Grep), "\n")],
Properties = Properties =
case (AppStart == none) or lists:keymember(mod, 1, RawAppData) of
true ->
[{vsn, VersionString},
{modules, Modules}];
false ->
[{vsn, VersionString}, [{vsn, VersionString},
{modules, Modules}, {modules, Modules},
{mod, {AppName, []}}], {mod, AppStart}]
end,
Store = fun(T, L) -> lists:keystore(element(1, T), 1, L, T) end, Store = fun(T, L) -> lists:keystore(element(1, T), 1, L, T) end,
AppData = lists:foldl(Store, RawAppData, Properties), AppData = lists:foldl(Store, RawAppData, Properties),
AppProfile = {application, AppName, AppData}, AppProfile = {application, AppName, AppData},
ok = log(info, "Writing app file: ~ts~n~tp", [AppFile, AppProfile]), ok = log(info, "Writing app file: ~ts", [AppFile]),
Notice =
"NOTE: From this point on ZX will only update the module list and version "
"number of your .app file. Any additional changes (start module/args, start "
"phases, etc.) will need to be done by hand.~n",
ok = io:format(Notice),
zx_lib:write_terms(AppFile, [AppProfile]). zx_lib:write_terms(AppFile, [AppProfile]).
@ -318,6 +391,8 @@ update_version(Realm, Name, OldVersion, NewVersion, OldMeta) ->
PackageID = {Realm, Name, NewVersion}, PackageID = {Realm, Name, NewVersion},
NewMeta = maps:put(package_id, PackageID, OldMeta), NewMeta = maps:put(package_id, PackageID, OldMeta),
ok = zx_lib:write_project_meta(NewMeta), ok = zx_lib:write_project_meta(NewMeta),
ok = update_source_vsn(NewVersion),
ok = update_app_vsn(Name, NewVersion),
{ok, OldVS} = zx_lib:version_to_string(OldVersion), {ok, OldVS} = zx_lib:version_to_string(OldVersion),
{ok, NewVS} = zx_lib:version_to_string(NewVersion), {ok, NewVS} = zx_lib:version_to_string(NewVersion),
log(info, "Version changed from ~s to ~s.", [OldVS, NewVS]). log(info, "Version changed from ~s to ~s.", [OldVS, NewVS]).
@ -339,7 +414,6 @@ list_realms() ->
%% them to stdout. %% them to stdout.
list_packages(Realm) -> list_packages(Realm) ->
ok = zx:start(),
case zx_daemon:list_packages(Realm) of case zx_daemon:list_packages(Realm) of
{ok, []} -> {ok, []} ->
log(info, "Realm ~tp has no packages available.", [Realm]); log(info, "Realm ~tp has no packages available.", [Realm]);
@ -415,22 +489,6 @@ add_realm(Path) ->
end. end.
-spec add_realm(Path, Data) -> ok
when Path :: file:filename(),
Data :: binary().
add_realm(Path, Data) ->
case erl_tar:extract({binary, Data}, [compressed, {cwd, zx_lib:zomp_dir()}]) of
ok ->
{Realm, _} = string:take(filename:basename(Path), ".", true),
log(info, "Realm ~ts is now visible to this system.", [Realm]);
{error, invalid_tar_checksum} ->
error_exit("~ts is not a valid realm file.", [Path], ?LINE);
{error, eof} ->
error_exit("~ts is not a valid realm file.", [Path], ?LINE)
end.
-spec drop_dep(zx:package_id()) -> ok. -spec drop_dep(zx:package_id()) -> ok.
%% @private %% @private
%% Remove the indicate dependency from the local project's zomp.meta record. %% Remove the indicate dependency from the local project's zomp.meta record.
@ -458,59 +516,6 @@ drop_dep(PackageID) ->
end. end.
-spec drop_realm(zx:realm()) -> ok.
drop_realm(Realm) ->
ok = file:set_cwd(zx_lib:zomp_dir()),
RealmConf = zx_lib:realm_conf(Realm),
case filelib:is_regular(RealmConf) of
true ->
Message =
"~n"
" WARNING: Are you SURE you want to remove realm ~ts?~n"
" (Only \"Y\" will confirm this action.)~n",
ok = io:format(Message, [Realm]),
case zx_tty:get_input() of
"Y" ->
ok = file:delete(RealmConf),
ok = drop_prime(Realm),
ok = clear_keys(Realm),
log(info, "All traces of realm ~ts have been removed.");
_ ->
log(info, "Aborting.")
end;
false ->
ok = log(warning, "Realm conf ~ts not found.", [RealmConf]),
clear_keys(Realm)
end.
-spec drop_prime(zx:realm()) -> ok.
drop_prime(Realm) ->
Path = "zomp.conf",
case file:consult(Path) of
{ok, Conf} ->
{managed, Primes} = lists:keyfind(managed, 1, Conf),
NewPrimes = lists:delete(Realm, Primes),
NewConf = lists:keystore(managed, 1, Primes, {managed, NewPrimes}),
ok = zx_lib:write_terms(Path, NewConf),
log(info, "Ensuring ~ts is not a prime in ~ts", [Realm, Path]);
{error, enoent} ->
ok
end.
-spec clear_keys(zx:realm()) -> ok.
clear_keys(Realm) ->
KeyDir = filename:join([zx_lib:zomp_dir(), "key", Realm]),
case filelib:is_dir(KeyDir) of
true -> zx_lib:rm_rf(KeyDir);
false -> log(warning, "Keydir ~ts not found", [KeyDir])
end.
-spec verup(Level) -> ok -spec verup(Level) -> ok
when Level :: string(). when Level :: string().
%% @private %% @private
@ -784,8 +789,7 @@ create_realm() ->
Realm = zx_tty:get_input(), Realm = zx_tty:get_input(),
case zx_lib:valid_lower0_9(Realm) of case zx_lib:valid_lower0_9(Realm) of
true -> true ->
RealmFile = filename:join(zx_lib:zomp_dir(), Realm ++ ".realm"), case realm_exists(Realm) of
case filelib:is_regular(RealmFile) of
false -> false ->
create_realm(Realm); create_realm(Realm);
true -> true ->
@ -964,71 +968,72 @@ create_realm(Realm, Address, Port, UserName, Email) ->
create_realm(Realm, Address, Port, UserName, Email, RealName) -> create_realm(Realm, Address, Port, UserName, Email, RealName) ->
ok = io:format("~nGenerating keys. This might take a while, so settle in...~n"), ok = io:format("~nGenerating keys. This might take a while, so settle in...~n"),
KeyName = UserName ++ "-root", KeyName = UserName ++ "-root",
ok = make_realm_dirs(Realm),
ok = zx_key:generate_rsa({Realm, KeyName}), ok = zx_key:generate_rsa({Realm, KeyName}),
Timestamp = calendar:now_to_universal_time(erlang:timestamp()),
RealmConf = RealmConf =
[{realm, Realm}, [{realm, Realm},
{prime, {Address, Port}}, {prime, {Address, Port}},
{sysop, UserName}, {sysop, UserName},
{key, KeyName}], {key, KeyName}],
UserFile = UserConf =
[{realm, Realm}, [{realm, Realm},
{username, UserName}, {username, UserName},
{realmname, RealName}, {realmname, RealName},
{contact_info, {"email", Email}}, {contact_info, {"email", Email}},
{keys, [KeyName]}], {keys, [KeyName]}],
ok = make_realm_dirs(Realm),
{ok, CWD} = file:get_cwd(), RealmConfPath = filename:join(zx_lib:path(etc, Realm), "realm.conf"),
ok = file:set_cwd(zx_lib:path(etc)), ok = zx_lib:write_terms(RealmConfPath, RealmConf),
PubKey = zx_key:name(pub, KeyName), UserConfPath = filename:join(zx_lib:path(etc, Realm), UserName ++ ".user"),
ConfFile = filename:join(Realm, "realm.conf"), ok = zx_lib:write_terms(UserConfPath, UserConf),
ok = file:make_dir(Realm), ok = create_realmfile(Realm, "."),
ok = zx_lib:write_terms(ConfFile, RealmConf), ZRF = Realm ++ ".zrf",
ZRF =
ok = erl_tar:create(ZRF, [ConfFile, PubKey], [compressed]),
Message = Message =
"===========================================================================~n" "===========================================================================~n"
"DONE!~n" "DONE!~n"
"~n" "~n"
"The realm ~ts has been created and is accessible from the current system.~n" "The realm ~tp has been created and is accessible from the current system.~n"
"Three configuration bundles have been created in the current directory:~n"
"~n" "~n"
" 1. ~ts ~n" "Other zomp nodes and zx users will need the new realm file, ~ts, to~n"
"This is the PRIVATE realm file you will need to install on the realm's prime~n" "access the realm. It does not include any public keys.~n"
"node. It includes the your (the sysop's) public key.~n"
"~n" "~n"
" 2. ~ts ~n" "On the PRIME NODE you need to run two command:~n"
"This file is the PUBLIC realm file other zomp nodes and zx users will need~n" "1. `zx add realm ~ts` to install the new .zrf there~n"
"to access the realm. It does not include your (the sysop's) public key.~n" "2. `zx make prime ~ts` to tell the node that it is prime for that realm~n"
"~n" "~n"
" 3. ~ts ~n" "On all ZX clients that want to access your new realm and on all subordinate~n"
"This is the bundle of ALL KEYS that are defined in this realm at the moment.~n" "Zomp nodes the command `zx add realm ~ts` will need to be run.~n"
"~n" "~n"
"Now you need to make copies of these three files and back them up.~n" "How to distribute ~ts is up to you.~n"
"~n"
"On the PRIME NODE you need to run `zx add realm ~ts` and follow the prompts~n"
"to cause it to begin serving that realm as prime. (Node restart required.)~n"
"~n"
"On all zx CLIENTS that want to access your new realm and on all subordinate~n"
"MIRROR NODES the command `zx add realm ~ts` will need to be run.~n"
"The method of public realm file distribution (~ts) is up to you.~n"
"~n"
"~n"
"Public & Private key installation (if you need to recover them or perform~n"
"sysop functions from another computer) is `zx add keybundle ~ts`.~n"
"===========================================================================~n", "===========================================================================~n",
Substitutions = Substitutions = [Realm, ZRF, ZRF, Realm, ZRF, ZRF],
[Realm,
PrimeZRF, PublicZRF, KeyBundle,
PrimeZRF,
PublicZRF, PublicZRF,
KeyBundle],
io:format(Message, Substitutions). io:format(Message, Substitutions).
-spec configure_zomp() - ok. -spec realm_exists(zx:realm()) -> boolean().
%% @private
%% Checks for remnants of a realm.
realm_exists(Realm) ->
Dirs = [etc, var, tmp, log, key, zsp, lib],
Check = fun(D) -> filelib:is_file(zx_lib:path(D)) end,
Managed = lists:member(Realm, proplists:get_value(managed, zx_lib:read_sys_conf())),
Found = lists:any(Check, Dirs),
Managed or Found.
-spec make_realm_dirs(zx:realm()) -> ok.
%% @private
%% This is an unsafe operation. The caller must be sure realm_exists/1 returns false
%% or be certain some other way that the file:make_sure/1 calls will succeed.
make_realm_dirs(Realm) ->
Dirs = [etc, var, tmp, log, key, zsp, lib],
Make = fun(D) -> ok = file:make_dir(zx_lib:path(D, Realm)) end,
lists:foreach(Make, Dirs).
-spec configure_zomp() -> ok.
configure_zomp() -> configure_zomp() ->
ZompSettings = ZompSettings =
@ -1040,54 +1045,84 @@ configure_zomp() ->
io:format("~tp~n", [ZompSettings]). io:format("~tp~n", [ZompSettings]).
-spec mktemp_dir(Prefix) -> Result -spec create_realmfile(Realm, Dir) -> ok
when Prefix :: string(), when Realm :: zx:realm(),
Result :: {ok, TempDir :: file:filename()} Dir :: file:filename().
| {error, Reason :: file:posix()}.
mktemp_dir(Prefix) -> create_realmfile(Realm, Dir) ->
Rand = integer_to_list(binary:decode_unsigned(crypto:strong_rand_bytes(8)), 36), RealmConf = zx_lib:load_realm_conf(Realm),
TempPath = filename:basedir(user_cache, Prefix), ok = log(info, "Realm found, creating realm file..."),
TempDir = filename:join(TempPath, Rand), KeyName = proplists:get_value(key, RealmConf),
Result1 = filelib:ensure_dir(TempDir), PubKeyPath = zx_key:name(pub, KeyName),
Result2 = file:make_dir(TempDir), {ok, PubDER} = file:read_file(PubKeyPath),
case {Result1, Result2} of Blob = term_to_binary({RealmConf, PubDER}),
{ok, ok} -> {ok, TempDir}; ZRF = filename:join(Dir, Realm ++ ".zrf"),
{ok, Error} -> Error; ok = file:write_file(ZRF, Blob),
{Error, _} -> Error log(info, "Realm conf file written to ~ts", [ZRF]).
-spec add_realm(Path, Data) -> ok
when Path :: file:filename(),
Data :: binary().
add_realm(Path, Data) ->
case erl_tar:extract({binary, Data}, [compressed, {cwd, zx_lib:zomp_dir()}]) of
ok ->
{Realm, _} = string:take(filename:basename(Path), ".", true),
log(info, "Realm ~ts is now visible to this system.", [Realm]);
{error, invalid_tar_checksum} ->
error_exit("~ts is not a valid realm file.", [Path], ?LINE);
{error, eof} ->
error_exit("~ts is not a valid realm file.", [Path], ?LINE)
end. end.
-spec create_realmfile(zx:realm()) -> no_return(). -spec drop_realm(zx:realm()) -> ok.
create_realmfile(Realm) -> drop_realm(Realm) ->
RealmConf = zx_lib:load_realm_conf(Realm), case realm_exists(Realm) of
ok = log(info, "Realm found, creating realm file..."), true ->
{revision, Revision} = lists:keyfind(revision, 1, RealmConf), Message =
{realm_keys, RealmKeys} = lists:keyfind(realm_keys, 1, RealmConf), "~n"
{package_keys, PackageKeys} = lists:keyfind(package_keys, 1, RealmConf), " WARNING: Are you SURE you want to remove realm ~ts?~n"
RealmKeyIDs = [element(1, K) || K <- RealmKeys], " (Only \"Y\" will confirm this action.)~n",
PackageKeyIDs = [element(1, K) || K <- PackageKeys], ok = io:format(Message, [Realm]),
create_realmfile(Realm, Revision, RealmKeyIDs, PackageKeyIDs). case zx_tty:get_input() of
"Y" ->
Dirs = [etc, var, tmp, log, key, zsp, lib],
RM = fun(D) -> ok = zx_lib:rm_rf(zx_lib:path(D, Realm)) end,
ok = lists:foreach(RM, Dirs),
ok = abdicate(Realm),
log(info, "All traces of realm ~ts have been removed.");
_ ->
log(info, "Aborting.")
end;
false ->
log(info, "Could not find any trace of realm ~tp", [Realm])
end.
-spec create_realmfile(Realm, Revision, RealmKeyIDs, PackageKeyIDs) -> ok -spec takeover(zx:realm()) -> ok.
when Realm :: zx:realm(), %% @private
Revision :: non_neg_integer(), %% Assume responsibilities as the prime node for the given realm. Only works if
RealmKeyIDs :: [zx:key_id()], %% the realm exists, of course.
PackageKeyIDs :: [zx:key_id()].
create_realmfile(Realm, Revision, RealmKeyIDs, PackageKeyIDs) -> takeover(Realm) ->
{ok, CWD} = file:get_cwd(), SysConf = zx_conf_sys:load(),
ok = file:set_cwd(zx_lib:zomp_dir()), case zx_conf_sys:add_managed(Realm, SysConf) of
KeyPath = fun({R, K}) -> filename:join(["key", R, K ++ ".pub.der"]) end, {ok, NewConf} -> zx_conf_sys:save(NewConf);
RealmKeyPaths = lists:map(KeyPath, RealmKeyIDs), {error, unconfigured} -> log(error, "Cannot take over an unconfigured realm.")
PackageKeyPaths = lists:map(KeyPath, PackageKeyIDs), end.
Targets = [zx_lib:realm_conf(Realm) | RealmKeyPaths ++ PackageKeyPaths],
OutFile = filename:join(CWD, Realm ++ "." ++ integer_to_list(Revision) ++ ".zrf"),
ok = erl_tar:create(OutFile, Targets, [compressed]), -spec abdicate(zx:realm()) -> ok.
ok = log(info, "Realm conf file written to ~ts", [OutFile]),
halt(0). abdicate(Realm) ->
SysConf = zx_conf_sys:load(),
case zx_conf_sys:rem_managed(Realm, SysConf) of
{ok, NewConf} -> zx_conf_sys:save(NewConf);
{error, unmanaged} -> log(error, "Cannot abdicate an unmanaged realm.")
end.

2
zx_dev
View File

@ -12,4 +12,4 @@ export ZX_DIR="$ZOMP_DIR/lib/otpr/zx/$VERSION"
pushd "$ZX_DIR" > /dev/null pushd "$ZX_DIR" > /dev/null
./make_zx ./make_zx
popd > /dev/null popd > /dev/null
erl -pa "$ZX_DIR/ebin" -run zx run $@ erl -noshell -pa "$ZX_DIR/ebin" -run zx do $@

15
zxh_dev Executable file
View File

@ -0,0 +1,15 @@
#!/bin/bash
pushd $(dirname $BASH_SOURCE) > /dev/null
ZX_DEV_ROOT=$PWD
popd > /dev/null
export ZOMP_DIR="$ZX_DEV_ROOT/tester"
rm -rf "$ZOMP_DIR"
cp -r "$ZX_DEV_ROOT/zomp" "$ZOMP_DIR"
VERSION=$(cat "$ZOMP_DIR/etc/version.txt")
export ZX_DIR="$ZOMP_DIR/lib/otpr/zx/$VERSION"
pushd "$ZX_DIR" > /dev/null
./make_zx
popd > /dev/null
erl -pa "$ZX_DIR/ebin" -run zx do $@