1use crate::{DBus, DBusResult, Uuid};
2use dbus_message_parser::{
3 message::{Message, MessageHeader, MessageType},
4 value::Value,
5};
6use futures::{
7 channel::mpsc::{channel, Receiver},
8 StreamExt,
9};
10use hex::{decode_to_slice, encode, FromHexError};
11use std::{
12 convert::TryInto,
13 io::Error as IoError,
14 str::{from_utf8, Utf8Error},
15 vec::IntoIter,
16};
17use thiserror::Error;
18use tokio::{fs::File, io::AsyncReadExt, spawn};
19
20#[derive(Debug, Error)]
21enum MachineIdError {
22 #[error("IO Error: {0}")]
23 IoError(#[from] IoError),
24 #[error("Machine ID file is not UTF-8: {0}")]
25 Utf8Error(#[from] Utf8Error),
26 #[error("Machine ID is too large")]
27 FileTooLarge,
28 #[error("Machine ID is too small")]
29 FileTooSmall,
30 #[error("Could not decode hex string")]
31 FromHexError(#[from] FromHexError),
32}
33
34async fn read_machine_id_from_file(path: &str) -> Result<Uuid, MachineIdError> {
35 let mut file = File::open(path).await?;
36 let mut uuid_str: [u8; 32] = [0; 32];
37 let mut uuid: Uuid = [0; 16];
38 let mut new_line: [u8; 2] = [0; 2];
39
40 let read = file.read_exact(&mut uuid_str).await?;
41 if read == uuid_str.len() {
42 let read = file.read(&mut new_line).await?;
44 let uuid_str = from_utf8(&uuid_str[..])?;
45 if (read == 0 || read == 1) && (new_line[0] == 0 || new_line[0] == b'\n') {
46 decode_to_slice(uuid_str, &mut uuid[..])?;
47 Ok(uuid)
48 } else {
49 Err(MachineIdError::FileTooLarge)
50 }
51 } else {
52 Err(MachineIdError::FileTooSmall)
53 }
54}
55
56async fn get_machine_id_from_file() -> Result<Uuid, ()> {
57 match read_machine_id_from_file("/var/lib/dbus/machine-id").await {
58 Ok(uuid) => Ok(uuid),
59 Err(e) => {
60 error!(
61 "Could not read Machine ID from /var/lib/dbus/machine-id: {}",
62 e
63 );
64 match read_machine_id_from_file("/etc/machine-id").await {
66 Ok(uuid) => Ok(uuid),
67 Err(e) => {
68 error!("Could not read Machine ID from /etc/machine-id: {}", e);
69 Err(())
70 }
71 }
72 }
73 }
74}
75
76async fn get_machine_id(header: &MessageHeader) -> Message {
77 match header.method_return() {
78 Ok(mut msg) => match get_machine_id_from_file().await {
79 Ok(uuid) => {
80 let uuid = encode(&uuid);
81 msg.add_value(Value::String(uuid));
82 msg
83 }
84 Err(_) => header.error(
85 "org.freedesktop.DBus.Peer.MachineIdError"
86 .try_into()
87 .unwrap(),
88 "Could not retrieve Machine ID.".to_string(),
89 ),
90 },
91 Err(msg) => msg,
92 }
93}
94
95pub async fn handle_peer(
99 dbus: &DBus,
100 header: MessageHeader,
101 mut body_iter: IntoIter<Value>,
102) -> DBusResult<()> {
103 let member = if let Some(member) = header.get_member() {
104 member
105 } else {
106 return Ok(());
107 };
108
109 let response = match member.as_ref() {
110 "GetMachineId" => {
111 if body_iter.next().is_none() {
112 get_machine_id(&header).await
113 } else {
114 header.invalid_args("Too many arguments".to_string())
115 }
116 }
117 "Ping" => {
118 if body_iter.next().is_none() {
119 header.method_return().unwrap()
122 } else {
123 header.invalid_args("Too many arguments".to_string())
124 }
125 }
126 _ => {
127 if let Some(msg) = header.unknown_member() {
128 msg
129 } else {
130 return Ok(());
131 }
132 }
133 };
134
135 dbus.send(response)
136}
137
138async fn peer(dbus: DBus, mut receiver: Receiver<Message>) {
139 while let Some(request) = receiver.next().await {
140 if MessageType::MethodCall != request.get_type() {
141 continue;
142 }
143
144 if let Ok((header, body)) = request.split() {
145 if let Err(e) = handle_peer(&dbus, header, body.into_iter()).await {
146 error!(
147 "Could not handle method for org.freedesktop.DBus.Peer: {}",
148 e
149 );
150 }
151 }
152 }
153}
154
155pub(super) fn add_peer(dbus: DBus) -> DBusResult<()> {
156 let (sender, receiver) = channel(1024);
157 let interface = "org.freedesktop.DBus.Peer".try_into().unwrap();
158 if let Err(e) = dbus.add_method_call_interface(interface, sender) {
160 return Err(e);
161 }
162
163 spawn(peer(dbus, receiver));
164 Ok(())
165}