mirror of
https://github.com/wez/wezterm.git
synced 2024-12-23 05:12:40 +03:00
Move server to its own wezterm-mux-server binary
This commit is contained in:
parent
a07004302a
commit
0c32963f1c
35
Cargo.lock
generated
35
Cargo.lock
generated
@ -651,6 +651,7 @@ name = "codec"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base91",
|
||||
"config",
|
||||
"leb128",
|
||||
"log",
|
||||
@ -3711,6 +3712,13 @@ dependencies = [
|
||||
"ws2_32-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "umask"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.6.0"
|
||||
@ -4111,7 +4119,6 @@ dependencies = [
|
||||
"async-task 1.3.1",
|
||||
"async-trait",
|
||||
"base64 0.10.1",
|
||||
"base91",
|
||||
"bitflags 1.2.1",
|
||||
"bstr 0.2.13",
|
||||
"cc",
|
||||
@ -4174,6 +4181,7 @@ dependencies = [
|
||||
"tinyvec",
|
||||
"toml",
|
||||
"uds_windows",
|
||||
"umask",
|
||||
"unicode-normalization",
|
||||
"unicode-segmentation",
|
||||
"unicode-width",
|
||||
@ -4185,6 +4193,31 @@ dependencies = [
|
||||
"winrt-notification",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wezterm-mux-server"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"codec",
|
||||
"config",
|
||||
"crossbeam",
|
||||
"daemonize",
|
||||
"filedescriptor",
|
||||
"hostname",
|
||||
"log",
|
||||
"mux",
|
||||
"openssl",
|
||||
"portable-pty",
|
||||
"pretty_env_logger",
|
||||
"promise",
|
||||
"rangeset",
|
||||
"rcgen",
|
||||
"structopt",
|
||||
"umask",
|
||||
"url",
|
||||
"wezterm-term",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wezterm-term"
|
||||
version = "0.1.0"
|
||||
|
@ -24,7 +24,6 @@ async-trait = "0.1"
|
||||
anyhow = "1.0"
|
||||
thiserror = "1.0"
|
||||
base64 = "0.10"
|
||||
base91 = { path = "base91" }
|
||||
rangeset = { path = "rangeset" }
|
||||
bitflags = "1.0"
|
||||
bstr = "0.2"
|
||||
@ -72,6 +71,7 @@ termwiz = { path = "termwiz" }
|
||||
textwrap = "0.11"
|
||||
tinyvec = "0.3"
|
||||
toml = "0.4"
|
||||
umask = { path = "umask" }
|
||||
unicode-normalization = "0.1"
|
||||
unicode-segmentation = "1.6"
|
||||
unicode-width = "0.1"
|
||||
@ -123,6 +123,7 @@ default = ["vendor_openssl"]
|
||||
vendor_openssl = ["openssl/vendored"]
|
||||
|
||||
[workspace]
|
||||
members = ["wezterm-mux-server"]
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
|
@ -20,3 +20,6 @@ termwiz = { path = "../termwiz" }
|
||||
varbincode = "0.1"
|
||||
wezterm-term = { path = "../term", features=["use_serde"] }
|
||||
zstd = "0.4"
|
||||
|
||||
[dev-dependencies]
|
||||
base91 = { path = "../base91" }
|
||||
|
@ -5,8 +5,6 @@ pub enum FrontEndSelection {
|
||||
OpenGL,
|
||||
Software,
|
||||
OldSoftware,
|
||||
MuxServer,
|
||||
Null,
|
||||
}
|
||||
impl_lua_conversion!(FrontEndSelection);
|
||||
|
||||
@ -19,7 +17,7 @@ impl Default for FrontEndSelection {
|
||||
impl FrontEndSelection {
|
||||
// TODO: find or build a proc macro for this
|
||||
pub fn variants() -> Vec<&'static str> {
|
||||
vec!["OpenGL", "Software", "OldSoftware", "MuxServer", "Null"]
|
||||
vec!["OpenGL", "Software", "OldSoftware"]
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,8 +25,6 @@ impl std::str::FromStr for FrontEndSelection {
|
||||
type Err = Error;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s.to_lowercase().as_ref() {
|
||||
"muxserver" => Ok(FrontEndSelection::MuxServer),
|
||||
"null" => Ok(FrontEndSelection::Null),
|
||||
"software" => Ok(FrontEndSelection::Software),
|
||||
"oldsoftware" => Ok(FrontEndSelection::OldSoftware),
|
||||
"opengl" => Ok(FrontEndSelection::OpenGL),
|
||||
|
@ -24,9 +24,9 @@ pub struct UnixDomain {
|
||||
|
||||
/// If we decide that we need to start the server, the command to run
|
||||
/// to set that up. The default is to spawn:
|
||||
/// `wezterm start --daemonize --front-end MuxServer`
|
||||
/// `wezterm-mux-server --daemonize`
|
||||
/// but it can be useful to set this to eg:
|
||||
/// `wsl -e wezterm --daemonize --front-end MuxServer` to start up
|
||||
/// `wsl -e wezterm-mux-server --daemonize` to start up
|
||||
/// a unix domain inside a wsl container.
|
||||
pub serve_command: Option<Vec<String>>,
|
||||
|
||||
@ -66,11 +66,10 @@ impl UnixDomain {
|
||||
match self.serve_command.as_ref() {
|
||||
Some(cmd) => Ok(cmd.iter().map(Into::into).collect()),
|
||||
None => Ok(vec![
|
||||
std::env::current_exe()?.into_os_string(),
|
||||
OsString::from("start"),
|
||||
std::env::current_exe()?
|
||||
.with_file_name("wezterm-mux-server")
|
||||
.into_os_string(),
|
||||
OsString::from("--daemonize"),
|
||||
OsString::from("--front-end"),
|
||||
OsString::from("MuxServer"),
|
||||
]),
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ use std::rc::Rc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
pub mod gui;
|
||||
pub mod muxserver;
|
||||
|
||||
pub use config::FrontEndSelection;
|
||||
|
||||
@ -37,18 +36,16 @@ pub fn shutdown() {
|
||||
}
|
||||
|
||||
pub fn try_new(sel: FrontEndSelection) -> Result<Rc<dyn FrontEnd>, Error> {
|
||||
let (front_end, is_gui) = match sel {
|
||||
FrontEndSelection::MuxServer => (muxserver::MuxServerFrontEnd::try_new(), false),
|
||||
FrontEndSelection::Null => (muxserver::MuxServerFrontEnd::new_null(), false),
|
||||
FrontEndSelection::Software => (gui::GuiFrontEnd::try_new_swrast(), true),
|
||||
FrontEndSelection::OldSoftware => (gui::GuiFrontEnd::try_new_no_opengl(), true),
|
||||
FrontEndSelection::OpenGL => (gui::GuiFrontEnd::try_new(), true),
|
||||
let front_end = match sel {
|
||||
FrontEndSelection::Software => gui::GuiFrontEnd::try_new_swrast(),
|
||||
FrontEndSelection::OldSoftware => gui::GuiFrontEnd::try_new_no_opengl(),
|
||||
FrontEndSelection::OpenGL => gui::GuiFrontEnd::try_new(),
|
||||
};
|
||||
|
||||
let front_end = front_end?;
|
||||
|
||||
FRONT_END.with(|f| *f.borrow_mut() = Some(Rc::clone(&front_end)));
|
||||
HAS_GUI_FRONT_END.store(is_gui, Ordering::Release);
|
||||
HAS_GUI_FRONT_END.store(true, Ordering::Release);
|
||||
|
||||
Ok(front_end)
|
||||
}
|
||||
|
@ -1,62 +0,0 @@
|
||||
//! Implements the multiplexer server frontend
|
||||
use crate::frontend::FrontEnd;
|
||||
use crate::server::listener::spawn_listener;
|
||||
use anyhow::{bail, Error};
|
||||
use crossbeam::channel::{unbounded as channel, Receiver};
|
||||
use log::info;
|
||||
use mux::Mux;
|
||||
use promise::*;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct MuxServerFrontEnd {
|
||||
rx: Receiver<SpawnFunc>,
|
||||
}
|
||||
|
||||
impl MuxServerFrontEnd {
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(clippy::new_ret_no_self))]
|
||||
fn new(start_listener: bool) -> Result<Rc<dyn FrontEnd>, Error> {
|
||||
let (tx, rx) = channel();
|
||||
|
||||
let tx_main = tx.clone();
|
||||
let tx_low = tx.clone();
|
||||
let queue_func = move |f: SpawnFunc| {
|
||||
tx_main.send(f).ok();
|
||||
};
|
||||
let queue_func_low = move |f: SpawnFunc| {
|
||||
tx_low.send(f).ok();
|
||||
};
|
||||
promise::spawn::set_schedulers(
|
||||
Box::new(move |task| queue_func(Box::new(move || task.run()))),
|
||||
Box::new(move |task| queue_func_low(Box::new(move || task.run()))),
|
||||
);
|
||||
|
||||
if start_listener {
|
||||
spawn_listener()?;
|
||||
}
|
||||
Ok(Rc::new(Self { rx }))
|
||||
}
|
||||
|
||||
pub fn try_new() -> Result<Rc<dyn FrontEnd>, Error> {
|
||||
Self::new(true)
|
||||
}
|
||||
|
||||
pub fn new_null() -> Result<Rc<dyn FrontEnd>, Error> {
|
||||
Self::new(false)
|
||||
}
|
||||
}
|
||||
|
||||
impl FrontEnd for MuxServerFrontEnd {
|
||||
fn run_forever(&self) -> Result<(), Error> {
|
||||
loop {
|
||||
match self.rx.recv() {
|
||||
Ok(func) => func(),
|
||||
Err(err) => bail!("while waiting for events: {:?}", err),
|
||||
}
|
||||
|
||||
if Mux::get().unwrap().is_empty() && mux::activity::Activity::count() == 0 {
|
||||
info!("No more tabs; all done!");
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
// Don't create a new standard console window when launched from the windows GUI.
|
||||
#![windows_subsystem = "windows"]
|
||||
|
||||
use crate::server::listener::umask;
|
||||
use anyhow::{anyhow, bail};
|
||||
use config::{wezterm_version, SshParameters};
|
||||
use mux::domain::{Domain, LocalDomain};
|
||||
@ -574,8 +573,7 @@ fn run_terminal_gui(config: config::ConfigHandle, opts: StartCommand) -> anyhow:
|
||||
let front_end_selection = opts.front_end.unwrap_or(config.front_end);
|
||||
let gui = crate::frontend::try_new(front_end_selection)?;
|
||||
let activity = Activity::new();
|
||||
let do_auto_connect =
|
||||
front_end_selection != FrontEndSelection::MuxServer && !opts.no_auto_connect;
|
||||
let do_auto_connect = !opts.no_auto_connect;
|
||||
|
||||
promise::spawn::spawn(async move {
|
||||
if let Err(err) = async_run_terminal_gui(cmd, do_auto_connect).await {
|
||||
@ -838,8 +836,6 @@ fn run() -> anyhow::Result<()> {
|
||||
SubCommand::ImageCat(cmd) => cmd.run(),
|
||||
SubCommand::Cli(cli) => {
|
||||
// Start a front end so that the futures executor is running
|
||||
let front_end = crate::frontend::try_new(FrontEndSelection::Null)?;
|
||||
|
||||
let initial = true;
|
||||
let mut ui = crate::connui::ConnectionUI::new_headless();
|
||||
let client = Client::new_default_unix_domain(initial, &mut ui)?;
|
||||
@ -971,7 +967,6 @@ fn run() -> anyhow::Result<()> {
|
||||
let stdin = std::io::stdin();
|
||||
consume_stream_then_exit_process(stdin.lock(), stream);
|
||||
});
|
||||
front_end.run_forever()?;
|
||||
}
|
||||
CliSubCommand::TlsCreds => {
|
||||
let creds = block_on(client.get_tls_creds())?;
|
||||
|
@ -1,34 +0,0 @@
|
||||
use anyhow::{anyhow, bail, Context, Error};
|
||||
use config::{configuration, TlsDomainServer};
|
||||
use log::error;
|
||||
use promise::spawn::spawn_into_main_thread;
|
||||
use std::net::TcpListener;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
|
||||
mod clientsession;
|
||||
mod local;
|
||||
mod ossl;
|
||||
mod pki;
|
||||
mod sessionhandler;
|
||||
pub mod umask;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref PKI: pki::Pki = pki::Pki::init().expect("failed to initialize PKI");
|
||||
}
|
||||
|
||||
pub fn spawn_listener() -> anyhow::Result<()> {
|
||||
let config = configuration();
|
||||
for unix_dom in &config.unix_domains {
|
||||
let mut listener = local::LocalListener::with_domain(unix_dom)?;
|
||||
thread::spawn(move || {
|
||||
listener.run();
|
||||
});
|
||||
}
|
||||
|
||||
for tls_server in &config.tls_servers {
|
||||
ossl::spawn_tls_listener(tls_server)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
@ -1,10 +1,9 @@
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::net::{UnixListener, UnixStream};
|
||||
use std::os::unix::net::UnixStream;
|
||||
#[cfg(windows)]
|
||||
use uds_windows::{UnixListener, UnixStream};
|
||||
use uds_windows::UnixStream;
|
||||
|
||||
pub mod client;
|
||||
pub mod domain;
|
||||
pub mod listener;
|
||||
pub mod pollable;
|
||||
pub mod tab;
|
||||
|
10
umask/Cargo.toml
Normal file
10
umask/Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "umask"
|
||||
version = "0.1.0"
|
||||
authors = ["Wez Furlong <wez@wezfurlong.org>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
35
wezterm-mux-server/Cargo.toml
Normal file
35
wezterm-mux-server/Cargo.toml
Normal file
@ -0,0 +1,35 @@
|
||||
[package]
|
||||
name = "wezterm-mux-server"
|
||||
version = "0.1.0"
|
||||
authors = ["Wez Furlong <wez@wezfurlong.org>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
codec = { path = "../codec" }
|
||||
config = { path = "../config" }
|
||||
crossbeam = "0.7"
|
||||
filedescriptor = { version="0.7", path = "../filedescriptor" }
|
||||
hostname = "0.3"
|
||||
log = "0.4"
|
||||
mux = { path = "../mux" }
|
||||
openssl = "0.10"
|
||||
portable-pty = { path = "../pty", features = ["serde_support"]}
|
||||
pretty_env_logger = "0.4"
|
||||
promise = { path = "../promise" }
|
||||
rangeset = { path = "../rangeset" }
|
||||
rcgen = "0.7"
|
||||
structopt = "0.3"
|
||||
umask = { path = "../umask" }
|
||||
url = "2"
|
||||
wezterm-term = { path = "../term", features=["use_serde"] }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
daemonize = { git = "https://github.com/wez/daemonize" }
|
||||
|
||||
[features]
|
||||
default = ["vendor_openssl"]
|
||||
# FIXME: find a way to magically disable vendor_openssl only on linux!
|
||||
vendor_openssl = ["openssl/vendored"]
|
@ -1,5 +1,5 @@
|
||||
use crate::server::listener::sessionhandler::SessionHandler;
|
||||
use crate::server::pollable::*;
|
||||
use crate::pollable::*;
|
||||
use crate::sessionhandler::SessionHandler;
|
||||
use anyhow::{bail, Context, Error};
|
||||
use codec::*;
|
||||
use crossbeam::channel::TryRecvError;
|
@ -1,5 +1,5 @@
|
||||
use crate::server::listener::clientsession;
|
||||
use crate::server::UnixListener;
|
||||
use crate::clientsession;
|
||||
use crate::UnixListener;
|
||||
use anyhow::{anyhow, Context as _};
|
||||
use config::{create_user_owned_dirs, UnixDomain};
|
||||
use promise::spawn::spawn_into_main_thread;
|
218
wezterm-mux-server/src/main.rs
Normal file
218
wezterm-mux-server/src/main.rs
Normal file
@ -0,0 +1,218 @@
|
||||
use anyhow::{anyhow, bail, Context, Error};
|
||||
use config::{configuration, TlsDomainServer};
|
||||
use crossbeam::channel::unbounded as channel;
|
||||
use log::error;
|
||||
use mux::activity::Activity;
|
||||
use mux::domain::{Domain, LocalDomain};
|
||||
use mux::Mux;
|
||||
use portable_pty::cmdbuilder::CommandBuilder;
|
||||
use promise::spawn::spawn_into_main_thread;
|
||||
use promise::*;
|
||||
use std::ffi::OsString;
|
||||
use std::net::TcpListener;
|
||||
use std::path::Path;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use structopt::*;
|
||||
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::net::{UnixListener, UnixStream};
|
||||
#[cfg(windows)]
|
||||
use uds_windows::{UnixListener, UnixStream};
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
#[structopt(
|
||||
about = "Wez's Terminal Emulator\nhttp://github.com/wez/wezterm",
|
||||
global_setting = structopt::clap::AppSettings::ColoredHelp,
|
||||
version = config::wezterm_version()
|
||||
)]
|
||||
struct Opt {
|
||||
/// Skip loading wezterm.lua
|
||||
#[structopt(short = "n")]
|
||||
skip_config: bool,
|
||||
|
||||
/// Detach from the foreground and become a background process
|
||||
#[structopt(long = "daemonize")]
|
||||
daemonize: bool,
|
||||
|
||||
/// Specify the current working directory for the initially
|
||||
/// spawned program
|
||||
#[structopt(long = "cwd", parse(from_os_str))]
|
||||
cwd: Option<OsString>,
|
||||
|
||||
/// Instead of executing your shell, run PROG.
|
||||
/// For example: `wezterm start -- bash -l` will spawn bash
|
||||
/// as if it were a login shell.
|
||||
#[structopt(parse(from_os_str))]
|
||||
prog: Vec<OsString>,
|
||||
}
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
pretty_env_logger::init_timed();
|
||||
//stats::Stats::init()?;
|
||||
let _saver = umask::UmaskSaver::new();
|
||||
|
||||
let opts = Opt::from_args();
|
||||
if !opts.skip_config {
|
||||
config::reload();
|
||||
}
|
||||
let config = config::configuration();
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
if opts.daemonize {
|
||||
let stdout = config.daemon_options.open_stdout()?;
|
||||
let stderr = config.daemon_options.open_stderr()?;
|
||||
let mut daemonize = daemonize::Daemonize::new()
|
||||
.stdout(stdout)
|
||||
.stderr(stderr)
|
||||
.working_directory(config::HOME_DIR.clone());
|
||||
|
||||
if !config::running_under_wsl() {
|
||||
// pid file locking is only partly function when running under
|
||||
// WSL 1; it is possible for the pid file to exist after a reboot
|
||||
// and for attempts to open and lock it to fail when there are no
|
||||
// other processes that might possibly hold a lock on it.
|
||||
// So, we only use a pid file when not under WSL.
|
||||
daemonize = daemonize.pid_file(config.daemon_options.pid_file());
|
||||
}
|
||||
if let Err(err) = daemonize.start() {
|
||||
use daemonize::DaemonizeError;
|
||||
match err {
|
||||
DaemonizeError::OpenPidfile
|
||||
| DaemonizeError::LockPidfile(_)
|
||||
| DaemonizeError::ChownPidfile(_)
|
||||
| DaemonizeError::WritePid => {
|
||||
bail!("{} {}", err, config.daemon_options.pid_file().display());
|
||||
}
|
||||
DaemonizeError::ChangeDirectory => {
|
||||
bail!("{} {}", err, config::HOME_DIR.display());
|
||||
}
|
||||
_ => return Err(err.into()),
|
||||
}
|
||||
}
|
||||
|
||||
// Remove some environment variables that aren't super helpful or
|
||||
// that are potentially misleading when we're starting up the
|
||||
// server.
|
||||
// We may potentially want to look into starting/registering
|
||||
// a session of some kind here as well in the future.
|
||||
for name in &[
|
||||
"OLDPWD",
|
||||
"PWD",
|
||||
"SHLVL",
|
||||
"SSH_AUTH_SOCK",
|
||||
"SSH_CLIENT",
|
||||
"SSH_CONNECTION",
|
||||
"_",
|
||||
] {
|
||||
std::env::remove_var(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let need_builder = !opts.prog.is_empty() || opts.cwd.is_some();
|
||||
|
||||
let cmd = if need_builder {
|
||||
let mut builder = if opts.prog.is_empty() {
|
||||
CommandBuilder::new_default_prog()
|
||||
} else {
|
||||
CommandBuilder::from_argv(opts.prog)
|
||||
};
|
||||
if let Some(cwd) = opts.cwd {
|
||||
builder.cwd(cwd);
|
||||
}
|
||||
Some(builder)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let domain: Arc<dyn Domain> = Arc::new(LocalDomain::new("local")?);
|
||||
let mux = Rc::new(mux::Mux::new(Some(domain.clone())));
|
||||
Mux::set_mux(&mux);
|
||||
|
||||
let (tx, rx) = channel();
|
||||
|
||||
let tx_main = tx.clone();
|
||||
let tx_low = tx.clone();
|
||||
let queue_func = move |f: SpawnFunc| {
|
||||
tx_main.send(f).ok();
|
||||
};
|
||||
let queue_func_low = move |f: SpawnFunc| {
|
||||
tx_low.send(f).ok();
|
||||
};
|
||||
promise::spawn::set_schedulers(
|
||||
Box::new(move |task| queue_func(Box::new(move || task.run()))),
|
||||
Box::new(move |task| queue_func_low(Box::new(move || task.run()))),
|
||||
);
|
||||
|
||||
spawn_listener()?;
|
||||
|
||||
let activity = Activity::new();
|
||||
|
||||
promise::spawn::spawn(async move {
|
||||
if let Err(err) = async_run(cmd).await {
|
||||
terminate_with_error(err);
|
||||
}
|
||||
drop(activity);
|
||||
});
|
||||
|
||||
loop {
|
||||
match rx.recv() {
|
||||
Ok(func) => func(),
|
||||
Err(err) => bail!("while waiting for events: {:?}", err),
|
||||
}
|
||||
|
||||
if Mux::get().unwrap().is_empty() && mux::activity::Activity::count() == 0 {
|
||||
log::info!("No more tabs; all done!");
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn async_run(cmd: Option<CommandBuilder>) -> anyhow::Result<()> {
|
||||
let mux = Mux::get().unwrap();
|
||||
|
||||
let domain = mux.default_domain();
|
||||
domain.attach().await?;
|
||||
|
||||
let config = config::configuration();
|
||||
let window_id = mux.new_empty_window();
|
||||
let _tab = mux
|
||||
.default_domain()
|
||||
.spawn(config.initial_size(), cmd, None, *window_id)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn terminate_with_error(err: anyhow::Error) -> ! {
|
||||
log::error!("{:#}; terminating", err);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
mod clientsession;
|
||||
mod local;
|
||||
mod ossl;
|
||||
mod pki;
|
||||
mod pollable;
|
||||
mod sessionhandler;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref PKI: pki::Pki = pki::Pki::init().expect("failed to initialize PKI");
|
||||
}
|
||||
|
||||
pub fn spawn_listener() -> anyhow::Result<()> {
|
||||
let config = configuration();
|
||||
for unix_dom in &config.unix_domains {
|
||||
let mut listener = local::LocalListener::with_domain(unix_dom)?;
|
||||
thread::spawn(move || {
|
||||
listener.run();
|
||||
});
|
||||
}
|
||||
|
||||
for tls_server in &config.tls_servers {
|
||||
ossl::spawn_tls_listener(tls_server)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
131
wezterm-mux-server/src/pollable.rs
Normal file
131
wezterm-mux-server/src/pollable.rs
Normal file
@ -0,0 +1,131 @@
|
||||
use crate::UnixStream;
|
||||
use anyhow::Error;
|
||||
use crossbeam::channel::{unbounded as channel, Receiver, Sender, TryRecvError};
|
||||
use filedescriptor::*;
|
||||
use std::cell::RefCell;
|
||||
use std::io::{Read, Write};
|
||||
use std::net::TcpStream;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
pub trait ReadAndWrite: std::io::Read + std::io::Write + Send + AsPollFd {
|
||||
fn set_non_blocking(&self, non_blocking: bool) -> anyhow::Result<()>;
|
||||
fn has_read_buffered(&self) -> bool;
|
||||
}
|
||||
impl ReadAndWrite for UnixStream {
|
||||
fn set_non_blocking(&self, non_blocking: bool) -> anyhow::Result<()> {
|
||||
self.set_nonblocking(non_blocking)?;
|
||||
Ok(())
|
||||
}
|
||||
fn has_read_buffered(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl ReadAndWrite for openssl::ssl::SslStream<std::net::TcpStream> {
|
||||
fn set_non_blocking(&self, non_blocking: bool) -> anyhow::Result<()> {
|
||||
self.get_ref().set_nonblocking(non_blocking)?;
|
||||
Ok(())
|
||||
}
|
||||
fn has_read_buffered(&self) -> bool {
|
||||
self.ssl().pending() != 0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PollableSender<T> {
|
||||
sender: Sender<T>,
|
||||
write: Arc<Mutex<FileDescriptor>>,
|
||||
}
|
||||
|
||||
impl<T: Send + Sync + 'static> PollableSender<T> {
|
||||
pub fn send(&self, item: T) -> anyhow::Result<()> {
|
||||
// Attempt to write to the pipe; if it fails due to
|
||||
// being full, that's fine: it means that the other end
|
||||
// is going to be signalled already so they won't miss
|
||||
// anything if our write doesn't happen.
|
||||
self.write.lock().unwrap().write_all(b"x").ok();
|
||||
self.sender.send(item).map_err(Error::msg)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for PollableSender<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
sender: self.sender.clone(),
|
||||
write: self.write.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PollableReceiver<T> {
|
||||
receiver: Receiver<T>,
|
||||
read: RefCell<FileDescriptor>,
|
||||
}
|
||||
|
||||
impl<T> PollableReceiver<T> {
|
||||
pub fn try_recv(&self) -> Result<T, TryRecvError> {
|
||||
// try to drain the pipe.
|
||||
// We do this regardless of whether we popped an item
|
||||
// so that we avoid being in a perpetually signalled state.
|
||||
let mut byte = [0u8; 64];
|
||||
self.read.borrow_mut().read(&mut byte).ok();
|
||||
|
||||
Ok(self.receiver.try_recv()?)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsPollFd for PollableReceiver<T> {
|
||||
fn as_poll_fd(&self) -> pollfd {
|
||||
self.read.borrow().as_socket_descriptor().as_poll_fd()
|
||||
}
|
||||
}
|
||||
|
||||
/// A channel that can be polled together with a socket.
|
||||
/// This uses the self-pipe trick but with a unix domain
|
||||
/// socketpair.
|
||||
/// In theory this should also work on windows, but will require
|
||||
/// windows 10 w/unix domain socket support.
|
||||
pub fn pollable_channel<T>() -> anyhow::Result<(PollableSender<T>, PollableReceiver<T>)> {
|
||||
let (sender, receiver) = channel();
|
||||
let (mut write, mut read) = socketpair()?;
|
||||
|
||||
write.set_non_blocking(true)?;
|
||||
read.set_non_blocking(true)?;
|
||||
|
||||
Ok((
|
||||
PollableSender {
|
||||
sender,
|
||||
write: Arc::new(Mutex::new(FileDescriptor::new(write))),
|
||||
},
|
||||
PollableReceiver {
|
||||
receiver,
|
||||
read: RefCell::new(FileDescriptor::new(read)),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
pub trait AsPollFd {
|
||||
fn as_poll_fd(&self) -> pollfd;
|
||||
}
|
||||
|
||||
impl AsPollFd for SocketDescriptor {
|
||||
fn as_poll_fd(&self) -> pollfd {
|
||||
pollfd {
|
||||
fd: *self,
|
||||
events: POLLIN,
|
||||
revents: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsPollFd for openssl::ssl::SslStream<TcpStream> {
|
||||
fn as_poll_fd(&self) -> pollfd {
|
||||
self.get_ref().as_socket_descriptor().as_poll_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsPollFd for UnixStream {
|
||||
fn as_poll_fd(&self) -> pollfd {
|
||||
self.as_socket_descriptor().as_poll_fd()
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
use crate::server::listener::PKI;
|
||||
use crate::server::pollable::*;
|
||||
use crate::pollable::*;
|
||||
use crate::PKI;
|
||||
use anyhow::anyhow;
|
||||
use codec::*;
|
||||
use config::keyassignment::SpawnTabDomain;
|
||||
@ -447,7 +447,7 @@ impl SessionHandler {
|
||||
Pdu::GetCodecVersion(_) => {
|
||||
send_response(Ok(Pdu::GetCodecVersionResponse(GetCodecVersionResponse {
|
||||
codec_vers: CODEC_VERSION,
|
||||
version_string: crate::wezterm_version().to_owned(),
|
||||
version_string: config::wezterm_version().to_owned(),
|
||||
})))
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user