From dbf94983541fb6d9f6084fee76380dc4d7bc9a93 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lars=20Sj=C3=B6strom?= <lars@radicore.se>
Date: Wed, 8 Jan 2025 11:09:34 +0100
Subject: [PATCH 1/2] feat(hostd): varlink interfaced host controller to manage
 machine configuration and boot mgmt

---
 .cargo/config.toml     |   4 -
 Cargo.lock             | 456 ++++++++++++++++++++++++++++++++++++++++-
 Cargo.toml             |   3 +-
 flake.nix              | 123 ++++++-----
 hostd/.gitignore       |   1 +
 hostd/Cargo.toml       |  23 +++
 hostd/src/api.rs       |  17 ++
 hostd/src/bin/hostd.rs |  75 +++++++
 hostd/src/context.rs   |   9 +
 hostd/src/lib.rs       |   4 +
 hostd/src/machine.rs   |  43 ++++
 hostd/src/sysupdate.rs |  52 +++++
 12 files changed, 752 insertions(+), 58 deletions(-)
 create mode 100644 hostd/.gitignore
 create mode 100644 hostd/Cargo.toml
 create mode 100644 hostd/src/api.rs
 create mode 100644 hostd/src/bin/hostd.rs
 create mode 100644 hostd/src/context.rs
 create mode 100644 hostd/src/lib.rs
 create mode 100644 hostd/src/machine.rs
 create mode 100644 hostd/src/sysupdate.rs

diff --git a/.cargo/config.toml b/.cargo/config.toml
index 5903a40..7f193ac 100644
--- a/.cargo/config.toml
+++ b/.cargo/config.toml
@@ -4,7 +4,3 @@ xtask = "run --package xtask --quiet --"
 [profile.dev]
 debug = 0
 strip = "debuginfo"
-
-[target.x86_64-unknown-linux-gnu]
-linker = "clang"
-rustflags = ["-C", "link-arg=-fuse-ld=mold", "-C", "target-cpu=native"]
diff --git a/Cargo.lock b/Cargo.lock
index e2d7e26..8885015 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -103,6 +103,132 @@ version = "1.0.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
 
