1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

stdbuf: add feat_external_stdbuf

Fixes https://github.com/uutils/coreutils/issues/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 <ecordonnier@snap.com>
This commit is contained in:
Etienne Cordonnier 2025-05-27 21:06:02 +02:00
parent 83424751c1
commit 99aa51a9a9
5 changed files with 82 additions and 9 deletions

View file

@ -3,6 +3,8 @@ linker = "x86_64-unknown-redox-gcc"
[env] [env]
PROJECT_NAME_FOR_VERSION_STRING = "uutils coreutils" PROJECT_NAME_FOR_VERSION_STRING = "uutils coreutils"
# See feat_external_libstdbuf in src/uu/stdbuf/Cargo.toml
LIBSTDBUF_DIR = "/usr/lib"
# libstdbuf must be a shared library, so musl libc can't be linked statically # libstdbuf must be a shared library, so musl libc can't be linked statically
# https://github.com/rust-lang/rust/issues/82193 # https://github.com/rust-lang/rust/issues/82193

View file

@ -37,6 +37,9 @@ test_risky_names = []
# * only build `uudoc` when `--feature uudoc` is activated # * only build `uudoc` when `--feature uudoc` is activated
uudoc = ["zip", "dep:uuhelp_parser"] uudoc = ["zip", "dep:uuhelp_parser"]
## features ## features
## Optional feature for stdbuf
# "feat_external_libstdbuf" == use an external libstdbuf.so for stdbuf instead of embedding it
feat_external_libstdbuf = ["stdbuf/feat_external_libstdbuf"]
# "feat_acl" == enable support for ACLs (access control lists; by using`--features feat_acl`) # "feat_acl" == enable support for ACLs (access control lists; by using`--features feat_acl`)
# NOTE: # NOTE:
# * On linux, the posix-acl/acl-sys crate requires `libacl` headers and shared library to be accessible in the C toolchain at compile time. # * On linux, the posix-acl/acl-sys crate requires `libacl` headers and shared library to be accessible in the C toolchain at compile time.

View file

@ -1,3 +1,4 @@
# spell-checker:ignore dpkg
[package] [package]
name = "uu_stdbuf" name = "uu_stdbuf"
description = "stdbuf ~ (uutils) run COMMAND with modified standard stream buffering" description = "stdbuf ~ (uutils) run COMMAND with modified standard stream buffering"
@ -23,6 +24,25 @@ libstdbuf = { package = "uu_stdbuf_libstdbuf", path = "src/libstdbuf" }
tempfile = { workspace = true } tempfile = { workspace = true }
uucore = { workspace = true, features = ["parser"] } uucore = { workspace = true, features = ["parser"] }
# "feat_external_libstdbuf": 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_DIR configures the
# installation directory during the build. E.g. LIBSTDBUF_DIR="/usr/lib"
[features]
feat_external_libstdbuf = []
[[bin]] [[bin]]
name = "stdbuf" name = "stdbuf"
path = "src/main.rs" path = "src/main.rs"

View file

