This commit is contained in:
Craig Everett 2017-11-29 16:04:11 +09:00
parent 20f27cbcab
commit eeac89c4c9

137
zx
View File

@ -31,7 +31,7 @@
-type state() :: #s{}. -type state() :: #s{}.
-type serial() :: pos_integer(). -type serial() :: integer().
-type package_id() :: {realm(), name(), version()}. -type package_id() :: {realm(), name(), version()}.
-type package() :: {realm(), name()}. -type package() :: {realm(), name()}.
-type realm() :: lower0_9(). -type realm() :: lower0_9().
@ -48,7 +48,7 @@
-type key_name() :: label(). -type key_name() :: label().
-type lower0_9() :: [$a..$z | $0..$9 | $_]. -type lower0_9() :: [$a..$z | $0..$9 | $_].
-type label() :: [$a..$z | $0..$9 | $_ | $- | $.]. -type label() :: [$a..$z | $0..$9 | $_ | $- | $.].
-type package_meta() :: #{}. -type package_meta() :: map().
@ -178,9 +178,10 @@ run(Identifier, Args) ->
initialize(Type, PackageID) -> initialize(Type, PackageID) ->
PackageString = package_string(PackageID), PackageString = package_string(PackageID),
ok = log(info, "Initializing ~s...", [PackageString]), ok = log(info, "Initializing ~s...", [PackageString]),
Meta = [{package_id, PackageID}, MetaList = [{package_id, PackageID},
{deps, []}, {deps, []},
{type, Type}], {type, Type}],
Meta = maps:from_list(MetaList),
ok = write_meta(Meta), ok = write_meta(Meta),
ok = log(info, "Project ~tp initialized.", [PackageString]), ok = log(info, "Project ~tp initialized.", [PackageString]),
Message = Message =
@ -312,7 +313,7 @@ ensure_installed(PackageID = {Realm, Name, Version}) ->
Name :: name(), Name :: name(),
Version :: version(), Version :: version(),
Result :: exact Result :: exact
| {ok, version()} | {ok, package_id()}
| not_found. | not_found.
%% @private %% @private
%% Fetch and install the latest compatible version of the given package ID, whether %% Fetch and install the latest compatible version of the given package ID, whether
@ -482,7 +483,7 @@ set_version(VersionString) ->
update_version(Arg) -> update_version(Arg) ->
Meta = read_meta(), Meta = read_meta(),
{package_id, PackageID} = lists:keyfind(package_id, 1, Meta), PackageID = maps:get(package_id, Meta),
update_version(Arg, PackageID, Meta). update_version(Arg, PackageID, Meta).
@ -528,7 +529,7 @@ update_version(NewVersion, {Realm, Name, OldVersion}, OldMeta) ->
update_version(Realm, Name, OldVersion, NewVersion, OldMeta) -> update_version(Realm, Name, OldVersion, NewVersion, OldMeta) ->
PackageID = {Realm, Name, NewVersion}, PackageID = {Realm, Name, NewVersion},
NewMeta = lists:keystore(package_id, 1, OldMeta, {package_id, PackageID}), NewMeta = maps:put(package_id, PackageID, OldMeta),
ok = write_meta(NewMeta), ok = write_meta(NewMeta),
ok = log(info, ok = log(info,
"Version changed from ~s to ~s.", "Version changed from ~s to ~s.",
@ -546,11 +547,11 @@ update_version(Realm, Name, OldVersion, NewVersion, OldMeta) ->
drop_dep(PackageID) -> drop_dep(PackageID) ->
PackageString = package_string(PackageID), PackageString = package_string(PackageID),
Meta = read_meta(), Meta = read_meta(),
{deps, Deps} = lists:keyfind(deps, 1, Meta), Deps = maps:get(deps, Meta),
case lists:member(PackageID, Deps) of case lists:member(PackageID, Deps) of
true -> true ->
NewDeps = lists:delete(PackageID, Deps), NewDeps = lists:delete(PackageID, Deps),
NewMeta = lists:keystore(deps, 1, Meta, {deps, NewDeps}), NewMeta = maps:put(deps, NewDeps, Meta),
ok = write_meta(NewMeta), ok = write_meta(NewMeta),
Message = "~ts removed from dependencies.", Message = "~ts removed from dependencies.",
ok = log(info, Message, [PackageString]), ok = log(info, Message, [PackageString]),
@ -660,7 +661,7 @@ execute(#s{type = lib, realm = Realm, name = Name, version = Version}, _) ->
package(TargetDir) -> package(TargetDir) ->
ok = log(info, "Packaging ~ts", [TargetDir]), ok = log(info, "Packaging ~ts", [TargetDir]),
Meta = read_meta(TargetDir), Meta = read_meta(TargetDir),
{package_id, {Realm, _, _}} = lists:keyfind(package_id, 1, Meta), {Realm, _, _} = maps:get(package_id, Meta),
KeyDir = filename:join([zomp_dir(), "key", Realm]), KeyDir = filename:join([zomp_dir(), "key", Realm]),
ok = force_dir(KeyDir), ok = force_dir(KeyDir),
Pattern = KeyDir ++ "/*.key.der", Pattern = KeyDir ++ "/*.key.der",
@ -689,7 +690,7 @@ package(TargetDir) ->
package(KeyID, TargetDir) -> package(KeyID, TargetDir) ->
Meta = read_meta(TargetDir), Meta = read_meta(TargetDir),
{package_id, PackageID} = lists:keyfind(package_id, 1, Meta), PackageID = maps:get(package_id, Meta),
true = element(1, PackageID) == element(1, KeyID), true = element(1, PackageID) == element(1, KeyID),
PackageString = package_string(PackageID), PackageString = package_string(PackageID),
ZrpFile = PackageString ++ ".zrp", ZrpFile = PackageString ++ ".zrp",
@ -710,7 +711,8 @@ package(KeyID, TargetDir) ->
{ok, Key} = loadkey(private, KeyID), {ok, Key} = loadkey(private, KeyID),
{ok, TgzBin} = file:read_file(TgzFile), {ok, TgzBin} = file:read_file(TgzFile),
Sig = public_key:sign(TgzBin, sha512, Key), Sig = public_key:sign(TgzBin, sha512, Key),
FinalMeta = [{modules, Modules}, {sig, {KeyID, Sig}} | Meta], Add = fun({K, V}, M) -> maps:put(K, V, M) end,
FinalMeta = lists:foldl(Add, Meta, [{modules, Modules}, {sig, {KeyID, Sig}}]),
ok = file:write_file("zomp.meta", term_to_binary(FinalMeta)), ok = file:write_file("zomp.meta", term_to_binary(FinalMeta)),
ok = erl_tar:create(ZrpFile, ["zomp.meta", TgzFile]), ok = erl_tar:create(ZrpFile, ["zomp.meta", TgzFile]),
ok = file:delete(TgzFile), ok = file:delete(TgzFile),
@ -946,7 +948,7 @@ confirm_serial(Realm, Socket, Hosts) ->
Socket; Socket;
{ok, Current} when Current > Serial -> {ok, Current} when Current > Serial ->
ok = log(info, "Node's serial newer than ours. Storing."), ok = log(info, "Node's serial newer than ours. Storing."),
NewSerials = lists:keystore(Realm, 1, Current, Serials), NewSerials = lists:keystore(Realm, 1, Current, {Realm, Serials}),
{ok, Host} = inet:peername(Socket), {ok, Host} = inet:peername(Socket),
ok = write_terms(hosts_cache_file(Realm), [Host | Hosts]), ok = write_terms(hosts_cache_file(Realm), [Host | Hosts]),
ok = write_terms(SerialFile, NewSerials), ok = write_terms(SerialFile, NewSerials),
@ -1402,6 +1404,12 @@ dialyze() ->
%%% Create Realm & Sysop %%% Create Realm & Sysop
-spec create_realm() -> no_return().
%% @private
%% Prompt the user to input the information necessary to create a new zomp realm,
%% package the data appropriately for the server and deliver the final keys and
%% realm file to the user.
create_realm() -> create_realm() ->
ConfFile = filename:join(zomp_dir(), "zomp.conf"), ConfFile = filename:join(zomp_dir(), "zomp.conf"),
case file:consult(ConfFile) of case file:consult(ConfFile) of
@ -1409,6 +1417,10 @@ create_realm() ->
{error, enoent} -> create_realm([]) {error, enoent} -> create_realm([])
end. end.
-spec create_realm(ZompConf) -> no_return()
when ZompConf :: [{Key :: atom(), Value :: term()}].
create_realm(ZompConf) -> create_realm(ZompConf) ->
Instructions = Instructions =
"~n" "~n"
@ -1432,6 +1444,11 @@ create_realm(ZompConf) ->
create_realm(ZompConf) create_realm(ZompConf)
end. end.
-spec create_realm(ZompConf, Realm) -> no_return()
when ZompConf :: [{Key :: atom(), Value :: term()}],
Realm :: realm().
create_realm(ZompConf, Realm) -> create_realm(ZompConf, Realm) ->
ExAddress = ExAddress =
case lists:keyfind(external_address, 1, ZompConf) of case lists:keyfind(external_address, 1, ZompConf) of
@ -1441,6 +1458,10 @@ create_realm(ZompConf, Realm) ->
end, end,
create_realm(ZompConf, Realm, ExAddress). create_realm(ZompConf, Realm, ExAddress).
-spec prompt_external_address() -> Result
when Result :: inet:hostname() | inet:ip_address().
prompt_external_address() -> prompt_external_address() ->
Message = external_address_prompt(), Message = external_address_prompt(),
ok = io:format(Message), ok = io:format(Message),
@ -1452,6 +1473,11 @@ prompt_external_address() ->
parse_address(String) parse_address(String)
end. end.
-spec prompt_external_address(Current) -> Result
when Current :: inet:hostname() | inet:ip_address(),
Result :: inet:hostname() | inet:ip_address().
prompt_external_address(Current) -> prompt_external_address(Current) ->
XAString = XAString =
case inet:ntoa(Current) of case inet:ntoa(Current) of
@ -1467,6 +1493,9 @@ prompt_external_address(Current) ->
String -> parse_address(String) String -> parse_address(String)
end. end.
-spec external_address_prompt() -> string().
external_address_prompt() -> external_address_prompt() ->
"~n" "~n"
" Enter a static, valid hostname or IPv4 or IPv6 address at which this host " " Enter a static, valid hostname or IPv4 or IPv6 address at which this host "
@ -1474,12 +1503,21 @@ external_address_prompt() ->
"need to be reached from the internet).~n" "need to be reached from the internet).~n"
" DO NOT INCLUDE A PORT NUMBER IN THIS STEP~n". " DO NOT INCLUDE A PORT NUMBER IN THIS STEP~n".
-spec parse_address(string()) -> inet:hostname() | inet:ip_address().
parse_address(String) -> parse_address(String) ->
case inet:parse_address(String) of case inet:parse_address(String) of
{ok, Address} -> Address; {ok, Address} -> Address;
{error, einval} -> String {error, einval} -> String
end. end.
-spec create_realm(ZompConf, Realm, ExAddress) -> no_return()
when ZompConf :: [{Key :: atom(), Value :: term()}],
Realm :: realm(),
ExAddress :: inet:hostname() | inet:ip_address().
create_realm(ZompConf, Realm, ExAddress) -> create_realm(ZompConf, Realm, ExAddress) ->
Current = Current =
case lists:keyfind(external_port, 1, ZompConf) of case lists:keyfind(external_port, 1, ZompConf) of
@ -1496,6 +1534,13 @@ create_realm(ZompConf, Realm, ExAddress) ->
ExPort = prompt_port_number(Current), ExPort = prompt_port_number(Current),
create_realm(ZompConf, Realm, ExAddress, ExPort). create_realm(ZompConf, Realm, ExAddress, ExPort).
-spec create_realm(ZompConf, Realm, ExAddress, ExPort) -> no_return()
when ZompConf :: [{Key :: atom(), Value :: term()}],
Realm :: realm(),
ExAddress :: inet:hostname() | inet:ip_address(),
ExPort :: inet:port_number().
create_realm(ZompConf, Realm, ExAddress, ExPort) -> create_realm(ZompConf, Realm, ExAddress, ExPort) ->
Current = Current =
case lists:keyfind(internal_port, 1, ZompConf) of case lists:keyfind(internal_port, 1, ZompConf) of
@ -1512,6 +1557,11 @@ create_realm(ZompConf, Realm, ExAddress, ExPort) ->
InPort = prompt_port_number(Current), InPort = prompt_port_number(Current),
create_realm(ZompConf, Realm, ExAddress, ExPort, InPort). create_realm(ZompConf, Realm, ExAddress, ExPort, InPort).
-spec prompt_port_number(Current) -> Result
when Current :: inet:port_number(),
Result :: inet:port_number().
prompt_port_number(Current) -> prompt_port_number(Current) ->
Instructions = Instructions =
" A valid port is any number from 1 to 65535." " A valid port is any number from 1 to 65535."
@ -1536,6 +1586,14 @@ prompt_port_number(Current) ->
end end
end. end.
-spec create_realm(ZompConf, Realm, ExAddress, ExPort, InPort) -> no_return()
when ZompConf :: [{Key :: atom(), Value :: term()}],
Realm :: realm(),
ExAddress :: inet:hostname() | inet:ip_address(),
ExPort :: inet:port_number(),
InPort :: inet:port_number().
create_realm(ZompConf, Realm, ExAddress, ExPort, InPort) -> create_realm(ZompConf, Realm, ExAddress, ExPort, InPort) ->
Instructions = Instructions =
"~n" "~n"
@ -1552,6 +1610,15 @@ create_realm(ZompConf, Realm, ExAddress, ExPort, InPort) ->
create_realm(ZompConf, Realm, ExAddress, ExPort, InPort) create_realm(ZompConf, Realm, ExAddress, ExPort, InPort)
end. end.
-spec create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, UserName) -> no_return()
when ZompConf :: [{Key :: atom(), Value :: term()}],
Realm :: realm(),
ExAddress :: inet:hostname() | inet:ip_address(),
ExPort :: inet:port_number(),
InPort :: inet:port_number(),
UserName :: string().
create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, UserName) -> create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, UserName) ->
Instructions = Instructions =
"~n" "~n"
@ -1579,6 +1646,17 @@ create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, UserName) ->
create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, UserName) create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, UserName)
end. end.
-spec create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, UserName, Email) ->
no_return()
when ZompConf :: [{Key :: atom(), Value :: term()}],
Realm :: realm(),
ExAddress :: inet:hostname() | inet:ip_address(),
ExPort :: inet:port_number(),
InPort :: inet:port_number(),
UserName :: string(),
Email :: string().
create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, UserName, Email) -> create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, UserName, Email) ->
Instructions = Instructions =
"~n" "~n"
@ -1671,6 +1749,8 @@ create_realm(ZompConf, Realm, ExAddress, ExPort, InPort, UserName, Email) ->
halt(0). halt(0).
-spec create_sysop() -> no_return().
create_sysop() -> create_sysop() ->
ok = log(info, "Fo' realz, yo! We be sysoppin up in hurr!"), ok = log(info, "Fo' realz, yo! We be sysoppin up in hurr!"),
halt(0). halt(0).
@ -1749,9 +1829,9 @@ verify(Data, Signature, PubKey) ->
%% Download a package to the local cache. %% Download a package to the local cache.
fetch(Socket, PackageID) -> fetch(Socket, PackageID) ->
ok = request_zrp(Socket, PackageID), {ok, LatestID} = request_zrp(Socket, PackageID),
ok = receive_zrp(Socket, PackageID), ok = receive_zrp(Socket, LatestID),
log(info, "Fetched ~ts", [package_string(PackageID)]). log(info, "Fetched ~ts", [package_string(LatestID)]).
request_zrp(Socket, PackageID) -> request_zrp(Socket, PackageID) ->
@ -2183,33 +2263,6 @@ hurr() -> io:format("That isn't an option.~n").
%%% Directory & File Management %%% Directory & File Management
%-spec move_file(From, To) -> Result
% when From :: file:filename(),
% To :: file:filename(),
% Result :: ok
% | {error, Reason},
% Reason :: bad_source
% | destination_exists
% | file:posix().
%%% @private
%%% Utility function to safely copy a file From one path To another without clobbering the
%%% destination in the event it already exists. Both the source and the destination must be
%%% complete filenames, not directories.
%
%move_file(From, To) ->
% case {filelib:is_regular(From), filelib:is_file(To)} of
% {false, false} ->
% case file:copy(From, To) of
% {ok, _} -> file:delete(From);
% Error -> Error
% end;
% {true, _} ->
% {error, bad_source};
% {false, true} ->
% {error, destination_exists}
% end.
-spec ensure_zomp_home() -> ok. -spec ensure_zomp_home() -> ok.
%% @private %% @private
%% Ensure the zomp home directory exists and is populated. %% Ensure the zomp home directory exists and is populated.