package mirage

  1. Overview
  2. Docs

Source file mirage.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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
(*
 * Copyright (c) 2013 Thomas Gazagnaire <thomas@gazagnaire.org>
 * Copyright (c) 2013 Anil Madhavapeddy <anil@recoil.org>
 * Copyright (c) 2018 Mindy Preston     <meetup@yomimono.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *)

open Functoria
open Devices
module Type = Type
module Impl = Functoria.Impl
module Info = Info
module Dune = Dune
module Context = Context
module Key = Devices.Key
module Runtime_arg = Devices.Runtime_arg
include Functoria.DSL

(** {2 OCamlfind predicates} *)

(** {2 Devices} *)

type qubesdb = Qubesdb.qubesdb

let qubesdb = Qubesdb.qubesdb
let default_qubesdb = Qubesdb.default_qubesdb

type sleep = Sleep.sleep

let sleep = Sleep.sleep
let default_sleep = Sleep.default_sleep
let no_sleep = Sleep.no_sleep

type ptime = Ptime.ptime

let ptime = Ptime.ptime
let default_ptime = Ptime.default_ptime
let no_ptime = Ptime.no_ptime
let mock_ptime = Ptime.mock_ptime

type mtime = Mtime.mtime

let mtime = Mtime.mtime
let default_mtime = Mtime.default_mtime
let no_mtime = Mtime.no_mtime
let mock_mtime = Mtime.mock_mtime

type random = Random.random

let random = Random.random
let default_random = Random.default_random
let no_random = Random.no_random

type kv_ro = Kv.ro

let kv_ro = Kv.ro
let direct_kv_ro = Kv.direct_kv_ro
let crunch = Kv.crunch
let generic_kv_ro = Kv.generic_kv_ro

type kv_rw = Kv.rw

let kv_rw = Kv.rw
let direct_kv_rw = Kv.direct_kv_rw
let kv_rw_mem = Kv.mem_kv_rw

let docteur ?mode ?name ?output ?analyze ?branch ?extra_deps remote =
  Block.docteur ?mode ?name ?output ?analyze ?branch ?extra_deps remote

let chamelon ~program_block_size block =
  Block.chamelon ~program_block_size $ block

let tar_kv_rw block = Block.tar_kv_rw block

type block = Block.block

let block = Block.block
let tar_kv_ro = Block.tar_kv_ro
let fat_ro = Block.fat_ro
let generic_block = Block.generic_block
let ramdisk = Block.ramdisk
let block_of_xenstore_id = Block.block_of_xenstore_id
let block_of_file = Block.block_of_file
let ccm_block ?nonce_len key block = Block.ccm_block ?nonce_len key $ block

type network = Network.network

let network = Network.network
let netif = Network.netif
let default_network = Network.default_network

type ethernet = Ethernet.ethernet

let ethernet = Ethernet.ethernet
let etif = Ethernet.ethif
let ethif = Ethernet.ethif

type arpv4 = Arp.arpv4

let arpv4 = Arp.arpv4
let arp = Arp.arp

type v4 = Ip.v4
type v6 = Ip.v6
type v4v6 = Ip.v4v6
type 'a ip = 'a Ip.ip
type ipv4 = Ip.ipv4
type ipv6 = Ip.ipv6
type ipv4v6 = Ip.ipv4v6

let ipv4 = Ip.ipv4
let ipv6 = Ip.ipv6
let ipv4_qubes = Ip.ipv4_qubes
let ipv4v6 = Ip.ipv4v6
let ipv4_of_dhcp = Ip.ipv4_of_dhcp
let create_ipv4 = Ip.create_ipv4
let create_ipv6 = Ip.create_ipv6
let create_ipv4v6 = Ip.create_ipv4v6

type 'a udp = 'a Udp.udp

let udp = Udp.udp

type udpv4v6 = Udp.udpv4v6

let udpv4v6 = Udp.udpv4v6
let direct_udp = Udp.direct_udp

type 'a tcp = 'a Tcp.tcp

let tcp = Tcp.tcp

type tcpv4v6 = Tcp.tcpv4v6

let tcpv4v6 = Tcp.tcpv4v6
let direct_tcp = Tcp.direct_tcp

type stackv4v6 = Stack.stackv4v6

let stackv4v6 = Stack.stackv4v6
let generic_stackv4v6 = Stack.generic_stackv4v6
let direct_stackv4v6 = Stack.direct_stackv4v6

