shadowsocks_rust/
sys.rs

1//! System related APIs
2
3/// Some systems set an artificially low soft limit on open file count, for compatibility
4/// with code that uses select and its hard-coded maximum file descriptor
5/// (limited by the size of fd_set).
6///
7/// Tokio (Mio) doesn't use select.
8///
9/// http://0pointer.net/blog/file-descriptor-limits.html
10/// https://github.com/golang/go/issues/46279
11#[cfg(all(unix, not(target_os = "android")))]
12pub fn adjust_nofile() {
13    use log::{debug, trace};
14    use std::{io::Error, mem};
15
16    unsafe {
17        let mut lim: libc::rlimit = mem::zeroed();
18        let ret = libc::getrlimit(libc::RLIMIT_NOFILE, &mut lim);
19        if ret < 0 {
20            debug!("getrlimit NOFILE failed, {}", Error::last_os_error());
21            return;
22        }
23
24        if lim.rlim_cur != lim.rlim_max {
25            trace!("rlimit NOFILE {:?} require adjustion", lim);
26            lim.rlim_cur = lim.rlim_max;
27
28            // On older macOS, setrlimit with rlim_cur = infinity will fail.
29            #[cfg(any(target_os = "macos", target_os = "ios", target_os = "watchos", target_os = "tvos"))]
30            {
31                use std::ptr;
32
33                unsafe extern "C" {
34                    fn sysctlbyname(
35                        name: *const libc::c_char,
36                        oldp: *mut libc::c_void,
37                        oldlenp: *mut libc::size_t,
38                        newp: *mut libc::c_void,
39                        newlen: libc::size_t,
40                    ) -> libc::c_int;
41                }
42
43                // CTL_KERN
44                //
45                // Name                         Type                    Changeable
46                // kern.maxfiles                int32_t                 yes
47                // kern.maxfilesperproc         int32_t                 yes
48
49                let name = b"kern.maxfilesperproc\0";
50                let mut nfile: i32 = 0;
51                let mut nfile_len: libc::size_t = mem::size_of_val(&nfile);
52
53                let ret = sysctlbyname(
54                    name.as_ptr() as *const _,
55                    &mut nfile as *mut _ as *mut _,
56                    &mut nfile_len,
57                    ptr::null_mut(),
58                    0,
59                );
60
61                if ret < 0 {
62                    debug!("sysctlbyname kern.maxfilesperproc failed, {}", Error::last_os_error());
63                } else {
64                    lim.rlim_cur = nfile as libc::rlim_t;
65                }
66            }
67
68            let ret = libc::setrlimit(libc::RLIMIT_NOFILE, &lim);
69            if ret < 0 {
70                debug!("setrlimit NOFILE {:?} failed, {}", lim, Error::last_os_error());
71            } else {
72                debug!("rlimit NOFILE adjusted {:?}", lim);
73            }
74        }
75    }
76}
77
78/// setuid(), setgid() for a specific user or uid
79#[cfg(unix)]
80pub fn run_as_user(uname: &str) -> std::io::Result<()> {
81    use log::error;
82    use std::{
83        ffi::{CStr, CString},
84        io::{Error, ErrorKind},
85    };
86
87    unsafe {
88        let pwd = match uname.parse::<libc::uid_t>() {
89            Ok(uid) => {
90                let mut pwd = libc::getpwuid(uid);
91                if pwd.is_null() {
92                    let uname = CString::new(uname).expect("username");
93                    pwd = libc::getpwnam(uname.as_ptr())
94                }
95                pwd
96            }
97            Err(..) => {
98                let uname = CString::new(uname).expect("username");
99                libc::getpwnam(uname.as_ptr())
100            }
101        };
102
103        if pwd.is_null() {
104            return Err(Error::new(ErrorKind::InvalidInput, format!("user {} not found", uname)));
105        }
106
107        let pwd = &*pwd;
108
109        // setgid first, because we may not allowed to do it anymore after setuid
110        if libc::setgid(pwd.pw_gid as libc::gid_t) != 0 {
111            let err = Error::last_os_error();
112
113            error!(
114                "could not change group id to user {:?}'s gid: {}, uid: {}, error: {}",
115                CStr::from_ptr(pwd.pw_name),
116                pwd.pw_gid,
117                pwd.pw_uid,
118                err
119            );
120            return Err(err);
121        }
122
123        if libc::initgroups(pwd.pw_name, pwd.pw_gid as _) != 0 {
124            let err = Error::last_os_error();
125            error!(
126                "could not change supplementary groups to user {:?}'s gid: {}, uid: {}, error: {}",
127                CStr::from_ptr(pwd.pw_name),
128                pwd.pw_gid,
129                pwd.pw_uid,
130                err
131            );
132            return Err(err);
133        }
134
135        if libc::setuid(pwd.pw_uid) != 0 {
136            let err = Error::last_os_error();
137            error!(
138                "could not change user id to user {:?}'s gid: {}, uid: {}, error: {}",
139                CStr::from_ptr(pwd.pw_name),
140                pwd.pw_gid,
141                pwd.pw_uid,
142                err
143            );
144            return Err(err);
145        }
146    }
147
148    Ok(())
149}