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