@@ -33,7 +33,11 @@ pub const CORTEX_CONFIG_DIR_ENV: &str = "CORTEX_CONFIG_DIR";
3333/// Checks in order:
3434/// 1. `CORTEX_CONFIG_DIR` environment variable
3535/// 2. `CORTEX_HOME` environment variable
36- /// 3. Default `~/.cortex` directory
36+ /// 3. Default `~/.cortex` directory (respecting SUDO_USER if running as root)
37+ ///
38+ /// When running with sudo, this function detects SUDO_USER and uses that
39+ /// user's home directory instead of /root/.cortex to prevent creating
40+ /// root-owned config files in the user's home directory.
3741pub fn find_cortex_home ( ) -> std:: io:: Result < PathBuf > {
3842 // Check CORTEX_CONFIG_DIR environment variable first (new)
3943 if let Ok ( val) = std:: env:: var ( CORTEX_CONFIG_DIR_ENV ) {
@@ -53,12 +57,117 @@ pub fn find_cortex_home() -> std::io::Result<PathBuf> {
5357 }
5458 }
5559
56- // Default to ~/.cortex
57- let mut home = dirs:: home_dir ( ) . ok_or_else ( || {
60+ // Default to ~/.cortex, but respect SUDO_USER if running as root
61+ let home = get_effective_home_dir ( ) ?;
62+ let cortex_home = home. join ( ".cortex" ) ;
63+ Ok ( cortex_home)
64+ }
65+
66+ /// Get the effective home directory, respecting SUDO_USER when running as root.
67+ ///
68+ /// This prevents creating root-owned config files in the user's home directory
69+ /// when running cortex with sudo.
70+ fn get_effective_home_dir ( ) -> std:: io:: Result < PathBuf > {
71+ // Check if we're running as root (uid 0 on Unix)
72+ #[ cfg( unix) ]
73+ {
74+ // Check if effective user is root
75+ let is_root = unsafe { libc:: geteuid ( ) } == 0 ;
76+
77+ if is_root {
78+ // Check for SUDO_USER to get the original user's home
79+ if let Ok ( sudo_user) = std:: env:: var ( "SUDO_USER" ) {
80+ if !sudo_user. is_empty ( ) && sudo_user != "root" {
81+ // Try to get the user's home directory from /etc/passwd
82+ if let Some ( user_home) = get_user_home_dir ( & sudo_user) {
83+ debug ! (
84+ user = %sudo_user,
85+ home = %user_home. display( ) ,
86+ "Using SUDO_USER's home directory"
87+ ) ;
88+ return Ok ( user_home) ;
89+ }
90+ }
91+ }
92+
93+ // Also check for SUDO_UID
94+ if let Ok ( sudo_uid) = std:: env:: var ( "SUDO_UID" ) {
95+ if let Ok ( uid) = sudo_uid. parse :: < u32 > ( ) {
96+ if uid != 0 {
97+ if let Some ( user_home) = get_user_home_dir_by_uid ( uid) {
98+ debug ! (
99+ uid = uid,
100+ home = %user_home. display( ) ,
101+ "Using SUDO_UID's home directory"
102+ ) ;
103+ return Ok ( user_home) ;
104+ }
105+ }
106+ }
107+ }
108+
109+ // Warn if running as root without SUDO_USER
110+ debug ! ( "Running as root without SUDO_USER, using root's home directory" ) ;
111+ }
112+ }
113+
114+ // Fall back to normal home directory detection
115+ dirs:: home_dir ( ) . ok_or_else ( || {
58116 std:: io:: Error :: new ( std:: io:: ErrorKind :: NotFound , "Home directory not found" )
59- } ) ?;
60- home. push ( ".cortex" ) ;
61- Ok ( home)
117+ } )
118+ }
119+
120+ /// Get a user's home directory by username.
121+ #[ cfg( unix) ]
122+ fn get_user_home_dir ( username : & str ) -> Option < PathBuf > {
123+ use std:: ffi:: CString ;
124+
125+ let username_c = CString :: new ( username) . ok ( ) ?;
126+ let passwd = unsafe { libc:: getpwnam ( username_c. as_ptr ( ) ) } ;
127+
128+ if passwd. is_null ( ) {
129+ return None ;
130+ }
131+
132+ let home_dir = unsafe {
133+ let home = ( * passwd) . pw_dir ;
134+ if home. is_null ( ) {
135+ return None ;
136+ }
137+ std:: ffi:: CStr :: from_ptr ( home) . to_str ( ) . ok ( ) ?. to_string ( )
138+ } ;
139+
140+ Some ( PathBuf :: from ( home_dir) )
141+ }
142+
143+ /// Get a user's home directory by UID.
144+ #[ cfg( unix) ]
145+ fn get_user_home_dir_by_uid ( uid : u32 ) -> Option < PathBuf > {
146+ let passwd = unsafe { libc:: getpwuid ( uid) } ;
147+
148+ if passwd. is_null ( ) {
149+ return None ;
150+ }
151+
152+ let home_dir = unsafe {
153+ let home = ( * passwd) . pw_dir ;
154+ if home. is_null ( ) {
155+ return None ;
156+ }
157+ std:: ffi:: CStr :: from_ptr ( home) . to_str ( ) . ok ( ) ?. to_string ( )
158+ } ;
159+
160+ Some ( PathBuf :: from ( home_dir) )
161+ }
162+
163+ #[ cfg( not( unix) ) ]
164+ fn get_user_home_dir ( _username : & str ) -> Option < PathBuf > {
165+ None
166+ }
167+
168+ #[ cfg( not( unix) ) ]
169+ fn get_user_home_dir_by_uid ( _uid : u32 ) -> Option < PathBuf > {
170+ None
62171}
63172
64173/// Get the config file path, checking CORTEX_CONFIG env var first.
0 commit comments