1 // Copyright (c) 2024 Huawei Technologies Co.,Ltd. All rights reserved. 2 // 3 // StratoVirt is licensed under Mulan PSL v2. 4 // You can use this software according to the terms and conditions of the Mulan 5 // PSL v2. 6 // You may obtain a copy of Mulan PSL v2 at: 7 // http://license.coscl.org.cn/MulanPSL2 8 // THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY 9 // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO 10 // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 11 // See the Mulan PSL v2 for more details. 12 13 use std::{ 14 env, 15 io::{Read, Write}, 16 os::unix::{ 17 io::AsRawFd, 18 net::{UnixListener, UnixStream}, 19 }, 20 path::PathBuf, 21 }; 22 23 use anyhow::{anyhow, bail, Context, Result}; 24 use nix::unistd::{self, chdir}; 25 26 use crate::utils::OzonecErr; 27 28 pub const NOTIFY_SOCKET: &str = "notify.sock"; 29 30 pub struct NotifyListener { 31 socket: UnixListener, 32 } 33 34 impl NotifyListener { new(root: PathBuf) -> Result<Self>35 pub fn new(root: PathBuf) -> Result<Self> { 36 // The length of path of Unix domain socket has the limit 108, which is smaller then 37 // the maximum length of file on Linux (255). 38 let cwd = env::current_dir().with_context(|| OzonecErr::GetCurDir)?; 39 chdir(&root).with_context(|| "Failed to chdir to root directory")?; 40 let listener = 41 UnixListener::bind(NOTIFY_SOCKET).with_context(|| "Failed to bind notify socket")?; 42 chdir(&cwd).with_context(|| "Failed to chdir to previous working directory")?; 43 Ok(Self { socket: listener }) 44 } 45 wait_for_start_container(&self) -> Result<()>46 pub fn wait_for_start_container(&self) -> Result<()> { 47 match self.socket.accept() { 48 Ok((mut socket, _)) => { 49 let mut response = String::new(); 50 socket 51 .read_to_string(&mut response) 52 .with_context(|| "Invalid response from notify socket")?; 53 } 54 Err(e) => { 55 bail!("Failed to accept on notify socket: {}", e); 56 } 57 } 58 Ok(()) 59 } 60 close(&self) -> Result<()>61 pub fn close(&self) -> Result<()> { 62 Ok(unistd::close(self.socket.as_raw_fd())?) 63 } 64 } 65 66 pub struct NotifySocket { 67 path: PathBuf, 68 } 69 70 impl NotifySocket { new(path: &PathBuf) -> Self71 pub fn new(path: &PathBuf) -> Self { 72 Self { path: path.into() } 73 } 74 notify_container_start(&mut self) -> Result<()>75 pub fn notify_container_start(&mut self) -> Result<()> { 76 let cwd = env::current_dir().with_context(|| OzonecErr::GetCurDir)?; 77 let root_path = self 78 .path 79 .parent() 80 .ok_or_else(|| anyhow!("Invalid notify socket path"))?; 81 chdir(root_path).with_context(|| "Failed to chdir to root directory")?; 82 83 let mut stream = 84 UnixStream::connect(NOTIFY_SOCKET).with_context(|| "Failed to connect notify.sock")?; 85 stream.write_all(b"start container")?; 86 chdir(&cwd).with_context(|| "Failed to chdir to previous working directory")?; 87 88 Ok(()) 89 } 90 } 91 92 #[cfg(test)] 93 mod test { 94 use std::fs::{create_dir_all, remove_dir_all}; 95 96 use nix::sys::wait::{waitpid, WaitStatus}; 97 98 use crate::linux::process::clone_process; 99 100 use super::*; 101 102 #[test] test_notify_socket()103 fn test_notify_socket() { 104 remove_dir_all("/tmp/ozonec").unwrap_or_default(); 105 106 let root = PathBuf::from("/tmp/ozonec/notify_socket"); 107 create_dir_all(&root).unwrap(); 108 109 let socket_path = root.join(NOTIFY_SOCKET); 110 let mut socket = NotifySocket::new(&socket_path); 111 let listener = NotifyListener::new(root.clone()).unwrap(); 112 let child = clone_process("notify_socket", || { 113 listener.wait_for_start_container().unwrap(); 114 Ok(1) 115 }) 116 .unwrap(); 117 socket.notify_container_start().unwrap(); 118 119 match waitpid(child, None) { 120 Ok(WaitStatus::Exited(_, s)) => { 121 assert_eq!(s, 1); 122 } 123 Ok(_) => (), 124 Err(e) => { 125 panic!("Failed to waitpid for child process: {e}"); 126 } 127 } 128 } 129 } 130