#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
#![cfg_attr(not(coverage_nightly), deny(unstable_features))]
#![deny(
missing_copy_implementations,
missing_debug_implementations,
missing_docs,
rust_2018_idioms,
unsafe_op_in_unsafe_fn
)]
#![warn(clippy::pedantic)]
#![allow(clippy::needless_doctest_main)]
#[cfg(not(any(target_os = "linux", target_os = "android")))]
compile_error!("pentacle only works on linux or android");
mod file;
mod syscall;
pub use crate::file::{MustSealError, SealOptions};
use std::fmt::{self, Debug};
use std::fs::File;
use std::io::{self, Error, Read, Write};
use std::ops::{Deref, DerefMut};
use std::os::unix::io::AsRawFd;
use std::os::unix::process::CommandExt;
use std::process::Command;
const OPTIONS: SealOptions<'static> = SealOptions::new().executable(true);
pub fn ensure_sealed() -> Result<(), Error> {
let mut file = File::open("/proc/self/exe")?;
if OPTIONS.is_sealed(&file) {
Ok(())
} else {
let mut command = SealedCommand::new(&mut file)?;
let mut args = std::env::args_os().fuse();
if let Some(arg0) = args.next() {
command.arg0(arg0);
}
command.args(args);
Err(command.exec())
}
}
#[must_use]
pub fn is_sealed() -> bool {
File::open("/proc/self/exe")
.map(|f| OPTIONS.is_sealed(&f))
.unwrap_or(false)
}
pub struct SealedCommand {
inner: Command,
_memfd: File,
}
impl SealedCommand {
pub fn new<R: Read>(program: &mut R) -> Result<Self, Error> {
let mut buf = [0; 8192];
let n = program.read(&mut buf)?;
let options = OPTIONS.close_on_exec(buf.get(..2) != Some(b"#!"));
let mut memfd = options.create()?;
memfd.write_all(&buf[..n])?;
io::copy(program, &mut memfd)?;
options.seal(&mut memfd)?;
Ok(Self {
inner: Command::new(format!("/proc/self/fd/{}", memfd.as_raw_fd())),
_memfd: memfd,
})
}
}
impl Debug for SealedCommand {
#[cfg_attr(coverage_nightly, coverage(off))]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt(f)
}
}
impl Deref for SealedCommand {
type Target = Command;
#[cfg_attr(coverage_nightly, coverage(off))]
fn deref(&self) -> &Command {
&self.inner
}
}
impl DerefMut for SealedCommand {
#[cfg_attr(coverage_nightly, coverage(off))]
fn deref_mut(&mut self) -> &mut Command {
&mut self.inner
}
}