Skip to content

Commit

Permalink
Remove tty-driver code and rename cwd to startup_cwd, to be prepared …
Browse files Browse the repository at this point in the history
…for a potential current_cwd
  • Loading branch information
msirringhaus committed Jan 7, 2025
1 parent 8070335 commit 92f6aa4
Show file tree
Hide file tree
Showing 12 changed files with 20 additions and 197 deletions.
169 changes: 1 addition & 168 deletions zellij-server/src/pty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,6 @@ use crate::{
ClientId, ServerInstruction,
};
use async_std::task::{self, JoinHandle};
#[cfg(target_os = "linux")]
use std::ops::RangeInclusive;
#[cfg(target_os = "linux")]
use std::os::unix::fs::MetadataExt;
use std::path::Path;
use std::sync::Arc;
use std::{collections::HashMap, os::unix::io::RawFd, path::PathBuf};
use zellij_utils::data::PaneManifest;
Expand Down Expand Up @@ -800,7 +795,7 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
for pane_info in infos.as_mut_slice() {
let pid = pty.get_child_pid(pane_info.id);
pane_info.pid = pid;
pane_info.cwd = pty
pane_info.startup_cwd = pty
.bus
.os_input
.as_ref()
Expand All @@ -810,15 +805,6 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
.map(|x| x.to_string_lossy().to_string())
})
.unwrap_or_default();

// Currently, only available on Linux, as it
// depends on the /proc-filesystem
#[cfg(target_os = "linux")]
{
// We can't send PathBufs via protobuf, so we have to convert to String
pane_info.tty = TtyDriver::find_tty_for_pid(pid)
.map(|x| x.to_string_lossy().to_string());
}
}
}
pty.bus
Expand Down Expand Up @@ -1653,156 +1639,3 @@ pub fn get_default_shell() -> PathBuf {
"/bin/sh".to_string()
}))
}

#[cfg(target_os = "linux")]
#[derive(Debug, Clone)]
struct TtyDriver {
path: PathBuf,
major: i32,
minor_range: RangeInclusive<i32>,
}

