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