+[[package]]
+name = "async-broadcast"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532"
+dependencies = [
+ "event-listener",
+ "event-listener-strategy",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-channel"
+version = "2.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a"
+dependencies = [
+ "concurrent-queue",
+ "event-listener-strategy",
+ "futures-core",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-executor"
+version = "1.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec"
+dependencies = [
+ "async-task",
+ "concurrent-queue",
+ "fastrand",
+ "futures-lite",
+ "slab",
+]
+
+[[package]]
+name = "async-fs"
+version = "2.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a"
+dependencies = [
+ "async-lock",
+ "blocking",
+ "futures-lite",
+]
+
+[[package]]
+name = "async-io"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059"
+dependencies = [
+ "async-lock",
+ "cfg-if",
+ "concurrent-queue",
+ "futures-io",
+ "futures-lite",
+ "parking",
+ "polling",
+ "rustix",
+ "slab",
+ "tracing",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "async-lock"
+version = "3.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18"
+dependencies = [
+ "event-listener",
+ "event-listener-strategy",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "async-process"
+version = "2.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb"
+dependencies = [
+ "async-channel",
+ "async-io",
+ "async-lock",
+ "async-signal",
+ "async-task",
+ "blocking",
+ "cfg-if",
+ "event-listener",
+ "futures-lite",
+ "rustix",
+ "tracing",
+]
+
+[[package]]
+name = "async-recursion"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "async-signal"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3"
+dependencies = [
+ "async-io",
+ "async-lock",
+ "atomic-waker",
+ "cfg-if",
+ "futures-core",
+ "futures-io",
+ "rustix",
+ "signal-hook-registry",
+ "slab",
+ "windows-sys 0.59.0",
+]
+
 [[package]]
 name = "async-stream"
 version = "0.3.6"
@@ -125,6 +251,12 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "async-task"
+version = "4.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
+
 [[package]]
 name = "async-trait"
 version = "0.1.85"
@@ -249,6 +381,19 @@ dependencies = [
  "generic-array",
 ]
 
+[[package]]
+name = "blocking"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea"
+dependencies = [
+ "async-channel",
+ "async-task",
+ "futures-io",
+ "futures-lite",
+ "piper",
+]
+
 [[package]]
 name = "bumpalo"
 version = "3.16.0"
@@ -632,6 +777,33 @@ dependencies = [
  "cfg-if",
 ]
 
+[[package]]
+name = "endi"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf"
+
+[[package]]
+name = "enumflags2"
+version = "0.7.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147"
+dependencies = [
+ "enumflags2_derive",
+ "serde",
+]
+
+[[package]]
+name = "enumflags2_derive"
+version = "0.7.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "equivalent"
 version = "1.0.1"
@@ -670,6 +842,16 @@ dependencies = [
  "pin-project-lite",
 ]
 
+[[package]]
+name = "event-listener-strategy"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2"
+dependencies = [
+ "event-listener",
+ "pin-project-lite",
+]
+
 [[package]]
 name = "fastrand"
 version = "2.3.0"
@@ -792,6 +974,19 @@ version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
 
+[[package]]
+name = "futures-lite"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532"
+dependencies = [
+ "fastrand",
+ "futures-core",
+ "futures-io",
+ "parking",
+ "pin-project-lite",
+]
+
 [[package]]
 name = "futures-macro"
 version = "0.3.31"
@@ -958,6 +1153,27 @@ dependencies = [
  "windows-sys 0.59.0",
 ]
 
+[[package]]
+name = "hostd"
+version = "0.2.0"
+dependencies = [
+ "anyhow",
+ "clap",
+ "dropshot",
+ "http",
+ "schemars",
+ "serde",
+ "slog",
+ "slog-async",
+ "sqlx",
+ "tokio",
+ "trace-request",
+ "tracing",
+ "tracing-slog",
+ "zbus",
+ "zbus_systemd",
+]
+
 [[package]]
 name = "hostname"
 version = "0.3.1"
@@ -1477,6 +1693,15 @@ version = "2.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
 
+[[package]]
+name = "memoffset"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
+dependencies = [
+ "autocfg",
+]
+
 [[package]]
 name = "mime"
 version = "0.3.17"
@@ -1537,6 +1762,19 @@ dependencies = [
  "tempfile",
 ]
 
+[[package]]
+name = "nix"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
+dependencies = [
+ "bitflags",
+ "cfg-if",
+ "cfg_aliases",
+ "libc",
+ "memoffset",
+]
+
 [[package]]
 name = "nu-ansi-term"
 version = "0.46.0"
@@ -1776,6 +2014,16 @@ dependencies = [
  "tracing",
 ]
 
+[[package]]
+name = "ordered-stream"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50"
+dependencies = [
+ "futures-core",
+ "pin-project-lite",
+]
+
 [[package]]
 name = "overload"
 version = "0.1.1"
@@ -1902,6 +2150,17 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
 
+[[package]]
+name = "piper"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066"
+dependencies = [
+ "atomic-waker",
+ "fastrand",
+ "futures-io",
+]
+
 [[package]]
 name = "pkcs1"
 version = "0.7.5"
@@ -1929,6 +2188,21 @@ version = "0.3.31"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2"
 
+[[package]]
+name = "polling"
+version = "3.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f"
+dependencies = [
+ "cfg-if",
+ "concurrent-queue",
+ "hermit-abi",
+ "pin-project-lite",
+ "rustix",
+ "tracing",
+ "windows-sys 0.59.0",
+]
+
 [[package]]
 name = "powerfmt"
 version = "0.2.0"
@@ -1944,6 +2218,15 @@ dependencies = [
  "zerocopy",
 ]
 
+[[package]]
+name = "proc-macro-crate"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b"
+dependencies = [
+ "toml_edit",
+]
+
 [[package]]
 name = "proc-macro2"
 version = "1.0.93"
@@ -2519,9 +2802,9 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.135"
+version = "1.0.138"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9"
+checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949"
 dependencies = [
  "itoa",
  "memchr",
@@ -2539,6 +2822,17 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "serde_repr"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
 [[package]]
 name = "serde_spanned"
 version = "0.6.8"
@@ -2947,6 +3241,12 @@ version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
 
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
 [[package]]
 name = "stringprep"
 version = "0.1.5"
@@ -2972,9 +3272,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
 
 [[package]]
 name = "syn"
-version = "2.0.96"
+version = "2.0.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
+checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -3286,7 +3586,7 @@ dependencies = [
  "serde",
  "serde_spanned",
  "toml_datetime",
- "winnow",
+ "winnow 0.6.24",
 ]
 
 [[package]]
@@ -3529,6 +3829,17 @@ dependencies = [
  "typify-impl",
 ]
 
+[[package]]
+name = "uds_windows"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9"
+dependencies = [
+ "memoffset",
+ "tempfile",
+ "winapi",
+]
+
 [[package]]
 name = "unicode-bidi"
 version = "0.3.18"
@@ -4006,6 +4317,15 @@ dependencies = [
  "memchr",
 ]
 
+[[package]]
+name = "winnow"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86e376c75f4f43f44db463cf729e0d3acbf954d13e22c51e26e4c264b4ab545f"
+dependencies = [
+ "memchr",
+]
+
 [[package]]
 name = "write16"
 version = "1.0.0"
@@ -4018,6 +4338,16 @@ version = "0.5.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
 
+[[package]]
+name = "xdg-home"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6"
+dependencies = [
+ "libc",
+ "windows-sys 0.59.0",
+]
+
 [[package]]
 name = "xtask"
 version = "0.2.0"
@@ -4052,6 +4382,80 @@ dependencies = [
  "synstructure",
 ]
 
+[[package]]
+name = "zbus"
+version = "5.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cbddd8b6cb25d5d8ec1b23277b45299a98bfb220f1761ca11e186d5c702507f8"
+dependencies = [
+ "async-broadcast",
+ "async-executor",
+ "async-fs",
+ "async-io",
+ "async-lock",
+ "async-process",
+ "async-recursion",
+ "async-task",
+ "async-trait",
+ "blocking",
+ "enumflags2",
+ "event-listener",
+ "futures-core",
+ "futures-util",
+ "hex",
+ "nix",
+ "ordered-stream",
+ "serde",
+ "serde_repr",
+ "static_assertions",
+ "tracing",
+ "uds_windows",
+ "windows-sys 0.59.0",
+ "winnow 0.7.1",
+ "xdg-home",
+ "zbus_macros",
+ "zbus_names",
+ "zvariant",
+]
+
+[[package]]
+name = "zbus_macros"
+version = "5.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dac404d48b4e9cf193c8b49589f3280ceca5ff63519e7e64f55b4cf9c47ce146"
+dependencies = [
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "zbus_names",
+ "zvariant",
+ "zvariant_utils",
+]
+
+[[package]]
+name = "zbus_names"
+version = "4.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7be68e64bf6ce8db94f63e72f0c7eb9a60d733f7e0499e628dfab0f84d6bcb97"
+dependencies = [
+ "serde",
+ "static_assertions",
+ "winnow 0.7.1",
+ "zvariant",
+]
+
+[[package]]
+name = "zbus_systemd"
+version = "0.25701.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b772577120750e5a5ab5a5530f38fd462a35fafd9ed475098714afd1b1855ec"
+dependencies = [
+ "futures",
+ "serde",
+ "zbus",
+]
+
 [[package]]
 name = "zerocopy"
 version = "0.7.35"
@@ -4149,3 +4553,45 @@ dependencies = [
  "cc",
  "pkg-config",
 ]
+
+[[package]]
+name = "zvariant"
+version = "5.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31c951c21879c6e1d46ac5adfc34f698fefb465d498cf4ac87545849bd71bb5a"
+dependencies = [
+ "endi",
+ "enumflags2",
+ "serde",
+ "static_assertions",
+ "winnow 0.7.1",
+ "zvariant_derive",
+ "zvariant_utils",
+]
+
+[[package]]
+name = "zvariant_derive"
+version = "5.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9eeb539471af098d9e63faf428c71ac4cd4efe0b5baa3c8a6b991c5f2543b70e"
+dependencies = [
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "zvariant_utils",
+]
+
+[[package]]
+name = "zvariant_utils"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e16edfee43e5d7b553b77872d99bc36afdda75c223ca7ad5e3fbecd82ca5fc34"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "serde",
+ "static_assertions",
+ "syn",
+ "winnow 0.7.1",
+]
diff --git a/Cargo.toml b/Cargo.toml
index d61baf6..5afc72d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,6 +4,7 @@ members = [
   "agent",
   "controller",
   "instrumentation",
+  "hostd",
   "trace-request",
   "xtask",
 ]
@@ -44,7 +45,5 @@ slog = "2.7.0"
 slog-async = "2.8.0"
 tokio = { version = "1.43.0", features = ["full"] }
 tracing = "0.1.41"
-tracing-core = "0.1.33"
-tracing-chrome = "0.7.2"
 tracing-slog = { git = "https://github.com/oxidecomputer/tracing-slog", default-features = false }
 uuid = { version = "1", features = [ "serde", "v4" ] }
diff --git a/flake.nix b/flake.nix
index ac97f6e..ad29dff 100644
--- a/flake.nix
+++ b/flake.nix
@@ -26,24 +26,21 @@
     flake-utils.lib.eachDefaultSystem (
       system:
       let
-        rustVersion = "1.83.0";
+        target = "x86_64-unknown-linux-musl";
 
-        overlays = [
-          (import rust-overlay)
-          (final: prev: {
-            nix-filter = nix-filter.lib;
-            rust-toolchain = pkgs.rust-bin.stable.${rustVersion}.default;
-            rust-dev-toolchain = pkgs.rust-toolchain.override {
-              extensions = [
-                "rust-analyzer"
-                "rust-src"
-              ];
-            };
-          })
-        ];
+        pkgs = import nixpkgs {
+          inherit system;
+          overlays = [ (import rust-overlay) ];
+          crossSystem.config = target;
+        };
+        staticPkgs = pkgs.pkgsStatic;
 
-        pkgs = import nixpkgs { inherit overlays system; };
-        craneLib = (crane.mkLib pkgs).overrideToolchain pkgs.rust-toolchain;
+        craneLib = (crane.mkLib staticPkgs).overrideToolchain (
+          p:
+          p.rust-bin.stable.latest.default.override {
+            targets = [ target ];
+          }
+        );
 
         src = pkgs.lib.fileset.toSource {
           root = ./.;
@@ -58,19 +55,20 @@
         commonArgs = {
           inherit src;
 
-          stdenv = pkgs.stdenvAdapters.useMoldLinker pkgs.stdenv;
           strictDeps = true;
-          cargoArtifacts = craneLib.buildDepsOnly commonArgs;
 
-          nativeBuildInputs = with pkgs; [
-            clang
-            mold-wrapped
-            pkg-config
-          ];
+          nativeBuildInputs = with staticPkgs.pkgsBuildHost; [ pkg-config ];
+          buildInputs = with staticPkgs.pkgsHostHost; [ openssl ];
 
-          buildInputs = with pkgs; [
-            openssl
-          ];
+          CARGO_BUILD_TARGET = target;
+          CARGO_BUILD_RUSTFLAGS = "-C target-feature=+crt-static";
+          "CARGO_TARGET_${pkgs.lib.toUpper (builtins.replaceStrings [ "-" ] [ "_" ] target)}_LINKER" =
+            "${staticPkgs.stdenv.cc.targetPrefix}cc";
+
+          OPENSSL_STATIC = true;
+          OPENSSL_DIR = "${staticPkgs.openssl.dev}";
+          OPENSSL_LIB_DIR = "${staticPkgs.openssl.out}/lib";
+          OPENSSL_INCLUDE_DIR = "${staticPkgs.openssl.dev}/include/";
         };
 
         buildCrate =
@@ -85,15 +83,49 @@
 
         patagia-agent = buildCrate "patagia-agent" ./agent;
         patagia-controller = buildCrate "patagia-controller" ./controller;
+        hostd = buildCrate "hostd" ./hostd;
         xtask = buildCrate "xtask" ./xtask;
       in
       {
         packages = {
-          inherit patagia-agent patagia-controller xtask;
+          inherit
+            hostd
+            patagia-agent
+            patagia-controller
+            xtask
+            ;
+
+          hostd-service =
+            let
+              hostd-service = pkgs.writeText "hostd.service" ''
+                [Unit]
+                Description=Patagia Hostd
+
+                [Service]
+                Environment=RUST_LOG=debug
+                ExecStart=${hostd}/bin/hostd
+                Restart=always
+                RestartSec=30s
+
+                [Install]
+                WantedBy=multi-user.target
+              '';
+            in
+            pkgs.portableService {
+              pname = "hostd";
+              version = "v0.0.1";
+              units = [ hostd-service ];
+            };
+
         };
 
         checks = {
-          inherit patagia-agent patagia-controller xtask;
+          inherit
+            hostd
+            patagia-agent
+            patagia-controller
+            xtask
+            ;
 
           audit = craneLib.cargoAudit (commonArgs // { inherit advisory-db; });
 
@@ -130,25 +162,22 @@
 
         devShells.default = pkgs.mkShell {
           nativeBuildInputs = commonArgs.nativeBuildInputs;
-          buildInputs =
-            with pkgs;
-            [
-              bacon
-              cargo-edit
-              cargo-features-manager
-              cargo-hakari
-              cargo-machete
-              cargo-nextest
-              cargo-watch
-              hyperfine
-              just
-              nixfmt-rfc-style
-              rust-dev-toolchain
-              sqls
-              sqlx-cli
-              watchexec
-            ]
-            ++ commonArgs.buildInputs;
+          buildInputs = with pkgs; [
+            bacon
+            cargo-edit
+            cargo-features-manager
+            cargo-hakari
+            cargo-machete
+            cargo-nextest
+            cargo-watch
+            hyperfine
+            just
+            nixfmt-rfc-style
+            rust-dev-toolchain
+            sqls
+            sqlx-cli
+            watchexec
+          ];
           RUST_BACKTRACE = 1;
           RUST_SRC_PATH = pkgs.rustPlatform.rustLibSrc; # Required for rust-analyzer
         };
diff --git a/hostd/.gitignore b/hostd/.gitignore
new file mode 100644
index 0000000..ea8c4bf
--- /dev/null
+++ b/hostd/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/hostd/Cargo.toml b/hostd/Cargo.toml
new file mode 100644
index 0000000..f3322c7
--- /dev/null
+++ b/hostd/Cargo.toml
@@ -0,0 +1,23 @@
+[package]
+name = "hostd"
+version.workspace = true
+edition.workspace = true
+
+[dependencies]
+anyhow.workspace = true
+tokio.workspace = true
+sqlx = { version = "0.8.3", default-features = false, features = [
+    "macros", "migrate", "postgres", "runtime-tokio", "tls-rustls", "time", "uuid"
+  ] }
+dropshot.workspace = true
+clap.workspace = true
+slog.workspace = true
+slog-async.workspace = true
+tracing-slog.workspace = true
+tracing.workspace = true
+trace-request = { path = "../trace-request" }
+schemars.workspace = true
+serde.workspace = true
+http.workspace = true
+zbus_systemd = { version = "0.25701.0", features = ["hostname1", "sysupdate1", "network1", "portable1", "resolve1", "systemd1"] }
+zbus = "5.4.0"
diff --git a/hostd/src/api.rs b/hostd/src/api.rs
new file mode 100644
index 0000000..7f68d70
--- /dev/null
+++ b/hostd/src/api.rs
@@ -0,0 +1,17 @@
+use anyhow::Result;
+use dropshot::ApiDescription;
+
+use std::sync::Arc;
+
+use crate::context::ControllerContext;
+use crate::machine;
+use crate::sysupdate;
+
+type ControllerApiDescription = ApiDescription<Arc<ControllerContext>>;
+
+pub fn api() -> Result<ControllerApiDescription> {
+    let mut api = ControllerApiDescription::new();
+    api.register(machine::describe)?;
+    api.register(sysupdate::list_versions)?;
+    Ok(api)
+}
diff --git a/hostd/src/bin/hostd.rs b/hostd/src/bin/hostd.rs
new file mode 100644
index 0000000..003671e
--- /dev/null
+++ b/hostd/src/bin/hostd.rs
@@ -0,0 +1,75 @@
+use anyhow::{anyhow, Result};
+
+use clap::Parser;
+use dropshot::{ConfigDropshot, ServerBuilder};
+use slog::Drain;
+use std::net::SocketAddr;
+use std::str::FromStr;
+use std::sync::Arc;
+use tracing_slog::TracingSlogDrain;
+
+use hostd::api;
+use hostd::context::ControllerContext;
+
+#[derive(Parser, Debug)]
+#[command(version, about, long_about = None)]
+struct Cli {
+    #[arg(
+        long = "telemetry-otlp-endpoint",
+        default_value = "http://localhost:4317",
+        env = "OTEL_EXPORTER_OTLP_ENDPOINT"
+    )]
+    otlp_endpoint: Option<String>,
+
+    #[arg(
+        long = "log-stderr",
+        short = 'v',
+        default_value = "false",
+        env = "LOG_TO_STDERR"
+    )]
+    log_stderr: bool,
+
+    #[arg(
+        long = "listen-address",
+        default_value = "127.0.0.1:9478",
+        env = "LISTEN_ADDRESS"
+    )]
+    listen_address: String,
+
+    #[arg(
+        long = "database-url",
+        default_value = "postgresql://localhost/patagia",
+        env = "DATABASE_URL"
+    )]
+    database_url: Option<String>,
+}
+
+#[tokio::main]
+async fn main() -> Result<()> {
+    let args = Cli::parse();
+
+    let config = ConfigDropshot {
+        bind_address: SocketAddr::from_str(&args.listen_address).unwrap(),
+        ..Default::default()
+    };
+
+    let logger = {
+        let level_drain = slog::LevelFilter(TracingSlogDrain, slog::Level::Debug).fuse();
+        let async_drain = slog_async::Async::new(level_drain).build().fuse();
+        slog::Logger::root(async_drain, slog::o!())
+    };
+
+    let dbus = zbus::Connection::system().await.unwrap();
+    let ctx = ControllerContext::new(dbus);
+
+    let api = api::api()?;
+
+    println!("Listening on http://{}", config.bind_address);
+
+    ServerBuilder::new(api, Arc::new(ctx), logger)
+        .config(config)
+        .start()
+        .map_err(|e| anyhow!("Error starting server: {:?}", e))?
+        .await
+        .map_err(|e| anyhow!(e))
+}
diff --git a/hostd/src/context.rs b/hostd/src/context.rs
new file mode 100644
index 0000000..de20783
--- /dev/null
+++ b/hostd/src/context.rs
@@ -0,0 +1,9 @@
+pub struct ControllerContext {
+    pub dbus: zbus::Connection,
+}
+
+impl ControllerContext {
+    pub fn new(dbus: zbus::Connection) -> ControllerContext {
+        ControllerContext { dbus }
+    }
+}
diff --git a/hostd/src/lib.rs b/hostd/src/lib.rs
new file mode 100644
index 0000000..3b7a5eb
--- /dev/null
+++ b/hostd/src/lib.rs
@@ -0,0 +1,4 @@
+pub mod api;
+pub mod context;
+pub mod machine;
+pub mod sysupdate;
diff --git a/hostd/src/machine.rs b/hostd/src/machine.rs
new file mode 100644
index 0000000..a2926c2
--- /dev/null
+++ b/hostd/src/machine.rs
@@ -0,0 +1,43 @@
+use dropshot::{endpoint, HttpError, HttpResponseOk, RequestContext};
+use schemars::JsonSchema;
+use serde::Serialize;
+use std::sync::Arc;
+use trace_request::trace_request;
+
+use crate::context::ControllerContext;
+
+/// Machine information
+#[derive(Serialize, JsonSchema)]
+#[serde(rename_all = "camelCase")]
+struct MachineInfo {
+    machine_id: String,
+}
+
+/// Fetch machine info
+#[endpoint {
+    method = GET,
+    path = "/machine_info",
+}]
+#[trace_request]
+pub async fn describe(
+    rqctx: RequestContext<Arc<ControllerContext>>,
+) -> Result<HttpResponseOk<MachineInfo>, HttpError> {
+    let hostnamed = zbus_systemd::hostname1::HostnamedProxy::new(&rqctx.context().dbus)
+        .await
+        .unwrap();
+
+    let machine_id = hostnamed
+        .machine_id()
+        .await
+        .map_err(|e| match e {
+            err => HttpError::for_internal_error(format!("Error: {}", err)),
+        })?
+        // convert bytes to hex string
+        .iter()
+        .map(|&b| format!("{:02x}", b))
+        .collect();
+
+    let machine_info = MachineInfo { machine_id };
+
+    Ok(HttpResponseOk(machine_info))
+}
diff --git a/hostd/src/sysupdate.rs b/hostd/src/sysupdate.rs
new file mode 100644
index 0000000..43873cd
--- /dev/null
+++ b/hostd/src/sysupdate.rs
@@ -0,0 +1,52 @@
+use dropshot::{endpoint, HttpError, HttpResponseOk, RequestContext};
+use schemars::JsonSchema;
+use serde::Serialize;
+use std::sync::Arc;
+use trace_request::trace_request;
+
+use crate::context::ControllerContext;
+
+const SYSUPDATE_HOST_PATH: &str = "/org/freedesktop/sysupdate1/target/host";
+
+#[derive(Serialize, JsonSchema)]
+#[serde(rename_all = "camelCase")]
+struct SysUpdate {
+    current_version: String,
+    versions: Vec<String>,
+}
+
+#[endpoint {
+    method = GET,
+    path = "/list_versions",
+}]
+#[trace_request]
+pub async fn list_versions(
+    rqctx: RequestContext<Arc<ControllerContext>>,
+) -> Result<HttpResponseOk<SysUpdate>, HttpError> {
+    let sysupdate_target = zbus_systemd::sysupdate1::TargetProxy::builder(&rqctx.context().dbus)
+        .path(SYSUPDATE_HOST_PATH)
+        .unwrap()
+        .build()
+        .await
+        .map_err(|e| match e {
+            err => HttpError::for_internal_error(format!("Error: {}", err)),
+        })?;
+
+    let versions = sysupdate_target.list(0).await.map_err(|e| match e {
+        err => {
+            println!("Error: {}", err);
+            HttpError::for_internal_error(format!("Error: {}", err))
+        }
+    })?;
+
+    let current_version = sysupdate_target.get_version().await.map_err(|e| match e {
+        err => HttpError::for_internal_error(format!("Error: {}", err)),
+    })?;
+
+    let sysupdate = SysUpdate {
+        versions,
+        current_version,
+    };
+
+    Ok(HttpResponseOk(sysupdate))
+}

