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