leenfetch_core/modules/linux/
title.rs1use std::env;
2use std::ffi::CString;
3use std::fs;
4
5pub fn get_titles(fqdn: bool) -> (String, String) {
12 let user = get_user();
13 let host = get_hostname(fqdn);
14
15 (user, host)
16}
17
18fn get_user() -> String {
19 if let Some(u) = env::var_os("USER") {
21 let s = u.to_string_lossy();
22 if !s.is_empty() {
23 return s.into();
24 }
25 }
26
27 let uid = unsafe { libc::getuid() };
29 if uid > 0 {
30 if let Some(pw) = get_pwuid(uid) {
32 return pw;
33 }
34 }
35
36 if let Ok(home) = env::var("HOME") {
38 if let Some(name) = home.rsplit('/').find(|s| !s.is_empty()) {
39 return name.to_string();
40 }
41 }
42
43 "unknown".into()
45}
46
47fn get_pwuid(uid: libc::uid_t) -> Option<String> {
48 let mut passwd = MaybeUninit::uninit();
50 let mut buf = vec![0u8; 1024];
51
52 let result = unsafe {
53 let mut pwd_ptr: *mut libc::passwd = std::ptr::null_mut();
54 libc::getpwuid_r(
55 uid,
56 passwd.as_mut_ptr(),
57 buf.as_mut_ptr() as *mut libc::c_char,
58 buf.len(),
59 &mut pwd_ptr,
60 )
61 };
62
63 if result == 0 && !passwd.as_ptr().is_null() {
64 let pwd = unsafe { passwd.assume_init() };
65 if !pwd.pw_name.is_null() {
66 let name = unsafe { CString::from_raw(pwd.pw_name) };
67 return Some(name.to_string_lossy().into_owned());
68 }
69 }
70 None
71}
72
73fn get_hostname(fqdn: bool) -> String {
74 let mut buf = [0u8; 256];
76 let len = unsafe { libc::gethostname(buf.as_mut_ptr() as *mut libc::c_char, buf.len()) };
77 if len == 0 {
78 let s = String::from_utf8_lossy(&buf)
79 .trim_end_matches('\0')
80 .to_string();
81 if !s.is_empty() && (s != "localhost" || !fqdn) {
82 if fqdn {
83 if let Ok(fqdn_name) = fs::read_to_string("/etc/hostname") {
85 let trimmed = fqdn_name.trim().to_string();
86 if !trimmed.is_empty() && trimmed.contains('.') {
87 return trimmed;
88 }
89 }
90 if let Ok(domain) = fs::read_to_string("/etc/resolv.conf") {
92 for line in domain.lines() {
93 if line.starts_with("domain ") {
94 let domain = line[7..].trim();
95 if !domain.is_empty() {
96 return format!("{}.{}", s, domain);
97 }
98 }
99 }
100 }
101 }
102 return s;
103 }
104 }
105
106 if let Some(h) = env::var_os("HOSTNAME") {
108 let s = h.to_string_lossy();
109 if !s.is_empty() {
110 return s.into();
111 }
112 }
113
114 if let Ok(hostname) = fs::read_to_string("/etc/hostname") {
116 let s = hostname.trim().to_string();
117 if !s.is_empty() {
118 return s;
119 }
120 }
121
122 "localhost".into()
123}
124
125use std::mem::MaybeUninit;
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130 use crate::test_utils::EnvLock;
131
132 #[test]
133 fn test_get_user_from_env() {
134 let env_lock = EnvLock::acquire(&["USER"]);
135 env_lock.set_var("USER", "testuser");
136 assert_eq!(get_user(), "testuser");
137 drop(env_lock);
138 }
139
140 #[test]
141 fn test_hostname_from_env() {
142 let env_lock = EnvLock::acquire(&["HOSTNAME"]);
143 env_lock.set_var("HOSTNAME", "testhost");
144 assert_eq!(get_hostname(false), "testhost");
145 drop(env_lock);
146 }
147
148 #[test]
149 fn test_hostname_command_fallback() {
150 let env_lock = EnvLock::acquire(&["HOSTNAME"]);
151 env_lock.remove_var("HOSTNAME");
152 let result = get_hostname(false);
153 assert!(!result.is_empty(), "Hostname should not be empty");
154 drop(env_lock);
155 }
156
157 #[test]
158 fn test_hostname_final_fallback() {
159 let result = get_hostname(false);
162 assert!(!result.is_empty());
163 }
164}