regent_sdk/hosts/
properties.rs1use std::path::PathBuf;
2
3use serde::{Deserialize, Serialize};
4
5use crate::hosts::handlers::HostHandler;
6
7use crate::{Privilege, RegentError};
8
9#[derive(Clone, Serialize, Deserialize, Debug)]
10#[serde(deny_unknown_fields)]
11pub struct HostProperties {
12 os_kind: OsKind,
13 hostname: Option<String>,
14}
15
16impl HostProperties {
17 pub async fn collect_dynamically<Handler: HostHandler>(
18 host_handler: &mut Handler,
19 ) -> Result<HostProperties, RegentError> {
20 if !host_handler.is_connected().await {
21 return Err(RegentError::NotConnectedToHost);
22 }
23
24 let mut os_kind = OsKind::Unknown;
25
26 if let Ok(os_release_file_content) = host_handler
28 .get_file(PathBuf::from("/etc/os-release"))
29 .await
30 {
31 let content = String::from_utf8_lossy(&os_release_file_content);
32 for line in content.lines() {
33 if line.starts_with("NAME=") {
34 let mut equals_iterator = line.split('=');
36 equals_iterator.next().unwrap(); let os_name_part = equals_iterator.next().unwrap();
38
39 let os_name = if os_name_part.starts_with('"') {
40 &os_name_part[1..os_name_part.len() - 1]
41 } else {
42 os_name_part
43 };
44
45 if os_name.contains("FreeBSD") {
46 os_kind = OsKind::FreeBsd(FreeBsdSpecifics);
47 } else {
48 let linux_flavor = if os_name.contains("Arch") {
49 LinuxFlavor::Arch
50 } else if os_name.contains("CentOS") {
51 LinuxFlavor::Fedora
52 } else if os_name.contains("Debian") {
53 LinuxFlavor::Debian
54 } else if os_name.contains("Ubuntu") {
55 LinuxFlavor::Debian
56 } else if os_name.contains("Arch") {
57 LinuxFlavor::Arch
58 } else if os_name.contains("openSUSE") {
59 LinuxFlavor::Suse
60 } else {
61 LinuxFlavor::Debian
63 };
64
65 let init_system = Self::detect_init_system(host_handler).await;
66 os_kind = OsKind::Linux(LinuxSpecifics {
67 linux_flavor,
68 init_system,
69 });
70 }
71
72 break;
73 }
74 }
75 }
76
77 #[cfg(feature = "windows")]
79 if let OsKind::Unknown = os_kind {
80 if let Ok(cmd_result) = host_handler.run_windows_command("systeminfo").await {
81 if cmd_result.stdout.contains("Microsoft Windows") {
82 os_kind = OsKind::Windows(WindowsSpecifics);
83 }
84 }
85 }
86
87 if let OsKind::Unknown = os_kind {
89 if let Ok(cmd_result) = host_handler
90 .run_command("sw_vers -productName", &Privilege::None)
91 .await
92 {
93 if cmd_result.stdout.contains("macOS") {
94 os_kind = OsKind::MacOs(MacOsSpecifics);
95 }
96 }
97 }
98
99 let hostname = Self::collect_hostname(host_handler, os_kind.clone()).await;
100
101 Ok(HostProperties { os_kind, hostname })
102 }
103
104 pub fn os_kind(&self) -> &OsKind {
105 &self.os_kind
106 }
107
108 pub fn hostname(&self) -> &Option<String> {
109 &self.hostname
110 }
111
112 async fn collect_hostname<Handler: HostHandler>(
113 host_handler: &mut Handler,
114 os_kind: OsKind,
115 ) -> Option<String> {
116 match os_kind {
117 OsKind::Unknown => None,
118 #[cfg(feature = "windows")]
119 OsKind::Windows(_) => {
120 let cmd_result = host_handler.run_windows_command("hostname").await.ok()?;
121 if cmd_result.return_code == 0 {
122 Some(cmd_result.stdout.trim().to_string())
123 } else {
124 None
125 }
126 }
127 OsKind::Linux(_) | OsKind::FreeBsd(_) | OsKind::MacOs(_) => {
128 if let Ok(hostname_file_content) =
129 host_handler.get_file(PathBuf::from("/etc/hostname")).await
130 {
131 Some(
132 String::from_utf8_lossy(&hostname_file_content)
133 .trim()
134 .to_string(),
135 )
136 } else {
137 let cmd_result = host_handler
138 .run_command("hostname", &Privilege::None)
139 .await
140 .ok()?;
141 if cmd_result.return_code == 0 {
142 Some(cmd_result.stdout.trim().to_string())
143 } else {
144 None
145 }
146 }
147 }
148 }
149 }
150
151 async fn detect_init_system<Handler: HostHandler>(host_handler: &mut Handler) -> InitSystem {
152 if host_handler
154 .get_file(PathBuf::from("/run/systemd/system"))
155 .await
156 .is_ok()
157 {
158 return InitSystem::Systemd;
159 }
160
161 if let Ok(cmd_result) = host_handler
163 .run_command("ps -p 1 -o comm=", &Privilege::None)
164 .await
165 {
166 if cmd_result.return_code == 0 {
167 let process_name = cmd_result.stdout.trim();
168 if process_name == "systemd" || process_name.contains("systemd") {
169 return InitSystem::Systemd;
170 }
171 }
172 }
173
174 InitSystem::Unknown
175 }
176}
177
178#[derive(Clone, Serialize, Deserialize, Debug)]
179pub enum OsKind {
180 Unknown,
181 #[cfg(feature = "windows")]
182 Windows(WindowsSpecifics),
183 FreeBsd(FreeBsdSpecifics),
184 MacOs(MacOsSpecifics),
185 Linux(LinuxSpecifics),
186}
187
188#[derive(Clone, Serialize, Deserialize, Debug)]
189pub struct LinuxSpecifics {
190 pub linux_flavor: LinuxFlavor,
191 pub init_system: InitSystem,
192}
193
194#[derive(Clone, Serialize, Deserialize, Debug)]
195pub struct MacOsSpecifics;
196
197#[derive(Clone, Serialize, Deserialize, Debug)]
198pub struct FreeBsdSpecifics;
199
200#[derive(Clone, Serialize, Deserialize, Debug)]
201#[cfg(feature = "windows")]
202pub struct WindowsSpecifics;
203
204#[derive(Clone, Serialize, Deserialize, Debug)]
205pub enum LinuxFlavor {
206 Debian,
207 Fedora,
208 Arch,
209 Suse,
210 Gentoo,
211}
212
213#[derive(Clone, Serialize, Deserialize, Debug)]
214pub enum InitSystem {
215 Unknown,
216 Systemd,
217}