@ -29,6 +29,26 @@ fn main() {
println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=src/libstdbuf/src/libstdbuf.rs"); println!("cargo:rerun-if-changed=src/libstdbuf/src/libstdbuf.rs");
// Check for external stdbuf feature requirements
#[cfg(feature = "feat_external_libstdbuf")]
{
if env::var("LIBSTDBUF_DIR").is_err() {
eprintln!(
"\n\x1b[31mError:\x1b[0m The 'feat_external_libstdbuf' feature requires the LIBSTDBUF_DIR environment variable to be set."
);
eprintln!(
"\x1b[33mUsage:\x1b[0m LIBSTDBUF_DIR=/path/to/lib/directory cargo build --features feat_external_libstdbuf"
);
eprintln!(
"\x1b[33mExample:\x1b[0m LIBSTDBUF_DIR=/usr/lib cargo build --features feat_external_libstdbuf"
);
eprintln!(
"\nThis directory should point to where libstdbuf.so / libstdbuf.dylib will be installed on the target system."
);
std::process::exit(1);
}
}
let out_dir = env::var("OUT_DIR").expect("OUT_DIR not set"); let out_dir = env::var("OUT_DIR").expect("OUT_DIR not set");
let target = env::var("TARGET").unwrap_or_else(|_| "unknown".to_string()); let target = env::var("TARGET").unwrap_or_else(|_| "unknown".to_string());

View file

@ -6,8 +6,6 @@
// spell-checker:ignore (ToDO) tempdir dyld dylib optgrps libstdbuf // spell-checker:ignore (ToDO) tempdir dyld dylib optgrps libstdbuf
use clap::{Arg, ArgAction, ArgMatches, Command}; use clap::{Arg, ArgAction, ArgMatches, Command};
use std::fs::File;
use std::io::Write;
use std::os::unix::process::ExitStatusExt; use std::os::unix::process::ExitStatusExt;
use std::path::PathBuf; use std::path::PathBuf;
use std::process; use std::process;
@ -29,16 +27,19 @@ mod options {
pub const COMMAND: &str = "command"; pub const COMMAND: &str = "command";
} }
#[cfg(any( #[cfg(all(
not(feature = "feat_external_libstdbuf"),
any(
target_os = "linux", target_os = "linux",
target_os = "android", target_os = "android",
target_os = "freebsd", target_os = "freebsd",
target_os = "netbsd", target_os = "netbsd",
target_os = "dragonfly" target_os = "dragonfly"
)
))] ))]
const STDBUF_INJECT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/libstdbuf.so")); const STDBUF_INJECT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/libstdbuf.so"));
#[cfg(target_vendor = "apple")] #[cfg(all(not(feature = "feat_external_libstdbuf"), target_vendor = "apple"))]
const STDBUF_INJECT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/libstdbuf.dylib")); const STDBUF_INJECT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/libstdbuf.dylib"));
enum BufferType { enum BufferType {
@ -137,7 +138,11 @@ fn set_command_env(command: &mut process::Command, buffer_name: &str, buffer_typ
} }
} }
#[cfg(not(feature = "feat_external_libstdbuf"))]
fn get_preload_env(tmp_dir: &TempDir) -> UResult<(String, PathBuf)> { fn get_preload_env(tmp_dir: &TempDir) -> UResult<(String, PathBuf)> {
use std::fs::File;
use std::io::Write;
let (preload, extension) = preload_strings()?; let (preload, extension) = preload_strings()?;
let inject_path = tmp_dir.path().join("libstdbuf").with_extension(extension); let inject_path = tmp_dir.path().join("libstdbuf").with_extension(extension);
@ -147,6 +152,29 @@ fn get_preload_env(tmp_dir: &TempDir) -> UResult<(String, PathBuf)> {
Ok((preload.to_owned(), inject_path)) Ok((preload.to_owned(), inject_path))
} }
#[cfg(feature = "feat_external_libstdbuf")]
fn get_preload_env(_tmp_dir: &TempDir) -> UResult<(String, PathBuf)> {
let (preload, extension) = preload_strings()?;
// Use the directory provided at compile time via LIBSTDBUF_DIR environment variable
// This will fail to compile if LIBSTDBUF_DIR is not set, which is the desired behavior
const LIBSTDBUF_DIR: &str = env!("LIBSTDBUF_DIR");
let path_buf = PathBuf::from(LIBSTDBUF_DIR)
.join("libstdbuf")
.with_extension(extension);
if path_buf.exists() {
return Ok((preload.to_owned(), path_buf));
}
Err(USimpleError::new(
1,
format!(
"External libstdbuf not found at configured path: {}",
path_buf.display()
),
))
}
#[uucore::main] #[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> { pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let matches = uu_app().try_get_matches_from(args).with_exit_code(125)?; let matches = uu_app().try_get_matches_from(args).with_exit_code(125)?;