let tcpv4v6_of_stackv4v6 v =
  let impl =
    let packages_v =
      Ip.right_tcpip_library ~sublibs:[ "stack-direct" ] "tcpip"
    in
    let connect _ modname = function
      | [ stackv4v6 ] ->
          code ~pos:__POS__ {ocaml|%s.connect %s|ocaml} modname stackv4v6
      | _ -> Misc.connect_err "tcpv4v6_of_stackv4v6" 1
    in
    impl ~packages_v ~connect "Tcpip_stack_direct.TCPV4V6"
      (stackv4v6 @-> tcpv4v6)
  in
  impl $ v

type conduit = Conduit.conduit

let conduit = Conduit.conduit
let conduit_direct = Conduit.conduit_direct

type resolver = Resolver.resolver

let resolver = Resolver.resolver
let resolver_unix_system = Resolver.resolver_unix_system
let resolver_dns = Resolver.resolver_dns

type happy_eyeballs = Happy_eyeballs.happy_eyeballs

let happy_eyeballs = Happy_eyeballs.happy_eyeballs

let generic_happy_eyeballs ?group ?aaaa_timeout ?connect_delay ?connect_timeout
    ?resolve_timeout ?resolve_retries ?timer_interval stackv4v6 =
  Happy_eyeballs.generic_happy_eyeballs ?group ?aaaa_timeout ?connect_delay
    ?connect_timeout ?resolve_timeout ?resolve_retries ?timer_interval ()
  $ stackv4v6

type dns_client = Dns.dns_client

let dns_client = Dns.dns_client

let generic_dns_client ?group ?timeout ?nameservers ?cache_size stackv4v6
    happy_eyeballs =
  Dns.generic_dns_client ?group ?timeout ?nameservers ?cache_size ()
  $ stackv4v6
  $ happy_eyeballs

type syslog = Syslog.syslog

let syslog = Syslog.syslog
let syslog_tls = Syslog.syslog_tls
let syslog_tcp = Syslog.syslog_tcp
let syslog_udp = Syslog.syslog_udp
let monitoring = Syslog.monitoring

type http = Http.http

let http = Http.http
let cohttp_server = Http.cohttp_server
let httpaf_server = Http.httpaf_server

type http_client = Http.http_client

let http_client = Http.http_client
let cohttp_client = Http.cohttp_client

type http_server = Http.http_server

let http_server = Http.http_server
let paf_server ~port tcpv4v6 = Http.paf_server port $ tcpv4v6

type alpn_client = Http.alpn_client

let alpn_client = Http.alpn_client
let paf_client tcpv4v6 mimic = Http.paf_client $ tcpv4v6 $ mimic

type argv = Functoria.argv

let argv = Functoria.argv
let default_argv = Argv.default_argv
let no_argv = Argv.no_argv

type reporter = Reporter.reporter

let reporter = Reporter.reporter
let default_reporter = Reporter.default_reporter
let no_reporter = Reporter.no_reporter

type mimic = Mimic.mimic

let mimic = Mimic.mimic

let mimic_happy_eyeballs stackv4v6 happy_eyeballs dns =
  Mimic.mimic_happy_eyeballs $ stackv4v6 $ happy_eyeballs $ dns

type git_client = Git.git_client

let git_client = Git.git_client
let merge_git_clients ctx0 ctx1 = Git.git_merge_clients $ ctx0 $ ctx1
let git_tcp tcpv4v6 ctx = Git.git_tcp $ tcpv4v6 $ ctx

let git_ssh ?group ?authenticator ?key ?password tcpv4v6 ctx =
  Git.git_ssh ?group ?authenticator ?key ?password () $ tcpv4v6 $ ctx

let git_http ?group ?authenticator ?headers tcpv4v6 ctx =
  Git.git_http ?group ?authenticator ?headers () $ tcpv4v6 $ ctx

let delay = job

let delay_startup =
  let delay_key = Runtime_arg.delay in
  let runtime_args = [ Runtime_arg.v delay_key ] in
  let packages = [ package ~max:"1.0.0" "duration" ] in
  let connect _ _ = function
    | [ delay_key ] ->
        code ~pos:__POS__ "Mirage_sleep.ns (Duration.of_sec %s)" delay_key
    | _ -> Misc.connect_err "delay_startup" 1
  in
  impl ~packages ~runtime_args ~connect "Mirage_runtime" delay

let unikernel_name = job

let unikernel_name =
  let connect i _ _ =
    code ~pos:__POS__ "Mirage_runtime.set_name %S; Lwt.return_unit"
      (Info.name i)
  in
  impl ~connect "Mirage_runtime" unikernel_name

