fn run()

in xar/clean_xar_mounts/src/main.rs [559:658]


fn run() -> Result<()> {
    let matches = App::new("Clean XAR Mounts")
        .arg(
            Arg::with_name("timeout")
                .long("timeout")
                .default_value("15")
                .help("time, in minutes, after a xar was mounted to attempt to unmount it"),
        )
        .arg(
            Arg::with_name("verbose")
                .long("verbose")
                .short("v")
                .help("display detailed output"),
        )
        .arg(
            Arg::with_name("dryrun")
                .long("dry-run")
                .help("display detailed output"),
        )
        .get_matches();
    let timeout = value_t!(matches, "timeout", u32)?;
    let dryrun = matches.is_present("dryrun");
    let level = if matches.is_present("verbose") {
        slog::Level::Debug
    } else {
        slog::Level::Info
    };

    let root_log = setup_logger(level);

    let orig_ns_fd = nix::fcntl::open(
        "/proc/self/ns/mnt",
        nix::fcntl::OFlag::O_RDONLY,
        nix::sys::stat::Mode::from_bits(0700).unwrap(),
    )?;
    let mount_namespaces = get_mount_namespaces()?;
    info!(
        root_log,
        "Considering {} namespaces",
        mount_namespaces.len()
    );
    for nsinfo in mount_namespaces {
        info!(
            root_log,
            "Entering namespace {:?}...", nsinfo.namespace_path
        );
        // Enter the new namespace and then check /proc/mounts for the
        // now-visible mounts.
        let mounts = get_mounts(&nsinfo, &root_log);
        if mounts.is_err() {
            info!(
                root_log,
                "Unable to read mounts in {:?}", nsinfo.namespace_path
            );
            continue;
        }
        let _ns_saver = NamespaceSaver::new(orig_ns_fd, &nsinfo.namespace_path);
        if _ns_saver.is_err() && nix::unistd::geteuid().is_root() {
            info!(
                root_log,
                "Unable to enter namespace {:?}, skipping", nsinfo.namespace_path
            );
            continue;
        }
        for mount in mounts.unwrap() {
            let result = should_unmount(&root_log, &mount, timeout)?;
            if result.should_unmount {
                // TODO: consider forking and chrooting into the
                // process' chroot rather than constructing a path
                // from outside.  It may not always be true that we
                // can append paths to find the actual mount point to
                // unmount.
                let mut target = mount.chroot.clone();
                target.push(&mount.mountpoint[1..]); // strip leading slash
                info!(
                    root_log,
                    "unmounting {:?}:{:?}", nsinfo.namespace_path, target
                );
                if !dryrun {
                    if nix::unistd::geteuid().is_root() {
                        if let Err(e) = nix::mount::umount(&target) {
                            info!(root_log, "Failed to unmount {:?}: {}", target, e);
                        }
                    } else {
                        let output = Command::new("fusermount").arg("-u").arg(&target).output()?;
                        if !output.status.success() {
                            info!(
                                root_log,
                                "fusermount -u failed to unmount {:?}: {}",
                                target,
                                String::from_utf8_lossy(&output.stderr).trim()
                            );
                        }
                    }
                }
            }
        }
    }
    Ok(())
}