package vif

  1. Overview
  2. Docs

Source file vif_options_unix.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
let error_msgf fmt = Fmt.kstr (fun msg -> Error (`Msg msg)) fmt
let sockaddr = ref Unix.(ADDR_INET (inet_addr_loopback, 8080))
let backlog = ref 64
let pid = ref None
let domains = ref None
let reporter = ref None
let level = ref None

let setup_config domains' sockaddr' backlog' pid' reporter' level' =
  sockaddr := sockaddr';
  backlog := backlog';
  pid := pid';
  domains := domains';
  reporter := reporter';
  level := Some level'

let config_from_globals () =
  Vif_config_unix.config ?reporter:!reporter ?level:!level ?domains:!domains
    ?pid:!pid ~backlog:!backlog !sockaddr

open Cmdliner

let port =
  let doc = "The port used by the HTTP server." in
  let open Arg in
  value & opt (some int) None & info [ "p"; "port" ] ~doc ~docv:"PORT"

let inet_addr =
  let doc = "The address to bind the HTTP server." in
  let parser str =
    try Ok (Unix.inet_addr_of_string str)
    with _ -> error_msgf "Invalid inet-addr: %S" str
  in
  let pp ppf inet_addr = Fmt.string ppf (Unix.string_of_inet_addr inet_addr) in
  let inet_addr = Arg.conv (parser, pp) in
  let open Arg in
  value
  & opt (some inet_addr) None
  & info [ "i"; "inet-addr" ] ~doc ~docv:"INET_ADDR"

let unix_socket =
  let doc = "The unix socket to bind the HTTP server." in
  let open Arg in
  value & opt (some string) None & info [ "U"; "unix-socket" ] ~doc ~docv:"PATH"

let setup_sockaddr inet_addr port unix_socket =
  match (inet_addr, port, unix_socket) with
  | None, None, None -> Ok Unix.(ADDR_INET (inet_addr_loopback, 8080))
  | None, Some port, None -> Ok Unix.(ADDR_INET (inet_addr_loopback, port))
  | Some inet_addr, None, None -> Ok Unix.(ADDR_INET (inet_addr, 8080))
  | Some inet_addr, Some port, None -> Ok Unix.(ADDR_INET (inet_addr, port))
  | None, _, Some unix_socket -> Ok Unix.(ADDR_UNIX unix_socket)
  | Some _, _, Some _ ->
      error_msgf
        "Impossible to initialize a server on a UNIX socket and an address at \
         the same time"

let setup_sockaddr =
  let open Term in
  const setup_sockaddr
  $ inet_addr
  $ port
  $ unix_socket
  |> term_result ~usage:true

let is_not_directory str =
  (Sys.file_exists str && Sys.is_directory str = false)
  || Sys.file_exists str = false

let pid =
  let doc = "Specify a file to record its process-id in." in
  let non_existing_file =
    let parser str =
      match Fpath.of_string str with
      | Ok _ as v when is_not_directory str -> v
      | Ok v -> error_msgf "%a already exists as a directory" Fpath.pp v
      | Error _ as err -> err
    in
    Arg.conv (parser, Fpath.pp)
  in
  let open Arg in
  value
  & opt (some non_existing_file) None
  & info [ "pid-file" ] ~doc ~docv:"PATH"

let domains =
  let doc = "The number of domains used by vif." in
  let open Arg in
  value & opt (some int) None & info [ "domains" ] ~doc ~docv:"DOMAINS"

let backlog =
  let doc =
    "The limit of outstanding connections in the socket's listen queue."
  in
  let open Arg in
  value & opt int 64 & info [ "backlog" ] ~doc ~docv:"NUMBER"

let docs_output = "OUTPUT"
let t0 = Unix.gettimeofday ()

let reporter sources ppf =
  let re = Option.map Re.compile sources in
  let print src =
    let neg = Fun.negate in
    let some re = (neg List.is_empty) (Re.matches re (Logs.Src.name src)) in
    Option.fold ~none:true ~some re
  in
  let report src level ~over k msgf =
    let k _ = over (); k () in
    let pp header _tags k ppf fmt =
      let t1 = Unix.gettimeofday () in
      let delta = t1 -. t0 in
      let delta = delta /. 1_000_000_000. in
      Fmt.kpf k ppf
        ("[+%a][%a]%a[%a]: " ^^ fmt ^^ "\n%!")
        Fmt.(styled `Blue (fmt "%04.04f"))
        delta
        Fmt.(styled `Cyan int)
        (Stdlib.Domain.self () :> int)
        Logs_fmt.pp_header (level, header)
        Fmt.(styled `Magenta string)
        (Logs.Src.name src)
    in
    match (level, print src) with
    | Logs.Debug, false -> k ()
    | _, true | _ -> msgf @@ fun ?header ?tags fmt -> pp header tags k ppf fmt
  in
  { Logs.report }

let setup_sources = function
  | [ (_, `None) ] -> None
  | res ->
      let res = List.map snd res in
      let res =
        List.fold_left
          (fun acc -> function `Re re -> re :: acc | _ -> acc)
          [] res
      in
      Some (Re.alt res)

let setup_reporter sources utf_8 style_renderer =
  Option.iter (Fmt.set_style_renderer Fmt.stdout) style_renderer;
  Fmt.set_utf_8 Fmt.stdout utf_8;
  Some (reporter sources Fmt.stdout)

let renderer =
  let env = Cmd.Env.info "VIF_FMT" in
  Fmt_cli.style_renderer ~env ~docs:docs_output ()

let regexp : (string * [ `None | `Re of Re.t ]) Arg.conv =
  let parser str =
    match Re.Pcre.re str with
    | re -> Ok (str, `Re re)
    | exception _ -> error_msgf "Invalid PCRegexp: %S" str
  in
  let pp ppf (str, _) = Fmt.string ppf str in
  Arg.conv (parser, pp)

let sources =
  let doc = "A regexp (PCRE syntax) to identify which log we print." in
  let open Arg in
  value & opt_all regexp [ ("", `None) ] & info [ "l" ] ~doc ~docv:"REGEXP"

let setup_sources = Term.(const setup_sources $ sources)

let utf_8 =
  let doc = "Allow us to emit UTF-8 characters." in
  let env = Cmd.Env.info "VIF_UTF_8" in
  let open Arg in
  value
  & opt bool true
  & info [ "with-utf-8" ] ~doc ~docv:"BOOL" ~docs:docs_output ~env

let setup_reporter =
  let open Term in
  const setup_reporter $ setup_sources $ utf_8 $ renderer

let verbosity =
  let env = Cmd.Env.info "VIF_LOGS" in
  Logs_cli.level ~env ~docs:docs_output ()

let setup_config =
  let open Term in
  const setup_config
  $ domains
  $ setup_sockaddr
  $ backlog
  $ pid
  $ setup_reporter
  $ verbosity