reverie_process/builder.rs
1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 * All rights reserved.
4 *
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the root directory of this source tree.
7 */
8
9use std::borrow::Cow;
10use std::collections::BTreeMap;
11use std::ffi::OsStr;
12use std::ffi::OsString;
13use std::io;
14use std::os::unix::ffi::OsStrExt;
15use std::os::unix::fs::PermissionsExt;
16use std::path;
17use std::path::Path;
18use std::path::PathBuf;
19
20use syscalls::Errno;
21
22use super::Command;
23use super::Container;
24use super::Mount;
25use super::Namespace;
26use super::PtyChild;
27use super::Stdio;
28use super::seccomp;
29use super::util::CStringArray;
30use super::util::to_cstring;
31
32impl Command {
33 /// Constructs a new `Command` for launching the program at path `program`,
34 /// with the following default configuration:
35 ///
36 /// * No arguments to the program
37 /// * Inherit the current process's environment
38 /// * Inherit the current process's working directory
39 /// * Inherit stdin/stdout/stderr for `spawn` or `status`, but create pipes
40 /// for `output`
41 ///
42 /// Builder methods are provided to change these defaults and
43 /// otherwise configure the process.
44 ///
45 /// If `program` is not an absolute path, the `PATH` will be searched in an
46 /// OS-defined way.
47 ///
48 /// # Examples
49 ///
50 /// Basic usage:
51 ///
52 /// ```no_run
53 /// use reverie_process::Command;
54 /// let command = Command::new("sh");
55 /// ```
56 pub fn new<S: AsRef<OsStr>>(program: S) -> Self {
57 let program = to_cstring(program);
58
59 let mut args = CStringArray::with_capacity(1);
60 args.push(program.clone());
61
62 Self {
63 program,
64 args,
65 pre_exec: Vec::new(),
66 container: Container::new(),
67 }
68 }
69
70 /// Sets the path to the program. This can be used to override what was
71 /// already set in [`Command::new`].
72 ///
73 /// NOTE: This also changes argument 0 to match `program`.
74 pub fn program<S: AsRef<OsStr>>(&mut self, program: S) -> &mut Self {
75 let cstring = to_cstring(program);
76 self.program = cstring.clone();
77 self.args.set(0, cstring);
78 self
79 }
80
81 /// Explicitly sets the first argument. By default, this is the same as the
82 /// program path and is what you want in most cases.
83 pub fn arg0<S: AsRef<OsStr>>(&mut self, arg0: S) -> &mut Self {
84 self.args.set(0, to_cstring(arg0));
85 self
86 }
87
88 /// Gets the first argument. Unless [`Command::arg0`] was used, this returns
89 /// the same string as [`Command::get_program`].
90 pub fn get_arg0(&self) -> &OsStr {
91 OsStr::from_bytes(self.args.get(0).to_bytes())
92 }
93
94 /// Adds an argument to pass to the program.
95 ///
96 /// Only one argument can be passed per use. So instead of:
97 ///
98 /// ```no_run
99 /// reverie_process::Command::new("sh").arg("-C /path/to/repo");
100 /// ```
101 ///
102 /// usage would be:
103 ///
104 /// ```no_run
105 /// reverie_process::Command::new("sh")
106 /// .arg("-C")
107 /// .arg("/path/to/repo");
108 /// ```
109 ///
110 /// To pass multiple arguments see [`args`].
111 ///
112 /// [`args`]: method@Self::args
113 ///
114 /// # Examples
115 ///
116 /// Basic usage:
117 ///
118 /// ```no_run
119 /// use reverie_process::Command;
120 ///
121 /// let command = Command::new("ls").arg("-l").arg("-a");
122 /// ```
123 pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self {
124 self.args.push(to_cstring(arg));
125 self
126 }
127
128 /// Adds multiple arguments to pass to the program.
129 ///
130 /// To pass a single argument see [`arg`].
131 ///
132 /// [`arg`]: method@Self::arg
133 ///
134 /// # Examples
135 ///
136 /// Basic usage:
137 ///
138 /// ```no_run
139 /// use reverie_process::Command;
140 ///
141 /// let command = Command::new("ls").args(&["-l", "-a"]);
142 /// ```
143 pub fn args<I, S>(&mut self, args: I) -> &mut Self
144 where
145 I: IntoIterator<Item = S>,
146 S: AsRef<OsStr>,
147 {
148 for arg in args {
149 self.arg(arg);
150 }
151 self
152 }
153
154 /// Returns an iterator of the arguments that will be passed to the program.
155 ///
156 /// This does not include the program name itself. It only includes the
157 /// arguments specified with [`Command::arg`] and [`Command::args`].
158 pub fn get_args(&self) -> impl Iterator<Item = &OsStr> {
159 self.args
160 .iter()
161 .skip(1)
162 .map(|arg| OsStr::from_bytes(arg.to_bytes()))
163 }
164
165 /// Prepends arguments to the beginning of the command. Note that arguments
166 /// are prepended *after* arg0, but before the rest of the arguments.
167 pub fn prepend_args<I, S>(&mut self, args: I) -> &mut Self
168 where
169 I: IntoIterator<Item = S>,
170 S: AsRef<OsStr>,
171 {
172 let new_args = CStringArray::with_capacity(self.args.len() + 1);
173 let mut old_args = core::mem::replace(&mut self.args, new_args).into_iter();
174
175 // Add arg0 first
176 if let Some(arg0) = old_args.next() {
177 self.args.push(arg0);
178 }
179
180 // Add the new arguments
181 self.args(args);
182
183 // Add the rest of the old arguments
184 for arg in old_args {
185 self.args.push(arg);
186 }
187
188 self
189 }
190
191 /// Inserts or updates an environment variable mapping.
192 ///
193 /// Note that environment variable names are case-insensitive (but
194 /// case-preserving) on Windows, and case-sensitive on all other platforms.
195 ///
196 /// # Examples
197 ///
198 /// Basic usage:
199 ///
200 /// ```no_run
201 /// use reverie_process::Command;
202 ///
203 /// let command = Command::new("ls").env("PATH", "/bin");
204 /// ```
205 pub fn env<K, V>(&mut self, key: K, val: V) -> &mut Self
206 where
207 K: AsRef<OsStr>,
208 V: AsRef<OsStr>,
209 {
210 self.container.env(key, val);
211 self
212 }
213
214 /// Adds or updates multiple environment variable mappings.
215 ///
216 /// # Examples
217 ///
218 /// Basic usage:
219 ///
220 /// ```no_run
221 /// use std::collections::HashMap;
222 /// use std::env;
223 ///
224 /// use reverie_process::Command;
225 /// use reverie_process::Stdio;
226 ///
227 /// let filtered_env: HashMap<String, String> = env::vars()
228 /// .filter(|&(ref k, _)| k == "TERM" || k == "TZ" || k == "LANG" || k == "PATH")
229 /// .collect();
230 ///
231 /// let command = Command::new("printenv")
232 /// .stdin(Stdio::null())
233 /// .stdout(Stdio::inherit())
234 /// .env_clear()
235 /// .envs(&filtered_env);
236 /// ```
237 pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Self
238 where
239 I: IntoIterator<Item = (K, V)>,
240 K: AsRef<OsStr>,
241 V: AsRef<OsStr>,
242 {
243 self.container.envs(vars);
244 self
245 }
246
247 /// Removes an environment variable mapping.
248 ///
249 /// # Examples
250 ///
251 /// Basic usage:
252 ///
253 /// ```no_run
254 /// use reverie_process::Command;
255 ///
256 /// let command = Command::new("ls").env_remove("PATH");
257 /// ```
258 pub fn env_remove<K: AsRef<OsStr>>(&mut self, key: K) -> &mut Self {
259 self.container.env_remove(key);
260 self
261 }
262
263 /// Clears the entire environment map for the child process.
264 ///
265 /// # Examples
266 ///
267 /// Basic usage:
268 ///
269 /// ```no_run
270 /// use reverie_process::Command;
271 ///
272 /// let command = Command::new("ls").env_clear();
273 /// ```
274 pub fn env_clear(&mut self) -> &mut Self {
275 self.container.env_clear();
276 self
277 }
278
279 /// Sets the working directory for the child process.
280 ///
281 /// # Interaction with `chroot`
282 ///
283 /// The working directory is set *after* the chroot is performed (if a chroot
284 /// directory is specified). Thus, the path given is relative to the chroot
285 /// directory. Otherwise, if no chroot directory is specified, the working
286 /// directory is relative to the current working directory of the parent
287 /// process at the time the child process is spawned.
288 ///
289 /// # Platform-specific behavior
290 ///
291 /// If the program path is relative (e.g., `"./script.sh"`), it's ambiguous
292 /// whether it should be interpreted relative to the parent's working
293 /// directory or relative to `current_dir`. The behavior in this case is
294 /// platform specific and unstable, and it's recommended to use
295 /// [`canonicalize`] to get an absolute program path instead.
296 ///
297 /// [`canonicalize`]: std::fs::canonicalize()
298 ///
299 /// # Examples
300 ///
301 /// Basic usage:
302 ///
303 /// ```no_run
304 /// use reverie_process::Command;
305 ///
306 /// let command = Command::new("ls").current_dir("/bin");
307 /// ```
308 pub fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut Self {
309 self.container.current_dir(dir);
310 self
311 }
312
313 /// Sets configuration for the child process's standard input (stdin) handle.
314 ///
315 /// Defaults to [`Stdio::inherit`] when used with `spawn` or `status`, and
316 /// defaults to [`Stdio::piped`] when used with `output`.
317 ///
318 /// # Examples
319 ///
320 /// Basic usage:
321 ///
322 /// ```no_run
323 /// use reverie_process::Command;
324 /// use reverie_process::Stdio;
325 ///
326 /// let command = Command::new("ls").stdin(Stdio::null());
327 /// ```
328 pub fn stdin<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Self {
329 self.container.stdin(cfg);
330 self
331 }
332
333 /// Sets configuration for the child process's standard output (stdout)
334 /// handle.
335 ///
336 /// Defaults to [`Stdio::inherit`] when used with `spawn` or `status`, and
337 /// defaults to [`Stdio::piped`] when used with `output`.
338 ///
339 /// # Examples
340 ///
341 /// Basic usage:
342 ///
343 /// ```no_run
344 /// use reverie_process::Command;
345 /// use reverie_process::Stdio;
346 ///
347 /// let command = Command::new("ls").stdout(Stdio::null());
348 /// ```
349 pub fn stdout<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Self {
350 self.container.stdout(cfg);
351 self
352 }
353
354 /// Sets configuration for the child process's standard error (stderr)
355 /// handle.
356 ///
357 /// Defaults to [`Stdio::inherit`] when used with `spawn` or `status`, and
358 /// defaults to [`Stdio::piped`] when used with `output`.
359 ///
360 /// # Examples
361 ///
362 /// Basic usage:
363 ///
364 /// ```no_run
365 /// use reverie_process::Command;
366 /// use reverie_process::Stdio;
367 ///
368 /// let command = Command::new("ls").stderr(Stdio::null());
369 /// ```
370 pub fn stderr<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Self {
371 self.container.stderr(cfg);
372 self
373 }
374
375 /// Changes the root directory of the calling process to the specified path.
376 /// This directory will be inherited by all child processes of the calling
377 /// process.
378 ///
379 /// Note that changing the root directory may cause the program to not be
380 /// found. As such, the program path should be relative to this directory.
381 pub fn chroot<P: AsRef<Path>>(&mut self, chroot: P) -> &mut Self {
382 self.container.chroot(chroot);
383 self
384 }
385
386 /// Unshares parts of the process execution context that are normally shared
387 /// with the parent process. This is useful for executing the child process
388 /// in a new namespace.
389 pub fn unshare(&mut self, namespace: Namespace) -> &mut Self {
390 self.container.unshare(namespace);
391 self
392 }
393
394 /// Schedules a closure to be run just before the `exec` function is invoked.
395 ///
396 /// The closure is allowed to return an I/O error whose OS error code will be
397 /// communicated back to the parent and returned as an error from when the
398 /// spawn was requested.
399 ///
400 /// Multiple closures can be registered and they will be called in order of
401 /// their registration. If a closure returns `Err` then no further closures
402 /// will be called and the spawn operation will immediately return with a
403 /// failure.
404 ///
405 /// # Safety
406 ///
407 /// This closure will be run in the context of the child process after a
408 /// `fork`. This primarily means that any modifications made to memory on
409 /// behalf of this closure will **not** be visible to the parent process.
410 /// This is often a very constrained environment where normal operations like
411 /// `malloc` or acquiring a mutex are not guaranteed to work (due to other
412 /// threads perhaps still running when the `fork` was run).
413 ///
414 /// This also means that all resources such as file descriptors and
415 /// memory-mapped regions got duplicated. It is your responsibility to make
416 /// sure that the closure does not violate library invariants by making
417 /// invalid use of these duplicates.
418 ///
419 /// When this closure is run, aspects such as the stdio file descriptors and
420 /// working directory have successfully been changed, so output to these
421 /// locations may not appear where intended.
422 pub unsafe fn pre_exec<F>(&mut self, f: F) -> &mut Self
423 where
424 F: FnMut() -> Result<(), Errno> + Send + Sync + 'static,
425 {
426 self.pre_exec.push(Box::new(f));
427 self
428 }
429
430 /// Returns the path to the program that was given to [`Command::new`].
431 ///
432 /// # Examples
433 ///
434 /// ```
435 /// use reverie_process::Command;
436 ///
437 /// let cmd = Command::new("echo");
438 /// assert_eq!(cmd.get_program(), "echo");
439 /// ```
440 pub fn get_program(&self) -> &OsStr {
441 OsStr::from_bytes(self.program.to_bytes())
442 }
443
444 /// Returns the working directory for the child process.
445 ///
446 /// This returns None if the working directory will not be changed.
447 pub fn get_current_dir(&self) -> Option<&Path> {
448 self.container.get_current_dir()
449 }
450
451 /// Returns an iterator of the environment variables that will be set when
452 /// the process is spawned. Note that this does not include any environment
453 /// variables inherited from the parent process.
454 pub fn get_envs(&self) -> impl Iterator<Item = (&OsStr, Option<&OsStr>)> {
455 self.container.get_envs()
456 }
457
458 /// Returns a mapping of all environment variables that the new child process
459 /// will inherit.
460 pub fn get_captured_envs(&self) -> BTreeMap<OsString, OsString> {
461 self.container.get_captured_envs()
462 }
463
464 /// Gets an environment variable. If the child process is to inherit this
465 /// environment variable from the current process, then this returns the
466 /// current process's environment variable unless it is to be overridden.
467 pub fn get_env<K: AsRef<OsStr>>(&self, env: K) -> Option<Cow<'_, OsStr>> {
468 self.container.get_env(env)
469 }
470
471 /// Maps one user ID to another.
472 ///
473 /// Implies `Namespace::USER`.
474 ///
475 /// # Example
476 ///
477 /// This is can be used to gain `CAP_SYS_ADMIN` privileges in the user
478 /// namespace by mapping the root user inside the container to the current
479 /// user outside of the container.
480 ///
481 /// ```no_run
482 /// use reverie_process::Command;
483 ///
484 /// let command = Command::new("ls").map_uid(1, unsafe { libc::getuid() });
485 /// ```
486 ///
487 /// # Implementation
488 ///
489 /// This modifies `/proc/{pid}/uid_map` where `{pid}` is the PID of the child
490 /// process. See [`user_namespaces(7)`] for more details.
491 ///
492 /// [`user_namespaces(7)`]: https://man7.org/linux/man-pages/man7/user_namespaces.7.html
493 pub fn map_uid(&mut self, inside_uid: libc::uid_t, outside_uid: libc::uid_t) -> &mut Self {
494 self.container.map_uid(inside_uid, outside_uid);
495 self
496 }
497
498 /// Maps potentially many user IDs inside the new user namespace to user IDs
499 /// outside of the user namespace.
500 ///
501 /// Implies `Namespace::USER`.
502 ///
503 /// # Implementation
504 ///
505 /// This modifies `/proc/{pid}/uid_map` where `{pid}` is the PID of the child
506 /// process. See [`user_namespaces(7)`] for more details.
507 ///
508 /// [`user_namespaces(7)`]: https://man7.org/linux/man-pages/man7/user_namespaces.7.html
509 pub fn map_uid_range(
510 &mut self,
511 starting_inside_uid: libc::uid_t,
512 starting_outside_uid: libc::uid_t,
513 count: u32,
514 ) -> &mut Self {
515 self.container
516 .map_uid_range(starting_inside_uid, starting_outside_uid, count);
517 self
518 }
519
520 /// Convience function for mapping root (inside the container) to the current
521 /// user ID (outside the container). This is useful for gaining new
522 /// capabilities inside the container, such as being able to mount file
523 /// systems.
524 ///
525 /// Implies `Namespace::USER`.
526 ///
527 /// This is the same as:
528 /// ```no_run
529 /// use reverie_process::Command;
530 ///
531 /// let command = Command::new("ls")
532 /// .map_uid(0, unsafe { libc::geteuid() })
533 /// .map_gid(0, unsafe { libc::getegid() });
534 /// ```
535 pub fn map_root(&mut self) -> &mut Self {
536 self.container.map_root();
537 self
538 }
539
540 /// Maps one group ID to another.
541 ///
542 /// Implies `Namespace::USER`.
543 ///
544 /// # Implementation
545 ///
546 /// This modifies `/proc/{pid}/gid_map` where `{pid}` is the PID of the child
547 /// process. See [`user_namespaces(7)`] for more details.
548 ///
549 /// [`user_namespaces(7)`]: https://man7.org/linux/man-pages/man7/user_namespaces.7.html
550 pub fn map_gid(&mut self, inside_gid: libc::gid_t, outside_gid: libc::gid_t) -> &mut Self {
551 self.container.map_gid(inside_gid, outside_gid);
552 self
553 }
554
555 /// Maps potentially many group IDs inside the new user namespace to group
556 /// IDs outside of the user namespace.
557 ///
558 /// Implies `Namespace::USER`.
559 ///
560 /// # Implementation
561 ///
562 /// This modifies `/proc/{pid}/gid_map` where `{pid}` is the PID of the child
563 /// process. See [`user_namespaces(7)`] for more details.
564 ///
565 /// [`user_namespaces(7)`]: https://man7.org/linux/man-pages/man7/user_namespaces.7.html
566 pub fn map_gid_range(
567 &mut self,
568 starting_inside_gid: libc::gid_t,
569 starting_outside_gid: libc::gid_t,
570 count: u32,
571 ) -> &mut Self {
572 self.container
573 .map_gid_range(starting_inside_gid, starting_outside_gid, count);
574 self
575 }
576
577 /// Sets the hostname of the container.
578 ///
579 /// Implies `Namespace::UTS`, which requires `CAP_SYS_ADMIN`.
580 ///
581 /// ```no_run
582 /// use reverie_process::Command;
583 ///
584 /// let command = Command::new("cat")
585 /// .arg("/proc/sys/kernel/hostname")
586 /// .map_root()
587 /// .hostname("foobar.local");
588 /// ```
589 pub fn hostname<S: Into<OsString>>(&mut self, hostname: S) -> &mut Self {
590 self.container.hostname(hostname);
591 self
592 }
593
594 /// Sets the domain name of the container.
595 ///
596 /// Implies `Namespace::UTS`, which requires `CAP_SYS_ADMIN`.
597 ///
598 /// # Example
599 ///
600 /// ```no_run
601 /// use reverie_process::Command;
602 ///
603 /// let command = Command::new("cat")
604 /// .arg("/proc/sys/kernel/domainname")
605 /// .map_root()
606 /// .domainname("foobar");
607 /// ```
608 pub fn domainname<S: Into<OsString>>(&mut self, domainname: S) -> &mut Self {
609 self.container.domainname(domainname);
610 self
611 }
612
613 /// Gets the hostname of the container.
614 pub fn get_hostname(&self) -> Option<&OsStr> {
615 self.container.get_hostname()
616 }
617
618 /// Gets the domainname of the container.
619 pub fn get_domainname(&self) -> Option<&OsStr> {
620 self.container.get_domainname()
621 }
622
623 /// Adds a file system to be mounted. Note that these are mounted in the same
624 /// order as given.
625 ///
626 /// Implies `Namespace::MOUNT`. Note that `Namespace::USER` should also have
627 /// been set and `map_uid` should have been called in order to gain the
628 /// privileges required to mount.
629 pub fn mount(&mut self, mount: Mount) -> &mut Self {
630 self.container.mount(mount);
631 self
632 }
633
634 /// Adds multiple mounts.
635 pub fn mounts<I>(&mut self, mounts: I) -> &mut Self
636 where
637 I: IntoIterator<Item = Mount>,
638 {
639 self.container.mounts(mounts);
640 self
641 }
642
643 /// Sets up the container to have local networking only. This will prevent
644 /// any network communication to the outside world.
645 ///
646 /// Implies `Namespace::NETWORK` and `Namespace::MOUNT`.
647 ///
648 /// This also causes a fresh `/sys` to be mounted to avoid seeing the host
649 /// network interfaces in `/sys/class/net`.
650 pub fn local_networking_only(&mut self) -> &mut Self {
651 self.container.local_networking_only();
652 self
653 }
654
655 /// Sets the seccomp filter. The filter is loaded immediately before `execve`
656 /// and *after* all `pre_exec` callbacks have been executed. Thus, you will
657 /// still be able to call filtered syscalls from `pre_exec` callbacks.
658 pub fn seccomp(&mut self, filter: seccomp::Filter) -> &mut Self {
659 self.container.seccomp(filter);
660 self
661 }
662
663 /// Indicates that we want to listen for seccomp events using
664 /// [seccomp_unotify(2)](https://man7.org/linux/man-pages/man2/seccomp_unotify.2.html).
665 ///
666 /// If this is set, the seccomp listener file descriptor will be accessible
667 /// via the `Child`.
668 pub fn seccomp_notify(&mut self) -> &mut Self {
669 self.container.seccomp_notify();
670 self
671 }
672
673 /// Sets the controlling pseudoterminal for the child process).
674 ///
675 /// In the child process, this has the effect of:
676 /// 1. Creating a new session (with `setsid()`).
677 /// 2. Using an `ioctl` to set the controlling terminal.
678 /// 3. Setting this file descriptor as the stdio streams.
679 ///
680 /// NOTE: Since this modifies the stdio streams, calling this will reset
681 /// [`Self::stdin`], [`Self::stdout`], and [`Self::stderr`] back to
682 /// [`Stdio::inherit()`].
683 pub fn pty(&mut self, child: PtyChild) -> &mut Self {
684 self.container.pty(child);
685 self
686 }
687
688 /// Finds the path to the program.
689 pub fn find_program(&self) -> io::Result<PathBuf> {
690 let program = Path::new(self.get_program());
691
692 if program.is_absolute() {
693 // Note: We shouldn't canonicalize here since that will follow
694 // symlinks. Instead, just make sure the file exists and is
695 // executable.
696 let metadata = program.metadata()?;
697
698 if metadata.is_file() && metadata.permissions().mode() & 0o111 != 0 {
699 Ok(program.to_path_buf())
700 } else {
701 Err(Errno::EPERM.into())
702 }
703 } else if program.components().count() == 1 {
704 let path = self.get_env("PATH").unwrap_or_default();
705
706 let paths = path
707 .as_bytes()
708 .split(|c| *c == b':')
709 .map(|bytes| Path::new(OsStr::from_bytes(bytes)));
710
711 find_program_in_paths(program, paths)
712 .ok_or_else(|| io::Error::other(format!("Could not find {:?} in $PATH", program)))
713 .map(path::absolute)?
714 } else {
715 // Assume it's in the current directory
716 let mut path = match self.get_current_dir() {
717 Some(path) => path.to_owned(),
718 None => std::env::current_dir()?,
719 };
720 path.push(program);
721 path.canonicalize()
722 }
723 }
724}
725
726fn find_program_in_paths<I, S>(program: &Path, iter: I) -> Option<PathBuf>
727where
728 I: IntoIterator<Item = S>,
729 S: AsRef<Path>,
730{
731 for path in iter.into_iter() {
732 let path = path.as_ref().join(program);
733 if let Ok(metadata) = path.metadata()
734 && metadata.is_file()
735 {
736 if metadata.permissions().mode() & 0o111 != 0 {
737 return Some(path);
738 } else {
739 continue;
740 }
741
742 #[cfg(not(unix))]
743 return Some(path);
744 }
745 }
746
747 None
748}
749
750#[cfg(test)]
751mod tests {
752 use super::*;
753
754 #[test]
755 fn find_program() {
756 assert!(Command::new("cat").find_program().unwrap().is_absolute(),);
757 }
758
759 #[test]
760 fn get_program() {
761 assert_eq!(Command::new("cat").get_program(), "cat");
762 }
763
764 #[test]
765 fn get_arg0() {
766 assert_eq!(Command::new("cat").get_arg0(), "cat");
767 assert_eq!(Command::new("cat").arg0("dog").get_arg0(), "dog");
768 assert_eq!(
769 Command::new("cat").arg0("dog").program("catdog").get_arg0(),
770 "catdog"
771 );
772 }
773
774 #[test]
775 fn get_args() {
776 assert_eq!(
777 Command::new("cat")
778 .arg("a")
779 .arg("b")
780 .arg("c")
781 .get_args()
782 .collect::<Vec<_>>(),
783 &[OsStr::new("a"), OsStr::new("b"), OsStr::new("c")]
784 );
785 }
786
787 #[test]
788 fn prepend_args() {
789 let mut command = Command::new("echo");
790 command.args(["1", "2", "3"]);
791 command.prepend_args(["a", "b", "c"]);
792 assert_eq!(command.get_arg0(), "echo");
793
794 let args = command.get_args().collect::<Vec<_>>();
795 assert_eq!(
796 args,
797 &[
798 OsStr::new("a"),
799 "b".as_ref(),
800 "c".as_ref(),
801 "1".as_ref(),
802 "2".as_ref(),
803 "3".as_ref()
804 ]
805 );
806 }
807}