feat(hostd): varlink interfaced host controller to manage machine configuration and boot mgmt
Some checks are pending
ci/woodpecker/pr/ci Pipeline is pending

This commit is contained in:
Lars Sjöström 2025-01-08 11:09:34 +01:00
parent 8e99ab4555
commit bb277591e2
No known key found for this signature in database
19 changed files with 1679 additions and 56 deletions

719
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -4,8 +4,10 @@ members = [
"agent",
"controller",
"instrumentation",
"hostd",
"trace-request",
"xtask",
"systemd-ipc",
]
default-members = [
"agent",
@ -44,7 +46,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" ] }

1
hostd/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

23
hostd/Cargo.toml Normal file
View file

@ -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"

17
hostd/src/api.rs Normal file
View file

@ -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)
}

View file

@ -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))
}

9
hostd/src/context.rs Normal file
View file

@ -0,0 +1,9 @@
pub struct ControllerContext {
pub dbus: zbus::Connection,
}
impl ControllerContext {
pub fn new(dbus: zbus::Connection) -> ControllerContext {
ControllerContext { dbus }
}
}

4
hostd/src/lib.rs Normal file
View file

@ -0,0 +1,4 @@
pub mod api;
pub mod context;
pub mod machine;
pub mod sysupdate;

43
hostd/src/machine.rs Normal file
View file

@ -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))
}

52
hostd/src/sysupdate.rs Normal file
View file

@ -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))
}

1
systemd-ipc/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

17
systemd-ipc/Cargo.toml Normal file
View file

@ -0,0 +1,17 @@
[package]
name = "systemd-ipc"
version.workspace = true
edition.workspace = true
[dependencies]
serde.workspace = true
serde_derive = "1.0.217"
serde_json = "1.0.135"
varlink = "11.0.1"
[build-dependencies]
varlink_generator = "10.1.0"
walkdir = "2.5.0"
[package.metadata.cargo-machete]
ignored = ["serde"]

12
systemd-ipc/build.rs Normal file
View file

@ -0,0 +1,12 @@
extern crate varlink_generator;
use walkdir::WalkDir;
fn main() {
println!("cargo:rerun-if-changed=src/*.varlink");
for entry in WalkDir::new("src").into_iter().filter_map(|e| e.ok()) {
if entry.file_name().to_str().unwrap().ends_with(".varlink") {
varlink_generator::cargo_build_tosource(&entry.path().display().to_string(), true);
}
}
}

3
systemd-ipc/src/addrs.rs Normal file
View file

@ -0,0 +1,3 @@
#[allow(dead_code)]
pub const SYSTEMD_HOSTNAME: &str = "unix:/run/systemd/io.systemd.Hostname";
pub const SYSTEMD_BOOTCONTROL: &str = "unix:/run/systemd/io.systemd.BootControl";

View file

@ -0,0 +1,70 @@
# Boot Loader control APIs
interface io.systemd.BootControl
# The type of a boot entry
type BootEntryType(
# Boot Loader Specification Type #1 entries (.conf files)
type1,
# Boot Loader Specification Type #2 entries (UKIs)
type2,
# Additional entries reported by boot loader
loader,
# Automatically generated entries
auto
)
# A structure encapsulating a boot entry
type BootEntry(
type: BootEntryType,
# The string identifier of the entry
id: ?string,
path: ?string,
root: ?string,
title: ?string,
showTitle: ?string,
sortKey: ?string,
version: ?string,
machineId: ?string,
architecture: ?string,
options: ?string,
linux: ?string,
efi: ?string,
initrd: ?[]string,
devicetree: ?string,
devicetreeOverlay: ?[]string,
# Indicates whether the boot loader reported this entry on the current boot
isReported: bool,
# Indicates the number of tries left for this boot entry before it is assumed to be not working.
triesLeft: ?int,
# Indicates the number of unsuccessful tries already made for this boot entry.
triesDone: ?int,
# Indicates whether this entry is the default entry.
isDefault: ?bool,
# Indicates whether this entry has been booted.
isSelected: ?bool
)
# Enumerates boot entries. Method call must be called with 'more' flag set. Each response returns one entry. If no entries are defined returns the NoSuchBootEntry error.
# [Requires 'more' flag]
method ListBootEntries() -> (
# A boot menu entry structure
entry: ?BootEntry
)
# Sets the reboot-to-firmware-UI flag of the firmware, if this concept exists. Returns the RebootToFirmwareNotSupported error if not.
method SetRebootToFirmware(
# The new value of the reboot-to-firmware-UI flag
state: bool
) -> ()
# Gets the current state of the reboot-to-firmware-UI flag of the firmware, if this concept exists. Returns the RebootToFirmwareNotSupported error if not.
method GetRebootToFirmware() -> (
# The current state of the reboot-to-firmware-UI flag
state: bool
)
# SetRebootToFirmware() and GetRebootToFirmware() return this if the firmware does not actually support the reboot-to-firmware-UI concept.
error RebootToFirmwareNotSupported()
# No boot entry defined.
error NoSuchBootEntry()

