tracexec_core/
cmdbuilder.rs1#![allow(unused)]
27
28use color_eyre::eyre::{Context, bail};
29use nix::libc;
30use std::collections::BTreeMap;
31use std::env;
32use std::ffi::{CString, OsStr, OsString};
33use std::os::unix::ffi::OsStringExt;
34use std::path::{Path, PathBuf};
35use tracing::warn;
36
37fn get_shell() -> String {
38 use nix::unistd::{AccessFlags, access};
39 use std::ffi::CStr;
40 use std::path::Path;
41 use std::str;
42
43 let ent = unsafe { libc::getpwuid(libc::getuid()) };
44 if !ent.is_null() {
45 let shell = unsafe { CStr::from_ptr((*ent).pw_shell) };
46 match shell.to_str().map(str::to_owned) {
47 Err(err) => {
48 warn!(
49 "passwd database shell could not be \
50 represented as utf-8: {err:#}, \
51 falling back to /bin/sh"
52 );
53 }
54 Ok(shell) => {
55 if let Err(err) = access(Path::new(&shell), AccessFlags::X_OK) {
56 warn!(
57 "passwd database shell={shell:?} which is \
58 not executable ({err:#}), falling back to /bin/sh"
59 );
60 } else {
61 return shell;
62 }
63 }
64 }
65 }
66 "/bin/sh".into()
67}
68
69#[derive(Clone, Debug, PartialEq, Eq)]
72pub struct CommandBuilder {
73 args: Vec<OsString>,
74 cwd: Option<PathBuf>,
75 pub(crate) umask: Option<libc::mode_t>,
76 controlling_tty: bool,
77}
78
79impl CommandBuilder {
80 pub fn new<S: AsRef<OsStr>>(program: S) -> Self {
83 Self {
84 args: vec![program.as_ref().to_owned()],
85 cwd: None,
86 umask: None,
87 controlling_tty: true,
88 }
89 }
90
91 pub fn from_argv(args: Vec<OsString>) -> Self {
93 Self {
94 args,
95 cwd: None,
96 umask: None,
97 controlling_tty: true,
98 }
99 }
100
101 pub fn set_controlling_tty(&mut self, controlling_tty: bool) {
108 self.controlling_tty = controlling_tty;
109 }
110
111 pub fn get_controlling_tty(&self) -> bool {
112 self.controlling_tty
113 }
114
115 pub fn new_default_prog() -> Self {
118 Self {
119 args: vec![],
120 cwd: None,
121 umask: None,
122 controlling_tty: true,
123 }
124 }
125
126 pub fn is_default_prog(&self) -> bool {
128 self.args.is_empty()
129 }
130
131 pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) {
134 if self.is_default_prog() {
135 panic!("attempted to add args to a default_prog builder");
136 }
137 self.args.push(arg.as_ref().to_owned());
138 }
139
140 pub fn args<I, S>(&mut self, args: I)
142 where
143 I: IntoIterator<Item = S>,
144 S: AsRef<OsStr>,
145 {
146 for arg in args {
147 self.arg(arg);
148 }
149 }
150
151 pub fn get_argv(&self) -> &Vec<OsString> {
152 &self.args
153 }
154
155 pub fn get_argv_mut(&mut self) -> &mut Vec<OsString> {
156 &mut self.args
157 }
158
159 pub fn cwd<D>(&mut self, dir: D)
160 where
161 D: AsRef<Path>,
162 {
163 self.cwd = Some(dir.as_ref().to_owned());
164 }
165
166 pub fn clear_cwd(&mut self) {
167 self.cwd.take();
168 }
169
170 pub fn get_cwd(&self) -> Option<&Path> {
171 self.cwd.as_deref()
172 }
173}
174
175impl CommandBuilder {
176 pub fn umask(&mut self, mask: Option<libc::mode_t>) {
177 self.umask = mask;
178 }
179
180 fn resolve_path(&self) -> Option<OsString> {
181 env::var_os("PATH")
182 }
183
184 fn search_path(&self, exe: &OsStr, cwd: &Path) -> color_eyre::Result<PathBuf> {
185 use nix::unistd::{AccessFlags, access};
186 use std::path::Path;
187
188 let exe_path: &Path = exe.as_ref();
189 if exe_path.is_relative() {
190 let abs_path = cwd.join(exe_path);
191 if abs_path.exists() {
192 return Ok(abs_path);
193 }
194
195 if let Some(path) = self.resolve_path() {
196 for path in std::env::split_paths(&path) {
197 let candidate = path.join(exe);
198 if access(&candidate, AccessFlags::X_OK).is_ok() {
199 return Ok(candidate);
200 }
201 }
202 }
203 bail!(
204 "Unable to spawn {} because it doesn't exist on the filesystem \
205 and was not found in PATH",
206 exe_path.display()
207 );
208 } else {
209 if let Err(err) = access(exe_path, AccessFlags::X_OK) {
210 bail!(
211 "Unable to spawn {} because it doesn't exist on the filesystem \
212 or is not executable ({err:#})",
213 exe_path.display()
214 );
215 }
216
217 Ok(PathBuf::from(exe))
218 }
219 }
220
221 pub(crate) fn build(self) -> color_eyre::Result<Command> {
223 use std::os::unix::process::CommandExt;
224 let cwd = env::current_dir()?;
225 let dir = if let Some(dir) = self.cwd.as_deref() {
226 dir.to_owned()
227 } else {
228 cwd
229 };
230 let resolved = self.search_path(&self.args[0], &dir)?;
231 tracing::trace!("resolved path to {:?}", resolved);
232
233 Ok(Command {
234 program: resolved,
235 args: self
236 .args
237 .into_iter()
238 .map(|a| CString::new(a.into_vec()))
239 .collect::<Result<_, _>>()?,
240 cwd: dir,
241 })
242 }
243}
244
245pub struct Command {
246 pub program: PathBuf,
247 pub args: Vec<CString>,
248 pub cwd: PathBuf,
249}