• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 //! shell
16 #![allow(missing_docs)]
17 
18 use crate::utils::hdc_log::*;
19 use hdc::config::TaskMessage;
20 use hdc::config::{HdcCommand, SHELL_PROG, SHELL_TEMP};
21 use hdc::transfer;
22 
23 use std::collections::HashMap;
24 use std::io::{self, Error, ErrorKind, Read as _, Write as _};
25 use std::os::fd::{AsFd, AsRawFd};
26 use std::os::unix::process::CommandExt;
27 use std::process::Child;
28 use std::sync::{Arc, Mutex};
29 use std::time::Duration;
30 
31 use ylong_runtime::sync::mpsc;
32 
33 struct Command {
34     inner: std::process::Command,
35 }
36 
37 impl Command {
new(prog: &str) -> Self38     pub fn new(prog: &str) -> Self {
39         Self {
40             inner: std::process::Command::new(prog),
41         }
42     }
43 
args(&mut self, args: Vec<&str>) -> &mut Self44     pub fn args(&mut self, args: Vec<&str>) -> &mut Self {
45         self.inner.args(args);
46         self
47     }
48 
set_pts(&mut self, pts: &Pts) -> io::Result<()>49     pub fn set_pts(&mut self, pts: &Pts) -> io::Result<()> {
50         let pipes = pts.setup_pipes()?;
51         self.inner.stdin(pipes.stdin);
52         self.inner.stdout(pipes.stdout);
53         self.inner.stderr(pipes.stderr);
54 
55         unsafe { self.inner.pre_exec(pts.session_leader()) };
56         Ok(())
57     }
58 
spawn(&mut self) -> io::Result<std::process::Child>59     pub fn spawn(&mut self) -> io::Result<std::process::Child> {
60         self.inner.spawn()
61     }
62 }
63 
64 struct Pty {
65     inner: nix::pty::PtyMaster,
66 }
67 
68 impl Pty {
new() -> io::Result<Self>69     pub fn new() -> io::Result<Self> {
70         if let Ok(pty_master) = nix::pty::posix_openpt(
71             nix::fcntl::OFlag::O_RDWR | nix::fcntl::OFlag::O_NOCTTY | nix::fcntl::OFlag::O_CLOEXEC,
72         ) {
73             if nix::pty::grantpt(&pty_master).is_ok() && nix::pty::unlockpt(&pty_master).is_ok() {
74                 return Ok(Self { inner: pty_master });
75             }
76         }
77         Err(Error::new(ErrorKind::Other, "pty init failed"))
78     }
79 
resize(&self, ws_row: u16, ws_col: u16)80     pub fn resize(&self, ws_row: u16, ws_col: u16) {
81         let size = nix::pty::Winsize {
82             ws_row,
83             ws_col,
84             ws_xpixel: 0,
85             ws_ypixel: 0,
86         };
87         let fd = self.inner.as_raw_fd();
88         let _ = unsafe { set_term_size(fd, std::ptr::NonNull::from(&size).as_ptr()) }.map(|_| ());
89     }
90 
get_pts(&self) -> io::Result<Pts>91     pub fn get_pts(&self) -> io::Result<Pts> {
92         let fd = std::fs::OpenOptions::new()
93             .read(true)
94             .write(true)
95             .open(nix::pty::ptsname_r(&self.inner)?)?
96             .into();
97         Ok(Pts { inner: fd })
98     }
99 }
100 
101 impl std::os::fd::AsFd for Pty {
as_fd(&self) -> std::os::fd::BorrowedFd<'_>102     fn as_fd(&self) -> std::os::fd::BorrowedFd<'_> {
103         let raw_fd = self.inner.as_raw_fd();
104         unsafe { std::os::fd::BorrowedFd::borrow_raw(raw_fd) }
105     }
106 }
107 
108 impl io::Read for Pty {
read(&mut self, buf: &mut [u8]) -> io::Result<usize>109     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
110         self.inner.read(buf)
111     }
112 }
113 
114 impl io::Write for Pty {
write(&mut self, buf: &[u8]) -> io::Result<usize>115     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
116         self.inner.write(buf)
117     }
118 
flush(&mut self) -> io::Result<()>119     fn flush(&mut self) -> io::Result<()> {
120         self.inner.flush()
121     }
122 }
123 
124 struct Pts {
125     inner: std::os::fd::OwnedFd,
126 }
127 
128 struct Pipes {
129     stdin: std::process::Stdio,
130     stdout: std::process::Stdio,
131     stderr: std::process::Stdio,
132 }
133 
134 impl Pts {
setup_pipes(&self) -> io::Result<Pipes>135     pub fn setup_pipes(&self) -> io::Result<Pipes> {
136         Ok(Pipes {
137             stdin: self.inner.try_clone()?.into(),
138             stdout: self.inner.try_clone()?.into(),
139             stderr: self.inner.try_clone()?.into(),
140         })
141     }
142 
session_leader(&self) -> impl FnMut() -> io::Result<()>143     pub fn session_leader(&self) -> impl FnMut() -> io::Result<()> {
144         let fd = self.inner.as_raw_fd();
145         move || {
146             nix::unistd::setsid()?;
147             unsafe { set_controlling_terminal(fd, std::ptr::null()) }?;
148             Ok(())
149         }
150     }
151 }
152 
153 nix::ioctl_write_ptr_bad!(set_term_size, libc::TIOCSWINSZ, nix::pty::Winsize);
154 
155 nix::ioctl_write_ptr_bad!(set_controlling_terminal, libc::TIOCSCTTY, libc::c_int);
156 
157 pub struct PtyTask {
158     pub handle: ylong_runtime::task::JoinHandle<()>,
159     pub tx: mpsc::BoundedSender<Vec<u8>>,
160 }
161 
162 struct PtyProcess {
163     pub pty: Pty,
164     pub child: Child,
165     pub pty_fd: i32,
166     channel_id: u32,
167 }
168 
169 impl PtyProcess {
new(pty: Pty, child: Child, channel_id: u32) -> Self170     fn new(pty: Pty, child: Child, channel_id: u32) -> Self {
171         let pty_fd = pty.as_fd().as_raw_fd();
172         Self {
173             pty,
174             child,
175             pty_fd,
176             channel_id,
177         }
178     }
179 
pty_echo( &mut self, buf: &mut [u8], ret_command: HdcCommand, ) -> io::Result<TaskMessage>180     async fn pty_echo(
181         &mut self,
182         buf: &mut [u8],
183         ret_command: HdcCommand,
184     ) -> io::Result<TaskMessage> {
185         match self.pty.read(buf) {
186             Ok(bytes) => {
187                 let message = TaskMessage {
188                     channel_id: self.channel_id,
189                     command: ret_command,
190                     payload: buf[..bytes].to_vec(),
191                 };
192                 hdc::trace!("read {bytes} bytes from pty");
193                 Ok(message)
194             }
195             Err(e) => {
196                 hdc::warn!("pty read failed: {e:?}");
197                 Err(e)
198             }
199         }
200     }
201 }
202 
init_pty_process(cmd: Option<String>, channel_id: u32) -> io::Result<PtyProcess>203 fn init_pty_process(cmd: Option<String>, channel_id: u32) -> io::Result<PtyProcess> {
204     let pty = Pty::new()?;
205     let pts = pty.get_pts()?;
206     pty.resize(24, 80);
207 
208     // Command::new(sh) for interactive
209     // Command::new(cmd[0]).args(cmd[1..]) for normal
210     let child = match cmd {
211         None => {
212             let mut command = Command::new(SHELL_PROG);
213             command.set_pts(&pts)?;
214             command.spawn()?
215         }
216         Some(cmd) => {
217             let trimed = cmd.trim_matches('"');
218             let params = ["-c", trimed].to_vec();
219             let mut proc = Command::new(SHELL_PROG);
220             let command = proc.args(params);
221             command.set_pts(&pts)?;
222             command.spawn()?
223         }
224     };
225     Ok(PtyProcess::new(pty, child, channel_id))
226 }
227 
subprocess_task( cmd: Option<String>, session_id: u32, channel_id: u32, ret_command: HdcCommand, mut rx: mpsc::BoundedReceiver<Vec<u8>>, )228 async fn subprocess_task(
229     cmd: Option<String>,
230     session_id: u32,
231     channel_id: u32,
232     ret_command: HdcCommand,
233     mut rx: mpsc::BoundedReceiver<Vec<u8>>,
234 ) {
235     let mut pty_process = init_pty_process(cmd, channel_id).unwrap();
236     let mut buf = [0_u8; 30720];
237 
238     loop {
239         let mut tv = nix::sys::time::TimeVal::new(0, 50000);
240         let mut set = nix::sys::select::FdSet::new();
241         set.insert(pty_process.pty_fd);
242 
243         match nix::sys::select::select(None, Some(&mut set), None, None, Some(&mut tv)) {
244             Ok(_) => {
245                 if set.contains(pty_process.pty_fd) {
246                     match pty_process.pty_echo(&mut buf, ret_command).await {
247                         Err(_) => break,
248                         Ok(message) => transfer::put(session_id, message).await,
249                     }
250                 }
251             }
252             Err(e) => {
253                 hdc::error!("select failed: {e:?}");
254                 break;
255             }
256         }
257 
258         if let Ok(val) = rx.recv_timeout(Duration::from_millis(50)).await {
259             pty_process.pty.write_all(&val).unwrap();
260             if val[..].contains(&0x4_u8) {
261                 // ctrl-D: end pty
262                 hdc::info!("ctrl-D: end pty");
263                 break;
264             }
265         }
266 
267         match pty_process.child.try_wait() {
268             Ok(Some(_)) => {
269                 hdc::debug!("interactive shell finish a process");
270                 // break;
271             }
272             Ok(None) => {}
273             Err(e) => {
274                 hdc::error!("interactive shell wait failed: {e:?}");
275                 break;
276             }
277         }
278     }
279     let message = TaskMessage {
280         channel_id,
281         command: HdcCommand::KernelChannelClose,
282         payload: vec![1],
283     };
284     transfer::put(session_id, message).await;
285     PtyMap::del(channel_id).await;
286 }
287 
288 impl PtyTask {
new( session_id: u32, channel_id: u32, cmd: Option<String>, ret_command: HdcCommand, ) -> Self289     pub fn new(
290         session_id: u32,
291         channel_id: u32,
292         cmd: Option<String>,
293         ret_command: HdcCommand,
294     ) -> Self {
295         let (tx, rx) = ylong_runtime::sync::mpsc::bounded_channel::<Vec<u8>>(16);
296         let handle = ylong_runtime::spawn(subprocess_task(
297             cmd,
298             session_id,
299             channel_id,
300             ret_command,
301             rx,
302         ));
303         Self { handle, tx }
304     }
305 }
306 
307 type PtyMap_ = Arc<Mutex<HashMap<u32, Arc<PtyTask>>>>;
308 pub struct PtyMap {}
309 impl PtyMap {
get_instance() -> PtyMap_310     fn get_instance() -> PtyMap_ {
311         static mut PTY_MAP: Option<PtyMap_> = None;
312         unsafe {
313             PTY_MAP
314                 .get_or_insert_with(|| Arc::new(Mutex::new(HashMap::new())))
315                 .clone()
316         }
317     }
318 
get(channel_id: u32) -> Option<Arc<PtyTask>>319     pub async fn get(channel_id: u32) -> Option<Arc<PtyTask>> {
320         let pty_map = Self::get_instance();
321         let map = pty_map.lock().unwrap();
322         if let Some(pty_task) = map.get(&channel_id) {
323             return Some(pty_task.clone());
324         }
325         None
326     }
327 
put(channel_id: u32, pty_task: PtyTask)328     pub async fn put(channel_id: u32, pty_task: PtyTask) {
329         let pty_map = Self::get_instance();
330         let mut map = pty_map.lock().unwrap();
331         let arc_pty_task = Arc::new(pty_task);
332         map.insert(channel_id, arc_pty_task);
333     }
334 
del(channel_id: u32)335     pub async fn del(channel_id: u32) {
336         let pty_map = Self::get_instance();
337         let mut map = pty_map.lock().unwrap();
338         map.remove(&channel_id);
339         let file_name = format!("{SHELL_TEMP}-{channel_id}");
340         let _ = std::fs::remove_file(file_name);
341     }
342 }
343