View file

@ -0,0 +1,32 @@
interface io.systemd.Hostname
method Describe() -> (
Hostname: string,
StaticHostname: ?string,
PrettyHostname: ?string,
DefaultHostname: ?string,
HostnameSource: string,
IconName: ?string,
Chassis: ?string,
Deployment: ?string,
Location: ?string,
KernelName: string,
KernelRelease: string,
KernelVersion: string,
OperatingSystemPrettyName: ?string,
OperatingSystemCPEName: ?string,
OperatingSystemHomeURL: ?string,
OperatingSystemSupportEnd: ?int,
OperatingSystemReleaseData: ?[]string,
MachineInformationData: ?[]string,
HardwareVendor: ?string,
HardwareModel: ?string,
HardwareSerial: ?string,
FirmwareVersion: ?string,
FirmwareVendor: ?string,
FirmwareDate: ?int,
MachineID: string,
BootID: string,
ProductUUID: ?string,
VSockCID: ?int
)

View file

@ -0,0 +1,356 @@
#![doc = "This file was automatically generated by the varlink rust generator"]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
use serde_derive::{Deserialize, Serialize};
use std::io::BufRead;
use std::sync::{Arc, RwLock};
use varlink::{self, CallTrait};
#[allow(dead_code)]
#[derive(Clone, PartialEq, Debug)]
#[allow(clippy::enum_variant_names)]
pub enum ErrorKind {
Varlink_Error,
VarlinkReply_Error,
NoSuchBootEntry(Option<NoSuchBootEntry_Args>),
RebootToFirmwareNotSupported(Option<RebootToFirmwareNotSupported_Args>),
}
impl ::std::fmt::Display for ErrorKind {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match self {
ErrorKind::Varlink_Error => write!(f, "Varlink Error"),
ErrorKind::VarlinkReply_Error => write!(f, "Varlink error reply"),
ErrorKind::NoSuchBootEntry(v) => {
write!(f, "io.systemd.BootControl.NoSuchBootEntry: {:#?}", v)
}
ErrorKind::RebootToFirmwareNotSupported(v) => write!(
f,
"io.systemd.BootControl.RebootToFirmwareNotSupported: {:#?}",
v
),
}
}
}
pub struct Error(
pub ErrorKind,
pub Option<Box<dyn std::error::Error + 'static + Send + Sync>>,
pub Option<&'static str>,
);
impl Error {
#[allow(dead_code)]
pub fn kind(&self) -> &ErrorKind {
&self.0
}
}
impl From<ErrorKind> for Error {
fn from(e: ErrorKind) -> Self {
Error(e, None, None)
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.1
.as_ref()
.map(|e| e.as_ref() as &(dyn std::error::Error + 'static))
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0, f)
}
}
impl std::fmt::Debug for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
use std::error::Error as StdError;
if let Some(ref o) = self.2 {
std::fmt::Display::fmt(o, f)?;
}
std::fmt::Debug::fmt(&self.0, f)?;
if let Some(e) = self.source() {
std::fmt::Display::fmt("\nCaused by:\n", f)?;
std::fmt::Debug::fmt(&e, f)?;
}
Ok(())
}
}
#[allow(dead_code)]
pub type Result<T> = std::result::Result<T, Error>;
impl From<varlink::Error> for Error {
fn from(e: varlink::Error) -> Self {
match e.kind() {
varlink::ErrorKind::VarlinkErrorReply(r) => Error(
ErrorKind::from(r),
Some(Box::from(e)),
Some(concat!(file!(), ":", line!(), ": ")),
),
_ => Error(
ErrorKind::Varlink_Error,
Some(Box::from(e)),
Some(concat!(file!(), ":", line!(), ": ")),
),
}
}
}
#[allow(dead_code)]
impl Error {
pub fn source_varlink_kind(&self) -> Option<&varlink::ErrorKind> {
use std::error::Error as StdError;
let mut s: &dyn StdError = self;
while let Some(c) = s.source() {
let k = self
.source()
.and_then(|e| e.downcast_ref::<varlink::Error>())
.map(|e| e.kind());
if k.is_some() {
return k;
}
s = c;
}
None
}
}
impl From<&varlink::Reply> for ErrorKind {
#[allow(unused_variables)]
fn from(e: &varlink::Reply) -> Self {
match e {
varlink::Reply {
error: Some(ref t), ..
} if t == "io.systemd.BootControl.NoSuchBootEntry" => match e {
varlink::Reply {
parameters: Some(p),
..
} => match serde_json::from_value(p.clone()) {
Ok(v) => ErrorKind::NoSuchBootEntry(v),
Err(_) => ErrorKind::NoSuchBootEntry(None),
},
_ => ErrorKind::NoSuchBootEntry(None),
},
varlink::Reply {
error: Some(ref t), ..
} if t == "io.systemd.BootControl.RebootToFirmwareNotSupported" => match e {
varlink::Reply {
parameters: Some(p),
..
} => match serde_json::from_value(p.clone()) {
Ok(v) => ErrorKind::RebootToFirmwareNotSupported(v),
Err(_) => ErrorKind::RebootToFirmwareNotSupported(None),
},
_ => ErrorKind::RebootToFirmwareNotSupported(None),
},
_ => ErrorKind::VarlinkReply_Error,
}
}
}
pub trait VarlinkCallError: varlink::CallTrait {
fn reply_no_such_boot_entry(&mut self) -> varlink::Result<()> {
self.reply_struct(varlink::Reply::error(
"io.systemd.BootControl.NoSuchBootEntry",
None,
))
}
fn reply_reboot_to_firmware_not_supported(&mut self) -> varlink::Result<()> {
self.reply_struct(varlink::Reply::error(
"io.systemd.BootControl.RebootToFirmwareNotSupported",
None,
))
}
}
impl<'a> VarlinkCallError for varlink::Call<'a> {}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct r#BootEntry {
pub r#type: BootEntryType,
pub r#id: Option<String>,
pub r#path: Option<String>,
pub r#root: Option<String>,
pub r#title: Option<String>,
pub r#showTitle: Option<String>,
pub r#sortKey: Option<String>,
pub r#version: Option<String>,
pub r#machineId: Option<String>,
pub r#architecture: Option<String>,
pub r#options: Option<String>,
pub r#linux: Option<String>,
pub r#efi: Option<String>,
pub r#initrd: Option<Vec<String>>,
pub r#devicetree: Option<String>,
pub r#devicetreeOverlay: Option<Vec<String>>,
pub r#isReported: bool,
pub r#triesLeft: Option<i64>,
pub r#triesDone: Option<i64>,
pub r#isDefault: Option<bool>,
pub r#isSelected: Option<bool>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub enum r#BootEntryType {
r#type1,
r#type2,
r#loader,
r#auto,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct NoSuchBootEntry_Args {}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct RebootToFirmwareNotSupported_Args {}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct GetRebootToFirmware_Reply {
pub r#state: bool,
}
impl varlink::VarlinkReply for GetRebootToFirmware_Reply {}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct GetRebootToFirmware_Args {}
pub trait Call_GetRebootToFirmware: VarlinkCallError {
fn reply(&mut self, r#state: bool) -> varlink::Result<()> {
self.reply_struct(GetRebootToFirmware_Reply { r#state }.into())
}
}
impl<'a> Call_GetRebootToFirmware for varlink::Call<'a> {}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct ListBootEntries_Reply {
#[serde(skip_serializing_if = "Option::is_none")]
pub r#entry: Option<BootEntry>,
}
impl varlink::VarlinkReply for ListBootEntries_Reply {}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct ListBootEntries_Args {}
pub trait Call_ListBootEntries: VarlinkCallError {
fn reply(&mut self, r#entry: Option<BootEntry>) -> varlink::Result<()> {
self.reply_struct(ListBootEntries_Reply { r#entry }.into())
}
}
impl<'a> Call_ListBootEntries for varlink::Call<'a> {}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct SetRebootToFirmware_Reply {}
impl varlink::VarlinkReply for SetRebootToFirmware_Reply {}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct SetRebootToFirmware_Args {
pub r#state: bool,
}
pub trait Call_SetRebootToFirmware: VarlinkCallError {
fn reply(&mut self) -> varlink::Result<()> {
self.reply_struct(varlink::Reply::parameters(None))
}
}
impl<'a> Call_SetRebootToFirmware for varlink::Call<'a> {}
pub trait VarlinkInterface {
fn get_reboot_to_firmware(
&self,
call: &mut dyn Call_GetRebootToFirmware,
) -> varlink::Result<()>;
fn list_boot_entries(&self, call: &mut dyn Call_ListBootEntries) -> varlink::Result<()>;
fn set_reboot_to_firmware(
&self,
call: &mut dyn Call_SetRebootToFirmware,
r#state: bool,
) -> varlink::Result<()>;
fn call_upgraded(
&self,
_call: &mut varlink::Call,
_bufreader: &mut dyn BufRead,
) -> varlink::Result<Vec<u8>> {
Ok(Vec::new())
}
}
pub trait VarlinkClientInterface {
fn get_reboot_to_firmware(
&mut self,
) -> varlink::MethodCall<GetRebootToFirmware_Args, GetRebootToFirmware_Reply, Error>;
fn list_boot_entries(
&mut self,
) -> varlink::MethodCall<ListBootEntries_Args, ListBootEntries_Reply, Error>;
fn set_reboot_to_firmware(
&mut self,
r#state: bool,
) -> varlink::MethodCall<SetRebootToFirmware_Args, SetRebootToFirmware_Reply, Error>;
}
#[allow(dead_code)]
pub struct VarlinkClient {
connection: Arc<RwLock<varlink::Connection>>,
}
impl VarlinkClient {
#[allow(dead_code)]
pub fn new(connection: Arc<RwLock<varlink::Connection>>) -> Self {
VarlinkClient { connection }
}
}
impl VarlinkClientInterface for VarlinkClient {
fn get_reboot_to_firmware(
&mut self,
) -> varlink::MethodCall<GetRebootToFirmware_Args, GetRebootToFirmware_Reply, Error> {
varlink::MethodCall::<GetRebootToFirmware_Args, GetRebootToFirmware_Reply, Error>::new(
self.connection.clone(),
"io.systemd.BootControl.GetRebootToFirmware",
GetRebootToFirmware_Args {},
)
}
fn list_boot_entries(
&mut self,
) -> varlink::MethodCall<ListBootEntries_Args, ListBootEntries_Reply, Error> {
varlink::MethodCall::<ListBootEntries_Args, ListBootEntries_Reply, Error>::new(
self.connection.clone(),
"io.systemd.BootControl.ListBootEntries",
ListBootEntries_Args {},
)
}
fn set_reboot_to_firmware(
&mut self,
r#state: bool,
) -> varlink::MethodCall<SetRebootToFirmware_Args, SetRebootToFirmware_Reply, Error> {
varlink::MethodCall::<SetRebootToFirmware_Args, SetRebootToFirmware_Reply, Error>::new(
self.connection.clone(),
"io.systemd.BootControl.SetRebootToFirmware",
SetRebootToFirmware_Args { r#state },
)
}
}
#[allow(dead_code)]
pub struct VarlinkInterfaceProxy {
inner: Box<dyn VarlinkInterface + Send + Sync>,
}
#[allow(dead_code)]
pub fn new(inner: Box<dyn VarlinkInterface + Send + Sync>) -> VarlinkInterfaceProxy {
VarlinkInterfaceProxy { inner }
}
impl varlink::Interface for VarlinkInterfaceProxy {
fn get_description(&self) -> &'static str {
"# Boot Loader control APIs\ninterface io.systemd.BootControl\n\n# The type of a boot entry\ntype BootEntryType(\n\t# Boot Loader Specification Type #1 entries (.conf files)\n\ttype1,\n\t# Boot Loader Specification Type #2 entries (UKIs)\n\ttype2,\n\t# Additional entries reported by boot loader\n\tloader,\n\t# Automatically generated entries\n\tauto\n)\n\n# A structure encapsulating a boot entry\ntype BootEntry(\n\ttype: BootEntryType,\n\t# The string identifier of the entry\n\tid: ?string,\n\tpath: ?string,\n\troot: ?string,\n\ttitle: ?string,\n\tshowTitle: ?string,\n\tsortKey: ?string,\n\tversion: ?string,\n\tmachineId: ?string,\n\tarchitecture: ?string,\n\toptions: ?string,\n\tlinux: ?string,\n\tefi: ?string,\n\tinitrd: ?[]string,\n\tdevicetree: ?string,\n\tdevicetreeOverlay: ?[]string,\n\t# Indicates whether the boot loader reported this entry on the current boot\n\tisReported: bool,\n\t# Indicates the number of tries left for this boot entry before it is assumed to be not working.\n\ttriesLeft: ?int,\n\t# Indicates the number of unsuccessful tries already made for this boot entry.\n\ttriesDone: ?int,\n\t# Indicates whether this entry is the default entry.\n\tisDefault: ?bool,\n\t# Indicates whether this entry has been booted.\n\tisSelected: ?bool\n)\n\n# Enumerates boot entries. Method call must be called with 'more' flag set. Each response returns one entry. If no entries are defined returns the NoSuchBootEntry error.\n# [Requires 'more' flag]\nmethod ListBootEntries() -> (\n\t# A boot menu entry structure\n\tentry: ?BootEntry\n)\n\n# Sets the reboot-to-firmware-UI flag of the firmware, if this concept exists. Returns the RebootToFirmwareNotSupported error if not.\nmethod SetRebootToFirmware(\n\t# The new value of the reboot-to-firmware-UI flag\n\tstate: bool\n) -> ()\n\n# Gets the current state of the reboot-to-firmware-UI flag of the firmware, if this concept exists. Returns the RebootToFirmwareNotSupported error if not.\nmethod GetRebootToFirmware() -> (\n\t# The current state of the reboot-to-firmware-UI flag\n\tstate: bool\n)\n\n# SetRebootToFirmware() and GetRebootToFirmware() return this if the firmware does not actually support the reboot-to-firmware-UI concept.\nerror RebootToFirmwareNotSupported()\n\n# No boot entry defined.\nerror NoSuchBootEntry()\n"
}
fn get_name(&self) -> &'static str {
"io.systemd.BootControl"
}
fn call_upgraded(
&self,
call: &mut varlink::Call,
bufreader: &mut dyn BufRead,
) -> varlink::Result<Vec<u8>> {
self.inner.call_upgraded(call, bufreader)
}
fn call(&self, call: &mut varlink::Call) -> varlink::Result<()> {
let req = call.request.unwrap();
match req.method.as_ref() {
"io.systemd.BootControl.GetRebootToFirmware" => self
.inner
.get_reboot_to_firmware(call as &mut dyn Call_GetRebootToFirmware),
"io.systemd.BootControl.ListBootEntries" => self
.inner
.list_boot_entries(call as &mut dyn Call_ListBootEntries),
"io.systemd.BootControl.SetRebootToFirmware" => {
if let Some(args) = req.parameters.clone() {
let args: SetRebootToFirmware_Args = match serde_json::from_value(args) {
Ok(v) => v,
Err(e) => {
let es = format!("{}", e);
let _ = call.reply_invalid_parameter(es.clone());
return Err(varlink::context!(varlink::ErrorKind::SerdeJsonDe(es)));
}
};
self.inner.set_reboot_to_firmware(
call as &mut dyn Call_SetRebootToFirmware,
args.r#state,
)
} else {
call.reply_invalid_parameter("parameters".into())
}
}
m => call.reply_method_not_found(String::from(m)),
}
}
}

View file

@ -0,0 +1,295 @@
#![doc = "This file was automatically generated by the varlink rust generator"]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
use serde_derive::{Deserialize, Serialize};
use std::io::BufRead;
use std::sync::{Arc, RwLock};
use varlink::{self, CallTrait};
#[allow(dead_code)]
#[derive(Clone, PartialEq, Debug)]
#[allow(clippy::enum_variant_names)]
pub enum ErrorKind {
Varlink_Error,
VarlinkReply_Error,
}
impl ::std::fmt::Display for ErrorKind {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match self {
ErrorKind::Varlink_Error => write!(f, "Varlink Error"),
ErrorKind::VarlinkReply_Error => write!(f, "Varlink error reply"),
}
}
}
pub struct Error(
pub ErrorKind,
pub Option<Box<dyn std::error::Error + 'static + Send + Sync>>,
pub Option<&'static str>,
);
impl Error {
#[allow(dead_code)]
pub fn kind(&self) -> &ErrorKind {
&self.0
}
}
impl From<ErrorKind> for Error {
fn from(e: ErrorKind) -> Self {
Error(e, None, None)
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.1
.as_ref()
.map(|e| e.as_ref() as &(dyn std::error::Error + 'static))
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0, f)
}
}
impl std::fmt::Debug for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
use std::error::Error as StdError;
if let Some(ref o) = self.2 {
std::fmt::Display::fmt(o, f)?;
}
std::fmt::Debug::fmt(&self.0, f)?;
if let Some(e) = self.source() {
std::fmt::Display::fmt("\nCaused by:\n", f)?;
std::fmt::Debug::fmt(&e, f)?;
}
Ok(())
}
}
#[allow(dead_code)]
pub type Result<T> = std::result::Result<T, Error>;
impl From<varlink::Error> for Error {
fn from(e: varlink::Error) -> Self {
match e.kind() {
varlink::ErrorKind::VarlinkErrorReply(r) => Error(
ErrorKind::from(r),
Some(Box::from(e)),
Some(concat!(file!(), ":", line!(), ": ")),
),
_ => Error(
ErrorKind::Varlink_Error,
Some(Box::from(e)),
Some(concat!(file!(), ":", line!(), ": ")),
),
}
}
}
#[allow(dead_code)]
impl Error {
pub fn source_varlink_kind(&self) -> Option<&varlink::ErrorKind> {
use std::error::Error as StdError;
let mut s: &dyn StdError = self;
while let Some(c) = s.source() {
let k = self
.source()
.and_then(|e| e.downcast_ref::<varlink::Error>())
.map(|e| e.kind());
if k.is_some() {
return k;
}
s = c;
}
None
}
}
impl From<&varlink::Reply> for ErrorKind {
#[allow(unused_variables)]
fn from(e: &varlink::Reply) -> Self {
match e {
_ => ErrorKind::VarlinkReply_Error,
}
}
}
pub trait VarlinkCallError: varlink::CallTrait {}
impl<'a> VarlinkCallError for varlink::Call<'a> {}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct Describe_Reply {
pub r#Hostname: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub r#StaticHostname: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub r#PrettyHostname: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub r#DefaultHostname: Option<String>,
pub r#HostnameSource: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub r#IconName: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub r#Chassis: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub r#Deployment: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub r#Location: Option<String>,
pub r#KernelName: String,
pub r#KernelRelease: String,
pub r#KernelVersion: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub r#OperatingSystemPrettyName: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub r#OperatingSystemCPEName: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub r#OperatingSystemHomeURL: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub r#OperatingSystemSupportEnd: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub r#OperatingSystemReleaseData: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub r#MachineInformationData: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub r#HardwareVendor: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub r#HardwareModel: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub r#HardwareSerial: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub r#FirmwareVersion: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub r#FirmwareVendor: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub r#FirmwareDate: Option<i64>,
pub r#MachineID: String,
pub r#BootID: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub r#ProductUUID: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub r#VSockCID: Option<i64>,
}
impl varlink::VarlinkReply for Describe_Reply {}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct Describe_Args {}
pub trait Call_Describe: VarlinkCallError {
fn reply(
&mut self,
r#Hostname: String,
r#StaticHostname: Option<String>,
r#PrettyHostname: Option<String>,
r#DefaultHostname: Option<String>,
r#HostnameSource: String,
r#IconName: Option<String>,
r#Chassis: Option<String>,
r#Deployment: Option<String>,
r#Location: Option<String>,
r#KernelName: String,
r#KernelRelease: String,
r#KernelVersion: String,
r#OperatingSystemPrettyName: Option<String>,
r#OperatingSystemCPEName: Option<String>,
r#OperatingSystemHomeURL: Option<String>,
r#OperatingSystemSupportEnd: Option<i64>,
r#OperatingSystemReleaseData: Option<Vec<String>>,
r#MachineInformationData: Option<Vec<String>>,
r#HardwareVendor: Option<String>,
r#HardwareModel: Option<String>,
r#HardwareSerial: Option<String>,
r#FirmwareVersion: Option<String>,
r#FirmwareVendor: Option<String>,
r#FirmwareDate: Option<i64>,
r#MachineID: String,
r#BootID: String,
r#ProductUUID: Option<String>,
r#VSockCID: Option<i64>,
) -> varlink::Result<()> {
self.reply_struct(
Describe_Reply {
r#Hostname,
r#StaticHostname,
r#PrettyHostname,
r#DefaultHostname,
r#HostnameSource,
r#IconName,
r#Chassis,
r#Deployment,
r#Location,
r#KernelName,
r#KernelRelease,
r#KernelVersion,
r#OperatingSystemPrettyName,
r#OperatingSystemCPEName,
r#OperatingSystemHomeURL,
r#OperatingSystemSupportEnd,
r#OperatingSystemReleaseData,
r#MachineInformationData,
r#HardwareVendor,
r#HardwareModel,
r#HardwareSerial,
r#FirmwareVersion,
r#FirmwareVendor,
r#FirmwareDate,
r#MachineID,
r#BootID,
r#ProductUUID,
r#VSockCID,
}
.into(),
)
}
}
impl<'a> Call_Describe for varlink::Call<'a> {}
pub trait VarlinkInterface {
fn describe(&self, call: &mut dyn Call_Describe) -> varlink::Result<()>;
fn call_upgraded(
&self,
_call: &mut varlink::Call,
_bufreader: &mut dyn BufRead,
) -> varlink::Result<Vec<u8>> {
Ok(Vec::new())
}
}
pub trait VarlinkClientInterface {
fn describe(&mut self) -> varlink::MethodCall<Describe_Args, Describe_Reply, Error>;
}
#[allow(dead_code)]
pub struct VarlinkClient {
connection: Arc<RwLock<varlink::Connection>>,
}
impl VarlinkClient {
#[allow(dead_code)]
pub fn new(connection: Arc<RwLock<varlink::Connection>>) -> Self {
VarlinkClient { connection }
}
}
impl VarlinkClientInterface for VarlinkClient {
fn describe(&mut self) -> varlink::MethodCall<Describe_Args, Describe_Reply, Error> {
varlink::MethodCall::<Describe_Args, Describe_Reply, Error>::new(
self.connection.clone(),
"io.systemd.Hostname.Describe",
Describe_Args {},
)
}
}
#[allow(dead_code)]
pub struct VarlinkInterfaceProxy {
inner: Box<dyn VarlinkInterface + Send + Sync>,
}
#[allow(dead_code)]
pub fn new(inner: Box<dyn VarlinkInterface + Send + Sync>) -> VarlinkInterfaceProxy {
VarlinkInterfaceProxy { inner }
}
impl varlink::Interface for VarlinkInterfaceProxy {
fn get_description(&self) -> &'static str {
"interface io.systemd.Hostname\n\nmethod Describe() -> (\n\tHostname: string,\n\tStaticHostname: ?string,\n\tPrettyHostname: ?string,\n\tDefaultHostname: ?string,\n\tHostnameSource: string,\n\tIconName: ?string,\n\tChassis: ?string,\n\tDeployment: ?string,\n\tLocation: ?string,\n\tKernelName: string,\n\tKernelRelease: string,\n\tKernelVersion: string,\n\tOperatingSystemPrettyName: ?string,\n\tOperatingSystemCPEName: ?string,\n\tOperatingSystemHomeURL: ?string,\n\tOperatingSystemSupportEnd: ?int,\n\tOperatingSystemReleaseData: ?[]string,\n\tMachineInformationData: ?[]string,\n\tHardwareVendor: ?string,\n\tHardwareModel: ?string,\n\tHardwareSerial: ?string,\n\tFirmwareVersion: ?string,\n\tFirmwareVendor: ?string,\n\tFirmwareDate: ?int,\n\tMachineID: string,\n\tBootID: string,\n\tProductUUID: ?string,\n\tVSockCID: ?int\n)\n"
}
fn get_name(&self) -> &'static str {
"io.systemd.Hostname"
}
fn call_upgraded(
&self,
call: &mut varlink::Call,
bufreader: &mut dyn BufRead,
) -> varlink::Result<Vec<u8>> {
self.inner.call_upgraded(call, bufreader)
}
fn call(&self, call: &mut varlink::Call) -> varlink::Result<()> {
let req = call.request.unwrap();
match req.method.as_ref() {
"io.systemd.Hostname.Describe" => self.inner.describe(call as &mut dyn Call_Describe),
m => call.reply_method_not_found(String::from(m)),
}
}
}

2
systemd-ipc/src/lib.rs Normal file
View file

@ -0,0 +1,2 @@
pub mod addrs;
pub mod io_systemd_hostname;