Register Alacritty shells as tty sessions on macOS
Unless the `shell` config is specified, launch the user's shell with: ```sh login -flp $USER /bin/sh -c "exec -a -shell /path/to/shell" ``` On macOS, just running a shell prefixed by `-` is not sufficient to be registered as a login session for things like `w` and `logname`. However, using the `login` command changes the directory to `$HOME` before running the program by default, which is not desired. The `-l` flag disables this behavior, but also skips prepending `-` to the executed program, so shells will not run as login shells. Instead we just do this part ourselves with `exec -a`. The result is login shells that run in the intended directory and are registered as tty sessions. Fixes #3420.
This commit is contained in:
parent
ed4614d0bd
commit
c3f3bd3666
|
@ -53,6 +53,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Windows `Open Alacritty Here` on root of drive displaying error
|
||||
- On macOS, `font.use_thin_strokes` did not work since Big Sur
|
||||
- On macOS, trying to load a disabled font would crash
|
||||
- On macOS, Alacritty sessions did not appear in the list of tty sessions for `w` and `who`
|
||||
|
||||
### Removed
|
||||
|
||||
|
|
|
@ -434,8 +434,7 @@
|
|||
# shell.
|
||||
#
|
||||
# Default:
|
||||
# - (macOS) /bin/bash --login
|
||||
# - (Linux/BSD) user login shell
|
||||
# - (Linux/BSD/macOS) `$SHELL` or the user's login shell, if `$SHELL` is unset
|
||||
# - (Windows) powershell
|
||||
#shell:
|
||||
# program: /bin/bash
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
//! TTY related functionality.
|
||||
|
||||
use std::borrow::Cow;
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
use std::env;
|
||||
use std::ffi::CStr;
|
||||
use std::fs::File;
|
||||
use std::io::{Error, ErrorKind, Result};
|
||||
|
@ -10,7 +7,7 @@ use std::mem::MaybeUninit;
|
|||
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||
use std::os::unix::process::CommandExt;
|
||||
use std::process::{Child, Command, Stdio};
|
||||
use std::ptr;
|
||||
use std::{env, ptr};
|
||||
|
||||
use libc::{self, c_int, winsize, TIOCSCTTY};
|
||||
use log::error;
|
||||
|
@ -21,7 +18,7 @@ use nix::sys::termios::{self, InputFlags, SetArg};
|
|||
use signal_hook::consts as sigconsts;
|
||||
use signal_hook_mio::v0_6::Signals;
|
||||
|
||||
use crate::config::{Program, PtyConfig};
|
||||
use crate::config::PtyConfig;
|
||||
use crate::event::{OnResize, WindowSize};
|
||||
use crate::tty::{ChildEvent, EventedPty, EventedReadWrite};
|
||||
|
||||
|
@ -121,17 +118,33 @@ impl Pty {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn default_shell(pw: &Passwd<'_>) -> Program {
|
||||
let shell_name = pw.shell.rsplit('/').next().unwrap();
|
||||
let argv = vec![String::from("-c"), format!("exec -a -{} {}", shell_name, pw.shell)];
|
||||
|
||||
Program::WithArgs { program: "/bin/bash".to_owned(), args: argv }
|
||||
/// Look for a shell in the `$SHELL` environment variable, then in `passwd`.
|
||||
fn default_shell(pw: &Passwd<'_>) -> String {
|
||||
env::var("SHELL").unwrap_or_else(|_| pw.shell.to_owned())
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
fn default_shell(pw: &Passwd<'_>) -> Program {
|
||||
Program::Just(env::var("SHELL").unwrap_or_else(|_| pw.shell.to_owned()))
|
||||
fn default_shell_command(pw: &Passwd<'_>) -> Command {
|
||||
Command::new(default_shell(pw))
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn default_shell_command(pw: &Passwd<'_>) -> Command {
|
||||
let shell = default_shell(pw);
|
||||
let shell_name = shell.rsplit('/').next().unwrap();
|
||||
|
||||
// On macOS, use the `login` command so the shell will appear as a tty session.
|
||||
let mut login_command = Command::new("/usr/bin/login");
|
||||
|
||||
// Exec the shell with argv[0] prepended by '-' so it becomes a login shell.
|
||||
// `login` normally does this itself, but `-l` disables this.
|
||||
let exec = format!("exec -a -{} {}", shell_name, shell);
|
||||
|
||||
// -f: Bypasses authentication for the already-logged-in user.
|
||||
// -l: Skips changing directory to $HOME and prepending '-' to argv[0].
|
||||
// -p: Preserves the environment.
|
||||
login_command.args(["-flp", pw.name, "/bin/sh", "-c", &exec]);
|
||||
login_command
|
||||
}
|
||||
|
||||
/// Create a new TTY and return a handle to interact with it.
|
||||
|
@ -148,16 +161,14 @@ pub fn new(config: &PtyConfig, window_size: WindowSize, window_id: Option<usize>
|
|||
let mut buf = [0; 1024];
|
||||
let pw = get_pw_entry(&mut buf)?;
|
||||
|
||||
let shell = match config.shell.as_ref() {
|
||||
Some(shell) => Cow::Borrowed(shell),
|
||||
None => Cow::Owned(default_shell(&pw)),
|
||||
let mut builder = if let Some(shell) = config.shell.as_ref() {
|
||||
let mut cmd = Command::new(shell.program());
|
||||
cmd.args(shell.args());
|
||||
cmd
|
||||
} else {
|
||||
default_shell_command(&pw)
|
||||
};
|
||||
|
||||
let mut builder = Command::new(shell.program());
|
||||
for arg in shell.args() {
|
||||
builder.arg(arg);
|
||||
}
|
||||
|
||||
// Setup child stdin/stdout/stderr as slave fd of PTY.
|
||||
// Ownership of fd is transferred to the Stdio structs and will be closed by them at the end of
|
||||
// this scope. (It is not an issue that the fd is closed three times since File::drop ignores
|
||||
|
@ -171,10 +182,6 @@ pub fn new(config: &PtyConfig, window_size: WindowSize, window_id: Option<usize>
|
|||
builder.env("USER", pw.name);
|
||||
builder.env("HOME", pw.dir);
|
||||
|
||||
// Set $SHELL environment variable on macOS, since login does not do it for us.
|
||||
#[cfg(target_os = "macos")]
|
||||
builder.env("SHELL", config.shell.as_ref().map_or(pw.shell, Program::program));
|
||||
|
||||
if let Some(window_id) = window_id {
|
||||
builder.env("WINDOWID", format!("{}", window_id));
|
||||
}
|
||||
|
@ -210,7 +217,7 @@ pub fn new(config: &PtyConfig, window_size: WindowSize, window_id: Option<usize>
|
|||
}
|
||||
|
||||
// Prepare signal handling before spawning child.
|
||||
let signals = Signals::new(&[sigconsts::SIGCHLD]).expect("error preparing signal handling");
|
||||
let signals = Signals::new([sigconsts::SIGCHLD]).expect("error preparing signal handling");
|
||||
|
||||
match builder.spawn() {
|
||||
Ok(child) => {
|
||||
|
@ -231,8 +238,12 @@ pub fn new(config: &PtyConfig, window_size: WindowSize, window_id: Option<usize>
|
|||
Ok(pty)
|
||||
},
|
||||
Err(err) => Err(Error::new(
|
||||
ErrorKind::NotFound,
|
||||
format!("Failed to spawn command '{}': {}", shell.program(), err),
|
||||
err.kind(),
|
||||
format!(
|
||||
"Failed to spawn command '{}': {}",
|
||||
builder.get_program().to_string_lossy(),
|
||||
err
|
||||
),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue