Skip to content

Commit e07d23d

Browse files
committed
stdbuf: add feat_external_stdbuf
Fixes #6591 "feat_external_stdbuf": use an external libstdbuf.so for stdbuf instead of embedding it into the stdbuf binary. There are 2 use-cases: 1. Installation of uutils-coreutils using cargo install (e.g. from crates.io which supports only "cargo install" as installation method). In this case, installing libstdbuf.so is impossible, because "cargo install" installs only binary programs (no cdylib), thus libstdbuf.so must be embedded into stdbuf and written to /tmp at runtime. This is a hack, and may not work on some platforms, e.g. because the SELinux permissions may not allow stdbuf to write to /tmp, /tmp may be read-only, libstdbuf.so may not work at all without SELinux labels, etc. 2. Installation of uutils-coreutils using an external tool, e.g. dpkg/apt on debian. In this case, libstdbuf.so should be installed separately to its correct location and the environment variable LIBSTDBUF_PATH configures the installation path during the build. E.g. LIBSTDBUF_PATH="/lib/libstdbuf.so" Signed-off-by: Etienne Cordonnier <[email protected]>
1 parent 4128efa commit e07d23d

File tree

5 files changed

+79
-9
lines changed

5 files changed

+79
-9
lines changed

.cargo/config.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ linker = "x86_64-unknown-redox-gcc"
33

44
[env]
55
PROJECT_NAME_FOR_VERSION_STRING = "uutils coreutils"
6+
LIBSTDBUF_PATH = "/usr/lib/libstdbuf.so"
67

78
# libstdbuf must be a shared library, so musl libc can't be linked statically
89
# https://github.com/rust-lang/rust/issues/82193

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ test_risky_names = []
3737
# * only build `uudoc` when `--feature uudoc` is activated
3838
uudoc = ["zip", "dep:uuhelp_parser"]
3939
## features
40+
## Optional feature for stdbuf
41+
# "feat_external_stdbuf" == use an external libstdbuf.so for stdbuf instead of embedding it
42+
feat_external_stdbuf = ["stdbuf/feat_external_stdbuf"]
4043
# "feat_acl" == enable support for ACLs (access control lists; by using`--features feat_acl`)
4144
# NOTE:
4245
# * On linux, the posix-acl/acl-sys crate requires `libacl` headers and shared library to be accessible in the C toolchain at compile time.

src/uu/stdbuf/Cargo.toml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# spell-checker:ignore dpkg
12
[package]
23
name = "uu_stdbuf"
34
description = "stdbuf ~ (uutils) run COMMAND with modified standard stream buffering"
@@ -23,6 +24,25 @@ libstdbuf = { package = "uu_stdbuf_libstdbuf", path = "src/libstdbuf" }
2324
tempfile = { workspace = true }
2425
uucore = { workspace = true, features = ["parser"] }
2526

27+
# "feat_external_stdbuf": use an external libstdbuf.so for stdbuf instead of embedding it into
28+
# the stdbuf binary.
29+
# There are 2 use-cases:
30+
# 1. Installation of uutils-coreutils using cargo install (e.g. from crates.io
31+
# which supports only "cargo install" as installation method). In this case,
32+
# installing libstdbuf.so is impossible, because "cargo install" installs
33+
# only binary programs (no cdylib), thus libstdbuf.so must be embedded into
34+
# stdbuf and written to /tmp at runtime. This is a hack, and may not work
35+
# on some platforms, e.g. because the SELinux permissions may not allow
36+
# stdbuf to write to /tmp, /tmp may be read-only, libstdbuf.so may not work
37+
# at all without SELinux labels, etc.
38+
#
39+
# 2. Installation of uutils-coreutils using an external tool, e.g. dpkg/apt on
40+
# debian. In this case, libstdbuf.so should be installed separately to its
41+
# correct location and the environment variable LIBSTDBUF_PATH configures the
42+
# installation path during the build. E.g. LIBSTDBUF_PATH="/lib/libstdbuf.so"
43+
[features]
44+
feat_external_stdbuf = []
45+
2646
[[bin]]
2747
name = "stdbuf"
2848
path = "src/main.rs"

