2016-09-08 21:13:10 +04:00
|
|
|
(**************************************************************************)
|
|
|
|
(* *)
|
|
|
|
(* Copyright (c) 2014 - 2016. *)
|
|
|
|
(* Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
|
|
|
|
(* *)
|
|
|
|
(* All rights reserved. No warranty, explicit or implicit, provided. *)
|
|
|
|
(* *)
|
|
|
|
(**************************************************************************)
|
|
|
|
|
|
|
|
(* Tezos Command line interface - Configuration and Arguments Parsing *)
|
|
|
|
|
2017-03-15 04:17:20 +04:00
|
|
|
open Client_commands
|
|
|
|
|
|
|
|
module Cfg_file = struct
|
|
|
|
open Data_encoding
|
|
|
|
|
|
|
|
let encoding =
|
|
|
|
conv
|
|
|
|
(fun { incoming_addr ; incoming_port ; tls ; web_port } ->
|
|
|
|
(Some incoming_addr, Some incoming_port, Some tls, Some web_port))
|
|
|
|
(fun (incoming_addr, incoming_port, tls, web_port) ->
|
|
|
|
let open Utils in
|
|
|
|
let incoming_addr = unopt ~default:default_cfg.incoming_addr incoming_addr in
|
|
|
|
let incoming_port = unopt ~default:default_cfg.incoming_port incoming_port in
|
|
|
|
let tls = unopt ~default:default_cfg.tls tls in
|
|
|
|
let web_port = unopt ~default:default_cfg.web_port web_port in
|
|
|
|
{ default_cfg with
|
|
|
|
incoming_addr ; incoming_port ; tls ; web_port })
|
|
|
|
(obj4
|
|
|
|
(opt "incoming_addr" string)
|
|
|
|
(opt "incoming_port" int16)
|
|
|
|
(opt "tls" bool)
|
|
|
|
(opt "web_port" int16))
|
|
|
|
|
|
|
|
let from_json json =
|
|
|
|
Data_encoding.Json.destruct encoding json
|
|
|
|
|
|
|
|
let read fp =
|
|
|
|
Data_encoding_ezjsonm.read_file fp >>=? fun json ->
|
|
|
|
return (from_json json)
|
|
|
|
|
|
|
|
let write out cfg =
|
|
|
|
Utils.write_file ~bin:false out
|
|
|
|
(Data_encoding.Json.construct encoding cfg |>
|
|
|
|
Data_encoding_ezjsonm.to_string)
|
|
|
|
end
|
2016-09-08 21:13:10 +04:00
|
|
|
|
2017-03-15 04:17:20 +04:00
|
|
|
exception Found of string
|
|
|
|
let preparse name argv =
|
2016-09-08 21:13:10 +04:00
|
|
|
try
|
2017-03-15 04:17:20 +04:00
|
|
|
for i = 0 to Array.length argv - 1 do
|
|
|
|
if argv.(i) = name && i <> Array.length argv - 1 then
|
|
|
|
raise (Found argv.(i+1))
|
|
|
|
done ;
|
|
|
|
None
|
|
|
|
with Found s -> Some s
|
2016-09-08 21:13:10 +04:00
|
|
|
|
|
|
|
(* Entry point *)
|
|
|
|
|
2017-03-15 04:17:20 +04:00
|
|
|
let parse_args ?(extra = (fun _cfg -> [])) usage dispatcher argv cctxt =
|
2016-09-08 21:13:10 +04:00
|
|
|
let open Lwt in
|
2017-03-15 04:17:20 +04:00
|
|
|
(* Init config reference which will be updated as args are parsed *)
|
|
|
|
let cfg = ref cctxt.Client_commands.config in
|
|
|
|
let set_block x =
|
|
|
|
match Node_rpc_services.Blocks.parse_block x with
|
|
|
|
| Error _ -> raise (Arg.Bad "Can't parse -block")
|
|
|
|
| Ok block -> cfg := { !cfg with block }
|
|
|
|
in
|
|
|
|
(* Command-line only args (not in config file) *)
|
|
|
|
let cli_args = [
|
|
|
|
"-base-dir", Arg.String (fun x -> cfg := { !cfg with base_dir = x }),
|
|
|
|
"The directory where the Tezos client will store all its data.\n\
|
|
|
|
default: " ^ Client_commands.(default_cfg.base_dir);
|
|
|
|
"-config-file", Arg.String (fun x -> cfg := { !cfg with config_file = x }),
|
|
|
|
"The main configuration file.\n\
|
|
|
|
default: " ^ Client_commands.(default_cfg.config_file);
|
|
|
|
"-timings", Arg.Bool (fun x -> cfg := { !cfg with print_timings = x }),
|
|
|
|
"Show RPC request times.\n\
|
|
|
|
default: " ^ string_of_bool Client_commands.(default_cfg.print_timings);
|
|
|
|
"-force", Arg.Bool (fun x -> cfg := { !cfg with force = x }),
|
|
|
|
"Show less courtesy than the average user.\n\
|
|
|
|
default: " ^ string_of_bool Client_commands.(default_cfg.force);
|
|
|
|
"-block", Arg.String set_block,
|
|
|
|
"The block on which to apply contextual commands.\n\
|
|
|
|
default: " ^ Node_rpc_services.Blocks.to_string Client_commands.(default_cfg.block);
|
|
|
|
] in
|
|
|
|
(* Command-line args which can be set in config file as well *)
|
|
|
|
let file_args = [
|
|
|
|
(* Network options *)
|
|
|
|
"-addr", Arg.String (fun x -> cfg := { !cfg with incoming_addr = x }),
|
|
|
|
"The IP address at which the node's RPC server can be reached.\n\
|
|
|
|
default: " ^ Client_commands.(default_cfg.incoming_addr);
|
|
|
|
"-port", Arg.Int (fun x -> cfg := { !cfg with incoming_port = x }),
|
|
|
|
"The TCP port at which the node's RPC server can be reached.\n\
|
|
|
|
default: " ^ string_of_int Client_commands.(default_cfg.incoming_port);
|
|
|
|
"-tls", Arg.Bool (fun x -> cfg := { !cfg with tls = x }),
|
|
|
|
"Use TLS to connect to node.\n\
|
|
|
|
default: " ^ string_of_bool Client_commands.(default_cfg.tls);
|
|
|
|
] in
|
|
|
|
let all_args = cli_args @ file_args @ (extra cfg) in
|
2016-12-02 02:20:23 +04:00
|
|
|
catch
|
|
|
|
(fun () ->
|
2017-03-15 04:17:20 +04:00
|
|
|
let args = ref all_args in
|
2016-12-02 02:20:23 +04:00
|
|
|
let anon dispatch n = match dispatch (`Arg n) with
|
|
|
|
| `Nop -> ()
|
|
|
|
| `Args nargs -> args := nargs @ !args
|
|
|
|
| `Fail exn -> raise exn
|
|
|
|
| `Res _ -> assert false in
|
|
|
|
Arg.parse_argv_dynamic
|
|
|
|
~current:(ref 0) argv args (anon (dispatcher ())) "\000" ;
|
|
|
|
let dispatch = dispatcher () in
|
2017-03-15 04:17:20 +04:00
|
|
|
(if Sys.file_exists !cfg.config_file then begin
|
|
|
|
try
|
|
|
|
(* Parse config file and init [cfg] with options defined in it *)
|
|
|
|
let config_file = !cfg.config_file in
|
|
|
|
Cfg_file.read config_file >>= begin function
|
|
|
|
| Error _err ->
|
|
|
|
cctxt.Client_commands.error
|
|
|
|
"Error: can't parse the configuration file: %s\n%!"
|
|
|
|
config_file
|
|
|
|
| Ok c ->
|
|
|
|
cfg := c ;
|
|
|
|
Lwt.return ()
|
|
|
|
end >>= fun () ->
|
|
|
|
(* parse once again to overwrite file options by cli ones *)
|
|
|
|
Arg.parse_argv_dynamic
|
|
|
|
~current:(ref 0) argv args (anon dispatch) "\000" ;
|
|
|
|
Lwt.return ()
|
|
|
|
with Sys_error msg ->
|
|
|
|
cctxt.Client_commands.error
|
|
|
|
"Error: can't read the configuration file: %s\n%!" msg
|
|
|
|
end else begin
|
2016-12-02 02:20:23 +04:00
|
|
|
try
|
|
|
|
(* parse once again with contextual options *)
|
|
|
|
Arg.parse_argv_dynamic
|
|
|
|
~current:(ref 0) argv args (anon dispatch) "\000" ;
|
2017-03-15 04:17:20 +04:00
|
|
|
Lwt_utils.create_dir (Filename.dirname !cfg.config_file)
|
|
|
|
>>= fun () ->
|
|
|
|
Cfg_file.write !cfg.config_file !cfg ;
|
2016-12-02 02:20:23 +04:00
|
|
|
Lwt.return ()
|
|
|
|
with Sys_error msg ->
|
2016-12-03 16:05:02 +04:00
|
|
|
cctxt.Client_commands.warning
|
2017-03-15 04:17:20 +04:00
|
|
|
"Warning: can't create the default configuration file: %s\n%!"
|
|
|
|
msg
|
2016-12-02 02:20:23 +04:00
|
|
|
end) >>= fun () ->
|
2017-03-15 04:17:20 +04:00
|
|
|
begin match dispatch `End with
|
|
|
|
| `Res res -> Lwt.return (res, !cfg)
|
|
|
|
| `Fail exn -> fail exn
|
|
|
|
| `Nop | `Args _ -> assert false
|
|
|
|
end)
|
2016-12-02 02:20:23 +04:00
|
|
|
(function
|
|
|
|
| Arg.Bad msg ->
|
|
|
|
(* FIXME: this is an ugly hack to circumvent [Arg]
|
|
|
|
spuriously printing options at the end of the error
|
|
|
|
message. *)
|
|
|
|
let msg = List.hd (Utils.split '\000' msg) in
|
2017-03-15 04:17:20 +04:00
|
|
|
Lwt.fail (Arg.Help (msg ^ usage all_args ^ "\n"))
|
2016-12-02 02:20:23 +04:00
|
|
|
| Arg.Help _ ->
|
2017-03-15 04:17:20 +04:00
|
|
|
Lwt.fail (Arg.Help (usage all_args ^ "\n"))
|
2016-12-02 02:20:23 +04:00
|
|
|
| exn -> Lwt.fail exn)
|
2016-09-08 21:13:10 +04:00
|
|
|
|
2017-03-15 04:17:20 +04:00
|
|
|
let preparse_args argv cctxt : cfg Lwt.t =
|
|
|
|
let cfg =
|
2016-12-02 02:20:23 +04:00
|
|
|
match preparse "-base-dir" argv with
|
2017-03-15 04:17:20 +04:00
|
|
|
| None -> default_cfg
|
|
|
|
| Some base_dir -> default_cfg_of_base_dir base_dir
|
|
|
|
in
|
|
|
|
let cfg =
|
2016-12-02 02:20:23 +04:00
|
|
|
match preparse "-config-file" argv with
|
2017-03-15 04:17:20 +04:00
|
|
|
| None -> cfg
|
|
|
|
| Some config_file -> { cfg with config_file }
|
|
|
|
in
|
|
|
|
let no_config () =
|
|
|
|
cctxt.Client_commands.warning
|
|
|
|
"Warning: config file not found\n%!" in
|
|
|
|
let corrupted_config msg =
|
|
|
|
cctxt.Client_commands.error
|
|
|
|
"Error: can't parse the configuration file: %s\n%s\n%!"
|
|
|
|
cfg.config_file msg in
|
2016-09-08 21:13:10 +04:00
|
|
|
begin
|
2017-03-15 04:17:20 +04:00
|
|
|
if Sys.file_exists cfg.config_file then try
|
|
|
|
match
|
|
|
|
Utils.read_file ~bin:false cfg.config_file
|
|
|
|
|> Data_encoding_ezjsonm.from_string
|
|
|
|
with
|
|
|
|
| exception _ ->
|
|
|
|
no_config () >>= fun () ->
|
|
|
|
Lwt.return cfg
|
|
|
|
| Error msg -> corrupted_config msg
|
|
|
|
| Ok cfg_json ->
|
|
|
|
try Lwt.return (Cfg_file.from_json cfg_json) with
|
|
|
|
| Invalid_argument msg
|
|
|
|
| Failure msg -> corrupted_config msg
|
2016-09-08 21:13:10 +04:00
|
|
|
with Sys_error msg ->
|
2016-12-03 16:05:02 +04:00
|
|
|
cctxt.Client_commands.error
|
2016-11-22 20:28:25 +04:00
|
|
|
"Error: can't read the configuration file: %s\n%!" msg
|
2017-03-15 04:17:20 +04:00
|
|
|
else Lwt.return cfg
|
|
|
|
end >>= fun cfg ->
|
|
|
|
let cfg =
|
2016-12-02 01:42:28 +04:00
|
|
|
match preparse "-tls" argv with
|
2017-03-15 04:17:20 +04:00
|
|
|
| None -> cfg
|
|
|
|
| Some _ -> { cfg with tls = true }
|
|
|
|
in
|
|
|
|
let cfg =
|
2016-12-02 02:20:23 +04:00
|
|
|
match preparse "-addr" argv with
|
2017-03-15 04:17:20 +04:00
|
|
|
| None -> cfg
|
|
|
|
| Some incoming_addr -> { cfg with incoming_addr }
|
|
|
|
in
|
2016-09-08 21:13:10 +04:00
|
|
|
begin
|
2016-12-02 02:20:23 +04:00
|
|
|
match preparse "-port" argv with
|
2017-03-15 04:17:20 +04:00
|
|
|
| None -> Lwt.return cfg
|
2016-09-08 21:13:10 +04:00
|
|
|
| Some port ->
|
2016-11-22 20:28:25 +04:00
|
|
|
try
|
2017-03-15 04:17:20 +04:00
|
|
|
let incoming_port = int_of_string port in
|
|
|
|
Lwt.return { cfg with incoming_port }
|
2016-09-08 21:13:10 +04:00
|
|
|
with _ ->
|
2016-12-03 16:05:02 +04:00
|
|
|
cctxt.Client_commands.error
|
2016-11-22 20:28:25 +04:00
|
|
|
"Error: can't parse the -port option: %S.\n%!" port
|
2017-03-15 04:17:20 +04:00
|
|
|
end >>= fun cfg ->
|
2016-09-08 21:13:10 +04:00
|
|
|
match preparse "-block" Sys.argv with
|
2017-03-15 04:17:20 +04:00
|
|
|
| None -> Lwt.return cfg
|
2016-09-08 21:13:10 +04:00
|
|
|
| Some x ->
|
|
|
|
match Node_rpc_services.Blocks.parse_block x with
|
|
|
|
| Error _ ->
|
2016-12-03 16:05:02 +04:00
|
|
|
cctxt.Client_commands.error
|
2016-11-22 20:28:25 +04:00
|
|
|
"Error: can't parse the -block option: %S.\n%!" x
|
2017-03-15 04:17:20 +04:00
|
|
|
| Ok block -> Lwt.return { cfg with block }
|