1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-22 21:01:36 +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:
Wez Furlong 2022-09-04 15:39:28 -07:00
parent 62ca174d19
commit 7158c435d5
3 changed files with 74 additions and 29 deletions

View File

@ -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"))

View File

@ -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

View File

@ -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> {