src/uu/stdbuf/build.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,26 @@ fn main() {
2929
println!("cargo:rerun-if-changed=build.rs");
3030
println!("cargo:rerun-if-changed=src/libstdbuf/src/libstdbuf.rs");
3131

32+
// Check for external stdbuf feature requirements
33+
#[cfg(feature = "feat_external_stdbuf")]
34+
{
35+
if env::var("LIBSTDBUF_PATH").is_err() {
36+
eprintln!(
37+
"\n\x1b[31mError:\x1b[0m The 'feat_external_stdbuf' feature requires the LIBSTDBUF_PATH environment variable to be set."
38+
);
39+
eprintln!(
40+
"\x1b[33mUsage:\x1b[0m LIBSTDBUF_PATH=/path/to/libstdbuf.so cargo build --features feat_external_stdbuf"
41+
);
42+
eprintln!(
43+
"\x1b[33mExample:\x1b[0m LIBSTDBUF_PATH=/usr/lib/libstdbuf.so cargo build --features feat_external_stdbuf"
44+
);
45+
eprintln!(
46+
"\nThis path should point to where libstdbuf.so / libstdbuf.dylib will be installed on the target system."
47+
);
48+
std::process::exit(1);
49+
}
50+
}
51+
3252
let out_dir = env::var("OUT_DIR").expect("OUT_DIR not set");
3353
let target = env::var("TARGET").unwrap_or_else(|_| "unknown".to_string());
3454

src/uu/stdbuf/src/stdbuf.rs

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
// spell-checker:ignore (ToDO) tempdir dyld dylib optgrps libstdbuf
77

88
use clap::{Arg, ArgAction, ArgMatches, Command};
9-
use std::fs::File;
10-
use std::io::Write;
119
use std::os::unix::process::ExitStatusExt;
1210
use std::path::PathBuf;
1311
use std::process;
@@ -29,16 +27,19 @@ mod options {
2927
pub const COMMAND: &str = "command";
3028
}
3129

32-
#[cfg(any(
33-
target_os = "linux",
34-
target_os = "android",
35-
target_os = "freebsd",
36-
target_os = "netbsd",
37-
target_os = "dragonfly"
30+
#[cfg(all(
31+
not(feature = "feat_external_stdbuf"),
32+
any(
33+
target_os = "linux",
34+
target_os = "android",
35+
target_os = "freebsd",
36+
target_os = "netbsd",
37+
target_os = "dragonfly"
38+
)
3839
))]
3940
const STDBUF_INJECT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/libstdbuf.so"));
4041

41-
#[cfg(target_vendor = "apple")]
42+
#[cfg(all(not(feature = "feat_external_stdbuf"), target_vendor = "apple"))]
4243
const STDBUF_INJECT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/libstdbuf.dylib"));
4344

4445
enum BufferType {
@@ -137,7 +138,11 @@ fn set_command_env(command: &mut process::Command, buffer_name: &str, buffer_typ
137138
}
138139
}
139140

141+
#[cfg(not(feature = "feat_external_stdbuf"))]
140142
fn get_preload_env(tmp_dir: &TempDir) -> UResult<(String, PathBuf)> {
143+
use std::fs::File;
144+
use std::io::Write;
145+
141146
let (preload, extension) = preload_strings()?;
142147
let inject_path = tmp_dir.path().join("libstdbuf").with_extension(extension);
143148

@@ -147,6 +152,27 @@ fn get_preload_env(tmp_dir: &TempDir) -> UResult<(String, PathBuf)> {
147152
Ok((preload.to_owned(), inject_path))
148153
}
149154

155+
#[cfg(feature = "feat_external_stdbuf")]
156+
fn get_preload_env(_tmp_dir: &TempDir) -> UResult<(String, PathBuf)> {
157+
let (preload, _extension) = preload_strings()?;
158+
159+
// Use the path provided at compile time via LIBSTDBUF_PATH environment variable
160+
// This will fail to compile if LIBSTDBUF_PATH is not set, which is the desired behavior
161+
const LIBSTDBUF_PATH: &str = env!("LIBSTDBUF_PATH");
162+
let path_buf = PathBuf::from(LIBSTDBUF_PATH);
163+
if path_buf.exists() {
164+
return Ok((preload.to_owned(), path_buf));
165+
}
166+
167+
Err(USimpleError::new(
168+
1,
169+
format!(
170+
"External libstdbuf not found at configured path: {}",
171+
LIBSTDBUF_PATH
172+
),
173+
))
174+
}
175+
150176
#[uucore::main]
151177
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
152178
let matches = uu_app().try_get_matches_from(args).with_exit_code(125)?;

0 commit comments

Comments
 (0)