Allow Zed to run under multiple user accounts simultaneously (#14143)
Closes #4607 This is an attempt to enable Zed to run under multiple user accounts on the same Mac, because it's a blocker to me really giving Zed a fair shot at being my primary editor. According to some helpful info from @ForLoveOfCats in #4607 the main reason why this doesn't work is because Zed is using a Unix socket or maybe a TCP socket with a hard-coded path and/or port. To me it looks like it's a TCP socket so I tried changing that code in here, but I'm stuck at trying to test it out because running `target/debug/zed` or `target/release/zed` seems to behave differently than running an actual app bundle. I had no luck copying the binary over to /Applications/Zed.app/Contents/MacOS/zed because it can't find WebRTC.framework which resides at a different relative path in the app bundle. If this seems like a desirable change to the core team then I'm looking for some guidance on how to build an app bundle or otherwise test out this change, or a nudge in the correct direction if I'm way off base with my current approach. Release Notes: - Added multiuser support for up to 100 users on the same machine. --------- Co-authored-by: Mikayla <mikayla@zed.dev>
This commit is contained in:
parent
837639535c
commit
b386b6c237
3 changed files with 54 additions and 4 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -14260,6 +14260,7 @@ dependencies = [
|
|||
"smol",
|
||||
"snippet_provider",
|
||||
"supermaven",
|
||||
"sysinfo",
|
||||
"tab_switcher",
|
||||
"task",
|
||||
"tasks_ui",
|
||||
|
|
|
@ -113,6 +113,7 @@ vim.workspace = true
|
|||
welcome.workspace = true
|
||||
workspace.workspace = true
|
||||
zed_actions.workspace = true
|
||||
sysinfo.workspace = true
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
windows.workspace = true
|
||||
|
|
|
@ -5,22 +5,70 @@ use std::{
|
|||
time::Duration,
|
||||
};
|
||||
|
||||
use sysinfo::System;
|
||||
|
||||
use release_channel::ReleaseChannel;
|
||||
|
||||
const LOCALHOST: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1);
|
||||
const CONNECT_TIMEOUT: Duration = Duration::from_millis(10);
|
||||
const RECEIVE_TIMEOUT: Duration = Duration::from_millis(35);
|
||||
const SEND_TIMEOUT: Duration = Duration::from_millis(20);
|
||||
const USER_BLOCK: u16 = 100;
|
||||
|
||||
fn address() -> SocketAddr {
|
||||
// These port numbers are offset by the user ID to avoid conflicts between
|
||||
// different users on the same machine. In addition to that the ports for each
|
||||
// release channel are spaced out by 100 to avoid conflicts between different
|
||||
// users running different release channels on the same machine. This ends up
|
||||
// interleaving the ports between different users and different release channels.
|
||||
//
|
||||
// On macOS user IDs start at 501 and on Linux they start at 1000. The first user
|
||||
// on a Mac with ID 501 running a dev channel build will use port 44238, and the
|
||||
// second user with ID 502 will use port 44239, and so on. User 501 will use ports
|
||||
// 44338, 44438, and 44538 for the preview, stable, and nightly channels,
|
||||
// respectively. User 502 will use ports 44339, 44439, and 44539 for the preview,
|
||||
// stable, and nightly channels, respectively.
|
||||
let port = match *release_channel::RELEASE_CHANNEL {
|
||||
ReleaseChannel::Dev => 43737,
|
||||
ReleaseChannel::Preview => 43738,
|
||||
ReleaseChannel::Stable => 43739,
|
||||
ReleaseChannel::Nightly => 43740,
|
||||
ReleaseChannel::Preview => 43737 + USER_BLOCK,
|
||||
ReleaseChannel::Stable => 43737 + (2 * USER_BLOCK),
|
||||
ReleaseChannel::Nightly => 43737 + (3 * USER_BLOCK),
|
||||
};
|
||||
let mut user_port = port;
|
||||
let mut sys = System::new_all();
|
||||
sys.refresh_all();
|
||||
if let Ok(current_pid) = sysinfo::get_current_pid() {
|
||||
if let Some(uid) = sys
|
||||
.process(current_pid)
|
||||
.and_then(|process| process.user_id())
|
||||
{
|
||||
let uid_u32 = get_uid_as_u32(uid);
|
||||
// Ensure that the user ID is not too large to avoid overflow when
|
||||
// calculating the port number. This seems unlikely but it doesn't
|
||||
// hurt to be safe.
|
||||
let max_port = 65535;
|
||||
let max_uid: u32 = max_port - port as u32;
|
||||
let wrapped_uid: u16 = (uid_u32 % max_uid) as u16;
|
||||
user_port += wrapped_uid;
|
||||
}
|
||||
}
|
||||
|
||||
SocketAddr::V4(SocketAddrV4::new(LOCALHOST, port))
|
||||
SocketAddr::V4(SocketAddrV4::new(LOCALHOST, user_port))
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn get_uid_as_u32(uid: &sysinfo::Uid) -> u32 {
|
||||
*uid.clone()
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn get_uid_as_u32(uid: &sysinfo::Uid) -> u32 {
|
||||
// Extract the RID which is an integer
|
||||
uid.to_string()
|
||||
.rsplit('-')
|
||||
.next()
|
||||
.and_then(|rid| rid.parse::<u32>().ok())
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
fn instance_handshake() -> &'static str {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue