wip
This commit is contained in:
parent
1e6132f7e5
commit
f20f205c4f
@ -64,7 +64,7 @@
|
||||
type := app | lib}.
|
||||
|
||||
-type outcome() :: ok
|
||||
| {error, Reason :: atom()}
|
||||
| {error, Reason :: term()}
|
||||
| {error, Code :: non_neg_integer()}
|
||||
| {error, Info :: string(), Code :: non_neg_integer()}.
|
||||
|
||||
@ -100,8 +100,10 @@ do(["list", "deps"]) ->
|
||||
done(zx_local:list_deps());
|
||||
do(["list", "deps", PackageString]) ->
|
||||
done(zx_local:list_deps(PackageString));
|
||||
do(["install", PackageFile]) ->
|
||||
done(zx_local:assimilate(PackageFile));
|
||||
do(["import", "zrp", PackageFile]) ->
|
||||
done(zx_daemon:import_zrp(PackageFile));
|
||||
do(["install", PackageString]) ->
|
||||
done(zx_daemon:install(PackageString));
|
||||
do(["set", "dep", PackageString]) ->
|
||||
done(zx_local:set_dep(PackageString));
|
||||
do(["set", "version", VersionString]) ->
|
||||
@ -331,7 +333,7 @@ run(Identifier, RunArgs) ->
|
||||
end,
|
||||
{ok, PackageID} = ensure_installed(FuzzyID),
|
||||
ok = build(PackageID),
|
||||
Dir = zx_lib:path(lib, PackageID),
|
||||
Dir = zx_lib:ppath(lib, PackageID),
|
||||
{ok, Meta} = zx_lib:read_project_meta(Dir),
|
||||
prepare(PackageID, Meta, Dir, RunArgs).
|
||||
|
||||
@ -527,46 +529,13 @@ tuplize(String, Acc) ->
|
||||
end.
|
||||
|
||||
|
||||
|
||||
%%% Package utilities
|
||||
|
||||
|
||||
-spec install(package_id()) -> ok.
|
||||
%% @private
|
||||
%% Install a package from the cache into the local system.
|
||||
%% Before calling this function it must be known that:
|
||||
%% - The zsp file is in the cache
|
||||
%% - The zsp file is valid
|
||||
%% - This function will only be called on startup by the launch process
|
||||
%% - The package is not already installed
|
||||
%% - If this function crashes it will completely halt the system
|
||||
|
||||
install(PackageID = {Realm, Name, _}) ->
|
||||
{ok, PackageString} = zx_lib:package_string(PackageID),
|
||||
ok = log(info, "Installing ~ts", [PackageString]),
|
||||
ZrpFile = filename:join(zx_lib:path(zsp, Realm, Name), zx_lib:namify_zsp(PackageID)),
|
||||
Files = zx_lib:extract_zsp_or_die(ZrpFile),
|
||||
TgzFile = zx_lib:namify_tgz(PackageID),
|
||||
{TgzFile, TgzData} = lists:keyfind(TgzFile, 1, Files),
|
||||
{"zomp.meta", MetaBin} = lists:keyfind("zomp.meta", 1, Files),
|
||||
Meta = binary_to_term(MetaBin),
|
||||
{KeyID, Signature} = maps:get(sig, Meta),
|
||||
{ok, PubKey} = zx_key:load(public, KeyID),
|
||||
ok = ensure_package_dirs(PackageID),
|
||||
PackageDir = zx_lib:path(lib, PackageID),
|
||||
ok = zx_lib:force_dir(PackageDir),
|
||||
ok = zx_key:verify(TgzData, Signature, PubKey),
|
||||
ok = erl_tar:extract({binary, TgzData}, [compressed, {cwd, PackageDir}]),
|
||||
log(info, "~ts installed", [PackageString]).
|
||||
|
||||
|
||||
-spec build(package_id()) -> ok.
|
||||
%% @private
|
||||
%% Given an AppID, build the project from source and add it to the current lib path.
|
||||
|
||||
build(PackageID) ->
|
||||
{ok, CWD} = file:get_cwd(),
|
||||
ok = file:set_cwd(zx_lib:path(lib, PackageID)),
|
||||
ok = file:set_cwd(zx_lib:ppath(lib, PackageID)),
|
||||
ok = zx_lib:build(),
|
||||
file:set_cwd(CWD).
|
||||
|
||||
@ -577,7 +546,7 @@ build(PackageID) ->
|
||||
%% run have been created or halt execution.
|
||||
|
||||
ensure_package_dirs(PackageID) ->
|
||||
Dirs = [zx_lib:path(D, PackageID) || D <- [etc, var, tmp, log, lib]],
|
||||
Dirs = [zx_lib:ppath(D, PackageID) || D <- [etc, var, tmp, log, lib]],
|
||||
lists:foreach(fun zx_lib:force_dir/1, Dirs).
|
||||
|
||||
|
||||
@ -642,10 +611,9 @@ usage() ->
|
||||
" zx reject PackageID~n"
|
||||
" zx add key Realm KeyName~n"
|
||||
" zx get key Realm KeyName~n"
|
||||
" zx rem key Realm KeyName~n"
|
||||
" zx create user~n"
|
||||
" zx create userfiles Realm UserName~n"
|
||||
" zx create keypair Realm~n"
|
||||
" zx create keypair~n"
|
||||
" zx export user UserID~n"
|
||||
" zx import user ZdufFile~n"
|
||||
"~n"
|
||||
@ -658,7 +626,6 @@ usage() ->
|
||||
" zx accept PackageID~n"
|
||||
" zx create realm~n"
|
||||
" zx create realmfile Realm~n"
|
||||
" zx create sysop~n"
|
||||
"~n"
|
||||
"Where~n"
|
||||
" PackageID :: A string of the form Realm-Name[-Version]~n"
|
||||
|
||||
@ -91,26 +91,24 @@ list_resigns(Realm) ->
|
||||
end.
|
||||
|
||||
|
||||
-spec submit(PackageFile) -> no_return()
|
||||
when PackageFile :: file:filename().
|
||||
-spec submit(ZspPath :: file:filename()) -> zx:outcome().
|
||||
%% @private
|
||||
%% Submit a package to the appropriate "prime" server for the given realm.
|
||||
|
||||
submit(PackageFile) ->
|
||||
Files = zx_lib:extract_zsp_or_die(PackageFile),
|
||||
{ok, PackageData} = file:read_file(PackageFile),
|
||||
{"zomp.meta", MetaBin} = lists:keyfind("zomp.meta", 1, Files),
|
||||
Meta = binary_to_term(MetaBin),
|
||||
{Realm, Package, Version} = maps:get(package_id, Meta),
|
||||
{ok, Socket} = connect_auth(Realm),
|
||||
ok = send(Socket, {submit, {Realm, Package, Version}}),
|
||||
submit(ZspPath) ->
|
||||
{ok, ZspBin} = file:read_file(ZspPath),
|
||||
<<SigSize:24, Sig:SigSize/binary, Signed/binary>> = ZspBin,
|
||||
<<MetaSize:16, MetaBin:MetaSize/binary, TarGz/binary>> = Signed,
|
||||
{ok, {PackageID, SigKeyName}} = zx_lib:b_to_ts(MetaBin),
|
||||
{ok, PubKey} = zx_key:load(public, {element(1, PackageID), SigKeyName}),
|
||||
true = zx_key:verify(Signed, Sig, PubKey),
|
||||
ok = send(Socket, {submit, PackageID}),
|
||||
ok = recv_or_die(Socket),
|
||||
ok = gen_tcp:send(Socket, PackageData),
|
||||
ok = gen_tcp:send(Socket, ZspBin),
|
||||
ok = log(info, "Done sending contents of ~tp", [PackageFile]),
|
||||
Outcome = recv_or_die(Socket),
|
||||
log(info, "Response: ~tp", [Outcome]),
|
||||
ok = disconnect(Socket),
|
||||
halt(0).
|
||||
disconnect(Socket).
|
||||
|
||||
|
||||
review(PackageString) ->
|
||||
|
||||
@ -148,6 +148,7 @@
|
||||
subscribe/1, unsubscribe/1,
|
||||
list/0, list/1, list/2, list/3, latest/1,
|
||||
fetch/1, verify_key/1,
|
||||
install/1, import_zsp/1,
|
||||
pending/1, packagers/1, maintainers/1, sysops/1]).
|
||||
-export([report/1, result/2, notify/2]).
|
||||
-export([start_link/0, stop/0]).
|
||||
@ -485,6 +486,27 @@ verify_key({Realm, KeyName}) ->
|
||||
request({verify_key, Realm, KeyName}).
|
||||
|
||||
|
||||
-spec import_zsp(Path :: file:filename()) -> zx:outcome().
|
||||
%% @doc
|
||||
%% Install a package from a local file.
|
||||
|
||||
import_zsp(Path) ->
|
||||
gen_server:call(?MODULE, {import_zsp, Path}).
|
||||
|
||||
|
||||
-spec install(PackageString :: string()) -> zx:outcome().
|
||||
%% @doc
|
||||
%% Install the specified package.
|
||||
|
||||
install(PackageString) ->
|
||||
case zx_lib:package_id(PackageString) of
|
||||
{ok, PackageID} ->
|
||||
gen_server:call(?MODULE, {install, PackageID});
|
||||
{error, invalid_package_string} ->
|
||||
{error, "Invalid package string. Try again.", 22}
|
||||
end.
|
||||
|
||||
|
||||
-spec pending(Package) -> {ok, RequestID}
|
||||
when Package :: zx:package(),
|
||||
RequestID :: id().
|
||||
@ -557,7 +579,7 @@ sysops(Realm) ->
|
||||
%% Private function to wrap the necessary bits up.
|
||||
|
||||
request(Action) ->
|
||||
gen_server:call(?MODULE, {request, self(), Action}).
|
||||
gen_server:call(?MODULE, {request, Action}).
|
||||
|
||||
|
||||
|
||||
@ -689,12 +711,19 @@ stop() ->
|
||||
handle_call({request, list}, _, State = #s{cx = CX}) ->
|
||||
Realms = cx_realms(CX),
|
||||
{reply, {ok, Realms}, State};
|
||||
handle_call({request, Requestor, Action}, From, State = #s{id = ID}) ->
|
||||
handle_call({request, Action}, From, State = #s{id = ID}) ->
|
||||
NewID = ID + 1,
|
||||
_ = gen_server:reply(From, {ok, NewID}),
|
||||
Requestor = element(1, From),
|
||||
NextState = do_request(Requestor, Action, State#s{id = NewID}),
|
||||
NewState = eval_queue(NextState),
|
||||
{noreply, NewState};
|
||||
handle_call({install, PackageID}, _, State) ->
|
||||
{Result, NewState} = do_install(PackageID, State),
|
||||
{reply, Result, NewState};
|
||||
handle_call({import_zsp, Path}, _, State) ->
|
||||
Result = do_import_zsp(Path),
|
||||
{reply, Result, NewState};
|
||||
handle_call(Unexpected, From, State) ->
|
||||
ok = log(warning, "Unexpected call ~tp: ~tp", [From, Unexpected]),
|
||||
{noreply, State}.
|
||||
@ -1208,6 +1237,86 @@ drop_requests(ReqIDs, Dropped, Requests) ->
|
||||
lists:fold(Partition, {Dropped, Requests}, ReqIDs).
|
||||
|
||||
|
||||
-spec install(PackageID, State) -> {Result, NewState}
|
||||
when PackageID :: zx:package_id(),
|
||||
State :: state(),
|
||||
Result :: zx:outcome(),
|
||||
NewState :: state().
|
||||
%% @private
|
||||
%% FIXME: This is about as useful as psuedocode at the moment. Meh.
|
||||
|
||||
install(PackageID, State) ->
|
||||
Path = zx_lib:zxp_path(PackageID),
|
||||
case file:is_regular(Path) of
|
||||
true ->
|
||||
do_install(PackageID, Path);
|
||||
false ->
|
||||
ok = do_fetch(PackageID),
|
||||
do_install(PackageID, Path)
|
||||
end.
|
||||
|
||||
|
||||
-spec do_import_zsp(file:filename()) -> zx:outcome().
|
||||
%% @private
|
||||
%% Dealing with data from the (probably local) filesystem can fail in a bajillion ways
|
||||
%% and spring memory leaks if one tries to get too clever. So I'm sidestepping all the
|
||||
%% madness with a "try++" here: spawning a suicidal helper.
|
||||
|
||||
do_import_zsp(Path) ->
|
||||
{Pid, Mon} = spawn_monitor(fun() -> actually_import(Path) end),
|
||||
receive
|
||||
{Pid, Outcome} ->
|
||||
true = demonitor(Mon, [flush]),
|
||||
Outcome;
|
||||
{'DOWN', Pid, process, Mon, Info} ->
|
||||
{error, Info};
|
||||
after 5000 ->
|
||||
{error, timeout}
|
||||
end.
|
||||
|
||||
|
||||
-spec actually_import(ZspPath) -> no_return()
|
||||
when ZspPath :: file:filename().
|
||||
%% @private
|
||||
%% The happy path of .zsp installation.
|
||||
%% Must NEVER be executed by the zx_daemon directly.
|
||||
|
||||
%% More generally, there are a few phases:
|
||||
%% 1- Loading the binary to extract the PackageID
|
||||
%% 2- Checking the signature
|
||||
%% 3- Moving the file to the cache
|
||||
%% 4- Wiping the destination directory
|
||||
%% 5- Extracting the TarGz to the destination
|
||||
%% Some combination of functions should make these steps happen in a way that isn't
|
||||
%% totally ridiculous, OR the bullet should just be bitten an allow for the
|
||||
%% redundant lines here and there in different package management functions.
|
||||
%%
|
||||
%% Use cases are:
|
||||
%% - Install a missing package from upstream
|
||||
%% - Install a missing package from the local cache
|
||||
%% - Reinstall a package from the local cache
|
||||
%% - Import a package to the cache from the local filesystem and install it
|
||||
%%
|
||||
%% The Correct Approach as determine by The Royal Me is that I'm going to accept the
|
||||
%% redundant code in the short-term because the data format is already decided.
|
||||
%% If a place to get more fancy with the phases becomes really obvious after writing
|
||||
%% identicalish segements of functions a few places then I'll break things apart.
|
||||
|
||||
actually_import(ZspPath) ->
|
||||
{ok, Bin = <<Size:24, Sig:Size/binary, Signed/binary>>} = file:read_file(ZspPath),
|
||||
<<MetaSize:16, MetaBin:MetaSize/binary, TarGz/binary>> = Signed,
|
||||
{ok, {PackageID, SigKeyName}} = zx_lib:b_to_ts(MetaBin),
|
||||
{ok, PubKey} = zx_key:load(public, {element(1, PackageID), SigKeyName}),
|
||||
true = zx_key:verify(Signed, Sig, PubKey),
|
||||
ok = file:write_file(zx_lib:zsp_path(PackageID), Bin),
|
||||
Destination = zx_lib:ppath(lib, PackageID),
|
||||
ok = filelib:ensure_dir(Destination),
|
||||
ok = zx_lib:rm_rf(Destination),
|
||||
ok = file:make_dir(Destination),
|
||||
ok = erl_tar:extract(TarGZ, [{cwd, Destination}]),
|
||||
zx_daemon ! {self(), ok}.
|
||||
|
||||
|
||||
|
||||
%%% Monitor Index ADT Interface Functions
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
|
||||
%%% Functions
|
||||
|
||||
-spec ensure_keypair(zx:key_id()) -> true | no_return().
|
||||
-spec ensure_keypair(zx:key_id()) -> zx:outcome().
|
||||
%% @private
|
||||
%% Check if both the public and private key based on KeyID exists.
|
||||
|
||||
@ -30,17 +30,17 @@ ensure_keypair(KeyID = {Realm, KeyName}) ->
|
||||
{true, true} ->
|
||||
true;
|
||||
{false, true} ->
|
||||
Message = "Public key ~tp/~tp cannot be found",
|
||||
ok = log(error, Message, [Realm, KeyName]),
|
||||
halt(1);
|
||||
Format = "Public key ~tp/~tp cannot be found",
|
||||
Message = io_lib:format(Message, [Realm, KeyName]),
|
||||
{error, Message, 2};
|
||||
{true, false} ->
|
||||
Message = "Private key ~tp/~tp cannot be found",
|
||||
ok = log(error, Message, [Realm, KeyName]),
|
||||
halt(1);
|
||||
Format = "Private key ~tp/~tp cannot be found",
|
||||
Message = io_lib:format(Message, [Realm, KeyName]),
|
||||
{error, Message, 2};
|
||||
{false, false} ->
|
||||
Message = "Key pair ~tp/~tp cannot be found",
|
||||
ok = log(error, Message, [Realm, KeyName]),
|
||||
halt(1)
|
||||
Format = "Key pair ~tp/~tp cannot be found",
|
||||
Message = io_lib:format(Message, [Realm, KeyName]),
|
||||
{error, Message, 2}
|
||||
end.
|
||||
|
||||
|
||||
@ -120,7 +120,8 @@ generate_rsa(KeyID = {Realm, KeyName}) ->
|
||||
PubFile = path(public, KeyID),
|
||||
ok = lists:foreach(fun zx_lib:halt_if_exists/1, [PemFile, KeyFile, PubFile]),
|
||||
ok = log(info, "Generating ~p and ~p. Please be patient...", [KeyFile, PubFile]),
|
||||
ok = gen_p_key(KeyFile),
|
||||
case gen_p_key(KeyFile) of
|
||||
ok ->
|
||||
ok = der_to_pem(KeyFile, PemFile),
|
||||
{ok, PemBin} = file:read_file(PemFile),
|
||||
[PemData] = public_key:pem_decode(PemBin),
|
||||
@ -135,24 +136,35 @@ generate_rsa(KeyID = {Realm, KeyName}) ->
|
||||
ok = lists:foreach(fun file:delete/1, [PemFile, KeyFile, PubFile]),
|
||||
ok = log(error, "Something has gone wrong."),
|
||||
{error, keygen_fail}
|
||||
end;
|
||||
{error, no_ssl} ->
|
||||
ok = log(error, "OpenSSL not found."),
|
||||
{error, keygen_fail}
|
||||
end.
|
||||
|
||||
|
||||
-spec gen_p_key(KeyFile) -> ok
|
||||
when KeyFile :: file:filename().
|
||||
-spec gen_p_key(KeyFile) -> Result
|
||||
when KeyFile :: file:filename()
|
||||
Result :: ok
|
||||
| {error, no_ssl}.
|
||||
%% @private
|
||||
%% Format an openssl shell command that will generate proper 16k RSA keys.
|
||||
|
||||
gen_p_key(KeyFile) ->
|
||||
case openssl() of
|
||||
{ok, OpenSSL} ->
|
||||
Command =
|
||||
io_lib:format("~ts genpkey"
|
||||
" -algorithm rsa"
|
||||
" -out ~ts"
|
||||
" -outform DER"
|
||||
" -pkeyopt rsa_keygen_bits:16384",
|
||||
[openssl(), KeyFile]),
|
||||
[OpenSSL, KeyFile]),
|
||||
Out = os:cmd(Command),
|
||||
io:format(Out).
|
||||
io:format(Out);
|
||||
Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
|
||||
-spec der_to_pem(KeyFile, PemFile) -> ok
|
||||
@ -196,11 +208,14 @@ check_key(KeyFile, PubFile) ->
|
||||
public_key:verify(TestMessage, sha512, Signature, Pub).
|
||||
|
||||
|
||||
-spec openssl() -> Executable | no_return()
|
||||
when Executable :: file:filename().
|
||||
-spec openssl() -> Result
|
||||
when Result :: {ok, Executable}
|
||||
| {error, no_ssl},
|
||||
Executable :: file:filename().
|
||||
%% @private
|
||||
%% Attempt to locate the installed openssl executable for use in shell commands.
|
||||
%% Halts execution with an error message if the executable cannot be found.
|
||||
%% TODO: Determine whether it is even worth it to perform this check VS restricting
|
||||
%% os:cmd/1 directed zx_key functions by platform.
|
||||
|
||||
openssl() ->
|
||||
OpenSSL =
|
||||
@ -208,16 +223,15 @@ openssl() ->
|
||||
{unix, _} -> "openssl";
|
||||
{win32, _} -> "openssl.exe"
|
||||
end,
|
||||
ok =
|
||||
case os:find_executable(OpenSSL) of
|
||||
false ->
|
||||
ok = log(error, "OpenSSL could not be found in this system's PATH."),
|
||||
ok = log(error, "Install OpenSSL and then retry."),
|
||||
error_exit("Missing system dependenct: OpenSSL", ?LINE);
|
||||
{error, no_ssl};
|
||||
Path ->
|
||||
log(info, "OpenSSL executable found at: ~ts", [Path])
|
||||
end,
|
||||
OpenSSL.
|
||||
log(info, "OpenSSL executable found at: ~ts", [Path]),
|
||||
OpenSSL
|
||||
end.
|
||||
|
||||
|
||||
-spec load(Type, KeyID) -> Result
|
||||
@ -242,20 +256,16 @@ load(Type, KeyID) ->
|
||||
end.
|
||||
|
||||
|
||||
-spec verify(Data, Signature, PubKey) -> ok | no_return()
|
||||
-spec verify(Data, Signature, PubKey) -> boolean()
|
||||
when Data :: binary(),
|
||||
Signature :: binary(),
|
||||
PubKey :: public_key:rsa_public_key().
|
||||
%% @private
|
||||
%% Verify the RSA Signature of some Data against the given PubKey or halt execution.
|
||||
%% This function always assumes sha512 is the algorithm being used.
|
||||
%% Should only ever be called by the initial launch process.
|
||||
%% Curry out the choice of algorithm. This will probably disappear in a few more
|
||||
%% versions as the details of sha512 and RSA gradually give way to the Brave New World.
|
||||
|
||||
verify(Data, Signature, PubKey) ->
|
||||
case public_key:verify(Data, sha512, Signature, PubKey) of
|
||||
true -> ok;
|
||||
false -> error_exit("Bad package signature!", ?LINE)
|
||||
end.
|
||||
public_key:verify(Data, sha512, Signature, PubKey).
|
||||
|
||||
|
||||
|
||||
|
||||
@ -25,11 +25,9 @@
|
||||
valid_lower0_9/1, valid_label/1, valid_version/1,
|
||||
string_to_version/1, version_to_string/1,
|
||||
package_id/1, package_string/1,
|
||||
namify_zsp/1, namify_tgz/1,
|
||||
zsp_path/1,
|
||||
namify_zsp/1, zsp_path/1,
|
||||
find_latest_compatible/2, installed/1,
|
||||
realm_conf/1, load_realm_conf/1,
|
||||
extract_zsp_or_die/1, halt_if_exists/1,
|
||||
build/0,
|
||||
rm_rf/1, rm/1,
|
||||
b_to_t/1, b_to_ts/1]).
|
||||
@ -582,37 +580,17 @@ package_string(_) ->
|
||||
when PackageID :: zx:package_id(),
|
||||
ZrpFileName :: file:filename().
|
||||
%% @private
|
||||
%% Map an PackageID to its correct .zsp package file name.
|
||||
%% Map a PackageID to its correct .zsp package file name.
|
||||
|
||||
namify_zsp(PackageID) -> namify(PackageID, "zsp").
|
||||
|
||||
|
||||
-spec namify_tgz(PackageID) -> TgzFileName
|
||||
when PackageID :: zx:package_id(),
|
||||
TgzFileName :: file:filename().
|
||||
%% @private
|
||||
%% Map an PackageID to its correct gzipped tarball source bundle filename.
|
||||
|
||||
namify_tgz(PackageID) -> namify(PackageID, "tgz").
|
||||
|
||||
|
||||
-spec namify(PackageID, Suffix) -> FileName
|
||||
when PackageID :: zx:package_id(),
|
||||
Suffix :: string(),
|
||||
FileName :: file:filename().
|
||||
%% @private
|
||||
%% Converts an PackageID to a canonical string, then appends the provided
|
||||
%% filename Suffix.
|
||||
|
||||
namify(PackageID, Suffix) ->
|
||||
namify_zsp(PackageID) ->
|
||||
{ok, PackageString} = package_string(PackageID),
|
||||
PackageString ++ "." ++ Suffix.
|
||||
PackageString ++ ".zsp".
|
||||
|
||||
|
||||
-spec zsp_path(zx:package_id()) -> file:filename().
|
||||
|
||||
zsp_path(PackageID) ->
|
||||
filename:join(path(zsp, element(1, PackageID)), namify_zsp(PackageID)).
|
||||
zsp_path({Realm, _, _}) ->
|
||||
filename:join(path(zsp, Realm), namify_zsp(PackageID)).
|
||||
|
||||
|
||||
-spec find_latest_compatible(Version, Versions) -> Result
|
||||
@ -683,7 +661,7 @@ realm_conf(Realm) ->
|
||||
| file:posix()
|
||||
| {Line :: integer(), Mod :: module(), Cause :: term()}.
|
||||
%% @private
|
||||
%% Load the config for the given realm or halt with an error.
|
||||
%% Load the config for the given realm.
|
||||
|
||||
load_realm_conf(Realm) ->
|
||||
Path = realm_conf(Realm),
|
||||
@ -696,41 +674,6 @@ load_realm_conf(Realm) ->
|
||||
end.
|
||||
|
||||
|
||||
-spec extract_zsp_or_die(FileName) -> Files | no_return()
|
||||
when FileName :: file:filename(),
|
||||
Files :: [{file:filename(), binary()}].
|
||||
%% @private
|
||||
%% Extract a zsp archive, if possible. If not possible, halt execution with as accurate
|
||||
%% an error message as can be managed.
|
||||
|
||||
extract_zsp_or_die(FileName) ->
|
||||
case erl_tar:extract(FileName, [memory]) of
|
||||
{ok, Files} ->
|
||||
Files;
|
||||
{error, {FileName, enoent}} ->
|
||||
Message = "Can't find file ~ts.",
|
||||
error_exit(Message, [FileName], ?LINE);
|
||||
{error, invalid_tar_checksum} ->
|
||||
Message = "~ts is not a valid zsp archive.",
|
||||
error_exit(Message, [FileName], ?LINE);
|
||||
{error, Reason} ->
|
||||
Message = "Extracting package file failed with: ~160tp.",
|
||||
error_exit(Message, [Reason], ?LINE)
|
||||
end.
|
||||
|
||||
|
||||
-spec halt_if_exists(file:filename()) -> ok | no_return().
|
||||
%% @private
|
||||
%% A helper function to guard against overwriting an existing file. Halts execution if
|
||||
%% the file is found to exist.
|
||||
|
||||
halt_if_exists(Path) ->
|
||||
case filelib:is_file(Path) of
|
||||
true -> error_exit("~ts already exists! Halting.", [Path], ?LINE);
|
||||
false -> ok
|
||||
end.
|
||||
|
||||
|
||||
-spec build() -> ok.
|
||||
%% @private
|
||||
%% Run any local `zxmake' script needed by the project for non-Erlang code (if present),
|
||||
@ -755,6 +698,7 @@ build() ->
|
||||
-spec rm_rf(file:filename()) -> ok | {error, file:posix()}.
|
||||
%% @private
|
||||
%% Recursively remove files and directories. Equivalent to `rm -rf'.
|
||||
%% Does not return an error on a nonexistant path.
|
||||
|
||||
rm_rf(Path) ->
|
||||
case filelib:is_dir(Path) of
|
||||
@ -764,7 +708,10 @@ rm_rf(Path) ->
|
||||
ok = lists:foreach(fun rm/1, Contents),
|
||||
file:del_dir(Path);
|
||||
false ->
|
||||
file:delete(Path)
|
||||
case filelib:is_regular(Path) of
|
||||
true -> file:delete(Path);
|
||||
false -> ok
|
||||
end
|
||||
end.
|
||||
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
-copyright("Craig Everett <zxq9@zxq9.com>").
|
||||
-license("GPL-3.0").
|
||||
|
||||
-export([initialize/2, assimilate/1, set_version/1,
|
||||
-export([initialize/2, set_version/1,
|
||||
list_realms/0, list_packages/1, list_versions/1,
|
||||
set_dep/1, list_deps/0, list_deps/1, drop_dep/1, verup/1, package/1,
|
||||
add_realm/1, drop_realm/1,
|
||||
@ -268,39 +268,6 @@ initialize_app_file({_, Name, Version}, AppStart) ->
|
||||
zx_lib:write_terms(AppFile, [AppProfile]).
|
||||
|
||||
|
||||
-spec assimilate(PackageFile) -> zx:outcome()
|
||||
when PackageFile :: file:filename().
|
||||
%% @private
|
||||
%% Receives a path to a file containing package data, examines it, and copies it to a
|
||||
%% canonical location under a canonical name.
|
||||
|
||||
assimilate(PackageFile) ->
|
||||
Files = zx_lib:extract_zsp_or_die(PackageFile),
|
||||
{ok, CWD} = file:get_cwd(),
|
||||
ok = file:set_cwd(zx_lib:zomp_dir()),
|
||||
{"zomp.meta", MetaBin} = lists:keyfind("zomp.meta", 1, Files),
|
||||
Meta = binary_to_term(MetaBin),
|
||||
PackageID = maps:get(package_id, Meta),
|
||||
TgzFile = zx_lib:namify_tgz(PackageID),
|
||||
{TgzFile, TgzData} = lists:keyfind(TgzFile, 1, Files),
|
||||
{KeyID, Signature} = maps:get(sig, Meta),
|
||||
{ok, PubKey} = zx_key:load(public, KeyID),
|
||||
case public_key:verify(TgzData, sha512, Signature, PubKey) of
|
||||
true ->
|
||||
ok = file:copy(PackageFile, zx_lib:zsp_path(PackageID)),
|
||||
assimilate2(CWD, PackageID);
|
||||
false ->
|
||||
{error, "Bad package signature.", 1}
|
||||
end.
|
||||
|
||||
|
||||
assimilate2(CWD, PackageID) ->
|
||||
ok = file:set_cwd(CWD),
|
||||
Message = "~ts is now locally available.",
|
||||
{ok, PackageString} = zx_lib:package_string(PackageID),
|
||||
log(info, Message, [PackageString]).
|
||||
|
||||
|
||||
-spec set_version(VersionString) -> zx:outcome()
|
||||
when VersionString :: string().
|
||||
%% @private
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user