#[cfg(target_os = "linux")]
impl TtyDriver {
/// Trys to find the TTY for a given process ID.
/// This is unfortunately not straight forward. We have to do:
/// 1. Read the tty_nr from /proc/<PID>/stat and do some bit-magic to get major and minor
/// 2. Read /proc/tty/drivers to see which path corresponds to which major number and minor range
/// 3. Match those 2 together and find a fitting driver
/// 4. 'Guess' the resulting path (e.g. either /dev/tty/2 or /dev/tty2)
/// 5. Verify the guess is correct by stat-ing the result and comparing major and minor
pub(crate) fn find_tty_for_pid(pid: i32) -> Option<PathBuf> {
// 1. Parse major and minor tty_nr
let (tty_major, tty_minor) = TtyDriver::get_tty_nr_for_pid(pid)?;
// 2. Parse /proc/tty/drivers
let drivers = TtyDriver::parse_tty_drivers();
// 3. Find a match
let driver = TtyDriver::match_drivers_to_tty_nr(drivers, tty_major, tty_minor)?;
// 4. and 5. Guess and verify path
let path = TtyDriver::guess_tty_path(&driver.path, tty_major, tty_minor)?;
Some(path)
}

fn verify_tty_path(path: &Path, tty_major: i32, tty_minor: i32) -> bool {
if let Ok(metadata) = path.metadata() {
let rdev = metadata.rdev() as i32;
let dev_major = rdev >> 8;
let dev_minor = rdev & 0xff;
if dev_major == tty_major && dev_minor == tty_minor {
return true;
}
}
false
}

fn guess_tty_path(path: &Path, tty_major: i32, tty_minor: i32) -> Option<PathBuf> {
// First, guess seperated by slash: (e.g. /dev/tty/2)
let mut res = path.join(format!("{tty_minor}"));
if res.exists() && TtyDriver::verify_tty_path(&res, tty_major, tty_minor) {
return Some(res);
}

// Otherwise, guess seperated by directly appending the number: (e.g. /dev/tty2)
let mut second_try = path.as_os_str().to_os_string();
second_try.push(format!("{tty_minor}"));
res = PathBuf::from(second_try);
if res.exists() && TtyDriver::verify_tty_path(&res, tty_major, tty_minor) {
return Some(res);
}

// No luck
None
}

fn match_drivers_to_tty_nr(
drivers: Vec<TtyDriver>,
tty_major: i32,
tty_minor: i32,
) -> Option<TtyDriver> {
drivers
.into_iter()
.find(|driver| driver.major == tty_major && driver.minor_range.contains(&tty_minor))
}

fn parse_tty_drivers() -> Vec<TtyDriver> {
let mut drivers = Vec::new();

// example output:
// /dev/tty /dev/tty 5 0 system:/dev/tty
// /dev/console /dev/console 5 1 system:console
// /dev/ptmx /dev/ptmx 5 2 system
// /dev/vc/0 /dev/vc/0 4 0 system:vtmaster
// rfcomm /dev/rfcomm 216 0-255 serial
// serial /dev/ttyS 4 64-95 serial
// pty_slave /dev/pts 136 0-1048575 pty:slave
// pty_master /dev/ptm 128 0-1048575 pty:master
// unknown /dev/tty 4 1-63 console
let drivers_raw = match std::fs::read_to_string(PathBuf::from("/proc/tty/drivers")) {
Ok(x) => x,
Err(_) => {
return drivers;
},
};

for line in drivers_raw.lines() {
let parts: Vec<_> = line.split_whitespace().collect();
if parts.len() < 4 {
// Something is wrong. Silently ignore this entry
continue;
}
let path = PathBuf::from(parts[1]);
let major = match parts[2].parse::<i32>() {
Ok(maj) => maj,
Err(_) => continue,
};
let tty_minor = parts[3];
let minor_range = match TtyDriver::parse_minor_range(tty_minor) {
Some(x) => x,
None => {
continue;
},
};
let driver = TtyDriver {
path,
major,
minor_range,
};
drivers.push(driver);
}
drivers
}

// Getting either "3" or "3-10" and parsing a Range from that
fn parse_minor_range(tty_minor: &str) -> Option<RangeInclusive<i32>> {
let minor_range: Vec<_> = tty_minor.split('-').collect();
if minor_range.len() == 1 {
let start = minor_range[0].parse::<i32>().ok()?;
Some(start..=start)
} else if minor_range.len() == 2 {
let start = minor_range[0].parse::<i32>().ok()?;
let end = minor_range[1].parse::<i32>().ok()?;
Some(start..=end)
} else {
None
}
}

fn get_tty_nr_for_pid(pid: i32) -> Option<(i32, i32)> {
if pid == -1 {
return None;
}
let stat = std::fs::read_to_string(PathBuf::from(format!("/proc/{pid}/stat"))).ok()?;

let tty_nr = stat
.split_whitespace()
.nth(6)
.and_then(|s| s.parse::<i32>().ok())?;
// from /usr/include/linux/kdev_t.h
// #define MAJOR(dev) ((dev)>>8)
// #define MINOR(dev) ((dev) & 0xff)
let tty_major = tty_nr >> 8;
let tty_minor = tty_nr & 0xff;

Some((tty_major, tty_minor))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ source: zellij-server/src/./unit/screen_tests.rs
expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())"
snapshot_kind: text
---
[SpawnTerminal(Some(OpenFile(OpenFilePayload { path: "/file/to/edit", line_number: None, cwd: Some("."), originating_plugin: None })), Some(false), Some("Editing: /file/to/edit"), None, false, ClientId(10)), UpdatePaneInfo(PaneManifest { panes: {0: [PaneInfo { id: 0, is_plugin: false, is_focused: true, is_fullscreen: false, is_floating: false, is_suppressed: false, title: "Pane #1", exited: false, exit_status: None, is_held: false, pane_x: 0, pane_content_x: 1, pane_y: 0, pane_content_y: 1, pane_rows: 20, pane_content_rows: 18, pane_columns: 61, pane_content_columns: 59, cursor_coordinates_in_pane: Some((1, 1)), terminal_command: None, plugin_url: None, is_selectable: true, pid: 0, cwd: None, tty: None }, PaneInfo { id: 1, is_plugin: false, is_focused: false, is_fullscreen: false, is_floating: false, is_suppressed: false, title: "Pane #2", exited: false, exit_status: None, is_held: false, pane_x: 61, pane_content_x: 62, pane_y: 0, pane_content_y: 1, pane_rows: 20, pane_content_rows: 18, pane_columns: 60, pane_content_columns: 58, cursor_coordinates_in_pane: Some((1, 1)), terminal_command: None, plugin_url: None, is_selectable: true, pid: 0, cwd: None, tty: None }]} }), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit]
[SpawnTerminal(Some(OpenFile(OpenFilePayload { path: "/file/to/edit", line_number: None, cwd: Some("."), originating_plugin: None })), Some(false), Some("Editing: /file/to/edit"), None, false, ClientId(10)), UpdatePaneInfo(PaneManifest { panes: {0: [PaneInfo { id: 0, is_plugin: false, is_focused: true, is_fullscreen: false, is_floating: false, is_suppressed: false, title: "Pane #1", exited: false, exit_status: None, is_held: false, pane_x: 0, pane_content_x: 1, pane_y: 0, pane_content_y: 1, pane_rows: 20, pane_content_rows: 18, pane_columns: 61, pane_content_columns: 59, cursor_coordinates_in_pane: Some((1, 1)), terminal_command: None, plugin_url: None, is_selectable: true, pid: 0, startup_cwd: None }, PaneInfo { id: 1, is_plugin: false, is_focused: false, is_fullscreen: false, is_floating: false, is_suppressed: false, title: "Pane #2", exited: false, exit_status: None, is_held: false, pane_x: 61, pane_content_x: 62, pane_y: 0, pane_content_y: 1, pane_rows: 20, pane_content_rows: 18, pane_columns: 60, pane_content_columns: 58, cursor_coordinates_in_pane: Some((1, 1)), terminal_command: None, plugin_url: None, is_selectable: true, pid: 0, startup_cwd: None }]} }), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit]
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ source: zellij-server/src/./unit/screen_tests.rs
expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())"
snapshot_kind: text
---
[SpawnTerminal(Some(OpenFile(OpenFilePayload { path: "/file/to/edit", line_number: Some(100), cwd: Some("."), originating_plugin: None })), Some(false), Some("Editing: /file/to/edit"), None, false, ClientId(10)), UpdatePaneInfo(PaneManifest { panes: {0: [PaneInfo { id: 0, is_plugin: false, is_focused: true, is_fullscreen: false, is_floating: false, is_suppressed: false, title: "Pane #1", exited: false, exit_status: None, is_held: false, pane_x: 0, pane_content_x: 1, pane_y: 0, pane_content_y: 1, pane_rows: 20, pane_content_rows: 18, pane_columns: 61, pane_content_columns: 59, cursor_coordinates_in_pane: Some((1, 1)), terminal_command: None, plugin_url: None, is_selectable: true, pid: 0, cwd: None, tty: None }, PaneInfo { id: 1, is_plugin: false, is_focused: false, is_fullscreen: false, is_floating: false, is_suppressed: false, title: "Pane #2", exited: false, exit_status: None, is_held: false, pane_x: 61, pane_content_x: 62, pane_y: 0, pane_content_y: 1, pane_rows: 20, pane_content_rows: 18, pane_columns: 60, pane_content_columns: 58, cursor_coordinates_in_pane: Some((1, 1)), terminal_command: None, plugin_url: None, is_selectable: true, pid: 0, cwd: None, tty: None }]} }), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit]
[SpawnTerminal(Some(OpenFile(OpenFilePayload { path: "/file/to/edit", line_number: Some(100), cwd: Some("."), originating_plugin: None })), Some(false), Some("Editing: /file/to/edit"), None, false, ClientId(10)), UpdatePaneInfo(PaneManifest { panes: {0: [PaneInfo { id: 0, is_plugin: false, is_focused: true, is_fullscreen: false, is_floating: false, is_suppressed: false, title: "Pane #1", exited: false, exit_status: None, is_held: false, pane_x: 0, pane_content_x: 1, pane_y: 0, pane_content_y: 1, pane_rows: 20, pane_content_rows: 18, pane_columns: 61, pane_content_columns: 59, cursor_coordinates_in_pane: Some((1, 1)), terminal_command: None, plugin_url: None, is_selectable: true, pid: 0, startup_cwd: None }, PaneInfo { id: 1, is_plugin: false, is_focused: false, is_fullscreen: false, is_floating: false, is_suppressed: false, title: "Pane #2", exited: false, exit_status: None, is_held: false, pane_x: 61, pane_content_x: 62, pane_y: 0, pane_content_y: 1, pane_rows: 20, pane_content_rows: 18, pane_columns: 60, pane_content_columns: 58, cursor_coordinates_in_pane: Some((1, 1)), terminal_command: None, plugin_url: None, is_selectable: true, pid: 0, startup_cwd: None }]} }), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit]
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ source: zellij-server/src/./unit/screen_tests.rs
expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())"
snapshot_kind: text
---
[SpawnTerminalHorizontally(Some(OpenFile(OpenFilePayload { path: "/file/to/edit", line_number: None, cwd: Some("."), originating_plugin: None })), Some("Editing: /file/to/edit"), 10), UpdatePaneInfo(PaneManifest { panes: {0: [PaneInfo { id: 0, is_plugin: false, is_focused: true, is_fullscreen: false, is_floating: false, is_suppressed: false, title: "Pane #1", exited: false, exit_status: None, is_held: false, pane_x: 0, pane_content_x: 1, pane_y: 0, pane_content_y: 1, pane_rows: 20, pane_content_rows: 18, pane_columns: 61, pane_content_columns: 59, cursor_coordinates_in_pane: Some((1, 1)), terminal_command: None, plugin_url: None, is_selectable: true, pid: 0, cwd: None, tty: None }, PaneInfo { id: 1, is_plugin: false, is_focused: false, is_fullscreen: false, is_floating: false, is_suppressed: false, title: "Pane #2", exited: false, exit_status: None, is_held: false, pane_x: 61, pane_content_x: 62, pane_y: 0, pane_content_y: 1, pane_rows: 20, pane_content_rows: 18, pane_columns: 60, pane_content_columns: 58, cursor_coordinates_in_pane: Some((1, 1)), terminal_command: None, plugin_url: None, is_selectable: true, pid: 0, cwd: None, tty: None }]} }), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit]
[SpawnTerminalHorizontally(Some(OpenFile(OpenFilePayload { path: "/file/to/edit", line_number: None, cwd: Some("."), originating_plugin: None })), Some("Editing: /file/to/edit"), 10), UpdatePaneInfo(PaneManifest { panes: {0: [PaneInfo { id: 0, is_plugin: false, is_focused: true, is_fullscreen: false, is_floating: false, is_suppressed: false, title: "Pane #1", exited: false, exit_status: None, is_held: false, pane_x: 0, pane_content_x: 1, pane_y: 0, pane_content_y: 1, pane_rows: 20, pane_content_rows: 18, pane_columns: 61, pane_content_columns: 59, cursor_coordinates_in_pane: Some((1, 1)), terminal_command: None, plugin_url: None, is_selectable: true, pid: 0, startup_cwd: None }, PaneInfo { id: 1, is_plugin: false, is_focused: false, is_fullscreen: false, is_floating: false, is_suppressed: false, title: "Pane #2", exited: false, exit_status: None, is_held: false, pane_x: 61, pane_content_x: 62, pane_y: 0, pane_content_y: 1, pane_rows: 20, pane_content_rows: 18, pane_columns: 60, pane_content_columns: 58, cursor_coordinates_in_pane: Some((1, 1)), terminal_command: None, plugin_url: None, is_selectable: true, pid: 0, startup_cwd: None }]} }), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit]
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ source: zellij-server/src/./unit/screen_tests.rs
expression: "format!(\"{:?}\", * received_pty_instructions.lock().unwrap())"
snapshot_kind: text
---
[SpawnTerminal(None, Some(false), None, None, false, ClientId(10)), UpdatePaneInfo(PaneManifest { panes: {0: [PaneInfo { id: 0, is_plugin: false, is_focused: true, is_fullscreen: false, is_floating: false, is_suppressed: false, title: "Pane #1", exited: false, exit_status: None, is_held: false, pane_x: 0, pane_content_x: 1, pane_y: 0, pane_content_y: 1, pane_rows: 20, pane_content_rows: 18, pane_columns: 61, pane_content_columns: 59, cursor_coordinates_in_pane: Some((1, 1)), terminal_command: None, plugin_url: None, is_selectable: true, pid: 0, cwd: None, tty: None }, PaneInfo { id: 1, is_plugin: false, is_focused: false, is_fullscreen: false, is_floating: false, is_suppressed: false, title: "Pane #2", exited: false, exit_status: None, is_held: false, pane_x: 61, pane_content_x: 62, pane_y: 0, pane_content_y: 1, pane_rows: 20, pane_content_rows: 18, pane_columns: 60, pane_content_columns: 58, cursor_coordinates_in_pane: Some((1, 1)), terminal_command: None, plugin_url: None, is_selectable: true, pid: 0, cwd: None, tty: None }]} }), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit]
[SpawnTerminal(None, Some(false), None, None, false, ClientId(10)), UpdatePaneInfo(PaneManifest { panes: {0: [PaneInfo { id: 0, is_plugin: false, is_focused: true, is_fullscreen: false, is_floating: false, is_suppressed: false, title: "Pane #1", exited: false, exit_status: None, is_held: false, pane_x: 0, pane_content_x: 1, pane_y: 0, pane_content_y: 1, pane_rows: 20, pane_content_rows: 18, pane_columns: 61, pane_content_columns: 59, cursor_coordinates_in_pane: Some((1, 1)), terminal_command: None, plugin_url: None, is_selectable: true, pid: 0, startup_cwd: None }, PaneInfo { id: 1, is_plugin: false, is_focused: false, is_fullscreen: false, is_floating: false, is_suppressed: false, title: "Pane #2", exited: false, exit_status: None, is_held: false, pane_x: 61, pane_content_x: 62, pane_y: 0, pane_content_y: 1, pane_rows: 20, pane_content_rows: 18, pane_columns: 60, pane_content_columns: 58, cursor_coordinates_in_pane: Some((1, 1)), terminal_command: None, plugin_url: None, is_selectable: true, pid: 0, startup_cwd: None }]} }), UpdateActivePane(Some(Terminal(0)), 1), UpdateActivePane(Some(Terminal(0)), 1), Exit]
Loading

0 comments on commit 92f6aa4

Please sign in to comment.