-
Notifications
You must be signed in to change notification settings - Fork 39
feat: Mac port #183 #300
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
feat: Mac port #183 #300
Changes from all commits
cdedcdf
9062bf3
ea590d4
7d6a21e
8f3ff4b
5f3e494
d44ee29
4630d7b
df17063
42cff10
8b58975
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -144,9 +144,21 @@ impl Server { | |
| #[instrument(skip_all, fields(cid = conn_id))] | ||
| fn handle_conn(&self, mut stream: UnixStream, conn_id: usize) -> anyhow::Result<()> { | ||
| // We want to avoid timing out while blocking the main thread. | ||
| // On macOS, set_read_timeout returns EINVAL if the peer has already | ||
| // closed (e.g., a daemon presence probe). This is documented in the | ||
| // macOS setsockopt(2) man page. Treat this the same as a broken pipe. | ||
| #[cfg(target_os = "macos")] | ||
| if let Err(e) = stream.set_read_timeout(Some(consts::SOCK_STREAM_TIMEOUT)) { | ||
| if e.raw_os_error() == Some(libc::EINVAL) { | ||
| info!("EINVAL setting read timeout, peer already closed (presence probe)"); | ||
| return Ok(()); | ||
| } | ||
| return Err(e).context("setting read timeout on inbound session"); | ||
| } | ||
| #[cfg(not(target_os = "macos"))] | ||
| stream | ||
| .set_read_timeout(Some(consts::SOCK_STREAM_TIMEOUT)) | ||
| .context("setting read timout on inbound session")?; | ||
| .context("setting read timeout on inbound session")?; | ||
|
|
||
| // advertize our protocol version to the client so that it can | ||
| // warn about mismatches | ||
|
|
@@ -1138,12 +1150,14 @@ where | |
| protocol::encode_to(&header, serializeable_stream).context("writing reply")?; | ||
|
|
||
| stream.set_write_timeout(None).context("unsetting write timout on inbound session")?; | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| /// check_peer makes sure that a process dialing in on the shpool | ||
| /// control socket has the same UID as the current user and that | ||
| /// both have the same executable path. | ||
| #[cfg(target_os = "linux")] | ||
| fn check_peer(sock: &UnixStream) -> anyhow::Result<()> { | ||
| use nix::sys::socket; | ||
|
|
||
|
|
@@ -1166,11 +1180,69 @@ fn check_peer(sock: &UnixStream) -> anyhow::Result<()> { | |
| Ok(()) | ||
| } | ||
|
|
||
| #[cfg(target_os = "macos")] | ||
| fn check_peer(sock: &UnixStream) -> anyhow::Result<()> { | ||
| use std::os::unix::io::AsRawFd; | ||
|
|
||
| let mut peer_uid: libc::uid_t = 0; | ||
| let mut peer_gid: libc::gid_t = 0; | ||
| unsafe { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add a |
||
| if libc::getpeereid(sock.as_raw_fd(), &mut peer_uid, &mut peer_gid) != 0 { | ||
| return Err(anyhow!( | ||
| "could not get peer uid from socket: {}", | ||
| io::Error::last_os_error() | ||
| )); | ||
| } | ||
| } | ||
| let peer_uid = unistd::Uid::from_raw(peer_uid); | ||
| let self_uid = unistd::Uid::current(); | ||
| if peer_uid != self_uid { | ||
| return Err(anyhow!("shpool prohibits connections across users")); | ||
| } | ||
|
|
||
| let mut peer_pid: libc::pid_t = 0; | ||
| let mut len = std::mem::size_of::<libc::pid_t>() as libc::socklen_t; | ||
| unsafe { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same thing about a |
||
| if libc::getsockopt( | ||
| sock.as_raw_fd(), | ||
| libc::SOL_LOCAL, | ||
| libc::LOCAL_PEERPID, | ||
| &mut peer_pid as *mut _ as *mut libc::c_void, | ||
| &mut len, | ||
| ) != 0 | ||
| { | ||
| return Err(anyhow!( | ||
| "could not get peer pid from socket: {}", | ||
| io::Error::last_os_error() | ||
| )); | ||
| } | ||
| } | ||
|
|
||
| let peer_pid = unistd::Pid::from_raw(peer_pid); | ||
| let self_pid = unistd::Pid::this(); | ||
| let peer_exe = exe_for_pid(peer_pid).context("could not resolve exe from the pid")?; | ||
| let self_exe = exe_for_pid(self_pid).context("could not resolve our own exe")?; | ||
| if peer_exe != self_exe { | ||
| warn!("attach binary differs from daemon binary"); | ||
| } | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| #[cfg(target_os = "linux")] | ||
| fn exe_for_pid(pid: unistd::Pid) -> anyhow::Result<PathBuf> { | ||
| let path = std::fs::read_link(format!("/proc/{pid}/exe"))?; | ||
| Ok(path) | ||
| } | ||
|
|
||
| #[cfg(target_os = "macos")] | ||
| fn exe_for_pid(pid: unistd::Pid) -> anyhow::Result<PathBuf> { | ||
| use libproc::proc_pid::pidpath; | ||
| let path = pidpath(pid.as_raw()) | ||
| .map_err(|e| anyhow!("could not get exe path for pid {}: {:?}", pid, e))?; | ||
| Ok(PathBuf::from(path)) | ||
| } | ||
|
|
||
| #[derive(Debug, Clone, PartialEq, Eq)] | ||
| pub enum ShellSelectionError { | ||
| BusyShellSession, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This block could be simplified by removing the
#[cfg(not(target_os = "macos"))]block below and moving the#[cfg(target_os = "macos")]block to only be attached to the if statement.In fact, since it looks like on linux EINVAL is only returned when the input is malformed, and we're using a safe wrapper that should never malform input, we could probably get away with not even including the cfg annotation. It's probably better to keep it just to be explicit though, since this would do the wrong thing on linux if we somehow did get EINVAL.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I originally had the EINVAL check as something which ran on all platforms but I was being particularly careful not to affect other platforms with potentially BSD 4.4/macOS specific stuff, mainly because I couldn't really find a definitive answer on what conditions we would see EINVAL on other platforms (even the macOS man pages leave a lot unanswered questions for me).
I'll take another swing at this section.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, probably a good idea. Thanks for being careful about that kind of thing.