localmount.rs (3356B)
1 use async_process::{Command, Stdio}; 2 use async_trait::async_trait; 3 use log::info; 4 use nix::unistd::sync; 5 6 use kanit_common::error::{Context, ErrorKind, Result, StaticError}; 7 use kanit_unit::{Dependencies, Unit}; 8 9 use crate::mounts::{is_fs_mounted, parse_mounts}; 10 use crate::oneshot::{Clock, Modules, RootFs}; 11 use crate::unit_name; 12 13 pub struct LocalMount; 14 15 async fn kill_fs_users(fs: &str) -> Result<()> { 16 Command::new("fuser") 17 .args(["-KILL", "-k", "-m", fs]) 18 .spawn() 19 .context("failed to spawn fuser")? 20 .status() 21 .await 22 .context("failed to wait fuser")?; 23 24 Ok(()) 25 } 26 27 #[async_trait] 28 impl Unit for LocalMount { 29 unit_name!("localmount"); 30 31 fn dependencies(&self) -> Dependencies { 32 Dependencies::new() 33 .need(RootFs.name()) 34 .after(Clock.name()) 35 .after(Modules.name()) 36 .clone() 37 } 38 39 async fn start(&mut self) -> Result<()> { 40 info!("mounting local filesystems"); 41 42 let succ = Command::new("mount") 43 .stdout(Stdio::null()) 44 .args(["-a", "-t", "noproc"]) 45 .spawn() 46 .context("failed to spawn mount")? 47 .status() 48 .await 49 .context("failed to wait on mount")? 50 .success(); 51 52 if !succ { 53 Err(StaticError("failed to mount local filesystems")).kind(ErrorKind::Recoverable)?; 54 } 55 56 Ok(()) 57 } 58 59 async fn stop(&mut self) -> Result<()> { 60 sync(); 61 62 let mounted = async_fs::read_to_string("/proc/mounts") 63 .await 64 .context("failed to read mounts")?; 65 // loopback 66 info!("unmounting loopback"); 67 68 for mount in parse_mounts(&mounted)? { 69 if !is_fs_mounted(mount.fs_file).await.unwrap_or(false) { 70 continue; 71 } 72 73 if !mount.fs_file.starts_with("/dev/loop") { 74 continue; 75 } 76 77 // should already be dead since init should've killed 78 kill_fs_users(mount.fs_file).await?; 79 80 Command::new("umount") 81 .args(["-d", mount.fs_file]) 82 .spawn() 83 .context("failed to spawn umount")? 84 .status() 85 .await 86 .context("failed to wait umount")?; 87 } 88 89 // now everything but network 90 info!("unmounting filesystems"); 91 for mount in parse_mounts(&mounted)? { 92 let n = mount.fs_file; 93 94 if !is_fs_mounted(n).await.unwrap_or(false) { 95 continue; 96 } 97 98 if n == "/" 99 || n == "/dev" 100 || n == "/sys" 101 || n == "/proc" 102 || n == "/run" 103 || n.starts_with("/dev/") 104 || n.starts_with("/sys/") 105 || n.starts_with("/proc/") 106 { 107 continue; 108 } 109 110 if mount.fs_mntopts.contains_key("_netdev") { 111 continue; 112 } 113 114 kill_fs_users(mount.fs_file).await?; 115 116 Command::new("umount") 117 .arg(mount.fs_file) 118 .spawn() 119 .context("failed to spawn umount")? 120 .status() 121 .await 122 .context("failed to wait umount")?; 123 } 124 125 Ok(()) 126 } 127 }