mirror of
https://github.com/wez/wezterm.git
synced 2024-11-25 21:07:39 +03:00
lua: patch os.getenv with our own function
ad9490ee8f
unset SHELL from the
environment on startup which had the consequence of causing
`os.getenv("SHELL")` to return `nil` when used in the config file.
Rather than simply restoring SHELL env var, recognize that reading
the environment from a long lived process is prone to seeing
stable environment forever.
We already compensate for this in the pty crate's understanding
of the base environment, so this commit patches `os.getenv`
and replaces it with our own imlementation that uses that same
logic.
The base environment logic has been extended to set SHELL from
the passwd database to round things out.
refs: https://github.com/wez/wezterm/discussions/2481
This commit is contained in:
parent
62ca174d19
commit
7158c435d5
@ -7,6 +7,7 @@ use anyhow::anyhow;
|
||||
use luahelper::{from_lua_value_dynamic, lua_value_to_dynamic};
|
||||
use mlua::{FromLua, Lua, Table, ToLuaMulti, Value, Variadic};
|
||||
use ordered_float::NotNan;
|
||||
use portable_pty::CommandBuilder;
|
||||
use std::convert::TryFrom;
|
||||
use std::path::Path;
|
||||
use std::sync::Mutex;
|
||||
@ -207,6 +208,12 @@ end
|
||||
wezterm_mod.set("shell_quote_arg", lua.create_function(shell_quote_arg)?)?;
|
||||
wezterm_mod.set("shell_split", lua.create_function(shell_split)?)?;
|
||||
|
||||
// Define our own os.getenv function that knows how to resolve current
|
||||
// environment values from eg: the registry on Windows, or for
|
||||
// the current SHELL value on unix, even if the user has changed
|
||||
// those values since wezterm was started
|
||||
get_or_create_module(&lua, "os")?.set("getenv", lua.create_function(getenv)?)?;
|
||||
|
||||
package.set("path", path_array.join(";"))?;
|
||||
}
|
||||
|
||||
@ -217,6 +224,23 @@ end
|
||||
Ok(lua)
|
||||
}
|
||||
|
||||
/// Resolve an environment variable.
|
||||
/// Lean on CommandBuilder's ability to update to current values of certain
|
||||
/// environment variables that may be adjusted via the registry or implicitly
|
||||
/// via eg: chsh (SHELL).
|
||||
fn getenv<'lua>(_: &'lua Lua, env: String) -> mlua::Result<Option<String>> {
|
||||
let cmd = CommandBuilder::new_default_prog();
|
||||
match cmd.get_env(&env) {
|
||||
Some(s) => match s.to_str() {
|
||||
Some(s) => Ok(Some(s.to_string())),
|
||||
None => Err(mlua::Error::external(format!(
|
||||
"env var {env} is not representable as UTF-8"
|
||||
))),
|
||||
},
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn shell_split<'lua>(_: &'lua Lua, line: String) -> mlua::Result<Vec<String>> {
|
||||
shlex::split(&line).ok_or_else(|| {
|
||||
mlua::Error::external(format!("cannot tokenize `{line}` using posix shell rules"))
|
||||
|
@ -15,6 +15,9 @@ As features stabilize some brief notes about them will accumulate here.
|
||||
* fontconfig: when locating a fallback font for a given codepoint, allow
|
||||
matching non-monospace fonts if we can't find any matching monospace fonts.
|
||||
[#2468](https://github.com/wez/wezterm/discussions/2468)
|
||||
* `os.getenv` now knows how to resolve environment variables that would normally
|
||||
require logging out to update, such as `SHELL` (if you `chsh` on unix systeams),
|
||||
or those set through the registry on Windows. [#2481](https://github.com/wez/wezterm/discussions/2481)
|
||||
|
||||
### 20220904-064125-9a6cee2b
|
||||
|
||||
|
@ -36,8 +36,40 @@ impl EnvEntry {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn get_shell() -> String {
|
||||
use nix::unistd::{access, AccessFlags};
|
||||
use std::ffi::CStr;
|
||||
use std::path::Path;
|
||||
use std::str;
|
||||
|
||||
let ent = unsafe { libc::getpwuid(libc::getuid()) };
|
||||
if !ent.is_null() {
|
||||
let shell = unsafe { CStr::from_ptr((*ent).pw_shell) };
|
||||
match shell.to_str().map(str::to_owned) {
|
||||
Err(err) => {
|
||||
log::warn!(
|
||||
"passwd database shell could not be \
|
||||
represented as utf-8: {err:#}, \
|
||||
falling back to /bin/sh"
|
||||
);
|
||||
}
|
||||
Ok(shell) => {
|
||||
if let Err(err) = access(Path::new(&shell), AccessFlags::X_OK) {
|
||||
log::warn!(
|
||||
"passwd database shell={shell:?} which is \
|
||||
not executable ({err:#}), falling back to /bin/sh"
|
||||
);
|
||||
} else {
|
||||
return shell;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"/bin/sh".into()
|
||||
}
|
||||
|
||||
fn get_base_env() -> BTreeMap<OsString, EnvEntry> {
|
||||
#[allow(unused_mut)]
|
||||
let mut env: BTreeMap<OsString, EnvEntry> = std::env::vars_os()
|
||||
.map(|(key, value)| {
|
||||
(
|
||||
@ -51,6 +83,18 @@ fn get_base_env() -> BTreeMap<OsString, EnvEntry> {
|
||||
})
|
||||
.collect();
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
env.insert(
|
||||
EnvEntry::map_key("SHELL".into()),
|
||||
EnvEntry {
|
||||
is_from_base_env: true,
|
||||
preferred_key: "SHELL".into(),
|
||||
value: get_shell().into(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use std::os::windows::ffi::OsStringExt;
|
||||
@ -275,7 +319,7 @@ impl CommandBuilder {
|
||||
self.envs.clear();
|
||||
}
|
||||
|
||||
fn get_env<K>(&self, key: K) -> Option<&OsStr>
|
||||
pub fn get_env<K>(&self, key: K) -> Option<&OsStr>
|
||||
where
|
||||
K: AsRef<OsStr>,
|
||||
{
|
||||
@ -450,9 +494,6 @@ impl CommandBuilder {
|
||||
/// fall back to looking it up from the password database.
|
||||
pub fn get_shell(&self) -> String {
|
||||
use nix::unistd::{access, AccessFlags};
|
||||
use std::ffi::CStr;
|
||||
use std::path::Path;
|
||||
use std::str;
|
||||
|
||||
if let Some(shell) = self.get_env("SHELL").and_then(OsStr::to_str) {
|
||||
match access(shell, AccessFlags::X_OK) {
|
||||
@ -464,30 +505,7 @@ impl CommandBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
let ent = unsafe { libc::getpwuid(libc::getuid()) };
|
||||
if !ent.is_null() {
|
||||
let shell = unsafe { CStr::from_ptr((*ent).pw_shell) };
|
||||
match shell.to_str().map(str::to_owned) {
|
||||
Err(err) => {
|
||||
log::warn!(
|
||||
"passwd database shell could not be \
|
||||
represented as utf-8: {err:#}, \
|
||||
falling back to /bin/sh"
|
||||
);
|
||||
}
|
||||
Ok(shell) => {
|
||||
if let Err(err) = access(Path::new(&shell), AccessFlags::X_OK) {
|
||||
log::warn!(
|
||||
"passwd database shell={shell:?} which is \
|
||||
not executable ({err:#}), falling back to /bin/sh"
|
||||
);
|
||||
} else {
|
||||
return shell;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"/bin/sh".into()
|
||||
get_shell().into()
|
||||
}
|
||||
|
||||
fn get_home_dir(&self) -> anyhow::Result<String> {
|
||||
|
Loading…
Reference in New Issue
Block a user