Skip to content

Commit 765bac9

Browse files
authored
fix: Inherit the login shell variable from the user session (#16)
Closes #15 This fixes the issue where the subshell's `SHELL` environment variable changes once already in a nix-shell by always getting the user's preferred shell from their UID.
1 parent ea3b3c2 commit 765bac9

7 files changed

Lines changed: 45 additions & 16 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ nix run github:eureka-cpu/ns -- <URI>#<ATTR>
3636
Add or remove it from your user profile:
3737

3838
```sh
39-
nix profile install github:eureka-cpu/ns
39+
nix profile add github:eureka-cpu/ns
4040
nix profile remove ns
4141
```
4242

dune-project

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
(name ns)
44

5-
(version 0.4.2)
5+
(version 0.4.3)
66

77
(generate_opam_files true)
88

lib/cmd.ml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ module Cmd = struct
1919
Bos.Cmd.v "nix"
2020
|>+ [ "develop" ] @ [ Uri.sprintf_uri_attr_opt entrypoint attribute ]
2121
|>+ Option.value ~default:[] force_experimental_features
22-
|>+ [ "--command"; Unix.shell ]
22+
|>+ [ "--command"; Unix.shell () ]
2323
;;
2424

2525
let legacy_nix_shell_from_entrypoint entrypoint attribute =
2626
Bos.Cmd.v "nix-shell"
2727
|>+ Option.value ~default:[] (Option.map (fun attr -> [ "--attr"; attr ]) attribute)
2828
|>+ [ entrypoint ]
29-
|>+ [ "--command"; Unix.shell ]
29+
|>+ [ "--command"; Unix.shell () ]
3030
;;
3131

3232
let nix_shell installables force_experimental_features =
@@ -40,7 +40,7 @@ module Cmd = struct
4040
(* Need to combine/validate that the installables given are all Nixpkgs *)
4141
Bos.Cmd.v "nix-shell"
4242
|>+ [ "--packages" ] @ installables
43-
|>+ [ "--command"; Unix.shell ]
43+
|>+ [ "--command"; Unix.shell () ]
4444
;;
4545

4646
let print_strategy ({ workdir; primary; fallback } : strategy) =

lib/util.ml

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,8 @@ module Util = struct
2222
Error.handle_ns_error "%s" (Error.sprintf_unix_error e path)
2323
;;
2424

25-
(** Get the user's shell from the environment *)
26-
let shell =
27-
match Sys.getenv_opt "SHELL" with
28-
| Some s -> s
29-
| None -> "/bin/sh"
30-
;;
25+
(** Get the user's login shell *)
26+
let shell () = (Unix.getpwuid (Unix.getuid ())).Unix.pw_shell
3127

3228
(** Whether a flake.nix file exists at the given directory *)
3329
let flake_exists_at dir = Sys.file_exists (Filename.concat dir "flake.nix")

ns.opam

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# This file is generated by dune, edit dune-project instead
22
opam-version: "2.0"
3-
version: "0.4.2"
3+
version: "0.4.3"
44
synopsis: "A unified interface for nix shell."
55
description:
66
"An intuitive nix shell interface that unifies the `nix shell`, `nix develop` and `nix-shell` commands."

test/dune

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@
1616
(targets test_ns) ; the Dune target name
1717
(deps test_ns.rs) ; path relative to this dune file
1818
(action
19-
(run rustc --test %{deps} -o %{targets} --allow warnings)))
19+
(run rustc --test %{deps} -o %{targets} -A warnings -l c)))

test/test_ns.rs

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,46 @@
1-
use std::{env, path, process, sync};
1+
use std::{env, ffi, path, process, sync};
22

33
static DUNE_TEST_DIR: sync::OnceLock<path::PathBuf> = sync::OnceLock::new();
44
static ENV_SHELL: sync::OnceLock<String> = sync::OnceLock::new();
55

66
fn dune_test_dir() -> &'static path::PathBuf {
77
DUNE_TEST_DIR.get_or_init(|| env::current_dir().expect("failed to get test directory"))
88
}
9-
fn env_shell() -> &'static String {
10-
ENV_SHELL.get_or_init(|| env::var("SHELL").expect("failed to get shell from environment"))
9+
10+
/// Minimal bindings to dynamically linked libc, used for getting the shell of the logged in user.
11+
mod libc {
12+
unsafe extern "C" {
13+
pub fn getuid() -> u32;
14+
pub fn getpwuid(uid: u32) -> *mut Passwd;
15+
}
16+
17+
#[repr(C)]
18+
pub struct Passwd {
19+
pub pw_name: *mut i8,
20+
pub pw_passwd: *mut i8,
21+
pub pw_uid: u32,
22+
pub pw_gid: u32,
23+
#[cfg(target_os = "macos")]
24+
pub pw_change: i64,
25+
#[cfg(target_os = "macos")]
26+
pub pw_class: *mut i8,
27+
pub pw_gecos: *mut i8,
28+
pub pw_dir: *mut i8,
29+
pub pw_shell: *mut i8,
30+
#[cfg(target_os = "macos")]
31+
pub pw_expire: i64,
32+
}
33+
}
34+
35+
fn env_shell() -> &'static str {
36+
ENV_SHELL.get_or_init(|| unsafe {
37+
let uid = libc::getuid();
38+
let pw = libc::getpwuid(uid);
39+
assert!(!pw.is_null());
40+
ffi::CStr::from_ptr((*pw).pw_shell)
41+
.to_string_lossy()
42+
.into_owned()
43+
})
1144
}
1245

1346
fn ns() -> path::PathBuf {

0 commit comments

Comments
 (0)