(** Functoria devices *)

type info = Functoria.info

let job = Functoria.job
let noop = Functoria.noop

let os_of_target i =
  match Info.get i Key.target with
  | #Key.mode_solo5 -> "Solo5_os"
  | #Key.mode_unix -> "Unix_os"
  | #Key.mode_xen -> "Xen_os"
  | #Key.mode_unikraft -> "Unikraft_os"

module Project = struct
  let name = "mirage"
  let version = "v4.10.0"

  let prelude info =
    Fmt.str
      {ocaml|open Lwt.Infix
type 'a io = 'a Lwt.t
let return = Lwt.return
let run t = %s.Main.run t ; exit 0|ocaml}
      (os_of_target info)

  (* The ocamlfind packages to use when compiling config.ml *)
  let packages = [ package "mirage" ]

  let name_of_target i =
    match Info.output i with
    | Some o -> o
    | None ->
        let name = Info.name i in
        let target = Info.get i Key.target in
        Fmt.str "%s-%a" name Key.pp_target target

  let dune i = Target.dune i
  let configure i = Target.configure i

  let dune_project =
    [ Dune.stanza {|
    (implicit_transitive_deps true)
    |} ]

  let dune_workspace =
    let f ?build_dir i =
      let stanzas = Target.build_context ?build_dir i in
      let main =
        Dune.stanza {|
(lang dune 2.9)

(context (default))
        |}
      in
      Dune.v (main :: stanzas)
    in
    Some f

  let context_name i = Target.context_name i

  let create jobs =
    let keys = Key.[ v target ] in
    let packages_v =
      (* XXX: use 4.10.0 here instead of hardcoding a version? *)
      let min = "4.9.0" and max = "4.10.0" in
      let common =
        [
          package ~scope:`Monorepo "lwt";
          package ~scope:`Monorepo ~min ~max "mirage-runtime";
          package ~scope:`Switch ~build:true ~min ~max "mirage";
          package ~scope:`Switch ~build:true ~min:"0.3.2" "opam-monorepo";
        ]
      in
      Key.match_ Key.(value target) @@ fun target ->
      Target.packages target @ common
    in
    let local_libs = List.map Impl.local_libs jobs |> List.concat in
    let install = Target.install in
    let extra_deps = List.map dep jobs in
    let connect _ _ _ = code ~pos:__POS__ "return ()" in
    impl ~keys ~packages_v ~local_libs ~configure ~dune ~connect ~extra_deps
      ~install "Mirage_runtime" job
end

include Lib.Make (Project)
module Tool = Tool.Make (Project)

(** Custom registration *)

let runtime_args argv =
  Functoria.runtime_args ~runtime_package:(package "mirage-runtime")
    ~runtime_modname:"Mirage_runtime" argv

let ocaml_runtime =
  let packages = [ package ~min:"1.0.1" ~max:"2.0.0" "cmdliner-stdlib" ] in
  let runtime_args =
    [
      Runtime_arg.v
        (Runtime_arg.create ~pos:__POS__
           "Cmdliner_stdlib.setup ~backtrace:(Some true) \
            ~randomize_hashtables:(Some true) ()");
    ]
  in
  impl ~packages ~runtime_args "Cmdliner_stdlib" job

let ( ++ ) acc x =
  match (acc, x) with
  | _, None -> acc
  | None, Some x -> Some [ x ]
  | Some acc, Some x -> Some (acc @ [ x ])

let register ?(argv = default_argv) ?(reporter = default_reporter ())
    ?(sleep = default_sleep) ?(ptime = default_ptime) ?(mtime = default_mtime)
    ?(random = default_random) ?src name jobs =
  if List.exists Functoria.Impl.app_has_no_arguments jobs then
    invalid_arg
      "Your configuration includes a job without arguments. Please add a \
       dependency in your config.ml: use `let main = Mirage.main \
       \"Unikernel.Hello\" (job @-> job) register \"hello\" [ main $ noop ]` \
       instead of `.. job .. [ main ]`.";
  let first = [ runtime_args argv; ocaml_runtime ] in
  let reporter = if reporter == no_reporter then None else Some reporter in
  let init =
    Some first
    ++ Some delay_startup
    ++ reporter
    ++ Some sleep
    ++ Some ptime
    ++ Some mtime
    ++ Some random
    ++ Some unikernel_name
  in
  register ?init ?src name jobs

let connect_err = Devices.Misc.connect_err

module Action = Functoria.Action