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