summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2024-04-12 15:39:36 +0200
committerGitHub <noreply@github.com>2024-04-12 15:39:36 +0200
commite68bc649d63d51178d224be3af7109ca324480f4 (patch)
treea3993ee2833b7b5ac952a35617c7037132571b71
parenta0f48c6731473b17a485c9967e80359826a5373c (diff)
feat(cli): allow starting a session detached (#3257)
* feat(cli): allow starting a session detached * fix tests
-rw-r--r--src/commands.rs26
-rw-r--r--zellij-client/src/cli_client.rs4
-rw-r--r--zellij-client/src/lib.rs69
-rw-r--r--zellij-client/src/os_input_output.rs4
-rw-r--r--zellij-server/src/os_input_output.rs13
-rw-r--r--zellij-server/src/unit/os_input_output_tests.rs2
-rw-r--r--zellij-utils/src/cli.rs4
7 files changed, 109 insertions, 13 deletions
diff --git a/src/commands.rs b/src/commands.rs
index d37a422a0..e0dbbdc2e 100644
--- a/src/commands.rs
+++ b/src/commands.rs
@@ -407,6 +407,7 @@ pub(crate) fn start_client(opts: CliArgs) {
let mut config_options = config_options.clone();
let mut opts = opts.clone();
let mut is_a_reconnect = false;
+ let mut should_create_detached = false;
if let Some(reconnect_to_session) = &reconnect_to_session {
// this is integration code to make session reconnects work with this existing,
@@ -417,6 +418,7 @@ pub(crate) fn start_client(opts: CliArgs) {
opts.command = Some(Command::Sessions(Sessions::Attach {
session_name: reconnect_to_session.name.clone(),
create: true,
+ background: false,
force_run_commands: false,
index: None,
options: None,
@@ -476,6 +478,7 @@ pub(crate) fn start_client(opts: CliArgs) {
if let Some(Command::Sessions(Sessions::Attach {
session_name,
create,
+ background,
force_run_commands,
index,
options,
@@ -487,9 +490,14 @@ pub(crate) fn start_client(opts: CliArgs) {
},
None => config_options,
};
+ should_create_detached = background;
let client = if let Some(idx) = index {
- attach_with_session_index(config_options.clone(), idx, create)
+ attach_with_session_index(
+ config_options.clone(),
+ idx,
+ create || should_create_detached,
+ )
} else {
let session_exists = session_name
.as_ref()
@@ -497,7 +505,10 @@ pub(crate) fn start_client(opts: CliArgs) {
.unwrap_or(false);
let resurrection_layout =
session_name.as_ref().and_then(|s| resurrection_layout(&s));
- if create && !session_exists && resurrection_layout.is_none() {
+ if (create || should_create_detached)
+ && !session_exists
+ && resurrection_layout.is_none()
+ {
session_name.clone().map(start_client_plan);
}
match (session_name.as_ref(), resurrection_layout) {
@@ -507,7 +518,11 @@ pub(crate) fn start_client(opts: CliArgs) {
}
ClientInfo::Resurrect(session_name.clone(), resurrection_layout)
},
- _ => attach_with_session_name(session_name, config_options.clone(), create),
+ _ => attach_with_session_name(
+ session_name,
+ config_options.clone(),
+ create || should_create_detached,
+ ),
}
};
@@ -541,6 +556,7 @@ pub(crate) fn start_client(opts: CliArgs) {
tab_position_to_focus,
pane_id_to_focus,
is_a_reconnect,
+ should_create_detached,
);
} else {
if let Some(session_name) = opts.session.clone() {
@@ -555,6 +571,7 @@ pub(crate) fn start_client(opts: CliArgs) {
None,
None,
is_a_reconnect,
+ should_create_detached,
);
} else {
if let Some(session_name) = config_options.session_name.as_ref() {
@@ -595,6 +612,7 @@ pub(crate) fn start_client(opts: CliArgs) {
None,
None,
is_a_reconnect,
+ should_create_detached,
);
},
_ => {
@@ -609,6 +627,7 @@ pub(crate) fn start_client(opts: CliArgs) {
None,
None,
is_a_reconnect,
+ should_create_detached,
);
},
}
@@ -632,6 +651,7 @@ pub(crate) fn start_client(opts: CliArgs) {
None,
None,
is_a_reconnect,
+ should_create_detached,
);
}
}
diff --git a/zellij-client/src/cli_client.rs b/zellij-client/src/cli_client.rs
index 6d25e21b9..30e406031 100644
--- a/zellij-client/src/cli_client.rs
+++ b/zellij-client/src/cli_client.rs
@@ -138,8 +138,8 @@ fn pipe_client(
let mut buffer = String::new();
let _ = stdin.read_line(&mut buffer);
if buffer.is_empty() {
- // TODO: consider notifying the relevant plugin that the pipe has ended with a
- // specialized message
+ let msg = create_msg(None);
+ os_input.send_to_server(msg);
break;
} else {
// we've got data! send it down the pipe (most common)
diff --git a/zellij-client/src/lib.rs b/zellij-client/src/lib.rs
index 2d74e0dd4..5e2f80603 100644
--- a/zellij-client/src/lib.rs
+++ b/zellij-client/src/lib.rs
@@ -29,6 +29,7 @@ use zellij_utils::{
errors::{ClientContext, ContextType, ErrorInstruction},
input::{config::Config, options::Options},
ipc::{ClientAttributes, ClientToServerMsg, ExitReason, ServerToClientMsg},
+ pane_size::Size,
termwiz::input::InputEvent,
};
use zellij_utils::{cli::CliArgs, input::layout::Layout};
@@ -168,7 +169,12 @@ pub fn start_client(
tab_position_to_focus: Option<usize>,
pane_id_to_focus: Option<(u32, bool)>, // (pane_id, is_plugin)
is_a_reconnect: bool,
+ start_detached_and_exit: bool,
) -> Option<ConnectToSession> {
+ if start_detached_and_exit {
+ start_server_detached(os_input, opts, config, config_options, info, layout);
+ return None;
+ }
info!("Starting Zellij client!");
let mut reconnect_to_session = None;
@@ -541,6 +547,69 @@ pub fn start_client(
reconnect_to_session
}
+pub fn start_server_detached(
+ mut os_input: Box<dyn ClientOsApi>,
+ opts: CliArgs,
+ config: Config,
+ config_options: Options,
+ info: ClientInfo,
+ layout: Option<Layout>,
+) {
+ envs::set_zellij("0".to_string());
+ config.env.set_vars();
+
+ let palette = config
+ .theme_config(&config_options)
+ .unwrap_or_else(|| os_input.load_palette());
+
+ let client_attributes = ClientAttributes {
+ size: Size { rows: 50, cols: 50 }, // just so size is not 0, it doesn't matter because we
+ // immediately detach
+ style: Style {
+ colors: palette,
+ rounded_corners: config.ui.pane_frames.rounded_corners,
+ hide_session_name: config.ui.pane_frames.hide_session_name,
+ },
+ keybinds: config.keybinds.clone(),
+ };
+
+ let create_ipc_pipe = || -> std::path::PathBuf {
+ let mut sock_dir = ZELLIJ_SOCK_DIR.clone();
+ std::fs::create_dir_all(&sock_dir).unwrap();
+ set_permissions(&sock_dir, 0o700).unwrap();
+ sock_dir.push(envs::get_session_name().unwrap());
+ sock_dir
+ };
+
+ let (first_msg, ipc_pipe) = match info {
+ ClientInfo::New(name) | ClientInfo::Resurrect(name, _) => {
+ envs::set_session_name(name.clone());
+ os_input.update_session_name(name);
+ let ipc_pipe = create_ipc_pipe();
+
+ spawn_server(&*ipc_pipe, opts.debug).unwrap();
+
+ (
+ ClientToServerMsg::NewClient(
+ client_attributes,
+ Box::new(opts),
+ Box::new(config_options.clone()),
+ Box::new(layout.unwrap()),
+ Box::new(config.plugins.clone()),
+ ),
+ ipc_pipe,
+ )
+ },
+ _ => {
+ eprintln!("Session already exists");
+ std::process::exit(1);
+ },
+ };
+
+ os_input.connect_to_server(&*ipc_pipe);
+ os_input.send_to_server(first_msg);
+}
+
#[cfg(test)]
#[path = "./unit/stdin_tests.rs"]
mod stdin_tests;
diff --git a/zellij-client/src/os_input_output.rs b/zellij-client/src/os_input_output.rs
index 1bfd6666a..47dcafb09 100644
--- a/zellij-client/src/os_input_output.rs
+++ b/zellij-client/src/os_input_output.rs
@@ -317,8 +317,8 @@ impl Clone for Box<dyn ClientOsApi> {
}
pub fn get_client_os_input() -> Result<ClientOsInputOutput, nix::Error> {
- let current_termios = termios::tcgetattr(0)?;
- let orig_termios = Some(Arc::new(Mutex::new(current_termios)));
+ let current_termios = termios::tcgetattr(0).ok();
+ let orig_termios = current_termios.map(|termios| Arc::new(Mutex::new(termios)));
let reading_from_stdin = Arc::new(Mutex::new(None));
Ok(ClientOsInputOutput {
orig_termios,
diff --git a/zellij-server/src/os_input_output.rs b/zellij-server/src/os_input_output.rs
index a2d9e36a8..640b6fbc9 100644
--- a/zellij-server/src/os_input_output.rs
+++ b/zellij-server/src/os_input_output.rs
@@ -221,7 +221,7 @@ fn handle_openpty(
fn handle_terminal(
cmd: RunCommand,
failover_cmd: Option<RunCommand>,
- orig_termios: termios::Termios,
+ orig_termios: Option<termios::Termios>,
quit_cb: Box<dyn Fn(PaneId, Option<i32>, RunCommand) + Send>,
terminal_id: u32,
) -> Result<(RawFd, RawFd)> {
@@ -229,7 +229,7 @@ fn handle_terminal(
// Create a pipe to allow the child the communicate the shell's pid to its
// parent.
- match openpty(None, Some(&orig_termios)) {
+ match openpty(None, &orig_termios) {
Ok(open_pty_res) => handle_openpty(open_pty_res, cmd, quit_cb, terminal_id),
Err(e) => match failover_cmd {
Some(failover_cmd) => {
@@ -279,7 +279,7 @@ fn separate_command_arguments(command: &mut PathBuf, args: &mut Vec<String>) {
/// set.
fn spawn_terminal(
terminal_action: TerminalAction,
- orig_termios: termios::Termios,
+ orig_termios: Option<termios::Termios>,
quit_cb: Box<dyn Fn(PaneId, Option<i32>, RunCommand) + Send>, // u32 is the exit_status
default_editor: Option<PathBuf>,
terminal_id: u32,
@@ -418,7 +418,7 @@ impl ClientSender {
#[derive(Clone)]
pub struct ServerOsInputOutput {
- orig_termios: Arc<Mutex<termios::Termios>>,
+ orig_termios: Arc<Mutex<Option<termios::Termios>>>,
client_senders: Arc<Mutex<HashMap<ClientId, ClientSender>>>,
terminal_id_to_raw_fd: Arc<Mutex<BTreeMap<u32, Option<RawFd>>>>, // A value of None means the
// terminal_id exists but is
@@ -876,7 +876,10 @@ impl Clone for Box<dyn ServerOsApi> {
}
pub fn get_server_os_input() -> Result<ServerOsInputOutput, nix::Error> {
- let current_termios = termios::tcgetattr(0)?;
+ let current_termios = termios::tcgetattr(0).ok();
+ if current_termios.is_none() {
+ log::warn!("Starting a server without a controlling terminal, using the default termios configuration.");
+ }
let orig_termios = Arc::new(Mutex::new(current_termios));
Ok(ServerOsInputOutput {
orig_termios,
diff --git a/zellij-server/src/unit/os_input_output_tests.rs b/zellij-server/src/unit/os_input_output_tests.rs
index 7e6510835..31892525d 100644
--- a/zellij-server/src/unit/os_input_output_tests.rs
+++ b/zellij-server/src/unit/os_input_output_tests.rs
@@ -36,7 +36,7 @@ fn get_cwd() {
termios::tcgetattr(test_terminal.slave()).expect("Could not configure the termios");
let server = ServerOsInputOutput {
- orig_termios: Arc::new(Mutex::new(test_termios)),
+ orig_termios: Arc::new(Mutex::new(Some(test_termios))),
client_senders: Arc::default(),
terminal_id_to_raw_fd: Arc::default(),
cached_resizes: Arc::default(),
diff --git a/zellij-utils/src/cli.rs b/zellij-utils/src/cli.rs
index 0388e5999..ba1898439 100644
--- a/zellij-utils/src/cli.rs
+++ b/zellij-utils/src/cli.rs
@@ -125,6 +125,10 @@ pub enum Sessions {
#[clap(short, long, value_parser)]
create: bool,
+ /// Create a detached session in the background if one does not exist
+ #[clap(short, long, value_parser)]
+ background: bool,
+
/// Number of the session index in the active sessions ordered creation date.
#[clap(long, value_parser)]
index: Option<usize>,