1pub mod app;
2
3pub mod connection;
4
5mod error;
6pub use error::IpmiError;
7
8pub mod storage;
9pub use storage::sdr::record::WithSensorRecordCommon;
10
11pub mod sensor_event;
12
13#[macro_use]
14mod fmt;
15#[cfg(test)]
16mod tests;
17
18pub use fmt::{LogOutput, Loggable, Logger};
19
20use connection::{
21 IpmiCommand, LogicalUnit, NetFn, ParseResponseError, Request, RequestTargetAddress,
22};
23use storage::sdr::{self, record::Record as SdrRecord};
24
25pub struct Ipmi<CON> {
26 inner: CON,
27}
28
29impl<CON> Ipmi<CON> {
30 pub fn release(self) -> CON {
31 self.inner
32 }
33}
34
35impl<CON> From<CON> for Ipmi<CON>
36where
37 CON: connection::IpmiConnection,
38{
39 fn from(value: CON) -> Self {
40 Self::new(value)
41 }
42}
43
44pub type IpmiCommandError<T, E> = IpmiError<T, ParseResponseError<E>>;
45
46impl<CON> Ipmi<CON>
47where
48 CON: connection::IpmiConnection,
49{
50 pub fn inner_mut(&mut self) -> &mut CON {
51 &mut self.inner
52 }
53
54 pub fn new(inner: CON) -> Self {
55 Self { inner }
56 }
57
58 pub fn sdrs(&mut self) -> SdrIter<CON> {
59 SdrIter {
60 ipmi: self,
61 next_id: Some(sdr::RecordId::FIRST),
62 }
63 }
64
65 pub fn send_recv<CMD>(
66 &mut self,
67 request: CMD,
68 ) -> Result<CMD::Output, IpmiCommandError<CON::Error, CMD::Error>>
69 where
70 CMD: IpmiCommand,
71 {
72 let target_address = match request.target() {
73 Some((a, c)) => RequestTargetAddress::BmcOrIpmb(a, c, LogicalUnit::Zero),
74 None => RequestTargetAddress::Bmc(LogicalUnit::Zero),
75 };
76
77 let message = request.into();
78 let (message_netfn, message_cmd) = (message.netfn(), message.cmd());
79 let mut request = Request::new(message, target_address);
80
81 let response = self.inner.send_recv(&mut request)?;
82
83 if response.netfn() != message_netfn || response.cmd() != message_cmd {
84 return Err(IpmiError::UnexpectedResponse {
85 netfn_sent: message_netfn,
86 netfn_recvd: response.netfn(),
87 cmd_sent: message_cmd,
88 cmd_recvd: response.cmd(),
89 });
90 }
91
92 CMD::parse_response(response.cc().into(), response.data()).map_err(|error| {
93 IpmiError::ParsingFailed {
94 error,
95 netfn: response.netfn(),
96 completion_code: response.cc(),
97 cmd: response.cmd(),
98 data: response.data().to_vec(),
99 }
100 })
101 }
102}
103
104pub struct SdrIter<'ipmi, CON> {
105 ipmi: &'ipmi mut Ipmi<CON>,
106 next_id: Option<sdr::RecordId>,
107}
108
109impl<T> Iterator for SdrIter<'_, T>
110where
111 T: connection::IpmiConnection,
112{
113 type Item = SdrRecord;
114
115 fn next(&mut self) -> Option<Self::Item> {
116 while let Some(current_id) = self.next_id.take() {
117 if current_id.is_last() {
118 return None;
119 }
120
121 let next_record = self
122 .ipmi
123 .send_recv(sdr::GetDeviceSdr::new(None, current_id));
124
125 match next_record {
126 Ok(record) => {
127 let next_record_id = record.next_entry;
128
129 if next_record_id == current_id {
130 log::error!("Got duplicate SDR record IDs! Stopping iteration.");
131 return None;
132 }
133
134 self.next_id = Some(next_record_id);
135 return Some(record.record);
136 }
137 Err(IpmiError::ParsingFailed {
138 error: ParseResponseError::Parse((e, next_record_id)),
139 ..
140 }) => {
141 log::warn!(
142 "Recoverable error while parsing SDR record 0x{:04X}: {e:?}. Skipping to next.",
143 current_id.value()
144 );
145 self.next_id = Some(next_record_id);
146 continue; }
148 Err(e) => {
149 log::error!(
150 "Unrecoverable error while parsing SDR record 0x{:04X}: {e:?}",
151 current_id.value()
152 );
153 return None;
154 }
155 }
156 }
157 None
158 }
159}