From 2c5996e4c07f054f5ede71f3d37ae00f5e04af93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignas=20Vy=C5=A1niauskas?= Date: Mon, 25 Nov 2024 17:19:20 +0200 Subject: [PATCH] dune fmt with profile = ocamlformat --- controller/bindings/connman/connman.ml | 287 +++++++-------- controller/bindings/connman/connman.mli | 107 +++--- controller/bindings/curl/curl.ml | 57 ++- controller/bindings/curl/curl.mli | 12 +- controller/bindings/locale/locale.mli | 3 + controller/bindings/rauc/rauc.ml | 70 ++-- controller/bindings/rauc/rauc.mli | 25 +- .../screen-settings/screen_settings.ml | 11 +- .../screen-settings/screen_settings.mli | 4 + controller/bindings/systemd/systemd.ml | 203 +++++++---- controller/bindings/timedate/timedate.ml | 38 +- controller/bindings/timedate/timedate.mli | 24 +- controller/bindings/util/util.ml | 2 +- controller/bindings/zerotier/zerotier.ml | 7 +- controller/bindings/zerotier/zerotier.mli | 2 +- controller/server/gui.ml | 150 ++++---- controller/server/gui.mli | 14 +- controller/server/health.ml | 3 +- controller/server/health.mli | 6 +- controller/server/info.ml | 35 +- controller/server/logging.ml | 16 +- controller/server/network.ml | 38 +- controller/server/network.mli | 12 +- controller/server/rauc_service.ml | 3 + controller/server/server.ml | 30 +- controller/server/update.ml | 51 ++- controller/server/update.mli | 32 +- controller/server/update_client.ml | 20 +- controller/server/update_client.mli | 10 +- controller/server/view/changelog_page.ml | 4 +- controller/server/view/changelog_page.mli | 2 +- controller/server/view/common/definition.ml | 10 +- controller/server/view/common/definition.mli | 18 +- controller/server/view/common/icon.ml | 163 ++++----- controller/server/view/common/icon.mli | 29 +- controller/server/view/common/page.ml | 74 ++-- controller/server/view/common/page.mli | 18 +- controller/server/view/error_page.ml | 21 +- controller/server/view/error_page.mli | 10 +- controller/server/view/info_page.ml | 72 ++-- controller/server/view/info_page.mli | 2 +- controller/server/view/licensing_page.ml | 50 ++- controller/server/view/licensing_page.mli | 2 +- controller/server/view/localization_page.ml | 132 ++++--- controller/server/view/localization_page.mli | 20 +- .../server/view/network_details_page.ml | 336 ++++++++---------- .../server/view/network_details_page.mli | 2 +- controller/server/view/network_list_page.ml | 108 +++--- controller/server/view/network_list_page.mli | 12 +- controller/server/view/status_page.ml | 90 +++-- controller/server/view/status_page.mli | 14 +- controller/tests/server/mocks/mock_rauc.ml | 47 +-- .../tests/server/mocks/mock_update_client.ml | 19 +- controller/tests/server/update/helpers.ml | 61 ++-- controller/tests/server/update/outcome.ml | 4 +- controller/tests/server/update/scenario.ml | 18 +- .../update/update_client_mock_server.ml | 74 ++-- .../server/update/update_client_tests.ml | 46 ++- .../tests/server/update/update_prop_tests.ml | 22 +- .../tests/server/update/update_tests.ml | 162 ++++----- 60 files changed, 1425 insertions(+), 1489 deletions(-) diff --git a/controller/bindings/connman/connman.ml b/controller/bindings/connman/connman.ml index d3e7fba7..f7587c5c 100644 --- a/controller/bindings/connman/connman.ml +++ b/controller/bindings/connman/connman.ml @@ -10,6 +10,7 @@ module OBus_proxy = struct include OBus_proxy let to_jsonm _ = `String "@opaque" + let of_jsonm_exn _ = failwith "Deserialization is not supported" end @@ -39,13 +40,13 @@ module Technology = struct | "p2p" -> Some P2P | _ -> None - type t = { - _proxy : (OBus_proxy.t[@sexp.opaque]); - name : string; - type' : type'; - powered : bool; - connected : bool; - } + type t = + { _proxy: (OBus_proxy.t[@sexp.opaque]) + ; name: string + ; type': type' + ; powered: bool + ; connected: bool + } [@@deriving sexp, protocol ~driver:(module Jsonm)] let set_property proxy ~name ~value = @@ -124,16 +125,16 @@ module Agent = struct |> Option.map OBus_value.C.(cast_single basic_string) in match requirement_opt with - | Some "mandatory" -> [ k ] + | Some "mandatory" -> [k] | _ -> [] ) fields in match (input, mandatory_inputs) with - | Passphrase p, [ "Passphrase" ] -> + | Passphrase p, ["Passphrase"] -> return - @@ Ok [ ("Passphrase", p |> OBus_value.C.(make_single basic_string)) ] - | None, [ "Passphrase" ] -> + @@ Ok [("Passphrase", p |> OBus_value.C.(make_single basic_string))] + | None, ["Passphrase"] -> let%lwt () = Logs_lwt.err ~src:log_src (fun m -> m "Passphrase requested from agent, but not available." @@ -213,8 +214,7 @@ module Agent = struct Lwt.fail obus_exn in Connman_interfaces.Net_connman_Agent.make - { - m_ReportError = + { m_ReportError= (fun obj (service, msg) -> let%lwt () = Logs_lwt.err ~src:log_src (fun m -> @@ -222,24 +222,22 @@ module Agent = struct ) in on_error (ManagerError (manager_error_of_string msg)) - ); - m_RequestInput = wrap_req request_input; - m_RequestBrowser = wrap_req request_browser; - m_Cancel = (fun obj () -> return_unit); - m_Release = (fun obj () -> return_unit); + ) + ; m_RequestInput= wrap_req request_input + ; m_RequestBrowser= wrap_req request_browser + ; m_Cancel= (fun obj () -> return_unit) + ; m_Release= (fun obj () -> return_unit) } let create ~(input : input) on_error = let%lwt system_bus = OBus_bus.system () in - let path = - [ "net"; "connman"; "agent"; Random.int 9999 |> string_of_int ] - in + let path = ["net"; "connman"; "agent"; Random.int 9999 |> string_of_int] in let%lwt () = Logs_lwt.debug ~src:log_src (fun m -> m "creating connman agent at %s" (String.concat "/" path) ) in - let obj = OBus_object.make ~interfaces:[ interface on_error ] path in + let obj = OBus_object.make ~interfaces:[interface on_error] path in let () = OBus_object.attach obj input in let () = OBus_object.export system_bus obj in return (path, obj) @@ -268,119 +266,113 @@ module Service = struct | WPS [@@deriving sexp, protocol ~driver:(module Jsonm)] - let supported_security_protocols = [ None; WEP; PSK ] + let supported_security_protocols = [None; WEP; PSK] + let string_of_security s = sexp_of_security s |> Sexplib.Sexp.to_string module IPv4 = struct - type t = { - method' : string; - address : string; - netmask : string; - gateway : string option; - } + type t = + { method': string + ; address: string + ; netmask: string + ; gateway: string option + } [@@deriving sexp, protocol ~driver:(module Jsonm)] let of_obus v = (fun () -> let open OBus_value.C in let properties = v |> cast_single (dict string variant) in - { - method' = - properties |> List.assoc "Method" |> cast_single basic_string; - address = - properties |> List.assoc "Address" |> cast_single basic_string; - netmask = - properties |> List.assoc "Netmask" |> cast_single basic_string; - gateway = + { method'= properties |> List.assoc "Method" |> cast_single basic_string + ; address= + properties |> List.assoc "Address" |> cast_single basic_string + ; netmask= + properties |> List.assoc "Netmask" |> cast_single basic_string + ; gateway= properties |> List.assoc_opt "Gateway" - |> Option.map (cast_single basic_string); + |> Option.map (cast_single basic_string) } ) |> CCResult.guard |> CCResult.to_opt end module IPv6 = struct - type t = { - method' : string; - address : string; - prefix_length : int; - gateway : string option; - privacy : string; - } + type t = + { method': string + ; address: string + ; prefix_length: int + ; gateway: string option + ; privacy: string + } [@@deriving sexp, protocol ~driver:(module Jsonm)] let of_obus v = (fun () -> let open OBus_value.C in let properties = v |> cast_single (dict string variant) in - { - method' = - properties |> List.assoc "Method" |> cast_single basic_string; - address = - properties |> List.assoc "Address" |> cast_single basic_string; - prefix_length = + { method'= properties |> List.assoc "Method" |> cast_single basic_string + ; address= + properties |> List.assoc "Address" |> cast_single basic_string + ; prefix_length= properties |> List.assoc "PrefixLength" |> cast_single basic_byte - |> int_of_char; - gateway = + |> int_of_char + ; gateway= properties |> List.assoc_opt "Gateway" - |> Option.map (cast_single basic_string); - privacy = - properties |> List.assoc "Privacy" |> cast_single basic_string; + |> Option.map (cast_single basic_string) + ; privacy= + properties |> List.assoc "Privacy" |> cast_single basic_string } ) |> CCResult.guard |> CCResult.to_opt end module Ethernet = struct - type t = { - method' : string; - interface : string; - address : string; - mtu : int; - } + type t = + { method': string + ; interface: string + ; address: string + ; mtu: int + } [@@deriving sexp, protocol ~driver:(module Jsonm)] let of_obus v = (fun () -> let open OBus_value.C in let properties = v |> cast_single (dict string variant) in - { - method' = - properties |> List.assoc "Method" |> cast_single basic_string; - interface = - properties |> List.assoc "Interface" |> cast_single basic_string; - address = - properties |> List.assoc "Address" |> cast_single basic_string; - mtu = properties |> List.assoc "MTU" |> cast_single basic_uint16; + { method'= properties |> List.assoc "Method" |> cast_single basic_string + ; interface= + properties |> List.assoc "Interface" |> cast_single basic_string + ; address= + properties |> List.assoc "Address" |> cast_single basic_string + ; mtu= properties |> List.assoc "MTU" |> cast_single basic_uint16 } ) |> CCResult.guard |> CCResult.to_opt end module Proxy = struct - type credentials = { - user : string; - password : (string[@sexp.opaque]); - } + type credentials = + { user: string + ; password: (string[@sexp.opaque]) + } [@@deriving sexp, protocol ~driver:(module Jsonm)] - type t = { - host : string; - port : int; - credentials : credentials option; - } + type t = + { host: string + ; port: int + ; credentials: credentials option + } [@@deriving sexp, protocol ~driver:(module Jsonm)] let make ?user ?password host port = - { - host; - port; - credentials = + { host + ; port + ; credentials= ( match (user, password) with | Some "", _ -> None - | Some u, Some p -> Some { user = u; password = p } + | Some u, Some p -> Some {user= u; password= p} | _ -> None - ); + ) } let validate str = @@ -389,19 +381,17 @@ module Service = struct match (Uri.scheme uri, Uri.host uri, Uri.port uri) with | Some "http", Some host, Some port -> Some - { - credentials = + { credentials= ( match (Uri.user uri, Uri.password uri) with | Some user, Some password -> Some - { - user = Uri.pct_decode user; - password = Uri.pct_decode password; + { user= Uri.pct_decode user + ; password= Uri.pct_decode password } | _ -> None - ); - host; - port; + ) + ; host + ; port } | _ -> None else None @@ -425,23 +415,23 @@ module Service = struct if include_userinfo then Uri.with_userinfo uri userinfo else uri end - type t = { - _proxy : (OBus_proxy.t[@sexp.opaque]); - _manager : (OBus_proxy.t[@sexp.opaque]); - id : string; - name : string; - type' : Technology.type'; - security : security list; - state : state; - strength : int option; - favorite : bool; - autoconnect : bool; - ipv4 : IPv4.t option; - ipv6 : IPv6.t option; - ethernet : Ethernet.t; - proxy : Proxy.t option; - nameservers : string list; - } + type t = + { _proxy: (OBus_proxy.t[@sexp.opaque]) + ; _manager: (OBus_proxy.t[@sexp.opaque]) + ; id: string + ; name: string + ; type': Technology.type' + ; security: security list + ; state: state + ; strength: int option + ; favorite: bool + ; autoconnect: bool + ; ipv4: IPv4.t option + ; ipv6: IPv6.t option + ; ethernet: Ethernet.t + ; proxy: Proxy.t option + ; nameservers: string list + } [@@deriving sexp, protocol ~driver:(module Jsonm)] (* Helper to parse a service from OBus *) @@ -504,25 +494,24 @@ module Service = struct nameservers security -> - { - _proxy = OBus_proxy.make ~peer:(OBus_context.sender context) ~path; - _manager = manager; - id = path |> CCList.last 1 |> CCList.hd; - name; - type'; - state; - strength; - favorite; - autoconnect; - ipv4 = + { _proxy= OBus_proxy.make ~peer:(OBus_context.sender context) ~path + ; _manager= manager + ; id= path |> CCList.last 1 |> CCList.hd + ; name + ; type' + ; state + ; strength + ; favorite + ; autoconnect + ; ipv4= ( if Option.is_some ipv4_user_config then ipv4_user_config else ipv4 - ); - ipv6; - ethernet; - proxy; - nameservers; - security; + ) + ; ipv6 + ; ethernet + ; proxy + ; nameservers + ; security } ) <*> (properties |> List.assoc_opt "Name" >>= string_of_obus) @@ -564,7 +553,7 @@ module Service = struct let dict = OBus_value.C.make_single OBus_value.C.(dict string variant) - [ ("Method", OBus_value.C.(make_single basic_string) "direct") ] + [("Method", OBus_value.C.(make_single basic_string) "direct")] in set_property service ~name:"Proxy.Configuration" ~value:dict @@ -572,12 +561,11 @@ module Service = struct let dict = OBus_value.C.make_single OBus_value.C.(dict string variant) - [ - ("Method", OBus_value.C.(make_single basic_string) "manual"); - ( "Servers", - OBus_value.C.(make_single (array basic_string)) - [ Proxy.to_uri ~include_userinfo:true proxy |> Uri.to_string ] - ); + [ ("Method", OBus_value.C.(make_single basic_string) "manual") + ; ( "Servers" + , OBus_value.C.(make_single (array basic_string)) + [Proxy.to_uri ~include_userinfo:true proxy |> Uri.to_string] + ) ] in set_property service ~name:"Proxy.Configuration" ~value:dict @@ -586,11 +574,10 @@ module Service = struct let dict = OBus_value.C.make_single OBus_value.C.(dict string variant) - [ - ("Method", OBus_value.C.(make_single basic_string) "manual"); - ("Address", OBus_value.C.(make_single basic_string) address); - ("Netmask", OBus_value.C.(make_single basic_string) netmask); - ("Gateway", OBus_value.C.(make_single basic_string) gateway); + [ ("Method", OBus_value.C.(make_single basic_string) "manual") + ; ("Address", OBus_value.C.(make_single basic_string) address) + ; ("Netmask", OBus_value.C.(make_single basic_string) netmask) + ; ("Gateway", OBus_value.C.(make_single basic_string) gateway) ] in set_property service ~name:"IPv4.Configuration" ~value:dict @@ -599,7 +586,7 @@ module Service = struct let dict = OBus_value.C.make_single OBus_value.C.(dict string variant) - [ ("Method", OBus_value.C.(make_single basic_string) "dhcp") ] + [("Method", OBus_value.C.(make_single basic_string) "dhcp")] in set_property service ~name:"IPv4.Configuration" ~value:dict @@ -618,21 +605,17 @@ module Service = struct let%lwt () = Logs_lwt.debug ~src:log_src (fun m -> m "connect to service %s" service.id) in - (* Store agent error in a local mutable variable *) let agent_reported_error = ref Option.None in let on_agent_error msg = Lwt.return (agent_reported_error := Some msg) in - (* Create and register an agent that will pass input to ConnMan *) let%lwt agent_path, agent = Agent.create ~input on_agent_error in let%lwt () = register_agent service._manager ~path:agent_path in - let destroy_agent () = (* Cleanup and destroy agent *) let%lwt () = unregister_agent service._manager ~path:agent_path in Agent.destroy agent in - let%lwt obus_resp = Lwt_result.catch (OBus_method.call Connman_interfaces.Net_connman_Service.m_Connect @@ -729,12 +712,11 @@ module Manager = struct let to_technology (path, properties) : Technology.t option = CCOption.( pure (fun name type' powered connected : Technology.t -> - { - _proxy = OBus_proxy.make ~peer:(OBus_context.sender context) ~path; - name; - type'; - powered; - connected; + { _proxy= OBus_proxy.make ~peer:(OBus_context.sender context) ~path + ; name + ; type' + ; powered + ; connected } ) <*> (properties |> List.assoc_opt "Name" >>= string_of_obus) @@ -954,8 +936,11 @@ module Net_connman_Service = struct OBus_method.call m_SetProperty proxy (name, value) let clear_property proxy ~name = OBus_method.call m_ClearProperty proxy name + let connect proxy = OBus_method.call m_Connect proxy () + let disconnect proxy = OBus_method.call m_Disconnect proxy () + let remove proxy = OBus_method.call m_Remove proxy () let move_before proxy ~service = @@ -967,6 +952,7 @@ module Net_connman_Service = struct OBus_method.call m_MoveAfter proxy service let reset_counters proxy = OBus_method.call m_ResetCounters proxy () + let property_changed proxy = OBus_signal.make s_PropertyChanged proxy end @@ -977,5 +963,6 @@ module Net_connman_Technology = struct OBus_method.call m_SetProperty proxy (name, value) let scan proxy = OBus_method.call m_Scan proxy () + let property_changed proxy = OBus_signal.make s_PropertyChanged proxy end diff --git a/controller/bindings/connman/connman.mli b/controller/bindings/connman/connman.mli index bd3aaebc..fb69727c 100644 --- a/controller/bindings/connman/connman.mli +++ b/controller/bindings/connman/connman.mli @@ -14,13 +14,13 @@ module Technology : sig Note that not all properties are encoded. *) - type t = { - _proxy : (OBus_proxy.t[@sexp.opaque]); - name : string; - type' : type'; - powered : bool; - connected : bool; - } + type t = + { _proxy: (OBus_proxy.t[@sexp.opaque]) + ; name: string + ; type': type' + ; powered: bool + ; connected: bool + } [@@deriving sexp, protocol ~driver:(module Jsonm)] (** Enable a technology *) @@ -72,50 +72,50 @@ module Service : sig (** IPv4 properties *) module IPv4 : sig - type t = { - method' : string; - address : string; - netmask : string; - gateway : string option; - } + type t = + { method': string + ; address: string + ; netmask: string + ; gateway: string option + } [@@deriving sexp, protocol ~driver:(module Jsonm)] end (** IPv6 properties *) module IPv6 : sig - type t = { - method' : string; - address : string; - prefix_length : int; - gateway : string option; - privacy : string; - } + type t = + { method': string + ; address: string + ; prefix_length: int + ; gateway: string option + ; privacy: string + } [@@deriving sexp, protocol ~driver:(module Jsonm)] end (** Ethernet properties *) module Ethernet : sig - type t = { - method' : string; - interface : string; - address : string; - mtu : int; - } + type t = + { method': string + ; interface: string + ; address: string + ; mtu: int + } [@@deriving sexp, protocol ~driver:(module Jsonm)] end module Proxy : sig - type credentials = { - user : string; - password : (string[@sexp.opaque]); - } + type credentials = + { user: string + ; password: (string[@sexp.opaque]) + } [@@deriving sexp, protocol ~driver:(module Jsonm)] - type t = { - host : string; - port : int; - credentials : credentials option; - } + type t = + { host: string + ; port: int + ; credentials: credentials option + } [@@deriving sexp, protocol ~driver:(module Jsonm)] (** [validate str] returns [t] if [str] is valid. @@ -143,36 +143,39 @@ module Service : sig Note that not all properties are encoded. *) - type t = { - _proxy : (OBus_proxy.t[@sexp.opaque]); - _manager : (OBus_proxy.t[@sexp.opaque]); - id : string; - name : string; - type' : Technology.type'; - security : security list; - state : state; - strength : int option; - favorite : bool; - autoconnect : bool; - ipv4 : IPv4.t option; - ipv6 : IPv6.t option; - ethernet : Ethernet.t; - proxy : Proxy.t option; - nameservers : string list; - } + type t = + { _proxy: (OBus_proxy.t[@sexp.opaque]) + ; _manager: (OBus_proxy.t[@sexp.opaque]) + ; id: string + ; name: string + ; type': Technology.type' + ; security: security list + ; state: state + ; strength: int option + ; favorite: bool + ; autoconnect: bool + ; ipv4: IPv4.t option + ; ipv6: IPv6.t option + ; ethernet: Ethernet.t + ; proxy: Proxy.t option + ; nameservers: string list + } [@@deriving sexp, protocol ~driver:(module Jsonm)] (** Helper to decide if service is connected *) val is_connected : t -> bool val set_direct_proxy : t -> unit Lwt.t + val set_manual_proxy : t -> Proxy.t -> unit Lwt.t val set_manual_ipv4 : t -> address:string -> netmask:string -> gateway:string -> unit Lwt.t val set_dhcp_ipv4 : t -> unit Lwt.t + val set_nameservers : t -> string list -> unit Lwt.t + val connect : ?input:Agent.input -> t -> unit Lwt.t (** Disconnect service. *) diff --git a/controller/bindings/curl/curl.ml b/controller/bindings/curl/curl.ml index 2a004655..4cfa3df3 100644 --- a/controller/bindings/curl/curl.ml +++ b/controller/bindings/curl/curl.ml @@ -49,47 +49,46 @@ let http_code_marker = '|' let parse_status_code_and_body str = let open Base.Option in - Base.String.rsplit2 ~on:http_code_marker str >>= fun (body, code_str) -> + Base.String.rsplit2 ~on:http_code_marker str + >>= fun (body, code_str) -> safe_int_of_string code_str >>= fun code -> return (code, body) let request ?proxy ?(headers = []) ?data ?(options = []) url = let cmd = - ( "", - (* path to Curl executable (uses PATH if empty string) *) + ( "" + , (* path to Curl executable (uses PATH if empty string) *) Array.concat - [ - [| - "curl"; - Uri.to_string url; - "--silent"; - "--show-error"; - "--write-out"; - Char.escaped http_code_marker ^ "%{http_code}"; - |]; - ( match proxy with - | Some p -> [| "--proxy"; Uri.to_string p; "--proxy-anyauth" |] + [ [| "curl" + ; Uri.to_string url + ; "--silent" + ; "--show-error" + ; "--write-out" + ; Char.escaped http_code_marker ^ "%{http_code}" + |] + ; ( match proxy with + | Some p -> [|"--proxy"; Uri.to_string p; "--proxy-anyauth"|] | None -> [||] - ); - headers - |> List.map (fun (k, v) -> [| "--header"; k ^ ":" ^ v |]) - |> Array.concat; - ( match data with - | Some d -> [| "--data"; d |] + ) + ; headers + |> List.map (fun (k, v) -> [|"--header"; k ^ ":" ^ v|]) + |> Array.concat + ; ( match data with + | Some d -> [|"--data"; d|] | None -> [||] - ); - Base.List.to_array options; + ) + ; Base.List.to_array options ] ) in match%lwt Lwt_result.catch (fun () -> exec cmd) with | Ok (Unix.WEXITED 0, stdout, _) -> ( - match parse_status_code_and_body stdout with - | Some (code, body) -> - if Cohttp.Code.is_success code then - Lwt.return (RequestSuccess (code, body)) - else Lwt.return (RequestFailure (UnsuccessfulStatus (code, body))) - | None -> Lwt.return (RequestFailure (UnreadableStatus stdout)) - ) + match parse_status_code_and_body stdout with + | Some (code, body) -> + if Cohttp.Code.is_success code then + Lwt.return (RequestSuccess (code, body)) + else Lwt.return (RequestFailure (UnsuccessfulStatus (code, body))) + | None -> Lwt.return (RequestFailure (UnreadableStatus stdout)) + ) | Ok (Unix.WEXITED n, _, stderr) -> Lwt.return (RequestFailure (ProcessExit (n, stderr))) | Ok (Unix.WSIGNALED signal, _, _stderr) -> diff --git a/controller/bindings/curl/curl.mli b/controller/bindings/curl/curl.mli index f1171112..788ad098 100644 --- a/controller/bindings/curl/curl.mli +++ b/controller/bindings/curl/curl.mli @@ -16,9 +16,9 @@ type result = val pretty_print_error : error -> string val request : - ?proxy:Uri.t -> - ?headers:(string * string) list -> - ?data:string -> - ?options:string list -> - Uri.t -> - result Lwt.t + ?proxy:Uri.t + -> ?headers:(string * string) list + -> ?data:string + -> ?options:string list + -> Uri.t + -> result Lwt.t diff --git a/controller/bindings/locale/locale.mli b/controller/bindings/locale/locale.mli index 838a614d..97559ace 100644 --- a/controller/bindings/locale/locale.mli +++ b/controller/bindings/locale/locale.mli @@ -1,4 +1,7 @@ val get_lang : unit -> string option Lwt.t + val set_lang : string -> unit Lwt.t + val get_keymap : unit -> string option Lwt.t + val set_keymap : string -> unit Lwt.t diff --git a/controller/bindings/rauc/rauc.ml b/controller/bindings/rauc/rauc.ml index 525cf596..1470908d 100644 --- a/controller/bindings/rauc/rauc.ml +++ b/controller/bindings/rauc/rauc.ml @@ -34,13 +34,13 @@ module Slot = struct | SystemA -> "system.a" | SystemB -> "system.b" - type status = { - device : string; - class' : string; - state : string; - version : string; - installed_timestamp : string; - } + type status = + { device: string + ; class': string + ; state: string + ; version: string + ; installed_timestamp: string + } [@@deriving sexp] end @@ -58,12 +58,13 @@ let mark_slot daemon slot status = else Lwt.fail_with "Wrong slot marked." let mark_good daemon slot = mark_slot daemon slot "good" + let mark_active daemon slot = mark_slot daemon slot "active" -type status = { - a : Slot.status; - b : Slot.status; -} +type status = + { a: Slot.status + ; b: Slot.status + } [@@deriving sexp] let json_of_status status = status |> sexp_of_status |> Ezjsonm.t_of_sexp @@ -77,12 +78,11 @@ let slot_status_of_obus (o : (string * OBus_value.V.single) list) : Slot.status | Some (Basic (String s)) -> s | _ -> failwith (Format.sprintf "could not get string from field %s" key) in - { - device = get_string "device" o; - class' = get_string "class" o; - state = get_string "state" o; - version = get_string "bundle.version" o; - installed_timestamp = get_string "installed.timestamp" o; + { device= get_string "device" o + ; class'= get_string "class" o + ; state= get_string "state" o + ; version= get_string "bundle.version" o + ; installed_timestamp= get_string "installed.timestamp" o } let get_status daemon = @@ -90,9 +90,8 @@ let get_status daemon = OBus_method.call De_pengutronix_rauc_Installer.m_GetSlotStatus (proxy daemon) () in - { - a = slot_status_of_obus (List.assoc "system.a" status_assoc); - b = slot_status_of_obus (List.assoc "system.b" status_assoc); + { a= slot_status_of_obus (List.assoc "system.a" status_assoc) + ; b= slot_status_of_obus (List.assoc "system.b" status_assoc) } |> return @@ -128,38 +127,46 @@ let install daemon source = (* Auto generated with obus-gen-client *) module De_pengutronix_rauc_Installer : sig val install : OBus_proxy.t -> source:string -> unit Lwt.t + val info : OBus_proxy.t -> bundle:string -> (string * string) Lwt.t val mark : - OBus_proxy.t -> - state:string -> - slot_identifier:string -> - (string * string) Lwt.t + OBus_proxy.t + -> state:string + -> slot_identifier:string + -> (string * string) Lwt.t val get_slot_status : OBus_proxy.t -> (string * (string * OBus_value.V.single) list) list Lwt.t val get_primary : OBus_proxy.t -> string Lwt.t + val completed : OBus_proxy.t -> int OBus_signal.t - val operation : OBus_proxy.t -> (string, [ `readable ]) OBus_property.t - val last_error : OBus_proxy.t -> (string, [ `readable ]) OBus_property.t + + val operation : OBus_proxy.t -> (string, [`readable]) OBus_property.t + + val last_error : OBus_proxy.t -> (string, [`readable]) OBus_property.t val progress : - OBus_proxy.t -> (int * string * int, [ `readable ]) OBus_property.t + OBus_proxy.t -> (int * string * int, [`readable]) OBus_property.t - val compatible : OBus_proxy.t -> (string, [ `readable ]) OBus_property.t - val variant : OBus_proxy.t -> (string, [ `readable ]) OBus_property.t - val boot_slot : OBus_proxy.t -> (string, [ `readable ]) OBus_property.t + val compatible : OBus_proxy.t -> (string, [`readable]) OBus_property.t + + val variant : OBus_proxy.t -> (string, [`readable]) OBus_property.t + + val boot_slot : OBus_proxy.t -> (string, [`readable]) OBus_property.t end = struct open De_pengutronix_rauc_Installer let install proxy ~source = OBus_method.call m_Install proxy source + let info proxy ~bundle = OBus_method.call m_Info proxy bundle let mark proxy ~state ~slot_identifier = OBus_method.call m_Mark proxy (state, slot_identifier) let get_slot_status proxy = OBus_method.call m_GetSlotStatus proxy () + let get_primary proxy = OBus_method.call m_GetPrimary proxy () let completed proxy = @@ -171,6 +178,7 @@ end = struct (OBus_signal.make s_Completed proxy) let operation proxy = OBus_property.make p_Operation proxy + let last_error proxy = OBus_property.make p_LastError proxy let progress proxy = @@ -179,6 +187,8 @@ end = struct (OBus_property.make p_Progress proxy) let compatible proxy = OBus_property.make p_Compatible proxy + let variant proxy = OBus_property.make p_Variant proxy + let boot_slot proxy = OBus_property.make p_BootSlot proxy end diff --git a/controller/bindings/rauc/rauc.mli b/controller/bindings/rauc/rauc.mli index 96a57830..8823c13b 100644 --- a/controller/bindings/rauc/rauc.mli +++ b/controller/bindings/rauc/rauc.mli @@ -9,16 +9,17 @@ module Slot : sig | SystemB val t_of_string : string -> t + val string_of_t : t -> string - type status = { - device : string; - class' : string; - state : string; - (* Fields that are only available when installed via RAUC (not from installer script)*) - version : string; - installed_timestamp : string; - } + type status = + { device: string + ; class': string + ; state: string + (* Fields that are only available when installed via RAUC (not from installer script)*) + ; version: string + ; installed_timestamp: string + } [@@deriving sexp] end @@ -33,10 +34,10 @@ val mark_good : t -> Slot.t -> unit Lwt.t val mark_active : t -> Slot.t -> unit Lwt.t (** Rauc status *) -type status = { - a : Slot.status; - b : Slot.status; -} +type status = + { a: Slot.status + ; b: Slot.status + } [@@deriving sexp] (** Encode status as json *) diff --git a/controller/bindings/screen-settings/screen_settings.ml b/controller/bindings/screen-settings/screen_settings.ml index a634efb9..96892a46 100644 --- a/controller/bindings/screen-settings/screen_settings.ml +++ b/controller/bindings/screen-settings/screen_settings.ml @@ -1,6 +1,7 @@ open Lwt let log_src = Logs.Src.create "screen-scaling" + let settings_file = "/var/lib/gui-localization/screen-scaling" (* Scaling options *) @@ -38,13 +39,15 @@ let label_of_scaling = function let set_scaling scaling = match scaling with | Default -> - Lwt_unix.file_exists settings_file >>= fun exists -> + Lwt_unix.file_exists settings_file + >>= fun exists -> if exists then Lwt_unix.unlink settings_file else return () | _ -> Util.write_to_file log_src settings_file (string_of_scaling scaling) let get_scaling () = - Lwt_unix.file_exists settings_file >>= fun exists -> + Lwt_unix.file_exists settings_file + >>= fun exists -> if exists then - Util.read_from_file log_src settings_file >|= fun s -> - s |> scaling_of_string |> Option.value ~default:Default + Util.read_from_file log_src settings_file + >|= fun s -> s |> scaling_of_string |> Option.value ~default:Default else return Default diff --git a/controller/bindings/screen-settings/screen_settings.mli b/controller/bindings/screen-settings/screen_settings.mli index db662923..003ccd39 100644 --- a/controller/bindings/screen-settings/screen_settings.mli +++ b/controller/bindings/screen-settings/screen_settings.mli @@ -4,7 +4,11 @@ type scaling = | Native val string_of_scaling : scaling -> string + val label_of_scaling : scaling -> string + val scaling_of_string : string -> scaling option + val get_scaling : unit -> scaling Lwt.t + val set_scaling : scaling -> unit Lwt.t diff --git a/controller/bindings/systemd/systemd.ml b/controller/bindings/systemd/systemd.ml index 7ea89f08..b0364dd2 100644 --- a/controller/bindings/systemd/systemd.ml +++ b/controller/bindings/systemd/systemd.ml @@ -14,7 +14,7 @@ module Manager = struct let peer = OBus_peer.make ~connection:system_bus ~name:"org.freedesktop.systemd1" in - OBus_proxy.make ~peer ~path:[ "org"; "freedesktop"; "systemd1" ] |> return + OBus_proxy.make ~peer ~path:["org"; "freedesktop"; "systemd1"] |> return type system_state = | Initializing @@ -82,10 +82,15 @@ module Org_freedesktop_systemd1_Manager = struct open Org_freedesktop_systemd1_Manager let version proxy = OBus_property.make p_Version proxy + let features proxy = OBus_property.make p_Features proxy + let virtualization proxy = OBus_property.make p_Virtualization proxy + let architecture proxy = OBus_property.make p_Architecture proxy + let tainted proxy = OBus_property.make p_Tainted proxy + let firmware_timestamp proxy = OBus_property.make p_FirmwareTimestamp proxy let firmware_timestamp_monotonic proxy = @@ -153,6 +158,7 @@ module Org_freedesktop_systemd1_Manager = struct OBus_property.make p_UnitsLoadFinishTimestampMonotonic proxy let log_level proxy = OBus_property.make p_LogLevel proxy + let log_target proxy = OBus_property.make p_LogTarget proxy let nnames proxy = @@ -181,9 +187,13 @@ module Org_freedesktop_systemd1_Manager = struct (OBus_property.make p_NFailedJobs proxy) let progress proxy = OBus_property.make p_Progress proxy + let environment proxy = OBus_property.make p_Environment proxy + let confirm_spawn proxy = OBus_property.make p_ConfirmSpawn proxy + let show_status proxy = OBus_property.make p_ShowStatus proxy + let unit_path proxy = OBus_property.make p_UnitPath proxy let default_standard_output proxy = @@ -199,8 +209,11 @@ module Org_freedesktop_systemd1_Manager = struct OBus_property.make p_ShutdownWatchdogUSec proxy let service_watchdogs proxy = OBus_property.make p_ServiceWatchdogs proxy + let control_group proxy = OBus_property.make p_ControlGroup proxy + let system_state proxy = OBus_property.make p_SystemState proxy + let exit_code proxy = OBus_property.make p_ExitCode proxy let default_timer_accuracy_usec proxy = @@ -270,7 +283,9 @@ module Org_freedesktop_systemd1_Manager = struct OBus_property.make p_DefaultLimitNOFILESoft proxy let default_limit_as proxy = OBus_property.make p_DefaultLimitAS proxy + let default_limit_assoft proxy = OBus_property.make p_DefaultLimitASSoft proxy + let default_limit_nproc proxy = OBus_property.make p_DefaultLimitNPROC proxy let default_limit_nprocsoft proxy = @@ -315,6 +330,7 @@ module Org_freedesktop_systemd1_Manager = struct OBus_property.make p_DefaultLimitRTTIMESoft proxy let default_tasks_max proxy = OBus_property.make p_DefaultTasksMax proxy + let timer_slack_nsec proxy = OBus_property.make p_TimerSlackNSec proxy let get_unit proxy x1 = @@ -415,6 +431,7 @@ module Org_freedesktop_systemd1_Manager = struct OBus_method.call m_SetUnitProperties proxy (x1, x2, x3) let ref_unit proxy x1 = OBus_method.call m_RefUnit proxy x1 + let unref_unit proxy x1 = OBus_method.call m_UnrefUnit proxy x1 let start_transient_unit proxy x1 x2 x3 x4 = @@ -447,12 +464,12 @@ module Org_freedesktop_systemd1_Manager = struct let x1 = List.map (fun (x1, x2, x3, x4, x5, x6) -> - ( Int32.to_int x1, - x2, - x3, - x4, - OBus_proxy.make ~peer:(OBus_context.sender context) ~path:x5, - OBus_proxy.make ~peer:(OBus_context.sender context) ~path:x6 + ( Int32.to_int x1 + , x2 + , x3 + , x4 + , OBus_proxy.make ~peer:(OBus_context.sender context) ~path:x5 + , OBus_proxy.make ~peer:(OBus_context.sender context) ~path:x6 ) ) x1 @@ -467,12 +484,12 @@ module Org_freedesktop_systemd1_Manager = struct let x1 = List.map (fun (x1, x2, x3, x4, x5, x6) -> - ( Int32.to_int x1, - x2, - x3, - x4, - OBus_proxy.make ~peer:(OBus_context.sender context) ~path:x5, - OBus_proxy.make ~peer:(OBus_context.sender context) ~path:x6 + ( Int32.to_int x1 + , x2 + , x3 + , x4 + , OBus_proxy.make ~peer:(OBus_context.sender context) ~path:x5 + , OBus_proxy.make ~peer:(OBus_context.sender context) ~path:x6 ) ) x1 @@ -484,6 +501,7 @@ module Org_freedesktop_systemd1_Manager = struct OBus_method.call m_CancelJob proxy x1 let clear_jobs proxy = OBus_method.call m_ClearJobs proxy () + let reset_failed proxy = OBus_method.call m_ResetFailed proxy () let list_units proxy = @@ -491,16 +509,16 @@ module Org_freedesktop_systemd1_Manager = struct let x1 = List.map (fun (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10) -> - ( x1, - x2, - x3, - x4, - x5, - x6, - OBus_proxy.make ~peer:(OBus_context.sender context) ~path:x7, - Int32.to_int x8, - x9, - OBus_proxy.make ~peer:(OBus_context.sender context) ~path:x10 + ( x1 + , x2 + , x3 + , x4 + , x5 + , x6 + , OBus_proxy.make ~peer:(OBus_context.sender context) ~path:x7 + , Int32.to_int x8 + , x9 + , OBus_proxy.make ~peer:(OBus_context.sender context) ~path:x10 ) ) x1 @@ -514,16 +532,16 @@ module Org_freedesktop_systemd1_Manager = struct let x1 = List.map (fun (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10) -> - ( x1, - x2, - x3, - x4, - x5, - x6, - OBus_proxy.make ~peer:(OBus_context.sender context) ~path:x7, - Int32.to_int x8, - x9, - OBus_proxy.make ~peer:(OBus_context.sender context) ~path:x10 + ( x1 + , x2 + , x3 + , x4 + , x5 + , x6 + , OBus_proxy.make ~peer:(OBus_context.sender context) ~path:x7 + , Int32.to_int x8 + , x9 + , OBus_proxy.make ~peer:(OBus_context.sender context) ~path:x10 ) ) x1 @@ -537,16 +555,16 @@ module Org_freedesktop_systemd1_Manager = struct let x1 = List.map (fun (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10) -> - ( x1, - x2, - x3, - x4, - x5, - x6, - OBus_proxy.make ~peer:(OBus_context.sender context) ~path:x7, - Int32.to_int x8, - x9, - OBus_proxy.make ~peer:(OBus_context.sender context) ~path:x10 + ( x1 + , x2 + , x3 + , x4 + , x5 + , x6 + , OBus_proxy.make ~peer:(OBus_context.sender context) ~path:x7 + , Int32.to_int x8 + , x9 + , OBus_proxy.make ~peer:(OBus_context.sender context) ~path:x10 ) ) x1 @@ -560,16 +578,16 @@ module Org_freedesktop_systemd1_Manager = struct let x1 = List.map (fun (x1, x2, x3, x4, x5, x6, x7, x8, x9, x10) -> - ( x1, - x2, - x3, - x4, - x5, - x6, - OBus_proxy.make ~peer:(OBus_context.sender context) ~path:x7, - Int32.to_int x8, - x9, - OBus_proxy.make ~peer:(OBus_context.sender context) ~path:x10 + ( x1 + , x2 + , x3 + , x4 + , x5 + , x6 + , OBus_proxy.make ~peer:(OBus_context.sender context) ~path:x7 + , Int32.to_int x8 + , x9 + , OBus_proxy.make ~peer:(OBus_context.sender context) ~path:x10 ) ) x1 @@ -581,12 +599,12 @@ module Org_freedesktop_systemd1_Manager = struct let x1 = List.map (fun (x1, x2, x3, x4, x5, x6) -> - ( Int32.to_int x1, - x2, - x3, - x4, - OBus_proxy.make ~peer:(OBus_context.sender context) ~path:x5, - OBus_proxy.make ~peer:(OBus_context.sender context) ~path:x6 + ( Int32.to_int x1 + , x2 + , x3 + , x4 + , OBus_proxy.make ~peer:(OBus_context.sender context) ~path:x5 + , OBus_proxy.make ~peer:(OBus_context.sender context) ~path:x6 ) ) x1 @@ -594,21 +612,32 @@ module Org_freedesktop_systemd1_Manager = struct return x1 let subscribe proxy = OBus_method.call m_Subscribe proxy () + let unsubscribe proxy = OBus_method.call m_Unsubscribe proxy () + let dump proxy = OBus_method.call m_Dump proxy () let dump_by_file_descriptor proxy = OBus_method.call m_DumpByFileDescriptor proxy () let reload proxy = OBus_method.call m_Reload proxy () + let reexecute proxy = OBus_method.call m_Reexecute proxy () + let exit proxy = OBus_method.call m_Exit proxy () + let reboot proxy = OBus_method.call m_Reboot proxy () + let power_off proxy = OBus_method.call m_PowerOff proxy () + let halt proxy = OBus_method.call m_Halt proxy () + let kexec proxy = OBus_method.call m_KExec proxy () + let switch_root proxy x1 x2 = OBus_method.call m_SwitchRoot proxy (x1, x2) + let set_environment proxy x1 = OBus_method.call m_SetEnvironment proxy x1 + let unset_environment proxy x1 = OBus_method.call m_UnsetEnvironment proxy x1 let unset_and_set_environment proxy x1 x2 = @@ -713,7 +742,9 @@ module Org_freedesktop_systemd1_Manager = struct (OBus_signal.make s_JobRemoved proxy) let startup_finished proxy = OBus_signal.make s_StartupFinished proxy + let unit_files_changed proxy = OBus_signal.make s_UnitFilesChanged proxy + let reloading proxy = OBus_signal.make s_Reloading proxy end @@ -721,41 +752,72 @@ module Org_freedesktop_systemd1_Unit = struct open Org_freedesktop_systemd1_Unit let id proxy = OBus_property.make p_Id proxy + let names proxy = OBus_property.make p_Names proxy + let following proxy = OBus_property.make p_Following proxy + let requires proxy = OBus_property.make p_Requires proxy + let requisite proxy = OBus_property.make p_Requisite proxy + let wants proxy = OBus_property.make p_Wants proxy + let binds_to proxy = OBus_property.make p_BindsTo proxy + let part_of proxy = OBus_property.make p_PartOf proxy + let required_by proxy = OBus_property.make p_RequiredBy proxy + let requisite_of proxy = OBus_property.make p_RequisiteOf proxy + let wanted_by proxy = OBus_property.make p_WantedBy proxy + let bound_by proxy = OBus_property.make p_BoundBy proxy + let consists_of proxy = OBus_property.make p_ConsistsOf proxy + let conflicts proxy = OBus_property.make p_Conflicts proxy + let conflicted_by proxy = OBus_property.make p_ConflictedBy proxy + let before proxy = OBus_property.make p_Before proxy + let after proxy = OBus_property.make p_After proxy + let on_failure proxy = OBus_property.make p_OnFailure proxy + let triggers proxy = OBus_property.make p_Triggers proxy + let triggered_by proxy = OBus_property.make p_TriggeredBy proxy + let propagates_reload_to proxy = OBus_property.make p_PropagatesReloadTo proxy let reload_propagated_from proxy = OBus_property.make p_ReloadPropagatedFrom proxy let joins_namespace_of proxy = OBus_property.make p_JoinsNamespaceOf proxy + let requires_mounts_for proxy = OBus_property.make p_RequiresMountsFor proxy + let documentation proxy = OBus_property.make p_Documentation proxy + let description proxy = OBus_property.make p_Description proxy + let load_state proxy = OBus_property.make p_LoadState proxy + let active_state proxy = OBus_property.make p_ActiveState proxy + let sub_state proxy = OBus_property.make p_SubState proxy + let fragment_path proxy = OBus_property.make p_FragmentPath proxy + let source_path proxy = OBus_property.make p_SourcePath proxy + let drop_in_paths proxy = OBus_property.make p_DropInPaths proxy + let unit_file_state proxy = OBus_property.make p_UnitFileState proxy + let unit_file_preset proxy = OBus_property.make p_UnitFilePreset proxy let state_change_timestamp proxy = @@ -789,16 +851,19 @@ module Org_freedesktop_systemd1_Unit = struct OBus_property.make p_InactiveEnterTimestampMonotonic proxy let can_start proxy = OBus_property.make p_CanStart proxy + let can_stop proxy = OBus_property.make p_CanStop proxy + let can_reload proxy = OBus_property.make p_CanReload proxy + let can_isolate proxy = OBus_property.make p_CanIsolate proxy let job proxy = OBus_property.map_r_with_context (fun context x -> (fun (x1, x2) -> - ( Int32.to_int x1, - OBus_proxy.make ~peer:(OBus_context.sender context) ~path:x2 + ( Int32.to_int x1 + , OBus_proxy.make ~peer:(OBus_context.sender context) ~path:x2 ) ) x @@ -806,16 +871,22 @@ module Org_freedesktop_systemd1_Unit = struct (OBus_property.make p_Job proxy) let stop_when_unneeded proxy = OBus_property.make p_StopWhenUnneeded proxy + let refuse_manual_start proxy = OBus_property.make p_RefuseManualStart proxy + let refuse_manual_stop proxy = OBus_property.make p_RefuseManualStop proxy + let allow_isolate proxy = OBus_property.make p_AllowIsolate proxy let default_dependencies proxy = OBus_property.make p_DefaultDependencies proxy let on_failure_job_mode proxy = OBus_property.make p_OnFailureJobMode proxy + let ignore_on_isolate proxy = OBus_property.make p_IgnoreOnIsolate proxy + let need_daemon_reload proxy = OBus_property.make p_NeedDaemonReload proxy + let job_timeout_usec proxy = OBus_property.make p_JobTimeoutUSec proxy let job_running_timeout_usec proxy = @@ -827,7 +898,9 @@ module Org_freedesktop_systemd1_Unit = struct OBus_property.make p_JobTimeoutRebootArgument proxy let condition_result proxy = OBus_property.make p_ConditionResult proxy + let assert_result proxy = OBus_property.make p_AssertResult proxy + let condition_timestamp proxy = OBus_property.make p_ConditionTimestamp proxy let condition_timestamp_monotonic proxy = @@ -857,7 +930,9 @@ module Org_freedesktop_systemd1_Unit = struct (OBus_property.make p_Asserts proxy) let load_error proxy = OBus_property.make p_LoadError proxy + let transient proxy = OBus_property.make p_Transient proxy + let perpetual proxy = OBus_property.make p_Perpetual proxy let start_limit_interval_usec proxy = @@ -869,10 +944,15 @@ module Org_freedesktop_systemd1_Unit = struct (OBus_property.make p_StartLimitBurst proxy) let start_limit_action proxy = OBus_property.make p_StartLimitAction proxy + let failure_action proxy = OBus_property.make p_FailureAction proxy + let success_action proxy = OBus_property.make p_SuccessAction proxy + let reboot_argument proxy = OBus_property.make p_RebootArgument proxy + let invocation_id proxy = OBus_property.make p_InvocationID proxy + let collect_mode proxy = OBus_property.make p_CollectMode proxy let start proxy x1 = @@ -924,5 +1004,6 @@ module Org_freedesktop_systemd1_Unit = struct OBus_method.call m_SetProperties proxy (x1, x2) let ref proxy = OBus_method.call m_Ref proxy () + let unref proxy = OBus_method.call m_Unref proxy () end diff --git a/controller/bindings/timedate/timedate.ml b/controller/bindings/timedate/timedate.ml index e24d6230..c2622532 100644 --- a/controller/bindings/timedate/timedate.ml +++ b/controller/bindings/timedate/timedate.ml @@ -24,7 +24,7 @@ let daemon () = return peer let proxy daemon = - OBus_proxy.make ~peer:daemon ~path:[ "org"; "freedesktop"; "timedate1" ] + OBus_proxy.make ~peer:daemon ~path:["org"; "freedesktop"; "timedate1"] let get_active_timezone daemon = let%lwt raw_tz = @@ -34,37 +34,55 @@ let get_active_timezone daemon = if String.length raw_tz = 0 then None |> return else Some raw_tz |> return let get_current_time daemon = - Lwt_process.pread ("", [| "date"; "+%Y-%m-%d %H:%M UTC%z" |]) + Lwt_process.pread ("", [|"date"; "+%Y-%m-%d %H:%M UTC%z"|]) let get_available_timezones daemon = (* Newer versions of systemd add a DBus property for this. *) - Lwt_process.pread_lines ("", [| "timedatectl"; "list-timezones" |]) + Lwt_process.pread_lines ("", [|"timedatectl"; "list-timezones"|]) |> Lwt_stream.to_list (* Auto generated with obus-gen-client *) module Org_freedesktop_timedate1 : sig - val timezone : OBus_proxy.t -> (string, [ `readable ]) OBus_property.t - val local_rtc : OBus_proxy.t -> (bool, [ `readable ]) OBus_property.t - val can_ntp : OBus_proxy.t -> (bool, [ `readable ]) OBus_property.t - val ntp : OBus_proxy.t -> (bool, [ `readable ]) OBus_property.t - val ntpsynchronized : OBus_proxy.t -> (bool, [ `readable ]) OBus_property.t - val time_usec : OBus_proxy.t -> (int64, [ `readable ]) OBus_property.t - val rtctime_usec : OBus_proxy.t -> (int64, [ `readable ]) OBus_property.t + val timezone : OBus_proxy.t -> (string, [`readable]) OBus_property.t + + val local_rtc : OBus_proxy.t -> (bool, [`readable]) OBus_property.t + + val can_ntp : OBus_proxy.t -> (bool, [`readable]) OBus_property.t + + val ntp : OBus_proxy.t -> (bool, [`readable]) OBus_property.t + + val ntpsynchronized : OBus_proxy.t -> (bool, [`readable]) OBus_property.t + + val time_usec : OBus_proxy.t -> (int64, [`readable]) OBus_property.t + + val rtctime_usec : OBus_proxy.t -> (int64, [`readable]) OBus_property.t + val set_time : OBus_proxy.t -> int64 -> bool -> bool -> unit Lwt.t + val set_timezone : OBus_proxy.t -> string -> bool -> unit Lwt.t + val set_local_rtc : OBus_proxy.t -> bool -> bool -> bool -> unit Lwt.t + val set_ntp : OBus_proxy.t -> bool -> bool -> unit Lwt.t end = struct open Org_freedesktop_timedate1 let timezone proxy = OBus_property.make p_Timezone proxy + let local_rtc proxy = OBus_property.make p_LocalRTC proxy + let can_ntp proxy = OBus_property.make p_CanNTP proxy + let ntp proxy = OBus_property.make p_NTP proxy + let ntpsynchronized proxy = OBus_property.make p_NTPSynchronized proxy + let time_usec proxy = OBus_property.make p_TimeUSec proxy + let rtctime_usec proxy = OBus_property.make p_RTCTimeUSec proxy + let set_time proxy x1 x2 x3 = OBus_method.call m_SetTime proxy (x1, x2, x3) + let set_timezone proxy x1 x2 = OBus_method.call m_SetTimezone proxy (x1, x2) let set_local_rtc proxy x1 x2 x3 = diff --git a/controller/bindings/timedate/timedate.mli b/controller/bindings/timedate/timedate.mli index 6ba82954..d6222ce6 100644 --- a/controller/bindings/timedate/timedate.mli +++ b/controller/bindings/timedate/timedate.mli @@ -19,15 +19,25 @@ val get_configured_timezone : unit -> string option Lwt.t val set_timezone : string -> unit Lwt.t module Org_freedesktop_timedate1 : sig - val timezone : OBus_proxy.t -> (string, [ `readable ]) OBus_property.t - val local_rtc : OBus_proxy.t -> (bool, [ `readable ]) OBus_property.t - val can_ntp : OBus_proxy.t -> (bool, [ `readable ]) OBus_property.t - val ntp : OBus_proxy.t -> (bool, [ `readable ]) OBus_property.t - val ntpsynchronized : OBus_proxy.t -> (bool, [ `readable ]) OBus_property.t - val time_usec : OBus_proxy.t -> (int64, [ `readable ]) OBus_property.t - val rtctime_usec : OBus_proxy.t -> (int64, [ `readable ]) OBus_property.t + val timezone : OBus_proxy.t -> (string, [`readable]) OBus_property.t + + val local_rtc : OBus_proxy.t -> (bool, [`readable]) OBus_property.t + + val can_ntp : OBus_proxy.t -> (bool, [`readable]) OBus_property.t + + val ntp : OBus_proxy.t -> (bool, [`readable]) OBus_property.t + + val ntpsynchronized : OBus_proxy.t -> (bool, [`readable]) OBus_property.t + + val time_usec : OBus_proxy.t -> (int64, [`readable]) OBus_property.t + + val rtctime_usec : OBus_proxy.t -> (int64, [`readable]) OBus_property.t + val set_time : OBus_proxy.t -> int64 -> bool -> bool -> unit Lwt.t + val set_timezone : OBus_proxy.t -> string -> bool -> unit Lwt.t + val set_local_rtc : OBus_proxy.t -> bool -> bool -> bool -> unit Lwt.t + val set_ntp : OBus_proxy.t -> bool -> bool -> unit Lwt.t end diff --git a/controller/bindings/util/util.ml b/controller/bindings/util/util.ml index aaf17fc5..11643945 100644 --- a/controller/bindings/util/util.ml +++ b/controller/bindings/util/util.ml @@ -33,7 +33,7 @@ let read_from_file log_src path = let write_to_file log_src path str = try - let%lwt fd = Lwt_unix.openfile path [ O_WRONLY; O_CREAT; O_TRUNC ] 0o755 in + let%lwt fd = Lwt_unix.openfile path [O_WRONLY; O_CREAT; O_TRUNC] 0o755 in let%lwt _bytes_written = Lwt_unix.write_string fd str 0 (String.length str) in diff --git a/controller/bindings/zerotier/zerotier.ml b/controller/bindings/zerotier/zerotier.ml index ffc59aeb..26cde27f 100644 --- a/controller/bindings/zerotier/zerotier.ml +++ b/controller/bindings/zerotier/zerotier.ml @@ -1,24 +1,25 @@ open Lwt let log_src = Logs.Src.create "zerotier" + let base_url = Uri.make ~scheme:"http" ~host:"localhost" ~port:9993 () let get_authtoken () = Util.read_from_file log_src "/var/lib/zerotier-one/authtoken.secret" -type status = { address : string } +type status = {address: string} let get_status () = Lwt_result.catch (fun () -> let%lwt authtoken = get_authtoken () in match%lwt Curl.request - ~headers:[ ("X-ZT1-Auth", authtoken) ] + ~headers:[("X-ZT1-Auth", authtoken)] (Uri.with_path base_url "status") with | RequestSuccess (_, body) -> let open Ezjsonm in from_string body |> get_dict |> List.assoc "address" |> get_string - |> fun address -> return { address } + |> fun address -> return {address} | RequestFailure error -> Lwt.fail_with (Curl.pretty_print_error error) ) diff --git a/controller/bindings/zerotier/zerotier.mli b/controller/bindings/zerotier/zerotier.mli index 6ee66d8e..2b8b0f0f 100644 --- a/controller/bindings/zerotier/zerotier.mli +++ b/controller/bindings/zerotier/zerotier.mli @@ -1,3 +1,3 @@ -type status = { address : string } +type status = {address: string} val get_status : unit -> (status, exn) Lwt_result.t diff --git a/controller/server/gui.ml b/controller/server/gui.ml index 280f0c90..a8b5c35d 100644 --- a/controller/server/gui.ml +++ b/controller/server/gui.ml @@ -7,7 +7,7 @@ let log_src = Logs.Src.create "gui" (* Middleware that makes static content available *) let static () = let static_dir = Util.resource_path (Fpath.v "static") in - Logs.debug (fun m -> m "static content dir: %s" static_dir); + Logs.debug (fun m -> m "static content dir: %s" static_dir) ; Opium.Middleware.static ~local_path:static_dir ~uri_prefix:"/static" () let page html = @@ -21,17 +21,16 @@ let resp_json ?code json = let header key req = Cohttp.Header.get (Request.headers req) key -type 'a timeout_params = { - duration : float; - on_timeout : unit -> 'a Lwt.t; -} +type 'a timeout_params = + { duration: float + ; on_timeout: unit -> 'a Lwt.t + } -let with_timeout { duration; on_timeout } f = - [ - f (); - (let%lwt () = Lwt_unix.sleep duration in +let with_timeout {duration; on_timeout} f = + [ f () + ; (let%lwt () = Lwt_unix.sleep duration in on_timeout () - ); + ) ] |> Lwt.pick @@ -52,20 +51,18 @@ let error_handling = Lwt.return @@ resp_json ~code:`Internal_server_error @@ `O - [ - ("error", `Bool true); - ("message", `String (Printexc.to_string exn)); + [ ("error", `Bool true) + ; ("message", `String (Printexc.to_string exn)) ] | _ -> Lwt.return (page (Error_page.html - { - message = + { message= exn |> Sexplib.Std.sexp_of_exn - |> Sexplib.Sexp.to_string_hum; - request = - req |> Request.sexp_of_t |> Sexplib.Sexp.to_string_hum; + |> Sexplib.Sexp.to_string_hum + ; request= + req |> Request.sexp_of_t |> Sexplib.Sexp.to_string_hum } ) ) @@ -98,9 +95,9 @@ module LocalizationGui = struct let group_id, name = match String.split_on_char '/' re_spaced |> List.rev with (* An unscoped entry, e.g. UTC. *) - | [ singleton ] -> (singleton, singleton) + | [singleton] -> (singleton, singleton) (* A humble entry, likely scoped to continent, e.g. Europe/Amsterdam. *) - | [ name; group_id ] -> (group_id, name) + | [name; group_id] -> (group_id, name) (* A multi-hierarchical entry, e.g. America/Argentina/Buenos_Aires. *) | name :: group_sections -> (String.concat "/" (List.rev group_sections), name) @@ -119,47 +116,44 @@ module LocalizationGui = struct in let%lwt current_lang = Locale.get_lang () in let langs = - [ - ("cs_CZ.UTF-8", "Czech"); - ("nl_NL.UTF-8", "Dutch"); - ("en_UK.UTF-8", "English (UK)"); - ("en_US.UTF-8", "English (US)"); - ("fi_FI.UTF-8", "Finnish"); - ("fr_FR.UTF-8", "French"); - ("de_DE.UTF-8", "German"); - ("it_IT.UTF-8", "Italian"); - ("pl_PL.UTF-8", "Polish"); - ("es_ES.UTF-8", "Spanish"); + [ ("cs_CZ.UTF-8", "Czech") + ; ("nl_NL.UTF-8", "Dutch") + ; ("en_UK.UTF-8", "English (UK)") + ; ("en_US.UTF-8", "English (US)") + ; ("fi_FI.UTF-8", "Finnish") + ; ("fr_FR.UTF-8", "French") + ; ("de_DE.UTF-8", "German") + ; ("it_IT.UTF-8", "Italian") + ; ("pl_PL.UTF-8", "Polish") + ; ("es_ES.UTF-8", "Spanish") ] in let%lwt current_keymap = Locale.get_keymap () in let keymaps = - [ - ("cz", "Czech"); - ("nl", "Dutch"); - ("gb", "English (UK)"); - ("us", "English (US)"); - ("fi", "Finnish"); - ("fr", "French"); - ("de", "German"); - ("ch", "German (Switzerland)"); - ("it", "Italian"); - ("pl", "Polish"); - ("es", "Spanish"); + [ ("cz", "Czech") + ; ("nl", "Dutch") + ; ("gb", "English (UK)") + ; ("us", "English (US)") + ; ("fi", "Finnish") + ; ("fr", "French") + ; ("de", "German") + ; ("ch", "German (Switzerland)") + ; ("it", "Italian") + ; ("pl", "Polish") + ; ("es", "Spanish") ] in let%lwt current_scaling = Screen_settings.get_scaling () in Lwt.return (page (Localization_page.html - { - timezone_groups; - current_timezone; - langs; - current_lang; - keymaps; - current_keymap; - current_scaling; + { timezone_groups + ; current_timezone + ; langs + ; current_lang + ; keymaps + ; current_keymap + ; current_scaling } ) ) @@ -169,7 +163,7 @@ module LocalizationGui = struct let%lwt form_data = urlencoded_pairs_of_body req in let%lwt _ = match form_data |> List.assoc_opt "timezone" with - | Some [ tz_id ] -> Timedate.set_timezone tz_id + | Some [tz_id] -> Timedate.set_timezone tz_id | _ -> return () in "/localization" |> Uri.of_string |> redirect' @@ -178,7 +172,7 @@ module LocalizationGui = struct let%lwt form_data = urlencoded_pairs_of_body req in let%lwt _ = match form_data |> List.assoc_opt "lang" with - | Some [ lang ] -> Locale.set_lang lang + | Some [lang] -> Locale.set_lang lang | _ -> return () in "/localization" |> Uri.of_string |> redirect' @@ -187,7 +181,7 @@ module LocalizationGui = struct let%lwt form_data = urlencoded_pairs_of_body req in let%lwt _ = match form_data |> List.assoc_opt "keymap" with - | Some [ keymap ] -> Locale.set_keymap keymap + | Some [keymap] -> Locale.set_keymap keymap | _ -> return () in "/localization" |> Uri.of_string |> redirect' @@ -196,11 +190,11 @@ module LocalizationGui = struct let%lwt form_data = urlencoded_pairs_of_body req in let%lwt _ = match form_data |> List.assoc_opt "scaling" with - | Some [ opt ] -> ( - match Screen_settings.scaling_of_string opt with - | Some s -> Screen_settings.set_scaling s - | None -> fail_with (Format.sprintf "Unknown screen setting: %s" opt) - ) + | Some [opt] -> ( + match Screen_settings.scaling_of_string opt with + | Some s -> Screen_settings.set_scaling s + | None -> fail_with (Format.sprintf "Unknown screen setting: %s" opt) + ) | _ -> return () in "/localization" |> Uri.of_string |> redirect' @@ -220,30 +214,22 @@ module NetworkGui = struct let overview ~(connman : Manager.t) req = let%lwt all_services = Manager.get_services connman in - let%lwt proxy = Manager.get_default_proxy connman in - let%lwt interfaces = Network.Interface.get_all () in - let pp_proxy p = let uri = p |> Service.Proxy.to_uri ~include_userinfo:false |> Uri.to_string in match p.credentials with - | Some { user; password } -> + | Some {user; password} -> let password_indication = if password = "" then "" else ", password: *****" in uri ^ " (user: " ^ user ^ password_indication ^ ")" | None -> uri in - let params : Network_list_page.params = - { - proxy = proxy |> Option.map pp_proxy; - services = all_services; - interfaces; - } + {proxy= proxy |> Option.map pp_proxy; services= all_services; interfaces} in match header "accept" req with | Some "application/json" -> @@ -285,7 +271,6 @@ module NetworkGui = struct let open Service.Proxy in let non_empty s = if s = "" then None else Some s in let opt_int_of_string s = try Some (int_of_string s) with _ -> None in - match form_data |> List.assoc_opt "proxy_enabled" with | None -> return None | Some _ -> ( @@ -306,7 +291,7 @@ module NetworkGui = struct in let password = match (keep_password, current_proxy_opt) with - | true, Some { host; port; credentials = Some { user; password } } -> + | true, Some {host; port; credentials= Some {user; password}} -> if host_input = Some host && port_input = Some port && user_input = Some user @@ -379,11 +364,10 @@ module NetworkGui = struct let%lwt form_data = urlencoded_pairs_of_body req in let passphrase = match form_data |> List.assoc_opt "passphrase" with - | Some [ passphrase ] -> Connman.Agent.Passphrase passphrase + | Some [passphrase] -> Connman.Agent.Passphrase passphrase | _ -> Connman.Agent.None in let%lwt service = with_service ~connman (param req "id") in - let%lwt () = Connman.Service.connect ~input:passphrase service in redirect' (Uri.of_string "/network") @@ -391,10 +375,8 @@ module NetworkGui = struct let update ~(connman : Connman.Manager.t) req = let%lwt form_data = urlencoded_pairs_of_body req in let%lwt service = with_service ~connman (param req "id") in - (* Static IP *) let%lwt () = update_static_ip service form_data in - (* Proxy *) let%lwt current_proxy = Manager.get_default_proxy connman in let%lwt () = @@ -402,7 +384,6 @@ module NetworkGui = struct | None -> Connman.Service.set_direct_proxy service | Some proxy -> Connman.Service.set_manual_proxy service proxy in - (* Grant time for changes to take effect and return to overview *) let%lwt () = Lwt_unix.sleep 0.5 in redirect' (Uri.of_string "/network") @@ -410,12 +391,10 @@ module NetworkGui = struct (** Remove a service **) let remove ~(connman : Connman.Manager.t) req = let%lwt service = with_service ~connman (param req "id") in - (* Clear settings. *) let%lwt () = Connman.Service.set_direct_proxy service in let%lwt () = Connman.Service.set_nameservers service [] in let%lwt () = Connman.Service.set_dhcp_ipv4 service in - let%lwt () = Connman.Service.remove service in redirect' (Uri.of_string "/network") @@ -432,8 +411,9 @@ end module StatusGui = struct open Status_page - let shutdown () = Util.run_cmd_no_stdout [| "halt"; "--poweroff" |] - let reboot () = Util.run_cmd_no_stdout [| "reboot" |] + let shutdown () = Util.run_cmd_no_stdout [|"halt"; "--poweroff"|] + + let reboot () = Util.run_cmd_no_stdout [|"reboot"|] let switch_slot rauc target_slot = let%lwt () = Rauc.mark_active rauc target_slot in @@ -469,8 +449,7 @@ module StatusGui = struct ~ok:(fun s -> Status_page.Status s) ~error:(fun e -> Status_page.Error (Printexc.to_string e)) in - { health = health_state; update = update_state; rauc; booted_slot } - |> return + {health= health_state; update= update_state; rauc; booted_slot} |> return let exec_and_resp_ok f req = f req >|= (fun _ -> `String "Ok") >|= respond @@ -527,14 +506,13 @@ module RemoteMaintenanceGui = struct Systemd.Manager.start_unit systemd "zerotierone.service" in with_timeout - { - duration = 2.0; - on_timeout = + { duration= 2.0 + ; on_timeout= (fun () -> let msg = "Timeout starting remote maintenance service." in let%lwt () = Logs_lwt.err (fun m -> m "%s" msg) in fail_with msg - ); + ) } wait_until_zerotier_is_on ) diff --git a/controller/server/gui.mli b/controller/server/gui.mli index 91327ec9..9b8bd836 100644 --- a/controller/server/gui.mli +++ b/controller/server/gui.mli @@ -1,8 +1,8 @@ val start : - port:int -> - systemd:Systemd.Manager.t -> - health_s:Health.state Lwt_react.S.t -> - update_s:Update.state Lwt_react.S.t -> - rauc:Rauc.t -> - connman:Connman.Manager.t -> - unit Lwt.t + port:int + -> systemd:Systemd.Manager.t + -> health_s:Health.state Lwt_react.S.t + -> update_s:Update.state Lwt_react.S.t + -> rauc:Rauc.t + -> connman:Connman.Manager.t + -> unit Lwt.t diff --git a/controller/server/health.ml b/controller/server/health.ml index 4d2026da..03c56ac7 100644 --- a/controller/server/health.ml +++ b/controller/server/health.ml @@ -12,14 +12,13 @@ type state = let rec run ~systemd ~rauc ~set_state = let set state = - set_state state; + set_state state ; run ~systemd ~rauc ~set_state state in function | Pending -> ( (* Wait for 30 seconds *) let%lwt () = Lwt_unix.sleep 30.0 in - let open Systemd in (* Check what system state as systemd reports *) match%lwt Systemd.Manager.get_system_state systemd with diff --git a/controller/server/health.mli b/controller/server/health.mli index 145ddede..23a21882 100644 --- a/controller/server/health.mli +++ b/controller/server/health.mli @@ -8,6 +8,6 @@ type state = (** Start system health monitor *) val start : - systemd:Systemd.Manager.t -> - rauc:Rauc.t -> - state Lwt_react.signal * unit Lwt.t + systemd:Systemd.Manager.t + -> rauc:Rauc.t + -> state Lwt_react.signal * unit Lwt.t diff --git a/controller/server/info.ml b/controller/server/info.ml index bc93f0f7..59f21af8 100644 --- a/controller/server/info.ml +++ b/controller/server/info.ml @@ -2,15 +2,15 @@ open Lwt let log_src = Logs.Src.create "info" -type t = { - app : string; - version : string; - update_url : string; - kiosk_url : string; - machine_id : string; - zerotier_address : string option; - local_time : string; -} +type t = + { app: string + ; version: string + ; update_url: string + ; kiosk_url: string + ; machine_id: string + ; zerotier_address: string option + ; local_time: string + } include Config.System @@ -19,7 +19,7 @@ let rec grouped n s = let l = String.length s in if n <= 0 then invalid_arg "Group size must be above 0" else if l = 0 then [] - else if l <= n then [ s ] + else if l <= n then [s] else List.cons (String.sub s 0 n) (grouped n (String.sub s n (l - n))) let get () = @@ -46,13 +46,12 @@ let get () = | None -> return "No timezone" in let local_time = current_time ^ " (" ^ timezone ^ ")" in - { - app = "PlayOS Controller"; - version; - update_url; - kiosk_url; - machine_id; - zerotier_address; - local_time; + { app= "PlayOS Controller" + ; version + ; update_url + ; kiosk_url + ; machine_id + ; zerotier_address + ; local_time } |> return diff --git a/controller/server/logging.ml b/controller/server/logging.ml index 78f5d029..0a965040 100644 --- a/controller/server/logging.ml +++ b/controller/server/logging.ml @@ -7,11 +7,10 @@ let reporter () = let buf_fmt ~like = let b = Buffer.create 512 in - ( Fmt.with_buffer ~like b, - fun () -> + ( Fmt.with_buffer ~like b + , fun () -> let m = Buffer.contents b in - Buffer.reset b; - m + Buffer.reset b ; m ) in let app, app_flush = buf_fmt ~like:Fmt.stdout in @@ -24,13 +23,10 @@ let reporter () = | Logs.App -> Lwt_io.write Lwt_io.stdout (app_flush ()) | _ -> Lwt_io.write Lwt_io.stderr (dst_flush ()) in - let unblock () = - over (); - Lwt.return_unit - in - Lwt.finalize write unblock |> Lwt.ignore_result; + let unblock () = over () ; Lwt.return_unit in + Lwt.finalize write unblock |> Lwt.ignore_result ; k () in reporter.Logs.report src level ~over:(fun () -> ()) k msgf in - { Logs.report } + {Logs.report} diff --git a/controller/server/network.ml b/controller/server/network.ml index 5f05175b..45c0754c 100644 --- a/controller/server/network.ml +++ b/controller/server/network.ml @@ -9,7 +9,6 @@ let enable_and_scan_wifi_devices ~connman = (let open Connman in (* Get all available technolgies *) let%lwt technologies = Manager.get_technologies connman in - (* enable all wifi devices *) let%lwt () = technologies @@ -18,25 +17,22 @@ let enable_and_scan_wifi_devices ~connman = ) |> List.map Technology.enable |> Lwt.join in - (* and start a scan. *) let%lwt () = technologies |> List.filter (fun (t : Technology.t) -> t.type' = Technology.Wifi) |> List.map Technology.scan |> Lwt.join in - return_unit ) (* Add a timeout to scan *) - |> fun p -> [ p; Lwt_unix.timeout 30.0 ] |> Lwt.pick + |> fun p -> [p; Lwt_unix.timeout 30.0] |> Lwt.pick ) let init ~connman = let%lwt () = Logs_lwt.info ~src:log_src (fun m -> m "initializing network connections") in - match%lwt enable_and_scan_wifi_devices ~connman with | Ok () -> Lwt_result.return () | Error exn -> @@ -49,37 +45,35 @@ let init ~connman = Lwt_result.fail exn module Interface = struct - type t = { - index : int; - name : string; - address : string; - link_type : string; - } + type t = + { index: int + ; name: string + ; address: string + ; link_type: string + } [@@deriving sexp, protocol ~driver:(module Jsonm)] let to_json i = Ezjsonm.( dict - [ - ("index", i.index |> int); - ("name", i.name |> string); - ("address", i.address |> string); - ("link_type", i.link_type |> string); + [ ("index", i.index |> int) + ; ("name", i.name |> string) + ; ("address", i.address |> string) + ; ("link_type", i.link_type |> string) ] |> value ) let of_json j = let dict = Ezjsonm.get_dict j in - { - index = dict |> List.assoc "ifindex" |> Ezjsonm.get_int; - name = dict |> List.assoc "ifname" |> Ezjsonm.get_string; - address = dict |> List.assoc "address" |> Ezjsonm.get_string; - link_type = dict |> List.assoc "link_type" |> Ezjsonm.get_string; + { index= dict |> List.assoc "ifindex" |> Ezjsonm.get_int + ; name= dict |> List.assoc "ifname" |> Ezjsonm.get_string + ; address= dict |> List.assoc "address" |> Ezjsonm.get_string + ; link_type= dict |> List.assoc "link_type" |> Ezjsonm.get_string } let get_all () = - let command = ("/run/current-system/sw/bin/ip", [| "ip"; "-j"; "link" |]) in + let command = ("/run/current-system/sw/bin/ip", [|"ip"; "-j"; "link"|]) in let%lwt json = Lwt_process.pread command in json |> Ezjsonm.from_string |> Ezjsonm.value |> Ezjsonm.get_list of_json |> return diff --git a/controller/server/network.mli b/controller/server/network.mli index 17c8c761..37fc3c54 100644 --- a/controller/server/network.mli +++ b/controller/server/network.mli @@ -5,12 +5,12 @@ val init : connman:Connman.Manager.t -> (unit, exn) Lwt_result.t module Interface : sig (** Network interface *) - type t = { - index : int; - name : string; - address : string; - link_type : string; - } + type t = + { index: int + ; name: string + ; address: string + ; link_type: string + } [@@deriving sexp, protocol ~driver:(module Jsonm)] val to_json : t -> Ezjsonm.value diff --git a/controller/server/rauc_service.ml b/controller/server/rauc_service.ml index 335ffc68..c63cea22 100644 --- a/controller/server/rauc_service.ml +++ b/controller/server/rauc_service.ml @@ -25,8 +25,11 @@ end) : S = struct Rauc.get_status t let get_booted_slot () : Rauc.Slot.t Lwt.t = Rauc.get_booted_slot t + let mark_good = Rauc.mark_good t + let get_primary () : Rauc.Slot.t option Lwt.t = Rauc.get_primary t + let install : string -> unit Lwt.t = Rauc.install t end diff --git a/controller/server/server.ml b/controller/server/server.ml index 6bea925a..1b082e11 100644 --- a/controller/server/server.ml +++ b/controller/server/server.ml @@ -1,27 +1,20 @@ open Lwt let main debug port = - Logs.set_reporter (Logging.reporter ()); - + Logs.set_reporter (Logging.reporter ()) ; if debug then Logs.set_level (Some Logs.Debug) - else Logs.set_level (Some Logs.Info); - + else Logs.set_level (Some Logs.Info) ; let%lwt server_info = Info.get () in - let%lwt () = Logs_lwt.info (fun m -> m "PlayOS Controller Daemon (%s)" server_info.version ) in - (* Connect with systemd *) let%lwt systemd = Systemd.Manager.connect () in - (* Connect with RAUC *) let%lwt rauc = Rauc.daemon () in - let health_s, health_p = Health.start ~systemd ~rauc in - (* Log changes in health state *) let%lwt () = Lwt_react.S.( @@ -36,13 +29,10 @@ let main debug port = >|= keep ) in - (* Connect with ConnMan *) let%lwt connman = Connman.Manager.connect () in - (* Start the update mechanism *) let update_s, update_p = Update.start ~connman ~rauc in - (* Log changes in update mechanism state *) let%lwt () = Lwt_react.S.( @@ -57,10 +47,8 @@ let main debug port = >|= keep ) in - (* Start the GUI *) let gui_p = Gui.start ~systemd ~port ~rauc ~connman ~update_s ~health_s in - let%lwt () = (* Initialize Network, parallel to starting server *) ( match%lwt Network.init ~connman with @@ -71,26 +59,24 @@ let main debug port = ) ) <&> Lwt.pick - [ - (* Make sure all threads run forever. *) - gui_p (* GUI *); - update_p (* Update mechanism *); - health_p (* Health monitoring *); + [ (* Make sure all threads run forever. *) + gui_p (* GUI *) + ; update_p (* Update mechanism *) + ; health_p (* Health monitoring *) ] in - Logs_lwt.info (fun m -> m "terminating") let () = let open Cmdliner in let debug_a = - Arg.(flag (info ~doc:"Enable debug output." [ "d"; "debug" ]) |> value) + Arg.(flag (info ~doc:"Enable debug output." ["d"; "debug"]) |> value) in let port_a = Arg.( opt int 3333 (info ~doc:"Port on which to start gui (http server)." ~docv:"PORT" - [ "p"; "port" ] + ["p"; "port"] ) |> value ) diff --git a/controller/server/update.ml b/controller/server/update.ml index 5ce08984..1a855f2c 100644 --- a/controller/server/update.ml +++ b/controller/server/update.ml @@ -6,21 +6,20 @@ let log_src = Logs.Src.create "update" (* Version handling *) (** Type containing version information *) -type version_info = { - (* the latest available version *) - latest : Semver.t; (* version of currently booted system *) - booted : Semver.t; (* version of inactive system *) - inactive : Semver.t; -} +type version_info = + { (* the latest available version *) + latest: Semver.t (* version of currently booted system *) + ; booted: Semver.t (* version of inactive system *) + ; inactive: Semver.t + } let sexp_of_version_info v = let open Sexplib in Sexp.( List - [ - List [ Atom "latest"; Atom (Semver.to_string v.latest) ]; - List [ Atom "booted"; Atom (Semver.to_string v.booted) ]; - List [ Atom "inactive"; Atom (Semver.to_string v.inactive) ]; + [ List [Atom "latest"; Atom (Semver.to_string v.latest)] + ; List [Atom "booted"; Atom (Semver.to_string v.booted)] + ; List [Atom "inactive"; Atom (Semver.to_string v.inactive)] ] ) @@ -42,13 +41,14 @@ type state = type sleep_duration = float (* seconds *) -type config = { - error_backoff_duration : sleep_duration; - check_for_updates_interval : sleep_duration; -} +type config = + { error_backoff_duration: sleep_duration + ; check_for_updates_interval: sleep_duration + } module type ServiceDeps = sig module ClientI : Update_client.S + module RaucI : Rauc_service.S val config : config @@ -56,6 +56,7 @@ end module type UpdateService = sig val run : set_state:(state -> unit) -> state -> unit Lwt.t + val run_step : state -> state Lwt.t end @@ -64,7 +65,6 @@ let evaluate_version_info current_primary booted_slot version_info = let up_to_date_with_latest v = Semver.compare v version_info.latest >= 0 in let booted_up_to_date = up_to_date_with_latest version_info.booted in let inactive_up_to_date = up_to_date_with_latest version_info.inactive in - if booted_up_to_date && inactive_up_to_date then (* Should not happen during the automatic update process (one partition must always be older than latest upstream), but can happen if e.g. a newer @@ -106,23 +106,20 @@ module Make (Deps : ServiceDeps) : UpdateService = struct open Deps let sleep_error_backoff () = Lwt_unix.sleep config.error_backoff_duration + let sleep_update_check () = Lwt_unix.sleep config.check_for_updates_interval (** Get version information *) let get_version_info () = let%lwt latest = ClientI.get_latest_version () >|= semver_of_string in let%lwt rauc_status = RaucI.get_status () in - let system_a_version = rauc_status.a.version |> semver_of_string in let system_b_version = rauc_status.b.version |> semver_of_string in - match%lwt RaucI.get_booted_slot () with | SystemA -> - { latest; booted = system_a_version; inactive = system_b_version } - |> return + {latest; booted= system_a_version; inactive= system_b_version} |> return | SystemB -> - { latest; booted = system_b_version; inactive = system_a_version } - |> return + {latest; booted= system_b_version; inactive= system_a_version} |> return (* Update mechanism process *) @@ -205,25 +202,21 @@ module Make (Deps : ServiceDeps) : UpdateService = struct (** Finite state machine handling updates *) let rec run ~set_state state = let%lwt next_state = run_step state in - set_state next_state; - run ~set_state next_state + set_state next_state ; run ~set_state next_state end let default_config : config = - { - error_backoff_duration = 30.0; - check_for_updates_interval = 1. *. 60. *. 60.; - } + {error_backoff_duration= 30.0; check_for_updates_interval= 1. *. 60. *. 60.} let build_deps ~connman ~(rauc : Rauc.t) : (module ServiceDeps) Lwt.t = let config = default_config in let raucI = Rauc_service.build_module rauc in let clientI = Update_client.build_module connman in - let module Deps = struct let config = config module RaucI = (val raucI) + module ClientI = (val clientI) end in Lwt.return (module Deps : ServiceDeps) @@ -234,12 +227,10 @@ let start ~connman ~(rauc : Rauc.t) = let () = Logs.info ~src:log_src (fun m -> m "update URL: %s" Config.System.update_url) in - let service = let%lwt deps = build_deps ~connman ~rauc in let module UpdateServiceI = Make ((val deps)) in UpdateServiceI.run ~set_state initial_state in - let () = Logs.info ~src:log_src (fun m -> m "Started") in (state_s, service) diff --git a/controller/server/update.mli b/controller/server/update.mli index 8f37c2e3..dcf1b4eb 100644 --- a/controller/server/update.mli +++ b/controller/server/update.mli @@ -1,11 +1,11 @@ (** Type containing version information. *) -type version_info = { - (* the latest available version *) - latest : Semver.t; (* version of currently booted system *) - booted : Semver.t; (* version of inactive system *) - inactive : Semver.t; -} +type version_info = + { (* the latest available version *) + latest: Semver.t (* version of currently booted system *) + ; booted: Semver.t (* version of inactive system *) + ; inactive: Semver.t + } [@@deriving sexp_of] (** State of update mechanism *) @@ -24,15 +24,16 @@ type state = type sleep_duration = float (* seconds *) -type config = { - (* time to sleep in seconds until retrying after a (Curl/HTTP) error *) - error_backoff_duration : sleep_duration; - (* time to sleep in seconds between checking for available updates *) - check_for_updates_interval : sleep_duration; -} +type config = + { (* time to sleep in seconds until retrying after a (Curl/HTTP) error *) + error_backoff_duration: sleep_duration + ; (* time to sleep in seconds between checking for available updates *) + check_for_updates_interval: sleep_duration + } module type ServiceDeps = sig module ClientI : Update_client.S + module RaucI : Rauc_service.S val config : config @@ -40,6 +41,7 @@ end module type UpdateService = sig val run : set_state:(state -> unit) -> state -> unit Lwt.t + val run_step : state -> state Lwt.t end @@ -48,6 +50,6 @@ module Make (_ : ServiceDeps) : UpdateService (* top-level entrypoint that uses global Config.System and initializes all the dependencies *) val start : - connman:Connman.Manager.t -> - rauc:Rauc.t -> - state Lwt_react.signal * unit Lwt.t + connman:Connman.Manager.t + -> rauc:Rauc.t + -> state Lwt_react.signal * unit Lwt.t diff --git a/controller/server/update_client.ml b/controller/server/update_client.ml index 155b091d..988607d7 100644 --- a/controller/server/update_client.ml +++ b/controller/server/update_client.ml @@ -10,7 +10,9 @@ end module type UpdateClientDeps = sig val base_url : Uri.t + val download_dir : string + val get_proxy : unit -> Uri.t option Lwt.t end @@ -18,12 +20,15 @@ let make_deps ?(download_dir = "/tmp") get_proxy base_url : (module UpdateClientDeps) = (module struct let base_url = base_url + let get_proxy = get_proxy + let download_dir = download_dir end ) let bundle_name = Config.System.bundle_name + let bundle_file_name version = Format.sprintf "%s-%s.raucb" bundle_name version let ensure_trailing_slash uri = @@ -32,7 +37,9 @@ let ensure_trailing_slash uri = module UpdateClient (DepsI : UpdateClientDeps) = struct let get_proxy = DepsI.get_proxy + let download_dir = DepsI.download_dir + let base_url_with_trailing_slash = ensure_trailing_slash DepsI.base_url let download_url version_string = @@ -59,13 +66,12 @@ module UpdateClient (DepsI : UpdateClientDeps) = struct Format.sprintf "%s/%s" download_dir (bundle_file_name version) in let options = - [ - "--continue-at"; - "-" (* resume download *); - "--limit-rate"; - "10M"; - "--output"; - bundle_path; + [ "--continue-at" + ; "-" (* resume download *) + ; "--limit-rate" + ; "10M" + ; "--output" + ; bundle_path ] in let%lwt proxy = get_proxy () in diff --git a/controller/server/update_client.mli b/controller/server/update_client.mli index 14b0b7ba..a7326058 100644 --- a/controller/server/update_client.mli +++ b/controller/server/update_client.mli @@ -11,15 +11,17 @@ end module type UpdateClientDeps = sig val base_url : Uri.t + val download_dir : string + val get_proxy : unit -> Uri.t option Lwt.t end val make_deps : - ?download_dir:string -> - (unit -> Uri.t option Lwt.t) -> - Uri.t -> - (module UpdateClientDeps) + ?download_dir:string + -> (unit -> Uri.t option Lwt.t) + -> Uri.t + -> (module UpdateClientDeps) module Make (_ : UpdateClientDeps) : S diff --git a/controller/server/view/changelog_page.ml b/controller/server/view/changelog_page.ml index 6bc1d875..9a90250e 100644 --- a/controller/server/view/changelog_page.ml +++ b/controller/server/view/changelog_page.ml @@ -2,5 +2,5 @@ open Tyxml.Html let html changelog = Page.html ~current_page:Page.Changelog - ~header:(Page.header_title ~icon:Icon.document [ txt "Changelog" ]) - (div ~a:[ a_class [ "d-Markdown" ] ] [ Unsafe.data changelog ]) + ~header:(Page.header_title ~icon:Icon.document [txt "Changelog"]) + (div ~a:[a_class ["d-Markdown"]] [Unsafe.data changelog]) diff --git a/controller/server/view/changelog_page.mli b/controller/server/view/changelog_page.mli index eae7d1ea..a8a535fd 100644 --- a/controller/server/view/changelog_page.mli +++ b/controller/server/view/changelog_page.mli @@ -1 +1 @@ -val html : string -> [> Html_types.html ] Tyxml.Html.elt +val html : string -> [> Html_types.html] Tyxml.Html.elt diff --git a/controller/server/view/common/definition.ml b/controller/server/view/common/definition.ml index 41c7bed9..dd49d93d 100644 --- a/controller/server/view/common/definition.ml +++ b/controller/server/view/common/definition.ml @@ -1,14 +1,10 @@ open Tyxml.Html let list ?a = - dl - ~a: - ([ a_class [ "d-Definitions__Definitions" ] ] @ Option.value ~default:[] a) + dl ~a:([a_class ["d-Definitions__Definitions"]] @ Option.value ~default:[] a) let term ?a = - dt ~a:([ a_class [ "d-Definitions__Term" ] ] @ Option.value ~default:[] a) + dt ~a:([a_class ["d-Definitions__Term"]] @ Option.value ~default:[] a) let description ?a = - dt - ~a: - ([ a_class [ "d-Definitions__Description" ] ] @ Option.value ~default:[] a) + dt ~a:([a_class ["d-Definitions__Description"]] @ Option.value ~default:[] a) diff --git a/controller/server/view/common/definition.mli b/controller/server/view/common/definition.mli index 570131d5..88572383 100644 --- a/controller/server/view/common/definition.mli +++ b/controller/server/view/common/definition.mli @@ -1,14 +1,14 @@ val list : - ?a:[< Html_types.dl_attrib > `Class ] Tyxml.Html.attrib list -> - [< Html_types.dl_content_fun ] Tyxml.Html.elt Tyxml.Html.list_wrap -> - [> Html_types.dl ] Tyxml.Html.elt + ?a:[< Html_types.dl_attrib > `Class] Tyxml.Html.attrib list + -> [< Html_types.dl_content_fun] Tyxml.Html.elt Tyxml.Html.list_wrap + -> [> Html_types.dl] Tyxml.Html.elt val term : - ?a:[< Html_types.dt_attrib > `Class ] Tyxml.Html.attrib list -> - [< Html_types.dt_content_fun ] Tyxml.Html.elt Tyxml.Html.list_wrap -> - [> Html_types.dt ] Tyxml.Html.elt + ?a:[< Html_types.dt_attrib > `Class] Tyxml.Html.attrib list + -> [< Html_types.dt_content_fun] Tyxml.Html.elt Tyxml.Html.list_wrap + -> [> Html_types.dt] Tyxml.Html.elt val description : - ?a:[< Html_types.dt_attrib > `Class ] Tyxml.Html.attrib list -> - [< Html_types.dt_content_fun ] Tyxml.Html.elt Tyxml.Html.list_wrap -> - [> Html_types.dt ] Tyxml.Html.elt + ?a:[< Html_types.dt_attrib > `Class] Tyxml.Html.attrib list + -> [< Html_types.dt_content_fun] Tyxml.Html.elt Tyxml.Html.list_wrap + -> [> Html_types.dt] Tyxml.Html.elt diff --git a/controller/server/view/common/icon.ml b/controller/server/view/common/icon.ml index 165930cd..edd7f790 100644 --- a/controller/server/view/common/icon.ml +++ b/controller/server/view/common/icon.ml @@ -5,15 +5,14 @@ open Tyxml.Svg let svg ?a ?stroke_width content = Tyxml.Html.svg ~a: - ([ - a_viewBox (0., 0., 24., 24.); - a_width (24., None); - a_height (24., None); - a_fill `None; - a_stroke `CurrentColor; - a_stroke_width (Option.value ~default:2. stroke_width, None); - a_stroke_linecap `Round; - a_stroke_linejoin `Round; + ([ a_viewBox (0., 0., 24., 24.) + ; a_width (24., None) + ; a_height (24., None) + ; a_fill `None + ; a_stroke `CurrentColor + ; a_stroke_width (Option.value ~default:2. stroke_width, None) + ; a_stroke_linecap `Round + ; a_stroke_linejoin `Round ] @ Option.value ~default:[] a ) @@ -21,22 +20,21 @@ let svg ?a ?stroke_width content = let line (x1, y1) (x2, y2) = Tyxml.Svg.line - ~a:[ a_x1 (x1, None); a_y1 (y1, None); a_x2 (x2, None); a_y2 (y2, None) ] + ~a:[a_x1 (x1, None); a_y1 (y1, None); a_x2 (x2, None); a_y2 (y2, None)] [] let circle (x, y) r = - Tyxml.Svg.circle ~a:[ a_cx (x, None); a_cy (y, None); a_r (r, None) ] [] + Tyxml.Svg.circle ~a:[a_cx (x, None); a_cy (y, None); a_r (r, None)] [] let rect ?rx ?fill (x1, y1) (x2, y2) = Tyxml.Svg.rect ~a: - [ - a_x (x1, None); - a_y (y1, None); - a_width (x2 -. x1, None); - a_height (y2 -. y1, None); - a_rx (Option.value ~default:0. rx, None); - a_fill (`Color (Option.value ~default:"transparent" fill, None)); + [ a_x (x1, None) + ; a_y (y1, None) + ; a_width (x2 -. x1, None) + ; a_height (y2 -. y1, None) + ; a_rx (Option.value ~default:0. rx, None) + ; a_fill (`Color (Option.value ~default:"transparent" fill, None)) ] [] @@ -44,9 +42,7 @@ let rect ?rx ?fill (x1, y1) (x2, y2) = let info = svg - [ - circle (12., 12.) 10.; line (12., 16.) (12., 12.); line (12., 8.) (12., 8.); - ] + [circle (12., 12.) 10.; line (12., 16.) (12., 12.); line (12., 8.) (12., 8.)] let wifi ?strength () = let strength = Option.value ~default:100 strength in @@ -57,119 +53,102 @@ let wifi ?strength () = else "Strong" in svg - ~a:[ a_class [ "d-WifiSignal--" ^ modifier ] ] - [ - path + ~a:[a_class ["d-WifiSignal--" ^ modifier]] + [ path ~a: - [ - a_class [ "d-WifiSignal__Wave--Outer" ]; - a_d "M1.42 9a16 16 0 0 1 21.16 0"; + [ a_class ["d-WifiSignal__Wave--Outer"] + ; a_d "M1.42 9a16 16 0 0 1 21.16 0" ] - []; - path + [] + ; path ~a: - [ - a_class [ "d-WifiSignal__Wave--Middle" ]; - a_d "M5 12.55a11 11 0 0 1 14.08 0"; + [ a_class ["d-WifiSignal__Wave--Middle"] + ; a_d "M5 12.55a11 11 0 0 1 14.08 0" ] - []; - path + [] + ; path ~a: - [ - a_class [ "d-WifiSignal__Wave--Inner" ]; - a_d "M8.53 16.11a6 6 0 0 1 6.95 0"; + [ a_class ["d-WifiSignal__Wave--Inner"] + ; a_d "M8.53 16.11a6 6 0 0 1 6.95 0" ] - []; - line (12., 20.) (12., 20.); + [] + ; line (12., 20.) (12., 20.) ] let ethernet = svg - [ - path ~a:[ a_d "M2 2 H22 V18 H18 V22 H6 V18 H2 Z" ] []; - line (6., 6.) (6., 10.); - line (10., 6.) (10., 10.); - line (14., 6.) (14., 10.); - line (18., 6.) (18., 10.); + [ path ~a:[a_d "M2 2 H22 V18 H18 V22 H6 V18 H2 Z"] [] + ; line (6., 6.) (6., 10.) + ; line (10., 6.) (10., 10.) + ; line (14., 6.) (14., 10.) + ; line (18., 6.) (18., 10.) ] let world = svg - [ - circle (12., 12.) 10.; - line (2., 12.) (22., 12.); - path + [ circle (12., 12.) 10. + ; line (2., 12.) (22., 12.) + ; path ~a: - [ - a_d + [ a_d "M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 \ - 1-4-10 15.3 15.3 0 0 1 4-10z"; + 1-4-10 15.3 15.3 0 0 1 4-10z" ] - []; + [] ] let power = svg - [ - path ~a:[ a_d "M18.36 6.64a9 9 0 1 1-12.73 0" ] []; - line (12., 2.) (12., 12.); - ] + [path ~a:[a_d "M18.36 6.64a9 9 0 1 1-12.73 0"] []; line (12., 2.) (12., 12.)] let screen = svg - [ - rect ~rx:2. (2.5, 2.) (21.5, 16.); - line (12., 16.) (12., 22.); - line (8., 22.) (16., 22.); + [ rect ~rx:2. (2.5, 2.) (21.5, 16.) + ; line (12., 16.) (12., 22.) + ; line (8., 22.) (16., 22.) ] let document = svg - [ - rect ~rx:1. (4., 2.) (20., 22.); - line (8., 8.) (16., 8.); - line (8., 12.) (16., 12.); - line (8., 16.) (16., 16.); + [ rect ~rx:1. (4., 2.) (20., 22.) + ; line (8., 8.) (16., 8.) + ; line (8., 12.) (16., 12.) + ; line (8., 16.) (16., 16.) ] let arrow_left = svg - [ - line (2., 12.) (22., 12.); - line (2., 12.) (12., 22.); - line (2., 12.) (12., 2.); + [ line (2., 12.) (22., 12.) + ; line (2., 12.) (12., 22.) + ; line (2., 12.) (12., 2.) ] let letter = svg ~stroke_width:1. - [ - rect ~rx:1. ~fill:"black" (2., 2.) (22., 22.); - text + [ rect ~rx:1. ~fill:"black" (2., 2.) (22., 22.) + ; text ~a: - [ - a_fill (`Color ("white", None)); - a_stroke (`Color ("white", None)); - a_font_size "16"; - Unsafe.string_attrib "x" "50%"; - Unsafe.string_attrib "y" "55%"; - a_dominant_baseline `Middle; - a_text_anchor `Middle; + [ a_fill (`Color ("white", None)) + ; a_stroke (`Color ("white", None)) + ; a_font_size "16" + ; Unsafe.string_attrib "x" "50%" + ; Unsafe.string_attrib "y" "55%" + ; a_dominant_baseline `Middle + ; a_text_anchor `Middle ] - [ txt "A" ]; + [txt "A"] ] let copyright = svg - [ - circle (12., 12.) 10.; - text + [ circle (12., 12.) 10. + ; text ~a: - [ - a_font_size "10"; - Unsafe.string_attrib "x" "50%"; - Unsafe.string_attrib "y" "55%"; - a_dominant_baseline `Middle; - a_text_anchor `Middle; + [ a_font_size "10" + ; Unsafe.string_attrib "x" "50%" + ; Unsafe.string_attrib "y" "55%" + ; a_dominant_baseline `Middle + ; a_text_anchor `Middle ] - [ txt "C" ]; + [txt "C"] ] diff --git a/controller/server/view/common/icon.mli b/controller/server/view/common/icon.mli index 8099ff09..8c02dd99 100644 --- a/controller/server/view/common/icon.mli +++ b/controller/server/view/common/icon.mli @@ -1,10 +1,19 @@ -val info : [> Html_types.svg ] Tyxml.Html.elt -val wifi : ?strength:int -> unit -> [> Html_types.svg ] Tyxml.Html.elt -val ethernet : [> Html_types.svg ] Tyxml.Html.elt -val world : [> Html_types.svg ] Tyxml.Html.elt -val power : [> Html_types.svg ] Tyxml.Html.elt -val screen : [> Html_types.svg ] Tyxml.Html.elt -val document : [> Html_types.svg ] Tyxml.Html.elt -val arrow_left : [> Html_types.svg ] Tyxml.Html.elt -val letter : [> Html_types.svg ] Tyxml.Html.elt -val copyright : [> Html_types.svg ] Tyxml.Html.elt +val info : [> Html_types.svg] Tyxml.Html.elt + +val wifi : ?strength:int -> unit -> [> Html_types.svg] Tyxml.Html.elt + +val ethernet : [> Html_types.svg] Tyxml.Html.elt + +val world : [> Html_types.svg] Tyxml.Html.elt + +val power : [> Html_types.svg] Tyxml.Html.elt + +val screen : [> Html_types.svg] Tyxml.Html.elt + +val document : [> Html_types.svg] Tyxml.Html.elt + +val arrow_left : [> Html_types.svg] Tyxml.Html.elt + +val letter : [> Html_types.svg] Tyxml.Html.elt + +val copyright : [> Html_types.svg] Tyxml.Html.elt diff --git a/controller/server/view/common/page.ml b/controller/server/view/common/page.ml index 50baf206..1c50d1be 100644 --- a/controller/server/view/common/page.ml +++ b/controller/server/view/common/page.ml @@ -42,67 +42,57 @@ let menu_label page = let menu_item current_page page = let is_active = current_page = Some page in let class_ = - "d-Menu__Item" :: (if is_active then [ "d-Menu__Item--Active" ] else []) + "d-Menu__Item" :: (if is_active then ["d-Menu__Item--Active"] else []) in a ~a: (a_href (menu_link page) :: a_class class_ - :: (if is_active then [ a_user_data "focus-active" "" ] else []) + :: (if is_active then [a_user_data "focus-active" ""] else []) ) - [ menu_icon page; txt (menu_label page) ] + [menu_icon page; txt (menu_label page)] let html ?current_page ?header content = let header = match header with | Some header -> - [ Tyxml.Html.header ~a:[ a_class [ "d-Layout__Header" ] ] [ header ] ] + [Tyxml.Html.header ~a:[a_class ["d-Layout__Header"]] [header]] | None -> [] in html - ~a:[ a_lang "en" ] + ~a:[a_lang "en"] (head (title (txt "PlayOS Controller")) - [ - meta ~a:[ a_charset "utf-8" ] (); - link ~rel:[ `Stylesheet ] ~href:"/static/reset.css" (); - link ~rel:[ `Stylesheet ] ~href:"/static/style.css" (); + [ meta ~a:[a_charset "utf-8"] () + ; link ~rel:[`Stylesheet] ~href:"/static/reset.css" () + ; link ~rel:[`Stylesheet] ~href:"/static/style.css" () ] ) (body - ~a:[ a_class [ "d-Layout" ] ] + ~a:[a_class ["d-Layout"]] (aside - ~a: - [ - a_class [ "d-Layout__Aside" ]; a_user_data "focus-group" "active"; - ] - [ - nav - ([ - Info; Network; Localization; SystemStatus; Changelog; Licensing; - ] + ~a:[a_class ["d-Layout__Aside"]; a_user_data "focus-group" "active"] + [ nav + ([Info; Network; Localization; SystemStatus; Changelog; Licensing] |> List.concat_map (fun page -> - [ menu_item current_page page; txt " " ] + [menu_item current_page page; txt " "] ) - ); - form + ) + ; form ~a: - [ - a_action (menu_link Shutdown); - a_method `Post; - a_class [ "d-Layout__Shutdown" ]; + [ a_action (menu_link Shutdown) + ; a_method `Post + ; a_class ["d-Layout__Shutdown"] ] - [ - button - ~a:[ a_class [ "d-Menu__Item" ] ] - [ menu_icon Shutdown; txt (menu_label Shutdown) ]; - ]; + [ button + ~a:[a_class ["d-Menu__Item"]] + [menu_icon Shutdown; txt (menu_label Shutdown)] + ] ] :: header - @ [ - main ~a:[ a_class [ "d-Layout__Main" ] ] [ content ]; - script ~a:[ a_src "/static/vendor/focus-shift.js" ] (txt ""); - script ~a:[ a_src "/static/client.js" ] (txt ""); + @ [ main ~a:[a_class ["d-Layout__Main"]] [content] + ; script ~a:[a_src "/static/vendor/focus-shift.js"] (txt "") + ; script ~a:[a_src "/static/client.js"] (txt "") ] ) ) @@ -111,25 +101,21 @@ let header_title ?back_url ?icon ?right_action content = let back_link = match back_url with | Some url -> - [ - a - ~a:[ a_class [ "d-Header__BackLink" ]; a_href url ] - [ Icon.arrow_left ]; - ] + [a ~a:[a_class ["d-Header__BackLink"]; a_href url] [Icon.arrow_left]] | None -> [] in let icon = match icon with - | Some icon -> [ span ~a:[ a_class [ "d-Header__Icon" ] ] [ icon ] ] + | Some icon -> [span ~a:[a_class ["d-Header__Icon"]] [icon]] | None -> [] in let right_action = match right_action with - | Some right_action -> [ right_action ] + | Some right_action -> [right_action] | None -> [] in div - ~a:[ a_class [ "d-Header__Line" ] ] - (h1 ~a:[ a_class [ "d-Header__Title" ] ] (back_link @ icon @ content) + ~a:[a_class ["d-Header__Line"]] + (h1 ~a:[a_class ["d-Header__Title"]] (back_link @ icon @ content) :: right_action ) diff --git a/controller/server/view/common/page.mli b/controller/server/view/common/page.mli index 3403b8da..721d0036 100644 --- a/controller/server/view/common/page.mli +++ b/controller/server/view/common/page.mli @@ -8,14 +8,14 @@ type page = | Shutdown val html : - ?current_page:page -> - ?header:[< Html_types.header_content_fun ] Tyxml.Html.elt -> - [< Html_types.main_content_fun ] Tyxml.Html.elt -> - [> Html_types.html ] Tyxml.Html.elt + ?current_page:page + -> ?header:[< Html_types.header_content_fun] Tyxml.Html.elt + -> [< Html_types.main_content_fun] Tyxml.Html.elt + -> [> Html_types.html] Tyxml.Html.elt val header_title : - ?back_url:Tyxml.Html.Xml.uri Tyxml.Html.wrap -> - ?icon:[< Html_types.span_content_fun ] Tyxml.Html.elt -> - ?right_action:[< Html_types.div_content_fun > `H1 ] Tyxml.Html.elt -> - [< Html_types.h1_content_fun > `A `Span ] Tyxml.Html.elt list -> - [> Html_types.div ] Tyxml.Html.elt + ?back_url:Tyxml.Html.Xml.uri Tyxml.Html.wrap + -> ?icon:[< Html_types.span_content_fun] Tyxml.Html.elt + -> ?right_action:[< Html_types.div_content_fun > `H1] Tyxml.Html.elt + -> [< Html_types.h1_content_fun > `A `Span] Tyxml.Html.elt list + -> [> Html_types.div] Tyxml.Html.elt diff --git a/controller/server/view/error_page.ml b/controller/server/view/error_page.ml index d6c27f38..d9a5bf73 100644 --- a/controller/server/view/error_page.ml +++ b/controller/server/view/error_page.ml @@ -1,18 +1,17 @@ open Tyxml.Html -type params = { - message : string; - request : string; -} +type params = + { message: string + ; request: string + } -let html { message; request } = +let html {message; request} = Page.html - ~header:(Page.header_title [ txt "Error" ]) + ~header:(Page.header_title [txt "Error"]) (div - [ - pre ~a:[ a_class [ "d-Preformatted" ] ] [ txt message ]; - details - (summary [ txt "Request" ]) - [ pre ~a:[ a_class [ "d-Preformatted" ] ] [ txt request ] ]; + [ pre ~a:[a_class ["d-Preformatted"]] [txt message] + ; details + (summary [txt "Request"]) + [pre ~a:[a_class ["d-Preformatted"]] [txt request]] ] ) diff --git a/controller/server/view/error_page.mli b/controller/server/view/error_page.mli index 454fde5f..70e4f209 100644 --- a/controller/server/view/error_page.mli +++ b/controller/server/view/error_page.mli @@ -1,6 +1,6 @@ -type params = { - message : string; - request : string; -} +type params = + { message: string + ; request: string + } -val html : params -> [> Html_types.html ] Tyxml.Html.elt +val html : params -> [> Html_types.html] Tyxml.Html.elt diff --git a/controller/server/view/info_page.ml b/controller/server/view/info_page.ml index 45130c0c..f13df21b 100644 --- a/controller/server/view/info_page.ml +++ b/controller/server/view/info_page.ml @@ -4,62 +4,52 @@ open Tyxml.Html let remote_maintenance_form action button_label = form ~a: - [ - a_action ("/remote-maintenance/" ^ action); - a_method `Post; - a_class [ "d-Info__RemoteMaintenanceForm" ]; - Unsafe.string_attrib "is" "disable-after-submit"; + [ a_action ("/remote-maintenance/" ^ action) + ; a_method `Post + ; a_class ["d-Info__RemoteMaintenanceForm"] + ; Unsafe.string_attrib "is" "disable-after-submit" ] - [ - input - ~a: - [ a_input_type `Submit; a_class [ "d-Button" ]; a_value button_label ] - (); + [ input + ~a:[a_input_type `Submit; a_class ["d-Button"]; a_value button_label] + () ] let remote_maintenance address = match address with | Some address -> - [ - span - ~a:[ a_class [ "d-Info__RemoteMaintenanceAddress" ] ] - [ txt address ]; - remote_maintenance_form "disable" "Disable"; + [ span ~a:[a_class ["d-Info__RemoteMaintenanceAddress"]] [txt address] + ; remote_maintenance_form "disable" "Disable" ] | None -> - [ - div - ~a:[ a_class [ "d-Note" ] ] - [ - txt + [ div + ~a:[a_class ["d-Note"]] + [ txt "Enabling remote maintenance allows Dividat to access this \ computer at a distance. For this purpose the computer's public \ IP address is shared with ZeroTier, a US-based company \ - providing an overlay network."; - ]; - remote_maintenance_form "enable" "Enable"; + providing an overlay network." + ] + ; remote_maintenance_form "enable" "Enable" ] let html server_info = Page.html ~current_page:Page.Info - ~header:(Page.header_title ~icon:Icon.info [ txt "Information" ]) + ~header:(Page.header_title ~icon:Icon.info [txt "Information"]) (div - [ - Definition.list - [ - Definition.term [ txt "Version" ]; - Definition.description [ txt server_info.version ]; - Definition.term [ txt "Update URL" ]; - Definition.description [ txt server_info.update_url ]; - Definition.term [ txt "Kiosk URL" ]; - Definition.description [ txt server_info.kiosk_url ]; - Definition.term [ txt "Machine ID" ]; - Definition.description [ txt server_info.machine_id ]; - Definition.term [ txt "Local time" ]; - Definition.description [ txt server_info.local_time ]; - Definition.term [ txt "Remote maintenance" ]; - Definition.description - (remote_maintenance server_info.zerotier_address); - ]; + [ Definition.list + [ Definition.term [txt "Version"] + ; Definition.description [txt server_info.version] + ; Definition.term [txt "Update URL"] + ; Definition.description [txt server_info.update_url] + ; Definition.term [txt "Kiosk URL"] + ; Definition.description [txt server_info.kiosk_url] + ; Definition.term [txt "Machine ID"] + ; Definition.description [txt server_info.machine_id] + ; Definition.term [txt "Local time"] + ; Definition.description [txt server_info.local_time] + ; Definition.term [txt "Remote maintenance"] + ; Definition.description + (remote_maintenance server_info.zerotier_address) + ] ] ) diff --git a/controller/server/view/info_page.mli b/controller/server/view/info_page.mli index 6b64ada1..386d9683 100644 --- a/controller/server/view/info_page.mli +++ b/controller/server/view/info_page.mli @@ -1 +1 @@ -val html : Info.t -> [> Html_types.html ] Tyxml.Html.elt +val html : Info.t -> [> Html_types.html] Tyxml.Html.elt diff --git a/controller/server/view/licensing_page.ml b/controller/server/view/licensing_page.ml index 9fa97362..0bd2933d 100644 --- a/controller/server/view/licensing_page.ml +++ b/controller/server/view/licensing_page.ml @@ -4,13 +4,12 @@ let log_src = Logs.Src.create "licensing_page" let tool ~name ~license_name ~license_content content = div - [ - h2 ~a:[ a_class [ "d-Title" ] ] [ txt name ]; - div content; - details - ~a:[ a_class [ "d-Licensing__Details" ] ] - (summary [ txt license_name ]) - [ pre ~a:[ a_class [ "d-Preformatted" ] ] [ txt license_content ] ]; + [ h2 ~a:[a_class ["d-Title"]] [txt name] + ; div content + ; details + ~a:[a_class ["d-Licensing__Details"]] + (summary [txt license_name]) + [pre ~a:[a_class ["d-Preformatted"]] [txt license_content]] ] let read_license key = @@ -23,30 +22,27 @@ let html = let%lwt qt6_license = read_license "QT6" in Lwt.return (Page.html ~current_page:Page.Licensing - ~header:(Page.header_title ~icon:Icon.copyright [ txt "Licensing" ]) + ~header:(Page.header_title ~icon:Icon.copyright [txt "Licensing"]) (div - [ - tool ~name:"PlayOS" ~license_name:"MIT License" + [ tool ~name:"PlayOS" ~license_name:"MIT License" ~license_content:playos_license - [ - p - ~a:[ a_class [ "d-Paragraph" ] ] - [ - txt "Source code is available at "; - span + [ p + ~a:[a_class ["d-Paragraph"]] + [ txt "Source code is available at " + ; span (* Using span as we don’t intend the user to leave the current page *) - ~a:[ a_class [ "d-Licensing__Link" ] ] - [ txt "https://github.com/dividat/playos" ]; - txt ", with instructions to build and modify the software."; - ]; - ]; - tool ~name:"Nixpkgs" ~license_name:"MIT License" - ~license_content:nixpkgs_license []; - tool ~name:"Feather" ~license_name:"MIT License" - ~license_content:feather_license []; - tool ~name:"Qt6" + ~a:[a_class ["d-Licensing__Link"]] + [txt "https://github.com/dividat/playos"] + ; txt ", with instructions to build and modify the software." + ] + ] + ; tool ~name:"Nixpkgs" ~license_name:"MIT License" + ~license_content:nixpkgs_license [] + ; tool ~name:"Feather" ~license_name:"MIT License" + ~license_content:feather_license [] + ; tool ~name:"Qt6" ~license_name:"GNU Lesser General Public License v3.0" - ~license_content:qt6_license []; + ~license_content:qt6_license [] ] ) ) diff --git a/controller/server/view/licensing_page.mli b/controller/server/view/licensing_page.mli index b34fe9cd..efd75eee 100644 --- a/controller/server/view/licensing_page.mli +++ b/controller/server/view/licensing_page.mli @@ -1 +1 @@ -val html : [> Html_types.html ] Tyxml.Html.elt Lwt.t +val html : [> Html_types.html] Tyxml.Html.elt Lwt.t diff --git a/controller/server/view/localization_page.ml b/controller/server/view/localization_page.ml index 355de43d..6a6d7349 100644 --- a/controller/server/view/localization_page.ml +++ b/controller/server/view/localization_page.ml @@ -1,46 +1,41 @@ open Tyxml.Html -type select_form_params = { - action_url : string; - legend : string; - select_name : string; - placeholder : string option; -} +type select_form_params = + { action_url: string + ; legend: string + ; select_name: string + ; placeholder: string option + } let select_form params options = form ~a: - [ - a_action params.action_url; - a_method `Post; - a_class [ "d-Localization__Form" ]; - Unsafe.string_attrib "is" "disable-after-submit"; + [ a_action params.action_url + ; a_method `Post + ; a_class ["d-Localization__Form"] + ; Unsafe.string_attrib "is" "disable-after-submit" ] - [ - label ~a:[ a_class [ "d-Localization__Legend" ] ] [ txt params.legend ]; - select + [ label ~a:[a_class ["d-Localization__Legend"]] [txt params.legend] + ; select ~a: - [ - a_name params.select_name; - a_class [ "d-Select"; "d-Localization__Select" ]; - a_required (); + [ a_name params.select_name + ; a_class ["d-Select"; "d-Localization__Select"] + ; a_required () ] ((params.placeholder |> Option.map (fun p -> - option ~a:[ a_disabled (); a_selected () ] (txt p) + option ~a:[a_disabled (); a_selected ()] (txt p) ) |> Base.Option.to_list ) @ options - ); - input - ~a:[ a_input_type `Submit; a_class [ "d-Button" ]; a_value "Set" ] - (); + ) + ; input ~a:[a_input_type `Submit; a_class ["d-Button"]; a_value "Set"] () ] let select_option current_id (id, name) = option - ~a:([ a_value id ] @ if current_id = Some id then [ a_selected () ] else []) + ~a:([a_value id] @ if current_id = Some id then [a_selected ()] else []) (txt name) let timezone_form timezone_groups current_timezone = @@ -49,88 +44,81 @@ let timezone_form timezone_groups current_timezone = (List.map (select_option current_timezone) timezones) in select_form - { - action_url = "/localization/timezone"; - legend = "Timezone"; - select_name = "timezone"; - placeholder = + { action_url= "/localization/timezone" + ; legend= "Timezone" + ; select_name= "timezone" + ; placeholder= ( if Option.is_none current_timezone then Some "Select your closest timezone…" else None - ); + ) } (List.map timezone_group timezone_groups) let language_form langs current_lang = select_form - { - action_url = "/localization/lang"; - legend = "Language"; - select_name = "lang"; - placeholder = + { action_url= "/localization/lang" + ; legend= "Language" + ; select_name= "lang" + ; placeholder= ( if Option.is_none current_lang then Some "Select your language…" else None - ); + ) } (List.map (select_option current_lang) langs) let keyboard_form keymaps current_keymap = select_form - { - action_url = "/localization/keymap"; - legend = "Keyboard"; - select_name = "keymap"; - placeholder = + { action_url= "/localization/keymap" + ; legend= "Keyboard" + ; select_name= "keymap" + ; placeholder= ( if Option.is_none current_keymap then Some "Select your keyboard layout…" else None - ); + ) } (List.map (select_option current_keymap) keymaps) let scaling_form current_scaling = - [ Screen_settings.Default; Screen_settings.FullHD; Screen_settings.Native ] + [Screen_settings.Default; Screen_settings.FullHD; Screen_settings.Native] |> List.map (fun s -> select_option (Some (Screen_settings.string_of_scaling current_scaling)) - ( Screen_settings.string_of_scaling s, - Screen_settings.label_of_scaling s + ( Screen_settings.string_of_scaling s + , Screen_settings.label_of_scaling s ) ) |> select_form - { - action_url = "/localization/scaling"; - legend = "Display resolution"; - select_name = "scaling"; - placeholder = None; + { action_url= "/localization/scaling" + ; legend= "Display resolution" + ; select_name= "scaling" + ; placeholder= None } -type params = { - timezone_groups : (string * (string * string) list) list; - current_timezone : string option; - langs : (string * string) list; - current_lang : string option; - keymaps : (string * string) list; - current_keymap : string option; - current_scaling : Screen_settings.scaling; -} +type params = + { timezone_groups: (string * (string * string) list) list + ; current_timezone: string option + ; langs: (string * string) list + ; current_lang: string option + ; keymaps: (string * string) list + ; current_keymap: string option + ; current_scaling: Screen_settings.scaling + } let html params = Page.html ~current_page:Page.Localization - ~header: - (Page.header_title ~icon:Icon.letter [ txt "Localization & Display" ]) + ~header:(Page.header_title ~icon:Icon.letter [txt "Localization & Display"]) (div - [ - timezone_form params.timezone_groups params.current_timezone; - language_form params.langs params.current_lang; - keyboard_form params.keymaps params.current_keymap; - scaling_form params.current_scaling; - aside - ~a:[ a_class [ "d-Localization__Note" ] ] - [ - txt + [ timezone_form params.timezone_groups params.current_timezone + ; language_form params.langs params.current_lang + ; keyboard_form params.keymaps params.current_keymap + ; scaling_form params.current_scaling + ; aside + ~a:[a_class ["d-Localization__Note"]] + [ txt "Note that changes to the keyboard, language and display \ - settings require a restart."; - ]; + settings require a restart." + ] ] ) diff --git a/controller/server/view/localization_page.mli b/controller/server/view/localization_page.mli index 1c85f310..44904b0d 100644 --- a/controller/server/view/localization_page.mli +++ b/controller/server/view/localization_page.mli @@ -1,11 +1,11 @@ -type params = { - timezone_groups : (string * (string * string) list) list; - current_timezone : string option; - langs : (string * string) list; - current_lang : string option; - keymaps : (string * string) list; - current_keymap : string option; - current_scaling : Screen_settings.scaling; -} +type params = + { timezone_groups: (string * (string * string) list) list + ; current_timezone: string option + ; langs: (string * string) list + ; current_lang: string option + ; keymaps: (string * string) list + ; current_keymap: string option + ; current_scaling: Screen_settings.scaling + } -val html : params -> [> Html_types.html ] Tyxml.Html.elt +val html : params -> [> Html_types.html] Tyxml.Html.elt diff --git a/controller/server/view/network_details_page.ml b/controller/server/view/network_details_page.ml index b357cba5..86edffeb 100644 --- a/controller/server/view/network_details_page.ml +++ b/controller/server/view/network_details_page.ml @@ -4,134 +4,114 @@ open Tyxml.Html let proxy_form proxy = let open Proxy in div - [ - label - ~a:[ a_class [ "d-Label" ] ] - [ - txt "Server"; - span - [ - input + [ label + ~a:[a_class ["d-Label"]] + [ txt "Server" + ; span + [ input ~a: - [ - a_input_type `Text; - a_class [ "d-Input"; "d-Network__Input" ]; - a_name "proxy_host"; - a_value + [ a_input_type `Text + ; a_class ["d-Input"; "d-Network__Input"] + ; a_name "proxy_host" + ; a_value ( match proxy with - | Some { host } -> host + | Some {host} -> host | _ -> "" - ); - a_placeholder "Host"; - a_pattern {|[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*|}; + ) + ; a_placeholder "Host" + ; a_pattern {|[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*|} ] - (); - txt ":"; - input + () + ; txt ":" + ; input ~a: - [ - a_input_type `Number; - a_class [ "d-Input" ]; - a_name "proxy_port"; - a_size 10; - a_step (Some 1.0); - a_value + [ a_input_type `Number + ; a_class ["d-Input"] + ; a_name "proxy_port" + ; a_size 10 + ; a_step (Some 1.0) + ; a_value ( match proxy with - | Some { port } -> string_of_int port + | Some {port} -> string_of_int port | _ -> "" - ); - a_placeholder "Port"; + ) + ; a_placeholder "Port" ] - (); - ]; - ]; - label - ~a:[ a_class [ "d-Label" ] ] - [ - txt "Username (optional)"; - input + () + ] + ] + ; label + ~a:[a_class ["d-Label"]] + [ txt "Username (optional)" + ; input ~a: - [ - a_input_type `Text; - a_class [ "d-Input"; "d-Network__Input" ]; - a_name "proxy_user"; - a_value + [ a_input_type `Text + ; a_class ["d-Input"; "d-Network__Input"] + ; a_name "proxy_user" + ; a_value ( match proxy with - | Some { credentials = Some { user } } -> user + | Some {credentials= Some {user}} -> user | _ -> "" - ); + ) ] - (); - ]; - div + () + ] + ; div ~a: ( match proxy with - | Some { credentials = Some { password } } -> + | Some {credentials= Some {password}} -> if password <> "" then - [ Unsafe.string_attrib "is" "keep-previous-password" ] + [Unsafe.string_attrib "is" "keep-previous-password"] else [] | _ -> [] ) - [ - label - ~a:[ a_class [ "d-Label" ] ] - [ - txt "Password (optional)"; - input + [ label + ~a:[a_class ["d-Label"]] + [ txt "Password (optional)" + ; input ~a: - [ - a_input_type `Password; - a_class [ "d-Input"; "d-Network__Input" ]; - a_name "proxy_password"; - a_value ""; - Unsafe.string_attrib "is" "show-password"; + [ a_input_type `Password + ; a_class ["d-Input"; "d-Network__Input"] + ; a_name "proxy_password" + ; a_value "" + ; Unsafe.string_attrib "is" "show-password" ] - (); - ]; - ]; + () + ] + ] ] let maybe_elem cond elem = if cond then Some elem else None let not_connected_form service = - let requires_passphrase = service.security <> [ None ] in + let requires_passphrase = service.security <> [None] in form ~a: - [ - a_action ("/network/" ^ service.id ^ "/connect"); - a_method `Post; - Unsafe.string_attrib "is" "disable-after-submit"; + [ a_action ("/network/" ^ service.id ^ "/connect") + ; a_method `Post + ; Unsafe.string_attrib "is" "disable-after-submit" ] (Option.to_list (maybe_elem requires_passphrase (label - ~a:[ a_class [ "d-Label" ] ] - [ - txt "Password"; - input + ~a:[a_class ["d-Label"]] + [ txt "Password" + ; input ~a: - [ - a_input_type `Password; - a_class [ "d-Input"; "d-Network__Input" ]; - a_name "passphrase"; - Unsafe.string_attrib "is" "show-password"; + [ a_input_type `Password + ; a_class ["d-Input"; "d-Network__Input"] + ; a_name "passphrase" + ; Unsafe.string_attrib "is" "show-password" ] - (); + () ] ) ) - @ [ - p - [ - input - ~a: - [ - a_input_type `Submit; - a_class [ "d-Button" ]; - a_value "Connect"; - ] - (); - ]; + @ [ p + [ input + ~a:[a_input_type `Submit; a_class ["d-Button"]; a_value "Connect"] + () + ] ] ) @@ -151,21 +131,18 @@ let is_static service = let static_ip_form service = let ip_input ~name ~labelTxt ~value ~pattern = - [ - label - ~a:[ a_class [ "d-Label" ] ] - [ - txt labelTxt; - input + [ label + ~a:[a_class ["d-Label"]] + [ txt labelTxt + ; input ~a: - [ - a_value value; - a_class [ "d-Input"; "d-Network__Input" ]; - a_name name; - a_pattern pattern; + [ a_value value + ; a_class ["d-Input"; "d-Network__Input"] + ; a_name name + ; a_pattern pattern ] - (); - ]; + () + ] ] in let ipv4_value f = @@ -176,17 +153,15 @@ let static_ip_form service = else "" in div - [ - p - ~a:[ a_class [ "d-Note" ] ] - [ - txt "A valid IP address must be in the form of "; - code ~a:[ a_class [ "d-Code" ] ] [ txt "n.n.n.n" ]; - txt ","; - br (); - txt "where n is a number in the range of 0-255."; - ]; - div + [ p + ~a:[a_class ["d-Note"]] + [ txt "A valid IP address must be in the form of " + ; code ~a:[a_class ["d-Code"]] [txt "n.n.n.n"] + ; txt "," + ; br () + ; txt "where n is a number in the range of 0-255." + ] + ; div (ip_input ~name:"static_ip_address" ~labelTxt:"Address" ~value:(ipv4_value (fun ipv4 -> ipv4.address)) ~pattern:ip_address_regex_pattern @@ -203,18 +178,16 @@ let static_ip_form service = else "" ) ~pattern:multi_ip_address_regex_pattern - @ [ - p - ~a:[ a_class [ "d-Note" ] ] - [ - txt + @ [ p + ~a:[a_class ["d-Note"]] + [ txt "To set multiple nameservers, use a comma separated list of \ - addresses."; - br (); - txt "eg. 1.1.1.1, 9.9.9.9"; - ]; + addresses." + ; br () + ; txt "eg. 1.1.1.1, 9.9.9.9" + ] ] - ); + ) ] let checked_input cond attrs = @@ -223,67 +196,58 @@ let checked_input cond attrs = let toggle_group ~is_enabled ~legend_text ~toggle_field contents = fieldset ~a: - [ - a_class - ([ "d-Network__ToggleGroup" ] - @ if is_enabled then [ "d-Network__ToggleGroup--Enabled" ] else [] - ); + [ a_class + (["d-Network__ToggleGroup"] + @ if is_enabled then ["d-Network__ToggleGroup--Enabled"] else [] + ) ] ~legend: (legend - [ - label - ~a:[ a_class [ "d-CheckboxLabel" ] ] - [ - checked_input is_enabled - [ - a_class [ "d-Checkbox" ]; - a_input_type `Checkbox; - a_name toggle_field; - a_onclick + [ label + ~a:[a_class ["d-CheckboxLabel"]] + [ checked_input is_enabled + [ a_class ["d-Checkbox"] + ; a_input_type `Checkbox + ; a_name toggle_field + ; a_onclick "this.closest('.d-Network__ToggleGroup').classList.toggle('d-Network__ToggleGroup--Enabled', \ - this.checked)"; - ]; - txt legend_text; - ]; + this.checked)" + ] + ; txt legend_text + ] ] ) - [ fieldset contents ] + [fieldset contents] let connected_form service = div - [ - form + [ form ~a: - [ - a_action ("/network/" ^ service.id ^ "/update"); - a_method `Post; - Unsafe.string_attrib "is" "disable-after-submit"; + [ a_action ("/network/" ^ service.id ^ "/update") + ; a_method `Post + ; Unsafe.string_attrib "is" "disable-after-submit" ] - [ - toggle_group + [ toggle_group ~is_enabled:(Option.is_some service.proxy) ~legend_text:"HTTP Proxy" ~toggle_field:"proxy_enabled" - [ proxy_form service.proxy ]; - toggle_group ~is_enabled:(is_static service) ~legend_text:"Static IP" + [proxy_form service.proxy] + ; toggle_group ~is_enabled:(is_static service) ~legend_text:"Static IP" ~toggle_field:"static_ip_enabled" - [ static_ip_form service ]; - input - ~a: - [ a_input_type `Submit; a_class [ "d-Button" ]; a_value "Update" ] - (); - ]; + [static_ip_form service] + ; input + ~a:[a_input_type `Submit; a_class ["d-Button"]; a_value "Update"] + () + ] ] let unsupported_notice service = p - ~a:[ a_class [ "d-Note" ] ] - [ - txt + ~a:[a_class ["d-Note"]] + [ txt "Connecting to this network is not possible, because it uses an \ - unsupported authentication protocol."; - br (); - txt + unsupported authentication protocol." + ; br () + ; txt @@ Printf.sprintf "Available authentication protocols for this network: %s" (String.concat ", " @@ -291,13 +255,13 @@ let unsupported_notice service = (fun s -> Sexplib.Sexp.to_string (sexp_of_security s)) service.security ) - ); + ) ] let html service = let is_service_connected = Connman.Service.is_connected service in let is_service_supported = - List.exists (fun e -> List.mem e service.security) [ PSK; WEP; None ] + List.exists (fun e -> List.mem e service.security) [PSK; WEP; None] || service.security = [] (* wired connections have this *) in @@ -313,15 +277,13 @@ let html service = let disconnect_button = form ~a: - [ - a_action ("/network/" ^ service.id ^ "/remove"); - a_method `Post; - Unsafe.string_attrib "is" "disable-after-submit"; + [ a_action ("/network/" ^ service.id ^ "/remove") + ; a_method `Post + ; Unsafe.string_attrib "is" "disable-after-submit" ] - [ - input - ~a:[ a_input_type `Submit; a_class [ "d-Button" ]; a_value "Forget" ] - (); + [ input + ~a:[a_input_type `Submit; a_class ["d-Button"]; a_value "Forget"] + () ] in Page.html ~current_page:Page.Network @@ -330,19 +292,17 @@ let html service = ?right_action: (if is_disconnectable then Some disconnect_button else None) ~icon - [ txt service.name ] + [txt service.name] ) (div - [ - ( if is_service_connected then connected_form service + [ ( if is_service_connected then connected_form service else if is_service_supported then not_connected_form service else unsupported_notice service - ); - div - ~a:[ a_class [ "d-Network__Properties" ] ] - [ - h2 ~a:[ a_class [ "d-Title" ] ] [ txt "Service Details" ]; - pre ~a:[ a_class [ "d-Preformatted" ] ] [ txt properties ]; - ]; + ) + ; div + ~a:[a_class ["d-Network__Properties"]] + [ h2 ~a:[a_class ["d-Title"]] [txt "Service Details"] + ; pre ~a:[a_class ["d-Preformatted"]] [txt properties] + ] ] ) diff --git a/controller/server/view/network_details_page.mli b/controller/server/view/network_details_page.mli index c1f7e029..5c31f153 100644 --- a/controller/server/view/network_details_page.mli +++ b/controller/server/view/network_details_page.mli @@ -1 +1 @@ -val html : Connman.Service.t -> [> Html_types.html ] Tyxml.Html.elt +val html : Connman.Service.t -> [> Html_types.html] Tyxml.Html.elt diff --git a/controller/server/view/network_list_page.ml b/controller/server/view/network_list_page.ml index 13411ae6..bd89d58e 100644 --- a/controller/server/view/network_list_page.ml +++ b/controller/server/view/network_list_page.ml @@ -3,45 +3,41 @@ open Tyxml.Html open Sexplib.Std open Protocol_conv_jsonm -let service_item ({ id; name; strength; ipv4 } as service) = +let service_item ({id; name; strength; ipv4} as service) = let icon = match strength with | Some s -> Icon.wifi ~strength:s () | None -> Icon.ethernet in let classes = - [ "d-NetworkList__Network" ] + ["d-NetworkList__Network"] @ if Connman.Service.is_connected service then - [ "d-NetworkList__Network--Connected" ] + ["d-NetworkList__Network--Connected"] else [] in li - [ - a - ~a:[ a_class classes; a_href ("/network/" ^ id) ] - [ - div [ txt name ]; - ( match ipv4 with + [ a + ~a:[a_class classes; a_href ("/network/" ^ id)] + [ div [txt name] + ; ( match ipv4 with | Some ipv4_addr -> - div - ~a:[ a_class [ "d-NetworkList__Address" ] ] - [ txt ipv4_addr.address ] + div ~a:[a_class ["d-NetworkList__Address"]] [txt ipv4_addr.address] | None -> space () - ); - div ~a:[ a_class [ "d-NetworkList__Icon" ] ] [ icon ]; - div ~a:[ a_class [ "d-NetworkList__Chevron" ] ] [ txt "ᐳ" ]; - ]; + ) + ; div ~a:[a_class ["d-NetworkList__Icon"]] [icon] + ; div ~a:[a_class ["d-NetworkList__Chevron"]] [txt "ᐳ"] + ] ] -type params = { - proxy : string option; - services : Connman.Service.t list; - interfaces : Network.Interface.t list; -} +type params = + { proxy: string option + ; services: Connman.Service.t list + ; interfaces: Network.Interface.t list + } [@@deriving protocol ~driver:(module Jsonm)] -let html { proxy; services; interfaces } = +let html {proxy; services; interfaces} = let connected_services, available_services = List.partition Connman.Service.is_connected services in @@ -53,60 +49,48 @@ let html { proxy; services; interfaces } = ~header: (Page.header_title ~icon:Icon.world ~right_action: - (a ~a:[ a_href "/network"; a_class [ "d-Button" ] ] [ txt "Refresh" ]) - [ txt "Network" ] + (a ~a:[a_href "/network"; a_class ["d-Button"]] [txt "Refresh"]) + [txt "Network"] ) (div - [ - ( if List.length connected_services = 0 then txt "" + [ ( if List.length connected_services = 0 then txt "" else section - [ - ul - ~a:[ a_class [ "d-NetworkList" ]; a_role [ "list" ] ] - (List.map service_item connected_services); + [ ul + ~a:[a_class ["d-NetworkList"]; a_role ["list"]] + (List.map service_item connected_services) ] - ); - Definition.list + ) + ; Definition.list (( match proxy with | Some p -> - [ - Definition.term [ txt "Proxy" ]; - Definition.description [ txt p ]; - ] + [Definition.term [txt "Proxy"]; Definition.description [txt p]] | None -> [] ) - @ [ - Definition.term [ txt "Internet" ]; - Definition.description - [ - div + @ [ Definition.term [txt "Internet"] + ; Definition.description + [ div ~a: - [ - a_class [ "d-Spinner" ]; - Unsafe.string_attrib "is" "internet-status"; + [ a_class ["d-Spinner"] + ; Unsafe.string_attrib "is" "internet-status" ] - []; - ]; + [] + ] ] - ); - section - [ - h2 ~a:[ a_class [ "d-Title" ] ] [ txt "Available Networks" ]; - ( if List.length available_services = 0 then - p - ~a:[ a_class [ "d-Paragraph" ] ] - [ txt "No networks available" ] + ) + ; section + [ h2 ~a:[a_class ["d-Title"]] [txt "Available Networks"] + ; ( if List.length available_services = 0 then + p ~a:[a_class ["d-Paragraph"]] [txt "No networks available"] else ul - ~a:[ a_class [ "d-NetworkList" ]; a_role [ "list" ] ] + ~a:[a_class ["d-NetworkList"]; a_role ["list"]] (List.map service_item available_services) - ); - ]; - section - [ - h2 ~a:[ a_class [ "d-Title" ] ] [ txt "Network Interfaces" ]; - pre ~a:[ a_class [ "d-Preformatted" ] ] [ txt interfaces_str ]; - ]; + ) + ] + ; section + [ h2 ~a:[a_class ["d-Title"]] [txt "Network Interfaces"] + ; pre ~a:[a_class ["d-Preformatted"]] [txt interfaces_str] + ] ] ) diff --git a/controller/server/view/network_list_page.mli b/controller/server/view/network_list_page.mli index 23b194f9..3133aa40 100644 --- a/controller/server/view/network_list_page.mli +++ b/controller/server/view/network_list_page.mli @@ -1,10 +1,10 @@ open Protocol_conv_jsonm -type params = { - proxy : string option; - services : Connman.Service.t list; - interfaces : Network.Interface.t list; -} +type params = + { proxy: string option + ; services: Connman.Service.t list + ; interfaces: Network.Interface.t list + } [@@deriving protocol ~driver:(module Jsonm)] -val html : params -> [> Html_types.html ] Tyxml.Html.elt +val html : params -> [> Html_types.html] Tyxml.Html.elt diff --git a/controller/server/view/status_page.ml b/controller/server/view/status_page.ml index 91dd911f..00dfea41 100644 --- a/controller/server/view/status_page.ml +++ b/controller/server/view/status_page.ml @@ -7,101 +7,96 @@ type rauc_state = | Error of string [@@deriving sexp] -type params = { - health : Health.state; - update : Update.state; - rauc : rauc_state; - booted_slot : Rauc.Slot.t; -} +type params = + { health: Health.state + ; update: Update.state + ; rauc: rauc_state + ; booted_slot: Rauc.Slot.t + } let definition term description = - [ - Definition.term [ txt term ]; - Definition.description - [ pre ~a:[ a_class [ "d-Preformatted" ] ] [ txt description ] ]; + [ Definition.term [txt term] + ; Definition.description + [pre ~a:[a_class ["d-Preformatted"]] [txt description]] ] let health_fmt s = s |> Health.sexp_of_state |> Sexplib.Sexp.to_string_hum + let update_fmt s = s |> Update.sexp_of_state |> Sexplib.Sexp.to_string_hum + let rauc_fmt s = s |> sexp_of_rauc_state |> Sexplib.Sexp.to_string_hum + let slot_fmt = Rauc.Slot.string_of_t -let opt_elem opt = Option.value ~default:[] @@ Option.map (fun e -> [ e ]) opt + +let opt_elem opt = Option.value ~default:[] @@ Option.map (fun e -> [e]) opt let action_form ?confirm_msg action button_label = form - ~a:[ a_action action; a_method `Post; a_class [ "d-Status__ActionForm" ] ] - [ - input + ~a:[a_action action; a_method `Post; a_class ["d-Status__ActionForm"]] + [ input ~a: - ([ - a_input_type `Submit; a_class [ "d-Button" ]; a_value button_label; - ] + ([a_input_type `Submit; a_class ["d-Button"]; a_value button_label] @ opt_elem (Option.map (fun m -> a_onclick (Format.sprintf "return confirm('%s');" m)) confirm_msg ) ) - (); + () ] -let note body = div ~a:[ a_class [ "d-Note" ] ] [ txt body ] +let note body = div ~a:[a_class ["d-Note"]] [txt body] let reboot_call = - [ - note + [ note "A new version of PlayOS has been installed, reboot to switch to the new \ - version."; - action_form "/system/reboot" "Reboot into updated version"; + version." + ; action_form "/system/reboot" "Reboot into updated version" ] let switch_to_newer_system_call target_slot = - [ - note + [ note "This machine has an out of date PlayOS version selected as the\n\ \ default. You can switch to the new version (requires a \ - reboot)."; - action_form + reboot)." + ; action_form ("/system/switch/" ^ slot_fmt target_slot) - "Switch to newer version and reboot"; + "Switch to newer version and reboot" ] let switch_to_older_system_call target_slot = - [ - note + [ note "You are running the latest version of PlayOS, but you can still\n\ - \ switch back to the older version (requires a reboot)."; - action_form + \ switch back to the older version (requires a reboot)." + ; action_form ("/system/switch/" ^ slot_fmt target_slot) - "Switch to older version and reboot"; + "Switch to older version and reboot" ] let reinstall_call target_slot = - [ - note + [ note "The PlayOS installation appears to be faulty, manual system\n\ \ reinstallation is recommended. Please contact support. \ You can\n\ \ attempt to switch to another system slot (requires \ - reboot)."; - action_form + reboot)." + ; action_form ("/system/switch/" ^ slot_fmt target_slot) - "Switch to other slot and reboot"; + "Switch to other slot and reboot" ] let factory_reset_call = let confirm_msg = "This will wipe all configuration and login data. Proceed?" in - [ - note + [ note "WARNING: Clears all user data and reboots the machine, resulting in a\n\ \ fresh install state. Will require to manually reconfigure \ network,\n\ \ localization and all other settings. Any active sessions \ and/or\n\ - \ logins will be expired."; - action_form ~confirm_msg "/system/factory-reset" "⚠ Factory Reset"; + \ logins will be expired." + ; action_form ~confirm_msg "/system/factory-reset" "⚠ Factory Reset" ] let other_slot = @@ -117,24 +112,23 @@ let suggested_action_of_state (update : Update.state) (rauc : rauc_state) | RebootRequired, _ -> Some (Definition.description reboot_call) | OutOfDateVersionSelected, Status _ -> Some (Definition.description (switch_to_newer_system_call target_slot)) - | UpToDate { booted; inactive }, Status _ when booted <> inactive -> + | UpToDate {booted; inactive}, Status _ when booted <> inactive -> Some (Definition.description (switch_to_older_system_call target_slot)) | ReinstallRequired, _ -> Some (Definition.description (reinstall_call target_slot)) | _ -> None -let html { health; booted_slot; update; rauc } = +let html {health; booted_slot; update; rauc} = let opt_action = suggested_action_of_state update rauc booted_slot in Page.html ~current_page:Page.SystemStatus - ~header:(Page.header_title ~icon:Icon.screen [ txt "System Status" ]) + ~header:(Page.header_title ~icon:Icon.screen [txt "System Status"]) (Definition.list (definition "Health" (health_fmt health) @ definition "Update State" (update_fmt update) @ opt_elem opt_action @ definition "RAUC" (rauc_fmt rauc) - @ [ - Definition.term [ txt "Factory reset" ]; - Definition.description factory_reset_call; + @ [ Definition.term [txt "Factory reset"] + ; Definition.description factory_reset_call ] ) ) diff --git a/controller/server/view/status_page.mli b/controller/server/view/status_page.mli index 447bc22c..8cb0e753 100644 --- a/controller/server/view/status_page.mli +++ b/controller/server/view/status_page.mli @@ -3,11 +3,11 @@ type rauc_state = | Installing | Error of string -type params = { - health : Health.state; - update : Update.state; - rauc : rauc_state; - booted_slot : Rauc.Slot.t; -} +type params = + { health: Health.state + ; update: Update.state + ; rauc: rauc_state + ; booted_slot: Rauc.Slot.t + } -val html : params -> [> Html_types.html ] Tyxml.Html.elt +val html : params -> [> Html_types.html] Tyxml.Html.elt diff --git a/controller/tests/server/mocks/mock_rauc.ml b/controller/tests/server/mocks/mock_rauc.ml index 3fcfd80f..85123d9f 100644 --- a/controller/tests/server/mocks/mock_rauc.ml +++ b/controller/tests/server/mocks/mock_rauc.ml @@ -1,18 +1,17 @@ open Rauc -type state = { - mutable rauc_status : Rauc.status; - mutable primary_slot : Slot.t option; - mutable booted_slot : Slot.t; -} +type state = + { mutable rauc_status: Rauc.status + ; mutable primary_slot: Slot.t option + ; mutable booted_slot: Slot.t + } let some_status : Rauc.Slot.status = - { - device = "Device"; - state = "Good"; - class' = "class"; - version = "0.0.0"; - installed_timestamp = "2023-01-01T00:00:00Z"; + { device= "Device" + ; state= "Good" + ; class'= "class" + ; version= "0.0.0" + ; installed_timestamp= "2023-01-01T00:00:00Z" } class mock failure_generator = @@ -23,21 +22,18 @@ class mock failure_generator = in object (self) val state : state = - { - rauc_status = { a = some_status; b = some_status }; - primary_slot = None; - booted_slot = Slot.SystemA; + { rauc_status= {a= some_status; b= some_status} + ; primary_slot= None + ; booted_slot= Slot.SystemA } method set_status slot status = match slot with - | Slot.SystemA -> - state.rauc_status <- { state.rauc_status with a = status } - | Slot.SystemB -> - state.rauc_status <- { state.rauc_status with b = status } + | Slot.SystemA -> state.rauc_status <- {state.rauc_status with a= status} + | Slot.SystemB -> state.rauc_status <- {state.rauc_status with b= status} method set_version slot version = - self#set_status slot { (self#get_slot_status slot) with version } + self#set_status slot {(self#get_slot_status slot) with version} method get_status () = state.rauc_status |> return @@ -47,8 +43,11 @@ class mock failure_generator = | Slot.SystemB -> state.rauc_status.b method set_primary some_slot = state.primary_slot <- some_slot + method get_primary () = state.primary_slot |> return + method set_booted_slot slot = state.booted_slot <- slot + method get_booted_slot () = return state.booted_slot method private extract_version bundle_path = @@ -76,7 +75,7 @@ class mock failure_generator = method install (bundle_path : string) : unit Lwt.t = let vsn = self#extract_version bundle_path in - self#check_if_bundle_is_valid bundle_path vsn; + self#check_if_bundle_is_valid bundle_path vsn ; let%lwt booted_slot = self#get_booted_slot () in let other_slot = match booted_slot with @@ -84,7 +83,7 @@ class mock failure_generator = | Slot.SystemB -> Slot.SystemA in (* "install" into non-booted slot *) - let () = self#set_status other_slot { some_status with version = vsn } in + let () = self#set_status other_slot {some_status with version= vsn} in (* Note: UpdateService or RAUC bindings do not explicitly set the primary, but it is part of RAUC's install process, so we simulate it here too. *) @@ -96,9 +95,13 @@ class mock failure_generator = method to_module = (module struct let get_status = self#get_status + let get_booted_slot = self#get_booted_slot + let mark_good = self#mark_good + let get_primary = self#get_primary + let install = self#install end : Rauc_service.S ) diff --git a/controller/tests/server/mocks/mock_update_client.ml b/controller/tests/server/mocks/mock_update_client.ml index b6d3d64f..e3b3d46c 100644 --- a/controller/tests/server/mocks/mock_update_client.ml +++ b/controller/tests/server/mocks/mock_update_client.ml @@ -1,8 +1,8 @@ -type state = { - mutable latest_version : string; - mutable available_bundles : (string, string) Hashtbl.t; - mutable base_url : string; -} +type state = + { mutable latest_version: string + ; mutable available_bundles: (string, string) Hashtbl.t + ; mutable base_url: string + } let test_bundle_name = "TEST_PLAYOS_BUNDLE" @@ -14,16 +14,16 @@ class mock failure_generator = in object (self) val state = - { - latest_version = "0.0.0"; - available_bundles = Hashtbl.create 5; - base_url = Config.System.update_url; + { latest_version= "0.0.0" + ; available_bundles= Hashtbl.create 5 + ; base_url= Config.System.update_url } method add_bundle vsn contents = Hashtbl.add state.available_bundles vsn contents method remove_bundle vsn = Hashtbl.remove state.available_bundles vsn + method set_latest_version vsn = state.latest_version <- vsn method private gen_stored_bundle_path vsn = @@ -45,6 +45,7 @@ class mock failure_generator = method to_module = (module struct let download = self#download + let get_latest_version = self#get_latest_version end : Update_client.S ) diff --git a/controller/tests/server/update/helpers.ml b/controller/tests/server/update/helpers.ml index d10a1c50..bdd699a7 100644 --- a/controller/tests/server/update/helpers.ml +++ b/controller/tests/server/update/helpers.ml @@ -11,8 +11,7 @@ let slot_to_string = function | Rauc.Slot.SystemA -> "SystemA" | Rauc.Slot.SystemB -> "SystemB" -let version_info_to_string ({ latest; booted; inactive } : Update.version_info) - = +let version_info_to_string ({latest; booted; inactive} : Update.version_info) = Format.sprintf "{latest=%s booted=%s inactive=%s}" (Semver.to_string latest) (Semver.to_string booted) (Semver.to_string inactive) @@ -23,13 +22,13 @@ let statefmt (state : Update.state) : string = (* === Mock init and setup === *) let default_test_config : Update.config = - { error_backoff_duration = 0.01; check_for_updates_interval = 0.05 } + {error_backoff_duration= 0.01; check_for_updates_interval= 0.05} -type test_context = { - update_client : Mock_update_client.mock; - rauc : Mock_rauc.mock; - update_service : (module Update.UpdateService); -} +type test_context = + { update_client: Mock_update_client.mock + ; rauc: Mock_rauc.mock + ; update_service: (module Update.UpdateService) + } (* see [init_test_deps] for usage *) let no_failure_gen () = Lwt.return false @@ -44,59 +43,63 @@ let init_test_deps ?(failure_gen_rauc = no_failure_gen) let rauc = new Mock_rauc.mock failure_gen_rauc in let module TestUpdateServiceDeps = struct module ClientI = (val update_client#to_module) + module RaucI = (val rauc#to_module) let config = test_config end in let module TestUpdateService = Update.Make (TestUpdateServiceDeps) in - { update_client; rauc; update_service = (module TestUpdateService) } + {update_client; rauc; update_service= (module TestUpdateService)} -type system_slot_spec = { - booted_slot : Rauc.Slot.t; - primary_slot : Rauc.Slot.t Option.t; - input_versions : Update.version_info; -} +type system_slot_spec = + { booted_slot: Rauc.Slot.t + ; primary_slot: Rauc.Slot.t Option.t + ; input_versions: Update.version_info + } -let slot_spec_to_string { booted_slot; primary_slot; input_versions } = +let slot_spec_to_string {booted_slot; primary_slot; input_versions} = Format.sprintf "booted=%s\tprimary=%s\tvsns%s" (slot_to_string booted_slot) (Option.map slot_to_string primary_slot |> Option.value ~default:"-") (version_info_to_string input_versions) -let setup_mocks_from_system_slot_spec { rauc; update_client } case = - let { booted_slot; primary_slot; input_versions } = case in - +let setup_mocks_from_system_slot_spec {rauc; update_client} case = + let {booted_slot; primary_slot; input_versions} = case in let booted_version = Semver.to_string input_versions.booted in let secondary_version = Semver.to_string input_versions.inactive in let upstream_version = Semver.to_string input_versions.latest in - let inactive_slot = other_slot booted_slot in - - rauc#set_version booted_slot booted_version; - rauc#set_version inactive_slot secondary_version; - rauc#set_booted_slot booted_slot; - rauc#set_primary primary_slot; - update_client#set_latest_version upstream_version; + rauc#set_version booted_slot booted_version ; + rauc#set_version inactive_slot secondary_version ; + rauc#set_booted_slot booted_slot ; + rauc#set_primary primary_slot ; + update_client#set_latest_version upstream_version ; () (* === Test data and data generation === *) let v1 = Semver.of_string "1.0.0" |> Option.get + let v2 = Semver.of_string "2.0.0" |> Option.get + let v3 = Semver.of_string "3.0.0" |> Option.get + let flatten_tuple (a, (b, c)) = (a, b, c) let product l1 l2 = List.concat_map (fun e1 -> List.map (fun e2 -> (e1, e2)) l2) l1 let product3 l1 l2 l3 = product l1 (product l2 l3) |> List.map flatten_tuple -let possible_versions = [ v1; v2; v3 ] -let possible_booted_slots = [ Rauc.Slot.SystemA; Rauc.Slot.SystemB ] + +let possible_versions = [v1; v2; v3] + +let possible_booted_slots = [Rauc.Slot.SystemA; Rauc.Slot.SystemB] + let possible_primary_slots = None :: List.map Option.some possible_booted_slots let vsn_triple_to_version_info (latest, booted, inactive) : Update.version_info = - { latest; booted; inactive } + {latest; booted; inactive} let all_possible_slot_spec_combos = let vsn_triples = @@ -108,6 +111,6 @@ let all_possible_slot_spec_combos = List.map (fun (vsns, booted_slot, primary_slot) -> let vsn_info = vsn_triple_to_version_info vsns in - { booted_slot; primary_slot; input_versions = vsn_info } + {booted_slot; primary_slot; input_versions= vsn_info} ) combos diff --git a/controller/tests/server/update/outcome.ml b/controller/tests/server/update/outcome.ml index d08d6949..e1512e6e 100644 --- a/controller/tests/server/update/outcome.ml +++ b/controller/tests/server/update/outcome.ml @@ -11,7 +11,7 @@ type expected_outcomes = outcomes: installing the update or not installing the update. *) let slot_spec_to_outcome - ({ booted_slot; primary_slot; input_versions } : Helpers.system_slot_spec) = + ({booted_slot; primary_slot; input_versions} : Helpers.system_slot_spec) = let booted_is_out_of_date = Semver.compare input_versions.booted input_versions.latest = -1 in @@ -58,9 +58,7 @@ let test_slot_spec case = in Alcotest_lwt.test_case test_case_descr `Quick (fun _ () -> let mocks = Helpers.init_test_deps () in - let () = Helpers.setup_mocks_from_system_slot_spec mocks case in - let module UpdateServiceI = (val mocks.update_service) in let%lwt out_state = UpdateServiceI.run_step GettingVersionInfo in if state_matches_expected_outcome out_state expected_outcome then diff --git a/controller/tests/server/update/scenario.ml b/controller/tests/server/update/scenario.ml index 06a29960..51688d70 100644 --- a/controller/tests/server/update/scenario.ml +++ b/controller/tests/server/update/scenario.ml @@ -3,7 +3,9 @@ *) type action_descr = string + type action_check = Update.state -> bool Lwt.t + type mock_update = unit -> unit type scenario_spec = @@ -92,11 +94,9 @@ let check_state expected_state_sequence prev_state cur_state = (* after a callback first spec should always be the next state we expect *) if not (is_state_spec spec) then failwith @@ "Expected a state spec, but got " ^ specfmt spec - ^ " - bad spec?"; - + ^ " - bad spec?" ; (* check if state spec matches the prev_state (i.e. initial state) *) let%lwt () = interpret_spec prev_state spec in - (* progress forward until we either reach the end or we hit a state spec, which means we have to progress the state machine *) lwt_while @@ -137,17 +137,15 @@ let scenario_from_system_spec ?(booted_slot = Rauc.Slot.SystemA) ?(primary_slot = Some Rauc.Slot.SystemA) ~(input_versions : Update.version_info) (expected_state : Update.state) = let init_state = Update.GettingVersionInfo in - fun mocks -> let expected_state_sequence = - [ - UpdateMock + [ UpdateMock (fun () -> Helpers.setup_mocks_from_system_slot_spec mocks - { booted_slot; primary_slot; input_versions } - ); - StateReached Update.GettingVersionInfo; - StateReached expected_state; + {booted_slot; primary_slot; input_versions} + ) + ; StateReached Update.GettingVersionInfo + ; StateReached expected_state ] in (expected_state_sequence, init_state) diff --git a/controller/tests/server/update/update_client_mock_server.ml b/controller/tests/server/update/update_client_mock_server.ml index 5d6081af..79559bc2 100644 --- a/controller/tests/server/update/update_client_mock_server.ml +++ b/controller/tests/server/update/update_client_mock_server.ml @@ -16,17 +16,17 @@ let get_random_available_port () = let () = Unix.close sock in (Unix.string_of_inet_addr real_addr, real_port) -type state = { - latest_version : string; - available_bundles : (string, string) Hashtbl.t; -} +type state = + { latest_version: string + ; available_bundles: (string, string) Hashtbl.t + } type range = int Option.t * int Option.t let mock_server () = object (self) val mutable state = - ref { latest_version = "0.0.0"; available_bundles = Hashtbl.create 5 } + ref {latest_version= "0.0.0"; available_bundles= Hashtbl.create 5} method add_bundle vsn contents = Hashtbl.add !state.available_bundles vsn contents @@ -34,8 +34,7 @@ let mock_server () = method remove_bundle vsn contents = Hashtbl.remove !state.available_bundles vsn - method set_latest_version vsn = - state := { !state with latest_version = vsn } + method set_latest_version vsn = state := {!state with latest_version= vsn} method private get_latest_handler _req = let resp = Response.of_string_body !state.latest_version in @@ -46,20 +45,20 @@ let mock_server () = let range = Cohttp.Header.get headers "Range" in match range with | Some range_str -> ( - try - let regex = Str.regexp "bytes=\\([0-9]*\\)-\\([0-9]*\\)" in - let m = Str.string_match regex range_str 0 in - let r_str_to_opt s = - if String.length s > 0 then Some (int_of_string s) else None - in - if m then - let range_start = Str.matched_group 1 range_str in - let range_end = Str.matched_group 2 range_str in - (r_str_to_opt range_start, r_str_to_opt range_end) - else failwith @@ "Unsupported range string: " ^ range_str - with e -> - failwith @@ "Failed to parse range headers: " ^ Printexc.to_string e - ) + try + let regex = Str.regexp "bytes=\\([0-9]*\\)-\\([0-9]*\\)" in + let m = Str.string_match regex range_str 0 in + let r_str_to_opt s = + if String.length s > 0 then Some (int_of_string s) else None + in + if m then + let range_start = Str.matched_group 1 range_str in + let range_end = Str.matched_group 2 range_str in + (r_str_to_opt range_start, r_str_to_opt range_end) + else failwith @@ "Unsupported range string: " ^ range_str + with e -> + failwith @@ "Failed to parse range headers: " ^ Printexc.to_string e + ) | None -> (None, None) method private range_resp (range_start, range_end) bundle = @@ -77,23 +76,22 @@ let mock_server () = let resp = match bundle with | Some bund -> ( - match range with - | None, None -> Response.of_string_body bund - | _ -> - let bundle_trunc, (b_start, b_end, b_total) = - self#range_resp range bund - in - let body = bundle_trunc |> Bytes.to_string |> Body.of_string in - let headers = - Cohttp.Header.of_list - [ - ( "Content-Range", - Format.sprintf "bytes %d-%d/%d" b_start b_end b_total - ); - ] - in - Response.create ~headers ~body () - ) + match range with + | None, None -> Response.of_string_body bund + | _ -> + let bundle_trunc, (b_start, b_end, b_total) = + self#range_resp range bund + in + let body = bundle_trunc |> Bytes.to_string |> Body.of_string in + let headers = + Cohttp.Header.of_list + [ ( "Content-Range" + , Format.sprintf "bytes %d-%d/%d" b_start b_end b_total + ) + ] + in + Response.create ~headers ~body () + ) | None -> Response.of_string_body ~code:`Not_found "Bundle version not found" in diff --git a/controller/tests/server/update/update_client_tests.ml b/controller/tests/server/update/update_client_tests.ml index fdee55bd..504df6d7 100644 --- a/controller/tests/server/update/update_client_tests.ml +++ b/controller/tests/server/update/update_client_tests.ml @@ -13,9 +13,9 @@ open Update_client open Update_client_mock_server let setup_log () = - Fmt_tty.setup_std_outputs (); - Logs.set_level @@ Some Logs.Debug; - Logs.set_reporter (Logs_fmt.reporter ()); + Fmt_tty.setup_std_outputs () ; + Logs.set_level @@ Some Logs.Debug ; + Logs.set_reporter (Logs_fmt.reporter ()) ; () type proxy_param = @@ -30,8 +30,8 @@ let process_proxy_spec spec server_url = | UseMockServer -> (* pretend mock server is a proxy, i.e. use an invalid base_url, and the actual server_url for the proxy *) - ( server_url |> Option.some, - (* Note: DO NOT use https here, because curl will attempt + ( server_url |> Option.some + , (* Note: DO NOT use https here, because curl will attempt to CONNECT and then this whole setup doesn't work *) Uri.of_string "http://some-invalid-url.local/" ) @@ -44,7 +44,7 @@ let rec wait_for_mock_server ?(timeout = 0.2) ?(remaining_tries = 3) url = | Curl.RequestSuccess _ -> Lwt.return () | Curl.RequestFailure err -> let err_msg = Curl.pretty_print_error err in - print_endline ("MockServer not up, err was: " ^ err_msg); + print_endline ("MockServer not up, err was: " ^ err_msg) ; if remaining_tries > 0 then let%lwt () = Lwt_unix.sleep timeout in wait_for_mock_server @@ -63,7 +63,7 @@ let run_test_case ?(proxy = NoProxy) switch f = let%lwt () = wait_for_mock_server server_url in Lwt_switch.add_hook (Some switch) (fun () -> Lwt.return @@ Lwt.cancel server_task - ); + ) ; let proxy_url, base_url = process_proxy_spec proxy (Uri.of_string server_url) in @@ -97,9 +97,9 @@ let test_download_bundle_ok server (module Client : S) = Alcotest.(check bool) "Bundle file is downloaded and saved" (Sys.file_exists bundle_path) - true; + true ; Alcotest.(check string) - "Bundle contents are correct" (read_file bundle_path) bundle; + "Bundle contents are correct" (read_file bundle_path) bundle ; Lwt.return () (* NOTE: This test checks that the client resumes the download @@ -111,8 +111,7 @@ let test_resume_bundle_download server (module Client : S) = let () = server#add_bundle version bundle_contents in let%lwt bundle_path = Client.download version in Alcotest.(check string) - "Bundle contents are only partial" (read_file bundle_path) bundle_contents; - + "Bundle contents are only partial" (read_file bundle_path) bundle_contents ; (* NOTE that bundle_contents is not a prefix of bundle_contents_extra ! This is on purpose: to check that download client does not simply overwrite the downloaded file, otherwise we would not be testing @@ -122,13 +121,12 @@ let test_resume_bundle_download server (module Client : S) = *) let bundle_contents_extra = "BUNDLE_CONTENTS: 111999" in let () = server#add_bundle version bundle_contents_extra in - let%lwt bundle_path = Client.download version in Alcotest.(check string) "Bundle contents are resumed, not overwritten" (read_file bundle_path) (* NOTE: this is not the same as [bundle_contents_extra], it is only the last bytes of it beyond the length of [bundle_contents] *) - "BUNDLE_CONTENTS: 123999"; + "BUNDLE_CONTENTS: 123999" ; Lwt.return () (* invalid proxy URL is set in the `run_test_case` function, see below *) @@ -142,7 +140,7 @@ let test_invalid_proxy_fail _ (module Client : S) = Alcotest.(check bool) "Curl raised an exception about invalid proxy" (Str.string_match (Str.regexp ".*Could not resolve proxy.*") exn 0) - true; + true ; Lwt.return () | other_exn -> Alcotest.fail @@ "Got unexpected exception: " @@ -154,10 +152,9 @@ let () = (* All tests cases are run with proxy setup and without to verify it works always *) let test_cases = - [ - ("Get latest version", test_get_version_ok); - ("Download bundle", test_download_bundle_ok); - ("Resume download works", test_resume_bundle_download); + [ ("Get latest version", test_get_version_ok) + ; ("Download bundle", test_download_bundle_ok) + ; ("Resume download works", test_resume_bundle_download) ] in (* An extra case to check that proxy settings are honored in general *) @@ -169,18 +166,17 @@ let () = in Lwt_main.run @@ Alcotest_lwt.run "Basic tests" - [ - ( "without-proxy", - List.map + [ ( "without-proxy" + , List.map (fun (name, test_f) -> Alcotest_lwt.test_case name `Quick (fun switch () -> run_test_case switch test_f ) ) test_cases - ); - ( "with-proxy", - invalid_proxy_case + ) + ; ( "with-proxy" + , invalid_proxy_case :: List.map (fun (name, test_f) -> Alcotest_lwt.test_case name `Quick (fun switch () -> @@ -188,5 +184,5 @@ let () = ) ) test_cases - ); + ) ] diff --git a/controller/tests/server/update/update_prop_tests.ml b/controller/tests/server/update/update_prop_tests.ml index 8d07e9e5..7e8db660 100644 --- a/controller/tests/server/update/update_prop_tests.ml +++ b/controller/tests/server/update/update_prop_tests.ml @@ -41,7 +41,6 @@ let test_random_failure_case = QCheck2.Gen.triple rand_failure_sequence_upd_client rand_failure_sequence_rauc rand_spec in - let print_t (seq_upd, seq_rauc, inp_case) = let fail_seq_to_str seq = List.map @@ -67,9 +66,8 @@ let test_random_failure_case = let failure_gen_upd = failure_seq_to_f seq_upd in let failure_gen_rauc = failure_seq_to_f seq_rauc in let test_config = - { - Update.error_backoff_duration = 0.001; - Update.check_for_updates_interval = 0.002; + { Update.error_backoff_duration= 0.001 + ; Update.check_for_updates_interval= 0.002 } in let mocks = @@ -87,7 +85,7 @@ let test_random_failure_case = @@ List.of_seq (Queue.to_seq state_seq) in let rec do_while ?(c = 0) loop_lim cur_state = - Queue.push cur_state state_seq; + Queue.push cur_state state_seq ; let out = run cur_state in match out with | Error e -> @@ -102,7 +100,7 @@ let test_random_failure_case = (Printexc.get_backtrace ()) (state_seq_to_str state_seq) | Ok Update.GettingVersionInfo -> - Queue.push Update.GettingVersionInfo state_seq; + Queue.push Update.GettingVersionInfo state_seq ; true | Ok state -> if c < loop_lim then do_while ~c:(c + 1) loop_lim state @@ -120,14 +118,12 @@ let test_random_failure_case = ~print:print_t gen test_check let () = - let argv_with_verbose = Array.append Sys.argv [| "--verbose" |] in + let argv_with_verbose = Array.append Sys.argv [|"--verbose"|] in Alcotest.run ~argv:argv_with_verbose ~and_exit:false "UpdateService qcheck/prop tests" - [ - ( "Fault injection test", - [ - QCheck_alcotest.to_alcotest ~verbose:true ~long:true - test_random_failure_case; + [ ( "Fault injection test" + , [ QCheck_alcotest.to_alcotest ~verbose:true ~long:true + test_random_failure_case ] - ); + ) ] diff --git a/controller/tests/server/update/update_tests.ml b/controller/tests/server/update/update_tests.ml index 381439f7..035ffb62 100644 --- a/controller/tests/server/update/update_tests.ml +++ b/controller/tests/server/update/update_tests.ml @@ -3,38 +3,34 @@ open Test_mocks open Update_test_helpers (* Main test scenario: full update process *) -let both_out_of_date ({ update_client; rauc } : Helpers.test_context) = +let both_out_of_date ({update_client; rauc} : Helpers.test_context) = let init_state = GettingVersionInfo in let booted_version = "10.0.0" in let inactive_version = "9.0.0" in let upstream_version = "10.0.2" in - let expected_bundle_name vsn = Mock_update_client.test_bundle_name ^ Scenario._WILDCARD_PAT ^ vsn ^ Scenario._WILDCARD_PAT in - let expected_state_sequence = - [ - Scenario.UpdateMock + [ Scenario.UpdateMock (fun () -> - rauc#set_version SystemA booted_version; - rauc#set_version SystemB inactive_version; - rauc#set_booted_slot SystemA; - + rauc#set_version SystemA booted_version ; + rauc#set_version SystemB inactive_version ; + rauc#set_booted_slot SystemA ; update_client#add_bundle upstream_version - ("BUNDLE_CONTENTS: " ^ upstream_version); + ("BUNDLE_CONTENTS: " ^ upstream_version) ; update_client#set_latest_version upstream_version - ); - Scenario.StateReached GettingVersionInfo; - Scenario.StateReached (Downloading upstream_version); - Scenario.StateReached + ) + ; Scenario.StateReached GettingVersionInfo + ; Scenario.StateReached (Downloading upstream_version) + ; Scenario.StateReached (Installing (Scenario._WILDCARD_PAT ^ expected_bundle_name upstream_version) - ); - Scenario.ActionDone - ( "bundle was installed into secondary slot", - fun _ -> + ) + ; Scenario.ActionDone + ( "bundle was installed into secondary slot" + , fun _ -> let status = rauc#get_slot_status SystemB in let () = Alcotest.(check string) @@ -42,53 +38,50 @@ let both_out_of_date ({ update_client; rauc } : Helpers.test_context) = upstream_version status.version in Lwt.return true - ); - Scenario.StateReached RebootRequired; - Scenario.StateReached GettingVersionInfo; + ) + ; Scenario.StateReached RebootRequired + ; Scenario.StateReached GettingVersionInfo ] in (expected_state_sequence, init_state) let delete_downloaded_bundle_on_err - ({ update_client; rauc } : Helpers.test_context) = + ({update_client; rauc} : Helpers.test_context) = let inactive_version = "9.0.0" in let upstream_version = "10.0.0" in - let init_state = Downloading upstream_version in let expected_bundle_name vsn = Mock_update_client.test_bundle_name ^ Scenario._WILDCARD_PAT ^ vsn ^ Scenario._WILDCARD_PAT in - let expected_state_sequence = - [ - Scenario.UpdateMock + [ Scenario.UpdateMock (fun () -> - rauc#set_version SystemB inactive_version; - rauc#set_booted_slot SystemA; + rauc#set_version SystemB inactive_version ; + rauc#set_booted_slot SystemA ; (* bundles that do not contain their own version will be treated as invalid by mock RAUC *) update_client#add_bundle upstream_version "CORRUPT_BUNDLE_CONTENTS" - ); - Scenario.StateReached (Downloading upstream_version); - Scenario.StateReached + ) + ; Scenario.StateReached (Downloading upstream_version) + ; Scenario.StateReached (Installing (Scenario._WILDCARD_PAT ^ expected_bundle_name upstream_version) - ); - Scenario.ActionDone - ( "bundle was deleted from path due to installation error", - fun (Installing path) -> + ) + ; Scenario.ActionDone + ( "bundle was deleted from path due to installation error" + , fun (Installing path) -> let status = rauc#get_slot_status SystemB in Alcotest.(check string) "Inactive slot remains in the same version" inactive_version - status.version; + status.version ; Alcotest.(check bool) "Downloaded corrupt bundle was deleted" false - (Sys.file_exists path); + (Sys.file_exists path) ; Lwt.return true - ); - Scenario.StateReached (ErrorInstalling Scenario._WILDCARD_PAT); - Scenario.StateReached GettingVersionInfo; + ) + ; Scenario.StateReached (ErrorInstalling Scenario._WILDCARD_PAT) + ; Scenario.StateReached GettingVersionInfo ] in (expected_state_sequence, init_state) @@ -96,30 +89,26 @@ let delete_downloaded_bundle_on_err let sleep_after_error_or_check_test () = (* long-ish timeouts, but these will run in parallel, so no biggie *) let test_config = - { error_backoff_duration = 1.0; check_for_updates_interval = 2.0 } + {error_backoff_duration= 1.0; check_for_updates_interval= 2.0} in - - let ({ update_service; _ } : Helpers.test_context) = + let ({update_service; _} : Helpers.test_context) = Helpers.init_test_deps ~test_config () in let module UpdateServiceI = (val update_service) in let error_states = - [ - ErrorGettingVersionInfo "err"; - ErrorInstalling "err"; - ErrorDownloading "err"; + [ ErrorGettingVersionInfo "err" + ; ErrorInstalling "err" + ; ErrorDownloading "err" ] in let post_check_states = - [ - UpToDate - (Helpers.vsn_triple_to_version_info (Helpers.v1, Helpers.v1, Helpers.v1)); - RebootRequired; - OutOfDateVersionSelected; - ReinstallRequired; + [ UpToDate + (Helpers.vsn_triple_to_version_info (Helpers.v1, Helpers.v1, Helpers.v1)) + ; RebootRequired + ; OutOfDateVersionSelected + ; ReinstallRequired ] in - let test_state expected_timeout inp_state = let start_time = Unix.gettimeofday () in (* NOTE: running the same step TWICE to ensure @@ -144,42 +133,42 @@ let sleep_after_error_or_check_test () = let both_newer_than_upstream = let input_versions = - { booted = Helpers.v3; inactive = Helpers.v2; latest = Helpers.v1 } + {booted= Helpers.v3; inactive= Helpers.v2; latest= Helpers.v1} in let expected_state = UpToDate input_versions in Scenario.scenario_from_system_spec ~input_versions expected_state let booted_newer_secondary_older = let input_versions = - { latest = Helpers.v2; booted = Helpers.v3; inactive = Helpers.v1 } + {latest= Helpers.v2; booted= Helpers.v3; inactive= Helpers.v1} in let expected_state = UpToDate input_versions in Scenario.scenario_from_system_spec ~input_versions expected_state let booted_older_secondary_newer = let input_versions = - { latest = Helpers.v2; booted = Helpers.v1; inactive = Helpers.v3 } + {latest= Helpers.v2; booted= Helpers.v1; inactive= Helpers.v3} in let expected_state = OutOfDateVersionSelected in Scenario.scenario_from_system_spec ~input_versions expected_state let booted_current_secondary_current = let input_versions = - { latest = Helpers.v2; booted = Helpers.v2; inactive = Helpers.v2 } + {latest= Helpers.v2; booted= Helpers.v2; inactive= Helpers.v2} in let expected_state = UpToDate input_versions in Scenario.scenario_from_system_spec ~input_versions expected_state let booted_current_secondary_older = let input_versions = - { latest = Helpers.v2; booted = Helpers.v2; inactive = Helpers.v1 } + {latest= Helpers.v2; booted= Helpers.v2; inactive= Helpers.v1} in let expected_state = UpToDate input_versions in Scenario.scenario_from_system_spec ~input_versions expected_state let booted_older_secondary_current = let input_versions = - { latest = Helpers.v2; booted = Helpers.v1; inactive = Helpers.v2 } + {latest= Helpers.v2; booted= Helpers.v1; inactive= Helpers.v2} in let expected_state = OutOfDateVersionSelected in Scenario.scenario_from_system_spec ~input_versions expected_state @@ -187,49 +176,46 @@ let booted_older_secondary_current = let () = Lwt_main.run @@ Alcotest_lwt.run "UpdateService tests" - [ - ( "Main cases, booted = primary", - [ - (* BOOTED = PRIMARY in all these *) + [ ( "Main cases, booted = primary" + , [ (* BOOTED = PRIMARY in all these *) Alcotest_lwt.test_case "Both slots out of date -> Update" `Quick (fun _ () -> Scenario.run both_out_of_date - ); - Alcotest_lwt.test_case "Both slots newer than upstream -> UpToDate" + ) + ; Alcotest_lwt.test_case "Both slots newer than upstream -> UpToDate" `Quick (fun _ () -> Scenario.run both_newer_than_upstream - ); - Alcotest_lwt.test_case + ) + ; Alcotest_lwt.test_case "Booted slot current, inactive older -> UpToDate" `Quick (fun _ () -> Scenario.run booted_current_secondary_older - ); - Alcotest_lwt.test_case + ) + ; Alcotest_lwt.test_case "Booted slot older, inactive current -> UpToDate" `Quick (fun _ () -> Scenario.run booted_older_secondary_current - ); - Alcotest_lwt.test_case + ) + ; Alcotest_lwt.test_case "Booted slot current, inactive current -> UpToDate" `Quick (fun _ () -> Scenario.run booted_current_secondary_current - ); - Alcotest_lwt.test_case + ) + ; Alcotest_lwt.test_case "Booted slot newer, inactive older -> UpToDate" `Quick (fun _ () -> Scenario.run booted_newer_secondary_older - ); - Alcotest_lwt.test_case + ) + ; Alcotest_lwt.test_case "Booted slot older, inactive newer -> OutOfDateVersionSelected" `Quick (fun _ () -> Scenario.run booted_older_secondary_newer - ); + ) ] - ); - ( "Error handling", - [ - Alcotest_lwt.test_case "Delete downloaded bundle on install error" + ) + ; ( "Error handling" + , [ Alcotest_lwt.test_case "Delete downloaded bundle on install error" `Quick (fun _ () -> Scenario.run delete_downloaded_bundle_on_err - ); - Alcotest_lwt.test_case "Sleep for a duration after error or check" + ) + ; Alcotest_lwt.test_case "Sleep for a duration after error or check" `Quick (fun _ () -> sleep_after_error_or_check_test () - ); + ) ] - ); - ( "All version/slot combinations", - List.map Outcome.test_slot_spec Helpers.all_possible_slot_spec_combos - ); + ) + ; ( "All version/slot combinations" + , List.map Outcome.test_slot_spec Helpers.all_possible_slot_spec_combos + ) ]