2016-09-08 21:13:10 +04:00
|
|
|
(**************************************************************************)
|
|
|
|
(* *)
|
2017-11-14 03:36:14 +04:00
|
|
|
(* Copyright (c) 2014 - 2017. *)
|
2016-09-08 21:13:10 +04:00
|
|
|
(* 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-09-19 13:31:35 +04:00
|
|
|
type error += Invalid_block_argument of string
|
|
|
|
type error += Invalid_port_arg of string
|
|
|
|
let () =
|
|
|
|
register_error_kind
|
|
|
|
`Branch
|
|
|
|
~id: "badBlocksArgument"
|
|
|
|
~title: "Bad Blocks Argument"
|
|
|
|
~description: "Blocks argument could not be parsed"
|
|
|
|
~pp:
|
|
|
|
(fun ppf s ->
|
|
|
|
Format.fprintf ppf "Value provided for -block flag (%s) could not be parsed" s)
|
|
|
|
Data_encoding.(obj1 (req "value" string))
|
|
|
|
(function Invalid_block_argument s -> Some s | _ -> None)
|
|
|
|
(fun s -> Invalid_block_argument s) ;
|
|
|
|
register_error_kind
|
|
|
|
`Branch
|
|
|
|
~id: "invalidPortArgument"
|
|
|
|
~title: "Bad Port Argument"
|
|
|
|
~description: "Port argument could not be parsed"
|
|
|
|
~pp:
|
|
|
|
(fun ppf s ->
|
|
|
|
Format.fprintf ppf "Value provided for -port flag (%s) could not be parsed" s)
|
|
|
|
Data_encoding.(obj1 (req "value" string))
|
|
|
|
(function Invalid_port_arg s -> Some s | _ -> None)
|
|
|
|
(fun s -> Invalid_port_arg s)
|
|
|
|
|
|
|
|
|
2017-03-31 02:42:13 +04:00
|
|
|
let (//) = Filename.concat
|
2017-03-15 04:17:20 +04:00
|
|
|
|
|
|
|
module Cfg_file = struct
|
2017-03-31 02:42:13 +04:00
|
|
|
|
|
|
|
type t = {
|
|
|
|
base_dir: string ;
|
|
|
|
node_addr: string ;
|
|
|
|
node_port: int ;
|
|
|
|
tls: bool ;
|
|
|
|
web_port: int ;
|
|
|
|
}
|
|
|
|
|
|
|
|
let default = {
|
|
|
|
base_dir = Client_commands.default_base_dir ;
|
|
|
|
node_addr = "127.0.0.1" ;
|
|
|
|
node_port = 8732 ;
|
|
|
|
tls = false ;
|
|
|
|
web_port = 8080 ;
|
|
|
|
}
|
|
|
|
|
2017-03-15 04:17:20 +04:00
|
|
|
open Data_encoding
|
|
|
|
|
|
|
|
let encoding =
|
|
|
|
conv
|
2017-03-31 02:42:13 +04:00
|
|
|
(fun { base_dir ; node_addr ; node_port ; tls ; web_port } ->
|
|
|
|
(base_dir, Some node_addr, Some node_port,
|
|
|
|
Some tls, Some web_port))
|
|
|
|
(fun (base_dir, node_addr, node_port, tls, web_port) ->
|
2017-09-19 13:31:35 +04:00
|
|
|
let open Utils in
|
|
|
|
let node_addr = unopt ~default:default.node_addr node_addr in
|
|
|
|
let node_port = unopt ~default:default.node_port node_port in
|
|
|
|
let tls = unopt ~default:default.tls tls in
|
|
|
|
let web_port = unopt ~default:default.web_port web_port in
|
|
|
|
{ base_dir ; node_addr ; node_port ; tls ; web_port })
|
2017-03-31 02:42:13 +04:00
|
|
|
(obj5
|
2017-09-19 13:31:35 +04:00
|
|
|
(req "base_dir" string)
|
|
|
|
(opt "node_addr" string)
|
|
|
|
(opt "node_port" int16)
|
|
|
|
(opt "tls" bool)
|
|
|
|
(opt "web_port" int16))
|
2017-03-15 04:17:20 +04:00
|
|
|
|
|
|
|
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)
|
2017-03-31 02:42:13 +04:00
|
|
|
|
2017-03-15 04:17:20 +04:00
|
|
|
end
|
2016-09-08 21:13:10 +04:00
|
|
|
|
2017-09-19 13:31:35 +04:00
|
|
|
type cli_args = {
|
|
|
|
block: Node_rpc_services.Blocks.block ;
|
|
|
|
print_timings: bool ;
|
|
|
|
log_requests: bool ;
|
|
|
|
force: bool ;
|
|
|
|
}
|
|
|
|
|
|
|
|
let default_cli_args = {
|
|
|
|
block = Client_commands.default_cfg.block ;
|
|
|
|
print_timings = false ;
|
|
|
|
log_requests = false ;
|
|
|
|
force = false ;
|
|
|
|
}
|
|
|
|
|
|
|
|
open Cli_entries
|
|
|
|
|
2017-09-27 11:55:20 +04:00
|
|
|
let string_parameter : (string, Client_commands.context) parameter =
|
|
|
|
parameter (fun _ x -> return x)
|
|
|
|
|
|
|
|
let block_parameter =
|
|
|
|
parameter
|
|
|
|
(fun _ block -> match Node_rpc_services.Blocks.parse_block block with
|
|
|
|
| Error _ ->
|
|
|
|
fail (Invalid_block_argument block)
|
|
|
|
| Ok block -> return block)
|
|
|
|
|
2017-09-19 13:31:35 +04:00
|
|
|
(* Command-line only args (not in config file) *)
|
|
|
|
let base_dir_arg =
|
|
|
|
default_arg
|
|
|
|
~parameter:"-base-dir"
|
|
|
|
~doc:"The directory where the Tezos client will store all its data."
|
|
|
|
~default:Client_commands.default_base_dir
|
2017-09-27 11:55:20 +04:00
|
|
|
string_parameter
|
2017-09-19 13:31:35 +04:00
|
|
|
let config_file_arg =
|
|
|
|
arg
|
|
|
|
~parameter:"-config-file"
|
|
|
|
~doc:"The main configuration file."
|
2017-09-27 11:55:20 +04:00
|
|
|
string_parameter
|
2017-09-19 13:31:35 +04:00
|
|
|
let timings_switch =
|
|
|
|
switch
|
|
|
|
~parameter:"-timings"
|
|
|
|
~doc:"Show RPC request times if present."
|
|
|
|
let force_switch =
|
|
|
|
switch
|
|
|
|
~parameter:"-force"
|
|
|
|
~doc:"Show less courtesy than the average user."
|
|
|
|
let block_arg =
|
|
|
|
default_arg
|
|
|
|
~parameter:"-block"
|
|
|
|
~doc:"The block on which to apply contextual commands."
|
|
|
|
~default:(Node_rpc_services.Blocks.to_string default_cli_args.block)
|
2017-09-27 11:55:20 +04:00
|
|
|
block_parameter
|
2017-09-19 13:31:35 +04:00
|
|
|
let log_requests_switch =
|
|
|
|
switch
|
|
|
|
~parameter:"-log-requests"
|
|
|
|
~doc:"Causes all requests and responses to the node to be logged."
|
|
|
|
|
|
|
|
(* Command-line args which can be set in config file as well *)
|
|
|
|
let addr_arg =
|
|
|
|
default_arg
|
|
|
|
~parameter:"-addr"
|
|
|
|
~doc:"The IP address of the node."
|
|
|
|
~default:Cfg_file.default.node_addr
|
2017-09-27 11:55:20 +04:00
|
|
|
string_parameter
|
2017-09-19 13:31:35 +04:00
|
|
|
let port_arg =
|
|
|
|
default_arg
|
|
|
|
~parameter:"-port"
|
|
|
|
~doc:"The RPC port of the node."
|
|
|
|
~default:(string_of_int Cfg_file.default.node_port)
|
2017-09-27 11:55:20 +04:00
|
|
|
(parameter
|
|
|
|
(fun _ x -> try
|
2017-11-13 19:34:00 +04:00
|
|
|
return (int_of_string x)
|
|
|
|
with Failure _ ->
|
|
|
|
fail (Invalid_port_arg x)))
|
2017-09-19 13:31:35 +04:00
|
|
|
let tls_switch =
|
|
|
|
switch
|
|
|
|
~parameter:"-tls"
|
|
|
|
~doc:"Use TLS to connect to node."
|
|
|
|
|
|
|
|
let global_options =
|
|
|
|
args9 base_dir_arg
|
|
|
|
config_file_arg
|
|
|
|
force_switch
|
|
|
|
timings_switch
|
|
|
|
block_arg
|
|
|
|
log_requests_switch
|
|
|
|
addr_arg
|
|
|
|
port_arg
|
|
|
|
tls_switch
|
|
|
|
|
2017-09-27 11:55:20 +04:00
|
|
|
let parse_config_args (ctx : Client_commands.context) argv =
|
2017-09-19 13:31:35 +04:00
|
|
|
parse_initial_options
|
|
|
|
global_options
|
|
|
|
ctx
|
|
|
|
argv >>|?
|
|
|
|
fun ((base_dir,
|
|
|
|
config_file,
|
|
|
|
force,
|
|
|
|
timings,
|
|
|
|
block,
|
|
|
|
log_requests,
|
|
|
|
node_addr,
|
|
|
|
node_port,
|
|
|
|
tls), remaining) ->
|
2017-03-31 02:42:13 +04:00
|
|
|
let config_file =
|
2017-09-19 13:31:35 +04:00
|
|
|
match config_file with
|
2017-03-31 02:42:13 +04:00
|
|
|
| None -> base_dir // "config"
|
|
|
|
| Some config_file -> config_file in
|
|
|
|
let config_dir = Filename.dirname config_file in
|
|
|
|
let cfg =
|
|
|
|
if not (Sys.file_exists config_file) then
|
|
|
|
{ Cfg_file.default with base_dir = base_dir }
|
|
|
|
else
|
|
|
|
match
|
|
|
|
Utils.read_file ~bin:false config_file
|
|
|
|
|> Data_encoding_ezjsonm.from_string
|
|
|
|
with
|
|
|
|
| exception (Sys_error msg) ->
|
|
|
|
Format.eprintf
|
|
|
|
"Error: can't read the configuration file: %s\n%s@."
|
|
|
|
config_file msg ;
|
|
|
|
exit 1
|
|
|
|
| exception _ ->
|
|
|
|
Format.eprintf "Warning: config file not found@." ;
|
|
|
|
{ Cfg_file.default with base_dir = base_dir }
|
|
|
|
| Error msg ->
|
|
|
|
Format.eprintf
|
|
|
|
"Error: can't parse the configuration file: %s\n%s@."
|
|
|
|
config_file msg ;
|
|
|
|
exit 1
|
|
|
|
| Ok cfg_json ->
|
|
|
|
try Cfg_file.from_json cfg_json
|
|
|
|
with exn ->
|
|
|
|
Format.eprintf
|
|
|
|
"Error: can't parse the configuration file: %s\n%a@."
|
|
|
|
config_file (fun ppf exn -> Json_encoding.print_error ppf exn) exn ;
|
|
|
|
exit 1 in
|
2017-09-19 13:31:35 +04:00
|
|
|
let tls = cfg.tls || tls in
|
2017-03-31 02:42:13 +04:00
|
|
|
let cfg = { cfg with tls ; node_port ; node_addr } in
|
|
|
|
if Sys.file_exists base_dir && not (Sys.is_directory base_dir) then begin
|
|
|
|
Format.eprintf "Error: %s is not a directory.@." base_dir ;
|
|
|
|
exit 1 ;
|
|
|
|
end ;
|
|
|
|
IO.mkdir base_dir ;
|
|
|
|
if Sys.file_exists config_dir && not (Sys.is_directory config_dir) then begin
|
|
|
|
Format.eprintf "Error: %s is not a directory.@." config_dir ;
|
|
|
|
exit 1 ;
|
|
|
|
end ;
|
|
|
|
IO.mkdir config_dir ;
|
|
|
|
if not (Sys.file_exists config_file) then Cfg_file.write config_file cfg ;
|
2017-09-19 13:31:35 +04:00
|
|
|
(cfg, { block ; print_timings = timings ; log_requests ; force }, remaining)
|