Skip to main content

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}