From d1e1baf15ad6bba5a95f8d0827781351261379dc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lars=20Sj=C3=B6strom?= <lars@radicore.se>
Date: Wed, 8 Jan 2025 11:09:34 +0100
Subject: [PATCH 2/2] chore: build static binaries with musl

---
 flake.nix | 41 +++++++++++++++++++++++++++++++----------
 1 file changed, 31 insertions(+), 10 deletions(-)

diff --git a/flake.nix b/flake.nix
index ac97f6e..8110be1 100644
--- a/flake.nix
+++ b/flake.nix
@@ -27,6 +27,8 @@
       system:
       let
         rustVersion = "1.83.0";
+        target = "x86_64-unknown-linux-musl";
+        isStatic = true;
 
         overlays = [
           (import rust-overlay)
@@ -43,7 +45,24 @@
         ];
 
         pkgs = import nixpkgs { inherit overlays system; };
-        craneLib = (crane.mkLib pkgs).overrideToolchain pkgs.rust-toolchain;
+
+        basePkgs = import nixpkgs (
+          {
+            localSystem = system;
+            overlays = [
+              (import rust-overlay)
+            ];
+          }
+          // pkgs.lib.optionalAttrs isStatic { crossSystem.config = target; }
+        );
+        crossPkgs = (if isStatic then basePkgs.pkgsStatic else basePkgs);
+
+        craneLib = (crane.mkLib crossPkgs).overrideToolchain (
+          p:
+          p.rust-bin.stable.${rustVersion}.default.override {
+            targets = [ target ];
+          }
+        );
 
         src = pkgs.lib.fileset.toSource {
           root = ./.;
@@ -58,19 +77,21 @@
         commonArgs = {
           inherit src;
 
-          stdenv = pkgs.stdenvAdapters.useMoldLinker pkgs.stdenv;
           strictDeps = true;
           cargoArtifacts = craneLib.buildDepsOnly commonArgs;
 
-          nativeBuildInputs = with pkgs; [
-            clang
-            mold-wrapped
-            pkg-config
-          ];
+          nativeBuildInputs = with crossPkgs.pkgsBuildHost; [ pkg-config ];
+          buildInputs = with crossPkgs.pkgsHostHost; [ openssl ];
 
-          buildInputs = with pkgs; [
-            openssl
-          ];
+          CARGO_BUILD_TARGET = target;
+          CARGO_BUILD_RUSTFLAGS = "-C target-feature=+crt-static";
+          "CARGO_TARGET_${pkgs.lib.toUpper (builtins.replaceStrings [ "-" ] [ "_" ] target)}_LINKER" =
+            "${crossPkgs.stdenv.cc.targetPrefix}cc";
+
+          OPENSSL_STATIC = true;
+          OPENSSL_DIR = "${crossPkgs.openssl.dev}";
+          OPENSSL_LIB_DIR = "${crossPkgs.openssl.out}/lib";
+          OPENSSL_INCLUDE_DIR = "${crossPkgs.openssl.dev}/include/";
         };
 
         buildCrate =